diff --git a/internal/provider/cisco/iosxr/intf.go b/internal/provider/cisco/iosxr/intf.go index 9c0c12675..997bd3857 100644 --- a/internal/provider/cisco/iosxr/intf.go +++ b/internal/provider/cisco/iosxr/intf.go @@ -46,6 +46,16 @@ const ( StateShutDown PhysIfStateType = "im-state-shutdown" ) +type Ifaces struct { + PhysIfList []struct { + Name string `json:"interface-name"` + } `json:"interface-configuration"` +} + +func (*Ifaces) XPath() string { + return "Cisco-IOS-XR-ifmgr-cfg:interface-configurations" +} + // Iface represents physical and bundle interfaces as part of the same struct as they share a lot of common configuration // and only differ in a few attributes like the interface name and the presence of bundle configuration or not. type Iface struct { @@ -183,12 +193,15 @@ func ValidateInterfaceName(name string) error { return fmt.Errorf("unsupported interface name format: %s", name) } -func ExtractInterfaceSpeedFromName(ifaceName string) (IFaceSpeed, error) { +func ExtractOwnerFromInterfaceName(ifaceName string) (IFaceSpeed, error) { // Owner of bundle interfaces is 'etherbundle' if bundleEtherRE.MatchString(ifaceName) { return EtherBundle, nil } + return ExtractInterfaceSpeedFromName(ifaceName) +} +func ExtractInterfaceSpeedFromName(ifaceName string) (IFaceSpeed, error) { // Match the port_type in an interface name /// // E.g. match TwentyFiveGigE of interface with name TwentyFiveGigE0/0/0/1 re := regexp.MustCompile(`^\D*`) @@ -211,6 +224,21 @@ func ExtractInterfaceSpeedFromName(ifaceName string) (IFaceSpeed, error) { } } +func MapInterfaceSpeedToNumeric(speed IFaceSpeed) (int32, error) { + switch speed { + case Speed10G: + return 10_000, nil + case Speed25G: + return 25_000, nil + case Speed40G: + return 40_000, nil + case Speed100G: + return 100_000, nil + default: + return 0, fmt.Errorf("unsupported interface speed %s", speed) + } +} + func CheckInterfaceNameTypeAggregate(name string) error { matches := bundleEtherRE.FindStringSubmatch(name) diff --git a/internal/provider/cisco/iosxr/provider.go b/internal/provider/cisco/iosxr/provider.go index 09d785c59..bcc92f14c 100644 --- a/internal/provider/cisco/iosxr/provider.go +++ b/internal/provider/cisco/iosxr/provider.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net" + "time" "github.com/ironcore-dev/network-operator/api/core/v1alpha1" "github.com/ironcore-dev/network-operator/internal/deviceutil" @@ -20,6 +21,7 @@ import ( var ( _ provider.Provider = &Provider{} + _ provider.DeviceProvider = &Provider{} _ provider.InterfaceProvider = &Provider{} ) @@ -48,6 +50,67 @@ func (p *Provider) Disconnect(ctx context.Context, conn *deviceutil.Connection) return p.conn.Close() } +func (p *Provider) ListPorts(ctx context.Context) ([]provider.DevicePort, error) { + iFaces := new(Ifaces) + err := p.client.GetConfig(ctx, iFaces) + if err != nil { + return nil, fmt.Errorf("failed to list ports: %w", err) + } + + dp := make([]provider.DevicePort, 0, len(iFaces.PhysIfList)) + for _, intf := range iFaces.PhysIfList { + s, _ := ExtractInterfaceSpeedFromName(intf.Name) + n, err := MapInterfaceSpeedToNumeric(s) + if err != nil { + return nil, fmt.Errorf("failed to map interface speed to numeric value: %w", err) + } + if s != "" { + dp = append(dp, provider.DevicePort{ + ID: intf.Name, + Type: string(s), + SupportedSpeedsGbps: []int32{n}, + }) + } + } + return dp, nil +} + +func (p *Provider) GetDeviceInfo(ctx context.Context) (*provider.DeviceInfo, error) { + i := new(BasicDeviceInfo) + + if err := p.client.GetState(ctx, i); err != nil { + return nil, err + } + + return &provider.DeviceInfo{ + Manufacturer: Manufacturer, + Model: i.Model, + SerialNumber: i.SerialNumber, + FirmwareVersion: i.FirmwareVersion, + }, nil +} + +func (p *Provider) GetLastRebootTime(ctx context.Context) (time.Time, error) { + t := new(SystemTime) + if err := p.client.GetState(ctx, t); err != nil { + return time.Time{}, err + } + uptimeDuration := time.Second * time.Duration(t.Uptime.Uptime) * -1 + return t.CurrTime.ConvertToTime().Add(uptimeDuration), nil +} + +func (p *Provider) Reboot(ctx context.Context, conn *deviceutil.Connection) error { + return errors.New("IOS XR Provider does not support rebooting the device") +} + +func (p *Provider) FactoryReset(ctx context.Context, conn *deviceutil.Connection) error { + return errors.New("IOS XR Provider does not support factory reset") +} + +func (p *Provider) Reprovision(cxt context.Context, conn *deviceutil.Connection) error { + return errors.New("IOS XR Provider does not support reprovisioning") +} + func (p *Provider) EnsureInterface(ctx context.Context, req *provider.EnsureInterfaceRequest) error { if p.client == nil { return errors.New("client is not connected") @@ -64,7 +127,7 @@ func (p *Provider) EnsureInterface(ctx context.Context, req *provider.EnsureInte // SubInterface . e.g TwentyFiveGigE0/0/0/3 // Bundle Interface/Port Channel Bundle-Ether // Vlans over Bundle Bundle-Ether. - _, err := ExtractInterfaceSpeedFromName(name) + _, err := ExtractOwnerFromInterfaceName(name) if err != nil { return err } @@ -242,7 +305,7 @@ func NewVlanSubinterface(firstTag, secondTag int32, vlanType string) VlanSubInte } func NewMTU(intName string, mtu int32) (MTUs, error) { - owner, err := ExtractInterfaceSpeedFromName(intName) + owner, err := ExtractOwnerFromInterfaceName(intName) if err != nil { message := "failed to extract MTU owner from interface name" + intName return MTUs{}, errors.New(message) diff --git a/internal/provider/cisco/iosxr/system.go b/internal/provider/cisco/iosxr/system.go new file mode 100644 index 000000000..13e9230ca --- /dev/null +++ b/internal/provider/cisco/iosxr/system.go @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package iosxr + +import "time" + +const Manufacturer = "Cisco" + +type BasicDeviceInfo struct { + // Model is the chassis model of the device, e.g. "NCS-57C3-MOD-SYS". + Model string `json:"model-name"` + + // SerialNumber is the serial number of the device, e.g. "9VT9OHZBC3H". + SerialNumber string `json:"serial-number"` + + // FirmwareVersion is the firmware version of the device, e.g. "25.2.2". + FirmwareVersion string `json:"firmware-version"` +} + +func (*BasicDeviceInfo) XPath() string { + return "Cisco-IOS-XR-platform-inventory-oper:/platform-inventory/racks/rack[name=0]/attributes/basic-info" +} + +type SystemTime struct { + CurrTime Clock `json:"clock"` + Uptime Uptime `json:"uptime"` +} + +type Uptime struct { + // Uptime stores the uptime in seconds + Uptime uint32 `json:"uptime"` +} + +type Clock struct { + Day int `json:"day"` + Hour int `json:"hour"` + Millisecond int `json:"millisecond"` + Minute int `json:"minute"` + Month int `json:"month"` + Second int `json:"second"` + TimeZone string `json:"time-zone"` + Year int `json:"year"` +} + +func (*SystemTime) XPath() string { + return "Cisco-IOS-XR-shellutil-oper:/system-time" +} + +func (c *Clock) ConvertToTime() time.Time { + return time.Date(c.Year, time.Month(c.Month), c.Day, c.Hour, c.Minute, c.Second, c.Millisecond, time.UTC) +} diff --git a/internal/provider/cisco/iosxr/system_test.go b/internal/provider/cisco/iosxr/system_test.go new file mode 100644 index 000000000..e4962efdc --- /dev/null +++ b/internal/provider/cisco/iosxr/system_test.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 +package iosxr + +func init() { + dInfo := BasicDeviceInfo{ + Model: "NCS-57C3-MOD-SYS", + SerialNumber: "9VT9OHZBC3H", + FirmwareVersion: "25.2.2", + } + Register("system", &dInfo) + + sysTime := SystemTime{ + CurrTime: Clock{ + Day: 1, + Hour: 1, + Millisecond: 1, + Minute: 1, + Month: 1, + Second: 1, + TimeZone: "UTC", + Year: 1990, + }, + Uptime: Uptime{ + Uptime: 123456, + }, + } + Register("system_time", &sysTime) +} diff --git a/internal/provider/cisco/iosxr/testdata/system.json b/internal/provider/cisco/iosxr/testdata/system.json new file mode 100644 index 000000000..c58db6856 --- /dev/null +++ b/internal/provider/cisco/iosxr/testdata/system.json @@ -0,0 +1,15 @@ +{ + "platform-inventory": { + "racks": { + "rack": { + "attributes": { + "basic-info": { + "model-name": "NCS-57C3-MOD-SYS", + "serial-number": "9VT9OHZBC3H", + "firmware-version": "25.2.2" + } + } + } + } + } +} diff --git a/internal/provider/cisco/iosxr/testdata/system_time.json b/internal/provider/cisco/iosxr/testdata/system_time.json new file mode 100644 index 000000000..e469c6b2a --- /dev/null +++ b/internal/provider/cisco/iosxr/testdata/system_time.json @@ -0,0 +1,17 @@ +{ + "system-time": { + "clock": { + "day": 1, + "hour": 1, + "millisecond": 1, + "minute": 1, + "month": 1, + "second": 1, + "time-zone": "UTC", + "year": 1990 + }, + "uptime": { + "uptime": 123456 + } + } +}