diff --git a/ansible/config_sonic_basedon_testbed.yml b/ansible/config_sonic_basedon_testbed.yml index 5c5436c0b85..9a943ef1da1 100644 --- a/ansible/config_sonic_basedon_testbed.yml +++ b/ansible/config_sonic_basedon_testbed.yml @@ -93,12 +93,19 @@ delegate_to: localhost ignore_errors: true + - name: determine whether to sort port_alias by index + set_fact: + sort_by_index: false + when: switch_type is defined and switch_type == "voq" and type is defined and type == "kvm" + - name: find interface name mapping and individual interface speed if defined from dut port_alias: hwsku: "{{ hwsku }}" card_type: "{{ card_type | default('fixed') }}" hostname: "{{ inventory_hostname | default('') }}" switchids: "{{ switchids | default([]) }}" + num_asic: "{{ num_asics }}" + sort_by_index: "{{ sort_by_index | default(true) }}" when: deploy is defined and deploy|bool == true - name: find interface name mapping and individual interface speed if defined with local data @@ -109,6 +116,7 @@ hostname: "{{ inventory_hostname | default('') }}" switchids: "{{ switchids | default([]) }}" slotid: "{{ slot_num | default(None) }}" + sort_by_index: "{{ sort_by_index | default(true) }}" delegate_to: localhost when: deploy is not defined or deploy|bool == false diff --git a/ansible/lab b/ansible/lab index 2cbed6e1444..4d9f4a32338 100644 --- a/ansible/lab +++ b/ansible/lab @@ -161,6 +161,40 @@ sonic_a7260: "0x2d": "Arista Networks" "0x2e": "Aboot-norcal7-7.2.3-pcie2x4-12345678" +sonic_nokia_multi_asic_lc: + vars: + hwsku: Nokia-IXR7250E-36x400G + iface_speed: 400000 + num_asics: 2 + start_topo_service: True + frontend_asics: [0,1] + card_type: linecard + hosts: + vlab-t2-03: + ansible_host: 10.250.0.123 + ansible_hostv6: fec0::ffff:afa:13 + slot_num: slot1 + loopback4096_ip: [192.0.0.0/32, 192.0.0.1/32] + loopback4096_ipv6: [2603:10e2:400::/128, 2603:10e2:400::1/128] + vlab-t2-04: + ansible_host: 10.250.0.124 + ansible_hostv6: fec0::ffff:afa:14 + slot_num: slot2 + loopback4096_ip: [192.0.0.3/32, 192.0.0.4/32] + loopback4096_ipv6: [2603:10e2:400::3/128, 2603:10e2:400::4/128] + +sonic_nokia_sup: + vars: + hwsku: Nokia-IXR7250E-SUP-10 + iface_speed: 400000 + start_topo_service: True + card_type: supervisor + hosts: + vlab-t2-sup1: + ansible_host: 10.250.0.125 + ansible_hostv6: fec0::ffff:afa:15 + slot_num: slot0 + sonic_multi_asic: vars: hwsku: msft_multi_asic_vs diff --git a/ansible/library/port_alias.py b/ansible/library/port_alias.py index 0535d8e5f4a..df505150740 100755 --- a/ansible/library/port_alias.py +++ b/ansible/library/port_alias.py @@ -99,8 +99,10 @@ def get_portconfig_path(self, slotid=None, asic_id=None): return None def get_portmap(self, asic_id=None, include_internal=False, - hostname=None, switchid=None, slotid=None): + hostname=None, switchid=None, slotid=None, card_type=None): aliases = [] + front_panel_aliases = [] + inband_aliases = [] portmap = {} aliasmap = {} portspeed = {} @@ -109,7 +111,8 @@ def get_portmap(self, asic_id=None, include_internal=False, front_panel_asic_ifnames = {} front_panel_asic_id = {} # All asic names - asic_if_names = [] + asic_if_names = {} + asic_if_ids = {} sysports = [] port_coreid_index = -1 port_core_portid_index = -1 @@ -171,22 +174,27 @@ def get_portmap(self, asic_id=None, include_internal=False, else: alias = name add_port = False + + if role == "Ext": + front_panel_aliases.append(alias) + if role == "Inb": + inband_aliases.append(alias) + if role in {"Ext"} or (role in ["Int", "Inb", "Rec"] and include_internal): add_port = True aliases.append( (alias, -1 if port_index == -1 or len(mapping) <= port_index else mapping[port_index])) portmap[name] = alias aliasmap[alias] = name - if role == "Ext" and (asic_name_index != -1) and (len(mapping) > asic_name_index): + + if (asic_name_index != -1) and (len(mapping) > asic_name_index): asicifname = mapping[asic_name_index] - # we only want following ASIC info in minigraph for multi-asic if asic_id is not None: - front_panel_asic_ifnames[alias] = asicifname - front_panel_asic_id[alias] = "ASIC" + \ - str(asic_id) - if (asic_name_index != -1) and (len(mapping) > asic_name_index): - asicifname = mapping[asic_name_index] - asic_if_names.append(asicifname) + asic_if_names[alias] = asicifname + asic_if_ids[alias] = "ASIC" + str(asic_id) + if role == "Ext": + front_panel_asic_ifnames[alias] = asicifname + front_panel_asic_id[alias] = "ASIC" + str(asic_id) if (speed_index != -1) and (len(mapping) > speed_index): speed = mapping[speed_index] sysport['speed'] = speed @@ -201,13 +209,16 @@ def get_portmap(self, asic_id=None, include_internal=False, if (num_voq_index != -1) and (len(mapping) > num_voq_index): voq = mapping[num_voq_index] sysport['num_voq'] = voq - sysport['name'] = name - sysport['hostname'] = hostname - sysport['asic_name'] = asic_name - sysport['switchid'] = switchid sysports.append(sysport) if port_index != -1 and len(mapping) > port_index: indexmap[mapping[port_index]] = name + + # Special handling for the Cpu port + if include_internal and card_type == "linecard": + aliases.append(("Cpu0/{}".format(asic_id if asic_id is not None else 0), -1)) + if asic_id is not None and card_type == "linecard": + asic_if_names["Cpu0/{}".format(asic_id)] = "Cpu0" + asic_if_ids["Cpu0/{}".format(asic_id)] = "ASIC" + str(asic_id) if len(sysports) > 0: sysport = {} sysport['name'] = 'Cpu0' @@ -220,8 +231,9 @@ def get_portmap(self, asic_id=None, include_internal=False, sysport['hostname'] = hostname sysports.insert(0, sysport) - return (aliases, portmap, aliasmap, portspeed, front_panel_asic_ifnames, front_panel_asic_id, asic_if_names, - sysports, indexmap) + return (aliases, front_panel_aliases, inband_aliases, portmap, aliasmap, portspeed, + front_panel_asic_ifnames, front_panel_asic_id, + asic_if_names, asic_if_ids, sysports, indexmap) def main(): @@ -233,33 +245,46 @@ def main(): card_type=dict(type='str', required=False), hostname=dict(type='str', required=False), switchids=dict(type='list', required=False), - slotid=dict(type='str', required=False) + slotid=dict(type='str', required=False), + sort_by_index=dict(type='bool', required=False, default=True) ), supports_check_mode=True ) m_args = module.params try: - aliases = [] - portmap = {} - aliasmap = {} - portspeed = {} - sysports = [] - indexmap = {} - # Map of ASIC interface names to front panel interfaces - front_panel_asic_ifnames = {} - front_panel_asic_ifs_asic_id = {} - # { asic_name: [ asic interfaces] } - asic_if_names = {} + aliases = [] # list of port aliases + front_panel_aliases = [] # list of (front panel port aliases, port indexes) + portmap = {} # name to alias map + aliasmap = {} # alias to name map + portspeed = {} # alias to speed map + sysports = [] # list of system ports + indexmap = {} # index to port name map + front_panel_asic_ifnames = {} # Map of interface aliases to interface names for front panel ports + front_panel_asic_ifs_asic_id = {} # Map of interface aliases to asic ids for front panel ports + asic_if_names = {} # Map of interface aliases to interface names for front panel ports + asic_if_asic_ids = {} # Map of interface aliases to asic ids + # Chassis related info + midplane_port_alias = [] # list of (midplane port aliases, port indexes) + inband_port_alias = [] # list of (inband port aliases, port indexes) if 'card_type' in m_args and m_args['card_type'] == 'supervisor': + midplane_port_alias.append(("Midplane", 0)) + if 'include_internal' in m_args and m_args['include_internal'] is True: + aliases.append(("Midplane", -1)) + module.exit_json(ansible_facts={'port_alias': aliases, + 'front_panel_port_alias': front_panel_aliases, + 'midplane_port_alias': midplane_port_alias, + 'inband_port_alias': inband_port_alias, 'port_name_map': portmap, 'port_alias_map': aliasmap, 'port_speed': portspeed, 'front_panel_asic_ifnames': [], 'front_panel_asic_ids': [], - 'asic_if_names': asic_if_names, - 'sysports': sysports}) + 'asic_if_names': [], + 'asic_if_asic_ids': [], + 'sysports': sysports, + 'port_index_map': indexmap}) return allmap = SonicPortAliasMap(m_args['hwsku']) switchids = None @@ -294,16 +319,32 @@ def main(): hostname = "" if 'hostname' in m_args: hostname = m_args['hostname'] + card_type = None + if 'card_type' in m_args: + card_type = m_args['card_type'] + + if card_type == 'linecard': + midplane_port_alias.append(("Midplane", 0)) # midplane port is always the first port (after the mgmt port) + if include_internal: + aliases.append(("Midplane", -1)) + + front_panel_aliases_set = set() + inband_port_alias_set = set() for asic_id in range(num_asic): if switchids and asic_id is not None: switchid = switchids[asic_id] if num_asic == 1: asic_id = None - (aliases_asic, portmap_asic, aliasmap_asic, portspeed_asic, front_panel_asic, front_panel_asic_ids, - asicifnames_asic, sysport_asic, index_name) = allmap.get_portmap( - asic_id, include_internal, hostname, switchid, slotid) + (aliases_asic, front_panel_aliases_asic, inband_port_alias_asic, portmap_asic, aliasmap_asic, + portspeed_asic, front_panel_asic, front_panel_asic_ids, + asicifnames_asic, asicifids_asic, sysport_asic, indexmap_asic) = allmap.get_portmap( + asic_id, include_internal, hostname, switchid, slotid, card_type) if aliases_asic is not None: aliases.extend(aliases_asic) + if front_panel_aliases_asic is not None: + front_panel_aliases_set.update(front_panel_aliases_asic) + if inband_port_alias_asic is not None: + inband_port_alias_set.update(inband_port_alias_asic) if portmap_asic is not None: portmap.update(portmap_asic) if aliasmap_asic is not None: @@ -315,32 +356,52 @@ def main(): if front_panel_asic_ids is not None: front_panel_asic_ifs_asic_id.update(front_panel_asic_ids) if asicifnames_asic is not None: - asic = 'ASIC' + str(asic_id) - asic_if_names[asic] = asicifnames_asic + asic_if_names.update(asicifnames_asic) + if asicifids_asic is not None: + asic_if_asic_ids.update(asicifids_asic) if sysport_asic is not None: sysports.extend(sysport_asic) - if index_name is not None: - indexmap.update(index_name) + if indexmap_asic is not None: + indexmap.update(indexmap_asic) # Sort the Interface Name needed in multi-asic - aliases.sort(key=lambda x: int(x[1])) + if m_args['sort_by_index']: + # Use the optional argument to enable opt out of sorting by index + aliases.sort(key=lambda x: int(x[1])) + # Get ASIC interface names list based on sorted aliases front_panel_asic_ifnames_list = [] front_panel_asic_ifs_asic_id_list = [] + asic_ifnames_list = [] + asic_ifs_asic_id_list = [] for k in aliases: if k[0] in front_panel_asic_ifnames: front_panel_asic_ifnames_list.append( front_panel_asic_ifnames[k[0]]) front_panel_asic_ifs_asic_id_list.append( front_panel_asic_ifs_asic_id[k[0]]) + if k[0] in asic_if_names: + asic_ifnames_list.append(asic_if_names[k[0]]) + asic_ifs_asic_id_list.append(asic_if_asic_ids[k[0]]) + + # Get front panel and inband interface alias list based on sorted aliases + for i, k in enumerate(aliases): + if k[0] in front_panel_aliases_set: + front_panel_aliases.append((k[0], i)) + if k[0] in inband_port_alias_set: + inband_port_alias.append((k[0], i)) module.exit_json(ansible_facts={'port_alias': [k[0] for k in aliases], + 'front_panel_port_alias': front_panel_aliases, + 'midplane_port_alias': midplane_port_alias, + 'inband_port_alias': inband_port_alias, 'port_name_map': portmap, 'port_alias_map': aliasmap, 'port_speed': portspeed, 'front_panel_asic_ifnames': front_panel_asic_ifnames_list, 'front_panel_asic_ifs_asic_id': front_panel_asic_ifs_asic_id_list, - 'asic_if_names': asic_if_names, + 'asic_if_names': asic_ifnames_list, + 'asic_if_asic_ids': asic_ifs_asic_id_list, 'sysports': sysports, 'port_index_map': indexmap}) @@ -349,7 +410,7 @@ def main(): module.fail_json(msg=fail_msg) except Exception as e: fail_msg = "failed to find the correct port config for " + \ - m_args['hwsku'] + str(e) + m_args['hwsku'] + "\n" + str(e) module.fail_json(msg=fail_msg) diff --git a/ansible/roles/vm_set/library/kvm_port.py b/ansible/roles/vm_set/library/kvm_port.py index 978627eb1e7..e215fba1a5b 100644 --- a/ansible/roles/vm_set/library/kvm_port.py +++ b/ansible/roles/vm_set/library/kvm_port.py @@ -22,9 +22,15 @@ def main(): module = AnsibleModule(argument_spec=dict( vmname=dict(required=True), + front_panel_port_aliases=dict(required=True, type=list), + midplane_port_aliases=dict(required=False, type=list, default=[]), + inband_port_aliases=dict(required=False, type=list, default=[]), )) vmname = module.params['vmname'] + fp_port_aliases = module.params['front_panel_port_aliases'] + midplane_port_aliases = module.params['midplane_port_aliases'] + inband_port_aliases = module.params['inband_port_aliases'] try: output = subprocess.check_output( @@ -36,24 +42,37 @@ def main(): mgmt_port = None fp_ports = {} - cur_fp_idx = 0 + midplane_ports = [] + inband_ports = [] - for msg in output.split('\n'): - fds = re.split(r'\s+', msg.lstrip()) + lines = output.split('\n')[2:] # the first two lines are table headers + eth_interfaces = [] + for line in lines: + fds = re.split(r'\s+', line.lstrip()) if len(fds) != 5: continue if fds[1] == "ethernet": - if mgmt_port is None: - mgmt_port = fds[0] - else: - fp_ports[cur_fp_idx] = fds[0] - cur_fp_idx = cur_fp_idx + 1 + eth_interfaces.append(fds[0]) + + if len(eth_interfaces) < 1 + len(fp_port_aliases) + len(midplane_port_aliases) + len(inband_port_aliases): + module.fail_json(msg="No enough ethernet ports for {}\n{}\n{}\n{}".format( + vmname, fp_port_aliases, midplane_port_aliases, inband_port_aliases)) - if mgmt_port is None: - module.fail_json(msg="failed to find mgmt port") + # extract mgmt port, fp_ports, midplane_ports(optional), inband_ports(optional) + mgmt_port = eth_interfaces[0] + eth_interfaces = eth_interfaces[1:] + cur_fp_idx = 0 + for portinfo in fp_port_aliases: + fp_ports[cur_fp_idx] = eth_interfaces[portinfo[1]] + cur_fp_idx += 1 + for portinfo in midplane_port_aliases: + midplane_ports.append(eth_interfaces[portinfo[1]]) + for portinfo in inband_port_aliases: + inband_ports.append(eth_interfaces[portinfo[1]]) module.exit_json(changed=False, ansible_facts={ - 'dut_mgmt_port': mgmt_port, 'dut_fp_ports': fp_ports}) + 'dut_mgmt_port': mgmt_port, 'dut_fp_ports': fp_ports, + 'dut_midplane_ports': midplane_ports, 'dut_inband_ports': inband_ports}) if __name__ == "__main__": diff --git a/ansible/roles/vm_set/library/vm_topology.py b/ansible/roles/vm_set/library/vm_topology.py index 0e07ff3d2d1..cdf4f10405d 100644 --- a/ansible/roles/vm_set/library/vm_topology.py +++ b/ansible/roles/vm_set/library/vm_topology.py @@ -163,8 +163,8 @@ # name of interface must be less than or equal to 15 bytes. MAX_INTF_LEN = 15 -VS_CHASSIS_INBAND_BRIDGE_NAME = "br-T2Inband" -VS_CHASSIS_MIDPLANE_BRIDGE_NAME = "br-T2Midplane" +VS_CHASSIS_INBAND_BRIDGE_NAME_TEMPLATE = "br-{vm_set_name}-inb" +VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE = "br-{vm_set_name}-mid" BACKEND_TOR_TYPE = "BackEndToRRouter" BACKEND_LEAF_TYPE = "BackEndLeafRouter" @@ -228,7 +228,8 @@ def adaptive_temporary_interface(vm_set_name, interface_name, reserved_space=0): class VMTopology(object): - def __init__(self, vm_names, vm_properties, fp_mtu, max_fp_num, topo, worker, is_dpu=False, dut_interfaces=None): + def __init__(self, vm_names, vm_properties, fp_mtu, max_fp_num, topo, worker, + is_dpu=False, is_vs_chassis=False, dut_interfaces=None): self.vm_names = vm_names self.vm_properties = vm_properties self.fp_mtu = fp_mtu @@ -240,7 +241,7 @@ def __init__(self, vm_names, vm_properties, fp_mtu, max_fp_num, topo, worker, is self._host_interfaces_active_active = None self.worker = worker self._is_dpu = is_dpu - return + self._is_vs_chassis = is_vs_chassis def init(self, vm_set_name, vm_base, duts_fp_ports, duts_name, ptf_exists=True, check_bridge=True): self.vm_set_name = vm_set_name @@ -330,6 +331,14 @@ def init(self, vm_set_name, vm_base, duts_fp_ports, duts_name, ptf_exists=True, self.bp_bridge = ROOT_BACK_BR_TEMPLATE % self.vm_set_name + if self._is_vs_chassis: + self._vs_chassis_midplane_br_name = VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE.format(vm_set_name=vm_set_name) + self._vs_chassis_inband_br_name = VS_CHASSIS_INBAND_BRIDGE_NAME_TEMPLATE.format(vm_set_name=vm_set_name) + if len(self._vs_chassis_midplane_br_name) > MAX_INTF_LEN: + raise ValueError("The length of VS chassis midplane bridge name is too long.") + if len(self._vs_chassis_inband_br_name) > MAX_INTF_LEN: + raise ValueError("The length of VS chassis inband bridge name is too long.") + # if the device is a bt0, build the mapping from interface to vlan id if self.dut_type == BACKEND_TOR_TYPE: default_vlan_config = self.topo.get("DUT", {}).get( @@ -461,12 +470,6 @@ def create_bridges(self): fp_br_name = adaptive_name(OVS_FP_BRIDGE_TEMPLATE, vm, fp_num) self.create_ovs_bridge(fp_br_name, self.fp_mtu) - if self.topo and 'DUT' in self.topo and 'vs_chassis' in self.topo['DUT']: - # We have a KVM based virtual chassis, need to create bridge for midplane and inband. - self.create_ovs_bridge(VS_CHASSIS_INBAND_BRIDGE_NAME, self.fp_mtu) - self.create_ovs_bridge( - VS_CHASSIS_MIDPLANE_BRIDGE_NAME, self.fp_mtu) - def create_ovs_bridge(self, bridge_name, mtu): logging.info('=== Create bridge %s with mtu %d ===' % (bridge_name, mtu)) @@ -483,11 +486,6 @@ def destroy_bridges(self): fp_br_name = adaptive_name(OVS_FP_BRIDGE_TEMPLATE, vm, fp_num) self.destroy_ovs_bridge(fp_br_name) - if self.topo and 'DUT' in self.topo and 'vs_chassis' in self.topo['DUT']: - # In case of KVM based virtual chassis, need to destroy bridge for midplane and inband. - self.destroy_ovs_bridge(VS_CHASSIS_INBAND_BRIDGE_NAME) - self.destroy_ovs_bridge(VS_CHASSIS_MIDPLANE_BRIDGE_NAME) - def destroy_ovs_bridge(self, bridge_name): logging.info('=== Destroy bridge %s ===' % bridge_name) VMTopology.cmd('ovs-vsctl --if-exists del-br %s' % bridge_name) @@ -931,13 +929,6 @@ def bind_fp_ports(self, disconnect_vm=False): ) self.worker.map(lambda args: self.bind_ovs_ports(*args), bind_ovs_ports_args) - if self.topo and 'DUT' in self.topo and 'vs_chassis' in self.topo['DUT']: - # We have a KVM based virtaul chassis, bind the midplane and inband ports - self.bind_vs_dut_ports( - VS_CHASSIS_INBAND_BRIDGE_NAME, self.topo['DUT']['vs_chassis']['inband_port']) - self.bind_vs_dut_ports( - VS_CHASSIS_MIDPLANE_BRIDGE_NAME, self.topo['DUT']['vs_chassis']['midplane_port']) - for k, attr in self.VM_LINKs.items(): logging.info("Create VM links for {} : {}".format(k, attr)) br_name = "br_{}".format(k.lower()) @@ -983,18 +974,6 @@ def unbind_fp_ports(self): self.worker.map(lambda args: self.unbind_ovs_ports(*args), unbind_ovs_ports_args) - if self.topo and 'DUT' in self.topo and 'vs_chassis' in self.topo['DUT']: - # We have a KVM based virtaul chassis, unbind the midplane and inband ports - self.unbind_vs_dut_ports( - VS_CHASSIS_INBAND_BRIDGE_NAME, self.topo['DUT']['vs_chassis']['inband_port']) - self.unbind_vs_dut_ports( - VS_CHASSIS_MIDPLANE_BRIDGE_NAME, self.topo['DUT']['vs_chassis']['midplane_port']) - # Remove the bridges as well - this is here instead of destroy_bridges as that is called with cmd: 'destroy' - # is called from 'testbed-cli.sh stop-vms' which takes a server name, an no testbed name, and thus has - # no topology associated with it. - self.destroy_ovs_bridge(VS_CHASSIS_INBAND_BRIDGE_NAME) - self.destroy_ovs_bridge(VS_CHASSIS_MIDPLANE_BRIDGE_NAME) - for k, attr in self.VM_LINKs.items(): logging.info("Remove VM links for {} : {}".format(k, attr)) br_name = "br_{}".format(k.lower()) @@ -1087,33 +1066,49 @@ def unbind_vm_backplane(self): VMTopology.iface_down(self.bp_bridge) VMTopology.cmd('brctl delbr %s' % self.bp_bridge) - def bind_vs_dut_ports(self, br_name, dut_ports): - # dut_ports is a list of port on each DUT that has to be bound together. eg. 30,30,30 - will bind ports - # 30 of each DUT together into bridge br_name - # Also for vm, a dut's ports would be of the format -. So, port '30' on vm with - # name 'vlab-02' would be 'vlab-02-31' + def bind_vs_chassis_ports(self, duts_midplane_ports, duts_inband_ports): + # We have a KVM based virtaul chassis, create two ovs bridges, bind the midplane and inband ports + self.create_ovs_bridge(self._vs_chassis_inband_br_name, self.fp_mtu) + self.create_ovs_bridge(self._vs_chassis_midplane_br_name, self.fp_mtu) + + for dut in duts_midplane_ports.keys(): + self.bind_vs_dut_ports( + self._vs_chassis_midplane_br_name, dut, duts_midplane_ports[dut]) + + for dut in duts_inband_ports.keys(): + self.bind_vs_dut_ports( + self._vs_chassis_inband_br_name, dut, duts_inband_ports[dut]) + + def unbind_vs_chassis_ports(self, duts_midplane_ports, duts_inband_ports): + # We have a KVM based virtaul chassis, bind the midplane and inband ports + for dut in duts_midplane_ports.keys(): + self.unbind_vs_dut_ports( + self._vs_chassis_midplane_br_name, dut, duts_midplane_ports[dut]) + + for dut in duts_inband_ports.keys(): + self.unbind_vs_dut_ports( + self._vs_chassis_inband_br_name, dut, duts_inband_ports[dut]) + + self.destroy_ovs_bridge(self._vs_chassis_inband_br_name) + self.destroy_ovs_bridge(self._vs_chassis_midplane_br_name) + + def bind_vs_dut_ports(self, br_name, dut_name, dut_ports): br_ports = VMTopology.get_ovs_br_ports(br_name) - for dut_index, a_port in enumerate(dut_ports): - dut_name = self.duts_name[dut_index] - port_name = "{}-{}".format(dut_name, (a_port + 1)) - br = VMTopology.get_ovs_bridge_by_port(port_name) + for port in dut_ports: + br = VMTopology.get_ovs_bridge_by_port(port) if br is not None and br != br_name: - VMTopology.cmd('ovs-vsctl del-port %s %s' % (br, port_name)) + VMTopology.cmd('ovs-vsctl del-port {} {}'.format(br, port)) - if port_name not in br_ports: - VMTopology.cmd('ovs-vsctl add-port %s %s' % - (br_name, port_name)) + if port not in br_ports: + VMTopology.cmd('ovs-vsctl add-port {} {}'.format(br_name, port)) - def unbind_vs_dut_ports(self, br_name, dut_ports): + def unbind_vs_dut_ports(self, br_name, dut_name, dut_ports): """unbind all ports except the vm port from an ovs bridge""" if VMTopology.intf_exists(br_name): - ports = VMTopology.get_ovs_br_ports(br_name) - for dut_index, a_port in enumerate(dut_ports): - dut_name = self.duts_name[dut_index] - port_name = "{}-{}".format(dut_name, (a_port + 1)) - if port_name in ports: - VMTopology.cmd('ovs-vsctl del-port %s %s' % - (br_name, port_name)) + br_ports = VMTopology.get_ovs_br_ports(br_name) + for port in dut_ports: + if port in br_ports: + VMTopology.cmd('ovs-vsctl del-port {} {}'.format(br_name, port)) def bind_ovs_ports(self, br_name, dut_iface, injected_iface, vm_iface, disconnect_vm=False): """ @@ -2103,6 +2098,8 @@ def main(): mgmt_bridge=dict(required=False, type='str'), duts_fp_ports=dict(required=False, type='dict'), duts_mgmt_port=dict(required=False, type='list'), + duts_midplane_ports=dict(required=False, type='dict', default={}), + duts_inband_ports=dict(required=False, type='dict', default={}), duts_name=dict(required=False, type='list'), dut_interfaces=dict(required=False, type='str'), fp_mtu=dict(required=False, type='int', default=DEFAULT_MTU), @@ -2110,6 +2107,7 @@ def main(): default=NUM_FP_VLANS_PER_FP), netns_mgmt_ip_addr=dict(required=False, type='str', default=None), is_dpu=(dict(required=False, type='bool', default=False)), + is_vs_chassis=(dict(required=False, type='bool', default=False)), use_thread_worker=dict(required=False, type='bool', default=True), thread_worker_count=dict(required=False, type='int', default=max(MIN_THREAD_WORKER_COUNT, @@ -2124,6 +2122,7 @@ def main(): max_fp_num = module.params['max_fp_num'] vm_properties = module.params['vm_properties'] is_dpu = module.params['is_dpu'] if 'is_dpu' in module.params else False + is_vs_chassis = module.params['is_vs_chassis'] dut_interfaces = module.params['dut_interfaces'] use_thread_worker = module.params['use_thread_worker'] thread_worker_count = module.params['thread_worker_count'] @@ -2134,10 +2133,10 @@ def main(): vm_names = [] try: - topo = module.params['topo'] worker = VMTopologyWorker(use_thread_worker, thread_worker_count) - net = VMTopology(vm_names, vm_properties, fp_mtu, max_fp_num, topo, worker, is_dpu, dut_interfaces) + net = VMTopology(vm_names, vm_properties, fp_mtu, max_fp_num, topo, worker, + is_dpu, is_vs_chassis, dut_interfaces) if cmd == 'create': net.create_bridges() @@ -2158,6 +2157,8 @@ def main(): vm_set_name = module.params['vm_set_name'] duts_fp_ports = module.params['duts_fp_ports'] + duts_midplane_ports = module.params['duts_midplane_ports'] + duts_inband_ports = module.params['duts_inband_ports'] duts_name = module.params['duts_name'] is_multi_duts = True if len(duts_name) > 1 else False @@ -2204,6 +2205,8 @@ def main(): net.bind_fp_ports() net.bind_vm_backplane() net.add_bp_port_to_docker(ptf_bp_ip_addr, ptf_bp_ipv6_addr) + if is_vs_chassis: + net.bind_vs_chassis_ports(duts_midplane_ports, duts_inband_ports) if net.netns: net.add_network_namespace() @@ -2250,6 +2253,8 @@ def main(): vm_set_name = module.params['vm_set_name'] topo = module.params['topo'] duts_fp_ports = module.params['duts_fp_ports'] + duts_midplane_ports = module.params['duts_midplane_ports'] + duts_inband_ports = module.params['duts_inband_ports'] duts_name = module.params['duts_name'] is_multi_duts = True if len(duts_name) > 1 else False @@ -2279,6 +2284,8 @@ def main(): net.unbind_vm_backplane() net.unbind_fp_ports() net.remove_injected_fp_ports_from_docker() + if is_vs_chassis: + net.unbind_vs_chassis_ports(duts_midplane_ports, duts_inband_ports) if hostif_exists: net.remove_host_ports() @@ -2348,11 +2355,15 @@ def main(): if vms_exists: net.unbind_fp_ports() + if is_vs_chassis: + net.unbind_vs_chassis_ports(duts_midplane_ports, duts_inband_ports) net.add_injected_fp_ports_to_docker() net.add_injected_VM_ports_to_docker() net.bind_fp_ports() net.bind_vm_backplane() net.add_bp_port_to_docker(ptf_bp_ip_addr, ptf_bp_ipv6_addr) + if is_vs_chassis: + net.bind_vs_chassis_ports(duts_midplane_ports, duts_inband_ports) if net.netns: net.add_network_namespace() diff --git a/ansible/roles/vm_set/tasks/add_topo.yml b/ansible/roles/vm_set/tasks/add_topo.yml index 7727d1e051e..b4d3d8edeaa 100644 --- a/ansible/roles/vm_set/tasks/add_topo.yml +++ b/ansible/roles/vm_set/tasks/add_topo.yml @@ -228,12 +228,15 @@ ptf_bp_ipv6_addr: "{{ ptf_bp_ipv6 }}" mgmt_bridge: "{{ mgmt_bridge }}" duts_fp_ports: "{{ duts_fp_ports }}" + duts_midplane_ports: "{{ duts_midplane_ports }}" + duts_inband_ports: "{{ duts_inband_ports }}" duts_mgmt_port: "{{ duts_mgmt_port }}" duts_name: "{{ duts_name.split(',') }}" fp_mtu: "{{ fp_mtu_size }}" max_fp_num: "{{ max_fp_num }}" netns_mgmt_ip_addr: "{{ netns_mgmt_ip if netns_mgmt_ip is defined else omit }}" dut_interfaces: "{{ dut_interfaces | default('') }}" + is_vs_chassis: "{{ is_vs_chassis | default(false) }}" become: yes - name: Bind topology {{ topo }} to DPUs. diff --git a/ansible/roles/vm_set/tasks/get_dut_port.yml b/ansible/roles/vm_set/tasks/get_dut_port.yml index 297448a9a54..18c56354e01 100644 --- a/ansible/roles/vm_set/tasks/get_dut_port.yml +++ b/ansible/roles/vm_set/tasks/get_dut_port.yml @@ -11,9 +11,33 @@ dut_mgmt_port: "" when: external_port is defined +- name: determine whether to include internal ports + set_fact: + include_internal: true + when: is_vs_chassis is defined and is_vs_chassis == true + +- name: determine whether to sort port_alias by index + set_fact: + sort_by_index: false + when: is_vs_chassis is defined and is_vs_chassis == true + +- set_fact: + hwsku: "{{ hostvars[dut_name].hwsku }}" + num_asic: "{{ hostvars[dut_name]['num_asics'] | default(1) }}" + card_type: "{{ hostvars[dut_name]['card_type'] | default('pizzabox') }}" + when: dut_name in hostvars + +- name: Get DUT port alias + port_alias: hwsku={{ hwsku }} num_asic={{ num_asic }} card_type={{ card_type }} include_internal={{ include_internal | default(false) }} sort_by_index={{ sort_by_index | default(true) }} + delegate_to: localhost + when: external_port is not defined and hostvars[dut_name].type is defined and hostvars[dut_name]['type'] == 'kvm' + - name: Get front panel and mgmt port for kvm vm kvm_port: vmname: "{{ dut_name }}" + front_panel_port_aliases: "{{ front_panel_port_alias }}" + midplane_port_aliases: "{{ midplane_port_alias }}" + inband_port_aliases: "{{ inband_port_alias }}" when: external_port is not defined and hostvars[dut_name].type is defined and hostvars[dut_name]['type'] == 'kvm' become: yes @@ -31,4 +55,6 @@ - set_fact: duts_fp_ports: "{{ duts_fp_ports|default({}) | combine( { dut_name: dut_fp_ports } ) }}" + duts_midplane_ports: "{{ duts_midplane_ports|default({}) | combine( { dut_name: dut_midplane_ports|default([]) } ) }}" + duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports|default([]) } ) }}" duts_mgmt_port: "{{ duts_mgmt_port|default([]) + [ dut_mgmt_port ] }}" diff --git a/ansible/roles/vm_set/tasks/remove_topo.yml b/ansible/roles/vm_set/tasks/remove_topo.yml index 96168f45555..8a03ce7a0d9 100644 --- a/ansible/roles/vm_set/tasks/remove_topo.yml +++ b/ansible/roles/vm_set/tasks/remove_topo.yml @@ -54,10 +54,13 @@ vm_base: "{{ VM_base }}" vm_type: "{{ vm_type }}" duts_fp_ports: "{{ duts_fp_ports }}" + duts_midplane_ports: "{{ duts_midplane_ports }}" + duts_inband_ports: "{{ duts_inband_ports }}" duts_mgmt_port: "{{ duts_mgmt_port }}" duts_name: "{{ duts_name.split(',') }}" max_fp_num: "{{ max_fp_num }}" dut_interfaces: "{{ dut_interfaces | default('') }}" + is_vs_chassis: "{{ is_vs_chassis | default(false) }}" become: yes - name: Unbind topology {{ topo }} to DPU VMs. base vm = {{ VM_base }} @@ -138,6 +141,8 @@ vm_set_name: "{{ vm_set_name }}" topo: "{{ topology }}" duts_fp_ports: "{{ duts_fp_ports }}" + duts_midplane_ports: "{{ duts_midplane_ports }}" + duts_inband_ports: "{{ duts_inband_ports }}" duts_mgmt_port: "{{ duts_mgmt_port }}" duts_name: "{{ duts_name.split(',') }}" max_fp_num: "{{ max_fp_num }}" diff --git a/ansible/roles/vm_set/tasks/renumber_topo.yml b/ansible/roles/vm_set/tasks/renumber_topo.yml index 3dd69966e08..ea43fbab71d 100644 --- a/ansible/roles/vm_set/tasks/renumber_topo.yml +++ b/ansible/roles/vm_set/tasks/renumber_topo.yml @@ -73,8 +73,12 @@ vm_base: "{{ VM_base }}" vm_type: "{{ vm_type }}" duts_fp_ports: "{{ duts_fp_ports }}" + duts_midplane_ports: "{{ duts_midplane_ports }}" + duts_inband_ports: "{{ duts_inband_ports }}" + duts_mgmt_port: "{{ duts_mgmt_port }}" duts_name: "{{ duts_name.split(',') }}" max_fp_num: "{{ max_fp_num }}" + is_vs_chassis: "{{ is_vs_chassis | default(false) }}" become: yes - name: Stop ptf container ptf_{{ vm_set_name }} @@ -168,11 +172,14 @@ ptf_bp_ipv6_addr: "{{ ptf_bp_ipv6 }}" mgmt_bridge: "{{ mgmt_bridge }}" duts_fp_ports: "{{ duts_fp_ports }}" + duts_midplane_ports: "{{ duts_midplane_ports }}" + duts_inband_ports: "{{ duts_inband_ports }}" duts_mgmt_port: "{{ duts_mgmt_port }}" duts_name: "{{ duts_name.split(',') }}" fp_mtu: "{{ fp_mtu_size }}" max_fp_num: "{{ max_fp_num }}" netns_mgmt_ip_addr: "{{ netns_mgmt_ip if netns_mgmt_ip is defined else omit }}" + is_vs_chassis: "{{ is_vs_chassis | default(false) }}" become: yes - name: Change MAC address for PTF interfaces diff --git a/ansible/roles/vm_set/tasks/start_sonic_vm.yml b/ansible/roles/vm_set/tasks/start_sonic_vm.yml index 0f83bc77751..40186d59871 100644 --- a/ansible/roles/vm_set/tasks/start_sonic_vm.yml +++ b/ansible/roles/vm_set/tasks/start_sonic_vm.yml @@ -16,6 +16,7 @@ serial_port: "{{ hostvars[dut_name]['serial_port'] }}" hwsku: "{{ hostvars[dut_name].hwsku }}" num_asic: "{{ hostvars[dut_name]['num_asics'] | default(1) }}" + card_type: "{{ hostvars[dut_name]['card_type'] | default('pizzabox') }}" - name: Remove arp entry for {{ dut_name }} shell: arp -d {{ mgmt_ip_address }} @@ -33,8 +34,18 @@ copy: src={{ src_disk_image }} dest={{ disk_image }} remote_src=True when: not file_stat.stat.exists +- name: determine whether to include internal ports + set_fact: + include_internal: true + when: is_vs_chassis is defined and is_vs_chassis == true + +- name: determine whether to sort port_alias by index + set_fact: + sort_by_index: false + when: is_vs_chassis is defined and is_vs_chassis == true + - name: Get DUT port alias - port_alias: hwsku={{ hostvars[dut_name].hwsku }} num_asic={{ num_asic }} + port_alias: hwsku={{ hwsku }} num_asic={{ num_asic }} card_type={{ card_type }} include_internal={{ include_internal | default(false) }} sort_by_index={{ sort_by_index | default(true) }} delegate_to: localhost - name: Define SONiC vm {{ dut_name }} diff --git a/ansible/roles/vm_set/templates/sonic.xml.j2 b/ansible/roles/vm_set/templates/sonic.xml.j2 index bcf7414e55c..9a741727cac 100644 --- a/ansible/roles/vm_set/templates/sonic.xml.j2 +++ b/ansible/roles/vm_set/templates/sonic.xml.j2 @@ -22,6 +22,13 @@ +{% elif hwsku == 'Nokia-IXR7250E-36x400G' or hwsku == 'Nokia-IXR7250E-SUP-10' %} + 8 + 10 + + + + {% elif "dualtor" in topo %} 6 6 diff --git a/ansible/setup_vs_chassis.sh b/ansible/setup_vs_chassis.sh new file mode 100644 index 00000000000..c3f6d48cb21 --- /dev/null +++ b/ansible/setup_vs_chassis.sh @@ -0,0 +1,262 @@ +#!/bin/bash + +# Define variables +platform="x86_64-kvm_x86_64-r0" +metadata_file="/etc/sonic/vs_chassis_metadata.json" +updategraph_file="/etc/sonic/updategraph.conf" +minigraph_input_file="/etc/sonic/minigraph.xml" +minigraph_output_file="/etc/sonic/minigraph.xml" +remove_macsec_script="/usr/local/bin/remove_macsec.py" +rc_local_file="/etc/rc.local" +platform_dir="/usr/share/sonic/device/$platform" +platform_chassisdb_conf_file="$platform_dir/chassisdb.conf" +chassis_db_address=10.6.0.100 +midplane_subnet=10.6.0.0/16 +num_asic=1 +services=("swss" "syncd" "bgp" "teamd" "gbsyncd" "database" "lldp") +platform_env_file="$platform_dir/platform_env.conf" +macsec_supported_arista_hwskus=("Arista-7800R3AK-36DM2-*" "Arista-7800R3AK-36D2-*" "Arista-7800R3A-36P-*" "Arista-7800R3A-36DM2-*" "Arista-7800R3A-36D-*" "Arista-7800R3A-36D2-*") +macsec_supported_nokia_hwskus=("Nokia-IXR7250E-36x100G" "Nokia-IXR7250E-36x400G") + +# Usage: match_pattern "string" "${pattern_list[@]}" +match_pattern() { + local string="$1" + shift + local pattern_list=("$@") + + for pattern in "${pattern_list[@]}"; do + case $string in + $pattern) return 0;; # Match found + esac + done + + return 1 # No match found +} + +# Function to extract values from metadata using jq +extract_metadata() { + local field=$1 + jq -r ".$field" "$metadata_file" +} + +# Function to check if a directory exists +check_directory() { + local directory=$1 + if [ ! -d "$directory" ]; then + echo "Directory not found: $directory" + exit 1 + fi +} + +# Function to remove an existing file +remove_existing_file() { + local file=$1 + if [ -f "$file" ]; then + rm "$file" + echo "Deleted existing file: $file" + fi +} + +# Function to get NUM_ASIC +get_num_asic() { + local asic_conf_file="$1" + if [ -f "$asic_conf_file" ]; then + num_asic_new=$(awk -F "=" '/^NUM_ASIC=/ {print $2}' "$asic_conf_file") + if [ -n "$num_asic" ]; then + echo "NUM_ASIC found in $asic_conf_file: $num_asic" + num_asic=$num_asic_new + else + echo "NUM_ASIC not found in $asic_conf_file" + exit 1 + fi + else + echo "asic.conf file not found $asic_conf_file" + fi +} + +# Function to update NUM_ASIC in platform ASIC configuration file +update_asic_conf() { + local file="$1" + if [ -f "$file" ]; then + sed -i "s/^NUM_ASIC=.*/NUM_ASIC=$num_asic/" "$file" + echo "NUM_ASIC: $num_asic updated in $file" + else + echo "NUM_ASIC=$num_asic" > "$file" + echo "Created $file with NUM_ASIC=$num_asic" + fi +} + +# Function to add commands to rc.local +add_commands_to_rc_local() { + local slot_num="$1" + local num_asic="$2" + sed -i "s/^exit 0/sudo ip link set dev eth1 down\nsudo ip link set dev eth1 name eth1-midplane\nsudo ip address add 10.6.$slot_num.100\/16 dev eth1-midplane\nsudo ip link set dev eth1-midplane up\n\nexit 0/" "$rc_local_file" + echo "Added commands to rename eth1 to eth1-midplane and added midplane ip: 10.6.$slot_num.100/16 in $rc_local_file" + + # if linecard and num_asic==1, rename the last port to Cpu0 + if [ "$is_linecard" = true ] && [ "$num_asic" -eq 1 ]; then + lanemap_file="$hwsku_dir/lanemap.ini" + if [ ! -f "$lanemap_file" ]; then + echo "lanemap.ini file not found: $lanemap_file" + exit 1 + fi + #read the last line of lanemap.ini file, if it contains Cpu0, then rename the prev_port+1 to Cpu0 + last_port=$(tail -n 1 "$lanemap_file" | cut -d ":" -f 1) + prev_port=$(tail -n 2 "$lanemap_file" | head -n 1 | cut -d ":" -f 1) + if [ "$last_port" = "Cpu0" ]; then + # Extract the numeric part of the interface name + num="${prev_port#eth}" + # Increment the numeric part + ((num++)) + # Construct the new interface name + cur_port="eth$num" + echo "Renaming $cur_port to $last_port" + + sed -i "s/^exit 0/sudo ip link set dev $cur_port down\nsudo ip link set dev $cur_port name $last_port\nsudo ip link set dev $last_port up\n\nexit 0/" "$rc_local_file" + echo "Added command to rename $cur_port to $last_port in $rc_local_file" + fi + fi +} + +# Function to stop and disable services +stop_disable_services() { + local num_asic="$1" + if [ "$num_asic" -gt 1 ]; then + for service in "${services[@]}"; do + echo "Stop/Disable/Mask $service" + sudo systemctl stop "$service" + sudo systemctl disable "$service" + sudo systemctl mask "$service" + sudo docker rm "$service" + done + fi +} + +# Function to set up midplane address and wait for supervisor +setup_midplane_and_wait_for_supervisor() { + local slot_num="$1" + if [ "$is_linecard" = true ]; then + echo "Setting up midplane address for linecard: $slot_num" + sudo ip -d addr show eth1 + sudo ip link set up dev eth1 + sudo ip addr add 10.6.$slot_num.100/16 dev eth1 + + echo "Waiting for supervisor to become reachable" + counter=0 + while [! ping -c 1 10.6.0.100 &> /dev/null && counter <= 300]; do + counter=$((counter+1)) + if [ $((counter%5)) -eq 0 ]; then + echo "Still waiting for supervisor to become reachable" + fi + sleep 1 + done + echo "Supervisor is reachable" + else + sudo ip -d addr show eth1 + sudo ip link set up dev eth1 + sudo ip addr add 10.6.$slot_num.100/16 dev eth1 + echo "Supervisor is up on midplane" + fi +} + +# Main script + +# Check if the metadata file exists +if [ ! -f "$metadata_file" ]; then + echo "Metadata file not found: $metadata_file" + exit 1 +fi + +# Disable Macsec Profile in minigraph +if [ -f "$minigraph_input_file" ] && [ -f "$remove_macsec_script" ]; then + python3 $remove_macsec_script "$minigraph_input_file" "$minigraph_output_file" + + if [ $? -eq 0 ]; then + echo "MacsecProfile successfully removed from $minigraph_output_file" + else + echo "Error: remove_macsec.py execution failed." + fi +fi + +# Disable DHCP Graph update service if enabled +if [ -f "$updategraph_file" ]; then + sudo sed -i 's/enabled=true/enabled=false/g' $updategraph_file + echo "Disabled updategraph service" +fi + +# Read metadata values +is_chassis=$(extract_metadata "is_chassis") +is_supervisor=$(extract_metadata "is_supervisor") +is_linecard=$(extract_metadata "is_linecard") +sup_slot_num=$(extract_metadata "sup_slot_num") +lc_slot_num=$(extract_metadata "lc_slot_num") +hw_sku=$(extract_metadata "hw_sku") + +# Display the extracted metadata +echo "is_chassis: $is_chassis" +echo "is_supervisor: $is_supervisor" +echo "is_linecard: $is_linecard" +echo "sup_slot_num: $sup_slot_num" +echo "lc_slot_num: $lc_slot_num" +echo "hw_sku: $hw_sku" + +# Check if the platform directory exists +check_directory "$platform_dir" + +# Check if the directory exists for the specified hardware SKU +hwsku_dir="/usr/share/sonic/device/$platform/$hw_sku" +check_directory "$hwsku_dir" + +# Set NUM_ASIC if the device is a linecard +if [ "$is_linecard" = true ]; then + asic_conf_file="$hwsku_dir/asic.conf" + get_num_asic "$asic_conf_file" +fi + +# Update NUM_ASIC in platform ASIC configuration file +update_asic_conf "$platform_dir/asic.conf" + +# Remove existing chassisdb.conf file +remove_existing_file "$platform_chassisdb_conf_file" + +# Configure chassisdb.conf file +if [ "$is_supervisor" = true ]; then + echo "start_chassis_db=1" > "$platform_chassisdb_conf_file" + echo "chassis_db_address=$chassis_db_address" >> "$platform_chassisdb_conf_file" + echo "midplane_subnet=$midplane_subnet" >> "$platform_chassisdb_conf_file" + echo "lag_id_start=1" >> "$platform_chassisdb_conf_file" + echo "lag_id_end=1024" >> "$platform_chassisdb_conf_file" + echo "Created $platform_chassisdb_conf_file for supervisor" +else + echo "chassis_db_address=$chassis_db_address" > "$platform_chassisdb_conf_file" + echo "midplane_subnet=$midplane_subnet" >> "$platform_chassisdb_conf_file" + echo "Created $platform_chassisdb_conf_file for linecard" +fi + +# Set macsec_enabled in platform_env.conf if the hardware SKU is supported +if match_pattern "$hw_sku" "${macsec_supported_arista_hwskus[@]}" || match_pattern "$hw_sku" "${macsec_supported_nokia_hwskus[@]}"; then + echo "macsec_enabled=1" > "$platform_env_file" + echo "Enabled MACsec in $platform_env_file for hwsku: $hw_sku" +fi + +# Set supversor in platform_env.conf if the device is marked as supervisor +if [ "$is_supervisor" = true ]; then + echo "supervisor=1" > "$platform_env_file" +fi + +slot_num=0 +if [ "$is_supervisor" = true ]; then + slot_num=$sup_slot_num +else + slot_num=$lc_slot_num +fi +# Add commands to rc.local +add_commands_to_rc_local "$slot_num" "$num_asic" + +# Stop and disable services +stop_disable_services "$num_asic" +# Unmask database since it is needed +sudo systemctl unmask database + +# Set up midplane address and wait for supervisor +setup_midplane_and_wait_for_supervisor "$slot_num" diff --git a/ansible/templates/vs_chassis_metadata.json.j2 b/ansible/templates/vs_chassis_metadata.json.j2 new file mode 100644 index 00000000000..d125c5cfd29 --- /dev/null +++ b/ansible/templates/vs_chassis_metadata.json.j2 @@ -0,0 +1,12 @@ +{ + "is_chassis": true, + "is_supervisor": {{ "true" if card_type == 'supervisor' else "false" }}, + "is_linecard": {{ "true" if card_type == 'linecard' else "false" }}, +{% if card_type == 'supervisor' %} + "sup_slot_num": {{ slot_num[4:] | int }}, +{% endif %} +{% if card_type == 'linecard' %} + "lc_slot_num": {{ slot_num[4:] | int }}, +{% endif %} + "hw_sku": "{{ hwsku }}" +} diff --git a/ansible/testbed-cli.sh b/ansible/testbed-cli.sh index 8a6c3fdacfb..e4918e3dfa7 100755 --- a/ansible/testbed-cli.sh +++ b/ansible/testbed-cli.sh @@ -331,6 +331,11 @@ function add_topo ansible-playbook fanout_connect.yml -i $vmfile --limit "$server" --vault-password-file="${passwd}" -e "dut=$duts" $fanout_options $@ fi + if [[ $topo == *"t2"* ]]; then + ansible-playbook -i ${inv_name} testbed_config_vchassis.yml --vault-password-file="$passfile" -l "$duts" -e testbed_name="$testbed_name" \ + -e topo="$topo" -e testbed_file=$tbfile -e vm_file=$vmfile -e server="$server" $@ + fi + # Delete the obsoleted arp entry for the PTF IP ip neighbor flush $ptf_ip || true done @@ -810,6 +815,25 @@ function deploy_topo_with_cache echo "Done!" } +function config_vs_chassis +{ + testbed_name=$1 + inventory=$2 + passfile=$3 + shift + shift + shift + + echo "Configuring testbed '$testbed_name' as a virtual chassis" + + read_file $testbed_name + + ansible-playbook -i "$inventory" testbed_config_vchassis.yml --vault-password-file="$passfile" -l "$duts" -e testbed_name="$testbed_name" \ + -e topo="$topo" -e testbed_file=$tbfile -e vm_file=$vmfile -e deploy=true $@ + + echo Done +} + vmfile=veos tbfile=testbed.yaml vm_type=ceos @@ -905,6 +929,8 @@ case "${subcmd}" in ;; collect-show-tech) collect_show_tech $@ ;; + config-vs-chassis) config_vs_chassis $@ + ;; *) usage ;; esac diff --git a/ansible/testbed_add_vm_topology.yml b/ansible/testbed_add_vm_topology.yml index 1f0a66c5207..3cb54e2b6aa 100644 --- a/ansible/testbed_add_vm_topology.yml +++ b/ansible/testbed_add_vm_topology.yml @@ -109,6 +109,17 @@ delegate_to: localhost when: duts_name.split(',')|length > 1 + - name: Determine if the testbed is a VS testbed + set_fact: + is_vs_testbed: true + when: hostvars[item].type is defined and hostvars[item].type == 'kvm' + loop: "{{ duts_name.split(',') }}" + + - name: Determine if the testbed is a VS chassis testbed + set_fact: + is_vs_chassis: true + when: base_topo == "t2" and (is_vs_testbed | default(false)) + roles: - { role: vm_set, action: 'stop_sonic_vm', when force_stop_sonic_vm is defined } - { role: vm_set, action: 'start_sonic_vm' } diff --git a/ansible/testbed_config_vchassis.yml b/ansible/testbed_config_vchassis.yml new file mode 100644 index 00000000000..98f6a2d70b4 --- /dev/null +++ b/ansible/testbed_config_vchassis.yml @@ -0,0 +1,87 @@ +- hosts: sonic + gather_facts: False + tasks: + - name: Check if the testbed is a virtual platform + set_fact: + is_vs_chassis: true + when: hostvars[inventory_hostname].type is defined and hostvars[inventory_hostname]['type'] == 'kvm' + + - name: Skip if not a virtual platform + meta: end_play + when: is_vs_chassis is not defined or is_vs_chassis == false + + - name: Check that variable testbed_name is defined + fail: msg="Define testbed_name variable with -e testbed_name=something" + when: testbed_name is not defined + + - debug: msg="testbed_name = {{ testbed_name }}" + + - name: Check that variable topo is defined + fail: msg="Define topo variable with -e topo=something" + when: topo is not defined + + - set_fact: + base_topo: "{{ topo.split('_') | first }}" + + - debug: msg="base_topo = {{ base_topo }}" + + - name: Check that base topo is "t2" + fail: msg="This playbook is only for T2 topologies" + when: base_topo != "t2" + + - name: Config VS chassis block + block: + - name: Load topo variables + include_vars: "vars/topo_{{ topo }}.yml" + + - name: Include server vars + include_vars: "host_vars/STR-ACS-VSERV-01.yml" + + - debug: msg="s/^exit 0/sudo ip address add {{ hostvars[inventory_hostname].ansible_host }}\/{{ mgmt_prefixlen }} dev eth0\nsudo ip route add 0.0.0.0\/0 via {{ mgmt_gw }} table default\nexit 0/" + + - name: Add mgmt address config commands to "/etc/rc.local" + shell: sed -i "s/^exit 0/sudo hostname {{ inventory_hostname }}\n\ + sudo ip address add {{ hostvars[inventory_hostname].ansible_host }}\/{{ mgmt_prefixlen }} dev eth0\n\ + sudo ip route add 0.0.0.0\/0 via {{ mgmt_gw }} table default\nexit 0/" "/etc/rc.local" + become: true + + - name: Set facts + set_fact: + hwsku: "{{ hostvars[inventory_hostname].hwsku }}" + num_asic: "{{ hostvars[inventory_hostname]['num_asics'] | default(1) }}" + card_type: "{{ hostvars[inventory_hostname].card_type | default('linecard') }}" + macsec_enabled: "{{ hostvars[inventory_hostname].macsec_enabled | default(false) }}" + slot_num: "{{ hostvars[inventory_hostname].slot_num }}" + + - name: Create vs_chassis_metadata.json + template: src=templates/vs_chassis_metadata.json.j2 + dest=/etc/sonic/vs_chassis_metadata.json + become: true + + - name: Copy setup_vs_chassis.sh to DUT + copy: + src: setup_vs_chassis.sh + dest: /usr/bin/setup_vs_chassis.sh + become: true + + - name: Execute setup_vs_chassis.sh to configure the DUT as a chassis device + shell: bash /usr/bin/setup_vs_chassis.sh + become: true + + - name: reboot DUTs + command: reboot + # async: 600 + # poll: 0 + become: true + + - name: Wait for switch to become reachable again + become: false + local_action: wait_for + args: + host: "{{ ansible_host }}" + port: 22 + state: started + search_regex: "OpenSSH_[\\w\\.]+ Debian" + delay: 10 + timeout: 600 + changed_when: false diff --git a/ansible/testbed_remove_vm_topology.yml b/ansible/testbed_remove_vm_topology.yml index e732b2f3eb9..5603fb5ecfd 100644 --- a/ansible/testbed_remove_vm_topology.yml +++ b/ansible/testbed_remove_vm_topology.yml @@ -88,6 +88,17 @@ delegate_to: localhost when: duts_name.split(',')|length > 1 + - name: Determine if the testbed is a VS testbed + set_fact: + is_vs_testbed: true + when: hostvars[item].type is defined and hostvars[item].type == 'kvm' + loop: "{{ duts_name.split(',') }}" + + - name: Determine if the testbed is a VS chassis testbed + set_fact: + is_vs_chassis: true + when: base_topo == "t2" and (is_vs_testbed | default(false)) + roles: - { role: vm_set, action: 'remove_topo' } - { role: vm_set, action: 'stop_sid' } diff --git a/ansible/testbed_renumber_vm_topology.yml b/ansible/testbed_renumber_vm_topology.yml index ca0832f0276..51005e4e23a 100644 --- a/ansible/testbed_renumber_vm_topology.yml +++ b/ansible/testbed_renumber_vm_topology.yml @@ -95,5 +95,16 @@ delegate_to: localhost when: duts_name.split(',')|length > 1 + - name: Determine if the testbed is a VS testbed + set_fact: + is_vs_testbed: true + when: hostvars[item].type is defined and hostvars[item].type == 'kvm' + loop: "{{ duts_name.split(',') }}" + + - name: Determine if the testbed is a VS chassis testbed + set_fact: + is_vs_chassis: true + when: base_topo == "t2" and (is_vs_testbed | default(false)) + roles: - { role: vm_set, action: 'renumber_topo' } diff --git a/ansible/vars/topo_t2-vs.yml b/ansible/vars/topo_t2-vs.yml deleted file mode 100644 index 22758503c5b..00000000000 --- a/ansible/vars/topo_t2-vs.yml +++ /dev/null @@ -1,162 +0,0 @@ -topology: - # 3 DUTs - 2 linecards (dut 0, 1) and 1 Supervisor card (dut 2) - 2 VMs per linecard - # line card dut0 is connected to 2 T3 VMs - one on lag and one on a single routed port - # line cards dut1 is connected to 2 T1 VMs - one on lag and one on a single routed port - # ptf ports are numbered 0-5 - dut_num: 3 - VMs: - ARISTA01T3: - vlans: - - "0.0@0" - - "0.1@1" - vm_offset: 0 - ARISTA03T3: - vlans: - - "0.2@2" - vm_offset: 1 - ARISTA01T1: - vlans: - - "1.0@3" - - "1.1@4" - vm_offset: 2 - ARISTA03T1: - vlans: - - "1.2@5" - vm_offset: 3 - - DUT: - loopback: - ipv4: - - 10.1.0.1/32 - - 10.1.0.2/32 - ipv6: - - FC00:10::1/128 - - FC00:11::1/128 - vs_chassis: - inband_port: - - 30 - - 30 - midplane_port: - - 31 - - 31 - - 31 - midplane_address: - - 10.0.5.1 - - 10.0.5.2 - - 10.0.5.16 - chassis_db_ip: 10.0.5.16 - -configuration_properties: - common: - podset_number: 400 - tor_number: 16 - tor_subnet_number: 8 - max_tor_subnet_number: 32 - tor_subnet_size: 128 - dut_asn: 65100 - dut_type: SpineRouter - nhipv4: 10.10.246.254 - nhipv6: FC0A::FF - core: - swrole: core - leaf: - swrole: leaf - -configuration: - ARISTA01T3: - properties: - - common - - core - bgp: - asn: 65200 - peers: - 65100: - - 10.0.0.0 - - FC00::1 - interfaces: - Loopback0: - ipv4: 100.1.0.1/32 - ipv6: 2064:100::1/128 - Ethernet1: - lacp: 1 - dut_index: 0 - Ethernet2: - lacp: 1 - dut_index: 0 - Port-Channel1: - ipv4: 10.0.0.1/31 - ipv6: FC00::2/126 - bp_interface: - ipv4: 10.10.246.1/24 - ipv6: fc0a::2/64 - - ARISTA03T3: - properties: - - common - - core - bgp: - asn: 65201 - peers: - 65100: - - 10.0.0.4 - - FC00::9 - interfaces: - Loopback0: - ipv4: 100.1.0.3/32 - ipv6: 2064:100::3/128 - Ethernet1: - ipv4: 10.0.0.5/31 - ipv6: FC00::a/126 - dut_index: 0 - bp_interface: - ipv4: 10.10.246.3/24 - ipv6: fc0a::6/64 - - ARISTA01T1: - properties: - - common - - leaf - bgp: - asn: 65000 - peers: - 65100: - - 10.0.0.6 - - FC00::d - interfaces: - Loopback0: - ipv4: 100.1.0.4/32 - ipv6: 2064:100::4/128 - Ethernet1: - lacp: 1 - dut_index: 1 - Ethernet2: - lacp: 1 - dut_index: 1 - Port-Channel1: - ipv4: 10.0.0.7/31 - ipv6: FC00::e/126 - bp_interface: - ipv4: 10.10.246.4/24 - ipv6: fc0a::8/64 - - ARISTA03T1: - properties: - - common - - leaf - bgp: - asn: 65000 - peers: - 65100: - - 10.0.0.10 - - FC00::15 - interfaces: - Loopback0: - ipv4: 100.1.0.6/32 - ipv6: 2064:100::6/128 - Ethernet1: - ipv4: 10.0.0.11/31 - ipv6: FC00::16/126 - dut_index: 1 - bp_interface: - ipv4: 10.10.246.6/24 - ipv6: fc0a::c/64 diff --git a/ansible/veos_vtb b/ansible/veos_vtb index c8e6011fc9b..a0ed1c665b6 100644 --- a/ansible/veos_vtb +++ b/ansible/veos_vtb @@ -35,7 +35,9 @@ all: - dualtor - dualtor-56 - dualtor-120 - - t2-vs + - t2 + - t2_2lc_min_ports-masic + - t2_2lc_min_ports - dualtor-mixed - dualtor-mixed-56 - dualtor-mixed-120 @@ -72,10 +74,10 @@ all: vlab-11: vlab-12: vlab-8k-01: - vlab-t2-01: - vlab-t2-02: - vlab-t2-sup: vlab-c-01: + vlab-t2-1-1: + vlab-t2-1-2: + vlab-t2-1-sup: vlab-c-02: ptf: hosts: @@ -196,34 +198,70 @@ all: ansible_hostv6: fec0::ffff:afa:4 type: simx hwsku: MSN3700 - vlab-t2-01: - ansible_host: 10.250.0.120 - ansible_hostv6: fec0::ffff:afa:10 + vlab-t2-1-1: + ansible_host: 10.250.0.123 + mgmt_prefixlen: 24 + mgmt_gw: 10.250.0.1 + ansible_hostv6: fec0::ffff:afa:13 + mgmt_gw_v6: fec0::1 type: kvm - hwsku: Force10-S6000 - serial_port: 9020 + hwsku: Nokia-IXR7250E-36x400G + serial_port: 9023 ansible_password: password ansible_user: admin + card_type: linecard + switch_type: voq slot_num: slot1 - vlab-t2-02: - ansible_host: 10.250.0.121 - ansible_hostv6: fec0::ffff:afa:11 + num_asics: 2 + macsec_enabled: True + voq_inband_intf: [Ethernet-IB0,Ethernet-IB1] + voq_inband_type: port + voq_inband_ip: [3.3.3.1/32,3.3.3.2/32] + voq_inband_ipv6: [3333::3:1/128,3333::3:2/128] + loopback4096_ip: [8.0.0.1/32,8.0.0.2/32] + loopback4096_ipv6: [2603:10e2:400::1/128,2603:10e2:400::2/128] + switchids: [0,2] + max_cores: 48 + frontend_asics: [0,1] + vlab-t2-1-2: + ansible_host: 10.250.0.124 + mgmt_prefixlen: 24 + mgmt_gw: 10.250.0.1 + ansible_hostv6: fec0::ffff:afa:14 + mgmt_gw_v6: fec0::1 type: kvm - hwsku: Force10-S6000 - serial_port: 9021 + hwsku: Nokia-IXR7250E-36x400G + serial_port: 9024 ansible_password: password ansible_user: admin + card_type: linecard + switch_type: voq slot_num: slot2 - vlab-t2-sup: - ansible_host: 10.250.0.122 - ansible_hostv6: fec0::ffff:afa:12 + num_asics: 2 + macsec_enabled: True + voq_inband_intf: [Ethernet-IB0,Ethernet-IB1] + voq_inband_type: port + voq_inband_ip: [3.3.3.3/32,3.3.3.4/32] + voq_inband_ipv6: [3333::3:3/128,3333::3:4/128] + loopback4096_ip: [8.0.0.3/32,8.0.0.4/32] + loopback4096_ipv6: [2603:10e2:400::3/128,2603:10e2:400::4/128] + switchids: [4,6] + max_cores: 48 + frontend_asics: [0,1] + vlab-t2-1-sup: + ansible_host: 10.250.0.125 + mgmt_prefixlen: 24 + mgmt_gw: 10.250.0.1 + ansible_hostv6: fec0::ffff:afa:15 + mgmt_gw_v6: fec0::1 type: kvm - hwsku: Force10-S6000 - serial_port: 9022 + hwsku: Nokia-IXR7250E-SUP-10 + serial_port: 9025 ansible_password: password ansible_user: admin card_type: supervisor - slot_num: slot3 + slot_num: slot0 + switch_type: fabric vlab-09: ansible_host: 10.250.0.115 ansible_hostv6: fec0::ffff:afb:1 diff --git a/ansible/vtestbed.yaml b/ansible/vtestbed.yaml index c4b1fe7fbc5..f94d81391d3 100644 --- a/ansible/vtestbed.yaml +++ b/ansible/vtestbed.yaml @@ -122,8 +122,8 @@ comment: Tests multi-asic virtual switch vm - conf-name: vms-kvm-t2 - group-name: vms6-4 - topo: t2-vs + group-name: vms6-4-m + topo: t2_2lc_min_ports-masic ptf_image_name: docker-ptf ptf: ptf-04 ptf_ip: 10.250.0.109/24 @@ -131,12 +131,12 @@ server: server_1 vm_base: VM0100 dut: - - vlab-t2-01 - - vlab-t2-02 - - vlab-t2-sup + - vlab-t2-1-1 + - vlab-t2-1-2 + - vlab-t2-1-sup inv_name: veos_vtb auto_recover: 'False' - comment: T2 Virtual chassis + comment: T2 Virtual chassis with Multi-ASIC LCs - conf-name: vms-kvm-t0-3 group-name: vms6-6 diff --git a/docs/testbed/README.testbed.vsChassis.md b/docs/testbed/README.testbed.vsChassis.md index 362032f4d13..5a59112beec 100644 --- a/docs/testbed/README.testbed.vsChassis.md +++ b/docs/testbed/README.testbed.vsChassis.md @@ -1,130 +1,52 @@ -# cEOS +# Description -This document discusses how to use cEOS as DUT neighbor device. +The goal of a virtual chassis is to simulate a SONiC disaggregated chassis on a Linux server. Currently, we have defined a virtual T2 testbed with Nokia-7250 SKUs in sonic-mgmt framework, named vms-kvm-t2. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-1-sup1) and two linecard modules(hostname: vlab-t2-1-1, vlab-t2-1-2). The midplane and in-band connectivity among the modules are established through OVS bridges. Each linecard has 2 eBGP peers - 1 over a 2-port LAG and 1 over a single interface port as shown below: -cEOS is the container-based EOS. All the software running inside -the container. Compared with vEOS, cEOS has much smaller memory -footprint. +The topology graph is as follows: -Follow [instruction](README.testbed.VsSetup.md) to setup cEOS testbed. + VM0100 VM0101 + || | + +---------||------------------------------------------|-------------------+ + | || | | + | +----------------------------------------------------------------+ | + | | Linecard1 | | + | +----------------------------------------------------------------+ | + | | | | + | +------------+ +--------------------+ +-----------------+ | + | | Supervisor |------| ovs br-T2Midplane | | ovs br-T2Inband | | + | +------------+ +--------------------+ +-----------------+ | + | | | | + | +----------------------------------------------------------------+ | + | | Linecard2 | | + | +----------------------------------------------------------------+ | + | || | | + +-----------||-----------------------------------------|------------------+ + || | + VM0102 VM0103 -In below example, there are four cEOS containers. +# Prerequesites -``` -lgh@jenkins-worker-15:~$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -fe48c207a51c ceosimage:4.23.2F-1 "/sbin/init systemd.…" 8 days ago Up 8 days ceos_vms6-1_VM0103 -52297010e66a ceosimage:4.23.2F-1 "/sbin/init systemd.…" 8 days ago Up 8 days ceos_vms6-1_VM0102 -8dd95269b312 ceosimage:4.23.2F-1 "/sbin/init systemd.…" 8 days ago Up 8 days ceos_vms6-1_VM0101 -3a50dd481bfb ceosimage:4.23.2F-1 "/sbin/init systemd.…" 8 days ago Up 8 days ceos_vms6-1_VM0100 -b91b48145def debian:jessie "bash" 8 days ago Up 8 days net_vms6-1_VM0103 -d1ff26d84249 debian:jessie "bash" 8 days ago Up 8 days net_vms6-1_VM0102 -1489f52b9617 debian:jessie "bash" 8 days ago Up 8 days net_vms6-1_VM0101 -ce1214a008ed debian:jessie "bash" 8 days ago Up 8 days net_vms6-1_VM0100 -``` +Before you begin, ensure you have followed the sonic-mgmt virtual testbed documentation: sonic-mgmt/README.testbed.VsSetup.md to setup ansible configuration and get yourself familiar with the sonic-mgmt framework. -## Resource consumption +For SONiC images, you should use the ones built from sonic-buildimage-msft/202205 branch or sonic-buildimage-msft/202405 branch. -A cEOS containers consumes around 1G memory. +# T2 Virtual Chassis Setup Process (with vSONiC neighbors) -``` -lgh@jenkins-worker-15:~$ docker stats --no-stream -CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 6 -fe48c207a51c ceos_vms6-1_VM0103 2.04% 970.9MiB / 125.9GiB 0.75% 0B / 0B 365MB / 55.8GB 138 -52297010e66a ceos_vms6-1_VM0102 2.19% 965.4MiB / 125.9GiB 0.75% 0B / 0B 237MB / 55.6GB 139 -8dd95269b312 ceos_vms6-1_VM0101 1.93% 980.9MiB / 125.9GiB 0.76% 0B / 0B 300MB / 55.9GB 138 -3a50dd481bfb ceos_vms6-1_VM0100 2.05% 970.2MiB / 125.9GiB 0.75% 0B / 0B 365MB / 56.1GB 138 -``` + 1. Enter your SONiC management container with this command: `docker exec -it /bin/bash` + 2. Navagate to the ansible directory: `cd /data/sonic-mgmt/ansible` + 3. Start a bunch of vSONiC neighbors: `./testbed-cli.sh -t vtestbed.yaml -m veos_vtb -n 8 -k vsonic start-vms server_1 password.txt` + 4. Spin up a t2 testbed using this command: `./testbed-cli.sh -t vtestbed.yaml -m veos_vtb -k vsonic add-topo vms-kvm-t2 password.txt` + 5. Deploy the minigraph: ./testbed-cli.sh -t vtestbed.yaml -m veos_vtb deploy-mg vms-kvm-t2 veos_vtb password.txt -## Network Setup +# Accessing Supervisor and Linecards -We first create a base container `net_${testbed_name}_${vm_name}`, inject six ethernet ports into the base container, -and then start cEOS `ceos_${testbed_name}_${vm_name}` container on top of the base container. The six ethernet ports -are used for -- 1 management port -- 4 front panel ports to DUT -- 1 backplane port to PTF docker +You will now have a virtual chassis deployed. Congratulations! You can access your supervisor by SSHing into its management IP: `ssh admin@10.250.0.125` +Similarly, you should be able to access the linecards via their management IPs as well. +The management IPs of all the modules can be found in the inventory file veos_vtb. -``` - +------------+ +----+ - | cEOS Ma0 +--------- VM0100-m ---+ br | - | | +----+ - | | - | | +--------------+ - | Et1 +----------VM0100-t0---+ br-VM0100-0 | - | | +--------------+ - | | - | | +--------------+ - | Et2 +----------VM0100-t1---+ br-VM0100-1 | - | | +--------------+ - | | - | | +--------------+ - | Et3 +----------VM0100-t2---+ br-VM0100-2 | - | | +--------------+ - | | - | | +--------------+ - | Et4 +----------VM0100-t3---+ br-VM0100-3 | - | | +--------------+ - | | - | | +--------------+ - | Et5 +----------VM0100-back--+ br-b-vms6-1 | - | | +--------------+ - +------------+ -``` +# Running tests -## Configuration +A T2 testbed consists of multiple DUTs since there are multiple modules each of which is deemed as a testable device. So, when you run tests on T2 virtual chassis, you generally should not use the `-d` option. -The `/mnt/flash` in cEOS container is mount to `/data/ceos/ceos_${testbed_name}_${vm_name}` on the host. The `/mnt/flash` -contiains the configuration file and logs. - -``` -lgh@jenkins-worker-15:~$ ls -l /data/ceos/ceos_vms6-1_VM0100/ -total 40 --rw-rw-r--+ 1 root root 924 Mar 31 07:35 AsuFastPktTransmit.log -drwxrwxr-x+ 2 root root 4096 Mar 31 03:31 Fossil --rw-rw-r--+ 1 root root 568 Mar 31 07:35 SsuRestore.log --rw-rw-r--+ 1 root root 568 Mar 31 07:35 SsuRestoreLegacy.log -drwxr-xr-x+ 4 897 88 4096 Mar 31 07:35 archive -drwxrwx---+ 3 root root 4096 Mar 18 06:12 debug -drwxrwxr-x+ 2 root root 4096 Mar 18 06:12 fastpkttx.backup --rw-rw-r--+ 1 root root 180 Mar 31 07:35 kickstart-config -drwxrwxr-x+ 3 root root 4096 Apr 8 09:11 persist --rw-rwxr--+ 1 root root 1915 Mar 18 06:12 startup-config -``` - -## Login - -There are two ways to get into cEOS container - -1. docker exec -``` -lgh@jenkins-worker-15:~$ docker exec -it ceos_vms6-1_VM0100 Cli -ARISTA01T1>show int status -Port Name Status Vlan Duplex Speed Type Flags Encapsulation -Et1 connected in Po1 full unconf EbraTestPhyPort -Et2 connected 1 full unconf EbraTestPhyPort -Et3 connected 1 full unconf EbraTestPhyPort -Et4 connected 1 full unconf EbraTestPhyPort -Et5 backplane connected routed full unconf EbraTestPhyPort -Ma0 connected routed full 10G 10/100/1000 -Po1 connected routed full unconf N/A - -ARISTA01T1> -``` - -2. ssh -``` -lgh@jenkins-worker-15:~$ ssh admin@10.250.0.51 -Password: -ARISTA01T1>show int status -Port Name Status Vlan Duplex Speed Type Flags Encapsulation -Et1 connected in Po1 full unconf EbraTestPhyPort -Et2 connected 1 full unconf EbraTestPhyPort -Et3 connected 1 full unconf EbraTestPhyPort -Et4 connected 1 full unconf EbraTestPhyPort -Et5 backplane connected routed full unconf EbraTestPhyPort -Ma0 connected routed full 10G 10/100/1000 -Po1 connected routed full unconf N/A - -ARISTA01T1> -``` +Example test run: (assuming you are using vsonic neighbors) +./run_tests.sh -n vms-kvm-t2 -c bgp/test_bgp_fact.py -f vtestbed.yaml -i ../ansible/veos_vtb -e "--neighbor_type=sonic" diff --git a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml index 5497a0373a7..d4dd3b09e16 100644 --- a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml +++ b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml @@ -2529,7 +2529,7 @@ vrf/test_vrf.py::TestVrfAclRedirect: reason: "Switch does not support ACL REDIRECT_ACTION." conditions_logical_operator: or conditions: - - "len([capabilities for capabilities in switch.values() if 'REDIRECT_ACTION' in capabilities]) == 0" + - "'switch' in vars() and len([capabilities for capabilities in switch.values() if 'REDIRECT_ACTION' in capabilities]) == 0" - "asic_type in ['vs']" vrf/test_vrf_attr.py: