3
3
# Copyright: (c) 2021, Lionel Hercot (@lhercot) <[email protected] >
4
4
# Copyright: (c) 2022, Cindy Zhao (@cizhao) <[email protected] >
5
5
# Copyright: (c) 2022, Akini Ross (@akinross) <[email protected] >
6
+ # Copyright: (c) 2025, Shreyas Srish (@shrsr) <[email protected] >
6
7
7
8
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
8
9
18
19
from ansible .module_utils .basic import json
19
20
from ansible .module_utils .basic import env_fallback
20
21
from ansible .module_utils .six import PY3
21
- from ansible .module_utils .six .moves import filterfalse
22
22
from ansible .module_utils .six .moves .urllib .parse import urlencode
23
23
from ansible .module_utils ._text import to_native , to_text
24
24
from ansible .module_utils .connection import Connection
@@ -73,53 +73,27 @@ def cmp(a, b):
73
73
74
74
75
75
def issubset (subset , superset ):
76
- """Recurse through nested dictionary and compare entries """
76
+ """Recurse through a nested dictionary and check if it is a subset of another. """
77
77
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 ):
78
+ if type (subset ) is not type (superset ):
88
79
return False
89
80
81
+ if not isinstance (subset , dict ):
82
+ if isinstance (subset , list ):
83
+ return all (item in superset for item in subset )
84
+ return subset == superset
85
+
90
86
for key , value in subset .items ():
91
- # Ignore empty values
92
87
if value is None :
93
- return True
88
+ continue
94
89
95
- # Item from subset is missing from superset
96
90
if key not in superset :
97
91
return False
98
92
99
- # Item has different types in subset and superset
100
- if isinstance (superset .get (key )) is not isinstance (value ):
101
- return False
93
+ superset_value = superset .get (key )
102
94
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
95
+ if not issubset (value , superset_value ):
96
+ return False
123
97
124
98
return True
125
99
@@ -252,7 +226,7 @@ def request(
252
226
if file is not None :
253
227
info = conn .send_file_request (method , uri , file , data , None , file_key , file_ext )
254
228
else :
255
- if data :
229
+ if data is not None :
256
230
info = conn .send_request (method , uri , json .dumps (data ))
257
231
else :
258
232
info = conn .send_request (method , uri )
@@ -310,6 +284,8 @@ def request(
310
284
self .fail_json (msg = "ND Error: {0}" .format (self .error .get ("message" )), data = data , info = info )
311
285
self .error = payload
312
286
if "code" in payload :
287
+ if self .status == 404 and ignore_not_found_error :
288
+ return {}
313
289
self .fail_json (msg = "ND Error {code}: {message}" .format (** payload ), data = data , info = info , payload = payload )
314
290
elif "messages" in payload and len (payload .get ("messages" )) > 0 :
315
291
self .fail_json (msg = "ND Error {code} ({severity}): {message}" .format (** payload ["messages" ][0 ]), data = data , info = info , payload = payload )
@@ -375,6 +351,37 @@ def get_obj(self, path, **kwargs):
375
351
self .fail_json (msg = "More than one object matches unique filter: {0}" .format (kwargs ))
376
352
return objs [0 ]
377
353
354
+ def get_object_by_nested_key_value (self , path , nested_key_path , value , data_key = None ):
355
+
356
+ response_data = self .request (path , method = "GET" )
357
+
358
+ if not response_data :
359
+ return None
360
+
361
+ object_list = []
362
+ if isinstance (response_data , list ):
363
+ object_list = response_data
364
+ elif data_key and data_key in response_data :
365
+ object_list = response_data .get (data_key )
366
+ else :
367
+ return None
368
+
369
+ keys = nested_key_path .split ("." )
370
+
371
+ for obj in object_list :
372
+ current_level = obj
373
+ for key in keys :
374
+ if isinstance (current_level , dict ):
375
+ current_level = current_level .get (key )
376
+ else :
377
+ current_level = None
378
+ break
379
+
380
+ if current_level == value :
381
+ return obj
382
+
383
+ return None
384
+
378
385
def sanitize (self , updates , collate = False , required = None , unwanted = None ):
379
386
"""Clean up unset keys from a request payload"""
380
387
if required is None :
@@ -506,8 +513,8 @@ def get_diff(self, unwanted=None):
506
513
if not self .existing and self .sent :
507
514
return True
508
515
509
- existing = self .existing
510
- sent = self .sent
516
+ existing = deepcopy ( self .existing )
517
+ sent = deepcopy ( self .sent )
511
518
512
519
for key in unwanted :
513
520
if isinstance (key , str ):
@@ -516,6 +523,7 @@ def get_diff(self, unwanted=None):
516
523
del existing [key ]
517
524
except KeyError :
518
525
pass
526
+ if key in sent :
519
527
try :
520
528
del sent [key ]
521
529
except KeyError :
@@ -524,12 +532,14 @@ def get_diff(self, unwanted=None):
524
532
key_path , last = key [:- 1 ], key [- 1 ]
525
533
try :
526
534
existing_parent = reduce (dict .get , key_path , existing )
527
- del existing_parent [last ]
535
+ if existing_parent is not None :
536
+ del existing_parent [last ]
528
537
except KeyError :
529
538
pass
530
539
try :
531
540
sent_parent = reduce (dict .get , key_path , sent )
532
- del sent_parent [last ]
541
+ if sent_parent is not None :
542
+ del sent_parent [last ]
533
543
except KeyError :
534
544
pass
535
545
return not issubset (sent , existing )
0 commit comments