-
Notifications
You must be signed in to change notification settings - Fork 714
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Pytest PFCWD config test Signed-off-by: Neetha John <nejo@microsoft.com> * Address LGTM Signed-off-by: Neetha John <nejo@microsoft.com> * Rename marker to syslog_marker Signed-off-by: Neetha John <nejo@microsoft.com> * Separate out verbose comments Signed-off-by: Neetha John <nejo@microsoft.com>
- Loading branch information
1 parent
4d1c2fd
commit 50a6bc1
Showing
6 changed files
with
619 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import logging | ||
import pytest | ||
from common.fixtures.conn_graph_facts import conn_graph_facts | ||
from files.pfcwd_helper import TrafficPorts, set_pfc_timers, select_test_ports | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
def pytest_addoption(parser): | ||
""" | ||
Command line args specific for the pfcwd test | ||
Args: | ||
parser: pytest parser object | ||
Returns: | ||
None | ||
""" | ||
parser.addoption('--warm-reboot', action='store', type=bool, default=False, | ||
help='Warm reboot needs to be enabled or not') | ||
|
||
@pytest.fixture(scope="module") | ||
def setup_pfc_test(duthost, ptfhost, conn_graph_facts): | ||
""" | ||
Sets up all the parameters needed for the PFC Watchdog tests | ||
Args: | ||
duthost: AnsibleHost instance for DUT | ||
ptfhost: AnsibleHost instance for PTF | ||
conn_graph_facts: fixture that contains the parsed topology info | ||
Yields: | ||
setup_info: dictionary containing pfc timers, generated test ports and selected test ports | ||
""" | ||
mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts'] | ||
port_list = mg_facts['minigraph_ports'].keys() | ||
ports = (' ').join(port_list) | ||
neighbors = conn_graph_facts['device_conn'] | ||
dut_facts = duthost.setup()['ansible_facts'] | ||
dut_eth0_ip = dut_facts['ansible_eth0']['ipv4']['address'] | ||
dut_eth0_mac = dut_facts['ansible_eth0']['macaddress'] | ||
vlan_nw = None | ||
|
||
if mg_facts['minigraph_vlans']: | ||
# gather all vlan specific info | ||
vlan_addr = mg_facts['minigraph_vlan_interfaces'][0]['addr'] | ||
vlan_prefix = mg_facts['minigraph_vlan_interfaces'][0]['prefixlen'] | ||
vlan_dev = mg_facts['minigraph_vlan_interfaces'][0]['attachto'] | ||
vlan_ips = duthost.get_ip_in_range(num=1, prefix="{}/{}".format(vlan_addr, vlan_prefix), exclude_ips=[vlan_addr])['ansible_facts']['generated_ips'] | ||
vlan_nw = vlan_ips[0].split('/')[0] | ||
|
||
# set unique MACS to PTF interfaces | ||
ptfhost.script("./scripts/change_mac.sh") | ||
|
||
duthost.shell("ip route flush {}/32".format(vlan_nw)) | ||
duthost.shell("ip route add {}/32 dev {}".format(vlan_nw, vlan_dev)) | ||
|
||
# build the port list for the test | ||
tp_handle = TrafficPorts(mg_facts, neighbors, vlan_nw) | ||
test_ports = tp_handle.build_port_list() | ||
# select a subset of ports from the generated port list | ||
selected_ports = select_test_ports(test_ports) | ||
|
||
setup_info = { 'test_ports': test_ports, | ||
'selected_test_ports': selected_ports, | ||
'pfc_timers' : set_pfc_timers() | ||
} | ||
|
||
# set poll interval | ||
duthost.command("pfcwd interval {}".format(setup_info['pfc_timers']['pfc_wd_poll_time'])) | ||
|
||
yield setup_info | ||
|
||
logger.info("--- Starting Pfcwd ---") | ||
duthost.command("pfcwd start_default") |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
import datetime | ||
import ipaddress | ||
|
||
class TrafficPorts(object): | ||
""" Generate a list of ports needed for the PFC Watchdog test""" | ||
def __init__(self, mg_facts, neighbors, vlan_nw): | ||
""" | ||
Args: | ||
mg_facts (dict): parsed minigraph info | ||
neighbors (list): 'device_conn' info from connection graph facts | ||
vlan_nw (string): ip in the vlan range specified in the DUT | ||
""" | ||
self.mg_facts = mg_facts | ||
self.bgp_info = self.mg_facts['minigraph_bgp'] | ||
self.port_idx_info = self.mg_facts['minigraph_port_indices'] | ||
self.pc_info = self.mg_facts['minigraph_portchannels'] | ||
self.vlan_info = self.mg_facts['minigraph_vlans'] | ||
self.neighbors = neighbors | ||
self.vlan_nw = vlan_nw | ||
self.test_ports = dict() | ||
self.pfc_wd_rx_port = None | ||
self.pfc_wd_rx_port_addr = None | ||
self.pfc_wd_rx_neighbor_addr = None | ||
self.pfc_wd_rx_port_id = None | ||
|
||
def build_port_list(self): | ||
""" | ||
Generate a list of ports to be used for the test | ||
For T0 topology, the port list is built parsing the portchannel and vlan info and for T1, | ||
port list is constructed from the interface info | ||
""" | ||
if self.mg_facts['minigraph_interfaces']: | ||
self.parse_intf_list() | ||
elif self.mg_facts['minigraph_portchannels']: | ||
self.parse_pc_list() | ||
if self.mg_facts['minigraph_vlans']: | ||
self.test_ports.update(self.parse_vlan_list()) | ||
return self.test_ports | ||
|
||
def parse_intf_list(self): | ||
""" | ||
Built the port info from the ports in 'minigraph_interfaces' | ||
The constructed port info is a dict with a port as the key (transmit port) and value contains | ||
all the info associated with this port (its fanout neighbor, receive port, receive ptf id, | ||
transmit ptf id, neighbor addr etc). The first port in the list is assumed to be the Rx port. | ||
The rest of the ports will use this port as the Rx port while populating their dict | ||
info. The selected Rx port when used as a transmit port will use the next port in | ||
the list as its associated Rx port | ||
""" | ||
pfc_wd_test_port = None | ||
first_pair = False | ||
for intf in self.mg_facts['minigraph_interfaces']: | ||
if ipaddress.ip_address(unicode(intf['addr'])).version != 4: | ||
continue | ||
# first port | ||
if not self.pfc_wd_rx_port: | ||
self.pfc_wd_rx_port = intf['attachto'] | ||
self.pfc_wd_rx_port_addr = intf['addr'] | ||
self.pfc_wd_rx_port_id = self.port_idx_info[self.pfc_wd_rx_port] | ||
elif not pfc_wd_test_port: | ||
# second port | ||
first_pair = True | ||
|
||
# populate info for all ports except the first one | ||
if first_pair or pfc_wd_test_port: | ||
pfc_wd_test_port = intf['attachto'] | ||
pfc_wd_test_port_addr = intf['addr'] | ||
pfc_wd_test_port_id = self.port_idx_info[pfc_wd_test_port] | ||
pfc_wd_test_neighbor_addr = None | ||
|
||
for item in self.bgp_info: | ||
if ipaddress.ip_address(unicode(item['addr'])).version != 4: | ||
continue | ||
if not self.pfc_wd_rx_neighbor_addr and item['peer_addr'] == self.pfc_wd_rx_port_addr: | ||
self.pfc_wd_rx_neighbor_addr = item['addr'] | ||
if item['peer_addr'] == pfc_wd_test_port_addr: | ||
pfc_wd_test_neighbor_addr = item['addr'] | ||
|
||
self.test_ports[pfc_wd_test_port] = {'test_neighbor_addr': pfc_wd_test_neighbor_addr, | ||
'rx_port': [self.pfc_wd_rx_port], | ||
'rx_neighbor_addr': self.pfc_wd_rx_neighbor_addr, | ||
'peer_device': self.neighbors[pfc_wd_test_port]['peerdevice'], | ||
'test_port_id': pfc_wd_test_port_id, | ||
'rx_port_id': [self.pfc_wd_rx_port_id], | ||
'test_port_type': 'interface' | ||
} | ||
# populate info for the first port | ||
if first_pair: | ||
self.test_ports[self.pfc_wd_rx_port] = {'test_neighbor_addr': self.pfc_wd_rx_neighbor_addr, | ||
'rx_port': [pfc_wd_test_port], | ||
'rx_neighbor_addr': pfc_wd_test_neighbor_addr, | ||
'peer_device': self.neighbors[self.pfc_wd_rx_port]['peerdevice'], | ||
'test_port_id': self.pfc_wd_rx_port_id, | ||
'rx_port_id': [pfc_wd_test_port_id], | ||
'test_port_type': 'interface' | ||
} | ||
|
||
first_pair = False | ||
|
||
def parse_pc_list(self): | ||
""" | ||
Built the port info from the ports in portchannel | ||
The constructed port info is a dict with a port as the key (transmit port) and value contains | ||
all the info associated with this port (its fanout neighbor, receive ports, receive | ||
ptf ids, transmit ptf ids, neighbor portchannel addr, its own portchannel addr etc). | ||
The first port in the list is assumed to be the Rx port. The rest | ||
of the ports will use this port as the Rx port while populating their dict | ||
info. The selected Rx port when used as a transmit port will use the next port in | ||
the list as its associated Rx port | ||
""" | ||
pfc_wd_test_port = None | ||
first_pair = False | ||
for item in self.mg_facts['minigraph_portchannel_interfaces']: | ||
if ipaddress.ip_address(unicode(item['addr'])).version != 4: | ||
continue | ||
pc = item['attachto'] | ||
# first port | ||
if not self.pfc_wd_rx_port: | ||
self.pfc_wd_rx_portchannel = pc | ||
self.pfc_wd_rx_port = self.pc_info[pc]['members'] | ||
self.pfc_wd_rx_port_addr = item['addr'] | ||
self.pfc_wd_rx_port_id = [self.port_idx_info[port] for port in self.pfc_wd_rx_port] | ||
elif not pfc_wd_test_port: | ||
# second port | ||
first_pair = True | ||
|
||
# populate info for all ports except the first one | ||
if first_pair or pfc_wd_test_port: | ||
pfc_wd_test_portchannel = pc | ||
pfc_wd_test_port = self.pc_info[pc]['members'] | ||
pfc_wd_test_port_addr = item['addr'] | ||
pfc_wd_test_port_id = [self.port_idx_info[port] for port in pfc_wd_test_port] | ||
pfc_wd_test_neighbor_addr = None | ||
|
||
for bgp_item in self.bgp_info: | ||
if ipaddress.ip_address(unicode(bgp_item['addr'])).version != 4: | ||
continue | ||
if not self.pfc_wd_rx_neighbor_addr and bgp_item['peer_addr'] == self.pfc_wd_rx_port_addr: | ||
self.pfc_wd_rx_neighbor_addr = bgp_item['addr'] | ||
if bgp_item['peer_addr'] == pfc_wd_test_port_addr: | ||
pfc_wd_test_neighbor_addr = bgp_item['addr'] | ||
|
||
for port in pfc_wd_test_port: | ||
self.test_ports[port] = {'test_neighbor_addr': pfc_wd_test_neighbor_addr, | ||
'rx_port': self.pfc_wd_rx_port, | ||
'rx_neighbor_addr': self.pfc_wd_rx_neighbor_addr, | ||
'peer_device': self.neighbors[port]['peerdevice'], | ||
'test_port_id': self.port_idx_info[port], | ||
'rx_port_id': self.pfc_wd_rx_port_id, | ||
'test_portchannel_members': pfc_wd_test_port_id, | ||
'test_port_type': 'portchannel' | ||
} | ||
# populate info for the first port | ||
if first_pair: | ||
for port in self.pfc_wd_rx_port: | ||
self.test_ports[port] = {'test_neighbor_addr': self.pfc_wd_rx_neighbor_addr, | ||
'rx_port': pfc_wd_test_port, | ||
'rx_neighbor_addr': pfc_wd_test_neighbor_addr, | ||
'peer_device': self.neighbors[port]['peerdevice'], | ||
'test_port_id': self.port_idx_info[port], | ||
'rx_port_id': pfc_wd_test_port_id, | ||
'test_portchannel_members': self.pfc_wd_rx_port_id, | ||
'test_port_type': 'portchannel' | ||
} | ||
|
||
first_pair = False | ||
|
||
def parse_vlan_list(self): | ||
""" | ||
Add vlan specific port info to the already populated port info dict. | ||
Each vlan interface will be the key and value contains all the info associated with this port | ||
(receive fanout neighbor, receive port receive ptf id, transmit ptf id, neighbor addr etc). | ||
Args: | ||
None | ||
Returns: | ||
temp_ports (dict): port info constructed from the vlan interfaces | ||
""" | ||
temp_ports = dict() | ||
vlan_members = self.vlan_info[self.vlan_info.keys()[0]]['members'] | ||
for item in vlan_members: | ||
temp_ports[item] = {'test_neighbor_addr': self.vlan_nw, | ||
'rx_port': self.pfc_wd_rx_port, | ||
'rx_neighbor_addr': self.pfc_wd_rx_neighbor_addr, | ||
'peer_device': self.neighbors[item]['peerdevice'], | ||
'test_port_id': self.port_idx_info[item], | ||
'rx_port_id': self.pfc_wd_rx_port_id, | ||
'test_port_type': 'vlan' | ||
} | ||
|
||
return temp_ports | ||
|
||
def set_pfc_timers(): | ||
""" | ||
Set PFC timers | ||
Args: | ||
None | ||
Returns: | ||
pfc_timers (dict) | ||
""" | ||
pfc_timers = {'pfc_wd_detect_time': 400, | ||
'pfc_wd_restore_time': 400, | ||
'pfc_wd_restore_time_large': 3000, | ||
'pfc_wd_poll_time': 400 | ||
} | ||
return pfc_timers | ||
|
||
|
||
def select_test_ports(test_ports): | ||
""" | ||
Select a subset of ports from the generated port info | ||
Args: | ||
test_ports (dict): Constructed port info | ||
Returns: | ||
selected_ports (dict): random port info or set of ports matching seed | ||
""" | ||
selected_ports = dict() | ||
seed = int(datetime.datetime.today().day) | ||
for key, value in test_ports.items(): | ||
if (int(value['test_port_id']) % 15) == (seed % 15): | ||
selected_ports.update({key:value}) | ||
|
||
if not selected_ports: | ||
random_port = test_ports.keys()[0] | ||
selected_ports[random_port] = test_ports[random_port] | ||
|
||
return selected_ports |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
r, ".* Port counter .* not implemented" | ||
r, ".* Port counter .* not supported" | ||
r, ".* Invalid port counter .*" | ||
r, ".* Unknown.*" | ||
r, ".* SAI_STATUS_ATTR_NOT_SUPPORT.*" | ||
r, ".* snmp.*" | ||
r, ".* Trying to remove nonexisting queue from flex counter .*" | ||
r, ".* ERR ntpd.*routing socket reports: No buffer space available.*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"pfc_wd_fwd_action": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 4000, | ||
"pfc_wd_restoration_time": 5000 | ||
}, | ||
"pfc_wd_invalid_action": { | ||
"pfc_wd_action": "invalid", | ||
"pfc_wd_detection_time": 4000, | ||
"pfc_wd_restoration_time": 5000 | ||
}, | ||
"pfc_wd_invalid_detect_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": "400a", | ||
"pfc_wd_restoration_time": 5000 | ||
}, | ||
"pfc_wd_low_detect_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 40, | ||
"pfc_wd_restoration_time": 5000 | ||
}, | ||
"pfc_wd_high_detect_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 4000000, | ||
"pfc_wd_restoration_time": 5000 | ||
}, | ||
"pfc_wd_invalid_restore_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 4000, | ||
"pfc_wd_restoration_time": "500c" | ||
}, | ||
"pfc_wd_low_restore_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 4000, | ||
"pfc_wd_restoration_time": 50 | ||
}, | ||
"pfc_wd_high_restore_time": { | ||
"pfc_wd_action": "forward", | ||
"pfc_wd_detection_time": 4000, | ||
"pfc_wd_restoration_time": 50000000 | ||
} | ||
} |
Oops, something went wrong.