diff --git a/config/device-classes/generic/adva_fsp3kr7.yaml b/config/device-classes/generic/adva_fsp3kr7.yaml index 97e5c18..df12fdd 100644 --- a/config/device-classes/generic/adva_fsp3kr7.yaml +++ b/config/device-classes/generic/adva_fsp3kr7.yaml @@ -27,27 +27,4 @@ identify: format: "$property$ ($read_value$)" os_version: - detection: snmpget - oid: .1.3.6.1.4.1.2544.1.11.2.2.1.5.0 - -components: - interfaces: - types: - dwdm: - detection: 1.3.6.1.4.1.2544.1.11.2.4.3.5.1.3 - specific_values: - rx_level: - oid: 1.3.6.1.4.1.2544.1.11.2.4.3.5.1.3 - operators: - - type: modify - modify_method: multiply - value: - detection: constant - value: 0.1 - tx_level: - oid: 1.3.6.1.4.1.2544.1.11.2.4.3.5.1.4 - operators: - - type: modify - modify_method: multiply - value: - detection: constant - value: 0.1 \ No newline at end of file + oid: .1.3.6.1.4.1.2544.1.11.2.2.1.5.0 \ No newline at end of file diff --git a/core/communicator/adva.go b/core/communicator/adva.go new file mode 100644 index 0000000..7be0bad --- /dev/null +++ b/core/communicator/adva.go @@ -0,0 +1,238 @@ +package communicator + +import ( + "context" + "fmt" + "github.com/inexio/thola/core/device" + "github.com/inexio/thola/core/network" + "github.com/inexio/thola/core/value" + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + "github.com/shopspring/decimal" + "regexp" + "strconv" + "strings" +) + +type advaCommunicator struct { + baseCommunicator +} + +// GetInterfaces returns the interfaces of adva devices. +func (c *advaCommunicator) GetInterfaces(ctx context.Context) ([]device.Interface, error) { + interfaces, err := c.sub.GetInterfaces(ctx) + if err != nil { + return nil, err + } + + if advaGetDWDMInterfaces(ctx, interfaces) != nil { + return nil, err + } + + if advaGetChannelMatrix(ctx, interfaces) != nil { + return nil, err + } + + return interfaces, nil +} + +func advaGetDWDMInterfaces(ctx context.Context, interfaces []device.Interface) error { + con, ok := network.DeviceConnectionFromContext(ctx) + if !ok || con.SNMP == nil { + return errors.New("no device connection available") + } + + specialInterfacesRaw, err := getValuesBySNMPWalk(ctx, deviceClassOIDs{ + "rx_power": deviceClassOID{ + SNMPGetConfiguration: network.SNMPGetConfiguration{ + OID: "1.3.6.1.4.1.2544.1.11.2.4.3.5.1.3", + }, + operators: propertyOperators{ + &modifyOperatorAdapter{ + &multiplyNumberModifier{ + value: &constantPropertyReader{ + Value: value.New("0.1"), + }, + }, + }, + }, + }, + "tx_power": deviceClassOID{ + SNMPGetConfiguration: network.SNMPGetConfiguration{ + OID: "1.3.6.1.4.1.2544.1.11.2.4.3.5.1.4", + }, + operators: propertyOperators{ + &modifyOperatorAdapter{ + &multiplyNumberModifier{ + value: &constantPropertyReader{ + Value: value.New("0.1"), + }, + }, + }, + }, + }, + }) + + if err != nil { + return errors.Wrap(err, "failed to read rx/tx power of ports") + } + + for i, networkInterface := range interfaces { + if specialValues, ok := specialInterfacesRaw[fmt.Sprint(*networkInterface.IfIndex)]; ok { + err := addSpecialInterfacesValuesToInterface("dwdm", &interfaces[i], specialValues) + if err != nil { + log.Ctx(ctx).Trace().Err(err).Msg("can't parse oid values into Interface struct") + return errors.Wrap(err, "can't parse oid values into Interface struct") + } + } + } + + rx100Values, err := advaGetPowerValues(ctx, ".1.3.6.1.4.1.2544.1.11.11.7.2.1.1.1.21") + if err != nil { + return errors.Wrap(err, "failed to get rx 100 values") + } + + tx100Values, err := advaGetPowerValues(ctx, ".1.3.6.1.4.1.2544.1.11.11.7.2.1.1.1.22") + if err != nil { + return errors.Wrap(err, "failed to get tx 100 values") + } + + for i, interf := range interfaces { + if interf.IfDescr != nil { + rxVal, rxOK := rx100Values[*interf.IfDescr] + txVal, txOK := tx100Values[*interf.IfDescr] + if (rxOK || txOK) && interf.DWDM == nil { + interfaces[i].DWDM = &device.DWDMInterface{} + } + if rxOK { + interfaces[i].DWDM.RXPower100G = &rxVal + } + if txOK { + interfaces[i].DWDM.TXPower100G = &txVal + } + } + } + + return nil +} + +func advaGetChannelMatrix(ctx context.Context, interfaces []device.Interface) error { + con, ok := network.DeviceConnectionFromContext(ctx) + if !ok || con.SNMP == nil { + return errors.New("no device connection available") + } + + facilityPhysInstValueInputPower := ".1.3.6.1.4.1.2544.1.11.11.7.2.1.1.1.2" + facilityPhysInstValueInputPowerValues, err := con.SNMP.SnmpClient.SNMPWalk(ctx, facilityPhysInstValueInputPower) + if err != nil { + return errors.Wrap(err, "failed to walk facilityPhysInstValueInputPower") + } + + var subtrees []string + var channels []device.OpticalChannel + + for _, res := range facilityPhysInstValueInputPowerValues { + subtree := strings.TrimPrefix(res.GetOID(), facilityPhysInstValueInputPower) + if s := strings.Split(strings.Trim(subtree, "."), "."); len(s) > 2 && s[len(s)-2] != "0" { + val, err := res.GetValueString() + if err != nil { + return errors.Wrap(err, "failed to get rx value of channel "+subtree) + } + a, err := decimal.NewFromString(val) + if err != nil { + return errors.Wrap(err, "failed to parse rx value of channel "+subtree) + } + b := decimal.NewFromFloat(0.1) + valFin, _ := a.Mul(b).Float64() + + subtrees = append(subtrees, subtree) + channels = append(channels, device.OpticalChannel{ + Channel: s[len(s)-2], + RXPower: &valFin, + }) + } + } + + for i, subtree := range subtrees { + res, err := con.SNMP.SnmpClient.SNMPGet(ctx, ".1.3.6.1.4.1.2544.1.11.11.7.2.1.1.1.1"+subtree) + if err != nil { + return errors.Wrap(err, "failed to get facilityPhysInstValueOutputPower for subtree "+subtree) + } + + if len(res) != 1 { + return errors.New("failed to get tx value of channel " + subtree) + } + + val, err := res[0].GetValueString() + if err != nil { + return errors.Wrap(err, "failed to get tx value of channel "+subtree) + } + a, err := decimal.NewFromString(val) + if err != nil { + return errors.Wrap(err, "failed to parse tx value of channel "+subtree) + } + b := decimal.NewFromFloat(0.1) + valFin, _ := a.Mul(b).Float64() + + channels[i].TXPower = &valFin + + p := strings.Split(strings.ReplaceAll(strings.Trim(subtree, "."), "33152", "N"), ".") + regex, err := regexp.Compile("-" + p[0] + "-" + p[1] + "-" + p[2]) + if err != nil { + return errors.Wrap(err, "failed to build regex") + } + + for j, interf := range interfaces { + if interf.IfDescr != nil && regex.MatchString(*interf.IfDescr) { + if interf.DWDM == nil { + interfaces[j].DWDM = &device.DWDMInterface{} + } + interfaces[j].DWDM.Channels = append(interfaces[j].DWDM.Channels, channels[i]) + break + } + } + } + + return nil +} + +func advaGetPowerValues(ctx context.Context, oid string) (map[string]float64, error) { + con, ok := network.DeviceConnectionFromContext(ctx) + if !ok || con.SNMP == nil { + return nil, errors.New("no device connection available") + } + + values, err := con.SNMP.SnmpClient.SNMPWalk(ctx, oid) + if err != nil { + return nil, errors.Wrap(err, "failed to walk facilityPhysInstValueCalculatedTotalPower") + } + + descrToValues := make(map[string]float64) + + for _, val := range values { + subtree := strings.TrimPrefix(val.GetOID(), oid) + subtreeSplit := strings.Split(strings.Trim(subtree, "."), ".") + if len(subtreeSplit) < 3 { + return nil, errors.New("invalid facilityPhysInstValueCalculatedTotalPower value") + } + + valueString, err := val.GetValueString() + if err != nil { + return nil, errors.Wrap(err, "failed to get rx value") + } + valueDecimal, err := decimal.NewFromString(valueString) + if err != nil { + return nil, errors.Wrap(err, "failed to parse rx value") + } + multiplier := decimal.NewFromFloat(0.1) + + portInt, err := strconv.Atoi(subtreeSplit[2]) + if err != nil { + return nil, errors.Wrap(err, "failed to parse oid to int") + } + + descrToValues["CH-"+subtreeSplit[0]+"-"+subtreeSplit[1]+"-C"+strconv.Itoa(portInt/256)], _ = valueDecimal.Mul(multiplier).Float64() + } + + return descrToValues, nil +} diff --git a/core/communicator/device_class_communicator.go b/core/communicator/device_class_communicator.go index bac5fef..a314a6b 100644 --- a/core/communicator/device_class_communicator.go +++ b/core/communicator/device_class_communicator.go @@ -111,7 +111,7 @@ func (o *deviceClassCommunicator) GetInterfaces(ctx context.Context) ([]device.I } for t, typeDef := range o.components.interfaces.Types { - specialInterfacesRaw, err := o.getValuesBySNMPWalk(ctx, typeDef.Values) + specialInterfacesRaw, err := getValuesBySNMPWalk(ctx, typeDef.Values) if err != nil { return nil, err } @@ -130,54 +130,6 @@ func (o *deviceClassCommunicator) GetInterfaces(ctx context.Context) ([]device.I return networkInterfaces, nil } -func addSpecialInterfacesValuesToInterface(interfaceType string, interf *device.Interface, specialValues interface{}) error { - switch interfaceType { - case "ether_like": - var specialValuesStruct device.EthernetLikeInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.EthernetLike = &specialValuesStruct - case "radio": - var specialValuesStruct device.RadioInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.Radio = &specialValuesStruct - case "dwdm": - var specialValuesStruct device.DWDMInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.DWDM = &specialValuesStruct - case "optical_transponder": - var specialValuesStruct device.OpticalTransponderInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.OpticalTransponder = &specialValuesStruct - case "optical_amplifier": - var specialValuesStruct device.OpticalAmplifierInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.OpticalAmplifier = &specialValuesStruct - case "optical_opm": - var specialValuesStruct device.OpticalOPMInterface - err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) - if err != nil { - return errors.Wrap(err, "failed to decode special values") - } - interf.OpticalOPM = &specialValuesStruct - } - return nil -} - func (o *deviceClassCommunicator) GetIfTable(ctx context.Context) ([]device.Interface, error) { if o.components.interfaces == nil || o.components.interfaces.IfTable == nil { log.Ctx(ctx).Trace().Str("property", "ifTable").Str("device_class", o.name).Msg("no interface information available") @@ -805,7 +757,7 @@ func convertRawInterfaces(ctx context.Context, interfacesRaw []map[string]value. return networkInterfaces, nil } -func (o *deviceClassCommunicator) getValuesBySNMPWalk(ctx context.Context, oids deviceClassOIDs) (map[string]map[string]interface{}, error) { +func getValuesBySNMPWalk(ctx context.Context, oids deviceClassOIDs) (map[string]map[string]interface{}, error) { networkInterfaces := make(map[string]map[string]interface{}) con, ok := network.DeviceConnectionFromContext(ctx) @@ -849,3 +801,51 @@ func (o *deviceClassCommunicator) getValuesBySNMPWalk(ctx context.Context, oids return networkInterfaces, nil } + +func addSpecialInterfacesValuesToInterface(interfaceType string, interf *device.Interface, specialValues map[string]interface{}) error { + switch interfaceType { + case "ether_like": + var specialValuesStruct device.EthernetLikeInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.EthernetLike = &specialValuesStruct + case "radio": + var specialValuesStruct device.RadioInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.Radio = &specialValuesStruct + case "dwdm": + var specialValuesStruct device.DWDMInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.DWDM = &specialValuesStruct + case "optical_transponder": + var specialValuesStruct device.OpticalTransponderInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.OpticalTransponder = &specialValuesStruct + case "optical_amplifier": + var specialValuesStruct device.OpticalAmplifierInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.OpticalAmplifier = &specialValuesStruct + case "optical_opm": + var specialValuesStruct device.OpticalOPMInterface + err := mapstructure.WeakDecode(specialValues, &specialValuesStruct) + if err != nil { + return errors.Wrap(err, "failed to decode special values") + } + interf.OpticalOPM = &specialValuesStruct + } + return nil +} diff --git a/core/communicator/device_class_to_code_communicator.go b/core/communicator/device_class_to_code_communicator.go index 54c09ca..273d0ab 100644 --- a/core/communicator/device_class_to_code_communicator.go +++ b/core/communicator/device_class_to_code_communicator.go @@ -21,6 +21,8 @@ func getCodeCommunicator(classIdentifier string, rel *relatedNetworkDeviceCommun return &iosCommunicator{baseCommunicator{rel}}, nil case "ekinops": return &ekinopsCommunicator{baseCommunicator{rel}}, nil + case "adva_fsp3kr7": + return &advaCommunicator{baseCommunicator{rel}}, nil } return nil, tholaerr.NewNotFoundError(fmt.Sprintf("no communicator found for device class identifier '%s'", classIdentifier)) } diff --git a/core/communicator/ekinops_module_reader_opm.go b/core/communicator/ekinops_module_reader_opm.go index dd788b0..9caf68d 100644 --- a/core/communicator/ekinops_module_reader_opm.go +++ b/core/communicator/ekinops_module_reader_opm.go @@ -165,7 +165,7 @@ func ekinopsReadOPMMetrics(ctx context.Context, oids ekinopsOPMOIDs) ([]device.O for channelIdx := 16; channelIdx <= 776; channelIdx += 8 { rxPower := channelValues[k][channelIdx] - channel := device.OpticalOPMChannel{ + channel := device.OpticalChannel{ Channel: fmt.Sprintf("C%.2f", channelNum), RXPower: &rxPower, } diff --git a/core/device/device.go b/core/device/device.go index 26e562f..5248229 100644 --- a/core/device/device.go +++ b/core/device/device.go @@ -166,8 +166,11 @@ type RadioInterface struct { // // swagger:model type DWDMInterface struct { - RXLevel *float64 `yaml:"rx_level,omitempty" json:"rx_level,omitempty" xml:"rx_level,omitempty" mapstructure:"rx_level"` - TXLevel *float64 `yaml:"tx_level,omitempty" json:"tx_level,omitempty" xml:"tx_level,omitempty" mapstructure:"tx_level"` + RXPower *float64 `yaml:"rx_power,omitempty" json:"rx_power,omitempty" xml:"rx_power,omitempty" mapstructure:"rx_power"` + TXPower *float64 `yaml:"tx_power,omitempty" json:"tx_power,omitempty" xml:"tx_power,omitempty" mapstructure:"tx_power"` + RXPower100G *float64 `yaml:"rx_power_100_g,omitempty" json:"rx_power_100_g,omitempty" xml:"rx_power_100_g,omitempty" mapstructure:"rx_power_100_g"` + TXPower100G *float64 `yaml:"tx_power_100_g,omitempty" json:"tx_power_100_g,omitempty" xml:"tx_power_100_g,omitempty" mapstructure:"tx_power_100_g"` + Channels []OpticalChannel `yaml:"channels,omitempty" json:"channels,omitempty" xml:"channels,omitempty" mapstructure:"channels"` } // OpticalTransponderInterface @@ -203,20 +206,21 @@ type OpticalAmplifierInterface struct { // // swagger:model type OpticalOPMInterface struct { - Identifier *string `yaml:"identifier,omitempty" json:"identifier,omitempty" xml:"identifier,omitempty" mapstructure:"identifier"` - Label *string `yaml:"label,omitempty" json:"label,omitempty" xml:"label,omitempty" mapstructure:"label"` - RXPower *float64 `yaml:"rx_power,omitempty" json:"rx_power,omitempty" xml:"rx_power,omitempty" mapstructure:"rx_power"` - Channels []OpticalOPMChannel `yaml:"channels,omitempty" json:"channels,omitempty" xml:"channels,omitempty" mapstructure:"channels"` + Identifier *string `yaml:"identifier,omitempty" json:"identifier,omitempty" xml:"identifier,omitempty" mapstructure:"identifier"` + Label *string `yaml:"label,omitempty" json:"label,omitempty" xml:"label,omitempty" mapstructure:"label"` + RXPower *float64 `yaml:"rx_power,omitempty" json:"rx_power,omitempty" xml:"rx_power,omitempty" mapstructure:"rx_power"` + Channels []OpticalChannel `yaml:"channels,omitempty" json:"channels,omitempty" xml:"channels,omitempty" mapstructure:"channels"` } -// OpticalOPMChannel +// OpticalChannel // -// OpticalOPMChannel represents an optical opm channel of an optical opm interface. +// OpticalChannel represents an optical channel. // // swagger:model -type OpticalOPMChannel struct { +type OpticalChannel struct { Channel string `yaml:"channel,omitempty" json:"channel,omitempty" xml:"channel,omitempty" mapstructure:"channel"` RXPower *float64 `yaml:"rx_power,omitempty" json:"rx_power,omitempty" xml:"rx_power,omitempty" mapstructure:"rx_power"` + TXPower *float64 `yaml:"tx_power,omitempty" json:"tx_power,omitempty" xml:"tx_power,omitempty" mapstructure:"tx_power"` } // diff --git a/core/request/check_interface_metrics_request_process.go b/core/request/check_interface_metrics_request_process.go index 224b71a..a68d5e2 100644 --- a/core/request/check_interface_metrics_request_process.go +++ b/core/request/check_interface_metrics_request_process.go @@ -468,15 +468,29 @@ func addCheckInterfacePerformanceData(interfaces []device.Interface, r *monitori //DWDM interface metrics if i.DWDM != nil { - if i.DWDM.RXLevel != nil { - err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("rx_level", *i.DWDM.RXLevel).SetLabel(*i.IfDescr)) + if i.DWDM.RXPower != nil { + err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("rx_power", *i.DWDM.RXPower).SetLabel(*i.IfDescr)) if err != nil { return err } } - if i.DWDM.TXLevel != nil { - err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("tx_level", *i.DWDM.TXLevel).SetLabel(*i.IfDescr)) + if i.DWDM.TXPower != nil { + err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("tx_power", *i.DWDM.TXPower).SetLabel(*i.IfDescr)) + if err != nil { + return err + } + } + + if i.DWDM.RXPower100G != nil { + err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("rx_power_100_g", *i.DWDM.RXPower100G).SetLabel(*i.IfDescr)) + if err != nil { + return err + } + } + + if i.DWDM.TXPower100G != nil { + err := r.AddPerformanceDataPoint(monitoringplugin.NewPerformanceDataPoint("tx_power_100_g", *i.DWDM.TXPower100G).SetLabel(*i.IfDescr)) if err != nil { return err } diff --git a/go.sum b/go.sum index 526b8ee..3039495 100644 --- a/go.sum +++ b/go.sum @@ -178,8 +178,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN 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/labstack/echo/v4 v4.2.0 h1:jkCSsjXmBmapVXF6U4BrSz/cgofWM0CU3Q74wQvXkIc= -github.com/labstack/echo/v4 v4.2.0/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.2.1 h1:LF5Iq7t/jrtUuSutNuiEWtB5eiHfZ5gSe2pcu5exjQw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=