Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions ansible/config_sonic_basedon_testbed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# 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 --vault-password-file=~/.password -l sonic_dut_name -e VM_base=VM0300 -e topo=t0 [-e deploy=true]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VM_base -> vm_base

#
# 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) or (iface_prefix is not defined) or (iface_postfix 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: save original minigraph file (if original file may don't 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

- debug: var=deploy

- 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.ori
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ori [](start = 68, length = 3)

-> orig

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably you're fixed another line. This is still 'ori'

become: true
ignore_errors: true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use lineinfile module to replace the enable=true line to enable=false line.

when: deploy is defined and deploy==True

- name: create minigraph file for SONiC device
become: true
template: src=templates/topo/{{ template_name }}.j2
dest=/etc/sonic/minigraph.xml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to disable /etc/sonic/updategraph.conf to avoid downloading url from dhcp

# 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
25 changes: 22 additions & 3 deletions ansible/group_vars/lab/lab.yml
Original file line number Diff line number Diff line change
@@ -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']
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure here: It should be '10.250.0.8' (without prefix), or '10.250.0.8/32'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked with current graph format, no /32


# 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
Expand Down
15 changes: 11 additions & 4 deletions ansible/lab
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
[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_prefix="Ethernet"
iface_postfix=""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be suffix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove iface_prefix and postfix, use port_config.ini in the buildimge repo to convert.

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

Expand Down
105 changes: 105 additions & 0 deletions ansible/library/testbed_vm_info.py
Original file line number Diff line number Diff line change
@@ -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:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only one space here.

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()

105 changes: 105 additions & 0 deletions ansible/templates/topo/dev_metadata.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<DeviceInfos>
<DeviceInfo>
<AutoNegotiation>true</AutoNegotiation>
<EthernetInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
{% for index in range(32) %}
{% set if_index=index*4 if ('Force10-S6000' in hwsku or 'MSN' in hwsku) else index+1 %}
<a:EthernetInterface>
<ElementType>DeviceInterface</ElementType>
<AlternateSpeeds i:nil="true"/>
<EnableAutoNegotiation>true</EnableAutoNegotiation>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
{% if 'Arista-7050-QX32' in hwsku and index > 23 %}
<InterfaceName>{{ iface_prefix }}{{ if_index }}</InterfaceName>
{% else %}
<InterfaceName>{{ iface_prefix }}{{ if_index }}{{ iface_postfix }}</InterfaceName>
{% endif %}
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
<Priority>0</Priority>
<Speed>{{ iface_speed }}</Speed>
</a:EthernetInterface>
{% endfor %}
</EthernetInterfaces>
<FlowControl>true</FlowControl>
<Height>0</Height>
<HwSku>{{ hwsku }}</HwSku>
<ManagementInterfaces/>
</DeviceInfo>
</DeviceInfos>
<MetadataDeclaration>
{% 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) %}
<Devices xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:DeviceMetadata>
<a:Name>{{ ansible_hostname }}</a:Name>
<a:Properties>
<a:DeviceProperty>
<a:Name>DhcpResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ dhcp_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>NTPResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ ntp_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>DeploymentId</a:Name>
<a:Reference i:nil="true"/>
<a:Value>1</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>QosProfile</a:Name>
<a:Reference i:nil="true"/>
<a:Value>Profile0</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>RadiusResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ radius_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>SnmpResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ snmp_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>SyslogResources</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ syslog_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>TacacsGroup</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ tacacs_group }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>TacacsServer</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ tacacs_servers_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>ForcedMgmtRoutes</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ forced_mgmt_routes_str }}</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>ErspanDestinationIpv4</a:Name>
<a:Reference i:nil="true"/>
<a:Value>{{ erspan_dest_str }}</a:Value>
</a:DeviceProperty>
</a:Properties>
</a:DeviceMetadata>
</Devices>
<Properties xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</MetadataDeclaration>

Loading