From 006f5ae65008d695ae0721919176daf57095a6a6 Mon Sep 17 00:00:00 2001 From: Saravanan Sellappa Date: Tue, 30 Mar 2021 23:05:06 -0700 Subject: [PATCH] changes to support fabric asic in gen-mg and chassis TestbedProcessing 1. Support chassis, multi-duts scenarios in TestbedProcessing. When testbed.yaml file contains cardtype=Linecard or cardtype=supervisor, TestbedProcessing will add that to lab, veos inventory files. When dut is multi-dut, TestbedProcessing will convert the dut type list to string. 2. Support fabric_info generation in fabric_info.py when the num_asic > 0 and cardtype is supervisor. 3. Use the fabric_info in minigraph templates to generated the fabric asic info 4. Use variable name cardtype instead of type in ansible, dut_utils.py and minigraph templates to be more explicit 5. allow t2 as a topo in testbed.py --- ansible/TestbedProcessing.py | 78 ++++++++++++++++++--- ansible/config_sonic_basedon_testbed.yml | 12 +++- ansible/library/conn_graph_facts.py | 4 ++ ansible/library/fabric_info.py | 74 +++++++++++++++++++ ansible/library/port_alias.py | 9 ++- ansible/module_utils/port_utils.py | 4 ++ ansible/roles/vm_set/library/vm_topology.py | 2 + ansible/templates/minigraph_cpg.j2 | 4 +- ansible/templates/minigraph_device.j2 | 2 +- ansible/templates/minigraph_dpg.j2 | 6 +- ansible/templates/minigraph_dpg_asic.j2 | 36 ++++++++++ ansible/templates/minigraph_png.j2 | 7 ++ ansible/templates/minigraph_template.j2 | 4 +- tests/common/helpers/dut_utils.py | 4 +- tests/common/testbed.py | 2 +- 15 files changed, 225 insertions(+), 23 deletions(-) create mode 100644 ansible/library/fabric_info.py diff --git a/ansible/TestbedProcessing.py b/ansible/TestbedProcessing.py index 52fbcbc55d12..731ecb591de9 100644 --- a/ansible/TestbedProcessing.py +++ b/ansible/TestbedProcessing.py @@ -164,7 +164,7 @@ def makeVMHostCreds(data, outfile): error handling: checks if attribute values are None type or string "None" """ def makeSonicLabDevices(data, outfile): - csv_columns = "Hostname,ManagementIp,HwSku,Type" + csv_columns = "Hostname,ManagementIp,HwSku,Type,CardType" topology = data csv_file = outfile @@ -175,8 +175,8 @@ def makeSonicLabDevices(data, outfile): hostname = device managementIP = str(deviceDetails.get("ansible").get("ansible_host")) hwsku = deviceDetails.get("hwsku") - devType = deviceDetails.get("device_type") - + devType = deviceDetails.get("device_type") #DevSonic, server, FanoutRoot etc + cardType = deviceDetails.get("cardtype") #supervisor, Linecard etc # catch empty values if not managementIP: managementIP = "" @@ -184,8 +184,10 @@ def makeSonicLabDevices(data, outfile): hwsku = "" if not devType: devType = "" + if not cardType: + cardType = "" - row = hostname + "," + managementIP + "," + hwsku + "," + devType + row = hostname + "," + managementIP + "," + hwsku + "," + devType + "," + cardType f.write(row + "\n") except IOError: print("I/O error: makeSonicLabDevices") @@ -240,6 +242,11 @@ def makeTestbed(data, outfile): ptf = "" if not comment: comment = "" + # dut is a list for multi-dut testbed, convert it to string + if type(dut) is not str: + dut = dut.__str__() + dut = dut.replace(",", ";") + dut = dut.replace(" ", "") row = confName + "," + groupName + "," + topo + "," + ptf_image_name + "," + ptf + "," + ptf_ip + "," + ptf_ipv6 + ","+ server + "," + vm_base + "," + dut + "," + comment f.write(row + "\n") @@ -265,6 +272,8 @@ def makeSonicLabLinks(data, outfile): for key, item in topology.items(): startDevice = key interfacesDetails = item.get("interfaces") + if not interfacesDetails: + continue for startPort, element in interfacesDetails.items(): startPort = startPort @@ -377,45 +386,87 @@ def makeLab(data, devices, testbed, outfile): toWrite.write("[" + key + "]\n") for host in value.get("host"): entry = host + dev = devices.get(host.lower()) if "ptf" in key: try: #get ansible host - ansible_host = testbed.get(host).get("ansible").get("ansible_host") + ansible_host = dev.get("ansible").get("ansible_host") entry += "\tansible_host=" + ansible_host.split("/")[0] except: print("\t\t" + host + ": ansible_host not found") if ansible_host: try: # get ansible ssh username - ansible_ssh_user = testbed.get(host.lower()).get("ansible").get("ansible_ssh_user") + ansible_ssh_user = dev.get("ansible").get("ansible_ssh_user") entry += "\tansible_ssh_user=" + ansible_ssh_user except: print("\t\t" + host + ": ansible_ssh_user not found") try: # get ansible ssh pass - ansible_ssh_pass = testbed.get(host.lower()).get("ansible").get("ansible_ssh_pass") + ansible_ssh_pass = dev.get("ansible").get("ansible_ssh_pass") entry += "\tansible_ssh_pass=" + ansible_ssh_pass except: print("\t\t" + host + ": ansible_ssh_pass not found") else: #not ptf container try: #get ansible host - ansible_host = devices.get(host.lower()).get("ansible").get("ansible_host") + ansible_host = dev.get("ansible").get("ansible_host") entry += "\tansible_host=" + ansible_host.split("/")[0] except: print("\t\t" + host + ": ansible_host not found") if ansible_host: try: # get ansible ssh username - ansible_ssh_user = devices.get(host.lower()).get("ansible").get("ansible_ssh_user") + ansible_ssh_user = dev.get("ansible").get("ansible_ssh_user") entry += "\tansible_ssh_user=" + ansible_ssh_user except: print("\t\t" + host + ": ansible_ssh_user not found") try: # get ansible ssh pass - ansible_ssh_pass = devices.get(host.lower()).get("ansible").get("ansible_ssh_pass") + ansible_ssh_pass = dev.get("ansible").get("ansible_ssh_pass") entry += "\tansible_ssh_pass=" + ansible_ssh_pass except: print("\t\t" + host + ": ansible_ssh_pass not found") + try: #get hwsku + hwsku = dev.get("hwsku") + if hwsku is not None: + entry += "\thwsku=" + hwsku + except: + print("\t\t" + host + ": hwsku not found") + + try: #get cardtype + cardtype = dev.get("cardtype") + if cardtype is not None: + entry += "\tcardtype=" + cardtype + except: + print("\t\t" + host + ": cardtype not found") + + try: #get num_asics + num_asic = dev.get("num_asic") + if num_asic is not None: + entry += "\tnum_asic=" + str( num_asic ) + except: + print("\t\t" + host + " num_asic not found") + + try: #get frontend_asics + frontend_asics = dev.get("frontend_asics") + if frontend_asics is not None: + entry += "\tfrontend_asics=" + frontend_asics.__str__() + except: + print("\t\t" + host + ": frontend_asics not found") + + try: #get asics_host_ip + asics_host_ip = dev.get("asics_host_ip") + if asics_host_ip is not None: + entry += " \tasics_host_ip=" + str( asics_host_ip ) + except: + print("\t\t" + host + " asics_host_ip not found") + + try: #get asics_host_ipv6 + asics_host_ipv6 = dev.get("asics_host_ipv6") + if asics_host_ipv6 is not None: + entry += "\tasics_host_ipv6=" + str( asics_host_ipv6 ) + except: + print("\t\t" + host + " asics_host_ipv6 not found") toWrite.write(entry + "\n") toWrite.write("\n") @@ -455,8 +506,13 @@ def makeVeos(data, veos, devices, outfile): entry = host try: - ansible_host = devices.get(host.lower()).get("ansible").get("ansible_host") + dev = devices.get(host.lower()) + ansible_host = dev.get("ansible").get("ansible_host") entry += "\tansible_host=" + ansible_host.split("/")[0] + if dev.get("device_type") == "DevSonic": + entry += "\ttype=" + dev.get("type") + entry += "\thwsku=" + dev.get("hwsku") + entry += "\tcardtype=" + dev.get("cardtype") except: try: ansible_host = veos.get(key).get(host).get("ansible_host") diff --git a/ansible/config_sonic_basedon_testbed.yml b/ansible/config_sonic_basedon_testbed.yml index 6820de106de7..d607511c67a7 100644 --- a/ansible/config_sonic_basedon_testbed.yml +++ b/ansible/config_sonic_basedon_testbed.yml @@ -71,14 +71,22 @@ delegate_to: localhost - name: find interface name mapping and individual interface speed if defined from dut - port_alias: hwsku="{{ hwsku }}" + port_alias: hwsku="{{ hwsku }}" cardtype="{{ cardtype }}" when: deploy is defined and deploy|bool == true - name: find interface name mapping and individual interface speed if defined with local data - port_alias: hwsku="{{ hwsku }}" num_asic="{{ num_asics }}" + port_alias: hwsku="{{ hwsku }}" num_asic="{{ num_asics }}" cardtype="{{ cardtype }}" delegate_to: localhost when: deploy is not defined or deploy|bool == false + - name: find and generate ASIC infomation + fabric_info: + hwsku: "{{ hwsku }}" + num_asic: "{{ num_asic }}" + asics_host_pfx: "{{ asics_host_ip }}" + asics_host_pfx6: "{{ asics_host_ipv6 }}" + when: deploy is defined and deploy|bool == true + - name: find all enabled host_interfaces set_fact: host_if_indexes: "{{ vm_topo_config['host_interfaces_by_dut'][dut_index|int] | difference(vm_topo_config['disabled_host_interfaces_by_dut'][dut_index|int]) }}" diff --git a/ansible/library/conn_graph_facts.py b/ansible/library/conn_graph_facts.py index bd8cafb5bbcc..fb26e503e920 100755 --- a/ansible/library/conn_graph_facts.py +++ b/ansible/library/conn_graph_facts.py @@ -163,8 +163,12 @@ def parse_graph(self): deviceinfo[hostname] = {} hwsku = dev.attrib['HwSku'] devtype = dev.attrib['Type'] + cardtype = "Linecard" + if 'CardType' in dev.attrib: + cardtype = dev.attrib['CardType'] deviceinfo[hostname]['HwSku'] = hwsku deviceinfo[hostname]['Type'] = devtype + deviceinfo[hostname]['CardType'] = cardtype self.links[hostname] = {} devicel2info = {} devicel3s = self.root.find(self.dpgtag).findall('DevicesL3Info') diff --git a/ansible/library/fabric_info.py b/ansible/library/fabric_info.py new file mode 100644 index 000000000000..56458c8cfc8c --- /dev/null +++ b/ansible/library/fabric_info.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +import os + +try: + from sonic_py_common import multi_asic +except ImportError: + print("Failed to import multi_asic") + +DOCUMENTATION = ''' +module: fabric_info.py +Ansible_version_added: 2.0.0.2 +short_description: Find SONiC Fabric ASIC inforamtion if applicable for the DUT +Description: + When the testbed has Fabric ASICs, this module helps to collect that information + which helps in generating the minigraph + Input: + hwsku num_asic + + Return Ansible_facts: + fabric_info: SONiC Fabric ASIC information + +''' + +EXAMPLES = ''' + - name: get Fabric ASIC info + fabric_info: hwsku='ACS-MSN2700' num_asic=1 +''' + +RETURN = ''' + ansible_facts{ + fabric_info: [{'asicname': 'ASIC0', 'ip_prefix': '10.1.0.33/32', 'ip6_prefix': 'FC00:1::33/128'}, + {'asicname': 'ASIC1', 'ip_prefix': '10.1.0.34/32', 'ip6_prefix': 'FC00:1::34/128'}] + } +''' + +def main(): + module = AnsibleModule( + argument_spec=dict( + hwsku=dict(required=True, type='str'), + num_asic=dict(type='str', required=False), + asics_host_pfx=dict(type='str', required=False), + asics_host_pfx6=dict(type='str', required=False) + ), + supports_check_mode=True + ) + m_args = module.params + try: + fabric_info = [] + # num_asic may not be present for fixed systems which have no Fabric ASIC. + # Then return empty fabric_info + if 'num_asic' not in m_args or int(m_args['num_asic']) <= 1: + module.exit_json(ansible_facts={'fabric_info': fabric_info}) + return + num_asic = int( m_args[ 'num_asic' ] ) + asics_host_pfx = str( m_args[ 'asics_host_pfx' ] ) + asics_host_pfx6 = str( m_args[ 'asics_host_pfx6' ] ) + for asic_id in range(num_asic): + key = "ASIC%d" % asic_id + data = { 'asicname': key, + 'ip_prefix': asics_host_pfx % asic_id, + 'ip6_prefix': asics_host_pfx6 % asic_id } + fabric_info.append( data ) + module.exit_json(ansible_facts={'fabric_info': fabric_info}) + except (IOError, OSError), e: + fail_msg = "IO error" + str(e) + module.fail_json(msg=fail_msg) + except Exception, e: + fail_msg = "failed to find the correct fabric asic info for "+m_args['hwsku'] + str(e) + module.fail_json(msg=fail_msg) + +from ansible.module_utils.basic import * +if __name__ == "__main__": + main() diff --git a/ansible/library/port_alias.py b/ansible/library/port_alias.py index 9e07ccc630f8..7c1e0d9ff584 100755 --- a/ansible/library/port_alias.py +++ b/ansible/library/port_alias.py @@ -155,7 +155,8 @@ def main(): module = AnsibleModule( argument_spec=dict( hwsku=dict(required=True, type='str'), - num_asic=dict(type='int', required=False) + num_asic=dict(type='int', required=False), + cardtype=dict(type='str', required=False) ), supports_check_mode=True ) @@ -165,6 +166,12 @@ def main(): portmap = {} aliasmap = {} portspeed = {} + if 'cardtype' in m_args and m_args['cardtype'] == 'supervisor': + module.exit_json(ansible_facts={'port_alias': aliases, + 'port_name_map': portmap, + 'port_alias_map': aliasmap, + 'port_speed': portspeed}) + return allmap = SonicPortAliasMap(m_args['hwsku']) # ASIC interface names of front panel interfaces front_panel_asic_ifnames = [] diff --git a/ansible/module_utils/port_utils.py b/ansible/module_utils/port_utils.py index 537a2993affb..aaef21665508 100644 --- a/ansible/module_utils/port_utils.py +++ b/ansible/module_utils/port_utils.py @@ -119,6 +119,10 @@ def get_port_alias_to_name_map(hwsku, asic_id=None): 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 == "Arista-7800R3-48CQ-LC" or\ + hwsku == "Arista-7800R3K-48CQ-LC": + for i in range(1, 48): + port_alias_to_name_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4) 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) diff --git a/ansible/roles/vm_set/library/vm_topology.py b/ansible/roles/vm_set/library/vm_topology.py index 00f6799fae71..311053618f06 100644 --- a/ansible/roles/vm_set/library/vm_topology.py +++ b/ansible/roles/vm_set/library/vm_topology.py @@ -455,6 +455,8 @@ def bind_fp_ports(self, disconnect_vm=False): br_name = OVS_FP_BRIDGE_TEMPLATE % (self.vm_names[self.vm_base_index + attr['vm_offset']], idx) vm_iface = OVS_FP_TAP_TEMPLATE % (self.vm_names[self.vm_base_index + attr['vm_offset']], idx) (dut_index, vlan_index, ptf_index) = VMTopology.parse_vm_vlan_port(vlan) + if len( self.duts_fp_ports[self.duts_name[dut_index]] ) == 0: + continue injected_iface = INJECTED_INTERFACES_TEMPLATE % (self.vm_set_name, ptf_index) self.bind_ovs_ports(br_name, self.duts_fp_ports[self.duts_name[dut_index]][str(vlan_index)], injected_iface, vm_iface, disconnect_vm) diff --git a/ansible/templates/minigraph_cpg.j2 b/ansible/templates/minigraph_cpg.j2 index 1f49ecb28b93..053d93f7a261 100644 --- a/ansible/templates/minigraph_cpg.j2 +++ b/ansible/templates/minigraph_cpg.j2 @@ -1,6 +1,7 @@ +{% if cardtype is not defined or cardtype != 'supervisor' %} {% for index in range(vms_number) %} {% set vm=vms[index] %} {% if vm_topo_config['vm'][vm]['peer_ipv4'][dut_index|int] %} @@ -77,9 +78,10 @@ {% endif %} {% endfor %} {% endfor %} +{% endif %} -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} {{ vm_topo_config['dut_asn'] }} {{ inventory_hostname }} diff --git a/ansible/templates/minigraph_device.j2 b/ansible/templates/minigraph_device.j2 index c47756653d90..966fbed59275 100644 --- a/ansible/templates/minigraph_device.j2 +++ b/ansible/templates/minigraph_device.j2 @@ -2,7 +2,7 @@ true -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} {% set num_of_intf = port_alias | length %} {% for index in range(num_of_intf) %} diff --git a/ansible/templates/minigraph_dpg.j2 b/ansible/templates/minigraph_dpg.j2 index 68f4a2044989..084261bd6f33 100644 --- a/ansible/templates/minigraph_dpg.j2 +++ b/ansible/templates/minigraph_dpg.j2 @@ -2,7 +2,7 @@ -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} HostIP Loopback0 @@ -117,6 +117,7 @@ {% endif %} +{% if cardtype is not defined or cardtype != 'supervisor' %} {% for index in range(vms_number) %} {% if vm_topo_config['vm'][vms[index]]['ip_intf'][dut_index|int] is not none %} @@ -156,11 +157,12 @@ {% endif %} {% endfor %} +{% endif %} {% endif %} -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} SNMP_ACL SNMP diff --git a/ansible/templates/minigraph_dpg_asic.j2 b/ansible/templates/minigraph_dpg_asic.j2 index cef693288d24..bbca6c7e6a16 100644 --- a/ansible/templates/minigraph_dpg_asic.j2 +++ b/ansible/templates/minigraph_dpg_asic.j2 @@ -204,5 +204,41 @@ +{% for asic in fabric_info %} + + + + + HostIP + Loopback0 + + {{ asic['ip_prefix'] }} + + {{ asic['ip_prefix'] }} + + + HostIP + Loopback0 + + {{ asic['ip6_prefix'] }} + + {{ asic['ip6_prefix'] }} + + + + + + + + {{ asic['asicname'] }} + + + + + + + + +{% endfor %} {% endfor %} diff --git a/ansible/templates/minigraph_png.j2 b/ansible/templates/minigraph_png.j2 index 128e8cae2045..04fd37f65ef2 100644 --- a/ansible/templates/minigraph_png.j2 +++ b/ansible/templates/minigraph_png.j2 @@ -1,5 +1,6 @@ +{% if cardtype is not defined or cardtype != 'supervisor' %} {% for index in range(vms_number) %} {% set vm_intfs=vm_topo_config['vm'][vms[index]]['intfs'][dut_index|int]|sort %} {% set dut_intfs=vm_topo_config['vm'][vms[index]]['interface_indexes'][dut_index|int]|sort %} @@ -66,6 +67,7 @@ true {% endfor %} +{% endif %} @@ -191,6 +193,11 @@ {{ asic }} Broadcom-Trident2 +{% endfor %} +{% for asic in asic_info %} + + {{ asic['asicname'] }} + {% endfor %} diff --git a/ansible/templates/minigraph_template.j2 b/ansible/templates/minigraph_template.j2 index 5894b9b5816b..76ba6e367db5 100644 --- a/ansible/templates/minigraph_template.j2 +++ b/ansible/templates/minigraph_template.j2 @@ -2,11 +2,11 @@ {% set vms=vm_topo_config['vm'].keys() | sort %} {% set vms_number = vms | length %} {% if 'loopback' in vm_topo_config['DUT'] %} -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} {% set lp_ipv4 = vm_topo_config['DUT']['loopback']['ipv4'][dut_index|int] %} {% set lp_ipv4_addr = lp_ipv4.split('/')[0] %} {% endif %} -{% if type is not defined or type != 'supervisor' %} +{% if cardtype is not defined or cardtype != 'supervisor' %} {% set lp_ipv6 = vm_topo_config['DUT']['loopback']['ipv6'][dut_index|int] %} {% set lp_ipv6_addr = lp_ipv6.split('/')[0] %} {% endif %} diff --git a/tests/common/helpers/dut_utils.py b/tests/common/helpers/dut_utils.py index 65b5a64a0644..97f00bc3fea6 100644 --- a/tests/common/helpers/dut_utils.py +++ b/tests/common/helpers/dut_utils.py @@ -14,12 +14,12 @@ def is_supervisor_node(inv_files, hostname): you can be get it from get_inventory_files in tests.common.utilities @param hostname: hostname as defined in the inventory Returns: - Currently, we are using 'type' in the inventory to make the decision. If 'type' for the node is defined in + Currently, we are using 'cardtype' in the inventory to make the decision. If 'cardtype' for the node is defined in the inventory, and it is 'supervisor', then return True, else return False. In future, we can change this logic if possible to derive it from the DUT. """ dut_vars = get_host_visible_vars(inv_files, hostname) - if 'type' in dut_vars and dut_vars['type'] == 'supervisor': + if 'cardtype' in dut_vars and dut_vars['cardtype'] == 'supervisor': return True return False diff --git a/tests/common/testbed.py b/tests/common/testbed.py index fd364605abbb..33d445261cd4 100644 --- a/tests/common/testbed.py +++ b/tests/common/testbed.py @@ -204,7 +204,7 @@ def write_line_break(self, data=None): explicit_start=True, Dumper=IncIndentDumper) def get_testbed_type(self, topo_name): - pattern = re.compile(r'^(t0|t1|ptf|fullmesh|dualtor)') + pattern = re.compile(r'^(t0|t1|t2|ptf|fullmesh|dualtor)') match = pattern.match(topo_name) if match == None: raise Exception("Unsupported testbed type - {}".format(topo_name))