-
Notifications
You must be signed in to change notification settings - Fork 1k
[pytest] PFCWD config test #1620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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" | ||
neethajohn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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 hidden or 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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.