Skip to content

Commit

Permalink
support for not all DUT ports connected to a fanout switch (#2517)
Browse files Browse the repository at this point in the history
What is the motivation for this PR?
Currently, it is required that all ports on DUT are in use and are connected to a fanout. However, there is a need to be able to run tests where all ports are not in use. Specifically, when dealing with

New Sonic devices
Sonic device with a front panel port used for in-band management
Chassis with lots of ports and multiple asics, were every port on every asic is not required to be covered
Chassis as a DUT, where the number of ports can be in hundreds
Expensive, high speed ports like 400G (hard to go from 400G down to 1/10G)
So, need to add support where not all DUT ports are connected to fanout and are thus not part of the testing.

How did you do it?
conn_graph_facts:

conn_graph_facts.py returns device_vlan_map_list. This used to be a dictionary with key being hostname and value being a list of vlanids for the fanout ports. Have modified this where the value instead of being a list of vlanids, it is a dictionary with key being the port_index and the value being the vlan id. This port_index is what gets put in the topology file. We get the port by looking at the host_port_vlans defined in the conn_graph for that device. This host_port_vlans has key being the Ethernet port (like Ethernet10) and value being a dictionary with key 'vlanlist' being a the list of fanout vlans. We check against all the ports 'vlanlist' to get the port on the DUT that connects to this fanout vlan, and then split on 'Ethernet' to get the port index.

For example - lets say on dut with hostname "dut1", we have port Ethernet10 connected to fanout w/ vlan 120, and Ethernet11 connected to fanout w/ vlan 121, then we would have:

  "host_port_vlans":
    {
      "Ethernet10": {
         "mode": "Access",
         "vlanids": "120",
         "vlanlist": [
            120
         ]
      },
      "Ethernet11": {
         "mode": "Access",
         "vlanids": "121",
         "vlanlist": [
            121
         ]
      }
   }

  "VlanList": [
     120,
     121
  ]
For vlan 120 in VlanList, we would iterate through host_port_vlans to find the port that has vlan 120 - in our case "Ethernet10". The port_index would then be "10". Similarly, for vlan 121, the port_index would be "Ethernet11".

So, returned device_vlan_map_list would be:

    "dut1" : {
       "10" : 120,
       "11" : 121
    }
vlan_port/kvm_port/mellanox_simx_port:

Updated to return (dut_fp_ports) a dictionary with key being the port index (same as in the topo file) and vlan being the port - instead of the just the list of ports.
bind/unbind vm_topology:

vlan_index is now a string in the dictionary of dut_fp_ports
updated regexp for checking valid vlan for multi-dut to be of the format '.@'
remove_dut_port.yml (bug fix):

set cmd to "remove" instead of "create" in vlan_port module call.
How did you verify/test it?
Tested against pizza box DUT with all DUT ports connected to a fanout, and also against another DUT where we have only 4 of the 52 ports connected to a different fanout.
  • Loading branch information
sanmalho-git authored Dec 28, 2020
1 parent 06688b6 commit d062c61
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 122 deletions.
37 changes: 36 additions & 1 deletion ansible/library/conn_graph_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from operator import itemgetter
from itertools import groupby
from collections import defaultdict
from natsort import natsorted
from ansible.module_utils.port_utils import get_port_alias_to_name_map
from ansible.module_utils.debug_utils import create_debug_file, print_debug_msg

DOCUMENTATION='''
Expand Down Expand Up @@ -275,6 +277,7 @@ def find_graph(hostnames):
filename = os.path.join(LAB_GRAPHFILE_PATH, fn)
lab_graph = Parse_Lab_Graph(filename)
lab_graph.parse_graph()
print_debug_msg(debug_fname, "For file %s, got hostnames %s" % (fn, lab_graph.devices))
if lab_graph.contains_hosts(hostnames):
print_debug_msg(debug_fname, ("Returning lab graph from conn graph file: %s for hosts %s" % (fn, hostnames)))
return lab_graph
Expand All @@ -287,6 +290,20 @@ def find_graph(hostnames):
lab_graph.parse_graph()
return lab_graph


def get_port_name_list(hwsku):
# Create a map of SONiC port name to physical port index
# Start by creating a list of all port names
port_alias_to_name_map = get_port_alias_to_name_map(hwsku)

# Create a map of SONiC port name to physical port index
# Start by creating a list of all port names
port_name_list = port_alias_to_name_map.values()
# Sort the list in natural order, because SONiC port names, when
# sorted in natural sort order, match the phyical port index order
port_name_list_sorted = natsorted(port_name_list)
return port_name_list_sorted

debug_fname = None

def main():
Expand Down Expand Up @@ -345,7 +362,25 @@ def main():
if host_vlan:
device_vlan_range.append(host_vlan["VlanRange"])
device_vlan_list.append(host_vlan["VlanList"])
device_vlan_map_list[hostname] = host_vlan["VlanList"]
port_vlans = lab_graph.get_host_port_vlans(hostname)
device_vlan_map_list[hostname] = {}

port_name_list_sorted = get_port_name_list(dev['HwSku'])
print_debug_msg(debug_fname,"For %s with hwsku %s, port_name_list is %s" % (hostname, dev['HwSku'], port_name_list_sorted))
for a_host_vlan in host_vlan["VlanList"]:
# Get the corresponding port for this vlan from the port vlan list for this hostname
found_port_for_vlan = False
for a_port in port_vlans:
if a_host_vlan in port_vlans[a_port]['vlanlist']:
if a_port in port_name_list_sorted:
port_index = port_name_list_sorted.index(a_port)
device_vlan_map_list[hostname][port_index] = a_host_vlan
found_port_for_vlan = True
break
else:
module.fail_json(msg="Did not find port for %s in the ports based on hwsku '%s' for host %s" % (a_port, dev['HwSku'], hostname))
if not found_port_for_vlan:
module.fail_json(msg="Did not find corresponding link for vlan %d in %s for host %s" % (a_host_vlan, port_vlans, hostname))
device_port_vlans.append(lab_graph.get_host_port_vlans(hostname))
results = {k: v for k, v in locals().items()
if (k.startswith("device_") and v)}
Expand Down
98 changes: 2 additions & 96 deletions ansible/library/minigraph_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ipaddr as ipaddress
from collections import defaultdict
from natsort import natsorted
from ansible.module_utils.port_utils import get_port_alias_to_name_map

from lxml import etree as ET
from lxml.etree import QName
Expand Down Expand Up @@ -465,102 +466,7 @@ def parse_xml(filename, hostname):

global port_alias_to_name_map

if hwsku == "Force10-S6000":
for i in range(0, 128, 4):
port_alias_to_name_map["fortyGigE0/%d" % i] = "Ethernet%d" % i
elif hwsku == "Force10-S6100":
for i in range(0, 4):
for j in range(0, 16):
port_alias_to_name_map["fortyGigE1/%d/%d" % (i+1, j+1)] = "Ethernet%d" % (i * 16 + j)
elif hwsku == "Force10-Z9100":
for i in range(0, 128, 4):
port_alias_to_name_map["hundredGigE1/%d" % (i/4 + 1)] = "Ethernet%d" % i
elif hwsku == "Arista-7050-QX32":
for i in range(1, 25):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
for i in range(25, 33):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7050-QX-32S":
for i in range(5, 29):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 5) * 4)
for i in range(29, 37):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 5) * 4)
elif hwsku == "Arista-7260CX3-C64" or hwsku == "Arista-7170-64C":
for i in range(1, 65):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7060CX-32S-C32" or hwsku == "Arista-7060CX-32S-Q32" or hwsku == "Arista-7060CX-32S-C32-T1" or hwsku == "Arista-7170-32CD-C32":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Mellanox-SN2700-D48C8":
# 50G ports
s50G_ports = [x for x in range(0, 24, 2)] + [x for x in range(40, 88, 2)] + [x for x in range(104, 128, 2)]

# 100G ports
s100G_ports = [x for x in range(24, 40, 4)] + [x for x in range(88, 104, 4)]

for i in s50G_ports:
alias = "etp%d" % (i / 4 + 1) + ("a" if i % 4 == 0 else "b")
port_alias_to_name_map[alias] = "Ethernet%d" % i
for i in s100G_ports:
alias = "etp%d" % (i / 4 + 1)
port_alias_to_name_map[alias] = "Ethernet%d" % i
elif hwsku == "Mellanox-SN2700" or hwsku == "ACS-MSN2700":
for i in range(1, 33):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7060CX-32S-D48C8":
# All possible breakout 50G port numbers:
all_ports = [ x for x in range(1, 33)]

# 100G ports
s100G_ports = [ x for x in range(7, 11) ]
s100G_ports += [ x for x in range(23, 27) ]

port_alias_to_name_map = port_alias_to_name_map_50G(all_ports, s100G_ports)
elif hwsku == "Arista-7260CX3-D108C8":
# All possible breakout 50G port numbers:
all_ports = [ x for x in range(1, 65)]

# 100G ports
s100G_ports = [ x for x in range(13, 21) ]

port_alias_to_name_map = port_alias_to_name_map_50G(all_ports, s100G_ports)
elif hwsku == "INGRASYS-S9100-C32":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "INGRASYS-S9100-C32" or hwsku == "INGRASYS-S9130-32X" or hwsku == "INGRASYS-S8810-32Q":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "INGRASYS-S8900-54XC":
for i in range(1, 49):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % (i - 1)
for i in range(49, 55):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 49) * 4 + 48)
elif hwsku == "INGRASYS-S8900-64XC":
for i in range(1, 49):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % (i - 1)
for i in range(49, 65):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 49) * 4 + 48)
elif hwsku == "Accton-AS7712-32X":
for i in range(1, 33):
port_alias_to_name_map["hundredGigE%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Celestica-DX010-C32":
for i in range(1, 33):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Seastone-DX010":
for i in range(1, 33):
port_alias_to_name_map["Eth%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Celestica-E1031-T48S4":
for i in range(1, 53):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1))
elif hwsku == "et6448m":
for i in range(0, 52):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i
elif hwsku == "newport":
for i in range(0, 256, 8):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i
else:
for i in range(0, 128, 4):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i
port_alias_to_name_map = get_port_alias_to_name_map(hwsku)

for child in root:
if child.tag == str(QName(ns, "DpgDec")):
Expand Down
115 changes: 115 additions & 0 deletions ansible/module_utils/port_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
def _port_alias_to_name_map_50G(all_ports, s100G_ports):
new_map = {}
# 50G ports
s50G_ports = list(set(all_ports) - set(s100G_ports))

for i in s50G_ports:
new_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
new_map["Ethernet%d/3" % i] = "Ethernet%d" % ((i - 1) * 4 + 2)

for i in s100G_ports:
new_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)

return new_map

def get_port_alias_to_name_map(hwsku):
port_alias_to_name_map = {}
if hwsku == "Force10-S6000":
for i in range(0, 128, 4):
port_alias_to_name_map["fortyGigE0/%d" % i] = "Ethernet%d" % i
elif hwsku == "Force10-S6100":
for i in range(0, 4):
for j in range(0, 16):
port_alias_to_name_map["fortyGigE1/%d/%d" % (i + 1, j + 1)] = "Ethernet%d" % (i * 16 + j)
elif hwsku == "Force10-Z9100":
for i in range(0, 128, 4):
port_alias_to_name_map["hundredGigE1/%d" % (i / 4 + 1)] = "Ethernet%d" % i
elif hwsku == "Arista-7050-QX32":
for i in range(1, 25):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
for i in range(25, 33):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7050-QX-32S":
for i in range(5, 29):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 5) * 4)
for i in range(29, 37):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 5) * 4)
elif hwsku == "Arista-7260CX3-C64" or hwsku == "Arista-7170-64C":
for i in range(1, 65):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7060CX-32S-C32" or hwsku == "Arista-7060CX-32S-Q32" or hwsku == "Arista-7060CX-32S-C32-T1" or hwsku == "Arista-7170-32CD-C32":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Mellanox-SN2700-D48C8":
# 50G ports
s50G_ports = [x for x in range(0, 24, 2)] + [x for x in range(40, 88, 2)] + [x for x in range(104, 128, 2)]

# 100G ports
s100G_ports = [x for x in range(24, 40, 4)] + [x for x in range(88, 104, 4)]

for i in s50G_ports:
alias = "etp%d" % (i / 4 + 1) + ("a" if i % 4 == 0 else "b")
port_alias_to_name_map[alias] = "Ethernet%d" % i
for i in s100G_ports:
alias = "etp%d" % (i / 4 + 1)
port_alias_to_name_map[alias] = "Ethernet%d" % i
elif hwsku == "Mellanox-SN2700" or hwsku == "ACS-MSN2700":
for i in range(1, 33):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Arista-7060CX-32S-D48C8":
# All possible breakout 50G port numbers:
all_ports = [x for x in range(1, 33)]

# 100G ports
s100G_ports = [x for x in range(7, 11)]
s100G_ports += [x for x in range(23, 27)]

port_alias_to_name_map = _port_alias_to_name_map_50G(all_ports, s100G_ports)
elif hwsku == "Arista-7260CX3-D108C8":
# All possible breakout 50G port numbers:
all_ports = [x for x in range(1, 65)]

# 100G ports
s100G_ports = [x for x in range(13, 21)]

port_alias_to_name_map = _port_alias_to_name_map_50G(all_ports, s100G_ports)
elif hwsku == "INGRASYS-S9100-C32":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "INGRASYS-S9100-C32" or hwsku == "INGRASYS-S9130-32X" or hwsku == "INGRASYS-S8810-32Q":
for i in range(1, 33):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "INGRASYS-S8900-54XC":
for i in range(1, 49):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % (i - 1)
for i in range(49, 55):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 49) * 4 + 48)
elif hwsku == "INGRASYS-S8900-64XC":
for i in range(1, 49):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % (i - 1)
for i in range(49, 65):
port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 49) * 4 + 48)
elif hwsku == "Accton-AS7712-32X":
for i in range(1, 33):
port_alias_to_name_map["hundredGigE%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Celestica-DX010-C32":
for i in range(1, 33):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Seastone-DX010":
for i in range(1, 33):
port_alias_to_name_map["Eth%d" % i] = "Ethernet%d" % ((i - 1) * 4)
elif hwsku == "Celestica-E1031-T48S4":
for i in range(1, 53):
port_alias_to_name_map["etp%d" % i] = "Ethernet%d" % ((i - 1))
elif hwsku == "et6448m":
for i in range(0, 52):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i
elif hwsku == "newport":
for i in range(0, 256, 8):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i
else:
for i in range(0, 128, 4):
port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i

return port_alias_to_name_map

6 changes: 4 additions & 2 deletions ansible/roles/vm_set/library/kvm_port.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def main():
module.fail_json(msg="failed to iflist dom %s" % vmname)

mgmt_port = None
fp_ports = []
fp_ports = {}
cur_fp_idx = 0

for l in output.split('\n'):
fds = re.split('\s+', l.lstrip())
Expand All @@ -46,7 +47,8 @@ def main():
if mgmt_port == None:
mgmt_port = fds[0]
else:
fp_ports.append(fds[0])
fp_ports[cur_fp_idx] = fds[0]
cur_fp_idx = cur_fp_idx + 1

if mgmt_port == None:
module.fail_json(msg="failed to find mgmt port")
Expand Down
4 changes: 2 additions & 2 deletions ansible/roles/vm_set/library/mellanox_simx_port.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ def main():
))

mgmt_port = None
fp_ports = []
fp_ports = {}

for i in range(1,33):
fp_ports.append("v000_p{}".format(i))
fp_ports[i] = "v000_p{}".format(i)

mgmt_port = "tap0"

Expand Down
21 changes: 8 additions & 13 deletions ansible/roles/vm_set/library/vlan_port.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/python

import itertools
import re
import sys
import time
Expand Down Expand Up @@ -62,11 +61,11 @@ def destroy_vlan_port(self, vlan_port):
return

def create_vlan_ports(self):
for vlan_id in self.vlan_ids:
for vlan_id in self.vlan_ids.values():
self.create_vlan_port(self.external_port, vlan_id)

def remove_vlan_ports(self):
for vlan_id in self.vlan_ids:
for vlan_id in self.vlan_ids.values():
vlan_port = "%s.%d" % (self.external_port, vlan_id)
self.destroy_vlan_port(vlan_port)

Expand Down Expand Up @@ -123,7 +122,7 @@ def main():
module = AnsibleModule(argument_spec=dict(
cmd=dict(required=True, choices=['create', 'remove', 'list']),
external_port=dict(required=True, type='str'),
vlan_ids=dict(required=True, type='list'),
vlan_ids=dict(required=True, type='dict'),
is_multi_duts=dict(required=False, type='bool', default=False),
))

Expand All @@ -132,15 +131,10 @@ def main():
vlan_ids = module.params['vlan_ids']
is_multi_duts = module.params['is_multi_duts']

_vlan_ids = vlan_ids
if is_multi_duts:
# flatten the list in the case of multi-DUTs
_vlan_ids = list(itertools.chain.from_iterable(_vlan_ids))
_vlan_ids.sort()

fp_ports = []
fp_ports = {}

vp = VlanPort(external_port, _vlan_ids)
vp = VlanPort(external_port, vlan_ids)

vp.up_external_port()
if cmd == "create":
Expand All @@ -150,12 +144,13 @@ def main():

fp_port_templ = external_port + ".%s"
if is_multi_duts:
fp_ports = []
fp_ports = {}
for dut_vlans in vlan_ids:
dut_vlans.sort()
fp_ports.append([fp_port_templ % vid for vid in dut_vlans])
else:
fp_ports = [fp_port_templ % vid for vid in vlan_ids]
for a_port_index, vid in vlan_ids.items():
fp_ports[a_port_index] = fp_port_templ % vid

module.exit_json(changed=False, ansible_facts={'dut_fp_ports': fp_ports})

Expand Down
Loading

0 comments on commit d062c61

Please sign in to comment.