Skip to content

Commit 4d377a6

Browse files
authored
[subinterface]Added additional checks in portchannel and subinterface commands (#2345)
*Added additional checks in subinterface and portchannel commands so they don't conflict. Without the checks, a subinterface could be created on a portchannel member and vice versa which will lead to SAI failure followed by orchagent crash.
1 parent bbcdf2e commit 4d377a6

11 files changed

+82
-39
lines changed

config/main.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,14 @@ def add_portchannel_member(ctx, portchannel_name, port_name):
21132113
ctx.fail(" {} has ip address configured".format(port_name))
21142114
return
21152115

2116+
for key in db.get_keys('VLAN_SUB_INTERFACE'):
2117+
if type(key) == tuple:
2118+
continue
2119+
intf = key.split(VLAN_SUB_INTERFACE_SEPARATOR)[0]
2120+
parent_intf = get_intf_longname(intf)
2121+
if parent_intf == port_name:
2122+
ctx.fail(" {} has subinterfaces configured".format(port_name))
2123+
21162124
# Dont allow a port to be member of port channel if it is configured as a VLAN member
21172125
for k,v in db.get_table('VLAN_MEMBER'):
21182126
if v == port_name:
@@ -6762,23 +6770,24 @@ def add_subinterface(ctx, subinterface_name, vid):
67626770

67636771
config_db = ctx.obj['db']
67646772
port_dict = config_db.get_table(intf_table_name)
6773+
parent_intf = get_intf_longname(interface_alias)
67656774
if interface_alias is not None:
67666775
if not port_dict:
67676776
ctx.fail("{} parent interface not found. {} table none".format(interface_alias, intf_table_name))
6768-
if get_intf_longname(interface_alias) not in port_dict.keys():
6777+
if parent_intf not in port_dict.keys():
67696778
ctx.fail("{} parent interface not found".format(subinterface_name))
67706779

67716780
# Validate if parent is portchannel member
67726781
portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER')
6773-
if interface_is_in_portchannel(portchannel_member_table, interface_alias):
6782+
if interface_is_in_portchannel(portchannel_member_table, parent_intf):
67746783
ctx.fail("{} is configured as a member of portchannel. Cannot configure subinterface"
6775-
.format(interface_alias))
6784+
.format(parent_intf))
67766785

67776786
# Validate if parent is vlan member
67786787
vlan_member_table = config_db.get_table('VLAN_MEMBER')
6779-
if interface_is_in_vlan(vlan_member_table, interface_alias):
6788+
if interface_is_in_vlan(vlan_member_table, parent_intf):
67806789
ctx.fail("{} is configured as a member of vlan. Cannot configure subinterface"
6781-
.format(interface_alias))
6790+
.format(parent_intf))
67826791

67836792
sub_intfs = [k for k,v in config_db.get_table('VLAN_SUB_INTERFACE').items() if type(k) != tuple]
67846793
if subinterface_name in sub_intfs:

tests/intfutil_test.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def test_subintf_status(self):
209209
expected_output = (
210210
"Sub port interface Speed MTU Vlan Admin Type\n"
211211
"-------------------- ------- ----- ------ ------- --------------------\n"
212-
" Eth32.10 40G 9100 100 up 802.1q-encapsulation\n"
212+
" Eth36.10 10M 9100 100 up 802.1q-encapsulation\n"
213213
" Ethernet0.10 25G 9100 10 up 802.1q-encapsulation\n"
214214
" Po0001.10 40G 9100 100 up 802.1q-encapsulation"
215215
)
@@ -248,10 +248,10 @@ def test_single_subintf_status(self):
248248
expected_output = (
249249
"Sub port interface Speed MTU Vlan Admin Type\n"
250250
"-------------------- ------- ----- ------ ------- --------------------\n"
251-
" Eth32.10 40G 9100 100 up 802.1q-encapsulation"
251+
" Eth36.10 10M 9100 100 up 802.1q-encapsulation"
252252
)
253-
# Test 'intfutil status Eth32.10'
254-
output = subprocess.check_output('intfutil -c status -i Eth32.10', stderr=subprocess.STDOUT, shell=True, text=True)
253+
# Test 'intfutil status Eth36.10'
254+
output = subprocess.check_output('intfutil -c status -i Eth36.10', stderr=subprocess.STDOUT, shell=True, text=True)
255255
print(output, file=sys.stderr)
256256
self.assertEqual(output.strip(), expected_output)
257257

@@ -272,9 +272,9 @@ def test_single_subintf_status_verbose(self):
272272
expected_output = "Command: intfutil -c status -i Ethernet0.10"
273273
self.assertEqual(result.output.split('\n')[0], expected_output)
274274

275-
result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["Eth32.10", "--verbose"])
275+
result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["Eth36.10", "--verbose"])
276276
print(result.output, file=sys.stderr)
277-
expected_output = "Command: intfutil -c status -i Eth32.10"
277+
expected_output = "Command: intfutil -c status -i Eth36.10"
278278
self.assertEqual(result.output.split('\n')[0], expected_output)
279279

280280
result = self.runner.invoke(show.cli.commands["subinterfaces"].commands["status"], ["Po0001.10", "--verbose"])

tests/ip_config_test.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ def test_add_del_interface_valid_ipv4(self):
5454
assert result.exit_code == 0
5555
assert ('Ethernet0.10', '10.11.10.1/24') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
5656

57-
# config int ip add Eth32.10 32.11.10.1/24
58-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["add"], ["Eth32.10", "32.11.10.1/24"], obj=obj)
57+
# config int ip add Eth36.10 32.11.10.1/24
58+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["add"], ["Eth36.10", "32.11.10.1/24"], obj=obj)
5959
print(result.exit_code, result.output)
6060
assert result.exit_code == 0
61-
assert ('Eth32.10', '32.11.10.1/24') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
61+
assert ('Eth36.10', '32.11.10.1/24') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
6262

6363
# config int ip remove Ethernet64 10.10.10.1/24
6464
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet64", "10.10.10.1/24"], obj=obj)
@@ -72,11 +72,11 @@ def test_add_del_interface_valid_ipv4(self):
7272
assert result.exit_code != 0
7373
assert ('Ethernet0.10', '10.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
7474

75-
# config int ip remove Eth32.10 32.11.10.1/24
76-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth32.10", "32.11.10.1/24"], obj=obj)
75+
# config int ip remove Eth36.10 32.11.10.1/24
76+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "32.11.10.1/24"], obj=obj)
7777
print(result.exit_code, result.output)
7878
assert result.exit_code != 0
79-
assert ('Eth32.10', '32.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
79+
assert ('Eth36.10', '32.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
8080

8181
def test_add_interface_invalid_ipv4(self):
8282
db = Db()
@@ -129,10 +129,10 @@ def test_add_del_interface_valid_ipv6(self):
129129
assert result.exit_code == 0
130130
assert ('Ethernet0.10', '1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
131131

132-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["add"], ["Eth32.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
132+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["add"], ["Eth36.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
133133
print(result.exit_code, result.output)
134134
assert result.exit_code == 0
135-
assert ('Eth32.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
135+
assert ('Eth36.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
136136

137137
# config int ip remove Ethernet72 2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34
138138
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet72", "2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
@@ -145,10 +145,10 @@ def test_add_del_interface_valid_ipv6(self):
145145
assert result.exit_code != 0
146146
assert ('Ethernet0.10', '1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
147147

148-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth32.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
148+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
149149
print(result.exit_code, result.output)
150150
assert result.exit_code != 0
151-
assert ('Eth32.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
151+
assert ('Eth36.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
152152

153153
def test_del_interface_case_sensitive_ipv6(self):
154154
db = Db()

tests/loopback_action_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
show_ip_interfaces_loopback_action_output="""\
88
Interface Action
99
--------------- --------
10-
Eth32.10 drop
10+
Eth36.10 drop
1111
Ethernet0 forward
1212
PortChannel0001 drop
1313
Vlan3000 forward

tests/mock_tables/appl_db.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
"admin_status": "up",
189189
"vlan": "10"
190190
},
191-
"INTF_TABLE:Eth32.10": {
191+
"INTF_TABLE:Eth36.10": {
192192
"admin_status": "up",
193193
"vrf_name": "Vrf1",
194194
"vlan": "100"
@@ -202,15 +202,15 @@
202202
"family": "IPv4",
203203
"scope": "global"
204204
},
205-
"INTF_TABLE:Eth32.10|32.10.11.12/24": {
205+
"INTF_TABLE:Eth36.10|32.10.11.12/24": {
206206
"family": "IPv4",
207207
"scope": "global"
208208
},
209209
"INTF_TABLE:Po0001.10|10.10.11.12/24": {
210210
"family": "IPv4",
211211
"scope": "global"
212212
},
213-
"INTF_TABLE:Eth32.10|3210::12/126": {
213+
"INTF_TABLE:Eth36.10|3210::12/126": {
214214
"family": "IPv6",
215215
"scope": "global"
216216
},

tests/mock_tables/config_db.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -376,16 +376,16 @@
376376
"VLAN_SUB_INTERFACE|Ethernet0.10|10.11.12.13/24": {
377377
"NULL" : "NULL"
378378
},
379-
"VLAN_SUB_INTERFACE|Eth32.10": {
379+
"VLAN_SUB_INTERFACE|Eth36.10": {
380380
"admin_status": "up",
381381
"loopback_action": "drop",
382382
"vrf_name": "Vrf1",
383383
"vlan": "100"
384384
},
385-
"VLAN_SUB_INTERFACE|Eth32.10|32.10.11.12/24": {
385+
"VLAN_SUB_INTERFACE|Eth36.10|32.10.11.12/24": {
386386
"NULL" : "NULL"
387387
},
388-
"VLAN_SUB_INTERFACE|Eth32.10|3210::12/126": {
388+
"VLAN_SUB_INTERFACE|Eth36.10|3210::12/126": {
389389
"NULL" : "NULL"
390390
},
391391
"VLAN_SUB_INTERFACE|Po0001.10": {

tests/portchannel_test.py

+13
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,19 @@ def test_add_portchannel_member_which_has_ipaddress(self):
147147
assert result.exit_code != 0
148148
assert "Error: Ethernet0 has ip address configured" in result.output
149149

150+
def test_add_portchannel_member_which_has_subintf(self):
151+
runner = CliRunner()
152+
db = Db()
153+
obj = {'db':db.cfgdb}
154+
155+
# add a portchannel member with port which has ip-address
156+
result = runner.invoke(config.config.commands["portchannel"].commands["member"].commands["add"], ["PortChannel1001", "Ethernet36"], obj=obj)
157+
print(result.exit_code)
158+
print(result.output)
159+
assert result.exit_code != 0
160+
print(result.output)
161+
assert "Error: Ethernet36 has subinterfaces configured" in result.output
162+
150163
def test_add_portchannel_member_which_is_member_of_vlan(self):
151164
runner = CliRunner()
152165
db = Db()

tests/show_vrf_test.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_vrf_show(self):
2929
Vrf101 Ethernet0.10
3030
Vrf102 PortChannel0002
3131
Vlan40
32-
Eth32.10
32+
Eth36.10
3333
Vrf103 Ethernet4
3434
Loopback0
3535
Po0002.101
@@ -53,7 +53,7 @@ def test_vrf_bind_unbind(self):
5353
Vrf101 Ethernet0.10
5454
Vrf102 PortChannel0002
5555
Vlan40
56-
Eth32.10
56+
Eth36.10
5757
Vrf103 Ethernet4
5858
Loopback0
5959
Po0002.101
@@ -86,10 +86,10 @@ def test_vrf_bind_unbind(self):
8686
assert result.exit_code == 0
8787
assert 'PortChannel002' not in db.cfgdb.get_table('PORTCHANNEL_INTERFACE')
8888

89-
result = runner.invoke(config.config.commands["interface"].commands["vrf"].commands["unbind"], ["Eth32.10"], obj=obj)
89+
result = runner.invoke(config.config.commands["interface"].commands["vrf"].commands["unbind"], ["Eth36.10"], obj=obj)
9090
print(result.exit_code, result.output)
9191
assert result.exit_code == 0
92-
assert ('vrf_name', 'Vrf102') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')['Eth32.10']
92+
assert ('vrf_name', 'Vrf102') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')['Eth36.10']
9393

9494
result = runner.invoke(config.config.commands["interface"].commands["vrf"].commands["unbind"], ["Ethernet0.10"], obj=obj)
9595
print(result.exit_code, result.output)
@@ -114,7 +114,7 @@ def test_vrf_bind_unbind(self):
114114
Vrf101 Ethernet0.10
115115
Vrf102 PortChannel0002
116116
Vlan40
117-
Eth32.10
117+
Eth36.10
118118
Vrf103 Ethernet4
119119
Loopback0
120120
Po0002.101

tests/static_routes_test.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -403,16 +403,16 @@ def test_static_route_nexthop_subinterface(self):
403403
print(result.exit_code, result.output)
404404
assert not ('2.2.3.5/32') in db.cfgdb.get_table('STATIC_ROUTE')
405405

406-
# config route add prefix 2.2.3.5/32 nexthop dev Eth32.10
406+
# config route add prefix 2.2.3.5/32 nexthop dev Eth36.10
407407
result = runner.invoke(config.config.commands["route"].commands["add"], \
408-
["prefix", "2.2.3.5/32", "nexthop", "dev", "Eth32.10"], obj=obj)
408+
["prefix", "2.2.3.5/32", "nexthop", "dev", "Eth36.10"], obj=obj)
409409
print(result.exit_code, result.output)
410410
assert ('2.2.3.5/32') in db.cfgdb.get_table('STATIC_ROUTE')
411-
assert db.cfgdb.get_entry('STATIC_ROUTE', '2.2.3.5/32') == {'nexthop': '', 'blackhole': 'false', 'distance': '0', 'ifname': 'Eth32.10', 'nexthop-vrf': ''}
411+
assert db.cfgdb.get_entry('STATIC_ROUTE', '2.2.3.5/32') == {'nexthop': '', 'blackhole': 'false', 'distance': '0', 'ifname': 'Eth36.10', 'nexthop-vrf': ''}
412412

413-
# config route del prefix 2.2.3.5/32 nexthop dev Eth32.10
413+
# config route del prefix 2.2.3.5/32 nexthop dev Eth36.10
414414
result = runner.invoke(config.config.commands["route"].commands["del"], \
415-
["prefix", "2.2.3.5/32", "nexthop", "dev", "Eth32.10"], obj=obj)
415+
["prefix", "2.2.3.5/32", "nexthop", "dev", "Eth36.10"], obj=obj)
416416
print(result.exit_code, result.output)
417417
assert not ('2.2.3.5/32') in db.cfgdb.get_table('STATIC_ROUTE')
418418

tests/subintf_test.py

+21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
import show.main as show
88
from utilities_common.db import Db
99

10+
SUB_INTF_ON_LAG_MEMBER_ERR="""\
11+
Usage: add [OPTIONS] <subinterface_name> <vid>
12+
Try "add --help" for help.
13+
14+
Error: Ethernet32 is configured as a member of portchannel. Cannot configure subinterface
15+
"""
1016

1117
class TestSubinterface(object):
1218
@classmethod
@@ -141,6 +147,21 @@ def test_invalid_subintf_creation(self):
141147
print(result.exit_code, result.output)
142148
assert result.exit_code != 0
143149

150+
def test_subintf_creation_on_lag_member(self):
151+
runner = CliRunner()
152+
db = Db()
153+
obj = {'db':db.cfgdb}
154+
155+
result = runner.invoke(config.config.commands["subinterface"].commands["add"], ["Ethernet32.10"], obj=obj)
156+
print(result.exit_code, result.output)
157+
assert result.exit_code != 0
158+
assert(result.output == SUB_INTF_ON_LAG_MEMBER_ERR)
159+
160+
result = runner.invoke(config.config.commands["subinterface"].commands["add"], ["Eth32.20"], obj=obj)
161+
print(result.exit_code, result.output)
162+
assert result.exit_code != 0
163+
assert(result.output == SUB_INTF_ON_LAG_MEMBER_ERR)
164+
144165
def test_subintf_vrf_bind_unbind(self):
145166
runner = CliRunner()
146167
db = Db()

tests/vrf_input/config_db.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"vrf_name": "Vrf101",
44
"admin_status": "up"
55
},
6-
"VLAN_SUB_INTERFACE|Eth32.10": {
6+
"VLAN_SUB_INTERFACE|Eth36.10": {
77
"vrf_name": "Vrf102",
88
"admin_status": "up",
99
"vlan": "100"

0 commit comments

Comments
 (0)