Skip to content

Commit a6465d2

Browse files
committed
[minor_change] Created nd_backup_schedule module using Ansible Network Resource Modules Approach
1 parent 3cab814 commit a6465d2

File tree

5 files changed

+816
-392
lines changed

5 files changed

+816
-392
lines changed

plugins/module_utils/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@
156156
"disable",
157157
"restart",
158158
"delete",
159+
"deleted",
159160
"update",
161+
"merged",
162+
"replaced",
163+
"overridden",
160164
)
161165

162166
INTERFACE_FLOW_RULES_TYPES_MAPPING = {"port_channel": "PORTCHANNEL", "physical": "PHYSICAL", "l3out_sub_interface": "L3_SUBIF", "l3out_svi": "SVI"}

plugins/module_utils/nd.py

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from ansible.module_utils.basic import json
1919
from ansible.module_utils.basic import env_fallback
2020
from ansible.module_utils.six import PY3
21-
from ansible.module_utils.six.moves import filterfalse
2221
from ansible.module_utils.six.moves.urllib.parse import urlencode
2322
from ansible.module_utils._text import to_native, to_text
2423
from ansible.module_utils.connection import Connection
@@ -73,53 +72,27 @@ def cmp(a, b):
7372

7473

7574
def issubset(subset, superset):
76-
"""Recurse through nested dictionary and compare entries"""
75+
"""Recurse through a nested dictionary and check if it is a subset of another."""
7776

78-
# Both objects are the same object
79-
if subset is superset:
80-
return True
81-
82-
# Both objects are identical
83-
if subset == superset:
84-
return True
85-
86-
# Both objects have a different type
87-
if isinstance(subset) is not isinstance(superset):
77+
if type(subset) is not type(superset):
8878
return False
8979

80+
if not isinstance(subset, dict):
81+
if isinstance(subset, list):
82+
return all(item in superset for item in subset)
83+
return subset == superset
84+
9085
for key, value in subset.items():
91-
# Ignore empty values
9286
if value is None:
93-
return True
87+
continue
9488

95-
# Item from subset is missing from superset
9689
if key not in superset:
9790
return False
9891

99-
# Item has different types in subset and superset
100-
if isinstance(superset.get(key)) is not isinstance(value):
101-
return False
92+
superset_value = superset.get(key)
10293

103-
# Compare if item values are subset
104-
if isinstance(value, dict):
105-
if not issubset(superset.get(key), value):
106-
return False
107-
elif isinstance(value, list):
108-
try:
109-
# NOTE: Fails for lists of dicts
110-
if not set(value) <= set(superset.get(key)):
111-
return False
112-
except TypeError:
113-
# Fall back to exact comparison for lists of dicts
114-
diff = list(filterfalse(lambda i: i in value, superset.get(key))) + list(filterfalse(lambda j: j in superset.get(key), value))
115-
if diff:
116-
return False
117-
elif isinstance(value, set):
118-
if not value <= superset.get(key):
119-
return False
120-
else:
121-
if not value == superset.get(key):
122-
return False
94+
if not issubset(value, superset_value):
95+
return False
12396

12497
return True
12598

@@ -210,6 +183,9 @@ def __init__(self, module):
210183

211184
# info output
212185
self.previous = dict()
186+
self.before = []
187+
self.commands = []
188+
self.after = []
213189
self.proposed = dict()
214190
self.sent = dict()
215191
self.stdout = None
@@ -433,6 +409,7 @@ def exit_json(self, **kwargs):
433409
if self.params.get("state") in ALLOWED_STATES_TO_APPEND_SENT_AND_PROPOSED:
434410
if self.params.get("output_level") in ("debug", "info"):
435411
self.result["previous"] = self.previous
412+
self.result["before"] = self.before
436413
# FIXME: Modified header only works for PATCH
437414
if not self.has_modified and self.previous != self.existing:
438415
self.result["changed"] = True
@@ -450,8 +427,10 @@ def exit_json(self, **kwargs):
450427
if self.params.get("state") in ALLOWED_STATES_TO_APPEND_SENT_AND_PROPOSED:
451428
self.result["sent"] = self.sent
452429
self.result["proposed"] = self.proposed
430+
self.result["commands"] = self.commands
453431

454432
self.result["current"] = self.existing
433+
self.result["after"] = self.after
455434

456435
if self.module._diff and self.result.get("changed") is True:
457436
self.result["diff"] = dict(
@@ -468,6 +447,7 @@ def fail_json(self, msg, **kwargs):
468447
if self.params.get("state") in ALLOWED_STATES_TO_APPEND_SENT_AND_PROPOSED:
469448
if self.params.get("output_level") in ("debug", "info"):
470449
self.result["previous"] = self.previous
450+
self.result["before"] = self.before
471451
# FIXME: Modified header only works for PATCH
472452
if not self.has_modified and self.previous != self.existing:
473453
self.result["changed"] = True
@@ -486,8 +466,10 @@ def fail_json(self, msg, **kwargs):
486466
if self.params.get("state") in ALLOWED_STATES_TO_APPEND_SENT_AND_PROPOSED:
487467
self.result["sent"] = self.sent
488468
self.result["proposed"] = self.proposed
469+
self.result["commands"] = self.commands
489470

490471
self.result["current"] = self.existing
472+
self.result["after"] = self.after
491473

492474
self.result.update(**kwargs)
493475
self.module.fail_json(msg=msg, **self.result)
@@ -499,23 +481,30 @@ def check_changed(self):
499481
existing["password"] = self.sent.get("password")
500482
return not issubset(self.sent, existing)
501483

502-
def get_diff(self, unwanted=None):
484+
def get_diff(self, unwanted=None, previous=None, payload=None):
503485
"""Check if existing payload and sent payload and removing keys that are not required"""
504486
if unwanted is None:
505487
unwanted = []
506-
if not self.existing and self.sent:
507-
return True
488+
489+
if previous is None and payload is None:
490+
if not self.existing and self.sent:
491+
return True
508492

509493
existing = self.existing
510494
sent = self.sent
511495

496+
if previous and payload:
497+
existing = previous
498+
sent = payload
499+
512500
for key in unwanted:
513501
if isinstance(key, str):
514502
if key in existing:
515503
try:
516504
del existing[key]
517505
except KeyError:
518506
pass
507+
if key in sent:
519508
try:
520509
del sent[key]
521510
except KeyError:
@@ -524,12 +513,14 @@ def get_diff(self, unwanted=None):
524513
key_path, last = key[:-1], key[-1]
525514
try:
526515
existing_parent = reduce(dict.get, key_path, existing)
527-
del existing_parent[last]
516+
if existing_parent is not None:
517+
del existing_parent[last]
528518
except KeyError:
529519
pass
530520
try:
531521
sent_parent = reduce(dict.get, key_path, sent)
532-
del sent_parent[last]
522+
if sent_parent is not None:
523+
del sent_parent[last]
533524
except KeyError:
534525
pass
535526
return not issubset(sent, existing)
@@ -567,3 +558,8 @@ def get_object_by_nested_key_value(self, path, nested_key_path, value, data_key=
567558
return obj
568559

569560
return None
561+
562+
def delete(self, check_mode, path):
563+
if not check_mode:
564+
self.request(path, method="DELETE")
565+
return {"path": path, "method": "DELETE"}

plugins/module_utils/utils.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,44 @@ def snake_to_camel(snake_str, upper_case_components=None):
2424
return camel_case_str
2525
else:
2626
return snake_str
27+
28+
29+
def compare_config_and_remote_objects(remote_objects, config_objects, key="name"):
30+
remote_object_names = {obj[key] for obj in remote_objects}
31+
config_object_names = {obj[key] for obj in config_objects}
32+
33+
# Common objects from Config (name in both remote and config data)
34+
update = [obj for obj in config_objects if obj[key] in remote_object_names]
35+
36+
# Unmatched objects from Remote (name not in Config)
37+
delete = [obj for obj in remote_objects if obj[key] not in config_object_names]
38+
39+
# Unmatched objects from Config (name not in Remote)
40+
create = [obj for obj in config_objects if obj[key] not in remote_object_names]
41+
42+
return {
43+
"config_data_update": update,
44+
"remote_data_delete": delete, # Only when state is overridden
45+
"config_data_create": create,
46+
}
47+
48+
49+
def compare_unordered_list_of_dicts(list1, list2):
50+
if (not isinstance(list1, list) or not isinstance(list2, list)) or (len(list1) != len(list2)):
51+
return False
52+
53+
for dict1 in list1:
54+
found_match = False
55+
for i, dict2 in enumerate(list2):
56+
if dict1 == dict2:
57+
list2.pop(i)
58+
found_match = True
59+
break
60+
if not found_match:
61+
return False
62+
63+
return True
64+
65+
66+
def wrap_objects_by_key(object_list, key="name"):
67+
return {obj.get(key): obj for obj in object_list}

0 commit comments

Comments
 (0)