Browse Source

[1751] addition of a new variables containing statistics data of each pid for multiple sender instances

Naoki Kambe 13 years ago
parent
commit
9c15821896
3 changed files with 130 additions and 5 deletions
  1. 51 5
      src/bin/stats/stats.py.in
  2. 7 0
      src/bin/stats/stats.spec
  3. 72 0
      src/bin/stats/tests/b10-stats_test.py

+ 51 - 5
src/bin/stats/stats.py.in

@@ -129,6 +129,8 @@ class Stats:
         self.module_name = self.mccs.get_module_spec().get_module_name()
         self.module_name = self.mccs.get_module_spec().get_module_name()
         self.modules = {}
         self.modules = {}
         self.statistics_data = {}
         self.statistics_data = {}
+        # statistics data by each pid
+        self.statistics_data_bypid = {}
         # get commands spec
         # get commands spec
         self.commands_spec = self.mccs.get_module_spec().get_commands_spec()
         self.commands_spec = self.mccs.get_module_spec().get_commands_spec()
         # add event handler related command_handler of ModuleCCSession
         # add event handler related command_handler of ModuleCCSession
@@ -265,14 +267,17 @@ class Stats:
                          + "owner: " + str(owner) + ", "
                          + "owner: " + str(owner) + ", "
                          + "name: " + str(name))
                          + "name: " + str(name))
 
 
-    def update_statistics_data(self, owner=None, **data):
+    def update_statistics_data(self, owner=None, pid=-1, **data):
         """
         """
         change statistics date of specified module into specified
         change statistics date of specified module into specified
         data. It updates information of each module first, and it
         data. It updates information of each module first, and it
         updates statistics data. If specified data is invalid for
         updates statistics data. If specified data is invalid for
         statistics spec of specified owner, it returns a list of error
         statistics spec of specified owner, it returns a list of error
         messeges. If there is no error or if neither owner nor data is
         messeges. If there is no error or if neither owner nor data is
-        specified in args, it returns None.
+        specified in args, it returns None. pid is the process id of
+        the sender module in order for stats to identify which
+        instance sends statistics data in the situation that multiple
+        instances are working.
         """
         """
         self.update_modules()
         self.update_modules()
         statistics_data = {}
         statistics_data = {}
@@ -290,7 +295,48 @@ class Stats:
             errors = []
             errors = []
             try:
             try:
                 if self.modules[owner].validate_statistics(False, data, errors):
                 if self.modules[owner].validate_statistics(False, data, errors):
-                    self.statistics_data[owner].update(data)
+                    # This is for multiple instances working. It is
+                    # assumed here that they send different statistics
+                    # data with each pid. Stats should save their
+                    # statistics data by pid. The statistics data,
+                    # which is the existing variable, is preserved by
+                    # summarizing from statistics data by pid. This is
+                    # an ad-hoc fix because administrators can not see
+                    # statistics by each instance via bindctl or
+                    # HTTP/XML. These interfaces aren't changed in
+                    # this fix.
+                    if owner in self.statistics_data_bypid:
+                        if pid in self.statistics_data_bypid[owner]:
+                            self.statistics_data_bypid[owner][pid].update(data)
+                        else:
+                            self.statistics_data_bypid[owner][pid] = data
+                    else:
+                        self.statistics_data_bypid[owner] = { pid : data }
+                    def _sum_bymodule(statistics_data_bypid):
+                        # sum recursively each value under each
+                        # element
+                        def _sum(a, b):
+                            if type(a) == dict:
+                                return dict([ (k, _sum(v, b[k])) if k in b else (k, v) \
+                                                  for (k, v) in a.items() ] \
+                                                + [ (k, v) for (k, v) in b.items() \
+                                                        if k not in a ])
+                            elif type(a) == list:
+                                return [ _sum(a[i], b[i]) if len(b) > i else a[i] \
+                                             for i in range(len(a)) ] \
+                                             + [ b[i] for i in range(len(b)) \
+                                                     if len(a) <= i ]
+                            elif type(a) == float or type(a) == int:
+                                return a + b
+                            # If str or other types than above, then
+                            # just replace with the newer value.
+                            return a
+                        ret = {}
+                        for data in statistics_data_bypid.values():
+                            ret.update(_sum(data, ret))
+                        return ret
+                    self.statistics_data[owner].update(
+                        _sum_bymodule(self.statistics_data_bypid[owner]))
                     return
                     return
             except KeyError:
             except KeyError:
                 errors.append("unknown module name: " + str(owner))
                 errors.append("unknown module name: " + str(owner))
@@ -379,11 +425,11 @@ class Stats:
                 1, "specified arguments are incorrect: " \
                 1, "specified arguments are incorrect: " \
                     + "owner: " + str(owner) + ", name: " + str(name))
                     + "owner: " + str(owner) + ", name: " + str(name))
 
 
-    def command_set(self, owner, data):
+    def command_set(self, owner, pid=-1, data={}):
         """
         """
         handle set command
         handle set command
         """
         """
-        errors = self.update_statistics_data(owner, **data)
+        errors = self.update_statistics_data(owner, pid, **data)
         if errors:
         if errors:
             return isc.config.create_answer(
             return isc.config.create_answer(
                 1, "errors while setting statistics data: " \
                 1, "errors while setting statistics data: " \

+ 7 - 0
src/bin/stats/stats.spec

@@ -72,6 +72,13 @@
             "item_description": "module name of the owner of the statistics data"
             "item_description": "module name of the owner of the statistics data"
           },
           },
 	  {
 	  {
+	    "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true,
+            "item_default": -1,
+            "item_description": "process id of the owner module"
+          },
+	  {
 	    "item_name": "data",
 	    "item_name": "data",
             "item_type": "map",
             "item_type": "map",
             "item_optional": false,
             "item_optional": false,

+ 72 - 0
src/bin/stats/tests/b10-stats_test.py

@@ -373,6 +373,40 @@ class TestStats(unittest.TestCase):
         self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'),
         self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'),
                          ['unknown module name: Dummy'])
                          ['unknown module name: Dummy'])
 
 
+    def test_update_modules_withpid(self):
+        # one pid of Auth
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=9999,
+                                          **{'queries.tcp':1001})
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9999 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9999])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9999]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid,
+                         {'Auth': {9999: {'queries.tcp': 1001}}})
+        # another pid of Auth
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=9998,
+                                          **{'queries.tcp':1002,
+                                             'queries.udp':1003})
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 2003)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], 1003)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9999 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue(9998 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9999])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9998])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9998])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9999]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9998]['queries.tcp'], 1002)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9998]['queries.udp'], 1003)
+
     def test_commands(self):
     def test_commands(self):
         # status
         # status
         self.assertEqual(self.stats.command_status(),
         self.assertEqual(self.stats.command_status(),
@@ -710,6 +744,44 @@ class TestStats(unittest.TestCase):
         self.assertRaises(stats.StatsError,
         self.assertRaises(stats.StatsError,
                           self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' })
                           self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' })
 
 
+    def test_command_set_withpid(self):
+        # one pid of Auth
+        retval = isc.config.ccsession.parse_answer(
+            self.stats.command_set(owner='Auth',
+                                   pid=9997,
+                                   data={ 'queries.tcp' : 1001 }))
+        self.assertEqual(retval, (0,None))
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertTrue('Stats' in self.stats.statistics_data)
+        self.assertTrue('last_update_time' in self.stats.statistics_data['Stats'])
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9997 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.tcp'], 1001)
+        # another pid of Auth
+        retval = isc.config.ccsession.parse_answer(
+            self.stats.command_set(owner='Auth',
+                                   pid=9996,
+                                   data={ 'queries.tcp' : 1002,
+                                          'queries.udp' : 1003,}))
+        self.assertEqual(retval, (0,None))
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 2003)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], 1003)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9997 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue(9996 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9996])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9996])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9996]['queries.tcp'], 1002)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9996]['queries.udp'], 1003)
+
 class TestOSEnv(unittest.TestCase):
 class TestOSEnv(unittest.TestCase):
     def test_osenv(self):
     def test_osenv(self):
         """
         """