Skip to content

Commit fe04117

Browse files
fixup! (feat): API side support QandQ Vlan configuration
1 parent cda5b26 commit fe04117

5 files changed

Lines changed: 98 additions & 9 deletions

File tree

api/core/v1alpha1/interface_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ type Switchport struct {
148148
// +kubebuilder:validation:Maximum=4094
149149
AccessVlan int32 `json:"accessVlan,omitempty"`
150150

151-
// InnerVlan specifies the VLAN id for QinQ access mode switchports.
151+
// InnerVlan specifies the customer VLAN id for QinQ access mode switchports.
152152
// +optional
153153
// +kubebuilder:validation:Minimum=1
154154
// +kubebuilder:validation:Maximum=4094

config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,8 @@ spec:
358358
minItems: 1
359359
type: array
360360
innerVlan:
361-
description: InnerVlan specifies the VLAN id for QinQ access mode
362-
switchports.
361+
description: InnerVlan specifies the customer VLAN id for QinQ
362+
access mode switchports.
363363
format: int32
364364
maximum: 4094
365365
minimum: 1

internal/provider/cisco/iosxr/intf.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package iosxr
55

66
import (
7-
"errors"
87
"fmt"
98
"regexp"
109
"strconv"
@@ -13,6 +12,11 @@ import (
1312
"github.com/ironcore-dev/network-operator/internal/provider/cisco/gnmiext/v2"
1413
)
1514

15+
var (
16+
bundleEtherRE = regexp.MustCompile(`^Bundle-Ether*`)
17+
physicalInterfaceRE = regexp.MustCompile(`(TenGigE|TwentyFiveGigE|FortyGigE|HundredGigE|GigabitEthernet|Te|TF|Fo|Hu)(\d{0,4})(\.(\d+))?$`)
18+
)
19+
1620
type IFaceSpeed string
1721

1822
const (
@@ -163,9 +167,26 @@ func (phys *PhysIfState) XPath() string {
163167
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)
164168
}
165169

166-
func ExtractInterfaceSpeedFromName(ifaceName string) (IFaceSpeed, error) {
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+
} else if beErr != nil && physErr == nil {
183+
return nil
184+
}
185+
return fmt.Errorf("unsupported interface name format: %s", name)
186+
}
187+
188+
func ExtractOwnerFromInterfaceName(ifaceName string) (IFaceSpeed, error) {
167189
// Owner of bundle interfaces is 'etherbundle'
168-
bundleEtherRE := regexp.MustCompile(`^Bundle-Ether*`)
169190
if bundleEtherRE.MatchString(ifaceName) {
170191
// For Bundle-Ether interfaces
171192
return EtherBundle, nil
@@ -194,9 +215,6 @@ func ExtractInterfaceSpeedFromName(ifaceName string) (IFaceSpeed, error) {
194215
}
195216

196217
func CheckInterfaceNameTypeAggregate(name string) error {
197-
if name == "" {
198-
return errors.New("interface name must not be empty")
199-
}
200218
// Matches Bundle-Ether<VLAN>[.<VLAN>] or BE<VLAN>[.<VLAN>]
201219
re := regexp.MustCompile(`^(Bundle-Ether|BE)(\d+)(\.(\d+))?$`)
202220
matches := re.FindStringSubmatch(name)
@@ -220,6 +238,16 @@ func CheckInterfaceNameTypeAggregate(name string) error {
220238
return nil
221239
}
222240

241+
func CheckInterfaceNameTypePhysical(name string) error {
242+
if !physicalInterfaceRE.MatchString(name) {
243+
return fmt.Errorf("unsupported physical interface format %s", name)
244+
}
245+
if _, err := ExtractVlanTagFromName(name); err != nil {
246+
return err
247+
}
248+
return nil
249+
}
250+
223251
func ExtractVlanTagFromName(name string) (vlanID int32, err error) {
224252
// TF0/0/0/3.2021 or Te0/0/0/3.2021 or Hun0/0/0/3.2021
225253
res := strings.Split(name, ".")

internal/provider/cisco/iosxr/intf_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package iosxr
55

6+
import "testing"
7+
68
func init() {
79
name := "TwentyFiveGigE0/0/0/14"
810

@@ -51,3 +53,58 @@ func init() {
5153
},
5254
})
5355
}
56+
57+
func TestValidateInterfaceName(t *testing.T) {
58+
tests := []struct {
59+
name string
60+
ifaceName string
61+
wantErr bool
62+
errMsg string
63+
}{
64+
{
65+
name: "valid TenGigE interface",
66+
ifaceName: "TenGigE0001",
67+
wantErr: false,
68+
},
69+
{
70+
name: "valid TenGigE interface",
71+
ifaceName: "TenGigE0001.100",
72+
wantErr: false,
73+
},
74+
{
75+
name: "missing ",
76+
ifaceName: "eth-1-1",
77+
wantErr: true,
78+
},
79+
{
80+
name: "valid Bundle-Ether interface",
81+
ifaceName: "Bundle-Ether1",
82+
wantErr: false,
83+
},
84+
{
85+
name: "valid Bundle-Ether with VLAN",
86+
ifaceName: "Bundle-Ether1.100",
87+
wantErr: false,
88+
},
89+
{
90+
name: "valid BE interface",
91+
ifaceName: "BE1",
92+
wantErr: false,
93+
},
94+
}
95+
96+
for _, tt := range tests {
97+
t.Run(tt.name, func(t *testing.T) {
98+
err := ValidateInterfaceName(tt.ifaceName)
99+
if tt.wantErr {
100+
if err == nil {
101+
t.Errorf("Interface name %s accepted as valid, expected error", tt.ifaceName)
102+
}
103+
} else {
104+
if err != nil {
105+
t.Errorf("Interface name %s rejected as invalid, expected valid. Error: %v", tt.ifaceName, err)
106+
}
107+
}
108+
})
109+
}
110+
}

internal/provider/cisco/iosxr/provider.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ func (p *Provider) EnsureInterface(ctx context.Context, req *provider.EnsureInte
5656

5757
name := req.Interface.Spec.Name
5858

59+
if err := ValidateInterfaceName(name); err != nil {
60+
return err
61+
}
62+
5963
// Configure different interface types based on the interface name
6064
// Interface <PortSpeed><rack><slot><port> e.g TwentyFiveGigE0/0/0/3
6165
// SubInterface <PotySpeed><rack><slot><port>.<vlan-id> e.g TwentyFiveGigE0/0/0/3

0 commit comments

Comments
 (0)