Repository: sonic-net/sonic-mgmt
Pull request #16716: Add support for set up virtual multi-asic chassis
Author: login: BYGX-wcr
Created: 2025-01-29T23:07:05Z | Merged: 2025-03-04T17:53:26Z

<!--
Please make sure you've read and understood our contributing guidelines;
https://github.com/sonic-net/SONiC/blob/gh-pages/CONTRIBUTING.md

Please provide following information to help code review process a bit easier:
-->
### Description of PR
<!--
- Please include a summary of the change and which issue is fixed.
- Please also include relevant motivation and context. Where should reviewer start? background context?
- List any dependencies that are required for this change.
-->

Summary: I modified the ansible framework to add support for automatic setup of virtual multi-asic chassis.

### Type of change

<!--
- Fill x for your type of change.
- e.g.
- [x] Bug fix
-->

- [ ] Bug fix
- [x] Testbed and Framework(new/improvement)
- [ ] New Test case
    - [ ] Skipped for non-supported platforms
- [ ] Test case improvement


### Back port request
- [ ] 202012
- [ ] 202205
- [ ] 202305
- [ ] 202311
- [ ] 202405
- [ ] 202411

### Approach
#### What is the motivation for this PR?

Currently, the virtual chassis setup in sonic-mgmt is very primitive and manual.

#### How did you do it?

Enhanced the ansible framework to support automatic topo-connect and minigraph generation of virtual multi-asic chassis

#### How did you verify/test it?

Set up the virtual chassis with 202205/202405-chassis VS images

#### Any platform specific information?

Only tested to work with 202205 and 202405 images so far.

#### Supported testbed topology if it's a new test case?

### Documentation
<!--
(If it's a new feature, new test case)
Did you update documentation/Wiki relevant to your implementation?
Link to the wiki page?
-->



Diff:
diff --git a/ansible/config_sonic_basedon_testbed.yml b/ansible/config_sonic_basedon_testbed.yml
index bcfb2ba242f..8d52952ae9b 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 <dut_hostname>-<port_num + 1>. 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..e2f26648daf 100644
--- a/ansible/roles/vm_set/tasks/get_dut_port.yml
+++ b/ansible/roles/vm_set/tasks/get_dut_port.yml
@@ -11,9 +11,32 @@
     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') }}"
+
+- 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 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
     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 cf37dfacd40..60d1192ee0d 100644
--- a/ansible/roles/vm_set/templates/sonic.xml.j2
+++ b/ansible/roles/vm_set/templates/sonic.xml.j2
@@ -22,6 +22,13 @@
     <model fallback='forbid'/>
     <topology sockets='1' dies='1' cores='3' threads='2'/>
   </cpu>
+{% elif hwsku == 'Nokia-IXR7250E-36x400G' or hwsku == 'Nokia-IXR7250E-SUP-10' %}
+  <memory unit='GB'>8</memory>
+  <vcpu placement='static'>10</vcpu>
+  <cpu mode='host-model'>
+    <model fallback='forbid'/>
+    <topology sockets='1' dies='1' cores='5' threads='2'/>
+  </cpu>
 {% elif "dualtor" in topo %}
   <memory unit='GiB'>8</memory>
   <currentMemory unit='GiB'>8</currentMemory>
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..827e160e19e
--- /dev/null
+++ b/ansible/testbed_config_vchassis.yml
@@ -0,0 +1,78 @@
+- hosts: sonic
+  gather_facts: False
+  tasks:
+  - 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 <container_name> /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 381b658ac66..0ea7577e8e9 100644
--- a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml
+++ b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml
@@ -2505,7 +2505,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:


Reviews:
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-06T03:15:36Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-06T03:16:16Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-06T03:19:01Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-06T03:21:08Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-12T01:03:57Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-12T01:04:03Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-12T01:04:11Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-12T01:04:18Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T21:45:05Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T21:50:23Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T21:51:59Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T21:53:05Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T21:54:14Z
user: login: deepak-singhal0408
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:04:11Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:41:24Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:43:35Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:43:55Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:45:42Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-13T22:45:48Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-14T00:27:24Z
user: login: deepak-singhal0408
body: Looks good!! Thanks,
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-02-14T18:05:39Z
user: login: wangxin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-02-28T02:48:08Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-28T19:14:54Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-28T19:15:18Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-28T19:23:36Z
user: login: wangxin
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-03T17:09:13Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T00:09:00Z
user: login: wangxin
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-04T00:38:24Z
user: login: nhe-NV
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T10:41:39Z
user: login: BYGX-wcr
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T16:51:40Z
user: login: nhe-NV
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T01:07:42Z
user: login: nhe-NV
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T01:56:43Z

Review comments:
diff_hunk: @@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+from ansible.module_utils.basic import AnsibleModule
path: ansible/library/gather_sysports.py
user: login: deepak-singhal0408
body: this is probably not needed.. please check how its generated for physical chassis.
created_at: 2025-02-06T03:15:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -47,6 +47,14 @@ topology:
         - "1.26@11"
       vm_offset: 7
   DUT:
+    vs_chassis:
+      inband_port:
path: ansible/vars/topo_t2_2lc_min_ports-masic.yml
user: login: deepak-singhal0408
body: lets not hardcode this. we could get this info from port_config.ini
created_at: 2025-02-06T03:16:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,6 +109,11 @@
     delegate_to: localhost
     when: duts_name.split(',')|length > 1
 
+  - name: Set up midplane and inband bridges
path: ansible/testbed_add_vm_topology.yml
user: login: deepak-singhal0408
body: this is already currently done in vm_topology.py. can we reuse that code? bridge name can be generated dynamically. 
created_at: 2025-02-06T03:19:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,6 +78,12 @@ all:
         vlab-t2-02:
         vlab-t2-sup:
         vlab-c-01:
+        vlab-t2-03:
path: ansible/veos_vtb
user: login: deepak-singhal0408
body: follow more uniform/extendable DUT naming convention.. something like vlab-t2-<testbed_bumber>-<01/02/sup>
created_at: 2025-02-06T03:21:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,6 +78,12 @@ all:
         vlab-t2-02:
         vlab-t2-sup:
         vlab-c-01:
+        vlab-t2-03:
path: ansible/veos_vtb
user: login: BYGX-wcr
body: updated
created_at: 2025-02-12T01:03:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -109,6 +109,11 @@
     delegate_to: localhost
     when: duts_name.split(',')|length > 1
 
+  - name: Set up midplane and inband bridges
path: ansible/testbed_add_vm_topology.yml
user: login: BYGX-wcr
body: updated
created_at: 2025-02-12T01:04:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -47,6 +47,14 @@ topology:
         - "1.26@11"
       vm_offset: 7
   DUT:
+    vs_chassis:
+      inband_port:
path: ansible/vars/topo_t2_2lc_min_ports-masic.yml
user: login: BYGX-wcr
body: removed
created_at: 2025-02-12T01:04:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+from ansible.module_utils.basic import AnsibleModule
path: ansible/library/gather_sysports.py
user: login: BYGX-wcr
body: removed
created_at: 2025-02-12T01:04:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -111,18 +111,22 @@
     "DEVICE_NEIGHBOR_METADATA": {
 {% set separator = joiner(",") %}
 {% for dut_name in duts_name.split(',') %}
-{{ separator() }}
         "{{ dut_name }}": {
                 "hwsku": "SONiC-VM",
                 "mgmt_addr": "{{ hostvars[dut_name]['ansible_host'] }}",
                 "type": "TorRouter"
         },
+{% endfor %}
         "exabgp": {
                 "hwsku": "exabgp",
                 "mgmt_addr": "{{ ptf_ip }}",
                 "type": "SpineRouter"
+        },
path: ansible/roles/sonic/templates/configdb.j2
user: login: deepak-singhal0408
body: can you explain this change?
created_at: 2025-02-13T21:45:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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:
path: docs/testbed/README.testbed.vsChassis.md
user: login: deepak-singhal0408
body: vms-kvm-t2-min: should it be vms-kvm-t2?
created_at: 2025-02-13T21:50:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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 <container_name> /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-min password.txt`
path: docs/testbed/README.testbed.vsChassis.md
user: login: deepak-singhal0408
body: same here
created_at: 2025-02-13T21:51:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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 <container_name> /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-min password.txt`
+    5. Deploy the minigraph: ./testbed-cli.sh -t vtestbed.yaml -m veos_vtb deploy-mg vms-kvm-t2-min 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-min -c bgp/test_bgp_fact.py -f vtestbed.yaml -i ../ansible/veos_vtb -e "--neighbor_type=sonic"
path: docs/testbed/README.testbed.vsChassis.md
user: login: deepak-singhal0408
body: does it work with ceos neighbors?
created_at: 2025-02-13T21:53:05Z
author_association: CONTRIBUTOR
path: ansible/vars/topo_t2-vs.yml
user: login: deepak-singhal0408
body: can we keep this file. just in case, someone is using it?
created_at: 2025-02-13T21:54:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -75,6 +78,12 @@ all:
         vlab-t2-02:
         vlab-t2-sup:
         vlab-c-01:
+        vlab-t2-03:
path: ansible/veos_vtb
user: login: deepak-singhal0408
body: @BYGX-wcr I dont see this change?
created_at: 2025-02-13T22:04:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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 <container_name> /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-min password.txt`
+    5. Deploy the minigraph: ./testbed-cli.sh -t vtestbed.yaml -m veos_vtb deploy-mg vms-kvm-t2-min 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-min -c bgp/test_bgp_fact.py -f vtestbed.yaml -i ../ansible/veos_vtb -e "--neighbor_type=sonic"
path: docs/testbed/README.testbed.vsChassis.md
user: login: BYGX-wcr
body: yes, the neighbor type does not matter
created_at: 2025-02-13T22:41:24Z
author_association: CONTRIBUTOR
path: ansible/vars/topo_t2-vs.yml
user: login: BYGX-wcr
body: there is no one using this after I deleted the old virtual chassis testbed
created_at: 2025-02-13T22:43:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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 <container_name> /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-min password.txt`
+    5. Deploy the minigraph: ./testbed-cli.sh -t vtestbed.yaml -m veos_vtb deploy-mg vms-kvm-t2-min 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-min -c bgp/test_bgp_fact.py -f vtestbed.yaml -i ../ansible/veos_vtb -e "--neighbor_type=sonic"
path: docs/testbed/README.testbed.vsChassis.md
user: login: BYGX-wcr
body: yes, neighbor type does not matter
created_at: 2025-02-13T22:43:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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:
path: docs/testbed/README.testbed.vsChassis.md
user: login: BYGX-wcr
body: updated
created_at: 2025-02-13T22:45:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-min. It uses a minimal topology configuration (topo_t2_2lc_min_ports-masic) that cobtains 3 KVM devices: one supervisor module(hostname: vlab-t2-sup1) and two linecard modules(hostname: vlab-t2-03, vlab-t2-04). 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 <container_name> /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-min password.txt`
path: docs/testbed/README.testbed.vsChassis.md
user: login: BYGX-wcr
body: updated
created_at: 2025-02-13T22:45:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -111,18 +111,22 @@
     "DEVICE_NEIGHBOR_METADATA": {
 {% set separator = joiner(",") %}
 {% for dut_name in duts_name.split(',') %}
-{{ separator() }}
         "{{ dut_name }}": {
                 "hwsku": "SONiC-VM",
                 "mgmt_addr": "{{ hostvars[dut_name]['ansible_host'] }}",
                 "type": "TorRouter"
         },
+{% endfor %}
         "exabgp": {
                 "hwsku": "exabgp",
                 "mgmt_addr": "{{ ptf_ip }}",
                 "type": "SpineRouter"
+        },
path: ansible/roles/sonic/templates/configdb.j2
user: login: BYGX-wcr
body: I forgot why I added this change. I vaguely remember that this was added to cope with some test. Since this change is not necessary for bringing up virtual chassis, I have removed this change. If we encounter problems in the future, we can add it back later.
created_at: 2025-02-14T00:27:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-{tb_name}-inb"
+VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE = "br-{tb_name}-mid"
path: ansible/roles/vm_set/library/vm_topology.py
user: login: wangxin
body: There is a chance that the bridge name length exceeds 16 characters. This is not allowed by Linux. 
Is there a way to make it more robust?

For other testbeds, we embed `vm_set_name` here rather than tb_name. Why introduce a new argument `tb_name`?
created_at: 2025-02-28T02:04:42Z
author_association: COLLABORATOR
diff_hunk: @@ -2118,12 +2120,14 @@ def main():
         supports_check_mode=False)
 
     cmd = module.params['cmd']
+    tb_name = module.params['tb_name']
path: ansible/roles/vm_set/library/vm_topology.py
user: login: wangxin
body: Why introduce new argument `tb_name` here? Can we use the `vm_set_name` like other testbeds?
created_at: 2025-02-28T02:07:20Z
author_association: COLLABORATOR
diff_hunk: @@ -228,12 +230,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) }}"
path: ansible/roles/vm_set/tasks/add_topo.yml
user: login: wangxin
body: Need to cover the updating of `vm_topology` call in playbook `ansible\roles\vm_set\tasks\renumber_topo.yml`. This playbook is for the `testbed-cli.sh restart-ptf` scenario.
created_at: 2025-02-28T02:47:04Z
author_association: COLLABORATOR
diff_hunk: @@ -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-{tb_name}-inb"
+VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE = "br-{tb_name}-mid"
path: ansible/roles/vm_set/library/vm_topology.py
user: login: BYGX-wcr
body: In line 249~255, we have a check on the length of the bridge name. Theorectically, the vm_set_name can be used for the naming purpose as well. Yet, I found that the vm_set_name is not guaranteed to be unique for every testbed. That will introduce incorrect setup if the user is not aware of the underlying mechanism.
created_at: 2025-02-28T19:14:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2118,12 +2120,14 @@ def main():
         supports_check_mode=False)
 
     cmd = module.params['cmd']
+    tb_name = module.params['tb_name']
path: ansible/roles/vm_set/library/vm_topology.py
user: login: BYGX-wcr
body: Check my comment above.
created_at: 2025-02-28T19:15:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -228,12 +230,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) }}"
path: ansible/roles/vm_set/tasks/add_topo.yml
user: login: BYGX-wcr
body: updated. Thanks for pointing that out!
created_at: 2025-02-28T19:23:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -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-{tb_name}-inb"
+VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE = "br-{tb_name}-mid"
path: ansible/roles/vm_set/library/vm_topology.py
user: login: wangxin
body: It should be OK as long as testbeds deployed on same test server are not using same vm_set_name. Did you notice testbeds on same server using same vm_set_name?
created_at: 2025-03-03T17:09:13Z
author_association: COLLABORATOR
diff_hunk: @@ -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-{tb_name}-inb"
+VS_CHASSIS_MIDPLANE_BRIDGE_NAME_TEMPLATE = "br-{tb_name}-mid"
path: ansible/roles/vm_set/library/vm_topology.py
user: login: BYGX-wcr
body: I have updated the code to remove tb_name and use vm_set_name instead.
created_at: 2025-03-04T00:08:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,4 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
path: ansible/roles/vm_set/tasks/get_dut_port.yml
user: login: nhe-NV
body: HI @BYGX-wcr 

After I merged to PR to my local, I ecounter failure when remove topo. Can you help to check? I did a bit debug for it. Looks like the problem is here: 

dut_midplane_ports  and dut_inband_ports  will be AnsibleUndefined if the condition in the line 40 is not true ( when: external_port is not defined and hostvars[dut_name].type is defined and hostvars[dut_name]['type'] == 'kvm') 

this will lead the retrun of the "get_dut_port.yml"  will be like followin:, and the value of duts_inband_ports and duts_midplane_ports will be "str" but the expected is dict, so it failed the remove-topo in my end.

......
......
"duts_inband_ports": "{u'hostname': AnsibleUndefined}", 
  "duts_mgmt_port": [
      ""
  ], 
 "duts_midplane_ports": "{u'hostname': AnsibleUndefined}"
.............

Here is the failure msg: 
    **"msg": "argument duts_inband_ports is of type <type 'str'> and we were unable to convert to dict: unable to evaluate string as dictionary"**

created_at: 2025-03-13T09:29:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,4 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
path: ansible/roles/vm_set/tasks/get_dut_port.yml
user: login: nhe-NV
body: I tried to add the default value of it the 2 params, then it start to work:

    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([]) } ) }}"

@BYGX-wcr  Will you help to check and fix the issue or I can open a PR to move forward with this change to resolve the issue? 
created_at: 2025-03-13T10:41:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,4 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
path: ansible/roles/vm_set/tasks/get_dut_port.yml
user: login: BYGX-wcr
body: yes, the root cause of this issue should be as you mentioned.
created_at: 2025-03-13T16:51:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,4 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
path: ansible/roles/vm_set/tasks/get_dut_port.yml
user: login: nhe-NV
body: @BYGX-wcr What will be the next step? will you open the PR to fix the issue? @wangxin  For your visibilility
created_at: 2025-03-14T01:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,4 +54,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 } ) }}"
+    duts_inband_ports: "{{ duts_inband_ports|default({}) | combine( { dut_name: dut_inband_ports } ) }}"
path: ansible/roles/vm_set/tasks/get_dut_port.yml
user: login: nhe-NV
body: @BYGX-wcr  Here is the PR: https://github.com/sonic-net/sonic-mgmt/pull/17516
created_at: 2025-03-14T01:56:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: mssonicbld
created_at: 2025-01-29T23:07:08Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-01-29T23:07:19Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-01-29T23:17:36Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-01-29T23:17:49Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-01-31T06:25:48Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-01-31T06:26:01Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-03T23:41:45Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-03T23:41:56Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-04T19:42:49Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-04T19:43:02Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-04T19:43:43Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-04T19:43:57Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-04T22:01:08Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-04T22:01:21Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-05T21:13:41Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-05T21:13:54Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-11T06:45:28Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-11T06:45:41Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-12T00:58:41Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-12T00:58:53Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-12T18:38:25Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-12T18:38:39Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-13T02:13:10Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-13T02:13:22Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-13T22:45:43Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-13T22:45:56Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-14T00:24:42Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-14T00:24:56Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-19T22:04:00Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-19T22:04:13Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: wangxin
created_at: 2025-02-28T02:53:39Z
body: There is also merge conflicts need to be resolved.
author_association: COLLABORATOR
user: login: mssonicbld
created_at: 2025-02-28T18:48:12Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-28T18:48:24Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-28T19:23:23Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-28T19:23:34Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-02-28T19:51:24Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-02-28T19:51:35Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: mssonicbld
created_at: 2025-03-03T22:01:10Z
body: /azp run
author_association: COLLABORATOR
user: login: azure-pipelines[bot]
created_at: 2025-03-03T22:01:22Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE
user: login: nhe-NV
created_at: 2025-03-13T10:44:04Z
body: @wangxin I just see you opened a PR to revert this,  Can you check my comments  in this PR, maybe my proposed change will also work for your end? if  so, then no need to revert it right?
author_association: CONTRIBUTOR