diff --git a/lib/ansible/module_utils/network/nso/nso.py b/lib/ansible/module_utils/network/nso/nso.py index 323923a3ddc..9fdb5acecb8 100644 --- a/lib/ansible/module_utils/network/nso/nso.py +++ b/lib/ansible/module_utils/network/nso/nso.py @@ -320,15 +320,16 @@ class ValueBuilder(object): return 'Value'.format( self.path, self.state, self.value) - def __init__(self, client): + def __init__(self, client, mode='config'): self._client = client + self._mode = mode self._schema_cache = {} self._module_prefix_map_cache = None self._values = [] self._values_dirty = False def build(self, parent, maybe_qname, value, schema=None): - qname, name = self._get_prefix_name(maybe_qname) + qname, name = self.get_prefix_name(maybe_qname) if name is None: path = parent else: @@ -349,16 +350,16 @@ class ValueBuilder(object): self._add_value(path, State.PRESENT, None, deps) else: if maybe_qname is None: - value_type = self._get_type(path) + value_type = self.get_type(path) else: value_type = self._get_child_type(parent, qname) if 'identityref' in value_type: if isinstance(value, list): value = [ll_v for ll_v, t_ll_v - in [self._get_prefix_name(v) for v in value]] + in [self.get_prefix_name(v) for v in value]] else: - value, t_value = self._get_prefix_name(value) + value, t_value = self.get_prefix_name(value) self._add_value(path, State.SET, value, deps) elif isinstance(value, dict): self._build_dict(path, schema, value) @@ -440,7 +441,7 @@ class ValueBuilder(object): def _build_dict(self, path, schema, value): keys = schema.get('key', []) for dict_key, dict_value in value.items(): - qname, name = self._get_prefix_name(dict_key) + qname, name = self.get_prefix_name(dict_key) if dict_key in ('__state', ) or name in keys: continue @@ -449,16 +450,25 @@ class ValueBuilder(object): def _build_leaf_list(self, path, schema, value): deps = schema.get('deps', []) - entry_type = self._get_type(path, schema) - # remove leaf list if treated as a list and then re-create the - # expected list entries. - self._add_value(path, State.ABSENT, None, deps) + entry_type = self.get_type(path, schema) + + if self._mode == 'verify': + for entry in value: + if 'identityref' in entry_type: + entry, t_entry = self.get_prefix_name(entry) + entry_path = '{0}{{{1}}}'.format(path, entry) + if not self._client.exists(entry_path): + self._add_value(entry_path, State.ABSENT, None, deps) + else: + # remove leaf list if treated as a list and then re-create the + # expected list entries. + self._add_value(path, State.ABSENT, None, deps) - for entry in value: - if 'identityref' in entry_type: - entry, t_entry = self._get_prefix_name(entry) - entry_path = '{0}{{{1}}}'.format(path, entry) - self._add_value(entry_path, State.PRESENT, None, deps) + for entry in value: + if 'identityref' in entry_type: + entry, t_entry = self.get_prefix_name(entry) + entry_path = '{0}{{{1}}}'.format(path, entry) + self._add_value(entry_path, State.PRESENT, None, deps) def _build_list(self, path, schema, value): deps = schema.get('deps', []) @@ -490,7 +500,7 @@ class ValueBuilder(object): value_type = self._get_child_type(path, key) if 'identityref' in value_type: - value, t_value = self._get_prefix_name(value) + value, t_value = self.get_prefix_name(value) key_parts.append(self._quote_key(value)) return ' '.join(key_parts) @@ -533,7 +543,7 @@ class ValueBuilder(object): self._values.append(ValueBuilder.Value(path, state, value, deps)) self._values_dirty = True - def _get_prefix_name(self, qname): + def get_prefix_name(self, qname): if not isinstance(qname, (str, unicode)): return qname, None if ':' not in qname: @@ -556,9 +566,9 @@ class ValueBuilder(object): parent_schema = all_schema['data'] meta = all_schema['meta'] schema = self._find_child(parent_path, parent_schema, key) - return self._get_type(parent_path, schema, meta) + return self.get_type(parent_path, schema, meta) - def _get_type(self, path, schema=None, meta=None): + def get_type(self, path, schema=None, meta=None): if schema is None or meta is None: all_schema = self._ensure_schema_cached(path) schema = all_schema['data'] @@ -686,7 +696,8 @@ def verify_version_str(version_str, required_versions): def normalize_value(expected_value, value, key): if value is None: return None - if isinstance(expected_value, bool): + if (isinstance(expected_value, bool) and + isinstance(value, (str, unicode))): return value == 'true' if isinstance(expected_value, int): try: diff --git a/lib/ansible/modules/network/nso/nso_verify.py b/lib/ansible/modules/network/nso/nso_verify.py index 087c66515e7..5e2cc472766 100644 --- a/lib/ansible/modules/network/nso/nso_verify.py +++ b/lib/ansible/modules/network/nso/nso_verify.py @@ -116,7 +116,7 @@ class NsoVerify(object): violations = [] # build list of values from configured data - value_builder = ValueBuilder(self._client) + value_builder = ValueBuilder(self._client, 'verify') for key, value in self._data.items(): value_builder.build('', key, value) @@ -146,11 +146,17 @@ class NsoVerify(object): n_value = normalize_value( expected_value.value, value, expected_value.path) if n_value != expected_value.value: - violations.append({ - 'path': expected_value.path, - 'expected-value': expected_value.value, - 'value': n_value - }) + # if the value comparision fails, try mapping identityref + value_type = value_builder.get_type(expected_value.path) + if value_type is not None and 'identityref' in value_type: + n_value, t_value = self.get_prefix_name(value) + + if expected_value.value != n_value: + violations.append({ + 'path': expected_value.path, + 'expected-value': expected_value.value, + 'value': n_value + }) else: raise ModuleFailException( 'value state {0} not supported at {1}'.format( diff --git a/test/units/modules/network/nso/nso_module.py b/test/units/modules/network/nso/nso_module.py index d2e2fd98267..18566cd4ebf 100644 --- a/test/units/modules/network/nso/nso_module.py +++ b/test/units/modules/network/nso/nso_module.py @@ -61,6 +61,10 @@ class MockResponse(object): def mock_call(calls, url, timeout, data=None, headers=None, method=None): + if len(calls) == 0: + raise ValueError('no call mock for method {0}({1})'.format( + url, data)) + result = calls[0] del calls[0] @@ -99,6 +103,8 @@ class TestNsoModule(unittest.TestCase): self.assertEqual(result['changed'], changed, result) for key, value in kwargs.items(): + if key not in result: + self.fail("{0} not in result {1}".format(key, result)) self.assertEqual(value, result[key]) return result diff --git a/test/units/modules/network/nso/test_nso_verify.py b/test/units/modules/network/nso/test_nso_verify.py index 06ee5284afe..34aa4530313 100644 --- a/test/units/modules/network/nso/test_nso_verify.py +++ b/test/units/modules/network/nso/test_nso_verify.py @@ -51,6 +51,7 @@ class TestNsoVerify(nso_module.TestNsoModule): def test_nso_verify_violation(self, open_url_mock): devices_schema = nso_module.load_fixture('devices_schema.json') device_schema = nso_module.load_fixture('device_schema.json') + description_schema = nso_module.load_fixture('description_schema.json') calls = [ MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}), @@ -61,6 +62,7 @@ class TestNsoVerify(nso_module.TestNsoModule): MockResponse('get_schema', {'path': '/ncs:devices/device'}, 200, '{"result": %s}' % (json.dumps(device_schema, ))), MockResponse('exists', {'path': '/ncs:devices/device{ce0}'}, 200, '{"result": {"exists": true}}'), MockResponse('get_value', {'path': '/ncs:devices/device{ce0}/description'}, 200, '{"result": {"value": "In Violation"}}'), + MockResponse('get_schema', {'path': '/ncs:devices/device/description'}, 200, '{"result": %s}' % (json.dumps(description_schema, ))), MockResponse('logout', {}, 200, '{"result": {}}'), ] open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)