Browse Source

[2179] Supported a identifier in isc.cc.data e.g. "xxx/yyy/zzz[i]"

If a target module returns data whose key name is such a identifier type, the
stats module can identify statistics data. The stats module checks the
validation of data even if such a identifier is specified. But when the stats
shows statistics data e.g. via bindctl, this type of identifier is not shown
for now.
Naoki Kambe 12 years ago
parent
commit
2c097fad9e
3 changed files with 114 additions and 13 deletions
  1. 37 10
      src/bin/stats/stats.py.in
  2. 65 2
      src/bin/stats/tests/b10-stats_test.py
  3. 12 1
      src/bin/stats/tests/test_utils.py

+ 37 - 10
src/bin/stats/stats.py.in

@@ -516,21 +516,48 @@ class Stats:
         # would be updated.
         errors = []
         if owner and data:
+            _data = self.statistics_data_bymid.copy()
             try:
-                if self.modules[owner].validate_statistics(False, data, errors):
-                    if owner in self.statistics_data_bymid:
-                        if mid in self.statistics_data_bymid[owner]:
+                for (_key, _val) in data.items():
+                    if self.modules[owner].validate_statistics(
+                        False, {_key: _val}, errors):
+                        if owner not in _data:
+                            _data[owner] = { mid: { _key: _val } }
+                        elif mid not in _data[owner]:
+                            _data[owner][mid] = { _key: _val }
+                        else:
                             # merge recursively old value and new
                             # value each other
-                            self.statistics_data_bymid[owner][mid] = \
-                                merge_oldnew(self.statistics_data_bymid[owner][mid],
-                                             data)
-                        else:
-                            self.statistics_data_bymid[owner][mid] = data
-                    else:
-                        self.statistics_data_bymid[owner] = { mid : data }
+                            _data[owner][mid] = \
+                                merge_oldnew(_data[owner][mid],
+                                             {_key: _val})
+                        continue
+                    # the key string might be a "xx/yy/zz[0]"
+                    # type. try it.
+                    if _key.find('/') >= 0 or \
+                            isc.cc.data.identifier_has_list_index(_key):
+                        # remove the last error
+                        if errors: errors.pop()
+                        # try updata and check validation in adavance
+                        __data = _data.copy()
+                        if owner not in _data:
+                            __data[owner] = {}
+                        if mid not in _data[owner]:
+                            __data[owner][mid] = {}
+                        # use the isc.cc.data.set method
+                        try:
+                            isc.cc.data.set(__data[owner][mid],
+                                            _key, _val)
+                            if self.modules[owner].validate_statistics(
+                                False, __data[owner][mid], errors):
+                                _data = __data
+                        except Exception as e:
+                            errors.append(
+                                "%s: %s" % (e.__class__.__name__, e))
             except KeyError:
                 errors.append("unknown module name: " + str(owner))
+            if not errors:
+                self.statistics_data_bymid = _data
 
         # Just consolidate statistics data of each module without
         # removing that of modules which have been already dead

+ 65 - 2
src/bin/stats/tests/b10-stats_test.py

@@ -454,6 +454,19 @@ class TestStats(unittest.TestCase):
         _test_exp4 = {
               'queries.udp': 4
             }
+        _test_exp5_1 = {
+              'queries.perzone': [
+                { },
+                {
+                  'queries.udp': 9876
+                }
+              ]
+            }
+        _test_exp5_2 = {
+              'queries.perzone[1]/queries.udp':
+                  isc.cc.data.find(_test_exp5_1,
+                                   'queries.perzone[1]/queries.udp')
+            }
         # Success cases
         self.assertEqual(self.stats.statistics_data['Stats']['lname'],
                          self.stats.cc_session.lname)
@@ -480,9 +493,17 @@ class TestStats(unittest.TestCase):
         # differential update
         self.assertIsNone(self.stats.update_statistics_data(
             'Auth', 'foo1', {'queries.perzone': [_test_exp3,_test_exp4]}))
+        _new_data = stats.merge_oldnew(_test_exp2,_test_exp4)
+        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
+                             ['foo1']['queries.perzone'], \
+                             [_test_exp1,_new_data])
+        self.assertIsNone(self.stats.update_statistics_data(
+            'Auth', 'foo1', _test_exp5_2))
+        _new_data = stats.merge_oldnew(_new_data,
+                                       _test_exp5_1['queries.perzone'][1])
         self.assertEqual(self.stats.statistics_data_bymid['Auth']\
                              ['foo1']['queries.perzone'], \
-                             [_test_exp1,stats.merge_oldnew(_test_exp2,_test_exp4)])
+                             [_test_exp1,_new_data])
         # Error cases
         self.assertEqual(self.stats.update_statistics_data('Stats', None,
                                                            {'lname': 0.0}),
@@ -515,6 +536,22 @@ class TestStats(unittest.TestCase):
                   'queries.udp': 4
               }
             }
+        _test_exp5_1 = {
+              'test10.example': {
+                 'queries.udp': 5432
+              }
+            }
+        _test_exp5_2 ={
+              'nds_queries.perzone/test10.example/queries.udp':
+                  isc.cc.data.find(_test_exp5_1,
+                                   'test10.example/queries.udp')
+            }
+        _test_exp6 = {
+              'foo/bar':  'brabra'
+            }
+        _test_exp7 = {
+              'foo[100]': 'bar'
+            }
         # Success cases
         self.assertIsNone(self.stats.update_statistics_data(
             'Auth', 'foo1', {'nds_queries.perzone': _test_exp1}))
@@ -534,12 +571,38 @@ class TestStats(unittest.TestCase):
         # differential update
         self.assertIsNone(self.stats.update_statistics_data(
             'Auth', 'foo1', {'nds_queries.perzone': dict(_test_exp3,**_test_exp4)}))
+        _new_val = dict(_test_exp1,
+                        **stats.merge_oldnew(_test_exp2,_test_exp4))
         self.assertEqual(self.stats.statistics_data_bymid['Auth']\
                              ['foo1']['nds_queries.perzone'],\
-                             dict(_test_exp1,**stats.merge_oldnew(_test_exp2,_test_exp4)))
+                             _new_val)
+        self.assertIsNone(self.stats.update_statistics_data(
+            'Auth', 'foo1', _test_exp5_2))
+        _new_val = stats.merge_oldnew(_new_val, _test_exp5_1)
+        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
+                             ['foo1']['nds_queries.perzone'],\
+                             _new_val)
+        self.assertIsNone(self.stats.update_statistics_data(
+            'Auth', 'foo2', _test_exp5_2))
+        _new_val = stats.merge_oldnew(_new_val, _test_exp5_1)
+        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
+                             ['foo2']['nds_queries.perzone'],\
+                             _test_exp5_1)
         # Error cases
         self.assertEqual(self.stats.update_statistics_data(
                 'Auth', 'foo1', {'nds_queries.perzone': None}), ['None should be a map'])
+        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
+                             ['foo1']['nds_queries.perzone'],\
+                             _new_val)
+        self.assertEqual(self.stats.update_statistics_data(
+                'Auth', 'foo1', _test_exp6), ['unknown item foo'])
+        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
+                             ['foo1']['nds_queries.perzone'],\
+                             _new_val)
+        self.assertEqual(self.stats.update_statistics_data(
+                'Boss', 'bar1', _test_exp7), ["KeyError: 'foo'"])
+        self.assertEqual(self.stats.update_statistics_data(
+                'Foo', 'foo1', _test_exp6), ['unknown module name: Foo'])
 
     def test_update_statistics_data_withmid(self):
         self.stats = stats.Stats()

+ 12 - 1
src/bin/stats/tests/test_utils.py

@@ -449,7 +449,18 @@ class MockAuth:
         sdata = { 'queries.tcp': self.queries_tcp,
                   'queries.udp': self.queries_udp,
                   'queries.perzone' : self.queries_per_zone,
-                  'nds_queries.perzone' : self.nds_queries_per_zone }
+                  'nds_queries.perzone' : {
+                    'test10.example': {
+                    'queries.tcp': \
+                      isc.cc.data.find(
+                        self.nds_queries_per_zone,
+                        'test10.example/queries.tcp')
+                    }
+                  },
+                  'nds_queries.perzone/test10.example/queries.udp' :
+                      isc.cc.data.find(self.nds_queries_per_zone,
+                                       'test10.example/queries.udp')
+                }
         if command == 'getstats':
             return isc.config.create_answer(0, sdata)
         return isc.config.create_answer(1, "Unknown Command")