nso_verify handle leaf-list in 4.5 and identityref (#37393)
NSO verify did not handle leaf-list value verification in 4.5 and later due to changes made for configuration writing made. map prefix for identityref types in verification.
This commit is contained in:
parent
1fd9a616a4
commit
6308047dc9
4 changed files with 51 additions and 26 deletions
|
@ -320,15 +320,16 @@ class ValueBuilder(object):
|
||||||
return 'Value<path={0}, state={1}, value={2}>'.format(
|
return 'Value<path={0}, state={1}, value={2}>'.format(
|
||||||
self.path, self.state, self.value)
|
self.path, self.state, self.value)
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client, mode='config'):
|
||||||
self._client = client
|
self._client = client
|
||||||
|
self._mode = mode
|
||||||
self._schema_cache = {}
|
self._schema_cache = {}
|
||||||
self._module_prefix_map_cache = None
|
self._module_prefix_map_cache = None
|
||||||
self._values = []
|
self._values = []
|
||||||
self._values_dirty = False
|
self._values_dirty = False
|
||||||
|
|
||||||
def build(self, parent, maybe_qname, value, schema=None):
|
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:
|
if name is None:
|
||||||
path = parent
|
path = parent
|
||||||
else:
|
else:
|
||||||
|
@ -349,16 +350,16 @@ class ValueBuilder(object):
|
||||||
self._add_value(path, State.PRESENT, None, deps)
|
self._add_value(path, State.PRESENT, None, deps)
|
||||||
else:
|
else:
|
||||||
if maybe_qname is None:
|
if maybe_qname is None:
|
||||||
value_type = self._get_type(path)
|
value_type = self.get_type(path)
|
||||||
else:
|
else:
|
||||||
value_type = self._get_child_type(parent, qname)
|
value_type = self._get_child_type(parent, qname)
|
||||||
|
|
||||||
if 'identityref' in value_type:
|
if 'identityref' in value_type:
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = [ll_v for ll_v, t_ll_v
|
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:
|
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)
|
self._add_value(path, State.SET, value, deps)
|
||||||
elif isinstance(value, dict):
|
elif isinstance(value, dict):
|
||||||
self._build_dict(path, schema, value)
|
self._build_dict(path, schema, value)
|
||||||
|
@ -440,7 +441,7 @@ class ValueBuilder(object):
|
||||||
def _build_dict(self, path, schema, value):
|
def _build_dict(self, path, schema, value):
|
||||||
keys = schema.get('key', [])
|
keys = schema.get('key', [])
|
||||||
for dict_key, dict_value in value.items():
|
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:
|
if dict_key in ('__state', ) or name in keys:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -449,16 +450,25 @@ class ValueBuilder(object):
|
||||||
|
|
||||||
def _build_leaf_list(self, path, schema, value):
|
def _build_leaf_list(self, path, schema, value):
|
||||||
deps = schema.get('deps', [])
|
deps = schema.get('deps', [])
|
||||||
entry_type = self._get_type(path, schema)
|
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)
|
|
||||||
|
|
||||||
for entry in value:
|
if self._mode == 'verify':
|
||||||
if 'identityref' in entry_type:
|
for entry in value:
|
||||||
entry, t_entry = self._get_prefix_name(entry)
|
if 'identityref' in entry_type:
|
||||||
entry_path = '{0}{{{1}}}'.format(path, entry)
|
entry, t_entry = self.get_prefix_name(entry)
|
||||||
self._add_value(entry_path, State.PRESENT, None, deps)
|
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)
|
||||||
|
|
||||||
def _build_list(self, path, schema, value):
|
def _build_list(self, path, schema, value):
|
||||||
deps = schema.get('deps', [])
|
deps = schema.get('deps', [])
|
||||||
|
@ -490,7 +500,7 @@ class ValueBuilder(object):
|
||||||
|
|
||||||
value_type = self._get_child_type(path, key)
|
value_type = self._get_child_type(path, key)
|
||||||
if 'identityref' in value_type:
|
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))
|
key_parts.append(self._quote_key(value))
|
||||||
return ' '.join(key_parts)
|
return ' '.join(key_parts)
|
||||||
|
|
||||||
|
@ -533,7 +543,7 @@ class ValueBuilder(object):
|
||||||
self._values.append(ValueBuilder.Value(path, state, value, deps))
|
self._values.append(ValueBuilder.Value(path, state, value, deps))
|
||||||
self._values_dirty = True
|
self._values_dirty = True
|
||||||
|
|
||||||
def _get_prefix_name(self, qname):
|
def get_prefix_name(self, qname):
|
||||||
if not isinstance(qname, (str, unicode)):
|
if not isinstance(qname, (str, unicode)):
|
||||||
return qname, None
|
return qname, None
|
||||||
if ':' not in qname:
|
if ':' not in qname:
|
||||||
|
@ -556,9 +566,9 @@ class ValueBuilder(object):
|
||||||
parent_schema = all_schema['data']
|
parent_schema = all_schema['data']
|
||||||
meta = all_schema['meta']
|
meta = all_schema['meta']
|
||||||
schema = self._find_child(parent_path, parent_schema, key)
|
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:
|
if schema is None or meta is None:
|
||||||
all_schema = self._ensure_schema_cached(path)
|
all_schema = self._ensure_schema_cached(path)
|
||||||
schema = all_schema['data']
|
schema = all_schema['data']
|
||||||
|
@ -686,7 +696,8 @@ def verify_version_str(version_str, required_versions):
|
||||||
def normalize_value(expected_value, value, key):
|
def normalize_value(expected_value, value, key):
|
||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
if isinstance(expected_value, bool):
|
if (isinstance(expected_value, bool) and
|
||||||
|
isinstance(value, (str, unicode))):
|
||||||
return value == 'true'
|
return value == 'true'
|
||||||
if isinstance(expected_value, int):
|
if isinstance(expected_value, int):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -116,7 +116,7 @@ class NsoVerify(object):
|
||||||
violations = []
|
violations = []
|
||||||
|
|
||||||
# build list of values from configured data
|
# 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():
|
for key, value in self._data.items():
|
||||||
value_builder.build('', key, value)
|
value_builder.build('', key, value)
|
||||||
|
|
||||||
|
@ -146,11 +146,17 @@ class NsoVerify(object):
|
||||||
n_value = normalize_value(
|
n_value = normalize_value(
|
||||||
expected_value.value, value, expected_value.path)
|
expected_value.value, value, expected_value.path)
|
||||||
if n_value != expected_value.value:
|
if n_value != expected_value.value:
|
||||||
violations.append({
|
# if the value comparision fails, try mapping identityref
|
||||||
'path': expected_value.path,
|
value_type = value_builder.get_type(expected_value.path)
|
||||||
'expected-value': expected_value.value,
|
if value_type is not None and 'identityref' in value_type:
|
||||||
'value': n_value
|
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:
|
else:
|
||||||
raise ModuleFailException(
|
raise ModuleFailException(
|
||||||
'value state {0} not supported at {1}'.format(
|
'value state {0} not supported at {1}'.format(
|
||||||
|
|
|
@ -61,6 +61,10 @@ class MockResponse(object):
|
||||||
|
|
||||||
|
|
||||||
def mock_call(calls, url, timeout, data=None, headers=None, method=None):
|
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]
|
result = calls[0]
|
||||||
del calls[0]
|
del calls[0]
|
||||||
|
|
||||||
|
@ -99,6 +103,8 @@ class TestNsoModule(unittest.TestCase):
|
||||||
self.assertEqual(result['changed'], changed, result)
|
self.assertEqual(result['changed'], changed, result)
|
||||||
|
|
||||||
for key, value in kwargs.items():
|
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])
|
self.assertEqual(value, result[key])
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -51,6 +51,7 @@ class TestNsoVerify(nso_module.TestNsoModule):
|
||||||
def test_nso_verify_violation(self, open_url_mock):
|
def test_nso_verify_violation(self, open_url_mock):
|
||||||
devices_schema = nso_module.load_fixture('devices_schema.json')
|
devices_schema = nso_module.load_fixture('devices_schema.json')
|
||||||
device_schema = nso_module.load_fixture('device_schema.json')
|
device_schema = nso_module.load_fixture('device_schema.json')
|
||||||
|
description_schema = nso_module.load_fixture('description_schema.json')
|
||||||
|
|
||||||
calls = [
|
calls = [
|
||||||
MockResponse('login', {}, 200, '{}', {'set-cookie': 'id'}),
|
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('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('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_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": {}}'),
|
MockResponse('logout', {}, 200, '{"result": {}}'),
|
||||||
]
|
]
|
||||||
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
|
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
|
||||||
|
|
Loading…
Reference in a new issue