Skip to content

Commit

Permalink
[VM]: add new topo wan-4link (4 link between each device based on wan…
Browse files Browse the repository at this point in the history
…-pub) (#6682)

Add wan-4link topo (each device is connected 4 physical links in one port channel)

What is the motivation for this PR?
Add a new topo based on wan-pub that support multi-links between devices.

How did you do it?
Add a new topo (wan-4link) and move wan related configuration which would not be covered in other parts under wan_dut_configuration.
  • Loading branch information
guangyao6 authored Nov 2, 2022
1 parent 29c30ab commit b3623be
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 36 deletions.
75 changes: 47 additions & 28 deletions ansible/library/topo_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import traceback
import ipaddress
import sys
import csv
from operator import itemgetter
from itertools import groupby
import yaml
import re
from ansible.module_utils.basic import AnsibleModule

DOCUMENTATION = '''
module: topo_facts.py
Expand All @@ -20,6 +18,7 @@
required: True
'''


def parse_vm_vlan_port(vlan):
"""
parse vm vlan port
Expand All @@ -33,11 +32,12 @@ def parse_vm_vlan_port(vlan):
vlan_index = vlan
ptf_index = vlan
else:
m = re.match("(\d+)\.(\d+)@(\d+)", vlan)
m = re.match(r"(\d+)\.(\d+)@(\d+)", vlan)
(dut_index, vlan_index, ptf_index) = (int(m.group(1)), int(m.group(2)), int(m.group(3)))

return (dut_index, vlan_index, ptf_index)


def parse_host_interfaces(hifs):
"""
parse host interfaces
Expand All @@ -61,6 +61,7 @@ def parse_host_interfaces(hifs):

return ret


def parse_console_interface(console_interface):
"""
parse console interface
Expand All @@ -74,6 +75,7 @@ def parse_console_interface(console_interface):
fields = console_interface.split('.')
return fields[0], fields[1], fields[2]


class ParseTestbedTopoinfo():
'''
Parse topology yml file
Expand Down Expand Up @@ -112,8 +114,8 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs
if 'interfaces' in topo_definition['configuration'][vm]:
for intf in topo_definition['configuration'][vm]['interfaces']:
dut_index = 0
if neigh_type == 'NEIGH_ASIC' and re.match("Eth(\d+)-", intf):
vmconfig[vm]['intfs'][dut_index].append(intf)
if neigh_type == 'NEIGH_ASIC' and re.match(r"Eth(\d+)-", intf):
vmconfig[vm]['intfs'][dut_index].append(intf)
elif 'Ethernet' in intf:
if 'dut_index' in topo_definition['configuration'][vm]['interfaces'][intf]:
dut_index = topo_definition['configuration'][vm]['interfaces'][intf]['dut_index']
Expand All @@ -131,7 +133,6 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs
vmconfig[vm]['bgp_ipv6'] = [None] * dut_num
vmconfig[vm]['bgp_asn'] = None


if 'configuration' in topo_definition:
if 'interfaces' in topo_definition['configuration'][vm]:
for intf in topo_definition['configuration'][vm]['interfaces']:
Expand All @@ -142,16 +143,22 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs
if 'dut_index' in topo_definition['configuration'][vm]['interfaces'][intf]:
dut_index = topo_definition['configuration'][vm]['interfaces'][intf]['dut_index']
elif 'Port-Channel' in intf:
m = re.search("(\d+)", intf)
m = re.search(r"(\d+)", intf)
dut_index = po_map[int(m.group(1))]

if isinstance(topo_definition['configuration'][vm]['interfaces'],dict) and 'ipv4' in topo_definition['configuration'][vm]['interfaces'][intf] and ('loopback' not in intf.lower()):
(peer_ipv4, ipv4_mask) = topo_definition['configuration'][vm]['interfaces'][intf]['ipv4'].split('/')
if (isinstance(topo_definition['configuration'][vm]['interfaces'], dict)
and 'ipv4' in topo_definition['configuration'][vm]['interfaces'][intf]
and ('loopback' not in intf.lower())):
(peer_ipv4, ipv4_mask) = \
topo_definition['configuration'][vm]['interfaces'][intf]['ipv4'].split('/')
vmconfig[vm]['peer_ipv4'][dut_index] = peer_ipv4
vmconfig[vm]['ipv4mask'][dut_index] = ipv4_mask
vmconfig[vm]['ip_intf'][dut_index] = intf
if isinstance(topo_definition['configuration'][vm]['interfaces'],dict) and 'ipv6' in topo_definition['configuration'][vm]['interfaces'][intf] and ('loopback' not in intf.lower()):
(ipv6_addr, ipv6_mask) = topo_definition['configuration'][vm]['interfaces'][intf]['ipv6'].split('/')
if (isinstance(topo_definition['configuration'][vm]['interfaces'], dict)
and 'ipv6' in topo_definition['configuration'][vm]['interfaces'][intf]
and ('loopback' not in intf.lower())):
(ipv6_addr, ipv6_mask) = \
topo_definition['configuration'][vm]['interfaces'][intf]['ipv6'].split('/')
vmconfig[vm]['peer_ipv6'][dut_index] = ipv6_addr.upper()
vmconfig[vm]['ipv6mask'][dut_index] = ipv6_mask
vmconfig[vm]['ip_intf'][dut_index] = intf
Expand All @@ -161,16 +168,18 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs
for ipstr in topo_definition['configuration'][vm]['bgp']['peers'][dut_asn]:
ip_mask = None
if '/' in ipstr:
(ipstr, ip_mask) = ipstr.split('/')
(ipstr, ip_mask) = ipstr.split('/')
if sys.version_info < (3, 0):
ip = ipaddress.ip_address(ipstr.decode('utf8'))
else:
ip = ipaddress.ip_address(ipstr)
for dut_index in range(0, dut_num):
if ip.version == 4:
# Each VM might not be connected to all the DUT's, so check if this VM is a peer to DUT at dut_index
# Each VM might not be connected to all the DUT's,
# so check if this VM is a peer to DUT at dut_index
if vmconfig[vm]['peer_ipv4'][dut_index]:
ipsubnet_str = vmconfig[vm]['peer_ipv4'][dut_index]+'/'+vmconfig[vm]['ipv4mask'][dut_index]
ipsubnet_str = \
vmconfig[vm]['peer_ipv4'][dut_index]+'/'+vmconfig[vm]['ipv4mask'][dut_index]
if sys.version_info < (3, 0):
ipsubnet = ipaddress.ip_interface(ipsubnet_str.decode('utf8'))
else:
Expand All @@ -181,9 +190,11 @@ def parse_topo_defintion(self, topo_definition, po_map, dut_num, neigh_type='VMs
vmconfig[vm]['bgp_ipv4'][dut_index] = ipstr.upper()
vmconfig[vm]['ipv4mask'][dut_index] = ip_mask if ip_mask else '32'
elif ip.version == 6:
# Each VM might not be connected to all the DUT's, so check if this VM is a peer to DUT at dut_index
# Each VM might not be connected to all the DUT's,
# so check if this VM is a peer to DUT at dut_index
if vmconfig[vm]['peer_ipv6'][dut_index]:
ipsubnet_str = vmconfig[vm]['peer_ipv6'][dut_index]+'/'+vmconfig[vm]['ipv6mask'][dut_index]
ipsubnet_str = \
vmconfig[vm]['peer_ipv6'][dut_index]+'/'+vmconfig[vm]['ipv6mask'][dut_index]
if sys.version_info < (3, 0):
ipsubnet = ipaddress.ip_interface(ipsubnet_str.decode('utf8'))
else:
Expand Down Expand Up @@ -213,7 +224,7 @@ def get_topo_config(self, topo_name, hwsku, asic_name):
asic_topo_config = dict()
po_map = [None] * 16 # maximum 16 port channel interfaces

### read topology definition
# read topology definition
if not os.path.isfile(topo_filename):
raise Exception("cannot find topology definition file under vars/topo_%s.yml file!" % topo_name)
else:
Expand All @@ -226,13 +237,13 @@ def get_topo_config(self, topo_name, hwsku, asic_name):
with open(asic_topo_filename) as f:
slot_definition = yaml.safe_load(f)

### parse topo file specified in vars/ to reverse as dut config
# parse topo file specified in vars/ to reverse as dut config
dut_num = 1
if 'dut_num' in topo_definition['topology']:
dut_num = topo_definition['topology']['dut_num']
vm_topo_config['dut_num'] = dut_num
if topo_definition['topology'].has_key('topo_type'):

if 'topo_type' in topo_definition['topology']:
vm_topo_config['topo_type'] = topo_definition['topology']['topo_type']

if 'VMs' in topo_definition['topology']:
Expand All @@ -248,13 +259,15 @@ def get_topo_config(self, topo_name, hwsku, asic_name):
vm_topo_config['dut_type'] = topo_definition['configuration_properties']['common']['dut_type']
vm_topo_config['dut_asn'] = dut_asn

for slot,asic_definition in slot_definition.items():
for slot, asic_definition in slot_definition.items():
asic_topo_config[slot] = dict()
for asic in asic_definition:
po_map_asic = [None] * 16 # maximum 16 port channel interfaces
asic_topo_config[slot][asic] = dict()
asic_topo_config[slot][asic]['asic_type'] = asic_definition[asic]['configuration_properties']['common']['asic_type']
asic_topo_config[slot][asic]['neigh_asic'] = self.parse_topo_defintion(asic_definition[asic], po_map_asic, 1, 'NEIGH_ASIC')
asic_topo_config[slot][asic]['asic_type'] = \
asic_definition[asic]['configuration_properties']['common']['asic_type']
asic_topo_config[slot][asic]['neigh_asic'] = \
self.parse_topo_defintion(asic_definition[asic], po_map_asic, 1, 'NEIGH_ASIC')

vm_topo_config['host_interfaces_by_dut'] = [[] for i in range(dut_num)]
if 'host_interfaces' in topo_definition['topology']:
Expand Down Expand Up @@ -288,10 +301,16 @@ def get_topo_config(self, topo_name, hwsku, asic_name):
vm_topo_config['DUT'] = {}

if 'devices_interconnect_interfaces' in topo_definition['topology']:
vm_topo_config['devices_interconnect_interfaces'] = topo_definition['topology']['devices_interconnect_interfaces']
vm_topo_config['devices_interconnect_interfaces'] = \
topo_definition['topology']['devices_interconnect_interfaces']
else:
vm_topo_config['devices_interconnect_interfaces'] = []

if 'wan_dut_configuration' in topo_definition:
vm_topo_config['wan_dut_configuration'] = [None]*dut_num
for _, v in topo_definition['wan_dut_configuration'].items():
vm_topo_config['wan_dut_configuration'][v['dut_offset']] = v

self.vm_topo_config = vm_topo_config
self.asic_topo_config = asic_topo_config
return vm_topo_config, asic_topo_config
Expand All @@ -317,9 +336,9 @@ def main():
'asic_topo_config': asic_topo_config})
except (IOError, OSError):
module.fail_json(msg="Can not find topo file for %s" % topo_name)
except Exception as e:
except Exception:
module.fail_json(msg=traceback.format_exc())

from ansible.module_utils.basic import *
if __name__== "__main__":

if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ interface {{ name }}
!
ssh server v2
end

File renamed without changes.
116 changes: 116 additions & 0 deletions ansible/roles/eos/templates/wan-pub-core.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{% set host = configuration[hostname] %}
{% set mgmt_ip = ansible_host %}
{% if vm_type is defined and vm_type == "ceos" %}
{% set mgmt_if_index = 0 %}
{% else %}
{% set mgmt_if_index = 1 %}
{% endif %}
no schedule tech-support
!
{% if vm_type is defined and vm_type == "ceos" %}
agent LicenseManager shutdown
agent PowerFuse shutdown
agent PowerManager shutdown
agent Thermostat shutdown
agent LedPolicy shutdown
agent StandbyCpld shutdown
agent Bfd shutdown
{% endif %}
!
hostname {{ hostname }}
!
vrf definition MGMT
rd 1:1
!
spanning-tree mode mstp
!
aaa root secret 0 123456
!
username admin privilege 15 role network-admin secret 0 123456
!
clock timezone UTC
!
lldp run
lldp management-address Management{{ mgmt_if_index }}
lldp management-address vrf MGMT
!
snmp-server community {{ snmp_rocommunity }} ro
snmp-server vrf MGMT
!
ip routing
ip routing vrf MGMT
ipv6 unicast-routing
!
{% if vm_mgmt_gw is defined %}
ip route vrf MGMT 0.0.0.0/0 {{ vm_mgmt_gw }}
{% else %}
ip route vrf MGMT 0.0.0.0/0 {{ mgmt_gw }}
{% endif %}
!
interface Management {{ mgmt_if_index }}
description TO LAB MGMT SWITCH
{% if vm_type is defined and vm_type == "ceos" %}
vrf MGMT
{% else %}
vrf forwarding MGMT
{% endif %}
ip address {{ mgmt_ip }}/{{ mgmt_prefixlen }}
no shutdown
!
{% for name, iface in host['interfaces'].items() %}
interface {{ name }}
{% if name.startswith('Loopback') %}
description LOOPBACK
{% else %}
mtu 9214
no switchport
no shutdown
{% endif %}
{% if name.startswith('Port-Channel') %}
port-channel min-links 1
{% endif %}
{% if iface['lacp'] is defined %}
channel-group {{ iface['lacp'] }} mode active
lacp rate normal
{% endif %}
{% if iface['ipv4'] is defined %}
ip address {{ iface['ipv4'] }}
{% endif %}
{% if iface['ipv6'] is defined %}
ipv6 enable
ipv6 address {{ iface['ipv6'] }}
ipv6 nd ra suppress
{% endif %}
no shutdown
!
{% endfor %}
!
interface {{ bp_ifname }}
description backplane
no switchport
no shutdown
{% if host['bp_interface']['ipv4'] is defined %}
ip address {{ host['bp_interface']['ipv4'] }}
{% endif %}
{% if host['bp_interface']['ipv6'] is defined %}
ipv6 enable
ipv6 address {{ host['bp_interface']['ipv6'] }}
ipv6 nd ra suppress
{% endif %}
no shutdown
!
{% for name, iface in host['interfaces'].items() if name.startswith('Loopback') %}
{% if iface['ipv4'] is defined %}
network {{ iface['ipv4'] }}
{% endif %}
{% if iface['ipv6'] is defined %}
network {{ iface['ipv6'] }}
{% endif %}
{% endfor %}
!
management api http-commands
no protocol https
protocol http
no shutdown
!
end
Loading

0 comments on commit b3623be

Please sign in to comment.