forked from sonic-net/sonic-mgmt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_neighbor_mac_noptf.py
More file actions
326 lines (264 loc) · 13.1 KB
/
Copy pathtest_neighbor_mac_noptf.py
File metadata and controls
326 lines (264 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import logging
import pytest
import time
from tests.common.utilities import wait_until
from tests.common.helpers.assertions import pytest_assert
from tests.common.config_reload import config_reload
logger = logging.getLogger(__name__)
pytestmark = [
pytest.mark.topology('any')
]
REDIS_NEIGH_ENTRY_MAC_ATTR = "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS"
ROUTE_TABLE_NAME = 'ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY'
DEFAULT_ROUTE_NUM = 2
class TestNeighborMacNoPtf:
"""
Test handling of neighbor MAC in SONiC switch
"""
TEST_MAC = {
4: ["08:bc:27:af:cc:45", "08:bc:27:af:cc:47"],
6: ["08:bc:27:af:cc:65", "08:bc:27:af:cc:67"],
}
TEST_INTF = {
4: {"intfIp": "29.0.0.1/24", "NeighborIp": "29.0.0.2"},
6: {"intfIp": "fe00::1/64", "NeighborIp": "fe00::2"},
}
def count_routes(self, asichost, prefix):
# Counts routes in ASIC_DB with a given prefix
num = asichost.shell(
'{} ASIC_DB eval "return #redis.call(\'keys\', \'{}:{{\\"dest\\":\\"{}*\')" 0'
.format(asichost.sonic_db_cli, ROUTE_TABLE_NAME, prefix),
module_ignore_errors=True, verbose=True)['stdout']
return int(num)
def _get_bgp_routes_asic(self, asichost):
# Get the routes installed by BGP in ASIC_DB by filtering out all local routes installed on asic
localv6 = self.count_routes(asichost, "fc") + self.count_routes(asichost, "fe")
localv4 = self.count_routes(asichost, "10.") + self.count_routes(asichost, "192.168.0.")
# these routes are present only on multi asic device, on single asic platform they will be zero
internal = self.count_routes(asichost, "8.") + self.count_routes(asichost, "2603")
allroutes = self.count_routes(asichost, "")
logger.info("asic[{}] localv4 routes {} localv6 routes {} internalv4 {} allroutes {}"
.format(asichost.asic_index, localv4, localv6, internal, allroutes))
bgp_routes_asic = allroutes - localv6 - localv4 - internal - DEFAULT_ROUTE_NUM
return bgp_routes_asic
def _check_no_bgp_routes(self, duthost):
bgp_routes = 0
# Checks that there are no routes installed by BGP in ASIC_DB
# by filtering out all local routes installed on testbed
for asic in duthost.asics:
bgp_routes += self._get_bgp_routes_asic(asic)
return bgp_routes == 0
@pytest.fixture(scope="module", autouse=True)
def setupDutConfig(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname):
"""
Disabled BGP to reduce load on switch and restores DUT configuration after test completes
Args:
duthost (AnsibleHost): Device Under Test (DUT)
Returns:
None
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
if not duthost.get_facts().get("modular_chassis"):
duthost.command("sudo config bgp shutdown all")
if not wait_until(120, 2.0, 0, self._check_no_bgp_routes, duthost):
pytest.fail('BGP Shutdown Timeout: BGP route removal exceeded 120 seconds.')
yield
logger.info("Reload Config DB")
config_reload(duthost, config_source='config_db', safe_reload=True, check_intf_up_ports=True)
@pytest.fixture(params=[4, 6])
def ipVersion(self, request):
"""
Parameterized fixture for IP versions. This Fixture will run the test twice for both
IPv4 and IPv6
Args:
request: pytest request object
Retruns:
ipVersion (int): IP version to be used for testing
"""
yield request.param
@pytest.fixture(scope="module")
def routedInterfaces(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname):
"""
Find routed interface to test neighbor MAC functionality with
Args:
duthost (AnsibleHost): Device Under Test (DUT)
Retruns:
routedInterface (str): Routed interface used for testing
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
testRoutedInterface = {}
def find_routed_interface():
for asichost in duthost.asics:
intfStatus = asichost.show_interface(command="status")["ansible_facts"]["int_status"]
for intf, status in list(intfStatus.items()):
if "routed" in status["vlan"] and "up" in status["oper_state"]:
testRoutedInterface[asichost.asic_index] = intf
return testRoutedInterface
if not wait_until(120, 2, 0, find_routed_interface):
pytest.fail('Failed to find routed interface in 120 s')
yield testRoutedInterface
@pytest.fixture
def verifyOrchagentPresence(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname):
"""
Verify orchagent is running before and after the test is finished
Args:
duthost (AnsibleHost): Device Under Test (DUT)
Returns:
None
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
def verifyOrchagentRunningOrAssert(duthost):
"""
Verifyes that orchagent is running, asserts otherwise
Args:
duthost (AnsibleHost): Device Under Test (DUT)
"""
result = duthost.shell(argv=["pgrep", "orchagent"])
orchagent_pids = result['stdout'].splitlines()
pytest_assert(len(orchagent_pids) == duthost.num_asics(), "Orchagent is not running")
for pid in orchagent_pids:
pytest_assert(int(pid) > 0, "Orchagent is not running")
verifyOrchagentRunningOrAssert(duthost)
yield
verifyOrchagentRunningOrAssert(duthost)
def __updateNeighborIp(self, asichost, intf, ipVersion, macIndex, action=None):
"""
Update IP neighbor
Args:
asichost (SonicHost): Asic Under Test (DUT)
intf (str): Interface name
ipVersion (Fixture<int>): IP version
macIndex (int): test MAC index to be used
action (str): action to perform
Returns:
None
"""
neighborIp = self.TEST_INTF[ipVersion]["NeighborIp"]
neighborMac = self.TEST_MAC[ipVersion][macIndex]
logger.info("{0} neighbor {1} lladdr {2} for {3}".format(action, neighborIp, neighborMac, intf))
cmd = asichost.ip_cmd if "add" in action else "{0} -{1}".format(asichost.ip_cmd, ipVersion)
cmd += " neigh {0} {1} lladdr {2} dev {3}".format(action, neighborIp, neighborMac, intf)
logger.info(cmd)
asichost.shell(cmd)
def __updateInterfaceIp(self, asichost, intf, ipVersion, action=None):
"""
Update interface IP
Args:
asichost (SonicHost): Asic Under Test (DUT)
intf (str): Interface name
ipVersion (Fixture<int>): IP version
action (str): action to perform
Returns:
None
"""
logger.info("{0} an ip entry '{1}' for {2}".format(action, self.TEST_INTF[ipVersion]["intfIp"], intf))
asichost.config_ip_intf(intf, self.TEST_INTF[ipVersion]["intfIp"], action)
@pytest.fixture(autouse=True)
def updateNeighborIp(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname,
enum_frontend_asic_index, routedInterfaces, ipVersion, verifyOrchagentPresence):
"""
Update Neighbor/Interface IP
Prepares the DUT for testing by adding IP to the test interface, add and update
the neighbor MAC 2 times.
Args:
duthost (AnsibleHost): Device Under Test (DUT)
routedInterface (Fixture<str>): test Interface name
ipVersion (Fixture<int>): IP version
verifyOrchagentPresence (Fixture): Make sure orchagent is running before and
after update takes place
Returns:
None
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
asichost = duthost.asic_instance(enum_frontend_asic_index)
routedInterface = routedInterfaces[asichost.asic_index]
self.__updateInterfaceIp(asichost, routedInterface, ipVersion, action="add")
self.__updateNeighborIp(asichost, routedInterface, ipVersion, 0, action="add")
self.__updateNeighborIp(asichost, routedInterface, ipVersion, 0, action="change")
self.__updateNeighborIp(asichost, routedInterface, ipVersion, 1, action="change")
time.sleep(2)
yield
self.__updateNeighborIp(asichost, routedInterface, ipVersion, 1, action="del")
self.__updateInterfaceIp(asichost, routedInterface, ipVersion, action="remove")
@pytest.fixture
def arpTableMac(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname,
enum_frontend_asic_index, ipVersion, updateNeighborIp):
"""
Retreive DUT ARP table MAC entry of neighbor IP
Args:
duthost (AnsibleHost): Device Under Test (DUT)
ipVersion (Fixture<int>): IP version
updateNeighborIp (Fixture<str>): test fixture that assign/update IP/neighbor MAC
Returns:
arpTableMac (str): ARP MAC entry of neighbor IP
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
asichost = duthost.asic_instance(enum_frontend_asic_index)
dutArpTable = asichost.switch_arptable()["ansible_facts"]["arptable"]
yield dutArpTable["v{0}".format(ipVersion)][self.TEST_INTF[ipVersion]["NeighborIp"]]["macaddress"]
@pytest.fixture
def redisNeighborMac(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname,
enum_frontend_asic_index, ipVersion, updateNeighborIp):
"""
Retreive DUT Redis MAC entry of neighbor IP
Args:
duthost (AnsibleHost): Device Under Test (DUT)
ipVersion (Fixture<int>): IP version
updateNeighborIp (Fixture<str>): test fixture that assign/update IP/neighbor MAC
Returns:
redisNeighborMac (str): Redis MAC entry of neighbor IP
"""
duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname]
asichost = duthost.asic_instance(enum_frontend_asic_index)
redis_cmd = "{} ASIC_DB KEYS \"ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY*\"".format(asichost.sonic_db_cli)
# Sometimes it may take longer than usual to update interface address, add neighbor, and also change
# neighbor MAC. Retry the validation of neighbor MAC to make test more robust.
retry = 0
maxRetry = 30
result = None
neighborMac = None
expectedMac = self.TEST_MAC[ipVersion][1]
while retry < maxRetry and neighborMac != expectedMac:
neighborKey = None
result = duthost.shell(redis_cmd)
for key in result["stdout_lines"]:
if self.TEST_INTF[ipVersion]["NeighborIp"] in key:
neighborKey = key
break
if neighborKey:
neighborKey = " '{}' {} ".format(
neighborKey,
REDIS_NEIGH_ENTRY_MAC_ATTR)
result = duthost.shell("{} ASIC_DB HGET {}".format(asichost.sonic_db_cli, neighborKey))
neighborMac = result['stdout_lines'][0].lower()
# Since neighbor MAC is also changed/updated, check if all the updates have been processed already.
# Stop retry if the neighbor MAC in ASIC_DB is what we expect.
if neighborMac == expectedMac:
logger.info("Verified MAC of neighbor {} after {} retries".format(
self.TEST_INTF[ipVersion]["NeighborIp"], retry))
break
logger.info("Failed to verify MAC of neighbor {}. Retry cnt: {}".format(
self.TEST_INTF[ipVersion]["NeighborIp"], retry))
retry += 1
time.sleep(2)
pytest_assert(neighborMac, "Neighbor key NOT found in Redis DB, Redis db Output '{0}'".format(result["stdout"]))
yield neighborMac
def testNeighborMacNoPtf(self, ipVersion, arpTableMac, redisNeighborMac):
"""
Neighbor MAC test
Args:
ipVersion (Fixture<int>): IP version
arpTableMac (Fixture<str>): ARP MAC entry of neighbor IP
redisNeighborMac (Fixture<str>): Redis MAC entry of neighbor IP
Returns:
None
"""
testMac = self.TEST_MAC[ipVersion][1]
pytest_assert(
arpTableMac.lower() == testMac,
"Failed to find test MAC address '{0}' in ARP table '{1}'".format(testMac, arpTableMac)
)
pytest_assert(
redisNeighborMac.lower() == testMac,
"Failed to find test MAC address '{0}' in Redis Neighbor table '{1}'".format(testMac, redisNeighborMac)
)