From 53e1636c337bcea3cce6d8c8db407c8bfdd7b548 Mon Sep 17 00:00:00 2001 From: Jason King Date: Wed, 1 May 2024 13:27:43 -0700 Subject: [PATCH] Updates to CML lab generation --- playbooks/cml_update_lab.yml | 98 +++++++++++++++++------------------- plugins/modules/cml_lab.py | 69 +++++++++---------------- 2 files changed, 71 insertions(+), 96 deletions(-) diff --git a/playbooks/cml_update_lab.yml b/playbooks/cml_update_lab.yml index 4640a7a..3a431b2 100644 --- a/playbooks/cml_update_lab.yml +++ b/playbooks/cml_update_lab.yml @@ -8,39 +8,26 @@ vars: start_from: 2 layout: kamada_kawai - inventory_dir: inventory scale: 500 - use_cat9kv: False + # Default device type mapping for CML default_cml_device_template: switch: node_definition: iosvl2 - ram: 768 tags: - switch type: switch router: node_definition: csr1000v - ram: 3072 tags: - router type: router - # l3switch: - # node_definition: Cat9000v - # image_definition: Cat9k - # ram: 18432 - # cpus: 4 - # tags: - # - l3switch - # type: l3switch l3switch: node_definition: iosvl2 - ram: 768 tags: - l3switch type: l3switch ext_conn: node_definition: external_connector - ram: 0 tags: [] # Default interface mapping for CML default_cml_default_mappings: @@ -53,53 +40,60 @@ tasks: - name: Get CDP data - include_role: + ansible.builtin.include_role: name: ciscops.mdd.nso tasks_from: exec_command vars: - nso_exec_command: show cdp neighbors + nso_exec_command: show cdp neighbors - - set_fact: + - name: Build CDP data table + ansible.builtin.set_fact: cdp_data: "{{ {'hostname': inventory_hostname, 'tags': tags if tags is defined else {}, 'cdp': nso_command_output} }}" - - name: Create device list with CDP data - set_fact: - devices: "{{ groups['network'] | map('extract', hostvars, 'cdp_data') | list }}" - run_once: yes + - name: Generate topology and mapping file + run_once: True + block: + - name: Create device list with CDP data + ansible.builtin.set_fact: + devices: "{{ groups['network'] | map('extract', hostvars, 'cdp_data') | list }}" - - set_fact: - cml_device_template: "{{ default_cml_device_template }}" - when: cml_device_template is not defined + - name: Create device mapping table + ansible.builtin.set_fact: + cml_device_template: "{{ default_cml_device_template }}" + when: cml_device_template is not defined - - set_fact: - cml_default_mappings: "{{ default_cml_default_mappings }}" - when: cml_default_mappings is not defined + - name: Create interface mapping table + ansible.builtin.set_fact: + cml_default_mappings: "{{ default_cml_default_mappings }}" + when: cml_default_mappings is not defined - - name: Generate topology - ciscops.mdd.cml_lab: - devices: "{{ devices }}" - start_from: "{{ start_from }}" - device_template: "{{ cml_device_template }}" - default_mappings: "{{ cml_default_mappings }}" - use_cat9kv: "{{ use_cat9kv | bool }}" - register: results - run_once: yes + - name: Get CML node definitions + ansible.builtin.set_fact: + cml_node_definitions: "{{ lookup('cisco.cml.cml_node_definition', None, cml_host=cml_host, cml_username=cml_username, cml_password=cml_password, allow_unsafe=True) }}" - - name: "Layout graph using the {{ layout }} layout" - set_fact: - topology: "{{ results.topology | ciscops.mdd.graph(layout=layout,scale=scale) }}" - run_once: yes - when: not layout == 'none' + - name: Generate topology + ciscops.mdd.cml_lab: + devices: "{{ devices }}" + start_from: "{{ start_from }}" + device_template: "{{ cml_device_template }}" + default_mappings: "{{ cml_default_mappings }}" + node_definitions: "{{ cml_node_definitions }}" + register: results - - name: Create topology file - copy: - content: "{{ topology | ciscops.mdd.to_even_nicer_yaml }}" - dest: "{{ lookup('env', 'PWD') }}/files/cml_lab.yaml" - run_once: yes - no_log: yes + - name: "Layout graph using the layout {{ layout }}" + ansible.builtin.set_fact: + topology: "{{ results.topology | ciscops.mdd.graph(layout=layout,scale=scale) }}" + when: not layout == 'none' - - name: Create mapping file - copy: - content: "{{ {'all': {'hosts': results.mappings}} | ciscops.mdd.to_even_nicer_yaml }}" - dest: "{{ lookup('env', 'PWD') }}/{{ inventory_dir }}/cml_intf_map.yml" - run_once: yes + - name: Create topology file + ansible.builtin.copy: + content: "{{ topology | ansible.builtin.to_nice_yaml(indent=2, sort_keys=False) }}" + dest: "{{ lookup('env', 'PWD') }}/files/cml_lab.yaml" + mode: "0644" + no_log: yes + + - name: Create mapping file + ansible.builtin.copy: + content: "{{ {'all': {'hosts': results.mappings}} | ansible.builtin.to_nice_yaml(indent=2, sort_keys=False) }}" + dest: "{{ inventory_dir }}/cml_intf_map.yml" + mode: "0644" diff --git a/plugins/modules/cml_lab.py b/plugins/modules/cml_lab.py index 98e4efb..d784660 100644 --- a/plugins/modules/cml_lab.py +++ b/plugins/modules/cml_lab.py @@ -60,11 +60,6 @@ required: false type: int default: 2 - use_cat9kv: - description: Whether or not to use the cat9kv as the l3switch in CML - required: no - type: bool - default: false """ EXAMPLES = r""" @@ -89,13 +84,13 @@ def create_node(node_input): "boot_disk_size": 0, "configuration": node_input["configuration"], "cpu_limit": 100, - "cpus": node_input.get("cpus", 1), + "cpus": node_input.get("cpus", 0), "data_volume": 0, "hide_links": False, "id": node_input["id"], "label": node_input["hostname"], "node_definition": node_input["node_definition"], - "ram": node_input["ram"], + "ram": node_input.get("ram", 0), "tags": node_input["tags"], "x": node_input["x_position"], "y": node_input.get("y_position", 0), @@ -130,55 +125,41 @@ def switch_generate_interface(c, m, s): return c, m, s -def add_interfaces_to_topology(topo_node, device_info, physical_interfaces, use_cat9kv=False): +def get_interfaces_from_node_definition(device_info, node_definitions): + """ + Get the interfaces from the node definition + """ + for node_definition in node_definitions: + if node_definition["id"] == device_info["node_definition"]: + return node_definition["device"]["interfaces"]["physical"] + +def add_interfaces_to_topology(topo_node, device_info, physical_interfaces, node_definitions): """ Adds interfaces to the devices in the CML topology """ number_of_interfaces = len(physical_interfaces) + 3 # initial + 2 spares number_of_interfaces += 4 - (number_of_interfaces % 4) # interfaces come in sets of 4 - if device_info["type"] == "l3switch" and use_cat9kv is True: - topo_node["interfaces"].append({ - "id": "i1", - "label": "GigabitEthernet0/0", - "slot": 0, - "type": "physical" - }) - counter = 1 - # cat9kv *requires* that 24 ports be configured for the 24-port version - for i in range(24): - topo_node["interfaces"].append({ - "id": "i{0}".format(counter + 1), - "label": "GigabitEthernet1/0/{0}".format(counter), - "slot": counter, - "type": "physical" - }) - counter += 1 - elif device_info["type"] == "switch" or (device_info["type"] == "l3switch" and use_cat9kv is not True): - slot = 0 - mod = 0 - counter = 0 + interfaces = get_interfaces_from_node_definition(device_info, node_definitions) + if device_info["type"] == "switch" or (device_info["type"] == "l3switch"): if_id = 1 for i in range(number_of_interfaces): topo_node["interfaces"].append({ "id": "i{0}".format(if_id), - "label": "GigabitEthernet{0}/{1}".format(mod, counter), - "slot": slot, + "label": interfaces[if_id - 1], + "slot": if_id - 1, "type": "physical" }) - counter, mod, slot = switch_generate_interface(counter, mod, slot) if_id += 1 elif device_info["type"] == "router": - slot = 0 - counter = 1 + if_id = 1 for i in range(number_of_interfaces): topo_node["interfaces"].append({ - "id": "i{0}".format(counter), - "label": "GigabitEthernet{0}".format(counter), - "slot": slot, + "id": "i{0}".format(if_id), + "label": interfaces[if_id - 1], + "slot": if_id - 1, "type": "physical" }) - slot += 1 - counter += 1 + if_id += 1 def map_physical_interfaces_to_logical_interfaces(topo_node, physical_interfaces, start_from): @@ -208,7 +189,7 @@ def map_physical_interfaces_to_logical_interfaces(topo_node, physical_interfaces def cml_topology_create_initial(devices_with_interface_dict, remote_device_info_full, start_from, device_template, - use_cat9kv=False, devices=None): + node_definitions, devices=None): """ Creates CML topology file and adds nodes :param devices_with_interface_dict: @@ -370,7 +351,7 @@ def cml_topology_create_initial(devices_with_interface_dict, remote_device_info_ node_counter += 1 x_position += 150 topo_node = create_node(device_info) - add_interfaces_to_topology(topo_node, device_info, devices_with_interface_dict[device], use_cat9kv) + add_interfaces_to_topology(topo_node, device_info, devices_with_interface_dict[device], node_definitions) physical_virtual_map = map_physical_interfaces_to_logical_interfaces(topo_node, devices_with_interface_dict[device], start_from) @@ -630,9 +611,9 @@ def main(): devices=dict(required=True, type='list', elements='dict'), device_template=dict(required=True, type='dict'), default_mappings=dict(required=True, type='dict'), + node_definitions=dict(required=True, type='list'), ext_conn=dict(required=False, type='bool', default=True), start_from=dict(required=False, type='int', default=2), - use_cat9kv=dict(required=False, type='bool', default=False) ) module = AnsibleModule(argument_spec=arguments, supports_check_mode=False) @@ -643,8 +624,8 @@ def main(): devices = module.params['devices'] device_template = module.params['device_template'] default_mappings = module.params['default_mappings'] + node_definitions = module.params['node_definitions'] start_from = module.params['start_from'] - use_cat9kv = module.params['use_cat9kv'] device_names = get_device_names(devices) @@ -657,7 +638,7 @@ def main(): devices_with_interface_dict = check_for_and_remove_error_links(device_links) sort_device_interfaces(devices_with_interface_dict) topology_cml, mappings_cml = cml_topology_create_initial(devices_with_interface_dict, remote_device_info_full, - start_from, device_template, use_cat9kv, device_names) + start_from, device_template, node_definitions, device_names) cml_topology_add_links(topology_cml, mappings_cml, device_links, device_names) if module.params['ext_conn']: cml_topology_add_external_connectors_and_links(topology_cml, device_template)