Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: xlate openconfig-system-ext:track-interface when sending to tes… #31

Merged
merged 16 commits into from
Sep 29, 2023
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Cisco Model Driven Devops Ansible Collection
# Cisco Model-Driven Devops Ansible Collection

This Ansible Collection is an implementation of Model-Driven DevOps and includes the following roles:
* [oc](https://github.com/model-driven-devops/ansible-mdd/blob/main/roles/oc/README.md)
Expand All @@ -8,14 +8,14 @@ This Ansible Collection is an implementation of Model-Driven DevOps and includes
* [netbox](https://github.com/model-driven-devops/ansible-mdd/blob/main/roles/netbox/README.md)
* [nso](https://github.com/model-driven-devops/ansible-mdd/blob/main/roles/nso/README.md)

## Dependancies
### Environmnet Variables
## Dependencies
### Environment Variables
If using NetBox:
- `NETBOX_API`
- `NETBOX_TOKEN`

To use an external NSO (ie. not deployed in CML):
- `NSO_URL` Protocol, FQDN or IP address and port (ex. `http://192.168.1.100:8080`)
To use an external NSO (i.e. not deployed in CML):
- `NSO_URL` Protocol, FQDN or IP address and port (e.g. `http://192.168.1.100:8080`)
- `NSO_USERNAME`
- `NSO_PASSWORD`

Expand Down
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace: ciscops
name: mdd

# The version of the collection. Must be compatible with semantic versioning
version: 1.2.7
version: 1.2.8

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md
Expand Down
4 changes: 2 additions & 2 deletions playbooks/cml_update_lab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@

- name: Create topology file
copy:
content: "{{ topology | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ topology | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ lookup('env', 'PWD') }}/files/cml_lab.yaml"
run_once: yes
no_log: yes

- name: Create mapping file
copy:
content: "{{ {'all': {'hosts': results.mappings}} | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ {'all': {'hosts': results.mappings}} | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ lookup('env', 'PWD') }}/{{ inventory_dir }}/cml_intf_map.yml"
run_once: yes
2 changes: 1 addition & 1 deletion playbooks/nso_dump_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
nso_device_config: "{ 'mdd_data': { 'config': {{ nso_device_config['config'] }} } }"

- copy:
content: "{{ nso_device_config | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ nso_device_config | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ current_dir }}/device_config_data/{{ inventory_hostname }}.yml"
6 changes: 3 additions & 3 deletions playbooks/nso_update_data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
when: ('oc' in mdd_data_types)
throttle: "{{ workers }}"
block:
- name: Translate and truncate interface names
- name: Translate/truncate interface names and truncate config
set_fact:
mdd_data: "{{ mdd_data | ciscops.mdd.intf_xform(cml_intf_xlate) }}"
when: (cml_group is defined and cml_group in group_names) and (cml_intf_xlate is defined and cml_intf_xlate)
mdd_data: "{{ mdd_data | ciscops.mdd.config_xform(cml_intf_xlate | default(None), cml_truncate_list | default(None)) }}"
when: (cml_intf_xlate is defined and cml_intf_xlate) or (cml_truncate_list is defined and cml_truncate_list)

- name: Update MDD Data
ansible.builtin.include_role:
Expand Down
8 changes: 4 additions & 4 deletions playbooks/show.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
roles:
- ciscops.mdd.data
tasks:
- name: Translate and truncate interface names
- name: Translate/truncate interface names and truncate config
set_fact:
mdd_data: "{{ mdd_data | ciscops.mdd.intf_xform(cml_intf_xlate) }}"
when: (cml_group is defined and cml_group in group_names) and (cml_intf_xlate is defined and cml_intf_xlate)
mdd_data: "{{ mdd_data | ciscops.mdd.config_xform(cml_intf_xlate | default(None), cml_truncate_list | default(None)) }}"
when: (cml_intf_xlate is defined and cml_intf_xlate) or (cml_truncate_list is defined and cml_truncate_list)

- debug:
var: mdd_data
53 changes: 48 additions & 5 deletions plugins/filter/intf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"openconfig-system-ext:ssh-source-interface",
"openconfig-network-instance:interface-id",
"openconfig-network-instance:index",
"openconfig-network-instance:local-address"
"openconfig-network-instance:local-address",
"openconfig-system-ext:track-interface"
]


Expand Down Expand Up @@ -51,20 +52,26 @@ def xlate_value(data, intf_dict):
return


def intf_xlate(data, intf_dict):
def intf_xlate(data, intf_dict=None):
if not data:
return {}

if intf_dict is None:
return data

data_out = data.copy()
xlate_value(data_out, intf_dict)

return data_out


def intf_truncate(data, intf_dict):
def intf_truncate(data, intf_dict=None):
if not data:
return {}

if intf_dict is None:
return data

regex_list = intf_dict.keys()
temp_interface_list = []
temp_stp_interface_list = []
Expand Down Expand Up @@ -145,12 +152,48 @@ def intf_truncate(data, intf_dict):
[instance_index]["openconfig-network-instance:mpls"]["openconfig-network-instance:global"]
["openconfig-network-instance:interface-attributes"]["openconfig-network-instance:interface"]) = temp_mpls_interface_list

# Truncate openconfig-acl interfaces
if "openconfig-acl:acl" in oc_data and "openconfig-acl:interfaces" in oc_data["openconfig-acl:acl"]:
temp_acl_interface_list = []
for interface in oc_data["openconfig-acl:acl"]["openconfig-acl:interfaces"]["openconfig-acl:interface"]:
if found_full_match(interface["openconfig-acl:id"].split(".")[0], intf_dict):
temp_acl_interface_list.append(interface)

data_out["mdd:openconfig"]["openconfig-acl:acl"]["openconfig-acl:interfaces"]["openconfig-acl:interface"] = temp_acl_interface_list

return data_out


def delete_key(data, key_list):
if isinstance(data, dict):
if key_list[0] in list(data):
if len(key_list) == 1:
del data[key_list[0]]
else:
key = key_list.pop(0)
delete_key(data[key], key_list)


def config_truncate(data, truncate_list=None):
"""Find all values from a nested dictionary for a given key."""

if not data:
return {}

if truncate_list is None:
return data

data_out = data.copy()

for path in truncate_list:
delete_key(data_out, path)
return data_out


def intf_xform(data, intf_dict):
def config_xform(data, intf_dict=None, truncate_list=None):
data = intf_truncate(data, intf_dict)
data = intf_xlate(data, intf_dict)
data = config_truncate(data, truncate_list)
return data


Expand All @@ -160,5 +203,5 @@ def filters(self):
return {
'intf_xlate': intf_xlate,
'intf_truncate': intf_truncate,
'intf_xform': intf_xform
'config_xform': config_xform
}
5 changes: 4 additions & 1 deletion plugins/filter/nso_oc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ def nso_oc(config_data):
}
native_dict = copy.deepcopy(config_data)
translation_notes = []
main_xe.build_xe_to_oc(config_data, native_dict, oc_dict, translation_notes)
oc_dict_clean = main_xe.build_xe_to_oc(config_data, native_dict, oc_dict, translation_notes)

if oc_dict_clean:
oc_dict = oc_dict_clean

mdd_dict['mdd_data'] = {
"mdd:openconfig": oc_dict['mdd:openconfig'],
Expand Down
36 changes: 36 additions & 0 deletions plugins/filter/to_even_nicer_yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import absolute_import, division, print_function

__metaclass__ = type

from ansible.utils.unsafe_proxy import AnsibleUnsafeText
import yaml


class MyDumper(yaml.Dumper):
def increase_indent(self, flow=False, indentless=False):
return super(MyDumper, self).increase_indent(flow, False)


def convert_ansible_unsafe_text_to_safe(data):
if isinstance(data, dict):
return {convert_ansible_unsafe_text_to_safe(k): convert_ansible_unsafe_text_to_safe(v) for k, v in data.items()}
elif isinstance(data, list):
return [convert_ansible_unsafe_text_to_safe(item) for item in data]
elif isinstance(data, AnsibleUnsafeText):
return str(data)
else:
return data


def to_even_nicer_yaml(config_data):
ansible_safe_config_data = convert_ansible_unsafe_text_to_safe(config_data)
return yaml.dump(ansible_safe_config_data, Dumper=MyDumper, default_flow_style=False, explicit_start=True,
sort_keys=False)


class FilterModule(object):

def filters(self):
return {
'to_even_nicer_yaml': to_even_nicer_yaml
}
21 changes: 15 additions & 6 deletions plugins/modules/mdd_combine.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,27 @@ def _merge(result_cfgs, v, path=None, filepath=None, hierarchy_level=None, playb
else:
result_cfgs[k] = _merge({}, v, path + [str(k)], filepath, hierarchy_level, playbook_tags, weight)
else:
# if key not there, add
# if key not there, add it
if k not in result_cfgs:
result_cfgs[k] = (v, filepath, playbook_tags, hierarchy_level, weight)
# if key found multiple places at same hierarchy level, error
elif k in result_cfgs and hierarchy_level == result_cfgs[k][3] and result_cfgs[k][0]:
# if key found multiple places at same hierarchy level and the new key's weight is higher, go with the highest weight.
elif k in result_cfgs and hierarchy_level == result_cfgs[k][3] and result_cfgs[k][0] and weight > \
result_cfgs[k][4]:
result_cfgs[k] = (v, filepath, playbook_tags, hierarchy_level, weight)
# if key found multiple places at same hierarchy level and the new key's weight is lower, skip.
elif k in result_cfgs and hierarchy_level == result_cfgs[k][3] and result_cfgs[k][0] and weight < \
result_cfgs[k][4]:
continue
# if key found multiple places at same hierarchy level and if the weight is the same, error.
elif k in result_cfgs and hierarchy_level == result_cfgs[k][3] and result_cfgs[k][0] and weight == \
result_cfgs[k][4]:
if filepath == result_cfgs[k][1]:
module.fail_json(
msg="Merge Error: key {1} was found multiple times at the same hierarchy level (level: {2}) in file {3}.".format(
msg="Merge Error: key {0} was found multiple times at the same hierarchy level (level: {1}) in file {2}.".format(
k, result_cfgs[k][3], filepath))
else:
module.fail_json(
msg="Merge Error: key {1} was found multiple times at the same hierarchy level (level: {2}) in files {3} and {4}.".format(
msg="Merge Error: key {0} was found multiple times at the same hierarchy level (level: {1}) in files {2} and {3}.".format(
k, result_cfgs[k][3], filepath, result_cfgs[k][1]))
module.exit_json(changed=False, failed=True)
# if key exists but weight is higher, replace
Expand Down Expand Up @@ -378,7 +387,7 @@ def find_and_read_configs(top_dir, device_name, filespec_list, default_weight, t
)
except yaml.YAMLError:
module.fail_json(
msg="An error occurred loading file {1}".format(os.path.join(current_dir, filename)))
msg="An error occurred loading file {0}".format(os.path.join(current_dir, filename)))
module.exit_json(changed=False, failed=True)

hierarchy_level += 1
Expand Down
6 changes: 3 additions & 3 deletions roles/nso/tasks/update_yaml_data.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- name: Write Config Data to Device Directory
copy:
content: "---\n{{ data | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ data | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ mdd_device_dir }}/config-data.yml"
vars:
data: "{{ { 'mdd_data': { 'config': nso_device_config['config'] } } }}"
Expand All @@ -17,15 +17,15 @@

- name: Write OC Data to Device Directory
copy:
content: "---\n{{ data | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ data | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ mdd_device_dir }}/oc-{{ (item.key | ansible.builtin.split(':'))[1] }}.yml"
with_dict: "{{ oc_data['mdd_data']['mdd:openconfig'] }}"
vars:
data: "{{ { 'mdd_data': { 'mdd:openconfig' : { item.key: item.value } } } }}"

- name: Write Native Data to Device Directory
copy:
content: "---\n{{ data | to_nice_yaml(indent=2,sort_keys=False) }}"
content: "{{ data | ciscops.mdd.to_even_nicer_yaml }}"
dest: "{{ mdd_device_dir }}/config-remaining.yml"
vars:
data: "{{ { 'mdd_data': { 'config': oc_data['mdd_data']['config'] } } }}"
Expand Down