Browse Source

'checkpoint' commit, can address individual list items, not set them yet, need a bit of refactoring first

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac405@3643 e5f2f494-b856-4b98-b285-d166d9295462
Jelte Jansen 14 years ago
parent
commit
96a12cbadf

+ 11 - 0
src/bin/auth/auth.spec.pre.in

@@ -7,6 +7,17 @@
         "item_type": "string",
         "item_optional": true,
         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3"
+      },
+      { "item_name": "simple_list_int",
+        "item_type": "list",
+        "item_optional": true,
+        "item_default": [ 1, 2, 3],
+        "list_item_spec":
+          { "item_name": "int_element",
+            "item_type": "integer",
+            "item_optional": false,
+            "item_default": 0
+          }
       }
     ],
     "commands": [

+ 5 - 2
src/bin/bindctl/bindcmd.py

@@ -553,6 +553,9 @@ class BindCmdInterpreter(Cmd):
 
             if cmd.command == "show":
                 values = self.config_data.get_value_maps(identifier)
+                print("[XX] VALUE MAPS:")
+                print(str(values))
+                print("[XX] END VALUE MAPS")
                 for value_map in values:
                     line = value_map['name']
                     if value_map['type'] in [ 'module', 'map', 'list' ]:
@@ -593,8 +596,8 @@ class BindCmdInterpreter(Cmd):
                 self.go(identifier)
         except isc.cc.data.DataTypeError as dte:
             print("Error: " + str(dte))
-        except isc.cc.data.DataNotFoundError as dnfe:
-            print("Error: " + identifier + " not found")
+        #except isc.cc.data.DataNotFoundError as dnfe:
+        #    print("Error: " + identifier + " not found")
         except KeyError as ke:
             print("Error: missing " + str(ke))
             raise ke

+ 3 - 3
src/bin/bindctl/bindctl-source.py.in

@@ -112,7 +112,7 @@ def set_bindctl_options(parser):
                       help = 'PEM formatted server certificate validation chain file')
 
 if __name__ == '__main__':
-    try:
+    #try:
         parser = OptionParser(version = __version__)
         set_bindctl_options(parser)
         (options, args) = parser.parse_args()
@@ -120,7 +120,7 @@ if __name__ == '__main__':
         tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain)
         prepare_config_commands(tool)
         tool.run()
-    except Exception as e:
-        print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
+    #except Exception as e:
+    #    print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
 
 

+ 33 - 2
src/lib/python/isc/cc/data.py

@@ -63,15 +63,40 @@ def _split_identifier(identifier):
     return id_parts
 
 def _find_child_el(element, id):
+    """Finds the child of element with the given id. If the id contains
+       [i], where i is a number, and the child element is a list, the
+       i-th element of that list is returned instead of the list itself.
+       Raises a DataTypeError if the element is of wrong type, if id
+       is not a string, or if the id string contains a bad value.
+       Raises a DataNotFoundError if the element at id could not be
+       found.
+    """
+    i = id.find('[')
+    e = id.find(']')
+    list_index = None
+    if i >= 0 and e > i + 1:
+        try:
+            list_index = int(id[i + 1:e])
+        except ValueError as ve:
+            # repack as datatypeerror
+            raise DataTypeError(ve)
+        id = id[:i]
     if type(element) == dict and id in element.keys():
-        return element[id]
+        result = element[id]
     else:
         raise DataNotFoundError(id + " in " + str(element))
+    if type(result) == list and list_index is not None:
+        print("[XX] GETTING ELEMENT NUMBER " + str(list_index) + " (of " + str(len(result)) + ")")
+        if list_index >= len(result):
+            print("[XX] OUT OF RANGE")
+            raise DataNotFoundError("Element " + str(list_index) + " in " + str(result))
+        result = result[list_index]
+    return result
 
 def find(element, identifier):
     """Returns the subelement in the given data element, raises DataNotFoundError if not found"""
     if (type(element) != dict and identifier != ""):
-        raise DataTypeError("element in merge() is not a dict")
+        raise DataTypeError("element in find() is not a dict")
     id_parts = _split_identifier(identifier)
     cur_el = element
     for id in id_parts:
@@ -88,12 +113,17 @@ def set(element, identifier, value):
        el.set().set().set() is possible)"""
     if type(element) != dict:
         raise DataTypeError("element in set() is not a dict")
+    print("[XX] full identifier: " + identifier)
     id_parts = _split_identifier(identifier)
     cur_el = element
+    print("[XX] Full element:")
+    print(element)
     for id in id_parts[:-1]:
         try:
+            print("[XX] find " + id)
             cur_el = _find_child_el(cur_el, id)
         except DataNotFoundError:
+            print("[XX] DNF for " + id)
             if value is None:
                 # ok we are unsetting a value that wasn't set in
                 # the first place. Simply stop.
@@ -102,6 +132,7 @@ def set(element, identifier, value):
             cur_el = cur_el[id]
 
     # value can be an empty list or dict, so check for None eplicitely
+    print("[XX] Current value: " + str(cur_el))
     if value is not None:
         cur_el[id_parts[-1]] = value
     elif id_parts[-1] in cur_el:

+ 11 - 0
src/lib/python/isc/cc/tests/data_test.py

@@ -95,6 +95,17 @@ class TestData(unittest.TestCase):
         self.assertRaises(data.DataTypeError, data.find, None, 1)
         self.assertRaises(data.DataTypeError, data.find, "123", "123")
         self.assertEqual(data.find("123", ""), "123")
+
+        d2 = { 'a': [ 1, 2, 3 ] }
+        self.assertEqual(data.find(d2, 'a[0]'), 1)
+        self.assertEqual(data.find(d2, 'a[1]'), 2)
+        self.assertEqual(data.find(d2, 'a[2]'), 3)
+        self.assertRaises(data.DataNotFoundError, data.find, d2, 'a[3]')
+        self.assertRaises(data.DataTypeError, data.find, d2, 'a[a]')
+
+        d3 = { 'a': [ { 'b': [ {}, { 'c': 'd' } ] } ] }
+        self.assertEqual(data.find(d3, 'a[0]/b[1]/c'), 'd')
+        self.assertRaises(data.DataNotFoundError, data.find, d3, 'a[1]/b[1]/c')
         
     def test_set(self):
         d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } }

+ 86 - 24
src/lib/python/isc/config/config_data.py

@@ -109,6 +109,12 @@ def find_spec_part(element, identifier):
     id_parts[:] = (value for value in id_parts if value != "")
     cur_el = element
     for id in id_parts:
+        # strip list selector part
+        # don't need it for the spec part, so just drop it
+        i = id.find('[')
+        e = id.find(']')
+        if i >= 0 and e > i + 1:
+            id = id[:i]
         if type(cur_el) == dict and 'map_item_spec' in cur_el.keys():
             found = False
             for cur_el_item in cur_el['map_item_spec']:
@@ -121,12 +127,20 @@ def find_spec_part(element, identifier):
             found = False
             for cur_el_item in cur_el:
                 if cur_el_item['item_name'] == id:
+                    #print("[XX] full list item:")
+                    #print(cur_el_item)
+                    #if 'list_item_spec' in cur_el_item:
+                    #    cur_el = cur_el_item['list_item_spec']
+                    #else:
                     cur_el = cur_el_item
                     found = True
             if not found:
                 raise isc.cc.data.DataNotFoundError(id + " in " + str(cur_el))
         else:
             raise isc.cc.data.DataNotFoundError("Not a correct config specification")
+    print("[XX] Returning: ")
+    print(cur_el)
+    print("[XX] end")
     return cur_el
 
 def spec_name_list(spec, prefix="", recurse=False):
@@ -336,28 +350,47 @@ class MultiConfigData:
         try:
             spec = find_spec_part(self._specifications[module].get_config_spec(), id)
             if 'item_default' in spec:
-                return spec['item_default']
+                i = id.find('[')
+                e = id.find(']')
+                if i >= 0 and e > i + 1 \
+                   and type(spec['item_default']) == list:
+                    default_list = spec['item_default']
+                    index = int(id[i + 1:e])
+                    if index < len(default_list):
+                        return default_list[index]
+                    else:
+                        return None
+                else:
+                    return spec['item_default']
             else:
                 return None
         except isc.cc.data.DataNotFoundError as dnfe:
             return None
 
-    def get_value(self, identifier):
+    def get_value(self, identifier, default = True):
         """Returns a tuple containing value,status.
            The value contains the configuration value for the given
            identifier. The status reports where this value came from;
            it is one of: LOCAL, CURRENT, DEFAULT or NONE, corresponding
            (local change, current setting, default as specified by the
-           specification, or not found at all)."""
+           specification, or not found at all). Does not check and
+           set DEFAULT if the argument 'default' is False (default
+           defaults to True)"""
         value = self.get_local_value(identifier)
+        print("[XX] mcd get_value() for: " + identifier)
+        print("[XX] mcd get_value() local: " + str(value))
         if value != None:
             return value, self.LOCAL
         value = self.get_current_value(identifier)
+        print("[XX] mcd get_value() current: " + str(value))
         if value != None:
             return value, self.CURRENT
-        value = self.get_default_value(identifier)
-        if value != None:
-            return value, self.DEFAULT
+        if default:
+            value = self.get_default_value(identifier)
+            print("[XX] mcd get_value() default: " + str(value))
+            if value != None:
+                return value, self.DEFAULT
+        print("[XX] mcd get_value() nothing found")
         return None, self.NONE
 
     def get_value_maps(self, identifier = None):
@@ -393,6 +426,7 @@ class MultiConfigData:
                         entry = {}
                         entry['name'] = item['item_name']
                         entry['type'] = item['item_type']
+                        print("[XX] GET VALUE FOR: " + str("/" + identifier + "/" + item['item_name']))
                         value, status = self.get_value("/" + identifier + "/" + item['item_name'])
                         entry['value'] = value
                         if status == self.LOCAL:
@@ -408,32 +442,53 @@ class MultiConfigData:
                     item = spec_part
                     if item['item_type'] == 'list':
                         li_spec = item['list_item_spec']
-                        item_list, status =  self.get_value("/" + identifier)
-                        if item_list != None:
-                            for value in item_list:
+                        print("[XX] GET VALUE FOR: " + str("/" + identifier))
+                        value, status =  self.get_value("/" + identifier)
+                        print("[XX] ITEM_LIST: " + str(value))
+                        if type(value) == list:
+                            for list_value in value:
                                 result_part2 = {}
                                 result_part2['name'] = li_spec['item_name']
-                                result_part2['value'] = value
+                                result_part2['value'] = list_value
                                 result_part2['type'] = li_spec['item_type']
                                 result_part2['default'] = False
                                 result_part2['modified'] = False
                                 result.append(result_part2)
+                        elif value is not None:
+                            entry = {}
+                            entry['name'] = li_spec['item_name']
+                            entry['type'] = li_spec['item_type']
+                            entry['value'] = value
+                            if status == self.LOCAL:
+                                entry['modified'] = True
+                            else:
+                                entry['modified'] = False
+                            if status == self.DEFAULT:
+                                entry['default'] = False
+                            else:
+                                entry['default'] = False
+                            result.append(entry)
                     else:
-                        entry = {}
-                        entry['name'] = item['item_name']
-                        entry['type'] = item['item_type']
                         #value, status = self.get_value("/" + identifier + "/" + item['item_name'])
-                        value, status = self.get_value("/" + identifier)
-                        entry['value'] = value
-                        if status == self.LOCAL:
-                            entry['modified'] = True
-                        else:
-                            entry['modified'] = False
-                        if status == self.DEFAULT:
-                            entry['default'] = False
-                        else:
-                            entry['default'] = False
-                        result.append(entry)
+                        print("[XX] GET VALUE FOR: " + str("/" + identifier))
+                        # The type of the config data is a list,
+                        # so we do not want to have a default if it's
+                        # out of range
+                        value, status = self.get_value("/" + identifier, False)
+                        if value is not None:
+                            entry = {}
+                            entry['name'] = item['item_name']
+                            entry['type'] = item['item_type']
+                            entry['value'] = value
+                            if status == self.LOCAL:
+                                entry['modified'] = True
+                            else:
+                                entry['modified'] = False
+                            if status == self.DEFAULT:
+                                entry['default'] = False
+                            else:
+                                entry['default'] = False
+                            result.append(entry)
         return result
 
     def set_value(self, identifier, value):
@@ -441,8 +496,15 @@ class MultiConfigData:
            there is a specification for the given identifier, the type
            is checked."""
         spec_part = self.find_spec_part(identifier)
+        print("[XX] SPEC PART FOR " + identifier + ": ")
+        print(spec_part)
         if spec_part != None:
+            i = identifier.find('[')
+            e = identifier.find(']')
+            if i >= 0 and e > i and spec_part['item_type'] == 'list':
+                spec_part = spec_part['list_item_spec']
             check_type(spec_part, value)
+        # TODO: get the local list to value
         isc.cc.data.set(self._local_changes, identifier, value)
  
     def get_config_item_list(self, identifier = None, recurse = False):