mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 03:11:24 -07:00
refactor moudule utils of hwc_utils.py (#55858)
* use navigate_value instead navigate_hash * add async wait method * update dict compare * remove unuse methods * not all modules have timeouts parameter * navigate_value, the input data may be None
This commit is contained in:
parent
34a8594c91
commit
d8314e1a45
6 changed files with 305 additions and 327 deletions
|
@ -3,6 +3,7 @@
|
|||
# https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
|
||||
THIRD_LIBRARIES_IMP_ERR = None
|
||||
|
@ -20,59 +21,14 @@ from ansible.module_utils.basic import (AnsibleModule, env_fallback,
|
|||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
def navigate_hash(source, path, default=None):
|
||||
if not (source and path):
|
||||
return None
|
||||
class HwcModuleException(Exception):
|
||||
def __init__(self, message):
|
||||
super(HwcModuleException, self).__init__()
|
||||
|
||||
key = path[0]
|
||||
path = path[1:]
|
||||
if key not in source:
|
||||
return default
|
||||
result = source[key]
|
||||
if path:
|
||||
return navigate_hash(result, path, default)
|
||||
else:
|
||||
return result
|
||||
self._message = message
|
||||
|
||||
|
||||
def remove_empty_from_dict(obj):
|
||||
return _DictClean(
|
||||
obj,
|
||||
lambda v: v is not None and v != {} and v != []
|
||||
)()
|
||||
|
||||
|
||||
def remove_nones_from_dict(obj):
|
||||
return _DictClean(obj, lambda v: v is not None)()
|
||||
|
||||
|
||||
def replace_resource_dict(item, value):
|
||||
""" Handles the replacement of dicts with values ->
|
||||
the needed value for HWC API"""
|
||||
if isinstance(item, list):
|
||||
items = []
|
||||
for i in item:
|
||||
items.append(replace_resource_dict(i, value))
|
||||
return items
|
||||
else:
|
||||
if not item:
|
||||
return item
|
||||
return item.get(value)
|
||||
|
||||
|
||||
def are_dicts_different(expect, actual):
|
||||
"""Remove all output-only from actual."""
|
||||
actual_vals = {}
|
||||
for k, v in actual.items():
|
||||
if k in expect:
|
||||
actual_vals[k] = v
|
||||
|
||||
expect_vals = {}
|
||||
for k, v in expect.items():
|
||||
if k in actual:
|
||||
expect_vals[k] = v
|
||||
|
||||
return DictComparison(expect_vals) != DictComparison(actual_vals)
|
||||
def __str__(self):
|
||||
return "[HwcClientException] message=%s" % self._message
|
||||
|
||||
|
||||
class HwcClientException(Exception):
|
||||
|
@ -116,9 +72,9 @@ def session_method_wrapper(f):
|
|||
code = r.status_code
|
||||
if code not in [200, 201, 202, 203, 204, 205, 206, 207, 208, 226]:
|
||||
msg = ""
|
||||
for i in [['message'], ['error', 'message']]:
|
||||
for i in ['message', 'error.message']:
|
||||
try:
|
||||
msg = navigate_hash(result, i)
|
||||
msg = navigate_value(result, i)
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
@ -281,14 +237,9 @@ class HwcModule(AnsibleModule):
|
|||
fallback=(env_fallback, ['ANSIBLE_HWC_PROJECT']),
|
||||
),
|
||||
region=dict(
|
||||
required=True, type='str',
|
||||
type='str',
|
||||
fallback=(env_fallback, ['ANSIBLE_HWC_REGION']),
|
||||
),
|
||||
timeouts=dict(type='dict', options=dict(
|
||||
create=dict(default='10m', type='str'),
|
||||
update=dict(default='10m', type='str'),
|
||||
delete=dict(default='10m', type='str'),
|
||||
), default={}),
|
||||
id=dict(type='str')
|
||||
)
|
||||
)
|
||||
|
@ -296,12 +247,13 @@ class HwcModule(AnsibleModule):
|
|||
super(HwcModule, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class DictComparison(object):
|
||||
class _DictComparison(object):
|
||||
''' This class takes in two dictionaries `a` and `b`.
|
||||
These are dictionaries of arbitrary depth, but made up of standard
|
||||
Python types only.
|
||||
This differ will compare all values in `a` to those in `b`.
|
||||
Note: Only keys in `a` will be compared. Extra keys in `b` will be ignored.
|
||||
If value in `a` is None, always returns True, indicating
|
||||
this value is no need to compare.
|
||||
Note: On all lists, order does matter.
|
||||
'''
|
||||
|
||||
|
@ -315,76 +267,136 @@ class DictComparison(object):
|
|||
return not self.__eq__(other)
|
||||
|
||||
def _compare_dicts(self, dict1, dict2):
|
||||
if len(dict1.keys()) != len(dict2.keys()):
|
||||
if dict1 is None:
|
||||
return True
|
||||
|
||||
if set(dict1.keys()) != set(dict2.keys()):
|
||||
return False
|
||||
|
||||
return all([
|
||||
self._compare_value(dict1.get(k), dict2.get(k)) for k in dict1
|
||||
])
|
||||
for k in dict1:
|
||||
if not self._compare_value(dict1.get(k), dict2.get(k)):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _compare_lists(self, list1, list2):
|
||||
"""Takes in two lists and compares them."""
|
||||
if list1 is None:
|
||||
return True
|
||||
|
||||
if len(list1) != len(list2):
|
||||
return False
|
||||
|
||||
difference = []
|
||||
for index in range(len(list1)):
|
||||
value1 = list1[index]
|
||||
if index < len(list2):
|
||||
value2 = list2[index]
|
||||
difference.append(self._compare_value(value1, value2))
|
||||
for i in range(len(list1)):
|
||||
if not self._compare_value(list1[i], list2[i]):
|
||||
return False
|
||||
|
||||
return all(difference)
|
||||
return True
|
||||
|
||||
def _compare_value(self, value1, value2):
|
||||
"""
|
||||
return: True: value1 is same as value2, otherwise False.
|
||||
"""
|
||||
if value1 is None:
|
||||
return True
|
||||
|
||||
if not (value1 and value2):
|
||||
return (not value1) and (not value2)
|
||||
|
||||
# Can assume non-None types at this point.
|
||||
if isinstance(value1, list):
|
||||
if isinstance(value1, list) and isinstance(value2, list):
|
||||
return self._compare_lists(value1, value2)
|
||||
elif isinstance(value1, dict):
|
||||
|
||||
elif isinstance(value1, dict) and isinstance(value2, dict):
|
||||
return self._compare_dicts(value1, value2)
|
||||
|
||||
# Always use to_text values to avoid unicode issues.
|
||||
return (to_text(value1, errors='surrogate_or_strict') == to_text(
|
||||
value2, errors='surrogate_or_strict'))
|
||||
|
||||
|
||||
def wait_to_finish(target, pending, refresh, timeout, min_interval=1, delay=3):
|
||||
is_last_time = False
|
||||
not_found_times = 0
|
||||
wait = 0
|
||||
|
||||
time.sleep(delay)
|
||||
|
||||
end = time.time() + timeout
|
||||
while not is_last_time:
|
||||
if time.time() > end:
|
||||
is_last_time = True
|
||||
|
||||
obj, status = refresh()
|
||||
|
||||
if obj is None:
|
||||
not_found_times += 1
|
||||
|
||||
if not_found_times > 10:
|
||||
raise HwcModuleException(
|
||||
"not found the object for %d times" % not_found_times)
|
||||
else:
|
||||
return (to_text(value1, errors='surrogate_or_strict')
|
||||
== to_text(value2, errors='surrogate_or_strict'))
|
||||
not_found_times = 0
|
||||
|
||||
if status in target:
|
||||
return obj
|
||||
|
||||
if pending and status not in pending:
|
||||
raise HwcModuleException(
|
||||
"unexpect status(%s) occured" % status)
|
||||
|
||||
if not is_last_time:
|
||||
wait *= 2
|
||||
if wait < min_interval:
|
||||
wait = min_interval
|
||||
elif wait > 10:
|
||||
wait = 10
|
||||
|
||||
time.sleep(wait)
|
||||
|
||||
raise HwcModuleException("asycn wait timeout after %d seconds" % timeout)
|
||||
|
||||
|
||||
class _DictClean(object):
|
||||
def __init__(self, obj, func):
|
||||
self.obj = obj
|
||||
self.keep_it = func
|
||||
def navigate_value(data, index, array_index=None):
|
||||
if array_index and (not isinstance(array_index, dict)):
|
||||
raise HwcModuleException("array_index must be dict")
|
||||
|
||||
def __call__(self):
|
||||
return self._clean_dict(self.obj)
|
||||
d = data
|
||||
for n in range(len(index)):
|
||||
if d is None:
|
||||
return None
|
||||
|
||||
def _clean_dict(self, obj):
|
||||
r = {}
|
||||
for k, v in obj.items():
|
||||
v1 = v
|
||||
if isinstance(v, dict):
|
||||
v1 = self._clean_dict(v)
|
||||
elif isinstance(v, list):
|
||||
v1 = self._clean_list(v)
|
||||
if self.keep_it(v1):
|
||||
r[k] = v1
|
||||
return r
|
||||
if not isinstance(d, dict):
|
||||
raise HwcModuleException(
|
||||
"can't navigate value from a non-dict object")
|
||||
|
||||
def _clean_list(self, obj):
|
||||
r = []
|
||||
for v in obj:
|
||||
v1 = v
|
||||
if isinstance(v, dict):
|
||||
v1 = self._clean_dict(v)
|
||||
elif isinstance(v, list):
|
||||
v1 = self._clean_list(v)
|
||||
if self.keep_it(v1):
|
||||
r.append(v1)
|
||||
return r
|
||||
i = index[n]
|
||||
if i not in d:
|
||||
raise HwcModuleException(
|
||||
"navigate value failed: key(%s) is not exist in dict" % i)
|
||||
d = d[i]
|
||||
|
||||
if not array_index:
|
||||
continue
|
||||
|
||||
k = ".".join(index[: (n + 1)])
|
||||
if k not in array_index:
|
||||
continue
|
||||
|
||||
if d is None:
|
||||
return None
|
||||
|
||||
if not isinstance(d, list):
|
||||
raise HwcModuleException(
|
||||
"can't navigate value from a non-list object")
|
||||
|
||||
j = array_index.get(k)
|
||||
if j >= len(d):
|
||||
raise HwcModuleException(
|
||||
"navigate value failed: the index is out of list")
|
||||
d = d[j]
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def build_path(module, path, kv=None):
|
||||
|
@ -411,4 +423,12 @@ def get_region(module):
|
|||
if module.params['region']:
|
||||
return module.params['region']
|
||||
|
||||
return module.params['project_name'].split("_")[0]
|
||||
return module.params['project'].split("_")[0]
|
||||
|
||||
|
||||
def is_empty_value(v):
|
||||
return (not v)
|
||||
|
||||
|
||||
def are_different_dicts(dict1, dict2):
|
||||
return _DictComparison(dict1) != _DictComparison(dict2)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue