Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 48 additions & 40 deletions ansible/library/conn_graph_facts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ansible.module_utils.port_utils import get_port_alias_to_name_map
from ansible.module_utils.debug_utils import create_debug_file, print_debug_msg


DOCUMENTATION='''
module: conn_graph_facts.py
version_added: 2.0
Expand Down Expand Up @@ -52,6 +53,7 @@

'''


EXAMPLES='''
- name: conn_graph_facts: host = "str-7260-11"

Expand Down Expand Up @@ -91,6 +93,7 @@

'''


class Parse_Lab_Graph():
"""
Parse the generated lab physical connection graph and insert Ansible fact of the graph
Expand All @@ -115,16 +118,16 @@ def __init__(self, xmlfile):

def port_vlanlist(self, vlanrange):
vlans = []
for vlanid in list(map(str.strip,vlanrange.split(','))):
for vlanid in list(map(str.strip, vlanrange.split(','))):
if vlanid.isdigit():
vlans.append(int(vlanid))
continue
elif '-' in vlanid:
vlanlist = list(map(str.strip,vlanid.split('-')))
vlanlist = list(map(str.strip, vlanid.split('-')))
vlans.extend(range(int(vlanlist[0]), int(vlanlist[1])+1))
continue
elif vlanid != '':
raise Exception, 'vlan range error "%s"'%vlanrange
raise ValueError('vlan range error "%s"' % vlanrange)
vlans = sorted(set(vlans))
return vlans

Expand Down Expand Up @@ -188,7 +191,7 @@ def convert_list2range(self, l):
"""
ranges = []
sl = sorted(set(l))
for k,g in groupby(enumerate(sl), lambda (i,x): i-x):
for _, g in groupby(enumerate(sl), lambda t: t[0] - t[1]):
group = list(map(itemgetter(1), g))
if len(group) == 1:
ranges.append(str(group[0]))
Expand All @@ -204,7 +207,7 @@ def get_host_vlan(self, hostname):
Calculate dpg vlan data for each link(port) and return a Switch/Device total Vlan range
"""

if hostname in self.devices and self.devices[hostname]['Type'].lower() == 'devsonic':
if hostname in self.devices and self.devices[hostname]['Type'].lower() == 'devsonic':
self.vlanport[hostname] = {}
for port in self.links[hostname]:
peerdevice = self.links[hostname][port]['peerdevice']
Expand All @@ -218,38 +221,29 @@ def get_host_vlan(self, hostname):

if hostname in self.vlanport:
dpgvlans = self.vlanport[hostname]
vlans = []
vlans = []
for intf in dpgvlans:
vlans += dpgvlans[intf]['vlanlist']
self.vlanrange = self.convert_list2range(vlans)
return {'VlanRange': self.vlanrange, 'VlanList': vlans }
return {'VlanRange': self.vlanrange, 'VlanList': vlans}

def get_host_device_info(self, hostname):
"""
return the given hostname device info of hwsku and type
"""
if hostname in self.devices:
return self.devices[hostname]
else:
return self.devices
return self.devices.get(hostname)

def get_host_port_vlans(self, hostname):
"""
return the given hostname device vlan port information
"""
if hostname in self.vlanport:
return self.vlanport[hostname]
else:
return self.vlanport
return self.vlanport.get(hostname)

def get_host_connections(self, hostname):
"""
return the given hostname device each individual connection
"""
if hostname in self.links:
return { hostname: self.links[hostname] }
else:
return self.links
return self.links.get(hostname)

def contains_hosts(self, hostnames):
return set(hostnames) <= set(self.devices)
Expand All @@ -259,14 +253,15 @@ def contains_hosts(self, hostnames):
EMPTY_GRAPH_FILE = 'empty_graph.xml'
LAB_GRAPHFILE_PATH = 'files/'

"""

def find_graph(hostnames):
"""
Find a graph file contains all devices in testbed.
duts are spcified by hostnames

Parameters:
hostnames: list of duts in the target testbed.
"""
def find_graph(hostnames):
"""
filename = os.path.join(LAB_GRAPHFILE_PATH, LAB_CONNECTION_GRAPH_FILE)
with open(filename) as fd:
file_list = yaml.safe_load(fd)
Expand Down Expand Up @@ -304,8 +299,10 @@ def get_port_name_list(hwsku):
port_name_list_sorted = natsorted(port_name_list)
return port_name_list_sorted


debug_fname = None


def main():
module = AnsibleModule(
argument_spec=dict(
Expand All @@ -322,10 +319,14 @@ def main():
global debug_fname
debug_fname = create_debug_file("/tmp/conn_graph_debug.txt")

hostnames = m_args['hosts']
anchor = m_args['anchor']
if not hostnames:
if m_args['hosts']:
hostnames = m_args['hosts']
elif m_args['host']:
hostnames = [m_args['host']]
else:
# return the whole graph
hostnames = []
try:
# When called by pytest, the file path is obscured to /tmp/.../.
# we need the caller to tell us where the graph files are with
Expand All @@ -345,31 +346,43 @@ def main():
target = anchor if anchor else hostnames
lab_graph = find_graph(target)

device_info = []
# early return for the whole graph or empty graph file(vtestbed)
if (
not hostnames or
not lab_graph.devices and not lab_graph.links and not lab_graph.vlanport
):
results = {
'device_info': lab_graph.devices,
'device_conn': lab_graph.links,
'device_port_vlans': lab_graph.vlanport,
}
module.exit_json(ansible_facts=results)

device_info = {}
device_conn = {}
device_port_vlans = []
device_vlan_range = []
device_vlan_list = []
device_port_vlans = {}
device_vlan_range = {}
device_vlan_list = {}
device_vlan_map_list = {}
for hostname in hostnames:
dev = lab_graph.get_host_device_info(hostname)
if dev is None:
module.fail_json(msg="cannot find info for %s" % hostname)
device_info.append(dev)
device_conn.update(lab_graph.get_host_connections(hostname))
device_info[hostname] = dev
device_conn[hostname] = lab_graph.get_host_connections(hostname)
host_vlan = lab_graph.get_host_vlan(hostname)
port_vlans = lab_graph.get_host_port_vlans(hostname)
# for multi-DUTs, must ensure all have vlan configured.
if host_vlan:
device_vlan_range.append(host_vlan["VlanRange"])
device_vlan_list.append(host_vlan["VlanList"])
device_vlan_range[hostname] = host_vlan["VlanRange"]
device_vlan_list[hostname] = host_vlan["VlanList"]
if dev["Type"].lower() != "devsonic":
device_vlan_map_list[hostname] = host_vlan["VlanList"]
else:
port_vlans = lab_graph.get_host_port_vlans(hostname)
device_vlan_map_list[hostname] = {}

port_name_list_sorted = get_port_name_list(dev['HwSku'])
print_debug_msg(debug_fname,"For %s with hwsku %s, port_name_list is %s" % (hostname, dev['HwSku'], port_name_list_sorted))
print_debug_msg(debug_fname, "For %s with hwsku %s, port_name_list is %s" % (hostname, dev['HwSku'], port_name_list_sorted))
for a_host_vlan in host_vlan["VlanList"]:
# Get the corresponding port for this vlan from the port vlan list for this hostname
found_port_for_vlan = False
Expand All @@ -384,15 +397,10 @@ def main():
module.fail_json(msg="Did not find port for %s in the ports based on hwsku '%s' for host %s" % (a_port, dev['HwSku'], hostname))
if not found_port_for_vlan:
module.fail_json(msg="Did not find corresponding link for vlan %d in %s for host %s" % (a_host_vlan, port_vlans, hostname))
device_port_vlans.append(lab_graph.get_host_port_vlans(hostname))
device_port_vlans[hostname] = port_vlans
results = {k: v for k, v in locals().items()
if (k.startswith("device_") and v)}

# TODO: Currently the results values are heterogeneous, let's change
# them all into dictionaries in the future.
if m_args['hosts'] is None:
results = {k: v[0] if isinstance(v, list) else v for k, v in results.items()}

module.exit_json(ansible_facts=results)
except (IOError, OSError):
module.fail_json(msg="Can not find lab graph file under {}".format(LAB_GRAPHFILE_PATH))
Expand Down
4 changes: 2 additions & 2 deletions ansible/roles/fanout/tasks/fanout_eos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
- name: build fanout startup config for Arista fanout leaf
template: src=arista_7260_deploy.j2
dest=/mnt/flash/startup-config
when: device_info.HwSku == "Arista-7260QX-64"
when: device_info[inventory_hostname]["HwSku"] == "Arista-7260QX-64"
become: yes

- name: build fanout startup config for 7060
template: src=arista_7060_deploy.j2
dest=/mnt/flash/startup-config
when: device_info.HwSku == "Arista-7060CX-32S"
when: device_info[inventory_hostname]["HwSku"] == "Arista-7060CX-32S"
become: yes

- name: reboot
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/fanout/tasks/fanout_mlnx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
### playbook
################################################################################################
- name: prepare fanout switch admin login info
set_fact: ansible_ssh_user={{ fanout_mlnx_user }} ansible_ssh_pass={{ fanout_mlnx_password }} peer_hwsku={{device_info['HwSku']}}
set_fact: ansible_ssh_user={{ fanout_mlnx_user }} ansible_ssh_pass={{ fanout_mlnx_password }} peer_hwsku={{device_info[inventory_hostname]['HwSku']}}
tags: always

##########################################
Expand Down
10 changes: 5 additions & 5 deletions ansible/roles/fanout/tasks/fanout_sonic.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
- debug: msg="{{ device_info }}"
- debug: msg="{{ device_info[inventory_hostname] }}"

- name: prepare fanout switch admin login info
set_fact: ansible_user={{ fanout_sonic_user }} ansible_password={{ fanout_sonic_password }}

- name: find interface name mapping
port_alias: hwsku="{{ device_info["HwSku"] }}"
port_alias: hwsku="{{ device_info[inventory_hostname]["HwSku"] }}"

- name: build fanout vlan config
fail: msg="SONiC fanout switch with HwSku {{ device_info.HwSku }} not supported"
when: device_info.HwSku != "Arista-7060CX-32S-C32"
fail: msg="SONiC fanout switch with HwSku {{ device_info[inventory_hostname]['HwSku'] }} not supported"
when: device_info[inventory_hostname]["HwSku"] != "Arista-7060CX-32S-C32"

- name: build fanout startup config for Arista 7060 SONiC leaf fanout
template: src=sonic_deploy_arista_7060.j2
dest=/etc/sonic/vlan.json
when: device_info.HwSku == "Arista-7060CX-32S-C32"
when: device_info[inventory_hostname]["HwSku"] == "Arista-7060CX-32S-C32"
become: yes

- name: disable all copp rules
Expand Down
2 changes: 1 addition & 1 deletion ansible/roles/fanout/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
delegate_to: localhost
tags: always

- set_fact: sw_type="{{ device_info['Type'] }}"
- set_fact: sw_type="{{ device_info[inventory_hostname]['Type'] }}"

- set_fact: os='eos'
when: os is not defined
Expand Down
6 changes: 5 additions & 1 deletion ansible/roles/fanout/tasks/rootfanout_connect.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@
register: lab

- set_fact:
dev_vlans: "{{ devinfo.ansible_facts.device_vlan_range|flatten(levels=1) }}"
lab_devices: "{{ lab.ansible_facts.device_info }}"

- name: Collect DUTs vlans
set_fact:
dev_vlans: "{{ dev_vlans|default([]) + item.value }}"
loop: "{{ devinfo['ansible_facts']['device_vlan_range'] | dict2items }}"

- name: Find the root fanout switch
set_fact:
ansible_host: "{{ item.value['mgmtip'] }}"
Expand Down
26 changes: 13 additions & 13 deletions ansible/roles/fanout/templates/arista_7060_deploy.j2
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,51 @@ ntp server vrf management {{ ntp_server }}
{% endfor %}
!
spanning-tree mode none
no spanning-tree vlan {{ device_vlan_range | list | join(',') }}
no spanning-tree vlan {{ device_vlan_range[inventory_hostname] | list | join(',') }}
!
aaa authorization exec default local
aaa root secret 0 {{ lab_admin_pass }}
!
username admin privilege 15 role network-admin secret 0 {{ lab_admin_pass }}
username eapi privilege 15 secret 0 {{ lab_admin_pass }}
!
vlan {{ device_vlan_range | list | join(',') }}
vlan {{ device_vlan_range[inventory_hostname] | list | join(',') }}
!
vrf definition management
rd 1:1
!
{% for i in range(1,33) %}
{% set intf = 'Ethernet' + i|string + '/1' %}
interface {{ intf }}
{% if intf in device_port_vlans and device_port_vlans[intf]['mode'] != "Trunk" %}
{% if intf in device_port_vlans[inventory_hostname] and device_port_vlans[inventory_hostname][intf]['mode'] != "Trunk" %}
{% if device_conn[inventory_hostname][intf]['speed'] == "100000" %}
description {{ device_conn[inventory_hostname][intf]['peerdevice'] }}-{{ device_conn[inventory_hostname][intf]['peerport'] }}
switchport access vlan {{ device_port_vlans[intf]['vlanids'] }}
switchport access vlan {{ device_port_vlans[inventory_hostname][intf]['vlanids'] }}
switchport mode dot1q-tunnel
spanning-tree portfast
speed forced 100gfull
error-correction encoding reed-solomon
no shutdown
{% elif device_conn[inventory_hostname][intf]['speed'] == "40000" %}
description {{ device_conn[inventory_hostname][intf]['peerdevice'] }}-{{ device_conn[inventory_hostname][intf]['peerport'] }}
switchport access vlan {{ device_port_vlans[intf]['vlanids'] }}
switchport access vlan {{ device_port_vlans[inventory_hostname][intf]['vlanids'] }}
switchport mode dot1q-tunnel
spanning-tree portfast
speed forced 40gfull
no shutdown
{% elif device_conn[inventory_hostname][intf]['speed'] == "50000" %}
description {{ device_conn[inventory_hostname][intf]['peerdevice'] }}-{{ device_conn[inventory_hostname][intf]['peerport'] }}
switchport access vlan {{ device_port_vlans[intf]['vlanids'] }}
switchport access vlan {{ device_port_vlans[inventory_hostname][intf]['vlanids'] }}
switchport mode dot1q-tunnel
spanning-tree portfast
speed forced 50gfull
no error-correction encoding
no shutdown
{% set intf = 'Ethernet' + i|string + '/3' %}
interface {{ intf }}
{% if intf in device_port_vlans and device_port_vlans[intf]['mode'] != "Trunk" %}
{% if intf in device_port_vlans[inventory_hostname] and device_port_vlans[inventory_hostname][intf]['mode'] != "Trunk" %}
description {{ device_conn[inventory_hostname][intf]['peerdevice'] }}-{{ device_conn[inventory_hostname][intf]['peerport'] }}
switchport access vlan {{ device_port_vlans[intf]['vlanids'] }}
switchport access vlan {{ device_port_vlans[inventory_hostname][intf]['vlanids'] }}
switchport mode dot1q-tunnel
spanning-tree portfast
speed forced 50gfull
Expand All @@ -72,10 +72,10 @@ interface {{ intf }}
{# Not valid speed value #}
shutdown
{% endif %}
{% elif intf in device_port_vlans and device_port_vlans[intf]['mode'] == 'Trunk' %}
{% elif intf in device_port_vlans[inventory_hostname] and device_port_vlans[inventory_hostname][intf]['mode'] == 'Trunk' %}
description {{ device_conn[inventory_hostname][intf]['peerdevice'] }}-{{ device_conn[inventory_hostname][intf]['peerport'] }}
switchport mode trunk
switchport trunk allowed vlan {{ device_port_vlans[intf]['vlanids'] }}
switchport trunk allowed vlan {{ device_port_vlans[inventory_hostname][intf]['vlanids'] }}
spanning-tree portfast
speed force 40gfull
no shutdown
Expand All @@ -87,16 +87,16 @@ interface {{ intf }}
!
interface Management 1
description TO LAB MGMT SWITCH
ip address {{ device_info["ManagementIp"] }}
ip address {{ device_info[inventory_hostname]["ManagementIp"] }}
no shutdown
!
# LACP packets pass through
mac address-table reserved forward 0180.c200.0002
# LLDP packets pass through
mac address-table reserved forward 0180.c200.000e
!
ip route 0.0.0.0/0 {{ device_info["ManagementGw"] }}
ip route vrf management 0.0.0.0/0 {{ device_info["ManagementGw"] }}
ip route 0.0.0.0/0 {{ device_info[inventory_hostname]["ManagementGw"] }}
ip route vrf management 0.0.0.0/0 {{ device_info[inventory_hostname]["ManagementGw"] }}
!
ip routing
no ip routing vrf management
Expand Down
Loading