Commit 7cc84b1b authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[trac926] add named map support for named map

also a small fix in lists
parent 2cd7eb5d
......@@ -400,9 +400,8 @@ class BindCmdInterpreter(Cmd):
print("Error: " + str(dnfe))
except isc.cc.data.DataAlreadyPresentError as dnfe:
print("Error: " + str(dnfe))
# [XX] TODO: add back
#except KeyError as ke:
# print("Error: missing " + str(ke))
except KeyError as ke:
print("Error: missing " + str(ke))
else:
self.apply_cmd(cmd)
......@@ -629,7 +628,7 @@ class BindCmdInterpreter(Cmd):
values = self.config_data.get_value_maps(identifier, show_all)
for value_map in values:
line = value_map['name']
if value_map['type'] in [ 'module', 'map' ]:
if value_map['type'] in [ 'module', 'map', 'named_map' ]:
line += "/"
elif value_map['type'] == 'list' \
and value_map['value'] != []:
......@@ -637,7 +636,9 @@ class BindCmdInterpreter(Cmd):
# we have more data to show
line += "/"
else:
line += "\t" + json.dumps(value_map['value'])
# if type is named_map, don't print value
if value_map['type'] != 'named_map':
line += "\t" + json.dumps(value_map['value'])
line += "\t" + value_map['type']
line += "\t"
if value_map['default']:
......
......@@ -444,7 +444,7 @@ class UIModuleCCSession(MultiConfigData):
else:
raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier)
def _add_value_to_named_map(self, identifier, value):
def _add_value_to_named_map(self, identifier, value, item_value):
if value is None:
raise isc.cc.data.DataNotFoundError("Need a name to add a new item to named_map " + str(identifier))
elif type(value) != str:
......@@ -454,7 +454,7 @@ class UIModuleCCSession(MultiConfigData):
if not cur_map:
cur_map = {}
if value not in cur_map:
cur_map[value] = {}
cur_map[value] = item_value
self.set_value(identifier, cur_map)
else:
raise isc.cc.data.DataAlreadyPresentError(value + " already in " + identifier)
......@@ -481,7 +481,10 @@ class UIModuleCCSession(MultiConfigData):
if 'list_item_spec' in module_spec:
self._add_value_to_list(identifier, value)
elif 'named_map_item_spec' in module_spec:
self._add_value_to_named_map(identifier, value)
item_value = None
if 'item_default' in module_spec['named_map_item_spec']:
item_value = module_spec['named_map_item_spec']['item_default']
self._add_value_to_named_map(identifier, value, item_value)
else:
raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named map")
......@@ -516,11 +519,11 @@ class UIModuleCCSession(MultiConfigData):
raise isc.cc.data.DataNotFoundError(value + " not found in named_map " + str(identifier))
def remove_value(self, identifier, value_str):
"""Remove a value from a configuration list. The value string
must be a string representation of the full item. Raises
a DataTypeError if the value at the identifier is not a list,
or if the given value_str does not match the list_item_spec
"""
"""Remove a value from a configuration list or named map.
The value string must be a string representation of the full
item. Raises a DataTypeError if the value at the identifier
is not a list, or if the given value_str does not match the
list_item_spec """
module_spec = self.find_spec_part(identifier)
if module_spec is None:
raise isc.cc.data.DataNotFoundError("Unknown item " + str(identifier))
......@@ -528,9 +531,10 @@ class UIModuleCCSession(MultiConfigData):
value = None
if value_str is not None:
value = isc.cc.data.parse_value_str(value_str)
isc.config.config_data.check_type(module_spec, [value])
if 'list_item_spec' in module_spec:
if value is not None:
isc.config.config_data.check_type(module_spec['list_item_spec'], value)
self._remove_value_from_list(identifier, value)
elif 'named_map_item_spec' in module_spec:
self._remove_value_from_named_map(identifier, value)
......
......@@ -145,6 +145,8 @@ def _find_spec_part_single(cur_spec, id_part):
return cur_spec['list_item_spec']
# not found
raise isc.cc.data.DataNotFoundError(id + " not found")
elif type(cur_spec) == dict and 'named_map_item_spec' in cur_spec.keys():
return cur_spec['named_map_item_spec']
elif type(cur_spec) == list:
for cur_spec_item in cur_spec:
if cur_spec_item['item_name'] == id:
......@@ -408,11 +410,38 @@ class MultiConfigData:
id_parts = isc.cc.data.split_identifier(id)
id_prefix = ""
while len(id_parts) > 0:
# [XX] TODO: Refactor
id_part = id_parts.pop(0)
item_id, list_indices = isc.cc.data.split_identifier_list_indices(id_part)
id_list = module + "/" + id_prefix + "/" + item_id
id_prefix += "/" + id_part
if list_indices is not None:
part_spec = find_spec_part(self._specifications[module].get_config_spec(), id_prefix)
if part_spec['item_type'] == 'named_map':
if len(id_parts) == 0:
if 'item_default' in part_spec:
return part_spec['item_default']
else:
return None
id_part = id_parts.pop(0)
named_map_value, type = self.get_value(id_list)
if id_part in named_map_value:
if len(id_parts) > 0:
# we are looking for the *default* value.
# so if not present in here, we need to
# lookup the one from the spec
rest_of_id = "/".join(id_parts)
result = isc.cc.data.find_no_exc(named_map_value[id_part], rest_of_id)
if result is None:
spec_part = self.find_spec_part(identifier)
if 'item_default' in spec_part:
return spec_part['item_default']
return result
else:
return named_map_value[id_part]
else:
return None
elif list_indices is not None:
# there's actually two kinds of default here for
# lists; they can have a default value (like an
# empty list), but their elements can also have
......@@ -449,7 +478,12 @@ class MultiConfigData:
spec = find_spec_part(self._specifications[module].get_config_spec(), id)
if 'item_default' in spec:
return spec['item_default']
# one special case, named_map
if spec['item_type'] == 'named_map':
print("is " + id_part + " in named map?")
return spec['item_default']
else:
return spec['item_default']
else:
return None
......@@ -509,12 +543,37 @@ class MultiConfigData:
for i in range(len(list_value)):
self._append_value_item(result, spec_part_list, "%s[%d]" % (identifier, i), all)
elif item_type == "map":
value, status = self.get_value(identifier)
# just show the specific contents of a map, we are
# almost never interested in just its name
spec_part_map = spec_part['map_item_spec']
self._append_value_item(result, spec_part_map, identifier, all)
elif item_type == "named_map":
value, status = self.get_value(identifier)
if status == self.NONE or (status == self.DEFAULT and value == {}):
raise isc.cc.data.DataNotFoundError(identifier)
# show just the one entry, when either the map is empty,
# or when this is element is not requested specifically
if (len(value.keys()) == 0 and (all or first)):
entry = _create_value_map_entry(identifier,
item_type,
{}, status)
result.append(entry)
elif not first and not all:
entry = _create_value_map_entry(identifier,
item_type,
None, status)
result.append(entry)
else:
spec_part_named_map = spec_part['named_map_item_spec']
for entry in value:
# xxxxxxxxxxx
self._append_value_item(result, spec_part_named_map, identifier + "/" + entry, all)
else:
value, status = self.get_value(identifier)
if status == self.NONE and not spec_part['item_optional']:
raise isc.cc.data.DataNotFoundError(identifier)
entry = _create_value_map_entry(identifier,
item_type,
value, status)
......
......@@ -691,6 +691,12 @@ class TestUIModuleCCSession(unittest.TestCase):
fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
return UIModuleCCSession(fake_conn)
def create_uccs_named_map(self, fake_conn):
module_spec = isc.config.module_spec_from_file(self.spec_file("spec32.spec"))
fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
return UIModuleCCSession(fake_conn)
def test_init(self):
fake_conn = fakeUIConn()
fake_conn.set_get_answer('/module_spec', {})
......@@ -711,12 +717,14 @@ class TestUIModuleCCSession(unittest.TestCase):
def test_add_remove_value(self):
fake_conn = fakeUIConn()
uccs = self.create_uccs2(fake_conn)
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, 1, "a")
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "no_such_item", "a")
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec2/item1", "a")
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, 1, "a")
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "no_such_item", "a")
self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "Spec2/item1", "a")
self.assertEqual({}, uccs._local_changes)
uccs.add_value("Spec2/item5", "foo")
self.assertEqual({'Spec2': {'item5': ['a', 'b', 'foo']}}, uccs._local_changes)
......@@ -726,11 +734,35 @@ class TestUIModuleCCSession(unittest.TestCase):
uccs.remove_value("Spec2/item5", "foo")
uccs.add_value("Spec2/item5", "foo")
self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
uccs.add_value("Spec2/item5", "foo")
self.assertRaises(isc.cc.data.DataAlreadyPresentError,
uccs.add_value, "Spec2/item5", "foo")
self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
self.assertRaises(isc.cc.data.DataNotFoundError,
uccs.remove_value, "Spec2/item5[123]", None)
uccs.remove_value("Spec2/item5[0]", None)
self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes)
def test_add_remove_value_named_map(self):
fake_conn = fakeUIConn()
uccs = self.create_uccs_named_map(fake_conn)
value, status = uccs.get_value("/Spec32/named_map_item")
self.assertEqual({'a': 1, 'b': 2}, value)
uccs.add_value("/Spec32/named_map_item", "foo")
value, status = uccs.get_value("/Spec32/named_map_item")
self.assertEqual({'a': 1, 'b': 2, 'foo': 3}, value)
uccs.set_value("/Spec32/named_map_item/bar", 4)
value, status = uccs.get_value("/Spec32/named_map_item")
self.assertEqual({'a': 1, 'b': 2, 'foo': 3, 'bar': 4}, value)
uccs.remove_value("/Spec32/named_map_item", "a")
uccs.remove_value("/Spec32/named_map_item", "foo")
value, status = uccs.get_value("/Spec32/named_map_item")
self.assertEqual({'b': 2, 'bar': 4}, value)
self.assertRaises(isc.cc.data.DataNotFoundError,
uccs.remove_value, "/Spec32/named_map_item",
"no_such_item")
def test_commit(self):
fake_conn = fakeUIConn()
uccs = self.create_uccs2(fake_conn)
......
......@@ -236,6 +236,7 @@ class TestConfigData(unittest.TestCase):
value, default = self.cd.get_value("item6/value2")
self.assertEqual(None, value)
self.assertEqual(False, default)
self.assertRaises(isc.cc.data.DataNotFoundError, self.cd.get_value, "item6/no_such_item")
def test_get_default_value(self):
self.assertEqual(1, self.cd.get_default_value("item1"))
......@@ -410,6 +411,7 @@ class TestMultiConfigData(unittest.TestCase):
self.assertEqual('a', value)
value = self.mcd.get_default_value("Spec2/item5[1]")
self.assertEqual('b', value)
self.assertRaises(self.mcd.get_default_value("Spec2/item5[2]"))
value = self.mcd.get_default_value("Spec2/item5[5]")
self.assertEqual(None, value)
value = self.mcd.get_default_value("Spec2/item5[0][1]")
......@@ -421,6 +423,17 @@ class TestMultiConfigData(unittest.TestCase):
value = self.mcd.get_default_value("Spec2/no_such_item/asdf")
self.assertEqual(None, value)
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
self.mcd.set_specification(module_spec)
value = self.mcd.get_default_value("Spec32/named_map_item")
self.assertEqual({ 'a': 1, 'b': 2}, value)
value = self.mcd.get_default_value("Spec32/named_map_item/a")
self.assertEqual(1, value)
value = self.mcd.get_default_value("Spec32/named_map_item/b")
self.assertEqual(2, value)
value = self.mcd.get_default_value("Spec32/named_map_item/no_such_item")
self.assertEqual(None, value)
def test_get_value(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
self.mcd.set_specification(module_spec)
......@@ -544,6 +557,29 @@ class TestMultiConfigData(unittest.TestCase):
maps = self.mcd.get_value_maps("/Spec22/value9")
self.assertEqual(expected, maps)
def test_get_value_maps_named_map(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
self.mcd.set_specification(module_spec)
maps = self.mcd.get_value_maps()
self.assertEqual([{'default': False, 'type': 'module',
'name': 'Spec32', 'value': None,
'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec32/named_map_item")
self.assertEqual([{'default': True, 'type': 'integer',
'name': 'Spec32/named_map_item/a',
'value': 1, 'modified': False},
{'default': True, 'type': 'integer',
'name': 'Spec32/named_map_item/b',
'value': 2, 'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec32/named_map_item/a")
self.assertEqual([{'default': True, 'type': 'integer',
'name': 'Spec32/named_map_item/a',
'value': 1, 'modified': False}], maps)
maps = self.mcd.get_value_maps("/Spec32/named_map_item/b")
self.assertEqual([{'default': True, 'type': 'integer',
'name': 'Spec32/named_map_item/b',
'value': 2, 'modified': False}], maps)
def test_set_value(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
self.mcd.set_specification(module_spec)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment