Skip to content

Commit 739e352

Browse files
authored
Merge branch 'CiscoDevNet:develop' into bgp_fabric
2 parents 46238b4 + c7885d7 commit 739e352

File tree

86 files changed

+4797
-3924
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+4797
-3924
lines changed

.github/workflows/main.yml

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121
runs-on: ubuntu-latest
2222
strategy:
2323
matrix:
24-
ansible: [2.15.12, 2.16.7]
24+
ansible: [2.15.13, 2.16.14, 2.17.12, 2.18.6]
2525
steps:
2626
- name: Check out code
2727
uses: actions/checkout@v2
2828

29-
- name: Set up Python "3.10"
29+
- name: Set up Python "3.11"
3030
uses: actions/setup-python@v1
3131
with:
32-
python-version: "3.10"
32+
python-version: "3.11"
3333

3434
- name: Install ansible-base (v${{ matrix.ansible }})
3535
run: pip install https://github.com/ansible/ansible/archive/v${{ matrix.ansible }}.tar.gz --disable-pip-version-check
@@ -52,11 +52,17 @@ jobs:
5252
runs-on: ubuntu-latest
5353
strategy:
5454
matrix:
55-
ansible: [2.15.12, 2.16.7]
55+
ansible: [2.15.13, 2.16.14, 2.17.12, 2.18.6]
5656
python: ["3.9", "3.10", "3.11"]
5757
exclude:
58-
- ansible: 2.16.7
58+
- ansible: 2.16.14
5959
python: "3.9"
60+
- ansible: 2.17.12
61+
python: "3.9"
62+
- ansible: 2.18.6
63+
python: "3.9"
64+
- ansible: 2.18.6
65+
python: "3.10"
6066
steps:
6167
- name: Set up Python (v${{ matrix.python }})
6268
uses: actions/setup-python@v1
@@ -87,15 +93,21 @@ jobs:
8793
runs-on: ubuntu-latest
8894
strategy:
8995
matrix:
90-
ansible: [2.15.12, 2.16.7]
96+
ansible: [2.15.13, 2.16.14, 2.17.12, 2.18.6]
9197
steps:
92-
- name: Set up Python "3.10"
98+
- name: Set up Python "3.11"
9399
uses: actions/setup-python@v1
94100
with:
95-
python-version: "3.10"
101+
python-version: "3.11"
96102

97103
- name: Install ansible-base (v${{ matrix.ansible }})
98104
run: pip install https://github.com/ansible/ansible/archive/v${{ matrix.ansible }}.tar.gz --disable-pip-version-check
105+
106+
- name: Install Pydantic (v2)
107+
run: pip install pydantic==2.11.4
108+
109+
- name: Install DeepDiff (v8.5.0)
110+
run: pip install deepdiff==8.5.0
99111

100112
- name: Install coverage (v7.3.4)
101113
run: pip install coverage==7.3.4
@@ -120,4 +132,4 @@ jobs:
120132

121133
- name: Generate coverage report
122134
run: coverage report
123-
working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/dcnm
135+
working-directory: /home/runner/.ansible/collections/ansible_collections/cisco/dcnm

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ venv.bak/
7979
.mypy_cache/
8080

8181
# Ignore Integration Tests Files Directories
82-
tests/integration/targets/ndfc_interface/files
82+
tests/integration/targets/ndfc_interface/files
83+
tests/integration/targets/dcnm_network/files

playbooks/roles/dcnm_network/dcnm_tests.yaml

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,71 @@
77
# Modify the vars section with details for your testing setup.
88
#
99
# NOTES:
10-
# 1. Ensure that the switches defined by ansible_switch1 and ansible_switch2 are
10+
# 1. Ensure that the switches defined by switch1 and switch2 are
1111
# not vPC Pairs.
1212
- hosts: dcnm
1313
gather_facts: no
1414
connection: ansible.netcommon.httpapi
1515

1616
vars:
17+
18+
#----------------------------------
1719
# Uncomment testcase to run a specific test
1820
# testcase: replaced_net_all
19-
test_fabric: nac-ndfc1
20-
ansible_switch1: 192.168.1.1
21-
ansible_switch2: 192.168.1.2
22-
ansible_sw1_int1: Ethernet1/15
23-
ansible_sw1_int2: Ethernet1/16
24-
ansible_sw1_int3: Ethernet1/17
25-
ansible_sw1_int4: Ethernet1/18
26-
#---
27-
ansible_sw2_int1: Ethernet1/15
28-
ansible_sw2_int2: Ethernet1/16
29-
ansible_sw2_int3: Ethernet1/17
30-
ansible_sw2_int4: Ethernet1/18
31-
ansible_sw2_int5: Ethernet1/19
32-
ansible_sw2_int6: Ethernet1/20
33-
21+
test_data_common:
22+
#---
23+
fabric: fabric-stage
24+
deploy: false
25+
#---
26+
# Resources
27+
#---
28+
sw1: 192.168.1.1
29+
sw2: 192.168.1.2
30+
#---
31+
# Common VRF setup
32+
#---
33+
vrf_1: ansible-vrf-int1
34+
vrf_1_id: 9008011
35+
vrf_1_vlan_id: 500
36+
#---
37+
vrf_2: Tenant-1
38+
vrf_2_id: 9008012
39+
vrf_2_vlan_id: 501
40+
#---
41+
vrf_3: Tenant-2
42+
vrf_3_id: 9008013
43+
vrf_3_vlan_id: 502
44+
#---
45+
# Interfaces
46+
#---
47+
sw1_int1: Ethernet1/15
48+
sw1_int2: Ethernet1/16
49+
sw1_int3: Ethernet1/17
50+
sw1_int4: Ethernet1/18
51+
#---
52+
sw2_int1: Ethernet1/15
53+
sw2_int2: Ethernet1/16
54+
sw2_int3: Ethernet1/17
55+
sw2_int4: Ethernet1/18
56+
sw2_int5: Ethernet1/19
57+
sw2_int6: Ethernet1/20
58+
#---
59+
net1: ansible-net13
60+
net1_net_id: 7005
61+
net1_default_net_template: Default_Network_Universal
62+
net1_net_extension_template: Default_Network_Extension_Universal
63+
net1_vlan_id: 1500
64+
net1_gw_ip_subnet: '192.168.30.1/24'
65+
#---
66+
net2: ansible-net12
67+
net2_net_id: 7002
68+
net2_default_net_template: Default_Network_Universal
69+
net2_net_extension_template: Default_Network_Extension_Universal
70+
net2_vlan_id: 151
71+
net2_gw_ip_subnet: '192.168.40.1/24'
72+
#---
73+
net1_vrf: Tenant-1
74+
net2_vrf: Tenant-2
75+
3476
roles:
3577
- dcnm_network

plugins/action/tests/__init__.py

Whitespace-only changes.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
from __future__ import absolute_import, division, print_function
2+
3+
4+
__metaclass__ = type
5+
6+
from pprint import pprint
7+
import json
8+
from ansible.utils.display import Display
9+
from ansible.plugins.action import ActionBase
10+
from ansible.module_utils.six import raise_from
11+
from ansible.errors import AnsibleError
12+
from ansible.module_utils.common.text.converters import to_native
13+
from ..plugin_utils.tools import load_yaml_file, process_deepdiff
14+
from ..plugin_utils.pydantic_schemas.dcnm_network.schemas import DcnmNetworkQuerySchema
15+
16+
try:
17+
from deepdiff import DeepDiff
18+
except ImportError as imp_exc:
19+
DEEPDIFF_IMPORT_ERROR = imp_exc
20+
else:
21+
DEEPDIFF_IMPORT_ERROR = None
22+
23+
if DEEPDIFF_IMPORT_ERROR:
24+
raise_from(
25+
AnsibleError('DeepDiff must be installed to use this plugin. Use pip or install test-requirements.'),
26+
DEEPDIFF_IMPORT_ERROR)
27+
28+
display = Display()
29+
30+
31+
class ActionModule(ActionBase):
32+
33+
def verify_deleted(self, results, check_deleted, expected_data, ndfc_data, config_path):
34+
if not check_deleted:
35+
return None
36+
existing_networks = set()
37+
for network in ndfc_data["response"]:
38+
existing_networks.add(network["parent"]["networkName"])
39+
40+
if config_path == "":
41+
# check for full delete
42+
if not ndfc_data["failed"] and len(existing_networks) == 0:
43+
results['msg'] = 'All networks are deleted'
44+
else:
45+
print("Networks still existing: ")
46+
print(existing_networks)
47+
results['failed'] = True
48+
results['msg'] = 'Error: Expected full delete as config_path is empty but networks still exist.'
49+
if ndfc_data["failed"]:
50+
results['msg'] += '\n\nError: ' + ndfc_data["error"]
51+
return results
52+
return results
53+
# checks for a partial delete
54+
deleted_networks = set()
55+
for network in expected_data["response"]:
56+
deleted_networks.add(network["parent"]["networkName"])
57+
58+
remaining_networks = existing_networks.intersection(deleted_networks)
59+
if len(remaining_networks) > 0:
60+
results['failed'] = True
61+
print("Expected networks to be deleted: ")
62+
print(deleted_networks)
63+
print("\nNetworks present in NDFC: ")
64+
print(existing_networks)
65+
print("\nNetworks still not deleted: ")
66+
print(remaining_networks)
67+
results['msg'] = 'All networks are not deleted'
68+
return results
69+
70+
print("Expected networks to be deleted: ")
71+
print(deleted_networks)
72+
print("\n\nNetworks present in NDFC: ")
73+
print(existing_networks)
74+
print("Networks still not deleted: ")
75+
print(remaining_networks)
76+
results['failed'] = False
77+
results['msg'] = 'Provided networks are deleted'
78+
return results
79+
80+
def run(self, tmp=None, task_vars=None):
81+
results = super(ActionModule, self).run(tmp, task_vars)
82+
results['failed'] = False
83+
84+
ndfc_data = self._task.args.get('ndfc_data', None)
85+
test_data = self._task.args.get('test_data', None)
86+
config_path = self._task.args.get('config_path', None)
87+
check_deleted = self._task.args.get('check_deleted', False)
88+
ignore_fields = list(self._task.args.get('ignore_fields', []))
89+
for input_item in [ndfc_data, test_data, config_path]:
90+
if input_item is None:
91+
results['failed'] = True
92+
results['msg'] = f"Required input parameter not found: '{input_item}'"
93+
return results
94+
95+
# removes ansible embeddings and converts to native python types
96+
native_ndfc_data = json.loads(json.dumps(ndfc_data, default=to_native))
97+
98+
test_fabric = test_data['fabric']
99+
100+
expected_data_parsed = None
101+
if config_path != "":
102+
# only parse if config file exists
103+
expected_config_data = load_yaml_file(config_path)
104+
expected_data = DcnmNetworkQuerySchema.yaml_config_to_dict(expected_config_data, test_fabric)
105+
106+
expected_data_parsed = DcnmNetworkQuerySchema.model_validate(expected_data).model_dump(exclude_none=True)
107+
108+
ndfc_data_parsed = DcnmNetworkQuerySchema.model_validate(native_ndfc_data).model_dump(exclude_none=True)
109+
110+
if deleted_results := self.verify_deleted(results, check_deleted, expected_data_parsed, ndfc_data_parsed, config_path):
111+
return deleted_results
112+
113+
validity = DeepDiff(
114+
expected_data_parsed,
115+
ndfc_data_parsed,
116+
ignore_order=True,
117+
cutoff_distance_for_pairs=0,
118+
cutoff_intersection_for_pairs=0,
119+
report_repetition=True
120+
)
121+
# Process the output of deepdiff to make it easier to read
122+
# Effects the iterable_item_added and iterable_item_removed to remove unneeded fields
123+
# ignore_extra_fields=True will ignore dictionary_item_added changes
124+
# This is useful when the the actual data has more fields than the expected data
125+
# keys_to_ignore is a list of fields to ignore, useful for auto provisioned fields which are not known
126+
processed_validity = process_deepdiff(validity, keys_to_ignore=ignore_fields, ignore_extra_fields=True)
127+
if processed_validity == {}:
128+
results['failed'] = False
129+
results['msg'] = f'Data is valid. \n\n Expected data: \n\n{expected_data}\n\nActual data: \n\n{ndfc_data_parsed}'
130+
else:
131+
results['failed'] = True
132+
print("\n\nExpected: ")
133+
pprint(expected_data_parsed)
134+
print("\n\nActual: ")
135+
pprint(ndfc_data_parsed)
136+
print("\n\nDifferences: ")
137+
pprint(processed_validity)
138+
results['msg'] = 'Data is not valid.'
139+
140+
return results

plugins/action/tests/plugin_utils/__init__.py

Whitespace-only changes.

plugins/action/tests/plugin_utils/pydantic_schemas/__init__.py

Whitespace-only changes.

plugins/action/tests/plugin_utils/pydantic_schemas/dcnm_network/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)