BIND 10 master, updated. 0a89b374d57877e3b1ea1f379276d74a34ea7d8f [master] Merge branch 'trac2225_statistics'
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jan 21 05:26:32 UTC 2013
The branch, master has been updated
via 0a89b374d57877e3b1ea1f379276d74a34ea7d8f (commit)
via dd637a386748c2a393acc3d437c071a10b9cc7f8 (commit)
via 2a337c756ad1064af1cbcaa8cd2c79be65f0b4d5 (commit)
via 28a20a3ee754d03ea90f13f76fa8e6d44c696b69 (commit)
via 2ad5c3c52b9106f68a6fb24a2ebfc85067877a04 (commit)
via be258adbecd48f1d46e47566f54843db1b579b9c (commit)
via 02a31879b8d8db032883d6e28d57c2369f2299cf (commit)
via df03141495a568971ff26d78a229beaa5b373e19 (commit)
via cd917b3660e1d944d749fb2fb19ce9dfb622c39c (commit)
via cc0c7a0a7b9b4abdb94b7c5a2007334b914218f8 (commit)
via e12dee22bbc97e49c7e5641cf50e39f64e5cbed2 (commit)
via 927c2a94869d76fdbb30018861ba235ddf9389f2 (commit)
via 6443e079d1d0dd3b0de766196a2f9b711bd4f4f5 (commit)
via 78f11bb1db706b793f2b45d94da3c613c2f8ca9e (commit)
via 08d9023ba335f084034ec31cf7e304964b267acb (commit)
via dc7e03f9cca07ef79b5741f4353f570f383dbb9b (commit)
via bc768d296fbd5b86b9d100672792605c79c81739 (commit)
via a9f24702174e022332b528b8d39a21c203b67d3e (commit)
via ef7776ea48e1ddbfb3b727836722cbfc8b65f4fa (commit)
via f098bc21fd0c528fdd55bdbd8f99f6fec36c9222 (commit)
via 607d55c17a1d1becedf6e0fb81673627cab763f8 (commit)
via 903087f3c9e778e762483cf11992e4f5ddc9b7d1 (commit)
via 65bbcd0970063c0923f597ee4413ccbcc969ba58 (commit)
via e8ed9fd77960f2e3c4cfc5948d356b47524d441c (commit)
via 31b16f3568b95073fc15b68e435215572f25c5b8 (commit)
via 6fe2c80c65634db3c5b9ef7fab2e2f48a507fc94 (commit)
via 87ff7feca9801dc6c78a5553c090819a66afe918 (commit)
via 48974a8f8351415321dcabf039296e9e7854fbe1 (commit)
via 652859d50201a5fd7d7ec3bc4266ead82fc86c4b (commit)
via aa70659de52c486695a6fc822b673adf51a2abcf (commit)
via c9e1f35632323f09a1e61f96f88df384e337f885 (commit)
via ad322965f290c5d594e084eb57f9bf16eff87717 (commit)
via ad6e16d25942d9bb308fcc1cf4ec839769956981 (commit)
via 2b861742dc6b8461984a1c1c0437b46a313f672b (commit)
via 0c8094b0e3e9ecae8fe44cb889b959ed467bac8b (commit)
via cf41749b421bd09330ca5cbec3e3e9a3cdae06d7 (commit)
via 02e63b13f8e523d913de3a516c442677dc15e4c6 (commit)
via 1e45b7161d9405b25072a4b6e979a18fa26981ce (commit)
via e78b80b2a929ab815dee2d4ff6c7e29315a5f2f7 (commit)
via 966b5ee5cf9512da8f4a4ea31f7f67ca3836df26 (commit)
via b95523d5350585a707693d680152903ede08bb0a (commit)
via 58771328093e3bb51463437cae9555da0ed945cc (commit)
via 17e0f403185b1e0136cf1059856c1f30a5bdfdec (commit)
via 1150b0ef4bae79e2ffe254dddd5863690a71d60f (commit)
via 65a2fbba5becb20d276aeb90e5d13d02190828c7 (commit)
via 32bb433cb895fd5013c050612abfe62fb09a689c (commit)
via 525dc51879ae8164ed085f5f6c48f8566853b230 (commit)
via 9a08bfa4933a435a4911cef029e63f57deed0dc3 (commit)
via e1aca7e9404c4064f0125b7596d67cbd8af932e5 (commit)
via f5dc6575c10281b39320167809689df16ae913cf (commit)
via 46dba3a3c60b81e609ee7ac2acc9641a8b62c9f3 (commit)
via 96ee507bbc179b6e3732114dfad67e6c5168c538 (commit)
via 3836cadb5368169a86a27e8af12374c8b715f5a5 (commit)
via 02e9061387eff505391762b5568c2e9700b021e5 (commit)
via b53ecd7c2508d159e92bf890f9d3b2e3747d053b (commit)
via a1fcd146a64717e59311eaeae9b97fb66857e636 (commit)
via 0e4b3df29c59a4b4cf4c75f37fd3c3180332e7fb (commit)
via 13c2eb59454d17b9d00c3cc7205ba19e8b69ec8f (commit)
via 8e0ebda2f002e9ef7530ac16ab7947104767d20d (commit)
via ad4eae2bc3410084b4ea07dd96118c7c1da17a25 (commit)
via 36df68ce895eefd601857fa5d8e1d701137fe218 (commit)
via a9a229b2314c336cf4dd7a257371231530dbae13 (commit)
via 48625915d6898ddca0eba5b3be512b1dcb29487b (commit)
via 0a10b2ed25935e83cd0402ae4df81e6d321d75e8 (commit)
via 78a4e4cd8735ad72dcaafe9701482c34d72fef3e (commit)
via d834a1a1b95f67b2ea2467b75e00fdfe9ac0c763 (commit)
via abd4fe9ffe48402cafdac9c1145f25c43da62332 (commit)
via 2c8b5b294bf5498b9525dcc1dcba7ca1869f01b6 (commit)
via c6aa9a4c2e17c51b28b91cef5b773b935caf317e (commit)
via 7dee66ffe8b7e3f5c49c20608f0f38508d861ced (commit)
via 117645fa11500c467045a380dc41d6999518c5dd (commit)
via 0ca61cffc9fb7f55789f1db88a41a9a5c1f5d34f (commit)
via 83e4b764a988e7774312ff75e0623bcff66f7009 (commit)
via 69bc301dec66ffd80960a45c882fd50c498121cc (commit)
via 2ba2f0269087e5dae4640091e711e4f2964d2fa3 (commit)
via 2f29fad03bb3c9885d0bcf84d39102e6ee902830 (commit)
via a52d10a93a24f736ed4112ea76778a61407a0a03 (commit)
via 123f5a29a7d5c40b0ca7a6ebe8af3b3790c29eca (commit)
via 525ca6149da521a00d02895a5184deef7f8359f9 (commit)
via 889dc09ad441f3e5a3ce2170fba402db258c2a08 (commit)
via 23818a5f2f8e5c68300b11ba8e71178dee8ad794 (commit)
via 949f8945514ac7d422a68f34d7cbf097c864bef8 (commit)
via 57e46e4378c4d8b8dd39cca093500672fa124e42 (commit)
via b89a21c32f1dbea865f632cf517cc335b9b70919 (commit)
via e0098c29f519e84214d012cd5d0ae7f25aa40f73 (commit)
via 977568b91a6595c7d2fd8a618d5f15783962d908 (commit)
via e5e001e23347199841f14f2c0880e2f5f304bd0f (commit)
via 8f73baf041ffda0351e5b9072e1ea43401ce497e (commit)
via c38d780b1390e3dc4c3bf702f6c059a8c3de3406 (commit)
via 5cc65af294ab05bba59614684d71d450eed57c37 (commit)
via 74aa99552605bb7639719501807a13c393e560b9 (commit)
via f950449fc2ffc2e31672ab99d0e432c3b3efc4bc (commit)
via 62c7075662a25c06ee6d877404b8ddad16dd41ee (commit)
via 7c00e40c1c815ea1b8dd13cc718772140cf575dc (commit)
via 96edf56fbab807319a975fe7df264ace0c5dbb37 (commit)
via 1c6a6aa89ebb3b512a09ad19609f3682b198993a (commit)
from 95d6795825d9df9634d2282b28d1d9a5761b6634 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 0a89b374d57877e3b1ea1f379276d74a34ea7d8f
Merge: 95d6795 dd637a3
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Jan 21 13:08:49 2013 +0900
[master] Merge branch 'trac2225_statistics'
-----------------------------------------------------------------------
Summary of changes:
configure.ac | 2 +
src/lib/python/isc/Makefile.am | 2 +-
src/lib/python/isc/statistics/Makefile.am | 9 +
src/lib/python/isc/statistics/__init__.py | 1 +
src/lib/python/isc/statistics/counters.py | 403 +++++++++++++++++++
.../python/isc/statistics}/tests/Makefile.am | 15 +-
.../python/isc/statistics/tests/counters_test.py | 416 ++++++++++++++++++++
.../isc/statistics/tests/testdata/test_spec1.spec | 26 ++
.../isc/statistics/tests/testdata/test_spec2.spec | 187 +++++++++
.../isc/statistics/tests/testdata/test_spec3.spec | 382 ++++++++++++++++++
10 files changed, 1435 insertions(+), 8 deletions(-)
create mode 100644 src/lib/python/isc/statistics/Makefile.am
create mode 100644 src/lib/python/isc/statistics/__init__.py
create mode 100644 src/lib/python/isc/statistics/counters.py
copy src/{bin/xfrin => lib/python/isc/statistics}/tests/Makefile.am (75%)
create mode 100644 src/lib/python/isc/statistics/tests/counters_test.py
create mode 100644 src/lib/python/isc/statistics/tests/testdata/test_spec1.spec
create mode 100644 src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
create mode 100644 src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 892bc6c..9ae7a30 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1214,6 +1214,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/server_common/tests/Makefile
src/lib/python/isc/sysinfo/Makefile
src/lib/python/isc/sysinfo/tests/Makefile
+ src/lib/python/isc/statistics/Makefile
+ src/lib/python/isc/statistics/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index e072de8..8f5f144 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,5 +1,5 @@
SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
-SUBDIRS += xfrin log_messages server_common ddns sysinfo
+SUBDIRS += xfrin log_messages server_common ddns sysinfo statistics
python_PYTHON = __init__.py
diff --git a/src/lib/python/isc/statistics/Makefile.am b/src/lib/python/isc/statistics/Makefile.am
new file mode 100644
index 0000000..9be1312
--- /dev/null
+++ b/src/lib/python/isc/statistics/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = . tests
+
+python_PYTHON = __init__.py counters.py
+pythondir = $(pyexecdir)/isc/statistics
+
+CLEANDIRS = __pycache__
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/statistics/__init__.py b/src/lib/python/isc/statistics/__init__.py
new file mode 100644
index 0000000..9e77ed6
--- /dev/null
+++ b/src/lib/python/isc/statistics/__init__.py
@@ -0,0 +1 @@
+from isc.statistics.counters import *
diff --git a/src/lib/python/isc/statistics/counters.py b/src/lib/python/isc/statistics/counters.py
new file mode 100644
index 0000000..99b989d
--- /dev/null
+++ b/src/lib/python/isc/statistics/counters.py
@@ -0,0 +1,403 @@
+# Copyright (C) 2012 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""BIND 10 statistics counters module
+
+This module handles the statistics counters for BIND 10 modules. For
+using the module `counter.py`, first a counters object should be created
+in each module (like b10-xfrin or b10-xfrout) after importing this
+module. A spec file can be specified as an argument when creating the
+counters object:
+
+ from isc.statistics import Counters
+ self.counters = Counters("/path/to/foo.spec")
+
+The first argument of Counters() can be specified, which is the location
+of the specification file (like src/bin/xfrout/xfrout.spec). If Counters
+is constructed this way, statistics counters can be accessed from each
+module. For example, in case that the item `xfrreqdone` is defined in
+statistics_spec in xfrout.spec, the following methods are
+callable. Since these methods require the string of the zone name in the
+first argument, if we have the following code in b10-xfrout:
+
+ self.counters.inc('zones', zone_name, 'xfrreqdone')
+
+then the counter for xfrreqdone corresponding to zone_name is
+incremented. For getting the current number of this counter, we can use
+the following code:
+
+ number = self.counters.get('zones', zone_name, 'xfrreqdone')
+
+then the current count is obtained and set in the variable
+`number`. Such a getter method would be mainly used for unit-testing.
+As other example, for the item `axfr_running`, the decrementer method is
+also callable. This method is used for decrementing a counter. For the
+item `axfr_running`, an argument like zone name is not required:
+
+ self.counters.dec('axfr_running')
+
+These methods are effective in other modules. For example, in case that
+this module `counter.py` is once imported in a main module such as
+b10-xfrout, then for the item `notifyoutv4`, the `inc()` method can be
+invoked in another module such as notify_out.py, which is firstly
+imported in the main module.
+
+ self.counters.inc('zones', zone_name, 'notifyoutv4')
+
+In this example this is for incrementing the counter of the item
+`notifyoutv4`. Thus, such statement can be also written in another
+library like isc.notify.notify_out. If this module `counter.py` isn't
+imported in the main module but imported in such a library module as
+isc.notify.notify_out, in this example, empty methods would be invoked,
+which is directly defined in `counter.py`.
+"""
+
+import threading
+import isc.config
+from datetime import datetime
+
+# static internal functions
+def _add_counter(element, spec, identifier):
+ """Returns value of the identifier if the identifier is in the
+ element. Otherwise, sets a default value from the spec and
+ returns it. If the top-level type of the identifier is named_set
+ and the second-level type is map, it sets a set of default values
+ under the level and then returns the default value of the
+ identifier. This method raises DataNotFoundError if the element is
+ invalid for spec."""
+ try:
+ return isc.cc.data.find(element, identifier)
+ except isc.cc.data.DataNotFoundError:
+ pass
+ # check whether spec and identifier are correct
+ isc.config.find_spec_part(spec, identifier)
+ # examine spec of the top-level item first
+ spec_ = isc.config.find_spec_part(spec, identifier.split('/')[0])
+ if spec_['item_type'] == 'named_set' and \
+ spec_['named_set_item_spec']['item_type'] == 'map':
+ map_spec = spec_['named_set_item_spec']['map_item_spec']
+ for name in isc.config.spec_name_list(map_spec):
+ spec_ = isc.config.find_spec_part(map_spec, name)
+ id_str = '%s/%s/%s' % \
+ tuple(identifier.split('/')[0:2] + [name])
+ isc.cc.data.set(element, id_str, spec_['item_default'])
+ else:
+ spec_ = isc.config.find_spec_part(spec, identifier)
+ isc.cc.data.set(element, identifier, spec_['item_default'])
+ return isc.cc.data.find(element, identifier)
+
+def _set_counter(element, spec, identifier, value):
+ """Invokes _add_counter() for checking whether the identifier is
+ in the element. If not, it creates a new identifier in the element
+ and set the default value from the spec. After that, it sets the
+ value specified in the arguments."""
+ _add_counter(element, spec, identifier)
+ isc.cc.data.set(element, identifier, value)
+
+def _get_counter(element, identifier):
+ """Returns the value of the identifier in the element"""
+ return isc.cc.data.find(element, identifier)
+
+def _inc_counter(element, spec, identifier, step=1):
+ """Increments the value of the identifier in the element to the
+ step from the current value. If the identifier isn't in the
+ element, it creates a new identifier in the element."""
+ isc.cc.data.set(element, identifier,
+ _add_counter(element, spec, identifier) + step)
+
+def _start_timer():
+ """Returns the current datetime as a datetime object."""
+ return datetime.now()
+
+def _stop_timer(start_time, element, spec, identifier):
+ """Sets duration time in seconds as a value of the identifier in
+ the element, which is in seconds between start_time and the
+ current time and is float-type."""
+ delta = datetime.now() - start_time
+ # FIXME: The following statement can be replaced by:
+ # sec = delta.total_seconds()
+ # but total_seconds() is not available in Python 3.1. Please update
+ # this code when we depend on Python 3.2.
+ sec = round(delta.days * 86400 + delta.seconds + \
+ delta.microseconds * 1E-6, 6)
+ _set_counter(element, spec, identifier, sec)
+
+def _concat(*args, sep='/'):
+ """A helper function that is used to generate an identifier for
+ statistics item names. It concatenates words in args with a
+ separator('/')
+ """
+ return sep.join(args)
+
+class _Statistics():
+ """Statistics data set"""
+ # default statistics data
+ _data = {}
+ # default statistics spec used in case the specfile is omitted when
+ # constructing a Counters() object
+ _spec = [
+ {
+ "item_name": "zones",
+ "item_type": "named_set",
+ "item_optional": False,
+ "item_default": {
+ "_SERVER_" : {
+ "notifyoutv4" : 0,
+ "notifyoutv6" : 0
+ }
+ },
+ "item_title": "Zone names",
+ "item_description": "Zone names",
+ "named_set_item_spec": {
+ "item_name": "zonename",
+ "item_type": "map",
+ "item_optional": False,
+ "item_default": {},
+ "item_title": "Zone name",
+ "item_description": "Zone name",
+ "map_item_spec": [
+ {
+ "item_name": "notifyoutv4",
+ "item_type": "integer",
+ "item_optional": False,
+ "item_default": 0,
+ "item_title": "IPv4 notifies",
+ "item_description": "Number of IPv4 notifies per zone name sent out"
+ },
+ {
+ "item_name": "notifyoutv6",
+ "item_type": "integer",
+ "item_optional": False,
+ "item_default": 0,
+ "item_title": "IPv6 notifies",
+ "item_description": "Number of IPv6 notifies per zone name sent out"
+ }
+ ]
+ }
+ }
+ ]
+
+class Counters():
+ """A class for holding and manipulating all statistics counters
+ for a module. A Counters object may be created by specifying a spec
+ file of the module in argument. According to statistics
+ specification in the spec file, a counter value can be incremented,
+ decremented or obtained. Methods such as inc(), dec() and get() are
+ useful for this. Counters objects also have timer functionality.
+ The timer can be started and stopped, and the duration between
+ start and stop can be obtained. Methods such as start_timer(),
+ stop_timer() and get() are useful for this. Saved counters can be
+ cleared by the method clear_all(). Manipulating counters and
+ timers can be temporarily disabled. If disabled, counter values are
+ not changed even if methods to update them are invoked. Including
+ per-zone counters, a list of counters which can be handled in the
+ class are like the following:
+
+ zones/example.com./notifyoutv4
+ zones/example.com./notifyoutv6
+ zones/example.com./xfrrej
+ zones/example.com./xfrreqdone
+ zones/example.com./soaoutv4
+ zones/example.com./soaoutv6
+ zones/example.com./axfrreqv4
+ zones/example.com./axfrreqv6
+ zones/example.com./ixfrreqv4
+ zones/example.com./ixfrreqv6
+ zones/example.com./xfrsuccess
+ zones/example.com./xfrfail
+ zones/example.com./time_to_ixfr
+ zones/example.com./time_to_axfr
+ ixfr_running
+ axfr_running
+ socket/unixdomain/open
+ socket/unixdomain/openfail
+ socket/unixdomain/close
+ socket/unixdomain/bindfail
+ socket/unixdomain/acceptfail
+ socket/unixdomain/accept
+ socket/unixdomain/senderr
+ socket/unixdomain/recverr
+ socket/ipv4/tcp/open
+ socket/ipv4/tcp/openfail
+ socket/ipv4/tcp/close
+ socket/ipv4/tcp/connfail
+ socket/ipv4/tcp/conn
+ socket/ipv4/tcp/senderr
+ socket/ipv4/tcp/recverr
+ socket/ipv6/tcp/open
+ socket/ipv6/tcp/openfail
+ socket/ipv6/tcp/close
+ socket/ipv6/tcp/connfail
+ socket/ipv6/tcp/conn
+ socket/ipv6/tcp/senderr
+ socket/ipv6/tcp/recverr
+ """
+
+ # '_SERVER_' is a special zone name representing an entire
+ # count. It doesn't mean a specific zone, but it means an
+ # entire count in the server.
+ _entire_server = '_SERVER_'
+ # zone names are contained under this dirname in the spec file.
+ _perzone_prefix = 'zones'
+ # default statistics data set
+ _statistics = _Statistics()
+
+ def __init__(self, spec_file_name=None):
+ """A constructor for the Counters class. A path to the spec file
+ can be specified in spec_file_name. Statistics data based on
+ statistics spec can be accumulated if spec_file_name is
+ specified. If omitted, a default statistics spec is used. The
+ default statistics spec is defined in a hidden class named
+ _Statistics().
+ """
+ self._zones_item_list = []
+ self._start_time = {}
+ self._disabled = False
+ self._rlock = threading.RLock()
+ if not spec_file_name: return
+ # change the default statistics spec
+ self._statistics._spec = \
+ isc.config.module_spec_from_file(spec_file_name).\
+ get_statistics_spec()
+ if self._perzone_prefix in \
+ isc.config.spec_name_list(self._statistics._spec):
+ self._zones_item_list = isc.config.spec_name_list(
+ isc.config.find_spec_part(
+ self._statistics._spec, self._perzone_prefix)\
+ ['named_set_item_spec']['map_item_spec'])
+
+ def clear_all(self):
+ """clears all statistics data"""
+ with self._rlock:
+ self._statistics._data = {}
+
+ def disable(self):
+ """disables incrementing/decrementing counters"""
+ with self._rlock:
+ self._disabled = True
+
+ def enable(self):
+ """enables incrementing/decrementing counters"""
+ with self._rlock:
+ self._disabled = False
+
+ def _incdec(self, *args, step=1):
+ """A common helper function for incrementing or decrementing a
+ counter. It acquires a lock to support multi-threaded
+ use. isc.cc.data.DataNotFoundError is raised when incrementing
+ the counter of the item undefined in the spec file."""
+ identifier = _concat(*args)
+ with self._rlock:
+ if self._disabled: return
+ _inc_counter(self._statistics._data,
+ self._statistics._spec,
+ identifier, step)
+
+ def inc(self, *args):
+ """An incrementer for a counter. It acquires a lock to support
+ multi-threaded use. isc.cc.data.DataNotFoundError is raised when
+ incrementing the counter of the item undefined in the spec file."""
+ return self._incdec(*args)
+
+ def dec(self, *args):
+ """A decrementer for a counter. It acquires a lock to support
+ multi-threaded use. isc.cc.data.DataNotFoundError is raised when
+ decrementing the counter of the item undefined in the spec file."""
+ return self._incdec(*args, step=-1)
+
+ def get(self, *args):
+ """A getter method for counters. It returns the current number
+ of the specified counter. isc.cc.data.DataNotFoundError is
+ raised when the counter doesn't have a number yet."""
+ identifier = _concat(*args)
+ return _get_counter(self._statistics._data, identifier)
+
+ def start_timer(self, *args):
+ """Starts a timer which is identified by args and keeps it
+ running until stop_timer() is called. It acquires a lock to
+ support multi-threaded use."""
+ identifier = _concat(*args)
+ with self._rlock:
+ if self._disabled: return
+ isc.cc.data.set(self._start_time, identifier, _start_timer())
+
+ def stop_timer(self, *args):
+ """Stops a timer which is identified by args. It acquires a lock
+ to support multi-threaded use. If the timer isn't started by
+ start_timer() yet, it raises no exception. However if args
+ aren't defined in the spec file, it raises DataNotFoundError.
+ """
+ identifier = _concat(*args)
+ with self._rlock:
+ if self._disabled: return
+ try:
+ start_time = isc.cc.data.find(self._start_time,
+ identifier)
+ except isc.cc.data.DataNotFoundError:
+ # do not set the end time if the timer isn't started
+ return
+ # set the end time
+ _stop_timer(
+ start_time,
+ self._statistics._data,
+ self._statistics._spec,
+ identifier)
+ # A datetime value of once used timer should be deleted
+ # for a future use.
+ # Here, names of branch and leaf are obtained from a
+ # string of identifier. The branch name is equivalent to
+ # the position of datetime to be deleted and the leaf name
+ # is equivalent to the value of datetime to be deleted.
+ (branch, leaf) = identifier.rsplit('/', 1)
+ # Then map of branch is obtained from self._start_time by
+ # using isc.cc.data.find().
+ branch_map = isc.cc.data.find(self._start_time, branch)
+ # Finally a value of the leaf name is deleted from the
+ # map.
+ del branch_map[leaf]
+
+ def get_statistics(self):
+ """Calculates an entire server's counts, and returns statistics
+ data in a format to send out to the stats module, including each
+ counter. If nothing is counted yet, then it returns an empty
+ dictionary."""
+ # entire copy
+ statistics_data = self._statistics._data.copy()
+ # If there is no 'zones' found in statistics_data,
+ # i.e. statistics_data contains no per-zone counter, it just
+ # returns statistics_data because calculating total counts
+ # across the zone names isn't necessary.
+ if self._perzone_prefix not in statistics_data:
+ return statistics_data
+ zones = statistics_data[self._perzone_prefix]
+ # Start calculation for '_SERVER_' counts
+ zones_spec = isc.config.find_spec_part(self._statistics._spec,
+ self._perzone_prefix)
+ zones_attrs = zones_spec['item_default'][self._entire_server]
+ zones_data = {}
+ for attr in zones_attrs:
+ id_str = '%s/%s' % (self._entire_server, attr)
+ sum_ = 0
+ for name in zones:
+ if attr in zones[name]:
+ sum_ += zones[name][attr]
+ if sum_ > 0:
+ _set_counter(zones_data, zones_spec,
+ id_str, sum_)
+ # insert entire-server counts
+ statistics_data[self._perzone_prefix] = dict(
+ statistics_data[self._perzone_prefix],
+ **zones_data)
+ return statistics_data
diff --git a/src/lib/python/isc/statistics/tests/Makefile.am b/src/lib/python/isc/statistics/tests/Makefile.am
new file mode 100644
index 0000000..8dcc296
--- /dev/null
+++ b/src/lib/python/isc/statistics/tests/Makefile.am
@@ -0,0 +1,33 @@
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
+PYTESTS = counters_test.py
+EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST += testdata/test_spec1.spec
+EXTRA_DIST += testdata/test_spec2.spec
+EXTRA_DIST += testdata/test_spec3.spec
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+else
+# Some systems need the ds path even if not all paths are necessary
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/datasrc/.libs
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ PYTHONPATH=$(COMMON_PYTHON_PATH) \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ B10_FROM_BUILD=$(abs_top_builddir) \
+ B10_FROM_SOURCE=$(abs_top_srcdir) \
+ TESTDATASRCDIR=$(abs_top_srcdir)/src/lib/python/isc/statistics/tests/testdata \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
diff --git a/src/lib/python/isc/statistics/tests/counters_test.py b/src/lib/python/isc/statistics/tests/counters_test.py
new file mode 100644
index 0000000..ff15efc
--- /dev/null
+++ b/src/lib/python/isc/statistics/tests/counters_test.py
@@ -0,0 +1,416 @@
+# Copyright (C) 2012 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+'''Tests for isc.statistics.counter'''
+
+import unittest
+import threading
+from datetime import timedelta
+import os
+import imp
+import isc.config
+
+TEST_ZONE_NAME_STR = "example.com."
+TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
+
+from isc.statistics import counters
+
+def setup_functor(event, cycle, functor, *args):
+ """Waits until the event is started, and then invokes the functor
+ by times of the cycle with args."""
+ event.wait()
+ for i in range(cycle): functor(*args)
+
+def start_functor(concurrency, number, functor, *args):
+ """Creates the threads of the number and makes them start. Sets
+ the event and waits until these threads are finished."""
+ threads = []
+ event = threading.Event()
+ for i in range(concurrency):
+ threads.append(threading.Thread(\
+ target=setup_functor, \
+ args=(event, number, functor,) + args))
+ for th in threads: th.start()
+ event.set()
+ for th in threads: th.join()
+
+class TestBasicMethods(unittest.TestCase):
+ TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
+
+ def setUp(self):
+ imp.reload(counters)
+ self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
+
+ def tearDown(self):
+ self.counters.clear_all()
+
+ def test_clear_counters(self):
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.get, 'counter')
+ self.counters.inc('counter')
+ self.assertEqual(self.counters.get('counter'), 1)
+ self.counters.clear_all()
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.get, 'counter')
+
+ def test_enablediable(self):
+ self.assertFalse(self.counters._disabled)
+ self.counters.disable()
+ self.assertTrue(self.counters._disabled)
+ self.counters.enable()
+ self.assertFalse(self.counters._disabled)
+
+ def test_add_counter_normal(self):
+ element = {'counter' : 1}
+ self.assertEqual(\
+ counters._add_counter(element, [], 'counter'), 1)
+
+ def test_add_counter_wrongspec(self):
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ counters._add_counter,
+ {}, [], 'counter')
+
+ def test_add_counter_empty(self):
+ self.assertEqual(\
+ counters._add_counter(
+ {},
+ [ { 'item_name' : 'counter',
+ 'item_type' : 'integer',
+ 'item_default' : 0 } ],
+ 'counter'), 0)
+
+ def test_add_counter_empty_namedset(self):
+ elem = {}
+ spec = [ { 'item_name': 'dirs',
+ 'item_type': 'named_set',
+ 'named_set_item_spec': {
+ 'item_name': 'dir',
+ 'item_type': 'map',
+ 'map_item_spec': [
+ { 'item_name': 'counter1',
+ 'item_type': 'integer',
+ 'item_default': 0 },
+ { 'item_name': 'counter2',
+ 'item_type': 'integer',
+ 'item_default': 0 } ]}
+ }]
+ self.assertEqual(\
+ counters._add_counter(elem, spec, 'dirs/foo/counter1'), 0)
+ self.assertEqual(\
+ counters._add_counter(elem, spec, 'dirs/bar/counter2'), 0)
+
+ def test_timer(self):
+ t1 = counters._start_timer()
+ t2 = t1 - timedelta(seconds=1)
+ self.assertEqual((t1 - t2).seconds, 1)
+ elem = {}
+ spec = [{ 'item_name': 'time',
+ 'item_type': 'real',
+ 'item_default': 0.0 }]
+ counters._stop_timer(t2, elem, spec, 'time')
+ self.assertGreater(counters._get_counter(elem,'time'), 1)
+
+ def test_rasing_incrementers(self):
+ """ use Thread"""
+ concurrency = 3 # number of the threads
+ number = 10000 # number of counting per thread
+ counter_name = "counter"
+ timer_name = "seconds"
+ start_time = counters._start_timer()
+ start_functor(concurrency, number, self.counters.inc,
+ counter_name)
+ counters._stop_timer(start_time,
+ self.counters._statistics._data,
+ self.counters._statistics._spec,
+ timer_name)
+ self.assertEqual(
+ counters._get_counter(self.counters._statistics._data,
+ counter_name),
+ concurrency * number)
+ self.assertGreater(
+ counters._get_counter(self.counters._statistics._data,
+ timer_name), 0)
+
+ def test_concat(self):
+ # only strings
+ a = ( 'a','b','c','d' )
+ self.assertEqual('a/b/c/d', counters._concat(*a))
+ self.assertEqual('aTbTcTd', counters._concat(*a, sep='T'))
+ self.assertEqual('a\\b\\c\\d', counters._concat(*a, sep='\\'))
+ # mixed with other types
+ b = a + (1,)
+ self.assertRaises(TypeError, counters._concat, *b)
+ b = a + (1.1,)
+ self.assertRaises(TypeError, counters._concat, *b)
+ b = a + ([],)
+ self.assertRaises(TypeError, counters._concat, *b)
+ b = a + ({},)
+ self.assertRaises(TypeError, counters._concat, *b)
+
+class BaseTestCounters():
+
+ def setUp(self):
+ imp.reload(counters)
+ self._statistics_data = {}
+ self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
+ self._entire_server = self.counters._entire_server
+ self._perzone_prefix = self.counters._perzone_prefix
+
+ def tearDown(self):
+ self.counters.clear_all()
+
+ def check_get_statistics(self):
+ """Checks no differences between the value returned from
+ get_statistics() and locally collected statistics data. Also
+ checks the result isn't changed even after the method is
+ invoked twice. Finally checks it is valid for the the
+ statistics spec."""
+ self.assertEqual(self.counters.get_statistics(),
+ self._statistics_data)
+ # Idempotency check
+ self.assertEqual(self.counters.get_statistics(),
+ self._statistics_data)
+ if self.TEST_SPECFILE_LOCATION:
+ self.assertTrue(isc.config.module_spec_from_file(
+ self.TEST_SPECFILE_LOCATION).validate_statistics(
+ False, self._statistics_data))
+ else:
+ self.assertTrue(isc.config.ModuleSpec(
+ {'module_name': 'Foo',
+ 'statistics': self.counters._statistics._spec}
+ ).validate_statistics(
+ False, self._statistics_data))
+
+ def test_perzone_counters(self):
+ # for per-zone counters
+ for name in self.counters._zones_item_list:
+ args = (self._perzone_prefix, TEST_ZONE_NAME_STR, name)
+ if name.find('time_to_') == 0:
+ self.counters.start_timer(*args)
+ self.counters.stop_timer(*args)
+ self.assertGreater(self.counters.get(*args), 0)
+ sec = self.counters.get(*args)
+ for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
+ isc.cc.data.set(self._statistics_data,
+ '%s/%s/%s' % (args[0], zone_str, name), sec)
+ # twice exec stopper, then second is not changed
+ self.counters.stop_timer(*args)
+ self.assertEqual(self.counters.get(*args), sec)
+ else:
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ # checks disable/enable
+ self.counters.disable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ self.counters.enable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 2)
+ for zone_str in (self._entire_server, TEST_ZONE_NAME_STR):
+ isc.cc.data.set(self._statistics_data,
+ '%s/%s/%s' % (args[0], zone_str, name), 2)
+ self.check_get_statistics()
+
+ def test_xfrrunning_counters(self):
+ # for counters of xfer running
+ _suffix = 'xfr_running'
+ _xfrrunning_names = \
+ isc.config.spec_name_list(self.counters._statistics._spec,
+ "", True)
+ for name in _xfrrunning_names:
+ if name.find(_suffix) != 1: continue
+ args = name.split('/')
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ self.counters.dec(*args)
+ self.assertEqual(self.counters.get(*args), 0)
+ # checks disable/enable
+ self.counters.disable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 0)
+ self.counters.enable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ self.counters.disable()
+ self.counters.dec(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ self.counters.enable()
+ self.counters.dec(*args)
+ self.assertEqual(self.counters.get(*args), 0)
+ self._statistics_data[name] = 0
+ self.check_get_statistics()
+
+ def test_socket_counters(self):
+ # for ipsocket/unixsocket counters
+ _prefix = 'socket/'
+ _socket_names = \
+ isc.config.spec_name_list(self.counters._statistics._spec,
+ "", True)
+ for name in _socket_names:
+ if name.find(_prefix) != 0: continue
+ args = name.split('/')
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ # checks disable/enable
+ self.counters.disable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 1)
+ self.counters.enable()
+ self.counters.inc(*args)
+ self.assertEqual(self.counters.get(*args), 2)
+ isc.cc.data.set(
+ self._statistics_data, '/'.join(args), 2)
+ self.check_get_statistics()
+
+ def test_undefined_item(self):
+ # test DataNotFoundError raising when specifying item defined
+ # in the specfile
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.inc, '__undefined__')
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.dec, '__undefined__')
+ self.counters.start_timer('__undefined__')
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.stop_timer, '__undefined__')
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.counters.get, '__undefined__')
+
+class TestCounters0(unittest.TestCase, BaseTestCounters):
+ TEST_SPECFILE_LOCATION = None
+ def setUp(self):
+ BaseTestCounters.setUp(self)
+ def tearDown(self):
+ BaseTestCounters.tearDown(self)
+
+class TestCounters1(unittest.TestCase, BaseTestCounters):
+ TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec1.spec'
+ def setUp(self):
+ BaseTestCounters.setUp(self)
+ def tearDown(self):
+ BaseTestCounters.tearDown(self)
+
+class TestCounters2(unittest.TestCase, BaseTestCounters):
+ TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
+ def setUp(self):
+ BaseTestCounters.setUp(self)
+ def tearDown(self):
+ BaseTestCounters.tearDown(self)
+
+class TestCounters3(unittest.TestCase, BaseTestCounters):
+ TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec3.spec'
+ @classmethod
+ def setUpClass(cls):
+ imp.reload(counters)
+ def setUp(self):
+ BaseTestCounters.setUp(self)
+ def tearDown(self):
+ BaseTestCounters.tearDown(self)
+
+class BaseDummyModule():
+ """A base dummy class"""
+ TEST_SPECFILE_LOCATION = TESTDATA_SRCDIR + os.sep + 'test_spec2.spec'
+ def __init__(self):
+ self.counters = counters.Counters(self.TEST_SPECFILE_LOCATION)
+
+ def get_counters(self):
+ return self.counters.get_statistics()
+
+ def clear_counters(self):
+ self.counters.clear_all()
+
+class DummyNotifyOut(BaseDummyModule):
+ """A dummy class equivalent to notify.notify_out.NotifyOut"""
+ def __init__(self):
+ self.counters = counters.Counters()
+
+ def inc_counters(self):
+ """increments counters"""
+ self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv4')
+ self.counters.inc('zones', TEST_ZONE_NAME_STR, 'notifyoutv6')
+
+class DummyXfroutSession(BaseDummyModule):
+ """A dummy class equivalent to XfroutSession in b10-xfrout"""
+ def inc_counters(self):
+ """increments counters"""
+ self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrreqdone')
+ self.counters.inc('zones', TEST_ZONE_NAME_STR, 'xfrrej')
+ self.counters.inc('axfr_running')
+ self.counters.inc('ixfr_running')
+ self.counters.dec('axfr_running')
+ self.counters.dec('ixfr_running')
+
+class DummyUnixSockServer(BaseDummyModule):
+ """A dummy class equivalent to UnixSockServer in b10-xfrout"""
+ def inc_counters(self):
+ """increments counters"""
+ self.counters.inc('socket', 'unixdomain', 'open')
+ self.counters.inc('socket', 'unixdomain', 'close')
+
+class DummyXfroutServer(BaseDummyModule):
+ """A dummy class equivalent to XfroutServer in b10-xfrout"""
+ def __init__(self):
+ super().__init__()
+ self.xfrout_sess = DummyXfroutSession()
+ self.unix_socket_server = DummyUnixSockServer()
+ self.notifier = DummyNotifyOut()
+
+ def inc_counters(self):
+ self.xfrout_sess.inc_counters()
+ self.unix_socket_server.inc_counters()
+ self.notifier.inc_counters()
+
+class TestDummyNotifyOut(unittest.TestCase):
+ """Tests counters are incremented in which the spec file is not
+ loaded"""
+ def setUp(self):
+ imp.reload(counters)
+ self.notifier = DummyNotifyOut()
+ self.notifier.inc_counters()
+
+ def tearDown(self):
+ self.notifier.clear_counters()
+
+ def test_counters(self):
+ self.assertEqual(
+ {'zones': {'_SERVER_': {'notifyoutv4': 1, 'notifyoutv6': 1},
+ TEST_ZONE_NAME_STR: {'notifyoutv4': 1, 'notifyoutv6': 1}}},
+ self.notifier.get_counters())
+
+class TestDummyXfroutServer(unittest.TestCase):
+ """Tests counters are incremented or decremented in which the same
+ spec file is multiply loaded in each child class"""
+ def setUp(self):
+ imp.reload(counters)
+ self.xfrout_server = DummyXfroutServer()
+ self.xfrout_server.inc_counters()
+
+ def tearDown(self):
+ self.xfrout_server.clear_counters()
+
+ def test_counters(self):
+ self.assertEqual(
+ {'axfr_running': 0, 'ixfr_running': 0,
+ 'socket': {'unixdomain': {'open': 1, 'close': 1}},
+ 'zones': {'_SERVER_': {'notifyoutv4': 1,
+ 'notifyoutv6': 1,
+ 'xfrrej': 1, 'xfrreqdone': 1},
+ TEST_ZONE_NAME_STR: {'notifyoutv4': 1,
+ 'notifyoutv6': 1,
+ 'xfrrej': 1,
+ 'xfrreqdone': 1}}},
+ self.xfrout_server.get_counters())
+
+if __name__== "__main__":
+ unittest.main()
diff --git a/src/lib/python/isc/statistics/tests/testdata/test_spec1.spec b/src/lib/python/isc/statistics/tests/testdata/test_spec1.spec
new file mode 100644
index 0000000..dbe7c6c
--- /dev/null
+++ b/src/lib/python/isc/statistics/tests/testdata/test_spec1.spec
@@ -0,0 +1,26 @@
+{
+ "module_spec": {
+ "module_name": "Simple",
+ "config_data": [],
+ "commands": [],
+ "statistics": [
+ {
+ "item_name": "counter",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_title": "Counter",
+ "item_description": "Counter",
+ "item_default": 0
+ },
+ {
+ "item_name": "seconds",
+ "item_type": "real",
+ "item_optional": false,
+ "item_title": "Seconds",
+ "item_description": "Seconds",
+ "item_default": 0.0
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec b/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
new file mode 100644
index 0000000..11b8706
--- /dev/null
+++ b/src/lib/python/isc/statistics/tests/testdata/test_spec2.spec
@@ -0,0 +1,187 @@
+{
+ "module_spec": {
+ "module_name": "XfroutLike",
+ "config_data": [],
+ "commands": [],
+ "statistics": [
+ {
+ "item_name": "zones",
+ "item_type": "named_set",
+ "item_optional": false,
+ "item_default": {
+ "_SERVER_" : {
+ "notifyoutv4" : 0,
+ "notifyoutv6" : 0,
+ "xfrrej" : 0,
+ "xfrreqdone" : 0
+ }
+ },
+ "item_title": "Zone names",
+ "item_description": "Zone names for Xfrout statistics",
+ "named_set_item_spec": {
+ "item_name": "zonename",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "item_title": "Zone name",
+ "item_description": "Zone name for Xfrout statistics",
+ "map_item_spec": [
+ {
+ "item_name": "notifyoutv4",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IPv4 notifies",
+ "item_description": "Number of IPv4 notifies per zone name sent out from Xfrout"
+ },
+ {
+ "item_name": "notifyoutv6",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IPv6 notifies",
+ "item_description": "Number of IPv6 notifies per zone name sent out from Xfrout"
+ },
+ {
+ "item_name": "xfrrej",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "XFR rejected requests",
+ "item_description": "Number of XFR requests per zone name rejected by Xfrout"
+ },
+ {
+ "item_name": "xfrreqdone",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Requested zone transfers",
+ "item_description": "Number of requested zone transfers completed per zone name"
+ }
+ ]
+ }
+ },
+ {
+ "item_name": "ixfr_running",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IXFR running",
+ "item_description": "Number of IXFRs in progress"
+ },
+ {
+ "item_name": "axfr_running",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "AXFR running",
+ "item_description": "Number of AXFRs in progress"
+ },
+ {
+ "item_name": "socket",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "unixdomain": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "bindfail": 0,
+ "acceptfail": 0,
+ "accept": 0,
+ "senderr": 0,
+ "recverr": 0
+ }
+ },
+ "item_title": "Socket",
+ "item_description": "Socket",
+ "map_item_spec": [
+ {
+ "item_name": "unixdomain",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "bindfail": 0,
+ "acceptfail": 0,
+ "accept": 0,
+ "senderr": 0,
+ "recverr": 0
+ },
+ "item_title": "Unix Domain",
+ "item_description": "Unix Domain",
+ "map_item_spec": [
+ {
+ "item_name": "open",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open",
+ "item_description": "Unix Domain sockets opened successfully"
+ },
+ {
+ "item_name": "openfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open failures",
+ "item_description": "Unix Domain sockets open failures"
+ },
+ {
+ "item_name": "close",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Close",
+ "item_description": "Unix Domain sockets closed"
+ },
+ {
+ "item_name": "bindfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Bind failures",
+ "item_description": "Unix Domain sockets bind failures"
+ },
+ {
+ "item_name": "acceptfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Accept failures",
+ "item_description": "Unix Domain sockets incoming accept failures"
+ },
+ {
+ "item_name": "accept",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Accept",
+ "item_description": "Unix Domain sockets incoming connections successfully accepted"
+ },
+ {
+ "item_name": "senderr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Send errors",
+ "item_description": "Unix Domain sockets send errors"
+ },
+ {
+ "item_name": "recverr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Receive errors",
+ "item_description": "Unix Domain sockets receive errors"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec b/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
new file mode 100644
index 0000000..c97a09a
--- /dev/null
+++ b/src/lib/python/isc/statistics/tests/testdata/test_spec3.spec
@@ -0,0 +1,382 @@
+{
+ "module_spec": {
+ "module_name": "XfrinLike",
+ "module_description": "XFR in daemon",
+ "config_data": [],
+ "commands": [],
+ "statistics": [
+ {
+ "item_name": "zones",
+ "item_type": "named_set",
+ "item_optional": false,
+ "item_default": {
+ "_SERVER_" : {
+ "soaoutv4": 0,
+ "soaoutv6": 0,
+ "axfrreqv4": 0,
+ "axfrreqv6": 0,
+ "ixfrreqv4": 0,
+ "ixfrreqv6": 0,
+ "xfrsuccess": 0,
+ "xfrfail": 0,
+ "time_to_ixfr": 0.0,
+ "time_to_axfr": 0.0
+ }
+ },
+ "item_title": "Zone names",
+ "item_description": "Zone names for Xfrout statistics",
+ "named_set_item_spec": {
+ "item_name": "zonename",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "item_title": "Zone name",
+ "item_description": "Zone name for Xfrout statistics",
+ "map_item_spec": [
+ {
+ "item_name": "soaoutv4",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "SOAOutv4",
+ "item_description": "Number of IPv4 SOA queries sent from Xfrin"
+ },
+ {
+ "item_name": "soaoutv6",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "SOAOutv6",
+ "item_description": "Number of IPv6 SOA queries sent from Xfrin"
+ },
+ {
+ "item_name": "axfrreqv4",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "AXFRReqv4",
+ "item_description": "Number of IPv4 AXFR requests sent from Xfrin"
+ },
+ {
+ "item_name": "axfrreqv6",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "AXFRReqv6",
+ "item_description": "Number of IPv6 AXFR requests sent from Xfrin"
+ },
+ {
+ "item_name": "ixfrreqv4",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IXFRReqv4",
+ "item_description": "Number of IPv4 IXFR requests sent from Xfrin"
+ },
+ {
+ "item_name": "ixfrreqv6",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IXFRReqv6",
+ "item_description": "Number of IPv6 IXFR requests sent from Xfrin"
+ },
+ {
+ "item_name": "xfrsuccess",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "XfrSuccess",
+ "item_description": "Number of zone transfer requests succeeded"
+ },
+ {
+ "item_name": "xfrfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "XfrFail",
+ "item_description": "Number of zone transfer requests failed"
+ },
+ {
+ "item_name": "time_to_ixfr",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 0.0,
+ "item_title": "Time to IXFR",
+ "item_description": "Elapsed time in seconds to do the last IXFR"
+ },
+ {
+ "item_name": "time_to_axfr",
+ "item_type": "real",
+ "item_optional": false,
+ "item_default": 0.0,
+ "item_title": "Time to AXFR",
+ "item_description": "Elapsed time in seconds to do the last AXFR"
+ }
+ ]
+ }
+ },
+ {
+ "item_name": "ixfr_running",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IXFRs running",
+ "item_description": "Number of IXFRs in progress"
+ },
+ {
+ "item_name": "axfr_running",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "AXFRs running",
+ "item_description": "Number of AXFRs in progress"
+ },
+ {
+ "item_name": "ixfr_deferred",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "IXFRs deferred",
+ "item_description": "Number of deferred IXFRs"
+ },
+ {
+ "item_name": "axfr_deferred",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "AXFRs deferred",
+ "item_description": "Number of deferred AXFRs"
+ },
+ {
+ "item_name": "soa_in_progress",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "SOA queries",
+ "item_description": "Number of SOA queries in progress"
+ },
+ {
+ "item_name": "socket",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "ipv4": {
+ "tcp": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ }
+ },
+ "ipv6": {
+ "tcp": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ }
+ }
+ },
+ "item_title": "Socket",
+ "item_description": "Socket",
+ "map_item_spec": [
+ {
+ "item_name": "ipv4",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "tcp": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ }
+ },
+ "item_title": "IPv4",
+ "item_description": "IPv4",
+ "map_item_spec": [
+ {
+ "item_name": "tcp",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ },
+ "item_title": "TCP",
+ "item_description": "TCP/IP",
+ "map_item_spec": [
+ {
+ "item_name": "open",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open",
+ "item_description": "IPv4 TCP sockets opened successfully"
+ },
+ {
+ "item_name": "openfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open failures",
+ "item_description": "IPv4 TCP sockets open failures"
+ },
+ {
+ "item_name": "close",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Close",
+ "item_description": "IPv4 TCP sockets closed"
+ },
+ {
+ "item_name": "connfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Connect failures",
+ "item_description": "IPv4 TCP sockets connection failures"
+ },
+ {
+ "item_name": "conn",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Connect",
+ "item_description": "IPv4 TCP connections established successfully"
+ },
+ {
+ "item_name": "senderr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Send errors",
+ "item_description": "IPv4 TCP sockets send errors"
+ },
+ {
+ "item_name": "recverr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Receive errors",
+ "item_description": "IPv4 TCP sockets receive errors"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "item_name": "ipv6",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "tcp": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ }
+ },
+ "item_title": "IPv6",
+ "item_description": "IPv6",
+ "map_item_spec": [
+ {
+ "item_name": "tcp",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {
+ "open": 0,
+ "openfail": 0,
+ "close": 0,
+ "connfail": 0,
+ "conn": 0,
+ "senderr": 0,
+ "recverr": 0
+ },
+ "item_title": "TCP",
+ "item_description": "TCP/IP",
+ "map_item_spec": [
+ {
+ "item_name": "open",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open",
+ "item_description": "IPv6 TCP sockets opened successfully"
+ },
+ {
+ "item_name": "openfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Open failures",
+ "item_description": "IPv6 TCP sockets open failures"
+ },
+ {
+ "item_name": "close",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Close",
+ "item_description": "IPv6 TCP sockets closed"
+ },
+ {
+ "item_name": "connfail",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Connect failures",
+ "item_description": "IPv6 TCP sockets connection failures"
+ },
+ {
+ "item_name": "conn",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Connect",
+ "item_description": "IPv6 TCP connections established successfully"
+ },
+ {
+ "item_name": "senderr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Send errors",
+ "item_description": "IPv6 TCP sockets send errors"
+ },
+ {
+ "item_name": "recverr",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 0,
+ "item_title": "Receive errors",
+ "item_description": "IPv6 TCP sockets receive errors"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
More information about the bind10-changes
mailing list