diff --git a/README.md b/README.md index 39839201..e609995f 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ States map to human readable names like this: 4 = "Ex-Incr" 5 = "Ex-Full" ``` +* VirtualChassis (status of members) +``` +0 = "NotPrsnt" +1 = "NotPrsnt" +``` * VRRP (state per interface) States map to human readable names like this: ``` diff --git a/accounting/collector.go b/accounting/collector.go index a0c167bc..b6cbcd78 100644 --- a/accounting/collector.go +++ b/accounting/collector.go @@ -96,9 +96,16 @@ func (c *accountingCollector) Collect(client *rpc.Client, ch chan<- prometheus.M func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFlow, error) { var x = AccountingFlowRpc{} - err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services accounting flow inline-jflow", &x) + if err != nil { + return nil, err + } } if x.Error.Message != "" { @@ -119,9 +126,16 @@ func (c *accountingCollector) accountingFlows(client *rpc.Client) (*AccountingFl func (c *accountingCollector) accountingFailures(client *rpc.Client) (*AccountingError, error) { var x = AccountingFlowErrorRpc{} - err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("0", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services accounting errors inline-jflow fpc-slot 0", &x) + if err != nil { + return nil, err + } } return &AccountingError{ diff --git a/alarm/collector.go b/alarm/collector.go index 55754d29..4df4b693 100644 --- a/alarm/collector.go +++ b/alarm/collector.go @@ -78,6 +78,13 @@ func (c *alarmCollector) alarmCounter(client *rpc.Client) (*AlarmCounter, *[]Ala "show chassis alarms", } + if client.Netconf { + cmds = []string{ + "", + "", + } + } + var alarms []AlarmDetails messages := make(map[string]interface{}) diff --git a/bfd/collector.go b/bfd/collector.go index 4dc0db06..f9746155 100644 --- a/bfd/collector.go +++ b/bfd/collector.go @@ -41,10 +41,17 @@ func (*bfdCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *bfdCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { - var x = bfdRpc{} - err := client.RunCommandAndParse("show bfd session extensive", &x) - if err != nil { - return err + var x = bfdRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show bfd session extensive", &x) + if err != nil { + return err + } } for _, bfds := range x.Information.BfdSessions { diff --git a/bgp/collector.go b/bgp/collector.go index 9c9b2680..9a14940e 100644 --- a/bgp/collector.go +++ b/bgp/collector.go @@ -77,9 +77,13 @@ func (c *bgpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *bgpCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = BGPRPC{} var cmd strings.Builder - cmd.WriteString("show bgp neighbor") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) + if client.Netconf { + cmd.WriteString("") + } else { + cmd.WriteString("show bgp neighbor") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } } err := client.RunCommandAndParse(cmd.String(), &x) @@ -87,6 +91,7 @@ func (c *bgpCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, return err } + for _, peer := range x.Information.Peers { c.collectForPeer(peer, ch, labelValues) } diff --git a/collectors.go b/collectors.go index 9429bb19..c92ad26d 100644 --- a/collectors.go +++ b/collectors.go @@ -33,6 +33,7 @@ import ( "github.com/czerwonk/junos_exporter/security" "github.com/czerwonk/junos_exporter/storage" "github.com/czerwonk/junos_exporter/system" + "github.com/czerwonk/junos_exporter/virtualchassis" "github.com/czerwonk/junos_exporter/vrrp" "github.com/czerwonk/junos_exporter/vpws" ) @@ -105,6 +106,7 @@ func (c *collectors) initCollectorsForDevices(device *connector.Device) { c.addCollectorIfEnabledForDevice(device, "system", f.System, system.NewCollector) c.addCollectorIfEnabledForDevice(device, "power", f.Power, power.NewCollector) c.addCollectorIfEnabledForDevice(device, "mac", f.MAC, mac.NewCollector) + c.addCollectorIfEnabledForDevice(device, "virtualchassis", f.VirtualChassis, virtualchassis.NewCollector) c.addCollectorIfEnabledForDevice(device, "vrrp", f.VRRP, vrrp.NewCollector) c.addCollectorIfEnabledForDevice(device, "vpws", f.VPWS, vpws.NewCollector) c.addCollectorIfEnabledForDevice(device, "mpls_lsp", f.MPLS_LSP, mpls_lsp.NewCollector) diff --git a/config/config.go b/config/config.go index 61ea8fa6..acff763d 100644 --- a/config/config.go +++ b/config/config.go @@ -57,10 +57,12 @@ type FeatureConfig struct { RPKI bool `yaml:"rpki,omitempty"` RPM bool `yaml:"rpm,omitempty"` Satellite bool `yaml:"satellite,omitempty"` + Netconf bool `yaml:"netconf,omitempty"` System bool `yaml:"system,omitempty"` Power bool `yaml:"power,omitempty"` MAC bool `yaml:"mac,omitempty"` MPLS_LSP bool `yaml:"mpls_lsp,omitempty"` + VirtualChassis bool `yaml:"virtualchassis,omitempty"` VPWS bool `yaml:"vpws,omitempty"` VRRP bool `yaml:"vrrp,omitempty"` } @@ -127,9 +129,11 @@ func setDefaultValues(c *Config) { f.RPKI = false f.RPM = false f.Satellite = false + f.Netconf = false f.Power = false f.MAC = false f.MPLS_LSP = false + f.VirtualChassis = false f.VPWS = false f.VRRP = false f.BFD = false diff --git a/connector/connection.go b/connector/connection.go index e9d1250f..89155d50 100644 --- a/connector/connection.go +++ b/connector/connection.go @@ -4,10 +4,12 @@ import ( "bytes" "net" "sync" - + "fmt" + "io" "github.com/pkg/errors" "golang.org/x/crypto/ssh" + "github.com/Juniper/go-netconf/netconf" ) // SSHConnection encapsulates the connection to the device @@ -17,10 +19,27 @@ type SSHConnection struct { conn net.Conn mu sync.Mutex done chan struct{} + netconf bool + netconfsession *netconf.Session +} + +type TransportSSH struct { + transportBasicIO + sshClient *ssh.Client + sshSession *ssh.Session } + // RunCommand runs a command against the device func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { + if c.netconf { + return c.RunCommandNETCONF(cmd) + } else { + return c.RunCommandSSH(cmd) + } +} + +func (c *SSHConnection) RunCommandSSH(cmd string) ([]byte, error) { c.mu.Lock() defer c.mu.Unlock() @@ -45,6 +64,53 @@ func (c *SSHConnection) RunCommand(cmd string) ([]byte, error) { return b.Bytes(), nil } + +func (c *SSHConnection) RunCommandNETCONF(cmd string) ([]byte, error) { + c.mu.Lock() + defer c.mu.Unlock() + var err error + + if c.client == nil { + return nil, errors.New("not connected") + } + + t := &TransportSSH{} + if c.netconfsession == nil { + t.sshSession, err = c.client.NewSession() + if err != nil { + return nil, errors.Wrap(err, "could not open session") + } + + writer, err := t.sshSession.StdinPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdin") + } + + reader, err := t.sshSession.StdoutPipe() + if err != nil { + return nil, errors.Wrap(err, "could not open session stdout") + } + + t.ReadWriteCloser = netconf.NewReadWriteCloser(reader, writer) + t.sshSession.RequestSubsystem("netconf") + c.netconfsession = netconf.NewSession(t) + } + + reply, err := c.netconfsession.Exec(netconf.RawMethod(cmd)) + + if err != nil { + if err == io.EOF { + //probably lost the session, closing to force a reopen + fmt.Println("Error - Closing") + c.netconfsession.Close() + c.netconfsession = nil + } + return nil, errors.Wrap(err, "could not run command") + } + + return []byte(reply.RawReply), nil +} + func (c *SSHConnection) isConnected() bool { return c.conn != nil } diff --git a/connector/connection_manager.go b/connector/connection_manager.go index 8a44ca56..e39451f0 100644 --- a/connector/connection_manager.go +++ b/connector/connection_manager.go @@ -32,6 +32,7 @@ func WithKeepAliveInterval(d time.Duration) Option { } } + // WithKeepAliveTimeout sets the timeout after an ssh connection to be determined dead (default 15 seconds) func WithKeepAliveTimeout(d time.Duration) Option { return func(m *SSHConnectionManager) { @@ -39,6 +40,7 @@ func WithKeepAliveTimeout(d time.Duration) Option { } } + // SSHConnectionManager manages SSH connections to different devices type SSHConnectionManager struct { connections map[string]*SSHConnection @@ -65,7 +67,7 @@ func NewConnectionManager(opts ...Option) *SSHConnectionManager { } // Connect connects to a device or returns an long living connection -func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { +func (m *SSHConnectionManager) Connect(device *Device, netconf bool) (*SSHConnection, error) { m.mu.Lock() defer m.mu.Unlock() @@ -77,10 +79,10 @@ func (m *SSHConnectionManager) Connect(device *Device) (*SSHConnection, error) { return connection, nil } - return m.connect(device) + return m.connect(device, netconf) } -func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { +func (m *SSHConnectionManager) connect(device *Device, netconf bool) (*SSHConnection, error) { client, conn, err := m.connectToDevice(device) if err != nil { return nil, err @@ -91,6 +93,7 @@ func (m *SSHConnectionManager) connect(device *Device) (*SSHConnection, error) { client: client, device: device, done: make(chan struct{}), + netconf: netconf, } go m.keepAlive(c) diff --git a/connector/transport.go b/connector/transport.go new file mode 100644 index 00000000..bfaec9ee --- /dev/null +++ b/connector/transport.go @@ -0,0 +1,183 @@ +package connector + +import ( + "bytes" + "encoding/xml" + "fmt" + "io" + "regexp" + "github.com/Juniper/go-netconf/netconf" + +) + +const ( + // msgSeperator is used to separate sent messages via NETCONF + msgSeperator = "]]>]]>" + msgSeperator_v11 = "\n##\n" +) + + +// Transport interface defines what characterisitics make up a NETCONF transport +// layer object. +type Transport interface { + Send([]byte) error + Receive() ([]byte, error) + Close() error + ReceiveHello() (*netconf.HelloMessage, error) + SendHello(*netconf.HelloMessage) error + SetVersion(version string) +} + +type transportBasicIO struct { + io.ReadWriteCloser + //new add + version string +} + +func (t *transportBasicIO) SetVersion(version string) { + t.version = version +} + +// Sends a well formated NETCONF rpc message as a slice of bytes adding on the +// nessisary framining messages. +func (t *transportBasicIO) Send(data []byte) error { + var seperator []byte + var dataInfo []byte + //headlen := 0 + if t.version == "v1.1" { + seperator = append(seperator, []byte(msgSeperator_v11)...) + } else { + seperator = append(seperator, []byte(msgSeperator)...) + } + + if t.version == "v1.1" { + header := fmt.Sprintf("\n#%d\n", len(string(data))) + dataInfo = append(dataInfo, header...) + //t.Write([]byte(header)) + //headlen = len([]byte(header)) + } + dataInfo = append(dataInfo, data...) + dataInfo = append(dataInfo, seperator...) + _, err := t.Write(dataInfo) + + return err +} + +func (t *transportBasicIO) Receive() ([]byte, error) { + var seperator []byte + if t.version == "v1.1" { + seperator = append(seperator, []byte(msgSeperator_v11)...) + } else { + seperator = append(seperator, []byte(msgSeperator)...) + } + return t.WaitForBytes([]byte(seperator)) +} + +func (t *transportBasicIO) SendHello(hello *netconf.HelloMessage) error { + val, err := xml.Marshal(hello) + if err != nil { + return err + } + + header := []byte(xml.Header) + val = append(header, val...) + err = t.Send(val) + return err +} + +func (t *transportBasicIO) ReceiveHello() (*netconf.HelloMessage, error) { + hello := new(netconf.HelloMessage) + + val, err := t.Receive() + if err != nil { + return hello, err + } + + err = xml.Unmarshal(val, hello) + return hello, err +} + +func (t *transportBasicIO) Writeln(b []byte) (int, error) { + t.Write(b) + t.Write([]byte("\n")) + return 0, nil +} + +func (t *transportBasicIO) WaitForFunc(f func([]byte) (int, error)) ([]byte, error) { + var out bytes.Buffer + buf := make([]byte, 8192) + + pos := 0 + for { + n, err := t.Read(buf[pos : pos+(len(buf)/2)]) + if err != nil { + if err != io.EOF { + return nil, err + } + break + } + + if n > 0 { + end, err := f(buf[0 : pos+n]) + if err != nil { + return nil, err + } + + if end > -1 { + out.Write(buf[0:end]) + return out.Bytes(), nil + } + + if pos > 0 { + out.Write(buf[0:pos]) + copy(buf, buf[pos:pos+n]) + } + + pos = n + } + } + + return nil, fmt.Errorf("WaitForFunc failed") +} + +func (t *transportBasicIO) WaitForBytes(b []byte) ([]byte, error) { + return t.WaitForFunc(func(buf []byte) (int, error) { + return bytes.Index(buf, b), nil + }) +} + +func (t *transportBasicIO) WaitForString(s string) (string, error) { + out, err := t.WaitForBytes([]byte(s)) + if out != nil { + return string(out), err + } + return "", err +} + +func (t *transportBasicIO) WaitForRegexp(re *regexp.Regexp) ([]byte, [][]byte, error) { + var matches [][]byte + out, err := t.WaitForFunc(func(buf []byte) (int, error) { + loc := re.FindSubmatchIndex(buf) + if loc != nil { + for i := 2; i < len(loc); i += 2 { + matches = append(matches, buf[loc[i]:loc[i+1]]) + } + return loc[1], nil + } + return -1, nil + }) + return out, matches, err +} + +// ReadWriteCloser represents a combined IO Reader and WriteCloser +type ReadWriteCloser struct { + io.Reader + io.WriteCloser +} + +// NewReadWriteCloser creates a new combined IO Reader and Write Closer from the +// provided objects +func NewReadWriteCloser(r io.Reader, w io.WriteCloser) *ReadWriteCloser { + return &ReadWriteCloser{r, w} +} + diff --git a/environment/collector.go b/environment/collector.go index 68a62e69..ecab65ca 100644 --- a/environment/collector.go +++ b/environment/collector.go @@ -78,30 +78,61 @@ func (c *environmentCollector) environmentItems(client *rpc.Client, ch chan<- pr "Present": 5, } - err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return nil - } + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return nil + } - // gather satellite data - if client.Satellite { - var y = RpcReply{} - err = client.RunCommandAndParseWithParser("show chassis environment satellite", func(b []byte) error { - if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { - log.Printf("system doesn't seem to have satellite enabled") - return nil - } + // gather satellite data + if client.Satellite { + var y = RpcReply{} + err = client.RunCommandAndParseWithParser("", func(b []byte) error { + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil + } - return parseXML(b, &y) + return parseXML(b, &y) + }) + if err != nil { + // probably no satellite, but let's continue the task + //return nil + } else { + // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) + if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { + x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } + } + } + } else { + err := client.RunCommandAndParseWithParser("show chassis environment", func(b []byte) error { + return parseXML(b, &x) }) if err != nil { return nil - } else { - // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) - if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { - x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } + + // gather satellite data + if client.Satellite { + var y = RpcReply{} + err = client.RunCommandAndParseWithParser("show chassis environment satellite", func(b []byte) error { + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil + } + + return parseXML(b, &y) + }) + if err != nil { + return nil + } else { + // add satellite details (only if y.MultiRoutingEngineResults.RoutingEngine has elements) + if len(y.MultiRoutingEngineResults.RoutingEngine) > 0 { + x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items = append(x.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items, y.MultiRoutingEngineResults.RoutingEngine[0].EnvironmentInformation.Items...) + } } } } @@ -132,11 +163,20 @@ func (c *environmentCollector) environmentPEMItems(client *rpc.Client, ch chan<- "Empty": 3, } - err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis environment pem", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/firewall/collector.go b/firewall/collector.go index 16bd7980..4654e181 100644 --- a/firewall/collector.go +++ b/firewall/collector.go @@ -48,9 +48,17 @@ func (*firewallCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *firewallCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = FirewallRpc{} - err := client.RunCommandAndParse("show firewall filter regex .*", &x) - if err != nil { - return err + + if client.Netconf { + err := client.RunCommandAndParse(".*", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show firewall filter regex .*", &x) + if err != nil { + return err + } } for _, t := range x.Information.Filters { diff --git a/fpc/collector.go b/fpc/collector.go index 2d5ce542..a272d427 100644 --- a/fpc/collector.go +++ b/fpc/collector.go @@ -100,12 +100,20 @@ func (c *fpcCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, // CollectFPC collects metrics from JunOS func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { - return parseXML(b, &r) - }) - - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc detail", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { @@ -121,11 +129,20 @@ func (c *fpcCollector) CollectFPCDetail(client *rpc.Client, ch chan<- prometheus // Collect collects metrics from JunOS func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { - return parseXML(b, &r) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { @@ -139,11 +156,20 @@ func (c *fpcCollector) CollectFPC(client *rpc.Client, ch chan<- prometheus.Metri func (c *fpcCollector) CollectPIC(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { r := RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { - return parseXML(b, &r) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis fpc pic-status", func(b []byte) error { + return parseXML(b, &r) + }) + if err != nil { + return err + } } for _, r := range r.MultiRoutingEngineResults.RoutingEngine { labels := append(labelValues, r.Name) diff --git a/go.mod b/go.mod index 46d8df57..0208dd40 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/czerwonk/junos_exporter go 1.18 require ( + github.com/Juniper/go-netconf v0.1.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.12.2 github.com/prometheus/client_model v0.2.0 @@ -21,6 +22,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.34.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b // indirect golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v3 v3.0.0-20220512140231-539c8e751b99 // indirect diff --git a/go.sum b/go.sum index 785fb2ac..70e9bbd9 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Juniper/go-netconf v0.1.1 h1:5fx/T7L2Fwq51UnESPOP1CXgGCs7IYxR/pnyC5quu/k= +github.com/Juniper/go-netconf v0.1.1/go.mod h1:2Fy6tQTWnL//D/Ll1hb0RYXN4jndcTyneRn6xj5E1VE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -109,7 +111,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -140,10 +141,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -200,6 +199,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b h1:VfPXB/wCGGt590QhD1bOpv2J/AmC/RJNTg/Q59HKSB0= +github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -332,7 +333,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY= golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -387,7 +387,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -469,7 +468,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/interfacediagnostics/collector.go b/interfacediagnostics/collector.go index c5695c54..1ad8b1e5 100644 --- a/interfacediagnostics/collector.go +++ b/interfacediagnostics/collector.go @@ -255,9 +255,16 @@ func (c *interfaceDiagnosticsCollector) Collect(client *rpc.Client, ch chan<- pr func (c *interfaceDiagnosticsCollector) interfaceDiagnostics(client *rpc.Client) ([]*InterfaceDiagnostics, error) { var x = InterfaceDiagnosticsRPC{} - err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) + if err != nil { + return nil, err + } } return interfaceDiagnosticsFromRPCResult(x), nil @@ -279,42 +286,77 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client *rp // // workaround: go through all lines of the XML and remove identical, consecutive lines - err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { - var ( - lines []string = strings.Split(string(b[:]), "\n") - lineIndex int - tmpByte []byte - ) - - // check if satellite is enabled - if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + var ( + lines []string = strings.Split(string(b[:]), "\n") + lineIndex int + tmpByte []byte + ) + + for lineIndex = range lines { + if lineIndex == 0 { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + continue + } + + // check if two consecutive lines are identical (except whitespaces) + if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { + // skip the duplicate line + continue + + } else { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + } + } + + return xml.Unmarshal(tmpByte, &x) + }) + + if err != nil { log.Printf("system doesn't seem to have satellite enabled") - return nil + return nil,nil } - - for lineIndex = range lines { - if lineIndex == 0 { - // add good lines to new byte buffer - tmpByte = append(tmpByte, lines[lineIndex]...) - continue + } else { + err := client.RunCommandAndParseWithParser("show interfaces diagnostics optics satellite", func(b []byte) error { + var ( + lines []string = strings.Split(string(b[:]), "\n") + lineIndex int + tmpByte []byte + ) + + // check if satellite is enabled + if string(b[:]) == "\nerror: syntax error, expecting : satellite\n" { + log.Printf("system doesn't seem to have satellite enabled") + return nil } - // check if two consecutive lines are identical (except whitespaces) - if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { - // skip the duplicate line - continue + for lineIndex = range lines { + if lineIndex == 0 { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + continue + } + + // check if two consecutive lines are identical (except whitespaces) + if strings.TrimSpace(lines[lineIndex]) == strings.TrimSpace(lines[lineIndex-1]) { + // skip the duplicate line + continue - } else { - // add good lines to new byte buffer - tmpByte = append(tmpByte, lines[lineIndex]...) + } else { + // add good lines to new byte buffer + tmpByte = append(tmpByte, lines[lineIndex]...) + } } - } - return xml.Unmarshal(tmpByte, &x) - }) + return xml.Unmarshal(tmpByte, &x) + }) - if err != nil { - return nil, err + if err != nil { + return nil, err + } } return interfaceDiagnosticsFromRPCResult(x), nil diff --git a/interfacelabels/dynamic_labels.go b/interfacelabels/dynamic_labels.go index 24339da6..d169e78a 100644 --- a/interfacelabels/dynamic_labels.go +++ b/interfacelabels/dynamic_labels.go @@ -47,9 +47,16 @@ type interfaceLabel struct { // CollectDescriptions collects labels from descriptions func (l *DynamicLabels) CollectDescriptions(device *connector.Device, client *rpc.Client, ifDescReg *regexp.Regexp) error { r := &InterfaceRPC{} - err := client.RunCommandAndParse("show interfaces descriptions", r) - if err != nil { - return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + if client.Netconf { + err := client.RunCommandAndParse("", r) + if err != nil { + return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + } + } else { + err := client.RunCommandAndParse("show interfaces descriptions", r) + if err != nil { + return errors.Wrap(err, "could not retrieve interface descriptions for "+device.Host) + } } l.parseDescriptions(device, r.Information.Interfaces, ifDescReg) diff --git a/interfacequeue/collector.go b/interfacequeue/collector.go index 4435a586..3ff9775b 100644 --- a/interfacequeue/collector.go +++ b/interfacequeue/collector.go @@ -101,9 +101,16 @@ func (c *interfaceQueueCollector) Describe(ch chan<- *prometheus.Desc) { func (c *interfaceQueueCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { q := InterfaceQueueRPC{} - err := client.RunCommandAndParse("show interfaces queue", &q) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &q) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show interfaces queue", &q) + if err != nil { + return err + } } for _, iface := range q.InterfaceInformation.Interfaces { diff --git a/interfaces/collector.go b/interfaces/collector.go index 110049c3..98d7bf7c 100644 --- a/interfaces/collector.go +++ b/interfaces/collector.go @@ -146,9 +146,16 @@ func (c *interfaceCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *interfaceCollector) interfaceStats(client *rpc.Client) ([]*InterfaceStats, error) { var x = InterfaceRpc{} - err := client.RunCommandAndParse("show interfaces extensive", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show interfaces extensive", &x) + if err != nil { + return nil, err + } } stats := make([]*InterfaceStats, 0) diff --git a/ipsec/collector.go b/ipsec/collector.go index aae8b882..95600870 100644 --- a/ipsec/collector.go +++ b/ipsec/collector.go @@ -49,9 +49,16 @@ func (*ipsecCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParse("show security ipsec security-associations", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show security ipsec security-associations", &x) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { @@ -63,14 +70,24 @@ func (c *ipsecCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } } - var conf = ConfigurationSecurityIpsec{} - err = client.RunCommandAndParse("show configuration security ipsec", &conf) - if err != nil { - return err + if client.Netconf { + var conf = ConfigurationSecurityIpsecnetconf{} + err := client.RunCommandAndParse("", &conf) + if err != nil { + return err + } + cls := append(labelValues, "N/A", "configured tunnels", "") + ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) + } else { + var conf = ConfigurationSecurityIpsec{} + err := client.RunCommandAndParse("show configuration security ipsec", &conf) + if err != nil { + return err + } + cls := append(labelValues, "N/A", "configured tunnels", "") + ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) } - cls := append(labelValues, "N/A", "configured tunnels", "") - ch <- prometheus.MustNewConstMetric(configuredTunnels, prometheus.GaugeValue, float64(len(conf.Configuration.Security.Ipsec.Vpn)), cls...) return nil } diff --git a/ipsec/rpc.go b/ipsec/rpc.go index cdbc8b23..0f26cc97 100644 --- a/ipsec/rpc.go +++ b/ipsec/rpc.go @@ -55,7 +55,7 @@ type ConfigurationSecurityIpsec struct { Configuration struct { Security struct { Ipsec struct { - Proposal struct { + Proposal []struct { Text string `xml:",chardata"` Name string `xml:"name"` Protocol string `xml:"protocol"` @@ -63,7 +63,7 @@ type ConfigurationSecurityIpsec struct { EncryptionAlgorithm string `xml:"encryption-algorithm"` LifetimeSeconds string `xml:"lifetime-seconds"` } `xml:"proposal"` - Policy struct { + Policy []struct { Name string `xml:"name"` Proposals string `xml:"proposals"` } `xml:"policy"` @@ -80,3 +80,33 @@ type ConfigurationSecurityIpsec struct { } `xml:"security"` } `xml:"configuration"` } + +type ConfigurationSecurityIpsecnetconf struct { + Configuration struct { + Security struct { + Ipsec struct { + Proposal []struct { + Text string `xml:",chardata"` + Name string `xml:"name"` + Protocol string `xml:"protocol"` + AuthenticationAlgorithm string `xml:"authentication-algorithm"` + EncryptionAlgorithm string `xml:"encryption-algorithm"` + LifetimeSeconds string `xml:"lifetime-seconds"` + } `xml:"proposal"` + Policy []struct { + Name string `xml:"name"` + Proposals string `xml:"proposals"` + } `xml:"policy"` + Vpn []struct { + Name string `xml:"name"` + BindInterface string `xml:"bind-interface"` + Ike struct { + Gateway string `xml:"gateway"` + IpsecPolicy string `xml:"ipsec-policy"` + } `xml:"ike"` + EstablishTunnels string `xml:"establish-tunnels"` + } `xml:"vpn"` + } `xml:"ipsec"` + } `xml:"security"` + } `xml:"data>configuration"` +} diff --git a/isis/collector.go b/isis/collector.go index 6714efbf..d3207452 100644 --- a/isis/collector.go +++ b/isis/collector.go @@ -56,9 +56,17 @@ func (c *isisCollector) isisAdjancies(client *rpc.Client) (*IsisAdjacencies, err total := 0 var x = IsisRpc{} - err := client.RunCommandAndParse("show isis adjacency", &x) - if err != nil { - return nil, err + + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show isis adjacency", &x) + if err != nil { + return nil, err + } } for _, adjacency := range x.Information.Adjacencies { diff --git a/junos_collector.go b/junos_collector.go index 36774adc..b8c6f390 100644 --- a/junos_collector.go +++ b/junos_collector.go @@ -81,7 +81,7 @@ func newJunosCollector(devices []*connector.Device, connectionManager *connector } func clientForDevice(device *connector.Device, connManager *connector.SSHConnectionManager) (*rpc.Client, error) { - conn, err := connManager.Connect(device) + conn, err := connManager.Connect(device, cfg.Features.Netconf) if err != nil { return nil, err } @@ -96,6 +96,10 @@ func clientForDevice(device *connector.Device, connManager *connector.SSHConnect c.EnableSatellite() } + if cfg.Features.Netconf { + c.EnableNetconf() + } + return c, nil } diff --git a/l2circuit/collector.go b/l2circuit/collector.go index 1e0dd678..2331922b 100644 --- a/l2circuit/collector.go +++ b/l2circuit/collector.go @@ -78,9 +78,16 @@ func (c *l2circuitCollector) Collect(client *rpc.Client, ch chan<- prometheus.Me func (c *l2circuitCollector) collectL2circuitMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = L2circuitRpc{} - err := client.RunCommandAndParse("show l2circuit connections brief", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show l2circuit connections brief", &x) + if err != nil { + return err + } } neighbors := x.Information.Neighbors diff --git a/lacp/collector.go b/lacp/collector.go index 97a9ae32..431e8c68 100644 --- a/lacp/collector.go +++ b/lacp/collector.go @@ -45,10 +45,17 @@ func (*lacpCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *lacpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { - var x = lacpRpc{} - err := client.RunCommandAndParse("show lacp interfaces", &x) - if err != nil { - return err + var x = lacpRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show lacp interfaces", &x) + if err != nil { + return err + } } for _, iface := range x.Information.LacpInterfaces { diff --git a/ldp/collector.go b/ldp/collector.go index bb8a1e51..79901011 100644 --- a/ldp/collector.go +++ b/ldp/collector.go @@ -62,9 +62,16 @@ func (c *ldpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPRpc{} - err := client.RunCommandAndParse("show ldp neighbor", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show ldp neighbor", &x) + if err != nil { + return err + } } neighbors := x.Information.Neighbors @@ -75,9 +82,16 @@ func (c *ldpCollector) collectLDPMetrics(client *rpc.Client, ch chan<- prometheu func (c *ldpCollector) collectLDPSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = LDPSessionRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { err := client.RunCommandAndParse("show ldp session", &x) - if err != nil { - return err + if err != nil { + return err + } } sessions := x.Information.Sessions diff --git a/mac/collector.go b/mac/collector.go index 1e40af76..956617a4 100644 --- a/mac/collector.go +++ b/mac/collector.go @@ -47,9 +47,16 @@ func (*macCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *macCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = MacRpc{} - err := client.RunCommandAndParse("show ethernet-switching table summary", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show ethernet-switching table summary", &x) + if err != nil { + return err + } } entry := x.Information.Table.Entry diff --git a/main.go b/main.go index 6d5f780e..40342b00 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ var ( interfaceQueuesEnabled = flag.Bool("queues.enabled", false, "Scrape interface queue metrics") rpkiEnabled = flag.Bool("rpki.enabled", false, "Scrape rpki metrics") satelliteEnabled = flag.Bool("satellite.enabled", false, "Scrape metrics from satellite devices") + netconfEnabled = flag.Bool("netconf.enabled", false, "enable netconf rpc query style") systemEnabled = flag.Bool("system.enabled", false, "Scrape system metrics") macEnabled = flag.Bool("mac.enabled", false, "Scrape MAC address table metrics") alarmFilter = flag.String("alarms.filter", "", "Regex to filter for alerts to ignore") @@ -215,6 +216,7 @@ func loadConfigFromFlags() *config.Config { f.RPKI = *rpkiEnabled f.Storage = *storageEnabled f.Satellite = *satelliteEnabled + f.Netconf = *netconfEnabled f.System = *systemEnabled f.Power = *powerEnabled f.MAC = *macEnabled diff --git a/mpls_lsp/collector.go b/mpls_lsp/collector.go index 26abb34a..c3b92628 100644 --- a/mpls_lsp/collector.go +++ b/mpls_lsp/collector.go @@ -48,10 +48,17 @@ func (*mpls_lspCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *mpls_lspCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { - var x = mpls_lspRpc{} - err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router - if err != nil { - return err + var x = mpls_lspRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) //ingress:Display LSPs originating at this router + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show mpls lsp ingress extensive", &x) //ingress:Display LSPs originating at this router + if err != nil { + return err + } } for _, lsp := range x.Information.Sessions { diff --git a/nat/collector.go b/nat/collector.go index 650695a8..073357a6 100644 --- a/nat/collector.go +++ b/nat/collector.go @@ -1,6 +1,7 @@ package nat import ( + "fmt" "github.com/czerwonk/junos_exporter/collector" "github.com/czerwonk/junos_exporter/rpc" "github.com/prometheus/client_golang/prometheus" @@ -506,9 +507,16 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} - err := client.RunCommandAndParse("show services nat statistics", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat statistics", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatInterface, 0) @@ -916,9 +924,14 @@ func (*natCollector) collectForInterface(s *NatInterface, ch chan<- prometheus.M func (c *natCollector) NatPoolInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*NatPoolInterface, error) { var x = NatPoolRpc{} - err := client.RunCommandAndParse("show services nat pool", &x) - if err != nil { - return nil, err + if client.Netconf { + //TODO + return nil, fmt.Errorf("not implemented") + } else { + err := client.RunCommandAndParse("show services nat pool", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatPoolInterface, 0) @@ -950,9 +963,14 @@ func (c *natCollector) collectForPoolInterface(s *NatPoolInterface, ch chan<- pr func (c *natCollector) NatPoolDetailInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*NatPoolDetailInterface, error) { var x = NatPoolDetailRpc{} - err := client.RunCommandAndParse("show services nat pool detail", &x) - if err != nil { - return nil, err + if client.Netconf { + //TODO + return nil, fmt.Errorf("not implemented") + } else { + err := client.RunCommandAndParse("show services nat pool detail", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*NatPoolDetailInterface, 0) @@ -992,9 +1010,16 @@ func (c *natCollector) collectForPoolDetailInterface(s *NatPoolDetailInterface, func (c *natCollector) ServiceSetsCpuInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*ServiceSetsCpuInterface, error) { var x = ServiceSetsCpuRpc{} - err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*ServiceSetsCpuInterface, 0) diff --git a/nat2/collector.go b/nat2/collector.go index b223eb45..29fb4432 100644 --- a/nat2/collector.go +++ b/nat2/collector.go @@ -225,9 +225,16 @@ func (c *natCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *natCollector) NatInterfaces(client *rpc.Client) ([]*NatInterface, error) { var x = NatRpc{} - err := client.RunCommandAndParse("show services nat statistics", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat statistics", &x) + if err != nil { + return nil, err + } } interfaces := make([]*NatInterface, 0) @@ -320,9 +327,16 @@ func (*natCollector) collectForInterface(s *NatInterface, ch chan<- prometheus.M func (c *natCollector) SrcNatPools(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]SrcNatPool, error) { var x = SrcNatPoolRpc{} - err := client.RunCommandAndParse("show services nat source pool all", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services nat source pool all", &x) + if err != nil { + return nil, err + } } return x.Information.Pools[:], nil @@ -392,9 +406,16 @@ func (c *natCollector) collectForSrcNatPool(s []SrcNatPool, ch chan<- prometheus func (c *natCollector) ServiceSetsCpuInterfaces(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) ([]*ServiceSetsCpuInterface, error) { var x = ServiceSetsCpuRpc{} - err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) - if err != nil { - return nil, err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil, err + } + } else { + err := client.RunCommandAndParse("show services service-sets cpu-usage", &x) + if err != nil { + return nil, err + } } interfacesdetail := make([]*ServiceSetsCpuInterface, 0) diff --git a/ospf/collector.go b/ospf/collector.go index 86ac8ffe..29e5d3d4 100644 --- a/ospf/collector.go +++ b/ospf/collector.go @@ -64,9 +64,14 @@ func (c *ospfCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = OspfRpc{} var cmd strings.Builder - cmd.WriteString("show ospf overview") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) + + if client.Netconf { + cmd.WriteString("") + } else { + cmd.WriteString("show ospf overview") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } } err := client.RunCommandAndParse(cmd.String(), &x) @@ -94,9 +99,14 @@ func (c *ospfCollector) collectOSPFMetrics(client *rpc.Client, ch chan<- prometh func (c *ospfCollector) collectOSPFv3Metrics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = Ospf3Rpc{} var cmd strings.Builder - cmd.WriteString("show ospf3 overview") - if c.LogicalSystem != "" { - cmd.WriteString(" logical-system " + c.LogicalSystem) + + if client.Netconf { + cmd.WriteString("") + } else { + cmd.WriteString("show ospf3 overview") + if c.LogicalSystem != "" { + cmd.WriteString(" logical-system " + c.LogicalSystem) + } } err := client.RunCommandAndParse(cmd.String(), &x) diff --git a/power/collector.go b/power/collector.go index 2ade7145..44af0fc6 100644 --- a/power/collector.go +++ b/power/collector.go @@ -88,11 +88,20 @@ func (c *powerCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric } var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis power", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/route/collector.go b/route/collector.go index fa81705a..d3a3742e 100644 --- a/route/collector.go +++ b/route/collector.go @@ -52,9 +52,16 @@ func (*routeCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routeCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RouteRpc{} - err := client.RunCommandAndParse("show route summary", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show route summary", &x) + if err != nil { + return err + } } for _, t := range x.Information.Tables { diff --git a/routingengine/collector.go b/routingengine/collector.go index c7c7df6c..5c392efc 100644 --- a/routingengine/collector.go +++ b/routingengine/collector.go @@ -168,11 +168,20 @@ func (*routingEngineCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *routingEngineCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show chassis routing-engine", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/rpc/rpc_client.go b/rpc/rpc_client.go index c5e55edf..0c07dc4f 100644 --- a/rpc/rpc_client.go +++ b/rpc/rpc_client.go @@ -3,7 +3,7 @@ package rpc import ( "encoding/xml" "fmt" - + "bytes" "log" "github.com/czerwonk/junos_exporter/connector" @@ -19,6 +19,7 @@ type Client struct { conn *connector.SSHConnection debug bool Satellite bool + Netconf bool } // NewClient creates a new client to connect to @@ -30,9 +31,16 @@ func NewClient(ssh *connector.SSHConnection) *Client { // RunCommandAndParse runs a command on JunOS and unmarshals the XML result func (c *Client) RunCommandAndParse(cmd string, obj interface{}) error { - return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { - return xml.Unmarshal(b, obj) - }) + if c.Netconf { + return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { + //in junos the xml interfaces contains line returns in the values + return xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), obj) + }) + } else { + return c.RunCommandAndParseWithParser(cmd, func(b []byte) error { + return xml.Unmarshal(b, obj) + }) + } } // RunCommandAndParseWithParser runs a command on JunOS and uses the given parser to handle the result @@ -41,7 +49,15 @@ func (c *Client) RunCommandAndParseWithParser(cmd string, parser Parser) error { log.Printf("Running command on %s: %s\n", c.conn.Host(), cmd) } - b, err := c.conn.RunCommand(fmt.Sprintf("%s | display xml", cmd)) + var err error + var b []byte + + if c.Netconf { + b, err = c.conn.RunCommand(cmd) + } else { + b, err = c.conn.RunCommand(fmt.Sprintf("%s | display xml", cmd)) + } + if err != nil { return err } @@ -73,3 +89,8 @@ func (c *Client) DisableDebug() { func (c *Client) EnableSatellite() { c.Satellite = true } + +// EnableNetconf enables netconf RPCs instead of SSH-CLI +func (c *Client) EnableNetconf() { + c.Netconf = true +} diff --git a/rpki/collector.go b/rpki/collector.go index bbf5cad8..35d48976 100644 --- a/rpki/collector.go +++ b/rpki/collector.go @@ -79,9 +79,16 @@ func (c *rpkiCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpkiCollector) collectSessions(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiSessionRpc{} - err := client.RunCommandAndParse("show validation session", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show validation session", &x) + if err != nil { + return err + } } for _, session := range x.Information.RpkiSessions { @@ -129,9 +136,16 @@ func (c *rpkiCollector) collectForSession(s RpkiSession, ch chan<- prometheus.Me func (c *rpkiCollector) collectStatistics(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpkiStatisticsRpc{} - err := client.RunCommandAndParse("show validation statistics", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show validation statistics", &x) + if err != nil { + return err + } } ch <- prometheus.MustNewConstMetric(memoryUtilizationDesc, prometheus.GaugeValue, float64(x.Information.Statistics.MemoryUtilization), labelValues...) diff --git a/rpm/collector.go b/rpm/collector.go index cfe9f863..67208724 100644 --- a/rpm/collector.go +++ b/rpm/collector.go @@ -68,9 +68,16 @@ func (c *rpmCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, func (c *rpmCollector) collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RPMRPC{} - err := client.RunCommandAndParse("show services rpm probe-results", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show services rpm probe-results", &x) + if err != nil { + return err + } } for _, probe := range x.Results.Probes { diff --git a/security/collector.go b/security/collector.go index 4c6eb3b5..4775fdc5 100644 --- a/security/collector.go +++ b/security/collector.go @@ -63,9 +63,16 @@ func (*securityCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *securityCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParse("show security monitoring", &x) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show security monitoring", &x) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { diff --git a/storage/collector.go b/storage/collector.go index 26869296..8c803d92 100644 --- a/storage/collector.go +++ b/storage/collector.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "strconv" "strings" + "bytes" "github.com/czerwonk/junos_exporter/collector" "github.com/czerwonk/junos_exporter/rpc" @@ -51,11 +52,20 @@ func (*storageCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *storageCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { var x = RpcReply{} - err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { - return parseXML(b, &x) - }) - if err != nil { - return err + if client.Netconf { + err := client.RunCommandAndParseWithParser("", func(b []byte) error { + return parseXMLnetconf(b, &x) + }) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParseWithParser("show system storage", func(b []byte) error { + return parseXML(b, &x) + }) + if err != nil { + return err + } } for _, re := range x.MultiRoutingEngineResults.RoutingEngine { @@ -97,3 +107,24 @@ func parseXML(b []byte, res *RpcReply) error { } return nil } + +func parseXMLnetconf(b []byte, res *RpcReply) error { + if strings.Contains(string(b), "multi-routing-engine-results") { + return xml.Unmarshal(b, res) + } + + fi := RpcReplyNoRE{} + + err := xml.Unmarshal(bytes.ReplaceAll(b, []byte("\n"), []byte("")), &fi) + if err != nil { + return err + } + + res.MultiRoutingEngineResults.RoutingEngine = []RoutingEngine{ + { + Name: "N/A", + StorageInformation: fi.StorageInformation, + }, + } + return nil +} diff --git a/system/collector.go b/system/collector.go index 6332492b..fe8c0afd 100644 --- a/system/collector.go +++ b/system/collector.go @@ -162,9 +162,16 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus r = new(BuffersRPC) - err = client.RunCommandAndParse("show system buffers", r) - if err != nil { - return err + if client.Netconf { + err = client.RunCommandAndParse("", r) + if err != nil { + return err + } + } else { + err = client.RunCommandAndParse("show system buffers", r) + if err != nil { + return err + } } if r.Output != "" { @@ -277,9 +284,16 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information r2 = new(SystemInformationRPC) - err = client.RunCommandAndParse("show system information", r2) - if err != nil { - return err + if client.Netconf { + err = client.RunCommandAndParse("", r2) + if err != nil { + return err + } + } else { + err = client.RunCommandAndParse("show system information", r2) + if err != nil { + return err + } } // create LabelSet (target, "model", "os", "os_version", "serial", "hostname", "alias", "slot_id", "state") @@ -298,7 +312,11 @@ func (c *systemCollector) CollectSystem(client *rpc.Client, ch chan<- prometheus // system information of satellites r3 = new(SatelliteChassisRPC) - err = client.RunCommandAndParse("show chassis satellite detail", r3) + if client.Netconf { + err = client.RunCommandAndParse("", r3) + } else { + err = client.RunCommandAndParse("show chassis satellite detail", r3) + } // there are various error messages when satellite is not enabled; thus here we just ignore the error and continue if err == nil { for i = range r3.SatelliteInfo.Satellite { diff --git a/virtualchassis/collector.go b/virtualchassis/collector.go new file mode 100644 index 00000000..52d4731d --- /dev/null +++ b/virtualchassis/collector.go @@ -0,0 +1,65 @@ +package virtualchassis + +import ( + "github.com/czerwonk/junos_exporter/collector" + "github.com/czerwonk/junos_exporter/rpc" + "github.com/prometheus/client_golang/prometheus" +) + +const prefix = "junos_virtualchassis_" + +var ( + virtualchassismemberstatus *prometheus.Desc +) + +func init() { + l := []string{"target", "status", "serial", "model", "id", "fpcslot", "role"} + virtualchassismemberstatus = prometheus.NewDesc(prefix+"member_status", "virtualchassis member-status (1: Prsnt, 0: NotPrsnt)", l, nil) +} + +type virtualchassisCollector struct { +} + +// Name returns the name of the collector +func (*virtualchassisCollector) Name() string { + return "virtualchassis" +} + +// NewCollector creates a new collector +func NewCollector() collector.RPCCollector { + return &virtualchassisCollector{} +} + +// Describe describes the metrics +func (*virtualchassisCollector) Describe(ch chan<- *prometheus.Desc) { + ch <- virtualchassismemberstatus +} + +// Collect collects metrics from JunOS +func (c *virtualchassisCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { + statusValues := map[string]int{ + "NotPrsnt": 0, + "Prsnt": 1, + } + + var x = virtualChassisRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return nil + } + } else { + err := client.RunCommandAndParse("show virtual-chassis", &x) + if err != nil { + return err + } + } + + for _, m := range x.VirtualChassisInformation.MemberList.Member { + l := labelValues + l = append(l, m.Status, m.SerialNumber, m.Model, m.Id, m.FpcSlot, m.Role ) + ch <- prometheus.MustNewConstMetric(virtualchassismemberstatus, prometheus.GaugeValue, float64(statusValues[m.Status]), l...) + } + + return nil +} diff --git a/virtualchassis/rpc.go b/virtualchassis/rpc.go new file mode 100644 index 00000000..f0a5660f --- /dev/null +++ b/virtualchassis/rpc.go @@ -0,0 +1,22 @@ +package virtualchassis + +type virtualChassisRpc struct { + VirtualChassisInformation struct { + VirtualChassisIdInformation struct { + VirtualChassisId string `xml:"virtual-chassis-id"` + VirtualChassisMode string `xml:"virtual-chassis-mode"` + } `xml:"virtual-chassis-id-information"` + MemberList struct { + Member []vcmembers `xml:"member"` + } `xml:"member-list"` + } `xml:"virtual-chassis-information"` +} + +type vcmembers struct { + Status string `xml:"member-status"` + Id string `xml:"member-id"` + FpcSlot string `xml:"fpc-slot"` + SerialNumber string `xml:"member-serial-number"` + Model string `xml:"member-model"` + Role string `xml:"member-role"` +} diff --git a/vpws/collector.go b/vpws/collector.go index b9db370f..9f458e51 100644 --- a/vpws/collector.go +++ b/vpws/collector.go @@ -52,10 +52,17 @@ func (*vpwsCollector) Describe(ch chan<- *prometheus.Desc) { // Collect collects metrics from JunOS func (c *vpwsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, labelValues []string) error { - var x = vpwsRpc{} - err := client.RunCommandAndParse("show evpn vpws-instance", &x) - if err != nil { - return err + var x = vpwsRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show evpn vpws-instance", &x) + if err != nil { + return err + } } for _, vInst := range x.Information.VpwsInstances { @@ -68,6 +75,12 @@ func (c *vpwsCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsSidMap[vSid.Status]), l...) } + + if vIf.RemoteStatus.LocalInterfaceName != "" { + l := append(labelValues, vInst.Name, vInst.RD, vIf.Name, "remote", vIf.RemoteStatus.Sid, vIf.RemoteStatus.LocalInterfaceName, "", "local", "") + ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsStatusMap[vIf.RemoteStatus.LocalInterfaceStatus]), l...) + } + for _, vSid := range vIf.RemoteStatus.SidPeInfo { l := append(labelValues, vInst.Name, vInst.RD, vIf.Name, "remote", vIf.RemoteStatus.Sid, vSid.IP, vSid.Esi, vSid.Mode, vSid.Role) ch <- prometheus.MustNewConstMetric(vpwsSid, prometheus.GaugeValue, float64(vpwsSidMap[vSid.Status]), l...) diff --git a/vpws/rpc.go b/vpws/rpc.go index 959eb619..762daad4 100644 --- a/vpws/rpc.go +++ b/vpws/rpc.go @@ -28,8 +28,10 @@ type vpwsInterface struct { } `xml:"evpn-vpws-service-id-local-status-table>evpn-vpws-sid-local"` RemoteStatus struct { - Sid string `xml:"evpn-vpws-sid-remote-value"` - SidPeInfo []vpwsSidPeInfo `xml:"evpn-vpws-sid-pe-status-table>evpn-vpws-sid-pe-info"` + Sid string `xml:"evpn-vpws-sid-remote-value"` + LocalInterfaceName string `xml:"evpn-vpws-sid-local-interface-name"` + LocalInterfaceStatus string `xml:"evpn-vpws-sid-local-interface-status"` + SidPeInfo []vpwsSidPeInfo `xml:"evpn-vpws-sid-pe-status-table>evpn-vpws-sid-pe-info"` } `xml:"evpn-vpws-service-id-remote-status-table>evpn-vpws-sid-remote"` } diff --git a/vrrp/collector.go b/vrrp/collector.go index 903ebf5e..5290449f 100644 --- a/vrrp/collector.go +++ b/vrrp/collector.go @@ -43,10 +43,17 @@ func (c *vrrpCollector) Collect(client *rpc.Client, ch chan<- prometheus.Metric, "master": 3, } - var x = VrrpRpc{} - err := client.RunCommandAndParse("show vrrp summary", &x) - if err != nil { - return err + var x = VrrpRpc{} + if client.Netconf { + err := client.RunCommandAndParse("", &x) + if err != nil { + return err + } + } else { + err := client.RunCommandAndParse("show vrrp summary", &x) + if err != nil { + return err + } } for _, iface := range x.Information.Interfaces {