diff --git a/ansible/config_sonic_basedon_testbed.yml b/ansible/config_sonic_basedon_testbed.yml
new file mode 100644
index 00000000000..94ff94e8b80
--- /dev/null
+++ b/ansible/config_sonic_basedon_testbed.yml
@@ -0,0 +1,98 @@
+# This Playbook run time generate matching configuration file for SONiC switch(Minigraph) based on a specific testbed topology specified in testbed.csv
+# When user call testbed-cli to deploy a testbed topology, use this playbook to generate matching SONiC minigraph file and deploy it into SONiC switch under test.
+# Or when you know your topology name, you may use this playbook alone to generate a minigraph matching your topology name without deploy it.
+#
+# VM Topologies are defined inside of vars/ directory in files vars/topo_{{ topology_name}}.yml
+# Every topology should have a name to distinct one topology from another on the server
+# Every topology contains a ptf container which will be used as placeholder for the injected interfaces from VMs, or direct connections to PTF host
+# VMs inventory file is also required to have all VMs ready for generating the minigraph file
+# VMs inventory is in file 'veos'
+#
+# Template files for generating minigraph.xml are defined in template/topo directory
+#
+# To generate and deploy minigraph for SONiC switch matching the VM topology please use following command
+# ansible-playbook -i lab config_sonic_basedon_testbed.yml -l sonic_dut_name -e vm_base=VM0300 -e topo=t0 [-e deploy=true]
+#
+# Parameters
+# -l str-msn2700-01 - the sonic_dut_name you are going to generate minigraph for
+# -e vm_base=VM0300 - the VM name which is used to as base to calculate VM name for this set
+# -e topo=t0 - the name of topology to generate minigraph file
+# -e deploy=True - if deploy the newly generated minigraph to the targent DUT, default is false if not defined
+#
+# After minigraph.xml is generated, the playbook will replace the original minigraph file under ansible/minigraph/ with the newly generated minigraph file for the SONiC device.
+# The playbook will based on deploy=True or False to deside if load the SONiC device with new minigraph or not.
+# If deploy=true, the playbook will reboot the SONiC switch after change to new minigraph to make it take effect
+#
+####################################################################################################################################################################################
+
+- hosts: sonic
+ gather_facts: yes
+ tasks:
+ - fail: msg="need to provide topology type and vm base like topo=t0 and vm_base=VM100"
+ when: (topo is not defined) or (vm_base is not defined)
+
+ - fail: msg="need hwsku, interface speed, netmask and interface prefix/postfix defined to generate configuration file"
+ when: (hwsku is not defined) or (iface_speed is not defined) or (mgmt_subnet_mask_length is not defined)
+
+ - set_fact:
+ VM_topo: "{% if 'ptf' in topo %}False{% else %}True{% endif %}"
+ remote_dut: "{{ ansible_ssh_host }}"
+ template_name: "{{ 't1' if topo=='ptf32' else topo }}"
+
+ - testbed_vm_info: base_vm="{{ vm_base }}" topo="{{ topo }}"
+ connection: local
+ when: VM_topo
+
+ - name: find interface name mapping
+ port_alias: hwsku="{{ hwsku }}"
+ connection: local
+
+ - debug: var=port_alias
+
+ - name: save original minigraph file (if original file does not exist, then ignore errors)
+ shell: mv minigraph/{{ inventory_hostname }}.xml minigraph/{{ inventory_hostname }}.xml.orig
+ connection: local
+ ignore_errors: true
+
+ - name: create minigraph file in minigraph folder
+ become: true
+ template: src=templates/topo/{{ template_name }}.j2
+ dest=minigraph/{{ inventory_hostname}}.xml
+ connection: local
+
+ - block:
+ - name: saved original minigraph file (if original file may don't exist, then ignore errors)
+ shell: mv /etc/sonic/minigraph.xml /etc/sonic/minigraph.xml.orig
+ become: true
+ ignore_errors: true
+
+ - name: create minigraph file for SONiC device
+ template: src=templates/topo/{{ template_name }}.j2
+ dest=/etc/sonic/minigraph.xml
+ become: true
+
+ - name: disable automatic minigraph update if we are deploying new minigraph into SONiC
+ lineinfile:
+ name: /etc/sonic/updategraph.conf
+ regexp: '^enabled='
+ line: 'enabled=false'
+ become: true
+
+ # reload the device and wait it to come back
+ - name: Reboot is required for minigraph change
+ shell: sleep 2 && shutdown -r now "Ansible Create new configuration Minigraph file, triggered reboot."
+ async: 1
+ poll: 0
+ become: true
+ ignore_errors: true
+
+ - name: waiting for switch to come back
+ local_action:
+ wait_for host={{ remote_dut }}
+ port=22
+ state=started
+ delay=30
+ timeout=300
+ become: false
+ changed_when: false
+ when: deploy is defined and deploy|bool == true
diff --git a/ansible/group_vars/lab/lab.yml b/ansible/group_vars/lab/lab.yml
index 2cafa9d4b04..4b33f607cad 100644
--- a/ansible/group_vars/lab/lab.yml
+++ b/ansible/group_vars/lab/lab.yml
@@ -1,15 +1,34 @@
---
-#starlab (str) group variables
-# file: group_vars/str.yml
+#testlab (lab) group variables
+# file: group_vars/lab.yml
# ntp variables
ntp_servers: ['10.0.0.1', '10.0.0.2']
# syslog variables
syslog_servers: ['10.0.0.5', '10.0.0.6']
-#
+
# dns variables
dns_servers: ['10.0.0.5', '10.0.0.6']
+
+# forced_mgmt_routes
+forced_mgmt_routes: ['10.0.0.100/31', '10.250.0.8', '10.255.0.0/28']
+
+# ErspanDestinationIpv4
+erspan_dest: ['10.0.0.7']
+
+radius_servers: []
+
+tacacs_servers: ['10.0.0.9', '10.0.0.8']
+
+# tacacs grous
+tacacs_group: 'testlab'
+
+# snmp servers
+snmp_servers: ['10.0.0.9']
+
+# dhcp replay servers
+dhcp_servers: ['10.0.0.1']
#
# snmp variables
snmp_rocommunity: public
diff --git a/ansible/lab b/ansible/lab
index f5803e3a7b9..056a2168dc7 100644
--- a/ansible/lab
+++ b/ansible/lab
@@ -1,14 +1,19 @@
-[sonic_latest]
-str-msn2700-01 ansible_host=10.251.0.188 sonic_version=v2
+[sonic_mlnx_40]
+str-msn2700-01 ansible_host=10.251.0.188
+
+[sonic_mlnx_40:vars]
+hwsku="ACS-MSN2700"
+iface_speed='40000'
+mgmt_subnet_mask_length="24"
[sonic:children]
-sonic_latest
+sonic_mlnx_40
[ptf]
ptf_ptf1 ansible_host=10.255.0.188 ansible_ssh_user=root ansible_ssh_pass=root
ptf_vms1-1 ansible_host=10.255.0.178 ansible_ssh_user=root ansible_ssh_pass=root
-[str:children]
+[lab:children]
sonic
fanout
diff --git a/ansible/library/port_alias.py b/ansible/library/port_alias.py
new file mode 100644
index 00000000000..18d9020a7e0
--- /dev/null
+++ b/ansible/library/port_alias.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+import re
+import os
+import traceback
+import subprocess
+from operator import itemgetter
+from itertools import groupby
+from collections import defaultdict
+
+DOCUMENTATION = '''
+module: port_alias.py
+Ansible_version_added: 2.0.0.2
+short_description: Find SONiC device port alias mapping if there is alias mapping
+Description:
+ Minigraph file is using SONiC deivce alias to describe the interface name, it's vendor and and hardware platform dependent
+ This module is used to find the correct port_config.ini for the hwsku and return Ansible ansible_facts.port_alias
+ The definition of this mapping is specified in http://github.com/azure/sonic-buildimage/device
+ You should build docker-sonic-mgmt from sonic-buildimage and run Ansible from sonic-mgmt docker container
+ Input:
+ hwsku
+
+ Return Ansible_facts:
+ port_alias: SONiC interface name or SONiC interface alias if alias is available
+
+'''
+
+EXAMPLES = '''
+ - name: get hardware interface name
+ port_alias: hwsku='ACS-MSN2700'
+'''
+
+### TODO: we could eventually use sonic config package to replace this port_alias module later ###############
+### Here are the expectation of files of device port_config.ini located, in case changed please modify it here
+FILE_PATH = '/usr/share/sonic/device'
+PORTMAP_FILE = 'port_config.ini'
+
+class SonicPortAliasMap():
+ """
+ Retrieve SONiC device interface port alias mapping
+
+ """
+ def __init__(self, hwsku):
+ self.filename = ''
+ self.hwsku = hwsku
+ self.portmap = []
+ return
+
+ def findfile(self):
+ for (rootdir, dirnames, filenames) in os.walk(FILE_PATH):
+ if self.hwsku in rootdir and len(dirnames) == 0 and PORTMAP_FILE in filenames:
+ self.filename = rootdir+'/'+PORTMAP_FILE
+
+ def get_portmap(self):
+ self.findfile()
+ if self.filename == '':
+ raise Exception("Something wrong when trying to find the portmap file, either the hwsku is not available or file location is not correct")
+ with open(self.filename) as f:
+ lines = f.readlines()
+ for line in lines:
+ if 'Ethernet' in line:
+ mapping = line.split()
+ if len(mapping) < 3:
+ self.portmap.append(mapping[0])
+ else:
+ self.portmap.append(mapping[2])
+ return
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ hwsku=dict(required=True, type='str')
+ ),
+ supports_check_mode=False
+ )
+ m_args = module.params
+ try:
+ allmap = SonicPortAliasMap(m_args['hwsku'])
+ allmap.get_portmap()
+ module.exit_json(ansible_facts={'port_alias': allmap.portmap})
+ except (IOError, OSError):
+ module.fail_json(msg=allmap.portmap)
+ except Exception:
+ module.fail_json(msg=allmap.portmap)
+
+from ansible.module_utils.basic import *
+if __name__ == "__main__":
+ main()
diff --git a/ansible/library/testbed_vm_info.py b/ansible/library/testbed_vm_info.py
new file mode 100644
index 00000000000..c60c7c7069d
--- /dev/null
+++ b/ansible/library/testbed_vm_info.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+
+import re
+import yaml
+import os
+import traceback
+import subprocess
+import ipaddr as ipaddress
+from operator import itemgetter
+from itertools import groupby
+from collections import defaultdict
+
+DOCUMENTATION = '''
+module: testbed_vm_info.py
+Ansible_version_added: 2.0.0.2
+short_description: Gather all related VMs info
+Description:
+ When deploy testbed topology with VM connected to SONiC, gather neighbor VMs info for generating SONiC minigraph file
+ options:
+ base_vm: base vm name defined in testbed.csv for the deployed topology; required: True
+ topo: topology name defined in testbed.csv for the deployed topology; required: True
+
+Ansible_facts:
+ 'neighbor_eosvm_mgmt': all VM hosts management IPs
+ 'topoall': topology information
+
+'''
+
+EXAMPLES = '''
+ - name: gather vm information
+ testbed_vm_info: base_vm='VM0100' topo='t1'
+'''
+
+### Here are the assumption/expectation of files to gather VM informations, if the file location or name changes, please modify it here
+TOPO_PATH = 'vars/'
+VM_INV_FILE = 'veos'
+
+
+class TestbedVMFacts():
+ """
+ Retrieve testbed VMs management information that for a specified toplogy defined in testbed.csv
+
+ """
+
+ def __init__(self, toponame, vmbase):
+ self.topofile = TOPO_PATH+'topo_'+toponame +'.yml'
+ self.start_index = int(re.findall('VM(\d+)', vmbase)[0])
+ self.vmhosts = {}
+ return
+
+
+ def get_neighbor_eos(self):
+ eos = {}
+ with open(self.topofile) as f:
+ vm_topology = yaml.load(f)
+ self.topoall = vm_topology
+ for vm in vm_topology['topology']['VMs']:
+ vm_index = int(vm_topology['topology']['VMs'][vm]['vm_offset'])+self.start_index
+ eos[vm] = vm_index
+ return eos
+
+
+ def gather_veos_vms(self):
+ vms = {}
+ with open(VM_INV_FILE) as f:
+ lines = f.readlines()
+ for line in lines:
+ if 'VM' in line and 'ansible_host' in line:
+ items = line.split()
+ vms[items[0]] = items[1].split('=')[1]
+ return vms
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ base_vm=dict(required=True, type='str'),
+ topo=dict(required=True, type='str'),
+ ),
+ supports_check_mode=False
+ )
+ m_args = module.params
+ topo_type = m_args['topo']
+ if 'ptf' in topo_type:
+ module.exit_json(ansible_facts={'neighbor_eosvm_mgmt': {}})
+ try:
+ vmsall = TestbedVMFacts(m_args['topo'], m_args['base_vm'])
+ neighbor_eos = vmsall.get_neighbor_eos()
+ vm_inv = vmsall.gather_veos_vms()
+ for eos in neighbor_eos:
+ vmname = 'VM'+format(neighbor_eos[eos], '04d')
+ if vmname in vm_inv:
+ vmsall.vmhosts[eos] = vm_inv[vmname]
+ else:
+ err_msg = "cannot find the vm " + vmname + " in VM inventory file, please make sure you have enough VMs for the topology you are using"
+ module.fail_json(msg=err_msg)
+ module.exit_json(ansible_facts={'neighbor_eosvm_mgmt':vmsall.vmhosts, 'topoall': vmsall.topoall})
+ except (IOError, OSError):
+ module.fail_json(msg="Can not find file "+vmsall.topofile+" or "+VM_INV_FILE)
+ except Exception as e:
+ module.fail_json(msg=traceback.format_exc())
+
+from ansible.module_utils.basic import *
+if __name__ == "__main__":
+ main()
+
diff --git a/ansible/templates/topo/dev_metadata.j2 b/ansible/templates/topo/dev_metadata.j2
new file mode 100644
index 00000000000..6ac28e8bb28
--- /dev/null
+++ b/ansible/templates/topo/dev_metadata.j2
@@ -0,0 +1,101 @@
+
+
+ true
+
+{% for index in range(32) %}
+{% set sonic_if_name='Ethernet'+((index*4)|string) %}
+
+ DeviceInterface
+
+ true
+ true
+ 1
+ {{ port_alias[index] }}
+
+ false
+ 0
+ 0
+ {{ iface_speed }}
+
+{% endfor %}
+
+ true
+ 0
+ {{ hwsku }}
+
+
+
+
+{% set syslog_servers_str=';'.join(syslog_servers) %}
+{% set dhcp_servers_str=';'.join(dhcp_servers) %}
+{% set forced_mgmt_routes_str = ';'.join(forced_mgmt_routes) %}
+{% set ntp_servers_str = ';'.join(ntp_servers) %}
+{% set snmp_servers_str = ';'.join(snmp_servers) %}
+{% set tacacs_servers_str = ';'.join(tacacs_servers) %}
+{% set radius_servers_str = ';'.join(radius_servers) %}
+{% set erspan_dest_str = ';'.join(erspan_dest) %}
+
+
+ {{ ansible_hostname }}
+
+
+ DhcpResources
+
+ {{ dhcp_servers_str }}
+
+
+ NTPResources
+
+ {{ ntp_servers_str }}
+
+
+ DeploymentId
+
+ 1
+
+
+ QosProfile
+
+ Profile0
+
+
+ RadiusResources
+
+ {{ radius_servers_str }}
+
+
+ SnmpResources
+
+ {{ snmp_servers_str }}
+
+
+ SyslogResources
+
+ {{ syslog_servers_str }}
+
+
+ TacacsGroup
+
+ {{ tacacs_group }}
+
+
+ TacacsServer
+
+ {{ tacacs_servers_str }}
+
+
+ ForcedMgmtRoutes
+
+ {{ forced_mgmt_routes_str }}
+
+
+ ErspanDestinationIpv4
+
+ {{ erspan_dest_str }}
+
+
+
+
+
+
+
diff --git a/ansible/templates/topo/t0.j2 b/ansible/templates/topo/t0.j2
new file mode 100644
index 00000000000..c918c14e818
--- /dev/null
+++ b/ansible/templates/topo/t0.j2
@@ -0,0 +1,197 @@
+
+
+
+
+{% for index in range(4) %}
+
+ false
+ {{ inventory_hostname }}
+ 10.0.0.{{ 56 + index*2 }}
+ ARISTA0{{ index+1 }}T1
+ 10.0.0.{{ 56 + index*2 +1 }}
+ 1
+ 10
+ 3
+
+
+ {{ inventory_hostname }}
+ FC00::{{ ('%0x' % (113 + index*4)) | upper }}
+ ARISTA0{{ index+1 }}T1
+ FC00::{{ ('%0x' % (113 + index*4 +1)) | upper }}
+ 1
+ 10
+ 3
+
+{% endfor %}
+
+
+
+ 65100
+ {{ inventory_hostname }}
+
+{% for index in range(4) %}
+
+ 10.0.0.{{ 57 + index*2 }}
+
+
+
+
+{% endfor %}
+
+ BGPPeer
+ 10.1.0.32
+
+
+
+ BGPSLBPassive
+ 10.255.0.0/27
+
+
+ BGPPeer
+ 10.1.0.32
+
+
+
+ BGPVac
+ 192.168.0.0/27
+
+
+
+
+{% for index in range(4) %}
+
+ 64600
+ ARISTA0{{ index+1 }}T1
+
+
+{% endfor %}
+
+
+
+
+
+
+
+ HostIP
+ Loopback0
+
+ 10.1.0.32/32
+
+ 10.1.0.32/32
+
+
+ HostIP1
+ Loopback0
+
+ FC00:1::32/128
+
+ FC00:1::32/128
+
+
+
+
+ HostIP
+ eth0
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+
+
+
+
+
+ {{ inventory_hostname }}
+
+{% for index in range(4) %}
+
+ PortChannel0{{ index+1 }}
+ {{ port_alias[28+index] }}
+
+
+{% endfor %}
+
+
+
+ Vlan1000
+ {{ port_alias[1] }};{{ port_alias[2] }};{{ port_alias[3] }};{{ port_alias[4] }};{{ port_alias[5] }};{{ port_alias[6] }};{{ port_alias[7] }};{{ port_alias[8] }};{{ port_alias[9] }};{{ port_alias[10] }};{{ port_alias[11] }};{{ port_alias[12] }};{{ port_alias[13] }};{{ port_alias[14] }};{{ port_alias[15] }};{{ port_alias[16] }};{{ port_alias[17] }};{{ port_alias[18] }};{{ port_alias[19] }};{{ port_alias[20] }};{{ port_alias[21] }};{{ port_alias[22] }};{{ port_alias[23] }};{{ port_alias[24] }}
+ False
+ 0.0.0.0/0
+
+ 1000
+ 1000
+ 192.168.0.0/27
+
+
+
+{% for index in range(4) %}
+
+
+ PortChannel0{{ index +1 }}
+ 10.0.0.{{ 56 + index*2 }}/31
+
+
+
+ PortChannel0{{ index +1 }}
+ FC00::{{ ('%0x' % (113+index*4)) | upper }}/126
+
+{% endfor %}
+
+
+ Vlan1000
+ 192.168.0.1/27
+
+
+
+
+
+
+
+
+
+
+{% for index in range(4) %}
+
+ DeviceInterfaceLink
+ ARISTA0{{ index+1 }}T1
+ Ethernet1
+ {{ inventory_hostname }}
+ {{ port_alias[28+index] }}
+
+{% endfor %}
+
+
+
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
+ {{ ansible_host }}
+
+
+{% if VM_topo %}
+{% for dev in neighbor_eosvm_mgmt %}
+{% if 'T1' in dev %}
+{% set dev_type = 'LeafRouter' %}
+{% elif 'T2' in dev %}
+{% set dev_type = 'SpineRouter' %}
+{% elif 'T0' in dev %}
+{% set dev_type = 'TorRouter' %}
+{% else %}
+{% set dev_ytpe = 'Unknown' %}
+{% endif %}
+
+ "{{ dev }}"
+
+ {{ neighbor_eosvm_mgmt[dev] }}
+
+ Arista-VM
+
+{% endfor %}
+{% endif %}
+
+
+{% include 'dev_metadata.j2' %}
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
diff --git a/ansible/templates/topo/t1-lag.j2 b/ansible/templates/topo/t1-lag.j2
new file mode 100644
index 00000000000..b1dc804a40f
--- /dev/null
+++ b/ansible/templates/topo/t1-lag.j2
@@ -0,0 +1,227 @@
+
+
+
+
+{% for index in range(16) %}
+
+ ARISTA{{ '%02d' % (index + 1) }}T0
+ 10.0.0.{{ (32 + index * 2 + 1) }}
+ {{ inventory_hostname }}
+ 10.0.0.{{ (32 + index * 2) }}
+ 1
+ 10
+ 3
+
+
+ ARISTA{{ '%02d' % (index + 1) }}T0
+ FC00::{{ ('%0x' % (65+index*4+1)) | upper }}
+ {{ inventory_hostname }}
+ FC00::{{ ('%0x' % (65+index*4)) | upper}}
+ 1
+ 10
+ 3
+
+{% if (index%2)==0 %}
+
+ {{ inventory_hostname }}
+ 10.0.0.{{ (index*2) }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ 10.0.0.{{ (index*2+1) }}
+ 1
+ 10
+ 3
+
+
+ {{ inventory_hostname }}
+ FC00::{{ ('%0x' % (index*4 +1)) | upper }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ FC00::{{ ('%0x' % (index*4+2)) | upper }}
+ 1
+ 10
+ 3
+
+{% endif %}
+{% endfor %}
+
+
+
+ 65100
+ {{ inventory_hostname }}
+
+{% for ip in range(16) %}
+
+ 10.0.0.{{ ip*2+33 }}
+
+
+
+{% if (ip%2)==0 %}
+
+ 10.0.0.{{ ip*2+1 }}
+
+
+
+{% endif %}
+{% endfor %}
+
+
+
+{% for i in range(1,17,1) %}
+
+ 640{{ '%02d' % i }}
+ ARISTA{{ '%02d' % i }}T0
+
+
+{% if (i%2)==1 %}
+
+ 65200
+ ARISTA{{ '%02d' % i }}T2
+
+
+{% endif %}
+{% endfor %}
+
+
+
+
+
+
+
+ HostIP
+ Loopback0
+
+ 10.1.0.32/32
+
+ 10.1.0.32/32
+
+
+ HostIP1
+ Loopback0
+
+ FC00:1::32/128
+
+ FC00:1::32/128
+
+
+
+
+ HostIP
+ eth0
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+
+
+
+
+ {{ inventory_hostname }}
+
+{% for index in range(8) %}
+
+ PortChannelInterface
+ PortChannel{{ index*8 }}
+ {{ port_alias[index*2] }};{{ port_alias[index*2+1] }}
+
+
+{% endfor %}
+
+
+
+{% for index in range(8) %}
+
+ IPInterface
+
+ PortChannel{{ index*8 }}
+ 10.0.0.{{ index*4 }}/31
+
+
+ IPInterface
+
+ PortChannel{{ index*8 }}
+ FC00::{{ ('%0x' % (index*8+1)) | upper }}/126
+
+{% endfor %}
+{% for index in range(16) %}
+
+ IPInterface
+
+ {{ port_alias[16+index] }}
+ 10.0.0.{{ 32+index*2 }}/31
+
+
+ IPInterface
+
+ {{ port_alias[16+index] }}
+ FC00::{{ ('%0x' % (64+index*4 +1)) | upper }}/126
+
+{% endfor %}
+
+
+
+
+
+
+
+
+
+{% for index in range(0,16,2) %}
+
+ DeviceInterfaceLink
+ {{ inventory_hostname }}
+ {{ port_alias[index] }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ Ethernet1
+
+
+ DeviceInterfaceLink
+ {{ inventory_hostname }}
+ {{ port_alias[index+1] }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ Ethernet2
+
+{% endfor %}
+{% for index in range(16) %}
+
+ DeviceInterfaceLink
+ {{ inventory_hostname }}
+ {{ port_alias[16+index] }}
+ ARISTA{{ '%02d' % (index+1) }}T0
+ Ethernet1
+
+{% endfor %}
+
+
+
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
+ {{ ansible_host }}
+
+
+{% if VM_topo %}
+{% for dev in neighbor_eosvm_mgmt.keys()|sort %}
+{% if 'T1' in dev %}
+{% set dev_type = 'LeafRouter' %}
+{% elif 'T2' in dev %}
+{% set dev_type = 'SpineRouter' %}
+{% elif 'T0' in dev %}
+{% set dev_type = 'ToRRouter' %}
+{% else %}
+{% set dev_type = 'Unknown' %}
+{% endif %}
+
+ "{{ dev }}"
+ Arista-VM
+
+ {{ neighbor_eosvm_mgmt[dev] }}
+
+
+{% endfor %}
+{% endif %}
+
+
+{% include 'dev_metadata.j2' %}
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
diff --git a/ansible/templates/topo/t1.j2 b/ansible/templates/topo/t1.j2
new file mode 100644
index 00000000000..79c427fe9aa
--- /dev/null
+++ b/ansible/templates/topo/t1.j2
@@ -0,0 +1,189 @@
+
+
+
+
+{% for index in range(16) %}
+
+ ARISTA{{ '%02d' % (index + 1) }}T0
+ 10.0.0.{{ (32 + index * 2 + 1) }}
+ {{ inventory_hostname }}
+ 10.0.0.{{ (32 + index * 2) }}
+ 1
+ 10
+ 3
+
+
+ ARISTA{{ '%02d' % (index + 1) }}T0
+ FC00::{{ ('%0x' % (65+index*4+1)) | upper }}
+ {{ inventory_hostname }}
+ FC00::{{ ('%0x' % (65+index*4)) | upper}}
+ 1
+ 10
+ 3
+
+
+ {{ inventory_hostname }}
+ 10.0.0.{{ (index*2) }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ 10.0.0.{{ (index*2+1) }}
+ 1
+ 10
+ 3
+
+
+ {{ inventory_hostname }}
+ FC00::{{ ('%0x' % (index*4 +1)) | upper }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ FC00::{{ ('%0x' % (index*4+2)) | upper }}
+ 1
+ 10
+ 3
+
+{% endfor %}
+
+
+
+ 65100
+ {{ inventory_hostname }}
+
+{% for ip in range(1,32,2) %}
+
+ 10.0.0.{{ 32+ip }}
+
+
+
+
+ 10.0.0.{{ ip }}
+
+
+
+{% endfor %}
+
+
+
+{% for i in range(1,17,1) %}
+
+ 640{{ '%02d' % i }}
+ ARISTA{{ '%02d' % i }}T0
+
+
+
+ 65200
+ ARISTA{{ '%02d' % i }}T2
+
+
+{% endfor %}
+
+
+
+
+
+
+
+ HostIP
+ Loopback0
+
+ 10.1.0.32/32
+
+ 10.1.0.32/32
+
+
+ HostIP1
+ Loopback0
+
+ FC00:1::32/128
+
+ FC00:1::32/128
+
+
+
+
+ HostIP
+ eth0
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+ {{ ansible_host }}/{{ mgmt_subnet_mask_length }}
+
+
+
+
+
+ {{ inventory_hostname }}
+
+
+
+{% for index in range(32) %}
+
+
+ {{ port_alias[index] }}
+ 10.0.0.{{ index*2 }}/31
+
+
+
+ {{ port_alias[index] }}
+ FC00::{{ ('%0x' % (index*4 +1)) | upper }}/126
+
+{% endfor %}
+
+
+
+
+
+
+
+
+
+{% for index in range(16) %}
+
+ DeviceInterfaceLink
+ {{ inventory_hostname }}
+ {{ port_alias[index] }}
+ ARISTA{{ '%02d' % (index+1) }}T2
+ Ethernet1
+
+{% endfor %}
+{% for index in range(16) %}
+
+ DeviceInterfaceLink
+ {{ inventory_hostname }}
+ {{ port_alias[16+index] }}
+ ARISTA{{ '%02d' % (index+1) }}T0
+ Ethernet1
+
+{% endfor %}
+
+
+
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
+ {{ ansible_host }}
+
+
+{% if VM_topo %}
+{% for dev in neighbor_eosvm_mgmt.keys()|sort %}
+{% if 'T1' in dev %}
+{% set dev_type = 'LeafRouter' %}
+{% elif 'T2' in dev %}
+{% set dev_type = 'SpineRouter' %}
+{% elif 'T0' in dev %}
+{% set dev_type = 'ToRRouter' %}
+{% else %}
+{% set dev_type = 'Unknown' %}
+{% endif %}
+
+ "{{ dev }}"
+ Arista-VM
+
+ {{ neighbor_eosvm_mgmt[dev] }}
+
+
+{% endfor %}
+{% endif %}
+
+
+{% include 'dev_metadata.j2' %}
+ {{ inventory_hostname }}
+ {{ hwsku }}
+
diff --git a/ansible/templates/topo/updategraph.conf b/ansible/templates/topo/updategraph.conf
new file mode 100644
index 00000000000..cd74f82777b
--- /dev/null
+++ b/ansible/templates/topo/updategraph.conf
@@ -0,0 +1 @@
+enable=false
diff --git a/ansible/veos b/ansible/veos
index 4f1df47615f..01cd5458981 100644
--- a/ansible/veos
+++ b/ansible/veos
@@ -13,6 +13,34 @@ VM0100 ansible_host=10.250.0.2
VM0101 ansible_host=10.250.0.3
VM0102 ansible_host=10.250.0.4
VM0103 ansible_host=10.250.0.5
+VM0104 ansible_host=10.250.0.6
+VM0105 ansible_host=10.250.0.7
+VM0106 ansible_host=10.250.0.8
+VM0107 ansible_host=10.250.0.9
+VM0108 ansible_host=10.250.0.10
+VM0109 ansible_host=10.250.0.11
+VM0110 ansible_host=10.250.0.12
+VM0111 ansible_host=10.250.0.13
+VM0112 ansible_host=10.250.0.14
+VM0113 ansible_host=10.250.0.15
+VM0114 ansible_host=10.250.0.16
+VM0115 ansible_host=10.250.0.17
+VM0116 ansible_host=10.250.0.18
+VM0117 ansible_host=10.250.0.19
+VM0118 ansible_host=10.250.0.20
+VM0119 ansible_host=10.250.0.21
+VM0120 ansible_host=10.250.0.22
+VM0121 ansible_host=10.250.0.23
+VM0122 ansible_host=10.250.0.24
+VM0123 ansible_host=10.250.0.25
+VM0124 ansible_host=10.250.0.26
+VM0125 ansible_host=10.250.0.27
+VM0126 ansible_host=10.250.0.28
+VM0127 ansible_host=10.250.0.29
+VM0128 ansible_host=10.250.0.30
+VM0129 ansible_host=10.250.0.31
+VM0130 ansible_host=10.250.0.32
+VM0131 ansible_host=10.250.0.33
[vms_2]
VM0200 ansible_host=10.250.0.51