@@ -6,21 +6,74 @@ package iosxr
66import (
77 "fmt"
88 "regexp"
9+ "strconv"
10+ "strings"
911
1012 "github.com/ironcore-dev/network-operator/internal/provider/cisco/gnmiext/v2"
1113)
1214
13- type PhysIf struct {
15+ var (
16+ bundleEtherRE = regexp .MustCompile (`^[bB]undle-[Ee]ther(\d+)(?:\.\d+)?$` )
17+ physicalInterfaceRE = regexp .MustCompile (`^(TenGigE|TwentyFiveGigE|FortyGigE|HundredGigE|GigabitEthernet)(\d){1}(\/\d){2}(\/\d+){1}(.\d{1,5})?$` )
18+ )
19+
20+ type IFaceSpeed string
21+
22+ const (
23+ Speed10G IFaceSpeed = "TenGigE"
24+ Speed25G IFaceSpeed = "TwentyFiveGigE"
25+ Speed40G IFaceSpeed = "FortyGigE"
26+ Speed100G IFaceSpeed = "HundredGigE"
27+ EtherBundle IFaceSpeed = "etherbundle"
28+ )
29+
30+ type BundlePortActivity string
31+
32+ const (
33+ PortActivityOn BundlePortActivity = "on"
34+ PortActivityActive BundlePortActivity = "active"
35+ PortActivityPassive BundlePortActivity = "passive"
36+ PortActivityInherit BundlePortActivity = "inherit"
37+ )
38+
39+ type PhysIfStateType string
40+
41+ const (
42+ StateUp PhysIfStateType = "im-state-up"
43+ StateDown PhysIfStateType = "im-state-down"
44+ StateNotReady PhysIfStateType = "im-state-not-ready"
45+ StateAdminDown PhysIfStateType = "im-state-admin-down"
46+ StateShutDown PhysIfStateType = "im-state-shutdown"
47+ )
48+
49+ // Iface represents physical and bundle interfaces as part of the same struct as they share a lot of common configuration
50+ // and only differ in a few attributes like the interface name and the presence of bundle configuration or not.
51+ type Iface struct {
1452 Name string `json:"-"`
15- Description string `json:"description"`
16- Active string `json:"active"`
17- Vrf string `json:"Cisco-IOS-XR-infra-rsi-cfg:vrf,omitempty"`
18- Statistics Statistics `json:"Cisco-IOS-XR-infra-statsd-cfg:statistics"`
19- IPv4Network IPv4Network `json:"Cisco-IOS-XR-ipv4-io-cfg:ipv4-network"`
20- IPv6Network IPv6Network `json:"Cisco-IOS-XR-ipv6-ma-cfg:ipv6-network"`
21- IPv6Neighbor IPv6Neighbor `json:"Cisco-IOS-XR-ipv6-nd-cfg:ipv6-neighbor"`
22- MTUs MTUs `json:"mtus"`
23- Shutdown gnmiext.Empty `json:"shutdown,omitempty"`
53+ Description string `json:"description,omitzero"`
54+ Statistics Statistics `json:"Cisco-IOS-XR-infra-statsd-cfg:statistics,omitzero"`
55+ MTUs MTUs `json:"mtus,omitzero"`
56+ Active string `json:"active,omitzero"`
57+ Vrf string `json:"Cisco-IOS-XR-infra-rsi-cfg:vrf,omitzero"`
58+ IPv4Network IPv4Network `json:"Cisco-IOS-XR-ipv4-io-cfg:ipv4-network,omitzero"`
59+ IPv6Network IPv6Network `json:"Cisco-IOS-XR-ipv6-ma-cfg:ipv6-network,omitzero"`
60+ IPv6Neighbor IPv6Neighbor `json:"Cisco-IOS-XR-ipv6-nd-cfg:ipv6-neighbor,omitzero"`
61+ Shutdown gnmiext.Empty `json:"shutdown,omitzero"`
62+
63+ // Existence of this object causes the creation of the software subinterface
64+ ModeNoPhysical string `json:"interface-mode-non-physical,omitzero"`
65+
66+ // BundleMember configuration for Physical interface as member of a Bundle-Ether
67+ BundleMember BundleMember `json:"Cisco-IOS-XR-bundlemgr-cfg:bundle-member,omitzero"`
68+
69+ // Mode in which an interface is running (e.g., virtual for subinterfaces)
70+ Mode gnmiext.Empty `json:"interface-virtual,omitzero"`
71+ Bundle Bundle `json:"Cisco-IOS-XR-bundlemgr-cfg:bundle,omitzero"`
72+ SubInterface VlanSubInterface `json:"Cisco-IOS-XR-l2-eth-infra-cfg:vlan-sub-configuration,omitzero"`
73+ }
74+
75+ type BundleMember struct {
76+ ID BundleID `json:"id"`
2477}
2578
2679type Statistics struct {
@@ -29,7 +82,7 @@ type Statistics struct {
2982
3083type IPv4Network struct {
3184 Addresses AddressesIPv4 `json:"addresses"`
32- Mtu uint16 `json:"mtu"`
85+ Mtu uint16 `json:"mtu,omitzero "`
3386}
3487
3588type AddressesIPv4 struct {
@@ -73,64 +126,171 @@ type MTU struct {
73126 Owner string `json:"owner"`
74127}
75128
76- func (i * PhysIf ) XPath () string {
129+ type BundleID struct {
130+ BundleID int32 `json:"bundle-id"`
131+ PortActivity string `json:"port-activity"`
132+ }
133+
134+ type Bundle struct {
135+ MinAct MinimumActive `json:"minimum-active"`
136+ }
137+
138+ type MinimumActive struct {
139+ Links int32 `json:"links"`
140+ }
141+
142+ type VlanSubInterface struct {
143+ VlanIdentifier VlanIdentifier `json:"vlan-identifier"`
144+ }
145+
146+ type VlanIdentifier struct {
147+ FirstTag int32 `json:"first-tag"`
148+ SecondTag int32 `json:"second-tag,omitzero"`
149+ VlanType string `json:"vlan-type"`
150+ }
151+
152+ func (i * Iface ) XPath () string {
77153 return fmt .Sprintf ("Cisco-IOS-XR-ifmgr-cfg:interface-configurations/interface-configuration[active=act][interface-name=%s]" , i .Name )
78154}
79155
80- func (i * PhysIf ) String () string {
81- return fmt .Sprintf ("Name: %s, Description=%s, ShutDown=%t " , i .Name , i .Description , i . Shutdown )
156+ func (i * Iface ) String () string {
157+ return fmt .Sprintf ("Name: %s, Description=%s" , i .Name , i .Description )
82158}
83159
84- type IFaceSpeed string
160+ type PhysIfState struct {
161+ State string `json:"state"`
162+ Name string `json:"-"`
163+ }
85164
86- const (
87- Speed10G IFaceSpeed = "TenGigE"
88- Speed25G IFaceSpeed = "TwentyFiveGigE"
89- Speed40G IFaceSpeed = "FortyGigE"
90- Speed100G IFaceSpeed = "HundredGigE"
91- )
165+ func (phys * PhysIfState ) XPath () string {
166+ // (fixme): hardcoded route processor for the moment
167+ return fmt .Sprintf ("Cisco-IOS-XR-ifmgr-oper:interface-properties/data-nodes/data-node[data-node-name=0/RP0/CPU0]/system-view/interfaces/interface[interface-name=%s]" , phys .Name )
168+ }
169+
170+ func ValidateInterfaceName (name string ) error {
171+ // Supported Interface name formats:
172+ // Physical Interface <PortSpeed><rack><slot><port> e.g TwentyFiveGigE0/0/0/3
173+ // SubInterface <PotySpeed><rack><slot><port>.<vlan-id> e.g TwentyFiveGigE0/0/0/3
174+ // Bundle Interface/Port Channel Bundle-Ether<BundleID>
175+ // Vlans over Bundle Bundle-Ether<BundleID>.<vlan-id>
176+
177+ beErr := CheckInterfaceNameTypeAggregate (name )
178+ physErr := CheckInterfaceNameTypePhysical (name )
179+
180+ if beErr == nil || physErr == nil {
181+ return nil
182+ }
183+ return fmt .Errorf ("unsupported interface name format: %s" , name )
184+ }
185+
186+ func ExtractInterfaceSpeedFromName (ifaceName string ) (IFaceSpeed , error ) {
187+ // Owner of bundle interfaces is 'etherbundle'
188+ if bundleEtherRE .MatchString (ifaceName ) {
189+ return EtherBundle , nil
190+ }
92191
93- func ExtractMTUOwnerFromIfaceName (ifaceName string ) (IFaceSpeed , error ) {
94192 // Match the port_type in an interface name <port_type>/<rack>/<slot/<module>/<port>
95193 // E.g. match TwentyFiveGigE of interface with name TwentyFiveGigE0/0/0/1
96194 re := regexp .MustCompile (`^\D*` )
97-
98- mtuOwner := string (re .Find ([]byte (ifaceName )))
99-
100- if mtuOwner == "" {
101- return "" , fmt .Errorf ("failed to extract MTU owner from interface name %s" , ifaceName )
195+ speed := string (re .Find ([]byte (ifaceName )))
196+ if speed == "" {
197+ return "" , fmt .Errorf ("failed to extract speed from interface name %s" , ifaceName )
102198 }
103199
104- switch mtuOwner {
200+ switch speed {
105201 case string (Speed10G ):
106202 return Speed10G , nil
107203 case string (Speed25G ):
108204 return Speed25G , nil
109205 case string (Speed40G ):
110- return Speed25G , nil
206+ return Speed40G , nil
111207 case string (Speed100G ):
112208 return Speed100G , nil
113209 default :
114- return "" , fmt .Errorf ("unsupported interface type %s for MTU owner extraction " , mtuOwner )
210+ return "" , fmt .Errorf ("unsupported interface type %s" , speed )
115211 }
116212}
117213
118- type PhysIfStateType string
214+ func CheckInterfaceNameTypeAggregate (name string ) error {
215+ matches := bundleEtherRE .FindStringSubmatch (name )
119216
120- const (
121- StateUp PhysIfStateType = "im-state-up"
122- StateDown PhysIfStateType = "im-state-down"
123- StateNotReady PhysIfStateType = "im-state-not-ready"
124- StateAdminDown PhysIfStateType = "im-state-admin-down"
125- StateShutDown PhysIfStateType = "im-state-shutdown"
126- )
217+ if matches == nil {
218+ return fmt .Errorf ("unsupported interface format %q, expected one of: %q" , name , bundleEtherRE .String ())
219+ }
220+ // Fixme(sven-rosenzweig): check BundleId range
221+ return nil
222+ }
127223
128- type PhysIfState struct {
129- State string `json:"state"`
130- Name string `json:"-"`
224+ func CheckInterfaceNameTypePhysical (name string ) error {
225+ if ! physicalInterfaceRE .MatchString (name ) {
226+ return fmt .Errorf ("unsupported physical interface format %s" , name )
227+ }
228+ return nil
131229}
132230
133- func (phys * PhysIfState ) XPath () string {
134- // (fixme): hardcoded route processor for the moment
135- return fmt .Sprintf ("Cisco-IOS-XR-ifmgr-oper:interface-properties/data-nodes/data-node[data-node-name=0/RP0/CPU0]/system-view/interfaces/interface[interface-name=%s]" , phys .Name )
231+ func ExtractVlanTagFromName (name string ) (vlanID int32 , err error ) {
232+ res := strings .Split (name , "." )
233+ switch len (res ) {
234+ case 1 :
235+ return 0 , nil
236+ case 2 :
237+ vlan , err := strconv .ParseInt (res [1 ], 10 , 32 )
238+ if err != nil {
239+ return 0 , fmt .Errorf ("failed to parse VLAN ID from interface name %q: %w" , name , err )
240+ }
241+ return int32 (vlan ), nil
242+ default :
243+ return 0 , fmt .Errorf ("unexpected interface name format %q, expected <interface> or <interface>.<vlan>" , name )
244+ }
245+ }
246+
247+ func ExtractBundleAndSubinterfaceId (name string ) (bundleId , subinterfaceId int32 , err error ) {
248+ // Extract bundle ID and optional subinterface ID from Bundle-Ether<id> or Bundle-Ether<id>.<subif_id>
249+ // Examples: Bundle-Ether200 -> (200, 0), Bundle-Ether200.4095 -> (200, 4095)
250+
251+ // Remove the "Bundle-Ether" or "BE" prefix
252+ var idPart string
253+
254+ if ! bundleEtherRE .MatchString (name ) {
255+ return 0 , 0 , fmt .Errorf ("interface name %q does not start with Bundle-Ether or bundle-ether" , name )
256+ }
257+ idPart = strings .TrimPrefix (strings .TrimPrefix (name , "Bundle-Ether" ), "bundle-ether" )
258+ parts := strings .Split (idPart , "." )
259+
260+ if len (parts ) == 0 || parts [0 ] == "" {
261+ return 0 , 0 , fmt .Errorf ("failed to extract bundle ID from interface name %q" , name )
262+ }
263+
264+ // Parse bundle ID
265+ bundleIdInt , err := strconv .ParseInt (parts [0 ], 10 , 32 )
266+ if err != nil {
267+ return 0 , 0 , fmt .Errorf ("failed to parse bundle ID from interface name %q: %w" , name , err )
268+ }
269+ bundleId = int32 (bundleIdInt )
270+
271+ // Parse subinterface ID if present
272+ if len (parts ) == 2 {
273+ subIfaceIdInt , err := strconv .ParseInt (parts [1 ], 10 , 32 )
274+ if err != nil {
275+ return 0 , 0 , fmt .Errorf ("failed to parse subinterface ID from interface name %q: %w" , name , err )
276+ }
277+ subinterfaceId = int32 (subIfaceIdInt )
278+ } else if len (parts ) > 2 {
279+ return 0 , 0 , fmt .Errorf ("unexpected interface name format %q, expected Bundle-Ether<id> or Bundle-Ether<id>.<subif_id>" , name )
280+ }
281+
282+ return bundleId , subinterfaceId , nil
283+ }
284+
285+ func CheckVlanRange (vlan string ) error {
286+ v , err := strconv .Atoi (vlan )
287+
288+ if err != nil {
289+ return fmt .Errorf ("failed to parse VLAN %q: %w" , vlan , err )
290+ }
291+
292+ if v < 1 || v > 4095 {
293+ return fmt .Errorf ("VLAN %s is out of range, valid range is 1-4095" , vlan )
294+ }
295+ return nil
136296}
0 commit comments