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
2 changes: 1 addition & 1 deletion ansible/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ deprecation_warnings = False
action_plugins = plugins/action
callback_plugins = plugins/callback
connection_plugins = plugins/connection
# lookup_plugins = /usr/share/ansible_plugins/lookup_plugins
lookup_plugins = plugins/lookup
# vars_plugins = /usr/share/ansible_plugins/vars_plugins
filter_plugins = plugins/filter
callback_whitelist = profile_tasks
Expand Down
46 changes: 46 additions & 0 deletions ansible/config_connection_db.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This playbook configures the connection_db
#
# To start connection db
# ansible-playbook -i veos config_connection_db.yml --vault-password-file=~/password.txt -e duts_name=str-msn2700-01,str-msn2700-02 -e configure_action=start_db
# ansible-playbook -i veos config_connection_db.yml --vault-password-file=~/password.txt -e connection_graph_filename=lab_connection_graph.xml -e configure_action=start_db
# Parameters:
# -e duts_name=str-msn2700-01,str-msn2700-02 - used to located connection graph file
# -e connection_graph_filename=lab_connection_graph.xml - pass connection graph file directly
# -e configure_action=start_db - could be one of: start_db, provision_db or stop_db

- name: Configure connection db
hosts: localhost
gather_facts: False

tasks:
- name: Check that variable configure_action is defined
fail: msg="Please pass extra variable 'configure_action'"
when: configure_action is not defined

- name: Check that variable connection_graph_filename or duts_name is defined
fail: msg="Please pass extra variable 'connection_grap_filename' or 'duts_name'"
when:
- provision_connection_db is not defined
- duts_name is not defined

- name: Find connection graph file
set_fact:
connection_graph_filename: "{{ lookup('graphfile', duts_name.split(',')) }}"
when: connection_graph_filename is not defined

- name: Set connection db server hostname
set_fact:
connection_db_host: "{{ item.value }}"
loop: "{{ connection_db_host_mapping | dict2items }}"
when: item.key == connection_graph_filename

- name: Get connection db server ip
set_fact:
connection_db_host_ip: "{{ ansible_ssh_host }}"
delegate_to: "{{ connection_db_host }}"

- name: Start connection_db
include_role:
name: connection_db
vars:
action: "{{ configure_action }}"
16 changes: 11 additions & 5 deletions ansible/files/creategraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
LAB_CONNECTION_GRAPH_ROOT_NAME = 'LabConnectionGraph'
LAB_CONNECTION_GRAPH_DPGL2_NAME = 'DevicesL2Info'


class LabGraph(object):

"""
Expand All @@ -40,7 +41,6 @@ def __init__(self, dev_csvfile=None, link_csvfile=None, cons_csvfile=None, pdu_c
self.csgroot = etree.Element('ConsoleGraphDeclaration')
self.pcgroot = etree.Element('PowerControlGraphDeclaration')


def read_devices(self):
with open(self.devcsv) as csv_dev:
csv_devices = csv.DictReader(filter(lambda row: row[0]!='#' and len(row.strip())!=0, csv_dev))
Expand Down Expand Up @@ -108,11 +108,17 @@ def generate_dpg(self):
hostname = dev.get('Hostname', '')
managementip = dev.get('ManagementIp', '')
devtype = dev['Type'].lower()
if hostname and ('fanout' in devtype or 'ixiachassis' in devtype):
###### Build Management interface IP here, if we create each device indivial minigraph file, we may comment this out
if not hostname:
continue
if devtype in ('server', 'devsonic'):
# Build Management interface IP for server and DUT
l3inforoot = etree.SubElement(self.dpgroot, 'DevicesL3Info', {'Hostname': hostname})
etree.SubElement(l3inforoot, 'ManagementIPInterface', {'Name': 'ManagementIp', 'Prefix': managementip})
elif 'fanout' in devtype or 'ixiachassis' in devtype:
# Build Management interface IP here, if we create each device indivial minigraph file, we may comment this out
l3inforoot = etree.SubElement(self.dpgroot, 'DevicesL3Info', {'Hostname': hostname})
etree.SubElement(l3inforoot, 'ManagementIPInterface', {'Name': 'ManagementIp', 'Prefix': managementip})
####### Build L2 information Here
# Build L2 information Here
l2inforoot = etree.SubElement(self.dpgroot, LAB_CONNECTION_GRAPH_DPGL2_NAME, {'Hostname': hostname})
vlanattr = {}
for link in self.links:
Expand Down Expand Up @@ -140,7 +146,7 @@ def create_xml(self):
'''

onexml = open(self.one_xmlfile, 'w')
root=etree.Element(LAB_CONNECTION_GRAPH_ROOT_NAME)
root = etree.Element(LAB_CONNECTION_GRAPH_ROOT_NAME)
root.append(self.pngroot)
root.append(self.dpgroot)
root.append(self.csgroot)
Expand Down
6 changes: 6 additions & 0 deletions ansible/files/lab_connection_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
</DeviceInterfaceLinks>
</PhysicalNetworkGraphDeclaration>
<DataPlaneGraph>
<DevicesL3Info Hostname="str-msn2700-01">
<ManagementIPInterface Name="ManagementIp" Prefix="10.251.0.188/23"/>
</DevicesL3Info>
<DevicesL3Info Hostname="str-7260-10">
<ManagementIPInterface Name="ManagementIp" Prefix="10.251.0.13/23"/>
</DevicesL3Info>
Expand Down Expand Up @@ -89,6 +92,9 @@
<InterfaceVlan mode="Trunk" portname="Ethernet19" vlanids=""/>
<InterfaceVlan mode="Trunk" portname="Ethernet30" vlanids="1681-1712"/>
</DevicesL2Info>
<DevicesL3Info Hostname="str-acs-serv-01">
<ManagementIPInterface Name="ManagementIp" Prefix="10.251.0.245/23"/>
</DevicesL3Info>
</DataPlaneGraph>
<ConsoleGraphDeclaration>
<DevicesConsoleInfo>
Expand Down
3 changes: 3 additions & 0 deletions ansible/group_vars/all/connection_db.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
connection_db_host_mapping:
lab_connection_graph.xml: STR-ACS-SERV-01
48 changes: 48 additions & 0 deletions ansible/plugins/lookup/graphfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from __future__ import (absolute_import, division, print_function)
import os.path
import yaml
import xml.etree.ElementTree as ET

from ansible.utils.display import Display
from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleError
__metaclass__ = type

DOCUMENTATION = """
lookup: graphfile
version_added: "1.0"
short_description: find connection graph file that has DUTs listed defined.
description:
- This lookup returns the connection graph file contains the DUTs.
options:
_terms:
description: list of DUT hostnames
required: True
"""

display = Display()
LAB_CONNECTION_GRAPH_FILE = 'graph_files.yml'


class LookupModule(LookupBase):

def run(self, terms, variables=None, **kwargs):
hostnames = terms[0]
display.debug('Graph file lookup DUTs: %s' % hostnames)
graph_list_file = self.find_file_in_search_path(variables, 'files', LAB_CONNECTION_GRAPH_FILE)
if not graph_list_file:
raise AnsibleError('Unable to locate %s' % LAB_CONNECTION_GRAPH_FILE)
with open(graph_list_file) as fd:
file_list = yaml.safe_load(fd)

for gf in file_list:
display.debug('Looking at conn graph file: %s' % gf)
gf = self.find_file_in_search_path(variables, 'files', gf)
if not gf:
continue
with open(gf) as fd:
root = ET.fromstring(fd.read())
hosts_all = [d.attrib['Hostname'] for d in root.iter('Device')]
if set(hostnames) <= set(hosts_all):
return [os.path.basename(gf)]
return []
67 changes: 67 additions & 0 deletions ansible/plugins/lookup/servercfgd_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from __future__ import (absolute_import, division, print_function)
import os.path

try:
from xmlrpclib import ServerProxy
except ImportError:
from xmllib.client import ServerProxy
from ansible.utils.display import Display
from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleError
__metaclass__ = type

DOCUMENTATION = """
lookup: servercfgd_client
version_added: "1.0"
short_description: Dispatches calls to remote functions registered in servercfgd
description:
- This lookup will make servercfgd procedure calls.
options:
_terms:
description: list of servercfgd registered function names
required: True
servercfgd_host:
description: IP address of target server that running servercfgd
type: string
conn_graph_file_content:
description: Content of connection graph file to provision db
type: string
required: False
enforce_provision:
description: True to enforce provisioning db
type: boolean
required: False
scripts:
description: List of Lua scripts to register
type: list
required: False
"""

display = Display()


class LookupModule(LookupBase):

def run(self, terms, variables=None, **kwargs):
"""Dispatches calls to servercfgd register functions."""
self.set_options(var_options=variables, direct=kwargs)
servercfgd_host = self.get_option('servercfgd_host')
display.vvv('servercfgd host address: %s' % servercfgd_host)
servercfgd = ServerProxy('http://%s:10033' % servercfgd_host)
display.vvv('servercfgd supported remote calls: %s' % servercfgd.system.listMethods())
for fname in terms:
if fname == 'init_connection_db':
servercfgd.init_connection_db()
elif fname == 'provision_connection_db':
conn_graph_file_content = str(self.get_option('conn_graph_file_content'))
enforce_provision = self.get_option('enforce_provision')
if not conn_graph_file_content:
raise AnsibleError("'conn_graph_file_content' is required for %s" % fname)
servercfgd.provision_connection_db(conn_graph_file_content, enforce_provision)
elif fname == 'register_scripts':
for script in self.get_option('scripts'):
script_name = os.path.splitext(os.path.basename(script))[0]
script_content = open(script).read()
servercfgd.register_script(script_name, script_content)
else:
raise AnsibleError('%s unsupported by servercfgd.' % fname)
29 changes: 29 additions & 0 deletions ansible/roles/connection_db/files/add_phy_link.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- KEYS[1] - start device port list key
-- KEYS[2] - start device port table key
-- KEYS[3] - end device port list key
-- KEYS[4] - end device port table key
-- ARGV[1] - start device
-- ARGV[2] - start port
-- ARGV[3] - end device
-- ARGV[4] - end port
-- ARGV[5] - band width
-- ARGV[6] - vlan mode

local start_device = ARGV[1]
local start_port = ARGV[2]
local end_device = ARGV[3]
local end_port = ARGV[4]
local bandwidth = ARGV[5]
local vlan_mode = ARGV[6]
local endport0 = start_device .. ':' .. start_port
local endport1 = end_device .. ':' .. end_port

local link_detail = string.format('%s:%s <--%s, %s--> %s:%s', start_device, start_port, vlan_mode, bandwidth, end_device, end_port)

redis.log(redis.LOG_NOTICE, 'Add physical link, details: ' .. link_detail)
redis.call('SADD', KEYS[1], start_port)
redis.call('SADD', KEYS[3], end_port)
redis.call('HSET', KEYS[2], unpack{'BandWidth', bandwidth, 'PhyPeerPort', endport1, 'VlanType', vlan_mode})
redis.call('HSET', KEYS[4], unpack{'BandWidth', bandwidth, 'PhyPeerPort', endport0, 'VlanType', vlan_mode})

return redis.status_reply("Finish adding physical link: " .. link_detail)
15 changes: 15 additions & 0 deletions ansible/roles/connection_db/files/add_server.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- KEYS[1] - server table key
-- ARGV[1] - JSONified dictinary contains server meta

redis.log(redis.LOG_NOTICE, 'Add server: ' .. ARGV[1])
local device_table_name = KEYS[1]
local device_meta = cjson.decode(ARGV[1])
local payload = {'HwSku', device_meta['HwSku'], 'ServerStatus', 'active'}

if device_meta['ManagementIp'] then
table.insert(payload, 'ManagementIp')
table.insert(payload, device_meta['ManagementIp'])
end

redis.call('HSET', device_table_name, unpack(payload))
return redis.status_reply('Finish adding server: ' .. ARGV[1])
34 changes: 34 additions & 0 deletions ansible/roles/connection_db/files/add_switch.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- KEYS[1] - switch table key
-- KEYS[2] - DUT list key
-- ARGV[1] - JSONified dictinary contains switch meta

redis.log(redis.LOG_NOTICE, 'Add switch: ' .. ARGV[1])
local switch_table_name = KEYS[1]
local dut_list_name = KEYS[2]
local switch_meta = cjson.decode(ARGV[1])
local switch_type = switch_meta['Type']
local payload = {'HwSku', switch_meta['HwSku']}

if switch_meta['ManagementIp'] then
table.insert(payload, 'ManagementIp')
table.insert(payload, switch_meta['ManagementIp'])
end

if string.find(switch_type, 'FanoutLeaf') then
table.insert(payload, 'Type')
table.insert(payload, 'leaf_fanout')
elseif string.find(switch_type, 'FanoutRoot') then
table.insert(payload, 'Type')
table.insert(payload, 'root_fanout')
elseif switch_type == 'DevSonic' then
table.insert(payload, 'Type')
table.insert(payload, 'dev_sonic')
table.insert(payload, 'ProvisionStatus')
table.insert(payload, 'not_provisioned')
redis.call('SADD', dut_list_name, switch_meta['Hostname'])
else
return redis.error_reply('Unsupported device: ' .. ARGV[1])
end

redis.call('HSET', switch_table_name, unpack(payload))
return redis.status_reply('Finish adding switch: ' .. ARGV[1])
13 changes: 13 additions & 0 deletions ansible/roles/connection_db/files/cleanup.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- ARGV[1:] glob-style key pattern to remove

local result = 0
for i = 1, #ARGV, 1 do
local matches = redis.call('KEYS', ARGV[i])
if next(matches) ~= nil then
for _, key in ipairs(matches) do
result = result + redis.call('DEL', key)
end
end
end

return result
Loading