diff --git a/pkg/features/interfacediagnostics/collector.go b/pkg/features/interfacediagnostics/collector.go index f1fd0991..d4ae6289 100644 --- a/pkg/features/interfacediagnostics/collector.go +++ b/pkg/features/interfacediagnostics/collector.go @@ -4,6 +4,7 @@ package interfacediagnostics import ( "encoding/xml" + "fmt" "log" "math" "strconv" @@ -63,6 +64,8 @@ type interfaceDiagnosticsCollector struct { rxSignalAvgOpticalPowerDesc *prometheus.Desc rxSignalAvgOpticalPowerDbmDesc *prometheus.Desc + + transceiverDesc *prometheus.Desc } // NewCollector creates a new collector @@ -128,6 +131,9 @@ func (c *interfaceDiagnosticsCollector) init() { c.laserRxOpticalPowerLowAlarmThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_alarm_threshold_dbm", "Laser rx power low alarm threshold_dbm in dBm", l, nil) c.laserRxOpticalPowerHighWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_high_warn_threshold_dbm", "Laser rx power high warn threshold_dbm in dBm", l, nil) c.laserRxOpticalPowerLowWarnThresholdDbmDesc = prometheus.NewDesc(prefix+"laser_rx_low_warn_threshold_dbm", "Laser rx power low warn threshold_dbm in dBm", l, nil) + + transceiver_labels := []string{"target", "name", "serial_number", "description", "speed", "fiber_type", "vendor_name", "vendor_part_number", "wavelength"} + c.transceiverDesc = prometheus.NewDesc("junos_interface_transceiver", "Transceiver Info", transceiver_labels, nil) } // Describe describes the metrics @@ -172,6 +178,8 @@ func (c *interfaceDiagnosticsCollector) Describe(ch chan<- *prometheus.Desc) { ch <- c.rxSignalAvgOpticalPowerDesc ch <- c.rxSignalAvgOpticalPowerDbmDesc + + ch <- c.transceiverDesc } // Collect collects metrics from JunOS @@ -187,11 +195,16 @@ func (c *interfaceDiagnosticsCollector) Collect(client collector.Client, ch chan if err != nil { return err } - diagnostics = append(diagnostics, diagnosticsSatellite...) } + diagnostics_dict := make(map[string]*interfaceDiagnostics) + for _, d := range diagnostics { + + index := strings.Split(d.Name, "-")[1] + diagnostics_dict[index] = d + l := append(labelValues, d.Name) l = append(l, c.labels.ValuesForInterface(client.Device(), d.Name)...) @@ -251,17 +264,126 @@ func (c *interfaceDiagnosticsCollector) Collect(client collector.Client, ch chan } } + err = createTransceiverMetrics(c, client, diagnostics_dict, ch, labelValues) + if err != nil { + return err + } return nil } -func (c *interfaceDiagnosticsCollector) interfaceDiagnostics(client collector.Client) ([]*interfaceDiagnostics, error) { - var x = result{} - err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) +func (c *interfaceDiagnosticsCollector) interfaceMediaInfo(client collector.Client) (map[string]*physicalInterface, error) { + var x = interfacesMediaStruct{} + err := client.RunCommandAndParse("show interfaces media", &x) if err != nil { return nil, err } - return interfaceDiagnosticsFromRPCResult(x), nil + return interfaceMediaInfoFromRPCResult(&x.InterfaceInformation.PhysicalInterface), nil +} + +func interfaceMediaInfoFromRPCResult(interfaceMediaList *[]physicalInterface) map[string]*physicalInterface { + + interfaceMediaDict := make(map[string]*physicalInterface) + + for _, i := range *interfaceMediaList { + if strings.HasPrefix(i.Name, "xe") || strings.HasPrefix(i.Name, "ge") || strings.HasPrefix(i.Name, "et") { + iface := i + slotIndex := iface.Name[3:] + interfaceMediaDict[slotIndex] = &iface + } + } + return interfaceMediaDict +} + +func (c *interfaceDiagnosticsCollector) chassisHardwareInfos(client collector.Client) ([]*transceiverInformation, error) { + var x = chassisHardware{} + err := client.RunCommandAndParse("show chassis hardware", &x) + if err != nil { + return nil, err + } + + return c.transceiverInfoFromRPCResult(client, x) +} + +func (c *interfaceDiagnosticsCollector) transceiverInfoFromRPCResult(client collector.Client, chassisHardware chassisHardware) ([]*transceiverInformation, error) { + transceiverList := make([]*transceiverInformation, 0) + + var chassisModules = chassisHardware.ChassisInventory.Chassis.ChassisModule + for _, module := range chassisModules { + if strings.Split(module.Name, " ")[0] != "FPC" { + continue + } + for _, subModule := range module.ChassisSubModule { + if strings.Split(subModule.Name, " ")[0] != "PIC" { + continue + } + fpc := strings.Split(module.Name, " ")[1] + pic := strings.Split(subModule.Name, " ")[1] + + picPortsInformation, err := c.getPicPortsFromRPCResult(client, fpc, pic) + if err != nil { + return nil, err + } + + for port, subSubModule := range subModule.ChassisSubSubModule { + port_name := strings.Split(subSubModule.Name, " ")[1] + subSubModule_pointer := subSubModule + id := fpc + "/" + pic + "/" + port_name + transceiver := transceiverInformation{ + Name: id, + ChassisHardwareInfo: &subSubModule_pointer, + PicPort: &picPortsInformation[port], + } + transceiverList = append(transceiverList, &transceiver) + } + } + } + return transceiverList, nil +} + +func (c *interfaceDiagnosticsCollector) getPicPortsFromRPCResult(client collector.Client, fpc string, pic string) ([]picPort, error) { + var x = fPCInformationStruct{} + command := fmt.Sprintf("show chassis pic fpc-slot %s pic-slot %s", fpc, pic) + err := client.RunCommandAndParse(command, &x) + if err != nil { + return nil, err + } + + return x.FPCInformation.FPC.PicDetail.PicPortInfoList, nil +} + +func createTransceiverMetrics(c *interfaceDiagnosticsCollector, client collector.Client, diagnostics_dict map[string]*interfaceDiagnostics, ch chan<- prometheus.Metric, labelValues []string) error { + + ifMediaDict, err := c.interfaceMediaInfo(client) + if err != nil { + return err + } + + transceiverInfo, err := c.chassisHardwareInfos(client) + if err != nil { + return err + } + + for _, t := range transceiverInfo { + chassisInfo := t.ChassisHardwareInfo + port_speed := "0" + oper_status := 0.0 + + if media, hit := ifMediaDict[t.Name]; hit { + if media.OperStatus == "up" { + oper_status = 1.0 + } + t.Name = media.Name + port_speed = media.Speed + } else { + t.Name = "slot-" + t.Name + } + + transceiver_labels := append(labelValues, t.Name, chassisInfo.SerialNumber, chassisInfo.Description, port_speed, t.PicPort.FiberMode, strings.TrimSpace(t.PicPort.SFPVendorName), strings.TrimSpace(t.PicPort.SFPVendorPno), t.PicPort.Wavelength) + + ch <- prometheus.MustNewConstMetric(c.transceiverDesc, prometheus.GaugeValue, oper_status, transceiver_labels...) + } + return nil } func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client collector.Client) ([]*interfaceDiagnostics, error) { @@ -321,6 +443,16 @@ func (c *interfaceDiagnosticsCollector) interfaceDiagnosticsSatellite(client col return interfaceDiagnosticsFromRPCResult(x), nil } +func (c *interfaceDiagnosticsCollector) interfaceDiagnostics(client collector.Client) ([]*interfaceDiagnostics, error) { + var x = result{} + err := client.RunCommandAndParse("show interfaces diagnostics optics", &x) + if err != nil { + return nil, err + } + + return interfaceDiagnosticsFromRPCResult(x), nil +} + func interfaceDiagnosticsFromRPCResult(res result) []*interfaceDiagnostics { diagnostics := make([]*interfaceDiagnostics, 0) diff --git a/pkg/features/interfacediagnostics/interface_stats.go b/pkg/features/interfacediagnostics/interface_stats.go new file mode 100644 index 00000000..3cfa8fb4 --- /dev/null +++ b/pkg/features/interfacediagnostics/interface_stats.go @@ -0,0 +1,7 @@ +package interfacediagnostics + +type transceiverInformation struct { + Name string + ChassisHardwareInfo *chassisSubSubModule + PicPort *picPort +} diff --git a/pkg/features/interfacediagnostics/rpc.go b/pkg/features/interfacediagnostics/rpc.go index 78e8746c..8fec7510 100644 --- a/pkg/features/interfacediagnostics/rpc.go +++ b/pkg/features/interfacediagnostics/rpc.go @@ -2,6 +2,126 @@ package interfacediagnostics +type fPCInformationStruct struct { + FPCInformation fPCInformation `xml:"fpc-information"` +} +type fPCInformation struct { + FPC fPC `xml:"fpc"` +} + +type fPC struct { + PicDetail picDetail `xml:"pic-detail"` +} + +type picDetail struct { + Slot int `xml:"slot"` + PicSlot int `xml:"pic-slot"` + PicType string `xml:"pic-type"` + State string `xml:"state"` + PicVersion string `xml:"pic-version"` + UpTime string `xml:"up-time"` + PicPortInfoList []picPort `xml:"port-information>port"` +} + +type picPort struct { + PortNumber int `xml:"port-number"` + CableType string `xml:"cable-type"` + FiberMode string `xml:"fiber-mode"` + SFPVendorName string `xml:"sfp-vendor-name"` + SFPVendorPno string `xml:"sfp-vendor-pno"` + Wavelength string `xml:"wavelength"` + SFPVendorFwVer string `xml:"sfp-vendor-fw-ver"` + SFPJnprVer string `xml:"sfp-jnpr-ver"` +} + +type interfacesMediaStruct struct { + InterfaceInformation struct { + PhysicalInterface []physicalInterface `xml:"physical-interface"` + } `xml:"interface-information"` +} + +type physicalInterface struct { + Name string `xml:"name"` + AdminStatus string `xml:"admin-status"` + OperStatus string `xml:"oper-status"` + LocalIndex string `xml:"local-index"` + SnmpIndex string `xml:"snmp-index"` + IfType string `xml:"if-type"` + LinkLevelType string `xml:"link-level-type"` + Mtu string `xml:"mtu"` + Speed string `xml:"speed"` + LinkType string `xml:"link-type"` + InterfaceFlapped struct { + Seconds uint64 `xml:"seconds,attr"` + Value string `xml:",chardata"` + } `xml:"interface-flapped"` + IfDeviceFlags ifDeviceFlags `xml:"if-device-flags"` + Stats trafficStat `xml:"traffic-statistics"` +} + +type trafficStat struct { + InputBytes uint64 `xml:"input-bytes"` + InputPackets uint64 `xml:"input-packets"` + OutputBytes uint64 `xml:"output-bytes"` + OutputPackets uint64 `xml:"output-packets"` + IPv6Traffic ipv6Stat `xml:"ipv6-transit-statistics"` +} +type ipv6Stat struct { + InputBytes uint64 `xml:"input-bytes"` + InputPackets uint64 `xml:"input-packets"` + OutputBytes uint64 `xml:"output-bytes"` + OutputPackets uint64 `xml:"output-packets"` +} + +type ifDeviceFlags struct { + Present bool `xml:"ifdf-present"` + Running bool `xml:"ifdf-running"` + Loopback bool `xml:"ifdf-loopback"` + Down bool `xml:"ifdf-down"` +} + +type chassisHardware struct { + ChassisInventory chassisInventory `xml:"chassis-inventory"` +} + +type chassisInventory struct { + Chassis chassis `xml:"chassis"` +} + +type chassis struct { + Name string `xml:"name"` + SerialNumber string `xml:"serial-number"` + Description string `xml:"description"` + ChassisModule []chassisModule `xml:"chassis-module"` +} + +type chassisModule struct { + Name string `xml:"name"` + PartNumber string `xml:"part-number"` + SerialNumber string `xml:"serial-number"` + Description string `xml:"description"` + CleiCode string `xml:"clei-code"` + ModelNumber string `xml:"model-number"` + ChassisSubModule []chassisSubModule `xml:"chassis-sub-module"` +} + +type chassisSubModule struct { + Name string `xml:"name"` + PartNumber string `xml:"part-number"` + SerialNumber string `xml:"serial-number"` + Description string `xml:"description"` + CleiCode string `xml:"clei-code"` + ModelNumber string `xml:"model-number"` + ChassisSubSubModule []chassisSubSubModule `xml:"chassis-sub-sub-module"` +} +type chassisSubSubModule struct { + Name string `xml:"name"` + Version string `xml:"version"` + PartNumber string `xml:"part-number"` + SerialNumber string `xml:"serial-number"` + Description string `xml:"description"` +} + type result struct { Information struct { Diagnostics []phyDiagInterface `xml:"physical-interface"`