Skip to content

Commit

Permalink
Merge branch 'master' of github.com:lwlcom/cisco_exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
l3akage committed Jan 24, 2021
2 parents f8162e7 + 936d519 commit 82b363d
Show file tree
Hide file tree
Showing 10 changed files with 36 additions and 29 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ web.telemetry-path | Path under which to expose metrics. | /metrics
ssh.targets | Comma seperated list of hosts to scrape |
ssh.user | Username to use for SSH connection | cisco_exporter
ssh.keyfile | Key file to use for SSH connection | cisco_exporter
ssh.timeout | Timeout in seconds to use for SSH connection | 5
ssh.batch-size | The SSH response batch size | 10000
debug | Show verbose debug output | false
legacy.ciphers | Allow insecure legacy ciphers: aes128-cbc 3des-cbc aes192-cbc aes256-cbc | false

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion cisco_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (c *ciscoCollector) collectForHost(host string, ch chan<- prometheus.Metric
ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, time.Since(t).Seconds(), l...)
}()

conn, err := connector.NewSSSHConnection(host, *sshUsername, *sshKeyFile, *legacyCiphers)
conn, err := connector.NewSSSHConnection(host, *sshUsername, *sshKeyFile, *legacyCiphers, *sshTimeout, *sshBatchSize)
if err != nil {
log.Errorln(err)
ch <- prometheus.MustNewConstMetric(upDesc, prometheus.GaugeValue, 0, l...)
Expand Down
22 changes: 10 additions & 12 deletions connector/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ import (
"golang.org/x/crypto/ssh"
)

const timeoutInSeconds = 5

var (
cachedConfig *ssh.ClientConfig
lock = &sync.Mutex{}
)

func config(user, keyFile string, legacyCiphers bool) (*ssh.ClientConfig, error) {
func config(user, keyFile string, legacyCiphers bool, timeout int) (*ssh.ClientConfig, error) {
lock.Lock()
defer lock.Unlock()

Expand All @@ -39,7 +37,7 @@ func config(user, keyFile string, legacyCiphers bool) (*ssh.ClientConfig, error)
User: user,
Auth: []ssh.AuthMethod{pk},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: timeoutInSeconds * time.Second,
Timeout: time.Duration(timeout) * time.Second,
}
if legacyCiphers {
cachedConfig.SetDefaults()
Expand All @@ -50,16 +48,17 @@ func config(user, keyFile string, legacyCiphers bool) (*ssh.ClientConfig, error)
}

// NewSSSHConnection connects to device
func NewSSSHConnection(host, user, keyFile string, legacyCiphers bool) (*SSHConnection, error) {
func NewSSSHConnection(host, user, keyFile string, legacyCiphers bool, timeout int, batchSize int) (*SSHConnection, error) {
if !strings.Contains(host, ":") {
host = host + ":22"
}

c := &SSHConnection{
Host: host,
legacyCiphers: legacyCiphers,
batchSize: batchSize,
}
err := c.Connect(user, keyFile)
err := c.Connect(user, keyFile, timeout)
if err != nil {
return nil, err
}
Expand All @@ -75,11 +74,12 @@ type SSHConnection struct {
stdout io.Reader
session *ssh.Session
legacyCiphers bool
batchSize int
}

// Connect connects to the device
func (c *SSHConnection) Connect(user, keyFile string) error {
config, err := config(user, keyFile, c.legacyCiphers)
func (c *SSHConnection) Connect(user, keyFile string, timeout int) error {
config, err := config(user, keyFile, c.legacyCiphers, timeout)
if err != nil {
return err
}
Expand Down Expand Up @@ -127,11 +127,9 @@ func (c *SSHConnection) RunCommand(cmd string) (string, error) {
select {
case res := <-outputChan:
return res.output, res.err
case <-time.After(timeoutInSeconds * time.Second):
case <-time.After(cachedConfig.Timeout):
return "", errors.New("Timeout reached")
}

return "", errors.New("Something went wrong")
}

// Close closes connection
Expand Down Expand Up @@ -160,7 +158,7 @@ func loadPublicKeyFile(file string) (ssh.AuthMethod, error) {

func (c *SSHConnection) readln(ch chan result, cmd string, r io.Reader) {
re := regexp.MustCompile(`.+#\s?$`)
buf := make([]byte, 10000)
buf := make([]byte, c.batchSize)
loadStr := ""
for {
n, err := r.Read(buf)
Expand Down
2 changes: 2 additions & 0 deletions interfaces/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ type Interface struct {

InputBytes float64
OutputBytes float64

Speed string
}
4 changes: 2 additions & 2 deletions interfaces/interface_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var (
)

func init() {
l := []string{"target", "name", "description", "mac"}
l := []string{"target", "name", "description", "mac", "speed"}
receiveBytesDesc = prometheus.NewDesc(prefix+"receive_bytes", "Received data in bytes", l, nil)
receiveErrorsDesc = prometheus.NewDesc(prefix+"receive_errors", "Number of errors caused by incoming packets", l, nil)
receiveDropsDesc = prometheus.NewDesc(prefix+"receive_drops", "Number of dropped incoming packets", l, nil)
Expand Down Expand Up @@ -94,7 +94,7 @@ func (c *interfaceCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me
}

for _, item := range items {
l := append(labelValues, item.Name, item.Description, item.MacAddress)
l := append(labelValues, item.Name, item.Description, item.MacAddress, item.Speed)

errorStatus := 0
if item.AdminStatus != item.OperStatus {
Expand Down
25 changes: 14 additions & 11 deletions interfaces/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ func (c *interfaceCollector) Parse(ostype string, output string) ([]Interface, e
return nil, errors.New("'show interface' is not implemented for " + ostype)
}
items := []Interface{}
newIfRegexp, _ := regexp.Compile(`(?:^!?(?: |admin|show|.+#).*$|^$)`)
macRegexp, _ := regexp.Compile(`^\s+Hardware(?: is|:) .+, address(?: is|:) (.*) \(.*\)$`)
deviceNameRegexp, _ := regexp.Compile(`^([a-zA-Z0-9\/\.-]+) is.*$`)
adminStatusRegexp, _ := regexp.Compile(`^.+ is (administratively)?\s*(up|down).*, line protocol is.*$`)
adminStatusNXOSRegexp, _ := regexp.Compile(`^\S+ is (up|down)(?:\s|,)(\(Administratively down\))?.*$`)
descRegexp, _ := regexp.Compile(`^\s+Description: (.*)$`)
dropsRegexp, _ := regexp.Compile(`^\s+Input queue: \d+\/\d+\/(\d+)\/\d+ .+ Total output drops: (\d+)$`)
inputBytesRegexp, _ := regexp.Compile(`^\s+\d+ (?:packets input,|input packets)\s+(\d+) bytes.*$`)
outputBytesRegexp, _ := regexp.Compile(`^\s+\d+ (?:packets output,|output packets)\s+(\d+) bytes.*$`)
inputErrorsRegexp, _ := regexp.Compile(`^\s+(\d+) input error(?:s,)? .*$`)
outputErrorsRegexp, _ := regexp.Compile(`^\s+(\d+) output error(?:s,)? .*$`)
newIfRegexp := regexp.MustCompile(`(?:^!?(?: |admin|show|.+#).*$|^$)`)
macRegexp := regexp.MustCompile(`^\s+Hardware(?: is|:) .+, address(?: is|:) (.*) \(.*\)$`)
deviceNameRegexp := regexp.MustCompile(`^([a-zA-Z0-9\/\.-]+) is.*$`)
adminStatusRegexp := regexp.MustCompile(`^.+ is (administratively)?\s*(up|down).*, line protocol is.*$`)
adminStatusNXOSRegexp := regexp.MustCompile(`^\S+ is (up|down)(?:\s|,)?(\(Administratively down\))?.*$`)
descRegexp := regexp.MustCompile(`^\s+Description: (.*)$`)
dropsRegexp := regexp.MustCompile(`^\s+Input queue: \d+\/\d+\/(\d+)\/\d+ .+ Total output drops: (\d+)$`)
inputBytesRegexp := regexp.MustCompile(`^\s+\d+ (?:packets input,|input packets)\s+(\d+) bytes.*$`)
outputBytesRegexp := regexp.MustCompile(`^\s+\d+ (?:packets output,|output packets)\s+(\d+) bytes.*$`)
inputErrorsRegexp := regexp.MustCompile(`^\s+(\d+) input error(?:s,)? .*$`)
outputErrorsRegexp := regexp.MustCompile(`^\s+(\d+) output error(?:s,)? .*$`)
speedRegexp := regexp.MustCompile(`^\s+(.*)-duplex,\s(\d+) ((\wb)/s).*$`)

current := Interface{}
lines := strings.Split(output, "\n")
Expand Down Expand Up @@ -75,6 +76,8 @@ func (c *interfaceCollector) Parse(ostype string, output string) ([]Interface, e
current.InputErrors = util.Str2float64(matches[1])
} else if matches := outputErrorsRegexp.FindStringSubmatch(line); matches != nil {
current.OutputErrors = util.Str2float64(matches[1])
} else if matches := speedRegexp.FindStringSubmatch(line); matches != nil {
current.Speed = matches[2] + " " + matches[3]
}
}
return append(items, current), nil
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ var (
sshHosts = flag.String("ssh.targets", "", "SSH Hosts to scrape")
sshUsername = flag.String("ssh.user", "cisco_exporter", "Username to use for SSH connection")
sshKeyFile = flag.String("ssh.keyfile", "", "Key file to use for SSH connection")
sshTimeout = flag.Int("ssh.timeout", 5, "Timeout to use for SSH connection")
sshBatchSize = flag.Int("ssh.batch-size", 10000, "The SSH response batch size")
debug = flag.Bool("debug", false, "Show verbose debug output in log")
legacyCiphers = flag.Bool("legacy.ciphers", false, "Allow legacy CBC ciphers")
bgpEnabled = flag.Bool("bgp.enabled", true, "Scrape bgp metrics")
Expand Down
2 changes: 1 addition & 1 deletion optics/optics_collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (*opticsCollector) Describe(ch chan<- *prometheus.Desc) {

// Collect collects metrics from Cisco
func (c *opticsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error {
out, err := client.RunCommand("show interface")
out, err := client.RunCommand("show interfaces stats | exclude disabled")
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions optics/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
// ParseInterfaces parses cli output and returns list of interface names
func (c *opticsCollector) ParseInterfaces(ostype string, output string) ([]string, error) {
if ostype != rpc.IOSXE && ostype != rpc.NXOS && ostype != rpc.IOS {
return nil, errors.New("'show interfaces' is not implemented for " + ostype)
return nil, errors.New("'show interfaces stats' is not implemented for " + ostype)
}
var items []string
deviceNameRegexp, _ := regexp.Compile(`^([a-zA-Z0-9\/\.-]+) is.*$`)
deviceNameRegexp, _ := regexp.Compile(`^([a-zA-Z0-9\/\.-]+)\s*$`)
lines := strings.Split(output, "\n")
for _, line := range lines {
matches := deviceNameRegexp.FindStringSubmatch(line)
Expand Down

0 comments on commit 82b363d

Please sign in to comment.