]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3413] BIND10 framework removal, part 1
authorTomek Mrugalski <tomasz@isc.org>
Mon, 2 Jun 2014 19:14:08 +0000 (21:14 +0200)
committerTomek Mrugalski <tomasz@isc.org>
Mon, 2 Jun 2014 19:14:08 +0000 (21:14 +0200)
139 files changed:
configure.ac
src/bin/Makefile.am
src/bin/bind10/.gitignore [deleted file]
src/bin/bind10/Makefile.am [deleted file]
src/bin/bind10/README [deleted file]
src/bin/bind10/TODO [deleted file]
src/bin/bind10/b10-init.xml [deleted file]
src/bin/bind10/bind10.in [deleted file]
src/bin/bind10/bind10.xml [deleted file]
src/bin/bind10/creatorapi.txt [deleted file]
src/bin/bind10/init.py.in [deleted file]
src/bin/bind10/init.spec [deleted file]
src/bin/bind10/init_messages.mes [deleted file]
src/bin/bind10/run_bind10.sh.in [deleted file]
src/bin/bind10/tests/.gitignore [deleted file]
src/bin/bind10/tests/Makefile.am [deleted file]
src/bin/bind10/tests/args_test.py [deleted file]
src/bin/bind10/tests/init_test.py.in [deleted file]
src/bin/bindctl/.gitignore [deleted file]
src/bin/bindctl/Makefile.am [deleted file]
src/bin/bindctl/README [deleted file]
src/bin/bindctl/TODO [deleted file]
src/bin/bindctl/__init__.py [deleted file]
src/bin/bindctl/bindcmd.py [deleted file]
src/bin/bindctl/bindctl.xml [deleted file]
src/bin/bindctl/bindctl_main.py.in [deleted file]
src/bin/bindctl/cmdparse.py [deleted file]
src/bin/bindctl/command_sets.py [deleted file]
src/bin/bindctl/exception.py [deleted file]
src/bin/bindctl/moduleinfo.py [deleted file]
src/bin/bindctl/mycollections.py [deleted file]
src/bin/bindctl/run_bindctl.sh.in [deleted file]
src/bin/bindctl/tests/.gitignore [deleted file]
src/bin/bindctl/tests/Makefile.am [deleted file]
src/bin/bindctl/tests/bindctl_test.in [deleted file]
src/bin/bindctl/tests/bindctl_test.py [deleted file]
src/bin/bindctl/tests/cmdparse_test.py [deleted file]
src/bin/cfgmgr/.gitignore [deleted file]
src/bin/cfgmgr/Makefile.am [deleted file]
src/bin/cfgmgr/b10-cfgmgr.py.in [deleted file]
src/bin/cfgmgr/b10-cfgmgr.xml [deleted file]
src/bin/cfgmgr/local_plugins/.gitignore [deleted file]
src/bin/cfgmgr/local_plugins/Makefile.am [deleted file]
src/bin/cfgmgr/plugins/.gitignore [deleted file]
src/bin/cfgmgr/plugins/Makefile.am [deleted file]
src/bin/cfgmgr/plugins/README [deleted file]
src/bin/cfgmgr/plugins/b10logging.py [deleted file]
src/bin/cfgmgr/plugins/datasrc.spec.pre.in [deleted file]
src/bin/cfgmgr/plugins/datasrc_config_plugin.py [deleted file]
src/bin/cfgmgr/plugins/logging.spec [deleted file]
src/bin/cfgmgr/plugins/tests/Makefile.am [deleted file]
src/bin/cfgmgr/plugins/tests/datasrc_test.py [deleted file]
src/bin/cfgmgr/plugins/tests/logging_test.py [deleted file]
src/bin/cfgmgr/plugins/tests/tsig_keys_test.py [deleted file]
src/bin/cfgmgr/plugins/tsig_keys.py [deleted file]
src/bin/cfgmgr/plugins/tsig_keys.spec [deleted file]
src/bin/cfgmgr/tests/.gitignore [deleted file]
src/bin/cfgmgr/tests/Makefile.am [deleted file]
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in [deleted file]
src/bin/cfgmgr/tests/testdata/plugins/testplugin.py [deleted file]
src/bin/cmdctl/.gitignore [deleted file]
src/bin/cmdctl/Makefile.am [deleted file]
src/bin/cmdctl/README [deleted file]
src/bin/cmdctl/TODO [deleted file]
src/bin/cmdctl/b10-certgen.cc [deleted file]
src/bin/cmdctl/b10-certgen.xml [deleted file]
src/bin/cmdctl/b10-cmdctl.xml [deleted file]
src/bin/cmdctl/cmdctl-accounts.csv [deleted file]
src/bin/cmdctl/cmdctl.py.in [deleted file]
src/bin/cmdctl/cmdctl.spec.pre.in [deleted file]
src/bin/cmdctl/cmdctl_messages.mes [deleted file]
src/bin/cmdctl/run_b10-cmdctl.sh.in [deleted file]
src/bin/cmdctl/tests/.gitignore [deleted file]
src/bin/cmdctl/tests/Makefile.am [deleted file]
src/bin/cmdctl/tests/b10-certgen_test.py [deleted file]
src/bin/cmdctl/tests/cmdctl_test.in [deleted file]
src/bin/cmdctl/tests/cmdctl_test.py [deleted file]
src/bin/cmdctl/tests/testdata/expired-certfile.pem [deleted file]
src/bin/cmdctl/tests/testdata/mangled-certfile.pem [deleted file]
src/bin/cmdctl/tests/testdata/noca-certfile.pem [deleted file]
src/bin/d2/tests/Makefile.am
src/bin/dhcp4/tests/Makefile.am
src/bin/dhcp4/tests/dhcp4_test.py [deleted file]
src/bin/dhcp6/tests/Makefile.am
src/bin/msgq/.gitignore [deleted file]
src/bin/msgq/Makefile.am [deleted file]
src/bin/msgq/TODO [deleted file]
src/bin/msgq/msgq.py.in [deleted file]
src/bin/msgq/msgq.spec [deleted file]
src/bin/msgq/msgq.xml [deleted file]
src/bin/msgq/msgq_messages.mes [deleted file]
src/bin/msgq/run_msgq.sh.in [deleted file]
src/bin/msgq/tests/.gitignore [deleted file]
src/bin/msgq/tests/Makefile.am [deleted file]
src/bin/msgq/tests/msgq_run_test.py [deleted file]
src/bin/msgq/tests/msgq_test.py [deleted file]
src/bin/stats/.gitignore [deleted file]
src/bin/stats/Makefile.am [deleted file]
src/bin/stats/b10-stats-httpd.xml [deleted file]
src/bin/stats/b10-stats.xml [deleted file]
src/bin/stats/stats-httpd-xml.tpl [deleted file]
src/bin/stats/stats-httpd-xsd.tpl [deleted file]
src/bin/stats/stats-httpd-xsl.tpl [deleted file]
src/bin/stats/stats-httpd.spec [deleted file]
src/bin/stats/stats.py.in [deleted file]
src/bin/stats/stats.spec [deleted file]
src/bin/stats/stats_httpd.py.in [deleted file]
src/bin/stats/stats_httpd_messages.mes [deleted file]
src/bin/stats/stats_messages.mes [deleted file]
src/bin/stats/tests/Makefile.am [deleted file]
src/bin/stats/tests/stats-httpd_test.py [deleted file]
src/bin/stats/tests/stats_test.py [deleted file]
src/bin/stats/tests/test_utils.py [deleted file]
src/bin/stats/tests/testdata/Makefile.am [deleted file]
src/bin/stats/tests/testdata/b10-config.db [deleted file]
src/bin/sysinfo/.gitignore [deleted file]
src/bin/sysinfo/Makefile.am [deleted file]
src/bin/sysinfo/isc-sysinfo.xml [deleted file]
src/bin/sysinfo/run_sysinfo.sh.in [deleted file]
src/bin/sysinfo/sysinfo.py.in [deleted file]
src/bin/tests/.gitignore [deleted file]
src/bin/tests/Makefile.am [deleted file]
src/bin/tests/README [deleted file]
src/bin/tests/process_rename_test.py.in [deleted file]
src/bin/usermgr/.gitignore [deleted file]
src/bin/usermgr/Makefile.am [deleted file]
src/bin/usermgr/README [deleted file]
src/bin/usermgr/b10-cmdctl-usermgr.py.in [deleted file]
src/bin/usermgr/b10-cmdctl-usermgr.xml [deleted file]
src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in [deleted file]
src/bin/usermgr/tests/Makefile.am [deleted file]
src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py [deleted file]
src/lib/config/tests/ccsession_unittests.cc
src/lib/python/isc/bind10/tests/Makefile.am
src/lib/python/isc/config/tests/Makefile.am
src/lib/python/isc/config/tests/ccsession_test.py [deleted file]
src/lib/python/isc/config/tests/cfgmgr_test.py [deleted file]
src/lib/python/isc/log/tests/Makefile.am
src/lib/python/isc/log/tests/log_test.py [deleted file]

index 8e6680bb0be2d90c220adbf559b7bc1c67df094e..f39c2575d706904af57c95005ba21dfb584fcb9e 100644 (file)
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10, 20140313, kea-dev@isc.org)
+AC_INIT(kea, 20140602-git, kea-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 
 # serial-tests is not available in automake version before 1.13, so
@@ -1415,31 +1415,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  ext/Makefile
                  m4macros/Makefile
                  Makefile
-                 src/bin/bind10/bind10
-                 src/bin/bind10/init.py
-                 src/bin/bind10/Makefile
-                 src/bin/bind10/run_bind10.sh
-                 src/bin/bind10/tests/init_test.py
-                 src/bin/bind10/tests/Makefile
-                 src/bin/bindctl/bindctl_main.py
-                 src/bin/bindctl/Makefile
-                 src/bin/bindctl/run_bindctl.sh
-                 src/bin/bindctl/tests/bindctl_test
-                 src/bin/bindctl/tests/Makefile
-                 src/bin/cfgmgr/b10-cfgmgr.py
-                 src/bin/cfgmgr/local_plugins/Makefile
-                 src/bin/cfgmgr/Makefile
-                 src/bin/cfgmgr/plugins/datasrc.spec.pre
-                 src/bin/cfgmgr/plugins/Makefile
-                 src/bin/cfgmgr/plugins/tests/Makefile
-                 src/bin/cfgmgr/tests/b10-cfgmgr_test.py
-                 src/bin/cfgmgr/tests/Makefile
-                 src/bin/cmdctl/cmdctl.py
-                 src/bin/cmdctl/cmdctl.spec.pre
-                 src/bin/cmdctl/Makefile
-                 src/bin/cmdctl/run_b10-cmdctl.sh
-                 src/bin/cmdctl/tests/cmdctl_test
-                 src/bin/cmdctl/tests/Makefile
                  src/bin/d2/Makefile
                  src/bin/d2/spec_config.h.pre
                  src/bin/d2/tests/Makefile
@@ -1457,26 +1432,8 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/bin/dhcp6/tests/test_data_files_config.h
                  src/bin/dhcp6/tests/test_libraries.h
                  src/bin/Makefile
-                 src/bin/msgq/Makefile
-                 src/bin/msgq/msgq.py
-                 src/bin/msgq/run_msgq.sh
-                 src/bin/msgq/tests/Makefile
                  src/bin/sockcreator/Makefile
                  src/bin/sockcreator/tests/Makefile
-                 src/bin/stats/Makefile
-                 src/bin/stats/stats_httpd.py
-                 src/bin/stats/stats.py
-                 src/bin/stats/tests/Makefile
-                 src/bin/stats/tests/testdata/Makefile
-                 src/bin/sysinfo/Makefile
-                 src/bin/sysinfo/run_sysinfo.sh
-                 src/bin/sysinfo/sysinfo.py
-                 src/bin/tests/Makefile
-                 src/bin/tests/process_rename_test.py
-                 src/bin/usermgr/b10-cmdctl-usermgr.py
-                 src/bin/usermgr/Makefile
-                 src/bin/usermgr/run_b10-cmdctl-usermgr.sh
-                 src/bin/usermgr/tests/Makefile
                  src/hooks/dhcp/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/tests/Makefile
@@ -1581,15 +1538,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
 ])
 
  AC_CONFIG_COMMANDS([permissions], [
-           chmod +x src/bin/bind10/bind10
-           chmod +x src/bin/bind10/run_bind10.sh
-           chmod +x src/bin/bindctl/run_bindctl.sh
-           chmod +x src/bin/bindctl/tests/bindctl_test
-           chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
-           chmod +x src/bin/cmdctl/tests/cmdctl_test
-           chmod +x src/bin/msgq/run_msgq.sh
-           chmod +x src/bin/sysinfo/run_sysinfo.sh
-           chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/log/tests/console_test.sh
            chmod +x src/lib/log/tests/destination_test.sh
index 121d43aad19b5c53e2d4dd915394560de55bcf66..8b970585cbdfa06ed2b8dee8c59fece475dfc5de 100644 (file)
@@ -1,6 +1,4 @@
 # The following build order must be maintained.
-SUBDIRS = bind10 bindctl cfgmgr msgq cmdctl \
-       usermgr stats tests  sockcreator dhcp4 dhcp6 \
-       d2 sysinfo
+SUBDIRS = sockcreator dhcp4 dhcp6 d2
 
 check-recursive: all-recursive
diff --git a/src/bin/bind10/.gitignore b/src/bin/bind10/.gitignore
deleted file mode 100644 (file)
index 411e4fd..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/run_bind10.sh
-/bind10
-/bind10.8
-/b10-init
-/b10-init.8
-/init.py
diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am
deleted file mode 100644 (file)
index b2e9f05..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-SUBDIRS = . tests
-
-sbin_SCRIPTS = bind10
-pkglibexec_SCRIPTS = b10-init
-CLEANFILES = b10-init b10-init.pyc init.pyc
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/init_messages.py
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/init_messages.pyc
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/init_messages.py
-pylogmessagedir = $(pyexecdir)/isc/log_messages/
-
-noinst_SCRIPTS = run_bind10.sh
-
-bind10dir = $(pkgdatadir)
-bind10_DATA = init.spec
-EXTRA_DIST = init.spec bind10.in
-
-man_MANS = b10-init.8 bind10.8
-DISTCLEANFILES = $(man_MANS) bind10
-EXTRA_DIST += $(man_MANS) b10-init.xml bind10.xml init_messages.mes
-
-if GENERATE_DOCS
-
-bind10.8: bind10.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/bind10.xml
-
-b10-init.8: b10-init.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-init.xml
-
-#dist-local-check-mans-enabled:
-#      @if grep "Man generation disabled" $(man_MANS) >/dev/null; then $(RM) $(man_MANS); fi
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-#dist-local-check-mans-enabled:
-#      @echo "*** --enable-generate-docs must be used in order to make dist"
-#      @false
-
-endif
-
-$(PYTHON_LOGMSGPKG_DIR)/work/init_messages.py : init_messages.mes
-       $(top_builddir)/src/lib/log/compiler/message \
-       -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/init_messages.mes
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-init: init.py $(PYTHON_LOGMSGPKG_DIR)/work/init_messages.py
-       $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-              -e "s|@@LIBDIR@@|$(libdir)|" \
-              -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" init.py >$@
-       chmod a+x $@
-
-pytest:
-       $(SHELL) tests/bind10_test
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/bind10/README b/src/bin/bind10/README
deleted file mode 100644 (file)
index d75c0cd..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-This directory contains the source for the "b10-init" program, as well as
-the "bind10" script that runs it.
-
-Files:
-  Makefile.am      - build information
-  README           - this file
-  TODO             - remaining development tasks for this program
-  bind10.py.in     - used to make bind10.py with proper Python paths
-  init.spec        - defines the options and commands
-  run_bind10.sh.in - use to make run_bind10.sh with proper Python paths
-
-The "tests" directory contains unit tests for the application.
-
-If you want to run the bind10 program from the source directory, then
-use the "run_bind10.sh" script. To run it in the installed location,
-use the "bind10" program.
diff --git a/src/bin/bind10/TODO b/src/bin/bind10/TODO
deleted file mode 100644 (file)
index 82abdb1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-- Read msgq configuration from configuration manager (Trac #213)
-  https://bind10.isc.org/ticket/213
-- Provide more administrator options:
-  - Get information on a process (returns list of times started & stopped, 
-    plus current information such as PID)
-  - Stop a component
-  - Force-stop a component
-- Mechanism to wait for child to start before continuing
-- Use .spec file to define commands
-- Rename "c-channel" stuff to msgq for clarity
-- Reply to shutdown message?
-- Some sort of group creation so termination signals can be sent to
-  children of children processes (if any)
-
diff --git a/src/bin/bind10/b10-init.xml b/src/bin/bind10/b10-init.xml
deleted file mode 100644 (file)
index de7832d..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>February 5, 2013</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-init</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-init</refname>
-    <refpurpose>BIND 10 Init process</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010-2013</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-init</command>
-      <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
-      <arg><option>-i</option></arg>
-      <arg><option>-m <replaceable>file</replaceable></option></arg>
-      <arg><option>-p <replaceable>data_path</replaceable></option></arg>
-      <arg><option>-u <replaceable>user</replaceable></option></arg>
-      <arg><option>-v</option></arg>
-      <arg><option>-w <replaceable>wait_time</replaceable></option></arg>
-      <arg><option>--clear-config</option></arg>
-      <arg><option>--cmdctl-port</option> <replaceable>port</replaceable></arg>
-      <arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
-      <arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
-      <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
-      <arg><option>--no-kill</option></arg>
-      <arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
-      <arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
-      <arg><option>--user <replaceable>user</replaceable></option></arg>
-      <arg><option>--verbose</option></arg>
-      <arg><option>--wait <replaceable>wait_time</replaceable></option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-
-    <para>The <command>b10-init</command> daemon starts up other
-    BIND 10 required daemons.  It handles restarting of exiting
-    programs and also the shutdown of all managed daemons.</para>
-
-<!-- TODO: list what it starts here? -->
-
-<!-- TODO
-    <para>The configuration of the <command>b10-init</command> daemon
-    is defined in the TODO configuration file, as described in the
-    <citerefentry><refentrytitle>TODO</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-    manual page.</para>
--->
-
-  </refsect1>
-
-  <refsect1>
-    <title>ARGUMENTS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term>
-          <option>-c</option> <replaceable>config-filename</replaceable>,
-          <option>--config-file</option> <replaceable>config-filename</replaceable>
-        </term>
-        <listitem>
-          <para>The configuration filename to use. Can be either absolute or
-          relative to data path. In case it is absolute, value of data path is
-          not considered.
-          Defaults to <filename>b10-config.db</filename>.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>--clear-config</option>
-        </term>
-        <listitem>
-         <para>
-           This will create a backup of the existing configuration
-           file, remove it and start
-           <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-            with the default configuration.
-           The name of the backup file can be found in the logs
-           (<varname>CFGMGR_BACKED_UP_CONFIG_FILE</varname>).
-           (It will append a number to the backup filename if a
-           previous backup file exists.)
-
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>--cmdctl-port</option> <replaceable>port</replaceable>
-        </term>
-        <listitem>
-         <para>The <command>b10-cmdctl</command> daemon will listen
-           on this port.
-           (See
-           <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-            for the default.)
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>-p</option> <replaceable>directory</replaceable>,
-          <option>--data-path</option> <replaceable>directory</replaceable>
-        </term>
-        <listitem>
-          <para>The path where BIND 10 programs look for various data files.
-         Currently only
-         <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-         uses it to locate the configuration file, but the usage
-         might be extended for other programs and other types of
-         files.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-m</option> <replaceable>file</replaceable>,
-           <option>--msgq-socket-file</option> <replaceable>file</replaceable></term>
-
-        <listitem>
-          <para>The UNIX domain socket file for the
-           <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            daemon to use.
-            The default is
-            <filename>/usr/local/var/bind10/msg_socket</filename>.
-<!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
-           </para>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-i</option>, <option>--no-kill</option></term>
-        <listitem>
-         <para>When this option is passed, <command>b10-init</command>
-         does not send SIGTERM and SIGKILL signals to modules during
-         shutdown. (This option was introduced for use during
-         testing.)</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
-<!-- TODO: example more detail. -->
-        <listitem>
-          <para>The username for <command>b10-init</command> to run as.
-            <command>b10-init</command> must be initially ran as the
-            root user to use this option.
-            The default is to run as the current user.</para>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>--pid-file</option> <replaceable>filename</replaceable></term>
-        <listitem>
-          <para>If defined, the PID of the <command>b10-init</command> is stored
-             in this file.
-          </para>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>--pretty-name <replaceable>name</replaceable></option></term>
-
-        <listitem>
-          <para>The name this process should have in tools like
-          <command>ps</command> or <command>top</command>. This
-          is handy if you have multiple versions/installations
-          of <command>b10-init</command>.
-<!-- TODO: only supported with setproctitle feature
-The default is the basename of ARG 0.
--->
-</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-v</option>, <option>--verbose</option></term>
-        <listitem>
-         <para>Display more about what is going on for
-         <command>b10-init</command> and its child processes.</para>
-<!-- TODO: not true about all children yet -->
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-w</option> <replaceable>wait_time</replaceable>, <option>--wait</option> <replaceable>wait_time</replaceable></term>
-        <listitem>
-         <para>Sets the amount of time that BIND 10 will wait for
-         the configuration manager (a key component of BIND 10)
-         to initialize itself before abandoning the start up and
-         terminating with an error.  The
-         <replaceable>wait_time</replaceable> is specified in
-         seconds and has a default value of 10.
-          </para>
-        </listitem>
-      </varlistentry>
-
-    </variablelist>
-  </refsect1>
-
-<!--
-TODO: configuration section
--->
-
-  <refsect1>
-    <title>CONFIGURATION AND COMMANDS</title>
-
-    <para>
-      The configuration provides settings for components for
-      <command>b10-init</command> to manage under
-      <varname>/Init/components/</varname>.
-      The default elements are:
-    </para>
-
-    <itemizedlist>
-
-      <listitem>
-        <para> <varname>/Init/components/b10-cmdctl</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Init/components/b10-stats</varname> </para>
-      </listitem>
-
-    </itemizedlist>
-
-    <para>
-      (Note that the startup of <command>b10-sockcreator</command>,
-      <command>b10-cfgmgr</command>, and <command>b10-msgq</command>
-      is not configurable. They are hardcoded and <command>b10-init</command>
-      will not run without them.)
-    </para>
-
-    <para>
-      The named sets for components contain the following settings:
-    </para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term><varname>address</varname></term>
-        <listitem>
-         <para>The name used for communicating to it on the message
-         bus.</para>
-<!-- NOTE: vorner said:
-These can be null, because the components are special ones, and
-the special class there already knows the address. It is (I hope)
-explained in the guide. I'd like to get rid of the special components
-sometime and I'd like it to teach to guess the address.
--->
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>kind</varname></term>
-        <listitem>
-          <para>
-            This defines how required a component is.
-            The possible settings for <varname>kind</varname> are:
-            <varname>core</varname> (system won't start if it won't
-            start and <command>b10-init</command> will shutdown if
-            a <quote>core</quote> component crashes),
-            <varname>dispensable</varname> (<command>b10-init</command>
-            will restart failing component),
-            and
-           <varname>needed</varname> (<command>b10-init</command>
-           will shutdown if component won't initially start, but
-           if crashes later, it will attempt to restart).
-            This setting is required.
-<!-- TODO: formatting -->
-          </para>
-        </listitem>
-      </varlistentry>
-
-<!--
-TODO: currently not used
-      <varlistentry>
-        <term> <varname>params</varname> </term>
-        <listitem>
-          <para>
-list
-</para>
-        </listitem>
-      </varlistentry>
--->
-
-      <varlistentry>
-        <term> <varname>priority</varname> </term>
-        <listitem>
-          <para>This is an integer. <command>b10-init</command>
-            will start the components with largest priority numbers first.
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-          <term> <varname>process</varname> </term>
-        <listitem>
-          <para>This is the filename of the executable to be started.
-            If not defined, then <command>b10-init</command> will
-            use the component name instead.
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-          <term> <varname>special</varname> </term>
-        <listitem>
-          <para>
-            This defines if the component is started a special, hardcoded
-            way.
-<!--
-TODO: document this ... but maybe some of these will be removed
-once we get rid of some using switches for components?
-
-auth
-cfgmgr
-cmdctl
-msgq
-resolver
-sockcreator
-xfrin
--->
-
-</para>
-        </listitem>
-      </varlistentry>
-
-    </variablelist>
-
-<!-- TODO: formating -->
-    <para>
-      The <varname>Init</varname> configuration commands are:
-    </para>
-
-<!-- TODO -->
-<!--
-    <para>
-      <command>drop_socket</command>
-      This is an internal command and not exposed to the administrator.
-    </para>
--->
-
-<!-- TODO -->
-<!--
-    <para>
-      <command>get_socket</command>
-      This is an internal command and not exposed to the administrator.
-    </para>
--->
-
-    <para>
-      <command>getstats</command> tells <command>b10-init</command>
-      to send its statistics data to the <command>b10-stats</command>
-      daemon.
-      This is an internal command and not exposed to the administrator.
-<!-- not defined in spec -->
-    </para>
-
-    <para>
-      <command>ping</command> is used to check the connection with the
-      <command>b10-init</command> daemon.
-      It returns the text <quote>pong</quote>.
-    </para>
-
-    <para>
-      <command>show_processes</command> lists the current processes
-      managed by <command>b10-init</command>.
-      The output is an array in JSON format containing the process
-      ID, the name for each and the address name used on each message bus.
-<!-- TODO: what is name? -->
-<!-- TODO: change to JSON object format? -->
-<!-- TODO: ticket #1406 -->
-    </para>
-
-    <para>
-      <command>shutdown</command> tells <command>b10-init</command>
-      to shutdown the BIND 10 servers.
-      It will tell each process it manages to shutdown and, when
-      complete, <command>b10-init</command> will exit.
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>STATISTICS DATA</title>
-
-    <para>
-      The statistics data collected by the <command>b10-stats</command>
-      daemon for <quote>Init</quote> include:
-    </para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term>boot_time</term>
-        <listitem><para>
-          The date and time that the <command>b10-init</command>
-          process started.
-          This is represented in ISO 8601 format.
-        </para></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-  </refsect1>
-
-  <refsect1>
-    <title>FILES</title>
-    <para><filename>sockcreator-XXXXXX/sockcreator</filename>
-    &mdash;
-    the Unix Domain socket located in a temporary file directory for
-    <command>b10-sockcreator</command>
-<!--    <citerefentry><refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum></citerefentry> -->
-    communication.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-auth</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-xfrout</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-zonemgr</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-stats-httpd</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-<!-- <citerefentry>
-        <refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>, -->
-
-  <refsect1 id='history'><title>HISTORY</title>
-    <para>The development of <command>b10-init</command>
-      was started in October 2009.
-      It was renamed and its configuration identifier changed
-      in February 2013.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>AUTHORS</title>
-    <para>
-      The <command>b10-init</command>
-      daemon was initially designed by Shane Kerr of ISC.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/bind10/bind10.in b/src/bin/bind10/bind10.in
deleted file mode 100755 (executable)
index 88c45c9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-# We use this wrapper script both for production and in-source tests; in
-# the latter case B10_FROM_BUILD environment is expected to be defined.
-if test -n "${B10_FROM_BUILD}"; then
-       exec ${B10_FROM_BUILD}/src/bin/bind10/b10-init $*
-else
-       prefix=@prefix@
-       exec_prefix=@exec_prefix@
-       exec @libexecdir@/@PACKAGE@/b10-init $*
-fi
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
deleted file mode 100644 (file)
index 16082f3..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>February 5, 2013</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>bind10</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>bind10</refname>
-    <refpurpose>BIND 10 start script</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2013</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>bind10</command>
-      <arg><option>options</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-
-    <para>The <command>bind10</command> script is a simple wrapper that
-    starts BIND 10 by running the <command>b10-init</command> daemon. All
-    options passed to <command>bind10</command> are directly passed on to
-    <command>b10-init</command>.</para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1 id='history'><title>HISTORY</title>
-    <para>The <command>bind10</command> script was added in February 2013.
-    </para>
-  </refsect1>
-
-</refentry>
diff --git a/src/bin/bind10/creatorapi.txt b/src/bin/bind10/creatorapi.txt
deleted file mode 100644 (file)
index d3e67f4..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-Socket creator API
-==================
-
-This API is between Init and other modules to allow them requesting of sockets.
-For simplicity, we will use the socket creator for all (even non-privileged)
-ports for now, but we should have some function where we can abstract it later.
-
-Goals
------
-* Be able to request a socket of any combination IPv4/IPv6 UDP/TCP bound to given
-  port and address (sockets that are not bound to anything can be created
-  without privileges, therefore are not requested from the socket creator).
-* Allow to provide the same socket to multiple modules (eg. multiple running
-  auth servers).
-* Allow releasing the sockets (in case all modules using it give it up,
-  terminate or crash).
-* Allow restricting of the sharing (don't allow shared socket between auth
-  and recursive, as the packets would often get to the wrong application,
-  show error instead).
-* Get the socket to the application.
-
-Transport of sockets
---------------------
-It seems we are stuck with current msgq for a while and there's a chance the
-new replacement will not be able to send sockets inbound. So, we need another
-channel.
-
-b10-init will create a unix-domain socket and listen on it. When something
-requests a socket over the command channel and the socket is created, some kind
-of token is returned to the application (which will represent the future
-socket). The application then connects to the unix-domain socket, sends the
-token over the connection (so Init will know which socket to send there, in case
-multiple applications ask for sockets simultaneously) and Init sends the socket
-in return.
-
-In theory, we could send the requests directly over the unix-domain
-socket, but it has two disadvantages:
-* The msgq handles serializing/deserializing of structured
-  information (like the parameters to be used), we would have to do it
-  manually on the socket.
-* We could place some kind of security in front of msgq (in case file
-  permissions are not enough, for example if they are not honored on
-  socket files, as indicated in the first paragraph of:
-  http://lkml.indiana.edu/hypermail/linux/kernel/0505.2/0008.html).
-  The socket would have to be secured separately. With the tokens,
-  there's some level of security already - someone not having the
-  token can't request a priviledged socket.
-
-Caching of sockets
-------------------
-To allow sending the same socket to multiple application, the Init process will
-hold a cache. Each socket that is created and sent is kept open in Init and
-preserved there as well. A reference count is kept with each of them.
-
-When another application asks for the same socket, it is simply sent from the
-cache instead of creating it again by the creator.
-
-When application gives the socket willingly (by sending a message over the
-command channel), the reference count can be decreased without problems. But
-when the application terminates or crashes, we need to decrease it as well.
-There's a problem, since we don't know which command channel connection (eg.
-lname) belongs to which PID. Furthermore, the applications don't need to be
-started by b10-init.
-
-There are two possibilities:
-* Let the msgq send messages about disconnected clients (eg. group message to
-  some name). This one is better if we want to migrate to dbus, since dbus
-  already has this capability as well as sending the sockets inbound (at least it
-  seems so on unix) and we could get rid of the unix-domain socket completely.
-* Keep the unix-domain connections open forever. Init can remember which socket
-  was sent to which connection and when the connection closes (because the
-  application crashed), it can drop all the references on the sockets. This
-  seems easier to implement.
-
-The commands
-------------
-* Command to release a socket. This one would have single parameter, the token
-  used to get the socket. After this, b10-init would decrease its reference
-  count and if it drops to zero, close its own copy of the socket. This
-  should be used when the module stops using the socket (and after closes
-  it). The library could remember the file-descriptor to token mapping (for
-  common applications that don't request the same socket multiple times in
-  parallel).
-* Command to request a socket. It would have parameters to specify which socket
-  (IP address, address family, port) and how to allow sharing. Sharing would be
-  one of:
-  - None
-  - Same kind of application (however, it is not entirely clear what
-    this means, in case it won't work out intuitively, we'll need to
-    define it somehow)
-  - Any kind of application
-  And a kind of application would be provided, to decide if the sharing is
-  possible (eg. if auth allows sharing with the same kind and something else
-  allows sharing with anything, the sharing is not possible, two auths can).
-
-  It would return either error (the socket can't be created or sharing is not
-  possible) or the token. Then there would be some time for the application to
-  pick up the requested socket.
-
-Examples
---------
-We probably would have a library with blocking calls to request the
-sockets, so a code could look like:
-
-(socket_fd, token) = request_socket(address, port, 'UDP', SHARE_SAMENAME, 'test-application')
-sock = socket.fromfd(socket_fd)
-
-# Some sock.send and sock.recv stuff here
-
-sock.close()
-release_socket(socket_fd) # or release_socket(token)
-
-Known limitations
------------------
-Currently the socket creator doesn't support specifying any socket
-options. If it turns out there are any options that need to be set
-before bind(), we'll need to extend it (and extend the protocol as
-well). If we want to support them, we'll have to solve a possible
-conflict (what to do when two applications request the same socket and
-want to share it, but want different options).
-
-The current socket creator doesn't know raw sockets, but if they are
-needed, it should be easy to add.
diff --git a/src/bin/bind10/init.py.in b/src/bin/bind10/init.py.in
deleted file mode 100755 (executable)
index 67491b3..0000000
+++ /dev/null
@@ -1,1389 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010,2011  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.
-
-"""
-This file implements the b10-init program.
-
-Its purpose is to start up the BIND 10 system, and then manage the
-processes, by starting and stopping processes, plus restarting
-processes that exit.
-
-To start the system, it first runs the c-channel program (msgq), then
-connects to that. It then runs the configuration manager, and reads
-its own configuration. Then it proceeds to starting other modules.
-
-The Python subprocess module is used for starting processes, but
-because this is not efficient for managing groups of processes,
-SIGCHLD signals are caught and processed using the signal module.
-
-Most of the logic is contained in the Init class. However, since Python
-requires that signal processing happen in the main thread, we do
-signal handling outside of that class, in the code running for
-__main__.
-"""
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import os
-from isc.util.address_formatter import AddressFormatter
-
-# If B10_FROM_SOURCE is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_SOURCE" in os.environ:
-    SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] +\
-                        "/src/bin/bind10/init.spec"
-else:
-    PREFIX = "@prefix@"
-    DATAROOTDIR = "@datarootdir@"
-    SPECFILE_LOCATION = "@datadir@/@PACKAGE@/init.spec"\
-                         .replace("${datarootdir}", DATAROOTDIR)\
-                         .replace("${prefix}", PREFIX)
-
-import subprocess
-import signal
-import re
-import errno
-import time
-import select
-import random
-import socket
-from optparse import OptionParser, OptionValueError
-import io
-import pwd
-import posix
-import copy
-
-from bind10_config import LIBEXECPATH
-import bind10_config
-import isc.cc
-import isc.util.process
-import isc.net.parse
-import isc.log
-import isc.config
-from isc.log_messages.init_messages import *
-import isc.bind10.component
-import isc.bind10.special_component
-import isc.bind10.socket_cache
-import isc.util.traceback_handler
-import libutil_io_python
-import tempfile
-
-isc.log.init("b10-init", buffer=True)
-logger = isc.log.Logger("init")
-
-# Pending system-wide debug level definitions, the ones we
-# use here are hardcoded for now
-DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
-DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
-
-# Messages sent over the unix domain socket to indicate if it is followed by a
-# real socket
-CREATOR_SOCKET_OK = b"1\n"
-CREATOR_SOCKET_UNAVAILABLE = b"0\n"
-
-# RCodes of known exceptions for the get_token command
-CREATOR_SOCKET_ERROR = 2
-CREATOR_SHARE_ERROR = 3
-
-# Assign this process some longer name
-isc.util.process.rename()
-
-# This is the version that gets displayed to the user.
-# The VERSION string consists of the module name, the module version
-# number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bind10 20110223 (BIND 10 @PACKAGE_VERSION@)"
-
-# This is for boot_time of Init
-_BASETIME = time.gmtime()
-
-# Detailed error message commonly used on startup failure, possibly due to
-# permission issue regarding log lock file.  We dump verbose message because
-# it may not be clear exactly what to do if it simply says
-# "failed to open <filename>: permission denied"
-NOTE_ON_LOCK_FILE = """\
-TIP: if this is about permission error for a lock file, check if the directory
-of the file is writable for the user of the bind10 process; often you need
-to start bind10 as a super user.  Also, if you specify the -u option to
-change the user and group, the directory must be writable for the group,
-and the created lock file must be writable for that user. Finally, make sure
-the lock file is not left in the directly before restarting.
-"""
-
-class ProcessInfoError(Exception): pass
-
-class ChangeUserError(Exception):
-    '''Exception raised when setuid/setgid fails.
-
-    When raised, it's expected to be propagated via underlying component
-    management modules to the top level so that it will help provide useful
-    fatal error message.
-
-    '''
-    pass
-
-class ProcessInfo:
-    """Information about a process"""
-
-    dev_null = open(os.devnull, "w")
-
-    def __init__(self, name, args, env={}, dev_null_stdout=False,
-                 dev_null_stderr=False):
-        self.name = name
-        self.args = args
-        self.env = env
-        self.dev_null_stdout = dev_null_stdout
-        self.dev_null_stderr = dev_null_stderr
-        self.process = None
-        self.pid = None
-
-    def _preexec_work(self):
-        """Function used before running a program that needs to run as a
-        different user."""
-        # First, put us into a separate process group so we don't get
-        # SIGINT signals on Ctrl-C (b10-init will shut everything down by
-        # other means).
-        os.setpgrp()
-
-    def _spawn(self):
-        if self.dev_null_stdout:
-            spawn_stdout = self.dev_null
-        else:
-            spawn_stdout = None
-        if self.dev_null_stderr:
-            spawn_stderr = self.dev_null
-        else:
-            spawn_stderr = None
-        # Environment variables for the child process will be a copy of those
-        # of the b10-init process with any additional specific variables given
-        # on construction (self.env).
-        spawn_env = copy.deepcopy(os.environ)
-        spawn_env.update(self.env)
-        spawn_env['PATH'] = LIBEXECPATH + ':' + spawn_env['PATH']
-        self.process = subprocess.Popen(self.args,
-                                        stdin=subprocess.PIPE,
-                                        stdout=spawn_stdout,
-                                        stderr=spawn_stderr,
-                                        close_fds=True,
-                                        env=spawn_env,
-                                        preexec_fn=self._preexec_work)
-        self.pid = self.process.pid
-
-    # spawn() and respawn() are the same for now, but in the future they
-    # may have different functionality
-    def spawn(self):
-        self._spawn()
-
-    def respawn(self):
-        self._spawn()
-
-class CChannelConnectError(Exception): pass
-
-class ProcessStartError(Exception): pass
-
-class Init:
-    """Init of BIND class."""
-
-    def __init__(self, msgq_socket_file=None, data_path=None,
-                 config_filename=None, clear_config=False,
-                 verbose=False, nokill=False, setuid=None, setgid=None,
-                 username=None, cmdctl_port=None, wait_time=10):
-        """
-            Initialize the Init of BIND. This is a singleton (only one can
-            run).
-
-            The msgq_socket_file specifies the UNIX domain socket file that the
-            msgq process listens on.  If verbose is True, then b10-init reports
-            what it is doing.
-
-            Data path and config filename are passed through to config manager
-            (if provided) and specify the config file to be used.
-
-            The cmdctl_port is passed to cmdctl and specify on which port it
-            should listen.
-
-            wait_time controls the amount of time (in seconds) that Init waits
-            for selected processes to initialize before continuing with the
-            initialization.  Currently this is only the configuration manager.
-        """
-        self.cc_session = None
-        self.ccs = None
-        self.curproc = None
-        self.msgq_socket_file = msgq_socket_file
-        self.component_config = {}
-        # Some time in future, it may happen that a single component has
-        # multple processes (like a pipeline-like component). If so happens,
-        # name "components" may be inappropriate. But as the code isn't
-        # probably completely ready for it, we leave it at components for
-        # now. We also want to support multiple instances of a single
-        # component. If it turns out that we'll have a single component with
-        # multiple same processes or if we start multiple components with the
-        # same configuration (we do this now, but it might change) is an open
-        # question.
-        self.components = {}
-        # Simply list of components that died and need to wait for a
-        # restart. Components manage their own restart schedule now
-        self.components_to_restart = []
-        self.runnable = False
-        self.__uid = setuid
-        self.__gid = setgid
-        self.username = username
-        self.verbose = verbose
-        self.nokill = nokill
-        self.data_path = data_path
-        self.config_filename = config_filename
-        self.clear_config = clear_config
-        self.cmdctl_port = cmdctl_port
-        self.wait_time = wait_time
-        self.msgq_timeout = 5
-
-        # _run_under_unittests is only meant to be used when testing. It
-        # bypasses execution of some code to help with testing.
-        self._run_under_unittests = False
-
-        self._component_configurator = isc.bind10.component.Configurator(self,
-            isc.bind10.special_component.get_specials())
-        # The priorities here make them start in the correct order. First
-        # the socket creator (which would drop root privileges by then),
-        # then message queue and after that the config manager (which uses
-        # the config manager)
-        self.__core_components = {
-            'sockcreator': {
-                'kind': 'core',
-                'special': 'sockcreator',
-                'priority': 200
-            },
-            'msgq': {
-                'kind': 'core',
-                'special': 'msgq',
-                'priority': 199
-            },
-            'cfgmgr': {
-                'kind': 'core',
-                'special': 'cfgmgr',
-                'priority': 198
-            }
-        }
-        self.__started = False
-        self.exitcode = 0
-
-        # If -v was set, enable full debug logging.
-        if self.verbose:
-            logger.set_severity("DEBUG", 99)
-        # This is set in init_socket_srv
-        self._socket_path = None
-        self._socket_cache = None
-        self._tmpdir = None
-        self._srv_socket = None
-        self._unix_sockets = {}
-
-    def __propagate_component_config(self, config):
-        comps = dict(config)
-        # Fill in the core components, so they stay alive
-        for comp in self.__core_components:
-            if comp in comps:
-                raise Exception(comp + " is core component managed by " +
-                                "b10-init, do not set it")
-            comps[comp] = self.__core_components[comp]
-        # Update the configuration
-        self._component_configurator.reconfigure(comps)
-
-    def change_user(self):
-        '''Change the user and group to those specified on construction.
-
-        This method is expected to be called by a component on initial
-        startup when the system is ready to switch the user and group
-        (i.e., once all components that need the privilege of the original
-        user have started).
-        '''
-        try:
-            if self.__gid is not None:
-                logger.info(BIND10_SETGID, self.__gid)
-                posix.setgid(self.__gid)
-        except Exception as ex:
-            raise ChangeUserError('failed to change group: ' + str(ex))
-
-        try:
-            if self.__uid is not None:
-                posix.setuid(self.__uid)
-                # We use one-shot logger after setuid here.  This will
-                # detect any permission issue regarding logging due to the
-                # result of setuid at the earliest opportunity.
-                isc.log.Logger("b10-init").info(BIND10_SETUID, self.__uid)
-        except Exception as ex:
-            raise ChangeUserError('failed to change user: ' + str(ex))
-
-    def config_handler(self, new_config):
-        # If this is initial update, don't do anything now, leave it to startup
-        if not self.runnable:
-            return
-        logger.debug(DBG_COMMANDS, BIND10_RECEIVED_NEW_CONFIGURATION,
-                     new_config)
-        try:
-            if 'components' in new_config:
-                self.__propagate_component_config(new_config['components'])
-            return isc.config.ccsession.create_answer(0)
-        except Exception as e:
-            logger.error(BIND10_RECONFIGURE_ERROR, e)
-            return isc.config.ccsession.create_answer(1, str(e))
-
-    def get_processes(self):
-        pids = list(self.components.keys())
-        pids.sort()
-        process_list = [ ]
-        for pid in pids:
-            process_list.append([pid, self.components[pid].name(),
-                                 self.components[pid].address()])
-        return process_list
-
-    def _get_stats_data(self):
-        return { 'boot_time':
-                     time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
-                 }
-
-    def command_handler(self, command, args):
-        logger.debug(DBG_COMMANDS, BIND10_RECEIVED_COMMAND, command)
-        answer = isc.config.ccsession.create_answer(1,
-                                                    "command not implemented")
-        if type(command) != str:
-            answer = isc.config.ccsession.create_answer(1, "bad command")
-        else:
-            if command == "shutdown":
-                self.runnable = False
-                answer = isc.config.ccsession.create_answer(0)
-            elif command == "getstats":
-                answer = isc.config.ccsession.create_answer(
-                    0, self._get_stats_data())
-            elif command == "ping":
-                answer = isc.config.ccsession.create_answer(0, "pong")
-            elif command == "show_processes":
-                answer = isc.config.ccsession. \
-                    create_answer(0, self.get_processes())
-            elif command == "get_socket":
-                answer = self._get_socket(args)
-            elif command == "drop_socket":
-                if "token" not in args:
-                    answer = isc.config.ccsession. \
-                        create_answer(1, "Missing token parameter")
-                else:
-                    try:
-                        self._socket_cache.drop_socket(args["token"])
-                        answer = isc.config.ccsession.create_answer(0)
-                    except Exception as e:
-                        answer = isc.config.ccsession.create_answer(1, str(e))
-            else:
-                answer = isc.config.ccsession.create_answer(1,
-                                                            "Unknown command")
-        return answer
-
-    def kill_started_components(self):
-        """
-            Called as part of the exception handling when a process fails to
-            start, this runs through the list of started processes, killing
-            each one.  It then clears that list.
-        """
-        logger.info(BIND10_KILLING_ALL_PROCESSES)
-        self.__kill_children(True)
-        self.components = {}
-
-    def _read_bind10_config(self):
-        """
-            Reads the parameters associated with the Init module itself.
-
-            This means the list of components we should start now.
-
-            This could easily be combined into start_all_processes, but
-            it stays because of historical reasons and because the tests
-            replace the method sometimes.
-        """
-        logger.info(BIND10_READING_INIT_CONFIGURATION)
-
-        config_data = self.ccs.get_full_config()
-        self.__propagate_component_config(config_data['components'])
-
-    def log_starting(self, process, port = None, address = None):
-        """
-            A convenience function to output a "Starting xxx" message if the
-            logging is set to DEBUG with debuglevel DBG_PROCESS or higher.
-            Putting this into a separate method ensures
-            that the output form is consistent across all processes.
-
-            The process name (passed as the first argument) is put into
-            self.curproc, and is used to indicate which process failed to
-            start if there is an error (and is used in the "Started" message
-            on success).  The optional port and address information are
-            appended to the message (if present).
-        """
-        self.curproc = process
-        if port is None and address is None:
-            logger.info(BIND10_STARTING_PROCESS, self.curproc)
-        elif address is None:
-            logger.info(BIND10_STARTING_PROCESS_PORT, self.curproc,
-                        port)
-        else:
-            logger.info(BIND10_STARTING_PROCESS_PORT_ADDRESS,
-                        self.curproc, AddressFormatter((address, port)))
-
-    def log_started(self, pid = None):
-        """
-            A convenience function to output a 'Started xxxx (PID yyyy)'
-            message.  As with starting_message(), this ensures a consistent
-            format.
-        """
-        if pid is None:
-            logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS, self.curproc)
-        else:
-            logger.debug(DBG_PROCESS, BIND10_STARTED_PROCESS_PID, self.curproc,
-                         pid)
-
-    def process_running(self, msg, who):
-        """
-            Some processes return a message to the Init after they have
-            started to indicate that they are running.  The form of the
-            message is a dictionary with contents {"running:", "<process>"}.
-            This method checks the passed message and returns True if the
-            "who" process is contained in the message (so is presumably
-            running).  It returns False for all other conditions and will
-            log an error if appropriate.
-        """
-        if msg is not None:
-            try:
-                if msg["running"] == who:
-                    return True
-                else:
-                    logger.error(BIND10_STARTUP_UNEXPECTED_MESSAGE, msg)
-            except:
-                logger.error(BIND10_STARTUP_UNRECOGNISED_MESSAGE, msg)
-
-        return False
-
-    # The next few methods start the individual processes of BIND-10.  They
-    # are called via start_all_processes().  If any fail, an exception is
-    # raised which is caught by the caller of start_all_processes(); this kills
-    # processes started up to that point before terminating the program.
-
-    def _make_process_info(self, name, args, env,
-                           dev_null_stdout=False, dev_null_stderr=False):
-        """
-            Wrapper around ProcessInfo(), useful to override
-            ProcessInfo() creation during testing.
-        """
-        return ProcessInfo(name, args, env, dev_null_stdout, dev_null_stderr)
-
-    def start_msgq(self):
-        """
-            Start the message queue and connect to the command channel.
-        """
-        self.log_starting("b10-msgq")
-        msgq_proc = self._make_process_info("b10-msgq", ["b10-msgq"],
-                                            self.c_channel_env,
-                                            not self.verbose, not self.verbose)
-        msgq_proc.spawn()
-        self.log_started(msgq_proc.pid)
-
-        # Now connect to the c-channel
-        cc_connect_start = time.time()
-        while self.cc_session is None:
-            # if we are run under unittests, break
-            if self._run_under_unittests:
-                break
-
-            # if we have been trying for "a while" give up
-            if (time.time() - cc_connect_start) > self.msgq_timeout:
-                if msgq_proc.process:
-                    msgq_proc.process.kill()
-                logger.error(BIND10_CONNECTING_TO_CC_FAIL)
-                raise CChannelConnectError("Unable to connect to c-channel " +
-                                           "after 5 seconds")
-
-            # try to connect, and if we can't wait a short while
-            try:
-                self.cc_session = isc.cc.Session(self.msgq_socket_file)
-            except isc.cc.session.SessionError:
-                time.sleep(0.1)
-
-        # Subscribe to the message queue.  The only messages we expect to
-        # receive on this channel are once relating to process startup.
-        if self.cc_session is not None:
-            self.cc_session.group_subscribe("Init")
-
-        return msgq_proc
-
-    def wait_msgq(self):
-        """
-            Wait for the message queue to fully start. It does so only after
-            the config manager connects to it. We know it is ready when it
-            starts answering commands.
-
-            We don't add a specific command for it here, an error response is
-            as good as positive one to know it is alive.
-        """
-        # We do 10 times shorter sleep here (since the start should be fast
-        # now), so we have 10 times more attempts.
-        time_remaining = self.wait_time * 10
-        retry = True
-        while time_remaining > 0 and retry:
-            try:
-                self.ccs.rpc_call('AreYouThere?', 'Msgq')
-                # We don't expect this to succeed. If it does, it's programmer
-                # error
-                raise Exception("Non-existing RPC call succeeded")
-            except isc.config.RPCRecipientMissing:
-                retry = True # Not there yet
-                time.sleep(0.1)
-                time_remaining -= 1
-            except isc.config.RPCError:
-                retry = False # It doesn't like the RPC, so it's alive now
-
-        if retry: # Still not started
-            raise ProcessStartError("Msgq didn't complete the second stage " +
-                                    "of startup")
-
-    def start_cfgmgr(self):
-        """
-            Starts the configuration manager process
-        """
-        self.log_starting("b10-cfgmgr")
-        args = ["b10-cfgmgr"]
-        if self.data_path is not None:
-            args.append("--data-path=" + self.data_path)
-        if self.config_filename is not None:
-            args.append("--config-filename=" + self.config_filename)
-        if self.clear_config:
-            args.append("--clear-config")
-        bind_cfgd = self._make_process_info("b10-cfgmgr", args,
-                                            self.c_channel_env)
-        bind_cfgd.spawn()
-        self.log_started(bind_cfgd.pid)
-
-        # Wait for the configuration manager to start up as
-        # subsequent initialization cannot proceed without it.  The
-        # time to wait can be set on the command line.
-        time_remaining = self.wait_time
-        msg, env = self.cc_session.group_recvmsg()
-        while time_remaining > 0 and not self.process_running(msg,
-                                                              "ConfigManager"):
-            logger.debug(DBG_PROCESS, BIND10_WAIT_CFGMGR)
-            time.sleep(1)
-            time_remaining = time_remaining - 1
-            msg, env = self.cc_session.group_recvmsg()
-
-        if not self.process_running(msg, "ConfigManager"):
-            raise ProcessStartError("Configuration manager process has not " +
-                                    "started")
-
-        return bind_cfgd
-
-    def start_ccsession(self, c_channel_env):
-        """
-            Start the CC Session
-
-            The argument c_channel_env is unused but is supplied to keep the
-            argument list the same for all start_xxx methods.
-
-            With regards to logging, note that as the CC session is not a
-            process, the log_starting/log_started methods are not used.
-        """
-        logger.info(BIND10_STARTING_CC)
-
-        # Unsubscribe from the other CC session first, because we only
-        # monitor one and msgq expects all data sent to us to be read,
-        # or it will close its side of the socket.
-        if self.cc_session is not None:
-            self.cc_session.group_unsubscribe("Init")
-
-        self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
-                                      self.config_handler,
-                                      self.command_handler,
-                                      socket_file = self.msgq_socket_file)
-        self.ccs.start()
-        logger.debug(DBG_PROCESS, BIND10_STARTED_CC)
-
-    # A couple of utility methods for starting processes...
-
-    def start_process(self, name, args, c_channel_env, port=None,
-                      address=None):
-        """
-            Given a set of command arguments, start the process and output
-            appropriate log messages.  If the start is successful, the process
-            is added to the list of started processes.
-
-            The port and address arguments are for log messages only.
-        """
-        self.log_starting(name, port, address)
-        newproc = self._make_process_info(name, args, c_channel_env)
-        newproc.spawn()
-        self.log_started(newproc.pid)
-        return newproc
-
-    def register_process(self, pid, component):
-        """
-        Put another process into b10-init to watch over it.  When the process
-        dies, the component.failed() is called with the exit code.
-
-        It is expected the info is a isc.bind10.component.BaseComponent
-        subclass (or anything having the same interface).
-        """
-        self.components[pid] = component
-
-    def start_simple(self, name):
-        """
-            Most of the BIND-10 processes are started with the command:
-
-                <process-name> [-v]
-
-            ... where -v is appended if verbose is enabled.  This method
-            generates the arguments from the name and starts the process.
-
-            The port and address arguments are for log messages only.
-        """
-        # Set up the command arguments.
-        args = [name]
-        if self.verbose:
-            args += ['-v']
-
-        # ... and start the process
-        return self.start_process(name, args, self.c_channel_env)
-
-    # The next few methods start up the rest of the BIND-10 processes.
-    # Although many of these methods are little more than a call to
-    # start_simple, they are retained (a) for testing reasons and (b) as a
-    # place where modifications can be made if the process start-up sequence
-    # changes for a given process.
-
-    def start_auth(self):
-        """
-            Start the Authoritative server
-        """
-        authargs = ['b10-auth']
-        if self.verbose:
-            authargs += ['-v']
-
-        # ... and start
-        return self.start_process("b10-auth", authargs, self.c_channel_env)
-
-    def start_resolver(self):
-        """
-            Start the Resolver.  At present, all these arguments and switches
-            are pure speculation.  As with the auth daemon, they should be
-            read from the configuration database.
-        """
-        self.curproc = "b10-resolver"
-        # XXX: this must be read from the configuration manager in the future
-        resargs = ['b10-resolver']
-        if self.verbose:
-            resargs += ['-v']
-
-        # ... and start
-        return self.start_process("b10-resolver", resargs, self.c_channel_env)
-
-    def start_cmdctl(self):
-        """
-            Starts the command control process
-        """
-        args = ["b10-cmdctl"]
-        if self.cmdctl_port is not None:
-            args.append("--port=" + str(self.cmdctl_port))
-        if self.verbose:
-            args.append("-v")
-        return self.start_process("b10-cmdctl", args, self.c_channel_env,
-                                  self.cmdctl_port)
-
-    def start_all_components(self):
-        """
-            Starts up all the components.  Any exception generated during the
-            starting of the components is handled by the caller.
-        """
-        # Start the real core (sockcreator, msgq, cfgmgr)
-        self._component_configurator.startup(self.__core_components)
-
-        # Connect to the msgq. This is not a process, so it's not handled
-        # inside the configurator.
-        self.start_ccsession(self.c_channel_env)
-
-        # Make sure msgq is fully started before proceeding to the rest
-        # of the components.
-        self.wait_msgq()
-
-        # Extract the parameters associated with Init.  This can only be
-        # done after the CC Session is started.  Note that the logging
-        # configuration may override the "-v" switch set on the command line.
-        self._read_bind10_config()
-
-        # TODO: Return the dropping of privileges
-
-    def startup(self):
-        """
-            Start the Init instance.
-
-            Returns None if successful, otherwise an string describing the
-            problem.
-        """
-        # Try to connect to the c-channel daemon, to see if it is already
-        # running
-        c_channel_env = {}
-        if self.msgq_socket_file is not None:
-             c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file
-        logger.debug(DBG_PROCESS, BIND10_CHECK_MSGQ_ALREADY_RUNNING)
-        try:
-            self.cc_session = isc.cc.Session(self.msgq_socket_file)
-            logger.fatal(BIND10_MSGQ_ALREADY_RUNNING)
-            if self.msgq_socket_file is not None:
-              socket_name = "socket file '" + self.msg_socket_file + "'"
-            else:
-              socket_name = "default socket file"
-            return "b10-msgq already running, or " + socket_name +\
-                " not cleaned - cannot start"
-        except isc.cc.session.SessionError:
-            # this is the case we want, where the msgq is not running
-            pass
-
-        # Start all components.  If any one fails to start, kill all started
-        # components and exit with an error indication.
-        try:
-            self.c_channel_env = c_channel_env
-            self.start_all_components()
-        except ChangeUserError as e:
-            self.kill_started_components()
-            return str(e) + '; ' + NOTE_ON_LOCK_FILE.replace('\n', ' ')
-        except Exception as e:
-            self.kill_started_components()
-            return "Unable to start " + self.curproc + ": " + str(e)
-
-        # Started successfully
-        self.runnable = True
-        self.__started = True
-        return None
-
-    def stop_process(self, process, recipient, pid):
-        """
-        Stop the given process, friendly-like. The process is the name it has
-        (in logs, etc), the recipient is the address on msgq. The pid is the
-        pid of the process (if we have multiple processes of the same name,
-        it might want to choose if it is for this one).
-        """
-        logger.info(BIND10_STOP_PROCESS, process)
-        try:
-            self.cc_session.group_sendmsg(isc.config.ccsession.
-                                          create_command('shutdown',
-                                                         {'pid': pid}),
-                                          recipient, recipient)
-        except:
-            logger.error(BIND10_COMPONENT_SHUTDOWN_ERROR, process)
-            raise
-
-    def component_shutdown(self, exitcode=0):
-        """
-        Stop the Init instance from a components' request. The exitcode
-        indicates the desired exit code.
-
-        If we did not start yet, it raises an exception, which is meant
-        to propagate through the component and configurator to the startup
-        routine and abort the startup immediately. If it is started up already,
-        we just mark it so we terminate soon.
-
-        It does set the exit code in both cases.
-        """
-        self.exitcode = exitcode
-        if not self.__started:
-            raise Exception("Component failed during startup");
-        else:
-            self.runnable = False
-
-    def shutdown(self):
-        """Stop the Init instance."""
-        logger.info(BIND10_SHUTDOWN)
-        # If ccsession is still there, inform rest of the system this module
-        # is stopping. Since everything will be stopped shortly, this is not
-        # really necessary, but this is done to reflect that b10-init is also
-        # 'just' a module.
-        self.ccs.send_stopping()
-
-        # try using the BIND 10 request to stop
-        try:
-            self._component_configurator.shutdown()
-        except:
-            pass
-        # XXX: some delay probably useful... how much is uncertain
-        # I have changed the delay from 0.5 to 1, but sometime it's
-        # still not enough.
-        time.sleep(1)
-        self.reap_children()
-
-        # Send TERM and KILL signals to modules if we're not prevented
-        # from doing so
-        if not self.nokill:
-            # next try sending a SIGTERM
-            self.__kill_children(False)
-            # finally, send SIGKILL (unmaskable termination) until everybody
-            # dies
-            while self.components:
-                # XXX: some delay probably useful... how much is uncertain
-                time.sleep(0.1)
-                self.reap_children()
-                self.__kill_children(True)
-            logger.info(BIND10_SHUTDOWN_COMPLETE)
-
-    def __kill_children(self, forceful):
-        '''Terminate remaining subprocesses by sending a signal.
-
-        The forceful paramter will be passed Component.kill().
-        This is a dedicated subroutine of shutdown(), just to unify two
-        similar cases.
-
-        '''
-        logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM
-        # We need to make a copy of values as the components may be modified
-        # in the loop.
-        for component in list(self.components.values()):
-            logger.info(logmsg, component.name(), component.pid())
-            try:
-                component.kill(forceful)
-            except OSError as ex:
-                # If kill() failed due to EPERM, it doesn't make sense to
-                # keep trying, so we just log the fact and forget that
-                # component.  Ignore other OSErrors (usually ESRCH because
-                # the child finally exited)
-                signame = "SIGKILL" if forceful else "SIGTERM"
-                logger.info(BIND10_SEND_SIGNAL_FAIL, signame,
-                            component.name(), component.pid(), ex)
-                if ex.errno == errno.EPERM:
-                    del self.components[component.pid()]
-
-    def _get_process_exit_status(self):
-        return os.waitpid(-1, os.WNOHANG)
-
-    def reap_children(self):
-        """Check to see if any of our child processes have exited,
-        and note this for later handling.
-        """
-        while True:
-            try:
-                (pid, exit_status) = self._get_process_exit_status()
-            except OSError as o:
-                if o.errno == errno.ECHILD:
-                    break
-                # XXX: should be impossible to get any other error here
-                raise
-            if pid == 0:
-                break
-            if pid in self.components:
-                # One of the components we know about.  Get information on it.
-                component = self.components.pop(pid)
-                logger.info(BIND10_PROCESS_ENDED, component.name(), pid,
-                            exit_status)
-                if component.is_running() and self.runnable:
-                    # Tell it it failed. But only if it matters (we are
-                    # not shutting down and the component considers itself
-                    # to be running.
-                    component_restarted = component.failed(exit_status);
-                    # if the process wants to be restarted, but not just yet,
-                    # it returns False
-                    if not component_restarted:
-                        self.components_to_restart.append(component)
-            else:
-                logger.info(BIND10_UNKNOWN_CHILD_PROCESS_ENDED, pid)
-
-    def restart_processes(self):
-        """
-            Restart any dead processes:
-
-            * Returns the time when the next process is ready to be restarted.
-            * If the server is shutting down, returns 0.
-            * If there are no processes, returns None.
-
-            The values returned can be safely passed into select() as the
-            timeout value.
-
-        """
-        if not self.runnable:
-            return 0
-        still_dead = []
-        # keep track of the first time we need to check this queue again,
-        # if at all
-        next_restart_time = None
-        now = time.time()
-        for component in self.components_to_restart:
-            # If the component was removed from the configurator between since
-            # scheduled to restart, just ignore it.  The object will just be
-            # dropped here.
-            if not self._component_configurator.has_component(component):
-                logger.info(BIND10_RESTART_COMPONENT_SKIPPED, component.name())
-            elif not component.restart(now):
-                still_dead.append(component)
-                if next_restart_time is None or\
-                   next_restart_time > component.get_restart_time():
-                    next_restart_time = component.get_restart_time()
-        self.components_to_restart = still_dead
-
-        return next_restart_time
-
-    def _get_socket(self, args):
-        """
-        Implementation of the get_socket CC command. It asks the cache
-        to provide the token and sends the information back.
-        """
-        try:
-            try:
-                addr = isc.net.parse.addr_parse(args['address'])
-                port = isc.net.parse.port_parse(args['port'])
-                protocol = args['protocol']
-                if protocol not in ['UDP', 'TCP']:
-                    raise ValueError("Protocol must be either UDP or TCP")
-                share_mode = args['share_mode']
-                if share_mode not in ['ANY', 'SAMEAPP', 'NO']:
-                    raise ValueError("Share mode must be one of ANY, SAMEAPP" +
-                                     " or NO")
-                share_name = args['share_name']
-            except KeyError as ke:
-                return \
-                    isc.config.ccsession.create_answer(1,
-                                                       "Missing parameter " +
-                                                       str(ke))
-
-            # FIXME: This call contains blocking IPC. It is expected to be
-            # short, but if it turns out to be problem, we'll need to do
-            # something about it.
-            token = self._socket_cache.get_token(protocol, addr, port,
-                                                 share_mode, share_name)
-            return isc.config.ccsession.create_answer(0, {
-                'token': token,
-                'path': self._socket_path
-            })
-        except isc.bind10.socket_cache.SocketError as e:
-            return isc.config.ccsession.create_answer(CREATOR_SOCKET_ERROR,
-                                                      str(e))
-        except isc.bind10.socket_cache.ShareError as e:
-            return isc.config.ccsession.create_answer(CREATOR_SHARE_ERROR,
-                                                      str(e))
-        except Exception as e:
-            return isc.config.ccsession.create_answer(1, str(e))
-
-    def socket_request_handler(self, token, unix_socket):
-        """
-        This function handles a token that comes over a unix_domain socket.
-        The function looks into the _socket_cache and sends the socket
-        identified by the token back over the unix_socket.
-        """
-        try:
-            token = str(token, 'ASCII') # Convert from bytes to str
-            fd = self._socket_cache.get_socket(token, unix_socket.fileno())
-            # FIXME: These two calls are blocking in their nature. An OS-level
-            # buffer is likely to be large enough to hold all these data, but
-            # if it wasn't and the remote application got stuck, we would have
-            # a problem. If there appear such problems, we should do something
-            # about it.
-            unix_socket.sendall(CREATOR_SOCKET_OK)
-            libutil_io_python.send_fd(unix_socket.fileno(), fd)
-        except Exception as e:
-            logger.info(BIND10_NO_SOCKET, token, e)
-            unix_socket.sendall(CREATOR_SOCKET_UNAVAILABLE)
-
-    def socket_consumer_dead(self, unix_socket):
-        """
-        This function handles when a unix_socket closes. This means all
-        sockets sent to it are to be considered closed. This function signals
-        so to the _socket_cache.
-        """
-        logger.info(BIND10_LOST_SOCKET_CONSUMER, unix_socket.fileno())
-        try:
-            self._socket_cache.drop_application(unix_socket.fileno())
-        except ValueError:
-            # This means the application holds no sockets. It's harmless, as it
-            # can happen in real life - for example, it requests a socket, but
-            # get_socket doesn't find it, so the application dies. It should be
-            # rare, though.
-            pass
-
-    def set_creator(self, creator):
-        """
-        Registeres a socket creator into the b10-init. The socket creator is
-        not used directly, but through a cache. The cache is created in this
-        method.
-
-        If called more than once, it raises a ValueError.
-        """
-        if self._socket_cache is not None:
-            raise ValueError("A creator was inserted previously")
-        self._socket_cache = isc.bind10.socket_cache.Cache(creator)
-
-    def init_socket_srv(self):
-        """
-        Creates and listens on a unix-domain socket to be able to send out
-        the sockets.
-
-        This method should be called after switching user, or the switched
-        applications won't be able to access the socket.
-        """
-        self._srv_socket = socket.socket(socket.AF_UNIX)
-        # We create a temporary directory somewhere safe and unique, to avoid
-        # the need to find the place ourself or bother users. Also, this
-        # secures the socket on some platforms, as it creates a private
-        # directory.
-        self._tmpdir = tempfile.mkdtemp(prefix='sockcreator-')
-        # Get the name
-        self._socket_path = os.path.join(self._tmpdir, "sockcreator")
-        # And bind the socket to the name
-        self._srv_socket.bind(self._socket_path)
-        self._srv_socket.listen(5)
-
-    def remove_socket_srv(self):
-        """
-        Closes and removes the listening socket and the directory where it
-        lives, as we created both.
-
-        It does nothing if the _srv_socket is not set (eg. it was not yet
-        initialized).
-        """
-        if self._srv_socket is not None:
-            self._srv_socket.close()
-            if os.path.exists(self._socket_path):
-                os.remove(self._socket_path)
-            if os.path.isdir(self._tmpdir):
-                os.rmdir(self._tmpdir)
-
-    def _srv_accept(self):
-        """
-        Accept a socket from the unix domain socket server and put it to the
-        others we care about.
-        """
-        (socket, conn) = self._srv_socket.accept()
-        self._unix_sockets[socket.fileno()] = (socket, b'')
-
-    def _socket_data(self, socket_fileno):
-        """
-        This is called when a socket identified by the socket_fileno needs
-        attention. We try to read data from there. If it is closed, we remove
-        it.
-        """
-        (sock, previous) = self._unix_sockets[socket_fileno]
-        while True:
-            try:
-                data = sock.recv(1, socket.MSG_DONTWAIT)
-            except socket.error as se:
-                # These two might be different on some systems
-                if se.errno == errno.EAGAIN or se.errno == errno.EWOULDBLOCK:
-                    # No more data now. Oh, well, just store what we have.
-                    self._unix_sockets[socket_fileno] = (sock, previous)
-                    return
-                else:
-                    data = b'' # Pretend it got closed
-            if len(data) == 0: # The socket got to it's end
-                del self._unix_sockets[socket_fileno]
-                self.socket_consumer_dead(sock)
-                sock.close()
-                return
-            else:
-                if data == b"\n":
-                    # Handle this token and clear it
-                    self.socket_request_handler(previous, sock)
-                    previous = b''
-                else:
-                    previous += data
-
-    def run(self, wakeup_fd):
-        """
-        The main loop, waiting for sockets, commands and dead processes.
-        Runs as long as the runnable is true.
-
-        The wakeup_fd descriptor is the read end of pipe where CHLD signal
-        handler writes.
-        """
-        ccs_fd = self.ccs.get_socket().fileno()
-        while self.runnable:
-            # clean up any processes that exited
-            self.reap_children()
-            next_restart = self.restart_processes()
-            if next_restart is None:
-                wait_time = None
-            else:
-                wait_time = max(next_restart - time.time(), 0)
-
-            # select() can raise EINTR when a signal arrives,
-            # even if they are resumable, so we have to catch
-            # the exception
-            try:
-                (rlist, wlist, xlist) = \
-                    select.select([wakeup_fd, ccs_fd,
-                                   self._srv_socket.fileno()] +
-                                   list(self._unix_sockets.keys()), [], [],
-                                  wait_time)
-            except select.error as err:
-                if err.args[0] == errno.EINTR:
-                    (rlist, wlist, xlist) = ([], [], [])
-                else:
-                    logger.fatal(BIND10_SELECT_ERROR, err)
-                    break
-
-            for fd in rlist + xlist:
-                if fd == ccs_fd:
-                    try:
-                        self.ccs.check_command()
-                    except isc.cc.session.ProtocolError:
-                        logger.fatal(BIND10_MSGQ_DISAPPEARED)
-                        self.runnable = False
-                        break
-                elif fd == wakeup_fd:
-                    os.read(wakeup_fd, 32)
-                elif fd == self._srv_socket.fileno():
-                    self._srv_accept()
-                elif fd in self._unix_sockets:
-                    self._socket_data(fd)
-
-# global variables, needed for signal handlers
-options = None
-b10_init = None
-
-def reaper(signal_number, stack_frame):
-    """A child process has died (SIGCHLD received)."""
-    # don't do anything...
-    # the Python signal handler has been set up to write
-    # down a pipe, waking up our select() bit
-    pass
-
-def get_signame(signal_number):
-    """Return the symbolic name for a signal."""
-    for sig in dir(signal):
-        if sig.startswith("SIG") and sig[3].isalnum():
-            if getattr(signal, sig) == signal_number:
-                return sig
-    return "Unknown signal %d" % signal_number
-
-# XXX: perhaps register atexit() function and invoke that instead
-def fatal_signal(signal_number, stack_frame):
-    """We need to exit (SIGINT or SIGTERM received)."""
-    global options
-    global b10_init
-    logger.info(BIND10_RECEIVED_SIGNAL, get_signame(signal_number))
-    signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-    b10_init.runnable = False
-
-def process_rename(option, opt_str, value, parser):
-    """Function that renames the process if it is requested by a option."""
-    isc.util.process.rename(value)
-
-def parse_args(args=sys.argv[1:], Parser=OptionParser):
-    """
-    Function for parsing command line arguments. Returns the
-    options object from OptionParser.
-    """
-    parser = Parser(version=VERSION)
-    parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
-                      type="string", default=None,
-                      help="UNIX domain socket file the b10-msgq daemon " +
-                      "will use")
-    parser.add_option("-i", "--no-kill", action="store_true", dest="nokill",
-                      default=False,
-                      help="do not send SIGTERM and SIGKILL signals to " +
-                      "modules during shutdown")
-    parser.add_option("-u", "--user", dest="user", type="string", default=None,
-                      help="Change user after startup (must run as root)")
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
-                      help="display more about what is going on")
-    parser.add_option("--pretty-name", type="string", action="callback",
-                      callback=process_rename,
-                      help="Set the process name (displayed in ps, top, ...)")
-    parser.add_option("-c", "--config-file", action="store",
-                      dest="config_file", default=None,
-                      help="Configuration database filename")
-    parser.add_option("--clear-config", action="store_true",
-                      dest="clear_config", default=False,
-                      help="Create backup of the configuration file and " +
-                           "start with a clean configuration")
-    parser.add_option("-p", "--data-path", dest="data_path",
-                      help="Directory to search for configuration files",
-                      default=None)
-    parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
-                      default=None, help="Port of command control")
-    parser.add_option("--pid-file", dest="pid_file", type="string",
-                      default=None,
-                      help="file to dump the PID of the BIND 10 process")
-    parser.add_option("-w", "--wait", dest="wait_time", type="int",
-                      default=10,
-                      help="Time (in seconds) to wait for config manager to "
-                      "start up")
-
-    (options, args) = parser.parse_args(args)
-
-    if options.cmdctl_port is not None:
-        try:
-            isc.net.parse.port_parse(options.cmdctl_port)
-        except ValueError as e:
-            parser.error(e)
-
-    if args:
-        parser.print_help()
-        sys.exit(1)
-
-    return options
-
-def dump_pid(pid_file):
-    """
-    Dump the PID of the current process to the specified file.  If the given
-    file is None this function does nothing.  If the file already exists,
-    the existing content will be removed.  If a system error happens in
-    creating or writing to the file, the corresponding exception will be
-    propagated to the caller.
-    """
-    if pid_file is None:
-        return
-    f = open(pid_file, "w")
-    f.write('%d\n' % os.getpid())
-    f.close()
-
-def unlink_pid_file(pid_file):
-    """
-    Remove the given file, which is basically expected to be the PID file
-    created by dump_pid().  The specified may or may not exist; if it
-    doesn't this function does nothing.  Other system level errors in removing
-    the file will be propagated as the corresponding exception.
-    """
-    if pid_file is None:
-        return
-    try:
-        os.unlink(pid_file)
-    except OSError as error:
-        if error.errno is not errno.ENOENT:
-            raise
-
-def remove_lock_files():
-    """
-    Remove various lock files which were created by code such as in the
-    logger. This function should be called after BIND 10 shutdown.
-    """
-
-    lockfiles = ["logger_lockfile"]
-
-    lpath = bind10_config.DATA_PATH
-    if "B10_FROM_BUILD" in os.environ:
-        lpath = os.environ["B10_FROM_BUILD"]
-    if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
-        lpath = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
-    if "B10_LOCKFILE_DIR_FROM_BUILD" in os.environ:
-        lpath = os.environ["B10_LOCKFILE_DIR_FROM_BUILD"]
-
-    for f in lockfiles:
-        fname = lpath + '/' + f
-        if os.path.isfile(fname):
-            try:
-                os.unlink(fname)
-            except OSError as e:
-                # We catch and ignore permission related error on unlink.
-                # This can happen if bind10 started with -u, created a lock
-                # file as a privileged user, but the directory is not writable
-                # for the changed user.  This setup will cause immediate
-                # start failure, and we leave verbose error message including
-                # the leftover lock file, so it should be acceptable to ignore
-                # it (note that it doesn't make sense to log this event at
-                # this poitn)
-                if e.errno != errno.EPERM and e.errno != errno.EACCES:
-                    raise
-
-    return
-
-def main():
-    global options
-    global b10_init
-    # Enforce line buffering on stdout, even when not a TTY
-    sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
-
-    options = parse_args()
-
-    # Announce startup.  Making this is the first log message.
-    try:
-        logger.info(BIND10_STARTING, VERSION)
-    except RuntimeError as e:
-        sys.stderr.write('ERROR: failed to write the initial log: %s\n' %
-                         str(e))
-        sys.stderr.write(NOTE_ON_LOCK_FILE)
-        sys.exit(1)
-
-    # Check user ID.
-    setuid = None
-    setgid = None
-    username = None
-    if options.user:
-        # Try getting information about the user, assuming UID passed.
-        try:
-            pw_ent = pwd.getpwuid(int(options.user))
-            setuid = pw_ent.pw_uid
-            setgid = pw_ent.pw_gid
-            username = pw_ent.pw_name
-        except ValueError:
-            pass
-        except KeyError:
-            pass
-
-        # Next try getting information about the user, assuming user name
-        # passed.
-        # If the information is both a valid user name and user number, we
-        # prefer the name because we try it second. A minor point, hopefully.
-        try:
-            pw_ent = pwd.getpwnam(options.user)
-            setuid = pw_ent.pw_uid
-            setgid = pw_ent.pw_gid
-            username = pw_ent.pw_name
-        except KeyError:
-            pass
-
-        if setuid is None:
-            logger.fatal(BIND10_INVALID_USER, options.user)
-            sys.exit(1)
-
-    # Create wakeup pipe for signal handlers
-    wakeup_pipe = os.pipe()
-    signal.set_wakeup_fd(wakeup_pipe[1])
-
-    # Set signal handlers for catching child termination, as well
-    # as our own demise.
-    signal.signal(signal.SIGCHLD, reaper)
-    signal.siginterrupt(signal.SIGCHLD, False)
-    signal.signal(signal.SIGINT, fatal_signal)
-    signal.signal(signal.SIGTERM, fatal_signal)
-
-    # Block SIGPIPE, as we don't want it to end this process
-    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-
-    try:
-        b10_init = Init(options.msgq_socket_file, options.data_path,
-                        options.config_file, options.clear_config,
-                        options.verbose, options.nokill,
-                        setuid, setgid, username, options.cmdctl_port,
-                        options.wait_time)
-        startup_result = b10_init.startup()
-        if startup_result:
-            logger.fatal(BIND10_STARTUP_ERROR, startup_result)
-            sys.exit(1)
-        b10_init.init_socket_srv()
-        logger.info(BIND10_STARTUP_COMPLETE)
-        dump_pid(options.pid_file)
-
-        # Let it run
-        b10_init.run(wakeup_pipe[0])
-
-        # shutdown
-        signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-        b10_init.shutdown()
-    finally:
-        # Clean up the filesystem
-        unlink_pid_file(options.pid_file)
-        remove_lock_files()
-        if b10_init is not None:
-            b10_init.remove_socket_srv()
-    sys.exit(b10_init.exitcode)
-
-if __name__ == "__main__":
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/bind10/init.spec b/src/bin/bind10/init.spec
deleted file mode 100644 (file)
index 62c6f09..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "Init",
-    "module_description": "Init process",
-    "config_data": [
-      {
-        "item_name": "components",
-        "item_type": "named_set",
-        "item_optional": false,
-        "item_default": {
-          "b10-stats": { "address": "Stats", "kind": "dispensable" },
-          "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
-        },
-        "named_set_item_spec": {
-          "item_name": "component",
-          "item_type": "map",
-          "item_optional": false,
-          "item_default": { },
-          "map_item_spec": [
-            {
-              "item_name": "special",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "process",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "kind",
-              "item_optional": false,
-              "item_type": "string",
-              "item_default": "dispensable"
-            },
-            {
-              "item_name": "address",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "params",
-              "item_optional": true,
-              "item_type": "list",
-              "list_item_spec": {
-                "item_name": "param",
-                "item_optional": false,
-                "item_type": "string",
-                "item_default": ""
-              }
-            },
-            {
-              "item_name": "priority",
-              "item_optional": true,
-              "item_type": "integer"
-            }
-          ]
-        }
-      }
-    ],
-    "commands": [
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down BIND 10",
-        "command_args": []
-      },
-      {
-        "command_name": "ping",
-        "command_description": "Ping the b10-init process",
-        "command_args": []
-      },
-      {
-        "command_name": "show_processes",
-        "command_description": "List the running BIND 10 processes",
-        "command_args": []
-      }
-    ],
-    "statistics": [
-      {
-        "item_name": "boot_time",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "1970-01-01T00:00:00Z",
-        "item_title": "Boot time",
-        "item_description": "A date time when bind10 process starts initially",
-        "item_format": "date-time"
-      }
-    ]
-  }
-}
-
-
diff --git a/src/bin/bind10/init_messages.mes b/src/bin/bind10/init_messages.mes
deleted file mode 100644 (file)
index a3e3413..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-# No namespace declaration - these constants go in the global namespace
-# of the xfrin messages python module.
-
-% BIND10_CHECK_MSGQ_ALREADY_RUNNING checking if msgq is already running
-The b10-init process is starting up and will now check if the message bus
-daemon is already running. If so, it will not be able to start, as it
-needs a dedicated message bus.
-
-% BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3
-The process terminated, but b10-init didn't expect it to, which means
-it must have failed.
-
-% BIND10_COMPONENT_RESTART component %1 is about to restart
-The named component failed previously and we will try to restart it to provide
-as flawless service as possible, but it should be investigated what happened,
-as it could happen again.
-
-% BIND10_COMPONENT_SHUTDOWN_ERROR An error occurred stopping component %1
-An attempt to gracefully shutdown a component failed.
-
-% BIND10_COMPONENT_START component %1 is starting
-The named component is about to be started by the b10-init process.
-
-% BIND10_COMPONENT_START_EXCEPTION component %1 failed to start: %2
-An exception (mentioned in the message) happened during the startup of the
-named component. The component is not considered started and further actions
-will be taken about it.
-
-% BIND10_COMPONENT_STOP component %1 is being stopped
-A component is about to be asked to stop willingly by the b10-init.
-
-% BIND10_COMPONENT_UNSATISFIED component %1 is required to run and failed
-A component failed for some reason (see previous messages). It is either a core
-component or needed component that was just started. In any case, the system
-can't continue without it and will terminate.
-
-% BIND10_CONFIGURATOR_BUILD building plan '%1' -> '%2'
-A debug message. This indicates that the configurator is building a plan
-how to change configuration from the older one to newer one. This does no
-real work yet, it just does the planning what needs to be done.
-
-% BIND10_CONFIGURATOR_PLAN_INTERRUPTED configurator plan interrupted, only %1 of %2 done
-There was an exception during some planned task. The plan will not continue and
-only some tasks of the plan were completed. The rest is aborted. The exception
-will be propagated.
-
-% BIND10_CONFIGURATOR_RECONFIGURE reconfiguring running components
-A different configuration of which components should be running is being
-installed. All components that are no longer needed will be stopped and
-newly introduced ones started. This happens at startup, when the configuration
-is read the first time, or when an operator changes configuration of the b10-init.
-
-% BIND10_CONFIGURATOR_RUN running plan of %1 tasks
-A debug message. The configurator is about to execute a plan of actions it
-computed previously.
-
-% BIND10_CONFIGURATOR_START bind10 component configurator is starting up
-The part that cares about starting and stopping the right component from
-the b10-init process is starting up. This happens only once at the startup
-of the b10-init process. It will start the basic set of processes now (the
-ones b10-init needs to read the configuration), the rest will be started
-after the configuration is known.
-
-% BIND10_CONFIGURATOR_STOP bind10 component configurator is shutting down
-The part that cares about starting and stopping processes in the b10-init is
-shutting down. All started components will be shut down now (more precisely,
-asked to terminate by their own, if they fail to comply, other parts of
-the b10-init process will try to force them).
-
-% BIND10_CONFIGURATOR_TASK performing task %1 on %2
-A debug message. The configurator is about to perform one task of the plan it
-is currently executing on the named component.
-
-% BIND10_CONNECTING_TO_CC_FAIL failed to connect to configuration/command channel; try -v to see output from msgq
-The b10-init process tried to connect to the communication channel for
-commands and configuration updates during initialization, but it
-failed.  This is a fatal startup error, and process will soon
-terminate after some cleanup.  There can be several reasons for the
-failure, but the most likely cause is that the msgq daemon failed to
-start, and the most likely cause of the msgq failure is that it
-doesn't have a permission to create a socket file for the
-communication.  To confirm that, you can see debug messages from msgq
-by starting BIND 10 with the -v command line option.  If it indicates
-permission problem for msgq, make sure the directory where the socket
-file is to be created is writable for the msgq process.  Note that if
-you specify the -u option to change process users, the directory must
-be writable for that user.
-
-% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified
-An error was encountered when the b10-init module specified
-statistics data which is invalid for the b10-init specification file.
-
-% BIND10_INVALID_USER invalid user: %1
-The b10-init process was started with the -u option, to drop root privileges
-and continue running as the specified user, but the user is unknown.
-
-% BIND10_KILLING_ALL_PROCESSES killing all started processes
-The b10-init module was not able to start every process it needed to start
-during startup, and will now kill the processes that did get started.
-
-% BIND10_LOST_SOCKET_CONSUMER consumer %1 of sockets disconnected, considering all its sockets closed
-A connection from one of the applications which requested a socket was
-closed. This means the application has terminated, so all the sockets it was
-using are now closed and bind10 process can release them as well, unless the
-same sockets are used by yet another application.
-
-% BIND10_MSGQ_ALREADY_RUNNING msgq daemon already running, cannot start
-There already appears to be a message bus daemon running. Either an
-old process was not shut down correctly, and needs to be killed, or
-another instance of BIND10, with the same msgq domain socket, is
-running, which needs to be stopped.
-
-% BIND10_MSGQ_DISAPPEARED msgq channel disappeared
-While listening on the message bus channel for messages, it suddenly
-disappeared. The msgq daemon may have died. This might lead to an
-inconsistent state of the system, and BIND 10 will now shut down.
-
-% BIND10_NO_SOCKET couldn't send a socket for token %1 because of error: %2
-An error occurred when the bind10 process was asked to send a socket file
-descriptor. The error is mentioned, most common reason is that the request
-is invalid and may not come from bind10 process at all.
-
-% BIND10_PROCESS_ENDED process %2 of %1 ended with status %3
-This indicates a process started previously terminated. The process id
-and component owning the process are indicated, as well as the exit code.
-This doesn't distinguish if the process was supposed to terminate or not.
-
-% BIND10_READING_INIT_CONFIGURATION reading b10-init configuration
-The b10-init process is starting up, and will now process the initial
-configuration, as received from the configuration manager.
-
-% BIND10_RECEIVED_COMMAND received command: %1
-The b10-init module received a command and shall now process it. The command
-is printed.
-
-% BIND10_RECEIVED_NEW_CONFIGURATION received new configuration: %1
-The b10-init module received a configuration update and is going to apply
-it now. The new configuration is printed.
-
-% BIND10_RECEIVED_SIGNAL received signal %1
-The b10-init module received the given signal.
-
-% BIND10_RECONFIGURE_ERROR Error applying new config: %1
-A new configuration was received, but there was an error doing the
-re-configuration.
-
-% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1
-The b10-init module tried to restart a component after it failed (crashed)
-unexpectedly, but the b10-init then found that the component had been removed
-from its local configuration of components to run.  This is an unusual
-situation but can happen if the administrator removes the component from
-the configuration after the component's crash and before the restart time.
-The b10-init module simply skipped restarting that module, and the whole system
-went back to the expected state (except that the crash itself is likely
-to be a bug).
-
-% BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2)
-The given process has been restarted successfully, and is now running
-with the given process id.
-
-% BIND10_RESURRECTING_PROCESS resurrecting dead %1 process...
-The given process has ended unexpectedly, and is now restarted.
-
-% BIND10_SELECT_ERROR error in select() call: %1
-There was a fatal error in the call to select(), used to see if a child
-process has ended or if there is a message on the message bus. This
-should not happen under normal circumstances and is considered fatal,
-so BIND 10 will now shut down. The specific error is printed.
-
-% BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2)
-The b10-init module is sending a SIGKILL signal to the given process.
-
-% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4
-The b10-init module sent a single (either SIGTERM or SIGKILL) to a process,
-but it failed due to some system level error.  There are two major cases:
-the target process has already terminated but the b10-init module had sent
-the signal before it noticed the termination.  In this case an error
-message should indicate something like "no such process".  This can be
-safely ignored.  The other case is that the b10-init module doesn't have
-the privilege to send a signal to the process.  It can typically
-happen when the b10-init module started as a privileged process, spawned a
-subprocess, and then dropped the privilege.  It includes the case for
-the socket creator when the b10-init process runs with the -u command line
-option.  In this case, the b10-init module simply gives up to terminate
-the process explicitly because it's unlikely to succeed by keeping
-sending the signal.  Although the socket creator is implemented so
-that it will terminate automatically when the b10-init process exits
-(and that should be the case for any other future process running with
-a higher privilege), but it's recommended to check if there's any
-remaining BIND 10 process if this message is logged.  For all other
-cases, the b10-init module will keep sending the signal until it confirms
-all child processes terminate.  Although unlikely, this could prevent
-the b10-init module from exiting, just keeping sending the signals.  So,
-again, it's advisable to check if it really terminates when this
-message is logged.
-
-% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
-The b10-init module is sending a SIGTERM signal to the given process.
-
-% BIND10_SETGID setting GID to %1
-The b10-init switches the process group ID to the given value.  This happens
-when BIND 10 starts with the -u option, and the group ID will be set to
-that of the specified user.
-
-% BIND10_SETUID setting UID to %1
-The b10-init switches the user it runs as to the given UID.
-
-% BIND10_SHUTDOWN stopping the server
-The b10-init process received a command or signal telling it to shut down.
-It will send a shutdown command to each process. The processes that do
-not shut down will then receive a SIGTERM signal. If that doesn't work,
-it shall send SIGKILL signals to the processes still alive.
-
-% BIND10_SHUTDOWN_COMPLETE all processes ended, shutdown complete
-All child processes have been stopped, and the b10-init process will now
-stop itself.
-
-% BIND10_SOCKCREATOR_BAD_CAUSE unknown error cause from socket creator: %1
-The socket creator reported an error when creating a socket. But the function
-which failed is unknown (not one of 'S' for socket or 'B' for bind).
-
-% BIND10_SOCKCREATOR_BAD_RESPONSE unknown response for socket request: %1
-The b10-init requested a socket from the creator, but the answer is unknown. This
-looks like a programmer error.
-
-% BIND10_SOCKCREATOR_EOF eof while expecting data from socket creator
-There should be more data from the socket creator, but it closed the socket.
-It probably crashed.
-
-% BIND10_SOCKCREATOR_INIT initializing socket creator parser
-The b10-init module initializes routines for parsing the socket creator
-protocol.
-
-% BIND10_SOCKCREATOR_KILL killing the socket creator
-The socket creator is being terminated the aggressive way, by sending it
-sigkill. This should not happen usually.
-
-% BIND10_SOCKCREATOR_TERMINATE terminating socket creator
-The b10-init module sends a request to terminate to the socket creator.
-
-% BIND10_SOCKCREATOR_TRANSPORT_ERROR transport error when talking to the socket creator: %1
-Either sending or receiving data from the socket creator failed with the given
-error. The creator probably crashed or some serious OS-level problem happened,
-as the communication happens only on local host.
-
-% BIND10_SOCKET_CREATED successfully created socket %1
-The socket creator successfully created and sent a requested socket, it has
-the given file number.
-
-% BIND10_SOCKET_ERROR error on %1 call in the creator: %2/%3
-The socket creator failed to create the requested socket. It failed on the
-indicated OS API function with given error.
-
-% BIND10_SOCKET_GET requesting socket [%1]:%2 of type %3 from the creator
-The b10-init forwards a request for a socket to the socket creator.
-
-% BIND10_STARTED_CC started configuration/command session
-Debug message given when BIND 10 has successfully started the object that
-handles configuration and commands.
-
-% BIND10_STARTED_PROCESS started %1
-The given process has successfully been started.
-
-% BIND10_STARTED_PROCESS_PID started %1 (PID %2)
-The given process has successfully been started, and has the given PID.
-
-% BIND10_STARTING starting BIND10: %1
-Informational message on startup that shows the full version.
-
-% BIND10_STARTING_CC starting configuration/command session
-Informational message given when BIND 10 is starting the session object
-that handles configuration and commands.
-
-% BIND10_STARTING_PROCESS starting process %1
-The b10-init module is starting the given process.
-
-% BIND10_STARTING_PROCESS_PORT starting process %1 (to listen on port %2)
-The b10-init module is starting the given process, which will listen on the
-given port number.
-
-% BIND10_STARTING_PROCESS_PORT_ADDRESS starting process %1 (to listen on %2)
-The b10-init module is starting the given process, which will listen on the
-given address and port number (written as <address>:<port>).
-
-% BIND10_STARTUP_COMPLETE BIND 10 started
-All modules have been successfully started, and BIND 10 is now running.
-
-% BIND10_STARTUP_ERROR error during startup: %1
-There was a fatal error when BIND10 was trying to start. The error is
-shown, and BIND10 will now shut down.
-
-% BIND10_STARTUP_UNEXPECTED_MESSAGE unrecognised startup message %1
-During the startup process, a number of messages are exchanged between the
-Init process and the processes it starts.  This error is output when a
-message received by the Init process is recognised as being of the
-correct format but is unexpected.  It may be that processes are starting
-of sequence.
-
-% BIND10_STARTUP_UNRECOGNISED_MESSAGE unrecognised startup message %1
-During the startup process, a number of messages are exchanged between the
-Init process and the processes it starts.  This error is output when a
-message received by the Init process is not recognised.
-
-% BIND10_STOP_PROCESS asking %1 to shut down
-The b10-init module is sending a shutdown command to the given module over
-the message channel.
-
-% BIND10_UNKNOWN_CHILD_PROCESS_ENDED unknown child pid %1 exited
-An unknown child process has exited. The PID is printed, but no further
-action will be taken by the b10-init process.
-
-% BIND10_WAIT_CFGMGR waiting for configuration manager process to initialize
-The configuration manager process is so critical to operation of BIND 10
-that after starting it, the Init module will wait for it to initialize
-itself before continuing.  This debug message is produced during the
-wait and may be output zero or more times depending on how long it takes
-the configuration manager to start up.  The total length of time Init
-will wait for the configuration manager before reporting an error is
-set with the command line --wait switch, which has a default value of
-ten seconds.
diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in
deleted file mode 100755 (executable)
index 09c9708..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-BIND10_PATH=@abs_top_builddir@/src/bin/bind10
-
-PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:@abs_top_builddir@/src/bin/memmgr:$PATH
-export PATH
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python/isc/cc:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
-export PYTHONPATH
-
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
-SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
-if test $SET_ENV_LIBRARY_PATH = yes; then
-       @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.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/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/threads/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
-       export @ENV_LIBRARY_PATH@
-fi
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-# TODO: We need to do this feature based (ie. no general from_source)
-# But right now we need a second one because some spec files are
-# generated and hence end up under builddir
-B10_FROM_BUILD=@abs_top_builddir@
-export B10_FROM_BUILD
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-exec ${BIND10_PATH}/b10-init "$@"
-
diff --git a/src/bin/bind10/tests/.gitignore b/src/bin/bind10/tests/.gitignore
deleted file mode 100644 (file)
index 575a4c5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/init_test.py
diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am
deleted file mode 100644 (file)
index d7e51d5..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-#PYTESTS = args_test.py bind10_test.py
-# NOTE: this has a generated test found in the builddir
-PYTESTS = init_test.py
-noinst_SCRIPTS = $(PYTESTS)
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       chmod +x $(abs_builddir)/$$pytest ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
-               $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
-       done
diff --git a/src/bin/bind10/tests/args_test.py b/src/bin/bind10/tests/args_test.py
deleted file mode 100644 (file)
index c08343b..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright (C) 2010,2013  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.
-
-"""
-This program tests the b10-init process to make sure that it runs while
-dropping permissions. It must be run as a user that can set permission.
-"""
-import unittest
-import os
-import sys
-import subprocess
-import select
-import time
-import pwd
-
-# Set to a valid user name on the system to run setuid() test
-#SUID_USER=None
-SUID_USER="shane"
-
-BIND10_EXE="../run_bind10.sh"
-TIMEOUT=3
-
-class TestInitArgs(unittest.TestCase):
-    def _waitForString(self, init, s):
-        found_string = False
-        start_time = time.time()
-        while time.time() < start_time + TIMEOUT:
-            (r,w,x) = select.select((init.stdout,), (), (), TIMEOUT)
-            if init.stdout in r:
-                s = init.stdout.readline()
-                if s == '':
-                    break
-                if s.startswith(s):
-                    found_string = True
-                    break
-        return found_string
-
-    def testNoArgs(self):
-        """Run bind10 without any arguments"""
-        init = subprocess.Popen(args=(BIND10_EXE,),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        started_ok = self._waitForString(init, '[bind10] BIND 10 started')
-        time.sleep(0.1)
-        init.terminate()
-        init.wait()
-        self.assertTrue(started_ok)
-
-    def testBadOption(self):
-        """Run bind10 with a bogus option"""
-        init = subprocess.Popen(args=(BIND10_EXE, "--badoption"),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        failed = self._waitForString(init, 'bind10: error: no such option: --badoption')
-        time.sleep(0.1)
-        init.terminate()
-        self.assertTrue(init.wait() == 2)
-        self.assertTrue(failed)
-
-    def testArgument(self):
-        """Run bind10 with an argument (this is not allowed)"""
-        init = subprocess.Popen(args=(BIND10_EXE, "argument"),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        failed = self._waitForString(init, 'Usage: bind10 [options]')
-        time.sleep(0.1)
-        init.terminate()
-        self.assertTrue(init.wait() == 1)
-        self.assertTrue(failed)
-
-    def testBadUser(self):
-        """Run bind10 with a bogus user"""
-        init = subprocess.Popen(args=(BIND10_EXE, "-u", "bogus_user"),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        failed = self._waitForString(init, "bind10: invalid user: 'bogus_user'")
-        time.sleep(0.1)
-        init.terminate()
-        self.assertTrue(init.wait() == 1)
-        self.assertTrue(failed)
-
-    def testBadUid(self):
-        """Run bind10 with a bogus user ID"""
-        init = subprocess.Popen(args=(BIND10_EXE, "-u", "999999999"),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        failed = self._waitForString(init, "bind10: invalid user: '999999999'")
-        time.sleep(0.1)
-        init.terminate()
-        self.assertTrue(init.wait() == 1)
-        self.assertTrue(failed)
-
-    def testFailSetUser(self):
-        """Try the -u option when we don't run as root"""
-        global SUID_USER
-        if SUID_USER is None:
-            self.skipTest("test needs a valid user (set when run)")
-        if os.getuid() == 0:
-            self.skipTest("test must not be run as root (uid is 0)")
-        # XXX: we depend on the "nobody" user
-        init = subprocess.Popen(args=(BIND10_EXE, "-u", "nobody"),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        failed = self._waitForString(init, "[bind10] Error on startup: Unable to start b10-msgq; Unable to change to user nobody")
-        time.sleep(0.1)
-        init.terminate()
-        self.assertTrue(init.wait() == 1)
-        self.assertTrue(failed)
-
-    def testSetUser(self):
-        """Try the -u option"""
-        global SUID_USER
-        if SUID_USER is None:
-            self.skipTest("test needs a valid user (set when run)")
-        if os.getuid() != 0:
-            self.skipTest("test must run as root (uid is not 0)")
-        if os.geteuid() != 0:
-            self.skipTest("test must run as root (euid is not 0)")
-
-        init = subprocess.Popen(args=(BIND10_EXE, "-u", SUID_USER),
-                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        started_ok = self._waitForString(init, '[bind10] BIND 10 started')
-        self.assertTrue(started_ok)
-        ps = subprocess.Popen(args=("ps", "axo", "user,pid"),
-                              stdout=subprocess.PIPE)
-        s = ps.stdout.readline()
-        ps_user = None
-        while True:
-            s = ps.stdout.readline()
-            if s == '': break
-            (user, pid) = s.split()
-            if int(pid) == init.pid:
-                ps_user = user.decode()
-                break
-        self.assertTrue(ps_user is not None)
-        self.assertTrue(ps_user == SUID_USER)
-        time.sleep(0.1)
-        init.terminate()
-        x = init.wait()
-        self.assertTrue(init.wait() == 0)
-
-    def testPrettyName(self):
-        """Try the --pretty-name option."""
-        CMD_PRETTY_NAME = b'init-name-test'
-        init = subprocess.Popen(args=(BIND10_EXE, '--pretty-name',
-            CMD_PRETTY_NAME), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        started_ok = self._waitForString(init, '[bind10] BIND 10 started')
-        self.assertTrue(started_ok)
-        ps = subprocess.Popen(args=("ps", "axo", "pid,comm"),
-                              stdout=subprocess.PIPE)
-        s = ps.stdout.readline()
-        command = None
-        while True:
-            s = ps.stdout.readline()
-            if s == '': break
-            (pid,comm) = s.split(None, 1)
-            if int(pid) == init.pid:
-                command = comm
-                break
-        self.assertEqual(command, CMD_PRETTY_NAME + b'\n')
-        time.sleep(0.1)
-        init.terminate()
-        init.wait()
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/src/bin/bind10/tests/init_test.py.in b/src/bin/bind10/tests/init_test.py.in
deleted file mode 100644 (file)
index af41eb6..0000000
+++ /dev/null
@@ -1,2489 +0,0 @@
-# Copyright (C) 2011  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.
-
-# Most of the time, we omit the "init" for brevity. Sometimes,
-# we want to be explicit about what we do, like when hijacking a library
-# call used by the b10-init.
-from init import Init, ProcessInfo, parse_args, dump_pid, unlink_pid_file, \
-    _BASETIME
-import init
-
-# XXX: environment tests are currently disabled, due to the preprocessor
-#      setup that we have now complicating the environment
-
-import unittest
-import sys
-import os
-import os.path
-import copy
-import signal
-import socket
-from isc.net.addr import IPAddr
-import time
-import isc.log
-import isc.config
-import isc.bind10.socket_cache
-import errno
-import random
-
-from isc.testutils.parse_args import TestOptParser, OptsError
-from isc.testutils.ccsession_mock import MockModuleCCSession
-
-class TestProcessInfo(unittest.TestCase):
-    def setUp(self):
-        # redirect stdout to a pipe so we can check that our
-        # process spawning is doing the right thing with stdout
-        self.old_stdout = os.dup(sys.stdout.fileno())
-        self.pipes = os.pipe()
-        os.dup2(self.pipes[1], sys.stdout.fileno())
-        os.close(self.pipes[1])
-        # note that we use dup2() to restore the original stdout
-        # to the main program ASAP in each test... this prevents
-        # hangs reading from the child process (as the pipe is only
-        # open in the child), and also insures nice pretty output
-
-    def tearDown(self):
-        # clean up our stdout munging
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        os.close(self.pipes[0])
-
-    def test_init(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
-        pi.spawn()
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        self.assertEqual(pi.name, 'Test Process')
-        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
-        self.assertEqual(pi.dev_null_stdout, False)
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-
-#    def test_setting_env(self):
-#        pi = ProcessInfo('Test Process', [ '/bin/true' ], env={'FOO': 'BAR'})
-#        os.dup2(self.old_stdout, sys.stdout.fileno())
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'],
-#                                   'FOO': 'BAR' })
-
-    def test_setting_null_stdout(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
-                         dev_null_stdout=True)
-        pi.spawn()
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        self.assertEqual(pi.dev_null_stdout, True)
-        self.assertEqual(os.read(self.pipes[0], 100), b"")
-
-    def test_respawn(self):
-        pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
-        pi.spawn()
-        # wait for old process to work...
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        # respawn it
-        old_pid = pi.pid
-        pi.respawn()
-        os.dup2(self.old_stdout, sys.stdout.fileno())
-        # make sure the new one started properly
-        self.assertEqual(pi.name, 'Test Process')
-        self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
-#        self.assertEqual(pi.env, { 'PATH': os.environ['PATH'],
-#                                   'PYTHON_EXEC': os.environ['PYTHON_EXEC'] })
-        self.assertEqual(pi.dev_null_stdout, False)
-        self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-        self.assertNotEqual(pi.pid, old_pid)
-
-class TestCacheCommands(unittest.TestCase):
-    """
-    Test methods of b10-init related to the socket cache and socket handling.
-    """
-    def setUp(self):
-        """
-        Prepare b10-init for some tests.
-
-        Also prepare some variables we need.
-        """
-        self.__b10_init = Init()
-        # Fake the cache here so we can pretend it is us and hijack the
-        # calls to its methods.
-        self.__b10_init._socket_cache = self
-        self.__b10_init._socket_path = '/socket/path'
-        self.__raise_exception = None
-        self.__socket_args = {
-            "port": 53,
-            "address": "::",
-            "protocol": "UDP",
-            "share_mode": "ANY",
-            "share_name": "app"
-        }
-        # What was and wasn't called.
-        self.__drop_app_called = None
-        self.__get_socket_called = None
-        self.__send_fd_called = None
-        self.__get_token_called = None
-        self.__drop_socket_called = None
-        init.libutil_io_python.send_fd = self.__send_fd
-
-    def __send_fd(self, to, socket):
-        """
-        A function to hook the send_fd in the b10-init.
-        """
-        self.__send_fd_called = (to, socket)
-
-    class FalseSocket:
-        """
-        A socket where we can fake methods we need instead of having a real
-        socket.
-        """
-        def __init__(self):
-            self.send = b""
-        def fileno(self):
-            """
-            The file number. Used for identifying the remote application.
-            """
-            return 42
-
-        def sendall(self, data):
-            """
-            Adds data to the self.send.
-            """
-            self.send += data
-
-    def drop_application(self, application):
-        """
-        Part of pretending to be the cache. Logs the parameter to
-        self.__drop_app_called.
-
-        In the case self.__raise_exception is set, the exception there
-        is raised instead.
-        """
-        if self.__raise_exception is not None:
-            raise self.__raise_exception
-        self.__drop_app_called = application
-
-    def test_consumer_dead(self):
-        """
-        Test that it calls the drop_application method of the cache.
-        """
-        self.__b10_init.socket_consumer_dead(self.FalseSocket())
-        self.assertEqual(42, self.__drop_app_called)
-
-    def test_consumer_dead_invalid(self):
-        """
-        Test that it doesn't crash in case the application is not known to
-        the cache, the b10_init doesn't crash, as this actually can happen in
-        practice.
-        """
-        self.__raise_exception = ValueError("This application is unknown")
-        # This doesn't crash
-        self.__b10_init.socket_consumer_dead(self.FalseSocket())
-
-    def get_socket(self, token, application):
-        """
-        Part of pretending to be the cache. If there's anything in
-        __raise_exception, it is raised. Otherwise, the call is logged
-        into __get_socket_called and a number is returned.
-        """
-        if self.__raise_exception is not None:
-            raise self.__raise_exception
-        self.__get_socket_called = (token, application)
-        return 13
-
-    def test_request_handler(self):
-        """
-        Test that a request for socket is forwarded and the socket is sent
-        back, if it returns a socket.
-        """
-        socket = self.FalseSocket()
-        # An exception from the cache
-        self.__raise_exception = ValueError("Test value error")
-        self.__b10_init.socket_request_handler(b"token", socket)
-        # It was called, but it threw, so it is not noted here
-        self.assertIsNone(self.__get_socket_called)
-        self.assertEqual(b"0\n", socket.send)
-        # It should not have sent any socket.
-        self.assertIsNone(self.__send_fd_called)
-        # Now prepare a valid scenario
-        self.__raise_exception = None
-        socket.send = b""
-        self.__b10_init.socket_request_handler(b"token", socket)
-        self.assertEqual(b"1\n", socket.send)
-        self.assertEqual((42, 13), self.__send_fd_called)
-        self.assertEqual(("token", 42), self.__get_socket_called)
-
-    def get_token(self, protocol, address, port, share_mode, share_name):
-        """
-        Part of pretending to be the cache. If there's anything in
-        __raise_exception, it is raised. Otherwise, the parameters are
-        logged into __get_token_called and a token is returned.
-        """
-        if self.__raise_exception is not None:
-            raise self.__raise_exception
-        self.__get_token_called = (protocol, address, port, share_mode,
-                                   share_name)
-        return "token"
-
-    def test_get_socket_ok(self):
-        """
-        Test the successful scenario of getting a socket.
-        """
-        result = self.__b10_init._get_socket(self.__socket_args)
-        [code, answer] = result['result']
-        self.assertEqual(0, code)
-        self.assertEqual({
-            'token': 'token',
-            'path': '/socket/path'
-        }, answer)
-        addr = self.__get_token_called[1]
-        self.assertTrue(isinstance(addr, IPAddr))
-        self.assertEqual("::", str(addr))
-        self.assertEqual(("UDP", addr, 53, "ANY", "app"),
-                         self.__get_token_called)
-
-    def test_get_socket_error(self):
-        """
-        Test that bad inputs are handled correctly, etc.
-        """
-        def check_code(code, args):
-            """
-            Pass the args there and check if it returns success or not.
-
-            The rest is not tested, as it is already checked in the
-            test_get_socket_ok.
-            """
-            [rcode, ranswer] = self.__b10_init._get_socket(args)['result']
-            self.assertEqual(code, rcode)
-            if code != 0:
-                # This should be an error message. The exact formatting
-                # is unknown, but we check it is string at least
-                self.assertTrue(isinstance(ranswer, str))
-
-        def mod_args(name, value):
-            """
-            Override a parameter in the args.
-            """
-            result = dict(self.__socket_args)
-            result[name] = value
-            return result
-
-        # Port too large
-        check_code(1, mod_args('port', 65536))
-        # Not numeric address
-        check_code(1, mod_args('address', 'example.org.'))
-        # Some bad values of enum-like params
-        check_code(1, mod_args('protocol', 'BAD PROTO'))
-        check_code(1, mod_args('share_mode', 'BAD SHARE'))
-        # Check missing parameters
-        for param in self.__socket_args.keys():
-            args = dict(self.__socket_args)
-            del args[param]
-            check_code(1, args)
-        # These are OK values for the enum-like parameters
-        # The ones from test_get_socket_ok are not tested here
-        check_code(0, mod_args('protocol', 'TCP'))
-        check_code(0, mod_args('share_mode', 'SAMEAPP'))
-        check_code(0, mod_args('share_mode', 'NO'))
-        # If an exception is raised from within the cache, it is converted
-        # to an error, not propagated
-        self.__raise_exception = Exception("Test exception")
-        check_code(1, self.__socket_args)
-        # The special "expected" exceptions
-        self.__raise_exception = \
-            isc.bind10.socket_cache.ShareError("Not shared")
-        check_code(3, self.__socket_args)
-        self.__raise_exception = \
-            isc.bind10.socket_cache.SocketError("Not shared", 13)
-        check_code(2, self.__socket_args)
-
-    def drop_socket(self, token):
-        """
-        Part of pretending to be the cache. If there's anything in
-        __raise_exception, it is raised. Otherwise, the parameter is stored
-        in __drop_socket_called.
-        """
-        if self.__raise_exception is not None:
-            raise self.__raise_exception
-        self.__drop_socket_called = token
-
-    def test_drop_socket(self):
-        """
-        Check the drop_socket command. It should directly call the method
-        on the cache. Exceptions should be translated to error messages.
-        """
-        # This should be OK and just propagated to the call.
-        self.assertEqual({"result": [0]},
-                         self.__b10_init.command_handler("drop_socket",
-                                                         {"token": "token"}))
-        self.assertEqual("token", self.__drop_socket_called)
-        self.__drop_socket_called = None
-        # Missing parameter
-        self.assertEqual({"result": [1, "Missing token parameter"]},
-                         self.__b10_init.command_handler("drop_socket", {}))
-        self.assertIsNone(self.__drop_socket_called)
-        # An exception is raised from within the cache
-        self.__raise_exception = ValueError("Test error")
-        self.assertEqual({"result": [1, "Test error"]},
-                         self.__b10_init.command_handler("drop_socket",
-                         {"token": "token"}))
-
-
-class TestInit(unittest.TestCase):
-    def setUp(self):
-        # Save original values that may be tweaked in some tests
-        self.__orig_setgid = init.posix.setgid
-        self.__orig_setuid = init.posix.setuid
-        self.__orig_logger_class = isc.log.Logger
-
-    def tearDown(self):
-        # Restore original values saved in setUp()
-        init.posix.setgid = self.__orig_setgid
-        init.posix.setuid = self.__orig_setuid
-        isc.log.Logger = self.__orig_logger_class
-
-    def test_init(self):
-        b10_init = Init()
-        self.assertEqual(b10_init.verbose, False)
-        self.assertEqual(b10_init.msgq_socket_file, None)
-        self.assertEqual(b10_init.cc_session, None)
-        self.assertEqual(b10_init.ccs, None)
-        self.assertEqual(b10_init.components, {})
-        self.assertEqual(b10_init.runnable, False)
-        self.assertEqual(b10_init.username, None)
-        self.assertIsNone(b10_init._socket_cache)
-
-    def __setgid(self, gid):
-        self.__gid_set = gid
-
-    def __setuid(self, uid):
-        self.__uid_set = uid
-
-    def test_change_user(self):
-        init.posix.setgid = self.__setgid
-        init.posix.setuid = self.__setuid
-
-        self.__gid_set = None
-        self.__uid_set = None
-        b10_init = Init()
-        b10_init.change_user()
-        # No gid/uid set in init, nothing called.
-        self.assertIsNone(self.__gid_set)
-        self.assertIsNone(self.__uid_set)
-
-        Init(setuid=42, setgid=4200).change_user()
-        # This time, it get's called
-        self.assertEqual(4200, self.__gid_set)
-        self.assertEqual(42, self.__uid_set)
-
-        def raising_set_xid(gid_or_uid):
-            ex = OSError()
-            ex.errno, ex.strerror = errno.EPERM, 'Operation not permitted'
-            raise ex
-
-        # Let setgid raise an exception
-        init.posix.setgid = raising_set_xid
-        init.posix.setuid = self.__setuid
-        self.assertRaises(init.ChangeUserError,
-                          Init(setuid=42, setgid=4200).change_user)
-
-        # Let setuid raise an exception
-        init.posix.setgid = self.__setgid
-        init.posix.setuid = raising_set_xid
-        self.assertRaises(init.ChangeUserError,
-                          Init(setuid=42, setgid=4200).change_user)
-
-        # Let initial log output after setuid raise an exception
-        init.posix.setgid = self.__setgid
-        init.posix.setuid = self.__setuid
-        isc.log.Logger = raising_set_xid
-        self.assertRaises(init.ChangeUserError,
-                          Init(setuid=42, setgid=4200).change_user)
-
-    def test_set_creator(self):
-        """
-        Test the call to set_creator. First time, the cache is created
-        with the passed creator. The next time, it throws an exception.
-        """
-        init = Init()
-        # The cache doesn't use it at start, so just create an empty class
-        class Creator: pass
-        creator = Creator()
-        init.set_creator(creator)
-        self.assertTrue(isinstance(init._socket_cache,
-                        isc.bind10.socket_cache.Cache))
-        self.assertEqual(creator, init._socket_cache._creator)
-        self.assertRaises(ValueError, init.set_creator, creator)
-
-    def test_socket_srv(self):
-        """Tests init_socket_srv() and remove_socket_srv() work as expected."""
-        init = Init()
-
-        self.assertIsNone(init._srv_socket)
-        self.assertIsNone(init._tmpdir)
-        self.assertIsNone(init._socket_path)
-
-        init.init_socket_srv()
-
-        self.assertIsNotNone(init._srv_socket)
-        self.assertNotEqual(-1, init._srv_socket.fileno())
-        self.assertEqual(os.path.join(init._tmpdir, 'sockcreator'),
-                         init._srv_socket.getsockname())
-
-        self.assertIsNotNone(init._tmpdir)
-        self.assertTrue(os.path.isdir(init._tmpdir))
-        self.assertIsNotNone(init._socket_path)
-        self.assertTrue(os.path.exists(init._socket_path))
-
-        # Check that it's possible to connect to the socket file (this
-        # only works if the socket file exists and the server listens on
-        # it).
-        s = socket.socket(socket.AF_UNIX)
-        try:
-            s.connect(init._socket_path)
-            can_connect = True
-            s.close()
-        except socket.error as e:
-            can_connect = False
-
-        self.assertTrue(can_connect)
-
-        init.remove_socket_srv()
-
-        self.assertEqual(-1, init._srv_socket.fileno())
-        self.assertFalse(os.path.exists(init._socket_path))
-        self.assertFalse(os.path.isdir(init._tmpdir))
-
-        # These should not fail either:
-
-        # second call
-        init.remove_socket_srv()
-
-        init._srv_socket = None
-        init.remove_socket_srv()
-
-    def test_init_alternate_socket(self):
-        init = Init("alt_socket_file")
-        self.assertEqual(init.verbose, False)
-        self.assertEqual(init.msgq_socket_file, "alt_socket_file")
-        self.assertEqual(init.cc_session, None)
-        self.assertEqual(init.ccs, None)
-        self.assertEqual(init.components, {})
-        self.assertEqual(init.runnable, False)
-        self.assertEqual(init.username, None)
-
-    def test_command_handler(self):
-        class DummySession():
-            def group_sendmsg(self, msg, group):
-                (self.msg, self.group) = (msg, group)
-            def group_recvmsg(self, nonblock, seq): pass
-        class DummyModuleCCSession():
-            module_spec = isc.config.module_spec.ModuleSpec({
-                    "module_name": "Init",
-                    "statistics": [
-                        {
-                            "item_name": "boot_time",
-                            "item_type": "string",
-                            "item_optional": False,
-                            "item_default": "1970-01-01T00:00:00Z",
-                            "item_title": "Boot time",
-                            "item_description": "A date time when bind10 process starts initially",
-                            "item_format": "date-time"
-                            }
-                        ]
-                    })
-            def get_module_spec(self):
-                return self.module_spec
-        init = Init()
-        init.verbose = True
-        init.cc_session = DummySession()
-        init.ccs = DummyModuleCCSession()
-        # a bad command
-        self.assertEqual(init.command_handler(-1, None),
-                         isc.config.ccsession.create_answer(1, "bad command"))
-        # "shutdown" command
-        self.assertEqual(init.command_handler("shutdown", None),
-                         isc.config.ccsession.create_answer(0))
-        self.assertFalse(init.runnable)
-        # "getstats" command
-        self.assertEqual(init.command_handler("getstats", None),
-                         isc.config.ccsession.create_answer(0,
-                            { 'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME) }))
-        # "ping" command
-        self.assertEqual(init.command_handler("ping", None),
-                         isc.config.ccsession.create_answer(0, "pong"))
-        # "show_processes" command
-        self.assertEqual(init.command_handler("show_processes", None),
-                         isc.config.ccsession.create_answer(0,
-                                                            init.get_processes()))
-        # an unknown command
-        self.assertEqual(init.command_handler("__UNKNOWN__", None),
-                         isc.config.ccsession.create_answer(1, "Unknown command"))
-
-        # Fake the get_token of cache and test the command works
-        init._socket_path = '/socket/path'
-        class cache:
-            def get_token(self, protocol, addr, port, share_mode, share_name):
-                return str(addr) + ':' + str(port)
-        init._socket_cache = cache()
-        args = {
-            "port": 53,
-            "address": "0.0.0.0",
-            "protocol": "UDP",
-            "share_mode": "ANY",
-            "share_name": "app"
-        }
-        # at all and this is the easiest way to check.
-        self.assertEqual({'result': [0, {'token': '0.0.0.0:53',
-                                         'path': '/socket/path'}]},
-                         init.command_handler("get_socket", args))
-        # The drop_socket is not tested here, but in TestCacheCommands.
-        # It needs the cache mocks to be in place and they are there.
-
-    def test_stop_process(self):
-        """
-        Test checking the stop_process method sends the right message over
-        the message bus.
-        """
-        class DummySession():
-            def group_sendmsg(self, msg, group, instance="*"):
-                (self.msg, self.group, self.instance) = (msg, group, instance)
-        init = Init()
-        init.cc_session = DummySession()
-        init.stop_process('process', 'address', 42)
-        self.assertEqual('address', init.cc_session.group)
-        self.assertEqual('address', init.cc_session.instance)
-        self.assertEqual({'command': ['shutdown', {'pid': 42}]},
-                         init.cc_session.msg)
-
-# Mock class for testing Init's usage of ProcessInfo
-class MockProcessInfo:
-    def __init__(self, name, args, env={}, dev_null_stdout=False,
-                 dev_null_stderr=False):
-        self.name = name
-        self.args = args
-        self.env = env
-        self.dev_null_stdout = dev_null_stdout
-        self.dev_null_stderr = dev_null_stderr
-        self.process = None
-        self.pid = None
-
-    def spawn(self):
-        # set some pid (only used for testing that it is not None anymore)
-        self.pid = 42147
-
-# Class for testing the Init without actually starting processes.
-# This is used for testing the start/stop components routines and
-# the Init commands.
-#
-# Testing that external processes start is outside the scope
-# of the unit test, by overriding the process start methods we can check
-# that the right processes are started depending on the configuration
-# options.
-class MockInit(Init):
-    def __init__(self):
-        Init.__init__(self)
-
-        # Set flags as to which of the overridden methods has been run.
-        self.msgq = False
-        self.cfgmgr = False
-        self.ccsession = False
-        self.auth = False
-        self.resolver = False
-        self.xfrout = False
-        self.xfrin = False
-        self.zonemgr = False
-        self.stats = False
-        self.stats_httpd = False
-        self.cmdctl = False
-        self.dhcp6 = False
-        self.dhcp4 = False
-        self.c_channel_env = {}
-        self.components = { }
-        self.creator = False
-        self.get_process_exit_status_called = False
-
-        class MockSockCreator(isc.bind10.component.Component):
-            def __init__(self, process, b10_init, kind, address=None,
-                         params=None):
-                isc.bind10.component.Component.__init__(self, process,
-                                                        b10_init, kind,
-                                                        'SockCreator')
-                self._start_func = b10_init.start_creator
-
-        specials = isc.bind10.special_component.get_specials()
-        specials['sockcreator'] = MockSockCreator
-        self._component_configurator = \
-            isc.bind10.component.Configurator(self, specials)
-
-    def start_creator(self):
-        self.creator = True
-        procinfo = ProcessInfo('b10-sockcreator', ['/bin/false'])
-        procinfo.pid = 1
-        return procinfo
-
-    def _read_bind10_config(self):
-        # Configuration options are set directly
-        pass
-
-    def start_msgq(self):
-        self.msgq = True
-        procinfo = ProcessInfo('b10-msgq', ['/bin/false'])
-        procinfo.pid = 2
-        return procinfo
-
-    def start_ccsession(self, c_channel_env):
-        # this is not a process, don't have to do anything with procinfo
-        self.ccsession = True
-
-    def start_cfgmgr(self):
-        self.cfgmgr = True
-        procinfo = ProcessInfo('b10-cfgmgr', ['/bin/false'])
-        procinfo.pid = 3
-        return procinfo
-
-    def start_auth(self):
-        self.auth = True
-        procinfo = ProcessInfo('b10-auth', ['/bin/false'])
-        procinfo.pid = 5
-        return procinfo
-
-    def start_resolver(self):
-        self.resolver = True
-        procinfo = ProcessInfo('b10-resolver', ['/bin/false'])
-        procinfo.pid = 6
-        return procinfo
-
-    def start_simple(self, name):
-        procmap = { 'b10-zonemgr': self.start_zonemgr,
-                    'b10-stats': self.start_stats,
-                    'b10-stats-httpd': self.start_stats_httpd,
-                    'b10-cmdctl': self.start_cmdctl,
-                    'b10-dhcp6': self.start_dhcp6,
-                    'b10-dhcp4': self.start_dhcp4,
-                    'b10-xfrin': self.start_xfrin,
-                    'b10-xfrout': self.start_xfrout }
-        return procmap[name]()
-
-    def start_xfrout(self):
-        self.xfrout = True
-        procinfo = ProcessInfo('b10-xfrout', ['/bin/false'])
-        procinfo.pid = 7
-        return procinfo
-
-    def start_xfrin(self):
-        self.xfrin = True
-        procinfo = ProcessInfo('b10-xfrin', ['/bin/false'])
-        procinfo.pid = 8
-        return procinfo
-
-    def start_zonemgr(self):
-        self.zonemgr = True
-        procinfo = ProcessInfo('b10-zonemgr', ['/bin/false'])
-        procinfo.pid = 9
-        return procinfo
-
-    def start_stats(self):
-        self.stats = True
-        procinfo = ProcessInfo('b10-stats', ['/bin/false'])
-        procinfo.pid = 10
-        return procinfo
-
-    def start_stats_httpd(self):
-        self.stats_httpd = True
-        procinfo = ProcessInfo('b10-stats-httpd', ['/bin/false'])
-        procinfo.pid = 11
-        return procinfo
-
-    def start_cmdctl(self):
-        self.cmdctl = True
-        procinfo = ProcessInfo('b10-cmdctl', ['/bin/false'])
-        procinfo.pid = 12
-        return procinfo
-
-    def start_dhcp6(self):
-        self.dhcp6 = True
-        procinfo = ProcessInfo('b10-dhcp6', ['/bin/false'])
-        procinfo.pid = 13
-        return procinfo
-
-    def start_dhcp4(self):
-        self.dhcp4 = True
-        procinfo = ProcessInfo('b10-dhcp4', ['/bin/false'])
-        procinfo.pid = 14
-        return procinfo
-
-    def stop_process(self, process, recipient, pid):
-        procmap = { 'b10-auth': self.stop_auth,
-                    'b10-resolver': self.stop_resolver,
-                    'b10-xfrout': self.stop_xfrout,
-                    'b10-xfrin': self.stop_xfrin,
-                    'b10-zonemgr': self.stop_zonemgr,
-                    'b10-stats': self.stop_stats,
-                    'b10-stats-httpd': self.stop_stats_httpd,
-                    'b10-cmdctl': self.stop_cmdctl }
-        procmap[process]()
-
-    # Some functions to pretend we stop processes, use by stop_process
-    def stop_msgq(self):
-        if self.msgq:
-            del self.components[2]
-        self.msgq = False
-
-    def stop_cfgmgr(self):
-        if self.cfgmgr:
-            del self.components[3]
-        self.cfgmgr = False
-
-    def stop_auth(self):
-        if self.auth:
-            del self.components[5]
-        self.auth = False
-
-    def stop_resolver(self):
-        if self.resolver:
-            del self.components[6]
-        self.resolver = False
-
-    def stop_xfrout(self):
-        if self.xfrout:
-            del self.components[7]
-        self.xfrout = False
-
-    def stop_xfrin(self):
-        if self.xfrin:
-            del self.components[8]
-        self.xfrin = False
-
-    def stop_zonemgr(self):
-        if self.zonemgr:
-            del self.components[9]
-        self.zonemgr = False
-
-    def stop_stats(self):
-        if self.stats:
-            del self.components[10]
-        self.stats = False
-
-    def stop_stats_httpd(self):
-        if self.stats_httpd:
-            del self.components[11]
-        self.stats_httpd = False
-
-    def stop_cmdctl(self):
-        if self.cmdctl:
-            del self.components[12]
-        self.cmdctl = False
-
-    def _get_process_exit_status(self):
-        if self.get_process_exit_status_called:
-            return (0, 0)
-        self.get_process_exit_status_called = True
-        return (53, 0)
-
-    def _get_process_exit_status_unknown_pid(self):
-        if self.get_process_exit_status_called:
-            return (0, 0)
-        self.get_process_exit_status_called = True
-        return (42, 0)
-
-    def _get_process_exit_status_raises_oserror_echild(self):
-        raise OSError(errno.ECHILD, 'Mock error')
-
-    def _get_process_exit_status_raises_oserror_other(self):
-        raise OSError(0, 'Mock error')
-
-    def _get_process_exit_status_raises_other(self):
-        raise Exception('Mock error')
-
-    def _make_mock_process_info(self, name, args, c_channel_env,
-                                dev_null_stdout=False, dev_null_stderr=False):
-        return MockProcessInfo(name, args, c_channel_env,
-                               dev_null_stdout, dev_null_stderr)
-
-class MockInitSimple(Init):
-    def __init__(self):
-        Init.__init__(self)
-        # Set which process has been started
-        self.started_process_name = None
-        self.started_process_args = None
-        self.started_process_env = None
-
-    def _make_mock_process_info(self, name, args, c_channel_env,
-                                dev_null_stdout=False, dev_null_stderr=False):
-        return MockProcessInfo(name, args, c_channel_env,
-                               dev_null_stdout, dev_null_stderr)
-
-    def start_process(self, name, args, c_channel_env, port=None,
-                      address=None):
-        self.started_process_name = name
-        self.started_process_args = args
-        self.started_process_env = c_channel_env
-        return None
-
-class TestStartStopProcessesInit(unittest.TestCase):
-    """
-    Check that the start_all_components method starts the right combination
-    of components and that the right components are started and stopped
-    according to changes in configuration.
-    """
-    def check_environment_unchanged(self):
-        # Check whether the environment has not been changed
-        self.assertEqual(original_os_environ, os.environ)
-
-    def check_started(self, init, core, auth, resolver):
-        """
-        Check that the right sets of services are started. The ones that
-        should be running are specified by the core, auth and resolver parameters
-        (they are groups of processes, eg. auth means b10-auth, -xfrout, -xfrin
-        and -zonemgr).
-        """
-        self.assertEqual(init.msgq, core)
-        self.assertEqual(init.cfgmgr, core)
-        self.assertEqual(init.ccsession, core)
-        self.assertEqual(init.creator, core)
-        self.assertEqual(init.auth, auth)
-        self.assertEqual(init.resolver, resolver)
-        self.assertEqual(init.xfrout, auth)
-        self.assertEqual(init.xfrin, auth)
-        self.assertEqual(init.zonemgr, auth)
-        self.assertEqual(init.stats, core)
-        self.assertEqual(init.stats_httpd, core)
-        self.assertEqual(init.cmdctl, core)
-        self.check_environment_unchanged()
-
-    def check_preconditions(self, init):
-        self.check_started(init, False, False, False)
-
-    def check_started_none(self, init):
-        """
-        Check that the situation is according to configuration where no servers
-        should be started. Some components still need to be running.
-        """
-        self.check_started(init, True, False, False)
-        self.check_environment_unchanged()
-
-    def check_started_both(self, init):
-        """
-        Check the situation is according to configuration where both servers
-        (auth and resolver) are enabled.
-        """
-        self.check_started(init, True, True, True)
-        self.check_environment_unchanged()
-
-    def check_started_auth(self, init):
-        """
-        Check the set of components needed to run auth only is started.
-        """
-        self.check_started(init, True, True, False)
-        self.check_environment_unchanged()
-
-    def check_started_resolver(self, init):
-        """
-        Check the set of components needed to run resolver only is started.
-        """
-        self.check_started(init, True, False, True)
-        self.check_environment_unchanged()
-
-    def check_started_dhcp(self, init, v4, v6):
-        """
-        Check if proper combinations of DHCPv4 and DHCpv6 can be started
-        """
-        self.assertEqual(v4, init.dhcp4)
-        self.assertEqual(v6, init.dhcp6)
-        self.check_environment_unchanged()
-
-    def construct_config(self, start_auth, start_resolver):
-        # The things that are common, not turned on an off
-        config = {}
-        config['b10-stats'] = { 'kind': 'dispensable', 'address': 'Stats' }
-        config['b10-stats-httpd'] = { 'kind': 'dispensable',
-                                      'address': 'StatsHttpd' }
-        config['b10-cmdctl'] = { 'kind': 'needed', 'special': 'cmdctl' }
-        if start_auth:
-            config['b10-auth'] = { 'kind': 'needed', 'special': 'auth' }
-            config['b10-xfrout'] = { 'kind': 'dispensable',
-                                     'address': 'Xfrout' }
-            config['b10-xfrin'] = { 'kind': 'dispensable',
-                                    'address': 'Xfrin' }
-            config['b10-zonemgr'] = { 'kind': 'dispensable',
-                                      'address': 'Zonemgr' }
-        if start_resolver:
-            config['b10-resolver'] = { 'kind': 'needed',
-                                       'special': 'resolver' }
-        return {'components': config}
-
-    def config_start_init(self, start_auth, start_resolver):
-        """
-        Test the configuration is loaded at the startup.
-        """
-        init = MockInit()
-        config = self.construct_config(start_auth, start_resolver)
-        class CC:
-            def get_full_config(self):
-                return config
-        # Provide the fake CC with data
-        init.ccs = CC()
-        # And make sure it's not overwritten
-        def start_ccsession():
-            init.ccsession = True
-        init.start_ccsession = lambda _: start_ccsession()
-        # We need to return the original _read_bind10_config
-        init._read_bind10_config = lambda: Init._read_bind10_config(init)
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        self.check_started(init, True, start_auth, start_resolver)
-        self.check_environment_unchanged()
-
-    def test_start_none(self):
-        self.config_start_init(False, False)
-
-    def test_start_resolver(self):
-        self.config_start_init(False, True)
-
-    def test_start_auth(self):
-        self.config_start_init(True, False)
-
-    def test_start_both(self):
-        self.config_start_init(True, True)
-
-    def test_config_start(self):
-        """
-        Test that the configuration starts and stops components according
-        to configuration changes.
-        """
-
-        # Create Init and ensure correct initialization
-        init = MockInit()
-        self.check_preconditions(init)
-
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        init.runnable = True
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_none(init)
-
-        # Enable both at once
-        init.config_handler(self.construct_config(True, True))
-        self.check_started_both(init)
-
-        # Not touched by empty change
-        init.config_handler({})
-        self.check_started_both(init)
-
-        # Not touched by change to the same configuration
-        init.config_handler(self.construct_config(True, True))
-        self.check_started_both(init)
-
-        # Turn them both off again
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_none(init)
-
-        # Not touched by empty change
-        init.config_handler({})
-        self.check_started_none(init)
-
-        # Not touched by change to the same configuration
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_none(init)
-
-        # Start and stop auth separately
-        init.config_handler(self.construct_config(True, False))
-        self.check_started_auth(init)
-
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_none(init)
-
-        # Start and stop resolver separately
-        init.config_handler(self.construct_config(False, True))
-        self.check_started_resolver(init)
-
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_none(init)
-
-        # Alternate
-        init.config_handler(self.construct_config(True, False))
-        self.check_started_auth(init)
-
-        init.config_handler(self.construct_config(False, True))
-        self.check_started_resolver(init)
-
-        init.config_handler(self.construct_config(True, False))
-        self.check_started_auth(init)
-
-    def test_config_start_once(self):
-        """
-        Tests that a component is started only once.
-        """
-        # Create Init and ensure correct initialization
-        init = MockInit()
-        self.check_preconditions(init)
-
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-
-        init.runnable = True
-        init.config_handler(self.construct_config(True, True))
-        self.check_started_both(init)
-
-        init.start_auth = lambda: self.fail("Started auth again")
-        init.start_xfrout = lambda: self.fail("Started xfrout again")
-        init.start_xfrin = lambda: self.fail("Started xfrin again")
-        init.start_zonemgr = lambda: self.fail("Started zonemgr again")
-        init.start_resolver = lambda: self.fail("Started resolver again")
-
-        # Send again we want to start them. Should not do it, as they are.
-        init.config_handler(self.construct_config(True, True))
-
-    def test_config_not_started_early(self):
-        """
-        Test that components are not started by the config handler before
-        startup.
-        """
-        init = MockInit()
-        self.check_preconditions(init)
-
-        init.start_auth = lambda: self.fail("Started auth again")
-        init.start_xfrout = lambda: self.fail("Started xfrout again")
-        init.start_xfrin = lambda: self.fail("Started xfrin again")
-        init.start_zonemgr = lambda: self.fail("Started zonemgr again")
-        init.start_resolver = lambda: self.fail("Started resolver again")
-
-        init.config_handler({'start_auth': True, 'start_resolver': True})
-
-    # Checks that DHCP (v4 and v6) components are started when expected
-    def test_start_dhcp(self):
-
-        # Create Init and ensure correct initialization
-        init = MockInit()
-        self.check_preconditions(init)
-
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        init.config_handler(self.construct_config(False, False))
-        self.check_started_dhcp(init, False, False)
-
-    def test_start_dhcp_v6only(self):
-        # Create Init and ensure correct initialization
-        init = MockInit()
-        self.check_preconditions(init)
-        # v6 only enabled
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        init.runnable = True
-        init._Init_started = True
-        config = self.construct_config(False, False)
-        config['components']['b10-dhcp6'] = { 'kind': 'needed',
-                                              'address': 'Dhcp6' }
-        init.config_handler(config)
-        self.check_started_dhcp(init, False, True)
-
-        # uncomment when dhcpv4 becomes implemented
-        # v4 only enabled
-        #init.cfg_start_dhcp6 = False
-        #init.cfg_start_dhcp4 = True
-        #self.check_started_dhcp(init, True, False)
-
-        # both v4 and v6 enabled
-        #init.cfg_start_dhcp6 = True
-        #init.cfg_start_dhcp4 = True
-        #self.check_started_dhcp(init, True, True)
-
-class MockComponent:
-    def __init__(self, name, pid, address=None):
-        self.name = lambda: name
-        self.pid = lambda: pid
-        self.address = lambda: address
-        self.restarted = False
-        self.forceful = False
-        self.running = True
-        self.has_failed = False
-
-    def get_restart_time(self):
-        return 0                # arbitrary dummy value
-
-    def restart(self, now):
-        self.restarted = True
-        return True
-
-    def is_running(self):
-        return self.running
-
-    def failed(self, status):
-        return self.has_failed
-
-    def kill(self, forceful):
-        self.forceful = forceful
-
-class TestInitCmd(unittest.TestCase):
-    def test_ping(self):
-        """
-        Confirm simple ping command works.
-        """
-        init = MockInit()
-        answer = init.command_handler("ping", None)
-        self.assertEqual(answer, {'result': [0, 'pong']})
-
-    def test_show_processes_empty(self):
-        """
-        Confirm getting a list of processes works.
-        """
-        init = MockInit()
-        answer = init.command_handler("show_processes", None)
-        self.assertEqual(answer, {'result': [0, []]})
-
-    def test_show_processes(self):
-        """
-        Confirm getting a list of processes works.
-        """
-        init = MockInit()
-        init.register_process(1, MockComponent('first', 1))
-        init.register_process(2, MockComponent('second', 2, 'Second'))
-        answer = init.command_handler("show_processes", None)
-        processes = [[1, 'first', None],
-                     [2, 'second', 'Second']]
-        self.assertEqual(answer, {'result': [0, processes]})
-
-class TestParseArgs(unittest.TestCase):
-    """
-    This tests parsing of arguments of the bind10 master process.
-    """
-    #TODO: Write tests for the original parsing, bad options, etc.
-    def test_no_opts(self):
-        """
-        Test correct default values when no options are passed.
-        """
-        options = parse_args([], TestOptParser)
-        self.assertEqual(None, options.data_path)
-        self.assertEqual(None, options.config_file)
-        self.assertEqual(None, options.cmdctl_port)
-
-    def test_data_path(self):
-        """
-        Test it can parse the data path.
-        """
-        self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
-        self.assertRaises(OptsError, parse_args, ['--data-path'],
-                          TestOptParser)
-        options = parse_args(['-p', '/data/path'], TestOptParser)
-        self.assertEqual('/data/path', options.data_path)
-        options = parse_args(['--data-path=/data/path'], TestOptParser)
-        self.assertEqual('/data/path', options.data_path)
-
-    def test_config_filename(self):
-        """
-        Test it can parse the config switch.
-        """
-        self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
-        self.assertRaises(OptsError, parse_args, ['--config-file'],
-                          TestOptParser)
-        options = parse_args(['-c', 'config-file'], TestOptParser)
-        self.assertEqual('config-file', options.config_file)
-        options = parse_args(['--config-file=config-file'], TestOptParser)
-        self.assertEqual('config-file', options.config_file)
-
-    def test_clear_config(self):
-        options = parse_args([], TestOptParser)
-        self.assertEqual(False, options.clear_config)
-        options = parse_args(['--clear-config'], TestOptParser)
-        self.assertEqual(True, options.clear_config)
-
-    def test_nokill(self):
-        options = parse_args([], TestOptParser)
-        self.assertEqual(False, options.nokill)
-        options = parse_args(['--no-kill'], TestOptParser)
-        self.assertEqual(True, options.nokill)
-        options = parse_args([], TestOptParser)
-        self.assertEqual(False, options.nokill)
-        options = parse_args(['-i'], TestOptParser)
-        self.assertEqual(True, options.nokill)
-
-    def test_cmdctl_port(self):
-        """
-        Test it can parse the command control port.
-        """
-        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
-                                                TestOptParser)
-        self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
-                                                TestOptParser)
-        self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
-                          TestOptParser)
-        options = parse_args(['--cmdctl-port=1234'], TestOptParser)
-        self.assertEqual(1234, options.cmdctl_port)
-
-class TestPIDFile(unittest.TestCase):
-    def setUp(self):
-        self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
-        if os.path.exists(self.pid_file):
-            os.unlink(self.pid_file)
-
-    def tearDown(self):
-        if os.path.exists(self.pid_file):
-            os.unlink(self.pid_file)
-
-    def check_pid_file(self):
-        # dump PID to the file, and confirm the content is correct
-        dump_pid(self.pid_file)
-        my_pid = os.getpid()
-        with open(self.pid_file, "r") as f:
-            self.assertEqual(my_pid, int(f.read()))
-
-    def test_dump_pid(self):
-        self.check_pid_file()
-
-        # make sure any existing content will be removed
-        with open(self.pid_file, "w") as f:
-            f.write('dummy data\n')
-        self.check_pid_file()
-
-    def test_unlink_pid_file_notexist(self):
-        dummy_data = 'dummy_data\n'
-
-        with open(self.pid_file, "w") as f:
-            f.write(dummy_data)
-
-        unlink_pid_file("no_such_pid_file")
-
-        # the file specified for unlink_pid_file doesn't exist,
-        # and the original content of the file should be intact.
-        with open(self.pid_file, "r") as f:
-            self.assertEqual(dummy_data, f.read())
-
-    def test_dump_pid_with_none(self):
-        # Check the behavior of dump_pid() and unlink_pid_file() with None.
-        # This should be no-op.
-        dump_pid(None)
-        self.assertFalse(os.path.exists(self.pid_file))
-
-        dummy_data = 'dummy_data\n'
-
-        with open(self.pid_file, "w") as f:
-            f.write(dummy_data)
-
-        unlink_pid_file(None)
-
-        with open(self.pid_file, "r") as f:
-            self.assertEqual(dummy_data, f.read())
-
-    def test_dump_pid_failure(self):
-        # the attempt to open file will fail, which should result in exception.
-        self.assertRaises(IOError, dump_pid,
-                          'nonexistent_dir' + os.sep + 'bind10.pid')
-
-class TestInitComponents(unittest.TestCase):
-    """
-    Test b10-init propagates component configuration properly to the
-    component configurator and acts sane.
-    """
-    def setUp(self):
-        self.__param = None
-        self.__called = False
-        self.__compconfig = {
-            'comp': {
-                'kind': 'needed',
-                'process': 'cat'
-            }
-        }
-        self._tmp_time = None
-        self._tmp_sleep = None
-        self._tmp_module_cc_session = None
-        self._tmp_cc_session = None
-
-    def tearDown(self):
-        if self._tmp_time is not None:
-            time.time = self._tmp_time
-        if self._tmp_sleep is not None:
-            time.sleep = self._tmp_sleep
-        if self._tmp_module_cc_session is not None:
-            isc.config.ModuleCCSession = self._tmp_module_cc_session
-        if self._tmp_cc_session is not None:
-            isc.cc.Session = self._tmp_cc_session
-
-    def __unary_hook(self, param):
-        """
-        A hook function that stores the parameter for later examination.
-        """
-        self.__param = param
-
-    def __nullary_hook(self):
-        """
-        A hook function that notes down it was called.
-        """
-        self.__called = True
-
-    def __check_core(self, config):
-        """
-        A function checking that the config contains parts for the valid
-        core component configuration.
-        """
-        self.assertIsNotNone(config)
-        for component in ['sockcreator', 'msgq', 'cfgmgr']:
-            self.assertTrue(component in config)
-            self.assertEqual(component, config[component]['special'])
-            self.assertEqual('core', config[component]['kind'])
-
-    def __check_extended(self, config):
-        """
-        This checks that the config contains the core and one more component.
-        """
-        self.__check_core(config)
-        self.assertTrue('comp' in config)
-        self.assertEqual('cat', config['comp']['process'])
-        self.assertEqual('needed', config['comp']['kind'])
-        self.assertEqual(4, len(config))
-
-    def test_correct_run(self):
-        """
-        Test the situation when we run in usual scenario, nothing fails,
-        we just start, reconfigure and then stop peacefully.
-        """
-        init = MockInit()
-        # Start it
-        orig = init._component_configurator.startup
-        init._component_configurator.startup = self.__unary_hook
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        init._component_configurator.startup = orig
-        self.__check_core(self.__param)
-        self.assertEqual(3, len(self.__param))
-
-        # Reconfigure it
-        self.__param = None
-        orig = init._component_configurator.reconfigure
-        init._component_configurator.reconfigure = self.__unary_hook
-        # Otherwise it does not work
-        init.runnable = True
-        init.config_handler({'components': self.__compconfig})
-        self.__check_extended(self.__param)
-        currconfig = self.__param
-        # If we reconfigure it, but it does not contain the components part,
-        # nothing is called
-        init.config_handler({})
-        self.assertEqual(self.__param, currconfig)
-        self.__param = None
-        init._component_configurator.reconfigure = orig
-        # Check a configuration that messes up the core components is rejected.
-        compconf = dict(self.__compconfig)
-        compconf['msgq'] = { 'process': 'echo' }
-        result = init.config_handler({'components': compconf})
-        # Check it rejected it
-        self.assertEqual(1, result['result'][0])
-
-        # We can't call shutdown, that one relies on the stuff in main
-        # We check somewhere else that the shutdown is actually called
-        # from there (the test_kills).
-
-    def __real_test_kill(self, nokill=False, ex_on_kill=None):
-        """
-        Helper function that does the actual kill functionality testing.
-        """
-        init = MockInit()
-        init.nokill = nokill
-
-        killed = []
-        class ImmortalComponent:
-            """
-            An immortal component. It does not stop when it is told so
-            (anyway it is not told so). It does not die if it is killed
-            the first time. It dies only when killed forcefully.
-            """
-            def __init__(self):
-                # number of kill() calls, preventing infinite loop.
-                self.__call_count = 0
-
-            def kill(self, forceful=False):
-                self.__call_count += 1
-                if self.__call_count > 2:
-                    raise Exception('Too many calls to ImmortalComponent.kill')
-
-                killed.append(forceful)
-                if ex_on_kill is not None:
-                    # If exception is given by the test, raise it here.
-                    # In the case of ESRCH, the process should have gone
-                    # somehow, so we clear the components.
-                    if ex_on_kill.errno == errno.ESRCH:
-                        init.components = {}
-                    raise ex_on_kill
-                if forceful:
-                    init.components = {}
-            def pid(self):
-                return 1
-            def name(self):
-                return "Immortal"
-        init.components = {}
-        init.register_process(1, ImmortalComponent())
-
-        # While at it, we check the configurator shutdown is actually called
-        orig = init._component_configurator.shutdown
-        init._component_configurator.shutdown = self.__nullary_hook
-        self.__called = False
-
-        init.ccs = MockModuleCCSession()
-        self.assertFalse(init.ccs.stopped)
-
-        init.shutdown()
-
-        self.assertTrue(init.ccs.stopped)
-
-        # Here, killed is an array where False is added if SIGTERM
-        # should be sent, or True if SIGKILL should be sent, in order in
-        # which they're sent.
-        if nokill:
-            self.assertEqual([], killed)
-        else:
-            if ex_on_kill is not None:
-                self.assertEqual([False], killed)
-            else:
-                self.assertEqual([False, True], killed)
-
-        self.assertTrue(self.__called)
-
-        init._component_configurator.shutdown = orig
-
-    def test_kills(self):
-        """
-        Test that b10-init kills components which don't want to stop.
-        """
-        self.__real_test_kill()
-
-    def test_kill_fail(self):
-        """Test cases where kill() results in an exception due to OS error.
-
-        The behavior should be different for EPERM, so we test two cases.
-
-        """
-
-        ex = OSError()
-        ex.errno, ex.strerror = errno.ESRCH, 'No such process'
-        self.__real_test_kill(ex_on_kill=ex)
-
-        ex.errno, ex.strerror = errno.EPERM, 'Operation not permitted'
-        self.__real_test_kill(ex_on_kill=ex)
-
-    def test_nokill(self):
-        """
-        Test that b10-init *doesn't* kill components which don't want to
-        stop, when asked not to (by passing the --no-kill option which
-        sets init.nokill to True).
-        """
-        self.__real_test_kill(True)
-
-    def test_component_shutdown(self):
-        """
-        Test the component_shutdown sets all variables accordingly.
-        """
-        init = MockInit()
-        self.assertRaises(Exception, init.component_shutdown, 1)
-        self.assertEqual(1, init.exitcode)
-        init._Init__started = True
-        init.component_shutdown(2)
-        self.assertEqual(2, init.exitcode)
-        self.assertFalse(init.runnable)
-
-    def test_init_config(self):
-        """
-        Test initial configuration is loaded.
-        """
-        init = MockInit()
-        # Start it
-        init._component_configurator.reconfigure = self.__unary_hook
-        # We need to return the original read_bind10_config
-        init._read_bind10_config = lambda: Init._read_bind10_config(init)
-        # And provide a session to read the data from
-        class CC:
-            pass
-        init.ccs = CC()
-        init.ccs.get_full_config = lambda: {'components': self.__compconfig}
-        init.wait_msgq = lambda: None
-        init.start_all_components()
-        self.__check_extended(self.__param)
-
-    def __setup_restart(self, init, component):
-        '''Common procedure for restarting a component used below.'''
-        init.components_to_restart = { component }
-        component.restarted = False
-        init.restart_processes()
-
-    def test_restart_processes(self):
-        '''Check some behavior on restarting processes.'''
-        init = MockInit()
-        init.runnable = True
-        component = MockComponent('test', 53)
-
-        # A component to be restarted will actually be restarted iff it's
-        # in the configurator's configuration.
-        # We bruteforce the configurator internal below; ugly, but the easiest
-        # way for the test.
-        init._component_configurator._components['test'] = (None, component)
-        self.__setup_restart(init, component)
-        self.assertTrue(component.restarted)
-        self.assertNotIn(component, init.components_to_restart)
-
-        # Remove the component from the configuration.  It won't be restarted
-        # even if scheduled, nor will remain in the to-be-restarted list.
-        del init._component_configurator._components['test']
-        self.__setup_restart(init, component)
-        self.assertFalse(component.restarted)
-        self.assertNotIn(component, init.components_to_restart)
-
-    def test_get_processes(self):
-        '''Test that procsses are returned correctly, sorted by pid.'''
-        init = MockInit()
-
-        pids = list(range(0, 20))
-        random.shuffle(pids)
-
-        for i in range(0, 20):
-            pid = pids[i]
-            component = MockComponent('test' + str(pid), pid,
-                                      'Test' + str(pid))
-            init.components[pid] = component
-
-        process_list = init.get_processes()
-        self.assertEqual(20, len(process_list))
-
-        last_pid = -1
-        for process in process_list:
-            pid = process[0]
-            self.assertLessEqual(last_pid, pid)
-            last_pid = pid
-            self.assertEqual([pid, 'test' + str(pid), 'Test' + str(pid)],
-                             process)
-
-    def _test_reap_children_helper(self, runnable, is_running, failed):
-        '''Construct a Init instance, set various data in it according to
-        passed args and check if the component was added to the list of
-        components to restart.'''
-        init = MockInit()
-        init.runnable = runnable
-
-        component = MockComponent('test', 53)
-        component.running = is_running
-        component.has_failed = failed
-        init.components[53] = component
-
-        self.assertNotIn(component, init.components_to_restart)
-
-        init.reap_children()
-
-        if runnable and is_running and not failed:
-            self.assertIn(component, init.components_to_restart)
-        else:
-            self.assertEqual([], init.components_to_restart)
-
-    def test_reap_children(self):
-        '''Test that children are queued to be restarted when they ask for it.'''
-        # test various combinations of 3 booleans
-        # (Init.runnable, component.is_running(), component.failed())
-        self._test_reap_children_helper(False, False, False)
-        self._test_reap_children_helper(False, False, True)
-        self._test_reap_children_helper(False, True,  False)
-        self._test_reap_children_helper(False, True,  True)
-        self._test_reap_children_helper(True,  False, False)
-        self._test_reap_children_helper(True,  False, True)
-        self._test_reap_children_helper(True,  True,  False)
-        self._test_reap_children_helper(True,  True,  True)
-
-        # setup for more tests below
-        init = MockInit()
-        init.runnable = True
-        component = MockComponent('test', 53)
-        init.components[53] = component
-
-        # case where the returned pid is unknown to us. nothing should
-        # happen then.
-        init.get_process_exit_status_called = False
-        init._get_process_exit_status = init._get_process_exit_status_unknown_pid
-        init.components_to_restart = []
-        # this should do nothing as the pid is unknown
-        init.reap_children()
-        self.assertEqual([], init.components_to_restart)
-
-        # case where init._get_process_exit_status() raises OSError with
-        # errno.ECHILD
-        init._get_process_exit_status = \
-            init._get_process_exit_status_raises_oserror_echild
-        init.components_to_restart = []
-        # this should catch and handle the OSError
-        init.reap_children()
-        self.assertEqual([], init.components_to_restart)
-
-        # case where init._get_process_exit_status() raises OSError with
-        # errno other than ECHILD
-        init._get_process_exit_status = \
-            init._get_process_exit_status_raises_oserror_other
-        with self.assertRaises(OSError):
-            init.reap_children()
-
-        # case where init._get_process_exit_status() raises something
-        # other than OSError
-        init._get_process_exit_status = \
-            init._get_process_exit_status_raises_other
-        with self.assertRaises(Exception):
-            init.reap_children()
-
-    def test_kill_started_components(self):
-        '''Test that started components are killed.'''
-        init = MockInit()
-
-        component = MockComponent('test', 53, 'Test')
-        init.components[53] = component
-
-        self.assertEqual([[53, 'test', 'Test']], init.get_processes())
-        init.kill_started_components()
-        self.assertEqual([], init.get_processes())
-        self.assertTrue(component.forceful)
-
-    def _start_msgq_helper(self, init, verbose):
-        init.verbose = verbose
-        pi = init.start_msgq()
-        self.assertEqual('b10-msgq', pi.name)
-        self.assertEqual(['b10-msgq'], pi.args)
-        self.assertEqual(pi.dev_null_stdout, not verbose)
-        self.assertEqual(pi.dev_null_stderr, not verbose)
-        self.assertEqual({'FOO': 'an env string'}, pi.env)
-
-        # this is set by ProcessInfo.spawn()
-        self.assertEqual(42147, pi.pid)
-
-    def test_start_msgq(self):
-        '''Test that b10-msgq is started.'''
-        init = MockInitSimple()
-        init.c_channel_env = {'FOO': 'an env string'}
-        init._run_under_unittests = True
-
-        # use the MockProcessInfo creator
-        init._make_process_info = init._make_mock_process_info
-
-        # non-verbose case
-        self._start_msgq_helper(init, False)
-
-        # verbose case
-        self._start_msgq_helper(init, True)
-
-    def test_start_msgq_timeout(self):
-        '''Test that b10-msgq startup attempts connections several times
-        and times out eventually.'''
-        b10_init = MockInitSimple()
-        b10_init.c_channel_env = {}
-        # set the timeout to an arbitrary pre-determined value (which
-        # code below depends on)
-        b10_init.msgq_timeout = 1
-        b10_init._run_under_unittests = False
-
-        # use the MockProcessInfo creator
-        b10_init._make_process_info = b10_init._make_mock_process_info
-
-        global attempts
-        global tsec
-        attempts = 0
-        tsec = 0
-        self._tmp_time = time.time
-        self._tmp_sleep = time.sleep
-        def _my_time():
-            global attempts
-            global tsec
-            attempts += 1
-            return tsec
-        def _my_sleep(nsec):
-            global tsec
-            tsec += nsec
-        time.time = _my_time
-        time.sleep = _my_sleep
-
-        global cc_sub
-        cc_sub = None
-        class DummySessionAlwaysFails():
-            def __init__(self, socket_file):
-                raise isc.cc.session.SessionError('Connection fails')
-            def group_subscribe(self, s):
-                global cc_sub
-                cc_sub = s
-
-        isc.cc.Session = DummySessionAlwaysFails
-
-        with self.assertRaises(init.CChannelConnectError):
-            # An exception will be thrown here when it eventually times
-            # out.
-            pi = b10_init.start_msgq()
-
-        # time.time() should be called 12 times within the while loop:
-        # starting from 0, and 11 more times from 0.1 to 1.1. There's
-        # another call to time.time() outside the loop, which makes it
-        # 13.
-        self.assertEqual(attempts, 13)
-
-        # group_subscribe() should not have been called here.
-        self.assertIsNone(cc_sub)
-
-        global cc_socket_file
-        cc_socket_file = None
-        cc_sub = None
-        class DummySession():
-            def __init__(self, socket_file):
-                global cc_socket_file
-                cc_socket_file = socket_file
-            def group_subscribe(self, s):
-                global cc_sub
-                cc_sub = s
-
-        isc.cc.Session = DummySession
-
-        # reset values
-        attempts = 0
-        tsec = 0
-
-        pi = b10_init.start_msgq()
-
-        # just one attempt, but 2 calls to time.time()
-        self.assertEqual(attempts, 2)
-
-        self.assertEqual(cc_socket_file, b10_init.msgq_socket_file)
-        self.assertEqual(cc_sub, 'Init')
-
-        # isc.cc.Session, time.time() and time.sleep() are restored
-        # during tearDown().
-
-    def _start_cfgmgr_helper(self, init, data_path, filename, clear_config):
-        expect_args = ['b10-cfgmgr']
-        if data_path is not None:
-            init.data_path = data_path
-            expect_args.append('--data-path=' + data_path)
-        if filename is not None:
-            init.config_filename = filename
-            expect_args.append('--config-filename=' + filename)
-        if clear_config:
-            init.clear_config = clear_config
-            expect_args.append('--clear-config')
-
-        pi = init.start_cfgmgr()
-        self.assertEqual('b10-cfgmgr', pi.name)
-        self.assertEqual(expect_args, pi.args)
-        self.assertEqual({'TESTENV': 'A test string'}, pi.env)
-
-        # this is set by ProcessInfo.spawn()
-        self.assertEqual(42147, pi.pid)
-
-    def test_wait_msgq(self):
-        """
-        Test we can wait for msgq to provide its own alias.
-
-        It is not available the first time, the second it is.
-        """
-        class RpcSession:
-            def __init__(self):
-                # Not yet called
-                self.called = 0
-
-            def rpc_call(self, command, recipient):
-                self.called += 1
-                if self.called == 1:
-                    raise isc.config.RPCRecipientMissing("Not yet")
-                elif self.called == 2:
-                    raise isc.config.RPCError(1, "What?")
-                else:
-                    raise Exception("Called too many times")
-
-        init = MockInitSimple()
-        init.wait_time = 1
-        init.ccs = RpcSession()
-        init.wait_msgq()
-        self.assertEqual(2, init.ccs.called)
-
-    def test_wait_msgq_fail(self):
-        """
-        Test the wait_msgq fails in case the msgq does not appear
-        after so many attempts.
-        """
-        class RpcSession:
-            def __init__(self):
-                self.called = 0
-
-            def rpc_call(self, command, recipient):
-                self.called += 1
-                raise isc.config.RPCRecipientMissing("Not yet")
-
-        b10init = MockInitSimple()
-        b10init.wait_time = 1
-        b10init.ccs = RpcSession()
-        self.assertRaises(init.ProcessStartError, b10init.wait_msgq)
-        self.assertEqual(10, b10init.ccs.called)
-
-    def test_start_cfgmgr(self):
-        '''Test that b10-cfgmgr is started.'''
-        class DummySession():
-            def __init__(self):
-                self._tries = 0
-            def group_recvmsg(self):
-                self._tries += 1
-                # return running on the 3rd try onwards
-                if self._tries >= 3:
-                    return ({'running': 'ConfigManager'}, None)
-                else:
-                    return ({}, None)
-
-        init = MockInitSimple()
-        init.c_channel_env = {'TESTENV': 'A test string'}
-        init.cc_session = DummySession()
-        init.wait_time = 5
-
-        # use the MockProcessInfo creator
-        init._make_process_info = init._make_mock_process_info
-
-        global attempts
-        attempts = 0
-        self._tmp_sleep = time.sleep
-        def _my_sleep(nsec):
-            global attempts
-            attempts += 1
-        time.sleep = _my_sleep
-
-        # defaults
-        self._start_cfgmgr_helper(init, None, None, False)
-
-        # check that 2 attempts were made. on the 3rd attempt,
-        # process_running() returns that ConfigManager is running.
-        self.assertEqual(attempts, 2)
-
-        # data_path is specified
-        self._start_cfgmgr_helper(init, '/var/lib/test', None, False)
-
-        # config_filename is specified. Because `init` is not
-        # reconstructed, data_path is retained from the last call to
-        # _start_cfgmgr_helper().
-        self._start_cfgmgr_helper(init, '/var/lib/test', 'foo.cfg', False)
-
-        # clear_config is specified. Because `init` is not reconstructed,
-        # data_path and config_filename are retained from the last call
-        # to _start_cfgmgr_helper().
-        self._start_cfgmgr_helper(init, '/var/lib/test', 'foo.cfg', True)
-
-    def test_start_cfgmgr_timeout(self):
-        '''Test that b10-cfgmgr startup attempts connections several times
-        and times out eventually.'''
-        class DummySession():
-            def group_recvmsg(self):
-                return (None, None)
-        b10_init = MockInitSimple()
-        b10_init.c_channel_env = {}
-        b10_init.cc_session = DummySession()
-        # set wait_time to an arbitrary pre-determined value (which code
-        # below depends on)
-        b10_init.wait_time = 2
-
-        # use the MockProcessInfo creator
-        b10_init._make_process_info = b10_init._make_mock_process_info
-
-        global attempts
-        attempts = 0
-        self._tmp_sleep = time.sleep
-        def _my_sleep(nsec):
-            global attempts
-            attempts += 1
-        time.sleep = _my_sleep
-
-        # We just check that an exception was thrown, and that several
-        # attempts were made to connect.
-        with self.assertRaises(init.ProcessStartError):
-            pi = b10_init.start_cfgmgr()
-
-        # 2 seconds of attempts every 1 second should result in 2 attempts
-        self.assertEqual(attempts, 2)
-
-        # time.sleep() is restored during tearDown().
-
-    def test_start_ccsession(self):
-        '''Test that CC session is started.'''
-        class DummySession():
-            def __init__(self, specfile, config_handler, command_handler,
-                         socket_file):
-                self.specfile = specfile
-                self.config_handler = config_handler
-                self.command_handler = command_handler
-                self.socket_file = socket_file
-                self.started = False
-            def start(self):
-                self.started = True
-        b10_init = MockInitSimple()
-        self._tmp_module_cc_session = isc.config.ModuleCCSession
-        isc.config.ModuleCCSession = DummySession
-
-        b10_init.start_ccsession({})
-        self.assertEqual(init.SPECFILE_LOCATION, b10_init.ccs.specfile)
-        self.assertEqual(b10_init.config_handler, b10_init.ccs.config_handler)
-        self.assertEqual(b10_init.command_handler,
-                         b10_init.ccs.command_handler)
-        self.assertEqual(b10_init.msgq_socket_file, b10_init.ccs.socket_file)
-        self.assertTrue(b10_init.ccs.started)
-
-        # isc.config.ModuleCCSession is restored during tearDown().
-
-    def test_start_process(self):
-        '''Test that processes can be started.'''
-        init = MockInit()
-
-        # use the MockProcessInfo creator
-        init._make_process_info = init._make_mock_process_info
-
-        pi = init.start_process('Test Process', ['/bin/true'], {})
-        self.assertEqual('Test Process', pi.name)
-        self.assertEqual(['/bin/true'], pi.args)
-        self.assertEqual({}, pi.env)
-
-        # this is set by ProcessInfo.spawn()
-        self.assertEqual(42147, pi.pid)
-
-    def test_register_process(self):
-        '''Test that processes can be registered with Init.'''
-        init = MockInit()
-        component = MockComponent('test', 53, 'Test')
-
-        self.assertFalse(53 in init.components)
-        init.register_process(53, component)
-        self.assertTrue(53 in init.components)
-        self.assertEqual(init.components[53].name(), 'test')
-        self.assertEqual(init.components[53].pid(), 53)
-        self.assertEqual(init.components[53].address(), 'Test')
-
-    def _start_simple_helper(self, init, verbose):
-        init.verbose = verbose
-
-        args = ['/bin/true']
-        if verbose:
-            args.append('-v')
-
-        init.start_simple('/bin/true')
-        self.assertEqual('/bin/true', init.started_process_name)
-        self.assertEqual(args, init.started_process_args)
-        self.assertEqual({'TESTENV': 'A test string'}, init.started_process_env)
-
-    def test_start_simple(self):
-        '''Test simple process startup.'''
-        init = MockInitSimple()
-        init.c_channel_env = {'TESTENV': 'A test string'}
-
-        # non-verbose case
-        self._start_simple_helper(init, False)
-
-        # verbose case
-        self._start_simple_helper(init, True)
-
-    def _start_auth_helper(self, init, verbose):
-        init.verbose = verbose
-
-        args = ['b10-auth']
-        if verbose:
-            args.append('-v')
-
-        init.start_auth()
-        self.assertEqual('b10-auth', init.started_process_name)
-        self.assertEqual(args, init.started_process_args)
-        self.assertEqual({'FOO': 'an env string'}, init.started_process_env)
-
-    def test_start_auth(self):
-        '''Test that b10-auth is started.'''
-        init = MockInitSimple()
-        init.c_channel_env = {'FOO': 'an env string'}
-
-        # non-verbose case
-        self._start_auth_helper(init, False)
-
-        # verbose case
-        self._start_auth_helper(init, True)
-
-    def _start_resolver_helper(self, init, verbose):
-        init.verbose = verbose
-
-        args = ['b10-resolver']
-        if verbose:
-            args.append('-v')
-
-        init.start_resolver()
-        self.assertEqual('b10-resolver', init.started_process_name)
-        self.assertEqual(args, init.started_process_args)
-        self.assertEqual({'BAR': 'an env string'}, init.started_process_env)
-
-    def test_start_resolver(self):
-        '''Test that b10-resolver is started.'''
-        init = MockInitSimple()
-        init.c_channel_env = {'BAR': 'an env string'}
-
-        # non-verbose case
-        self._start_resolver_helper(init, False)
-
-        # verbose case
-        self._start_resolver_helper(init, True)
-
-    def _start_cmdctl_helper(self, init, verbose, port = None):
-        init.verbose = verbose
-
-        args = ['b10-cmdctl']
-
-        if port is not None:
-            init.cmdctl_port = port
-            args.append('--port=9353')
-
-        if verbose:
-            args.append('-v')
-
-        init.start_cmdctl()
-        self.assertEqual('b10-cmdctl', init.started_process_name)
-        self.assertEqual(args, init.started_process_args)
-        self.assertEqual({'BAZ': 'an env string'}, init.started_process_env)
-
-    def test_start_cmdctl(self):
-        '''Test that b10-cmdctl is started.'''
-        init = MockInitSimple()
-        init.c_channel_env = {'BAZ': 'an env string'}
-
-        # non-verbose case
-        self._start_cmdctl_helper(init, False)
-
-        # verbose case
-        self._start_cmdctl_helper(init, True)
-
-        # with port, non-verbose case
-        self._start_cmdctl_helper(init, False, 9353)
-
-        # with port, verbose case
-        self._start_cmdctl_helper(init, True, 9353)
-
-    def test_socket_data(self):
-        '''Test that Init._socket_data works as expected.'''
-        class MockSock:
-            def __init__(self, fd, throw):
-                self.fd = fd
-                self.throw = throw
-                self.buf = b'Hello World.\nYou are so nice today.\nXX'
-                self.i = 0
-
-            def recv(self, bufsize, flags = 0):
-                if bufsize != 1:
-                    raise Exception('bufsize != 1')
-                if flags != socket.MSG_DONTWAIT:
-                    raise Exception('flags != socket.MSG_DONTWAIT')
-                # after 15 recv()s, throw a socket.error with EAGAIN to
-                # get _socket_data() to save back what's been read. The
-                # number 15 is arbitrarily chosen, but the checks then
-                # depend on this being 15, i.e., if you adjust this
-                # number, you may have to adjust the checks below too.
-                if self.throw and self.i > 15:
-                    raise socket.error(errno.EAGAIN, 'Try again')
-                if self.i >= len(self.buf):
-                    return b'';
-                t = self.i
-                self.i += 1
-                return self.buf[t:t+1]
-
-            def close(self):
-                return
-
-        class MockInitSocketData(Init):
-            def __init__(self, throw):
-                self._unix_sockets = {42: (MockSock(42, throw), b'')}
-                self.requests = []
-                self.dead = []
-
-            def socket_request_handler(self, previous, sock):
-                self.requests.append({sock.fd: previous})
-
-            def socket_consumer_dead(self, sock):
-                self.dead.append(sock.fd)
-
-        # Case where we get data every time we call recv()
-        init = MockInitSocketData(False)
-        init._socket_data(42)
-        self.assertEqual(init.requests,
-                         [{42: b'Hello World.'},
-                          {42: b'You are so nice today.'}])
-        self.assertEqual(init.dead, [42])
-        self.assertEqual({}, init._unix_sockets)
-
-        # Case where socket.recv() raises EAGAIN. In this case, the
-        # routine is supposed to save what it has back to
-        # Init._unix_sockets.
-        init = MockInitSocketData(True)
-        init._socket_data(42)
-        self.assertEqual(init.requests, [{42: b'Hello World.'}])
-        self.assertFalse(init.dead)
-        self.assertEqual(len(init._unix_sockets), 1)
-        self.assertEqual(init._unix_sockets[42][1], b'You')
-
-    def test_startup(self):
-        '''Test that Init.startup() handles failures properly.'''
-        class MockInitStartup(Init):
-            def __init__(self, throw):
-                self.throw = throw
-                self.started = False
-                self.killed = False
-                self.msgq_socket_file = None
-                self.curproc = 'myproc'
-                self.runnable = False
-
-            def start_all_components(self):
-                self.started = True
-                if self.throw is True:
-                    raise Exception('Assume starting components has failed.')
-                elif self.throw:
-                    raise self.throw
-
-            def kill_started_components(self):
-                self.killed = True
-
-        class DummySession():
-            def __init__(self, socket_file):
-                raise isc.cc.session.SessionError('This is the expected case.')
-
-        class DummySessionSocketExists():
-            def __init__(self, socket_file):
-                # simulate that connect passes
-                return
-
-        isc.cc.Session = DummySession
-
-        # All is well case, where all components are started
-        # successfully. We check that the actual call to
-        # start_all_components() is made, and Init.runnable is true.
-        b10_init = MockInitStartup(False)
-        r = b10_init.startup()
-        self.assertIsNone(r)
-        self.assertTrue(b10_init.started)
-        self.assertFalse(b10_init.killed)
-        self.assertTrue(b10_init.runnable)
-        self.assertEqual({}, b10_init.c_channel_env)
-
-        # Case where starting components fails. We check that
-        # kill_started_components() is called right after, and
-        # Init.runnable is not modified.
-        b10_init = MockInitStartup(True)
-        r = b10_init.startup()
-        # r contains an error message
-        self.assertEqual(r, 'Unable to start myproc: Assume starting components has failed.')
-        self.assertTrue(b10_init.started)
-        self.assertTrue(b10_init.killed)
-        self.assertFalse(b10_init.runnable)
-        self.assertEqual({}, b10_init.c_channel_env)
-
-        # Check if msgq_socket_file is carried over
-        b10_init = MockInitStartup(False)
-        b10_init.msgq_socket_file = 'foo'
-        r = b10_init.startup()
-        self.assertEqual({'BIND10_MSGQ_SOCKET_FILE': 'foo'},
-                         b10_init.c_channel_env)
-
-        # Check failure of changing user results in a different message
-        b10_init = MockInitStartup(init.ChangeUserError('failed to chusr'))
-        r = b10_init.startup()
-        self.assertIn('failed to chusr', r)
-        self.assertTrue(b10_init.killed)
-
-        # Check the case when socket file already exists
-        isc.cc.Session = DummySessionSocketExists
-        b10_init = MockInitStartup(False)
-        r = b10_init.startup()
-        self.assertIn('already running', r)
-
-        # isc.cc.Session is restored during tearDown().
-
-class SocketSrvTest(unittest.TestCase):
-    """
-    This tests some methods of b10-init related to the unix domain sockets
-    used to transfer other sockets to applications.
-    """
-    def setUp(self):
-        """
-        Create the b10-init to test, testdata and backup some functions.
-        """
-        self.__b10_init = Init()
-        self.__select_backup = init.select.select
-        self.__select_called = None
-        self.__socket_data_called = None
-        self.__consumer_dead_called = None
-        self.__socket_request_handler_called = None
-
-    def tearDown(self):
-        """
-        Restore functions.
-        """
-        init.select.select = self.__select_backup
-
-    class __FalseSocket:
-        """
-        A mock socket for the select and accept and stuff like that.
-        """
-        def __init__(self, owner, fileno=42):
-            self.__owner = owner
-            self.__fileno = fileno
-            self.data = None
-            self.closed = False
-
-        def fileno(self):
-            return self.__fileno
-
-        def accept(self):
-            return (self.__class__(self.__owner, 13), "/path/to/socket")
-
-        def recv(self, bufsize, flags=0):
-            self.__owner.assertEqual(1, bufsize)
-            self.__owner.assertEqual(socket.MSG_DONTWAIT, flags)
-            if isinstance(self.data, socket.error):
-                raise self.data
-            elif self.data is not None:
-                if len(self.data):
-                    result = self.data[0:1]
-                    self.data = self.data[1:]
-                    return result
-                else:
-                    raise socket.error(errno.EAGAIN, "Would block")
-            else:
-                return b''
-
-        def close(self):
-            self.closed = True
-
-    class __CCS:
-        """
-        A mock CCS, just to provide the socket file number.
-        """
-        class __Socket:
-            def fileno(self):
-                return 1
-        def get_socket(self):
-            return self.__Socket()
-
-    def __select_accept(self, r, w, x, t):
-        self.__select_called = (r, w, x, t)
-        return ([42], [], [])
-
-    def __select_data(self, r, w, x, t):
-        self.__select_called = (r, w, x, t)
-        return ([13], [], [])
-
-    def __accept(self):
-        """
-        Hijack the accept method of the b10-init.
-
-        Notes down it was called and stops b10-init.
-        """
-        self.__accept_called = True
-        self.__b10_init.runnable = False
-
-    def test_srv_accept_called(self):
-        """
-        Test that the _srv_accept method of b10-init is called when the
-        listening socket is readable.
-        """
-        self.__b10_init.runnable = True
-        self.__b10_init._srv_socket = self.__FalseSocket(self)
-        self.__b10_init._srv_accept = self.__accept
-        self.__b10_init.ccs = self.__CCS()
-        init.select.select = self.__select_accept
-        self.__b10_init.run(2)
-        # It called the accept
-        self.assertTrue(self.__accept_called)
-        # And the select had the right parameters
-        self.assertEqual(([2, 1, 42], [], [], None), self.__select_called)
-
-    def test_srv_accept(self):
-        """
-        Test how the _srv_accept method works.
-        """
-        self.__b10_init._srv_socket = self.__FalseSocket(self)
-        self.__b10_init._srv_accept()
-        # After we accepted, a new socket is added there
-        socket = self.__b10_init._unix_sockets[13][0]
-        # The socket is properly stored there
-        self.assertTrue(isinstance(socket, self.__FalseSocket))
-        # And the buffer (yet empty) is there
-        self.assertEqual({13: (socket, b'')}, self.__b10_init._unix_sockets)
-
-    def __socket_data(self, socket):
-        self.__b10_init.runnable = False
-        self.__socket_data_called = socket
-
-    def test_socket_data(self):
-        """
-        Test that a socket that wants attention gets it.
-        """
-        self.__b10_init._srv_socket = self.__FalseSocket(self)
-        self.__b10_init._socket_data = self.__socket_data
-        self.__b10_init.ccs = self.__CCS()
-        self.__b10_init._unix_sockets = {13: (self.__FalseSocket(self, 13), b'')}
-        self.__b10_init.runnable = True
-        init.select.select = self.__select_data
-        self.__b10_init.run(2)
-        self.assertEqual(13, self.__socket_data_called)
-        self.assertEqual(([2, 1, 42, 13], [], [], None), self.__select_called)
-
-    def __prepare_data(self, data):
-        socket = self.__FalseSocket(self, 13)
-        self.__b10_init._unix_sockets = {13: (socket, b'')}
-        socket.data = data
-        self.__b10_init.socket_consumer_dead = self.__consumer_dead
-        self.__b10_init.socket_request_handler = self.__socket_request_handler
-        return socket
-
-    def __consumer_dead(self, socket):
-        self.__consumer_dead_called = socket
-
-    def __socket_request_handler(self, token, socket):
-        self.__socket_request_handler_called = (token, socket)
-
-    def test_socket_closed(self):
-        """
-        Test that a socket is removed and the socket_consumer_dead is called
-        when it is closed.
-        """
-        socket = self.__prepare_data(None)
-        self.__b10_init._socket_data(13)
-        self.assertEqual(socket, self.__consumer_dead_called)
-        self.assertEqual({}, self.__b10_init._unix_sockets)
-        self.assertTrue(socket.closed)
-
-    def test_socket_short(self):
-        """
-        Test that if there's not enough data to get the whole socket, it is
-        kept there, but nothing is called.
-        """
-        socket = self.__prepare_data(b'tok')
-        self.__b10_init._socket_data(13)
-        self.assertEqual({13: (socket, b'tok')}, self.__b10_init._unix_sockets)
-        self.assertFalse(socket.closed)
-        self.assertIsNone(self.__consumer_dead_called)
-        self.assertIsNone(self.__socket_request_handler_called)
-
-    def test_socket_continue(self):
-        """
-        Test that we call the token handling function when the whole token
-        comes. This test pretends to continue reading where the previous one
-        stopped.
-        """
-        socket = self.__prepare_data(b"en\nanothe")
-        # The data to finish
-        self.__b10_init._unix_sockets[13] = (socket, b'tok')
-        self.__b10_init._socket_data(13)
-        self.assertEqual({13: (socket, b'anothe')}, self.__b10_init._unix_sockets)
-        self.assertFalse(socket.closed)
-        self.assertIsNone(self.__consumer_dead_called)
-        self.assertEqual((b'token', socket),
-                         self.__socket_request_handler_called)
-
-    def test_broken_socket(self):
-        """
-        If the socket raises an exception during the read other than EAGAIN,
-        it is broken and we remove it.
-        """
-        sock = self.__prepare_data(socket.error(errno.ENOMEM,
-            "There's more memory available, but not for you"))
-        self.__b10_init._socket_data(13)
-        self.assertEqual(sock, self.__consumer_dead_called)
-        self.assertEqual({}, self.__b10_init._unix_sockets)
-        self.assertTrue(sock.closed)
-
-    def test_log_starting(self):
-        """
-        Checks the log_starting call doesn't raise any errors
-        (does not check actual log output)
-        """
-        self.__b10_init.log_starting("foo")
-        self.__b10_init.log_starting("foo", 1)
-        self.__b10_init.log_starting("foo", 1, "192.0.2.1")
-
-
-class TestFunctions(unittest.TestCase):
-    def setUp(self):
-        self.lockfile_testpath = \
-            "@abs_top_builddir@/src/bin/bind10/tests/lockfile_test"
-        self.assertFalse(os.path.exists(self.lockfile_testpath))
-        os.mkdir(self.lockfile_testpath)
-        self.assertTrue(os.path.isdir(self.lockfile_testpath))
-        self.__isfile_orig = init.os.path.isfile
-        self.__unlink_orig = init.os.unlink
-
-    def tearDown(self):
-        os.rmdir(self.lockfile_testpath)
-        self.assertFalse(os.path.isdir(self.lockfile_testpath))
-        os.environ["B10_LOCKFILE_DIR_FROM_BUILD"] = "@abs_top_builddir@"
-        init.os.path.isfile = self.__isfile_orig
-        init.os.unlink = self.__unlink_orig
-
-    def test_remove_lock_files(self):
-        os.environ["B10_LOCKFILE_DIR_FROM_BUILD"] = self.lockfile_testpath
-
-        # create lockfiles for the testcase
-        lockfiles = ["logger_lockfile"]
-        for f in lockfiles:
-            fname = os.environ["B10_LOCKFILE_DIR_FROM_BUILD"] + '/' + f
-            self.assertFalse(os.path.exists(fname))
-            open(fname, "w").close()
-            self.assertTrue(os.path.isfile(fname))
-
-        # first call should clear up all the lockfiles
-        init.remove_lock_files()
-
-        # check if the lockfiles exist
-        for f in lockfiles:
-            fname = os.environ["B10_LOCKFILE_DIR_FROM_BUILD"] + '/' + f
-            self.assertFalse(os.path.isfile(fname))
-
-        # second call should not assert anyway
-        init.remove_lock_files()
-
-    def test_remove_lock_files_fail(self):
-        # Permission error on unlink is ignored; other exceptions are really
-        # unexpected and propagated.
-        def __raising_unlink(unused, ex):
-            raise ex
-
-        init.os.path.isfile = lambda _: True
-        os_error = OSError()
-        init.os.unlink = lambda f: __raising_unlink(f, os_error)
-
-        os_error.errno = errno.EPERM
-        init.remove_lock_files() # no disruption
-
-        os_error.errno = errno.EACCES
-        init.remove_lock_files() # no disruption
-
-        os_error.errno = errno.ENOENT
-        self.assertRaises(OSError, init.remove_lock_files)
-
-        init.os.unlink = lambda f: __raising_unlink(f, Exception('bad'))
-        self.assertRaises(Exception, init.remove_lock_files)
-
-    def test_get_signame(self):
-        # just test with some samples
-        signame = init.get_signame(signal.SIGTERM)
-        self.assertEqual('SIGTERM', signame)
-        signame = init.get_signame(signal.SIGKILL)
-        self.assertEqual('SIGKILL', signame)
-        # 59426 is hopefully an unused signal on most platforms
-        signame = init.get_signame(59426)
-        self.assertEqual('Unknown signal 59426', signame)
-
-    def test_fatal_signal(self):
-        self.assertIsNone(init.b10_init)
-        init.b10_init = Init()
-        init.b10_init.runnable = True
-        init.fatal_signal(signal.SIGTERM, None)
-        # Now, runnable must be False
-        self.assertFalse(init.b10_init.runnable)
-        init.b10_init = None
-
-if __name__ == '__main__':
-    # store os.environ for test_unchanged_environment
-    original_os_environ = copy.deepcopy(os.environ)
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/bindctl/.gitignore b/src/bin/bindctl/.gitignore
deleted file mode 100644 (file)
index 2d4ffba..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/bindctl
-/bindctl_main.py
-/run_bindctl.sh
-/bindctl.1
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
deleted file mode 100644 (file)
index 1134240..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-SUBDIRS = . tests
-
-bin_SCRIPTS = bindctl
-man_MANS = bindctl.1
-
-EXTRA_DIST = $(man_MANS) bindctl.xml
-
-noinst_SCRIPTS = run_bindctl.sh
-
-python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \
-               mycollections.py command_sets.py
-pythondir = $(pyexecdir)/bindctl
-
-bindctldir = $(pkgdatadir)
-
-CLEANFILES = bindctl bindctl_main.pyc
-DISTCLEANFILES = $(man_MANS)
-
-if GENERATE_DOCS
-
-bindctl.1: bindctl.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/bindctl.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-bindctl: bindctl_main.py
-       $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-              -e "s|@@SYSCONFDIR@@|@sysconfdir@|" \
-              -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bindctl_main.py >$@
-       chmod a+x $@
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/bindctl/README b/src/bin/bindctl/README
deleted file mode 100644 (file)
index a9a185f..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-1. Start bindctl by run command "sh bindctl".
-2. Login to b10-cmdctl with the username and password. The username and password will be saved in file default_user.csv automatically after logining successfully, so next time bindctl will login with the username and password saved in default_user.csv. For more usage information, please turn to "man bindctl".
diff --git a/src/bin/bindctl/TODO b/src/bin/bindctl/TODO
deleted file mode 100644 (file)
index 079d309..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-1. Refactor the code for bindctl.
-2. Update man page for bindctl provided by jreed.
-3. Add more unit tests.
-4. Need Review(When command line syntax is changed later, the following 
-functions should be updated first.):
-        bindcmd.py:
-            apply_config_cmd()
-            _validate_cmd()
-            complete()
-            
-        cmdparse.py:
-            _parse_params
-
-        moduleinfo.py:
-            get_param_name_by_position
-5. Understandable error message when cert_file is missing.
-6. Pick location to save default user. Or use the cfgmgr for this.
-   If the default user is saved in file, its password shouldn't be saved in plaintext.
-7. Need to think of what exactly to do with responses received to commands
-   (currently it simply print the map)
-8. Remove bindctl-source.py.in(replace with bindctl-source.py) when merging to trunk.
diff --git a/src/bin/bindctl/__init__.py b/src/bin/bindctl/__init__.py
deleted file mode 100644 (file)
index 6a7afe1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-__all__ = ['mycollections', 'exception', 'moduleinfo', 'command', 'bindctl']
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
deleted file mode 100644 (file)
index a83775b..0000000
+++ /dev/null
@@ -1,945 +0,0 @@
-# Copyright (C) 2009  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.
-
-"""This module holds the BindCmdInterpreter class. This provides the
-   core functionality for bindctl. It maintains a session with
-   b10-cmdctl, holds local configuration and module information, and
-   handles command line interface commands"""
-
-import sys
-from cmd import Cmd
-from bindctl.exception import *
-from bindctl.moduleinfo import *
-from bindctl.cmdparse import BindCmdParser
-from bindctl import command_sets
-from xml.dom import minidom
-import isc.config
-import isc.cc.data
-import http.client
-import json
-import inspect
-import pprint
-import ssl, socket
-import os, time, random, re
-import os.path
-import getpass
-from hashlib import sha1
-import csv
-import pwd
-import getpass
-import copy
-import errno
-
-try:
-    from collections import OrderedDict
-except ImportError:
-    from bindctl.mycollections import OrderedDict
-
-# if we have readline support, use that, otherwise use normal stdio
-try:
-    import readline
-    # Only consider spaces as word boundaries; identifiers can contain
-    # '/' and '[]', and configuration item names can in theory use any
-    # printable  character. See the discussion in tickets #1345 and
-    # #2254 for more information.
-    readline.set_completer_delims(' ')
-
-    my_readline = readline.get_line_buffer
-except ImportError:
-    my_readline = sys.stdin.readline
-
-# Used for tab-completion of 'identifiers' (i.e. config values)
-# If a command parameter has this name, the tab completion hints
-# are derived from config data
-CFGITEM_IDENTIFIER_PARAM = 'identifier'
-
-CSV_FILE_NAME = 'default_user.csv'
-CONFIG_MODULE_NAME = 'config'
-CONST_BINDCTL_HELP = """
-usage: <module name> <command name> [param1 = value1 [, param2 = value2]]
-Type Tab character to get the hint of module/command/parameters.
-Type \"help(? h)\" for help on bindctl.
-Type \"<module_name> help\" for help on the specific module.
-Type \"<module_name> <command_name> help\" for help on the specific command.
-\nAvailable module names: """
-
-class ValidatedHTTPSConnection(http.client.HTTPSConnection):
-    '''Overrides HTTPSConnection to support certification
-    validation. '''
-    def __init__(self, host, ca_certs):
-        http.client.HTTPSConnection.__init__(self, host)
-        self.ca_certs = ca_certs
-
-    def connect(self):
-        ''' Overrides the connect() so that we do
-        certificate validation. '''
-        sock = socket.create_connection((self.host, self.port),
-                                        self.timeout)
-        if self._tunnel_host:
-            self.sock = sock
-            self._tunnel()
-
-        req_cert = ssl.CERT_NONE
-        if self.ca_certs:
-            req_cert = ssl.CERT_REQUIRED
-        self.sock = ssl.wrap_socket(sock, self.key_file,
-                                    self.cert_file,
-                                    cert_reqs=req_cert,
-                                    ca_certs=self.ca_certs)
-
-class BindCmdInterpreter(Cmd):
-    """simple bindctl example."""
-
-    def __init__(self, server_port='localhost:8080', pem_file=None,
-                 csv_file_dir=None):
-        Cmd.__init__(self)
-        self.location = ""
-        self.prompt_end = '> '
-        if sys.stdin.isatty():
-            self.prompt = self.prompt_end
-        else:
-            self.prompt = ""
-        self.ruler = '-'
-        self.modules = OrderedDict()
-        self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl."))
-        self.server_port = server_port
-        self.conn = ValidatedHTTPSConnection(self.server_port,
-                                             ca_certs=pem_file)
-        self.session_id = self._get_session_id()
-        self.config_data = None
-        if csv_file_dir is not None:
-            self.csv_file_dir = csv_file_dir
-        else:
-            self.csv_file_dir = pwd.getpwnam(getpass.getuser()).pw_dir + \
-                os.sep + '.bind10' + os.sep
-
-    def _print(self, *args):
-        '''Simple wrapper around calls to print that can be overridden in
-           unit tests.'''
-        print(*args)
-
-    def _get_session_id(self):
-        '''Generate one session id for the connection. '''
-        rand = os.urandom(16)
-        now = time.time()
-        session_id = sha1(("%s%s%s" %(rand, now,
-                                      socket.gethostname())).encode())
-        digest = session_id.hexdigest()
-        return digest
-
-    def run(self):
-        '''Parse commands from user and send them to cmdctl.'''
-
-        # Show helper warning about a well known issue.  We only do this
-        # when stdin is attached to a terminal, because otherwise it doesn't
-        # matter and is just noisy, and could even be harmful if the output
-        # is processed by a script that expects a specific format.
-        if my_readline == sys.stdin.readline and sys.stdin.isatty():
-            sys.stdout.write("""\
-WARNING: The Python readline module isn't available, so some command line
-         editing features (including command history management) will not
-         work.  See the BIND 10 guide for more details.\n\n""")
-
-        try:
-            if not self.login_to_cmdctl():
-                return 1
-
-            self.cmdloop()
-            self._print('\nExit from bindctl')
-            return 0
-        except FailToLogin as err:
-            # error already printed when this was raised, ignoring
-            return 1
-        except KeyboardInterrupt:
-            self._print('\nExit from bindctl')
-            return 0
-        except socket.error as err:
-            self._print('Failed to send request, the connection is closed')
-            return 1
-        except http.client.CannotSendRequest:
-            self._print('Can not send request, the connection is busy')
-            return 1
-
-    def _get_saved_user_info(self, dir, file_name):
-        ''' Read all the available username and password pairs saved in
-        file(path is "dir + file_name"), Return value is one list of elements
-        ['name', 'password'], If get information failed, empty list will be
-        returned.'''
-        if (not dir) or (not os.path.exists(dir)):
-            return []
-
-        try:
-            csvfile = None
-            users = []
-            csvfile = open(dir + file_name)
-            users_info = csv.reader(csvfile)
-            for row in users_info:
-                users.append([row[0], row[1]])
-        except (IOError, IndexError) as err:
-            self._print("Error reading saved username and password "
-                        "from %s%s: %s" % (dir, file_name, err))
-        finally:
-            if csvfile:
-                csvfile.close()
-            return users
-
-    def _save_user_info(self, username, passwd, dir, file_name):
-        ''' Save username and password in file "dir + file_name"
-        If it's saved properly, return True, or else return False. '''
-        try:
-            if not os.path.exists(dir):
-                os.mkdir(dir, 0o700)
-
-            csvfilepath = dir + file_name
-            csvfile = open(csvfilepath, 'w')
-            os.chmod(csvfilepath, 0o600)
-            writer = csv.writer(csvfile)
-            writer.writerow([username, passwd])
-            csvfile.close()
-        except IOError as err:
-            self._print("Error saving user information:", err)
-            self._print("user info file name: %s%s" % (dir, file_name))
-            return False
-
-        return True
-
-    def _try_login(self, username, password):
-        '''
-        Attempts to log into cmdctl by sending a POST with the given
-        username and password. On success of the POST (not the login,
-        but the network operation), it returns a tuple (response, data).
-        We check for some failures such as SSL errors and socket errors
-        which could happen due to the environment in which BIND 10 runs.
-        On failure, it raises a FailToLogin exception and prints some
-        information on the failure.  This call is essentially 'private',
-        but made 'protected' for easier testing.
-        '''
-        param = {'username': username, 'password' : password}
-        try:
-            response = self.send_POST('/login', param)
-            data = response.read().decode()
-            # return here (will raise error after try block)
-            return (response, data)
-        except (ssl.SSLError, socket.error) as err:
-            self._print('Error while sending login information:', err)
-            pass
-        raise FailToLogin()
-
-    def login_to_cmdctl(self):
-        '''Login to cmdctl with the username and password given by
-        the user. After the login is sucessful, the username and
-        password will be saved in 'default_user.csv', when run the next
-        time, username and password saved in 'default_user.csv' will be
-        used first.
-        '''
-        # Look at existing username/password combinations and try to log in
-        users = self._get_saved_user_info(self.csv_file_dir, CSV_FILE_NAME)
-        for row in users:
-            response, data = self._try_login(row[0], row[1])
-
-            if response.status == http.client.OK:
-                # Is interactive?
-                if sys.stdin.isatty():
-                    self._print(data + ' login as ' + row[0])
-                return True
-
-        # No valid logins were found, prompt the user for a username/password
-        count = 0
-        if not os.path.exists(self.csv_file_dir + CSV_FILE_NAME):
-            self._print('\nNo stored password file found.\n\n'
-                        'When the system is first set up you need to create '
-                        'at least one user account.\n'
-                        'For information on how to set up a BIND 10 system, '
-                        'please check see the\n'
-                        'BIND 10 Guide: \n\n'
-                        'http://bind10.isc.org/docs/bind10-guide.html#quick-start-auth-dns\n\n'
-
-                        'If a user account has been set up, please check the '
-                        'b10-cmdctl log for other\n'
-                        'information.\n')
-        else:
-            self._print('Login failed: either the user name or password is '
-                        'invalid.\n')
-
-        # If this was not an interactive session do not prompt for login info.
-        if not sys.stdin.isatty():
-            return False
-
-        while True:
-            count = count + 1
-            if count > 3:
-                self._print("Too many authentication failures")
-                return False
-
-            username = input("Username: ")
-            passwd = getpass.getpass()
-
-            response, data = self._try_login(username, passwd)
-            self._print(data)
-
-            if response.status == http.client.OK:
-                self._save_user_info(username, passwd, self.csv_file_dir,
-                                     CSV_FILE_NAME)
-                return True
-
-    def _update_commands(self):
-        '''Update the commands of all modules. '''
-        for module_name in self.config_data.get_config_item_list():
-            self._prepare_module_commands(self.config_data.get_module_spec(module_name))
-
-    def _send_message(self, url, body):
-        headers = {"cookie" : self.session_id}
-        self.conn.request('GET', url, body, headers)
-        res = self.conn.getresponse()
-        return res.status, res.read()
-
-    def send_GET(self, url, body = None):
-        '''Send GET request to cmdctl, session id is send with the name
-        'cookie' in header.
-        '''
-        status, reply_msg = self._send_message(url, body)
-        if status == http.client.UNAUTHORIZED:
-            if self.login_to_cmdctl():
-                # successful, so try send again
-                status, reply_msg = self._send_message(url, body)
-
-        if reply_msg:
-            return json.loads(reply_msg.decode())
-        else:
-            return {}
-
-
-    def send_POST(self, url, post_param=None):
-        '''Send POST request to cmdctl, session id is send with the name
-        'cookie' in header.
-        Format: /module_name/command_name
-        parameters of command is encoded as a map
-        '''
-        param = None
-        if post_param is not None and len(post_param) != 0:
-            param = json.dumps(post_param)
-
-        headers = {"cookie" : self.session_id}
-        self.conn.request('POST', url, param, headers)
-        return self.conn.getresponse()
-
-    def _update_all_modules_info(self):
-        ''' Get all modules' information from cmdctl, including
-        specification file and configuration data. This function
-        should be called before interpreting command line or complete-key
-        is entered. This may not be the best way to keep bindctl
-        and cmdctl share same modules information, but it works.'''
-        if self.config_data is not None:
-            self.config_data.update_specs_and_config()
-        else:
-            self.config_data = isc.config.UIModuleCCSession(self)
-        self._update_commands()
-
-    def precmd(self, line):
-        if line != 'EOF':
-            self._update_all_modules_info()
-        return line
-
-    def postcmd(self, stop, line):
-        '''Update the prompt after every command, but only if we
-           have a tty as output'''
-        if sys.stdin.isatty():
-            self.prompt = self.location + self.prompt_end
-        return stop
-
-    def _prepare_module_commands(self, module_spec):
-        '''Prepare the module commands'''
-        module = ModuleInfo(name = module_spec.get_module_name(),
-                            desc = module_spec.get_module_description())
-        for command in module_spec.get_commands_spec():
-            cmd = CommandInfo(name = command["command_name"],
-                              desc = command["command_description"])
-            for arg in command["command_args"]:
-                param = ParamInfo(name = arg["item_name"],
-                                  type = arg["item_type"],
-                                  optional = bool(arg["item_optional"]),
-                                  param_spec = arg)
-                if ("item_default" in arg):
-                    param.default = arg["item_default"]
-                if ("item_description" in arg):
-                    param.desc = arg["item_description"]
-                cmd.add_param(param)
-            module.add_command(cmd)
-        self.add_module_info(module)
-
-    def _validate_cmd(self, cmd):
-        '''validate the parameters and merge some parameters together,
-        merge algorithm is based on the command line syntax, later, if
-        a better command line syntax come out, this function should be
-        updated first.
-        '''
-        if not cmd.module in self.modules:
-            raise CmdUnknownModuleSyntaxError(cmd.module)
-
-        module_info = self.modules[cmd.module]
-        if not module_info.has_command_with_name(cmd.command):
-            raise CmdUnknownCmdSyntaxError(cmd.module, cmd.command)
-
-        command_info = module_info.get_command_with_name(cmd.command)
-        manda_params = command_info.get_mandatory_param_names()
-        all_params = command_info.get_param_names()
-
-        # If help is entered, don't do further parameter validation.
-        for val in cmd.params.keys():
-            if val == "help":
-                return
-
-        params = cmd.params.copy()
-        if not params and manda_params:
-            raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])
-        elif params and not all_params:
-            raise CmdUnknownParamSyntaxError(cmd.module, cmd.command,
-                                             list(params.keys())[0])
-        elif params:
-            param_name = None
-            param_count = len(params)
-            for name in params:
-                # either the name of the parameter must be known, or
-                # the 'name' must be an integer (ie. the position of
-                # an unnamed argument
-                if type(name) == int:
-                    # lump all extraneous arguments together as one big final one
-                    # todo: check if last param type is a string?
-                    while (param_count > 2 and
-                           param_count > len(command_info.params) - 1):
-                        params[param_count - 2] += " " + params[param_count - 1]
-                        del(params[param_count - 1])
-                        param_count = len(params)
-                        cmd.params = params.copy()
-
-                    # (-1, help is always in the all_params list)
-                    if name >= len(all_params) - 1:
-                        # add to last known param
-                        if param_name:
-                            cmd.params[param_name] += cmd.params[name]
-                        else:
-                            raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, cmd.params[name])
-                    else:
-                        # replace the numbered items by named items
-                        param_name = command_info.get_param_name_by_position(name, param_count)
-                        cmd.params[param_name] = cmd.params[name]
-                        del cmd.params[name]
-
-                elif not name in all_params:
-                    raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, name)
-
-            param_nr = 0
-            for name in manda_params:
-                if not name in params and not param_nr in params:
-                    raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
-                param_nr += 1
-
-        # Convert parameter value according to parameter spec
-        # file. Ignore check for commands belonging to module 'config'
-        # or 'execute'.
-        if cmd.module != CONFIG_MODULE_NAME and\
-           cmd.module != command_sets.EXECUTE_MODULE_NAME:
-            for param_name in cmd.params:
-                param_spec = command_info.get_param_with_name(param_name).param_spec
-                try:
-                    cmd.params[param_name] = isc.config.config_data.convert_type(param_spec, cmd.params[param_name])
-                except isc.cc.data.DataTypeError as e:
-                    raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n'
-                                                     % (param_name, param_spec['item_type']) + str(e))
-
-    def _handle_cmd(self, cmd):
-        '''Handle a command entered by the user'''
-        if cmd.command == "help" or ("help" in cmd.params.keys()):
-            self._handle_help(cmd)
-        elif cmd.module == CONFIG_MODULE_NAME:
-            self.apply_config_cmd(cmd)
-        elif cmd.module == command_sets.EXECUTE_MODULE_NAME:
-            self.apply_execute_cmd(cmd)
-        else:
-            self.apply_cmd(cmd)
-
-    def add_module_info(self, module_info):
-        '''Add the information about one module'''
-        self.modules[module_info.name] = module_info
-
-    def get_module_names(self):
-        '''Return the names of all known modules'''
-        return list(self.modules.keys())
-
-    #override methods in cmd
-    def default(self, line):
-        self._parse_cmd(line)
-
-    def emptyline(self):
-        pass
-
-    def do_help(self, name):
-        self._print(CONST_BINDCTL_HELP)
-        for k in self.modules.values():
-            n = k.get_name()
-            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
-                self._print("    %s" % n)
-                self._print(textwrap.fill(k.get_desc(),
-                            initial_indent="            ",
-                            subsequent_indent="    " +
-                            " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
-                            width=70))
-            else:
-                self._print(textwrap.fill("%s%s%s" %
-                            (k.get_name(),
-                            " "*(CONST_BINDCTL_HELP_INDENT_WIDTH -
-                                 len(k.get_name())),
-                            k.get_desc()),
-                            initial_indent="    ",
-                            subsequent_indent="    " +
-                            " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
-                            width=70))
-
-    def onecmd(self, line):
-        if line == 'EOF' or line.lower() == "quit":
-            self.conn.close()
-            return True
-
-        if line == 'h':
-            line = 'help'
-
-        Cmd.onecmd(self, line)
-
-    def _get_identifier_startswith(self, id_text):
-        """Return the tab-completion hints for identifiers starting with
-           id_text.
-
-           Parameters:
-           id_text (string): the currently entered identifier part, which
-           is to be completed.
-        """
-        # Strip starting "/" from id_text
-        if id_text.startswith('/'):
-            id_text = id_text[1:]
-        # Get all items from the given module (up to the first /)
-        list = self.config_data.get_config_item_list(
-                        id_text.rpartition("/")[0], recurse=True)
-        # filter out all possibilities that don't match currently entered
-        # text part
-        hints = [val for val in list if val.startswith(id_text)]
-        return hints
-
-    def _cmd_has_identifier_param(self, cmd):
-        """
-        Returns True if the given (parsed) command is known and has a
-        parameter which points to a config data identifier
-
-        Parameters:
-        cmd (cmdparse.BindCmdParser): command context, including given params
-
-        """
-        if cmd.module not in self.modules:
-            return False
-        command = self.modules[cmd.module].get_command_with_name(cmd.command)
-        return command.has_param_with_name(CFGITEM_IDENTIFIER_PARAM)
-
-    def complete(self, text, state):
-        """
-        Returns tab-completion hints. See the python documentation of the
-        readline and Cmd modules for more information.
-
-        The first time this is called (within one 'completer' action), it
-        has state 0, and a list of possible completions is made. This list
-        is stored; complete() will then be called with increasing values of
-        state, until it returns None. For each call it returns the state'th
-        element of the hints it collected in the first call.
-
-        The hints list contents depend on which part of the full command
-        line; if no module is given yet, it will list all modules. If a
-        module is given, but no command, it will complete with module
-        commands. If both have been given, it will create the hints based on
-        the command parameters.
-
-        If module and command have already been specified, and the command
-        has a parameter 'identifier', the configuration data is used to
-        create the hints list.
-
-        Parameters:
-        text (string): The text entered so far in the 'current' part of
-                       the command (module, command, parameters)
-        state (int): state used in the readline tab-completion logic;
-                     0 on first call, increasing by one until there are
-                     no (more) hints to return.
-
-        Returns the string value of the hints list with index 'state',
-        or None if no (more) hints are available.
-        """
-        if state == 0:
-            self._update_all_modules_info()
-            text = text.strip()
-            hints = []
-            cur_line = my_readline()
-            try:
-                cmd = BindCmdParser(cur_line)
-                if not cmd.params and text:
-                    hints = self._get_command_startswith(cmd.module, text)
-                elif self._cmd_has_identifier_param(cmd):
-                    # If the command has an argument that is a configuration
-                    # identifier (currently, this is only a subset of
-                    # the config commands), then don't tab-complete with
-                    # hints derived from command parameters, but from
-                    # possible configuration identifiers.
-                    #
-                    # This solves the issue reported in #2254, where
-                    # there were hints such as 'argument' and 'identifier'.
-                    #
-                    # Since they are replaced, the tab-completion no longer
-                    # adds 'help' as an option (but it still works)
-                    #
-                    # Also, currently, tab-completion does not work
-                    # together with 'config go' (it does not take 'current
-                    # position' into account). But config go currently has
-                    # problems by itself, unrelated to completion.
-                    hints = self._get_identifier_startswith(text)
-                else:
-                    hints = self._get_param_startswith(cmd.module, cmd.command,
-                                                       text)
-
-            except CmdModuleNameFormatError:
-                if not text:
-                    hints = self.get_module_names()
-
-            except CmdMissCommandNameFormatError as e:
-                if not text.strip(): # command name is empty
-                    hints = self.modules[e.module].get_command_names()
-                else:
-                    hints = self._get_module_startswith(text)
-
-            except CmdCommandNameFormatError as e:
-                if e.module in self.modules:
-                    hints = self._get_command_startswith(e.module, text)
-
-            except CmdParamFormatError as e:
-                hints = self._get_param_startswith(e.module, e.command, text)
-
-            except BindCtlException:
-                hints = []
-
-            self.hint = hints
-
-        if state < len(self.hint):
-            return self.hint[state]
-        else:
-            return None
-
-
-    def _get_module_startswith(self, text):
-        return [module
-                for module in self.modules
-                if module.startswith(text)]
-
-
-    def _get_command_startswith(self, module, text):
-        if module in self.modules:
-            return [command
-                    for command in self.modules[module].get_command_names()
-                    if command.startswith(text)]
-
-        return []
-
-
-    def _get_param_startswith(self, module, command, text):
-        if module in self.modules:
-            module_info = self.modules[module]
-            if command in module_info.get_command_names():
-                cmd_info = module_info.get_command_with_name(command)
-                params = cmd_info.get_param_names()
-                hint = []
-                if text:
-                    hint = [val for val in params if val.startswith(text)]
-                else:
-                    hint = list(params)
-
-                if len(hint) == 1 and hint[0] != "help":
-                    hint[0] = hint[0] + " ="
-
-                return hint
-
-        return []
-
-    def _parse_cmd(self, line):
-        try:
-            cmd = BindCmdParser(line)
-            self._validate_cmd(cmd)
-            self._handle_cmd(cmd)
-        except (IOError, http.client.HTTPException) as err:
-            self._print('Error: ', err)
-        except BindCtlException as err:
-            self._print("Error! ", err)
-            self._print_correct_usage(err)
-        except isc.cc.data.DataTypeError as err:
-            self._print("Error! ", err)
-        except isc.cc.data.DataTypeError as dte:
-            self._print("Error: " + str(dte))
-        except isc.cc.data.DataNotFoundError as dnfe:
-            self._print("Error: " + str(dnfe))
-        except isc.cc.data.DataAlreadyPresentError as dape:
-            self._print("Error: " + str(dape))
-        except KeyError as ke:
-            self._print("Error: missing " + str(ke))
-
-    def _print_correct_usage(self, ept):
-        if isinstance(ept, CmdUnknownModuleSyntaxError):
-            self.do_help(None)
-
-        elif isinstance(ept, CmdUnknownCmdSyntaxError):
-            self.modules[ept.module].module_help()
-
-        elif isinstance(ept, CmdMissParamSyntaxError) or \
-             isinstance(ept, CmdUnknownParamSyntaxError):
-             self.modules[ept.module].command_help(ept.command)
-
-
-    def _append_space_to_hint(self):
-        """Append one space at the end of complete hint."""
-        self.hint = [(val + " ") for val in self.hint]
-
-
-    def _handle_help(self, cmd):
-        if cmd.command == "help":
-            self.modules[cmd.module].module_help()
-        else:
-            self.modules[cmd.module].command_help(cmd.command)
-
-
-    def apply_config_cmd(self, cmd):
-        '''Handles a configuration command.
-           Raises a DataTypeError if a wrong value is set.
-           Raises a DataNotFoundError if a wrong identifier is used.
-           Raises a KeyError if the command was not complete
-        '''
-        identifier = self.location
-        if 'identifier' in cmd.params:
-            if not identifier.endswith("/"):
-                identifier += "/"
-            if cmd.params['identifier'].startswith("/"):
-                identifier = cmd.params['identifier']
-            else:
-                if cmd.params['identifier'].startswith('['):
-                    identifier = identifier[:-1]
-                identifier += cmd.params['identifier']
-
-            # Check if the module is known; for unknown modules
-            # we currently deny setting preferences, as we have
-            # no way yet to determine if they are ok.
-            module_name = identifier.split('/')[1]
-            if module_name != "" and (self.config_data is None or \
-               not self.config_data.have_specification(module_name)):
-                self._print("Error: Module '" + module_name +
-                            "' unknown or not running")
-                return
-
-        if cmd.command == "show":
-            # check if we have the 'all' argument
-            show_all = False
-            if 'argument' in cmd.params:
-                if cmd.params['argument'] == 'all':
-                    show_all = True
-                elif 'identifier' not in cmd.params:
-                    # no 'all', no identifier, assume this is the
-                    #identifier
-                    identifier += cmd.params['argument']
-                else:
-                    self._print("Error: unknown argument " +
-                                cmd.params['argument'] +
-                                ", or multiple identifiers given")
-                    return
-            values = self.config_data.get_value_maps(identifier, show_all)
-            for value_map in values:
-                line = value_map['name']
-                if value_map['type'] in [ 'module', 'map' ]:
-                    line += "/"
-                elif value_map['type'] == 'list' \
-                     and value_map['value'] != []:
-                    # do not print content of non-empty lists if
-                    # we have more data to show
-                    line += "/"
-                else:
-                    # if type is named_set, don't print value if None
-                    # (it is either {} meaning empty, or None, meaning
-                    # there actually is data, but not to be shown with
-                    # the current command
-                    if value_map['type'] == 'named_set' and\
-                       value_map['value'] is None:
-                        line += "/\t"
-                    else:
-                        line += "\t" + json.dumps(value_map['value'])
-                line += "\t" + value_map['type']
-                line += "\t"
-                if value_map['default']:
-                    line += "(default)"
-                if value_map['modified']:
-                    line += "(modified)"
-                self._print(line)
-        elif cmd.command == "show_json":
-            if identifier == "":
-                self._print("Need at least the module to show the "
-                            "configuration in JSON format")
-            else:
-                data, default = self.config_data.get_value(identifier)
-                self._print(json.dumps(data))
-        elif cmd.command == "add":
-            self.config_data.add_value(identifier,
-                                       cmd.params.get('value_or_name'),
-                                       cmd.params.get('value_for_set'))
-        elif cmd.command == "remove":
-            if 'value' in cmd.params:
-                self.config_data.remove_value(identifier, cmd.params['value'])
-            else:
-                self.config_data.remove_value(identifier, None)
-        elif cmd.command == "set":
-            if 'identifier' not in cmd.params:
-                self._print("Error: missing identifier or value")
-            else:
-                parsed_value = None
-                try:
-                    parsed_value = json.loads(cmd.params['value'])
-                except Exception as exc:
-                    # ok could be an unquoted string, interpret as such
-                    parsed_value = cmd.params['value']
-                self.config_data.set_value(identifier, parsed_value)
-        elif cmd.command == "unset":
-            self.config_data.unset(identifier)
-        elif cmd.command == "revert":
-            self.config_data.clear_local_changes()
-        elif cmd.command == "commit":
-            try:
-                self.config_data.commit()
-            except isc.config.ModuleCCSessionError as mcse:
-                self._print(str(mcse))
-        elif cmd.command == "diff":
-            self._print(self.config_data.get_local_changes())
-        elif cmd.command == "go":
-            self.go(identifier)
-
-    def go(self, identifier):
-        '''Handles the config go command, change the 'current' location
-           within the configuration tree. '..' will be interpreted as
-           'up one level'.'''
-        id_parts = isc.cc.data.split_identifier(identifier)
-
-        new_location = ""
-        for id_part in id_parts:
-            if (id_part == ".."):
-                # go 'up' one level
-                new_location, a, b = new_location.rpartition("/")
-            else:
-                new_location += "/" + id_part
-        # check if exists, if not, revert and error
-        v,d = self.config_data.get_value(new_location)
-        if v is None:
-            self._print("Error: " + identifier + " not found")
-            return
-
-        self.location = new_location
-
-    def apply_execute_cmd(self, command):
-        '''Handles the 'execute' command, which executes a number of
-           (preset) statements. The command set to execute is either
-           read from a file (e.g. 'execute file <file>'.) or one
-           of the sets as defined in command_sets.py'''
-        if command.command == 'file':
-            try:
-                with open(command.params['filename']) as command_file:
-                    commands = command_file.readlines()
-            except IOError as ioe:
-                self._print("Error: " + str(ioe))
-                return
-        elif command_sets.has_command_set(command.command):
-            commands = command_sets.get_commands(command.command)
-        else:
-            # Should not be reachable; parser should've caught this
-            raise Exception("Unknown execute command type " + command.command)
-
-        # We have our set of commands now, depending on whether 'show' was
-        # specified, show or execute them
-        if 'show' in command.params and command.params['show'] == 'show':
-            self.__show_execute_commands(commands)
-        else:
-            self.__apply_execute_commands(commands)
-
-    def __show_execute_commands(self, commands):
-        '''Prints the command list without executing them'''
-        for line in commands:
-            self._print(line.strip())
-
-    def __apply_execute_commands(self, commands):
-        '''Applies the configuration commands from the given iterator.
-           This is the method that catches, comments, echo statements, and
-           other directives. All commands not filtered by this method are
-           interpreted as if they are directly entered in an active session.
-           Lines starting with any of the following characters are not
-           passed directly:
-           # - These are comments
-           ! - These are directives
-               !echo: print the rest of the line
-               !verbose on/off: print the commands themselves too
-               Unknown directives are ignored (with a warning)
-           The execution is stopped if there are any errors.
-        '''
-        verbose = False
-        try:
-            for line in commands:
-                line = line.strip()
-                if verbose:
-                    self._print(line)
-                if line.startswith('#') or len(line) == 0:
-                    continue
-                elif line.startswith('!'):
-                    if re.match('^!echo ', line, re.I) and len(line) > 6:
-                        self._print(line[6:])
-                    elif re.match('^!verbose\s+on\s*$', line, re.I):
-                        verbose = True
-                    elif re.match('^!verbose\s+off$', line, re.I):
-                        verbose = False
-                    else:
-                        self._print("Warning: ignoring unknown directive: " +
-                                    line)
-                else:
-                    cmd = BindCmdParser(line)
-                    self._validate_cmd(cmd)
-                    self._handle_cmd(cmd)
-        except (isc.config.ModuleCCSessionError,
-                IOError, http.client.HTTPException,
-                BindCtlException, isc.cc.data.DataTypeError,
-                isc.cc.data.DataNotFoundError,
-                isc.cc.data.DataAlreadyPresentError,
-                KeyError) as err:
-            self._print('Error: ', err)
-            self._print()
-            self._print('Depending on the contents of the script, and which')
-            self._print('commands it has called, there can be committed and')
-            self._print('local changes. It is advised to check your settings')
-            self._print(', and revert local changes with "config revert".')
-
-    def apply_cmd(self, cmd):
-        '''Handles a general module command'''
-        url = '/' + cmd.module + '/' + cmd.command
-        cmd_params = None
-        if (len(cmd.params) != 0):
-            cmd_params = json.dumps(cmd.params)
-
-        reply = self.send_POST(url, cmd.params)
-        data = reply.read().decode()
-        # The reply is a string containing JSON data,
-        # parse it, then prettyprint
-        if data != "" and data != "{}":
-            self._print(json.dumps(json.loads(data), sort_keys=True,
-                                   indent=4))
diff --git a/src/bin/bindctl/bindctl.xml b/src/bin/bindctl/bindctl.xml
deleted file mode 100644 (file)
index 426c478..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>June 20, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>bindctl</refentrytitle>
-    <manvolnum>1</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>bindctl</refname>
-    <refpurpose>control and configure BIND 10</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>bindctl</command>
-      <arg><option>-a <replaceable>address</replaceable></option></arg>
-      <arg><option>-h</option></arg>
-      <arg><option>-c <replaceable>file</replaceable></option></arg>
-      <arg><option>-p <replaceable>number</replaceable></option></arg>
-      <arg><option>--address <replaceable>address</replaceable></option></arg>
-      <arg><option>--help</option></arg>
-      <arg><option>--certificate-chain <replaceable>file</replaceable></option></arg>
-      <arg><option>--csv-file-dir<replaceable>file</replaceable></option></arg>
-      <arg><option>--port <replaceable>number</replaceable></option></arg>
-      <arg><option>--version</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      The <command>bindctl</command> tool is a user interface to
-      the BIND 10 services.  The program can be used to control
-      the components and configure the BIND 10 options.  The options
-      may be specified
-<!-- TODO: as command-line arguments, or --> via its
-      interactive command interpreter.
-    </para>
-
-    <para>
-      <command>bindctl</command> communicates over a HTTPS REST-ful
-      interface provided by
-      <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-      The
-<citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      daemon stores the configurations.
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>ARGUMENTS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term><option>-a</option> <replaceable>address</replaceable>, <option>--address</option> <replaceable>address</replaceable></term>
-
-        <listitem>
-          <para>The IPv4 or IPv6 address to use to connect to the running
-            <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            daemon.
-            The default is 127.0.0.1.
-          </para>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-c</option> <replaceable>file</replaceable>,
-        <option>--certificate-chain</option> <replaceable>file</replaceable></term>
-
-        <listitem>
-          <para>The PEM formatted server certificate validation chain file.
-          </para>
-<!-- TODO: any default? -->
-<!-- TODO: any way to choose this for cmdctl? -->
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-         <option>--csv-file-dir</option><replaceable>file</replaceable>
-       </term>
-
-        <listitem>
-          <para>
-           The directory name in which the user/password CSV file
-            is stored (see AUTHENTICATION).
-           By default this option doesn't have any value,
-           in which case the "<filename>.bind10</filename>" directory
-            under the user's home directory will be used.
-          </para>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-h</option>,
-          <option>--help</option></term>
-        <listitem><para>
-          Display command usage.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-p</option> <replaceable>number</replaceable>, <option>--port</option> <replaceable>number</replaceable></term>
-
-        <listitem>
-          <para>The port number to use to connect to the running
-            <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-            daemon.
-            The default is 8080.</para>
-<!-- TODO: -->
-            <note><simpara>This default port number may change.</simpara></note>
-         </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>--version</option></term>
-        <listitem><para>
-          Display the version number and exit.</para>
-        </listitem>
-      </varlistentry>
-
-    </variablelist>
-
-  </refsect1>
-
-  <refsect1>
-    <title>AUTHENTICATION</title>
-
-    <para>
-      The tool will authenticate using a username and password.
-      On the first successful login, it will save the details to
-      a comma-separated-value (CSV) file
-      which will be used for later uses of <command>bindctl</command>.
-      The file name is "<filename>default_user.csv</filename>"
-      located under the directory specified by the
-      <option>--csv-file-dir</option> option.
-    </para>
-
-<!-- TODO: mention HTTPS? -->
-
-  </refsect1>
-
-  <refsect1>
-    <title>USAGE</title>
-
-    <para>
-      The <command>bindctl</command> prompt shows
-      <quote>&gt; </quote>.
-       The prompt will also display the location if changed.
-       The options are based on the module in use.
-       The usage is:
-       <command>module</command> <command>command</command> <replaceable>param1 = value1 , <replaceable>param2 = value2</replaceable></replaceable>
-     </para>
-
-     <para>
-       <command>bindctl</command>'s
-       interactive interface provides command-line completion and
-       hints.  Press the Tab key to get a hint for the module,
-       command, and/or parameters.
-<!-- TODO: no hints at a blank line though ? -->
-
-      The arrow keys and Emacs-style editing keys may be used to edit
-      and recall previous lines.
-<!-- TODO:  all Emacs-style editing keys? -->
-     </para>
-
-     <para>
-       You can use the <command>help</command>
-       keyword to receive usage assistance for a module or a module's
-       command.
-     </para>
-
-     <para>
-       The <command>quit</command>
-       command is used to exit
-       <command>bindctl</command>.
-       (It doesn't stop the BIND 10 services.)
-     </para>
-
-     <para>
-       The following module is available by default:
-       <command>config</command> for Configuration commands.
-<!-- TODO: or is config from the cfgmgr module? -->
-       Additional modules may be available, such as
-       <command>Init</command>, <command>Xfrin</command>, and
-       <command>Auth</command>.
-     </para>
-
-<!--
-.Sh CONFIGURATION COMMANDS
-The following configuration commands are recognized by
-.Nm :
-.Bl -tag -width Ic
-.It Ic add Ar identifier Ar value
-Add a value to a list.
-.\" TODO: explain better
-.\" TODO: define list, map, others ...
-.\" TODO: why the value?
-.It Ic commit
-Commit all local changes.
-.\" TODO: what does this do?
-.It Ic go Ar identifier
-Go to the specified location within the configuration.
-.\" TODO: explain this
-.It Ic help
-Get help for the
-.Em config
-module.
-.It Ic remove Ar identifier Ar value
-Remove a value from a list.
-.\" TODO: why the value?
-.It Ic revert
-Revert all local changes (that have not been committed).
-.\" TODO: what does this do?
-.It Ic set Ar identifier Ar value
-Set a configuration value.
-.\" TODO
-.It Ic show Op Ar identifier.
-.\" TODO
-Show the currently set configuration values.
-If no
-.Ar identifier
-is given, the current location is used (as set by the
-.Ic go
-command).
-If a config option is a list or a map, the value is not
-shown directly, but must be requested separately.
-.\" TODO: example of this list or map
-.It Ic unset Ar identifier
-Remove a configuration value.
-If the option is mandatory, it will reset to the default value.
-.\" TODO: how to know default values before?
-.El
-.\" This manual page only covers builtin commands.
-.\" .Sh PARKINGLOT COMMANDS
-.\" .Bl -tag -width Ic
-.\" .It Ic help
-.\" Get help for the
-.\" .Em ParkingLot
-.\" module.
-.\"
-.\" TODO: Command arguments which have embedded spaces may be quoted with
-.\" quote
-.\" .Sq \&"
-.\" marks.
-.Pp
-.Pp command-line editing?
-.Pp
-.Sh EXAMPLES
-The following are some
-.Nm
-examples:
-.Bl -tag -width abcde
-.It Ic "config help"
-Show the commands for the
-.Em config
-module.
-.It Ic "config go ParkingLot/"
-Go to the
-.Dq ParkingLot/
-configuration location.
--->
-
-  </refsect1>
-
-<!-- TODO: any options? -->
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-<!-- TODO: point to developer docs -->
-  </refsect1>
-
-  <refsect1>
-    <title>AUTHORS</title>
-    <para>
-      The <command>bindctl</command>
-      tool and library were initially coded by Zhang Likun of CNNIC
-      for the BIND 10 project.
-      The initial manual page was written by Jeremy C. Reed of ISC.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-       The initial version (with internal name of
-       <command>BigTool</command>)
-       was started in October 2009.
-    </para>
-  </refsect1>
-
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
-
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
deleted file mode 100755 (executable)
index 68a6237..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2009  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.
-
-"""This is the main calling class for the bindctl configuration and
-   command tool. It sets up a command interpreter and runs that."""
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-
-from bindctl.moduleinfo import *
-from bindctl.bindcmd import *
-from bindctl import command_sets
-import pprint
-from optparse import OptionParser, OptionValueError
-import isc.util.process
-import isc.util.traceback_handler
-
-isc.util.process.rename()
-
-# This is the version that gets displayed to the user.
-# The VERSION string consists of the module name, the module version
-# number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "bindctl 20110217 (BIND 10 @PACKAGE_VERSION@)"
-
-DEFAULT_IDENTIFIER_DESC = "The identifier specifies the config item. Child elements are separated with the '/' character. List indices can be specified with '[i]', where i is an integer specifying the index, starting with 0. Examples: 'Init/start_auth', 'Auth/listen_on[0]/address'. If no identifier is given, shows the item at the current location."
-
-def prepare_config_commands(tool):
-    '''Prepare fixed commands for local configuration editing'''
-    module = ModuleInfo(name = CONFIG_MODULE_NAME, desc = "Configuration commands.")
-    cmd = CommandInfo(name = "show", desc = "Show configuration.")
-    param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
-    cmd.add_param(param)
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="show_json",
-                      desc="Show full configuration in JSON format.")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="add", desc=
-        "Add an entry to configuration list or a named set. "
-        "When adding to a list, the command has one optional argument, "
-        "a value to add to the list. The value must be in correct JSON "
-        "and complete. When adding to a named set, it has one "
-        "mandatory parameter (the name to add), and an optional "
-        "parameter value, similar to when adding to a list. "
-        "In either case, when no value is given, an entry will be "
-        "constructed with default values.")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name="value_or_name", type="string", optional=True,
-                      desc="Specifies a value to add to the list, or the name when adding to a named set. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-    param = ParamInfo(name="value_for_set", type="string", optional=True,
-                      desc="Specifies an optional value to add to the named map. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="remove", desc="Remove entry from configuration list or named set.")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=True, desc = "When identifier is a list, specifies a value to remove from the list. It must be in correct JSON format and complete. When it is a named set, specifies the name to remove.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="set", desc="Set a configuration value.")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    param = ParamInfo(name="value", type="string", optional=False,
-                      desc="Specifies a value to set. It must be in correct JSON format and complete.")
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="unset", desc="Unset a configuration value (i.e. revert to the default, if any).")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=False, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="diff", desc="Show all local changes that have not been committed.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="revert", desc="Revert all local changes.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="commit", desc="Commit all local changes.")
-    module.add_command(cmd)
-
-    cmd = CommandInfo(name="go", desc="Go to a specific configuration part.")
-    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
-                      optional=False, desc=DEFAULT_IDENTIFIER_DESC)
-    cmd.add_param(param)
-    module.add_command(cmd)
-
-    tool.add_module_info(module)
-
-def check_port(option, opt_str, value, parser):
-    if (value < 0) or (value > 65535):
-        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
-    parser.values.port = value
-
-def check_addr(option, opt_str, value, parser):
-    ipstr = value
-    ip_family = socket.AF_INET
-    if (ipstr.find(':') != -1):
-        ip_family = socket.AF_INET6
-
-    try:
-        socket.inet_pton(ip_family, ipstr)
-    except:
-        raise OptionValueError("%s invalid ip address" % ipstr)
-
-    parser.values.addr = value
-
-def set_bindctl_options(parser):
-    parser.add_option('-p', '--port', dest='port', type='int',
-                      action='callback', callback=check_port,
-                      default='8080', help='port for cmdctl of bind10')
-
-    parser.add_option('-a', '--address', dest='addr', type='string',
-                      action='callback', callback=check_addr,
-                      default='127.0.0.1', help='IP address for cmdctl of bind10')
-
-    parser.add_option('-c', '--certificate-chain', dest='cert_chain',
-                      type='string', action='store',
-                      help='PEM formatted server certificate validation chain file')
-
-    parser.add_option('--csv-file-dir', dest='csv_file_dir', type='string',
-                      default=None, action='store',
-                      help='Directory to store the password CSV file')
-
-def main():
-    parser = OptionParser(version = VERSION)
-    set_bindctl_options(parser)
-    (options, args) = parser.parse_args()
-    server_addr = options.addr + ':' + str(options.port)
-    tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain,
-                              csv_file_dir=options.csv_file_dir)
-    prepare_config_commands(tool)
-    command_sets.prepare_execute_commands(tool)
-    result = tool.run()
-    sys.exit(result)
-
-if __name__ == '__main__':
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/bindctl/cmdparse.py b/src/bin/bindctl/cmdparse.py
deleted file mode 100644 (file)
index 30909a3..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-# Copyright (C) 2009  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.
-
-
-"""This module holds the command parser class for bindctl"""
-
-import re
-from bindctl.exception import *
-try:
-    from collections import OrderedDict
-except ImportError:
-    from bindctl.mycollections import OrderedDict
-
-param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*"
-
-# The value string can be a sequence without space or comma
-# characters, or a string surroundedby quotation marks(such marks
-# can be part of string in an escaped form)
-#param_value_str  = "(?P<param_value>[\"\'].+?(?<!\\\)[\"\']|[^\'\"][^, ]+)"
-param_value_str  = "(?P<param_value>[^\'\" ][^, ]+)"
-param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']"
-next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
-
-
-PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str +
-                                      param_value_with_quota_str +
-                                      next_params_str)
-PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
-# Used for module and command name
-NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
-
-# this removes all whitespace in the given string, except when
-# between " quotes
-_remove_unquoted_whitespace = \
-    lambda text:'"'.join( it if i%2 else ''.join(it.split())
-        for i,it in enumerate(text.split('"'))  )
-
-
-def _remove_list_and_map_whitespace(text):
-    """Returns a string where the whitespace between matching [ and ]
-       is removed, unless quoted"""
-    # regular expression aren't really the right tool, since we may have
-    # nested structures
-    result = []
-    start_pos = 0
-    pos = 0
-    list_count = 0
-    map_count = 0
-    cur_start_list_pos = None
-    cur_start_map_pos = None
-    for i in text:
-        if i == '[' and map_count == 0:
-            if list_count == 0:
-                result.append(text[start_pos:pos + 1])
-                cur_start_list_pos = pos + 1
-            list_count = list_count + 1
-        elif i == ']' and map_count == 0:
-            if list_count > 0:
-                list_count = list_count - 1
-                if list_count == 0:
-                    result.append(_remove_unquoted_whitespace(text[cur_start_list_pos:pos + 1]))
-                    start_pos = pos + 1
-        if i == '{' and list_count == 0:
-            if map_count == 0:
-                result.append(text[start_pos:pos + 1])
-                cur_start_map_pos = pos + 1
-            map_count = map_count + 1
-        elif i == '}' and list_count == 0:
-            if map_count > 0:
-                map_count = map_count - 1
-                if map_count == 0:
-                    result.append(_remove_unquoted_whitespace(text[cur_start_map_pos:pos + 1]))
-                    start_pos = pos + 1
-
-
-        pos = pos + 1
-    if start_pos <= len(text):
-        result.append(text[start_pos:len(text)])
-    return "".join(result)
-
-
-class BindCmdParser:
-    """ This class will parse the command line user input into three parts:
-    module name, command, parameters
-    the first two parts are strings and parameter is one hash,
-    parameters part is optional
-
-    Example: zone reload, zone_name=example.com
-    module == zone
-    command == reload
-    params == [zone_name = 'example.com']
-    """
-
-    def __init__(self, cmd):
-        self.params = OrderedDict()
-        self.module = ''
-        self.command = ''
-        self._parse_cmd(cmd)
-
-    def _parse_cmd(self, text_str):
-        '''Parse command line. '''
-        # Get module name
-        groups = NAME_PATTERN.match(text_str)
-        if not groups:
-            raise CmdModuleNameFormatError
-
-        self.module = groups.group('name')
-        cmd_str = groups.group('others')
-        if cmd_str:
-            if not groups.group('blank'):
-                raise CmdModuleNameFormatError
-        else:
-            raise CmdMissCommandNameFormatError(self.module)
-
-        # Get command name
-        groups = NAME_PATTERN.match(cmd_str)
-        if (not groups):
-            raise CmdCommandNameFormatError(self.module)
-
-        self.command = groups.group('name')
-        param_str = groups.group('others')
-        if param_str:
-            if not groups.group('blank'):
-                raise CmdCommandNameFormatError(self.module)
-
-            self._parse_params(param_str)
-
-    def _remove_list_whitespace(self, text):
-        return ""
-
-    def _parse_params(self, param_text):
-        """convert a=b,c=d into one hash """
-        param_text = _remove_list_and_map_whitespace(param_text)
-
-        # Check parameter name "help"
-        param = NAME_PATTERN.match(param_text)
-        if param and param.group('name') == "help":
-            self.params["help"] = "help"
-            return
-
-        while True:
-            if not param_text.strip():
-                break
-
-            groups = PARAM_PATTERN.match(param_text) or \
-                     PARAM_WITH_QUOTA_PATTERN.match(param_text)
-            if not groups:
-                # ok, fill in the params in the order entered
-                params = re.findall("([^\" ]+|\".*\")", param_text)
-                i = 0
-                for p in params:
-                    self.params[i] = p
-                    i += 1
-                break
-            else:
-                self.params[groups.group('param_name')] = groups.group('param_value')
-                param_text = groups.group('next_params')
-                if not param_text or (not param_text.strip()):
-                    break
-
-                if not groups.group('blank') and \
-                   not groups.group('comma'):
-                    raise CmdParamFormatError(self.module, self.command)
diff --git a/src/bin/bindctl/command_sets.py b/src/bin/bindctl/command_sets.py
deleted file mode 100644 (file)
index b146c38..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-# 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.
-
-# This file provides a built-in set of 'execute' commands, for common
-# functions, such as adding an initial auth server.
-# By calling the function prepare_execute_commands, the
-# commands in the command_sets map are added to the virtual
-# component called 'execute'. This is done in bindctl_main.
-
-from bindctl.moduleinfo import *
-# The name of the 'virtual' command set execution module in bindctl
-EXECUTE_MODULE_NAME = 'execute'
-
-# This is a map of command names to lists
-# Each element in the set should itself be a dict containing:
-# 'description': A string with a description of the command set
-# 'commands': A list of bindctl commands
-command_sets = {
-    'init_authoritative_server': {
-        'description':
-            'Configure and run a basic Authoritative server, with default '+
-            'SQLite3 backend, and xfrin and xfrout functionality',
-        'commands':
-            [
-            '!echo adding Authoritative server component',
-            'config add /Init/components b10-auth',
-            'config set /Init/components/b10-auth/kind needed',
-            'config set /Init/components/b10-auth/special auth',
-            '!echo adding Xfrin component',
-            'config add /Init/components b10-xfrin',
-            'config set /Init/components/b10-xfrin/address Xfrin',
-            'config set /Init/components/b10-xfrin/kind dispensable',
-            '!echo adding Xfrout component',
-            'config add /Init/components b10-xfrout',
-            'config set /Init/components/b10-xfrout/address Xfrout',
-            'config set /Init/components/b10-xfrout/kind dispensable',
-            '!echo adding Zone Manager component',
-            'config add /Init/components b10-zonemgr',
-            'config set /Init/components/b10-zonemgr/address Zonemgr',
-            'config set /Init/components/b10-zonemgr/kind dispensable',
-            '!echo Components added. Please enter "config commit" to',
-            '!echo finalize initial setup and run the components.'
-            ]
-    }
-}
-
-def has_command_set(name):
-    return name in command_sets
-
-def get_commands(name):
-    return command_sets[name]['commands']
-
-def get_description(name):
-    return command_sets[name]['description']
-
-# For each
-def prepare_execute_commands(tool):
-    """This function is called by bindctl_main, and sets up the commands
-       defined here for use in bindctl."""
-    # common parameter
-    param_show = ParamInfo(name="show", type="string", optional=True,
-        desc="Show the list of commands without executing them")
-
-    # The command module
-    module = ModuleInfo(name=EXECUTE_MODULE_NAME,
-                        desc="Execute a given set of commands")
-
-    # Command to execute a file
-    cmd = CommandInfo(name="file", desc="Read commands from file")
-    param = ParamInfo(name="filename", type="string", optional=False,
-                      desc="File to read the set of commands from.")
-    cmd.add_param(param)
-    cmd.add_param(param_show)
-    module.add_command(cmd)
-
-    # and loop through all command sets defined above
-    for name in command_sets:
-        cmd = CommandInfo(name=name, desc=get_description(name))
-        cmd.add_param(param_show)
-        module.add_command(cmd)
-
-    tool.add_module_info(module)
diff --git a/src/bin/bindctl/exception.py b/src/bin/bindctl/exception.py
deleted file mode 100644 (file)
index c6c4865..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (C) 2009  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.
-
-"""This module holds exception classes specific for bindctl"""
-class BindCtlException(Exception):
-    """Abstract base class shared by all bindctl exceptions"""
-    def __str__(self):
-        return "Big tool has problem"
-
-# Begin define Format exception
-
-class CmdFormatError(BindCtlException):
-    """Command is malformed"""
-    def __str__(self):
-        return "Command is malformed"
-
-        
-class CmdModuleNameFormatError(CmdFormatError):
-    """module name format error"""
-
-    def __str__(self):
-        return "Module name format error: the character of name can only be '0-9a-zA-Z_'" 
-                      
-                
-class CmdCommandNameFormatError(CmdFormatError):
-    """command name format error"""
-    
-    def __init__(self, module):
-        self.module = module        
-        
-    def __str__(self):
-        return "Command name format error: the character of name can only be '0-9a-zA-Z_'"      
-        
-        
-class CmdMissCommandNameFormatError(CmdFormatError):
-    """Module name isn't finished"""
-    
-    def __init__(self, module):
-        self.module = module
-        
-    def __str__(self):
-        return "command name is missed"   
-
-
-class CmdParamFormatError(CmdFormatError):
-    """Command is malformed which parameter isn't key value pair"""
-    
-    def __init__(self, module, command):        
-        self.module = module
-        self.command = command        
-
-    def __str__(self):
-        return  "Parameter format error, it should be 'key = value'"         
-        
-# Begin define the exception for syntax
-
-class CmdSyntaxError(BindCtlException):
-    """Command line has syntax error"""
-    
-    def __str__(self):
-        return "Command line has syntax error"
-
-
-class CmdUnknownModuleSyntaxError(CmdSyntaxError):
-    """Command is unknown"""
-    def __init__(self, module):
-        self.module = module
-
-    def __str__(self):
-        return str("Unknown module '%s'" % self.module)
-        
-
-class CmdUnknownCmdSyntaxError(CmdSyntaxError):
-    """Command is unknown"""
-    def __init__(self, module, command):
-        self.module = module
-        self.command = command
-
-    def __str__(self):
-        return str("Unknown command '%s' to module '%s'" % 
-                    (self.command, self.module))
-                    
-
-class CmdUnknownParamSyntaxError(CmdSyntaxError):
-    """The parameter of command is unknown"""
-    def __init__(self, module, command, param):
-        self.module = module
-        self.command = command
-        self.param = param
-
-    def __str__(self):
-        return str("Unknown parameter '%s' to command '%s' of module '%s'" %
-                   (self.param, self.command, self.module))
-                   
-
-class CmdMissParamSyntaxError(CmdSyntaxError):
-    """The parameter of one command is missed"""
-    def __init__(self, module, command, param):
-        self.module = module
-        self.command = command
-        self.param = param
-
-    def __str__(self):
-        return str("Parameter '%s' is missed for command '%s' of module '%s'" % 
-                   (self.param, self.command, self.module))
-
-
-class FailToLogin(BindCtlException):
-    def __str__(self):
-        return "Fail to login to cmdctl"
-
-
diff --git a/src/bin/bindctl/moduleinfo.py b/src/bin/bindctl/moduleinfo.py
deleted file mode 100644 (file)
index 33114d8..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-# Copyright (C) 2009  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.
-
-"""This module holds classes representing modules, commands and
-   parameters for use in bindctl"""
-
-import textwrap
-
-try:
-    from collections import OrderedDict
-except ImportError:
-    from bindctl.mycollections import OrderedDict
-
-# Define value type
-STRING_TYPE = "string"
-LIST_TYPE = "list"
-INT_TYPE = "int"
-
-MODULE_NODE_NAME = 'module'
-COMMAND_NODE_NAME = 'command'
-PARAM_NODE_NAME = 'param'
-
-# this is used to align the descriptions in help output
-CONST_BINDCTL_HELP_INDENT_WIDTH=12
-
-
-class ParamInfo:
-    """One parameter of one command.
-    Each command parameter has five attributes:
-    parameter name, parameter type, parameter value,
-    parameter description and paramter's spec(got from
-    module spec file). 
-    """
-    def __init__(self, name, desc = '', type = STRING_TYPE, 
-                 optional = False, value = '', default_value = '', 
-                 param_spec = None):
-        self.name = name
-        self.type = type
-        self.value = value
-        self.default_value = default_value                           
-        self.desc = desc
-        self.is_optional = optional
-        self.param_spec = param_spec
-    
-    def __str__(self):        
-        return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))
-
-    def get_basic_info(self):
-        if self.is_optional:
-            opt_str = "optional"
-        else:
-            opt_str = "mandatory"
-        return "%s (%s, %s)" % (self.name, self.type, opt_str)
-
-    def get_desc(self):
-        return self.desc
-
-class CommandInfo:
-    """One command which is provided by one bind10 module, it has zero
-       or more parameters
-    """
-
-    def __init__(self, name, desc = ""):
-        self.name = name
-        self.desc = desc
-        self.params = OrderedDict()        
-        # Set default parameter "help"
-        self.add_param(ParamInfo("help", 
-                                  desc = "Get help for command.",
-                                  optional = True))
-                
-    def __str__(self):
-        return str("%s \t(%s)" % (self.name, self.desc))
-
-    def get_name(self):
-        return self.name
-
-    def get_desc(self):
-        return self.desc;
-    
-    def add_param(self, paraminfo):
-        """Add a ParamInfo object to this CommandInfo"""
-        self.params[paraminfo.name] = paraminfo
-        
-
-    def has_param_with_name(self, param_name):
-        """Returns true if the parameter with param_name exists"""
-        return param_name in self.params
-        
-
-    def get_param_with_name(self, param_name):
-        """Returns the ParamInfo with the given name. Raises a
-           KeyError if it doesn't exist"""
-        return self.params[param_name]
-        
-
-    def get_params(self):
-        """Returns a list of all ParamInfo objects for this CommandInfo"""
-        return list(self.params.values())
-        
-
-    def get_param_names(self):
-        """Returns a list of the names of all parameters for this command"""
-        return list(self.params.keys())
-        
-        
-    def get_mandatory_param_names(self):
-        """Returns a list of the names of all mandatory parameters for
-           this command"""
-        all_names = self.params.keys()
-        return [name for name in all_names 
-                if not self.params[name].is_optional]        
-        
-    def get_param_name_by_position(self, pos, param_count):
-        """
-        Find a proper parameter name for the position 'pos':
-        If param_count is equal to the count of mandatory parameters of command,
-        and there is some optional parameter, find the first mandatory parameter 
-        from the position 'pos' to the end. Else, return the name on position pos.
-        (This function will be changed if bindctl command line syntax is changed
-        in the future. )
-        """
-        if type(pos) != int:
-            raise KeyError(str(pos) + " is not an integer")
-
-        else:
-            params = self.params.copy()
-            del params['help']
-            count = len(params)
-            if (pos >= count):
-                raise KeyError(str(pos) + " out of range")
-
-            mandatory_count = len(self.get_mandatory_param_names())
-            param_names = list(params.keys())
-            if (param_count == mandatory_count) and (param_count < count):
-                while pos < count:
-                    if not params[param_names[pos]].is_optional:
-                        return param_names[pos]
-                    pos += 1
-                
-                raise KeyError(str(pos) + "parameters have error")
-            else:
-                return param_names[pos]
-
-
-    def command_help(self):
-        """Prints the help info for this command to stdout"""
-        print("Command ", self)
-        print("\t\thelp (Get help for command)")
-
-        params = self.params.copy()
-        del params["help"]
-
-        if len(params) == 0:
-            print("This command has no parameters")
-            return
-
-        print("Parameters:")
-        for info in params.values():
-            print("    %s" % info.get_basic_info())
-            description = info.get_desc()
-            if description != "":
-                print(textwrap.fill(description,
-                      initial_indent="        ",
-                      subsequent_indent="        ",
-                      width=70))
-
-class ModuleInfo:
-    """Define the information of one module, include module name, 
-    module supporting commands.
-    """    
-    
-    def __init__(self, name, desc = ""):
-        self.name = name
-        self.desc = desc
-        self.commands = OrderedDict()         
-        self.add_command(CommandInfo(name = "help", 
-                                     desc = "Get help for module."))
-        
-    def __str__(self):
-        return str("%s \t%s" % (self.name, self.desc))
-
-    def get_name(self):
-        return self.name
-
-    def get_desc(self):
-        return self.desc
-
-    def add_command(self, command_info):
-        """Add a CommandInfo to this ModuleInfo."""
-        self.commands[command_info.name] = command_info
-        
-    def has_command_with_name(self, command_name):
-        """Returns true if this module has a command with the given name."""
-        return command_name in self.commands
-        
-    def get_command_with_name(self, command_name):
-        """Returns the CommandInfo for the command with the given name.
-           Raises a KeyError if not found"""
-        return self.commands[command_name]
-        
-    def get_commands(self):
-        """Returns a list of all CommandInfo objects for this module."""
-        return list(self.commands.values())
-        
-    def get_command_names(self):
-        """Returns a list of the names of all commands for this module."""
-        return list(self.commands.keys())
-
-    def module_help(self):
-        """Prints the help info for this module to stdout"""
-        print("Module " + str(self))
-        print("Available commands:")
-        for k in self.commands.values():
-            n = k.get_name()
-            if len(n) >= CONST_BINDCTL_HELP_INDENT_WIDTH:
-                print("    %s" % n)
-                print(textwrap.fill(k.get_desc(),
-                      initial_indent="            ",
-                      subsequent_indent="    " +
-                      " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
-                      width=70))
-            else:
-                print(textwrap.fill("%s%s%s" %
-                    (k.get_name(),
-                     " "*(CONST_BINDCTL_HELP_INDENT_WIDTH - len(k.get_name())),
-                     k.get_desc()),
-                    initial_indent="    ",
-                    subsequent_indent="    " +
-                    " " * CONST_BINDCTL_HELP_INDENT_WIDTH,
-                    width=70))
-            
-    def command_help(self, command):
-        """Prints the help info for the command with the given name.
-           Raises KeyError if not found"""
-        self.commands[command].command_help()    
-
diff --git a/src/bin/bindctl/mycollections.py b/src/bin/bindctl/mycollections.py
deleted file mode 100644 (file)
index 42279bd..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (C) 2010  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.
-
-from collections import MutableMapping
-
-class OrderedDict(dict, MutableMapping):
-
-    # Methods with direct access to underlying attributes
-
-    def __init__(self, *args, **kwds):
-        if len(args) > 1:
-            raise TypeError('expected at 1 argument, got %d', len(args))
-        if not hasattr(self, '_keys'):
-            self._keys = []
-        self.update(*args, **kwds)
-
-    def clear(self):
-        del self._keys[:]
-        dict.clear(self)
-
-    def __setitem__(self, key, value):
-        if key not in self:
-            self._keys.append(key)
-        dict.__setitem__(self, key, value)
-
-    def __delitem__(self, key):
-        dict.__delitem__(self, key)
-        self._keys.remove(key)
-
-    def __iter__(self):
-        return iter(self._keys)
-
-    def __reversed__(self):
-        return reversed(self._keys)
-
-    def popitem(self):
-        if not self:
-            raise KeyError
-        key = self._keys.pop()
-        value = dict.pop(self, key)
-        return key, value
-
-    def __reduce__(self):
-        items = [[k, self[k]] for k in self]
-        inst_dict = vars(self).copy()
-        inst_dict.pop('_keys', None)
-        return (self.__class__, (items,), inst_dict)
-
-    # Methods with indirect access via the above methods
-
-    setdefault = MutableMapping.setdefault
-    update = MutableMapping.update
-    pop = MutableMapping.pop
-    keys = MutableMapping.keys
-    values = MutableMapping.values
-    items = MutableMapping.items
-
-    def __repr__(self):
-        pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
-        return '%s({%s})' % (self.__class__.__name__, pairs)
-
-    def copy(self):
-        return self.__class__(self)
-
-    @classmethod
-    def fromkeys(cls, iterable, value=None):
-        d = cls()
-        for key in iterable:
-            d[key] = value
-        return d
diff --git a/src/bin/bindctl/run_bindctl.sh.in b/src/bin/bindctl/run_bindctl.sh.in
deleted file mode 100755 (executable)
index 0bd8818..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl
-
-PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python/isc/cc:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
-export PYTHONPATH
-
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
-SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
-if test $SET_ENV_LIBRARY_PATH = yes; then
-       @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.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/util/threads/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
-       export @ENV_LIBRARY_PATH@
-fi
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-cd ${BINDCTL_PATH}
-exec ${PYTHON_EXEC} -O bindctl "$@"
diff --git a/src/bin/bindctl/tests/.gitignore b/src/bin/bindctl/tests/.gitignore
deleted file mode 100644 (file)
index 284bacc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/bindctl_test
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
deleted file mode 100644 (file)
index c781d7c..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = bindctl_test.py cmdparse_test.py
-EXTRA_DIST = $(PYTESTS)
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin  \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
diff --git a/src/bin/bindctl/tests/bindctl_test.in b/src/bin/bindctl/tests/bindctl_test.in
deleted file mode 100644 (file)
index 66706ed..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-BINDCTL_TEST_PATH=@abs_top_srcdir@/src/bin/bindctl/tests
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_srcdir@/src/bin
-export PYTHONPATH
-
-cd ${BINDCTL_TEST_PATH}
-exec ${PYTHON_EXEC} -O bindctl_test.py $*
-
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
deleted file mode 100644 (file)
index c3262e1..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-# Copyright (C) 2009  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.
-
-
-import unittest
-import isc.cc.data
-import os
-import io
-import errno
-import sys
-import socket
-import ssl
-import http.client
-import pwd
-import getpass
-import re
-import json
-from optparse import OptionParser
-from isc.config.config_data import ConfigData, MultiConfigData
-from isc.config.module_spec import ModuleSpec
-from isc.testutils.parse_args import TestOptParser, OptsError
-from bindctl_main import set_bindctl_options
-from bindctl import cmdparse
-from bindctl import bindcmd
-from bindctl.moduleinfo import *
-from bindctl.exception import *
-try:
-    from collections import OrderedDict
-except ImportError:
-    from mycollections import OrderedDict
-
-class TestCmdLex(unittest.TestCase):
-
-    def my_assert_raise(self, exception_type, cmd_line):
-        self.assertRaises(exception_type, cmdparse.BindCmdParser, cmd_line)
-
-
-    def testCommandWithoutParameter(self):
-        cmd_parser = cmdparse.BindCmdParser("zone add")
-        assert cmd_parser.module == "zone"
-        assert cmd_parser.command == "add"
-        self.assertEqual(len(cmd_parser.params), 0)
-
-
-    def testCommandWithParameters(self):
-        lines = {"zone add zone_name = cnnic.cn, file = cnnic.cn.file master=1.1.1.1",
-                 "zone add zone_name = \"cnnic.cn\", file ='cnnic.cn.file' master=1.1.1.1  ",
-                 "zone add zone_name = 'cnnic.cn\", file ='cnnic.cn.file' master=1.1.1.1, " }
-
-        for cmd_line in lines:
-            cmd_parser = cmdparse.BindCmdParser(cmd_line)
-            assert cmd_parser.module == "zone"
-            assert cmd_parser.command == "add"
-            assert cmd_parser.params["zone_name"] == "cnnic.cn"
-            assert cmd_parser.params["file"] == "cnnic.cn.file"
-            assert cmd_parser.params["master"] == '1.1.1.1'
-
-    def testCommandWithParamters_2(self):
-        '''Test whether the parameters in key=value can be parsed properly.'''
-        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1:34::2')
-        self.assertEqual(cmd_parser.params['name'], '1:34::2')
-
-        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1\"\'34**&2'
-                                            ' value=44\"\'\"')
-        self.assertEqual(cmd_parser.params['name'], '1\"\'34**&2')
-        self.assertEqual(cmd_parser.params['value'], '44\"\'\"')
-
-        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1\"\'34**&2'
-                                            ',value=  44\"\'\"')
-        self.assertEqual(cmd_parser.params['name'], '1\"\'34**&2')
-        self.assertEqual(cmd_parser.params['value'], '44\"\'\"')
-
-        cmd_parser = cmdparse.BindCmdParser('zone cmd name =  1\'34**&2'
-                                            'value=44\"\'\" value = '
-                                            '\"==============\'')
-        self.assertEqual(cmd_parser.params['name'], '1\'34**&2value=44\"\'\"')
-        self.assertEqual(cmd_parser.params['value'], '==============')
-
-        cmd_parser = cmdparse.BindCmdParser('zone cmd name =    \"1234, '
-                                            '567890 \" value ==&*/')
-        self.assertEqual(cmd_parser.params['name'], '1234, 567890 ')
-        self.assertEqual(cmd_parser.params['value'], '=&*/')
-
-    def testCommandWithListParam(self):
-        cmd_parser = cmdparse.BindCmdParser("zone set zone_name='cnnic.cn', "
-                                            "master='1.1.1.1, 2.2.2.2'")
-        assert cmd_parser.params["master"] == '1.1.1.1, 2.2.2.2'
-
-    def testCommandWithHelpParam(self):
-        cmd_parser = cmdparse.BindCmdParser("zone add help")
-        assert cmd_parser.params["help"] == "help"
-
-        cmd_parser = cmdparse.BindCmdParser("zone add help *&)&)*&&$#$^%")
-        assert cmd_parser.params["help"] == "help"
-        self.assertEqual(len(cmd_parser.params), 1)
-
-
-    def testCmdModuleNameFormatError(self):
-        self.my_assert_raise(CmdModuleNameFormatError, "zone=good")
-        self.my_assert_raise(CmdModuleNameFormatError, "zo/ne")
-        self.my_assert_raise(CmdModuleNameFormatError, "")
-        self.my_assert_raise(CmdModuleNameFormatError, "=zone")
-        self.my_assert_raise(CmdModuleNameFormatError, "zone,")
-
-
-    def testCmdMissCommandNameFormatError(self):
-        self.my_assert_raise(CmdMissCommandNameFormatError, "zone")
-        self.my_assert_raise(CmdMissCommandNameFormatError, "zone ")
-        self.my_assert_raise(CmdMissCommandNameFormatError, "help ")
-
-
-    def testCmdCommandNameFormatError(self):
-        self.my_assert_raise(CmdCommandNameFormatError, "zone =d")
-        self.my_assert_raise(CmdCommandNameFormatError, "zone z=d")
-        self.my_assert_raise(CmdCommandNameFormatError, "zone z-d ")
-        self.my_assert_raise(CmdCommandNameFormatError, "zone zdd/")
-        self.my_assert_raise(CmdCommandNameFormatError, "zone zdd/ \"")
-
-class TestCmdSyntax(unittest.TestCase):
-
-    def _create_bindcmd(self):
-        """Create one bindcmd"""
-
-        tool = bindcmd.BindCmdInterpreter()
-        string_spec = { 'item_type' : 'string',
-                       'item_optional' : False,
-                       'item_default' : ''}
-        int_spec = { 'item_type' : 'integer',
-                       'item_optional' : False,
-                       'item_default' : 10}
-        zone_file_param = ParamInfo(name = "zone_file",
-                                    param_spec = string_spec)
-        zone_name = ParamInfo(name = 'zone_name', param_spec = string_spec)
-        load_cmd = CommandInfo(name = "load")
-        load_cmd.add_param(zone_file_param)
-        load_cmd.add_param(zone_name)
-
-        param_master = ParamInfo(name = "master", optional = True,
-                                 param_spec = string_spec)
-        param_master = ParamInfo(name = "port", optional = True,
-                                 param_spec = int_spec)
-        param_allow_update = ParamInfo(name = "allow_update",
-                                       optional = True,
-                                       param_spec = string_spec)
-        set_cmd = CommandInfo(name = "set")
-        set_cmd.add_param(param_master)
-        set_cmd.add_param(param_allow_update)
-        set_cmd.add_param(zone_name)
-
-        reload_all_cmd = CommandInfo(name = "reload_all")
-
-        zone_module = ModuleInfo(name = "zone")
-        zone_module.add_command(load_cmd)
-        zone_module.add_command(set_cmd)
-        zone_module.add_command(reload_all_cmd)
-
-        tool.add_module_info(zone_module)
-        return tool
-
-
-    def setUp(self):
-        self.bindcmd = self._create_bindcmd()
-
-
-    def no_assert_raise(self, cmd_line):
-        cmd_parser = cmdparse.BindCmdParser(cmd_line)
-        self.bindcmd._validate_cmd(cmd_parser)
-
-
-    def my_assert_raise(self, exception_type, cmd_line):
-        cmd_parser = cmdparse.BindCmdParser(cmd_line)
-        self.assertRaises(exception_type, self.bindcmd._validate_cmd,
-                          cmd_parser)
-
-
-    def testValidateSuccess(self):
-        self.no_assert_raise("zone load zone_file='cn' zone_name='cn'")
-        self.no_assert_raise("zone load zone_file='cn', zone_name='cn', ")
-        self.no_assert_raise("zone help ")
-        self.no_assert_raise("zone load help ")
-        self.no_assert_raise("zone help help='dd' ")
-        self.no_assert_raise("zone set allow_update='1.1.1.1' zone_name='cn'")
-        self.no_assert_raise("zone set zone_name='cn'")
-        self.my_assert_raise(isc.cc.data.DataTypeError,
-                             "zone set zone_name ='cn', port='cn'")
-        self.no_assert_raise("zone reload_all")
-
-    def testCmdUnknownModuleSyntaxError(self):
-        self.my_assert_raise(CmdUnknownModuleSyntaxError, "zoned d")
-        self.my_assert_raise(CmdUnknownModuleSyntaxError, "dd dd  ")
-
-    def testCmdUnknownCmdSyntaxError(self):
-        self.my_assert_raise(CmdUnknownCmdSyntaxError, "zone dd")
-
-    def testCmdMissParamSyntaxError(self):
-        self.my_assert_raise(CmdMissParamSyntaxError,
-                             "zone load zone_file='cn'")
-        self.my_assert_raise(CmdMissParamSyntaxError,
-                             "zone load zone_name='cn'")
-        self.my_assert_raise(CmdMissParamSyntaxError,
-                             "zone set allow_update='1.1.1.1'")
-        self.my_assert_raise(CmdMissParamSyntaxError,
-                             "zone set ")
-
-    def testCmdUnknownParamSyntaxError(self):
-        self.my_assert_raise(CmdUnknownParamSyntaxError,
-                             "zone load zone_d='cn'")
-        self.my_assert_raise(CmdUnknownParamSyntaxError,
-                             "zone reload_all zone_name = 'cn'")
-        self.my_assert_raise(CmdUnknownParamSyntaxError,
-                             "zone help a b c")
-
-class TestModuleInfo(unittest.TestCase):
-
-    def test_get_param_name_by_position(self):
-        cmd = CommandInfo('command')
-        cmd.add_param(ParamInfo('name'))
-        cmd.add_param(ParamInfo('age'))
-        cmd.add_param(ParamInfo('data', optional = True))
-        cmd.add_param(ParamInfo('sex'))
-        self.assertEqual('name', cmd.get_param_name_by_position(0, 2))
-        self.assertEqual('age', cmd.get_param_name_by_position(1, 2))
-        self.assertEqual('sex', cmd.get_param_name_by_position(2, 3))
-        self.assertEqual('data', cmd.get_param_name_by_position(2, 4))
-        self.assertEqual('data', cmd.get_param_name_by_position(2, 4))
-
-        self.assertRaises(KeyError, cmd.get_param_name_by_position, 4, 4)
-
-
-
-class TestNameSequence(unittest.TestCase):
-    """
-    Test if the module/command/parameters is saved in the order creation
-    """
-
-    def _create_bindcmd(self):
-        """Create one bindcmd"""
-
-        self._cmd = CommandInfo(name = "load")
-        self.module = ModuleInfo(name = "zone")
-        self.tool = bindcmd.BindCmdInterpreter()
-        for random_str in self.random_names:
-            self._cmd.add_param(ParamInfo(name = random_str))
-            self.module.add_command(CommandInfo(name = random_str))
-            self.tool.add_module_info(ModuleInfo(name = random_str))
-
-    def setUp(self):
-        self.random_names = ['1erdfeDDWsd', '3fe', '2009erd',
-                             'Fe231', 'tere142', 'rei8WD']
-        self._create_bindcmd()
-
-    def testSequence(self):
-        param_names = self._cmd.get_param_names()
-        cmd_names = self.module.get_command_names()
-        module_names = self.tool.get_module_names()
-
-        i = 0
-        while i < len(self.random_names):
-            assert self.random_names[i] == param_names[i+1]
-            assert self.random_names[i] == cmd_names[i+1]
-            assert self.random_names[i] == module_names[i+1]
-            i = i + 1
-
-# tine class to fake a UIModuleCCSession, but only the config data
-# parts for the next set of tests
-class FakeCCSession(MultiConfigData):
-    def __init__(self):
-        self._local_changes = {}
-        self._current_config = {}
-        self._specifications = {}
-        self.add_foo_spec()
-
-    def add_foo_spec(self):
-        spec = { "module_name": "foo",
-                 "config_data": [
-                 { "item_name": "an_int",
-                   "item_type": "integer",
-                   "item_optional": False,
-                   "item_default": 1
-                 },
-                 { "item_name": "a_list",
-                   "item_type": "list",
-                   "item_optional": False,
-                   "item_default": [],
-                   "list_item_spec":
-                   { "item_name": "a_string",
-                     "item_type": "string",
-                     "item_optional": False,
-                     "item_default": "bar"
-                   }
-                 }
-                 ]
-               }
-        self.set_specification(ModuleSpec(spec))
-
-
-# fake socket
-class FakeSocket():
-    def __init__(self):
-        self.run = True
-
-    def connect(self, to):
-        if not self.run:
-            raise socket.error
-
-    def close(self):
-        self.run = False
-
-    def send(self, data):
-        if not self.run:
-            raise socket.error
-        return len(data)
-
-    def makefile(self, type):
-        return self
-
-    def sendall(self, data):
-        if not self.run:
-            raise socket.error
-        return len(data)
-
-
-class TestConfigCommands(unittest.TestCase):
-    def setUp(self):
-        self.tool = bindcmd.BindCmdInterpreter()
-        mod_info = ModuleInfo(name = "foo")
-        self.tool.add_module_info(mod_info)
-        self.tool.config_data = FakeCCSession()
-        self.stdout_backup = sys.stdout
-        self.printed_messages = []
-        self.tool._print = self.store_print
-
-    def test_precmd(self):
-        def update_all_modules_info():
-            raise socket.error
-        def precmd(line):
-            self.tool.precmd(line)
-        self.tool._update_all_modules_info = update_all_modules_info
-        # If line is equals to 'EOF', _update_all_modules_info()
-        # shouldn't be called
-        precmd('EOF')
-        self.assertRaises(socket.error, precmd, 'continue')
-
-    def store_print(self, *args):
-        '''Method to override _print in BindCmdInterpreter.
-           Instead of printing the values, appends the argument tuple
-           to the list in self.printed_messages'''
-        self.printed_messages.append(" ".join(map(str, args)))
-
-    def __check_printed_message(self, expected_message, printed_message):
-        self.assertIsNotNone(re.match(expected_message, printed_message),
-                             "Printed message '" + printed_message +
-                             "' does not match '" + expected_message + "'")
-
-    def __check_printed_messages(self, expected_messages):
-        '''Helper test function to check the printed messages against a list
-           of regexps'''
-        self.assertEqual(len(expected_messages), len(self.printed_messages))
-        for _ in map(self.__check_printed_message,
-                     expected_messages,
-                     self.printed_messages):
-            pass
-
-    def test_try_login(self):
-        # Make sure __try_login raises the correct exception
-        # upon failure of either send_POST or the read() on the
-        # response
-
-        orig_send_POST = self.tool.send_POST
-        expected_printed_messages = []
-        try:
-            def send_POST_raiseImmediately(self, params):
-                raise socket.error("test error")
-
-            self.tool.send_POST = send_POST_raiseImmediately
-            self.assertRaises(FailToLogin, self.tool._try_login, "foo", "bar")
-            expected_printed_messages.append(
-                'Error while sending login information: test error')
-            self.__check_printed_messages(expected_printed_messages)
-
-            def create_send_POST_raiseOnRead(exception):
-                '''Create a replacement send_POST() method that raises
-                   the given exception when read() is called on the value
-                   returned from send_POST()'''
-                def send_POST_raiseOnRead(self, params):
-                    class MyResponse:
-                        def read(self):
-                            raise exception
-                    return MyResponse()
-                return send_POST_raiseOnRead
-
-            # basic socket error
-            self.tool.send_POST =\
-                create_send_POST_raiseOnRead(socket.error("read error"))
-            self.assertRaises(FailToLogin, self.tool._try_login, "foo", "bar")
-            expected_printed_messages.append(
-                'Error while sending login information: read error')
-            self.__check_printed_messages(expected_printed_messages)
-
-            # connection reset
-            exc = socket.error("connection reset")
-            exc.errno = errno.ECONNRESET
-            self.tool.send_POST =\
-                create_send_POST_raiseOnRead(exc)
-            self.assertRaises(FailToLogin, self.tool._try_login, "foo", "bar")
-            expected_printed_messages.append(
-                'Error while sending login information: connection reset')
-            self.__check_printed_messages(expected_printed_messages)
-
-            # 'normal' SSL error
-            exc = ssl.SSLError()
-            self.tool.send_POST =\
-                create_send_POST_raiseOnRead(exc)
-            self.assertRaises(FailToLogin, self.tool._try_login, "foo", "bar")
-            expected_printed_messages.append(
-                'Error while sending login information: .*')
-            self.__check_printed_messages(expected_printed_messages)
-
-            # 'EOF' SSL error
-            exc = ssl.SSLError()
-            exc.errno = ssl.SSL_ERROR_EOF
-            self.tool.send_POST =\
-                create_send_POST_raiseOnRead(exc)
-            self.assertRaises(FailToLogin, self.tool._try_login, "foo", "bar")
-            expected_printed_messages.append(
-                'Error while sending login information: .*')
-            self.__check_printed_messages(expected_printed_messages)
-
-            # any other exception should be passed through
-            self.tool.send_POST =\
-                create_send_POST_raiseOnRead(ImportError())
-            self.assertRaises(ImportError, self.tool._try_login, "foo", "bar")
-            self.__check_printed_messages(expected_printed_messages)
-
-        finally:
-            self.tool.send_POST = orig_send_POST
-
-    def test_try_login_calls_cmdctl(self):
-        # Make sure _try_login() makes the right API call to cmdctl.
-        orig_conn = self.tool.conn
-        try:
-            class MyConn:
-                def __init__(self):
-                    self.method = None
-                    self.url = None
-                    self.param = None
-                    self.headers = None
-
-                def request(self, method, url, param, headers):
-                    self.method = method
-                    self.url = url
-                    self.param = param
-                    self.headers = headers
-
-                def getresponse(self):
-                    class MyResponse:
-                        def __init__(self):
-                            self.status = http.client.OK
-                        def read(self):
-                            class MyData:
-                                def decode(self):
-                                    return json.dumps(True)
-                            return MyData()
-                    return MyResponse()
-
-            self.tool.conn = MyConn()
-            self.assertTrue(self.tool._try_login('user32', 'pass64'))
-            self.assertEqual(self.tool.conn.method, 'POST')
-            self.assertEqual(self.tool.conn.url, '/login')
-            self.assertEqual(json.loads(self.tool.conn.param),
-                             {"password": "pass64", "username": "user32"})
-            self.assertIn('cookie', self.tool.conn.headers)
-        finally:
-            self.tool.conn = orig_conn
-
-    def test_run(self):
-        def login_to_cmdctl():
-            return True
-        def cmd_loop():
-            self.tool._send_message("/module_spec", None)
-
-        self.tool.login_to_cmdctl = login_to_cmdctl
-        # rewrite cmdloop() to avoid interactive mode
-        self.tool.cmdloop = cmd_loop
-
-        self.tool.conn.sock = FakeSocket()
-        self.tool.conn.sock.close()
-
-        self.assertEqual(1, self.tool.run())
-
-        # First few lines may be some kind of heading, or a warning that
-        # Python readline is unavailable, so we do a sub-string check.
-        self.assertIn("Failed to send request, the connection is closed",
-                      self.printed_messages)
-        self.assertEqual(1, len(self.printed_messages))
-
-        # validate log message for http.client.CannotSendRequest
-        self.assertEqual(1, self.tool.run())
-
-        # First few lines may be some kind of heading, or a warning that
-        # Python readline is unavailable, so we do a sub-string check.
-        self.assertIn("Can not send request, the connection is busy",
-                      self.printed_messages)
-        self.assertEqual(2, len(self.printed_messages))
-
-    def test_apply_cfg_command_int(self):
-        self.tool.location = '/'
-
-        self.assertEqual((1, MultiConfigData.DEFAULT),
-                         self.tool.config_data.get_value("/foo/an_int"))
-
-        cmd_parser = cmdparse.BindCmdParser('config set identifier='
-                                            '"foo/an_int" value="5"')
-        self.tool.apply_config_cmd(cmd_parser)
-        self.assertEqual((5, MultiConfigData.LOCAL),
-                         self.tool.config_data.get_value("/foo/an_int"))
-
-        cmd_parser = cmdparse.BindCmdParser('config unset identifier='
-                                            '"foo/an_int"')
-        self.tool.apply_config_cmd(cmd_parser)
-
-        self.assertEqual((1, MultiConfigData.DEFAULT),
-                         self.tool.config_data.get_value("/foo/an_int"))
-
-        # this should raise a NotFoundError
-        cmd_parser = cmdparse.BindCmdParser('config set identifier='
-                                            '"foo/bar" value="[]"')
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          self.tool.apply_config_cmd, cmd_parser)
-
-        cmd_parser = cmdparse.BindCmdParser('config unset identifier='
-                                            '"foo/bar"')
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          self.tool.apply_config_cmd, cmd_parser)
-
-        # this should raise a TypeError
-        cmd_parser = cmdparse.BindCmdParser('config set identifier='
-                                            '"foo/an_int" value="[]"')
-        self.assertRaises(isc.cc.data.DataTypeError,
-                          self.tool.apply_config_cmd, cmd_parser)
-
-    # this is a very specific one for use with a set of list tests
-    # to try out the flexibility of the parser (only in the next test)
-    def clt(self, full_cmd_string, item_value):
-        cmd_parser = cmdparse.BindCmdParser(full_cmd_string)
-        self.tool.apply_config_cmd(cmd_parser)
-        self.assertEqual(([item_value], MultiConfigData.LOCAL),
-                         self.tool.config_data.get_value("/foo/a_list"))
-
-    def test_apply_cfg_command_list(self):
-        self.tool.location = '/'
-
-        self.assertEqual(([], MultiConfigData.DEFAULT),
-                         self.tool.config_data.get_value("/foo/a_list"))
-
-        self.clt("config set identifier=\"foo/a_list\" value=[\"a\"]", "a")
-        self.clt("config set identifier=\"foo/a_list\" value =[\"b\"]", "b")
-        self.clt("config set identifier=\"foo/a_list\" value= [\"c\"]", "c")
-        self.clt("config set identifier=\"foo/a_list\" value = [\"d\"]", "d")
-        self.clt("config set identifier =\"foo/a_list\" value=[\"e\"]", "e")
-        self.clt("config set identifier= \"foo/a_list\" value=[\"f\"]", "f")
-        self.clt("config set identifier = \"foo/a_list\" value=[\"g\"]", "g")
-        self.clt("config set identifier = \"foo/a_list\" value = [\"h\"]", "h")
-        self.clt("config set identifier = \"foo/a_list\" value=[\"i\" ]", "i")
-        self.clt("config set identifier = \"foo/a_list\" value=[ \"j\"]", "j")
-        self.clt("config set identifier = \"foo/a_list\" value=[ \"k\" ]", "k")
-
-        # this should raise a TypeError
-        cmd_parser = cmdparse.BindCmdParser('config set identifier='
-                                            '"foo/a_list" value="a"')
-        self.assertRaises(isc.cc.data.DataTypeError,
-                          self.tool.apply_config_cmd, cmd_parser)
-
-        cmd_parser = cmdparse.BindCmdParser('config set identifier='
-                                            '"foo/a_list" value=[1]')
-        self.assertRaises(isc.cc.data.DataTypeError,
-                          self.tool.apply_config_cmd, cmd_parser)
-
-    def tearDown(self):
-        sys.stdout = self.stdout_backup
-
-    def test_cmd_has_identifier_param(self):
-        module = ModuleInfo(name="test_module")
-
-        cmd = CommandInfo(name="command_with_identifier")
-        param = ParamInfo(name=bindcmd.CFGITEM_IDENTIFIER_PARAM)
-        cmd.add_param(param)
-        module.add_command(cmd)
-
-        cmd = CommandInfo(name="command_without_identifier")
-        param = ParamInfo(name="some_argument")
-        cmd.add_param(param)
-        module.add_command(cmd)
-
-        self.tool.add_module_info(module)
-
-        cmd_parser = cmdparse.BindCmdParser('test_module '
-                                            'command_with_identifier')
-        self.assertTrue(self.tool._cmd_has_identifier_param(cmd_parser))
-
-        cmd_parser = cmdparse.BindCmdParser('test_module '
-                                            'command_without_identifier')
-        self.assertFalse(self.tool._cmd_has_identifier_param(cmd_parser))
-
-        cmd_parser = cmdparse.BindCmdParser('badmodule '
-                                            'command_without_identifier')
-        self.assertFalse(self.tool._cmd_has_identifier_param(cmd_parser))
-
-    def test_get_identifier_startswith(self):
-        hints = self.tool._get_identifier_startswith("/")
-        self.assertEqual(['foo/an_int', 'foo/a_list'], hints)
-
-        hints = self.tool._get_identifier_startswith("/foo/an")
-        self.assertEqual(['foo/an_int'], hints)
-
-        hints = self.tool._get_identifier_startswith("/bar")
-        self.assertEqual([], hints)
-
-class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
-    def __init__(self):
-        pass
-
-class TestBindCmdInterpreter(unittest.TestCase):
-
-    def setUp(self):
-        self.old_stdout = sys.stdout
-
-    def tearDown(self):
-        sys.stdout = self.old_stdout
-
-    def _create_invalid_csv_file(self, csvfilename):
-        import csv
-        csvfile = open(csvfilename, 'w')
-        writer = csv.writer(csvfile)
-        writer.writerow(['name1'])
-        writer.writerow(['name2'])
-        csvfile.close()
-
-    def test_csv_file_dir(self):
-        # Checking default value
-        home_dir = pwd.getpwnam(getpass.getuser()).pw_dir
-        self.assertEqual(home_dir + os.sep + '.bind10' + os.sep,
-                         bindcmd.BindCmdInterpreter().csv_file_dir)
-
-        new_csv_dir = '/something/different/'
-        custom_cmd = bindcmd.BindCmdInterpreter(csv_file_dir=new_csv_dir)
-        self.assertEqual(new_csv_dir, custom_cmd.csv_file_dir)
-
-    def test_get_saved_user_info(self):
-        with open(os.devnull, 'w') as f:
-            sys.stdout = f
-            cmd = bindcmd.BindCmdInterpreter()
-            users = cmd._get_saved_user_info('/notexist', 'csv_file.csv')
-            self.assertEqual([], users)
-
-            csvfilename = 'csv_file.csv'
-            self._create_invalid_csv_file(csvfilename)
-            users = cmd._get_saved_user_info('./', csvfilename)
-            self.assertEqual([], users)
-            os.remove(csvfilename)
-
-class TestCommandLineOptions(unittest.TestCase):
-    def setUp(self):
-        self.parser = TestOptParser()
-        set_bindctl_options(self.parser)
-
-    def test_csv_file_dir(self):
-        # by default the option is "undefined"
-        (options, _) = self.parser.parse_args([])
-        self.assertEqual(None, options.csv_file_dir)
-
-        # specify the option, valid case.
-        (options, _) = self.parser.parse_args(['--csv-file-dir', 'some_dir'])
-        self.assertEqual('some_dir', options.csv_file_dir)
-
-        # missing option arg; should trigger parser error.
-        self.assertRaises(OptsError, self.parser.parse_args,
-                          ['--csv-file-dir'])
-
-if __name__== "__main__":
-    unittest.main()
-
diff --git a/src/bin/bindctl/tests/cmdparse_test.py b/src/bin/bindctl/tests/cmdparse_test.py
deleted file mode 100644 (file)
index 9150ed3..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (C) 2009  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.
-
-
-import unittest
-from bindctl import cmdparse
-
-class TestCmdParse(unittest.TestCase):
-
-    def test_remove_unquoted_whitespace(self):
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a"), "a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a"), "a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a "), "a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" a "), "a")
-        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a"), "a ")
-        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a"), " a")
-        self.assertNotEqual(cmdparse._remove_unquoted_whitespace("a "), "a ")
-        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), " a ")
-        self.assertNotEqual(cmdparse._remove_unquoted_whitespace(" a "), "b")
-
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\""), "\"abc\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\""), "\"abc\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"abc\" "), "\"abc\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc\" "), "\"abc\"")
-        
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("\" abc\""), "\" abc\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"a bc\""), "\"a bc\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("\"ab c\" "), "\"ab c\"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \"abc \" "), "\"abc \"")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace(" \" a b c \" "), "\" a b c \"")
-        
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\" abc\"a"), "a\" abc\"a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"a bc\"a"), "a\"a bc\"a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a\"ab c\" a"), "a\"ab c\"a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \"abc \" a"), "a\"abc \"a")
-        self.assertEqual(cmdparse._remove_unquoted_whitespace("a \" a b c \" a"), "a\" a b c \"a")
-
-    # short-hand function to make the set of tests more readable
-    def rws(self, a, b):
-        self.assertEqual(cmdparse._remove_list_and_map_whitespace(a), b)
-
-    def test_remove_list_whitespace(self):
-        self.rws("a", "a")
-        self.rws(" a ", " a ")
-        self.rws(" [a] ", " [a] ")
-        self.rws(" [ a] ", " [a] ")
-        self.rws(" [ a ] ", " [a] ")
-        self.rws(" [ a b c ] ", " [abc] ")
-        self.rws(" [ a \"b c\" ] ", " [a\"b c\"] ")
-        self.rws("a [ a \"b c\" ] a", "a [a\"b c\"] a")
-        self.rws("a] [ a \"b c\" ] a", "a] [a\"b c\"] a")
-        self.rws(" [ a [b c] ] ", " [a[bc]] ")
-        self.rws(" [ a b][ c d ] ", " [ab][cd] ")
-        self.rws(" [ a b] [ c d ] ", " [ab] [cd] ")
-        
-        self.rws("a", "a")
-        self.rws(" a ", " a ")
-        self.rws(" {a} ", " {a} ")
-        self.rws(" { a} ", " {a} ")
-        self.rws(" { a } ", " {a} ")
-        self.rws(" { a b c } ", " {abc} ")
-        self.rws(" { a \"b c\" } ", " {a\"b c\"} ")
-        self.rws("a { a \"b c\" } a", "a {a\"b c\"} a")
-        self.rws("a} { a \"b c\" } a", "a} {a\"b c\"} a")
-        self.rws(" { a {b c} } ", " {a{bc}} ")
-        self.rws(" { a b}{ c d } ", " {ab}{cd} ")
-        self.rws(" { a b} { c d } ", " {ab} {cd} ")
-
-        self.rws(" [ a b]{ c d } ", " [ab]{cd} ")
-        self.rws(" [ a b{ c d }] ", " [ab{cd}] ")
-        self.rws(" [ a b{ \"c d\" }] ", " [ab{\"c d\"}] ")
-        
-
-if __name__== "__main__":
-    unittest.main()
-    
diff --git a/src/bin/cfgmgr/.gitignore b/src/bin/cfgmgr/.gitignore
deleted file mode 100644 (file)
index 1ef37ef..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/b10-cfgmgr
-/b10-cfgmgr.py
-/b10-cfgmgr.8
diff --git a/src/bin/cfgmgr/Makefile.am b/src/bin/cfgmgr/Makefile.am
deleted file mode 100644 (file)
index 9c73f79..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-SUBDIRS = . plugins local_plugins tests
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-pkglibexec_SCRIPTS = b10-cfgmgr
-
-CLEANFILES = b10-cfgmgr b10-cfgmgr.pyc
-
-b10_cfgmgrdir = @localstatedir@/@PACKAGE@
-#B10_cfgmgr_DATA = 
-
-man_MANS = b10-cfgmgr.8
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-cfgmgr.xml
-
-if GENERATE_DOCS
-
-b10-cfgmgr.8: b10-cfgmgr.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-cfgmgr.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cfgmgr: b10-cfgmgr.py
-       $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cfgmgr.py >$@
-       chmod a+x $@
-
-install-data-local:
-       $(mkinstalldirs) $(DESTDIR)/@localstatedir@/@PACKAGE@
-
-install-data-hook:
-       -chmod 2770 $(DESTDIR)/@localstatedir@/@PACKAGE@
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
deleted file mode 100755 (executable)
index 4bc56df..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010  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.
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-
-import bind10_config
-from isc.cc import SessionError
-import isc.util.process
-import signal
-import os
-from optparse import OptionParser
-import glob
-import os.path
-import imp
-import isc.log
-isc.log.init("b10-cfgmgr", buffer=True)
-from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
-from isc.log_messages.cfgmgr_messages import *
-import isc.util.traceback_handler
-
-isc.util.process.rename()
-
-# Import some paths from our configuration
-DATA_PATH = bind10_config.DATA_PATH
-PLUGIN_PATHS = bind10_config.PLUGIN_PATHS
-PREFIX = bind10_config.PREFIX
-DEFAULT_CONFIG_FILE = "b10-config.db"
-
-cm = None
-
-def parse_options(args=sys.argv[1:], Parser=OptionParser):
-    parser = Parser()
-    parser.add_option("-p", "--data-path", dest="data_path",
-                      help="Directory to search for configuration files " +
-                      "(default=" + DATA_PATH + ")", default=None)
-    parser.add_option("-c", "--config-filename", dest="config_file",
-                      help="Configuration database filename " +
-                      "(default=" + DEFAULT_CONFIG_FILE + ")",
-                      default=None)
-    parser.add_option("--clear-config", action="store_true",
-                      dest="clear_config", default=False,
-                      help="Back up the configuration file and start with " +
-                           "a clean one")
-    (options, args) = parser.parse_args(args)
-    if args:
-        parser.error("No non-option arguments allowed")
-    return options
-
-def signal_handler(signal, frame):
-    global cm
-    if cm:
-        cm.running = False
-
-def load_plugins(path, cm):
-    """Load all python files in the given path and treat them as plugins."""
-    # Find the python files
-    plugins = glob.glob(path + os.sep + '*.py')
-    for plugin in plugins:
-        # Generate the name of the plugin
-        filename = os.path.basename(plugin)
-        name = filename[:-3]
-        # Load it
-        module = imp.load_source(name, plugin)
-        # Ask it to provide the spec and checking function
-        (spec, check_func) = module.load()
-        # And insert it into the manager
-        cm.set_virtual_module(spec, check_func)
-
-
-def determine_path_and_file(data_path_option, config_file_option):
-    """Given the data path and config file as specified on the command line
-       (or not specified, as may be the case), determine the full path and
-       file to use when starting the config manager;
-       - if neither are given, use defaults
-       - if both are given, use both
-       - if only data path is given, use default file in that path
-       - if only file is given, use cwd() + file (if file happens to
-         be an absolute file name, path will be ignored)
-       Arguments are either a string, or None.
-       Returns a tuple containing (result_path, result_file).
-    """
-    data_path = data_path_option
-    config_file = config_file_option
-    if config_file is None:
-        config_file = DEFAULT_CONFIG_FILE
-        if data_path is None:
-            data_path = DATA_PATH
-    else:
-        if data_path is None:
-            data_path = os.getcwd()
-    return (data_path, config_file)
-
-def main():
-    options = parse_options()
-    global cm
-    try:
-        (data_path, config_file) = determine_path_and_file(options.data_path,
-                                                           options.config_file)
-        cm = ConfigManager(data_path, config_file, None, options.clear_config)
-        signal.signal(signal.SIGINT, signal_handler)
-        signal.signal(signal.SIGTERM, signal_handler)
-        cm.read_config()
-        for ppath in PLUGIN_PATHS:
-            load_plugins(ppath, cm)
-        cm.notify_b10_init()
-        cm.run()
-    except SessionError as se:
-        logger.fatal(CFGMGR_CC_SESSION_ERROR, se)
-        return 1
-    except KeyboardInterrupt as kie:
-        logger.info(CFGMGR_STOPPED_BY_KEYBOARD)
-    except ConfigManagerDataReadError as cmdre:
-        logger.fatal(CFGMGR_DATA_READ_ERROR, cmdre)
-        return 2
-    return 0
-
-if __name__ == "__main__":
-    sys.exit(isc.util.traceback_handler.traceback_handler(main))
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
deleted file mode 100644 (file)
index 78dde76..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>June 20, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-cfgmgr</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-cfgmgr</refname>
-    <refpurpose>Configuration manager</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-cfgmgr</command>
-      <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
-      <arg><option>-p <replaceable>data_path</replaceable></option></arg>
-      <arg><option>--clear-config</option></arg>
-      <arg><option>--config-filename <replaceable>config-filename</replaceable></option></arg>
-      <arg><option>--data-path <replaceable>data_path</replaceable></option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      The <command>b10-cfgmgr</command> daemon handles all BIND 10
-      system configuration. It provides persistent storage for
-      configuration, and notifies running BIND 10 modules of
-      configuration changes.
-    </para>
-
-    <para>
-      The <command>bindctl</command> can be used to talk to this
-      configuration manager via a <command>b10-cmdctl</command> connection.
-    </para>
-
-<!-- TODO: briefly explain why both msqg channel and cmdctl communication -->
-    <para>
-      This daemon communicates over a <command>b10-msgq</command> C-Channel
-      connection.  If this connection is not established,
-      <command>b10-cfgmgr</command> will exit.
-    </para>
-<!-- TODO: will it exit?  -->
-
-    <para>
-      The daemon may be cleanly stopped by sending the SIGTERM
-      signal to the process.  This shutdown does not notify the
-      subscribers.
-    </para>
-
-<!-- TODO: add a verbose or quiet switch so it is not so noisy -->
-  </refsect1>
-
-  <refsect1>
-    <title>ARGUMENTS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-      <varlistentry>
-        <term>
-          <option>--clear-config</option>
-        </term>
-        <listitem>
-          <para>
-            This will create a backup of the existing configuration
-            file, remove it, and
-            <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-            will use the default configurations.
-            The name of the backup file can be found in the logs
-            (<varname>CFGMGR_BACKED_UP_CONFIG_FILE</varname>).
-            (It will append a number to the backup filename if a
-            previous backup file exists.)
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>-c</option> <replaceable>config-filename</replaceable>,
-          <option>--config-filename</option> <replaceable>config-filename</replaceable>
-        </term>
-        <listitem>
-          <para>The configuration database filename to use. Can be either
-          absolute or relative to data path.
-          It defaults to "<filename>b10-config.db</filename>".</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>-p</option> <replaceable>data-path</replaceable>,
-          <option>--data-path</option> <replaceable>data-path</replaceable>
-        </term>
-        <listitem>
-          <para>The path where BIND 10 looks for files. The
-          configuration file is looked for here, if it is relative. If it is
-          absolute, the path is ignored.</para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>FILES</title>
-<!-- TODO: fix path -->
-    <para><filename>/usr/local/var/bind10/b10-config.db</filename>
-      &mdash; Configuration storage file.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>.
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      The <command>b10-cfgmgr</command> daemon and configuration
-      specification were initially designed by Jelte Jansen of ISC.
-      Its development began in October 2009.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/cfgmgr/local_plugins/.gitignore b/src/bin/cfgmgr/local_plugins/.gitignore
deleted file mode 100644 (file)
index 724690c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/datasrc.spec
diff --git a/src/bin/cfgmgr/local_plugins/Makefile.am b/src/bin/cfgmgr/local_plugins/Makefile.am
deleted file mode 100644 (file)
index 2f4dd39..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# Nothing is installed from this directory.  This local_plugins
-# directory overrides the plugins directory when lettuce is run, and the
-# spec file here is used to serve the static zone from the source tree
-# for testing (instead of installation prefix).
-
-noinst_DATA = datasrc.spec
-
-datasrc.spec: ../plugins/datasrc.spec.pre
-       $(SED) -e "s|@@STATIC_ZONE_FILE@@|$(abs_top_builddir)/src/lib/datasrc/static.zone|;s|@@SQLITE3_DATABASE_FILE@@|$(abs_top_builddir)/local.zone.sqlite3|" ../plugins/datasrc.spec.pre >$@
-
-CLEANFILES = datasrc.spec
diff --git a/src/bin/cfgmgr/plugins/.gitignore b/src/bin/cfgmgr/plugins/.gitignore
deleted file mode 100644 (file)
index 9641e02..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/datasrc.spec
-/datasrc.spec.pre
diff --git a/src/bin/cfgmgr/plugins/Makefile.am b/src/bin/cfgmgr/plugins/Makefile.am
deleted file mode 100644 (file)
index 2fa1e12..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-SUBDIRS = tests
-
-EXTRA_DIST = README logging.spec tsig_keys.spec
-
-datasrc.spec: datasrc.spec.pre
-       $(SED) -e "s|@@STATIC_ZONE_FILE@@|$(pkgdatadir)/static.zone|;s|@@SQLITE3_DATABASE_FILE@@|$(localstatedir)/$(PACKAGE)/zone.sqlite3|" datasrc.spec.pre >$@
-
-config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
-config_plugin_DATA = logging.spec tsig_keys.spec
-
-python_PYTHON = b10logging.py tsig_keys.py
-pythondir = $(config_plugindir)
-
-CLEANFILES = b10logging.pyc tsig_keys.pyc datasrc.spec
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/plugins/README b/src/bin/cfgmgr/plugins/README
deleted file mode 100644 (file)
index 27fb0c0..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-How to write a configuration plugin
-===================================
-
-The plugins are used to describe configuration modules that have no hosting
-process. Therefore there's no process to provide their specification and to
-check them for correctness. So the plugin takes this responsibility.
-
-Each plugin is a python file installed into the
-`@prefix@/share/@PACKAGE@/config_plugins` directory (usually
-`/usr/share/bind10/config_plugins`). It is loaded automatically at startup.
-
-The entrypoint of a plugin is function called `load()`. It should return a
-tuple, first value should be the module specification (usually instance of
-`isc.config.module_spec.ModuleSpec`, loaded by `module_spec_from_file()`).
-
-The second value is a callable object. It will be called whenever the
-configuration of module needs to be checked. The only parameter will be the new
-config (as python dictionary). To accept the new configuration, return None. If
-you return a string, it is taken as an error message. If you raise an
-exception, the config is rejected as well, however it is not considered a
-graceful rejection, but a failure of the module.
-
-So, this is how a plugin could look like:
-
-  from isc.config.module_spec import module_spec_from_file
-
-  def check(config):
-      if config['bogosity'] > 1:
-          return "Too bogus to live with"
-      else:
-          return None
-
-  def load():
-      return (module_spec_from_file('module_spec.spec'), check)
diff --git a/src/bin/cfgmgr/plugins/b10logging.py b/src/bin/cfgmgr/plugins/b10logging.py
deleted file mode 100644 (file)
index e288c6d..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright (C) 2011  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.
-
-# This is the configuration plugin for logging options
-# The name is 'b10logging' because logging.py is an existing module
-#
-# For a technical background, see
-# http://bind10.isc.org/wiki/LoggingCppApiDesign
-#
-
-from isc.config.module_spec import module_spec_from_file
-from isc.util.file import path_search
-from bind10_config import PLUGIN_PATHS
-spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
-
-ALLOWED_SEVERITIES = [ 'default',
-                       'debug',
-                       'info',
-                       'warn',
-                       'error',
-                       'fatal',
-                       'none' ]
-ALLOWED_DESTINATIONS = [ 'console',
-                         'file',
-                         'syslog' ]
-ALLOWED_STREAMS = [ 'stdout',
-                    'stderr' ]
-
-def check(config):
-    # Check the data layout first
-    errors=[]
-    if not spec.validate_config(False, config, errors):
-        return ' '.join(errors)
-    # The 'layout' is ok, now check for specific values
-    if 'loggers' in config:
-        for logger in config['loggers']:
-            # name should always be present
-            name = logger['name']
-            # report an error if name starts with * but not *.,
-            # or if * is not the first character.
-            # TODO: we might want to also warn or error if the
-            # logger name is not an existing module, but we can't
-            # really tell that from here at this point
-            star_pos = name.find('*')
-            if star_pos > 0 or\
-               name == '*.' or\
-               (star_pos == 0 and len(name) > 1 and name[1] != '.'):
-                errors.append("Bad logger name: '" + name + "': * can "
-                              "only be used instead of the full "
-                              "first-level name, e.g. '*' or "
-                              "'*.subsystem'")
-
-            if 'severity' in logger and\
-               logger['severity'].lower() not in ALLOWED_SEVERITIES:
-                errors.append("bad severity value for logger " + name +
-                              ": " + logger['severity'])
-            if 'output_options' in logger:
-                for output_option in logger['output_options']:
-                    if 'destination' in output_option:
-                        destination = output_option['destination'].lower()
-                        if destination not in ALLOWED_DESTINATIONS:
-                            errors.append("bad destination for logger " +
-                            name + ": " + output_option['destination'])
-                        else:
-                            # if left to default, output is stdout, and
-                            # it will not show in the updated config,
-                            # so 1. we only need to check it if present,
-                            # and 2. if destination is changed, so should
-                            # output. So first check checks 'in', and the
-                            # others 'not in' for 'output'
-                            if destination == "console" and\
-                               'output' in output_option and\
-                               output_option['output'] not in ALLOWED_STREAMS:
-                                errors.append("bad output for logger " + name +
-                                              ": " + output_option['output'] +
-                                              ", must be stdout or stderr")
-                            elif destination == "file" and\
-                                 ('output' not in output_option or\
-                                  output_option['output'] == ""):
-                                    errors.append("destination set to file but "
-                                                  "output not set to any "
-                                                  "filename for logger "
-                                                  + name)
-                            elif destination == "syslog" and\
-                                 'output' not in output_option or\
-                                 output_option['output'] == "":
-                                    errors.append("destination set to syslog but "
-                                                  "output not set to any facility"
-                                                  " for logger " + name)
-
-    if errors:
-        return ', '.join(errors)
-    return None
-
-def load():
-    return (spec, check)
-
diff --git a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
deleted file mode 100644 (file)
index 3f16758..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-    "module_spec": {
-        "module_name": "data_sources",
-        "module_description": "The sources of authoritative DNS data",
-        "config_data": [
-            {
-                "item_name": "classes",
-                "item_type": "named_set",
-                "item_optional": false,
-                "item_default": {
-                    "IN": [
-                        {
-                            "type": "sqlite3",
-                            "params": {
-                                "database_file": "@@SQLITE3_DATABASE_FILE@@"
-                            }
-                        }
-                    ],
-                    "CH": [
-                        {
-                            "type": "MasterFiles",
-                            "cache-enable": true,
-                            "params": {"BIND": "@@STATIC_ZONE_FILE@@"}
-                        }
-                    ]
-                },
-                "named_set_item_spec": {
-                    "item_name": "class",
-                    "item_type": "list",
-                    "item_optional": false,
-                    "item_default": [],
-                    "list_item_spec": {
-                        "item_name": "source",
-                        "item_type": "map",
-                        "item_optional": false,
-                        "item_default": {},
-                        "map_item_spec": [
-                            {
-                                "item_name": "type",
-                                "item_type": "string",
-                                "item_optional": false,
-                                "item_default": ""
-                            },
-                            {
-                                "item_name": "params",
-                                "item_type": "any",
-                                "item_optional": false,
-                                "item_default": null
-                            },
-                            {
-                                "item_name": "cache-enable",
-                                "item_type": "boolean",
-                                "item_optional": false,
-                                "item_default": false
-                            },
-                            {
-                                "item_name": "cache-zones",
-                                "item_type": "list",
-                                "item_optional": true,
-                                "list_item_spec": {
-                                    "item_name": "zone",
-                                    "item_type": "string",
-                                    "item_optional": false,
-                                    "item_default": ""
-                                }
-                            },
-                            {
-                                "item_name": "name",
-                                "item_type": "string",
-                                "item_optional": true
-                            },
-                            {
-                                "item_name": "cache-type",
-                                "item_type": "string",
-                                "item_optional": true,
-                                "item_default": "local"
-                            }
-                        ]
-                    }
-                }
-            }
-        ],
-        "commands": []
-    }
-}
diff --git a/src/bin/cfgmgr/plugins/datasrc_config_plugin.py b/src/bin/cfgmgr/plugins/datasrc_config_plugin.py
deleted file mode 100644 (file)
index 12710d5..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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.
-
-from isc.config.module_spec import module_spec_from_file
-from isc.util.file import path_search
-from bind10_config import PLUGIN_PATHS
-import isc.dns
-import isc.datasrc
-import json
-import os.path
-import copy
-
-spec = module_spec_from_file(path_search('datasrc.spec', PLUGIN_PATHS))
-
-def check(config):
-    """
-    Check the configuration.
-    """
-    # Check the data layout first
-    errors=[]
-    if not spec.validate_config(False, config, errors):
-        return ' '.join(errors)
-
-    classes = config.get('classes')
-    # Nothing passed here
-    if classes is None:
-        return None
-
-    for rr_class_str in classes:
-        try:
-            rr_class = isc.dns.RRClass(rr_class_str)
-        except isc.dns.InvalidRRClass as irc:
-            return "The class '" + rr_class_str + "' is invalid"
-
-        dlist = isc.datasrc.ConfigurableClientList(rr_class)
-        # We get a copy here, as we are going to mangle the configuration.
-        # But we don't want our changes to propagate outside, to the real
-        # configuration.
-        client_config = copy.deepcopy(classes.get(rr_class_str))
-
-        for client in client_config:
-            if client['type'] == 'MasterFiles':
-                if not client.get('cache-enable', False):
-                    return 'The cache must be enabled in MasterFiles type'
-                params = client.get('params')
-                if type(params) != dict:
-                    return 'Params of MasterFiles must be a named set'
-                for name in params:
-                    try:
-                        isc.dns.Name(name)
-                    except Exception as e: # There are many related exceptions
-                        return str(e)
-                    if not os.path.exists(params[name]):
-                        return "Master file " + params[name] + " does not exist"
-                # We remove the list of zones locally. We already checked them,
-                # and the client list would have skipped them anyway, as we
-                # forbid cache. But it would produce a warning and we don't
-                # want that here.
-                client['params'] = {}
-
-        try:
-            dlist.configure(json.dumps(client_config), False)
-        except isc.datasrc.Error as dse:
-            return str(dse)
-    return None
-
-def load():
-    return (spec, check)
diff --git a/src/bin/cfgmgr/plugins/logging.spec b/src/bin/cfgmgr/plugins/logging.spec
deleted file mode 100644 (file)
index da20ce5..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-    "module_spec": {
-        "module_name": "Logging",
-        "module_description": "Logging options",
-        "config_data": [
-            {
-                "item_name": "loggers",
-                "item_type": "list",
-                "item_optional": false,
-                "item_default": [],
-                "list_item_spec": {
-                  "item_name": "logger",
-                  "item_type": "map",
-                  "item_optional": false,
-                  "item_default": {},
-                  "map_item_spec": [
-                  {  "item_name": "name",
-                     "item_type": "string",
-                     "item_optional": false,
-                     "item_default": ""
-                  },
-                  {  "item_name": "severity",
-                     "item_type": "string",
-                     "item_optional": false,
-                     "item_default": "INFO"
-                  },
-                  {  "item_name": "debuglevel",
-                     "item_type": "integer",
-                     "item_optional": false,
-                     "item_default": 0
-                  },
-                  {  "item_name": "additive",
-                     "item_type": "boolean",
-                     "item_optional": false,
-                     "item_default": false
-                  },
-                  { "item_name": "output_options",
-                    "item_type": "list",
-                    "item_optional": false,
-                    "item_default": [],
-                    "list_item_spec": {
-                      "item_name": "output_option",
-                      "item_type": "map",
-                      "item_optional": false,
-                      "item_default": {},
-                      "map_item_spec": [
-                      { "item_name": "destination",
-                        "item_type": "string",
-                        "item_optional": false,
-                        "item_default": "console"
-                      },
-                      { "item_name": "output",
-                        "item_type": "string",
-                        "item_optional": false,
-                        "item_default": "stdout"
-                      },
-                      { "item_name": "flush",
-                        "item_type": "boolean",
-                        "item_optional": false,
-                        "item_default": true
-                      },
-                      { "item_name": "maxsize",
-                        "item_type": "integer",
-                        "item_optional": false,
-                        "item_default": 0
-                      },
-                      { "item_name": "maxver",
-                        "item_type": "integer",
-                        "item_optional": false,
-                        "item_default": 0
-                      }
-                      ]
-                    }
-                  }
-                  ]
-                }
-            }
-        ],
-        "commands": []
-    }
-}
diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am
deleted file mode 100644 (file)
index a596bc0..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = tsig_keys_test.py logging_test.py
-
-EXTRA_DIST = $(PYTESTS)
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-endif
-
-# test using command-line arguments, so use check-local target instead of TESTS
-# We need to set B10_FROM_BUILD as some of the tests need to create lock file
-# for logging.
-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 ; \
-       B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
-       B10_FROM_BUILD=$(abs_top_builddir) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
diff --git a/src/bin/cfgmgr/plugins/tests/datasrc_test.py b/src/bin/cfgmgr/plugins/tests/datasrc_test.py
deleted file mode 100644 (file)
index 546e534..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-# Copyright (C) 2011  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.
-
-# Make sure we can load the module, put it into path
-import sys
-import os
-import unittest
-import json
-sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
-import isc.log
-
-import datasrc_config_plugin
-
-class DatasrcTest(unittest.TestCase):
-    def reject(self, config):
-        """
-        Just a shortcut to check the config is rejected.
-        """
-        old = json.dumps(config)
-        self.assertIsNotNone(datasrc_config_plugin.check({"classes":
-                                                         config}))
-        # There's some data mangling inside the plugin. Check it does
-        # not propagate out, as it could change the real configuration.
-        self.assertEqual(old, json.dumps(config))
-
-    def accept(self, config):
-        """
-        Just a shortcut to check the config is accepted.
-        """
-        old = json.dumps(config)
-        self.assertIsNone(datasrc_config_plugin.check({"classes":
-                                                      config}))
-        # There's some data mangling inside the plugin. Check it does
-        # not propagate out, as it could change the real configuration.
-        self.assertEqual(old, json.dumps(config))
-
-    def test_load(self):
-        """
-        Checks the entry point returns the correct values.
-        """
-        (spec, check) = datasrc_config_plugin.load()
-        # It returns the checking function
-        self.assertEqual(check, datasrc_config_plugin.check)
-        # The plugin stores it's spec
-        self.assertEqual(spec, datasrc_config_plugin.spec)
-
-    def test_empty(self):
-        """
-        Check an empty input is OK.
-        """
-        self.accept({})
-
-    def test_invalid_spec(self):
-        """
-        Check it rejects stuff that is not well-formed according
-        to the spec.
-        """
-        self.reject("test")
-        self.reject(13)
-        self.reject([])
-        self.reject({"IN": {}})
-        self.reject({"IN": [{"bad-name": True}]})
-
-    def test_class(self):
-        """
-        The class is rejected, if it is wrong.
-        """
-        self.reject({"BAD": []})
-        self.reject({"": []})
-        # But with a good one, it works
-        for c in ["IN", "CH", "HS"]:
-            self.accept({c: []})
-
-    def test_mem_ok(self):
-        """
-        Test we accept an in-memory data source. It doesn't really matter
-        which one it is. We just want to make sure we accept something
-        and this one does not need any kind of path mangling to find
-        plugins.
-        """
-        self.accept({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        }]})
-
-    def test_invalid_mem_params(self):
-        """
-        The client list skips in-memory sources. So we check it locally that
-        invalid things are rejected.
-        """
-        # The 'params' key is mandatory for MasterFiles
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True
-        }]})
-        # The cache must be enabled
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": False,
-            "params": {}
-        }]})
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "params": {}
-        }]})
-        # Bad params type
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": []
-        }]})
-        # Bad name
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {
-                "example....org.": '/file/does/not/exist'
-            }
-        }]})
-
-    def test_no_such_file_mem(self):
-        """
-        We also check the existence of master files. Not the actual content,
-        though.
-        """
-        self.reject({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {
-                "example.org.": '/file/does/not/exist'
-            }
-        }]})
-
-    def test_names_present(self):
-        """
-        Test we don't choke on configuration with the "name" being present on
-        some items.
-        """
-        self.accept({"IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {},
-            "name": "Whatever"
-        }]})
-
-    def test_names_default_classes(self):
-        """
-        Test we can have a client of the same type in different classes
-        without specified name. The defaults should be derived both from
-        the type and the class.
-        """
-        self.accept({
-        "IN": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        }],
-        "CH": [{
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        }]})
-
-    def test_names_collision(self):
-        """
-        Reject when two names are the same.
-
-        Cases are:
-        - Explicit names.
-        - Two default names turn out to be the same (same type and class).
-        - One explicit is set to the same as the default one.
-        """
-        self.reject({"IN": [
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {},
-            "name": "Whatever"
-        },
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {},
-            "name": "Whatever"
-        }]})
-        # The same, but across different classes is allowed (we would
-        # identify the data source by class+name tuple)
-        self.accept({
-        "IN": [
-            {
-                "type": "MasterFiles",
-                "cache-enable": True,
-                "params": {},
-                "name": "Whatever"
-            }
-        ],
-        "CH": [
-            {
-                "type": "MasterFiles",
-                "cache-enable": True,
-                "params": {},
-                "name": "Whatever"
-            }
-        ]})
-        self.reject({"IN": [
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        },
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        }]})
-        self.reject({"IN": [
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {},
-            "name": "MasterFiles"
-        },
-        {
-            "type": "MasterFiles",
-            "cache-enable": True,
-            "params": {}
-        }]})
-
-if __name__ == '__main__':
-    isc.log.init("bind10")
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/cfgmgr/plugins/tests/logging_test.py b/src/bin/cfgmgr/plugins/tests/logging_test.py
deleted file mode 100644 (file)
index 818a596..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (C) 2011  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.
-
-# Make sure we can load the module, put it into path
-import sys
-import os
-sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
-
-import b10logging
-import unittest
-
-class LoggingConfCheckTest(unittest.TestCase):
-    def test_load(self):
-        """
-        Checks the entry point returns the correct values.
-        """
-        (spec, check) = b10logging.load()
-        # It returns the checking function
-        self.assertEqual(check, b10logging.check)
-        # The plugin stores it's spec
-        self.assertEqual(spec, b10logging.spec)
-
-    def test_logger_conf(self):
-        self.assertEqual(None,
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'DEBUG',
-                                            'debuglevel': 50,
-                                            'output_options':
-                                            [{'destination': 'file',
-                                              'output': '/some/file'
-                                            }]
-                                           },
-                                           {'name': 'b10-resolver',
-                                            'severity': 'WARN',
-                                            'additive': True,
-                                            'output_options':
-                                            [{'destination': 'console',
-                                              'output': 'stderr',
-                                              'flush': True
-                                            }]
-                                           },
-                                           {'name': 'b10-resolver.resolver',
-                                            'severity': 'ERROR',
-                                            'output_options': []
-                                           },
-                                           {'name': '*.cache',
-                                            'severity': 'INFO'
-                                           }
-                                          ]}))
-    def do_bad_name_test(self, name):
-        err_str = "Bad logger name: '" + name + "': * can only be "\
-                  "used instead of the full first-level name, e.g. "\
-                  "'*' or '*.subsystem'"
-        self.assertEqual(err_str,
-                         b10logging.check({'loggers':
-                                          [{'name': name,
-                                            'severity': 'DEBUG'},
-                                          ]}))
-        
-    def test_logger_bad_name(self):
-        self.do_bad_name_test("*.")
-        self.do_bad_name_test("*foo")
-        self.do_bad_name_test("*foo.lib")
-        self.do_bad_name_test("foo*")
-        self.do_bad_name_test("foo*.lib")
-
-    def test_logger_bad_severity(self):
-        self.assertEqual('bad severity value for logger *: BADVAL',
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'BADVAL'}]}))
-
-    def test_logger_bad_destination(self):
-        self.assertEqual('bad destination for logger *: baddest',
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'INFO',
-                                            'output_options': [
-                                            { 'destination': 'baddest' }
-                                            ]}]}))
-
-    def test_logger_bad_console_output(self):
-        self.assertEqual('bad output for logger *: bad_output, must be stdout or stderr',
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'INFO',
-                                            'output_options': [
-                                            { 'destination': 'console',
-                                              'output': 'bad_output'
-                                            }
-                                            ]}]}))
-
-    def test_logger_bad_file_output(self):
-        self.assertEqual('destination set to file but output not set to any filename for logger *',
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'INFO',
-                                            'output_options': [
-                                            { 'destination': 'file' }
-                                            ]}]}))
-
-    def test_logger_bad_syslog_output(self):
-        self.assertEqual('destination set to syslog but output not set to any facility for logger *',
-                         b10logging.check({'loggers':
-                                          [{'name': '*',
-                                            'severity': 'INFO',
-                                            'output_options': [
-                                            { 'destination': 'syslog' }
-                                            ]}]}))
-
-    def test_logger_bad_type(self):
-        self.assertEqual('123 should be a string',
-                         b10logging.check({'loggers':
-                                          [{'name': 123,
-                                            'severity': 'INFO'}]}))
-        self.assertEqual('123 should be a string',
-                         b10logging.check({'loggers':
-                                          [{'name': 'bind10',
-                                            'severity': 123}]}))
-
-if __name__ == '__main__':
-        unittest.main()
diff --git a/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py b/src/bin/cfgmgr/plugins/tests/tsig_keys_test.py
deleted file mode 100644 (file)
index 575879d..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-# Copyright (C) 2011  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.
-
-# Make sure we can load the module, put it into path
-import sys
-import os
-sys.path.extend(os.environ["B10_TEST_PLUGIN_DIR"].split(':'))
-
-import tsig_keys
-import unittest
-import isc.config.module_spec
-
-class TSigKeysTest(unittest.TestCase):
-    def test_load(self):
-        """
-        Checks the entry point returns the correct values.
-        """
-        (spec, check) = tsig_keys.load()
-        # It returns the checking function
-        self.assertEqual(check, tsig_keys.check)
-        # The plugin stores it's spec
-        self.assertEqual(spec, tsig_keys.spec)
-
-    def test_spec(self):
-        """
-        Checks the spec is looking sane (doesn't do really deep check here).
-        """
-        spec = tsig_keys.spec
-        # In python, we don't generally check the type of something, because
-        # of the duck typing.
-        # But this is unittest, so we check it does what we intend and
-        # supplying that's behaving the same but is different is not our
-        # intention
-        self.assertTrue(isinstance(spec, isc.config.module_spec.ModuleSpec))
-        # Correct name
-        self.assertEqual("tsig_keys", spec.get_module_name())
-        # There are no commands, nobody would handle them anyway
-        self.assertEqual([], spec.get_commands_spec())
-        # There's some nonempty configuration
-        self.assertNotEqual({}, spec.get_config_spec())
-
-    def test_missing_keys(self):
-        """
-        Test that missing keys doesn't kill us. There are just no keys there.
-        """
-        self.assertEqual(None, tsig_keys.check({}))
-
-    def test_data_empty(self):
-        """Check we accept valid config with empty set of tsig keys."""
-        self.assertEqual(None, tsig_keys.check({'keys': []}))
-
-    def test_keys_valid(self):
-        """
-        Check we accept some valid keys (we don't check all the algorithms,
-        that's the job of isc.dns.TSIGKey).
-        """
-        self.assertEqual(None, tsig_keys.check({'keys':
-            ['testkey:QklORCAxMCBpcyBjb29sCg==',
-             'test.key:QklORCAxMCBpcyBjb29sCg==:hmac-sha1']}))
-
-    def test_keys_same_name(self):
-        """
-        Test we reject when we have multiple keys with the same name.
-        """
-        self.assertEqual("Multiple TSIG keys with name 'test.key.'",
-                         tsig_keys.check({'keys':
-                                         ['test.key:QklORCAxMCBpcyBjb29sCg==',
-                                          'test.key:b3RoZXIK']}))
-
-    def test_invalid_key(self):
-        """
-        Test we reject invalid key.
-        """
-        self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
-                         tsig_keys.check({'keys': ['invalid.key']}))
-        self.assertEqual(
-            "TSIG: Incomplete input for base64: 123",
-            tsig_keys.check({'keys': ['invalid.key:123']}))
-
-    def test_bad_format(self):
-        """
-        Test we fail on bad format. We don't really care much how here, though,
-        as this should not get in through config manager anyway.
-        """
-        self.assertNotEqual(None, tsig_keys.check({'bad_name': {}}))
-        self.assertNotEqual(None, tsig_keys.check({'keys': 'not_list'}))
-        self.assertNotEqual(None, tsig_keys.check({'keys': 42}))
-        self.assertNotEqual(None, tsig_keys.check({'keys': {}}))
-
-if __name__ == '__main__':
-        unittest.main()
diff --git a/src/bin/cfgmgr/plugins/tsig_keys.py b/src/bin/cfgmgr/plugins/tsig_keys.py
deleted file mode 100644 (file)
index d57e645..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2011  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.
-
-# This is the plugin for tsig_keys configuration section. The TSIG keyring
-# lives there (eg. all the shared secrets, with some exceptions where there
-# are some TSIG keys elsewhere, but these should be removed soon). We do
-# sanity checking of user configuration here, simply by trying to construct
-# all the keys here.
-
-from isc.config.module_spec import module_spec_from_file
-from isc.util.file import path_search
-from pydnspp import TSIGKey, InvalidParameter
-from bind10_config import PLUGIN_PATHS
-spec = module_spec_from_file(path_search('tsig_keys.spec', PLUGIN_PATHS))
-
-def check(config):
-    # Check the data layout first
-    errors=[]
-    if not spec.validate_config(False, config, errors):
-        return ' '.join(errors)
-    # Get the list of keys, if any
-    keys = config.get('keys', [])
-    # Run through them, check they can be constructed and there are no
-    # duplicates
-    keyNames = set()
-    for key in keys:
-        try:
-            name = str(TSIGKey(key).get_key_name())
-        except InvalidParameter as e:
-            return "TSIG: " + str(e)
-        if name in keyNames:
-            return "Multiple TSIG keys with name '" + name + "'"
-        keyNames.add(name)
-    # No error found, so let's assume it's OK
-    return None
-
-def load():
-    return (spec, check)
diff --git a/src/bin/cfgmgr/plugins/tsig_keys.spec b/src/bin/cfgmgr/plugins/tsig_keys.spec
deleted file mode 100644 (file)
index e558dd2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-    "module_spec": {
-        "module_name": "tsig_keys",
-        "module_description": "The TSIG keyring is stored here",
-        "config_data": [
-            {
-                "item_name": "keys",
-                "item_type": "list",
-                "item_optional": false,
-                "item_default": [],
-                "list_item_spec": {
-                    "item_name": "key",
-                    "item_type": "string",
-                    "item_optional": false,
-                    "item_default": ""
-                }
-            }
-        ],
-        "commands": []
-    }
-}
diff --git a/src/bin/cfgmgr/tests/.gitignore b/src/bin/cfgmgr/tests/.gitignore
deleted file mode 100644 (file)
index dcdab2d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/b10-cfgmgr_test.py
diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am
deleted file mode 100644 (file)
index 6293571..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = b10-cfgmgr_test.py
-
-noinst_SCRIPTS = $(PYTESTS)
-EXTRA_DIST = testdata/plugins/testplugin.py
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       chmod +x $(abs_builddir)/$$pytest ; \
-       TESTDATA_PATH=$(abs_srcdir)/testdata \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \
-       $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
-       done
-
-CLEANDIRS = testdata/plugins/__pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
deleted file mode 100644 (file)
index c7c1081..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-# Copyright (C) 2010  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 the configuration manager run script
-#
-
-import unittest
-import os
-import sys
-import bind10_config
-from isc.testutils.parse_args import OptsError, TestOptParser
-
-class MyConfigManager:
-    def __init__(self, path, filename, session=None, rename_config_file=False):
-        self._path = path
-        self.read_config_called = False
-        self.notify_b10_init_called = False
-        self.run_called = False
-        self.write_config_called = False
-        self.rename_config_called = False
-        self.running = True
-        self.virtual_modules = []
-
-    def read_config(self):
-        self.read_config_called = True
-
-    def notify_b10_init(self):
-        self.notify_b10_init_called = True
-
-    def run(self):
-        self.run_called = True
-
-    def write_config(self):
-        self.write_config_called = True
-
-    def rename_config_file(self, ofile, nfile):
-        self.rename_config_called = True
-
-    def set_virtual_module(self, spec, function):
-        self.virtual_modules.append((spec, function))
-
-class TestPlugins(unittest.TestCase):
-    def test_load_plugins(self):
-        """Test we can successfully find and load the mock plugin."""
-        # Let it load the plugin
-        b = __import__("b10-cfgmgr")
-        # The parameters aren't important for this test
-        cm = MyConfigManager(None, None)
-        b.load_plugins(os.environ['TESTDATA_PATH'] + os.sep + 'plugins', cm)
-        # Check exactly one plugin was loaded and the right data were fed into
-        # the cm
-        self.assertEqual(len(cm.virtual_modules), 1)
-        (spec, check) = cm.virtual_modules[0]
-        self.assertEqual(spec.get_module_name(), "mock_config_plugin")
-        self.assertEqual(check(None), "Mock config plugin")
-
-class TestConfigManagerStartup(unittest.TestCase):
-    def test_cfgmgr(self):
-        # some creative module use;
-        # b10-cfgmgr has a hyphen, so we use __import__
-        # this also gives us the chance to override the imported
-        # module ConfigManager in it.
-        b = __import__("b10-cfgmgr")
-        orig_load = b.load_plugins
-        b.PLUGIN_PATHS = ["/plugin/path"]
-        self.loaded_plugins = False
-        def load_plugins(path, cm):
-            # Check it's called with proper arguments
-            self.assertEqual(path, "/plugin/path")
-            self.assertTrue(isinstance(cm, MyConfigManager))
-            self.loaded_plugins = True
-        b.load_plugins = load_plugins
-        b.ConfigManager = MyConfigManager
-
-        b.main()
-        b.load_plugins = orig_load
-
-        self.assertTrue(b.cm.read_config_called)
-        self.assertTrue(b.cm.notify_b10_init_called)
-        self.assertTrue(b.cm.run_called)
-        self.assertTrue(self.loaded_plugins)
-        # if there are no changes, config is not written
-        self.assertFalse(b.cm.write_config_called)
-        self.assertFalse(b.cm.rename_config_called)
-
-        self.assertTrue(b.cm.running)
-        b.signal_handler(None, None)
-        self.assertFalse(b.cm.running)
-
-        # TODO: take value from the 'global config module'
-        # (and rename the .in away from this file again)
-        data_path = "@localstatedir@/@PACKAGE@".replace("${prefix}", "@prefix@")
-        self.assertEqual(data_path, b.DATA_PATH)
-
-        # remove the 'module' again, or later tests may fail
-        # (if it is already present it won't be loaded again)
-        sys.modules.pop("b10-cfgmgr")
-
-    def test_cfgmgr_from_source(self):
-        tmp_env_var = "/just/some/dir"
-        env_var = None
-        if "B10_FROM_SOURCE" in os.environ:
-            env_var = os.environ["B10_FROM_SOURCE"]
-
-        os.environ["B10_FROM_SOURCE"] = tmp_env_var
-        bind10_config.reload()
-        b = __import__("b10-cfgmgr", globals(), locals())
-        b.PLUGIN_PATH = [] # It's enough to test plugins in one test
-        b.ConfigManager = MyConfigManager
-        self.assertEqual(tmp_env_var, b.DATA_PATH)
-
-        if env_var != None:
-            os.environ["B10_FROM_SOURCE"] = env_var
-        bind10_config.reload()
-
-        sys.modules.pop("b10-cfgmgr")
-
-class TestParseArgs(unittest.TestCase):
-    """
-    Test for the parsing of command line arguments. We provide a different
-    array to parse instead.
-    """
-
-    def test_defaults(self):
-        """
-        Test the default values when no options are provided.
-        """
-        # Pass it empty array, not our arguments
-        b = __import__("b10-cfgmgr")
-        parsed = b.parse_options([], TestOptParser)
-        self.assertEqual(None, parsed.data_path)
-        self.assertEqual(None, parsed.config_file)
-
-    def test_wrong_args(self):
-        """
-        Test it fails when we pass invalid option.
-        """
-        b = __import__("b10-cfgmgr")
-        self.assertRaises(OptsError, b.parse_options, ['--wrong-option'],
-                          TestOptParser)
-
-    def test_not_arg(self):
-        """
-        Test it fails when there's an argument that's not option
-        (eg. without -- at the beginning).
-        """
-        b = __import__("b10-cfgmgr")
-        self.assertRaises(OptsError, b.parse_options, ['not-option'],
-                          TestOptParser)
-
-    def test_datapath(self):
-        """
-        Test overwriting the data path.
-        """
-        b = __import__("b10-cfgmgr")
-        parsed = b.parse_options(['--data-path=/path'], TestOptParser)
-        self.assertEqual('/path', parsed.data_path)
-        self.assertEqual(None, parsed.config_file)
-        parsed = b.parse_options(['-p', '/path'], TestOptParser)
-        self.assertEqual('/path', parsed.data_path)
-        self.assertEqual(None, parsed.config_file)
-        self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
-        self.assertRaises(OptsError, b.parse_options, ['--data-path'],
-                          TestOptParser)
-
-    def test_db_filename(self):
-        """
-        Test setting the configuration database file.
-        """
-        b = __import__("b10-cfgmgr")
-        parsed = b.parse_options(['--config-filename=filename'],
-                                 TestOptParser)
-        self.assertEqual(None, parsed.data_path)
-        self.assertEqual("filename", parsed.config_file)
-        parsed = b.parse_options(['-c', 'filename'], TestOptParser)
-        self.assertEqual(None, parsed.data_path)
-        self.assertEqual("filename", parsed.config_file)
-        self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
-        self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
-                          TestOptParser)
-
-    def test_determine_path_and_file(self):
-        b = __import__("b10-cfgmgr")
-        self.assertEqual((b.DATA_PATH, b.DEFAULT_CONFIG_FILE),
-                         b.determine_path_and_file(None, None))
-        self.assertEqual(("/foo", b.DEFAULT_CONFIG_FILE),
-                         b.determine_path_and_file("/foo", None))
-        self.assertEqual((os.getcwd(), "file.config"),
-                         b.determine_path_and_file(None, "file.config"))
-        self.assertEqual(("/foo", "bar"),
-                         b.determine_path_and_file("/foo", "bar"))
-
-    def test_clear_config(self):
-        b = __import__("b10-cfgmgr")
-        parsed = b.parse_options([], TestOptParser)
-        self.assertFalse(parsed.clear_config)
-        parsed = b.parse_options(['--clear-config'], TestOptParser)
-        self.assertTrue(parsed.clear_config)
-
-if __name__ == '__main__':
-    unittest.main()
-
diff --git a/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py b/src/bin/cfgmgr/tests/testdata/plugins/testplugin.py
deleted file mode 100644 (file)
index a50eefe..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2011  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.
-
-# A test plugin. It does mostly nothing, just provides a function we can
-# recognize from the test. However, it looks like a real plugin, with the
-# (almost) correct interface, even when it's not used.
-
-class MockSpec:
-    """Mock spec, contains no data, it can only provide it's name.
-       It'll not really be used for anything."""
-    def get_module_name(self):
-        return "mock_config_plugin"
-
-def mock_check_config(config):
-    """Mock function to check config. Does nothing, only returns
-       an "error" string to indicate it's this one."""
-    return "Mock config plugin"
-
-def load():
-    """When a plugin is loaded, this is called to provide the spec and
-       checking function."""
-    return (MockSpec(), mock_check_config)
diff --git a/src/bin/cmdctl/.gitignore b/src/bin/cmdctl/.gitignore
deleted file mode 100644 (file)
index e702e49..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/b10-certgen
-/b10-certgen.1
-/b10-cmdctl
-/b10-cmdctl.8
-/cmdctl-certfile.pem
-/cmdctl-keyfile.pem
-/cmdctl.py
-/cmdctl.spec
-/cmdctl.spec.pre
-/run_b10-cmdctl.sh
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
deleted file mode 100644 (file)
index a2d04a3..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-SUBDIRS = . tests
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-pkglibexec_SCRIPTS = b10-cmdctl
-
-bin_PROGRAMS = b10-certgen
-
-nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py
-pylogmessagedir = $(pyexecdir)/isc/log_messages/
-
-b10_cmdctldir = $(pkgdatadir)
-
-CERTFILES = cmdctl-keyfile.pem cmdctl-certfile.pem
-
-b10_cmdctl_DATA = cmdctl.spec
-
-CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.pyc
-
-man_MANS = b10-cmdctl.8 b10-certgen.1
-DISTCLEANFILES = $(man_MANS) cmdctl-certfile.pem cmdctl-keyfile.pem
-EXTRA_DIST  = $(man_MANS) b10-certgen.xml b10-cmdctl.xml cmdctl_messages.mes
-EXTRA_DIST += cmdctl-accounts.csv
-
-if GENERATE_DOCS
-
-b10-cmdctl.8: b10-cmdctl.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-cmdctl.xml
-
-b10-certgen.1: b10-certgen.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-certgen.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-cmdctl.spec: cmdctl.spec.pre
-       $(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
-
-$(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py : cmdctl_messages.mes
-       $(top_builddir)/src/lib/log/compiler/message \
-       -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cmdctl_messages.mes
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cmdctl: cmdctl.py $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py $(CERTFILES)
-       $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
-       chmod a+x $@
-
-b10_certgen_SOURCES = b10-certgen.cc
-b10_certgen_CXXFLAGS = $(BOTAN_INCLUDES)
-b10_certgen_LDFLAGS = $(BOTAN_LIBS)
-
-# Generate the initial certificates immediately
-cmdctl-keyfile.pem: b10-certgen
-       ./b10-certgen -q -w
-
-# This is a hack, as b10-certgen creates both cmdctl-keyfile.pem and
-# cmdctl-certfile.pem, and in a parallel make, making these targets
-# simultaneously may result in corrupted files. With GNU make, there is
-# a non-portable way of working around this with pattern rules, but we
-# adopt this hack instead. The downside is that cmdctl-certfile.pem will
-# not be re-generated if cmdctl-keyfile.pem exists and is older. See
-# Trac ticket #2962.
-cmdctl-certfile.pem: cmdctl-keyfile.pem
-       touch $(builddir)/cmdctl-keyfile.pem
-
-if INSTALL_CONFIGURATIONS
-
-# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
-# because these file will contain sensitive information.
-install-data-local:
-       $(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
-       for f in $(CERTFILES) ; do      \
-         if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then      \
-           ${INSTALL} -m 640 $$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ;  \
-         fi ;  \
-       done
-
-endif
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/cmdctl/README b/src/bin/cmdctl/README
deleted file mode 100644 (file)
index cba200f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-When b10-cmdctrl runs, user should provide one certificate and create one account for bindctl. 
-
-
-1. Get a certificate
-
-b10-cmdctrl is a https server, so a certificate should be acquired for the SSL-encrypted connection services it provides.
-There are many ways of acquiring appropriate cetificates, such as buying one from a certification authority. Another common practice is to generate a self-signed certificate. The simplest way to do this is with the OpenSSL package, using something like the following:
-      
-   openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem
-   
-   copy the content from the generated cert.pem. to create_your_cert.pem.   
-
-2. Generate one account for bindctl or other web client tools.
-
-    run command:
-    python \parkinglot\tools\usr_mgr\usr_mgr.py
-
-    The account information is saved in passwd.csv
-
-
-
-
diff --git a/src/bin/cmdctl/TODO b/src/bin/cmdctl/TODO
deleted file mode 100644 (file)
index 09827f5..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-. Add return code for RESTful API document of b10-cmdctl.
-. Update man page for b10-cmdctl?
-. Add check for the content of key/certificate file
-  (when cmdctl starts or is configured by bindctl).
-. Use only one msgq/session to communicate with other modules?
-. Add more test cases, especially about the cases where CmdctlException
-  is raised
diff --git a/src/bin/cmdctl/b10-certgen.cc b/src/bin/cmdctl/b10-certgen.cc
deleted file mode 100644 (file)
index 59bba9e..0000000
+++ /dev/null
@@ -1,429 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-#include <botan/botan.h>
-#include <botan/x509self.h>
-#include <botan/x509stor.h>
-#include <botan/rsa.h>
-#include <botan/dsa.h>
-#include <botan/data_src.h>
-using namespace Botan;
-
-#include <iostream>
-#include <fstream>
-#include <memory>
-#include <getopt.h>
-
-// For cleaner 'does not exist or is not readable' output than
-// botan provides
-#include <unistd.h>
-#include <errno.h>
-
-// This is a simple tool that creates a self-signed PEM certificate
-// for use with BIND 10. It creates a simple certificate for initial
-// setup. Currently, all values are hardcoded defaults. For future
-// versions, we may want to add more options for administrators.
-
-// It will create a PEM file containing a certificate with the following
-// values:
-// common name: localhost
-// organization: BIND10
-// country code: US
-
-// Additional error return codes; these are specifically
-// chosen to be distinct from validation error codes as
-// provided by Botan. Their main use is to distinguish
-// error cases in the unit tests.
-const int DECODING_ERROR = 100;
-const int BAD_OPTIONS = 101;
-const int READ_ERROR = 102;
-const int WRITE_ERROR = 103;
-const int UNKNOWN_ERROR = 104;
-const int NO_SUCH_FILE = 105;
-const int FILE_PERMISSION_ERROR = 106;
-
-void
-usage() {
-    std::cout << "Usage: b10-certgen [OPTION]..." << std::endl;
-    std::cout << "Validate, create, or update a self-signed certificate for "
-                 "use with b10-cmdctl" << std::endl;
-    std::cout << "" << std::endl;
-    std::cout << "Options:" << std::endl;
-    std::cout << "-c, --certfile=FILE\t\tfile to read or store the certificate"
-              << std::endl;
-    std::cout << "-f, --force\t\t\toverwrite existing certificate even if it"
-              << std::endl <<"\t\t\t\tis valid" << std::endl;
-    std::cout << "-h, --help\t\t\tshow this help" << std::endl;
-    std::cout << "-k, --keyfile=FILE\t\tfile to store the generated private key"
-              << std::endl;
-    std::cout << "-w, --write\t\t\tcreate a new certificate if the given file"
-              << std::endl << "\t\t\t\tdoes not exist, or if is is not valid"
-              << std::endl;
-    std::cout << "-q, --quiet\t\t\tprint no output when creating or validating"
-              << std::endl;
-}
-
-/// \brief Returns true if the given file exists
-///
-/// \param filename The file to check
-/// \return true if file exists
-bool
-fileExists(const std::string& filename) {
-    return (access(filename.c_str(), F_OK) == 0);
-}
-
-/// \brief Returns true if the given file exists and is readable
-///
-/// \param filename The file to check
-/// \return true if file exists and is readable
-bool
-fileIsReadable(const std::string& filename) {
-    return (access(filename.c_str(), R_OK) == 0);
-}
-
-/// \brief Returns true if the given file exists and is writable
-///
-/// \param filename The file to check
-/// \return true if file exists and is writable
-bool
-fileIsWritable(const std::string& filename) {
-    return (access(filename.c_str(), W_OK) == 0);
-}
-
-/// \brief Helper function for readable error output;
-///
-/// Returns string representation of X509 result code
-/// This does not appear to be provided by Botan itself
-///
-/// \param code An \c X509_Code instance
-/// \return A human-readable c string
-const char*
-X509CodeToString(const X509_Code& code) {
-    // note that this list provides more than we would
-    // need in this context, it is just the enum from
-    // the source code of Botan.
-    switch (code) {
-    case VERIFIED:
-        return ("verified");
-    case UNKNOWN_X509_ERROR:
-        return ("unknown x509 error");
-    case CANNOT_ESTABLISH_TRUST:
-        return ("cannot establish trust");
-    case CERT_CHAIN_TOO_LONG:
-        return ("certificate chain too long");
-    case SIGNATURE_ERROR:
-        return ("signature error");
-    case POLICY_ERROR:
-        return ("policy error");
-    case INVALID_USAGE:
-        return ("invalid usage");
-    case CERT_FORMAT_ERROR:
-        return ("certificate format error");
-    case CERT_ISSUER_NOT_FOUND:
-        return ("certificate issuer not found");
-    case CERT_NOT_YET_VALID:
-        return ("certificate not yet valid");
-    case CERT_HAS_EXPIRED:
-        return ("certificate has expired");
-    case CERT_IS_REVOKED:
-        return ("certificate has been revoked");
-    case CRL_FORMAT_ERROR:
-        return ("crl format error");
-    case CRL_NOT_YET_VALID:
-        return ("crl not yet valid");
-    case CRL_HAS_EXPIRED:
-        return ("crl has expired");
-    case CA_CERT_CANNOT_SIGN:
-        return ("CA cert cannot sign");
-    case CA_CERT_NOT_FOR_CERT_ISSUER:
-        return ("CA certificate not for certificate issuer");
-    case CA_CERT_NOT_FOR_CRL_ISSUER:
-        return ("CA certificate not for crl issuer");
-    default:
-        return ("Unknown X509 code");
-    }
-}
-
-class CertificateTool {
-public:
-    CertificateTool(bool quiet) : quiet_(quiet) {}
-
-    int
-    createKeyAndCertificate(const std::string& key_file_name,
-                            const std::string& cert_file_name) {
-        try {
-            AutoSeeded_RNG rng;
-
-            // Create and store a private key
-            print("Creating key file " + key_file_name);
-            RSA_PrivateKey key(rng, 2048);
-            std::ofstream key_file(key_file_name.c_str());
-            if (!key_file.good()) {
-                print(std::string("Error writing to ") + key_file_name +
-                      ": " + std::strerror(errno));
-                return (WRITE_ERROR);
-            }
-            key_file << PKCS8::PEM_encode(key, rng, "");
-            if (!key_file.good()) {
-                print(std::string("Error writing to ") + key_file_name +
-                      ": " + std::strerror(errno));
-                return (WRITE_ERROR);
-            }
-            key_file.close();
-
-            // Certificate options, currently hardcoded.
-            // For a future version we may want to make these
-            // settable.
-            X509_Cert_Options opts;
-            opts.common_name = "localhost";
-            opts.organization = "UNKNOWN";
-            opts.country = "XX";
-
-            opts.CA_key();
-
-            print("Creating certificate file " + cert_file_name);
-
-            // The exact call changed after 1.8, adding the
-            // hash function option
-#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
-            X509_Certificate cert =
-            X509::create_self_signed_cert(opts, key, "SHA-256", rng);
-#else
-            X509_Certificate cert =
-            X509::create_self_signed_cert(opts, key, rng);
-#endif
-
-            std::ofstream cert_file(cert_file_name.c_str());
-            if (!cert_file.good()) {
-                print(std::string("Error writing to ") + cert_file_name +
-                      ": " + std::strerror(errno));
-                return (WRITE_ERROR);
-            }
-            cert_file << cert.PEM_encode();
-            if (!cert_file.good()) {
-                print(std::string("Error writing to ") + cert_file_name +
-                      ": " + std::strerror(errno));
-                return (WRITE_ERROR);
-            }
-            cert_file.close();
-        } catch(std::exception& e) {
-            std::cout << "Error creating key or certificate: " << e.what()
-                      << std::endl;
-            return (UNKNOWN_ERROR);
-        }
-        return (0);
-    }
-
-    int
-    validateCertificate(const std::string& certfile) {
-        // Since we are dealing with a self-signed certificate here, we
-        // also use the certificate to check itself; i.e. we add it
-        // as a trusted certificate, then validate the certificate itself.
-        //const X509_Certificate cert(certfile);
-        try {
-            X509_Store store;
-            DataSource_Stream in(certfile);
-            store.add_trusted_certs(in);
-
-            const X509_Code result = store.validate_cert(certfile);
-
-            if (result == VERIFIED) {
-                print(certfile + " is valid");
-            } else {
-                print(certfile + " failed to verify: " +
-                      X509CodeToString(result));
-            }
-            return (result);
-        } catch (const Botan::Decoding_Error& bde) {
-            print(certfile + " failed to verify: " + bde.what());
-            return (DECODING_ERROR);
-        } catch (const Botan::Stream_IO_Error& bsie) {
-            print(certfile + " not read: " + bsie.what());
-            return (READ_ERROR);
-        }
-    }
-
-    /// \brief Runs the tool
-    ///
-    /// \param create_cert  Create certificate if true, validate if false.
-    ///                     Does nothing if certificate exists and is valid.
-    /// \param force_create Create new certificate even if it is valid.
-    /// \param certfile     Certificate file to read to or write from.
-    /// \param keyfile      Key file to write if certificate is created.
-    ///                     Ignored if create_cert is false
-    /// \return zero on success, non-zero on failure
-    int
-    run(bool create_cert, bool force_create, const std::string& certfile,
-        const std::string& keyfile)
-    {
-        if (create_cert) {
-            // Unless force is given, only create it if the current
-            // one is not OK
-
-            // First do some basic permission checks; both files
-            // should either not exist, or be both readable
-            // and writable
-            // The checks are done one by one so all errors can
-            // be enumerated in one go
-            if (fileExists(certfile)) {
-                if (!fileIsReadable(certfile)) {
-                    print(certfile + " not readable: " + std::strerror(errno));
-                    create_cert = false;
-                }
-                if (!fileIsWritable(certfile)) {
-                    print(certfile + " not writable: " + std::strerror(errno));
-                    create_cert = false;
-                }
-            }
-            // The key file really only needs write permissions (for
-            // b10-certgen that is)
-            if (fileExists(keyfile)) {
-                if (!fileIsWritable(keyfile)) {
-                    print(keyfile + " not writable: " + std::strerror(errno));
-                    create_cert = false;
-                }
-            }
-            if (!create_cert) {
-                print("Not creating new certificate, "
-                      "check file permissions");
-                return (FILE_PERMISSION_ERROR);
-            }
-
-            // If we reach this, we know that if they exist, we can both
-            // read and write them, so now it's up to content checking
-            // and/or force_create
-
-            if (force_create || !fileExists(certfile) ||
-                validateCertificate(certfile) != VERIFIED) {
-                return (createKeyAndCertificate(keyfile, certfile));
-            } else {
-                print("Not creating a new certificate (use -f to force)");
-            }
-        } else {
-            if (!fileExists(certfile)) {
-                print(certfile + ": " + std::strerror(errno));
-                return (NO_SUCH_FILE);
-            }
-            if (!fileIsReadable(certfile)) {
-                print(certfile + " not readable: " + std::strerror(errno));
-                return (FILE_PERMISSION_ERROR);
-            }
-            int result = validateCertificate(certfile);
-            if (result != 0) {
-                print("Running with -w would overwrite the certificate");
-            }
-            return (result);
-        }
-        return (0);
-    }
-private:
-    /// Prints the message to stdout unless quiet_ is true
-    void print(const std::string& msg) {
-        if (!quiet_) {
-            std::cout << msg << std::endl;
-        }
-    }
-
-    bool quiet_;
-};
-
-int
-main(int argc, char* argv[])
-{
-    Botan::LibraryInitializer init;
-
-    // create or check certificate
-    bool create_cert = false;
-    // force creation even if not necessary
-    bool force_create = false;
-    // don't print any output
-    bool quiet = false;
-
-    // default certificate file
-    std::string certfile("cmdctl-certfile.pem");
-    // default key file
-    std::string keyfile("cmdctl-keyfile.pem");
-
-    // whether or not the above values have been
-    // overridden (used in command line checking)
-    bool certfile_default = true;
-    bool keyfile_default = true;
-
-    // It would appear some environments insist on
-    // char* here (Sunstudio on Solaris), so we const_cast
-    // them to get rid of compiler warnings.
-    const struct option long_options[] = {
-        { const_cast<char*>("certfile"), required_argument, NULL, 'c' },
-        { const_cast<char*>("force"), no_argument, NULL, 'f' },
-        { const_cast<char*>("help"), no_argument, NULL, 'h' },
-        { const_cast<char*>("keyfile"), required_argument, NULL, 'k' },
-        { const_cast<char*>("write"), no_argument, NULL, 'w' },
-        { const_cast<char*>("quiet"), no_argument, NULL, 'q' },
-        { NULL, 0, NULL, 0 }
-    };
-
-    int opt, option_index;
-    while ((opt = getopt_long(argc, argv, "c:fhk:wq", long_options,
-                              &option_index)) != -1) {
-        switch (opt) {
-            case 'c':
-                certfile = optarg;
-                certfile_default = false;
-                break;
-            case 'f':
-                force_create = true;
-                break;
-            case 'h':
-                usage();
-                return (0);
-                break;
-            case 'k':
-                keyfile = optarg;
-                keyfile_default = false;
-                break;
-            case 'w':
-                create_cert = true;
-                break;
-            case 'q':
-                quiet = true;
-                break;
-            default:
-                // A message will have already been output about the error.
-                return (BAD_OPTIONS);
-        }
-    }
-
-    if (optind < argc) {
-        std::cout << "Error: extraneous arguments" << std::endl << std::endl;
-        usage();
-        return (BAD_OPTIONS);
-    }
-
-    // Some sanity checks on option combinations
-    if (create_cert && (certfile_default ^ keyfile_default)) {
-        std::cout << "Error: keyfile and certfile must both be specified "
-                     "if one of them is when calling b10-certgen in write "
-                     "mode." << std::endl;
-        return (BAD_OPTIONS);
-    }
-    if (!create_cert && !keyfile_default) {
-        std::cout << "Error: keyfile is not used when not in write mode"
-                  << std::endl;
-        return (BAD_OPTIONS);
-    }
-
-    // Initialize the tool and perform the appropriate action(s)
-    CertificateTool tool(quiet);
-    return (tool.run(create_cert, force_create, certfile, keyfile));
-}
diff --git a/src/bin/cmdctl/b10-certgen.xml b/src/bin/cmdctl/b10-certgen.xml
deleted file mode 100644 (file)
index d7fe100..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>November 15, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-certgen</refentrytitle>
-    <manvolnum>1</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-certgen</refname>
-    <refpurpose>X509 Certificate generation tool for use with b10-cmdctl</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2012</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-certgen</command>
-        <group choice="opt">
-          <arg choice="[OPTION]..."><option>-</option></arg>
-        </group>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>The <command>b10-certgen</command> tool validates, creates, or
-      updates a self-signed X509 certificate for use in b10-cmdctl.
-    </para>
-
-    <para>
-      The connection between <command>bindctl</command> and
-      <command>b10-cmdctl</command> is done over HTTPS, and therefore
-      <command>b10-cmdctl</command> needs a certificate. Since these
-      certificates have expiry dates, they also need to be regenerated at
-      some point.
-
-      There are many tools to do so, but for ease of use, <command>
-      b10-certgen</command> can create a simple self-signed certificate.
-
-      By default, it will not create anything, but it will merely check an
-      existing certificate (if not specified, cmdctl-certfile.pem, in the
-      current working directory). And print whether it is valid, and
-      whether it would update if the option '-w' is given.
-
-      With that option, the certificate could then be replaced by a newly
-      created one. If the certificate is still valid, it would still not
-      be overwritten (however, if it is found to be invalid, for example
-      because it has expired, it would create a new one).
-
-      A new certificate is always created if the certificate file does
-      not exist, or if creation is forced (with the -f option).
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>ARGUMENTS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term>
-          <option>-c <replaceable>file</replaceable></option>,
-          <option>--certfile=<replaceable>file</replaceable></option>
-        </term>
-        <listitem>
-          <para>
-            File to read the certificate from, or write the certificate to.
-            If <option>-w</option> and <option>-c</option> are used,
-            <option>-k</option> is mandatory as well.
-          </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term>
-          <option>-f</option>,
-          <option>--force</option>
-        </term>
-        <listitem>
-          <para>
-            Force updating of certificate when <option>-w</option> is used,
-            even if the existing certificate is still valid.
-          </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term>
-          <option>-h</option>,
-          <option>--help</option>
-        </term>
-        <listitem>
-          <para>
-            Print the command line arguments and exit.
-          </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term>
-          <option>-k <replaceable>file</replaceable></option>,
-          <option>--keyfile=<replaceable>file</replaceable></option>
-        </term>
-        <listitem>
-          <para>
-            File to write the private key to. This option is only valid when <option>-w</option> is used, and if this option is used, <option>-c</option> is mandatory as well.
-          </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term>
-          <option>-w</option>,
-          <option>--write</option>
-        </term>
-        <listitem>
-          <para>
-            Check the given certificate file. If it does not exist, a new
-            private key and certificate are created. If it does exist, the
-            certificate is validated. If it is not valid (for instance
-            because it has expired), it is overwritten with a newly created
-            certificate. If it is valid, nothing happens (use
-            <option>-f</option> to force an update in that case).
-          </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term>
-          <option>-q</option>,
-          <option>--quiet</option>
-        </term>
-        <listitem>
-          <para>
-            Don't print informational messages (only command-line errors are
-            printed). Useful in scripts when only the return code is needed.
-          </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      The <command>b10-certgen</command> tool was first implemented
-      in November 2012 for the ISC BIND 10 project.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>EXAMPLE</title>
-    <para>
-      To update an expired certificate in BIND 10 that has been installed to
-      /usr/local:
-      <screen>
-$> cd /usr/local/etc/bind10/
-
-$> b10-certgen
-cmdctl-certfile.pem failed to verify: certificate has expired
-Running with -w would overwrite the certificate
-
-$> b10-certgen --write
-cmdctl-certfile.pem failed to verify: certificate has expired
-Creating key file cmdctl-keyfile.pem
-Creating certificate file cmdctl-certfile.pem
-
-$> b10-certgen --write
-cmdctl-certfile.pem is valid
-Not creating a new certificate (use -f to force)
-      </screen>
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
deleted file mode 100644 (file)
index c66837c..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>February 28, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-cmdctl</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-cmdctl</refname>
-    <refpurpose>BIND 10 remote control daemon</refpurpose>
-<!-- TODO: is that okay? -->
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010-2012</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-cmdctl</command>
-
-      <arg><option>-a <replaceable>string</replaceable></option></arg>
-      <arg><option>-h</option></arg>
-      <arg><option>-i <replaceable>number</replaceable></option></arg>
-      <arg><option>-p <replaceable>number</replaceable></option></arg>
-      <arg><option>-v</option></arg>
-      <arg><option>--address <replaceable>string</replaceable></option></arg>
-      <arg><option>--help</option></arg>
-      <arg><option>--idle-timeout <replaceable>number</replaceable></option></arg>
-      <arg><option>--port <replaceable>number</replaceable></option></arg>
-      <arg><option>--verbose</option></arg>
-      <arg><option>--version</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>The <command>b10-cmdctl</command> daemon provides an entry
-      for commands sent to the BIND 10 services.
-      For example, the <command>bindctl</command> user interface
-      communicates via <command>b10-cmdctl</command>.
-    </para>
-
-    <para>
-      It is a lightweight HTTPS server with HTTP Digest Authentication
-      (username and password validation).
-      It offers a RESTful style interface.
-<!-- TODO: document the RESTful APIs -->
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>OPTIONS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term><option>-a <replaceable>string</replaceable></option>,
-          <option>--address <replaceable>string</replaceable></option></term>
-        <listitem><para>
-          The IP address that <command>b10-cmdctl</command> will listen on.
-          The default is 127.0.0.1.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-h</option>,
-          <option>--help</option></term>
-        <listitem><para>
-          Display command usage.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-i <replaceable>number</replaceable></option>,
-          <option>--idle-timeout <replaceable>number</replaceable></option></term>
-        <listitem><para>
-          The socket idle timeout for the HTTPS connection in seconds.
-          The default is 1200 seconds.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-p <replaceable>number</replaceable></option>,
-          <option>--port <replaceable>number</replaceable></option></term>
-        <listitem><para>
-          The port number <command>b10-cmdctl</command> will listen on.
-          The default is 8080.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-v</option></term>
-        <term><option>--verbose</option></term>
-        <listitem><para>
-          Enable verbose mode.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>
-          <option>--version</option></term>
-        <listitem><para>
-          Display the version number and exit.</para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-
-  </refsect1>
-
-  <refsect1>
-    <title>CONFIGURATION AND COMMANDS</title>
-    <para>
-      The configurable settings are:
-    </para>
-
-    <para>
-      <varname>accounts_file</varname> defines the path to the
-      user accounts database.
-      The default is
-      <filename>/usr/local/etc/bind10/cmdctl-accounts.csv</filename>.
-    </para>
-
-    <para>
-      <varname>cert_file</varname> defines the path to the
-      PEM certificate file.
-      The default is
-      <filename>/usr/local/etc/bind10/cmdctl-certfile.pem</filename>.
-    </para>
-
-    <para>
-      <varname>key_file</varname> defines the path to the PEM private key
-      file.
-      The default is
-      <filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>.
-    </para>
-
-<!-- TODO: formating -->
-    <para>
-      The configuration command is:
-    </para>
-
-    <para>
-      <command>shutdown</command> exits <command>b10-cmdctl</command>.
-      This has an optional <varname>pid</varname> argument to
-      select the process ID to stop.
-      (Note that the b10-init process may restart this service
-      if configured.)
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>FILES</title>
-<!-- TODO: replace /usr/local -->
-<!-- TODO: permissions -->
-<!-- TODO: what about multiple accounts? -->
-<!-- TODO: shouldn't the password file name say cmdctl in it? -->
-    <para><filename>/usr/local/etc/bind10/cmdctl-accounts.csv</filename>
-      &mdash; account database containing the name, hashed password,
-      and the salt.
-    </para>
-<!-- TODO: replace /usr/local -->
-<!-- TODO: permissions -->
-<!-- TODO: shouldn't have both in same file, will be configurable -->
-    <para><filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>
-      &mdash; contains the Private key.
-    </para>
-    <para><filename>/usr/local/etc/bind10/cmdctl-certfile.pem</filename>
-      &mdash; contains the Certificate.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>AUTHORS</title>
-    <para>
-      The <command>b10-cmdctl</command> daemon was initially designed
-      and coded by Zhang Likun of CNNIC.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/cmdctl/cmdctl-accounts.csv b/src/bin/cmdctl/cmdctl-accounts.csv
deleted file mode 100644 (file)
index dc1a052..0000000
+++ /dev/null
@@ -1 +0,0 @@
-root,6f0c73bd33101a5ec0294b3ca39fec90ef4717fe,"\7f{5hV&$^(]!uV,3H>E~=f`I;,HgMl""`Eyao4^0l|Nlz|%R9Y0v)#/t'u@CzJ$U\7f)"\r
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
deleted file mode 100755 (executable)
index 2e56629..0000000
+++ /dev/null
@@ -1,718 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010  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.
-
-''' cmdctl module is the configuration entry point for all commands from bindctl
-or some other web tools client of bind10. cmdctl is pure https server which provi-
-des RESTful API. When command client connecting with cmdctl, it should first login
-with legal username and password.
-    When cmdctl starting up, it will collect command specification and
-configuration specification/data of other available modules from configmanager, then
-wait for receiving request from client, parse the request and resend the request to
-the proper module. When getting the request result from the module, send back the
-resut to client.
-'''
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import os
-import socketserver
-import http.server
-import urllib.parse
-import json
-import re
-import ssl, socket
-import isc
-import pprint
-import csv
-import random
-import time
-import signal
-import errno
-from isc.config import ccsession
-import isc.cc.proto_defs
-import isc.util.process
-import isc.net.parse
-from optparse import OptionParser, OptionValueError
-from hashlib import sha1
-from isc.util import socketserver_mixin
-from isc.log_messages.cmdctl_messages import *
-import isc.util.traceback_handler
-
-isc.log.init("b10-cmdctl", buffer=True)
-logger = isc.log.Logger("cmdctl")
-
-# Debug level for communication with BIND10
-DBG_CMDCTL_MESSAGING = logger.DBGLVL_COMMAND
-
-try:
-    import threading
-except ImportError:
-    import dummy_threading as threading
-
-isc.util.process.rename()
-
-__version__ = 'BIND10'
-URL_PATTERN = re.compile('/([\w]+)(?:/([\w]+))?/?')
-CONFIG_DATA_URL = 'config_data'
-MODULE_SPEC_URL = 'module_spec'
-
-
-# If B10_FROM_BUILD is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_BUILD" in os.environ:
-    SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/cmdctl"
-else:
-    PREFIX = "@prefix@"
-    DATAROOTDIR = "@datarootdir@"
-    SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
-SPECFILE_LOCATION = SPECFILE_PATH + os.sep + "cmdctl.spec"
-
-class CmdctlException(Exception):
-    pass
-
-def check_file(file_name):
-    # TODO: Check contents of certificate file
-    if not os.path.exists(file_name):
-        raise CmdctlException("'%s' does not exist" % file_name)
-
-    if not os.path.isfile(file_name):
-        raise CmdctlException("'%s' is not a file" % file_name)
-
-    if not os.access(file_name, os.R_OK):
-        raise CmdctlException("'%s' is not readable" % file_name)
-
-
-class SecureHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
-    '''https connection request handler.
-    Currently only GET and POST are supported.  '''
-    def __init__(self, request, client_address, server):
-        http.server.BaseHTTPRequestHandler.__init__(self, request,
-                                                    client_address, server)
-        self.session_id = None
-
-    def do_GET(self):
-        '''The client should send its session id in header with
-        the name 'cookie'
-        '''
-        self.session_id = self.headers.get('cookie')
-        rcode, reply = http.client.OK, []
-        if self._is_session_valid():
-            if self._is_user_logged_in():
-                rcode, reply = self._handle_get_request()
-            else:
-                rcode, reply = http.client.UNAUTHORIZED, ["please login"]
-        else:
-            rcode = http.client.BAD_REQUEST
-
-        self.send_response(rcode)
-        self.end_headers()
-        self.wfile.write(json.dumps(reply).encode())
-
-    def _handle_get_request(self):
-        '''Currently only support the following three url GET request '''
-        id, module = self._parse_request_path()
-        return self.server.get_reply_data_for_GET(id, module)
-
-    def _is_session_valid(self):
-        return self.session_id is not None
-
-    def _is_user_logged_in(self):
-        login_time = self.server.user_sessions.get(self.session_id)
-        if not login_time:
-            return False
-
-        idle_time = time.time() - login_time
-        if idle_time > self.server.idle_timeout:
-            return False
-        # Update idle time
-        self.server.user_sessions[self.session_id] = time.time()
-        return True
-
-    def _parse_request_path(self):
-        '''Parse the url, the legal url should like /ldh or /ldh/ldh '''
-        groups = URL_PATTERN.match(self.path)
-        if not groups:
-            return (None, None)
-        else:
-            return (groups.group(1), groups.group(2))
-
-    def do_POST(self):
-        '''Process POST request. '''
-        '''Process user login and send command to proper module
-        The client should send its session id in header with
-        the name 'cookie'
-        '''
-        self.session_id = self.headers.get('cookie')
-        rcode, reply = http.client.OK, []
-        if self._is_session_valid():
-            if self.path == '/login':
-                rcode, reply = self._handle_login()
-            elif self._is_user_logged_in():
-                rcode, reply = self._handle_post_request()
-            else:
-                rcode, reply = http.client.UNAUTHORIZED, ["please login"]
-        else:
-            rcode, reply = http.client.BAD_REQUEST, ["session isn't valid"]
-
-        self.send_response(rcode)
-        self.end_headers()
-        self.wfile.write(json.dumps(reply).encode())
-
-    def _handle_login(self):
-        if self._is_user_logged_in():
-            return http.client.OK, ["user has already login"]
-        is_user_valid, error_info = self._check_user_name_and_pwd()
-        if is_user_valid:
-            self.server.save_user_session_id(self.session_id)
-            return http.client.OK, ["login success"]
-        else:
-            return http.client.UNAUTHORIZED, error_info
-
-    def _check_user_name_and_pwd(self):
-        '''Check user name and its password '''
-        length = self.headers.get('Content-Length')
-
-        if not length:
-            return False, ["invalid username or password"]
-
-        try:
-            user_info = json.loads((self.rfile.read(int(length))).decode())
-        except:
-            return False, ["invalid username or password"]
-
-        user_name = user_info.get('username')
-        if not user_name:
-            return False, ["need user name"]
-        if not self.server.get_user_info(user_name):
-            logger.info(CMDCTL_NO_SUCH_USER, user_name)
-            return False, ["username or password error"]
-
-        user_pwd = user_info.get('password')
-        if not user_pwd:
-            return False, ["need password"]
-        local_info = self.server.get_user_info(user_name)
-        pwd_hashval = sha1((user_pwd + local_info[1]).encode())
-        if pwd_hashval.hexdigest() != local_info[0]:
-            logger.info(CMDCTL_BAD_PASSWORD, user_name)
-            return False, ["username or password error"]
-
-        return True, None
-
-
-    def _handle_post_request(self):
-        '''Handle all the post request from client. '''
-        mod, cmd = self._parse_request_path()
-        if (not mod) or (not cmd):
-            return http.client.BAD_REQUEST, ['malformed url']
-
-        param = None
-        len = self.headers.get('Content-Length')
-        if len:
-            try:
-                post_str = str(self.rfile.read(int(len)).decode())
-                param = json.loads(post_str)
-            except:
-                pass
-
-        rcode, reply = self.server.send_command_to_module(mod, cmd, param)
-        ret = http.client.OK
-        if rcode != 0:
-            ret = http.client.BAD_REQUEST
-        return ret, reply
-
-    def log_request(self, code='-', size='-'):
-        '''Rewrite the log request function, log nothing.'''
-        pass
-
-
-class CommandControl():
-    '''Get all modules' config data/specification from configmanager.
-    receive command from client and resend it to proper module.
-    '''
-    def __init__(self, httpserver, verbose = False):
-        ''' httpserver: the http server which use the object of
-        CommandControl to communicate with other modules. '''
-        self._verbose = verbose
-        self._httpserver = httpserver
-        self.__msg_handler_thread = None # set in _start_msg_handle_thread
-        self._lock = threading.Lock()
-        self._setup_session()
-        self.modules_spec = self._get_modules_specification()
-        self._config_data = self._get_config_data_from_config_manager()
-        self._serving = True
-        self._start_msg_handle_thread()
-
-    def _setup_session(self):
-        '''Setup the session for receving the commands
-        sent from other modules. There are two sessions
-        for cmdctl, one(self.module_cc) is used for receiving
-        commands sent from other modules, another one (self._cc)
-        is used to send the command from Bindctl or other tools
-        to proper modules.'''
-        self._cc = isc.cc.Session()
-        self._module_cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
-                                              self.config_handler,
-                                              self.command_handler)
-        self._module_name = self._module_cc.get_module_spec().get_module_name()
-        self._cmdctl_config_data = self._module_cc.get_full_config()
-        self._module_cc.start()
-
-    def _accounts_file_check(self, filepath):
-        ''' Check whether the accounts file is valid, each row
-        should be a list with 3 items.'''
-        csvfile = None
-        errstr = None
-        try:
-            csvfile = open(filepath)
-            reader = csv.reader(csvfile)
-            for row in reader:
-                a = (row[0], row[1], row[2])
-        except (IOError, IndexError) as e:
-            errstr = 'Invalid accounts file: ' + str(e)
-        finally:
-            if csvfile:
-                csvfile.close()
-
-        return errstr
-
-    def _config_data_check(self, new_config):
-        ''' Check whether the new config data is valid or
-        not. '''
-        errstr = None
-        for key in new_config:
-            if key == 'version':
-                continue
-            elif key in ['key_file', 'cert_file']:
-                # TODO: we only check whether the file exist, is a
-                # file, and is readable; but further check need to be done:
-                # eg. whether the private/certificate is valid.
-                path = new_config[key]
-                try:
-                    check_file(path)
-                except CmdctlException as cce:
-                    errstr = str(cce)
-            elif key == 'accounts_file':
-                errstr = self._accounts_file_check(new_config[key])
-            else:
-                errstr = 'unknown config item: ' + key
-
-            if errstr != None:
-                logger.error(CMDCTL_BAD_CONFIG_DATA, errstr);
-                return ccsession.create_answer(1, errstr)
-
-        return ccsession.create_answer(0)
-
-    def config_handler(self, new_config):
-        answer = self._config_data_check(new_config)
-        rcode, val = ccsession.parse_answer(answer)
-        if rcode != 0:
-            return answer
-
-        with self._lock:
-            for key in new_config:
-                if key in self._cmdctl_config_data:
-                    self._cmdctl_config_data[key] = new_config[key]
-        return answer
-
-    def _get_current_thread(self):
-        """A simple wrapper of returning the 'current' thread object.
-
-        This is extracted as a 'protected' method so tests can override for
-        their convenience.
-
-        """
-        return threading.currentThread()
-
-    def command_handler(self, command, args):
-        """Handle commands from other modules.
-
-        This method must not be called by any other threads than
-        __msg_handler_thread invoked at the intialization of the class;
-        otherwise it would cause critical race or dead locks.
-
-        """
-        # Check the restriction described above.
-        assert self._get_current_thread() == self.__msg_handler_thread
-
-        answer = ccsession.create_answer(0)
-        if command == ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
-            # The 'value' of a specification update can be either
-            # a specification, or None. In the first case, simply
-            # set it. If it is None, delete the module if it is
-            # known.
-            with self._lock:
-                if args[1] is None:
-                    if args[0] in self.modules_spec:
-                        del self.modules_spec[args[0]]
-                    else:
-                        answer = ccsession.create_answer(1,
-                                                         'No such module: ' +
-                                                         args[0])
-                else:
-                    self.modules_spec[args[0]] = args[1]
-
-        elif command == ccsession.COMMAND_SHUTDOWN:
-            #When cmdctl get 'shutdown' command from b10-init,
-            #shutdown the outer httpserver.
-            self._module_cc.send_stopping()
-            self._httpserver.shutdown()
-            self._serving = False
-
-        else:
-            answer = ccsession.create_answer(1, 'unknown command: ' + command)
-
-        return answer
-
-    def _start_msg_handle_thread(self):
-        ''' Start one thread to handle received message from msgq.'''
-        td = threading.Thread(target=self._handle_msg_from_msgq)
-        td.daemon = True
-        self.__msg_handler_thread = td
-        td.start()
-
-    def _handle_msg_from_msgq(self):
-        '''Process all the received commands with module session. '''
-        while self._serving:
-            self._module_cc.check_command(False)
-
-    def _parse_command_result(self, rcode, reply):
-        '''Ignore the error reason when command rcode isn't 0, '''
-        if rcode != 0:
-            return {}
-        return reply
-
-    def _get_config_data_from_config_manager(self):
-        '''Get config data for all modules from configmanager '''
-        rcode, reply = self.send_command('ConfigManager', ccsession.COMMAND_GET_CONFIG)
-        return self._parse_command_result(rcode, reply)
-
-    def _update_config_data(self, module_name, command_name):
-        '''Get lastest config data for all modules from configmanager '''
-        if module_name == 'ConfigManager' and command_name == ccsession.COMMAND_SET_CONFIG:
-            data = self._get_config_data_from_config_manager()
-            with self._lock:
-                self._config_data = data
-
-    def get_config_data(self):
-        with self._lock:
-            data = self._config_data
-        return data
-
-    def get_modules_spec(self):
-        with self._lock:
-            spec = self.modules_spec
-        return spec
-
-    def _get_modules_specification(self):
-        '''Get all the modules' specification files. '''
-        rcode, reply = self.send_command('ConfigManager', ccsession.COMMAND_GET_MODULE_SPEC)
-        return self._parse_command_result(rcode, reply)
-
-    def send_command_with_check(self, module_name, command_name, params=None):
-        '''Before send the command to modules, check if module_name, command_name
-        parameters are legal according the spec file of the module.
-        Return rcode, dict. TODO, the rcode should be defined properly.
-        rcode = 0: dict is the correct returned value.
-        rcode > 0: dict is : { 'error' : 'error reason' }
-        '''
-        # core module ConfigManager does not have a specification file
-        if module_name == 'ConfigManager':
-            return self.send_command(module_name, command_name, params)
-
-        specs = self.get_modules_spec()
-        if module_name not in specs.keys():
-            return 1, {'error' : 'unknown module'}
-
-        spec_obj = isc.config.module_spec.ModuleSpec(specs[module_name], False)
-        errors = []
-        if not spec_obj.validate_command(command_name, params, errors):
-            return 1, {'error': errors[0]}
-
-        return self.send_command(module_name, command_name, params)
-
-    def send_command(self, module_name, command_name, params=None):
-        """Send the command from bindctl to proper module.
-
-        Note that commands sent to Cmdctl itself are also delivered via the
-        CC session.  Since this method is called from a thread handling a
-        particular HTTP session, it cannot directly call command_handler().
-
-        """
-        errstr = 'unknown error'
-        answer = None
-        logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_SEND_COMMAND,
-                     command_name, module_name)
-
-        # FIXME: Due to the fact that we use a separate session
-        # from the module one (due to threads and blocking), and
-        # because the plain cc session does not have the high-level
-        # rpc-call method, we use the low-level way and create the
-        # command ourselves.
-        msg = ccsession.create_command(command_name, params)
-        seq = self._cc.group_sendmsg(msg, module_name, want_answer=True)
-        logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_COMMAND_SENT, command_name,
-                     module_name)
-        # TODO, it may be blocked, msqg need to add a new interface waiting
-        # in timeout.
-        try:
-            answer, env = self._cc.group_recvmsg(False, seq)
-        except isc.cc.session.SessionTimeout:
-            errstr = "Module '%s' not responding" % module_name
-
-        if answer:
-            try:
-                rcode, arg = ccsession.parse_answer(answer)
-                if rcode == 0:
-                    self._update_config_data(module_name, command_name)
-                    if arg != None:
-                        return rcode, arg
-                    else:
-                        return rcode, {}
-                else:
-                    errstr = \
-                        str(answer[isc.cc.proto_defs.CC_PAYLOAD_RESULT][1])
-            except ccsession.ModuleCCSessionError as mcse:
-                errstr = str("Error in ccsession answer:") + str(mcse)
-
-        logger.error(CMDCTL_COMMAND_ERROR, command_name, module_name, errstr)
-        return 1, {'error': errstr}
-
-    def get_cmdctl_config_data(self):
-        ''' If running in source code tree, use keyfile, certificate
-        and user accounts file in source code. '''
-        if "B10_FROM_SOURCE" in os.environ:
-            sysconf_path = os.environ["B10_FROM_SOURCE"] + "/src/bin/cmdctl/"
-            accountsfile  = sysconf_path + "cmdctl-accounts.csv"
-            keyfile = sysconf_path + "cmdctl-keyfile.pem"
-            certfile = sysconf_path + "cmdctl-certfile.pem"
-            return (keyfile, certfile, accountsfile)
-
-        with self._lock:
-            keyfile = self._cmdctl_config_data.get('key_file')
-            certfile = self._cmdctl_config_data.get('cert_file')
-            accountsfile = self._cmdctl_config_data.get('accounts_file')
-
-        return (keyfile, certfile, accountsfile)
-
-class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
-                       socketserver.ThreadingMixIn,
-                       http.server.HTTPServer):
-    '''Make the server address can be reused.'''
-    allow_reuse_address = True
-
-    def __init__(self, server_address, RequestHandlerClass,
-                 CommandControlClass,
-                 idle_timeout = 1200, verbose = False):
-        '''idle_timeout: the max idle time for login'''
-        socketserver_mixin.NoPollMixIn.__init__(self)
-        try:
-            http.server.HTTPServer.__init__(self, server_address, RequestHandlerClass)
-            logger.debug(DBG_CMDCTL_MESSAGING, CMDCTL_STARTED,
-                         server_address[0], server_address[1])
-        except socket.error as err:
-            if err.errno == errno.EADDRINUSE:
-                raise CmdctlException(("Error creating server, because " +\
-                                       "port %d on address %s is " +\
-                                       "already in use. Please stop " +\
-                                       "the application that uses it or " +\
-                                       "see the guide about using a " +\
-                                       "different port for b10-cmdctl.") % \
-                                      (server_address[1], \
-                                       server_address[0]))
-            else:
-                raise CmdctlException("Error creating server, because: %s" % str(err))
-
-        self.user_sessions = {}
-        self.idle_timeout = idle_timeout
-        self.cmdctl = CommandControlClass(self, verbose)
-        self._verbose = verbose
-        self._lock = threading.Lock()
-        self._user_infos = {}
-        self.__accounts_file = None
-        self.__accounts_file_mtime = 0
-
-    def _create_user_info(self, accounts_file):
-        '''Read all user's name and its' salt, hashed password
-        from accounts file.'''
-
-        # If the file does not exist, set accounts to empty, and return
-        if not os.path.exists(accounts_file):
-            self._user_infos = {}
-            self.__accounts_file = None
-            self.__accounts_file_mtime = 0
-            return
-
-        # If the filename hasn't changed, and the file itself
-        # has neither, do nothing
-        accounts_file_mtime = os.stat(accounts_file).st_mtime
-        if self.__accounts_file == accounts_file and\
-           accounts_file_mtime <= self.__accounts_file_mtime:
-            return
-
-        with self._lock:
-            self._user_infos = {}
-            csvfile = None
-            try:
-                csvfile = open(accounts_file)
-                reader = csv.reader(csvfile)
-                for row in reader:
-                    self._user_infos[row[0]] = [row[1], row[2]]
-            except (IOError, IndexError) as e:
-                logger.error(CMDCTL_USER_DATABASE_READ_ERROR,
-                             accounts_file, e)
-            finally:
-                if csvfile:
-                    csvfile.close()
-
-        self.__accounts_file = accounts_file
-        self.__accounts_file_mtime = accounts_file_mtime
-        if len(self._user_infos) == 0:
-            logger.error(CMDCTL_NO_USER_ENTRIES_READ)
-
-    def get_user_info(self, username):
-        '''Get user's salt and hashed string. If the user
-        doesn't exist, return None, or else, the list
-        [salt, hashed password] will be returned.'''
-        with self._lock:
-            info = self._user_infos.get(username)
-        return info
-
-    def save_user_session_id(self, session_id):
-        ''' Record user's id and login time. '''
-        self.user_sessions[session_id] = time.time()
-
-    def _check_key_and_cert(self, key, cert):
-        check_file(key)
-        check_file(cert);
-
-    def _wrap_socket_in_ssl_context(self, sock, key, cert):
-        try:
-            self._check_key_and_cert(key, cert)
-            ssl_sock = ssl.wrap_socket(sock,
-                                       server_side=True,
-                                       certfile=cert,
-                                       keyfile=key,
-                                       ssl_version=ssl.PROTOCOL_SSLv23)
-            # Return here (if control leaves this blocks it will raise an
-            # error)
-            return ssl_sock
-        except ssl.SSLError as err:
-            self.close_request(sock)
-            logger.error(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
-            raise
-        except (CmdctlException, IOError, socket.error) as cce:
-            self.close_request(sock)
-            logger.error(CMDCTL_SSL_SETUP_FAILURE_READING_CERT, cce)
-            raise
-
-    def get_request(self):
-        '''Get client request socket and wrap it in SSL context. '''
-        key, cert, account_file = self.cmdctl.get_cmdctl_config_data()
-        self._create_user_info(account_file)
-        newsocket, fromaddr = self.socket.accept()
-        ssl_sock = self._wrap_socket_in_ssl_context(newsocket, key, cert)
-        return (ssl_sock, fromaddr)
-
-    def get_reply_data_for_GET(self, id, module):
-        '''Currently only support the following three url GET request '''
-        rcode, reply = http.client.NO_CONTENT, []
-        if not module:
-            if id == CONFIG_DATA_URL:
-                rcode, reply = http.client.OK, self.cmdctl.get_config_data()
-            elif id == MODULE_SPEC_URL:
-                rcode, reply = http.client.OK, self.cmdctl.get_modules_spec()
-
-        return rcode, reply
-
-    def send_command_to_module(self, module_name, command_name, params):
-        return self.cmdctl.send_command_with_check(module_name, command_name, params)
-
-httpd = None
-
-def signal_handler(signal, frame):
-    if httpd:
-        httpd.shutdown()
-    sys.exit(0)
-
-def set_signal_handler():
-    signal.signal(signal.SIGTERM, signal_handler)
-    signal.signal(signal.SIGINT, signal_handler)
-
-def run(addr = 'localhost', port = 8080, idle_timeout = 1200, verbose = False):
-    ''' Start cmdctl as one https server. '''
-    httpd = SecureHTTPServer((addr, port), SecureHTTPRequestHandler,
-                             CommandControl, idle_timeout, verbose)
-
-    httpd.serve_forever()
-
-def check_port(option, opt_str, value, parser):
-    try:
-        parser.values.port = isc.net.parse.port_parse(value)
-    except ValueError as e:
-        raise OptionValueError(str(e))
-
-def check_addr(option, opt_str, value, parser):
-    try:
-        isc.net.parse.addr_parse(value)
-        parser.values.addr = value
-    except ValueError as e:
-        raise OptionValueError(str(e))
-
-def set_cmd_options(parser):
-    parser.add_option('-p', '--port', dest = 'port', type = 'int',
-            action = 'callback', callback=check_port,
-            default = '8080', help = 'port cmdctl will use')
-
-    parser.add_option('-a', '--address', dest = 'addr', type = 'string',
-            action = 'callback', callback=check_addr,
-            default = '127.0.0.1', help = 'IP address cmdctl will use')
-
-    parser.add_option('-i', '--idle-timeout', dest = 'idle_timeout', type = 'int',
-            default = '1200', help = 'login idle time out')
-
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
-            help="display more about what is going on")
-
-def main():
-    set_signal_handler()
-    parser = OptionParser(version = __version__)
-    set_cmd_options(parser)
-    (options, args) = parser.parse_args()
-    result = 1                  # in case of failure
-    try:
-        if options.verbose:
-            logger.set_severity("DEBUG", 99)
-        run(options.addr, options.port, options.idle_timeout, options.verbose)
-        result = 0
-    except isc.cc.SessionError as err:
-        logger.fatal(CMDCTL_CC_SESSION_ERROR, err)
-    except isc.cc.SessionTimeout:
-        logger.fatal(CMDCTL_CC_SESSION_TIMEOUT)
-    except KeyboardInterrupt:
-        logger.info(CMDCTL_STOPPED_BY_KEYBOARD)
-    except CmdctlException as err:
-        logger.fatal(CMDCTL_UNCAUGHT_EXCEPTION, err);
-
-    if httpd:
-        httpd.shutdown()
-
-    logger.info(CMDCTL_EXITING)
-
-    sys.exit(result)
-
-if __name__ == '__main__':
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/cmdctl/cmdctl.spec.pre.in b/src/bin/cmdctl/cmdctl.spec.pre.in
deleted file mode 100644 (file)
index 87aeb11..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "Cmdctl",
-    "module_description": "Interface for command and control",
-    "config_data": [
-      {
-        "item_name": "key_file",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "@@SYSCONFDIR@@/@PACKAGE@/cmdctl-keyfile.pem"
-      },
-      {
-        "item_name": "cert_file",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "@@SYSCONFDIR@@/@PACKAGE@/cmdctl-certfile.pem"
-      },
-      {
-        "item_name": "accounts_file",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "@@SYSCONFDIR@@/@PACKAGE@/cmdctl-accounts.csv"
-      }
-    ],
-    "commands": [
-      {
-        "command_name": "shutdown",
-        "command_description": "shutdown cmdctl",
-        "command_args": [
-          {
-            "item_name": "pid",
-            "item_type": "integer",
-            "item_optional": true
-          }
-        ]
-      }
-    ]
-  }
-}
-
-
diff --git a/src/bin/cmdctl/cmdctl_messages.mes b/src/bin/cmdctl/cmdctl_messages.mes
deleted file mode 100644 (file)
index 32afce3..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-# No namespace declaration - these constants go in the global namespace
-# of the cmdctl_messages python module.
-
-% CMDCTL_BAD_CONFIG_DATA error in config data: %1
-There was an error reading the updated configuration data. The specific
-error is printed.
-
-% CMDCTL_BAD_PASSWORD bad password for user: %1
-A login attempt was made to b10-cmdctl, but the password was wrong.
-Users can be managed with the tool b10-cmdctl-usermgr.
-
-% CMDCTL_CC_SESSION_ERROR error reading from cc channel: %1
-There was a problem reading from the command and control channel. The
-most likely cause is that the message bus daemon is not running.
-
-% CMDCTL_CC_SESSION_TIMEOUT timeout on cc channel
-A timeout occurred when waiting for essential data from the cc session.
-This usually occurs when b10-cfgmgr is not running or not responding.
-Since we are waiting for essential information, this is a fatal error,
-and the cmdctl daemon will now shut down.
-
-% CMDCTL_COMMAND_ERROR error in command %1 to module %2: %3
-An error was encountered sending the given command to the given module.
-Either there was a communication problem with the module, or the module
-was not able to process the command, and sent back an error. The
-specific error is printed in the message.
-
-% CMDCTL_COMMAND_SENT command '%1' to module '%2' was sent
-This debug message indicates that the given command has been sent to
-the given module.
-
-% CMDCTL_EXITING exiting
-The b10-cmdctl daemon is exiting.
-
-% CMDCTL_NO_SUCH_USER username not found in user database: %1
-A login attempt was made to b10-cmdctl, but the username was not known.
-Users can be added with the tool b10-cmdctl-usermgr.
-
-% CMDCTL_NO_USER_ENTRIES_READ failed to read user information, all users will be denied
-The b10-cmdctl daemon was unable to find any user data in the user
-database file. Either it was unable to read the file (in which case
-this message follows a message CMDCTL_USER_DATABASE_READ_ERROR
-containing a specific error), or the file was empty. Users can be added
-with the tool b10-cmdctl-usermgr.
-
-% CMDCTL_SEND_COMMAND sending command %1 to module %2
-This debug message indicates that the given command is being sent to
-the given module.
-
-% CMDCTL_SSL_SETUP_FAILURE_READING_CERT failed to read certificate or key: %1
-The b10-cmdctl daemon is unable to read either the certificate file or
-the private key file, and is therefore unable to accept any SSL connections.
-The specific error is printed in the message.
-The administrator should solve the issue with the files, or recreate them
-with the b10-certgen tool.
-
-% CMDCTL_SSL_SETUP_FAILURE_USER_DENIED failed to create an SSL connection (user denied): %1
-The user was denied because the SSL connection could not successfully
-be set up. The specific error is given in the log message. Possible
-causes may be that the ssl request itself was bad, or the local key or
-certificate file could not be read.
-
-% CMDCTL_STARTED cmdctl is listening for connections on %1:%2
-The cmdctl daemon has started and is now listening for connections.
-
-% CMDCTL_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
-There was a keyboard interrupt signal to stop the cmdctl daemon. The
-daemon will now shut down.
-
-% CMDCTL_UNCAUGHT_EXCEPTION uncaught exception: %1
-The b10-cmdctl daemon encountered an uncaught exception and
-will now shut down. This is indicative of a programming error and
-should not happen under normal circumstances. The exception message
-is printed.
-
-% CMDCTL_USER_DATABASE_READ_ERROR failed to read user database file %1: %2
-The b10-cmdctl daemon was unable to read the user database file. The
-file may be unreadable for the daemon, or it may be corrupted. In the
-latter case, it can be recreated with b10-cmdctl-usermgr. The specific
-error is printed in the log message.
diff --git a/src/bin/cmdctl/run_b10-cmdctl.sh.in b/src/bin/cmdctl/run_b10-cmdctl.sh.in
deleted file mode 100644 (file)
index 454e02c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-CMD_CTRLD_PATH=@abs_top_builddir@/src/bin/cmdctl
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python/isc/cc:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
-export PYTHONPATH
-
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
-SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
-if test $SET_ENV_LIBRARY_PATH = yes; then
-        @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.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/util/threads/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
-        export @ENV_LIBRARY_PATH@
-fi
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-cd ${CMD_CTRLD_PATH}
-exec ${PYTHON_EXEC} b10-cmdctl "$@"
diff --git a/src/bin/cmdctl/tests/.gitignore b/src/bin/cmdctl/tests/.gitignore
deleted file mode 100644 (file)
index ab9dfef..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/cmdctl_test
diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am
deleted file mode 100644 (file)
index 93bf040..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
-PYTESTS = cmdctl_test.py b10-certgen_test.py
-EXTRA_DIST = $(PYTESTS)
-EXTRA_DIST += testdata/expired-certfile.pem
-EXTRA_DIST += testdata/mangled-certfile.pem
-EXTRA_DIST += testdata/noca-certfile.pem
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-endif
-
-CLEANFILES = test-keyfile.pem test-certfile.pem
-
-# 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 ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cmdctl \
-       CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
-       CMDCTL_BUILD_PATH=$(abs_top_builddir)/src/bin/cmdctl \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
diff --git a/src/bin/cmdctl/tests/b10-certgen_test.py b/src/bin/cmdctl/tests/b10-certgen_test.py
deleted file mode 100644 (file)
index 8054aa1..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-# 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.
-
-# Note: the main code is in C++, but what we are mostly testing is
-# options and behaviour (output/file creation, etc), which is easier
-# to test in python.
-
-import unittest
-import os
-from subprocess import call
-import subprocess
-import ssl
-import stat
-
-def run(command):
-    """
-    Small helper function that returns a tuple of (rcode, stdout, stderr) after
-    running the given command (an array of command and arguments, as passed on
-    to subprocess).
-    """
-    subp = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    (stdout, stderr) = subp.communicate()
-    return (subp.returncode, stdout, stderr)
-
-class FileDeleterContext:
-    """
-    Simple Context Manager that deletes a given set of files when the context
-    is left.
-    """
-    def __init__(self, files):
-        self.files = files
-
-    def __enter__(self):
-        pass
-
-    def __exit__(self, type, value, traceback):
-        for f in self.files:
-            if os.path.exists(f):
-                os.unlink(f)
-
-class FilePermissionContext:
-    """
-    Simple Context Manager that temporarily modifies file permissions for
-    a given file
-    """
-    def __init__(self, f, unset_flags = [], set_flags = []):
-        """
-        Initialize file permission context.
-        See the stat module for possible flags to set or unset.
-        The flags are changed when the context is entered (i.e.
-        you can create the context first without any change)
-        The flags are changed back when the context is left.
-
-        Parameters:
-        f: string, file to change permissions for
-        unset_flags: list of flags to unset
-        set_flags: list of flags to set
-        """
-        self.file = f
-        self.orig_mode = os.stat(f).st_mode
-        new_mode = self.orig_mode
-        for flag in unset_flags:
-            new_mode = new_mode & ~flag
-        for flag in set_flags:
-            new_mode = new_mode | flag
-        self.new_mode = new_mode
-
-    def __enter__(self):
-        os.chmod(self.file, self.new_mode)
-
-    def __exit__(self, type, value, traceback):
-        os.chmod(self.file, self.orig_mode)
-
-def read_file_data(filename):
-    """
-    Simple text file reader that returns its contents as an array
-    """
-    with open(filename) as f:
-        return f.readlines()
-
-class TestCertGenTool(unittest.TestCase):
-    TOOL = '../b10-certgen'
-
-    def run_check(self, expected_returncode, expected_stdout, expected_stderr, command):
-        """
-        Runs the given command, and checks return code, and outputs (if provided).
-        Arguments:
-        expected_returncode, return code of the command
-        expected_stdout, (multiline) string that is checked agains stdout.
-                         May be None, in which case the check is skipped.
-        expected_stderr, (multiline) string that is checked agains stderr.
-                         May be None, in which case the check is skipped.
-        """
-        (returncode, stdout, stderr) = run(command)
-        self.assertEqual(expected_returncode, returncode, " ".join(command))
-        if expected_stdout is not None:
-            self.assertEqual(expected_stdout, stdout.decode())
-        if expected_stderr is not None:
-            self.assertEqual(expected_stderr, stderr.decode())
-
-    def validate_certificate(self, expected_result, certfile):
-        """
-        Validate a certificate, using the quiet option of the tool; it runs
-        the check option (-c) for the given base name of the certificate (-f
-        <certfile>), and compares the return code to the given
-        expected_result value
-        """
-        self.run_check(expected_result, '', '',
-                       [self.TOOL, '-q', '-c', certfile])
-        # Same with long options
-        self.run_check(expected_result, '', '',
-                       [self.TOOL, '--quiet', '--certfile', certfile])
-
-
-    def test_basic_creation(self):
-        """
-        Tests whether basic creation with no arguments (except output
-        file name) successfully creates a key and certificate
-        """
-        keyfile = 'test-keyfile.pem'
-        certfile = 'test-certfile.pem'
-        command = [ self.TOOL, '-q', '-w', '-c', certfile, '-k', keyfile ]
-        self.creation_helper(command, certfile, keyfile)
-        # Do same with long options
-        command = [ self.TOOL, '--quiet', '--write', '--certfile=' + certfile, '--keyfile=' + keyfile ]
-        self.creation_helper(command, certfile, keyfile)
-
-    def creation_helper(self, command, certfile, keyfile):
-        """
-        Helper method for test_basic_creation.
-        Performs the actual checks
-        """
-        with FileDeleterContext([keyfile, certfile]):
-            self.assertFalse(os.path.exists(keyfile))
-            self.assertFalse(os.path.exists(certfile))
-            self.run_check(0, '', '', command)
-            self.assertTrue(os.path.exists(keyfile))
-            self.assertTrue(os.path.exists(certfile))
-
-            # Validate the certificate that was just created
-            self.validate_certificate(0, certfile)
-
-            # When run with the same options, it should *not* create it again,
-            # as the current certificate should still be valid
-            certdata = read_file_data(certfile)
-            keydata = read_file_data(keyfile)
-
-            self.run_check(0, '', '', command)
-
-            self.assertEqual(certdata, read_file_data(certfile))
-            self.assertEqual(keydata, read_file_data(keyfile))
-
-            # but if we add -f, it should force a new creation
-            command.append('-f')
-            self.run_check(0, '', '', command)
-            self.assertNotEqual(certdata, read_file_data(certfile))
-            self.assertNotEqual(keydata, read_file_data(keyfile))
-
-    def test_check_bad_certificates(self):
-        """
-        Tests a few pre-created certificates with the -c option
-        """
-        path = os.environ['CMDCTL_SRC_PATH'] + '/tests/testdata/'
-        self.validate_certificate(10, path + 'expired-certfile.pem')
-        self.validate_certificate(100, path + 'mangled-certfile.pem')
-        self.validate_certificate(17, path + 'noca-certfile.pem')
-
-    def test_bad_options(self):
-        """
-        Tests some combinations of commands that should fail.
-        """
-        # specify -c but not -k
-        self.run_check(101,
-                       'Error: keyfile and certfile must both be specified '
-                       'if one of them is when calling b10-certgen in write '
-                       'mode.\n',
-                       '', [self.TOOL, '-w', '-c', 'foo'])
-        self.run_check(101,
-                       'Error: keyfile and certfile must both be specified '
-                       'if one of them is when calling b10-certgen in write '
-                       'mode.\n',
-                       '', [self.TOOL, '-w', '-k', 'foo'])
-        self.run_check(101,
-                       'Error: keyfile is not used when not in write mode\n',
-                       '', [self.TOOL, '-k', 'foo'])
-        # Extraneous argument
-        self.run_check(101, None, None, [self.TOOL, 'foo'])
-        # No such file
-        self.run_check(105, None, None, [self.TOOL, '-c', 'foo'])
-
-    @unittest.skipIf(os.getuid() == 0,
-                     'test cannot be run as root user')
-    def test_permissions(self):
-        """
-        Test some combinations of correct and bad permissions.
-        """
-        keyfile = 'mod-keyfile.pem'
-        certfile = 'mod-certfile.pem'
-        command = [ self.TOOL, '-q', '-w', '-c', certfile, '-k', keyfile ]
-        # Delete them at the end
-        with FileDeleterContext([keyfile, certfile]):
-            # Create the two files first
-            self.run_check(0, '', '', command)
-            self.validate_certificate(0, certfile)
-
-            # Make the key file unwritable
-            with FilePermissionContext(keyfile, unset_flags = [stat.S_IWUSR]):
-                self.run_check(106, '', '', command)
-                # Should have no effect on validation
-                self.validate_certificate(0, certfile)
-
-            # Make the cert file unwritable
-            with FilePermissionContext(certfile, unset_flags = [stat.S_IWUSR]):
-                self.run_check(106, '', '', command)
-                # Should have no effect on validation
-                self.validate_certificate(0, certfile)
-
-            # Make the key file unreadable (this should not matter)
-            with FilePermissionContext(keyfile, unset_flags = [stat.S_IRUSR]):
-                self.run_check(0, '', '', command)
-
-                # unreadable key file should also not have any effect on
-                # validation
-                self.validate_certificate(0, certfile)
-
-            # Make the cert file unreadable (this should matter)
-            with FilePermissionContext(certfile, unset_flags = [stat.S_IRUSR]):
-                self.run_check(106, '', '', command)
-
-                # Unreadable cert file should also fail validation
-                self.validate_certificate(106, certfile)
-
-        # Not directly a permission problem, but trying to check or create
-        # in a nonexistent directory returns different error codes
-        self.validate_certificate(105, 'fakedir/cert')
-        self.run_check(103, '', '', [ self.TOOL, '-q', '-w', '-c',
-                                      'fakedir/cert', '-k', 'fakedir/key' ])
-
-if __name__== '__main__':
-    unittest.main()
-
diff --git a/src/bin/cmdctl/tests/cmdctl_test.in b/src/bin/cmdctl/tests/cmdctl_test.in
deleted file mode 100644 (file)
index 7cb5d7c..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-CMDCTL_TEST_PATH=@abs_top_srcdir@/src/bin/cmdctl/tests
-PYTHONPATH=@abs_top_srcdir@/src/bin/cmdctl
-export PYTHONPATH
-
-cd ${CMDCTL_TEST_PATH}
-exec ${PYTHON_EXEC} -O cmdctl_test.py $*
-
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
deleted file mode 100644 (file)
index 7af8b0d..0000000
+++ /dev/null
@@ -1,790 +0,0 @@
-# Copyright (C) 2009  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.
-
-
-import unittest
-import ssl, socket
-import tempfile
-import time
-import stat
-import sys
-from cmdctl import *
-import isc.log
-
-assert 'CMDCTL_SRC_PATH' in os.environ,\
-       "Please run this test with 'make check'"
-SRC_FILE_PATH = os.environ['CMDCTL_SRC_PATH'] + os.sep
-
-assert 'CMDCTL_BUILD_PATH' in os.environ,\
-       "Please run this test with 'make check'"
-BUILD_FILE_PATH = os.environ['CMDCTL_BUILD_PATH'] + os.sep
-
-# Rewrite the class for unittest.
-class MySecureHTTPRequestHandler(SecureHTTPRequestHandler):
-    def __init__(self):
-        self.session_id = None
-
-    def send_response(self, rcode):
-        self.rcode = rcode
-
-    def end_headers(self):
-        pass
-
-class FakeSecureHTTPServer(SecureHTTPServer):
-    def __init__(self):
-        self.user_sessions = {}
-        self.cmdctl = FakeCommandControlForTestRequestHandler()
-        self._verbose = True
-        self._user_infos = {}
-        self.idle_timeout = 1200
-        self._lock = threading.Lock()
-
-class FakeCommandControlForTestRequestHandler(CommandControl):
-    def __init__(self):
-        self._config_data = {}
-        self.modules_spec = {}
-        self._lock = threading.Lock()
-
-    def send_command(self, mod, cmd, param):
-        return 0, {}
-
-# context to temporarily make a file unreadable
-class UnreadableFile:
-    def __init__(self, file_name):
-        self.file_name = file_name
-        self.orig_mode = os.stat(file_name).st_mode
-
-    def __enter__(self):
-        os.chmod(self.file_name, self.orig_mode & ~stat.S_IRUSR)
-
-    def __exit__(self, type, value, traceback):
-        os.chmod(self.file_name, self.orig_mode)
-
-class TmpTextFile:
-    """
-    Context class for temporarily creating a text file with some
-    lines of content.
-
-    The file is automatically deleted if the context is left, so
-    make sure to not use the path of an existing file!
-    """
-    def __init__(self, path, contents):
-        self.__path = path
-        self.__contents = contents
-
-    def __enter__(self):
-        with open(self.__path, 'w') as f:
-            f.write("\n".join(self.__contents) + "\n")
-
-    def __exit__(self, type, value, traceback):
-        os.unlink(self.__path)
-
-
-class TestSecureHTTPRequestHandler(unittest.TestCase):
-    def setUp(self):
-        self.old_stdout = sys.stdout
-        sys.stdout = open(os.devnull, 'w')
-        self.handler = MySecureHTTPRequestHandler()
-        self.handler.server = FakeSecureHTTPServer()
-        self.handler.server.user_sessions = {}
-        self.handler.server._user_infos = {}
-        self.handler.headers = {}
-        self.handler.rfile = open('input.tmp', 'w+b')
-        self.handler.wfile = open('output.tmp', 'w+b')
-
-    def tearDown(self):
-        sys.stdout.close()
-        sys.stdout = self.old_stdout
-        self.handler.wfile.close()
-        os.remove('output.tmp')
-        self.handler.rfile.close()
-        os.remove('input.tmp')
-
-    def test_is_session_valid(self):
-        self.assertIsNone(self.handler.session_id)
-        self.assertFalse(self.handler._is_session_valid())
-        self.handler.session_id = 4234
-        self.assertTrue(self.handler._is_session_valid())
-
-    def test_parse_request_path(self):
-        self.handler.path = ''
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == None) and (cmd == None))
-
-        self.handler.path = '/abc'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == 'abc') and (cmd == None))
-
-        self.handler.path = '/abc/edf'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == 'abc') and (cmd == 'edf'))
-
-        self.handler.path = '/abc/edf/ghi'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == 'abc') and (cmd == 'edf'))
-
-    def test_parse_request_path_1(self):
-        self.handler.path = '/ab*c'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == 'ab') and cmd == None)
-
-        self.handler.path = '/abc/ed*fdd/ddd'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == 'abc') and cmd == 'ed')
-
-        self.handler.path = '/-*/edfdd/ddd'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == None) and (cmd == None))
-
-        self.handler.path = '/-*/edfdd/ddd'
-        mod, cmd = self.handler._parse_request_path()
-        self.assertTrue((mod == None) and (cmd == None))
-
-    def test_do_GET(self):
-        self.handler.do_GET()
-        self.assertEqual(self.handler.rcode, http.client.BAD_REQUEST)
-
-    def test_do_GET_1(self):
-        self.handler.headers['cookie'] = 12345
-        self.handler.do_GET()
-        self.assertEqual(self.handler.rcode, http.client.UNAUTHORIZED)
-
-    def test_do_GET_2(self):
-        self.handler.headers['cookie'] = 12345
-        self.handler.server.user_sessions[12345] = time.time() + 1000000
-        self.handler.path = '/how/are'
-        self.handler.do_GET()
-        self.assertEqual(self.handler.rcode, http.client.NO_CONTENT)
-
-    def test_do_GET_3(self):
-        self.handler.headers['cookie'] = 12346
-        self.handler.server.user_sessions[12346] = time.time() + 1000000
-        path_vec = ['config_data', 'module_spec']
-        for path in path_vec:
-            self.handler.path = '/' + path
-            self.handler.do_GET()
-            self.assertEqual(self.handler.rcode, http.client.OK)
-
-    def test_is_user_logged_in(self):
-        self.handler.server.user_sessions = {}
-        self.handler.session_id = 12345
-        self.assertTrue(self.handler._is_user_logged_in() == False)
-
-        self.handler.server.user_sessions[12345] = time.time()
-        self.assertTrue(self.handler._is_user_logged_in())
-
-        self.handler.server.user_sessions[12345] = time.time() - 1500
-        self.handler.idle_timeout = 1200
-        self.assertTrue(self.handler._is_user_logged_in() == False)
-
-    def test_check_user_name_and_pwd(self):
-        self.handler.headers = {}
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['invalid username or password'])
-
-    def test_check_user_name_and_pwd_1(self):
-        user_info = {'username':'root', 'password':'abc123'}
-        len = self.handler.rfile.write(json.dumps(user_info).encode())
-        self.handler.headers['Content-Length'] = len
-        self.handler.rfile.seek(0, 0)
-
-        self.handler.server._user_infos['root'] = ['aa', 'aaa']
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['username or password error'])
-
-    def test_check_user_name_and_pwd_2(self):
-        user_info = {'username':'root', 'password':'abc123'}
-        len = self.handler.rfile.write(json.dumps(user_info).encode())
-        self.handler.headers['Content-Length'] = len - 1
-        self.handler.rfile.seek(0, 0)
-
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['invalid username or password'])
-
-    def test_check_user_name_and_pwd_3(self):
-        user_info = {'usernae':'root', 'password':'abc123'}
-        len = self.handler.rfile.write(json.dumps(user_info).encode())
-        self.handler.headers['Content-Length'] = len
-        self.handler.rfile.seek(0, 0)
-
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['need user name'])
-
-    def test_check_user_name_and_pwd_4(self):
-        user_info = {'username':'root', 'pssword':'abc123'}
-        len = self.handler.rfile.write(json.dumps(user_info).encode())
-        self.handler.headers['Content-Length'] = len
-        self.handler.rfile.seek(0, 0)
-
-        self.handler.server._user_infos['root'] = ['aa', 'aaa']
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['need password'])
-
-    def test_check_user_name_and_pwd_5(self):
-        user_info = {'username':'root', 'password':'abc123'}
-        len = self.handler.rfile.write(json.dumps(user_info).encode())
-        self.handler.headers['Content-Length'] = len
-        self.handler.rfile.seek(0, 0)
-
-        ret, msg = self.handler._check_user_name_and_pwd()
-        self.assertFalse(ret)
-        self.assertEqual(msg, ['username or password error'])
-
-    def test_do_POST(self):
-        self.handler.headers = {}
-        self.handler.do_POST()
-        self.assertEqual(self.handler.rcode, http.client.BAD_REQUEST)
-
-    def test_do_POST_1(self):
-        self.handler.headers = {}
-        self.handler.headers['cookie'] = 12345
-        self.handler.path = '/'
-        self.handler.do_POST()
-        self.assertEqual(self.handler.rcode, http.client.UNAUTHORIZED)
-
-    def test_handle_post_request(self):
-        self.handler.path = '/cfgmgr/revert'
-        self.handler.headers = {}
-        rcode, reply = self.handler._handle_post_request()
-        self.assertEqual(http.client.BAD_REQUEST, rcode)
-
-    def test_handle_post_request_1(self):
-        self.handler.path = '/*d/revert'
-        self.handler.headers = {}
-        rcode, reply = self.handler._handle_post_request()
-        self.assertEqual(http.client.BAD_REQUEST, rcode)
-
-    def _gen_module_spec(self):
-        spec = { 'commands': [
-                  { 'command_name' :'command',
-                    'command_args': [ {
-                            'item_name' : 'param1',
-                            'item_type' : 'integer',
-                            'item_optional' : False,
-                            'item_default' : 0
-                           } ],
-                    'command_description' : 'cmd description'
-                  }
-                ]
-               }
-
-        return spec
-
-    def test_handle_post_request_2(self):
-        params = {'param1':123}
-        len = self.handler.rfile.write(json.dumps(params).encode())
-        self.handler.headers['Content-Length'] = len
-
-        self.handler.rfile.seek(0, 0)
-        self.handler.path = '/module/command'
-        self.handler.server.cmdctl.modules_spec = {}
-        self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
-        rcode, reply = self.handler._handle_post_request()
-        self.assertEqual(http.client.OK, rcode)
-
-    def test_handle_post_request_3(self):
-        params = {'param1':'abc'}
-        len = self.handler.rfile.write(json.dumps(params).encode())
-        self.handler.headers['Content-Length'] = len
-
-        self.handler.rfile.seek(0, 0)
-        self.handler.path = '/module/command'
-        self.handler.server.cmdctl.modules_spec = {}
-        self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
-        rcode, reply = self.handler._handle_post_request()
-        self.assertEqual(http.client.BAD_REQUEST, rcode)
-
-    def test_handle_login(self):
-        orig_is_user_logged_in = self.handler._is_user_logged_in
-        orig_check_user_name_and_pwd = self.handler._check_user_name_and_pwd
-        try:
-            def create_is_user_logged_in(status):
-                '''Create a replacement _is_user_logged_in() method.'''
-                def my_is_user_logged_in():
-                    return status
-                return my_is_user_logged_in
-
-            # Check case where _is_user_logged_in() returns True
-            self.handler._is_user_logged_in = create_is_user_logged_in(True)
-            self.handler.headers['cookie'] = 12345
-            self.handler.path = '/login'
-            self.handler.do_POST()
-            self.assertEqual(self.handler.rcode, http.client.OK)
-            self.handler.wfile.seek(0, 0)
-            d = self.handler.wfile.read()
-            self.assertEqual(json.loads(d.decode()),
-                             ['user has already login'])
-
-            # Clear the output
-            self.handler.wfile.seek(0, 0)
-            self.handler.wfile.truncate()
-
-            # Check case where _is_user_logged_in() returns False
-            self.handler._is_user_logged_in = create_is_user_logged_in(False)
-
-            def create_check_user_name_and_pwd(status, error_info=None):
-                '''Create a replacement _check_user_name_and_pwd() method.'''
-                def my_check_user_name_and_pwd():
-                    return status, error_info
-                return my_check_user_name_and_pwd
-
-            # (a) Check case where _check_user_name_and_pwd() returns
-            # valid user status
-            self.handler._check_user_name_and_pwd = \
-                create_check_user_name_and_pwd(True)
-            self.handler.do_POST()
-            self.assertEqual(self.handler.rcode, http.client.OK)
-            self.handler.wfile.seek(0, 0)
-            d = self.handler.wfile.read()
-            self.assertEqual(json.loads(d.decode()), ['login success'])
-
-            # Clear the output
-            self.handler.wfile.seek(0, 0)
-            self.handler.wfile.truncate()
-
-            # (b) Check case where _check_user_name_and_pwd() returns
-            # invalid user status
-            self.handler._check_user_name_and_pwd = \
-                create_check_user_name_and_pwd(False, ['login failed'])
-            self.handler.do_POST()
-            self.assertEqual(self.handler.rcode, http.client.UNAUTHORIZED)
-            self.handler.wfile.seek(0, 0)
-            d = self.handler.wfile.read()
-            self.assertEqual(json.loads(d.decode()), ['login failed'])
-
-        finally:
-            self.handler._is_user_logged_in = orig_is_user_logged_in
-            self.handler._check_user_name_and_pwd = orig_check_user_name_and_pwd
-
-class MockSession:
-    """Act like isc.cc.Session, stealing group_sendmsg/recvmsg().
-
-    The initial simple version only records given parameters in
-    group_sendmsg() for later inspection and raise a timeout exception
-    from recvmsg().  As we see the need for more test cases these methods
-    should be extended.
-
-    """
-    def __init__(self, sent_messages):
-        self.__sent_messages = sent_messages
-
-    def group_sendmsg(self, msg, module_name, want_answer):
-        self.__sent_messages.append((msg, module_name))
-
-    def group_recvmsg(self, nonblock, seq):
-        raise isc.cc.session.SessionTimeout('dummy timeout')
-
-class MyCommandControl(CommandControl):
-    def __init__(self, httpserver, verbose):
-        super().__init__(httpserver, verbose)
-        self.sent_messages = [] # for inspection; allow tests to see it
-        self._cc = MockSession(self.sent_messages)
-
-    def _get_modules_specification(self):
-        return {}
-
-    def _get_config_data_from_config_manager(self):
-        return {}
-
-    def _setup_session(self):
-        spec_file = BUILD_FILE_PATH + 'cmdctl.spec'
-        module_spec = isc.config.module_spec_from_file(spec_file)
-        config = isc.config.config_data.ConfigData(module_spec)
-        self._module_name = 'Cmdctl'
-        self._cmdctl_config_data = config.get_full_config()
-
-    def _handle_msg_from_msgq(self):
-        pass
-
-    def _start_msg_handle_thread(self): # just not bother to be threads
-        pass
-
-    def _get_current_thread(self):
-        return None
-
-class TestCommandControl(unittest.TestCase):
-
-    def setUp(self):
-        self.old_stdout = sys.stdout
-        sys.stdout = open(os.devnull, 'w')
-        self.cmdctl = MyCommandControl(None, True)
-
-    def tearDown(self):
-        sys.stdout.close()
-        sys.stdout = self.old_stdout
-
-    def _check_config(self, cmdctl):
-        key, cert, account = cmdctl.get_cmdctl_config_data()
-        self.assertIsNotNone(key)
-        self.assertIsNotNone(cert)
-        self.assertIsNotNone(account)
-
-    def test_get_cmdctl_config_data(self):
-        old_env = os.environ
-        if 'B10_FROM_SOURCE' in os.environ:
-            del os.environ['B10_FROM_SOURCE']
-        self.cmdctl.get_cmdctl_config_data()
-        self._check_config(self.cmdctl)
-        os.environ = old_env
-
-        old_env = os.environ
-        os.environ['B10_FROM_SOURCE'] = '../'
-        self._check_config(self.cmdctl)
-        os.environ = old_env
-
-    def test_parse_command_result(self):
-        self.assertEqual({}, self.cmdctl._parse_command_result(1, {'error' : 1}))
-        self.assertEqual({'a': 1}, self.cmdctl._parse_command_result(0, {'a' : 1}))
-
-    def _check_answer(self, answer, rcode_, msg_):
-        rcode, msg = ccsession.parse_answer(answer)
-        self.assertEqual(rcode, rcode_)
-        self.assertEqual(msg, msg_)
-
-    def test_command_handler(self):
-        answer = self.cmdctl.command_handler('unknown-command', None)
-        self._check_answer(answer, 1, 'unknown command: unknown-command')
-
-        # Send a real command. Mock stuff so the shutdown command doesn't
-        # cause an exception.
-        class ModuleCC:
-            def send_stopping():
-                pass
-        self.cmdctl._module_cc = ModuleCC
-        called = []
-        class Server:
-            def shutdown():
-                called.append('shutdown')
-        self.cmdctl._httpserver = Server
-        answer = self.cmdctl.command_handler('shutdown', None)
-        rcode, msg = ccsession.parse_answer(answer)
-        self.assertEqual(rcode, 0)
-        self.assertIsNone(msg)
-        self.assertEqual(['shutdown'], called)
-
-    def test_command_handler_spec_update(self):
-        # Should not be present
-        self.assertFalse("foo" in self.cmdctl.modules_spec)
-
-        answer = self.cmdctl.command_handler(
-            ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", {} ])
-        rcode, msg = ccsession.parse_answer(answer)
-        self.assertEqual(rcode, 0)
-        self.assertEqual(msg, None)
-
-        # Should now be present
-        self.assertTrue("foo" in self.cmdctl.modules_spec)
-
-        # When sending specification 'None', it should be removed
-        answer = self.cmdctl.command_handler(
-            ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
-        rcode, msg = ccsession.parse_answer(answer)
-        self.assertEqual(rcode, 0)
-        self.assertEqual(msg, None)
-
-        # Should no longer be present
-        self.assertFalse("foo" in self.cmdctl.modules_spec)
-
-        # Don't store 'None' if it wasn't there in the first place!
-        answer = self.cmdctl.command_handler(
-            ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
-        rcode, msg = ccsession.parse_answer(answer)
-        self.assertEqual(rcode, 1)
-        self.assertEqual(msg, "No such module: foo")
-
-        # Should still not present
-        self.assertFalse("foo" in self.cmdctl.modules_spec)
-
-
-    def test_check_config_handler(self):
-        answer = self.cmdctl.config_handler({'non-exist': 123})
-        self._check_answer(answer, 1, 'unknown config item: non-exist')
-
-        old_env = os.environ
-        os.environ['B10_FROM_SOURCE'] = '../'
-        self._check_config(self.cmdctl)
-        os.environ = old_env
-
-        answer = self.cmdctl.config_handler({'key_file': '/user/non-exist_folder'})
-        self._check_answer(answer, 1, "'/user/non-exist_folder' does not exist")
-
-        answer = self.cmdctl.config_handler({'cert_file': '/user/non-exist_folder'})
-        self._check_answer(answer, 1, "'/user/non-exist_folder' does not exist")
-
-        answer = self.cmdctl.config_handler({'accounts_file': '/user/non-exist_folder'})
-        self._check_answer(answer, 1,
-                "Invalid accounts file: [Errno 2] No such file or directory: '/user/non-exist_folder'")
-
-        # Test with invalid accounts file
-        file_name = 'tmp.account.file'
-        temp_file = open(file_name, 'w')
-        writer = csv.writer(temp_file)
-        writer.writerow(['a', 'b'])
-        temp_file.close()
-        answer = self.cmdctl.config_handler({'accounts_file': file_name})
-        self._check_answer(answer, 1, "Invalid accounts file: list index out of range")
-        os.remove(file_name)
-
-    def test_send_command(self):
-        # Send a command to other module.  We check an expected message
-        # is sent via the session (cmdct._cc).  Due to the behavior of
-        # our mock session object the anser will be "fail", but it's not
-        # the subject of this test, and so it's okay.
-        # TODO: more detailed cases should be tested.
-        rcode, value = self.cmdctl.send_command('Init', 'shutdown', None)
-        self.assertEqual(1, len(self.cmdctl.sent_messages))
-        self.assertEqual(({'command': ['shutdown']}, 'Init'),
-                         self.cmdctl.sent_messages[-1])
-        self.assertEqual(1, rcode)
-
-        # Send a command to cmdctl itself.  Should be the same effect.
-        rcode, value = self.cmdctl.send_command('Cmdctl', 'shutdown',
-                                                None)
-        self.assertEqual(2, len(self.cmdctl.sent_messages))
-        self.assertEqual(({'command': ['shutdown']}, 'Cmdctl'),
-                         self.cmdctl.sent_messages[-1])
-        self.assertEqual(1, rcode)
-
-class MySecureHTTPServer(SecureHTTPServer):
-    def server_bind(self):
-        pass
-
-class TestSecureHTTPServer(unittest.TestCase):
-    def setUp(self):
-        self.old_stdout = sys.stdout
-        self.old_stderr = sys.stderr
-        sys.stdout = open(os.devnull, 'w')
-        sys.stderr = sys.stdout
-        self.server = MySecureHTTPServer(('localhost', 8080),
-                                         MySecureHTTPRequestHandler,
-                                         MyCommandControl, verbose=True)
-
-    def tearDown(self):
-        # both sys.stdout and sys.stderr are the same, so closing one is
-        # sufficient
-        sys.stdout.close()
-        sys.stdout = self.old_stdout
-        sys.stderr = self.old_stderr
-
-    def test_addr_in_use(self):
-        server_one = None
-        try:
-            server_one = SecureHTTPServer(('localhost', 53531),
-                                        MySecureHTTPRequestHandler,
-                                        MyCommandControl)
-        except CmdctlException:
-            pass
-        else:
-            self.assertRaises(CmdctlException, SecureHTTPServer,
-                              ('localhost', 53531),
-                              MySecureHTTPRequestHandler, MyCommandControl)
-        if server_one:
-            server_one.server_close()
-
-    def test_create_user_info(self):
-        self.server._create_user_info('/local/not-exist')
-        self.assertEqual(0, len(self.server._user_infos))
-
-        self.server._create_user_info(SRC_FILE_PATH + 'cmdctl-accounts.csv')
-        self.assertEqual(1, len(self.server._user_infos))
-        self.assertTrue('root' in self.server._user_infos)
-
-    def test_get_user_info(self):
-        self.assertIsNone(self.server.get_user_info('root'))
-        self.server._create_user_info(SRC_FILE_PATH + 'cmdctl-accounts.csv')
-        self.assertIn('6f0c73bd33101a5ec0294b3ca39fec90ef4717fe',
-                      self.server.get_user_info('root'))
-
-        # When the file is not changed calling _create_user_info() again
-        # should have no effect. In order to test this, we overwrite the
-        # user-infos that were just set and make sure it isn't touched by
-        # the call (so make sure it isn't set to some empty value)
-        fake_users_val = { 'notinfile': [] }
-        self.server._user_infos = fake_users_val
-        self.server._create_user_info(SRC_FILE_PATH + 'cmdctl-accounts.csv')
-        self.assertEqual(fake_users_val, self.server._user_infos)
-
-    def test_create_user_info_changing_file_time(self):
-        self.assertEqual(0, len(self.server._user_infos))
-
-        # Create a file
-        accounts_file = BUILD_FILE_PATH + 'new_file.csv'
-        with TmpTextFile(accounts_file, ['root,foo,bar']):
-            self.server._create_user_info(accounts_file)
-            self.assertEqual(1, len(self.server._user_infos))
-            self.assertTrue('root' in self.server._user_infos)
-
-            # Make sure re-reading is a noop if file was not modified
-            fake_users_val = { 'notinfile': [] }
-            self.server._user_infos = fake_users_val
-            self.server._create_user_info(accounts_file)
-            self.assertEqual(fake_users_val, self.server._user_infos)
-
-        # create the file again, this time read should not be a noop
-        with TmpTextFile(accounts_file, ['otherroot,foo,bar']):
-            # Set mtime in future
-            stat = os.stat(accounts_file)
-            os.utime(accounts_file, (stat.st_atime, stat.st_mtime + 10))
-            self.server._create_user_info(accounts_file)
-            self.assertEqual(1, len(self.server._user_infos))
-            self.assertTrue('otherroot' in self.server._user_infos)
-
-    def test_create_user_info_changing_file_name(self):
-        """
-        Check that the accounts file is re-read if the file name is different
-        """
-        self.assertEqual(0, len(self.server._user_infos))
-
-        # Create two files
-        accounts_file1 = BUILD_FILE_PATH + 'new_file.csv'
-        accounts_file2 = BUILD_FILE_PATH + 'new_file2.csv'
-        with TmpTextFile(accounts_file2, ['otherroot,foo,bar']):
-            with TmpTextFile(accounts_file1, ['root,foo,bar']):
-                self.server._create_user_info(accounts_file1)
-                self.assertEqual(1, len(self.server._user_infos))
-                self.assertTrue('root' in self.server._user_infos)
-
-                # Make sure re-reading is a noop if file was not modified
-                fake_users_val = { 'notinfile': [] }
-                self.server._user_infos = fake_users_val
-                self.server._create_user_info(accounts_file1)
-                self.assertEqual(fake_users_val, self.server._user_infos)
-
-                # But a different file should be read
-                self.server._create_user_info(accounts_file2)
-                self.assertEqual(1, len(self.server._user_infos))
-                self.assertTrue('otherroot' in self.server._user_infos)
-
-    def test_create_user_info_nonexistent_file(self):
-        # Even if there was data initially, if set to a nonexistent
-        # file it should result in no users
-        accounts_file = BUILD_FILE_PATH + 'new_file.csv'
-        self.assertFalse(os.path.exists(accounts_file))
-        fake_users_val = { 'notinfile': [] }
-        self.server._user_infos = fake_users_val
-        self.server._create_user_info(accounts_file)
-        self.assertEqual({}, self.server._user_infos)
-
-        # Should it now be created it should be read
-        with TmpTextFile(accounts_file, ['root,foo,bar']):
-            self.server._create_user_info(accounts_file)
-            self.assertEqual(1, len(self.server._user_infos))
-            self.assertTrue('root' in self.server._user_infos)
-
-    def test_check_file(self):
-        # Just some file that we know exists
-        file_name = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
-        check_file(file_name)
-        self.assertRaises(CmdctlException, check_file, '/local/not-exist')
-        self.assertRaises(CmdctlException, check_file, '/')
-
-    @unittest.skipIf(os.getuid() == 0,
-                     'test cannot be run as root user')
-    def test_check_file_for_unreadable(self):
-        file_name = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
-        with UnreadableFile(file_name):
-            self.assertRaises(CmdctlException, check_file, file_name)
-
-    def test_check_key_and_cert(self):
-        keyfile = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
-        certfile = BUILD_FILE_PATH + 'cmdctl-certfile.pem'
-
-        # no exists
-        self.assertRaises(CmdctlException, self.server._check_key_and_cert,
-                          keyfile, '/local/not-exist')
-        self.assertRaises(CmdctlException, self.server._check_key_and_cert,
-                         '/local/not-exist', certfile)
-
-        # not a file
-        self.assertRaises(CmdctlException, self.server._check_key_and_cert,
-                          keyfile, '/')
-        self.assertRaises(CmdctlException, self.server._check_key_and_cert,
-                         '/', certfile)
-
-        # All OK (also happens to check the context code above works)
-        self.server._check_key_and_cert(keyfile, certfile)
-
-    @unittest.skipIf(os.getuid() == 0,
-                     'test cannot be run as root user')
-    def test_check_key_and_cert_for_unreadable(self):
-        keyfile = BUILD_FILE_PATH + 'cmdctl-keyfile.pem'
-        certfile = BUILD_FILE_PATH + 'cmdctl-certfile.pem'
-
-        # no read permission
-        with UnreadableFile(certfile):
-            self.assertRaises(CmdctlException,
-                              self.server._check_key_and_cert,
-                              keyfile, certfile)
-
-        with UnreadableFile(keyfile):
-            self.assertRaises(CmdctlException,
-                              self.server._check_key_and_cert,
-                              keyfile, certfile)
-
-    def test_wrap_sock_in_ssl_context(self):
-        sock = socket.socket()
-
-        # Bad files should result in a CmdctlException in the basic file
-        # checks
-        self.assertRaises(CmdctlException,
-                          self.server._wrap_socket_in_ssl_context,
-                          sock,
-                          'no_such_file', 'no_such_file')
-
-        # Using a non-certificate file would cause an SSLError
-        self.assertRaises(socket.error,
-                          self.server._wrap_socket_in_ssl_context,
-                          sock,
-                          BUILD_FILE_PATH + 'cmdctl.py',
-                          BUILD_FILE_PATH + 'cmdctl-certfile.pem')
-
-        # Should succeed
-        sock1 = socket.socket()
-        ssl_sock = self.server._wrap_socket_in_ssl_context(sock1,
-                                   BUILD_FILE_PATH + 'cmdctl-keyfile.pem',
-                                   BUILD_FILE_PATH + 'cmdctl-certfile.pem')
-        self.assertTrue(isinstance(ssl_sock, ssl.SSLSocket))
-
-        # wrap_socket can also raise IOError, which should be caught and
-        # handled like the other errors.
-        # Force this by temporarily disabling our own file checks
-        orig_check_func = self.server._check_key_and_cert
-        try:
-            self.server._check_key_and_cert = lambda x,y: None
-            self.assertRaises(IOError,
-                              self.server._wrap_socket_in_ssl_context,
-                              sock,
-                              'no_such_file', 'no_such_file')
-        finally:
-            self.server._check_key_and_cert = orig_check_func
-
-class TestFuncNotInClass(unittest.TestCase):
-    def test_check_port(self):
-        self.assertRaises(OptionValueError, check_port, None, 'port', -1, None)
-        self.assertRaises(OptionValueError, check_port, None, 'port', 65536, None)
-        self.assertRaises(OptionValueError, check_addr, None, 'ipstr', 'a.b.d', None)
-        self.assertRaises(OptionValueError, check_addr, None, 'ipstr', '1::0:a.b', None)
-
-
-if __name__== "__main__":
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
-
-
diff --git a/src/bin/cmdctl/tests/testdata/expired-certfile.pem b/src/bin/cmdctl/tests/testdata/expired-certfile.pem
deleted file mode 100644 (file)
index 384a222..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDhzCCAvCgAwIBAgIJALwngNFik7ONMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD
-VQQGEwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwG
-A1UEChMFY25uaWMxDjAMBgNVBAsTBWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3Vu
-MSIwIAYJKoZIhvcNAQkBFhN6aGFuZ2xpa3VuQGNubmljLmNuMB4XDTEwMDEwNzEy
-NDcxOFoXDTExMDEwNzEyNDcxOFowgYoxCzAJBgNVBAYTAmNuMRAwDgYDVQQIEwdi
-ZWlqaW5nMRAwDgYDVQQHEwdiZWlqaW5nMQ4wDAYDVQQKEwVjbm5pYzEOMAwGA1UE
-CxMFY25uaWMxEzARBgNVBAMTCnpoYW5nbGlrdW4xIjAgBgkqhkiG9w0BCQEWE3po
-YW5nbGlrdW5AY25uaWMuY24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOkg
-JbEkYoy9SEsU9t/mfxlaiCqNhxCqqgeodVEdiPKJ7LoVl21mRjazWBiHQbQ1e2Ka
-UiCJz68RwV7u92bIqe1bsNgNqoCPQqsQPtEoCPzfbiM1tIke0s/h6+8l6ne+yg21
-O825x5Anjq+6THLGCDcO4L2RWo+4PwJnVGrgBPKLAgMBAAGjgfIwge8wHQYDVR0O
-BBYEFJKM/O0ViGlwtb3JEci/DLTO/7DaMIG/BgNVHSMEgbcwgbSAFJKM/O0ViGlw
-tb3JEci/DLTO/7DaoYGQpIGNMIGKMQswCQYDVQQGEwJjbjEQMA4GA1UECBMHYmVp
-amluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwGA1UEChMFY25uaWMxDjAMBgNVBAsT
-BWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3VuMSIwIAYJKoZIhvcNAQkBFhN6aGFu
-Z2xpa3VuQGNubmljLmNuggkAvCeA0WKTs40wDAYDVR0TBAUwAwEB/zANBgkqhkiG
-9w0BAQUFAAOBgQBh5N6isMAQAFFD+pbfpppjQlO4vUNcEdzPdeuBFaf9CsX5ZdxV
-jmn1ZuGm6kRzqUPwPSxvCIAY0wuSu1g7YREPAZ3XBVwcg6262iGOA6n7E+nv5PLz
-EuZ1oUg+IfykUIoflKH6xZB4MyPL+EgkMT+i9BrngaXHXF8tEO30YppMiA==
------END CERTIFICATE-----
diff --git a/src/bin/cmdctl/tests/testdata/mangled-certfile.pem b/src/bin/cmdctl/tests/testdata/mangled-certfile.pem
deleted file mode 100644 (file)
index 7c47fda..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDhzCCAvCgAwIBAgIJALwngNFik7ONMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD
-VQQGEwJjbjEQMA4GA1UECBMHYmVpamluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwG
-A1UEChMFY25uaWMxDjAMBgNVBAsTBWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3Vu
-MSIwIAYJKoZIhvcNAQkBFhN6aGFuZ2xpa3VuQGNubmljLmNuMB4XDTEwMDEwNzEy
-NDcxOFoXDTExMDEwNzEyNDcxOFowgYoxCzAJBgNVBAYTAmNuMRAwDgYDVQQIEwdi
-ZWlqaW5nMraWDgYDVQQHEwdiZWlqaW5nMQ4wDAYDVQQKEwVjbm5pYzEOMAwGA1UE
-CxMFY25uaWMxeZaRBgNVBAMTCnpoYW5nbGlrdW4xIjAgBgkqhkiG9w0BCQEWE3po
-YW5nbGlrdW5AY25UAwMuY24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOkg
-JbEkYoy9SEsU9t/mfxLAICqNhxCqqgeodVEdiPKJ7LoVl21mRjazWBiHQbQ1e2Ka
-UiCJz68RwV7u92bIqe1bsNgNqoCPQqsQPtEoCPzfbiM1tIke0s/h6+8l6ne+yg21
-O825x5Anjq+6THLGCDcO4L2RWo+4PwJnVGrgBPKLAgMBAAGjgfIwge8wHQYDVR0O
-BBYEFJKM/O0ViGlwtb3JEci/DLTO/7DaMIG/BgNVHSMEgbcwgbSAFJKM/O0ViGlw
-tb3JEci/DLTO/7DaoYGQpIGNMIGKMQswCQYDVQQGEwJjbjEQMA4GA1UECBMHYmVp
-amluZzEQMA4GA1UEBxMHYmVpamluZzEOMAwGA1UEChMFY25uaWMxDjAMBgNVBAsT
-BWNubmljMRMwEQYDVQQDEwp6aGFuZ2xpa3VuMSIwIAYJKoZIhvcNAQkBFhN6aGFu
-Z2xpa3VuQGNubmljLmNuggkAvCeA0WKTs40wDAYDVR0TBAUwAwEB/zANBgkqhkiG
-9w0BAQUFAAOBgQBh5N6isMAQAFFD+pbfpppjQlO4vUNcEdzPdeuBFaf9CsX5ZdxV
-jmn1ZuGm6kRzqUPwPSxvCIAY0wuSu1g7YREPAZ3XBVwcg6262iGOA6n7E+nv5PLz
-EuZ1oUg+IfykUIoflKH6xZB4MyPL+EgkMT+i9BrngaXHXF8tEO30YppMiA==
------END CERTIFICATE-----
diff --git a/src/bin/cmdctl/tests/testdata/noca-certfile.pem b/src/bin/cmdctl/tests/testdata/noca-certfile.pem
deleted file mode 100644 (file)
index 5636869..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIRALUIj3nnW5uDE/+fglPvUDwwDQYJKoZIhvcNAQELBQAw
-HjELMAkGA1UEBhMCVVMxDzANBgNVBAMTBkJJTkQxMDAeFw0xMjExMTQxMjQ5MjVa
-Fw0xMzExMTQxMjQ5MjVaMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQDEwZCSU5EMTAw
-ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkIOPfs3Aw9kNDu1JqA2w3
-84/n9oUgAwAlHVmuJv7ZDw1MDaIKHjsh3DW09z+nv67GVksI7pFtAw5O4mnTDxpa
-JT0NKzhvYGfe8VdV/hWDogTIdk1QBJNZ2/id8z0h8z5001sARXPf+4mHBJslenH3
-YtZs22BG5RBLULtZ/2Nr7JkdfLlc6D5PCoDG22r1OiFkYVdCWfLDjisVIbSYPBtY
-BlKAIrvbmOtWcaGM+vQAhl0T5N8WRCKhaQH0DEmzQNckkYd7rSECo57KYiuvOdzp
-d+3bWTgGGy2ff0o3LZypv0O5s0TDC2H6hYtN4bUbcChUJbFu9b5sVZaOEVZtUsyD
-AgMBAAGjPzA9MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgTwMB0GA1UdDgQW
-BBSqGzsEDNs9E7gBL5pD6XVAwUo4DTANBgkqhkiG9w0BAQsFAAOCAQEAMTNB8NCU
-dnLFZ0jNpvecbECkX/OWGlBYU4/CsoNiibwp4CtUYS2A4NFVjWAyuzLSHhRQi0vJ
-CCWLpKL4VTkaDN5Oft42iUhvEXMnriJqpfXHnjCiBwFFSPl5WKfMIaRNK+tF4zbB
-F+FGNEEmYG3t/ni82orDLq4oy+7CoQwzZNzj5yoV6q7O9kLR9OMPNwJrc27A4erB
-7VMRZslSrNA4uA6YhMZl8iEvO1H801ct0zTxawrCihPOZOCSLew35xjztO7d3YH8
-YavOu5kzeu7AgZ2n75H/qU47ZgBjbonn9Osvrct+RIwZuWTB2bDML8JhNaZCq0aA
-TDBC0QWqIYypLg==
------END CERTIFICATE-----
index 2072be4d13ea6db87b2d6560f410b55a74c41263..35a80b0afaa87728ec0deee739b9a8d54ff8ab1a 100644 (file)
@@ -1,8 +1,3 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-
-PYTESTS = d2_test.py
-EXTRA_DIST = $(PYTESTS)
-
 # Explicitly specify paths to dynamic libraries required by loadable python
 # modules. That is required on Mac OS systems. Otherwise we will get exception
 # about python not being able to load liblog library.
@@ -11,16 +6,6 @@ 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
-# test using command-line arguments, so use check-local target instead of TESTS
-check-local:
-       for pytest in $(PYTESTS) ; do \
-       echo Running test: $$pytest ; \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-               $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin
index c835aaa1bf87e37c78ad0ce97ee41a8cb85e659e..67b849e2d2f534da6b2402d32efa875ce15190c7 100644 (file)
@@ -1,7 +1,3 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-
-PYTESTS = dhcp4_test.py
-EXTRA_DIST = $(PYTESTS)
 
 # Explicitly specify paths to dynamic libraries required by loadable python
 # modules. That is required on Mac OS systems. Otherwise we will get exception
@@ -11,16 +7,6 @@ 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
-# test using command-line arguments, so use check-local target instead of TESTS
-check-local:
-       for pytest in $(PYTESTS) ; do \
-       echo Running test: $$pytest ; \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-               $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
 AM_CPPFLAGS += -I$(top_srcdir)/src/bin
diff --git a/src/bin/dhcp4/tests/dhcp4_test.py b/src/bin/dhcp4/tests/dhcp4_test.py
deleted file mode 100644 (file)
index 37e9629..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-# 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.
-
-from init import ProcessInfo
-
-import unittest
-import sys
-import os
-import signal
-import socket
-from isc.net.addr import IPAddr
-import time
-import isc
-import fcntl
-
-class TestDhcpv4Daemon(unittest.TestCase):
-    def setUp(self):
-        # Don't redirect stdout/stderr here as we want to print out things
-        # during the test
-        #
-        # However, we do want to set the logging lock directory to somewhere
-        # to which we can write - use the current working directory.  We then
-        # set the appropriate environment variable.  os.putenv() may be not
-        # supported on some platforms as suggested in
-        # http://docs.python.org/release/3.2/library/os.html?highlight=putenv#os.environ:
-        # "If the platform supports the putenv() function...". It was checked
-        # that it does not work on Ubuntu. To overcome this problem we access
-        # os.environ directly.
-        lockdir_envvar = "B10_LOCKFILE_DIR_FROM_BUILD"
-        if lockdir_envvar not in os.environ:
-            os.environ[lockdir_envvar] = os.getcwd()
-
-    def tearDown(self):
-        pass
-
-    def readPipe(self, pipe_fd):
-        """
-        Reads bytes from a pipe and returns a character string.  If nothing is
-        read, or if there is an error, an empty string is returned.
-
-        pipe_fd - Pipe file descriptor to read
-        """
-        try:
-            data = os.read(pipe_fd, 16384)
-            # Make sure we have a string
-            if (data is None):
-                data = ""
-            else:
-                data = str(data)
-        except OSError:
-            data = ""
-
-        return data
-
-    def runCommand(self, params, wait=1):
-        """
-        This method runs a command and returns a tuple: (returncode, stdout, stderr)
-        """
-        ## @todo: Convert this into generic method and reuse it in dhcp4 and dhcp6
-
-        print("Running command: %s" % (" ".join(params)))
-
-        # redirect stdout to a pipe so we can check that our
-        # process spawning is doing the right thing with stdout
-        self.stdout_old = os.dup(sys.stdout.fileno())
-        self.stdout_pipes = os.pipe()
-        os.dup2(self.stdout_pipes[1], sys.stdout.fileno())
-        os.close(self.stdout_pipes[1])
-
-        # do the same trick for stderr:
-        self.stderr_old = os.dup(sys.stderr.fileno())
-        self.stderr_pipes = os.pipe()
-        os.dup2(self.stderr_pipes[1], sys.stderr.fileno())
-        os.close(self.stderr_pipes[1])
-
-        # note that we use dup2() to restore the original stdout
-        # to the main program ASAP in each test... this prevents
-        # hangs reading from the child process (as the pipe is only
-        # open in the child), and also insures nice pretty output
-
-        pi = ProcessInfo('Test Process', params)
-        pi.spawn()
-        time.sleep(wait)
-        os.dup2(self.stdout_old, sys.stdout.fileno())
-        os.dup2(self.stderr_old, sys.stderr.fileno())
-        self.assertNotEqual(pi.process, None)
-        self.assertTrue(type(pi.pid) is int)
-
-        # Set non-blocking read on pipes. Process may not print anything
-        # on specific output and the we would hang without this.
-        fd = self.stdout_pipes[0]
-        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
-        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
-
-        fd = self.stderr_pipes[0]
-        fl = fcntl.fcntl(fd, fcntl.F_GETFL)
-        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
-
-        # As we don't know how long the subprocess will take to start and
-        # produce output, we'll loop and sleep for 250 ms between each
-        # iteration.  To avoid an infinite loop, we'll loop for a maximum
-        # of five seconds: that should be enough.
-        for count in range(20):
-            # Read something from stderr and stdout (these reads don't block).
-            output = self.readPipe(self.stdout_pipes[0])
-            error  = self.readPipe(self.stderr_pipes[0])
-
-            # If the process has already exited, or if it has output something,
-            # quit the loop now.
-            if pi.process.poll() is not None or len(error) > 0 or len(output) > 0:
-                break
-
-            # Process still running, try again in 250 ms.
-            time.sleep(0.25)
-
-        # Exited loop, kill the process if it is still running
-        if pi.process.poll() is None:
-            try:
-                pi.process.terminate()
-            except OSError:
-                print("Ignoring failed kill attempt. Process is dead already.")
-
-        # call this to get returncode, process should be dead by now
-        rc = pi.process.wait()
-
-        # Clean up our stdout/stderr munging.
-        os.dup2(self.stdout_old, sys.stdout.fileno())
-        os.close(self.stdout_old)
-        os.close(self.stdout_pipes[0])
-
-        os.dup2(self.stderr_old, sys.stderr.fileno())
-        os.close(self.stderr_old)
-        os.close(self.stderr_pipes[0])
-
-        # Free up resources (file descriptors) from the ProcessInfo object
-        # TODO: For some reason, this gives an error if the process has ended,
-        #       although it does cause all descriptors still allocated to the
-        #       object to be freed.
-        pi = None
-
-        print ("Process finished, return code=%d, stdout=%d bytes, stderr=%d bytes"
-               % (rc, len(output), len(error)) )
-
-        return (rc, output, error)
-
-    def test_alive(self):
-        print("Note: Purpose of some of the tests is to check if DHCPv4 server can be started,")
-        print("      not that is can bind sockets correctly. Please ignore binding errors.")
-
-        (returncode, output, error) = self.runCommand(["../b10-dhcp4", "-v"])
-        output_text = str(output) + str(error)
-        self.assertEqual(output_text.count("DHCP4_STARTING"), 1)
-
-    def test_portnumber_0(self):
-        print("Check that specifying port number 0 is not allowed.")
-
-        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-p', '0'])
-
-        # When invalid port number is specified, return code must not be success
-        self.assertTrue(returncode != 0)
-
-        # Check that there is an error message about invalid port number printed on stderr
-        self.assertEqual( str(error).count("Failed to parse port number"), 1)
-
-    def test_portnumber_missing(self):
-        print("Check that -p option requires a parameter.")
-
-        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p'])
-
-        # When invalid port number is specified, return code must not be success
-        self.assertTrue(returncode != 0)
-
-        # Check that there is an error message about invalid port number printed on stderr
-        self.assertEqual( str(error).count("option requires an argument"), 1)
-
-    def test_portnumber_invalid1(self):
-        print("Check that -p option is check against bogus port number (999999).")
-
-        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p','999999'])
-
-        # When invalid port number is specified, return code must not be success
-        self.assertTrue(returncode != 0)
-
-        # Check that there is an error message about invalid port number printed on stderr
-        self.assertEqual( str(error).count("Failed to parse port number"), 1)
-
-    def test_portnumber_invalid2(self):
-        print("Check that -p option is check against bogus port number (123garbage).")
-
-        (returncode, output, error) = self.runCommand(['../b10-dhcp4', '-p','123garbage'])
-
-        # When invalid port number is specified, return code must not be success
-        self.assertTrue(returncode != 0)
-
-        # Check that there is an error message about invalid port number printed on stderr
-        self.assertEqual( str(error).count("Failed to parse port number"), 1)
-
-if __name__ == '__main__':
-    unittest.main()
index 75452db730e69a2460a11c568c2a495abd639a89..d82197dd4505b699a76981a81dc0877f07ec0d58 100644 (file)
@@ -1,5 +1,3 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = dhcp6_test.py
 SHTESTS =
 # The test of dynamic reconfiguration based on signals will work only
 # if we are using file based configuration approach.
@@ -8,7 +6,7 @@ SHTESTS += dhcp6_reconfigure_test.sh
 SHTESTS += dhcp6_sigterm_test.sh
 SHTESTS += dhcp6_sigint_test.sh
 endif
-EXTRA_DIST = $(PYTESTS) $(SHTESTS)
+EXTRA_DIST = $(SHTESTS)
 
 # Explicitly specify paths to dynamic libraries required by loadable python
 # modules. That is required on Mac OS systems. Otherwise we will get exception
@@ -20,14 +18,6 @@ endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
-       for pytest in $(PYTESTS) ; do \
-       echo Running test: $$pytest ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
-               $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
        for shtest in $(SHTESTS) ; do \
        echo Running test: $$shtest ; \
        export B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir); \
diff --git a/src/bin/msgq/.gitignore b/src/bin/msgq/.gitignore
deleted file mode 100644 (file)
index ecd1bad..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/b10-msgq
-/msgq.py
-/run_msgq.sh
-/b10-msgq.8
diff --git a/src/bin/msgq/Makefile.am b/src/bin/msgq/Makefile.am
deleted file mode 100644 (file)
index 185d47a..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-SUBDIRS = . tests
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-pkglibexec_SCRIPTS = b10-msgq
-
-b10_msgqdir = $(pkgdatadir)
-b10_msgq_DATA = msgq.spec
-
-CLEANFILES = b10-msgq msgq.pyc
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.pyc
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.pyo
-
-man_MANS = b10-msgq.8
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) msgq.xml msgq_messages.mes msgq.spec
-
-nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
-pylogmessagedir = $(pyexecdir)/isc/log_messages/
-BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
-
-if GENERATE_DOCS
-
-b10-msgq.8: msgq.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/msgq.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-# Define rule to build logging source files from message file
-$(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py : msgq_messages.mes
-       $(top_builddir)/src/lib/log/compiler/message \
-       -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/msgq_messages.mes
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-msgq: msgq.py
-       $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@
-       chmod a+x $@
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/msgq/TODO b/src/bin/msgq/TODO
deleted file mode 100644 (file)
index 0e926cb..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-== TODO for MsgQ
-
-* The code could use some refactoring.
-
-* Refactor:  Right now, the MsgQ class manages each connection using several
-lists.  There should be a Client class which holds most of this per-client
-data.  Some will need to remain in the main "driver" MsgQ class (such as the
-list of clients and their lnames) but buffer management, reading from a
-socket, etc. could all go into this client class.
-
-* More tests:  A test to send to strange locations should be added, to test
-that code path (a bug which I am committing with this file...)
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
deleted file mode 100755 (executable)
index c68567c..0000000
+++ /dev/null
@@ -1,886 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010  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.
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-
-"""This code implements the msgq daemon."""
-
-import subprocess
-import signal
-import os
-import socket
-import sys
-import struct
-import errno
-import time
-import select
-import random
-import threading
-import isc.config.ccsession
-from optparse import OptionParser, OptionValueError
-import isc.util.process
-import isc.util.traceback_handler
-from isc.cc.proto_defs import *
-import isc.log
-from isc.log_messages.msgq_messages import *
-
-import isc.cc
-
-isc.util.process.rename()
-
-isc.log.init("b10-msgq", buffer=True)
-# Logger that is used in the actual msgq handling - startup, shutdown and the
-# poller thread.
-logger = isc.log.Logger("msgq")
-# A separate copy for the master/config thread when the poller thread runs.
-# We use a separate instance, since the logger itself doesn't have to be
-# thread safe.
-config_logger = isc.log.Logger("msgq")
-TRACE_START = logger.DBGLVL_START_SHUT
-TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
-TRACE_DETAIL = logger.DBGLVL_TRACE_DETAIL
-
-# This is the version that gets displayed to the user.
-# The VERSION string consists of the module name, the module version
-# number, and the overall BIND 10 version number (set in configure.ac).
-VERSION = "b10-msgq 20110127 (BIND 10 @PACKAGE_VERSION@)"
-
-# If B10_FROM_BUILD is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_SOURCE" in os.environ:
-    SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/msgq"
-else:
-    PREFIX = "@prefix@"
-    DATAROOTDIR = "@datarootdir@"
-    SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}",
-                                                  DATAROOTDIR). \
-                                                  replace("${prefix}", PREFIX)
-SPECFILE_LOCATION = SPECFILE_PATH + "/msgq.spec"
-
-class MsgQReceiveError(Exception): pass
-
-class MsgQRunningError(Exception): pass
-
-class MsgQCloseOnReceive(Exception):
-    """Exception raised when reading data from a socket results in 'shutdown'.
-
-    This happens when msgq received 0-length data.  This class holds whether
-    it happens in the middle of reading (i.e. after reading some) via
-    partial_read parameter, which is set to True if and only if so.
-    This will be used by an upper layer catching the exception to distinguish
-    the severity of the event.
-
-    """
-    def __init__(self, reason, partial_read):
-        self.partial_read = partial_read
-        self.__reason = reason
-
-    def __str__(self):
-        return self.__reason
-
-class SubscriptionManager:
-    def __init__(self, cfgmgr_ready):
-        """
-        Initialize the subscription manager.
-        parameters:
-        * cfgmgr_ready: A callable object run once the config manager
-            subscribes. This is a hackish solution, but we can't read
-            the configuration sooner.
-        """
-        self.subscriptions = {}
-        self.__cfgmgr_ready = cfgmgr_ready
-        self.__cfgmgr_ready_called = False
-
-    def subscribe(self, group, instance, socket):
-        """Add a subscription."""
-        target = ( group, instance )
-        if target in self.subscriptions:
-            logger.debug(TRACE_BASIC, MSGQ_SUBS_APPEND_TARGET, group, instance)
-            if socket not in self.subscriptions[target]:
-                self.subscriptions[target].append(socket)
-        else:
-            logger.debug(TRACE_BASIC, MSGQ_SUBS_NEW_TARGET, group, instance)
-            self.subscriptions[target] = [ socket ]
-        if group == "ConfigManager" and not self.__cfgmgr_ready_called:
-            logger.debug(TRACE_BASIC, MSGQ_CFGMGR_SUBSCRIBED)
-            self.__cfgmgr_ready_called = True
-            self.__cfgmgr_ready()
-
-    def unsubscribe(self, group, instance, socket):
-        """Remove the socket from the one specific subscription."""
-        target = ( group, instance )
-        if target in self.subscriptions:
-            if socket in self.subscriptions[target]:
-                self.subscriptions[target].remove(socket)
-                return True
-        return False
-
-    def unsubscribe_all(self, socket):
-        """Remove the socket from all subscriptions."""
-        removed_from = []
-        for subs, socklist in self.subscriptions.items():
-            if socket in socklist:
-                socklist.remove(socket)
-                removed_from.append(subs)
-        return removed_from
-
-    def find_sub(self, group, instance):
-        """Return an array of sockets which want this specific group,
-        instance."""
-        target = (group, instance)
-        if target in self.subscriptions:
-            return self.subscriptions[target]
-        else:
-            return []
-
-    def find(self, group, instance):
-        """Return an array of sockets who should get something sent to
-        this group, instance pair.  This includes wildcard subscriptions."""
-        target = (group, instance)
-        partone = self.find_sub(group, instance)
-        parttwo = self.find_sub(group, CC_INSTANCE_WILDCARD)
-        return list(set(partone + parttwo))
-
-class MsgQ:
-    """Message Queue class."""
-    # did we find a better way to do this?
-    SOCKET_FILE = os.path.join("@localstatedir@",
-                               "@PACKAGE_NAME@",
-                               "msgq_socket").replace("${prefix}",
-                                                      "@prefix@")
-
-    def __init__(self, socket_file=None, verbose=False):
-        """Initialize the MsgQ master.
-
-        The socket_file specifies the path to the UNIX domain socket
-        that the msgq process listens on. If it is None, the
-        environment variable BIND10_MSGQ_SOCKET_FILE is used. If that
-        is not set, it will default to
-        @localstatedir@/@PACKAGE_NAME@/msg_socket.
-        If verbose is True, then the MsgQ reports
-        what it is doing.
-        """
-
-        if socket_file is None:
-            if "BIND10_MSGQ_SOCKET_FILE" in os.environ:
-                self.socket_file = os.environ["BIND10_MSGQ_SOCKET_FILE"]
-            else:
-                self.socket_file = self.SOCKET_FILE
-        else:
-            self.socket_file = socket_file
-
-        self.verbose = verbose
-        self.runnable = False
-        self.listen_socket = False
-        self.sockets = {}
-        self.connection_counter = random.random()
-        self.hostname = socket.gethostname()
-        self.subs = SubscriptionManager(self.cfgmgr_ready)
-        self.lnames = {}
-        self.fd_to_lname = {}
-        self.sendbuffs = {}
-        self.running = False
-        self.__cfgmgr_ready = None
-        self.__cfgmgr_ready_cond = threading.Condition()
-        # A lock used when the message queue does anything more complicated.
-        # It is mostly a safety measure, the threads doing so should be mostly
-        # independent, and the one with config session should be read only,
-        # but with threads, one never knows. We use threads for concurrency,
-        # not for performance, so we use wide lock scopes to be on the safe
-        # side.
-        self.__lock = threading.Lock()
-        self._session = None
-        self.__poller_sock = None
-
-    def members_notify(self, event, params):
-        """
-        Thin wrapper around ccs's notify. Send a notification about change
-        of some list that can be requested by the members command.
-
-        The event is one of:
-        - connected (client connected to MsgQ)
-        - disconected (client disconnected from MsgQ)
-        - subscribed (client subscribed to a group)
-        - unsubscribed (client unsubscribed from a group)
-
-        The params is dict containing:
-        - client: The lname of the client in question.
-        - group (for 'subscribed' and 'unsubscribed' events):
-          The group the client subscribed or unsubscribed from.
-
-        The notification occurs after the event, so client a subscribing for
-        notifications will get a notification about its own subscription, but
-        will not get a notification when it unsubscribes.
-        """
-        # Due to the interaction between threads (and fear it might influence
-        # sending stuff), we test this method in msgq_run_test, instead of
-        # mocking the ccs.
-        if self._session: # Don't send before we have started up
-            self._session.notify('cc_members', event, params)
-
-    def cfgmgr_ready(self, ready=True):
-        """Notify that the config manager is either subscribed, or
-           that the msgq is shutting down and it won't connect, but
-           anybody waiting for it should stop anyway.
-
-           The ready parameter signifies if the config manager is subscribed.
-
-           This method can be called multiple times, but second and any
-           following call is simply ignored. This means the "abort" version
-           of the call can be used on any stop unconditionally, even when
-           the config manager already connected.
-        """
-        with self.__cfgmgr_ready_cond:
-            if self.__cfgmgr_ready is not None:
-                # This is a second call to this method. In that case it does
-                # nothing.
-                return
-            self.__cfgmgr_ready = ready
-            self.__cfgmgr_ready_cond.notify_all()
-
-    def wait_cfgmgr(self):
-        """Wait for msgq to subscribe.
-
-           When this returns, the config manager is either subscribed, or
-           msgq gave up waiting for it. Success is signified by the return
-           value.
-        """
-        with self.__cfgmgr_ready_cond:
-            # Wait until it either aborts or subscribes
-            while self.__cfgmgr_ready is None:
-                self.__cfgmgr_ready_cond.wait()
-            return self.__cfgmgr_ready
-
-    def setup_listener(self):
-        """Set up the listener socket.  Internal function."""
-        logger.debug(TRACE_BASIC, MSGQ_LISTENER_SETUP, self.socket_file)
-
-        if os.path.exists(self.socket_file):
-            # Rather than just blindly removing the socket file, attempt to
-            # connect to the existing socket to see if there is an existing
-            # msgq running. Only if that fails do we remove the file and
-            # attempt to create a new socket.
-            existing_msgq = None
-            try:
-                existing_msgq = isc.cc.Session(self.socket_file)
-            except isc.cc.session.SessionError:
-                existing_msgq = None
-
-            if existing_msgq:
-                existing_msgq.close()
-                logger.fatal(MSGQ_ALREADY_RUNNING)
-                raise MsgQRunningError("b10-msgq already running")
-
-            os.remove(self.socket_file)
-
-        try:
-            self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            self.listen_socket.bind(self.socket_file)
-            self.listen_socket.listen(1024)
-        except Exception as e:
-            # remove the file again if something goes wrong
-            # (note this is a catch-all, but we reraise it)
-            if os.path.exists(self.socket_file):
-                os.remove(self.socket_file)
-            self.listen_socket.close()
-            logger.fatal(MSGQ_LISTENER_FAILED, self.socket_file, e)
-            raise e
-
-    def setup_signalsock(self):
-        """Create a socket pair used to signal when we want to finish.
-           Using a socket is easy and thread/signal safe way to signal
-           the termination.
-        """
-        # The __poller_sock will be the end in the poller. When it is
-        # closed, we should shut down.
-        (self.__poller_sock, self.__control_sock) = socket.socketpair()
-
-    def setup(self):
-        """Configure listener socket, polling, etc.
-           Raises a socket.error if the socket_file cannot be
-           created.
-        """
-
-        self.setup_signalsock()
-        self.setup_listener()
-
-        logger.debug(TRACE_START, MSGQ_LISTENER_STARTED);
-
-        self.runnable = True
-
-    def process_accept(self):
-        """Process an accept on the listening socket."""
-        newsocket, ipaddr = self.listen_socket.accept()
-        # TODO: When we have logging, we might want
-        # to add a debug message here that a new connection
-        # was made
-        self.register_socket(newsocket)
-
-    def register_socket(self, newsocket):
-        """
-        Internal function to insert a socket. Used by process_accept and some
-        tests.
-        """
-        self.sockets[newsocket.fileno()] = newsocket
-        lname = self.newlname()
-        self.lnames[lname] = newsocket
-        self.fd_to_lname[newsocket.fileno()] = lname
-
-        logger.debug(TRACE_BASIC, MSGQ_SOCKET_REGISTERED, newsocket.fileno(),
-                     lname)
-
-        self.members_notify('connected', {'client': lname})
-
-    def kill_socket(self, fd, sock):
-        """Fully close down the socket."""
-
-        unsubscribed_from = self.subs.unsubscribe_all(sock)
-        lname = self.fd_to_lname[fd]
-        del self.fd_to_lname[fd]
-        del self.lnames[lname]
-        sock.close()
-        del self.sockets[fd]
-        if fd in self.sendbuffs:
-            del self.sendbuffs[fd]
-        logger.debug(TRACE_BASIC, MSGQ_SOCK_CLOSE, fd)
-        # Filter out just the groups.
-        unsubscribed_from_groups = set(map(lambda x: x[0], unsubscribed_from))
-        for group in unsubscribed_from_groups:
-            self.members_notify('unsubscribed', {
-                                    'client': lname,
-                                    'group': group
-                                })
-        self.members_notify('disconnected', {'client': lname})
-
-    def __getbytes(self, fd, sock, length, continued):
-        """Get exactly the requested bytes, or raise an exception if
-           EOF.
-
-           continued is set to True if this method is called to complete
-           already read data.
-           """
-        received = b''
-        while len(received) < length:
-            try:
-                data = sock.recv(length - len(received))
-
-            except socket.error as err:
-                # This case includes ECONNRESET, which seems to happen when
-                # the remote client has closed its socket at some subtle
-                # timing (it should normally result in receiving empty data).
-                # Since we didn't figure out how exactly that could happen,
-                # we treat it just like other really-unexpected socket errors.
-                raise MsgQReceiveError(str(err))
-            if len(data) == 0:
-                raise MsgQCloseOnReceive("EOF", continued)
-            received += data
-            continued = True
-        return received
-
-    def read_packet(self, fd, sock):
-        """Read a correctly formatted packet.  Will raise exceptions if
-           something fails."""
-        lengths = self.__getbytes(fd, sock, 6, False)
-        overall_length, routing_length = struct.unpack(">IH", lengths)
-        if overall_length < 2:
-            raise MsgQReceiveError("overall_length < 2")
-        overall_length -= 2
-        if routing_length > overall_length:
-            raise MsgQReceiveError("routing_length > overall_length")
-        if routing_length == 0:
-            raise MsgQReceiveError("routing_length == 0")
-        data_length = overall_length - routing_length
-        # probably need to sanity check lengths here...
-        routing = self.__getbytes(fd, sock, routing_length, True)
-        if data_length > 0:
-            data = self.__getbytes(fd, sock, data_length, True)
-        else:
-            data = None
-        return (routing, data)
-
-    def process_packet(self, fd, sock):
-        """Process one packet."""
-        try:
-            routing, data = self.read_packet(fd, sock)
-        except (MsgQReceiveError, MsgQCloseOnReceive) as err:
-            # If it's MsgQCloseOnReceive and that happens without reading
-            # any data, it basically means the remote client has closed the
-            # socket, so we log it as debug information.  Otherwise, it's
-            # a somewhat unexpected event, so we consider it an "error".
-            if isinstance(err, MsgQCloseOnReceive) and not err.partial_read:
-                logger.debug(TRACE_BASIC, MSGQ_CLOSE_ON_RECV, fd)
-            else:
-                logger.error(MSGQ_RECV_ERROR, fd, err)
-            self.kill_socket(fd, sock)
-            return
-
-        try:
-            routingmsg = isc.cc.message.from_wire(routing)
-        except DecodeError as err:
-            self.kill_socket(fd, sock)
-            logger.error(MSGQ_HDR_DECODE_ERROR, fd, err)
-            return
-
-        self.process_command(fd, sock, routingmsg, data)
-
-    def process_command(self, fd, sock, routing, data):
-        """Process a single command.  This will split out into one of the
-           other functions."""
-        logger.debug(TRACE_DETAIL, MSGQ_RECV_HDR, routing)
-        cmd = routing[CC_HEADER_TYPE]
-        if cmd == CC_COMMAND_SEND:
-            self.process_command_send(sock, routing, data)
-        elif cmd == CC_COMMAND_SUBSCRIBE:
-            self.process_command_subscribe(sock, routing, data)
-        elif cmd == CC_COMMAND_UNSUBSCRIBE:
-            self.process_command_unsubscribe(sock, routing, data)
-        elif cmd == CC_COMMAND_GET_LNAME:
-            self.process_command_getlname(sock, routing, data)
-        elif cmd == CC_COMMAND_PING:
-            # Command for testing purposes
-            self.process_command_ping(sock, routing, data)
-        elif cmd == CC_COMMAND_STOP:
-            self.stop()
-        else:
-            logger.error(MSGQ_INVALID_CMD, cmd)
-
-    def preparemsg(self, env, msg = None):
-        if type(env) == dict:
-            env = isc.cc.message.to_wire(env)
-        if type(msg) == dict:
-            msg = isc.cc.message.to_wire(msg)
-        length = 2 + len(env);
-        if msg:
-            length += len(msg)
-        ret = struct.pack("!IH", length, len(env))
-        ret += env
-        if msg:
-            ret += msg
-        return ret
-
-    def sendmsg(self, sock, env, msg = None):
-        self.send_prepared_msg(sock, self.preparemsg(env, msg))
-
-    def _send_data(self, sock, data):
-        """
-        Send a piece of data to the given socket.  This method is
-        essentially "private" to MsgQ, but defined as if it were "protected"
-        for easier access from tests.
-
-        Parameters:
-        sock: The socket to send to
-        data: The list of bytes to send
-        Returns:
-        An integer or None. If an integer (which can be 0), it signals
-        the number of bytes sent. If None, the socket appears to have
-        been closed on the other end, and it has been killed on this
-        side too.
-        """
-        try:
-            # We set the socket nonblocking, MSG_DONTWAIT doesn't exist
-            # on some OSes
-            sock.setblocking(0)
-            return sock.send(data)
-        except socket.error as e:
-            if e.errno in [ errno.EAGAIN, errno.EWOULDBLOCK, errno.EINTR ]:
-                return 0
-            elif e.errno in [ errno.EPIPE, errno.ECONNRESET, errno.ENOBUFS ]:
-                # EPIPE happens if the remote module has terminated by the time
-                # of this send; its severity can vary, but in many cases it
-                # shouldn't be critical, so we log it separately as a warning.
-                if e.errno == errno.EPIPE:
-                    logger.warn(MSGQ_CLOSE_ON_SEND, sock.fileno())
-                else:
-                    logger.error(MSGQ_SEND_ERROR, sock.fileno(),
-                                 errno.errorcode[e.errno])
-                self.kill_socket(sock.fileno(), sock)
-                return None
-            else:
-                raise e
-        finally:
-            # And set it back again
-            sock.setblocking(1)
-
-    def send_prepared_msg(self, sock, msg):
-        '''
-        Add a message to the queue. If there's nothing waiting, try
-        to send it right away.
-
-        Return if the socket is still alive. It can return false if the
-        socket dies (for example due to EPIPE in the attempt to send).
-        Returning true does not guarantee the message will be delivered,
-        but returning false means it won't.
-        '''
-        # Try to send the data, but only if there's nothing waiting
-        fileno = sock.fileno()
-        if fileno in self.sendbuffs:
-            amount_sent = 0
-        else:
-            amount_sent = self._send_data(sock, msg)
-            if amount_sent is None:
-                # Socket has been killed, drop the send
-                return False
-
-        # Still something to send, add it to outgoing queue
-        if amount_sent < len(msg):
-            now = time.clock()
-            # Append it to buffer (but check the data go away)
-            if fileno in self.sendbuffs:
-                (last_sent, buff) = self.sendbuffs[fileno]
-                tdelta = now - last_sent
-                if tdelta > 0.1:
-                    logger.error(MSGQ_SOCKET_TIMEOUT_ERROR, fileno, tdelta)
-                    self.kill_socket(fileno, sock)
-                    return False
-                buff += msg
-            else:
-                buff = msg[amount_sent:]
-                last_sent = now
-            self.sendbuffs[fileno] = (last_sent, buff)
-        return True
-
-    def _process_write(self, fileno):
-        # Try to send some data from the buffer
-        (_, msg) = self.sendbuffs[fileno]
-        sock = self.sockets[fileno]
-        amount_sent = self._send_data(sock, msg)
-        if amount_sent is not None:
-            # Keep the rest
-            msg = msg[amount_sent:]
-            if len(msg) == 0:
-                # If there's no more, stop requesting for write availability
-                del self.sendbuffs[fileno]
-            else:
-                self.sendbuffs[fileno] = (time.clock(), msg)
-
-    def newlname(self):
-        """Generate a unique connection identifier for this socket.
-        This is done by using an increasing counter and the current
-        time."""
-        self.connection_counter += 1
-        return "%x_%x@%s" % (time.time(), self.connection_counter,
-                             self.hostname)
-
-    def process_command_ping(self, sock, routing, data):
-        self.sendmsg(sock, { CC_HEADER_TYPE : CC_COMMAND_PONG }, data)
-
-    def process_command_getlname(self, sock, routing, data):
-        lname = [ k for k, v in self.lnames.items() if v == sock ][0]
-        self.sendmsg(sock, { CC_HEADER_TYPE : CC_COMMAND_GET_LNAME },
-                     { CC_PAYLOAD_LNAME : lname })
-
-    def process_command_send(self, sock, routing, data):
-        group = routing[CC_HEADER_GROUP]
-        instance = routing[CC_HEADER_INSTANCE]
-        to = routing[CC_HEADER_TO]
-        if group == None or instance == None:
-            # FIXME: Should we log them instead?
-            return  # ignore invalid packets entirely
-
-        if to == CC_TO_WILDCARD:
-            sockets = self.subs.find(group, instance)
-        else:
-            if to in self.lnames:
-                sockets = [ self.lnames[to] ]
-            else:
-                sockets = []
-
-        msg = self.preparemsg(routing, data)
-
-        if sock in sockets:
-            # Don't bounce to self
-            sockets.remove(sock)
-
-        has_recipient = False
-        for socket in sockets:
-            if self.send_prepared_msg(socket, msg):
-                has_recipient = True
-        if not has_recipient and routing.get(CC_HEADER_WANT_ANSWER) and \
-            CC_HEADER_REPLY not in routing:
-            # We have no recipients. But the sender insists on a reply
-            # (and the message isn't a reply itself). We need to send
-            # an error to satisfy the request, since there's nobody
-            # else who can.
-            #
-            # We omit the replies on purpose. The recipient might generate
-            # the response by copying and mangling the header of incoming
-            # message (just like we do below) and would include the want_answer
-            # by accident. And we want to avoid loops of errors. Also, it
-            # is unclear if the knowledge of undeliverable reply would be
-            # of any use to the sender, and it should be much rarer situation.
-
-            # The real errors would be positive, 1 most probably. We use
-            # negative errors for delivery errors to distinguish them a
-            # little. We probably should have a way to provide more data
-            # in the error message.
-            payload = isc.config.ccsession.create_answer(CC_REPLY_NO_RECPT,
-                                                         "No such recipient")
-            # We create the header based on the current one. But we don't
-            # want to mangle it for the caller, so we get a copy. A shallow
-            # one should be enough, we modify the dict only.
-            header = routing.copy()
-            header[CC_HEADER_REPLY] = routing[CC_HEADER_SEQ]
-            # Dummy lname not assigned to clients
-            header[CC_HEADER_FROM] = "msgq"
-            header[CC_HEADER_TO] = routing[CC_HEADER_FROM]
-            # We keep the seq as it is. We don't need to track the message
-            # and we will not confuse the sender. The sender would use an
-            # unique id for each message, so we won't return one twice to it.
-            errmsg = self.preparemsg(header, payload)
-            # Send it back.
-            self.send_prepared_msg(sock, errmsg)
-
-    def process_command_subscribe(self, sock, routing, data):
-        group = routing[CC_HEADER_GROUP]
-        instance = routing[CC_HEADER_INSTANCE]
-        if group == None or instance == None:
-            return  # ignore invalid packets entirely
-        self.subs.subscribe(group, instance, sock)
-        lname = self.fd_to_lname[sock.fileno()]
-        self.members_notify('subscribed',
-                            {
-                                'client': lname,
-                                'group': group
-                            })
-
-    def process_command_unsubscribe(self, sock, routing, data):
-        group = routing[CC_HEADER_GROUP]
-        instance = routing[CC_HEADER_INSTANCE]
-        if group == None or instance == None:
-            return  # ignore invalid packets entirely
-        if self.subs.unsubscribe(group, instance, sock):
-            lname = self.fd_to_lname[sock.fileno()]
-            self.members_notify('unsubscribed',
-                                {
-                                    'client': lname,
-                                    'group': group
-                                })
-
-    def run(self):
-        """Process messages.  Forever.  Mostly."""
-        self.running = True
-
-        self.run_select()
-
-    def run_select(self):
-        while self.running:
-            reads = list(self.fd_to_lname.keys())
-            if self.listen_socket.fileno() != -1: # Skip in tests
-                reads.append(self.listen_socket.fileno())
-            if self.__poller_sock and self.__poller_sock.fileno() != -1:
-                reads.append(self.__poller_sock.fileno())
-            writes = list(self.sendbuffs.keys())
-            (read_ready, write_ready) = ([], [])
-            try:
-                (read_ready, write_ready, _) = select.select(reads, writes,
-                                                             []);
-            except select.error as err:
-                if err.args[0] == errno.EINTR:
-                    continue # Just try it again if interrupted.
-                else:
-                    logger.fatal(MSGQ_SELECT_ERROR, err)
-                    break
-            with self.__lock:
-                write_ready = set(write_ready)
-                for fd in read_ready:
-                    # Do only one operation per loop iteration on the given fd.
-                    # It could be possible to perform both, but it may have
-                    # undesired side effects in special situations (like, if the
-                    # read closes the socket).
-                    if fd in write_ready:
-                        write_ready.remove(fd)
-                    if fd == self.listen_socket.fileno():
-                        self.process_accept()
-                    elif self.__poller_sock and fd == \
-                        self.__poller_sock.fileno():
-                        # The signal socket. We should terminate now.
-                        self.running = False
-                        break
-                    else:
-                        self.process_packet(fd, self.sockets[fd])
-                for fd in write_ready:
-                    self._process_write(fd)
-
-    def stop(self):
-        # Signal it should terminate.
-        self.__control_sock.close()
-        self.__control_sock = None
-        # Abort anything waiting on the condition, just to make sure it's not
-        # blocked forever
-        self.cfgmgr_ready(False)
-
-    def cleanup_signalsock(self):
-        """Close the signal sockets. We could do it directly in shutdown,
-           but this part is reused in tests.
-        """
-        if self.__poller_sock:
-            self.__poller_sock.close()
-            self.__poller_sock = None
-        if self.__control_sock:
-            self.__control_sock.close()
-            self.__control_sock = None
-
-    def shutdown(self):
-        """Stop the MsgQ master."""
-        logger.debug(TRACE_START, MSGQ_SHUTDOWN)
-        self.listen_socket.close()
-        self.cleanup_signalsock()
-        # Close all the sockets too. In real life, there should be none now,
-        # as Msgq should be the last one. But some tests don't adhere to this
-        # and create a new Msgq for each test, which led to huge socket leaks.
-        # Some other threads put some other things in instead of sockets, so
-        # we catch whatever exceptions there we can. This should be safe,
-        # because in real operation, we will terminate now anyway, implicitly
-        # closing anything anyway.
-        for sock in self.sockets.values():
-            try:
-                sock.close()
-            except Exception:
-                pass
-        if os.path.exists(self.socket_file):
-            os.remove(self.socket_file)
-
-    def config_handler(self, new_config):
-        """The configuration handler (run in a separate thread).
-           Not tested, currently effectively empty.
-        """
-        config_logger.debug(TRACE_DETAIL, MSGQ_CONFIG_DATA, new_config)
-
-        with self.__lock:
-            if not self.running:
-                return
-
-            # TODO: Any config handling goes here.
-
-            return isc.config.create_answer(0)
-
-    def command_handler(self, command, args):
-        """The command handler (run in a separate thread)."""
-        config_logger.debug(TRACE_DETAIL, MSGQ_COMMAND, command, args)
-
-        with self.__lock:
-            if not self.running:
-                return
-
-            # TODO: Who does validation? The ModuleCCSession or must we?
-
-            if command == 'members':
-                # List all members of MsgQ or of a group.
-                if args is None:
-                    args = {}
-                group = args.get('group')
-                if group:
-                    return isc.config.create_answer(0,
-                        list(map(lambda sock: self.fd_to_lname[sock.fileno()],
-                                 self.subs.find(group, ''))))
-                else:
-                    return isc.config.create_answer(0,
-                                                    list(self.lnames.keys()))
-
-            config_logger.error(MSGQ_COMMAND_UNKNOWN, command)
-            return isc.config.create_answer(1, 'unknown command: ' + command)
-
-def signal_handler(msgq, signal, frame):
-    if msgq:
-        msgq.stop()
-
-def main():
-    def check_port(option, opt_str, value, parser):
-        """Function to insure that the port we are passed is actually
-        a valid port number. Used by OptionParser() on startup."""
-        intval = int(value)
-        if (intval < 0) or (intval > 65535):
-            raise OptionValueError("%s requires a port number (0-65535)" %
-                                   opt_str)
-        parser.values.msgq_port = intval
-
-    # Parse any command-line options.
-    parser = OptionParser(version=VERSION)
-    # TODO: Should we remove the option?
-    parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
-                      help="display more about what is going on")
-    parser.add_option("-s", "--socket-file", dest="msgq_socket_file",
-                      type="string", default=None,
-                      help="UNIX domain socket file the msgq daemon will use")
-    (options, args) = parser.parse_args()
-
-    # Announce startup.
-    logger.debug(TRACE_START, MSGQ_START, VERSION)
-
-    msgq = MsgQ(options.msgq_socket_file, options.verbose)
-
-    signal.signal(signal.SIGTERM,
-                  lambda signal, frame: signal_handler(msgq, signal, frame))
-
-    # Ignore SIGPIPE. We handle errors writing to children using try in the
-    # appropriate places and the delivery of a signal is very heavy handed.
-    # Windows doesn't support SIGPIPE so don't try it there.
-    if not sys.platform.startswith('win'):
-        signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-
-    try:
-        msgq.setup()
-    except Exception as e:
-        logger.fatal(MSGQ_START_FAIL, e)
-        sys.exit(1)
-
-    # We run the processing in a separate thread. This is because we want to
-    # connect to the msgq ourself. But the cc library is unfortunately blocking
-    # in many places and waiting for the processing part to answer, it would
-    # deadlock.
-    poller_thread = threading.Thread(target=msgq.run)
-    poller_thread.daemon = True
-    try:
-        poller_thread.start()
-        if msgq.wait_cfgmgr():
-            # Once we get the config manager, we can read our own config.
-            session = isc.config.ModuleCCSession(SPECFILE_LOCATION,
-                                                 msgq.config_handler,
-                                                 msgq.command_handler,
-                                                 None, True,
-                                                 msgq.socket_file)
-            msgq._session = session
-            session.start()
-            # And we create a thread that'll just wait for commands and
-            # handle them. We don't terminate the thread, we set it to
-            # daemon. Once the main thread terminates, it'll just die.
-            def run_session():
-                while True:
-                    # As the check_command has internal mutex that is shared
-                    # with sending part (which includes notify). So we don't
-                    # want to hold it long-term and block using select.
-                    fileno = session.get_socket().fileno()
-                    try:
-                        (reads, _, _) = select.select([fileno], [], [])
-                    except select.error as se:
-                        if se.args[0] != errno.EINTR:
-                            raise
-                    session.check_command(True)
-            background_thread = threading.Thread(target=run_session)
-            background_thread.daemon = True
-            background_thread.start()
-        poller_thread.join()
-    except KeyboardInterrupt:
-        pass
-
-    msgq.shutdown()
-
-    logger.info(MSGQ_EXITING)
-
-if __name__ == "__main__":
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/msgq/msgq.spec b/src/bin/msgq/msgq.spec
deleted file mode 100644 (file)
index 4b388c5..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "Msgq",
-    "module_description": "The message queue",
-    "config_data": [],
-    "commands": [
-      {
-        "command_name": "members",
-        "command_description": "Provide the list of members of a group or of the whole MsgQ if no group is given.",
-        "command_args": [
-          {
-            "item_name": "group",
-            "item_optional": true,
-            "item_type": "string"
-          }
-        ]
-      }
-    ]
-  }
-}
diff --git a/src/bin/msgq/msgq.xml b/src/bin/msgq/msgq.xml
deleted file mode 100644 (file)
index 8cef10c..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>June 25, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-msgq</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-msgq</refname>
-    <refpurpose>message routing daemon for the Command Channel</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-msgq</command>
-      <arg><option>-s <replaceable>file</replaceable></option></arg>
-      <arg><option>-v</option></arg>
-      <arg><option>--socket-file <replaceable>file</replaceable></option></arg>
-      <arg><option>--verbose</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      The <command>b10-msgq</command>
-      daemon provides message routing for the Command Channel.
-    </para>
-
-    <para>
-      The Command Channel is a message bus and subscription manager.
-      Programs may subscribe to certain groups to receive messages
-      for that group.
-      Every new connection to <command>b10-msgq</command> is
-      assigned a unique identifier -- this is the local name.
-      The commands it handles are:
-      <itemizedlist>
-
-      <listitem><para>
-        <command>getlname</command>
-        &mdash; receive local name.
-      </para></listitem>
-
-      <listitem><para>
-        <command>send</command>
-        &mdash; send a message to defined subscribers.
-      </para></listitem>
-
-      <listitem><para>
-        <command>subscribe</command>
-       &mdash; add a subscription. This means it is a listener
-       for messages for a specific group.
-      </para></listitem>
-
-      <listitem><para>
-        <command>unsubscribe</command>
-        &mdash; remove a subscription.
-      </para></listitem>
-
-      </itemizedlist>
-    </para>
-
-    <para>
-      The <command>b10-msgq</command> daemon may be cleanly stopped by
-      sending the SIGTERM signal to the process.
-      This shutdown does not notify the subscribers.
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>OPTIONS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <variablelist>
-      <varlistentry>
-        <term><option>-s <replaceable>file</replaceable></option>,
-          <option>--socket-file <replaceable>file</replaceable></option></term>
-        <listitem><para>
-          The UNIX domain socket file this daemon will use.
-          The default is
-          <filename>/usr/local/var/bind10/msg_socket</filename>.
-<!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
-          </para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-v</option>, <option>--verbose</option></term>
-        <listitem><para>
-          Enabled verbose mode. This enables diagnostic messages to
-          STDERR.  Displays more about what <command>b10-msgq</command>
-          is doing.
-<!-- TODO: is STDERR correct? -->
-        </para></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-<!-- TODO: point to developer docs -->
-  </refsect1>
-
-  <refsect1>
-    <title>AUTHORS</title>
-    <para>
-      The <command>b10-msgq</command> daemon and Control Channel specification
-      were initially designed by Michael Graff of ISC.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-       The python version was first coded in December 2009.
-       The C version with now deprecated wire format was coded in September
-       2009.
-    </para>
-  </refsect1>
-
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
-
diff --git a/src/bin/msgq/msgq_messages.mes b/src/bin/msgq/msgq_messages.mes
deleted file mode 100644 (file)
index 0cc97ca..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-# No namespace declaration - these constants go in the global namespace
-# of the ddns messages python module.
-
-# When you add a message to this file, it is a good idea to run
-# <topsrcdir>/tools/reorder_message_file.py to make sure the
-# messages are in the correct order.
-
-% MSGQ_ALREADY_RUNNING Another copy of b10-msgq is already running.
-Only a single instance of b10-msgq should ever be run at one time.
-This instance will now terminate.
-
-% MSGQ_CFGMGR_SUBSCRIBED The config manager subscribed to message queue
-This is a debug message. The message queue has little bit of special handling
-for the configuration manager. This special handling is happening now.
-
-% MSGQ_CLOSE_ON_RECV Reading from socket canceled as it's closed: FD=%1
-A debug message.  The msgq daemon was notified of a read event on a
-socket, but its initial read operation failed because the remote
-client has closed its socket.  This is possible in a normal operation
-when a module shuts down.
-
-% MSGQ_CLOSE_ON_SEND Sending to socket failed as already closed (okay to ignore on shutdown): FD=%1
-The msgq daemon tries to send some data to a client module, but it
-failed because the socket has been closed.  This normally means the
-client terminates (for some reason - either gracefully or as a crash)
-while other modules try to send a message to the terminated module.
-Since msgq doesn't keep track of the status of client modules, this
-can happen and is not really an error for msgq; however, it can still
-be an unexpected event for the BIND 10 system as a whole in that this
-particular message is lost, so it's logged as a warning.  If this
-message is logged for a running BIND 10 system, it's suggested to
-check other log messages; there may be an error from other modules
-reporting a missing response message.  One common, less critical case
-where this message is logged is during shutdown.  The ordering of
-process shutdown is basically arbitrary at this moment, so it's
-possible that some module tries to send a "quitting" message to some
-other module but the latter has already shut down.  Such cases are
-generally non critical, but you may want to check other possible error
-messages.
-
-% MSGQ_COMMAND Running command %1 with arguments %2
-Debug message. The message queue received a command and it is running it.
-
-% MSGQ_COMMAND_UNKNOWN Unknown command '%1'
-The message queue received a command from other module, but it doesn't
-recognize it. This is probably either a coding error or inconsistency between
-the message queue version and version of the module.
-
-% MSGQ_CONFIG_DATA Received configuration update for the msgq: %1
-Debug message. The message queue received a configuration update, handling it.
-
-% MSGQ_EXITING exiting
-The msgq daemon is exiting.
-
-% MSGQ_HDR_DECODE_ERROR Error decoding header received from socket %1: %2
-The socket with mentioned file descriptor sent a packet. However, it was not
-possible to decode the routing header of the packet. The packet is ignored.
-This may be caused by a programmer error (one of the components sending invalid
-data) or possibly by incompatible version of msgq and the component (but that's
-unlikely, as the protocol is not changed often).
-
-% MSGQ_INVALID_CMD Received invalid command: %1
-An unknown command listed in the log has been received. It is ignored. This
-indicates either a programmer error (eg. a typo in the command name) or
-incompatible version of a module and message queue daemon.
-
-% MSGQ_LISTENER_FAILED Failed to initialize listener on socket file '%1': %2
-The message queue daemon tried to listen on a file socket (the path is in the
-message), but it failed. The error from the operating system is logged.
-
-% MSGQ_LISTENER_SETUP Starting to listen on socket file '%1'
-Debug message. The listener is trying to open a listening socket.
-
-% MSGQ_LISTENER_STARTED Successfully started to listen
-Debug message. The message queue successfully opened a listening socket and
-waits for incoming connections.
-
-% MSGQ_RECV_ERROR Error reading from socket %1: %2
-There was a low-level error when reading from a socket. The error is logged and
-the corresponding socket is dropped.  The errors include receiving
-broken or (non empty but) incomplete data.  In either case it usually suggests
-something unexpected happens within the BIND 10 system; it's probably
-better to restart the system, and if it continues it should be
-reported as a bug.  One known, probably non critical case is
-the "connection reset by peer" (or its variants) socket error appearing
-on shutdown.  It's known this happens when the remote client closes the
-connection as part of shutdown process.  Such cases are normally expected
-to be reported as receiving empty data (which we log it at the debug level
-as the MSGQ_CLOSE_ON_RECV message), but for some (yet) unknown reason
-it can also be reported as the system error.  At shutdown time it's expected
-that connections are closed, so it's probably safe to ignore these messages
-in such a case.  We still log them as an error as we've not figured out
-how exactly that can happen.  In future, we may make the shutdown process
-more robust so the msgq daemon can explicitly know when a client shuts down
-more reliably.  If and when it's implemented this error message won't appear
-on shutdown unless there's really something unexpected.
-
-% MSGQ_RECV_HDR Received header: %1
-Debug message. This message includes the whole routing header of a packet.
-
-% MSGQ_SELECT_ERROR Error while waiting for events: %1
-A low-level error happened when waiting for events, the error is logged. The
-reason for this varies, but it usually means the system is short on some
-resources.
-
-% MSGQ_SEND_ERROR Error while sending to socket %1: %2
-There was a low-level error when sending data to a socket. The error is logged
-and the corresponding socket is dropped.
-
-% MSGQ_SHUTDOWN Stopping Msgq
-Debug message. The message queue is shutting down.
-
-% MSGQ_SOCKET_REGISTERED Registered a socket descriptor %1 with lname %2
-Debug message. The msgq daemon accepted a session request on the
-shown descriptor of socket and assigned a unique identifier (lname)
-for the client on that socket.
-
-% MSGQ_SOCKET_TIMEOUT_ERROR Killing socket %1 because timeout exceeded (%2)
-Outgoing data was queued up on a socket connected to msgq, but the other
-side is not reading it. It could be deadlocked, or may not be monitoring
-it. Both cases are programming errors and should be corrected. The socket
-is closed on the msgq side.
-
-% MSGQ_SOCK_CLOSE Closing socket fd %1
-Debug message. Closing the mentioned socket.
-
-% MSGQ_START Msgq version %1 starting
-Debug message. The message queue is starting up.
-
-% MSGQ_START_FAIL Error during startup: %1
-There was an error during early startup of the daemon. More concrete error is
-in the log. The daemon terminates as a result.
-
-% MSGQ_SUBS_APPEND_TARGET Appending to existing target for subscription to group '%1' for instance '%2'
-Debug message. Creating a new subscription by appending it to already existing
-data structure.
-
-% MSGQ_SUBS_NEW_TARGET Creating new target for subscription to group '%1' for instance '%2'
-Debug message. Creating a new subscription. Also creating a new data structure
-to hold it.
diff --git a/src/bin/msgq/run_msgq.sh.in b/src/bin/msgq/run_msgq.sh.in
deleted file mode 100644 (file)
index 6b175d6..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-MYPATH_PATH=@abs_top_builddir@/src/bin/msgq
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python/isc/cc:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
-export PYTHONPATH
-
-# If necessary (rare cases), explicitly specify paths to dynamic libraries
-# required by loadable python modules.
-SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
-if test $SET_ENV_LIBRARY_PATH = yes; then
-    @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.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/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
-    export @ENV_LIBRARY_PATH@
-fi
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-# TODO: We need to do this feature based (ie. no general from_source)
-# But right now we need a second one because some spec files are
-# generated and hence end up under builddir
-B10_FROM_BUILD=@abs_top_builddir@
-export B10_FROM_BUILD
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-cd ${MYPATH_PATH}
-exec ${PYTHON_EXEC} -O b10-msgq "$@"
diff --git a/src/bin/msgq/tests/.gitignore b/src/bin/msgq/tests/.gitignore
deleted file mode 100644 (file)
index 70acfff..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/msgq_test
diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am
deleted file mode 100644 (file)
index 025b84d..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = msgq_test.py msgq_run_test.py
-EXTRA_DIST = $(PYTESTS)
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/msgq \
-       BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \
-       B10_FROM_SOURCE=$(abs_top_srcdir) \
-       B10_FROM_BUILD=$(abs_top_builddir) \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
diff --git a/src/bin/msgq/tests/msgq_run_test.py b/src/bin/msgq/tests/msgq_run_test.py
deleted file mode 100644 (file)
index 5b0c711..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-# Copyright (C) 2013  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.
-
-"""
-In this test file, we actually start msgq as a process and test it
-as a whole. It may be considered a system test instead of unit test,
-but apart from the terminology, we don't care much. We need to test
-the message queue works as expected, together with the libraries.
-
-In each test, we first start a timeout (because we do some waits
-for messages and if they wouldn't come, the test could block indefinitely).
-The timeout is long, because it is for the case the test fails.
-
-We then start the msgq and wait for the socket file to appear
-(that should indicate it is ready to receive connections). Then the
-actual test starts. After the test, we kill it and remove the test file.
-
-We also register signal handlers for many signals. Even in the case
-the test is interrupted or crashes, we should ensure the message queue
-itself is terminated.
-"""
-
-import unittest
-import os
-import signal
-import sys
-import subprocess
-import time
-
-import isc.log
-import isc.cc.session
-from isc.cc.proto_defs import *
-
-# Due to problems with too long path on build bots, we place the socket
-# into the top-level build directory. That is ugly, but works.
-SOCKET_PATH = os.path.abspath(os.environ['B10_FROM_BUILD'] + '/msgq.sock')
-MSGQ_PATH = os.environ['B10_FROM_BUILD'] + '/src/bin/msgq/run_msgq.sh'
-TIMEOUT = 15 # Some long time (seconds), for single test.
-
-class MsgqRunTest(unittest.TestCase):
-    def setUp(self):
-        """
-        As described above - check the socket file does not exist.
-        Then register signals and timeouts. Finally, launch msgq
-        and wait for it to start.
-        """
-        self.__msgq = None
-        self.__opened_connections = []
-        # A precondition check
-        self.assertFalse(os.path.exists(SOCKET_PATH))
-        signal.alarm(TIMEOUT)
-        self.__orig_signals = {}
-        # Register handlers for many signals. Most of them probably
-        # can't happen in python, but we register them anyway just to be
-        # safe.
-        for sig in [signal.SIGHUP, signal.SIGINT, signal.SIGQUIT,
-            signal.SIGILL, signal.SIGTRAP, signal.SIGABRT, signal.SIGBUS,
-            signal.SIGFPE, signal.SIGALRM, signal.SIGTERM]:
-            self.__orig_signals[sig] = signal.signal(sig, self.__signal)
-        # Start msgq
-        self.__msgq = subprocess.Popen([MSGQ_PATH, '-s', SOCKET_PATH],
-                                       close_fds=True)
-        # Some testing data
-        self.__no_recpt = {"result": [-1, "No such recipient"]}
-        # Wait for it to become ready (up to the alarm-set timeout)
-        connection = None
-        while not connection:
-            try:
-                # If the msgq is ready, this'll succeed. If not, it'll throw
-                # session error.
-                connection = isc.cc.session.Session(SOCKET_PATH)
-            except isc.cc.session.SessionError:
-                time.sleep(0.1) # Retry after a short time
-        # We have the connection now, that means it works. Close this
-        # connection, we won't use it. Each test gets enough new connections
-        # of its own.
-        connection.close()
-
-    def __message(self, data):
-        """
-        Provide some testing message. The data will be included in it, so
-        several different messages can be created.
-        """
-        return {"Message": "Text", "Data": data}
-
-    def tearDown(self):
-        """
-        Perform cleanup after the test.
-        """
-        self.__cleanup()
-
-    def __signal(self, signal, frame):
-        """
-        Called from a signal handler. We perform some cleanup, output
-        a complain and terminate with error.
-        """
-        self.__cleanup()
-        sys.stderr.write("Test terminating from signal " + str(signal) +
-                         " in " + str(frame) + "\n")
-        sys.exit(1)
-
-    def __cleanup(self):
-        """
-        Kill msgq (if running) and restore original signal handlers.
-        """
-        # Remove the socket (as we kill, msgq might not clean up)
-        for conn in self.__opened_connections:
-            conn.close()
-        self.__opened_connections = []
-        if self.__msgq:
-            self.__msgq.kill()
-            self.__msgq = None
-        if os.path.exists(SOCKET_PATH):
-            os.unlink(SOCKET_PATH)
-        for sig in self.__orig_signals:
-            signal.signal(sig, self.__orig_signals[sig])
-        # Cancel timeout (so someone else is not hit by it)
-        signal.alarm(0)
-
-    def __get_connection(self):
-        """
-        Create a connection to the daemon and make sure it is properly closed
-        at the end of the test.
-        """
-        connection = isc.cc.session.Session(SOCKET_PATH)
-        self.__opened_connections.append(connection)
-        return connection
-
-    def test_send_direct(self):
-        """
-        Connect twice to msgq, send a message from one to another using direct
-        l-name and see it comes.
-        """
-        # Create the connections
-        conn1 = self.__get_connection()
-        conn2 = self.__get_connection()
-        # Send the message
-        lname1 = conn1.lname
-        conn2.group_sendmsg(self.__message(1), "*", to=lname1)
-        # Receive the message and see it contains correct data
-        (msg, env) = conn1.group_recvmsg(nonblock=False)
-        self.assertEqual(self.__message(1), msg)
-        # We don't check there are no extra headers, just that none are missing
-        # or wrong.
-        self.assertEqual(lname1, env[CC_HEADER_TO])
-        self.assertEqual(conn2.lname, env[CC_HEADER_FROM])
-        self.assertEqual("*", env[CC_HEADER_GROUP])
-        self.assertEqual(CC_INSTANCE_WILDCARD, env[CC_HEADER_INSTANCE])
-        self.assertEqual(CC_COMMAND_SEND, env[CC_HEADER_TYPE])
-        self.assertFalse(env[CC_HEADER_WANT_ANSWER])
-
-    def __barrier(self, connections):
-        """
-        Make sure all previous commands on all supplied connections are
-        processed, by sending a ping and waiting for an answer.
-        """
-        for c in connections:
-            c.sendmsg({"type": "ping"})
-        for c in connections:
-            pong = c.recvmsg(nonblock=False)
-            self.assertEqual(({"type": "pong"}, None), pong)
-
-    def test_send_group(self):
-        """
-        Create several connections. First, try to send a message to a (empty)
-        group and see an error is bounced back. Then subscribe the others
-        to the group and send it again. Send to a different group and see it
-        bounced back. Unsubscribe and see it is bounced again.
-
-        Then the other connections answer (after unsubscribing, strange, but
-        legal). See both answers come.
-
-        Then, look there are no more waiting messages.
-        """
-        conn_a = self.__get_connection()
-        conn_b = []
-        for i in range(0, 10):
-            conn_b.append(self.__get_connection())
-        # Send a message to empty group and get an error answer
-        seq = conn_a.group_sendmsg(self.__message(1), "group",
-                                   want_answer=True)
-        (msg, env) = conn_a.group_recvmsg(nonblock=False, seq=seq)
-        self.assertEqual(self.__no_recpt, msg)
-        self.assertEqual(conn_a.lname, env[CC_HEADER_TO])
-        # Subscribe the two connections
-        for c in conn_b:
-            c.group_subscribe("group")
-        # The subscribe doesn't wait for answer, so make sure it is
-        # all processed before continuing.
-        self.__barrier(conn_b)
-        # Send a message to the group (this time not empty)
-        seq = conn_a.group_sendmsg(self.__message(2), "group",
-                                   want_answer=True)
-        envs = []
-        for c in conn_b:
-            (msg, env) = c.group_recvmsg(nonblock=False)
-            self.assertEqual(self.__message(2), msg)
-            self.assertEqual(conn_a.lname, env[CC_HEADER_FROM])
-            # The daemon does not mangle the headers. Is it OK?
-            self.assertEqual(CC_TO_WILDCARD, env[CC_HEADER_TO])
-            self.assertEqual("group", env[CC_HEADER_GROUP])
-            self.assertEqual(CC_INSTANCE_WILDCARD, env[CC_HEADER_INSTANCE])
-            self.assertEqual(CC_COMMAND_SEND, env[CC_HEADER_TYPE])
-            self.assertTrue(env[CC_HEADER_WANT_ANSWER])
-            envs.append(env)
-        # Send to non-existing group
-        seq_ne = conn_a.group_sendmsg(self.__message(3), "no-group",
-                                      want_answer=True)
-        (msg, env) = conn_a.group_recvmsg(nonblock=False, seq=seq_ne)
-        self.assertEqual(self.__no_recpt, msg)
-        self.assertEqual(conn_a.lname, env[CC_HEADER_TO])
-        # Unsubscribe the connections
-        for c in conn_b:
-            c.group_unsubscribe("group")
-        # Synchronize the unsubscriptions
-        self.__barrier(conn_b)
-        seq_ne = conn_a.group_sendmsg(self.__message(4), "group",
-                                      want_answer=True)
-        (msg, env) = conn_a.group_recvmsg(nonblock=False, seq=seq_ne)
-        self.assertEqual(self.__no_recpt, msg)
-        self.assertEqual(conn_a.lname, env[CC_HEADER_TO])
-        # Send answers for the original message that was delivered
-        lnames = set()
-        for (c, env) in zip(conn_b, envs):
-            c.group_reply(env, self.__message("Reply"))
-            lnames.add(c.lname)
-        # Check the both answers come
-        while lnames:
-            # While there are still connections we didn't get the answer from
-            # (the order is not guaranteed, therefore the juggling with set)
-            (msg, env) = conn_a.group_recvmsg(nonblock=False, seq=seq)
-            self.assertEqual(self.__message("Reply"), msg)
-            lname = env[CC_HEADER_FROM]
-            self.assertTrue(lname in lnames)
-            lnames.remove(lname)
-
-        # The barrier makes the msgq process everything we sent. As the
-        # processing is single-threaded in it, any stray message would have
-        # arrived before the barrier ends.
-        self.__barrier(conn_b)
-        self.__barrier([conn_a])
-        for c in conn_b:
-            self.assertEqual((None, None), c.group_recvmsg())
-        self.assertEqual((None, None), conn_a.group_recvmsg())
-
-    def test_conn_disconn(self):
-        """
-        Keep connecting and disconnecting, checking we can still send
-        and receive messages.
-        """
-        conn = self.__get_connection()
-        conn.group_subscribe("group")
-        for i in range(0, 50):
-            new = self.__get_connection()
-            new.group_subscribe("group")
-            self.__barrier([conn, new])
-            new.group_sendmsg(self.__message(i), "group")
-            (msg, env) = conn.group_recvmsg(nonblock=False)
-            self.assertEqual(self.__message(i), msg)
-            conn.close()
-            conn = new
-
-    def test_notifications(self):
-        """
-        Check that the MsgQ is actually sending notifications about events.
-        We create a socket, subscribe the socket itself and see it receives
-        it's own notification.
-
-        Testing all the places where notifications happen is task for the
-        common unit tests in msgq_test.py.
-
-        The test is here, because there might be some trouble with multiple
-        threads in msgq (see the note about locking on the module CC session
-        when sending message from one thread and listening for commands in the
-        other) which would be hard to test using pure unit tests. Testing
-        runnig whole msgq tests that implicitly.
-        """
-        conn = self.__get_connection()
-        # Activate the session, pretend to be the config manager.
-        conn.group_subscribe('ConfigManager')
-        # Answer request for logging config
-        (msg, env) = conn.group_recvmsg(nonblock=False)
-        self.assertEqual({'command': ['get_config',
-                                      {'module_name': 'Logging'}]},
-                         msg)
-        conn.group_reply(env, {'result': [0, {}]})
-        # It sends its spec.
-        (msg, env) = conn.group_recvmsg(nonblock=False)
-        self.assertEqual('module_spec', msg['command'][0])
-        conn.group_reply(env, {'result': [0]})
-        # It asks for its own config
-        (msg, env) = conn.group_recvmsg(nonblock=False)
-        self.assertEqual({'command': ['get_config',
-                                      {'module_name': 'Msgq'}]},
-                         msg)
-        conn.group_reply(env, {'result': [0, {}]})
-        # Synchronization - make sure the session is running before
-        # we continue, so we get the notification. Similar synchronisation
-        # as in b10-init, but we don't have full ccsession here, so we
-        # do so manually.
-        synchronised = False
-        attempts = 100
-        while not synchronised and attempts > 0:
-            time.sleep(0.1)
-            seq = conn.group_sendmsg({'command': ['Are you running?']},
-                                     'Msgq', want_answer=True)
-            msg = conn.group_recvmsg(nonblock=False, seq=seq)
-            synchronised = msg[0] != -1
-            attempts -= 1
-        self.assertTrue(synchronised)
-        # The actual test
-        conn.group_subscribe('notifications/cc_members')
-        (msg, env) = conn.group_recvmsg(nonblock=False)
-        self.assertEqual({'notification': ['subscribed', {
-            'client': conn.lname,
-            'group': 'notifications/cc_members'
-        }]}, msg)
-
-    def test_multiple_invocations(self):
-        """
-        Check to make sure that an attempt to start a second copy of the MsgQ
-        daemon fails.
-        """
-
-        self.assertTrue (os.path.exists(SOCKET_PATH))
-        self.__retcode = subprocess.call([MSGQ_PATH, '-s', SOCKET_PATH])
-        self.assertNotEqual(self.__retcode, 0)
-
-        # Verify that the socket still exists and works. We re-call
-        # test_send_direct as a means of testing that the existing
-        # daemon is still behaving correctly.
-        self.assertTrue (os.path.exists(SOCKET_PATH))
-        self.test_send_direct()
-
-if __name__ == '__main__':
-    isc.log.init("msgq-tests")
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
deleted file mode 100644 (file)
index 210246d..0000000
+++ /dev/null
@@ -1,1121 +0,0 @@
-# Copyright (C) 2010-2013  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.
-
-import msgq
-from msgq import SubscriptionManager, MsgQ
-
-import unittest
-import os
-import socket
-import select # needed only for #3014. can be removed once it's solved
-import signal
-import sys
-import time
-import errno
-import threading
-import isc.cc
-import collections
-import isc.log
-import struct
-import json
-
-#
-# Currently only the subscription part and some sending is implemented...
-# I'd have to mock out a socket, which, while not impossible, is not trivial.
-#
-
-class TestSubscriptionManager(unittest.TestCase):
-    def setUp(self):
-        self.__cfgmgr_ready_called = 0
-        self.sm = SubscriptionManager(self.cfgmgr_ready)
-
-    def cfgmgr_ready(self):
-        # Called one more time
-        self.__cfgmgr_ready_called += 1
-
-    def test_subscription_add_delete_manager(self):
-        self.sm.subscribe("a", "*", 'sock1')
-        self.assertEqual(self.sm.find_sub("a", "*"), [ 'sock1' ])
-
-    def test_subscription_add_delete_other(self):
-        self.sm.subscribe("a", "*", 'sock1')
-        self.sm.unsubscribe("a", "*", 'sock2')
-        self.assertEqual(self.sm.find_sub("a", "*"), [ 'sock1' ])
-
-    def test_subscription_add_several_sockets(self):
-        socks = [ 's1', 's2', 's3', 's4', 's5' ]
-        for s in socks:
-            self.sm.subscribe("a", "*", s)
-        self.assertEqual(self.sm.find_sub("a", "*"), socks)
-
-    def test_unsubscribe(self):
-        socks = [ 's1', 's2', 's3', 's4', 's5' ]
-        for s in socks:
-            self.sm.subscribe("a", "*", s)
-        self.assertTrue(self.sm.unsubscribe("a", "*", 's3'))
-        # Unsubscribe from group it is not in
-        self.assertFalse(self.sm.unsubscribe("a", "*", 's42'))
-        self.assertEqual(self.sm.find_sub("a", "*"),
-                         [ 's1', 's2', 's4', 's5' ])
-
-    def test_unsubscribe_all(self):
-        self.sm.subscribe('g1', 'i1', 's1')
-        self.sm.subscribe('g1', 'i1', 's2')
-        self.sm.subscribe('g1', 'i2', 's1')
-        self.sm.subscribe('g1', 'i2', 's2')
-        self.sm.subscribe('g2', 'i1', 's1')
-        self.sm.subscribe('g2', 'i1', 's2')
-        self.sm.subscribe('g2', 'i2', 's1')
-        self.sm.subscribe('g2', 'i2', 's2')
-        self.assertEqual(set([('g1', 'i1'), ('g1', 'i2'), ('g2', 'i1'),
-                              ('g2', 'i2')]),
-                         set(self.sm.unsubscribe_all('s1')))
-        self.assertEqual(self.sm.find_sub("g1", "i1"), [ 's2' ])
-        self.assertEqual(self.sm.find_sub("g1", "i2"), [ 's2' ])
-        self.assertEqual(self.sm.find_sub("g2", "i1"), [ 's2' ])
-        self.assertEqual(self.sm.find_sub("g2", "i2"), [ 's2' ])
-
-    def test_find(self):
-        self.sm.subscribe('g1', 'i1', 's1')
-        self.sm.subscribe('g1', '*', 's2')
-        self.assertEqual(set(self.sm.find("g1", "i1")), set([ 's1', 's2' ]))
-
-    def test_find_sub(self):
-        self.sm.subscribe('g1', 'i1', 's1')
-        self.sm.subscribe('g1', '*', 's2')
-        self.assertEqual(self.sm.find_sub("g1", "i1"), [ 's1' ])
-
-    def test_open_socket_parameter(self):
-        self.assertFalse(os.path.exists("./my_socket_file"))
-        msgq = MsgQ("./my_socket_file");
-        msgq.setup()
-        self.assertTrue(os.path.exists("./my_socket_file"))
-        msgq.shutdown();
-        self.assertFalse(os.path.exists("./my_socket_file"))
-
-    def test_open_socket_environment_variable(self):
-        self.assertFalse(os.path.exists("my_socket_file"))
-        os.environ["BIND10_MSGQ_SOCKET_FILE"] = "./my_socket_file"
-        msgq = MsgQ();
-        msgq.setup()
-        self.assertTrue(os.path.exists("./my_socket_file"))
-        msgq.shutdown();
-        self.assertFalse(os.path.exists("./my_socket_file"))
-
-    def test_open_socket_default(self):
-        env_var = None
-        orig_socket_file = None
-        if "BIND10_MSGQ_SOCKET_FILE" in os.environ:
-            env_var = os.environ["BIND10_MSGQ_SOCKET_FILE"]
-            del os.environ["BIND10_MSGQ_SOCKET_FILE"]
-        # temporarily replace the class "default" not to be disrupted by
-        # any running BIND 10 instance.
-        if "BIND10_TEST_SOCKET_FILE" in os.environ:
-            MsgQ.SOCKET_FILE = os.environ["BIND10_TEST_SOCKET_FILE"]
-        socket_file = MsgQ.SOCKET_FILE
-        self.assertFalse(os.path.exists(socket_file))
-        msgq = MsgQ();
-        try:
-            msgq.setup()
-            self.assertTrue(os.path.exists(socket_file))
-            msgq.shutdown()
-            self.assertFalse(os.path.exists(socket_file))
-        except socket.error:
-            # ok, the install path doesn't exist at all,
-            # so we can't check any further
-            pass
-        if env_var is not None:
-            os.environ["BIND10_MSGQ_SOCKET_FILE"] = env_var
-        if orig_socket_file is not None:
-            MsgQ.SOCKET_FILE = orig_socket_file
-
-    def test_open_socket_bad(self):
-        msgq = MsgQ("/does/not/exist")
-        self.assertRaises(socket.error, msgq.setup)
-        # But we can clean up after that.
-        msgq.shutdown()
-
-    def test_subscribe_cfgmgr(self):
-        """Test special handling of the config manager. Once it subscribes,
-           the message queue needs to connect and read the config. But not
-           before and only once.
-        """
-        self.assertEqual(0, self.__cfgmgr_ready_called)
-        # Not called when something else subscribes
-        self.sm.subscribe('SomethingElse', '*', 's1')
-        self.assertEqual(0, self.__cfgmgr_ready_called)
-        # Called whenever the config manager subscribes
-        self.sm.subscribe('ConfigManager', '*', 's2')
-        self.assertEqual(1, self.__cfgmgr_ready_called)
-        # But not called again when it subscribes again (should not
-        # happen in practice, but we make sure anyway)
-        self.sm.subscribe('ConfigManager', '*', 's3')
-        self.assertEqual(1, self.__cfgmgr_ready_called)
-
-class MsgQTest(unittest.TestCase):
-    """
-    Tests for the behaviour of MsgQ. This is for the core of MsgQ, other
-    subsystems are in separate test fixtures.
-    """
-    def setUp(self):
-        self.__msgq = MsgQ()
-
-    def parse_msg(self, msg):
-        """
-        Parse a binary representation of message to the routing header and the
-        data payload. It assumes the message is correctly encoded and the
-        payload is not omitted. It'd probably throw in other cases, but we
-        don't use it in such situations in this test.
-        """
-        (length, header_len) = struct.unpack('>IH', msg[:6])
-        header = json.loads(msg[6:6 + header_len].decode('utf-8'))
-        data = json.loads(msg[6 + header_len:].decode('utf-8'))
-        return (header, data)
-
-    def test_unknown_command(self):
-        """
-        Test the command handler returns error when the command is unknown.
-        """
-        # Fake we are running, to disable test workarounds
-        self.__msgq.running = True
-        self.assertEqual({'result': [1, "unknown command: unknown"]},
-                         self.__msgq.command_handler('unknown', {}))
-
-    def test_get_members(self):
-        """
-        Test getting members of a group or of all connected clients.
-        """
-        # Push two dummy "clients" into msgq (the ugly way, by directly
-        # tweaking relevant data structures).
-        class Sock:
-            def __init__(self, fileno):
-                self.fileno = lambda: fileno
-        self.__msgq.lnames['first'] = Sock(1)
-        self.__msgq.lnames['second'] = Sock(2)
-        self.__msgq.fd_to_lname[1] = 'first'
-        self.__msgq.fd_to_lname[2] = 'second'
-        # Subscribe them to some groups
-        self.__msgq.process_command_subscribe(self.__msgq.lnames['first'],
-                                              {'group': 'G1', 'instance': '*'},
-                                              None)
-        self.__msgq.process_command_subscribe(self.__msgq.lnames['second'],
-                                              {'group': 'G1', 'instance': '*'},
-                                              None)
-        self.__msgq.process_command_subscribe(self.__msgq.lnames['second'],
-                                              {'group': 'G2', 'instance': '*'},
-                                              None)
-        # Now query content of some groups through the command handler.
-        self.__msgq.running = True # Enable the command handler
-        def check_both(result):
-            """
-            Check the result is successful one and it contains both lnames (in
-            any order).
-            """
-            array = result['result'][1]
-            self.assertEqual(set(['first', 'second']), set(array))
-            self.assertEqual({'result': [0, array]}, result)
-            # Make sure the result can be encoded as JSON
-            # (there seems to be types that look like a list but JSON choks
-            # on them)
-            json.dumps(result)
-        # Members of the G1 and G2
-        self.assertEqual({'result': [0, ['second']]},
-                         self.__msgq.command_handler('members',
-                                                     {'group': 'G2'}))
-        check_both(self.__msgq.command_handler('members', {'group': 'G1'}))
-        # We pretend that all the possible groups exist, just that most
-        # of them are empty. So requesting for Empty is request for an empty
-        # group and should not fail.
-        self.assertEqual({'result': [0, []]},
-                         self.__msgq.command_handler('members',
-                                                     {'group': 'Empty'}))
-        # Without the name of the group, we just get all the clients.
-        check_both(self.__msgq.command_handler('members', {}))
-        # Omitting the parameters completely in such case is OK
-        check_both(self.__msgq.command_handler('members', None))
-
-    def notifications_setup(self):
-        """
-        Common setup of some notifications tests. Mock several things.
-        """
-        # Mock the method to send notifications (we don't really want
-        # to send them now, just see they'd be sent).
-        # Mock the poller, as we don't need it at all (and we don't have
-        # real socket to give it now).
-        notifications = []
-        def send_notification(event, params):
-            notifications.append((event, params))
-        class FakePoller:
-            def register(self, socket, mode):
-                pass
-            def unregister(self, sock):
-                pass
-        self.__msgq.members_notify = send_notification
-        self.__msgq.poller = FakePoller()
-
-        # Create a socket
-        class Sock:
-            def __init__(self, fileno):
-                self.fileno = lambda: fileno
-            def close(self):
-                pass
-        sock = Sock(1)
-        return notifications, sock
-
-    def test_notifies(self):
-        """
-        Test the message queue sends notifications about connecting,
-        disconnecting and subscription changes.
-        """
-        notifications, sock = self.notifications_setup()
-
-        # We should notify about new cliend when we register it
-        self.__msgq.register_socket(sock)
-        lname = self.__msgq.fd_to_lname[1] # Steal the lname
-        self.assertEqual([('connected', {'client': lname})], notifications)
-        del notifications[:]
-
-        # A notification should happen for a subscription to a group
-        self.__msgq.process_command_subscribe(sock, {'group': 'G',
-                                                     'instance': '*'},
-                                              None)
-        self.assertEqual([('subscribed', {'client': lname, 'group': 'G'})],
-                         notifications)
-        del notifications[:]
-
-        # As well for unsubscription
-        self.__msgq.process_command_unsubscribe(sock, {'group': 'G',
-                                                       'instance': '*'},
-                                                None)
-        self.assertEqual([('unsubscribed', {'client': lname, 'group': 'G'})],
-                         notifications)
-        del notifications[:]
-
-        # Unsubscription from a group it isn't subscribed to
-        self.__msgq.process_command_unsubscribe(sock, {'group': 'H',
-                                                       'instance': '*'},
-                                                None)
-        self.assertEqual([], notifications)
-
-        # And, finally, for removal of client
-        self.__msgq.kill_socket(sock.fileno(), sock)
-        self.assertEqual([('disconnected', {'client': lname})], notifications)
-
-    def test_notifies_implicit_kill(self):
-        """
-        Test that the unsubscription notifications are sent before the socket
-        is dropped, even in case it does not unsubscribe explicitly.
-        """
-        notifications, sock = self.notifications_setup()
-
-        # Register and subscribe. Notifications for these are in above test.
-        self.__msgq.register_socket(sock)
-        lname = self.__msgq.fd_to_lname[1] # Steal the lname
-        self.__msgq.process_command_subscribe(sock, {'group': 'G',
-                                                     'instance': '*'},
-                                              None)
-        del notifications[:]
-
-        self.__msgq.kill_socket(sock.fileno(), sock)
-        # Now, the notification for unsubscribe should be first, second for
-        # the disconnection.
-        self.assertEqual([('unsubscribed', {'client': lname, 'group': 'G'}),
-                          ('disconnected', {'client': lname})
-                         ], notifications)
-
-    def test_undeliverable_errors(self):
-        """
-        Send several packets through the MsgQ and check it generates
-        undeliverable notifications under the correct circumstances.
-
-        The test is not exhaustive as it doesn't test all combination
-        of existence of the recipient, addressing schemes, want_answer
-        header and the reply header. It is not needed, these should
-        be mostly independent. That means, for example, if the message
-        is a reply and there's no recipient to send it to, the error
-        would not be generated no matter if we addressed the recipient
-        by lname or group. If we included everything, the test would
-        have too many scenarios with little benefit.
-        """
-        self.__sent_messages = []
-        def fake_send_prepared_msg(socket, msg):
-            self.__sent_messages.append((socket, msg))
-            return True
-        self.__msgq.send_prepared_msg = fake_send_prepared_msg
-        # These would be real sockets in the MsgQ, but we pass them as
-        # parameters only, so we don't need them to be. We use simple
-        # integers to tell one from another.
-        sender = 1
-        recipient = 2
-        another_recipiet = 3
-        # The routing headers and data to test with.
-        routing = {
-            'to': '*',
-            'from': 'sender',
-            'group': 'group',
-            'instance': '*',
-            'seq': 42
-        }
-        data = {
-            "data": "Just some data"
-        }
-
-        # Some common checking patterns
-        def check_error():
-            self.assertEqual(1, len(self.__sent_messages))
-            self.assertEqual(1, self.__sent_messages[0][0])
-            self.assertEqual(({
-                                  'group': 'group',
-                                  'instance': '*',
-                                  'reply': 42,
-                                  'seq': 42,
-                                  'from': 'msgq',
-                                  'to': 'sender',
-                                  'want_answer': True
-                              }, {'result': [-1, "No such recipient"]}),
-                              self.parse_msg(self.__sent_messages[0][1]))
-            self.__sent_messages = []
-
-        def check_no_message():
-            self.assertEqual([], self.__sent_messages)
-
-        def check_delivered(rcpt_socket=recipient):
-            self.assertEqual(1, len(self.__sent_messages))
-            self.assertEqual(rcpt_socket, self.__sent_messages[0][0])
-            self.assertEqual((routing, data),
-                             self.parse_msg(self.__sent_messages[0][1]))
-            self.__sent_messages = []
-
-        # Send the message. No recipient, but errors are not requested,
-        # so none is generated.
-        self.__msgq.process_command_send(sender, routing, data)
-        check_no_message()
-
-        # It should act the same if we explicitly say we do not want replies.
-        routing["want_answer"] = False
-        self.__msgq.process_command_send(sender, routing, data)
-        check_no_message()
-
-        # Ask for errors if it can't be delivered.
-        routing["want_answer"] = True
-        self.__msgq.process_command_send(sender, routing, data)
-        check_error()
-
-        # If the message is a reply itself, we never generate the errors
-        routing["reply"] = 3
-        self.__msgq.process_command_send(sender, routing, data)
-        check_no_message()
-
-        # If there are recipients (but no "reply" header), the error should not
-        # be sent and the message should get delivered.
-        del routing["reply"]
-        self.__msgq.subs.find = lambda group, instance: [recipient]
-        self.__msgq.process_command_send(sender, routing, data)
-        check_delivered()
-
-        # When we send a direct message and the recipient is not there, we get
-        # the error too
-        routing["to"] = "lname"
-        self.__msgq.process_command_send(sender, routing, data)
-        check_error()
-
-        # But when the recipient is there, it is delivered and no error is
-        # generated.
-        self.__msgq.lnames["lname"] = recipient
-        self.__msgq.process_command_send(sender, routing, data)
-        check_delivered()
-
-        # If an attempt to send fails, consider it no recipient.
-        def fail_send_prepared_msg(socket, msg):
-            '''
-            Pretend sending a message failed. After one call, return to the
-            usual mock, so the errors or other messages can be sent.
-            '''
-            self.__msgq.send_prepared_msg = fake_send_prepared_msg
-            return False
-
-        self.__msgq.send_prepared_msg = fail_send_prepared_msg
-        self.__msgq.process_command_send(sender, routing, data)
-        check_error()
-
-        # But if there are more recipients and only one fails, it should
-        # be delivered to the other and not considered an error
-        self.__msgq.send_prepared_msg = fail_send_prepared_msg
-        routing["to"] = '*'
-        self.__msgq.subs.find = lambda group, instance: [recipient,
-                                                         another_recipiet]
-        self.__msgq.process_command_send(sender, routing, data)
-        check_delivered(rcpt_socket=another_recipiet)
-
-class DummySocket:
-    """
-    Dummy socket class.
-    This one does nothing at all, but some calls are used.
-    It is mainly intended to override the listen socket for msgq, which
-    we do not need in these tests.
-    """
-    def fileno():
-        return -1
-
-    def close():
-        pass
-
-class BadSocket:
-    """
-    Special socket wrapper class. Once given a socket in its constructor,
-    it completely behaves like that socket, except that its send() call
-    will only actually send one byte per call, and optionally raise a given
-    exception at a given time.
-    """
-    def __init__(self, real_socket, raise_on_send=0, send_exception=None):
-        """
-        Parameters:
-        real_socket: The actual socket to wrap
-        raise_on_send: integer. If higher than 0, and send_exception is
-                       not None, send_exception will be raised on the
-                       'raise_on_send'th call to send().
-        send_exception: if not None, this exception will be raised
-                        (if raise_on_send is not 0)
-        """
-        self.socket = real_socket
-        self.send_count = 0
-        self.raise_on_send = raise_on_send
-        self.send_exception = send_exception
-
-    # completely wrap all calls and member access
-    # (except explicitly overridden ones)
-    def __getattr__(self, name, *args):
-        attr = getattr(self.socket, name)
-        if isinstance(attr, collections.Callable):
-            def callable_attr(*args):
-                return attr.__call__(*args)
-            return callable_attr
-        else:
-            return attr
-
-    def send(self, data):
-        self.send_count += 1
-        if self.send_exception is not None and\
-           self.send_count == self.raise_on_send:
-            raise self.send_exception
-
-        if len(data) > 0:
-            return self.socket.send(data[:1])
-        else:
-            return 0
-
-class MsgQThread(threading.Thread):
-    """
-    Very simple thread class that runs msgq.run() when started,
-    and stores the exception that msgq.run() raises, if any.
-    """
-    def __init__(self, msgq):
-        threading.Thread.__init__(self)
-        self.msgq_ = msgq
-        self.caught_exception = None
-        self.lock = threading.Lock()
-
-    def run(self):
-        try:
-            self.msgq_.run()
-        except Exception as exc:
-            # Store the exception to make the test fail if necessary
-            self.caught_exception = exc
-
-    def stop(self):
-        self.msgq_.stop()
-
-class SendNonblock(unittest.TestCase):
-    """
-    Tests that the whole thing will not get blocked if someone does not read.
-    """
-
-    def terminate_check(self, task, timeout=30):
-        """
-        Runs task in separate process (task is a function) and checks
-        it terminates sooner than timeout.
-        """
-        task_pid = os.fork()
-        if task_pid == 0:
-            # Kill the forked process after timeout by SIGALRM
-            signal.alarm(timeout)
-            # Run the task
-            # If an exception happens or we run out of time, we terminate
-            # with non-zero
-            task()
-            # If we got here, then everything worked well and in time
-            # In that case, we terminate successfully
-            os._exit(0)        # needs exit code
-        else:
-            (pid, status) = os.waitpid(task_pid, 0)
-            self.assertEqual(0, status,
-                "The task did not complete successfully in time")
-
-    def get_msgq_with_sockets(self):
-        '''
-        Create a message queue and prepare it for use with a socket pair.
-        The write end is put into the message queue, so we can check it.
-        It returns (msgq, read_end, write_end). It is expected the sockets
-        are closed by the caller afterwards.
-
-        Also check the sockets are registered correctly (eg. internal data
-        structures are there for them).
-        '''
-        msgq = MsgQ()
-        # We do only partial setup, so we don't create the listening socket
-        (read, write) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        msgq.register_socket(write)
-        self.assertEqual(1, len(msgq.lnames))
-        self.assertEqual(write, msgq.lnames[msgq.fd_to_lname[write.fileno()]])
-        return (msgq, read, write)
-
-    def infinite_sender(self, sender):
-        """
-        Sends data until an exception happens. socket.error is caught,
-        as it means the socket got closed. Sender is called to actually
-        send the data.
-        """
-        (msgq, read, write) = self.get_msgq_with_sockets()
-        # Keep sending while it is not closed by the msgq
-        try:
-            while True:
-                sender(msgq, write)
-        except socket.error:
-            pass
-
-        # Explicitly close temporary socket pair as the Python
-        # interpreter expects it.  It may not be 100% exception safe,
-        # but since this is only for tests we prefer brevity.
-        # Actually, the write end is often closed by the sender.
-        if write.fileno() != -1:
-            # Some of the senders passed here kill the socket internally.
-            # So kill it only if not yet done so. If the socket is closed,
-            # it gets -1 as fileno().
-            msgq.kill_socket(write.fileno(), write)
-        self.assertFalse(msgq.lnames)
-        self.assertFalse(msgq.fd_to_lname)
-        read.close()
-
-    def test_infinite_sendmsg(self):
-        """
-        Tries sending messages (and not reading them) until it either times
-        out (in blocking call, wrong) or closes it (correct).
-        """
-        data = "data"
-        for i in range(1, 10):
-            data += data
-        self.terminate_check(lambda: self.infinite_sender(
-            lambda msgq, socket: msgq.sendmsg(socket, {}, {"message" : data})))
-
-    def test_infinite_sendprepared(self):
-        """
-        Tries sending data (and not reading them) until it either times
-        out (in blocking call, wrong) or closes it (correct).
-        """
-        data = b"data"
-        for i in range(1, 10):
-            data += data
-        self.terminate_check(lambda: self.infinite_sender(
-            lambda msgq, socket: msgq.send_prepared_msg(socket, data)))
-
-    def test_sendprepared_success(self):
-        '''
-        Test the send_prepared_msg returns success when queueing messages.
-        It does so on the first attempt (when it actually tries to send
-        something to the socket) and on any attempt that follows and the
-        buffer is already full.
-        '''
-        (msgq, read, write) = self.get_msgq_with_sockets()
-        # Now keep sending until we fill in something into the internal
-        # buffer.
-        while not write.fileno() in msgq.sendbuffs:
-            self.assertTrue(msgq.send_prepared_msg(write, b'data'))
-        read.close()
-        write.close()
-
-    def test_sendprepared_epipe(self):
-        '''
-        Test the send_prepared_msg returns false when we try to queue a
-        message and the other side is not there any more. It should be done
-        with EPIPE, so not a fatal error.
-        '''
-        (msgq, read, write) = self.get_msgq_with_sockets()
-        # Close one end. It should make a EPIPE on the other.
-        read.close()
-        # Now it should soft-fail
-        self.assertFalse(msgq.send_prepared_msg(write, b'data'))
-        write.close()
-
-    def send_many(self, data):
-        """
-        Tries that sending a command many times and getting an answer works.
-        """
-        msgq = MsgQ()
-        # msgq.run needs to compare with the listen_socket, so we provide
-        # a replacement
-        msgq.listen_socket = DummySocket
-        (queue, out) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        def run():
-            length = len(data)
-            queue_pid = os.fork()
-            if queue_pid == 0:
-                signal.alarm(120)
-                msgq.setup_signalsock()
-                msgq.register_socket(queue)
-                msgq.run()
-                msgq.cleanup_signalsock()
-            else:
-                try:
-                    def killall(signum, frame):
-                        os.kill(queue_pid, signal.SIGTERM)
-                        os._exit(1)
-                    signal.signal(signal.SIGALRM, killall)
-                    msg = msgq.preparemsg({"type" : "ping"}, data)
-                    now = time.clock()
-                    while time.clock() - now < 0.2:
-                        out.sendall(msg)
-                        # Check the answer
-                        (routing, received) = msgq.read_packet(out.fileno(),
-                            out)
-                        self.assertEqual({"type" : "pong"},
-                            isc.cc.message.from_wire(routing))
-                        self.assertEqual(data, received)
-                finally:
-                    os.kill(queue_pid, signal.SIGTERM)
-        self.terminate_check(run)
-
-        # Explicitly close temporary socket pair as the Python
-        # interpreter expects it.  It may not be 100% exception safe,
-        # but since this is only for tests we prefer brevity.
-        queue.close()
-        out.close()
-
-    def test_small_sends(self):
-        """
-        Tests sending small data many times.
-        """
-        self.send_many(b"data")
-
-    def test_large_sends(self):
-        """
-        Tests sending large data many times.
-        """
-        data = b"data"
-        for i in range(1, 20):
-            data = data + data
-        self.send_many(data)
-
-    def do_send(self, write, read, control_write, control_read,
-                expect_arrive=True, expect_send_exception=None):
-        """
-        Makes a msgq object that is talking to itself,
-        run it in a separate thread so we can use and
-        test run().
-        It is given two sets of connected sockets; write/read, and
-        control_write/control_read. The former may be throwing errors
-        and mangle data to test msgq. The second is mainly used to
-        send msgq the stop command.
-        (Note that the terms 'read' and 'write' are from the msgq
-        point of view, so the test itself writes to 'control_read')
-        Parameters:
-        write: a socket that is used to send the data to
-        read: a socket that is used to read the data from
-        control_write: a second socket for communication with msgq
-        control_read: a second socket for communication with msgq
-        expect_arrive: if True, the read socket is read from, and the data
-                       that is read is expected to be the same as the data
-                       that has been sent to the write socket.
-        expect_send_exception: if not None, this is the exception that is
-                               expected to be raised by msgq
-        """
-
-        # Some message and envelope data to send and check
-        env = b'{"env": "foo"}'
-        msg = b'{"msg": "bar"}'
-
-        msgq = MsgQ()
-        # Don't need a listen_socket
-        msgq.listen_socket = DummySocket
-        msgq.setup_signalsock()
-        msgq.register_socket(write)
-        msgq.register_socket(control_write)
-        # Queue the message for sending
-        msgq.sendmsg(write, env, msg)
-
-        # Run it in a thread
-        msgq_thread = MsgQThread(msgq)
-        # If we're done, just kill it
-        msgq_thread.start()
-
-        if expect_arrive:
-            (recv_env, recv_msg) = msgq.read_packet(read.fileno(),
-                read)
-            self.assertEqual(env, recv_env)
-            self.assertEqual(msg, recv_msg)
-
-        # Tell msgq to stop
-        msg = msgq.preparemsg({"type" : "stop"})
-        control_read.sendall(msg)
-
-        # Wait for thread to stop if it hasn't already.
-        # Put in a (long) timeout; the thread *should* stop, but if it
-        # does not, we don't want the test to hang forever
-        msgq_thread.join(60)
-        # Fail the test if it didn't stop
-        self.assertFalse(msgq_thread.isAlive(), "Thread did not stop")
-
-        # Clean up some internals of msgq (usually called as part of
-        # shutdown, but we skip that one here)
-        msgq.cleanup_signalsock()
-
-        # Check the exception from the thread, if any
-        # First, if we didn't expect it; reraise it (to make test fail and
-        # show the stacktrace for debugging)
-        if expect_send_exception is None:
-            if msgq_thread.caught_exception is not None:
-                raise msgq_thread.caught_exception
-        else:
-            # If we *did* expect it, fail it there was none
-            self.assertIsNotNone(msgq_thread.caught_exception)
-
-    def do_send_with_send_error(self, raise_on_send, send_exception,
-                                expect_answer=True,
-                                expect_send_exception=None):
-        """
-        Sets up two connected sockets, wraps the sender socket into a BadSocket
-        class, then performs a do_send() test.
-        Parameters:
-        raise_on_send: the byte at which send_exception should be raised
-                       (see BadSocket)
-        send_exception: the exception to raise (see BadSocket)
-        expect_answer: whether the send is expected to complete (and hence
-                       the read socket should get the message)
-        expect_send_exception: the exception msgq is expected to raise when
-                               send_exception is raised by BadSocket.
-        """
-        (write, read) = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
-        (control_write, control_read) = socket.socketpair(socket.AF_UNIX,
-                                                          socket.SOCK_STREAM)
-        badwrite = BadSocket(write, raise_on_send, send_exception)
-        self.do_send(badwrite, read, control_write, control_read,
-                     expect_answer, expect_send_exception)
-        write.close()
-        read.close()
-        control_write.close()
-        control_read.close()
-
-    def test_send_raise_recoverable(self):
-        """
-        Test whether msgq survices a recoverable socket errors when sending.
-        Two tests are done: one where the error is raised on the 3rd octet,
-                            and one on the 23rd.
-        """
-        for err in [ errno.EAGAIN, errno.EWOULDBLOCK, errno.EINTR ]:
-            sockerr = socket.error(err, 'Socket error')
-            self.do_send_with_send_error(3, sockerr)
-            self.do_send_with_send_error(23, sockerr)
-
-    def test_send_raise_nonrecoverable(self):
-        """
-        Test whether msgq survives socket errors that are nonrecoverable
-        (for said socket that is, i.e. EPIPE etc).
-        Two tests are done: one where the error is raised on the 3rd octet,
-                            and one on the 23rd.
-        """
-        for err in [ errno.EPIPE, errno.ENOBUFS, errno.ECONNRESET ]:
-            sockerr = socket.error(err, 'Socket error')
-            self.do_send_with_send_error(3, sockerr, False)
-            self.do_send_with_send_error(23, sockerr, False)
-
-    def otest_send_raise_crash(self):
-        """
-        Test whether msgq does NOT survive on a general exception.
-        Note, perhaps it should; but we'd have to first discuss and decide
-        how it should recover (i.e. drop the socket and consider the client
-        dead?
-        It may be a coding problem in msgq itself, and we certainly don't
-        want to ignore those.
-        """
-        sockerr = Exception("just some general exception")
-        self.do_send_with_send_error(3, sockerr, False, sockerr)
-        self.do_send_with_send_error(23, sockerr, False, sockerr)
-
-class ThreadTests(unittest.TestCase):
-    """Test various things around thread synchronization."""
-
-    def setUp(self):
-        self.__msgq = MsgQ()
-        self.__abort_wait = False
-        self.__result = None
-        self.__notify_thread = threading.Thread(target=self.__notify)
-        self.__wait_thread = threading.Thread(target=self.__wait)
-        # Make sure the threads are killed if left behind by the test.
-        self.__notify_thread.daemon = True
-        self.__wait_thread.daemon = True
-
-    def __notify(self):
-        """Call the cfgmgr_ready."""
-        if self.__abort_wait:
-            self.__msgq.cfgmgr_ready(False)
-        else:
-            self.__msgq.cfgmgr_ready()
-
-    def __wait(self):
-        """Wait for config manager and store the result."""
-        self.__result = self.__msgq.wait_cfgmgr()
-
-    def test_wait_cfgmgr(self):
-        """One thread signals the config manager subscribed, the other
-           waits for it. We then check it terminated correctly.
-        """
-        self.__notify_thread.start()
-        self.__wait_thread.start()
-        # Timeout to ensure the test terminates even on failure
-        self.__wait_thread.join(60)
-        self.assertTrue(self.__result)
-
-    def test_wait_cfgmgr_2(self):
-        """Same as test_wait_cfgmgr, but starting the threads in reverse order
-           (the result should be the same).
-        """
-        self.__wait_thread.start()
-        self.__notify_thread.start()
-        # Timeout to ensure the test terminates even on failure
-        self.__wait_thread.join(60)
-        self.assertTrue(self.__result)
-
-    def test_wait_abort(self):
-        """Similar to test_wait_cfgmgr, but the config manager is never
-           subscribed and it is aborted.
-        """
-        self.__abort_wait = True
-        self.__wait_thread.start()
-        self.__notify_thread.start()
-        # Timeout to ensure the test terminates even on failure
-        self.__wait_thread.join(60)
-        self.assertIsNotNone(self.__result)
-        self.assertFalse(self.__result)
-
-    def __check_ready_and_abort(self):
-        """Check that when we first say the config manager is ready and then
-           try to abort, it uses the first result.
-        """
-        self.__msgq.cfgmgr_ready()
-        self.__msgq.cfgmgr_ready(False)
-        self.__result = self.__msgq.wait_cfgmgr()
-
-    def test_ready_and_abort(self):
-        """Perform the __check_ready_and_abort test, but in a separate thread,
-           so in case something goes wrong with the synchronisation and it
-           deadlocks, the test will terminate anyway.
-        """
-        test_thread = threading.Thread(target=self.__check_ready_and_abort)
-        test_thread.daemon = True
-        test_thread.start()
-        test_thread.join(60)
-        self.assertTrue(self.__result)
-
-class SocketTests(unittest.TestCase):
-    '''Test cases for micro behaviors related to socket operations.
-
-    Some cases are covered as part of other tests, but in this fixture
-    we check more details of specific method related to socket operation,
-    with the help of mock classes to avoid expensive overhead.
-
-    '''
-    class MockSocket():
-        '''A mock socket used instead of standard socket objects.'''
-        def __init__(self):
-            self.ex_on_send = None # raised from send() if not None
-            self.recv_result = b'test' # dummy data or exception
-            self.blockings = [] # history of setblocking() params
-        def setblocking(self, on):
-            self.blockings.append(on)
-        def send(self, data):
-            if self.ex_on_send is not None:
-                raise self.ex_on_send
-            return 10           # arbitrary choice
-        def recv(self, len):
-            if isinstance(self.recv_result, Exception):
-                raise self.recv_result
-            ret = self.recv_result
-            self.recv_result = b'' # if called again, return empty data
-            return ret
-        def fileno(self):
-            return 42           # arbitrary choice
-
-    class LoggerWrapper():
-        '''A simple wrapper of logger to inspect log messages.'''
-        def __init__(self, logger):
-            self.error_called = 0
-            self.warn_called = 0
-            self.debug_called = 0
-            self.orig_logger = logger
-        def error(self, *args):
-            self.error_called += 1
-            self.orig_logger.error(*args)
-        def warn(self, *args):
-            self.warn_called += 1
-            self.orig_logger.warn(*args)
-        def debug(self, *args):
-            self.debug_called += 1
-            self.orig_logger.debug(*args)
-
-    def mock_kill_socket(self, fileno, sock):
-        '''A replacement of MsgQ.kill_socket method for inspection.'''
-        self.__killed_socket = (fileno, sock)
-        if fileno in self.__msgq.sockets:
-            del self.__msgq.sockets[fileno]
-
-    def setUp(self):
-        self.__msgq = MsgQ()
-        self.__msgq.kill_socket = self.mock_kill_socket
-        self.__sock = self.MockSocket()
-        self.__data = b'dummy'
-        self.__msgq.sockets[42] = self.__sock
-        self.__msgq.sendbuffs[42] = (None, b'testdata')
-        self.__sock_error = socket.error()
-        self.__killed_socket = None
-        self.__logger = self.LoggerWrapper(msgq.logger)
-        msgq.logger = self.__logger
-        self.__orig_select = msgq.select.select
-
-    def tearDown(self):
-        msgq.logger = self.__logger.orig_logger
-        msgq.select.select = self.__orig_select
-
-    def test_send_data(self):
-        # Successful case: _send_data() returns the hardcoded value, and
-        # setblocking() is called twice with the expected parameters
-        self.assertEqual(10, self.__msgq._send_data(self.__sock, self.__data))
-        self.assertEqual([0, 1], self.__sock.blockings)
-        self.assertIsNone(self.__killed_socket)
-
-    def test_send_data_interrupt(self):
-        '''send() is interrupted. send_data() returns 0, sock isn't killed.'''
-        expected_blockings = []
-        for eno in [errno.EAGAIN, errno.EWOULDBLOCK, errno.EINTR]:
-            self.__sock_error.errno = eno
-            self.__sock.ex_on_send = self.__sock_error
-            self.assertEqual(0, self.__msgq._send_data(self.__sock,
-                                                       self.__data))
-            expected_blockings.extend([0, 1])
-            self.assertEqual(expected_blockings, self.__sock.blockings)
-            self.assertIsNone(self.__killed_socket)
-
-    def test_send_data_error(self):
-        '''Unexpected error happens on send().  The socket is killed.
-
-        If the error is EPIPE, it's logged at the warn level; otherwise
-        an error message is logged.
-
-        '''
-        expected_blockings = []
-        expected_errors = 0
-        expected_warns = 0
-        for eno in [errno.EPIPE, errno.ECONNRESET, errno.ENOBUFS]:
-            self.__sock_error.errno = eno
-            self.__sock.ex_on_send = self.__sock_error
-            self.__killed_socket = None # clear any previuos value
-            self.assertEqual(None, self.__msgq._send_data(self.__sock,
-                                                          self.__data))
-            self.assertEqual((42, self.__sock), self.__killed_socket)
-            expected_blockings.extend([0, 1])
-            self.assertEqual(expected_blockings, self.__sock.blockings)
-
-            if eno == errno.EPIPE:
-                expected_warns += 1
-            else:
-                expected_errors += 1
-            self.assertEqual(expected_errors, self.__logger.error_called)
-            self.assertEqual(expected_warns, self.__logger.warn_called)
-
-    def test_process_packet(self):
-        '''Check some failure cases in handling an incoming message.'''
-        expected_errors = 0
-        expected_debugs = 0
-
-        # if socket.recv() fails due to socket.error, it will be logged
-        # as error and the socket will be killed regardless of errno.
-        for eno in [errno.ENOBUFS, errno.ECONNRESET]:
-            self.__sock_error.errno = eno
-            self.__sock.recv_result = self.__sock_error
-            self.__killed_socket = None # clear any previuos value
-            self.__msgq.process_packet(42, self.__sock)
-            self.assertEqual((42, self.__sock), self.__killed_socket)
-            expected_errors += 1
-            self.assertEqual(expected_errors, self.__logger.error_called)
-            self.assertEqual(expected_debugs, self.__logger.debug_called)
-
-        # if socket.recv() returns empty data, the result depends on whether
-        # there's any preceding data; in the second case below, at least
-        # 6 bytes of data will be expected, and the second call to our faked
-        # recv() returns empty data.  In that case it will be logged as error.
-        for recv_data in [b'', b'short']:
-            self.__sock.recv_result = recv_data
-            self.__killed_socket = None
-            self.__msgq.process_packet(42, self.__sock)
-            self.assertEqual((42, self.__sock), self.__killed_socket)
-            if len(recv_data) == 0:
-                expected_debugs += 1
-            else:
-                expected_errors += 1
-            self.assertEqual(expected_errors, self.__logger.error_called)
-            self.assertEqual(expected_debugs, self.__logger.debug_called)
-
-    def test_do_select(self):
-        """
-        Check the behaviour of the run_select method.
-
-        In particular, check that we skip writing to the sockets we read,
-        because a read may have side effects (like closing the socket) and
-        we want to prevent strange behavior.
-        """
-        self.__read_called = []
-        self.__write_called = []
-        self.__reads = None
-        self.__writes = None
-        def do_read(fd, socket):
-            self.__read_called.append(fd)
-            self.__msgq.running = False
-        def do_write(fd):
-            self.__write_called.append(fd)
-            self.__msgq.running = False
-        self.__msgq.process_packet = do_read
-        self.__msgq._process_write = do_write
-        self.__msgq.fd_to_lname = {42: 'lname', 44: 'other', 45: 'unused'}
-        # The do_select does index it, but just passes the value. So reuse
-        # the dict to safe typing in the test.
-        self.__msgq.sockets = self.__msgq.fd_to_lname
-        self.__msgq.sendbuffs = {42: 'data', 43: 'data'}
-        def my_select(reads, writes, errors):
-            self.__reads = reads
-            self.__writes = writes
-            self.assertEqual([], errors)
-            return ([42, 44], [42, 43], [])
-        msgq.select.select = my_select
-        self.__msgq.listen_socket = DummySocket
-
-        self.__msgq.running = True
-        self.__msgq.run_select()
-
-        self.assertEqual([42, 44], self.__read_called)
-        self.assertEqual([43], self.__write_called)
-        self.assertEqual({42, 44, 45}, set(self.__reads))
-        self.assertEqual({42, 43}, set(self.__writes))
-
-if __name__ == '__main__':
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/stats/.gitignore b/src/bin/stats/.gitignore
deleted file mode 100644 (file)
index 8dacd7e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/b10-stats
-/b10-stats-httpd
-/stats.py
-/stats_httpd.py
-/b10-stats-httpd.8
-/b10-stats.8
diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am
deleted file mode 100644 (file)
index ef678d4..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-SUBDIRS = tests
-
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-
-pkglibexec_SCRIPTS = b10-stats b10-stats-httpd
-
-b10_statsdir = $(pkgdatadir)
-b10_stats_DATA = stats.spec stats-httpd.spec
-b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl
-
-nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
-nodist_pylogmessage_PYTHON += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
-pylogmessagedir = $(pyexecdir)/isc/log_messages/
-
-CLEANFILES = b10-stats stats.pyc
-CLEANFILES += b10-stats-httpd stats_httpd.pyc
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.pyc
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
-CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.pyc
-
-man_MANS = b10-stats.8 b10-stats-httpd.8
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml
-EXTRA_DIST += stats.spec stats-httpd.spec
-EXTRA_DIST += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl
-EXTRA_DIST += stats_messages.mes stats_httpd_messages.mes
-
-if GENERATE_DOCS
-
-b10-stats.8: b10-stats.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats.xml
-
-b10-stats-httpd.8: b10-stats-httpd.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-stats-httpd.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-$(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py : stats_messages.mes
-       $(top_builddir)/src/lib/log/compiler/message \
-       -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_messages.mes
-
-$(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py : stats_httpd_messages.mes
-       $(top_builddir)/src/lib/log/compiler/message \
-       -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_httpd_messages.mes
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-stats: stats.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
-       $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|"  stats.py >$@
-       chmod a+x $@
-
-b10-stats-httpd: stats_httpd.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
-       $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats_httpd.py >$@
-       chmod a+x $@
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml
deleted file mode 100644 (file)
index 4510110..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>February 28, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-stats-httpd</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-stats-httpd</refname>
-    <refpurpose>BIND 10 HTTP server for HTTP/XML interface of statistics</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2011-2012</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>      <command>b10-stats-httpd</command>
-      <arg><option>-v</option></arg>|<arg><option>--verbose</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      <command>b10-stats-httpd</command> is a standalone HTTP server. It is
-      intended for HTTP/XML interface for statistics module. This server
-      process runs as a process separated from the process of the BIND 10 Stats
-      daemon (<command>b10-stats</command>). The server is initially executed
-      by the b10-init process and eventually
-      exited by it.  The server is intended to serve requests by HTTP
-      clients like web browsers and third-party modules. When the server is
-      asked, it requests BIND 10 statistics data or its schema from
-      <command>b10-stats</command> which sends the data back in Python
-      dictionary format, and the server converts it into XML format. The server
-      sends it to the HTTP client. The server can send three types of documents,
-      which are XML (Extensible Markup Language), XSD (XML Schema definition)
-      and XSL (Extensible Stylesheet Language). The XML document is the
-      statistics data of BIND 10, the XSD document is the data schema of it,
-      and the XSL document is the style sheet to be showed for the web
-      browsers. There is different URL for each document. But please note that
-      you would be redirected to the URL of XML document if you request the URL
-      of the root document. For example, you would be redirected to
-      http://127.0.0.1:8000/bind10/statistics/xml if you request
-      http://127.0.0.1:8000/. Please see the manual and the spec file
-      of <command>b10-stats</command> for more details about the items of BIND
-      10 statistics. The server uses CC session in communication
-      with <command>b10-stats</command>. CC session is provided
-      by <command>b10-msgq</command> which is started
-      by <command>b10-init</command> in advance. The server is implemented by
-      HTTP-server libraries included in Python 3. The server obtains the
-      configuration from the config manager (<command>b10-cfgmgr</command>) in
-      runtime. Please see below for more details about this spec file and
-      configuration of the server.
-    </para>
-  </refsect1>
-
-<!-- TODO: this is too verbose; move some of this into the guide instead -->
-
-  <refsect1>
-    <title>OPTIONS</title>
-    <para>The argument is as follow:</para>
-    <variablelist>
-      <varlistentry>
-        <term><option>-v</option>, <option>--verbose</option></term>
-        <listitem>
-         <para>
-          <command>b10-stats-httpd</command> switches to verbose mode and sends
-          verbose messages to STDOUT.
-         </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>FILES</title>
-    <para>
-      <filename>/usr/local/share/bind10/stats-httpd.spec</filename>
-      <!--TODO: The filename should be computed from prefix-->
-      &mdash; the spec file of <command>b10-stats-httpd</command>. This file
-      contains configurable settings
-      of <command>b10-stats-httpd</command>. This setting can be configured in
-      runtime via
-      <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>. Please
-      see the manual
-      of <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum> about
-      how to configure the settings.
-    </para>
-    <para>
-      <filename>/usr/local/share/bind10/stats-httpd-xml.tpl</filename>
-      <!--TODO: The filename should be computed from prefix-->
-      &mdash; the template file of XML document.
-    </para>
-    <para>
-      <filename>/usr/local/share/bind10/stats-httpd-xsd.tpl</filename>
-      <!--TODO: The filename should be computed from prefix-->
-      &mdash; the template file of XSD document.
-    </para>
-    <para>
-      <filename>/usr/local/share/bind10/stats-httpd-xsl.tpl</filename>
-      <!--TODO: The filename should be computed from prefix-->
-      &mdash; the template file of XSL document.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>CONFIGURATION AND COMMANDS</title>
-    <para>
-      The configurable setting in
-      <filename>stats-httpd.spec</filename> is:
-    </para>
-    <variablelist>
-      <varlistentry>
-        <term><varname>listen_on</varname></term>
-        <listitem>
-         <para>
-           a list of pairs of address and port for
-           <command>b10-stats-httpd</command> to listen HTTP requests on. The
-           pair consists of the <varname>address</varname> string
-           and <varname>port</varname> number. The default setting is the list
-           of address 127.0.0.1 port 8000. If the server is started by the
-           default setting being left, for example, the URL for XML document
-           is http://127.0.0.1:8000/bind10/statistics/xml. And also IPv6
-           addresses can be configured and they works in the runtime
-           environment for dual stack. You can change the settings
-           through <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
-         </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-    <para>
-      The commands in <filename>stats-httpd.spec</filename> are:
-    </para>
-    <variablelist>
-      <varlistentry>
-        <term><command>status</command></term>
-        <listitem>
-         <para>
-           shows the status of <command>b10-stats-httpd</command> with its
-           PID.
-         </para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
-        <term><command>shutdown</command></term>
-        <listitem>
-         <para>
-           exits the <command>b10-stats-httpd</command> process.
-            This has an optional <varname>pid</varname> argument to
-            select the process ID to stop.
-            (Note that the b10-init process may restart this service
-            if configured.)
-          </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-       <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      <command>b10-stats-httpd</command> was designed and implemented by Naoki
-      Kambe of JPRS in March 2011.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
deleted file mode 100644 (file)
index 83fdd36..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-               [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>June 20, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-stats</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-stats</refname>
-    <refpurpose>BIND 10 statistics module</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010-2012</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-stats</command>
-      <arg><option>-v</option></arg>
-      <arg><option>--verbose</option></arg>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      The <command>b10-stats</command> daemon collects statistics data
-      from each BIND 10 module. Its statistics information may be
-      reported via <command>bindctl</command> or
-      <command>b10-stats-httpd</command>.  It is started by
-      <command>b10-init</command> and communicates by using the
-      Command Channel by <command>b10-msgq</command> with other
-      modules like <command>b10-init</command>, <command>b10-auth</command>
-      and so on. <command>b10-stats</command> periodically requests statistics
-      data from each module. The interval time can be configured
-      via <command>bindctl</command>. <command>b10-stats</command> cannot
-      accept any command from other modules for updating statistics data. The
-      stats module collects data and
-      aggregates it. <command>b10-stats</command> invokes an internal
-      command for <command>b10-init</command> after its initial
-      starting to make sure it collects statistics data from
-      <command>b10-init</command>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>OPTIONS</title>
-    <para>The arguments are as follows:</para>
-    <variablelist>
-      <varlistentry>
-        <term><option>-v</option>, <option>--verbose</option></term>
-        <listitem>
-          <para>
-            This enables maximum debug logging.
-          </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
-
-  <refsect1>
-    <title>CONFIGURATION AND COMMANDS</title>
-
-    <para>
-      The only configurable setting in <filename>stats.spec</filename>
-      is:
-    </para>
-
-    <variablelist>
-      <varlistentry>
-        <term><varname>poll-interval</varname></term>
-        <listitem>
-          <para>
-            is a time interval in seconds at which <command>b10-stats</command>
-            requests each module to return its statistics data. The default is 60
-            seconds. Polling can be disabled by setting it to 0. The type of the
-            value should be an unsigned integer. Negative integers are ignored.
-          </para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-
-<!-- TODO: formating -->
-    <para>
-      The configuration commands are:
-    </para>
-
-    <para>
-      <command>show</command> will send the statistics data
-      in JSON format.
-      By default, it outputs all the statistics data it has collected.
-      An optional item name may be specified to receive individual output.
-    </para>
-
-    <para>
-      <command>showschema</command> will send the schema of the statistics data
-      in JSON format. The output is equivalent to the statistics part
-      of <filename>stats.spec</filename>.
-    </para>
-
-    <para>
-      <command>shutdown</command> will shutdown the
-      <command>b10-stats</command> process.
-      This has an optional <varname>pid</varname> argument to
-      select the process ID to stop.
-      (Note that the b10-init process may restart this service
-      if configured.)
-    </para>
-
-    <para>
-      <command>status</command> simply indicates that the daemon is
-      running.
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>STATISTICS DATA</title>
-
-    <para>
-      The <command>b10-stats</command> daemon contains these
-      <quote>Stats</quote> statistics:
-    </para>
-
-    <variablelist>
-
-
-      <varlistentry>
-        <term>boot_time</term>
-        <listitem><simpara>The date and time when this daemon was
-          started in ISO 8601 format.
-          This is a constant which can't be reset except by restarting
-          <command>b10-stats</command>.
-        </simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>last_update_time</term>
-        <listitem><simpara>The date and time (in ISO 8601 format)
-          when this daemon last received data from another component.
-        </simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>lname</term>
-        <listitem><simpara>This is the name used for the
-          <command>b10-msgq</command> command-control channel.
-          (This is a constant which can't be reset except by restarting
-          <command>b10-stats</command>.)
-        </simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>report_time</term>
-        <listitem><simpara>The latest report date and time in
-          ISO 8601 format.</simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>start_time</term>
-        <listitem><simpara>This is the date and time (in ISO 8601 format)
-          when this daemon started collecting data.
-        </simpara></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>timestamp</term>
-        <listitem><simpara>The current date and time represented in
-          seconds since UNIX epoch (1970-01-01T00:00:00Z) with
-          precision (delimited with a period) up to
-          one hundred thousandth of second.</simpara></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-    <para>
-      See other manual pages for explanations for their statistics
-      that are kept track by <command>b10-stats</command>.
-    </para>
-
-  </refsect1>
-
-  <refsect1>
-    <title>FILES</title>
-    <para><filename>/usr/local/share/bind10/stats.spec</filename>
-      <!--TODO: The filename should be computed from prefix-->
-      &mdash; This is a spec file for <command>b10-stats</command>. It
-      contains commands for <command>b10-stats</command>. They can be
-      invoked
-      via <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-init</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>b10-stats-httpd</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citerefentry>
-        <refentrytitle>bindctl</refentrytitle><manvolnum>1</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      The <command>b10-stats</command> daemon was initially designed
-      and implemented by Naoki Kambe of JPRS in October 2010.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/stats/stats-httpd-xml.tpl b/src/bin/stats/stats-httpd-xml.tpl
deleted file mode 100644 (file)
index ed91423..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="$xsl_url_path"?>
-$xml_string
\ No newline at end of file
diff --git a/src/bin/stats/stats-httpd-xsd.tpl b/src/bin/stats/stats-httpd-xsd.tpl
deleted file mode 100644 (file)
index 75f926b..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<schema targetNamespace="$xsd_namespace"
-        xmlns="http://www.w3.org/2001/XMLSchema"
-        xmlns:bind10="$xsd_namespace">
-  <annotation>
-    <documentation>XML schema of statistics data in BIND 10</documentation>
-  </annotation>
-  <element name="statistics">
-    <annotation><documentation>A set of statistics data</documentation></annotation>
-    <complexType>
-      <sequence>
-        <element name="item" maxOccurs="unbounded" minOccurs="1">
-          <complexType>
-           <attribute name="identifier" type="string" use="required">
-            <annotation>
-              <appinfo>Identifier</appinfo>
-              <documentation>Identifier of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="value" type="string" use="optional">
-            <annotation>
-              <appinfo>Value</appinfo>
-              <documentation>Value of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="owner" type="string" use="required">
-            <annotation>
-              <appinfo>Owner</appinfo>
-              <documentation>Owner module name</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="uri" type="anyURI" use="required">
-            <annotation>
-              <appinfo>URI</appinfo>
-              <documentation>URI of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="name" type="string" use="required">
-            <annotation>
-              <appinfo>Name</appinfo>
-              <documentation>Name of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="type" use="required">
-               <annotation>
-                 <appinfo>Type</appinfo>
-                 <documentation>Type of item</documentation>
-               </annotation>
-               <simpleType>
-                 <restriction base="token">
-                   <enumeration value="boolean"/>
-                   <enumeration value="integer"/>
-                   <enumeration value="real"/>
-                   <enumeration value="string"/>
-                   <enumeration value="map"/>
-                   <enumeration value="list"/>
-                   <enumeration value="named_set"/>
-                   <enumeration value="any"/>
-                 </restriction>
-               </simpleType>
-           </attribute>
-           <attribute name="description" type="string" use="optional">
-            <annotation>
-              <appinfo>Description</appinfo>
-              <documentation>Description of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="title" type="string" use="optional">
-            <annotation>
-              <appinfo>Title</appinfo>
-              <documentation>Title of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="optional" type="boolean" use="optional">
-            <annotation>
-              <appinfo>Optional</appinfo>
-              <documentation>The item is optional or not</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="default" type="string" use="optional">
-            <annotation>
-              <appinfo>Default</appinfo>
-              <documentation>Default of item</documentation>
-            </annotation>
-           </attribute>
-           <attribute name="format" type="string" use="optional">
-            <annotation>
-              <appinfo>Format</appinfo>
-              <documentation>Format of item value</documentation>
-            </annotation>
-           </attribute>
-          </complexType>
-        </element>
-      </sequence>
-    </complexType>
-  </element>
-</schema>
diff --git a/src/bin/stats/stats-httpd-xsl.tpl b/src/bin/stats/stats-httpd-xsl.tpl
deleted file mode 100644 (file)
index 3dd3b90..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet version="1.0"
-  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml"
-  xmlns:bind10="$xsd_namespace">
-  <xsl:output method="html" encoding="UTF-8"
-    doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
-    doctype-system=" http://www.w3.org/TR/html4/loose.dtd " />
-  <xsl:template match="/">
-    <html lang="en">
-      <head>
-        <title>BIND 10 Statistics</title>
-        <style type="text/css"><![CDATA[
-table {
-  border: 1px #000000 solid;
-  border-collapse: collapse;
-}
-td, th {
-  padding: 3px 20px;
-  border: 1px #000000 solid;
-}
-td.title {
-  text-decoration:underline;
-}
-]]>
-        </style>
-      </head>
-      <body>
-        <h1>BIND 10 Statistics</h1>
-        <xsl:apply-templates />
-      </body>
-    </html>
-  </xsl:template>
-  <xsl:template match="bind10:statistics">
-    <table>
-      <tr>
-       <th>Identifier</th><th>Value</th><th>Description</th>
-      </tr>
-      <xsl:for-each select="item">
-       <tr>
-         <td>
-           <xsl:element name="a">
-             <xsl:attribute name="href"><xsl:value-of select="@uri" /></xsl:attribute>
-             <xsl:value-of select="@identifier" />
-           </xsl:element>
-         </td>
-         <td>
-           <xsl:if test="@value"><xsl:value-of select="@value" /></xsl:if>
-         </td>
-         <td>
-           <xsl:if test="@description"><xsl:value-of select="@description" /></xsl:if>
-         </td>
-       </tr>
-      </xsl:for-each>
-    </table>
-  </xsl:template>
-</xsl:stylesheet>
diff --git a/src/bin/stats/stats-httpd.spec b/src/bin/stats/stats-httpd.spec
deleted file mode 100644 (file)
index 783611d..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "StatsHttpd",
-    "module_description": "Stats HTTP daemon",
-    "config_data": [
-      {
-        "item_name": "listen_on",
-        "item_type": "list",
-        "item_optional": false,
-        "item_default": [
-          {
-            "address": "127.0.0.1",
-            "port": 8000
-          }
-        ],
-        "list_item_spec": {
-          "item_name": "address",
-          "item_type": "map",
-          "item_optional": false,
-          "item_default": {},
-          "map_item_spec": [
-            {
-              "item_name": "address",
-              "item_type": "string",
-              "item_optional": true,
-              "item_default": "127.0.0.1",
-              "item_description": "listen-on address for HTTP"
-            },
-            {
-              "item_name": "port",
-              "item_type": "integer",
-              "item_optional": true,
-              "item_default": 8000,
-              "item_description": "listen-on port for HTTP"
-            }
-          ]
-        },
-        "item_description": "http listen-on address and port"
-      }
-    ],
-    "commands": [
-      {
-        "command_name": "status",
-        "command_description": "Status of the stats httpd",
-        "command_args": []
-      },
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down the stats httpd",
-        "command_args": [
-          {
-            "item_name": "pid",
-            "item_type": "integer",
-            "item_optional": true
-          }
-        ]
-      }
-    ]
-  }
-}
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
deleted file mode 100755 (executable)
index 38d0b02..0000000
+++ /dev/null
@@ -1,752 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010-2013  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.
-
-"""
-Statistics daemon in BIND 10
-
-"""
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import os
-from time import time, strftime, gmtime
-from optparse import OptionParser, OptionValueError
-import errno
-import select
-
-import isc.cc
-import isc.config
-import isc.util.process
-import isc.util.traceback_handler
-import isc.log
-from isc.log_messages.stats_messages import *
-
-isc.log.init("b10-stats", buffer=True)
-logger = isc.log.Logger("stats")
-
-# Some constants for debug levels.
-DBG_STATS_MESSAGING = logger.DBGLVL_COMMAND
-
-# This is for boot_time of Stats
-_BASETIME = gmtime()
-
-# for setproctitle
-isc.util.process.rename()
-
-# If B10_FROM_SOURCE is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_SOURCE" in os.environ:
-    SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
-        "src" + os.sep + "bin" + os.sep + "stats" + os.sep + "stats.spec"
-else:
-    PREFIX = "@prefix@"
-    DATAROOTDIR = "@datarootdir@"
-    SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "stats.spec"
-    SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\
-        .replace("${prefix}", PREFIX)
-
-def get_timestamp():
-    """
-    get current timestamp
-    """
-    return time()
-
-def get_datetime(gmt=None):
-    """
-    get current datetime
-    """
-    if not gmt: gmt = gmtime()
-    return strftime("%Y-%m-%dT%H:%M:%SZ", gmt)
-
-def get_spec_defaults(spec):
-    """
-    extracts the default values of the items from spec specified in
-    arg, and returns the dict-type variable which is a set of the item
-    names and the default values
-    """
-    if type(spec) is not list: return {}
-    def _get_spec_defaults(spec):
-        item_type = spec['item_type']
-        if item_type == "integer":
-            return int(spec.get('item_default', 0))
-        elif item_type == "real":
-            return float(spec.get('item_default', 0.0))
-        elif item_type == "boolean":
-            return bool(spec.get('item_default', False))
-        elif item_type == "string":
-            return str(spec.get('item_default', ""))
-        elif item_type == "list":
-            return spec.get(
-                    "item_default",
-                    [ _get_spec_defaults(spec["list_item_spec"]) ])
-        elif item_type == "map":
-            return spec.get(
-                    "item_default",
-                    dict([ (s["item_name"], _get_spec_defaults(s)) for s in spec["map_item_spec"] ]) )
-        elif item_type == "named_set":
-            # in a named_set type, it returns {} as a default value
-            return spec.get("item_default", {})
-        else:
-            return spec.get("item_default", None)
-    return dict([ (s['item_name'], _get_spec_defaults(s)) for s in spec ])
-
-def _accum(a, b):
-    """If the first arg is dict or list type, two values
-    would be merged and accumlated. This is for internal use."""
-
-    # If both of args are dict or list type, two
-    # values are merged.
-    if type(a) is dict and type(b) is dict:
-        return dict([ (k, _accum(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) is list and type(b) is list:
-        return [ _accum(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 ]
-    # If both of args are integer or float type, two
-    # values are added.
-    elif (type(a) is int or type(a) is float) \
-            and (type(b) is int or type(b) is float):
-        return a + b
-
-    # If both of args are string type,
-    # values are compared and bigger one is returned.
-    elif type(a) is str and type(b) is str:
-        if a < b: return b
-        return a
-
-    # If the first arg is None type, the second value is returned.
-    elif a is None:
-        return b
-
-    # Nothing matches above, the first arg is returned
-    return a
-
-def merge_oldnew(old, new):
-    """
-    Merges two arguments.  If old data contains the corresponding name
-    against new data, the value of the name is replaced with the
-    corresponding value in new data. Otherwise, the new date are added
-    at same name or same id. Both old data and new data should be same
-    data type. This method returns the merged result.
-    """
-    # If the first arg is dict or list type, two values
-    # would be merged
-    if type(old) is dict and type(new) is dict:
-        return dict([ (k, merge_oldnew(old[k], v)) \
-                          if k in old else (k, v) \
-                          for (k, v) in new.items() ] \
-                        + [ (k, v) \
-                                for (k, v) in old.items() \
-                                if k not in new ])
-    elif type(old) is list and type(new) is list:
-        return [ merge_oldnew(old[i], new[i]) \
-                     if len(old) > i else new[i] \
-                     for i in range(len(new)) ] \
-                     + [ old[i] \
-                             for i in range(len(old)) \
-                             if len(new) <= i ]
-    else:
-        return new
-
-class Callback():
-    """
-    A Callback handler class
-    """
-    def __init__(self, command=None, args=(), kwargs={}):
-        self.command = command
-        self.args = args
-        self.kwargs = kwargs
-
-    def __call__(self, *args, **kwargs):
-        if not args: args = self.args
-        if not kwargs: kwargs = self.kwargs
-        if self.command: return self.command(*args, **kwargs)
-
-class StatsError(Exception):
-    """Exception class for Stats class"""
-    pass
-
-class InitSessionTimeout(isc.cc.session.SessionTimeout):
-    """SessionTimeout Exception in the session between the stats module and the
-    init module"""
-    pass
-
-class Stats:
-    """
-    Main class of stats module
-    """
-    def __init__(self, module_ccsession_class=isc.config.ModuleCCSession):
-        '''Constructor
-
-        module_ccsession_class is parameterized so that test can specify
-        a mocked class to test the behavior without involving network I/O.
-        In other cases this parameter shouldn't be specified.
-
-        '''
-        self.running = False
-        # create ModuleCCSession object
-        self.mccs = module_ccsession_class(SPECFILE_LOCATION,
-                                           self.config_handler,
-                                           self.command_handler)
-        self.cc_session = self.mccs._session
-        # get module spec
-        self.module_name = self.mccs.get_module_spec().get_module_name()
-        self.modules = {}
-        self.statistics_data = {}
-        # statistics data by each mid
-        self.statistics_data_bymid = {}
-        # get commands spec
-        self.commands_spec = self.mccs.get_module_spec().get_commands_spec()
-        # add event handler related command_handler of ModuleCCSession
-        self.callbacks = {}
-        for cmd in self.commands_spec:
-            # add prefix "command_"
-            name = "command_" + cmd["command_name"]
-            try:
-                callback = getattr(self, name)
-                kwargs = get_spec_defaults(cmd["command_args"])
-                self.callbacks[name] = Callback(command=callback, kwargs=kwargs)
-            except AttributeError:
-                raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"])
-        self.config = {}
-        self.mccs.start()
-        # setup my config
-        self.config = dict([
-                (itm['item_name'], self.mccs.get_value(itm['item_name'])[0])
-                for itm in self.mccs.get_module_spec().get_config_spec()
-                ])
-        # set a absolute timestamp polling at next time
-        self.next_polltime = get_timestamp() + self.get_interval()
-
-        self._init_statistics_data()
-
-    def _init_statistics_data(self):
-        """initialized Statistics data.
-
-        This method is a dedicated subroutine of __int__(), but extracted
-        so tests can override it to avoid blocking network operation.
-
-        """
-        self.update_modules()
-        self.update_statistics_data(
-            self.module_name,
-            self.cc_session.lname,
-            {'lname': self.cc_session.lname,
-             'boot_time': get_datetime(_BASETIME),
-             'last_update_time': get_datetime()})
-        # define the variable of the last time of polling
-        self._lasttime_poll = 0.0
-
-    def get_interval(self):
-        """return the current value of 'poll-interval'"""
-        return self.config['poll-interval']
-
-    def _get_multi_module_list(self):
-        """Returns a module list which is running as multiple modules."""
-        # It counts the number of instances of same module by
-        # examining the third value from the array result of
-        # 'show_processes' of Init
-        try:
-            value = self.mccs.rpc_call('show_processes', 'Init')
-        except isc.config.RPCRecipientMissing:
-            # This has been SessionTimeout before, so we keep the original
-            # behavior.
-            raise
-        except isc.config.RPCError:
-            # TODO: Is it OK to just pass? As part of refactoring, preserving
-            # the original behaviour.
-            value = None
-        except isc.cc.session.SessionTimeout as e:
-            raise InitSessionTimeout(e)
-        modules = []
-        if type(value) is list:
-            # NOTE: For example, the "show_processes" command
-            # of Init is assumed to return the response in this
-            # format:
-            #  [
-            #  ...
-            #    [
-            #      20061,
-            #      "b10-auth",
-            #      "Auth"
-            #    ],
-            #    [
-            #      20103,
-            #      "b10-auth-2",
-            #      "Auth"
-            #    ]
-            #  ...
-            #  ]
-            # If multiple instances of the same module are
-            # running, the address names of them, which are at the
-            # third element, must be also same. Thus, the value of
-            # the third element of each outer element is read here
-            # for counting multiple instances.  This is a
-            # workaround for counting the instances. This should
-            # be fixed in another proper way in the future
-            # release.
-            modules = [ v[2] if type(v) is list and len(v) > 2 \
-                            else None for v in value ]
-        return modules
-
-    def _query_statistics(self, modules):
-        """Queries each module statistics and returns sequences to use
-        for receiving that answers based on the argument 'modules' which
-        is a list of modules running as multiple modules"""
-        # start requesting each module to collect statistics data
-        sequences = []
-        for (module_name, data) in self.get_statistics_data().items():
-            # skip if module_name is 'Stats'
-            if module_name == self.module_name:
-                continue
-            logger.debug(DBG_STATS_MESSAGING, STATS_SEND_STATISTICS_REQUEST,
-                         module_name)
-            cmd = isc.config.ccsession.create_command(
-                "getstats", None) # no argument
-            # Not using rpc_call here. We first send a bunch of commands, then
-            # collect all the answers. This eliminates some of the round-trip
-            # times. Unfortunately, rpc_call is not flexible enough to allow
-            # this, though the future rpc_call_async could.
-            seq = self.cc_session.group_sendmsg(cmd, module_name,
-                                                want_answer=True)
-            sequences.append((module_name, seq))
-            cnt = modules.count(module_name)
-            if cnt > 1:
-                sequences = sequences + [ (module_name, seq) \
-                                              for i in range(cnt-1) ]
-        return sequences
-
-    def _collect_statistics(self, sequences):
-        """Based on sequences which is a list of values returned from
-        group_sendmsg(), collects statistics data from each module. If
-        a SessionTimeout exception is raised when collecting from the
-        module, skips it and goes to collect from the next module."""
-        # start receiving statistics data
-        _statistics_data = []
-        for (module_name, seq) in sequences:
-            try:
-                answer, env = self.cc_session.group_recvmsg(False, seq)
-                if answer:
-                    rcode, args = isc.config.ccsession.parse_answer(answer)
-                    if rcode == 0:
-                        _statistics_data.append(
-                            (module_name, env['from'], args))
-            # skip this module if SessionTimeout raised
-            except isc.cc.session.SessionTimeout:
-                logger.warn(STATS_SKIP_COLLECTING, module_name)
-        return _statistics_data
-
-    def _refresh_statistics(self, statistics_data):
-        """Refreshes statistics_data into internal statistics data to
-        display, which is a list of a tuple of module_name, lname, and
-        args"""
-        # update statistics data
-        _statistics_data = statistics_data[:]
-        self.update_modules()
-        while len(_statistics_data) > 0:
-            (_module_name, _lname, _args) = _statistics_data.pop(0)
-            if not self.update_statistics_data(_module_name, _lname, _args):
-                self.update_statistics_data(
-                    self.module_name,
-                    self.cc_session.lname,
-                    {'last_update_time': get_datetime()})
-
-    def do_polling(self):
-        """Polls modules for statistics data. Return nothing. First
-           search multiple instances of same module. Second requests
-           each module to invoke 'getstats'. Finally updates internal
-           statistics data every time it gets from each instance."""
-        try:
-            modules = self._get_multi_module_list()
-            sequences = self._query_statistics(modules)
-            _statistics_data = self._collect_statistics(sequences)
-            self._refresh_statistics(_statistics_data)
-        except InitSessionTimeout:
-            logger.warn(STATS_SKIP_POLLING)
-        # if successfully done or skipped, set the last time of polling
-        self._lasttime_poll = get_timestamp()
-
-    def _check_command(self, nonblock=False):
-        """check invoked command by waiting for 'poll-interval' seconds
-
-        This is a dedicated subroutine of start(), but extracted and defined
-        as a 'protected' method so that tests can replace it.
-
-        """
-        # backup original timeout
-        orig_timeout = self.cc_session.get_timeout()
-        # set cc-session timeout to half of a second(500ms)
-        self.cc_session.set_timeout(500)
-        try:
-            answer, env = self.cc_session.group_recvmsg(nonblock)
-            self.mccs.check_command_without_recvmsg(answer, env)
-        except isc.cc.session.SessionTimeout:
-            pass # waited for poll-interval seconds
-        # restore timeout
-        self.cc_session.set_timeout(orig_timeout)
-
-    def start(self):
-        """
-        Start stats module
-        """
-        logger.info(STATS_STARTING)
-
-        try:
-            self.running = True
-            while self.running:
-                self._check_command()
-                now = get_timestamp()
-                intval = self.get_interval()
-                if intval > 0 and now >= self.next_polltime:
-                    # decide the next polling timestamp
-                    self.next_polltime += intval
-                    # adjust next time
-                    if self.next_polltime < now:
-                        self.next_polltime = now
-                    self.do_polling()
-        finally:
-            self.mccs.send_stopping()
-
-    def config_handler(self, new_config):
-        """
-        handle a configure from the cc channel
-        """
-        logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG,
-                     new_config)
-        errors = []
-        if not self.mccs.get_module_spec().\
-                validate_config(False, new_config, errors):
-                return isc.config.ccsession.create_answer(
-                    1, ", ".join(errors))
-
-        if 'poll-interval' in new_config \
-                and new_config['poll-interval'] < 0:
-            return isc.config.ccsession.create_answer(
-                1, "Negative integer ignored")
-
-        self.config.update(new_config)
-        if 'poll-interval' in self.config:
-            # update next polling timestamp
-            self.next_polltime = get_timestamp() + self.get_interval()
-        return isc.config.create_answer(0)
-
-    def command_handler(self, command, kwargs):
-        """
-        handle commands from the cc channel
-        """
-        name = 'command_' + command
-        if name in self.callbacks:
-            callback = self.callbacks[name]
-            if kwargs:
-                return callback(**kwargs)
-            else:
-                return callback()
-        else:
-            logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command)
-            return isc.config.create_answer(1, "Unknown command: '"+str(command)+"'")
-
-    def update_modules(self):
-        """
-        updates information of each module. This method gets each
-        module's information from the config manager and sets it into
-        self.modules. If its getting from the config manager fails, it
-        raises StatsError.
-        """
-        modules = {}
-        try:
-            value = self.mccs.rpc_call(isc.config.ccsession. \
-                                       COMMAND_GET_STATISTICS_SPEC,
-                                       'ConfigManager')
-        except isc.config.RPCError as e:
-            raise StatsError("Updating module spec fails: " + str(e))
-        for mod in value:
-            spec = { "module_name" : mod }
-            if value[mod] and type(value[mod]) is list:
-                spec["statistics"] = value[mod]
-            modules[mod] = isc.config.module_spec.ModuleSpec(spec)
-        modules[self.module_name] = self.mccs.get_module_spec()
-        self.modules = modules
-
-    def get_statistics_data(self, owner=None, name=None):
-        """
-        returns statistics data which stats module has of each
-        module. If it can't find specified statistics data, it raises
-        StatsError.
-        """
-        self.update_modules()
-        if owner and name:
-            try:
-                return {owner:{name:self.statistics_data[owner][name]}}
-            except KeyError:
-                pass
-        elif owner:
-            try:
-                return {owner: self.statistics_data[owner]}
-            except KeyError:
-                pass
-        elif name:
-            pass
-        else:
-            return self.statistics_data
-        raise StatsError("No statistics data found: "
-                         + "owner: " + str(owner) + ", "
-                         + "name: " + str(name))
-
-    def update_statistics_data(self, owner=None, mid=None, data=None):
-        """
-        change statistics data of specified module into specified
-        data. It updates information of each module first, and it
-        updates statistics data. If specified data is invalid for
-        statistics spec of specified owner, it returns a list of error
-        messages. If there is no error or if neither owner nor data is
-        specified in args, it returns None. The 'mid' argument is an
-        identifier of the sender module in order for stats to identify which
-        instance sends statistics data in the situation that multiple
-        instances are working.
-        """
-        # Note:
-        # The fix of #1751 is for multiple instances working. It is
-        # assumed here that they send different statistics data with
-        # each sender module id (mid). Stats should save their statistics data
-        # by mid. The statistics data, which is the existing variable, is
-        # preserved by accumlating from statistics data by the mid. 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.
-
-        def _accum_bymodule(statistics_data_bymid):
-            """This is an internal method for the superordinate
-            method. It accumulates statistics data of each module id
-            by module. It returns a accumulated result."""
-            # FIXME: A issue might happen when consolidating
-            # statistics of the multiple instances. If they have
-            # different statistics data which are not for adding each
-            # other, this might happen: If these are integer or float,
-            # these are added each other. If these are string , these
-            # are compared and consolidated into bigger one.  If one
-            # of them is None type , these might be consolidated
-            # into not None-type one. Otherwise these are overwritten
-            # into one of them.
-            ret = {}
-            for data in statistics_data_bymid.values():
-                ret.update(_accum(data, ret))
-            return ret
-
-        # Firstly, it gets default statistics data in each spec file.
-        statistics_data = {}
-        for (name, module) in self.modules.items():
-            value = get_spec_defaults(module.get_statistics_spec())
-            if module.validate_statistics(True, value):
-                statistics_data[name] = value
-        self.statistics_data = statistics_data
-
-        # If the "owner" and "data" arguments in this function are
-        # specified, then the variable of statistics data of each module id
-        # would be updated.
-        errors = []
-        if owner and data:
-            _data = self.statistics_data_bymid.copy()
-            try:
-                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
-                            _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: " + 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
-        mlist = [ k for k in self.statistics_data_bymid.keys() ]
-        for m in mlist:
-            if self.statistics_data_bymid[m]:
-                if m in self.statistics_data:
-                    # propagate the default values by times of
-                    # instances
-                    _len = len(self.statistics_data_bymid[m])
-                    for i in range(0, _len - 1):
-                        self.statistics_data[m] = _accum(
-                            self.statistics_data[m],
-                            self.statistics_data[m])
-                    # replace the default values with summaries of the
-                    # collected values of each module. But the default
-                    # values which are not included in collected
-                    # values are not replaced.
-                    self.statistics_data[m] = merge_oldnew(
-                        self.statistics_data[m],
-                        _accum_bymodule(self.statistics_data_bymid[m]))
-
-        if errors:
-            logger.warn(
-                STATS_RECEIVED_INVALID_STATISTICS_DATA, owner)
-            return errors
-
-    def command_status(self):
-        """
-        handle status command
-        """
-        logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND)
-        return isc.config.create_answer(
-            0, "Stats is up. (PID " + str(os.getpid()) + ")")
-
-    def command_shutdown(self, pid=None):
-        """
-        handle shutdown command
-
-        The pid argument is ignored, it is here to match the signature.
-        """
-        logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
-        self.running = False
-        return isc.config.create_answer(0)
-
-    def command_show(self, owner=None, name=None):
-        """
-        handle show command
-        """
-        # decide if polling should be done based on the the last time of
-        # polling. If more than one second has passed since the last
-        # request to each module, the stats module requests each module
-        # statistics data and then shows the latest result. Otherwise,
-        # the stats module just shows statistics data which it has.
-        if get_timestamp() - self._lasttime_poll > 1.0:
-            self.do_polling()
-        if owner or name:
-            logger.debug(DBG_STATS_MESSAGING,
-                         STATS_RECEIVED_SHOW_NAME_COMMAND,
-                         str(owner)+", "+str(name))
-        else:
-            logger.debug(DBG_STATS_MESSAGING,
-                         STATS_RECEIVED_SHOW_ALL_COMMAND)
-        errors = self.update_statistics_data(
-            self.module_name,
-            self.cc_session.lname,
-            {'timestamp': get_timestamp(),
-             'report_time': get_datetime()}
-            )
-        if errors:
-            raise StatsError("stats spec file is incorrect: "
-                             + ", ".join(errors))
-        try:
-            return isc.config.create_answer(
-                0, self.get_statistics_data(owner, name))
-        except StatsError:
-            return isc.config.create_answer(
-                1, "specified arguments are incorrect: " \
-                    + "owner: " + str(owner) + ", name: " + str(name))
-
-    def command_showschema(self, owner=None, name=None):
-        """
-        handle show command
-        """
-        if owner or name:
-            logger.debug(DBG_STATS_MESSAGING,
-                         STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND,
-                         str(owner)+", "+str(name))
-        else:
-            logger.debug(DBG_STATS_MESSAGING,
-                         STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND)
-        self.update_modules()
-        schema = {}
-        schema_byname = {}
-        for mod in self.modules:
-            spec = self.modules[mod].get_statistics_spec()
-            schema_byname[mod] = {}
-            if spec:
-                schema[mod] = spec
-                for item in spec:
-                    schema_byname[mod][item['item_name']] = item
-        if owner:
-            try:
-                if name:
-                    return isc.config.create_answer(0, {owner:[schema_byname[owner][name]]})
-                else:
-                    return isc.config.create_answer(0, {owner:schema[owner]})
-            except KeyError:
-                pass
-        else:
-            if name:
-                return isc.config.create_answer(1, "module name is not specified")
-            else:
-                return isc.config.create_answer(0, schema)
-        return isc.config.create_answer(
-                1, "specified arguments are incorrect: " \
-                    + "owner: " + str(owner) + ", name: " + str(name))
-
-def main():
-    try:
-        parser = OptionParser()
-        parser.add_option(
-            "-v", "--verbose", dest="verbose", action="store_true",
-            help="enable maximum debug logging")
-        (options, args) = parser.parse_args()
-        if options.verbose:
-            isc.log.init("b10-stats", "DEBUG", 99, buffer=True)
-        stats = Stats()
-        stats.start()
-    except OptionValueError as ove:
-        logger.fatal(STATS_BAD_OPTION_VALUE, ove)
-        sys.exit(1)
-    except isc.cc.session.SessionError as se:
-        logger.fatal(STATS_CC_SESSION_ERROR, se)
-        sys.exit(1)
-    except StatsError as se:
-        logger.fatal(STATS_START_ERROR, se)
-        sys.exit(1)
-    except KeyboardInterrupt as kie:
-        logger.info(STATS_STOPPED_BY_KEYBOARD)
-
-    logger.info(STATS_EXITING)
-
-if __name__ == "__main__":
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec
deleted file mode 100644 (file)
index dab10a2..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-{
-  "module_spec": {
-    "module_name": "Stats",
-    "module_description": "Stats daemon",
-    "config_data": [
-      { "item_name": "poll-interval",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 60
-      }
-    ],
-    "commands": [
-      {
-        "command_name": "status",
-        "command_description": "Show status of the stats daemon",
-        "command_args": []
-      },
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down the stats module",
-        "command_args": [
-          {
-            "item_name": "pid",
-            "item_type": "integer",
-            "item_optional": true
-          }
-        ]
-      },
-      {
-        "command_name": "show",
-        "command_description": "Show the specified/all statistics data",
-        "command_args": [
-          {
-            "item_name": "owner",
-            "item_type": "string",
-            "item_optional": true,
-            "item_default": "",
-            "item_description": "module name of the owner of the statistics data"
-          },
-         {
-           "item_name": "name",
-            "item_type": "string",
-            "item_optional": true,
-            "item_default": "",
-            "item_description": "statistics item name of the owner"
-          }
-        ]
-      },
-      {
-        "command_name": "showschema",
-        "command_description": "show the specified/all statistics shema",
-        "command_args": [
-          {
-            "item_name": "owner",
-            "item_type": "string",
-            "item_optional": true,
-            "item_default": "",
-            "item_description": "module name of the owner of the statistics data"
-          },
-         {
-           "item_name": "name",
-            "item_type": "string",
-            "item_optional": true,
-            "item_default": "",
-            "item_description": "statistics item name of the owner"
-          }
-        ]
-      }
-    ],
-    "statistics": [
-      {
-        "item_name": "report_time",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "1970-01-01T00:00:00Z",
-        "item_title": "Report time",
-        "item_description": "A date time when stats module reports",
-        "item_format": "date-time"
-      },
-      {
-        "item_name": "boot_time",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "1970-01-01T00:00:00Z",
-        "item_title": "Boot time",
-        "item_description": "A date time when the stats module starts initially or when the stats module restarts",
-        "item_format": "date-time"
-      },
-      {
-        "item_name": "last_update_time",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "1970-01-01T00:00:00Z",
-        "item_title": "Last update time",
-        "item_description": "The latest date time when the stats module receives from other modules like auth server or b10-init process and so on",
-        "item_format": "date-time"
-      },
-      {
-        "item_name": "timestamp",
-        "item_type": "real",
-        "item_optional": false,
-        "item_default": 0.0,
-        "item_title": "Timestamp",
-        "item_description": "A current time stamp since epoch time (1970-01-01T00:00:00Z)"
-      },
-      {
-        "item_name": "lname",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "",
-        "item_title": "Local Name",
-        "item_description": "A localname of stats module given via CC protocol"
-      }
-    ]
-  }
-}
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
deleted file mode 100755 (executable)
index df0c04c..0000000
+++ /dev/null
@@ -1,628 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2011-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.
-
-"""
-A standalone HTTP server for HTTP/XML interface of statistics in BIND 10
-
-"""
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import os
-import time
-import errno
-import select
-from optparse import OptionParser, OptionValueError
-import http.server
-import socket
-import string
-import xml.etree.ElementTree
-import urllib.parse
-import re
-
-import isc.cc
-import isc.config
-import isc.util.process
-import isc.util.traceback_handler
-from isc.util.address_formatter import AddressFormatter
-
-import isc.log
-from isc.log_messages.stats_httpd_messages import *
-
-isc.log.init("b10-stats-httpd", buffer=True)
-logger = isc.log.Logger("stats-httpd")
-
-# Some constants for debug levels.
-DBG_STATSHTTPD_INIT = logger.DBGLVL_START_SHUT
-DBG_STATSHTTPD_MESSAGING = logger.DBGLVL_COMMAND
-
-# If B10_FROM_SOURCE is set in the environment, we use data files
-# from a directory relative to that, otherwise we use the ones
-# installed on the system
-if "B10_FROM_SOURCE" in os.environ:
-    BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
-        "src" + os.sep + "bin" + os.sep + "stats"
-else:
-    PREFIX = "@prefix@"
-    DATAROOTDIR = "@datarootdir@"
-    BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@"
-    BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
-SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd.spec"
-XML_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xml.tpl"
-XSD_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsd.tpl"
-XSL_TEMPLATE_LOCATION = BASE_LOCATION + os.sep + "stats-httpd-xsl.tpl"
-
-# These variables are paths part of URL.
-# eg. "http://${address}" + XXX_URL_PATH
-XML_URL_PATH = '/bind10/statistics/xml'
-XSD_URL_PATH = '/bind10/statistics.xsd'
-XSL_URL_PATH = '/bind10/statistics.xsl'
-# TODO: This should be considered later.
-XSD_NAMESPACE = 'http://bind10.isc.org/bind10'
-XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
-
-# constant parameter of XML
-XML_ROOT_ELEMENT = 'bind10:statistics'
-XML_ROOT_ATTRIB = { 'xsi:schemaLocation' : '%s %s' % (XSD_NAMESPACE, XSD_URL_PATH),
-                    'xmlns:bind10' : XSD_NAMESPACE,
-                    'xmlns:xsi' : XMLNS_XSI }
-
-# Assign this process name
-isc.util.process.rename()
-
-def item_name_list(element, identifier):
-    """Return a list of strings. The strings are string expression of
-    the first argument element which is dict type. The second argument
-    identifier is a string for specifying the strings which are
-    returned from this method as a list. For example, if we specify as
-
-      item_name_list({'a': {'aa': [0, 1]}, 'b': [0, 1]}, 'a/aa'),
-
-    then it returns
-
-      ['a/aa', 'a/aa[0]', 'a/aa[1]'].
-
-    If an empty string is specified in the second argument, all
-    possible strings are returned as a list.  In that example if we
-    specify an empty string in the second argument, then it returns
-
-      ['a', 'a/aa', 'a/aa[0]', 'a/aa[1]', 'b', 'b[0]', 'b[1]'].
-
-    The key name of element which is in the first argument is sorted.
-    Even if we specify as
-
-      item_name_list({'xx': 0, 'a': 1, 'x': 2}, ''),
-
-    then it returns
-
-      ['a', 'x', 'xx'].
-
-    This method internally invokes isc.cc.data.find(). The arguments
-    of this method are passed to isc.cc.data.find(). So an exception
-    DataNotFoundError or DataTypeError might be raised via
-    isc.cc.data.find() depending on the arguments. See details of
-    isc.cc.data.find() for more information about exceptions"""
-    elem = isc.cc.data.find(element, identifier)
-    ret = []
-    ident = identifier
-    if ident:
-        ret.append(ident)
-    if type(elem) is dict:
-        if ident:
-            ident = ident + '/'
-        for key in sorted(elem.keys()):
-            idstr = '%s%s' % (ident, key)
-            ret += item_name_list(element, idstr)
-    elif type(elem) is list:
-        for i in range(0, len(elem)):
-            idstr = '%s[%d]' % (ident, i)
-            ret += item_name_list(element, idstr)
-    return ret
-
-class HttpHandler(http.server.BaseHTTPRequestHandler):
-    """HTTP handler class for HttpServer class. The class inherits the super
-    class http.server.BaseHTTPRequestHandler. It implements do_GET()
-    and do_HEAD() and overrides log_message()"""
-    def do_GET(self):
-        body = self.send_head()
-        if body is not None:
-            self.wfile.write(body.encode())
-
-    def do_HEAD(self):
-        self.send_head()
-
-    def send_head(self):
-        try:
-            req_path = self.path
-            req_path = urllib.parse.urlsplit(req_path).path
-            req_path = urllib.parse.unquote(req_path)
-            body = None
-            # In case that the requested path (req_path),
-            # e.g. /bind10/statistics/Auth/, is started with
-            # XML_URL_PATH + '/'
-            if req_path.find('%s/' % XML_URL_PATH) == 0:
-                # remove './' from the path if there is
-                req_path = os.path.normpath(req_path)
-                # get the strings tailing after XML_URL_PATH
-                req_path = req_path.lstrip('%s/' % XML_URL_PATH)
-                # remove empty dir names from the path if there are
-                path_dirs = req_path.split('/')
-                path_dirs = [ d for d in filter(None, path_dirs) ]
-                req_path = '/'.join(path_dirs)
-                # pass the complete requested path,
-                # e.g. Auth/queries.upd, to xml_handler()
-                body = self.server.xml_handler(req_path)
-            # In case that the requested path (req_path) is exactly
-            # matched with XSD_URL_PATH
-            elif req_path == XSD_URL_PATH:
-                body = self.server.xsd_handler()
-            # In case that the requested path (req_path) is exactly
-            # matched with XSL_URL_PATH
-            elif req_path == XSL_URL_PATH:
-                body = self.server.xsl_handler()
-            else:
-                if 'Host' in self.headers.keys() and \
-                        ( req_path == '/' or req_path == XML_URL_PATH ):
-                    # redirect to XML URL only when requested with '/' or XML_URL_PATH
-                    toloc = "http://%s%s/" % (self.headers.get('Host'), XML_URL_PATH)
-                    self.send_response(302)
-                    self.send_header("Location", toloc)
-                    self.end_headers()
-                    return None
-                else:
-                    # Couldn't find HOST
-                    self.send_error(404)
-                    return None
-        except StatsHttpdDataError as err:
-            # Couldn't find neither specified module name nor
-            # specified item name
-            self.send_error(404)
-            logger.error(STATSHTTPD_SERVER_DATAERROR, err)
-            return None
-        except Exception as err:
-            self.send_error(500)
-            logger.error(STATSHTTPD_SERVER_ERROR, err)
-            return None
-        else:
-            self.send_response(200)
-            self.send_header("Content-type", "text/xml")
-            self.send_header("Content-Length", len(body))
-            self.send_header("Last-Modified", self.date_time_string(time.time()))
-            self.end_headers()
-            return body
-
-    def log_message(self, format, *args):
-        """overrides the parent method log_message()
-        to use the bind10 logging framework.
-        """
-        logger.debug(DBG_STATSHTTPD_MESSAGING, STATSHTTPD_HTTPLOG,
-                     self.address_string(),
-                     format%args)
-
-class HttpServerError(Exception):
-    """Exception class for HttpServer class. It is intended to be
-    passed from the HttpServer object to the StatsHttpd object."""
-    pass
-
-class HttpServer(http.server.HTTPServer):
-    """HTTP Server class. The class inherits the super
-    http.server.HTTPServer. Some parameters are specified as
-    arguments, which are xml_handler, xsd_handler, xsl_handler, and
-    log_writer. These all are parameters which the StatsHttpd object
-    has. The handler parameters are references of functions which
-    return body of each document. The last parameter log_writer is
-    reference of writer function to just write to
-    sys.stderr.write. They are intended to be referred by HttpHandler
-    object."""
-    def __init__(self, server_address, handler,
-                 xml_handler, xsd_handler, xsl_handler, log_writer):
-        self.server_address = server_address
-        self.xml_handler = xml_handler
-        self.xsd_handler = xsd_handler
-        self.xsl_handler = xsl_handler
-        self.log_writer = log_writer
-        http.server.HTTPServer.__init__(self, server_address, handler)
-
-class StatsHttpdError(Exception):
-    """Exception class for StatsHttpd class. It is intended to be
-    thrown from the the StatsHttpd object to the HttpHandler object or
-    main routine."""
-    pass
-
-class StatsHttpdDataError(Exception):
-    """Exception class for StatsHttpd class. The reason seems to be
-    due to the data. It is intended to be thrown from the the
-    StatsHttpd object to the HttpHandler object or main routine."""
-    pass
-
-class StatsHttpd:
-    """The main class of HTTP server of HTTP/XML interface for
-    statistics module. It handles HTTP requests, and command channel
-    and config channel CC session. It uses select.select function
-    while waiting for clients requests."""
-    def __init__(self):
-        self.running = False
-        self.poll_intval = 0.5
-        self.write_log = sys.stderr.write
-        self.mccs = None
-        self.httpd = []
-        self.open_mccs()
-        self.config = {}
-        self.load_config()
-        self.http_addrs = []
-        self.mccs.start()
-        try:
-            self.open_httpd()
-        except HttpServerError:
-            # if some exception, e.g. address in use, is raised, then it closes mccs and httpd
-            self.close_mccs()
-            raise
-
-    def open_mccs(self):
-        """Opens a ModuleCCSession object"""
-        # create ModuleCCSession
-        logger.debug(DBG_STATSHTTPD_INIT, STATSHTTPD_STARTING_CC_SESSION)
-        self.mccs = isc.config.ModuleCCSession(
-            SPECFILE_LOCATION, self.config_handler, self.command_handler)
-        self.cc_session = self.mccs._session
-
-    def close_mccs(self):
-        """Closes a ModuleCCSession object"""
-        if self.mccs is None:
-            return
-        self.mccs.send_stopping()
-
-        logger.debug(DBG_STATSHTTPD_INIT, STATSHTTPD_CLOSING_CC_SESSION)
-        self.mccs.close()
-        self.mccs = None
-
-    def load_config(self, new_config={}):
-        """Loads configuration from spec file or new configuration
-        from the config manager"""
-        # load config
-        if len(self.config) == 0:
-            self.config = dict([
-                (itm['item_name'], self.mccs.get_value(itm['item_name'])[0])
-                for itm in self.mccs.get_module_spec().get_config_spec()
-                ])
-        self.config.update(new_config)
-        # set addresses and ports for HTTP
-        addrs = []
-        if 'listen_on' in self.config:
-            for cf in self.config['listen_on']:
-                if 'address' in cf and 'port' in cf:
-                    addrs.append((cf['address'], cf['port']))
-        self.http_addrs = addrs
-
-    def open_httpd(self):
-        """Opens sockets for HTTP. Iterating each HTTP address to be
-        configured in spec file"""
-        for addr in self.http_addrs:
-            self.httpd.append(self._open_httpd(addr))
-
-    def _open_httpd(self, server_address):
-        httpd = None
-        try:
-            # get address family for the server_address before
-            # creating HttpServer object. If a specified address is
-            # not numerical, gaierror may be thrown.
-            address_family = socket.getaddrinfo(
-                server_address[0], server_address[1], 0,
-                socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST
-                )[0][0]
-            HttpServer.address_family = address_family
-            httpd = HttpServer(
-                server_address, HttpHandler,
-                self.xml_handler, self.xsd_handler, self.xsl_handler,
-                self.write_log)
-            logger.info(STATSHTTPD_STARTED,
-                        AddressFormatter(server_address, address_family))
-            return httpd
-        except (socket.gaierror, socket.error,
-                OverflowError, TypeError) as err:
-           if httpd:
-                httpd.server_close()
-           raise HttpServerError(
-               "Invalid address %s, port %s: %s: %s" %
-               (server_address[0], server_address[1],
-                err.__class__.__name__, err))
-
-    def close_httpd(self):
-        """Closes sockets for HTTP"""
-        while len(self.httpd)>0:
-            ht = self.httpd.pop()
-            logger.info(STATSHTTPD_CLOSING,
-                        AddressFormatter(ht.server_address))
-            ht.server_close()
-
-    def start(self):
-        """Starts StatsHttpd objects to run. Waiting for client
-        requests by using select.select functions"""
-        self.running = True
-        while self.running:
-            try:
-                (rfd, wfd, xfd) = select.select(
-                    self.get_sockets(), [], [], self.poll_intval)
-            except select.error as err:
-                # select.error exception is caught only in the case of
-                # EINTR, or in other cases it is just thrown.
-                if err.args[0] == errno.EINTR:
-                    (rfd, wfd, xfd) = ([], [], [])
-                else:
-                    raise
-            # FIXME: This module can handle only one request at a
-            # time. If someone sends only part of the request, we block
-            # waiting for it until we time out.
-            # But it isn't so big issue for administration purposes.
-            for fd in rfd + xfd:
-                if fd == self.mccs.get_socket():
-                    self.mccs.check_command(nonblock=False)
-                    continue
-                for ht in self.httpd:
-                    if fd == ht.socket:
-                        ht.handle_request()
-                        break
-        self.stop()
-
-    def stop(self):
-        """Stops the running StatsHttpd objects. Closes CC session and
-        HTTP handling sockets"""
-        logger.info(STATSHTTPD_SHUTDOWN)
-        self.close_httpd()
-        self.close_mccs()
-        self.running = False
-
-    def get_sockets(self):
-        """Returns sockets to select.select"""
-        sockets = []
-        if self.mccs is not None:
-            sockets.append(self.mccs.get_socket())
-        if len(self.httpd) > 0:
-            for ht in self.httpd:
-                sockets.append(ht.socket)
-        return sockets
-
-    def config_handler(self, new_config):
-        """Config handler for the ModuleCCSession object. It resets
-        addresses and ports to listen HTTP requests on."""
-        logger.debug(DBG_STATSHTTPD_MESSAGING, STATSHTTPD_HANDLE_CONFIG,
-                   new_config)
-        errors = []
-        if not self.mccs.get_module_spec().\
-                validate_config(False, new_config, errors):
-                return isc.config.ccsession.create_answer(
-                    1, ", ".join(errors))
-        # backup old config
-        old_config = self.config.copy()
-        self.load_config(new_config)
-        # If the http sockets aren't opened or
-        # if new_config doesn't have 'listen_on', it returns
-        if len(self.httpd) == 0 or 'listen_on' not in new_config:
-            return isc.config.ccsession.create_answer(0)
-        self.close_httpd()
-        try:
-            self.open_httpd()
-        except HttpServerError as err:
-            logger.error(STATSHTTPD_SERVER_INIT_ERROR, err)
-            # restore old config
-            self.load_config(old_config)
-            self.open_httpd()
-            return isc.config.ccsession.create_answer(1, str(err))
-        else:
-            return isc.config.ccsession.create_answer(0)
-
-    def command_handler(self, command, args):
-        """Command handler for the ModuleCCSesson object. It handles
-        "status" and "shutdown" commands."""
-        if command == "status":
-            logger.debug(DBG_STATSHTTPD_MESSAGING,
-                         STATSHTTPD_RECEIVED_STATUS_COMMAND)
-            return isc.config.ccsession.create_answer(
-                0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")")
-        elif command == "shutdown":
-            logger.debug(DBG_STATSHTTPD_MESSAGING,
-                         STATSHTTPD_RECEIVED_SHUTDOWN_COMMAND)
-            self.running = False
-            return isc.config.ccsession.create_answer(0)
-        else:
-            logger.debug(DBG_STATSHTTPD_MESSAGING,
-                         STATSHTTPD_RECEIVED_UNKNOWN_COMMAND, command)
-            return isc.config.ccsession.create_answer(
-                1, "Unknown command: " + str(command))
-
-    def get_stats_data(self, owner=None, name=None):
-        """Requests statistics data to the Stats daemon and returns
-        the data which obtains from it. The first argument is the
-        module name which owns the statistics data, the second
-        argument is one name of the statistics items which the the
-        module owns. The second argument cannot be specified when the
-        first argument is not specified. It returns the statistics
-        data of the specified module or item. When the session timeout
-        or the session error is occurred, it raises
-        StatsHttpdError. When the stats daemon returns none-zero
-        value, it raises StatsHttpdDataError."""
-        param = {}
-        if owner is None and name is None:
-            param = None
-        if owner is not None:
-            param['owner'] = owner
-        if name is not None:
-            param['name'] = name
-        try:
-            return self.mccs.rpc_call('show', 'Stats', params=param)
-        except (isc.cc.session.SessionTimeout,
-                isc.cc.session.SessionError,
-                isc.config.RPCRecipientMissing) as err:
-            raise StatsHttpdError("%s: %s" %
-                                  (err.__class__.__name__, err))
-        except isc.config.RPCError as e:
-            raise StatsHttpdDataError("Stats module: %s" % str(e))
-
-    def get_stats_spec(self, owner=None, name=None):
-        """Requests statistics data to the Stats daemon and returns
-        the data which obtains from it. The first argument is the
-        module name which owns the statistics data, the second
-        argument is one name of the statistics items which the the
-        module owns. The second argument cannot be specified when the
-        first argument is not specified. It returns the statistics
-        specification of the specified module or item. When the
-        session timeout or the session error is occurred, it raises
-        StatsHttpdError. When the stats daemon returns none-zero
-        value, it raises StatsHttpdDataError."""
-        param = {}
-        if owner is None and name is None:
-            param = None
-        if owner is not None:
-            param['owner'] = owner
-        if name is not None:
-            param['name'] = name
-        try:
-            return self.mccs.rpc_call('showschema', 'Stats', params=param)
-        except isc.config.RPCError as e:
-            raise StatsHttpdDataError("Stats module: %s" % str(e))
-        except (isc.cc.session.SessionTimeout,
-                isc.cc.session.SessionError) as err:
-            raise StatsHttpdError("%s: %s" %
-                                  (err.__class__.__name__, err))
-
-
-    def xml_handler(self, path=''):
-        """Requests the specified statistics data and specification by
-        using the functions get_stats_data and get_stats_spec
-        respectively and loads the XML template file and returns the
-        string of the XML document.The argument is a path in the
-        requested URI. For example, the path is assumed to be like
-        ${module_name}/${top_level_item_name}/${second_level_item_name}/..."""
-
-        dirs = [ d for d in path.split("/") if len(d) > 0 ]
-        module_name = None
-        item_name = None
-        if len(dirs) > 0:
-            module_name = dirs[0]
-        if len(dirs) > 1:
-            item_name = dirs[1]
-            # removed an index string when list-type value is
-            # requested. Because such a item name can be accept by the
-            # stats module currently.
-            item_name = re.sub('\[\d+\]$', '', item_name)
-        stats_spec = self.get_stats_spec(module_name, item_name)
-        stats_data = self.get_stats_data(module_name, item_name)
-        path_list = []
-        try:
-            path_list = item_name_list(stats_data, path)
-        except isc.cc.data.DataNotFoundError as err:
-            raise StatsHttpdDataError(
-                "%s: %s" % (err.__class__.__name__, err))
-        item_list = []
-        for path in path_list:
-            dirs = path.split("/")
-            if len(dirs) < 2: continue
-            item = {}
-            item['identifier'] = path
-            value = isc.cc.data.find(stats_data, path)
-            if type(value) is bool:
-                value = str(value).lower()
-            if type(value) is dict or type(value) is list:
-                value = None
-            if value is not None:
-                item['value'] = str(value)
-            owner = dirs[0]
-            item['owner'] = owner
-            item['uri'] = urllib.parse.quote('%s/%s' % (XML_URL_PATH, path))
-            item_path = '/'.join(dirs[1:])
-            spec = isc.config.find_spec_part(stats_spec[owner], item_path)
-            for key in ['name', 'type', 'description', 'title', \
-                            'optional', 'default', 'format']:
-                value = spec.get('item_%s' % key)
-                if type(value) is bool:
-                    value = str(value).lower()
-                if type(value) is dict or type(value) is list:
-                    value = None
-                if value is not None:
-                    item[key] = str(value)
-            item_list.append(item)
-        xml_elem = xml.etree.ElementTree.Element(
-            XML_ROOT_ELEMENT, attrib=XML_ROOT_ATTRIB)
-        for item in item_list:
-            item_elem = xml.etree.ElementTree.Element('item', attrib=item)
-            xml_elem.append(item_elem)
-        # The coding conversion is tricky. xml..tostring() of Python 3.2
-        # returns bytes (not string) regardless of the coding, while
-        # tostring() of Python 3.1 returns a string.  To support both
-        # cases transparently, we first make sure tostring() returns
-        # bytes by specifying utf-8 and then convert the result to a
-        # plain string (code below assume it).
-        # FIXME: Non-ASCII characters might be lost here. Consider how
-        # the whole system should handle non-ASCII characters.
-        xml_string = str(xml.etree.ElementTree.tostring(xml_elem, encoding='utf-8'),
-                         encoding='us-ascii')
-        self.xml_body = self.open_template(XML_TEMPLATE_LOCATION).substitute(
-            xml_string=xml_string, xsl_url_path=XSL_URL_PATH)
-        return self.xml_body
-
-    def xsd_handler(self):
-        """Loads the XSD template file, replaces the variable strings,
-        and returns the string of the XSD document."""
-        self.xsd_body = self.open_template(XSD_TEMPLATE_LOCATION).substitute(
-            xsd_namespace=XSD_NAMESPACE)
-        return self.xsd_body
-
-    def xsl_handler(self):
-        """Loads the XSL template file, replaces the variable strings,
-        and returns the string of the XSL document."""
-        self.xsl_body = self.open_template(XSL_TEMPLATE_LOCATION).substitute(
-            xsd_namespace=XSD_NAMESPACE)
-        return self.xsl_body
-
-    def open_template(self, file_name):
-        """It opens a template file, and it loads all lines to a
-        string variable and returns string. Template object includes
-        the variable. Limitation of a file size isn't needed there. XXXX"""
-        lines = None
-        try:
-            with open(file_name, 'r') as f:
-                lines = "".join(f.readlines())
-        except IOError as err:
-            raise StatsHttpdDataError(
-                "%s: %s" % (err.__class__.__name__, err))
-        return string.Template(lines)
-
-def main():
-    try:
-        parser = OptionParser()
-        parser.add_option(
-            "-v", "--verbose", dest="verbose", action="store_true",
-            help="enable maximum debug logging")
-        (options, args) = parser.parse_args()
-        if options.verbose:
-            isc.log.init("b10-stats-httpd", "DEBUG", 99, buffer=True)
-        stats_httpd = StatsHttpd()
-        stats_httpd.start()
-    except OptionValueError as ove:
-        logger.fatal(STATSHTTPD_BAD_OPTION_VALUE, ove)
-        sys.exit(1)
-    except isc.cc.session.SessionError as se:
-        logger.fatal(STATSHTTPD_CC_SESSION_ERROR, se)
-        sys.exit(1)
-    except HttpServerError as hse:
-        logger.fatal(STATSHTTPD_START_SERVER_INIT_ERROR, hse)
-        sys.exit(1)
-    except KeyboardInterrupt as kie:
-        logger.info(STATSHTTPD_STOPPED_BY_KEYBOARD)
-
-    logger.info(STATSHTTPD_EXITING)
-
-if __name__ == "__main__":
-    isc.util.traceback_handler.traceback_handler(main)
diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes
deleted file mode 100644 (file)
index 930a745..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-# No namespace declaration - these constants go in the global namespace
-# of the stats_httpd_messages python module.
-
-% STATSHTTPD_BAD_OPTION_VALUE bad command line argument: %1
-The stats-httpd module was called with a bad command-line argument
-and will not start.
-
-% STATSHTTPD_CC_SESSION_ERROR error connecting to message bus: %1
-The stats-httpd module was unable to connect to the BIND 10 command
-and control bus. A likely problem is that the message bus daemon
-(b10-msgq) is not running. The stats-httpd module will now shut down.
-
-% STATSHTTPD_CLOSING closing %1
-The stats-httpd daemon will stop listening for requests on the given
-address and port number.
-
-% STATSHTTPD_CLOSING_CC_SESSION stopping cc session
-Debug message indicating that the stats-httpd module is disconnecting
-from the command and control bus.
-
-% STATSHTTPD_EXITING exiting
-The stats HTTP server is exiting.
-
-% STATSHTTPD_HANDLE_CONFIG reading configuration: %1
-The stats-httpd daemon has received new configuration data and will now
-process it. The (changed) data is printed.
-
-% STATSHTTPD_HTTPLOG %1 %2
-Debug HTTP log message. These are the messages logged by the http server
-instance. For most logs, the message shows HTTP client and query
-information like HTTP method, URI, and status code, but the http server
-can also log other information, such as extended status reports.
-
-% STATSHTTPD_RECEIVED_SHUTDOWN_COMMAND shutdown command received
-A shutdown command was sent to the stats-httpd module, and it will
-now shut down.
-
-% STATSHTTPD_RECEIVED_STATUS_COMMAND received command to return status
-A status command was sent to the stats-httpd module, and it will
-respond with 'Stats Httpd is up.' and its PID.
-
-% STATSHTTPD_RECEIVED_UNKNOWN_COMMAND received unknown command: %1
-An unknown command has been sent to the stats-httpd module. The
-stats-httpd module will respond with an error, and the command will
-be ignored.
-
-% STATSHTTPD_SERVER_DATAERROR HTTP server data error: %1
-An internal error occurred while handling an HTTP request. An HTTP 404
-response will be sent back, and the specific error is printed. This
-is an error condition that likely points the specified data
-corresponding to the requested URI is incorrect.
-
-% STATSHTTPD_SERVER_ERROR HTTP server error: %1
-An internal error occurred while handling an HTTP request. An HTTP 500
-response will be sent back, and the specific error is printed. This
-is an error condition that likely points to a module that is not
-responding correctly to statistics requests.
-
-% STATSHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1
-There was a problem initializing the HTTP server in the stats-httpd
-module upon receiving its configuration data. The most likely cause
-is a port binding problem or a bad configuration value. The specific
-error is printed in the message. The new configuration is ignored,
-and an error is sent back.
-
-% STATSHTTPD_SHUTDOWN shutting down
-The stats-httpd daemon is shutting down.
-
-% STATSHTTPD_STARTED listening on %1
-The stats-httpd daemon will now start listening for requests on the
-given address and port number.
-
-% STATSHTTPD_STARTING_CC_SESSION starting cc session
-Debug message indicating that the stats-httpd module is connecting to
-the command and control bus.
-
-% STATSHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1
-There was a problem initializing the HTTP server in the stats-httpd
-module upon startup. The most likely cause is that it was not able
-to bind to the listening port. The specific error is printed, and the
-module will shut down.
-
-% STATSHTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
-There was a keyboard interrupt signal to stop the stats-httpd
-daemon. The daemon will now shut down.
-
-% STATSHTTPD_UNKNOWN_CONFIG_ITEM unknown configuration item: %1
-The stats-httpd daemon received a configuration update from the
-configuration manager. However, one of the items in the
-configuration is unknown. The new configuration is ignored, and an
-error is sent back. As possible cause is that there was an upgrade
-problem, and the stats-httpd version is out of sync with the rest of
-the system.
diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes
deleted file mode 100644 (file)
index 38dddcc..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
-#
-# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS.  IN NO EVENT SHALL ISC 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.
-
-# No namespace declaration - these constants go in the global namespace
-# of the stats_messages python module.
-
-% STATS_BAD_OPTION_VALUE bad command line argument: %1
-The stats module was called with a bad command-line argument and will
-not start.
-
-% STATS_CC_SESSION_ERROR error connecting to message bus: %1
-The stats module was unable to connect to the BIND 10 command and
-control bus. A likely problem is that the message bus daemon
-(b10-msgq) is not running. The stats module will now shut down.
-
-% STATS_EXITING exiting
-The stats module process is exiting.
-
-% STATS_RECEIVED_INVALID_STATISTICS_DATA received invalid statistics data from %1
-Invalid statistics data has been received from the module while
-polling and it has been discarded.
-
-% STATS_RECEIVED_NEW_CONFIG received new configuration: %1
-This debug message is printed when the stats module has received a
-configuration update from the configuration manager.
-
-% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema
-The stats module received a command to show all statistics schemas of all modules.
-
-% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1
-The stats module received a command to show the specified statistics schema of the specified module.
-
-% STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics
-The stats module received a command to show all statistics that it has
-collected.
-
-% STATS_RECEIVED_SHOW_NAME_COMMAND received command to show statistics for %1
-The stats module received a command to show the statistics that it has
-collected for the given item.
-
-% STATS_RECEIVED_SHUTDOWN_COMMAND shutdown command received
-A shutdown command was sent to the stats module and it will now shut down.
-
-% STATS_RECEIVED_STATUS_COMMAND received command to return status
-A status command was sent to the stats module. It will return a
-response indicating that it is running normally.
-
-% STATS_RECEIVED_UNKNOWN_COMMAND received unknown command: %1
-An unknown command has been sent to the stats module. The stats module
-will respond with an error and the command will be ignored.
-
-% STATS_SEND_STATISTICS_REQUEST requesting %1 to send statistics
-This debug message is printed when a request is sent to the module
-to send its data to the stats module.
-
-% STATS_SKIP_COLLECTING skipped collecting statistics from %1
-The stats module temporarily encountered an internal messaging bus error while
-collecting statistics from the module, then it skipped collecting statistics
-from it and will try to collect from the next module. The lack of statistics
-will be recovered at the next polling round.
-
-% STATS_SKIP_POLLING skipped polling statistics to modules
-The stats module temporarily encountered an internal messaging bus error while
-collecting initial information to collect statistics from the init module, then
-it skipped polling statistics and will try to do next time.
-
-% STATS_STARTING starting
-The stats module will be now starting.
-
-% STATS_START_ERROR stats module error: %1
-An internal error occurred while starting the stats module. The stats
-module will be now shutting down.
-
-% STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
-There was a keyboard interrupt signal to stop the stats module. The
-daemon will now shut down.
-
-% STATS_UNKNOWN_COMMAND_IN_SPEC unknown command in specification file: %1
-The specification file for the stats module contains a command that
-is unknown in the implementation. The most likely cause is an
-installation problem, where the specification file stats.spec is
-from a different version of BIND 10 than the stats module itself.
-Please check your installation.
diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am
deleted file mode 100644 (file)
index 06d11a1..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-SUBDIRS = testdata .
-
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = stats_test.py stats-httpd_test.py
-EXTRA_DIST = $(PYTESTS) test_utils.py
-CLEANFILES = test_utils.pyc
-
-# 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/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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests:$(abs_top_builddir)/src/bin/msgq:$(abs_top_builddir)/src/lib/python/isc/config \
-       B10_FROM_SOURCE=$(abs_top_srcdir) \
-       BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
-       CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/bin/stats/tests/testdata \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/stats-httpd_test.py b/src/bin/stats/tests/stats-httpd_test.py
deleted file mode 100644 (file)
index a37302d..0000000
+++ /dev/null
@@ -1,1128 +0,0 @@
-# Copyright (C) 2011-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.
-
-"""
-In each of these tests we start several virtual components. They are
-not the real components, no external processes are started. They are
-just simple mock objects running each in its own thread and pretending
-to be bind10 modules. This helps testing the stats http server in a
-close to real environment.
-"""
-
-import unittest
-import os
-import imp
-import socket
-import errno
-import select
-import string
-import time
-import threading
-import http.client
-import xml.etree.ElementTree
-import random
-import urllib.parse
-import sys
-# load this module for xml validation with xsd. For this test, an
-# installation of lxml is required in advance. See http://lxml.de/.
-try:
-    from lxml import etree as lxml_etree
-except ImportError:
-    lxml_etree = None
-
-import isc
-import isc.log
-import stats_httpd
-import stats
-from test_utils import ThreadingServerManager, SignalHandler, \
-    MyStatsHttpd, CONST_BASETIME
-from isc.testutils.ccsession_mock import MockModuleCCSession
-from isc.config import RPCRecipientMissing, RPCError
-
-# This test suite uses xml.etree.ElementTree.XMLParser via
-# xml.etree.ElementTree.parse. On the platform where expat isn't
-# installed, ImportError is raised and it's failed. Check expat is
-# available before the test invocation. Skip this test if it's
-# unavailable.
-try:
-    # ImportError raised if xpat is unavailable
-    xml_parser = xml.etree.ElementTree.XMLParser()
-except ImportError:
-    xml_parser = None
-
-# set XML Namespaces for testing
-XMLNS_XSL = "http://www.w3.org/1999/XSL/Transform"
-XMLNS_XHTML = "http://www.w3.org/1999/xhtml"
-XMLNS_XSD = "http://www.w3.org/2001/XMLSchema"
-XMLNS_XSI = stats_httpd.XMLNS_XSI
-
-DUMMY_DATA = {
-    'Init' : {
-        "boot_time": time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME)
-        },
-    'Auth' : {
-        "queries.tcp": 6,
-        "queries.udp": 4,
-        "queries.perzone": [{
-                "zonename": "test1.example",
-                "queries.tcp": 10,
-                "queries.udp": 8
-                }, {
-                "zonename": "test2.example",
-                "queries.tcp": 8,
-                "queries.udp": 6
-                }],
-        "nds_queries.perzone": {
-                "test10.example": {
-                    "queries.tcp": 10,
-                    "queries.udp": 8
-                  },
-                "test20.example": {
-                    "queries.tcp": 8,
-                    "queries.udp": 6
-                  }
-                }
-        },
-    'Stats' : {
-        "report_time": time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-        "boot_time": time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-        "last_update_time": time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-        "lname": "4d70d40a_c@host",
-        "timestamp": time.mktime(CONST_BASETIME)
-        }
-    }
-
-# Bad practice: this should be localized
-stats._BASETIME = CONST_BASETIME
-stats.get_timestamp = lambda: time.mktime(CONST_BASETIME)
-stats.get_datetime = lambda x=None: time.strftime("%Y-%m-%dT%H:%M:%SZ", CONST_BASETIME)
-
-def get_availaddr(address='127.0.0.1', port=8001):
-    """returns a tuple of address and port which is available to
-    listen on the platform. The first argument is a address for
-    search. The second argument is a port for search. If a set of
-    address and port is failed on the search for the availability, the
-    port number is increased and it goes on the next trial until the
-    available set of address and port is looked up. If the port number
-    reaches over 65535, it may stop the search and raise a
-    OverflowError exception."""
-    while True:
-        for addr in socket.getaddrinfo(
-            address, port, 0,
-            socket.SOCK_STREAM, socket.IPPROTO_TCP):
-            sock = socket.socket(addr[0], socket.SOCK_STREAM)
-            try:
-                sock.bind((address, port))
-                return (address, port)
-            except socket.error:
-                continue
-            finally:
-                if sock: sock.close()
-        # This address and port number are already in use.
-        # next port number is added
-        port = port + 1
-
-def is_ipv6_enabled(address='::1', port=8001):
-    """checks IPv6 enabled on the platform. address for check is '::1'
-    and port for check is random number between 8001 and
-    65535. Retrying is 3 times even if it fails. The built-in socket
-    module provides a 'has_ipv6' parameter, but it's not used here
-    because there may be a situation where the value is True on an
-    environment where the IPv6 config is disabled."""
-    for p in random.sample(range(port, 65535), 3):
-        try:
-            sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-            sock.bind((address, p))
-            return True
-        except socket.error:
-            continue
-        finally:
-            if sock: sock.close()
-    return False
-
-class TestItemNameList(unittest.TestCase):
-
-    def test_item_name_list(self):
-        # for a one-element list
-        self.assertEqual(['a'],
-                         stats_httpd.item_name_list({'a':1}, 'a'))
-        # for a dict under a dict
-        self.assertEqual(['a','a/b'],
-                         stats_httpd.item_name_list({'a':{'b':1}}, 'a'))
-        self.assertEqual(['a/b'],
-                         stats_httpd.item_name_list({'a':{'b':1}}, 'a/b'))
-        self.assertEqual(['a','a/b','a/b/c'],
-                         stats_httpd.item_name_list({'a':{'b':{'c':1}}}, 'a'))
-        self.assertEqual(['a/b','a/b/c'],
-                         stats_httpd.item_name_list({'a':{'b':{'c':1}}},
-                                                    'a/b'))
-        self.assertEqual(['a/b/c'],
-                         stats_httpd.item_name_list({'a':{'b':{'c':1}}},
-                                                    'a/b/c'))
-        # for a list under a dict
-        self.assertEqual(['a[2]'],
-                         stats_httpd.item_name_list({'a':[1,2,3]}, 'a[2]'))
-        self.assertEqual(['a', 'a[0]', 'a[1]', 'a[2]'],
-                         stats_httpd.item_name_list({'a':[1,2,3]}, 'a'))
-        self.assertEqual(['a', 'a[0]', 'a[1]', 'a[2]'],
-                         stats_httpd.item_name_list({'a':[1,2,3]}, ''))
-        # for a list under a dict under a dict
-        self.assertEqual(['a', 'a/b', 'a/b[0]', 'a/b[1]', 'a/b[2]'],
-                         stats_httpd.item_name_list({'a':{'b':[1,2,3]}}, 'a'))
-        self.assertEqual(['a', 'a/b', 'a/b[0]', 'a/b[1]', 'a/b[2]'],
-                         stats_httpd.item_name_list({'a':{'b':[1,2,3]}}, ''))
-        self.assertEqual(['a/b', 'a/b[0]', 'a/b[1]', 'a/b[2]'],
-                         stats_httpd.item_name_list({'a':{'b':[1,2,3]}}, 'a/b'))
-        # for a mixed case of the above
-        self.assertEqual(['a', 'a/b', 'a/b[0]', 'a/b[1]', 'a/b[2]', 'a/c'],
-                         stats_httpd.item_name_list(
-                {'a':{'b':[1,2,3], 'c':1}}, 'a'))
-        self.assertEqual(['a/b', 'a/b[0]', 'a/b[1]', 'a/b[2]'],
-                         stats_httpd.item_name_list(
-                {'a':{'b':[1,2,3], 'c':1}}, 'a/b'))
-        self.assertEqual(['a/c'],
-                         stats_httpd.item_name_list(
-                {'a':{'b':[1,2,3], 'c':1}}, 'a/c'))
-        # for specifying a wrong identifier which is not found in
-        # element
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                         stats_httpd.item_name_list, {'x':1}, 'a')
-        # for specifying a string in element and an empty string in
-        # identifier
-        self.assertEqual([],
-                         stats_httpd.item_name_list('a', ''))
-        # for specifying empty strings in element and identifier
-        self.assertEqual([],
-                         stats_httpd.item_name_list('', ''))
-        # for specifying wrong element, which is an non-empty string,
-        # and an non-empty string in identifier
-        self.assertRaises(isc.cc.data.DataTypeError,
-                         stats_httpd.item_name_list, 'a', 'a')
-        # for specifying None in element and identifier
-        self.assertRaises(isc.cc.data.DataTypeError,
-                         stats_httpd.item_name_list, None, None)
-        # for specifying non-dict in element
-        self.assertRaises(isc.cc.data.DataTypeError,
-                         stats_httpd.item_name_list, [1,2,3], 'a')
-        self.assertRaises(isc.cc.data.DataTypeError,
-                         stats_httpd.item_name_list, [1,2,3], '')
-        # for checking key names sorted which consist of element
-        num = 11
-        keys = [ 'a', 'aa', 'b' ]
-        keys.sort(reverse=True)
-        dictlist = dict([ (k, list(range(num))) for k in keys ])
-        keys.sort()
-        ans = []
-        for k in keys:
-            ans += [k] + [ '%s[%d]' % (k, i) for i in range(num) ]
-        self.assertEqual(ans,
-                         stats_httpd.item_name_list(dictlist, ''))
-
-class TestHttpHandler(unittest.TestCase):
-    """Tests for HttpHandler class"""
-    def setUp(self):
-        # set the signal handler for deadlock
-        self.sig_handler = SignalHandler(self.fail)
-        DUMMY_DATA['Stats']['lname'] = 'test-lname'
-        (self.address, self.port) = get_availaddr()
-        self.stats_httpd_server = ThreadingServerManager(MyStatsHttpd,
-                                                         (self.address,
-                                                          self.port))
-        self.stats_httpd = self.stats_httpd_server.server
-        self.stats_httpd_server.run()
-        self.client = http.client.HTTPConnection(self.address, self.port)
-        self.client._http_vsn_str = 'HTTP/1.0\n'
-        self.client.connect()
-
-    def tearDown(self):
-        self.client.close()
-        self.stats_httpd_server.shutdown()
-        # reset the signal handler
-        self.sig_handler.reset()
-
-    @unittest.skipUnless(xml_parser, "skipping the test using XMLParser")
-    def test_do_GET(self):
-        self.assertTrue(type(self.stats_httpd.httpd) is list)
-        self.assertEqual(len(self.stats_httpd.httpd), 1)
-        self.assertEqual((self.address, self.port), self.stats_httpd.http_addrs[0])
-
-        def check_XML_URL_PATH(path=''):
-            url_path = '%s/%s' % (stats_httpd.XML_URL_PATH, path)
-            url_path = urllib.parse.quote(url_path)
-            self.client.putrequest('GET', url_path)
-            self.client.endheaders()
-            response = self.client.getresponse()
-            self.assertEqual(response.getheader("Content-type"), "text/xml")
-            self.assertGreater(int(response.getheader("Content-Length")), 0)
-            self.assertEqual(response.status, 200)
-            xml_doctype = response.readline().decode()
-            xsl_doctype = response.readline().decode()
-            self.assertGreater(len(xml_doctype), 0)
-            self.assertGreater(len(xsl_doctype), 0)
-            root = xml.etree.ElementTree.parse(response).getroot()
-            self.assertGreater(root.tag.find('statistics'), 0)
-            schema_loc = '{%s}schemaLocation' % XMLNS_XSI
-            # check the path of XSD
-            self.assertEqual(root.attrib[schema_loc],
-                             stats_httpd.XSD_NAMESPACE + ' '
-                             + stats_httpd.XSD_URL_PATH)
-            # check the path of XSL
-            self.assertTrue(xsl_doctype.startswith(
-                    '<?xml-stylesheet type="text/xsl" href="' +
-                    stats_httpd.XSL_URL_PATH
-                    + '"?>'))
-            # check whether the list of 'identifier' attributes in
-            # root is same as the list of item names in DUMMY_DATA
-            id_list = [ elm.attrib['identifier'] for elm in root ]
-            item_list = [ it for it in \
-                              stats_httpd.item_name_list(DUMMY_DATA, path) \
-                              if len(it.split('/')) > 1 ]
-            self.assertEqual(id_list, item_list)
-            for elem in root:
-                attr = elem.attrib
-                value = isc.cc.data.find(DUMMY_DATA, attr['identifier'])
-                # No 'value' attribute should be found in the 'item'
-                # element when datatype of the value is list or dict.
-                if type(value) is list or type(value) is dict:
-                    self.assertFalse('value' in attr)
-                # The value of the 'value' attribute should be checked
-                # after casting it to string type if datatype of the
-                # value is int or float. Because attr['value'] returns
-                # string type even if its value is int or float.
-                elif type(value) is int or type(value) is float:
-                    self.assertEqual(attr['value'], str(value))
-                else:
-                    self.assertEqual(attr['value'], value)
-
-        # URL is '/bind10/statistics/xml'
-        check_XML_URL_PATH()
-        for path in stats_httpd.item_name_list(DUMMY_DATA, ''):
-            check_XML_URL_PATH(path)
-
-        def check_XSD_URL_PATH():
-            url_path = stats_httpd.XSD_URL_PATH
-            url_path = urllib.parse.quote(url_path)
-            self.client.putrequest('GET', url_path)
-            self.client.endheaders()
-            response = self.client.getresponse()
-            self.assertEqual(response.getheader("Content-type"), "text/xml")
-            self.assertGreater(int(response.getheader("Content-Length")), 0)
-            self.assertEqual(response.status, 200)
-            root = xml.etree.ElementTree.parse(response).getroot()
-            url_xmlschema = '{%s}' % XMLNS_XSD
-            self.assertGreater(root.tag.find('schema'), 0)
-            self.assertTrue(hasattr(root, 'attrib'))
-            self.assertTrue('targetNamespace' in root.attrib)
-            self.assertEqual(root.attrib['targetNamespace'],
-                             stats_httpd.XSD_NAMESPACE)
-
-        # URL is '/bind10/statistics/xsd'
-        check_XSD_URL_PATH()
-
-        def check_XSL_URL_PATH():
-            url_path = stats_httpd.XSL_URL_PATH
-            url_path = urllib.parse.quote(url_path)
-            self.client.putrequest('GET', url_path)
-            self.client.endheaders()
-            response = self.client.getresponse()
-            self.assertEqual(response.getheader("Content-type"), "text/xml")
-            self.assertGreater(int(response.getheader("Content-Length")), 0)
-            self.assertEqual(response.status, 200)
-            root = xml.etree.ElementTree.parse(response).getroot()
-            url_trans = '{%s}' % XMLNS_XSL
-            url_xhtml = '{%s}' % XMLNS_XHTML
-            self.assertEqual(root.tag, url_trans + 'stylesheet')
-
-        # URL is '/bind10/statistics/xsl'
-        check_XSL_URL_PATH()
-
-        # 302 redirect
-        self.client._http_vsn_str = 'HTTP/1.1'
-        self.client.putrequest('GET', '/')
-        self.client.putheader('Host', self.address)
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 302)
-        self.assertEqual(response.getheader('Location'),
-                         "http://%s:%d%s/" % (self.address, self.port, stats_httpd.XML_URL_PATH))
-
-        # 404 NotFound (random path)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', '/path/to/foo/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', '/bind10/foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', '/bind10/statistics/foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + 'Auth') # with no slash
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-        # 200 ok
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/#foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/?foo=bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-        # 404 NotFound (too long path)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/Init/boot_time/a')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-        # 404 NotFound (nonexistent module name)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/Foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSD_URL_PATH + '/Foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSL_URL_PATH + '/Foo')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-        # 404 NotFound (nonexistent item name)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/Foo/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSD_URL_PATH + '/Foo/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSL_URL_PATH + '/Foo/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-        # 404 NotFound (existent module but nonexistent item name)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/Auth/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSD_URL_PATH + '/Auth/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-        self.client._http_vsn_str = 'HTTP/1.0'
-        self.client.putrequest('GET', stats_httpd.XSL_URL_PATH + '/Auth/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-    def test_do_GET_failed1(self):
-        # failure case (Stats is down, so rpc_call() results in an exception)
-        # Note: this should eventually be RPCRecipientMissing.
-        self.stats_httpd._rpc_answers.append(
-            isc.cc.session.SessionTimeout('timeout'))
-
-        # request XML
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 500)
-
-        # request XSD
-        self.client.putrequest('GET', stats_httpd.XSD_URL_PATH)
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-        # request XSL
-        self.client.putrequest('GET', stats_httpd.XSL_URL_PATH)
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-    def test_do_GET_failed2(self):
-        # failure case(Stats replies an error)
-        self.stats_httpd._rpc_answers.append(
-            RPCError(1, "specified arguments are incorrect: I have an error."))
-
-        # request XML
-        self.client.putrequest('GET', stats_httpd.XML_URL_PATH + '/')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-        # request XSD
-        self.stats_httpd._rpc_answers.append(
-            RPCError(1, "specified arguments are incorrect: I have an error."))
-        self.client.putrequest('GET', stats_httpd.XSD_URL_PATH)
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-        # request XSL
-        self.stats_httpd._rpc_answers.append(
-            RPCError(1, "specified arguments are incorrect: I have an error."))
-        self.client.putrequest('GET', stats_httpd.XSL_URL_PATH)
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-    def test_do_HEAD(self):
-        self.client.putrequest('HEAD', stats_httpd.XML_URL_PATH + '/')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 200)
-
-        self.client.putrequest('HEAD', '/path/to/foo/bar')
-        self.client.endheaders()
-        response = self.client.getresponse()
-        self.assertEqual(response.status, 404)
-
-    @unittest.skipUnless(lxml_etree, "skipping XML validation with XSD")
-    def test_xml_validation_with_xsd(self):
-        """Tests for XML validation with XSD. If lxml is not
-        installed, this tests would be skipped."""
-        def request_xsd():
-            url_path = stats_httpd.XSD_URL_PATH
-            url_path = urllib.parse.quote(url_path)
-            self.client.putrequest('GET', url_path)
-            self.client.endheaders()
-            xsd_doc = self.client.getresponse()
-            xsd_doc = lxml_etree.parse(xsd_doc)
-            return lxml_etree.XMLSchema(xsd_doc)
-
-        def request_xmldoc(path=''):
-            url_path = '%s/%s' % (stats_httpd.XML_URL_PATH, path)
-            url_path = urllib.parse.quote(url_path)
-            self.client.putrequest('GET', url_path)
-            self.client.endheaders()
-            xml_doc = self.client.getresponse()
-            return lxml_etree.parse(xml_doc)
-
-        # request XSD and XML
-        xsd = request_xsd()
-        xml_doc = request_xmldoc()
-        # do validation
-        self.assertTrue(xsd.validate(xml_doc))
-
-        # validate each paths in DUMMY_DATA
-        for path in stats_httpd.item_name_list(DUMMY_DATA, ''):
-            # request XML
-            xml_doc = request_xmldoc(path)
-            # do validation
-            self.assertTrue(xsd.validate(xml_doc))
-
-class TestHttpServerError(unittest.TestCase):
-    """Tests for HttpServerError exception"""
-    def test_raises(self):
-        try:
-            raise stats_httpd.HttpServerError('Nothing')
-        except stats_httpd.HttpServerError as err:
-            self.assertEqual(str(err), 'Nothing')
-
-class TestHttpServer(unittest.TestCase):
-    """Tests for HttpServer class"""
-    def setUp(self):
-        # set the signal handler for deadlock
-        self.sig_handler = SignalHandler(self.fail)
-
-    def tearDown(self):
-        if hasattr(self, "stats_httpd"):
-            self.stats_httpd.stop()
-        # reset the signal handler
-        self.sig_handler.reset()
-
-    def test_httpserver(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertEqual(type(self.stats_httpd.httpd), list)
-        self.assertEqual(len(self.stats_httpd.httpd), 1)
-        for httpd in self.stats_httpd.httpd:
-            self.assertTrue(isinstance(httpd, stats_httpd.HttpServer))
-
-class TestStatsHttpdError(unittest.TestCase):
-    """Tests for StatsHttpdError exception"""
-
-    def test_raises1(self):
-        try:
-            raise stats_httpd.StatsHttpdError('Nothing')
-        except stats_httpd.StatsHttpdError as err:
-            self.assertEqual(str(err), 'Nothing')
-
-    def test_raises2(self):
-        try:
-            raise stats_httpd.StatsHttpdDataError('Nothing')
-        except stats_httpd.StatsHttpdDataError as err:
-            self.assertEqual(str(err), 'Nothing')
-
-class TestStatsHttpd(unittest.TestCase):
-    """Tests for StatsHttpd class"""
-
-    def setUp(self):
-        # set the signal handler for deadlock
-        self.sig_handler = SignalHandler(self.fail)
-        # checking IPv6 enabled on this platform
-        self.ipv6_enabled = is_ipv6_enabled()
-        # instantiation of StatsHttpd indirectly calls gethostbyaddr(), which
-        # can block for an uncontrollable period, leading many undesirable
-        # results.  We should rather eliminate the reliance, but until we
-        # can make such fundamental cleanup we replace it with a faked method;
-        # in our test scenario the return value doesn't matter.
-        self.__gethostbyaddr_orig = socket.gethostbyaddr
-        socket.gethostbyaddr = lambda x: ('test.example.', [], None)
-
-        # Some tests replace this library function.  Keep the original for
-        # restor
-        self.__orig_select_select = select.select
-
-    def tearDown(self):
-        socket.gethostbyaddr = self.__gethostbyaddr_orig
-        if hasattr(self, "stats_httpd"):
-            self.stats_httpd.stop()
-        # reset the signal handler
-        self.sig_handler.reset()
-
-        # restore original of replaced library
-        select.select = self.__orig_select_select
-
-    def test_init(self):
-        server_address = get_availaddr()
-        self.stats_httpd = MyStatsHttpd(server_address)
-        self.assertEqual(self.stats_httpd.running, False)
-        self.assertEqual(self.stats_httpd.poll_intval, 0.5)
-        self.assertNotEqual(len(self.stats_httpd.httpd), 0)
-        self.assertIsNotNone(self.stats_httpd.mccs)
-        self.assertIsNotNone(self.stats_httpd.cc_session)
-        # The real CfgMgr would return 'version', but our test mock omits it,
-        # so the len(config) should be 1
-        self.assertEqual(len(self.stats_httpd.config), 1)
-        self.assertTrue('listen_on' in self.stats_httpd.config)
-        self.assertEqual(len(self.stats_httpd.config['listen_on']), 1)
-        self.assertTrue('address' in self.stats_httpd.config['listen_on'][0])
-        self.assertTrue('port' in self.stats_httpd.config['listen_on'][0])
-        self.assertTrue(server_address in set(self.stats_httpd.http_addrs))
-        self.assertEqual('StatsHttpd', self.stats_httpd.mccs.\
-                             get_module_spec().get_module_name())
-
-    def test_init_hterr(self):
-        """Test the behavior of StatsHttpd constructor when open_httpd fails.
-
-        We specifically check the following two:
-        - close_mccs() is called (so stats-httpd tells ConfigMgr it's shutting
-          down)
-        - the constructor results in HttpServerError exception.
-
-        """
-        self.__mccs_closed = False
-        def call_checker():
-            self.__mccs_closed = True
-        class FailingStatsHttpd(MyStatsHttpd):
-            def open_httpd(self):
-                raise stats_httpd.HttpServerError
-            def close_mccs(self):
-                call_checker()
-        self.assertRaises(stats_httpd.HttpServerError, FailingStatsHttpd)
-        self.assertTrue(self.__mccs_closed)
-
-    def test_openclose_mccs(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        mccs = self.stats_httpd.mccs
-        self.assertFalse(self.stats_httpd.mccs.stopped)
-        self.assertFalse(self.stats_httpd.mccs.closed)
-        self.stats_httpd.close_mccs()
-        self.assertTrue(mccs.stopped)
-        self.assertTrue(mccs.closed)
-        self.assertIsNone(self.stats_httpd.mccs)
-        self.stats_httpd.open_mccs()
-        self.assertIsNotNone(self.stats_httpd.mccs)
-        self.stats_httpd.mccs = None
-        self.assertIsNone(self.stats_httpd.mccs)
-        self.assertIsNone(self.stats_httpd.close_mccs())
-
-    def test_mccs(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertIsNotNone(self.stats_httpd.mccs.get_socket())
-        self.assertTrue(
-            isinstance(self.stats_httpd.mccs.get_socket(), socket.socket))
-        self.assertIsNotNone(self.stats_httpd.cc_session)
-        statistics_spec = self.stats_httpd.get_stats_spec()
-        for mod in DUMMY_DATA:
-            self.assertTrue(mod in statistics_spec)
-            for cfg in statistics_spec[mod]:
-                self.assertTrue('item_name' in cfg)
-                self.assertTrue(cfg['item_name'] in DUMMY_DATA[mod])
-            self.assertTrue(len(statistics_spec[mod]), len(DUMMY_DATA[mod]))
-        self.stats_httpd.close_mccs()
-        self.assertIsNone(self.stats_httpd.mccs)
-
-    def test_httpd(self):
-        # dual stack (addresses is ipv4 and ipv6)
-        if self.ipv6_enabled:
-            server_addresses = (get_availaddr('::1'), get_availaddr())
-            self.stats_httpd = MyStatsHttpd(*server_addresses)
-            for ht in self.stats_httpd.httpd:
-                self.assertTrue(isinstance(ht, stats_httpd.HttpServer))
-                self.assertTrue(ht.address_family in set([socket.AF_INET,
-                                                          socket.AF_INET6]))
-                self.assertTrue(isinstance(ht.socket, socket.socket))
-                ht.socket.close() # to silence warning about resource leak
-            self.stats_httpd.close_mccs() # ditto
-
-        # dual stack (address is ipv6)
-        if self.ipv6_enabled:
-            server_addresses = get_availaddr('::1')
-            self.stats_httpd = MyStatsHttpd(server_addresses)
-            for ht in self.stats_httpd.httpd:
-                self.assertTrue(isinstance(ht, stats_httpd.HttpServer))
-                self.assertEqual(ht.address_family, socket.AF_INET6)
-                self.assertTrue(isinstance(ht.socket, socket.socket))
-                ht.socket.close()
-            self.stats_httpd.close_mccs() # ditto
-
-        # dual/single stack (address is ipv4)
-        server_addresses = get_availaddr()
-        self.stats_httpd = MyStatsHttpd(server_addresses)
-        for ht in self.stats_httpd.httpd:
-            self.assertTrue(isinstance(ht, stats_httpd.HttpServer))
-            self.assertEqual(ht.address_family, socket.AF_INET)
-            self.assertTrue(isinstance(ht.socket, socket.socket))
-            ht.socket.close()
-        self.stats_httpd.close_mccs()
-
-    def test_httpd_anyIPv4(self):
-        # any address (IPv4)
-        server_addresses = get_availaddr(address='0.0.0.0')
-        self.stats_httpd = MyStatsHttpd(server_addresses)
-        for ht in self.stats_httpd.httpd:
-            self.assertTrue(isinstance(ht, stats_httpd.HttpServer))
-            self.assertEqual(ht.address_family,socket.AF_INET)
-            self.assertTrue(isinstance(ht.socket, socket.socket))
-
-    def test_httpd_anyIPv6(self):
-        # any address (IPv6)
-        if self.ipv6_enabled:
-            server_addresses = get_availaddr(address='::')
-            self.stats_httpd = MyStatsHttpd(server_addresses)
-            for ht in self.stats_httpd.httpd:
-                self.assertTrue(isinstance(ht, stats_httpd.HttpServer))
-                self.assertEqual(ht.address_family,socket.AF_INET6)
-                self.assertTrue(isinstance(ht.socket, socket.socket))
-
-    def test_httpd_failed(self):
-        # existent hostname
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          get_availaddr(address='localhost'))
-
-        # nonexistent hostname
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          ('my.host.domain', 8000))
-
-        # over flow of port number
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          ('127.0.0.1', 80000))
-
-        # negative
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          ('127.0.0.1', -8000))
-
-        # alphabet
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          ('127.0.0.1', 'ABCDE'))
-
-        # Address already in use
-        server_addresses = get_availaddr()
-        server = MyStatsHttpd(server_addresses)
-        self.assertRaises(stats_httpd.HttpServerError, MyStatsHttpd,
-                          server_addresses)
-
-    def __faked_select(self, ex=None):
-        """A helper subroutine for tests using faked select.select.
-
-        See test_running() for basic features.  If ex is not None,
-        it's assumed to be an exception object and will be raised on the
-        first call.
-
-        """
-        self.assertTrue(self.stats_httpd.running)
-        self.__call_count += 1
-        if ex is not None and self.__call_count == 1:
-            raise ex
-        if self.__call_count == 2:
-            self.stats_httpd.running  = False
-        assert self.__call_count <= 2 # safety net to avoid infinite loop
-        return ([], [], [])
-
-    def test_running(self):
-        # Previous version of this test checks the result of "status" and
-        # "shutdown" commands; however, they are more explicitly tested
-        # in specific tests.  In this test we only have to check:
-        # - start() will set 'running' to True
-        # - as long as 'running' is True, it keeps calling select.select
-        # - when running becomes False, it exists from the loop and calls
-        #   stop()
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertFalse(self.stats_httpd.running)
-
-        # In this test we'll call select.select() 2 times: on the first call
-        # stats_httpd.running should be True; on the second call the faked
-        # select() will set it to False.
-        self.__call_count = 0
-        select.select = lambda r, w, x, t: self.__faked_select()
-        self.stats_httpd.start()
-        self.assertFalse(self.stats_httpd.running)
-        self.assertIsNone(self.stats_httpd.mccs) # stop() clears .mccs
-
-    def test_running_fail(self):
-        # A failure case of start(): we close the (real but dummy) socket for
-        # the CC session.  This breaks the select-loop due to exception
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.stats_httpd.mccs.get_socket().close()
-        self.assertRaises(ValueError, self.stats_httpd.start)
-
-    def test_failure_with_a_select_error (self):
-        """checks select.error is raised if the exception except
-        errno.EINTR is raised while it's selecting"""
-        def raise_select_except(*args):
-            raise select.error('dummy error')
-        select.select = raise_select_except
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertRaises(select.error, self.stats_httpd.start)
-
-    def test_nofailure_with_errno_EINTR(self):
-        """checks no exception is raised if errno.EINTR is raised
-        while it's selecting"""
-        self.__call_count = 0
-        select.select = lambda r, w, x, t: self.__faked_select(
-            select.error(errno.EINTR))
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.stats_httpd.start() # shouldn't leak the exception
-        self.assertFalse(self.stats_httpd.running)
-        self.assertIsNone(self.stats_httpd.mccs)
-
-    def test_open_template(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        # successful conditions
-        tmpl = self.stats_httpd.open_template(
-            stats_httpd.XML_TEMPLATE_LOCATION)
-        self.assertTrue(isinstance(tmpl, string.Template))
-        opts = dict(
-            xml_string="<dummy></dummy>",
-            xsl_url_path="/path/to/")
-        lines = tmpl.substitute(opts)
-        for n in opts:
-            self.assertGreater(lines.find(opts[n]), 0)
-        tmpl = self.stats_httpd.open_template(
-            stats_httpd.XSD_TEMPLATE_LOCATION)
-        self.assertTrue(isinstance(tmpl, string.Template))
-        opts = dict(xsd_namespace="http://host/path/to/")
-        lines = tmpl.substitute(opts)
-        for n in opts:
-            self.assertGreater(lines.find(opts[n]), 0)
-        tmpl = self.stats_httpd.open_template(
-            stats_httpd.XSL_TEMPLATE_LOCATION)
-        self.assertTrue(isinstance(tmpl, string.Template))
-        opts = dict(xsd_namespace="http://host/path/to/")
-        lines = tmpl.substitute(opts)
-        for n in opts:
-            self.assertGreater(lines.find(opts[n]), 0)
-        # unsuccessful condition
-        self.assertRaises(
-            stats_httpd.StatsHttpdDataError,
-            self.stats_httpd.open_template, '/path/to/foo/bar')
-
-    def test_commands(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertEqual(self.stats_httpd.command_handler("status", None),
-                         isc.config.ccsession.create_answer(
-                0, "Stats Httpd is up. (PID " + str(os.getpid()) + ")"))
-        self.stats_httpd.running = True
-        self.assertEqual(self.stats_httpd.command_handler("shutdown", None),
-                         isc.config.ccsession.create_answer(0))
-        self.assertFalse(self.stats_httpd.running)
-        self.assertEqual(
-            self.stats_httpd.command_handler("__UNKNOWN_COMMAND__", None),
-            isc.config.ccsession.create_answer(
-                1, "Unknown command: __UNKNOWN_COMMAND__"))
-
-    def test_config(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        self.assertEqual(
-            self.stats_httpd.config_handler(dict(_UNKNOWN_KEY_=None)),
-            isc.config.ccsession.create_answer(
-                1, "unknown item _UNKNOWN_KEY_"))
-
-        addresses = get_availaddr()
-        self.assertEqual(
-            self.stats_httpd.config_handler(
-                dict(listen_on=[dict(address=addresses[0],port=addresses[1])])),
-            isc.config.ccsession.create_answer(0))
-        self.assertTrue("listen_on" in self.stats_httpd.config)
-        for addr in self.stats_httpd.config["listen_on"]:
-            self.assertTrue("address" in addr)
-            self.assertTrue("port" in addr)
-            self.assertTrue(addr["address"] == addresses[0])
-            self.assertTrue(addr["port"] == addresses[1])
-
-        if self.ipv6_enabled:
-            addresses = get_availaddr("::1")
-            self.assertEqual(
-                self.stats_httpd.config_handler(
-                dict(listen_on=[dict(address=addresses[0],port=addresses[1])])),
-                isc.config.ccsession.create_answer(0))
-            self.assertTrue("listen_on" in self.stats_httpd.config)
-            for addr in self.stats_httpd.config["listen_on"]:
-                self.assertTrue("address" in addr)
-                self.assertTrue("port" in addr)
-                self.assertTrue(addr["address"] == addresses[0])
-                self.assertTrue(addr["port"] == addresses[1])
-
-        addresses = get_availaddr()
-        self.assertEqual(
-            self.stats_httpd.config_handler(
-                dict(listen_on=[dict(address=addresses[0],port=addresses[1])])),
-            isc.config.ccsession.create_answer(0))
-        self.assertTrue("listen_on" in self.stats_httpd.config)
-        for addr in self.stats_httpd.config["listen_on"]:
-            self.assertTrue("address" in addr)
-            self.assertTrue("port" in addr)
-            self.assertTrue(addr["address"] == addresses[0])
-            self.assertTrue(addr["port"] == addresses[1])
-        (ret, arg) = isc.config.ccsession.parse_answer(
-            self.stats_httpd.config_handler(
-                dict(listen_on=[dict(address="1.2.3.4",port=543210)]))
-            )
-        self.assertEqual(ret, 1)
-
-    @unittest.skipUnless(xml_parser, "skipping the test using XMLParser")
-    def test_xml_handler(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        module_name = 'Dummy'
-        stats_spec = \
-            { module_name :
-                  [{
-                        "item_name": "foo",
-                        "item_type": "string",
-                        "item_optional": False,
-                        "item_default": "bar",
-                        "item_description": "foo is bar",
-                        "item_title": "Foo"
-                        },
-                   {
-                        "item_name": "foo2",
-                        "item_type": "list",
-                        "item_optional": False,
-                        "item_default": [
-                            {
-                                "zonename" : "test1",
-                                "queries.udp" : 1,
-                                "queries.tcp" : 2
-                                },
-                            {
-                                "zonename" : "test2",
-                                "queries.udp" : 3,
-                                "queries.tcp" : 4
-                                }
-                        ],
-                        "item_title": "Foo bar",
-                        "item_description": "Foo bar",
-                        "list_item_spec": {
-                            "item_name": "foo2-1",
-                            "item_type": "map",
-                            "item_optional": False,
-                            "item_default": {},
-                            "map_item_spec": [
-                                {
-                                    "item_name": "foo2-1-1",
-                                    "item_type": "string",
-                                    "item_optional": False,
-                                    "item_default": "",
-                                    "item_title": "Foo2 1 1",
-                                    "item_description": "Foo bar"
-                                    },
-                                {
-                                    "item_name": "foo2-1-2",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Foo2 1 2",
-                                    "item_description": "Foo bar"
-                                    },
-                                {
-                                    "item_name": "foo2-1-3",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Foo2 1 3",
-                                    "item_description": "Foo bar"
-                                    }
-                                ]
-                            }
-                        }]
-              }
-        stats_data = \
-            { module_name : { 'foo':'bar',
-                          'foo2': [
-                            {
-                                "foo2-1-1" : "bar1",
-                                "foo2-1-2" : 10,
-                                "foo2-1-3" : 9
-                                },
-                            {
-                                "foo2-1-1" : "bar2",
-                                "foo2-1-2" : 8,
-                                "foo2-1-3" : 7
-                                }
-                            ] } }
-        self.stats_httpd.get_stats_spec = lambda x,y: stats_spec
-        self.stats_httpd.get_stats_data = lambda x,y: stats_data
-        xml_string = self.stats_httpd.xml_handler()
-        stats_xml = xml.etree.ElementTree.fromstring(xml_string)
-        schema_loc = '{%s}schemaLocation' % XMLNS_XSI
-        self.assertEqual(stats_xml.attrib[schema_loc],
-                         stats_httpd.XML_ROOT_ATTRIB['xsi:schemaLocation'])
-        stats_data = stats_data[module_name]
-        stats_spec = stats_spec[module_name]
-        names = stats_httpd.item_name_list(stats_data, '')
-        for i in range(0, len(names)):
-            self.assertEqual('%s/%s' % (module_name, names[i]), stats_xml[i].attrib['identifier'])
-            value = isc.cc.data.find(stats_data, names[i])
-            if type(value) is int:
-                value = str(value)
-            if type(value) is dict or type(value) is list:
-                self.assertFalse('value' in stats_xml[i].attrib)
-            else:
-                self.assertEqual(value, stats_xml[i].attrib['value'])
-            self.assertEqual(module_name, stats_xml[i].attrib['owner'])
-            self.assertEqual(urllib.parse.quote('%s/%s/%s' % (stats_httpd.XML_URL_PATH,
-                                                              module_name, names[i])),
-                             stats_xml[i].attrib['uri'])
-            spec = isc.config.find_spec_part(stats_spec, names[i])
-            self.assertEqual(spec['item_name'], stats_xml[i].attrib['name'])
-            self.assertEqual(spec['item_type'], stats_xml[i].attrib['type'])
-            self.assertEqual(spec['item_description'], stats_xml[i].attrib['description'])
-            self.assertEqual(spec['item_title'], stats_xml[i].attrib['title'])
-            self.assertEqual(str(spec['item_optional']).lower(), stats_xml[i].attrib['optional'])
-            default = spec['item_default']
-            if type(default) is int:
-                default = str(default)
-            if type(default) is dict or type(default) is list:
-                self.assertFalse('default' in stats_xml[i].attrib)
-            else:
-                self.assertEqual(default, stats_xml[i].attrib['default'])
-            self.assertFalse('item_format' in spec)
-            self.assertFalse('format' in stats_xml[i].attrib)
-
-    @unittest.skipUnless(xml_parser, "skipping the test using XMLParser")
-    def test_xsd_handler(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        xsd_string = self.stats_httpd.xsd_handler()
-        stats_xsd = xml.etree.ElementTree.fromstring(xsd_string)
-        ns = '{%s}' % XMLNS_XSD
-        stats_xsd = stats_xsd[1].find('%scomplexType/%ssequence/%selement' % ((ns,)*3))
-        self.assertEqual('item', stats_xsd.attrib['name'])
-        stats_xsd = stats_xsd.find('%scomplexType' % ns)
-        type_types = ('boolean', 'integer', 'real', 'string', 'map', \
-                          'list', 'named_set', 'any')
-        attribs = [('identifier', 'string', 'required'),
-                   ('value', 'string', 'optional'),
-                   ('owner', 'string', 'required'),
-                   ('uri', 'anyURI', 'required'),
-                   ('name', 'string', 'required'),
-                   ('type', type_types, 'required'),
-                   ('description', 'string', 'optional'),
-                   ('title', 'string', 'optional'),
-                   ('optional', 'boolean', 'optional'),
-                   ('default', 'string', 'optional'),
-                   ('format', 'string', 'optional')]
-        for i in range(0, len(attribs)):
-            self.assertEqual(attribs[i][0], stats_xsd[i].attrib['name'])
-            if attribs[i][0] == 'type':
-                stats_xsd_types = \
-                    stats_xsd[i].find('%ssimpleType/%srestriction' % \
-                                          ((ns,)*2))
-                for j in range(0, len(attribs[i][1])):
-                    self.assertEqual(attribs[i][1][j], \
-                                         stats_xsd_types[j].attrib['value'])
-            else:
-                self.assertEqual(attribs[i][1], stats_xsd[i].attrib['type'])
-            self.assertEqual(attribs[i][2], stats_xsd[i].attrib['use'])
-
-    @unittest.skipUnless(xml_parser, "skipping the test using XMLParser")
-    def test_xsl_handler(self):
-        self.stats_httpd = MyStatsHttpd(get_availaddr())
-        xsl_string = self.stats_httpd.xsl_handler()
-        stats_xsl = xml.etree.ElementTree.fromstring(xsl_string)
-        nst = '{%s}' % XMLNS_XSL
-        nsx = '{%s}' % XMLNS_XHTML
-        self.assertEqual("bind10:statistics", stats_xsl[2].attrib['match'])
-        stats_xsl = stats_xsl[2].find('%stable' % nsx)
-        self.assertEqual('item', stats_xsl[1].attrib['select'])
-        stats_xsl = stats_xsl[1].find('%str' % nsx)
-        self.assertEqual('@uri', stats_xsl[0].find(
-                '%selement/%sattribute/%svalue-of' % ((nst,)*3)).attrib['select'])
-        self.assertEqual('@identifier', stats_xsl[0].find(
-                '%selement/%svalue-of' % ((nst,)*2)).attrib['select'])
-        self.assertEqual('@value', stats_xsl[1].find('%sif' % nst).attrib['test'])
-        self.assertEqual('@value', stats_xsl[1].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select'])
-        self.assertEqual('@description', stats_xsl[2].find('%sif' % nst).attrib['test'])
-        self.assertEqual('@description', stats_xsl[2].find('%sif/%svalue-of' % ((nst,)*2)).attrib['select'])
-
-class Z_TestOSEnv(unittest.TestCase):
-    def test_for_without_B10_FROM_SOURCE(self):
-        # Note: this test is sensitive due to its substantial side effect of
-        # reloading.  For exmaple, it affects tests that tweak module
-        # attributes (such as test_init_hterr).  It also breaks logging
-        # setting for unit tests.  To minimize these effects, we use
-        # workaround: make it very likely to run at the end of the tests
-        # by naming the test class "Z_".
-
-        # just lets it go through the code without B10_FROM_SOURCE env
-        # variable
-        if "B10_FROM_SOURCE" in os.environ:
-            tmppath = os.environ["B10_FROM_SOURCE"]
-            os.environ.pop("B10_FROM_SOURCE")
-            imp.reload(stats_httpd)
-            os.environ["B10_FROM_SOURCE"] = tmppath
-            imp.reload(stats_httpd)
-
-if __name__ == "__main__":
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/stats/tests/stats_test.py b/src/bin/stats/tests/stats_test.py
deleted file mode 100644 (file)
index 14e49f9..0000000
+++ /dev/null
@@ -1,1668 +0,0 @@
-# Copyright (C) 2010-2013  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.
-
-"""
-In each of these tests we start several virtual components. They are
-not the real components, no external processes are started. They are
-just simple mock objects running each in its own thread and pretending
-to be bind10 modules. This helps testing the stats module in a close
-to real environment.
-"""
-
-import unittest
-import os
-import io
-import time
-import imp
-import sys
-
-import stats
-import isc.log
-from test_utils import MyStats
-from isc.config.ccsession import create_answer
-
-class TestUtilties(unittest.TestCase):
-    items = [
-        { 'item_name': 'test_int1',  'item_type': 'integer', 'item_default': 12345      },
-        { 'item_name': 'test_real1', 'item_type': 'real',    'item_default': 12345.6789 },
-        { 'item_name': 'test_bool1', 'item_type': 'boolean', 'item_default': True       },
-        { 'item_name': 'test_str1',  'item_type': 'string',  'item_default': 'ABCD'     },
-        { 'item_name': 'test_list1', 'item_type': 'list',    'item_default': [1,2,3],
-          'list_item_spec' : { 'item_name': 'number',   'item_type': 'integer' } },
-        { 'item_name': 'test_map1',  'item_type': 'map',     'item_default': {'a':1,'b':2,'c':3},
-          'map_item_spec'  : [ { 'item_name': 'a',   'item_type': 'integer'},
-                               { 'item_name': 'b',   'item_type': 'integer'},
-                               { 'item_name': 'c', 'item_type': 'integer'} ] },
-        { 'item_name': 'test_int2',  'item_type': 'integer' },
-        { 'item_name': 'test_real2', 'item_type': 'real'    },
-        { 'item_name': 'test_bool2', 'item_type': 'boolean' },
-        { 'item_name': 'test_str2',  'item_type': 'string'  },
-        { 'item_name': 'test_list2', 'item_type': 'list',
-          'list_item_spec' : { 'item_name': 'number',   'item_type': 'integer' } },
-        { 'item_name': 'test_map2',  'item_type': 'map',
-          'map_item_spec'  : [ { 'item_name': 'A', 'item_type': 'integer'},
-                               { 'item_name': 'B', 'item_type': 'integer'},
-                               { 'item_name': 'C', 'item_type': 'integer'} ] },
-        { 'item_name': 'test_none',  'item_type': 'none'    },
-        { 'item_name': 'test_list3', 'item_type': 'list',    'item_default': ["one","two","three"],
-          'list_item_spec' : { 'item_name': 'number', 'item_type': 'string' } },
-        { 'item_name': 'test_map3',  'item_type': 'map',     'item_default': {'a':'one','b':'two','c':'three'},
-          'map_item_spec'  : [ { 'item_name': 'a', 'item_type': 'string'},
-                               { 'item_name': 'b', 'item_type': 'string'},
-                               { 'item_name': 'c', 'item_type': 'string'} ] },
-        {
-          'item_name': 'test_named_set',
-          'item_type': 'named_set',
-          'item_default': { },
-          'named_set_item_spec': {
-            'item_name': 'name',
-            'item_type': 'map',
-            'item_default': { },
-            'map_item_spec': [
-              {
-                'item_name': 'number1',
-                'item_type': 'integer'
-                },
-              {
-                'item_name': 'number2',
-                'item_type': 'integer'
-                }
-              ]
-            }
-          }
-        ]
-
-    def setUp(self):
-        self.const_timestamp = 1308730448.965706
-        self.const_timetuple = (2011, 6, 22, 8, 14, 8, 2, 173, 0)
-        self.const_datetime = '2011-06-22T08:14:08Z'
-        self.__orig_time = stats.time
-        self.__orig_gmtime = stats.gmtime
-        stats.time = lambda : self.const_timestamp
-        stats.gmtime = lambda : self.const_timetuple
-
-    def tearDown(self):
-        stats.time = self.__orig_time
-        stats.gmtime = self.__orig_gmtime
-
-    def test_get_spec_defaults(self):
-        self.assertEqual(
-            stats.get_spec_defaults(self.items), {
-                'test_int1'  : 12345              ,
-                'test_real1' : 12345.6789         ,
-                'test_bool1' : True               ,
-                'test_str1'  : 'ABCD'             ,
-                'test_list1' : [1,2,3]            ,
-                'test_map1'  : {'a':1,'b':2,'c':3},
-                'test_int2'  : 0 ,
-                'test_real2' : 0.0,
-                'test_bool2' : False,
-                'test_str2'  : "",
-                'test_list2' : [0],
-                'test_map2'  : { 'A' : 0, 'B' : 0, 'C' : 0 },
-                'test_none'  : None,
-                'test_list3' : [ "one", "two", "three" ],
-                'test_map3'  : { 'a' : 'one', 'b' : 'two', 'c' : 'three' },
-                'test_named_set' : {} })
-        self.assertEqual(stats.get_spec_defaults(None), {})
-        self.assertRaises(KeyError, stats.get_spec_defaults, [{'item_name':'Foo'}])
-
-    def test_get_timestamp(self):
-        self.assertEqual(stats.get_timestamp(), self.const_timestamp)
-
-    def test_get_datetime(self):
-        self.assertEqual(stats.get_datetime(), self.const_datetime)
-        self.assertNotEqual(stats.get_datetime(
-                (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime)
-
-    def test__accum(self):
-        self.assertEqual(stats._accum(None, None), None)
-        self.assertEqual(stats._accum(None, "b"), "b")
-        self.assertEqual(stats._accum("a", None), "a")
-        self.assertEqual(stats._accum(1, 2), 3)
-        self.assertEqual(stats._accum(0.5, 0.3), 0.8)
-        self.assertEqual(stats._accum(1, 0.3), 1.3)
-        self.assertEqual(stats._accum(0.5, 2), 2.5)
-        self.assertEqual(stats._accum('aa','bb'), 'bb')
-        self.assertEqual(stats._accum('1970-01-01T09:00:00Z','2012-08-09T09:33:31Z'),
-                         '2012-08-09T09:33:31Z')
-        self.assertEqual(stats._accum(
-                [1, 2, 3], [4, 5]), [5, 7, 3])
-        self.assertEqual(stats._accum(
-                [4, 5], [1, 2, 3]), [5, 7, 3])
-        self.assertEqual(stats._accum(
-                [1, 2, 3], [None, 5, 6]), [1, 7, 9])
-        self.assertEqual(stats._accum(
-                [None, 5, 6], [1, 2, 3]), [1, 7, 9])
-        self.assertEqual(stats._accum(
-                [1, 2, 3], [None, None, None, None]), [1,2,3,None])
-        self.assertEqual(stats._accum(
-                [[1,2],3],[[],5,6]), [[1,2],8,6])
-        self.assertEqual(stats._accum(
-                {'one': 1, 'two': 2, 'three': 3},
-                {'one': 4, 'two': 5}),
-                         {'one': 5, 'two': 7, 'three': 3})
-        self.assertEqual(stats._accum(
-                {'one': 1, 'two': 2, 'three': 3},
-                {'four': 4, 'five': 5}),
-                         {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
-        self.assertEqual(stats._accum(
-                {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
-                {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
-                         {'one':[3,2], 'two':[7,5,5], 'three':[None,3,None], 'four': 'FOUR'})
-        self.assertEqual(stats._accum(
-                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
-                [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
-                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 5, 'five': 7, 'six': 9} ])
-
-    def test_merge_oldnre(self):
-        self.assertEqual(stats.merge_oldnew(1, 2), 2)
-        self.assertEqual(stats.merge_oldnew(0.5, 0.3), 0.3)
-        self.assertEqual(stats.merge_oldnew('aa','bb'), 'bb')
-        self.assertEqual(stats.merge_oldnew(
-                [1, 2, 3], [4, 5]), [4, 5, 3])
-        self.assertEqual(stats.merge_oldnew(
-                [4, 5], [1, 2, 3]), [1, 2, 3])
-        self.assertEqual(stats.merge_oldnew(
-                [1, 2, 3], [None, 5, 6]), [None, 5, 6])
-        self.assertEqual(stats.merge_oldnew(
-                [None, 5, 6], [1, 2, 3]), [1, 2, 3])
-        self.assertEqual(stats.merge_oldnew(
-                [1, 2, 3], [None, None, None, None]), [None, None, None, None])
-        self.assertEqual(stats.merge_oldnew(
-                [[1,2],3],[[],5,6]), [[1,2],5,6])
-        self.assertEqual(stats.merge_oldnew(
-                {'one': 1, 'two': 2, 'three': 3},
-                {'one': 4, 'two': 5}),
-                         {'one': 4, 'two': 5, 'three': 3})
-        self.assertEqual(stats.merge_oldnew(
-                {'one': 1, 'two': 2, 'three': 3},
-                {'four': 4, 'five': 5}),
-                         {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
-        self.assertEqual(stats.merge_oldnew(
-                {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
-                {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
-                         {'one':[2,2], 'two':[4,5,5], 'three':[None,None,None], 'four': 'FOUR'})
-        self.assertEqual(stats.merge_oldnew(
-                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
-                [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
-                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 1, 'five': 2, 'six': 3} ])
-
-class TestCallback(unittest.TestCase):
-    def setUp(self):
-        self.dummy_func = lambda *x, **y : (x, y)
-        self.dummy_args = (1,2,3)
-        self.dummy_kwargs = {'a':1,'b':2,'c':3}
-        self.cback1 = stats.Callback(
-            command=self.dummy_func,
-            args=self.dummy_args,
-            kwargs=self.dummy_kwargs
-            )
-        self.cback2 = stats.Callback(
-            args=self.dummy_args,
-            kwargs=self.dummy_kwargs
-            )
-        self.cback3 = stats.Callback(
-            command=self.dummy_func,
-            kwargs=self.dummy_kwargs
-            )
-        self.cback4 = stats.Callback(
-            command=self.dummy_func,
-            args=self.dummy_args
-            )
-
-    def test_init(self):
-        self.assertEqual((self.cback1.command, self.cback1.args, self.cback1.kwargs),
-                         (self.dummy_func, self.dummy_args, self.dummy_kwargs))
-        self.assertEqual((self.cback2.command, self.cback2.args, self.cback2.kwargs),
-                         (None, self.dummy_args, self.dummy_kwargs))
-        self.assertEqual((self.cback3.command, self.cback3.args, self.cback3.kwargs),
-                         (self.dummy_func, (), self.dummy_kwargs))
-        self.assertEqual((self.cback4.command, self.cback4.args, self.cback4.kwargs),
-                         (self.dummy_func, self.dummy_args, {}))
-
-    def test_call(self):
-        self.assertEqual(self.cback1(), (self.dummy_args, self.dummy_kwargs))
-        self.assertEqual(self.cback1(100, 200), ((100, 200), self.dummy_kwargs))
-        self.assertEqual(self.cback1(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
-        self.assertEqual(self.cback2(), None)
-        self.assertEqual(self.cback3(), ((), self.dummy_kwargs))
-        self.assertEqual(self.cback3(100, 200), ((100, 200), self.dummy_kwargs))
-        self.assertEqual(self.cback3(a=100, b=200), ((), {'a':100, 'b':200}))
-        self.assertEqual(self.cback4(), (self.dummy_args, {}))
-        self.assertEqual(self.cback4(100, 200), ((100, 200), {}))
-        self.assertEqual(self.cback4(a=100, b=200), (self.dummy_args, {'a':100, 'b':200}))
-
-class TestStats(unittest.TestCase):
-    def setUp(self):
-        # set the signal handler for deadlock
-        self.const_timestamp = 1308730448.965706
-        self.const_datetime = '2011-06-22T08:14:08Z'
-        self.const_default_datetime = '1970-01-01T00:00:00Z'
-        # Record original module-defined functions in case we replace them
-        self.__orig_timestamp = stats.get_timestamp
-        self.__orig_get_datetime = stats.get_datetime
-
-    def tearDown(self):
-        # restore the stored original function in case we replaced them
-        stats.get_timestamp = self.__orig_timestamp
-        stats.get_datetime = self.__orig_get_datetime
-
-    def test_init(self):
-        self.stats = MyStats()
-        self.assertEqual(self.stats.module_name, 'Stats')
-        self.assertFalse(self.stats.running)
-        self.assertTrue('command_show' in self.stats.callbacks)
-        self.assertTrue('command_status' in self.stats.callbacks)
-        self.assertTrue('command_shutdown' in self.stats.callbacks)
-        self.assertTrue('command_show' in self.stats.callbacks)
-        self.assertTrue('command_showschema' in self.stats.callbacks)
-        self.assertEqual(self.stats.config['poll-interval'], 60)
-
-    def test_init_undefcmd(self):
-        spec_str = """\
-{
-  "module_spec": {
-    "module_name": "Stats",
-    "module_description": "Stats daemon",
-    "config_data": [],
-    "commands": [
-      {
-        "command_name": "_undef_command_",
-        "command_description": "a undefined command in stats",
-        "command_args": []
-      }
-    ],
-    "statistics": []
-  }
-}
-"""
-        orig_spec_location = stats.SPECFILE_LOCATION
-        stats.SPECFILE_LOCATION = io.StringIO(spec_str)
-        self.assertRaises(stats.StatsError, MyStats)
-        stats.SPECFILE_LOCATION = orig_spec_location
-
-    def __send_command(self, stats, command_name, params=None):
-        '''Emulate a command arriving to stats by directly calling callback'''
-        return isc.config.ccsession.parse_answer(
-            stats.command_handler(command_name, params))
-
-    def test_check_command(self):
-        """Test _check_command sets proper timeout values and sets proper values
-        returned from group_recvmsg"""
-        stat = MyStats()
-        set_timeouts = []
-        orig_timeout = 1
-        stat.cc_session.get_timeout = lambda: orig_timeout
-        stat.cc_session.set_timeout = lambda x: set_timeouts.append(x)
-        msg = create_answer(0, 'msg')
-        env = {'from': 'frominit'}
-        stat._answers = [(msg, env)]
-        stat._check_command()
-        self.assertListEqual([500, orig_timeout], set_timeouts)
-        self.assertEqual(msg, stat.mccs._msg)
-        self.assertEqual(env, stat.mccs._env)
-
-    def test_check_command_sessiontimeout(self):
-        """Test _check_command doesn't perform but sets proper timeout values in
-        case that a SesstionTimeout exception is caught while doing
-        group_recvmsg()"""
-        stat = MyStats()
-        set_timeouts = []
-        orig_timeout = 1
-        stat.cc_session.get_timeout = lambda: orig_timeout
-        stat.cc_session.set_timeout = lambda x: set_timeouts.append(x)
-        msg = create_answer(0, 'msg')
-        env = {'from': 'frominit'}
-        stat._answers = [(msg, env)]
-        # SessionTimeout is raised while doing group_recvmsg()
-        ex = isc.cc.session.SessionTimeout
-        def __raise(*x): raise ex(*x)
-        stat.cc_session.group_recvmsg = lambda x: __raise()
-        stat._check_command()
-        self.assertListEqual([500, orig_timeout], set_timeouts)
-        self.assertEqual(None, stat.mccs._msg)
-        self.assertEqual(None, stat.mccs._env)
-
-    def test_start(self):
-        # Define a separate exception class so we can be sure that's actually
-        # the one raised in __check_start() below
-        class CheckException(Exception):
-            pass
-
-        def __check_start(tested_stats):
-            self.assertTrue(tested_stats.running)
-            raise CheckException # terminate the loop
-
-        # start without err
-        self.stats = MyStats()
-        self.assertFalse(self.stats.running)
-        self.stats._check_command = lambda: __check_start(self.stats)
-        # We are going to confirm start() will set running to True, avoiding
-        # to fall into a loop with the exception trick.
-        self.assertRaises(CheckException, self.stats.start)
-        self.assertEqual(self.__send_command(self.stats, "status"),
-                         (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
-        self.assertTrue(self.stats.mccs.stopped)
-
-    def test_start_set_next_polltime(self):
-        """Test start() properly sets the time next_polltime to do_poll() next
-        time"""
-        orig_get_timestamp = stats.get_timestamp
-        stats.get_timestamp = lambda : self.const_timestamp
-        stat = MyStats()
-        # manupilate next_polltime to go it through the inner if-condition
-        stat.next_polltime = self.const_timestamp - stat.get_interval() - 1
-        # stop an infinity loop at once
-        def __stop_running(): stat.running = False
-        stat.do_polling = __stop_running
-        # do nothing in _check_command()
-        stat._check_command = lambda: None
-        stat.start()
-        # check stat.next_polltime reassigned
-        self.assertEqual(self.const_timestamp, stat.next_polltime)
-        stats.get_timestamp = orig_get_timestamp
-
-    def test_shutdown(self):
-        def __check_shutdown(tested_stats):
-            self.assertTrue(tested_stats.running)
-            self.assertEqual(self.__send_command(tested_stats, "shutdown"),
-                             (0, None))
-            self.assertFalse(tested_stats.running)
-            # override get_interval() so it won't go poll statistics
-            tested_stats.get_interval = lambda : 0
-
-        self.stats = MyStats()
-        self.stats._check_command = lambda: __check_shutdown(self.stats)
-        self.stats.start()
-        self.assertTrue(self.stats.mccs.stopped)
-
-    def test_handlers(self):
-        """Test command_handler"""
-
-        __stats = MyStats()
-
-        # 'show' command.  We're going to check the expected methods are
-        # called in the expected order, and check the resulting response.
-        # Details of each method are tested separately.
-        call_log = []
-        def __steal_method(fn_name, *arg):
-            call_log.append((fn_name, arg))
-            if fn_name == 'update_stat':
-                return False        # "no error"
-            if fn_name == 'showschema':
-                return isc.config.create_answer(0, 'no error')
-
-        # Fake some methods and attributes for inspection
-        __stats.do_polling = lambda: __steal_method('polling')
-        __stats.update_statistics_data = \
-            lambda x, y, z: __steal_method('update_stat', x, y, z)
-        __stats.update_modules = lambda: __steal_method('update_module')
-        __stats.mccs.lname = 'test lname'
-        __stats.statistics_data = {'Init': {'boot_time': self.const_datetime}}
-
-        # skip initial polling
-        stats.get_timestamp = lambda: 0
-        __stats._lasttime_poll = 0
-
-        stats.get_datetime = lambda: 42 # make the result predictable
-
-        # now send the command
-        self.assertEqual(
-            self.__send_command(
-                __stats, 'show',
-                params={ 'owner' : 'Init', 'name'  : 'boot_time' }),
-            (0, {'Init': {'boot_time': self.const_datetime}}))
-        # Check if expected methods are called
-        self.assertEqual([('update_stat',
-                           ('Stats', 'test lname',
-                            {'timestamp': 0,
-                             'report_time': 42})),
-                          ('update_module', ())], call_log)
-
-        # Then update faked timestamp so the initial polling will happen, and
-        # confirm that.
-        call_log = []
-        stats.get_timestamp = lambda: 10
-        self.assertEqual(
-            self.__send_command(
-                __stats, 'show',
-                params={ 'owner' : 'Init', 'name'  : 'boot_time' }),
-            (0, {'Init': {'boot_time': self.const_datetime}}))
-        self.assertEqual([('polling', ()),
-                          ('update_stat',
-                           ('Stats', 'test lname',
-                            {'timestamp': 10,
-                             'report_time': 42})),
-                          ('update_module', ())], call_log)
-
-        # 'status' command.  We can confirm the behavior without any fake
-        self.assertEqual(
-            self.__send_command(__stats, 'status'),
-            (0, "Stats is up. (PID " + str(os.getpid()) + ")"))
-
-        # 'showschema' command.  update_modules() will be called, which
-        # (implicitly) confirms the correct method is called; further details
-        # are tested separately.
-        call_log = []
-        (rcode, value) = self.__send_command(__stats, 'showschema')
-        self.assertEqual([('update_module', ())], call_log)
-
-        # Unknown command.  Error should be returned
-        self.assertEqual(
-            self.__send_command(__stats, '__UNKNOWN__'),
-            (1, "Unknown command: '__UNKNOWN__'"))
-
-    def test_update_modules(self):
-        """Confirm the behavior of Stats.update_modules().
-
-        It checks whether the expected command is sent to ConfigManager,
-        and whether the answer from ConfigManager is handled as expected.
-
-        """
-
-        def __check_rpc_call(command, group):
-            self.assertEqual('ConfigManager', group)
-            self.assertEqual(command,
-                             isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC)
-            answer_value = {'Init': [{
-                        "item_name": "boot_time",
-                        "item_type": "string",
-                        "item_optional": False,
-                        # Use a different default so we can check it below
-                        "item_default": "2013-01-01T00:00:01Z",
-                        "item_title": "Boot time",
-                        "item_description": "dummy desc",
-                        "item_format": "date-time"
-                        }]}
-            return answer_value
-
-        self.stats = MyStats()
-        self.stats.cc_session.rpc_call = __check_rpc_call
-
-        self.stats.update_modules()
-
-        # Stats is always incorporated.  For others, only the ones returned
-        # by group_recvmsg() above is available.
-        self.assertTrue('Stats' in self.stats.modules)
-        self.assertTrue('Init' in self.stats.modules)
-        self.assertFalse('Dummy' in self.stats.modules)
-
-        my_statistics_data = stats.get_spec_defaults(
-            self.stats.modules['Stats'].get_statistics_spec())
-        self.assertTrue('report_time' in my_statistics_data)
-        self.assertTrue('boot_time' in my_statistics_data)
-        self.assertTrue('last_update_time' in my_statistics_data)
-        self.assertTrue('timestamp' in my_statistics_data)
-        self.assertTrue('lname' in my_statistics_data)
-        self.assertEqual(my_statistics_data['report_time'],
-                         self.const_default_datetime)
-        self.assertEqual(my_statistics_data['boot_time'],
-                         self.const_default_datetime)
-        self.assertEqual(my_statistics_data['last_update_time'],
-                         self.const_default_datetime)
-        self.assertEqual(my_statistics_data['timestamp'], 0.0)
-        self.assertEqual(my_statistics_data['lname'], "")
-        my_statistics_data = stats.get_spec_defaults(
-            self.stats.modules['Init'].get_statistics_spec())
-        self.assertTrue('boot_time' in my_statistics_data)
-        self.assertEqual(my_statistics_data['boot_time'],
-                         "2013-01-01T00:00:01Z")
-
-        # Error case
-        def __raise_on_rpc_call(x, y):
-            raise isc.config.RPCError(99, 'error')
-        orig_parse_answer = stats.isc.config.ccsession.parse_answer
-        self.stats.cc_session.rpc_call = __raise_on_rpc_call
-        self.assertRaises(stats.StatsError, self.stats.update_modules)
-
-    def test_get_statistics_data(self):
-        """Confirm the behavior of Stats.get_statistics_data().
-
-        It should first call update_modules(), and then retrieve the requested
-        data from statistics_data.  We confirm this by fake update_modules()
-        where we set the expected data in statistics_data.
-
-        """
-        self.stats = MyStats()
-        def __faked_update_modules():
-            self.stats.statistics_data = { \
-                'Stats': {
-                    'report_time': self.const_default_datetime,
-                    'boot_time': None,
-                    'last_update_time': None,
-                    'timestamp': 0.0,
-                    'lname': 'dummy name'
-                    },
-                'Init': { 'boot_time': None }
-                }
-
-        self.stats.update_modules = __faked_update_modules
-
-        my_statistics_data = self.stats.get_statistics_data()
-        self.assertTrue('Stats' in my_statistics_data)
-        self.assertTrue('Init' in my_statistics_data)
-        self.assertTrue('boot_time' in my_statistics_data['Init'])
-
-        my_statistics_data = self.stats.get_statistics_data(owner='Stats')
-        self.assertTrue('Stats' in my_statistics_data)
-        self.assertTrue('report_time' in my_statistics_data['Stats'])
-        self.assertTrue('boot_time' in my_statistics_data['Stats'])
-        self.assertTrue('last_update_time' in my_statistics_data['Stats'])
-        self.assertTrue('timestamp' in my_statistics_data['Stats'])
-        self.assertTrue('lname' in my_statistics_data['Stats'])
-        self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
-                          owner='Foo')
-
-        my_statistics_data = self.stats.get_statistics_data(
-            owner='Stats', name='report_time')
-        self.assertEqual(my_statistics_data['Stats']['report_time'],
-                         self.const_default_datetime)
-
-        my_statistics_data = self.stats.get_statistics_data(
-            owner='Stats', name='boot_time')
-        self.assertTrue('boot_time' in my_statistics_data['Stats'])
-
-        my_statistics_data = self.stats.get_statistics_data(
-            owner='Stats', name='last_update_time')
-        self.assertTrue('last_update_time' in my_statistics_data['Stats'])
-
-        my_statistics_data = self.stats.get_statistics_data(
-            owner='Stats', name='timestamp')
-        self.assertEqual(my_statistics_data['Stats']['timestamp'], 0.0)
-
-        my_statistics_data = self.stats.get_statistics_data(
-            owner='Stats', name='lname')
-        self.assertTrue(len(my_statistics_data['Stats']['lname']) >0)
-        self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
-                          owner='Stats', name='Bar')
-        self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
-                          owner='Foo', name='Bar')
-        self.assertRaises(stats.StatsError, self.stats.get_statistics_data,
-                          name='Bar')
-
-    def test_update_statistics_data(self):
-        """test for list-type statistics"""
-        self.stats = MyStats()
-        _test_exp1 = {
-              'zonename': 'test1.example',
-              'queries.tcp': 5,
-              'queries.udp': 4
-            }
-        _test_exp2 = {
-              'zonename': 'test2.example',
-              'queries.tcp': 3,
-              'queries.udp': 2
-            }
-        _test_exp3 = {}
-        _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)
-        self.stats.update_statistics_data(
-            'Stats', self.stats.cc_session.lname,
-            {'lname': 'foo@bar'})
-        self.assertEqual(self.stats.statistics_data['Stats']['lname'],
-                         'foo@bar')
-        self.assertIsNone(self.stats.update_statistics_data(
-            'Auth', 'foo1', {'queries.perzone': [_test_exp1]}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['queries.perzone'],\
-                             [_test_exp1])
-        self.assertIsNone(self.stats.update_statistics_data(
-            'Auth', 'foo1', {'queries.perzone': [_test_exp2]}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['queries.perzone'],\
-                             [_test_exp2])
-        self.assertIsNone(self.stats.update_statistics_data(
-            'Auth', 'foo1', {'queries.perzone': [_test_exp1,_test_exp2]}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['queries.perzone'],
-                         [_test_exp1,_test_exp2])
-        # 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,_new_data])
-        # Error cases
-        self.assertEqual(self.stats.update_statistics_data('Stats', None,
-                                                           {'lname': 0.0}),
-                         ['0.0 should be a string'])
-        self.assertEqual(self.stats.update_statistics_data('Dummy', None,
-                                                           {'foo': 'bar'}),
-                         ['unknown module name: Dummy'])
-        self.assertEqual(self.stats.update_statistics_data(
-                'Auth', 'foo1', {'queries.perzone': [None]}), ['None should be a map'])
-
-    def test_update_statistics_data_pt2(self):
-        """test for named_set-type statistics"""
-        self.stats = MyStats()
-        _test_exp1 = \
-            { 'test10.example': { 'queries.tcp': 5, 'queries.udp': 4 } }
-        _test_exp2 = \
-            { 'test20.example': { 'queries.tcp': 3, 'queries.udp': 2 } }
-        _test_exp3 = {}
-        _test_exp4 = { 'test20.example': { '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}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['nds_queries.perzone'],\
-                             _test_exp1)
-        self.assertIsNone(self.stats.update_statistics_data(
-            'Auth', 'foo1', {'nds_queries.perzone': _test_exp2}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['nds_queries.perzone'],\
-                         dict(_test_exp1,**_test_exp2))
-        self.assertIsNone(self.stats.update_statistics_data(
-            'Auth', 'foo1', {'nds_queries.perzone':
-                                 dict(_test_exp1, **_test_exp2)}))
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']\
-                             ['foo1']['nds_queries.perzone'],
-                         dict(_test_exp1, **_test_exp2))
-        # 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'],\
-                             _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(
-                'Init', '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 = MyStats()
-
-        # This test relies on existing statistics data at the Stats object.
-        # This version of test prepares the data using the do_polling() method;
-        # that's a bad practice because a unittest for a method
-        # (update_statistics_data) would heavily depend on details of another
-        # method (do_polling).  However, there's currently no direct test
-        # for do_polling (which is also bad), so we still keep that approach,
-        # partly for testing do_polling indirectly.  #2781 should provide
-        # direct test for do_polling, with which this test scenario should
-        # also be changed to be more stand-alone.
-
-        # We use the knowledge of what kind of messages are sent via
-        # do_polling, and return the following faked answer directly.
-        self.stats._answers = [
-            # Answer for "show_processes"
-            (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
-                               [1035, 'b10-auth-2', 'Auth']]),  None),
-            # Answers for "getstats".  2 for Auth instances and 1 for Init.
-            # we return some bogus values for Init, but the rest of the test
-            # doesn't need it, so it's okay.
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth1'}),
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth2'}),
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth3'})
-            ]
-        # do_polling calls update_modules internally; in our scenario there's
-        # no change in modules, so we make it no-op.
-        self.stats.update_modules = lambda: None
-        # Now call do_polling.
-        self.stats.do_polling()
-
-        # samples of query number
-        bar1_tcp = 1001
-        bar2_tcp = 2001
-        bar3_tcp = 1002
-        bar3_udp = 1003
-        # two auth instances invoked, so we double the pre-set stat values
-        sum_qtcp = self.stats._queries_tcp * 2
-        sum_qudp = self.stats._queries_udp * 2
-        self.stats.update_statistics_data('Auth', "bar1@foo",
-                                          {'queries.tcp': bar1_tcp})
-        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'],
-                         bar1_tcp + sum_qtcp)
-        self.assertTrue('Auth' in self.stats.statistics_data_bymid)
-        self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
-        self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid
-                        ['Auth']['bar1@foo'])
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
-                         {'queries.tcp': bar1_tcp})
-        # check consolidation of statistics data even if there is
-        # non-existent mid of Auth
-        self.stats.update_statistics_data('Auth', "bar2@foo",
-                                          {'queries.tcp': bar2_tcp})
-        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'],
-                         bar1_tcp + bar2_tcp + sum_qtcp)
-        self.assertTrue('Auth' in self.stats.statistics_data_bymid)
-        self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
-        self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo'],
-                         {'queries.tcp': bar1_tcp})
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar2@foo'],
-                         {'queries.tcp': bar2_tcp})
-        # kill running Auth but the statistics data doesn't change
-        self.stats.update_statistics_data()
-        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'],
-                         bar1_tcp + bar2_tcp + sum_qtcp)
-        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'],
-                         sum_qudp)
-        self.assertTrue('Auth' in self.stats.statistics_data_bymid)
-        # restore statistics data of killed auth
-        self.stats.update_statistics_data('Auth',
-                                          "bar1@foo",
-                                          {'queries.tcp': bar1_tcp})
-        # set another mid of Auth
-        self.stats.update_statistics_data('Auth',
-                                          "bar3@foo",
-                                          {'queries.tcp':bar3_tcp,
-                                           'queries.udp':bar3_udp})
-        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'],
-                         bar1_tcp + bar2_tcp + bar3_tcp + sum_qtcp)
-        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'],
-                         bar3_udp + sum_qudp)
-        self.assertTrue('Auth' in self.stats.statistics_data_bymid)
-        self.assertTrue('bar1@foo' in self.stats.statistics_data_bymid['Auth'])
-        self.assertTrue('bar3@foo' in self.stats.statistics_data_bymid['Auth'])
-        self.assertTrue('queries.tcp' in self.stats.statistics_data_bymid['Auth']['bar1@foo'])
-        self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
-        self.assertTrue('queries.udp' in self.stats.statistics_data_bymid['Auth']['bar3@foo'])
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar1@foo']['queries.tcp'], bar1_tcp)
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.tcp'], bar3_tcp)
-        self.assertEqual(self.stats.statistics_data_bymid['Auth']['bar3@foo']['queries.udp'], bar3_udp)
-
-    def test_config(self):
-        orig_get_timestamp = stats.get_timestamp
-        stats.get_timestamp = lambda : self.const_timestamp
-        stat = MyStats()
-
-        # test updating poll-interval
-        self.assertEqual(stat.config['poll-interval'], 60)
-        self.assertEqual(stat.get_interval(), 60)
-        self.assertEqual(stat.next_polltime, self.const_timestamp + 60)
-        self.assertEqual(stat.config_handler({'poll-interval': 120}),
-                         isc.config.create_answer(0))
-        self.assertEqual(stat.config['poll-interval'], 120)
-        self.assertEqual(stat.get_interval(), 120)
-        self.assertEqual(stat.next_polltime, self.const_timestamp + 120)
-        stats.get_timestamp = orig_get_timestamp
-        self.assertEqual(stat.config_handler({'poll-interval': "foo"}),
-                         isc.config.create_answer(1, 'foo should be an integer'))
-        self.assertEqual(stat.config_handler({'poll-interval': -1}),
-                         isc.config.create_answer(1, 'Negative integer ignored'))
-        # unknown item
-        self.assertEqual(
-            stat.config_handler({'_UNKNOWN_KEY_': None}),
-            isc.config.ccsession.create_answer(
-                1, "unknown item _UNKNOWN_KEY_"))
-        # test no change if zero interval time
-        self.assertEqual(stat.config_handler({'poll-interval': 0}),
-                         isc.config.create_answer(0))
-        self.assertEqual(stat.config['poll-interval'], 0)
-
-        # see the comment for test_update_statistics_data_withmid.  We abuse
-        # do_polling here, too.  With #2781 we should make it more direct.
-        stat._answers = [\
-            # Answer for "show_processes"
-            (create_answer(0, []),  None),
-            # Answers for "getstats" for Init (the other one for Auth, but
-            # that doesn't matter for this test)
-            (create_answer(0, stat._init_sdata), {'from': 'init'}),
-            (create_answer(0, stat._init_sdata), {'from': 'init'})
-            ]
-        stat.update_modules = lambda: None
-
-        self.assertEqual(
-            self.__send_command(
-                stat, 'show',
-                params={ 'owner' : 'Init', 'name'  : 'boot_time' }),
-            (0, {'Init': {'boot_time': self.const_datetime}}))
-
-    def test_commands(self):
-        self.stats = MyStats()
-
-        # status
-        self.assertEqual(self.stats.command_status(),
-                isc.config.create_answer(
-                0, "Stats is up. (PID " + str(os.getpid()) + ")"))
-
-        # shutdown
-        self.stats.running = True
-        self.assertEqual(self.stats.command_shutdown(),
-                         isc.config.create_answer(0))
-        self.assertFalse(self.stats.running)
-
-    def test_command_show_error(self):
-        self.stats = MyStats()
-        self.assertEqual(self.stats.command_show(owner='Foo', name=None),
-                         isc.config.create_answer(
-                1,
-                "specified arguments are incorrect: owner: Foo, name: None"))
-        self.assertEqual(self.stats.command_show(owner='Foo', name='_bar_'),
-                         isc.config.create_answer(
-                1,
-                "specified arguments are incorrect: owner: Foo, name: _bar_"))
-        self.assertEqual(self.stats.command_show(owner='Foo', name='bar'),
-                         isc.config.create_answer(
-                1,
-                "specified arguments are incorrect: owner: Foo, name: bar"))
-
-    def test_command_show_auth(self):
-        self.stats = MyStats()
-        self.stats.update_modules = lambda: None
-
-        # Test data borrowed from test_update_statistics_data_withmid
-        self.stats._answers = [
-            (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
-                               [1035, 'b10-auth-2', 'Auth']]),  None),
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth1'}),
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth2'}),
-            (create_answer(0, self.stats._auth_sdata), {'from': 'auth3'})
-            ]
-
-        num_instances = 2
-        sum_qtcp = 0
-        sum_qudp = 0
-        sum_qtcp_perzone1 = 0
-        sum_qudp_perzone1 = 0
-        sum_qtcp_perzone2 = 4 * num_instances
-        sum_qudp_perzone2 = 3 * num_instances
-        sum_qtcp_nds_perzone10 = 0
-        sum_qudp_nds_perzone10 = 0
-        sum_qtcp_nds_perzone20 = 4 * num_instances
-        sum_qudp_nds_perzone20 = 3 * num_instances
-
-        self.maxDiff = None
-        for a in (0, num_instances):
-            sum_qtcp += self.stats._queries_tcp
-            sum_qudp += self.stats._queries_udp
-            sum_qtcp_perzone1 += self.stats._queries_per_zone[0]['queries.tcp']
-            sum_qudp_perzone1 += self.stats._queries_per_zone[0]['queries.udp']
-            sum_qtcp_nds_perzone10 += \
-                self.stats._nds_queries_per_zone['test10.example']['queries.tcp']
-            sum_qudp_nds_perzone10 += \
-                self.stats._nds_queries_per_zone['test10.example']['queries.udp']
-
-        self.assertEqual(self.stats.command_show(owner='Auth'),
-                         isc.config.create_answer(
-                0, {'Auth':{ 'queries.udp': sum_qudp,
-                     'queries.tcp': sum_qtcp,
-                     'queries.perzone': [{ 'zonename': 'test1.example',
-                                           'queries.udp': sum_qudp_perzone1,
-                                           'queries.tcp': sum_qtcp_perzone1 },
-                                         { 'zonename': 'test2.example',
-                                           'queries.udp': sum_qudp_perzone2,
-                                           'queries.tcp': sum_qtcp_perzone2 }
-                                         ],
-                     'nds_queries.perzone': { 'test10.example' : {
-                                              'queries.udp': sum_qudp_nds_perzone10,
-                                              'queries.tcp': sum_qtcp_nds_perzone10 },
-                                              'test20.example' : {
-                                              'queries.udp': sum_qudp_nds_perzone20,
-                                              'queries.tcp': sum_qtcp_nds_perzone20 }
-                             }}}))
-        self.assertEqual(self.stats.command_show(owner='Auth', name='queries.udp'),
-                         isc.config.create_answer(
-                0, {'Auth': {'queries.udp': sum_qudp}}))
-        self.assertEqual(self.stats.command_show(owner='Auth', name='queries.perzone'),
-                         isc.config.create_answer(
-                0, {'Auth': {'queries.perzone': [
-                            { 'zonename': 'test1.example',
-                              'queries.udp': sum_qudp_perzone1,
-                              'queries.tcp': sum_qtcp_perzone1 },
-                            { 'zonename': 'test2.example',
-                              'queries.udp': sum_qudp_perzone2,
-                              'queries.tcp': sum_qtcp_perzone2 }]}}))
-        self.assertEqual(self.stats.command_show(owner='Auth', name='nds_queries.perzone'),
-                         isc.config.create_answer(
-                0, {'Auth': {'nds_queries.perzone': {
-                            'test10.example': {
-                                'queries.udp': sum_qudp_nds_perzone10,
-                                'queries.tcp': sum_qtcp_nds_perzone10 },
-                            'test20.example': {
-                                'queries.udp': sum_qudp_nds_perzone20,
-                                'queries.tcp': sum_qtcp_nds_perzone20 }}}}))
-
-    def test_command_show_stats(self):
-        self.stats = MyStats()
-        orig_get_datetime = stats.get_datetime
-        orig_get_timestamp = stats.get_timestamp
-        stats.get_datetime = lambda x=None: self.const_datetime
-        stats.get_timestamp = lambda : self.const_timestamp
-        self.assertEqual(self.stats.command_show(owner='Stats',
-                                                 name='report_time'),
-                         isc.config.create_answer(
-                0, {'Stats': {'report_time':self.const_datetime}}))
-        self.assertEqual(self.stats.command_show(owner='Stats',
-                                                 name='timestamp'),
-                         isc.config.create_answer(
-                0, {'Stats': {'timestamp':self.const_timestamp}}))
-        stats.get_datetime = orig_get_datetime
-        stats.get_timestamp = orig_get_timestamp
-        self.stats.do_polling = lambda : None
-        self.stats.modules[self.stats.module_name] = \
-            isc.config.module_spec.ModuleSpec(
-            { "module_name": self.stats.module_name, "statistics": [] } )
-        self.assertRaises(
-            stats.StatsError, self.stats.command_show,
-            owner=self.stats.module_name, name='bar')
-
-    def test_command_showchema(self):
-        self.stats = MyStats()
-        (rcode, value) = isc.config.ccsession.parse_answer(
-            self.stats.command_showschema())
-        self.assertEqual(rcode, 0)
-        self.assertEqual(len(value), 3)
-        self.assertTrue('Stats' in value)
-        self.assertTrue('Init' in value)
-        self.assertTrue('Auth' in value)
-        self.assertFalse('__Dummy__' in value)
-        schema = value['Stats']
-        self.assertEqual(len(schema), 5)
-        for item in schema:
-            self.assertTrue(len(item) == 6 or len(item) == 7)
-            self.assertTrue('item_name' in item)
-            self.assertTrue('item_type' in item)
-            self.assertTrue('item_optional' in item)
-            self.assertTrue('item_default' in item)
-            self.assertTrue('item_title' in item)
-            self.assertTrue('item_description' in item)
-            if len(item) == 7:
-                self.assertTrue('item_format' in item)
-
-        schema = value['Init']
-        self.assertEqual(len(schema), 1)
-        for item in schema:
-            self.assertTrue(len(item) == 7)
-            self.assertTrue('item_name' in item)
-            self.assertTrue('item_type' in item)
-            self.assertTrue('item_optional' in item)
-            self.assertTrue('item_default' in item)
-            self.assertTrue('item_title' in item)
-            self.assertTrue('item_description' in item)
-            self.assertTrue('item_format' in item)
-
-        schema = value['Auth']
-        self.assertEqual(len(schema), 4)
-        for item in schema:
-            if item['item_type'] == 'list' or item['item_type'] == 'named_set':
-                self.assertEqual(len(item), 7)
-            else:
-                self.assertEqual(len(item), 6)
-            self.assertTrue('item_name' in item)
-            self.assertTrue('item_type' in item)
-            self.assertTrue('item_optional' in item)
-            self.assertTrue('item_default' in item)
-            self.assertTrue('item_title' in item)
-            self.assertTrue('item_description' in item)
-
-        (rcode, value) = isc.config.ccsession.parse_answer(
-            self.stats.command_showschema(owner='Stats'))
-        self.assertEqual(rcode, 0)
-        self.assertTrue('Stats' in value)
-        self.assertFalse('Init' in value)
-        self.assertFalse('Auth' in value)
-        for item in value['Stats']:
-            self.assertTrue(len(item) == 6 or len(item) == 7)
-            self.assertTrue('item_name' in item)
-            self.assertTrue('item_type' in item)
-            self.assertTrue('item_optional' in item)
-            self.assertTrue('item_default' in item)
-            self.assertTrue('item_title' in item)
-            self.assertTrue('item_description' in item)
-            if len(item) == 7:
-                self.assertTrue('item_format' in item)
-
-        (rcode, value) = isc.config.ccsession.parse_answer(
-            self.stats.command_showschema(owner='Stats', name='report_time'))
-        self.assertEqual(rcode, 0)
-        self.assertTrue('Stats' in value)
-        self.assertFalse('Init' in value)
-        self.assertFalse('Auth' in value)
-        self.assertEqual(len(value['Stats'][0]), 7)
-        self.assertTrue('item_name' in value['Stats'][0])
-        self.assertTrue('item_type' in value['Stats'][0])
-        self.assertTrue('item_optional' in value['Stats'][0])
-        self.assertTrue('item_default' in value['Stats'][0])
-        self.assertTrue('item_title' in value['Stats'][0])
-        self.assertTrue('item_description' in value['Stats'][0])
-        self.assertTrue('item_format' in value['Stats'][0])
-        self.assertEqual(value['Stats'][0]['item_name'], 'report_time')
-        self.assertEqual(value['Stats'][0]['item_format'], 'date-time')
-
-        self.assertEqual(self.stats.command_showschema(owner='Foo'),
-                         isc.config.create_answer(
-                1, "specified arguments are incorrect: owner: Foo, name: None"))
-        self.assertEqual(self.stats.command_showschema(owner='Foo', name='bar'),
-                         isc.config.create_answer(
-                1, "specified arguments are incorrect: owner: Foo, name: bar"))
-        self.assertEqual(self.stats.command_showschema(owner='Auth'),
-                         isc.config.create_answer(
-                0, {'Auth': [{
-                        "item_default": 0,
-                        "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
-                        "item_name": "queries.tcp",
-                        "item_optional": False,
-                        "item_title": "Queries TCP",
-                        "item_type": "integer"
-                        },
-                    {
-                        "item_default": 0,
-                        "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially",
-                        "item_name": "queries.udp",
-                        "item_optional": False,
-                        "item_title": "Queries UDP",
-                        "item_type": "integer"
-                        },
-                    {
-                        "item_name": "queries.perzone",
-                        "item_type": "list",
-                        "item_optional": False,
-                        "item_default": [
-                            {
-                                "zonename" : "test1.example",
-                                "queries.udp" : 1,
-                                "queries.tcp" : 2
-                                },
-                            {
-                                "zonename" : "test2.example",
-                                "queries.udp" : 3,
-                                "queries.tcp" : 4
-                                }
-                        ],
-                        "item_title": "Queries per zone",
-                        "item_description": "Queries per zone",
-                        "list_item_spec": {
-                            "item_name": "zones",
-                            "item_type": "map",
-                            "item_optional": False,
-                            "item_default": {},
-                            "map_item_spec": [
-                                {
-                                    "item_name": "zonename",
-                                    "item_type": "string",
-                                    "item_optional": False,
-                                    "item_default": "",
-                                    "item_title": "Zonename",
-                                    "item_description": "Zonename"
-                                    },
-                                {
-                                    "item_name": "queries.udp",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Queries UDP per zone",
-                                    "item_description": "A number of UDP query counts per zone"
-                                    },
-                                {
-                                    "item_name": "queries.tcp",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Queries TCP per zone",
-                                    "item_description": "A number of TCP query counts per zone"
-                                    }
-                                ]
-                            }
-                        },
-                    {
-                        "item_name": "nds_queries.perzone",
-                        "item_type": "named_set",
-                        "item_optional": False,
-                        "item_default": {
-                            "test10.example" : {
-                                "queries.udp" : 1,
-                                "queries.tcp" : 2
-                            },
-                            "test20.example" : {
-                                "queries.udp" : 3,
-                                "queries.tcp" : 4
-                            }
-                        },
-                        "item_title": "Queries per zone",
-                        "item_description": "Queries per zone",
-                        "named_set_item_spec": {
-                            "item_name": "zonename",
-                            "item_type": "map",
-                            "item_optional": False,
-                            "item_default": {},
-                            "item_title": "Zonename",
-                            "item_description": "Zonename",
-                            "map_item_spec": [
-                                {
-                                    "item_name": "queries.udp",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Queries UDP per zone",
-                                    "item_description": "A number of UDP query counts per zone"
-                                    },
-                                {
-                                    "item_name": "queries.tcp",
-                                    "item_type": "integer",
-                                    "item_optional": False,
-                                    "item_default": 0,
-                                    "item_title": "Queries TCP per zone",
-                                    "item_description": "A number of TCP query counts per zone"
-                                    }
-                                ]
-                            }
-                        }]}))
-        self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.tcp'),
-                         isc.config.create_answer(
-                0, {'Auth': [{
-                    "item_default": 0,
-                    "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially",
-                    "item_name": "queries.tcp",
-                    "item_optional": False,
-                    "item_title": "Queries TCP",
-                    "item_type": "integer"
-                    }]}))
-        self.assertEqual(self.stats.command_showschema(owner='Auth', name='queries.perzone'),
-                         isc.config.create_answer(
-                0, {'Auth':[{
-                    "item_name": "queries.perzone",
-                    "item_type": "list",
-                    "item_optional": False,
-                    "item_default": [
-                        {
-                            "zonename" : "test1.example",
-                            "queries.udp" : 1,
-                            "queries.tcp" : 2
-                            },
-                        {
-                            "zonename" : "test2.example",
-                            "queries.udp" : 3,
-                            "queries.tcp" : 4
-                            }
-                    ],
-                    "item_title": "Queries per zone",
-                    "item_description": "Queries per zone",
-                    "list_item_spec": {
-                        "item_name": "zones",
-                        "item_type": "map",
-                        "item_optional": False,
-                        "item_default": {},
-                        "map_item_spec": [
-                            {
-                                "item_name": "zonename",
-                                "item_type": "string",
-                                "item_optional": False,
-                                "item_default": "",
-                                "item_title": "Zonename",
-                                "item_description": "Zonename"
-                                },
-                            {
-                                "item_name": "queries.udp",
-                                "item_type": "integer",
-                                "item_optional": False,
-                                "item_default": 0,
-                                "item_title": "Queries UDP per zone",
-                                "item_description": "A number of UDP query counts per zone"
-                                },
-                            {
-                                "item_name": "queries.tcp",
-                                "item_type": "integer",
-                                "item_optional": False,
-                                "item_default": 0,
-                                "item_title": "Queries TCP per zone",
-                                "item_description": "A number of TCP query counts per zone"
-                                }
-                            ]
-                         }
-                     }]}))
-        self.assertEqual(self.stats.command_showschema(owner='Auth', name='nds_queries.perzone'),
-                         isc.config.create_answer(
-                0, {'Auth':[{
-                    "item_name": "nds_queries.perzone",
-                    "item_type": "named_set",
-                    "item_optional": False,
-                    "item_default": {
-                        "test10.example" : {
-                            "queries.udp" : 1,
-                            "queries.tcp" : 2
-                        },
-                        "test20.example" : {
-                            "queries.udp" : 3,
-                            "queries.tcp" : 4
-                        }
-                    },
-                    "item_title": "Queries per zone",
-                    "item_description": "Queries per zone",
-                    "named_set_item_spec": {
-                        "item_name": "zonename",
-                        "item_type": "map",
-                        "item_optional": False,
-                        "item_default": {},
-                        "item_title": "Zonename",
-                        "item_description": "Zonename",
-                        "map_item_spec": [
-                            {
-                                "item_name": "queries.udp",
-                                "item_type": "integer",
-                                "item_optional": False,
-                                "item_default": 0,
-                                "item_title": "Queries UDP per zone",
-                                "item_description": "A number of UDP query counts per zone"
-                                },
-                            {
-                                "item_name": "queries.tcp",
-                                "item_type": "integer",
-                                "item_optional": False,
-                                "item_default": 0,
-                                "item_title": "Queries TCP per zone",
-                                "item_description": "A number of TCP query counts per zone"
-                                }
-                            ]
-                        }
-                    }]}))
-
-        self.assertEqual(self.stats.command_showschema(owner='Stats', name='bar'),
-                         isc.config.create_answer(
-                1, "specified arguments are incorrect: owner: Stats, name: bar"))
-        self.assertEqual(self.stats.command_showschema(name='bar'),
-                         isc.config.create_answer(
-                1, "module name is not specified"))
-
-    def test_get_multi_module_list(self):
-        """Test _get_multi_module_list() returns a module list which is running
-        as multiple modules."""
-        stat = MyStats()
-        # no answer
-        self.assertListEqual([], stat._get_multi_module_list())
-        # proc list returned
-        proc_list = [
-            [29317, 'b10-xfrout', 'Xfrout'],
-            [29318, 'b10-xfrin', 'Xfrin'],
-            [20061, 'b10-auth','Auth'],
-            [20103, 'b10-auth-2', 'Auth']]
-        mod_list = [ a[2] for a in proc_list ]
-        stat._answers = [
-            # Answer for "show_processes"
-            (create_answer(0, proc_list), {'from': 'init'})
-            ]
-        self.assertListEqual(mod_list, stat._get_multi_module_list())
-        # invalid proc list
-        stat._answers = [
-            # Answer for "show_processes"
-            (create_answer(0, [[999, 'invalid', 'Invalid'], 'invalid']),
-             {'from': 'init'})
-            ]
-        self.assertListEqual(['Invalid', None], stat._get_multi_module_list())
-
-    def test_get_multi_module_list_rpcrecipientmissing(self):
-        """Test _get_multi_module_list() raises an RPCRecipientMissing exception
-        if rcp_call() raise the exception"""
-        # RPCRecipientMissing case
-        stat = MyStats()
-        ex = isc.config.RPCRecipientMissing
-        def __raise(*x): raise ex(*x)
-        stat.mccs.rpc_call = lambda x,y: __raise('Error')
-        self.assertRaises(ex, stat._get_multi_module_list)
-
-    def test_get_multi_module_list_rpcerror(self):
-        """Test _get_multi_module_list() returns an empty list if rcp_call()
-        raise an RPCError exception"""
-        # RPCError case
-        stat = MyStats()
-        ex = isc.config.RPCError
-        def __raise(*x): raise ex(*x)
-        stat.mccs.rpc_call = lambda x,y: __raise(99, 'Error')
-        self.assertListEqual([], stat._get_multi_module_list())
-
-    def test_get_multi_module_list_initsessiontimeout(self):
-        """Test _get_multi_module_list() raises an InitSeeionTimeout exception
-        if a CC session times out in rcp_call()"""
-        # InitSeeionTimeout case
-        stat = MyStats()
-        ex = isc.cc.session.SessionTimeout
-        def __raise(*x): raise ex(*x)
-        stat.mccs.rpc_call = lambda x,y: __raise()
-        self.assertRaises(stats.InitSessionTimeout, stat._get_multi_module_list)
-
-    def test_query_statistics(self):
-        """Test _query_statistics returns a list of pairs of module and
-        sequences from group_sendmsg()"""
-        stat = MyStats()
-        # imitate stat.get_statistics_data().items. The order of the array
-        # returned by items() should be preserved.
-        class DummyDict:
-            def items(self):
-                return [('Init', 'dummy'), ('Stats', 'dummy'), ('Auth', 'dummy')]
-        stat.get_statistics_data = lambda: DummyDict()
-        mod = ('Init', 'Auth', 'Auth')
-        seq = [('Init', stat._seq + 1),
-               ('Auth', stat._seq + 2),
-               ('Auth', stat._seq + 2) ]
-        self.assertListEqual(seq, stat._query_statistics(mod))
-
-    def test_collect_statistics(self):
-        """Test _collect_statistics() collects statistics data from each module
-        based on the sequences which is a list of values returned from
-        group_sendmsg()"""
-        stat = MyStats()
-        seq = [ ('Init', stat._seq + 1),
-                ('Auth', stat._seq + 2),
-                ('Auth', stat._seq + 2) ]
-        ret = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'}),
-               ('Auth', 'fromauth1', {'queries.tcp': 100}),
-               ('Auth', 'fromauth2', {'queries.udp': 200})]
-        stat._answers = [
-            (create_answer(0, r[2]), {'from': r[1]}) for r in ret
-            ]
-        self.assertListEqual(ret, stat._collect_statistics(seq))
-
-    def test_collect_statistics_nodata(self):
-        """Test _collect_statistics() returns empty statistics data if
-        a module returns an empty list"""
-        stat = MyStats()
-        seq = []
-        stat._answers = []
-        ret = []
-        self.assertListEqual(ret, stat._collect_statistics(seq))
-
-    def test_collect_statistics_nonzero_rcode(self):
-        """Test _collect_statistics() returns empty statistics data if
-        a module returns non-zero rcode"""
-        stat = MyStats()
-        seq = [('Init', stat._seq + 1)]
-        stat._answers = [
-            (create_answer(1, 'error'), {'from': 'frominit'})
-            ]
-        ret = []
-        self.assertListEqual(ret, stat._collect_statistics(seq))
-
-    def test_collect_statistics_sessiontimeout(self):
-        """Test _collect_statistics() collects statistics data from each module
-        based on the sequences which is a list of values returned from
-        group_sendmsg(). In this test case, SessionTimeout exceptions are raised
-        while collecting from Auth. This tests _collect_statistics skips
-        collecting from Auth."""
-        # SessionTimeout case
-        stat = MyStats()
-        ex = isc.cc.session.SessionTimeout
-        def __raise(*x): raise ex(*x)
-        # SessionTimeout is raised when asking to Auth
-        stat.cc_session.group_recvmsg = lambda x,seq: \
-            __raise() if seq == stat._seq + 2 else stat._answers.pop(0)
-        seq = [ ('Init', stat._seq + 1),
-                ('Auth', stat._seq + 2),
-                ('Auth', stat._seq + 2) ]
-        ret = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'})]
-        stat._answers = [
-            (create_answer(0, r[2]), {'from': r[1]}) for r in ret
-            ]
-        self.assertListEqual(ret, stat._collect_statistics(seq))
-
-    def test_refresh_statistics(self):
-        """Test _refresh_statistics() refreshes statistics data from given data
-        """
-        stat = MyStats()
-        self.assertEqual(self.const_default_datetime,
-                         stat.statistics_data['Init']['boot_time'])
-        self.assertEqual(0,
-                         stat.statistics_data['Auth']['queries.tcp'])
-        self.assertEqual(0,
-                         stat.statistics_data['Auth']['queries.udp'])
-        # change stats.get_datetime() for testing 'last_update_time'
-        orig_get_datetime = stats.get_datetime
-        stats.get_datetime = lambda : self.const_datetime
-        arg = [('Init', 'frominit', {'boot_time': '2013-01-01T00:00:00Z'}),
-               ('Auth', 'fromauth1', {'queries.tcp': 100}),
-               ('Auth', 'fromauth2', {'queries.udp': 200})]
-        stat._refresh_statistics(arg)
-        self.assertEqual('2013-01-01T00:00:00Z',
-                         stat.statistics_data['Init']['boot_time'])
-        self.assertEqual(100,
-                         stat.statistics_data['Auth']['queries.tcp'])
-        self.assertEqual(200,
-                         stat.statistics_data['Auth']['queries.udp'])
-        self.assertEqual(self.const_datetime,
-                         stat.statistics_data['Stats']['last_update_time'])
-        stats.get_datetime = orig_get_datetime
-
-    def test_polling_init(self):
-        """check statistics data of 'Init'."""
-
-        stat = MyStats()
-        # At this point 'stat' is initialized with statistics for Stats,
-        # Init and Auth modules.  In this test, we only need to check for Init
-        # statistics, while do_polling() can ask for module statistics in an
-        # unpredictable order (if hash randomization is enabled, which is
-        # the case by default for Python 3.3).  To make it predictable and
-        # ensure the prepared answer doesn't cause disruption, we remove the
-        # information for the Auth module for this test.
-        del stat.statistics_data['Auth']
-
-        stat.update_modules = lambda: None
-
-        stat._answers = [
-            # Answer for "show_processes"
-            (create_answer(0, []),  None),
-            # Answers for "getstats" for Init (type of boot_time is invalid)
-            (create_answer(0, {'boot_time': self.const_datetime}),
-             {'from': 'init'}),
-            ]
-
-        stat.do_polling()
-        self.assertEqual(
-            stat.statistics_data_bymid['Init']['init'],
-            {'boot_time': self.const_datetime})
-
-    def test_polling_consolidate(self):
-        """check statistics data of multiple instances of same module."""
-        stat = MyStats()
-        stat.update_modules = lambda: None
-
-        # Test data borrowed from test_update_statistics_data_withmid
-        stat._answers = [
-            (create_answer(0, [[1034, 'b10-auth-1', 'Auth'],
-                               [1035, 'b10-auth-2', 'Auth']]),  None),
-            (create_answer(0, stat._auth_sdata), {'from': 'auth1'}),
-            (create_answer(0, stat._auth_sdata), {'from': 'auth2'}),
-            (create_answer(0, stat._auth_sdata), {'from': 'auth3'})
-            ]
-
-        stat.do_polling()
-
-        # check statistics data of each 'Auth' instances.  expected data
-        # for 'nds_queries.perzone' is special as it needs data merge.
-        self.assertEqual(2, len(stat.statistics_data_bymid['Auth'].values()))
-        for s in stat.statistics_data_bymid['Auth'].values():
-            self.assertEqual(
-                s, {'queries.perzone': stat._auth_sdata['queries.perzone'],
-                    'nds_queries.perzone': stat._nds_queries_per_zone,
-                    'queries.tcp': stat._auth_sdata['queries.tcp'],
-                    'queries.udp': stat._auth_sdata['queries.udp']})
-
-        # check consolidation of statistics data of the auth instances.
-        # it's union of the reported data and the spec default.
-        n = len(stat.statistics_data_bymid['Auth'].values())
-        self.maxDiff = None
-        self.assertEqual(
-            stat.statistics_data['Auth'],
-            {'queries.perzone': [
-                    {'zonename': 'test1.example',
-                     'queries.tcp': 5 * n,
-                     'queries.udp': 4 * n},
-                    {'zonename': 'test2.example',
-                     'queries.tcp': 4 * n,
-                     'queries.udp': 3 * n},
-                    ],
-             'nds_queries.perzone': {
-                    'test10.example': {
-                        'queries.tcp': 5 * n,
-                        'queries.udp': 4 * n
-                        },
-                    'test20.example': {
-                        'queries.tcp': 4 * n,
-                        'queries.udp': 3 * n
-                        },
-                    },
-             'queries.tcp': 3 * n,
-             'queries.udp': 2 * n})
-
-    def test_polling_stats(self):
-        """Check statistics data of 'Stats'
-
-        This is actually irrelevant to do_polling(), but provided to
-        compatibility of older tests.
-
-        """
-        stat = MyStats()
-        self.assertEqual(len(stat.statistics_data['Stats']), 5)
-        self.assertTrue('boot_time' in stat.statistics_data['Stats'])
-        self.assertTrue('last_update_time' in stat.statistics_data['Stats'])
-        self.assertTrue('report_time' in stat.statistics_data['Stats'])
-        self.assertTrue('timestamp' in stat.statistics_data['Stats'])
-        self.assertEqual(stat.statistics_data['Stats']['lname'],
-                         stat.mccs._session.lname)
-
-    def test_refresh_statistics_broken_statistics_data(self):
-        """Test _refresh_statistics() doesn't incorporate broken statistics data
-        """
-        stat = MyStats()
-        # check default statistics data of 'Init'
-        self.assertEqual(
-            {'boot_time': self.const_default_datetime},
-            stat.statistics_data['Init'])
-        last_update_time = stat.statistics_data['Stats']['last_update_time']
-        # _refresh_statistics() should ignore the invalid statistics_data(type
-        # of boot_time is invalid); default data shouldn't be replaced.
-        arg = [('Init', 'lname', {'boot_time': 1})]
-        stat._refresh_statistics(arg)
-        self.assertEqual(
-            {'boot_time': self.const_default_datetime},
-            stat.statistics_data['Init'])
-        # 'last_update_time' doesn't change
-        self.assertEqual(
-            last_update_time,
-            stat.statistics_data['Stats']['last_update_time'])
-
-    def test_polling_update_lasttime_poll(self):
-        """Test _lasttime_poll is updated after do_polling()
-        """
-        orig_get_timestamp = stats.get_timestamp
-        stats.get_timestamp = lambda : self.const_timestamp
-        stat = MyStats()
-        self.assertEqual(0.0, stat._lasttime_poll)
-        stat.do_polling()
-        self.assertEqual(self.const_timestamp, stat._lasttime_poll)
-        stats.get_timestamp = orig_get_timestamp
-
-    def test_polling_initsessiontimeout(self):
-        """Test _lasttime_poll is updated after do_polling() in case that it catches
-        InitSesionTimeout at _get_multi_module_list()
-        """
-        orig_get_timestamp = stats.get_timestamp
-        stats.get_timestamp = lambda : self.const_timestamp
-        ex = stats.InitSessionTimeout
-        def __raise(*x): raise ex(*x)
-        stat = MyStats()
-        self.assertEqual(0.0, stat._lasttime_poll)
-        stat._get_multi_module_list = lambda: __raise()
-        stat.do_polling()
-        self.assertEqual(self.const_timestamp, stat._lasttime_poll)
-        stats.get_timestamp = orig_get_timestamp
-
-class Z_TestOSEnv(unittest.TestCase):
-    # Running this test would break logging setting.  To prevent it from
-    # affecting other tests we use the same workaround as Z_TestOSEnv in
-    # stats-httpd_test.py.
-    def test_osenv(self):
-        """
-        test for the environ variable "B10_FROM_SOURCE"
-        "B10_FROM_SOURCE" is set in Makefile
-        """
-        # test case having B10_FROM_SOURCE
-        self.assertTrue("B10_FROM_SOURCE" in os.environ)
-        self.assertEqual(stats.SPECFILE_LOCATION, \
-                             os.environ["B10_FROM_SOURCE"] + os.sep + \
-                             "src" + os.sep + "bin" + os.sep + "stats" + \
-                             os.sep + "stats.spec")
-        # test case not having B10_FROM_SOURCE
-        path = os.environ["B10_FROM_SOURCE"]
-        os.environ.pop("B10_FROM_SOURCE")
-        self.assertFalse("B10_FROM_SOURCE" in os.environ)
-        # import stats again
-        imp.reload(stats)
-        # revert the changes
-        os.environ["B10_FROM_SOURCE"] = path
-        imp.reload(stats)
-
-if __name__ == "__main__":
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
deleted file mode 100644 (file)
index 99cc980..0000000
+++ /dev/null
@@ -1,581 +0,0 @@
-# Copyright (C) 2011-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.
-
-"""
-Utilities and mock modules for unittests of statistics modules
-
-"""
-import os
-import io
-import time
-import threading
-import json
-import signal
-import socket
-
-import isc.config.cfgmgr
-import stats
-import stats_httpd
-
-CONST_BASETIME = (2011, 6, 22, 8, 14, 8, 2, 173, 0)
-
-class SignalHandler():
-    """A signal handler class for deadlock in unittest"""
-    def __init__(self, fail_handler, timeout=20):
-        """sets a schedule in SIGARM for invoking the handler via
-        unittest.TestCase after timeout seconds (default is 20)"""
-        self.fail_handler = fail_handler
-        self.orig_handler = signal.signal(signal.SIGALRM, self.sig_handler)
-        signal.alarm(timeout)
-
-    def reset(self):
-        """resets the schedule in SIGALRM"""
-        signal.alarm(0)
-        signal.signal(signal.SIGALRM, self.orig_handler)
-
-    def sig_handler(self, signal, frame):
-        """invokes unittest.TestCase.fail as a signal handler"""
-        self.fail_handler("A deadlock might be detected")
-
-class ThreadingServerManager:
-    def __init__(self, server, *args, **kwargs):
-        self.server = server(*args, **kwargs)
-        self.server_name = server.__name__
-        self.server._thread = threading.Thread(
-            name=self.server_name, target=self.server.run)
-        self.server._thread.daemon = True
-
-    def run(self):
-        self.server._thread.start()
-        self.server._started.wait()
-        self.server._started.clear()
-
-    def shutdown(self, blocking=False):
-        """Shut down the server by calling its own shutdown() method.
-           Then wait for its thread to finish. If blocking is True,
-           the thread.join() blocks until the thread finishes. If not,
-           it uses a zero timeout. The latter is necessary in a number
-           of existing tests. We should redo this part (we should not
-           even need threads in most, if not all, of these threads, see
-           ticket #1668)"""
-        self.server.shutdown()
-        if blocking:
-            self.server._thread.join()
-        else:
-            self.server._thread.join(0) # timeout is 0
-
-INIT_SPEC_STR = """\
-{
-  "module_spec": {
-    "module_name": "Init",
-    "module_description": "Mock Master process",
-    "config_data": [
-      {
-        "item_name": "components",
-        "item_type": "named_set",
-        "item_optional": false,
-        "item_default": {
-          "b10-stats": { "address": "Stats", "kind": "dispensable" },
-          "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
-        },
-        "named_set_item_spec": {
-          "item_name": "component",
-          "item_type": "map",
-          "item_optional": false,
-          "item_default": { },
-          "map_item_spec": [
-            {
-              "item_name": "special",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "process",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "kind",
-              "item_optional": false,
-              "item_type": "string",
-              "item_default": "dispensable"
-            },
-            {
-              "item_name": "address",
-              "item_optional": true,
-              "item_type": "string"
-            },
-            {
-              "item_name": "params",
-              "item_optional": true,
-              "item_type": "list",
-              "list_item_spec": {
-                "item_name": "param",
-                "item_optional": false,
-                "item_type": "string",
-                "item_default": ""
-              }
-            },
-            {
-              "item_name": "priority",
-              "item_optional": true,
-              "item_type": "integer"
-            }
-          ]
-        }
-      }
-    ],
-    "commands": [
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down BIND 10",
-        "command_args": []
-      },
-      {
-        "command_name": "ping",
-        "command_description": "Ping the b10-init process",
-        "command_args": []
-      },
-      {
-        "command_name": "show_processes",
-        "command_description": "List the running BIND 10 processes",
-        "command_args": []
-      }
-    ],
-    "statistics": [
-      {
-        "item_name": "boot_time",
-        "item_type": "string",
-        "item_optional": false,
-        "item_default": "1970-01-01T00:00:00Z",
-        "item_title": "Boot time",
-        "item_description": "A date time when bind10 process starts initially",
-        "item_format": "date-time"
-      }
-    ]
-  }
-}
-"""
-
-# Note: this is derived of the spec for the DNS authoritative server, but
-# for the purpose of this test, it's completely irrelevant to DNS.
-# Some statisittics specs do not make sense for practical sense but used
-# just cover various types of statistics data (list, map/dict, etc).
-AUTH_SPEC_STR = """\
-{
-  "module_spec": {
-    "module_name": "Auth",
-    "module_description": "Mock Authoritative service",
-    "config_data": [],
-    "commands": [],
-    "statistics": [
-      {
-        "item_name": "queries.tcp",
-        "item_type": "integer",
-        "item_optional": false,
-        "item_default": 0,
-        "item_title": "Queries TCP",
-        "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
-      },
-      {
-        "item_name": "queries.udp",
-        "item_type": "integer",
-        "item_optional": false,
-        "item_default": 0,
-        "item_title": "Queries UDP",
-        "item_description": "A number of total query counts which all auth servers receive over UDP since they started initially"
-      },
-      {
-        "item_name": "queries.perzone",
-        "item_type": "list",
-        "item_optional": false,
-        "item_default": [
-          {
-            "zonename" : "test1.example",
-            "queries.udp" : 1,
-            "queries.tcp" : 2
-          },
-          {
-            "zonename" : "test2.example",
-            "queries.udp" : 3,
-            "queries.tcp" : 4
-          }
-        ],
-        "item_title": "Queries per zone",
-        "item_description": "Queries per zone",
-        "list_item_spec": {
-          "item_name": "zones",
-          "item_type": "map",
-          "item_optional": false,
-          "item_default": {},
-          "map_item_spec": [
-            {
-              "item_name": "zonename",
-              "item_type": "string",
-              "item_optional": false,
-              "item_default": "",
-              "item_title": "Zonename",
-              "item_description": "Zonename"
-            },
-            {
-              "item_name": "queries.udp",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "Queries UDP per zone",
-              "item_description": "A number of UDP query counts per zone"
-            },
-            {
-              "item_name": "queries.tcp",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "Queries TCP per zone",
-              "item_description": "A number of TCP query counts per zone"
-            }
-          ]
-        }
-      },
-      {
-        "item_name": "nds_queries.perzone",
-        "item_type": "named_set",
-        "item_optional": false,
-        "item_default": {
-          "test10.example" : {
-            "queries.udp" : 1,
-            "queries.tcp" : 2
-          },
-          "test20.example" : {
-            "queries.udp" : 3,
-            "queries.tcp" : 4
-          }
-        },
-        "item_title": "Queries per zone",
-        "item_description": "Queries per zone",
-        "named_set_item_spec": {
-          "item_name": "zonename",
-          "item_type": "map",
-          "item_optional": false,
-          "item_default": {},
-          "item_title": "Zonename",
-          "item_description": "Zonename",
-          "map_item_spec": [
-            {
-              "item_name": "queries.udp",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "Queries UDP per zone",
-              "item_description": "A number of UDP query counts per zone"
-            },
-            {
-              "item_name": "queries.tcp",
-              "item_type": "integer",
-              "item_optional": false,
-              "item_default": 0,
-              "item_title": "Queries TCP per zone",
-              "item_description": "A number of TCP query counts per zone"
-            }
-          ]
-        }
-      }
-    ]
-  }
-}
-"""
-
-class MyModuleCCSession(isc.config.ConfigData):
-    """Mocked ModuleCCSession class.
-
-    This class incorporates the module spec directly from the file,
-    and works as if the ModuleCCSession class as much as possible
-    without involving network I/O.
-
-    """
-    def __init__(self, spec_file, config_handler, command_handler):
-        module_spec = isc.config.module_spec_from_file(spec_file)
-        isc.config.ConfigData.__init__(self, module_spec)
-        self._session = self
-        self.stopped = False
-        self.closed = False
-        self.lname = 'mock_mod_ccs'
-        self._msg = None
-        self._env = None
-
-    def start(self):
-        pass
-
-    def send_stopping(self):
-        self.stopped = True     # just record it's called to inspect it later
-
-    def close(self):
-        self.closed = True
-
-    def check_command_without_recvmsg(self, msg, env):
-        self._msg = msg
-        self._env = env
-
-class MyStats(stats.Stats):
-    """A faked Stats class for unit tests.
-
-    This class inherits most of the real Stats class, but replaces the
-    ModuleCCSession with a fake one so we can avoid network I/O in tests,
-    and can also inspect or tweak messages via the session more easily.
-    This class also maintains some faked module information and statistics
-    data that can be retrieved from the implementation of the Stats class.
-
-    """
-    def __init__(self):
-        # First, setup some internal attributes.  All of them are essentially
-        # private (so prefixed with double '_'), but some are defined as if
-        # "protected" (with a single '_') for the convenient of tests that
-        # may want to inspect or tweak them.
-
-        # initial seq num for faked group_sendmsg, arbitrary choice.
-        self._seq = 4200
-        # if set, use them as faked response to group_recvmsg (see below).
-        # it's a list of tuples, each of which is of (answer, envelope).
-        self._answers = []
-        # the default answer from faked recvmsg if _answers is empty
-        self.__default_answer = isc.config.ccsession.create_answer(
-            0, {'Init':
-                    json.loads(INIT_SPEC_STR)['module_spec']['statistics'],
-                'Auth':
-                    json.loads(AUTH_SPEC_STR)['module_spec']['statistics']
-                })
-        # setup faked auth statistics
-        self.__init_auth_stat()
-        # statistics data for faked Init module
-        self._init_sdata = {
-            'boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME)
-            }
-
-        # Incorporate other setups of the real Stats module.  We use the faked
-        # ModuleCCSession to avoid blocking network operation.  Note also that
-        # we replace _init_statistics_data() (see below), so we don't
-        # initialize statistics data yet.
-        stats.Stats.__init__(self, MyModuleCCSession)
-
-        # replace some (faked) ModuleCCSession methods so we can inspect/fake
-        # the data exchanged via the CC session, then call
-        # _init_statistics_data.  This will get the Stats module info from
-        # the file directly and some amount information about the Init and
-        # Auth modules (hardcoded below).
-        self.cc_session.group_sendmsg = self.__group_sendmsg
-        self.cc_session.group_recvmsg = self.__group_recvmsg
-        self.cc_session.rpc_call = self.__rpc_call
-        stats.Stats._init_statistics_data(self)
-
-    def __init_auth_stat(self):
-        self._queries_tcp = 3
-        self._queries_udp = 2
-        self._queries_per_zone = [{
-                'zonename': 'test1.example', 'queries.tcp': 5, 'queries.udp': 4
-                }]
-        self._nds_queries_per_zone = \
-            { 'test10.example': { 'queries.tcp': 5, 'queries.udp': 4 } }
-        self._auth_sdata = \
-            { 'queries.tcp': self._queries_tcp,
-              'queries.udp': self._queries_udp,
-              'queries.perzone' : self._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')
-              }
-
-    def _init_statistics_data(self):
-        # Inherited from real Stats class, just for deferring the
-        # initialization until we are ready.
-        pass
-
-    def __group_sendmsg(self, command, destination, want_answer=False):
-        """Faked ModuleCCSession.group_sendmsg for tests.
-
-        Skipping actual network communication, and just returning an internally
-        generated sequence number.
-
-        """
-        self._seq += 1
-        return self._seq
-
-    def __group_recvmsg(self, nonblocking = True, seq = None):
-        """Faked ModuleCCSession.group_recvmsg for tests.
-
-        Skipping actual network communication, and returning an internally
-        prepared answer. sequence number.  If faked anser is given in
-        _answers, use it; otherwise use the default.  we don't actually check
-        the sequence.
-
-        """
-        if len(self._answers) == 0:
-            return self.__default_answer, {'from': 'no-matter'}
-        return self._answers.pop(0)
-
-    def __rpc_call(self, command, group):
-        """Faked ModuleCCSession.rpc_call for tests.
-
-        At the moment we don't have to cover failure cases, so this is a
-        simple wrapper for the faked group_recvmsg().
-
-        """
-        answer, _ = self.__group_recvmsg(None, None)
-        return isc.config.ccsession.parse_answer(answer)[1]
-
-class MyStatsHttpd(stats_httpd.StatsHttpd):
-    """A faked StatsHttpd class for unit tests.
-
-    This class inherits most of the real StatsHttpd class, but replaces the
-    ModuleCCSession with a fake one so we can avoid network I/O in tests,
-    and can also inspect or tweak messages via the session more easily.
-
-    """
-
-    ORIG_SPECFILE_LOCATION = stats_httpd.SPECFILE_LOCATION
-    def __init__(self, *server_address):
-        self._started = threading.Event()
-        self.__dummy_sock = None # see below
-
-        # Prepare commonly used statistics schema and data requested in
-        # stats-httpd tests.  For the purpose of these tests, the content of
-        # statistics data is not so important (they don't test whther the
-        # counter values are correct, etc), so hardcoding the common case
-        # should suffice.  Note also that some of the statistics values and
-        # specs don't make sense in practice (see also comments on
-        # AUTH_SPEC_STR).
-        with open(stats.SPECFILE_LOCATION) as f:
-            stat_spec_str = f.read()
-        self.__default_spec_answer = {
-            'Init': json.loads(INIT_SPEC_STR)['module_spec']['statistics'],
-            'Auth': json.loads(AUTH_SPEC_STR)['module_spec']['statistics'],
-            'Stats': json.loads(stat_spec_str)['module_spec']['statistics']
-            }
-        self.__default_data_answer = {
-            'Init': {'boot_time':
-                         time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME)},
-            'Stats': {'last_update_time':
-                          time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-                      'report_time':
-                          time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-                      'lname': 'test-lname',
-                      'boot_time':
-                          time.strftime('%Y-%m-%dT%H:%M:%SZ', CONST_BASETIME),
-                      'timestamp': time.mktime(CONST_BASETIME)},
-            'Auth': {'queries.udp': 4, 'queries.tcp': 6,
-                     'queries.perzone': [
-                    {'queries.udp': 8, 'queries.tcp': 10,
-                     'zonename': 'test1.example'},
-                    {'queries.udp': 6, 'queries.tcp': 8,
-                     'zonename': 'test2.example'}],
-                     'nds_queries.perzone': {
-                    'test10.example': {'queries.udp': 8, 'queries.tcp': 10},
-                    'test20.example': {'queries.udp': 6, 'queries.tcp': 8}}}}
-
-        # if set, use them as faked response to rpc_call (see below).
-        # it's a list of answer data of rpc_call.
-        self._rpc_answers = []
-
-        if server_address:
-            stats_httpd.SPECFILE_LOCATION = \
-                self.__create_specfile(*server_address)
-            try:
-                stats_httpd.StatsHttpd.__init__(self)
-            finally:
-                if hasattr(stats_httpd.SPECFILE_LOCATION, "close"):
-                    stats_httpd.SPECFILE_LOCATION.close()
-                stats_httpd.SPECFILE_LOCATION = self.ORIG_SPECFILE_LOCATION
-        else:
-            stats_httpd.StatsHttpd.__init__(self)
-
-        # replace some (faked) ModuleCCSession methods so we can inspect/fake.
-        # in order to satisfy select.select() we need some real socket.  We
-        # use an unusable AF_UNIX socket; we won't actually use it for
-        # communication.
-        self.cc_session.rpc_call = self.__rpc_call
-        self.__dummy_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        self.mccs.get_socket = lambda: self.__dummy_sock
-
-    def open_mccs(self):
-        self.mccs = MyModuleCCSession(stats_httpd.SPECFILE_LOCATION,
-                                      self.config_handler,
-                                      self.command_handler)
-        self.cc_session = self.mccs._session
-        self.mccs.start = self.load_config # force reload
-
-        # check_command could be called from the main select() loop due to
-        # Linux's bug of spurious wakeup.  We don't need the actual behavior
-        # of check_command in our tests, so we can basically replace it with a
-        # no-op mock function.
-        def mock_check_command(nonblock):
-            pass
-        self.mccs.check_command = mock_check_command
-
-    def close_mccs(self):
-        super().close_mccs()
-        if self.__dummy_sock is not None:
-            self.__dummy_sock.close()
-            self.__dummy_sock = None
-
-    def __rpc_call(self, command, group, params={}):
-        """Faked ModuleCCSession.rpc_call for tests.
-
-        The stats httpd module only issues two commands: 'showschema' and
-        'show'.  In most cases we can simply use the prepared default
-        answer.  If customization is needed, the test case can add a
-        faked answer by appending it to _rpc_answers.  If the added object
-        is of Exception type this method raises it instead of return it,
-        emulating the situation where rpc_call() results in an exception.
-
-        """
-        if len(self._rpc_answers) == 0:
-            if command == 'showschema':
-                return self.__default_spec_answer
-            elif command == 'show':
-                return self.__default_data_answer
-            assert False, "unexpected command for faked rpc_call: " + command
-
-        answer = self._rpc_answers.pop(0)
-        if issubclass(type(answer), Exception):
-            raise answer
-        return answer
-
-    def __create_specfile(self, *server_address):
-        spec_io = open(self.ORIG_SPECFILE_LOCATION)
-        try:
-            spec = json.load(spec_io)
-            spec_io.close()
-            config = spec['module_spec']['config_data']
-            for i in range(len(config)):
-                if config[i]['item_name'] == 'listen_on':
-                    config[i]['item_default'] = \
-                        [ dict(address=a[0], port=a[1])
-                          for a in server_address ]
-                    break
-            return io.StringIO(json.dumps(spec))
-        finally:
-            spec_io.close()
-
-    def run(self):
-        self._started.set()
-        self.start()
-
-    def shutdown(self):
-        self.command_handler('shutdown', None)
diff --git a/src/bin/stats/tests/testdata/Makefile.am b/src/bin/stats/tests/testdata/Makefile.am
deleted file mode 100644 (file)
index 8f9da09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-EXTRA_DIST = b10-config.db
diff --git a/src/bin/stats/tests/testdata/b10-config.db b/src/bin/stats/tests/testdata/b10-config.db
deleted file mode 100644 (file)
index 7dd9daf..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{ "version": 2,
-  "Init": {
-    "components": {
-      "b10-auth": {
-        "kind": "needed",
-        "special": "auth"
-      },
-      "b10-auth-2": {
-        "kind": "needed",
-        "special": "auth"
-      }
-    }
-  }
-}
diff --git a/src/bin/sysinfo/.gitignore b/src/bin/sysinfo/.gitignore
deleted file mode 100644 (file)
index b5c759d..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/isc-sysinfo
-/isc-sysinfo.1
-/run_sysinfo.sh
-/sysinfo.py
diff --git a/src/bin/sysinfo/Makefile.am b/src/bin/sysinfo/Makefile.am
deleted file mode 100644 (file)
index 1618535..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-bin_SCRIPTS = isc-sysinfo
-noinst_SCRIPTS = run_sysinfo.sh
-
-CLEANFILES = isc-sysinfo sysinfo.pyc
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-isc-sysinfo: sysinfo.py
-       $(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" sysinfo.py >$@
-       chmod a+x $@
-
-MAN1_FILES = \
-       isc-sysinfo.xml
-
-man_MANS = \
-       $(MAN1_FILES:.xml=.1)
-DISTCLEANFILES = $(man_MANS)
-
-if GENERATE_DOCS
-
-.xml.1:
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-EXTRA_DIST = $(man_MANS) $(MAN1_FILES)
-
-CLEANDIRS = __pycache__
-
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/sysinfo/isc-sysinfo.xml b/src/bin/sysinfo/isc-sysinfo.xml
deleted file mode 100644 (file)
index 919ed83..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>June 26, 2012</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>isc-sysinfo</refentrytitle>
-    <manvolnum>1</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>isc-sysinfo</refname>
-    <refpurpose>BIND 10 system information display tool</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2012</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>isc-sysinfo</command>
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>
-      The <command>isc-sysinfo</command> program collects and outputs a
-      variety of information about the system that BIND 10 is running
-      on. This information can be useful to people involved in debugging
-      and technical support.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>ARGUMENTS</title>
-
-    <variablelist>
-
-      <varlistentry>
-        <term>-h</term>
-        <listitem><para>
-          Displays usage instructions.
-        </para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term>-o <replaceable class="parameter">output-file</replaceable></term>
-        <listitem><para>
-          If an output file is specified, the output
-          of <command>isc-sysinfo</command> is written to this file. By
-          default, the output is written to standard output.
-        </para></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-  </refsect1>
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>bind10</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      The <command>isc-sysinfo</command> daemon was initially
-      implemented by ISC staff in June, 2012.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/sysinfo/run_sysinfo.sh.in b/src/bin/sysinfo/run_sysinfo.sh.in
deleted file mode 100755 (executable)
index b5593b9..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#! /bin/sh
-
-# 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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-SYSINFO_PATH=@abs_top_builddir@/src/bin/sysinfo
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/lib/python
-export PYTHONPATH
-
-cd ${SYSINFO_PATH}
-exec ${PYTHON_EXEC} -O sysinfo.py "$@"
diff --git a/src/bin/sysinfo/sysinfo.py.in b/src/bin/sysinfo/sysinfo.py.in
deleted file mode 100755 (executable)
index 477b9de..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-#!@PYTHON@
-
-# 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.
-
-"""
-ISC sysinfo program.
-
-"""
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import getopt
-from isc.sysinfo import *
-
-def usage():
-    print("Usage: %s [-h] [-o <output-file>]" % sys.argv[0], \
-              file=sys.stderr)
-    exit(1)
-
-def write_value(out, fmt, call):
-    '''Helper function for standard value writing.
-       Writes the result from the call in the given format to out.
-       Does not write anything if the result of the call is None.
-    '''
-    value = call()
-    if value is not None:
-        out.write(fmt % value)
-
-def main():
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], "o:h", \
-                                       ["output", "help"])
-    except getopt.GetoptError as e:
-        print(str(e))
-        usage()
-        exit(1)
-
-    output_filename = None
-    for option, arg in opts:
-        if option in ("-o", "--output"):
-            output_filename = arg
-        elif option in ("-h", "--help"):
-            usage()
-        else:
-            assert False, "unhandled option"
-
-    if output_filename is None:
-        f = sys.stdout
-    else:
-        f = open(output_filename, 'w')
-
-    s = SysInfoFromFactory()
-
-    f.write('ISC Sysinfo tool\n')
-    f.write('================\n')
-
-    f.write('\nCPU\n');
-    write_value(f, ' + Number of processors: %d\n', s.get_num_processors)
-    write_value(f, ' + Endianness: %s\n', s.get_endianness)
-
-    f.write('\nPlatform\n');
-    write_value(f, ' + Operating system: %s\n', s.get_platform_name)
-    write_value(f, ' + Distribution: %s\n', s.get_platform_distro)
-    write_value(f, ' + Kernel version: %s\n', s.get_platform_version)
-
-    if s.get_platform_is_smp() is not None:
-        f.write(' + SMP kernel: ')
-        if s.get_platform_is_smp():
-            f.write('yes')
-        else:
-            f.write('no')
-        f.write('\n')
-
-    write_value(f, ' + Machine name: %s\n', s.get_platform_machine)
-    write_value(f, ' + Hostname: %s\n', s.get_platform_hostname)
-    write_value(f, ' + Uptime: %d seconds', s.get_uptime)
-    write_value(f, ' (%s)\n', s.get_uptime_desc)
-
-    write_value(f, ' + Loadavg: %f %f %f\n', s.get_loadavg)
-
-    f.write('\nMemory\n');
-    write_value(f, ' + Total: %d bytes\n', s.get_mem_total)
-    write_value(f, ' + Free: %d bytes\n', s.get_mem_free)
-    write_value(f, ' + Cached: %d bytes\n', s.get_mem_cached)
-    write_value(f, ' + Buffers: %d bytes\n', s.get_mem_buffers)
-    write_value(f, ' + Swap total: %d bytes\n', s.get_mem_swap_total)
-    write_value(f, ' + Swap free: %d bytes\n', s.get_mem_swap_free)
-
-    f.write('\n\nNetwork\n');
-    f.write('-------\n\n');
-
-    f.write('Interfaces\n')
-    f.write('~~~~~~~~~~\n\n')
-
-    write_value(f, '%s', s.get_net_interfaces)
-
-    f.write('\nRouting table\n')
-    f.write('~~~~~~~~~~~~~\n\n')
-    write_value(f, '%s', s.get_net_routing_table)
-
-    f.write('\nStatistics\n')
-    f.write('~~~~~~~~~~\n\n')
-    write_value(f, '%s', s.get_net_stats)
-
-    f.write('\nConnections\n')
-    f.write('~~~~~~~~~~~\n\n')
-    write_value(f, '%s', s.get_net_connections)
-
-    try:
-        if os.getuid() != 0:
-            sys.stderr.write('\n')
-            sys.stderr.write('NOTE: You have to run this program as the root user so that it can\n')
-            sys.stderr.write('      collect all the information it requires. Some information is\n')
-            sys.stderr.write('      only available to the root user.\n\n')
-    except Exception:
-        pass
-
-    if f != sys.stdout:
-        f.close()
-
-if __name__ == '__main__':
-    main()
diff --git a/src/bin/tests/.gitignore b/src/bin/tests/.gitignore
deleted file mode 100644 (file)
index b39aa86..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/process_rename_test.py
diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am
deleted file mode 100644 (file)
index 535257c..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = process_rename_test.py
-noinst_SCRIPTS = $(PYTESTS)
-# .py will be generated by configure, so we don't have to include it
-# in EXTRA_DIST.
-
-# 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/util/threads/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-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 ; \
-       chmod +x $(abs_builddir)/$$pytest ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
-       $(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
-       done
diff --git a/src/bin/tests/README b/src/bin/tests/README
deleted file mode 100644 (file)
index 1de18b4..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory does not contain any code a user might want to run. It contains
-"global" tests -- tests that are not specific to a single library or program.
-Do not expect to find b10-tests here, there's no such program.
diff --git a/src/bin/tests/process_rename_test.py.in b/src/bin/tests/process_rename_test.py.in
deleted file mode 100644 (file)
index 7e6a9c9..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-#
-# 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 to see if every python process renames itself."""
-import unittest
-import os
-import os.path
-import isc.util.process
-import re
-
-class TestRename(unittest.TestCase):
-    """Test scanning all python scripts if they rename themself."""
-    def __scan(self, directory, script, fun):
-        # Scan one script if it contains call to the renaming function
-        filename = os.path.join(directory, script)
-        if not os.path.exists(filename):
-            # We either didn't compile it yet or it is not compiled based
-            # on some environmental condition. That is OK, we don't want
-            # to break on that.
-            return
-        with open(filename) as f:
-            data = ''.join(f.readlines())
-        prettyname = 'src' + filename[filename.rfind('../') + 2:]
-        self.assertTrue(fun.search(data),
-            "Didn't find a call to isc.util.process.rename in " + prettyname)
-
-    def test_calls(self):
-        """
-        Test if every script renames itself.
-
-        Scan all Makefile and look for scripts.
-        Then scan them by looking at the source text
-        (without actually running them)
-        """
-
-        # Scripts named in this list are not expected to be renamed and
-        # should be excluded from the scan.
-        EXCLUDED_SCRIPTS = ['isc-sysinfo', 'bind10']
-
-        # Regexp to find all the *_SCRIPTS = something lines (except for
-        # noinst_SCRIPTS, which are scripts for tests), including line
-        # continuations (backslash and newline)
-        excluded_lines = re.compile(r'^(noinst_SCRIPTS.*$)', re.MULTILINE)
-        lines = re.compile(r'^\w+_SCRIPTS\s*=\s*((.|\\\n)*)$',
-            re.MULTILINE)
-        # Script name regular expression
-        scripts = re.compile(r'((\w|[-.0-9])+)')
-        # Line with the call
-        fun = re.compile(r'^\s*isc\.util\.process\.rename\s*\(.*\)\s*(|#.*)$',
-            re.MULTILINE)
-
-        # Find all Makefile and extract names of scripts
-        for (d, _, fs) in os.walk('@top_builddir@'):
-            if 'Makefile' in fs:
-                with open(os.path.join(d, "Makefile")) as f:
-                    makefile = ''.join(f.readlines())
-                for (var, _) in lines.findall(re.sub(excluded_lines, '',
-                                                     makefile)):
-                    for (script, _) in scripts.findall(var):
-                        if script in EXCLUDED_SCRIPTS:
-                            continue
-                        self.__scan(d, script, fun)
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/src/bin/usermgr/.gitignore b/src/bin/usermgr/.gitignore
deleted file mode 100644 (file)
index 36e32a0..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/b10-cmdctl-usermgr
-/b10-cmdctl-usermgr.py
-/run_b10-cmdctl-usermgr.sh
-/b10-cmdctl-usermgr.8
diff --git a/src/bin/usermgr/Makefile.am b/src/bin/usermgr/Makefile.am
deleted file mode 100644 (file)
index d356d20..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-SUBDIRS = tests
-
-sbin_SCRIPTS = b10-cmdctl-usermgr
-noinst_SCRIPTS = run_b10-cmdctl-usermgr.sh
-
-b10_cmdctl_usermgrdir = $(pkgdatadir)
-
-CLEANFILES=    b10-cmdctl-usermgr b10-cmdctl-usermgr.pyc
-
-man_MANS = b10-cmdctl-usermgr.8
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-cmdctl-usermgr.xml
-
-if GENERATE_DOCS
-
-b10-cmdctl-usermgr.8: b10-cmdctl-usermgr.xml
-       @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-cmdctl-usermgr.xml
-
-else
-
-$(man_MANS):
-       @echo Man generation disabled.  Creating dummy $@.  Configure with --enable-generate-docs to enable it.
-       @echo Man generation disabled.  Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
-
-# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cmdctl-usermgr: b10-cmdctl-usermgr.py
-       $(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cmdctl-usermgr.py >$@
-       chmod a+x $@
-
-CLEANDIRS = __pycache__
-clean-local:
-       rm -rf $(CLEANDIRS)
diff --git a/src/bin/usermgr/README b/src/bin/usermgr/README
deleted file mode 100644 (file)
index e7d9835..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-Create new account by running the following command:
-
-b10-cmdctl-usermgr -f filename
-
-If parameter '-f' is specified, new accounts will be appended to that file if it exists, or else the accounts will be saved in default file 'cmdctl-accounts.csv'.
-
diff --git a/src/bin/usermgr/b10-cmdctl-usermgr.py.in b/src/bin/usermgr/b10-cmdctl-usermgr.py.in
deleted file mode 100755 (executable)
index b0fd30f..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2010  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 COMMAND OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS COMMAND, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-'''
-This tool implements user management for b10-cmdctl. It is used to
-add and remove users from the accounts file.
-'''
-import sys; sys.path.append ('@@PYTHONPATH@@')
-from bind10_config import SYSCONFPATH
-from collections import OrderedDict
-import random
-from hashlib import sha1
-import csv
-import getpass
-from optparse import OptionParser, OptionValueError
-import os
-import isc.util.process
-
-isc.util.process.rename()
-
-VERSION_STRING = "b10-cmdctl-usermgr @PACKAGE_VERSION@"
-DEFAULT_FILE = SYSCONFPATH + "/cmdctl-accounts.csv"
-
-# Actions that can be performed (used for argument parsing,
-# code paths, and output)
-COMMAND_ADD = "add"
-COMMAND_DELETE = "delete"
-
-# Non-zero return codes, used in tests
-BAD_ARGUMENTS = 1
-FILE_ERROR = 2
-USER_EXISTS = 3
-USER_DOES_NOT_EXIST = 4
-
-class UserManager:
-    def __init__(self, options, args):
-        self.options = options
-        self.args = args
-
-    def __print(self, msg):
-        if not self.options.quiet:
-            print(msg)
-
-    def __gen_password_hash(self, password):
-        salt = "".join(chr(random.randint(ord('!'), ord('~')))\
-                    for x in range(64))
-        saltedpwd = sha1((password + salt).encode()).hexdigest()
-        return salt, saltedpwd
-
-    def __read_user_info(self):
-        """
-        Read the existing user info
-        Raises an IOError if the file can't be read
-        """
-        # Currently, this is done quite naively (there is no
-        # check that the file is modified between read and write)
-        # But taking multiple simultaneous users of this tool on the
-        # same file seems unnecessary at this point.
-        self.user_info = OrderedDict()
-        if os.path.exists(self.options.output_file):
-            # Just let any file read error bubble up; it will
-            # be caught in the run() method
-            with open(self.options.output_file, newline='') as csvfile:
-                reader = csv.reader(csvfile, strict=True)
-                for row in reader:
-                    self.user_info[row[0]] = row
-
-    def __save_user_info(self):
-        """
-        Write out the (modified) user info
-        Raises an IOError if the file can't be written
-        """
-        # Just let any file write error bubble up; it will
-        # be caught in the run() method
-        with open(self.options.output_file, 'w',
-                  newline='') as csvfile:
-            writer = csv.writer(csvfile)
-            for row in self.user_info.values():
-                writer.writerow(row)
-
-    def __add_user(self, name, password):
-        """
-        Add the given username/password combination to the stored user_info.
-        First checks if the username exists, and returns False if so.
-        If not, it is added, and this method returns True.
-        """
-        if name in self.user_info:
-            return False
-        salt, pw = self.__gen_password_hash(password)
-        self.user_info[name] = [name, pw, salt]
-        return True
-
-    def __delete_user(self, name):
-        """
-        Removes the row with the given name from the stored user_info
-        First checks if the username exists, and returns False if not.
-        Otherwise, it is removed, and this mehtod returns True
-        """
-        if name not in self.user_info:
-            return False
-        del self.user_info[name]
-        return True
-
-    # overridable input() call, used in testing
-    def _input(self, prompt):
-        return input(prompt)
-
-    # in essence this is private, but made 'protected' for ease
-    # of testing
-    def _prompt_for_username(self, command):
-        # Note, direct prints here are intentional
-        while True:
-            name = self._input("Username to " + command + ": ")
-            if name == "":
-                print("Error username can't be empty")
-                continue
-
-            if command == COMMAND_ADD and name in self.user_info:
-                 print("user already exists")
-                 continue
-            elif command == COMMAND_DELETE and name not in self.user_info:
-                 print("user does not exist")
-                 continue
-
-            return name
-
-    # in essence this is private, but made 'protected' for ease
-    # of testing
-    def _prompt_for_password(self):
-        # Note, direct prints here are intentional
-        while True:
-            pwd1 = getpass.getpass("Choose a password: ")
-            if pwd1 == "":
-                print("Error: password cannot be empty")
-                continue
-            pwd2 = getpass.getpass("Re-enter password: ")
-            if pwd1 != pwd2:
-                print("passwords do not match, try again")
-                continue
-            return pwd1
-
-    def __verify_options_and_args(self):
-        """
-        Basic sanity checks on command line arguments.
-        Returns False if there is a problem, True if everything seems OK.
-        """
-        if len(self.args) < 1:
-            self.__print("Error: no command specified")
-            return False
-        if len(self.args) > 3:
-            self.__print("Error: extraneous arguments")
-            return False
-        if self.args[0] not in [ COMMAND_ADD, COMMAND_DELETE ]:
-            self.__print("Error: command must be either add or delete")
-            return False
-        if self.args[0] == COMMAND_DELETE and len(self.args) > 2:
-            self.__print("Error: delete only needs username, not a password")
-            return False
-        return True
-
-    def run(self):
-        if not self.__verify_options_and_args():
-            return BAD_ARGUMENTS
-
-        try:
-            self.__print("Using accounts file: " + self.options.output_file)
-            self.__read_user_info()
-
-            command = self.args[0]
-
-            if len(self.args) > 1:
-                username = self.args[1]
-            else:
-                username = self._prompt_for_username(command)
-
-            if command == COMMAND_ADD:
-                if len(self.args) > 2:
-                    password = self.args[2]
-                else:
-                    password = self._prompt_for_password()
-                if not self.__add_user(username, password):
-                    print("Error: username exists")
-                    return USER_EXISTS
-            elif command == COMMAND_DELETE:
-                if not self.__delete_user(username):
-                    print("Error: username does not exist")
-                    return USER_DOES_NOT_EXIST
-
-            self.__save_user_info()
-            return 0
-        except IOError as ioe:
-            self.__print("Error accessing " + ioe.filename +\
-                         ": " + str(ioe.strerror))
-            return FILE_ERROR
-        except csv.Error as csve:
-            self.__print("Error parsing csv file: " + str(csve))
-            return FILE_ERROR
-
-def set_options(parser):
-    parser.add_option("-f", "--file",
-                      dest="output_file", default=DEFAULT_FILE,
-                      help="Accounts file to modify"
-                     )
-    parser.add_option("-q", "--quiet",
-                      dest="quiet", action="store_true", default=False,
-                      help="Quiet mode, don't print any output"
-                     )
-
-def main():
-    usage = "usage: %prog [options] <command> [username] [password]\n\n"\
-            "Arguments:\n"\
-            "  command\t\teither 'add' or 'delete'\n"\
-            "  username\t\tthe username to add or delete\n"\
-            "  password\t\tthe password to set for the added user\n"\
-            "\n"\
-            "If username or password are not specified, %prog will\n"\
-            "prompt for them. It is recommended practice to let the\n"\
-            "tool prompt for the password, as command-line\n"\
-            "arguments can be visible through history or process\n"\
-            "viewers."
-    parser = OptionParser(usage=usage, version=VERSION_STRING)
-    set_options(parser)
-    (options, args) = parser.parse_args()
-
-    usermgr = UserManager(options, args)
-    return usermgr.run()
-
-if __name__ == '__main__':
-    sys.exit(main())
-
diff --git a/src/bin/usermgr/b10-cmdctl-usermgr.xml b/src/bin/usermgr/b10-cmdctl-usermgr.xml
deleted file mode 100644 (file)
index 26940d7..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-              [<!ENTITY mdash "&#8212;">]>
-<!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS.  IN NO EVENT SHALL ISC 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 COMMAND OF CONTRACT, NEGLIGENCE
- - OR OTHER TORTIOUS COMMAND, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- - PERFORMANCE OF THIS SOFTWARE.
--->
-
-<refentry>
-
-  <refentryinfo>
-    <date>March 17, 2010</date>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>b10-cmdctl-usermgr</refentrytitle>
-    <manvolnum>8</manvolnum>
-    <refmiscinfo>BIND10</refmiscinfo>
-  </refmeta>
-
-  <refnamediv>
-    <refname>b10-cmdctl-usermgr</refname>
-    <refpurpose>cmdctl user maintenance tool</refpurpose>
-  </refnamediv>
-
-  <docinfo>
-    <copyright>
-      <year>2010</year>
-      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
-    </copyright>
-  </docinfo>
-
-  <refsynopsisdiv>
-    <cmdsynopsis>
-      <command>b10-cmdctl-usermgr</command>
-
-      <arg><option>-f <replaceable>filename</replaceable></option></arg>
-      <arg><option>-h</option></arg>
-      <arg><option>--file=<replaceable>filename</replaceable></option></arg>
-      <arg><option>--help</option></arg>
-      <arg><option>--version</option></arg>
-      <arg choice="plain"><replaceable>command</replaceable></arg>
-      <arg><option>username</option></arg>
-      <arg><option>password</option></arg>
-
-    </cmdsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>DESCRIPTION</title>
-    <para>The <command>b10-cmdctl-usermgr</command> tool may be used
-      to add and remove accounts with passwords for the
-      <citerefentry><refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      daemon.
-    </para>
-
-    <para>
-      By default, the accounts are saved in the
-      <filename>cmdctl-accounts.csv</filename> file in the system config
-      directory, unless the <option>--filename</option> switch is used.
-      The entry is appended to or removed from the file.
-    </para>
-
-    <para>
-      The tool can't replace existing entries, but this can easily be
-      accomplished by removing the entry and adding a new one.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>OPTIONS</title>
-
-    <para>The arguments are as follows:</para>
-
-    <para>
-      command is either 'add' or 'delete', respectively to add or delete users.
-    </para>
-
-    <para>
-      If a username and password are given (or just a username in case of
-      deletion), these are used. Otherwise, the tool shall prompt for a
-      username and/or password. It is recommended practice to let the
-      tool prompt for the password, as command-line arguments can be
-      visible through history or process viewers.
-    </para>
-
-    <variablelist>
-
-      <varlistentry>
-        <term><option>-h</option></term>
-        <term><option>--help</option></term>
-        <listitem><para>
-          Report the usage statement and exit.
-        </para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>-f <replaceable>filename</replaceable></option></term>
-        <term><option>--file <replaceable>filename</replaceable></option></term>
-        <listitem><para>
-          Specify the accounts file to update. The default is
-          <filename>cmdctl-accounts.csv</filename> in the system config
-          directory.
-        </para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><option>--version</option></term>
-        <listitem><para>
-          Report the version and exit.
-        </para></listitem>
-      </varlistentry>
-
-    </variablelist>
-
-  </refsect1>
-
-<!--
-  <refsect1>
-    <title>FILES</title>
-    <para>
-    </para>
-  </refsect1>
--->
-
-  <refsect1>
-    <title>SEE ALSO</title>
-    <para>
-      <citerefentry>
-        <refentrytitle>b10-cmdctl</refentrytitle><manvolnum>8</manvolnum>
-      </citerefentry>,
-      <citetitle>BIND 10 Guide</citetitle>.
-    </para>
-  </refsect1>
-
-  <refsect1>
-    <title>HISTORY</title>
-    <para>
-      The <command>b10-cmdctl-usermgr</command> tool
-      was implemented in January 2010
-      by Zhang Likun of CNNIC for the ISC BIND 10 project.
-    </para>
-  </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in b/src/bin/usermgr/run_b10-cmdctl-usermgr.sh.in
deleted file mode 100755 (executable)
index 8e8e743..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  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.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-MYPATH_PATH=@abs_top_builddir@/src/bin/usermgr
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-cd ${MYPATH_PATH}
-exec ${PYTHON_EXEC} b10-cmdctl-usermgr "$@"
diff --git a/src/bin/usermgr/tests/Makefile.am b/src/bin/usermgr/tests/Makefile.am
deleted file mode 100644 (file)
index 230f164..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
-PYTESTS = b10-cmdctl-usermgr_test.py
-EXTRA_DIST = $(PYTESTS)
-
-CLEANFILES = *.csv
-
-# 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 ; \
-       $(LIBRARY_PATH_PLACEHOLDER) \
-       PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cmdctl \
-       CMDCTL_BUILD_PATH=$(abs_top_builddir)/src/bin/cmdctl \
-       CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
-       B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
-       $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
-       done
diff --git a/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py b/src/bin/usermgr/tests/b10-cmdctl-usermgr_test.py
deleted file mode 100644 (file)
index 2da418d..0000000
+++ /dev/null
@@ -1,511 +0,0 @@
-# Copyright (C) 2013  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 COMMAND OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS COMMAND, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-import csv
-from hashlib import sha1
-import getpass
-import imp
-import os
-import subprocess
-import stat
-import sys
-import unittest
-from bind10_config import SYSCONFPATH
-
-class PrintCatcher:
-    def __init__(self):
-        self.stdout_lines = []
-
-    def __enter__(self):
-        self.__orig_stdout_write = sys.stdout.write
-        def new_write(line):
-            self.stdout_lines.append(line)
-
-        sys.stdout.write = new_write
-        return self
-
-    def __exit__(self, type, value, traceback):
-        sys.stdout.write = self.__orig_stdout_write
-
-class OverrideGetpass:
-    def __init__(self, new_getpass):
-        self.__new_getpass = new_getpass
-        self.__orig_getpass = getpass.getpass
-
-    def __enter__(self):
-        getpass.getpass = self.__new_getpass
-        return self
-
-    def __exit__(self, type, value, traceback):
-        getpass.getpass = self.__orig_getpass
-
-# input() is a built-in function and not easily overridable
-# so this one uses usermgr for that
-class OverrideInput:
-    def __init__(self, usermgr, new_getpass):
-        self.__usermgr = usermgr
-        self.__new_input = new_getpass
-        self.__orig_input = usermgr._input
-
-    def __enter__(self):
-        self.__usermgr._input = self.__new_input
-        return self
-
-    def __exit__(self, type, value, traceback):
-        self.__usermgr._input = self.__orig_input
-
-def run(command):
-    """
-    Small helper function that returns a tuple of (rcode, stdout, stderr)
-    after running the given command (an array of command and arguments, as
-    passed on to subprocess).
-    Parameters:
-    command: an array of command and argument strings, which will be
-             passed to subprocess.Popen()
-    """
-    subp = subprocess.Popen(command, stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE)
-    (stdout, stderr) = subp.communicate()
-    return (subp.returncode, stdout, stderr)
-
-class TestUserMgr(unittest.TestCase):
-    TOOL = '../b10-cmdctl-usermgr'
-    OUTPUT_FILE = 'test_users.csv'
-
-    def setUp(self):
-        self.delete_output_file()
-        # For access to the actual module, we load it directly
-        self.usermgr_module = imp.load_source('usermgr',
-                                              '../b10-cmdctl-usermgr.py')
-        # And instantiate 1 instance (with fake options/args)
-        self.usermgr = self.usermgr_module.UserManager(object(), object())
-
-    def tearDown(self):
-        self.delete_output_file()
-
-    def delete_output_file(self):
-        if os.path.exists(self.OUTPUT_FILE):
-            os.remove(self.OUTPUT_FILE)
-
-    def check_output_file(self, expected_content):
-        self.assertTrue(os.path.exists(self.OUTPUT_FILE))
-
-        csv_entries = []
-        with open(self.OUTPUT_FILE, newline='') as csvfile:
-            reader = csv.reader(csvfile)
-            csv_entries = [row for row in reader]
-
-        self.assertEqual(len(expected_content), len(csv_entries))
-        csv_entries.reverse()
-        for expected_entry in expected_content:
-            expected_name = expected_entry[0]
-            expected_pass = expected_entry[1]
-
-            csv_entry = csv_entries.pop()
-            entry_name = csv_entry[0]
-            entry_salt = csv_entry[2]
-            entry_hash = csv_entry[1]
-
-            self.assertEqual(expected_name, entry_name)
-            expected_hash =\
-                sha1((expected_pass + entry_salt).encode()).hexdigest()
-            self.assertEqual(expected_hash, entry_hash)
-
-    def run_check(self, expected_returncode, expected_stdout, expected_stderr,
-                  command):
-        """
-        Runs the given command, and checks return code, and outputs (if provided).
-        Arguments:
-        expected_returncode, return code of the command
-        expected_stdout, (multiline) string that is checked against stdout.
-                         May be None, in which case the check is skipped.
-        expected_stderr, (multiline) string that is checked against stderr.
-                         May be None, in which case the check is skipped.
-
-        Returns the standard output and error captured to a string.
-        """
-        (returncode, stdout, stderr) = run(command)
-        if expected_stderr is not None:
-            self.assertEqual(expected_stderr, stderr.decode())
-        if expected_stdout is not None:
-            self.assertEqual(expected_stdout, stdout.decode())
-        self.assertEqual(expected_returncode, returncode, " ".join(command))
-        return (stdout.decode(), stderr.decode())
-
-    def test_help(self):
-        self.run_check(0,
-'''Usage: b10-cmdctl-usermgr [options] <command> [username] [password]
-
-Arguments:
-  command              either 'add' or 'delete'
-  username             the username to add or delete
-  password             the password to set for the added user
-
-If username or password are not specified, b10-cmdctl-usermgr will
-prompt for them. It is recommended practice to let the
-tool prompt for the password, as command-line
-arguments can be visible through history or process
-viewers.
-
-Options:
-  --version             show program's version number and exit
-  -h, --help            show this help message and exit
-  -f OUTPUT_FILE, --file=OUTPUT_FILE
-                        Accounts file to modify
-  -q, --quiet           Quiet mode, don't print any output
-''',
-                       '',
-                       [self.TOOL, '-h'])
-
-    def test_add_delete_users_ok(self):
-        """
-        Test that a file is created, and users are added.
-        Also tests quiet mode for adding a user to an existing file.
-        """
-        # content is a list of (user, pass) tuples
-        expected_content = []
-
-        # Creating a file
-        self.run_check(0,
-                       'Using accounts file: test_users.csv\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user1', 'pass1'
-                       ])
-        expected_content.append(('user1', 'pass1'))
-        self.check_output_file(expected_content)
-
-        # Add to existing file
-        self.run_check(0,
-                       'Using accounts file: test_users.csv\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user2', 'pass2'
-                       ])
-        expected_content.append(('user2', 'pass2'))
-        self.check_output_file(expected_content)
-
-        # Quiet mode
-        self.run_check(0,
-                       '',
-                       '',
-                       [ self.TOOL, '-q',
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user3', 'pass3'
-                       ])
-        expected_content.append(('user3', 'pass3'))
-        self.check_output_file(expected_content)
-
-        # Delete a user (let's pick the middle one)
-        self.run_check(0,
-                       '',
-                       '',
-                       [ self.TOOL, '-q',
-                         '-f', self.OUTPUT_FILE,
-                         'delete', 'user2'
-                       ])
-        del expected_content[1]
-        self.check_output_file(expected_content)
-
-    def test_add_delete_users_bad(self):
-        """
-        More add/delete tests, this time for some error scenarios
-        """
-        # content is a list of (user, pass) tuples
-        expected_content = []
-        # First add one
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user', 'pass'
-                       ])
-        expected_content.append(('user', 'pass'))
-        self.check_output_file(expected_content)
-
-        # Adding it again should error
-        self.run_check(3,
-                       'Using accounts file: test_users.csv\n'
-                       'Error: username exists\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user', 'pass'
-                       ])
-        self.check_output_file(expected_content)
-
-        # Deleting a non-existent one should fail too
-        self.run_check(4,
-                       'Using accounts file: test_users.csv\n'
-                       'Error: username does not exist\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'delete', 'nosuchuser'
-                       ])
-        self.check_output_file(expected_content)
-
-    def test_bad_arguments(self):
-        """
-        Assorted tests with bad command-line arguments
-        """
-        self.run_check(1,
-                       'Error: no command specified\n',
-                       '',
-                       [ self.TOOL ])
-        self.run_check(1,
-                       'Error: command must be either add or delete\n',
-                       '',
-                       [ self.TOOL, 'foo' ])
-        self.run_check(1,
-                       'Error: extraneous arguments\n',
-                       '',
-                       [ self.TOOL, 'add', 'user', 'pass', 'toomuch' ])
-        self.run_check(1,
-                       'Error: delete only needs username, not a password\n',
-                       '',
-                       [ self.TOOL, 'delete', 'user', 'pass' ])
-
-    def test_default_file(self):
-        """
-        Check the default file is the correct one.
-        """
-        # Hardcoded path .. should be ok since this is run from make check
-        self.assertEqual(SYSCONFPATH + '/cmdctl-accounts.csv',
-                         self.usermgr_module.DEFAULT_FILE)
-
-    def test_prompt_for_password_different(self):
-        """
-        Check that the method that prompts for a password verifies that
-        the same value is entered twice
-        """
-        # returns a different string (the representation of the number
-        # of times it has been called), until it has been called
-        # over 10 times, in which case it will always return "11"
-        getpass_different_called = 0
-        def getpass_different(question):
-            nonlocal getpass_different_called
-            getpass_different_called += 1
-            if getpass_different_called > 10:
-                return "11"
-            else:
-                return str(getpass_different_called)
-
-        with PrintCatcher() as pc:
-            with OverrideGetpass(getpass_different):
-                pwd = self.usermgr._prompt_for_password()
-                self.assertEqual(12, getpass_different_called)
-                self.assertEqual("11", pwd)
-                # stdout should be 5 times the no match string;
-                expected_output = "passwords do not match, try again\n"*5
-                self.assertEqual(expected_output, ''.join(pc.stdout_lines))
-
-    def test_prompt_for_password_empty(self):
-        """
-        Check that the method that prompts for a password verifies that
-        the value entered is not empty
-        """
-        # returns an empty string until it has been called over 10
-        # times
-        getpass_empty_called = 0
-        def getpass_empty(prompt):
-            nonlocal getpass_empty_called
-            getpass_empty_called += 1
-            if getpass_empty_called > 10:
-                return "nonempty"
-            else:
-                return ""
-
-        with PrintCatcher() as pc:
-            with OverrideGetpass(getpass_empty):
-                pwd = self.usermgr._prompt_for_password()
-                self.assertEqual("nonempty", pwd)
-                self.assertEqual(12, getpass_empty_called)
-                # stdout should be 10 times the 'cannot be empty' string
-                expected_output = "Error: password cannot be empty\n"*10
-                self.assertEqual(expected_output, ''.join(pc.stdout_lines))
-
-    def test_prompt_for_user(self):
-        """
-        Test that the method that prompts for a username verifies that
-        is not empty, and that it exists (or does not, depending on the
-        action that is specified)
-        """
-        new_input_called = 0
-        input_results = [ '', '', 'existinguser', 'nonexistinguser',
-                          '', '', 'nonexistinguser', 'existinguser' ]
-        def new_input(prompt):
-            nonlocal new_input_called
-
-            if new_input_called < len(input_results):
-                result = input_results[new_input_called]
-            else:
-                result = 'empty'
-            new_input_called += 1
-            return result
-
-        # add fake user (value doesn't matter, method only checks for key)
-        self.usermgr.user_info = { 'existinguser': None }
-
-        expected_output = ''
-
-        with PrintCatcher() as pc:
-            with OverrideInput(self.usermgr, new_input):
-                # should skip the first three since empty or existing
-                # are not allowed, then return 'nonexistinguser'
-                username = self.usermgr._prompt_for_username(
-                                self.usermgr_module.COMMAND_ADD)
-                self.assertEqual('nonexistinguser', username)
-                expected_output += "Error username can't be empty\n"*2
-                expected_output += "user already exists\n"
-                self.assertEqual(expected_output, ''.join(pc.stdout_lines))
-
-                # For delete, should again not accept empty (in a while true
-                # loop), and this time should not accept nonexisting users
-                username = self.usermgr._prompt_for_username(
-                                self.usermgr_module.COMMAND_DELETE)
-                self.assertEqual('existinguser', username)
-                expected_output += "Error username can't be empty\n"*2
-                expected_output += "user does not exist\n"
-                self.assertEqual(expected_output, ''.join(pc.stdout_lines))
-
-    def test_bad_file(self):
-        """
-        Check for graceful handling of bad file argument
-        """
-        self.run_check(2,
-                       'Using accounts file: /\n'
-                       'Error accessing /: Is a directory\n',
-                       '',
-                       [ self.TOOL, '-f', '/', 'add', 'user', 'pass' ])
-
-        # Make sure we can initially write to the test file
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user1', 'pass1'
-                       ])
-
-    @unittest.skipIf(os.getuid() == 0,
-                     'test cannot be run as root user')
-    def test_bad_file_permissions(self):
-        """
-        Check for graceful handling of bad file argument
-        """
-        # Create the test file
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user1', 'pass1'
-                       ])
-
-        # Make it non-writable (don't worry about cleanup, the
-        # file should be deleted after each test anyway
-        os.chmod(self.OUTPUT_FILE, stat.S_IRUSR)
-        self.run_check(2,
-                       'Using accounts file: test_users.csv\n'
-                       'Error accessing test_users.csv: Permission denied\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user2', 'pass1'
-                       ])
-
-        self.run_check(2,
-                       'Using accounts file: test_users.csv\n'
-                       'Error accessing test_users.csv: Permission denied\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'delete', 'user1'
-                       ])
-
-        # Making it write-only should have the same effect
-        os.chmod(self.OUTPUT_FILE, stat.S_IWUSR)
-        self.run_check(2,
-                       'Using accounts file: test_users.csv\n'
-                       'Error accessing test_users.csv: Permission denied\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user2', 'pass1'
-                       ])
-
-        self.run_check(2,
-                       'Using accounts file: test_users.csv\n'
-                       'Error accessing test_users.csv: Permission denied\n',
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'delete', 'user1'
-                       ])
-
-    def test_missing_fields(self):
-        """
-        Test that an invalid csv file is handled gracefully
-        """
-        # Valid but incomplete csv; should be handled
-        # correctly
-        with open(self.OUTPUT_FILE, 'w', newline='') as f:
-            f.write('onlyuserfield\n')
-            f.write('userfield,saltfield\n')
-            f.write(',emptyuserfield,passwordfield\n')
-
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user1', 'pass1'
-                       ])
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'delete', 'onlyuserfield'
-                       ])
-        self.run_check(0, None, None,
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'delete', ''
-                       ])
-
-    def test_bad_data(self):
-        # I can only think of one invalid format, an unclosed string
-        with open(self.OUTPUT_FILE, 'w', newline='') as f:
-            f.write('a,"\n')
-        # Different versions of the csv library return different errors.
-        # So we need to check the output in a little more complex way.
-        # We ask the run_check not to check the output and check it
-        # ourselves.
-        (stdout, stderr) = self.run_check(2, None,
-                       '',
-                       [ self.TOOL,
-                         '-f', self.OUTPUT_FILE,
-                         'add', 'user1', 'pass1'
-                       ])
-        # This looks little bit awkward, but is probably easiest with
-        # just 2 known possibilities. If there are more, we'll have to
-        # think of something else.
-        self.assertTrue(stdout ==
-                        'Using accounts file: test_users.csv\n'
-                        'Error parsing csv file: newline inside string\n' or
-                        stdout ==
-                        'Using accounts file: test_users.csv\n'
-                        'Error parsing csv file: unexpected end of data\n')
-
-
-
-if __name__== '__main__':
-    unittest.main()
-
index b9c70d03b786ab706a6f3a4178c5506345421eaa..175c27b374904e0c12f6667f9e7848419f57067b 100644 (file)
@@ -868,27 +868,6 @@ TEST_F(CCSessionTest, delayedStart) {
                  FakeSession::DoubleRead);
 }
 
-TEST_F(CCSessionTest, loggingStart) {
-    // provide the logging module spec
-    ConstElementPtr log_spec = moduleSpecFromFile(LOG_SPEC_FILE).getFullSpec();
-    session.getMessages()->add(createAnswer(0, log_spec));
-    // just give an empty config
-    session.getMessages()->add(createAnswer(0, el("{}")));
-    ModuleCCSession mccs(ccspecfile("spec2.spec"), session, NULL, NULL,
-                         true, true);
-    EXPECT_TRUE(session.haveSubscription("Logging", "*"));
-}
-
-TEST_F(CCSessionTest, loggingStartBadSpec) {
-    // provide the logging module spec
-    session.getMessages()->add(createAnswer(0, el("{}")));
-    // just give an empty config
-    session.getMessages()->add(createAnswer(0, el("{}")));
-    EXPECT_THROW(new ModuleCCSession(ccspecfile("spec2.spec"), session,
-                 NULL, NULL), ModuleSpecError);
-    EXPECT_FALSE(session.haveSubscription("Logging", "*"));
-}
-
 // Similar to the above, but more implicitly by calling addRemoteConfig().
 // We should construct ModuleCCSession with start_immediately being false
 // if we need to call addRemoteConfig().
index 12fb94878c19105c6af238e113b82bc87e092450..fbd0821048ee321f42ce519e18c3643a6e62f830 100644 (file)
@@ -1,7 +1,7 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 #PYTESTS = args_test.py bind10_test.py
 # NOTE: this has a generated test found in the builddir
-PYTESTS = sockcreator_test.py component_test.py socket_cache_test.py
+PYTESTS = 
 
 EXTRA_DIST = $(PYTESTS)
 
index 8c79c84e55e73ce46acbb5d3c7859c6e4c85527d..58037a7779fab35e552abb97c037b38faa3f1344 100644 (file)
@@ -1,5 +1,5 @@
 PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
-PYTESTS = ccsession_test.py cfgmgr_test.py config_data_test.py
+PYTESTS = config_data_test.py
 PYTESTS += module_spec_test.py
 EXTRA_DIST = $(PYTESTS)
 EXTRA_DIST += unittest_fakesession.py
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
deleted file mode 100644 (file)
index 9c33ef6..0000000
+++ /dev/null
@@ -1,1358 +0,0 @@
-# Copyright (C) 2010  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 the ConfigData and MultiConfigData classes
-#
-
-import unittest
-import os
-from isc.config.ccsession import *
-from isc.config.config_data import BIND10_CONFIG_DATA_VERSION
-from unittest_fakesession import FakeModuleCCSession, WouldBlockForever
-import bind10_config
-import isc.log
-
-class TestHelperFunctions(unittest.TestCase):
-    def test_parse_answer(self):
-        self.assertRaises(ModuleCCSessionError, parse_answer, 1)
-        self.assertRaises(ModuleCCSessionError, parse_answer, { 'just a dict': 1 })
-        self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': 1 })
-        self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [] })
-        self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [ 'not_an_rcode' ] })
-        self.assertRaises(ModuleCCSessionError, parse_answer, { 'result': [ 1, 2 ] })
-
-        rcode, val = parse_answer({ 'result': [ 0 ] })
-        self.assertEqual(0, rcode)
-        self.assertEqual(None, val)
-        rcode, val = parse_answer({ 'result': [ 0, "something" ] })
-        self.assertEqual(0, rcode)
-        self.assertEqual("something", val)
-        rcode, val = parse_answer({ 'result': [ 1, "some error" ] })
-        self.assertEqual(1, rcode)
-        self.assertEqual("some error", val)
-
-    def test_create_answer(self):
-        self.assertRaises(ModuleCCSessionError, create_answer, 'not_an_int')
-        self.assertRaises(ModuleCCSessionError, create_answer, 1, 2)
-        self.assertRaises(ModuleCCSessionError, create_answer, 1)
-        self.assertEqual({ 'result': [ 0 ] }, create_answer(0))
-        self.assertEqual({ 'result': [ 1, 'something bad' ] }, create_answer(1, 'something bad'))
-        self.assertEqual({ 'result': [ 0, 'something good' ] }, create_answer(0, 'something good'))
-        self.assertEqual({ 'result': [ 0, ['some', 'list' ] ] }, create_answer(0, ['some', 'list']))
-        self.assertEqual({ 'result': [ 0, {'some': 'map' } ] }, create_answer(0, {'some': 'map'}))
-
-    def test_parse_command(self):
-        cmd, arg = parse_command(1)
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({})
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'not a command': 1})
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'command': 1})
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'command': []})
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'command': [ 1 ]})
-        self.assertEqual(None, cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'command': [ 'command' ]})
-        self.assertEqual('command', cmd)
-        self.assertEqual(None, arg)
-
-        cmd, arg = parse_command({ 'command': [ 'command', 1 ]})
-        self.assertEqual('command', cmd)
-        self.assertEqual(1, arg)
-
-        cmd, arg = parse_command({ 'command': [ 'command', ['some', 'argument', 'list'] ]})
-        self.assertEqual('command', cmd)
-        self.assertEqual(['some', 'argument', 'list'], arg)
-
-    def test_create_command(self):
-        self.assertRaises(ModuleCCSessionError, create_command, 1)
-        self.assertEqual({'command': [ 'my_command' ]}, create_command('my_command'))
-        self.assertEqual({'command': [ 'my_command', 1 ]}, create_command('my_command', 1))
-        self.assertEqual({'command': [ 'my_command', [ 'some', 'list' ] ]}, create_command('my_command', [ 'some', 'list' ]))
-        self.assertEqual({'command': [ 'my_command', { 'some': 'map' } ]}, create_command('my_command', { 'some': 'map' }))
-
-class TestModuleCCSession(unittest.TestCase):
-    def setUp(self):
-        if 'CONFIG_TESTDATA_PATH' in os.environ:
-            self.data_path = os.environ['CONFIG_TESTDATA_PATH']
-        else:
-            self.data_path = "../../../testdata"
-
-    def spec_file(self, file):
-        return self.data_path + os.sep + file
-
-    def create_session(self, spec_file_name, config_handler = None,
-                       command_handler = None, cc_session = None):
-        return ModuleCCSession(self.spec_file(spec_file_name),
-                               config_handler, command_handler,
-                               cc_session, False)
-
-    def test_init(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        self.assertEqual(isc.config.module_spec_from_file(self.spec_file("spec1.spec"))._module_spec, mccs.specification._module_spec)
-        self.assertEqual(None, mccs._config_handler)
-        self.assertEqual(None, mccs._command_handler)
-
-    def test_start1(self):
-        fake_session = FakeModuleCCSession()
-        self.assertFalse("Spec1" in fake_session.subscriptions)
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        self.assertTrue("Spec1" in fake_session.subscriptions)
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg(None, 'Spec1')
-        fake_session.group_sendmsg(None, 'Spec1')
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-        self.assertEqual({'command': ['module_spec', {'module_name': 'Spec1'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec1'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec1")
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec1")
-        mccs.start()
-        self.assertEqual(len(fake_session.message_queue), 2)
-
-        self.assertEqual({'command': ['module_spec', {'module_name': 'Spec1'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec1'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        mccs = None
-        self.assertFalse("Spec1" in fake_session.subscriptions)
-
-    def test_start2(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg(None, 'Spec2')
-        fake_session.group_sendmsg(None, 'Spec2')
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
-        fake_session.group_sendmsg({'result': [ 0, {} ]}, "Spec2")
-        mccs.start()
-        self.assertEqual(len(fake_session.message_queue), 2)
-
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-    def test_start3(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg(None, 'Spec2')
-        fake_session.group_sendmsg(None, 'Spec2')
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
-        fake_session.group_sendmsg({'result': [ 0, {} ]}, "Spec2")
-        mccs.start()
-        self.assertEqual(len(fake_session.message_queue), 2)
-
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-    def test_start4(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg(None, 'Spec2')
-        fake_session.group_sendmsg(None, 'Spec2')
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
-        fake_session.group_sendmsg({'result': [ 1, "just an error" ]}, "Spec2")
-        mccs.start()
-        self.assertEqual(len(fake_session.message_queue), 2)
-
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-    def test_start5(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg(None, 'Spec2')
-        fake_session.group_sendmsg(None, 'Spec2')
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec2")
-        fake_session.group_sendmsg({'result': [ 0, {"Wrong": True} ]}, "Spec2")
-        self.assertRaises(ModuleCCSessionError, mccs.start)
-        self.assertEqual(len(fake_session.message_queue), 2)
-
-        self.assertEqual({'command': ['module_spec', mccs.specification._module_spec]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-    def test_stop(self):
-        fake_session = FakeModuleCCSession()
-        self.assertFalse("Spec1" in fake_session.subscriptions)
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        self.assertTrue("Spec1" in fake_session.subscriptions)
-
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.send_stopping()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'command': ['stopping', {'module_name': 'Spec1'}]},
-                         fake_session.get_message('ConfigManager', None))
-
-    def test_get_socket(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        self.assertNotEqual(None, mccs.get_socket())
-
-    def test_get_session(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        self.assertEqual(fake_session, mccs._session)
-
-    def test_close(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.close()
-        self.assertEqual(None, fake_session._socket)
-
-    def test_del_opened(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.__del__() # with opened fake_session
-
-    def test_del_closed(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        fake_session.close()
-        mccs.__del__() # with closed fake_session
-
-    def rpc_check(self, reply):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        fake_session.message_queue = [
-            ["Spec1", None, reply, False]
-        ]
-        exception = None
-        try:
-            result = mccs.rpc_call("test", "Spec2", params={
-                                       "param1": "Param 1",
-                                       "param2": "Param 2"
-                                   })
-        except Exception as e:
-            # We first want to check the value sent, raise the exception
-            # afterwards. So store it for a short while.
-            exception = e
-        self.assertEqual([
-                ["Spec2", "*", {"command": ["test", {
-                    "param1": "Param 1",
-                    "param2": "Param 2"
-                }]}, True]
-            ], fake_session.message_queue)
-        if exception is not None:
-            raise exception
-        return result
-
-    def test_rpc_call_success(self):
-        """
-        Test we can send an RPC (command) and get an answer. The answer is
-        success in this case.
-        """
-        result = self.rpc_check({"result": [0, {"Hello": "a"}]})
-        self.assertEqual({"Hello": "a"}, result)
-
-    def test_rpc_call_success_none(self):
-        """
-        Test the success case of RPC command, but the answer is empty
-        (eg. a "void" function on the remote side).
-        """
-        self.assertIsNone(self.rpc_check({"result": [0]}))
-
-    def test_rpc_call_malformed_answer(self):
-        """
-        Test it successfully raises ModuleCCSessionError when a malformed
-        reply is sent.
-        """
-        self.assertRaises(ModuleCCSessionError, self.rpc_check, ["Nonsense"])
-
-    def test_rpc_call_error(self):
-        """
-        Test it raises an exception when the remote side reports an error.
-        """
-        self.assertRaises(RPCError, self.rpc_check, {"result": [1, "Error"]})
-
-    def test_rpc_call_no_recpt(self):
-        """
-        Test RPC raises an error when the recipient is not there.
-        """
-        self.assertRaises(RPCRecipientMissing, self.rpc_check,
-                          {"result": [-1, "Error"]})
-
-    def test_notify(self):
-        """
-        Test the sent notification has the right format.
-        """
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.notify("group", "event", {"param": True})
-        self.assertEqual([
-            ["notifications/group", "*", {"notification": ["event", {
-                "param": True
-            }]}, False]], fake_session.message_queue)
-
-    def test_notify_no_params(self):
-        """
-        Test the sent notification has the right format, this time
-        without passing parameters.
-        """
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.notify("group", "event")
-        self.assertEqual([
-                ["notifications/group", "*", {"notification": ["event"]},
-                 False]
-            ],
-            fake_session.message_queue)
-
-    def test_notify_receive(self):
-        """
-        Test we can subscribe to notifications, receive them, unsubscribe, etc.
-        """
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        fake_session.group_sendmsg({"notification": ["event", {
-            "param": True
-        }]}, "notifications/group")
-        # Not subscribed to notifications -> not subscribed to
-        # 'notifications/group' -> message not eaten yet
-        mccs.check_command()
-        self.assertEqual(fake_session.message_queue, [['notifications/group',
-                         None, {'notification': ['event', {'param': True}]},
-                         False]])
-        # Place to log called notifications
-        notifications = []
-        def notified(tag, event, params):
-            notifications.append((tag, event, params))
-        # Subscribe to the notifications. Twice.
-        id1 = mccs.subscribe_notification('group',
-                                          lambda event, params:
-                                              notified("first", event, params))
-        id2 = mccs.subscribe_notification('group',
-                                          lambda event, params:
-                                              notified("second", event,
-                                              params))
-        # Now the message gets eaten because we are subscribed, and both
-        # callbacks are called.
-        mccs.check_command()
-        self.assertEqual(fake_session.message_queue, [])
-        self.assertEqual(notifications, [
-            ("first", "event", {'param': True}),
-            ("second", "event", {'param': True})
-        ])
-        del notifications[:]
-        # If a notification for different group comes, it is left untouched.
-        fake_session.group_sendmsg({"notification": ["event", {
-            "param": True
-        }]}, "notifications/other")
-        mccs.check_command()
-        self.assertEqual(notifications, [])
-        self.assertEqual(fake_session.message_queue, [['notifications/other',
-                         None, {'notification': ['event', {'param': True}]},
-                         False]])
-        del fake_session.message_queue[:]
-        # Unsubscribe one of the notifications and see that only the other
-        # is triggered.
-        mccs.unsubscribe_notification(id2)
-        fake_session.group_sendmsg({"notification": ["event", {
-            "param": True
-        }]}, "notifications/group")
-        mccs.check_command()
-        self.assertEqual(fake_session.message_queue, [])
-        self.assertEqual(notifications, [
-            ("first", "event", {'param': True})
-        ])
-        del notifications[:]
-        # If we try to unsubscribe again, it complains.
-        self.assertRaises(KeyError, mccs.unsubscribe_notification, id2)
-        # Unsubscribe the other one too. From now on, it doesn't eat the
-        # messages again.
-        mccs.unsubscribe_notification(id1)
-        fake_session.group_sendmsg({"notification": ["event", {
-            "param": True
-        }]}, "notifications/group")
-        mccs.check_command()
-        self.assertEqual(fake_session.message_queue, [['notifications/group',
-                         None, {'notification': ['event', {'param': True}]},
-                         False]])
-
-    def my_config_handler_ok(self, new_config):
-        return isc.config.ccsession.create_answer(0)
-
-    def my_config_handler_err(self, new_config):
-        return isc.config.ccsession.create_answer(1, "just an error")
-
-    def my_config_handler_exc(self, new_config):
-        raise Exception("just an exception")
-
-    def my_command_handler_ok(self, command, args):
-        return isc.config.ccsession.create_answer(0)
-
-    def my_command_handler_no_answer(self, command, args):
-        pass
-
-    def test_check_command1(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        fake_session.group_sendmsg({'result': [ 0 ]}, "Spec1")
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec1': 'a' })
-
-        fake_session.group_sendmsg(cmd, 'Spec1')
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [2, 'Spec1 has no config handler']},
-                         fake_session.get_message('Spec1', None))
-
-    def test_check_command2(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec1': 'a' })
-        fake_session.group_sendmsg(cmd, 'Spec1')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [1, 'No config_data specification']},
-                         fake_session.get_message('Spec1', None))
-
-    def test_check_command3(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 2 })
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command4(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_err)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 'aaa' })
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [1, 'aaa should be an integer']},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command5(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_exc)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 'aaa' })
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [1, 'aaa should be an integer']},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command6(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command("print_message", "just a message")
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [2, 'Spec2 has no command handler']},
-                         fake_session.get_message('Spec2', None))
-
-    """Many check_command tests look too similar, this is common body."""
-    def common_check_command_check(self, cmd_handler,
-            cmd_check=lambda mccs, _: mccs.check_command()):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(cmd_handler)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command("print_message", "just a message")
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        cmd_check(mccs, fake_session)
-        return fake_session
-
-    def test_check_command7(self):
-        fake_session = self.common_check_command_check(
-            self.my_command_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command8(self):
-        fake_session = self.common_check_command_check(
-            self.my_command_handler_no_answer)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-    def test_check_command_block(self):
-        """See if the message gets there even in blocking mode."""
-        fake_session = self.common_check_command_check(
-            self.my_command_handler_ok,
-            lambda mccs, _: mccs.check_command(False))
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command_block_timeout(self):
-        """Check it works if session has timeout and it sets it back."""
-        def cmd_check(mccs, session):
-            session.set_timeout(1)
-            mccs.check_command(False)
-        fake_session = self.common_check_command_check(
-            self.my_command_handler_ok, cmd_check)
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-        self.assertEqual(fake_session.get_timeout(), 1)
-
-    def test_check_command_blocks_forever(self):
-        """Check it would wait forever checking a command."""
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
-
-    def test_check_command_blocks_forever_timeout(self):
-        """Like above, but it should wait forever even with timeout here."""
-        fake_session = FakeModuleCCSession()
-        fake_session.set_timeout(1)
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
-
-    def test_check_command_without_recvmsg1(self):
-        "copied from test_check_command2"
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec1': 'abcd' })
-        env = { 'group': 'Spec1', 'from':None };
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [1, 'No config_data specification']},
-                         fake_session.get_message('Spec1', None))
-
-    def test_check_command_without_recvmsg2(self):
-        "copied from test_check_command3"
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'item1': 2 })
-        self.assertEqual(len(fake_session.message_queue), 0)
-        env = { 'group':'Spec2', 'from':None }
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                          fake_session.get_message('Spec2', None))
-
-    def test_check_command_without_recvmsg3(self):
-        "copied from test_check_command7"
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-        cmd = isc.config.ccsession.create_command("print_message", "just a message")
-        env = { 'group':'Spec2', 'from':None }
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-
-    def test_check_command_block_timeout(self):
-        """Check it works if session has timeout and it sets it back."""
-        def cmd_check(mccs, session):
-            session.set_timeout(1)
-            mccs.check_command(False)
-        fake_session = self.common_check_command_check(
-            self.my_command_handler_ok, cmd_check)
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [0]},
-                         fake_session.get_message('Spec2', None))
-        self.assertEqual(fake_session.get_timeout(), 1)
-
-    def test_check_command_blocks_forever(self):
-        """Check it would wait forever checking a command."""
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
-
-    def test_check_command_blocks_forever_timeout(self):
-        """Like above, but it should wait forever even with timeout here."""
-        fake_session = FakeModuleCCSession()
-        fake_session.set_timeout(1)
-        mccs = self.create_session("spec2.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
-
-    # Now there's a group of tests testing both add_remote_config and
-    # add_remote_config_by_name. Since they are almost the same (they differ
-    # just in the parameter and that the second one asks one more question over
-    # the bus), the actual test code is shared.
-    #
-    # These three functions are helper functions to easy up the writing of them.
-    # To write a test, there need to be 3 functions. First, the function that
-    # does the actual test. It looks like:
-    # def _internal_test(self, function_lambda, param, fill_other_messages):
-    #
-    # The function_lambda provides the tested function if called on the
-    # ccsession. The param is the parameter to pass to the function (either
-    # the module name or the spec file name. The fill_other_messages fills
-    # needed messages (the answer containing the module spec in case of add by
-    # name, no messages in the case of adding by spec file) into the fake bus.
-    # So, the code would look like:
-    #
-    # * Create the fake session and tested ccsession object
-    # * function = function_lambda(ccsession object)
-    # * fill_other_messages(fake session)
-    # * Fill in answer to the get_module_config command
-    # * Test by calling function(param)
-    #
-    # Then you need two wrappers that do launch the tests. There are helpers
-    # for that, so you can just call:
-    # def test_by_spec(self)
-    #     self._common_remote_module_test(self._internal_test)
-    # def test_by_name(self)
-    #     self._common_remote_module_by_name_test(self._internal_test)
-    def _common_remote_module_test(self, internal_test):
-        internal_test(lambda ccs: ccs.add_remote_config,
-                      self.spec_file("spec2.spec"),
-                      lambda session: None)
-
-    def _prepare_spec_message(self, session, spec_name):
-        # It could have been one command, but the line would be way too long
-        # to even split it
-        spec_file = self.spec_file(spec_name)
-        spec = isc.config.module_spec_from_file(spec_file)
-        session.group_sendmsg({'result': [0, spec.get_full_spec()]}, "Spec1")
-
-    def _common_remote_module_by_name_test(self, internal_test):
-        internal_test(lambda ccs: ccs.add_remote_config_by_name, "Spec2",
-                      lambda session: self._prepare_spec_message(session,
-                                                                 "spec2.spec"))
-
-    def _internal_remote_module(self, function_lambda, parameter,
-                                fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.remove_remote_config("Spec2")
-        function = function_lambda(mccs)
-
-        self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
-
-        self.assertFalse("Spec2" in fake_session.subscriptions)
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = function(parameter)
-        self.assertTrue("Spec2" in fake_session.subscriptions)
-        self.assertEqual("Spec2", rmodname)
-        self.assertRaises(isc.cc.data.DataNotFoundError, mccs.get_remote_config_value, rmodname, "asdf")
-        value, default = mccs.get_remote_config_value(rmodname, "item1")
-        self.assertEqual(1, value)
-        self.assertEqual(True, default)
-
-        mccs.remove_remote_config(rmodname)
-        self.assertFalse("Spec2" in fake_session.subscriptions)
-        self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
-
-        # test if unsubscription is also sent when object is deleted
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg({'result' : [0]}, 'Spec2')
-        rmodname = function(parameter)
-        self.assertTrue("Spec2" in fake_session.subscriptions)
-        mccs = None
-        function = None
-        self.assertFalse("Spec2" in fake_session.subscriptions)
-
-    def test_remote_module(self):
-        """
-        Test we can add a remote config and get the configuration.
-        Remote module specified by the spec file name.
-        """
-        self._common_remote_module_test(self._internal_remote_module)
-
-    def test_remote_module_by_name(self):
-        """
-        Test we can add a remote config and get the configuration.
-        Remote module specified its name.
-        """
-        self._common_remote_module_by_name_test(self._internal_remote_module)
-
-    def _internal_remote_module_with_custom_config(self, function_lambda,
-                                                   parameter,
-                                                   fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        function = function_lambda(mccs)
-        # override the default config value for "item1". add_remote_config[_by_name]()
-        # should incorporate the overridden value, and we should be able to
-        # get it via get_remote_config_value().
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg({'result': [0, {"item1": 10}]}, 'Spec2')
-        rmodname = function(parameter)
-        value, default = mccs.get_remote_config_value(rmodname, "item1")
-        self.assertEqual(10, value)
-        self.assertEqual(False, default)
-
-    def test_remote_module_with_custom_config(self):
-        """
-        Test the config of module will load non-default values on
-        initialization.
-        Remote module specified by the spec file name.
-        """
-        self._common_remote_module_test(
-            self._internal_remote_module_with_custom_config)
-
-    def test_remote_module_by_name_with_custom_config(self):
-        """
-        Test the config of module will load non-default values on
-        initialization.
-        Remote module its name.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_remote_module_with_custom_config)
-
-    def _internal_ignore_command_remote_module(self, function_lambda, param,
-                                               fill_other_messages):
-        # Create a Spec1 module and subscribe to remote config for Spec2
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_command_handler(self.my_command_handler_ok)
-        function = function_lambda(mccs)
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = function(param)
-
-        # remove the commands from queue
-        while len(fake_session.message_queue) > 0:
-            fake_session.get_message("ConfigManager")
-
-        # check if the command for the module itself is received
-        cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
-        fake_session.group_sendmsg(cmd, 'Spec1')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 1)
-        self.assertEqual({'result': [ 0 ]},
-                         fake_session.get_message('Spec1', None))
-
-        # check if the command for the other module is ignored
-        cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
-        fake_session.group_sendmsg(cmd, 'Spec2')
-        self.assertEqual(len(fake_session.message_queue), 1)
-        mccs.check_command()
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-    def test_ignore_command_remote_module(self):
-        """
-        Test that commands for remote modules aren't handled.
-        Remote module specified by the spec file name.
-        """
-        self._common_remote_module_test(
-            self._internal_ignore_command_remote_module)
-
-    def test_ignore_command_remote_module_by_name(self):
-        """
-        Test that commands for remote modules aren't handled.
-        Remote module specified by its name.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_ignore_command_remote_module)
-
-    def _internal_check_command_without_recvmsg_remote_module(self,
-                                                              function_lambda,
-                                                              param,
-                                                              fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        function = function_lambda(mccs)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = function(param)
-        if (len(fake_session.message_queue) == 2):
-            self.assertEqual({'command': ['get_module_spec',
-                                          {'module_name': 'Spec2'}]},
-                             fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
-        env = { 'group':'Spec2', 'from':None }
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-    def test_check_command_without_recvmsg_remote_module(self):
-        """
-        Test updates on remote module.
-        The remote module is specified by the spec file name.
-        """
-        self._common_remote_module_test(
-            self._internal_check_command_without_recvmsg_remote_module)
-
-    def test_check_command_without_recvmsg_remote_module_by_name(self):
-        """
-        Test updates on remote module.
-        The remote module is specified by its name.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_check_command_without_recvmsg_remote_module)
-
-    def _internal_check_command_without_recvmsg_remote_module2(self,
-                                                               function_lambda,
-                                                               param,
-                                                               fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        function = function_lambda(mccs)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = function(param)
-        if (len(fake_session.message_queue) == 2):
-            self.assertEqual({'command': ['get_module_spec',
-                                          {'module_name': 'Spec2'}]},
-                             fake_session.get_message('ConfigManager', None))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
-        env = { 'group':'Spec3', 'from':None }
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-    def test_check_command_without_recvmsg_remote_module2(self):
-        """
-        Test updates on remote module.
-        The remote module is specified by the spec file name.
-        """
-        self._common_remote_module_test(
-            self._internal_check_command_without_recvmsg_remote_module2)
-
-    def test_check_command_without_recvmsg_remote_module_by_name2(self):
-        """
-        Test updates on remote module.
-        The remote module is specified by its name.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_check_command_without_recvmsg_remote_module2)
-
-    def _internal_remote_module_bad_config(self, function_lambda, parameter,
-                                           fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        function = function_lambda(mccs)
-        # Provide wrong config data. It should be rejected.
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg({'result': [0, {"bad_item": -1}]}, 'Spec2')
-        self.assertRaises(isc.config.ModuleCCSessionError,
-                          function, parameter)
-
-    def test_remote_module_bad_config(self):
-        """
-        Test the remote module rejects bad config data.
-        """
-        self._common_remote_module_test(
-            self._internal_remote_module_bad_config)
-
-    def test_remote_module_by_name_bad_config(self):
-        """
-        Test the remote module rejects bad config data.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_remote_module_bad_config)
-
-    def _internal_remote_module_error_response(self, function_lambda,
-                                               parameter, fill_other_messages):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        function = function_lambda(mccs)
-        # Provide wrong config data. It should be rejected.
-        fill_other_messages(fake_session)
-        fake_session.group_sendmsg({'result': [1, "An error, and I mean it!"]},
-                                   'Spec2')
-        self.assertRaises(isc.config.ModuleCCSessionError,
-                          function, parameter)
-
-    def test_remote_module_bad_config(self):
-        """
-        Test the remote module complains if there's an error response."
-        """
-        self._common_remote_module_test(
-            self._internal_remote_module_error_response)
-
-    def test_remote_module_by_name_bad_config(self):
-        """
-        Test the remote module complains if there's an error response."
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_remote_module_error_response)
-
-    def test_remote_module_bad_config(self):
-        """
-        Test the remote module rejects bad config data.
-        """
-        self._common_remote_module_by_name_test(
-            self._internal_remote_module_bad_config)
-
-    def test_module_name_mismatch(self):
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self._prepare_spec_message(fake_session, 'spec1.spec')
-        self.assertRaises(isc.config.ModuleCCSessionError,
-                          mccs.add_remote_config_by_name, "Spec2")
-
-    def test_logconfig_handler(self):
-        # test whether default_logconfig_handler reacts nicely to
-        # bad data. We assume the actual logger output is tested
-        # elsewhere
-        self.assertRaises(TypeError, default_logconfig_handler);
-        self.assertRaises(TypeError, default_logconfig_handler, 1);
-
-        spec = isc.config.module_spec_from_file(
-            path_search('logging.spec', bind10_config.PLUGIN_PATHS))
-        config_data = ConfigData(spec)
-
-        self.assertRaises(TypeError, default_logconfig_handler, 1, config_data)
-
-        default_logconfig_handler({}, config_data)
-
-        # Wrong data should not raise, but simply not be accepted
-        # This would log a lot of errors, so we may want to suppress that later
-        default_logconfig_handler({ "bad_data": "indeed" }, config_data)
-        default_logconfig_handler({ "bad_data": 1}, config_data)
-        default_logconfig_handler({ "bad_data": 1123 }, config_data)
-        default_logconfig_handler({ "bad_data": True }, config_data)
-        default_logconfig_handler({ "bad_data": False }, config_data)
-        default_logconfig_handler({ "bad_data": 1.1 }, config_data)
-        default_logconfig_handler({ "bad_data": [] }, config_data)
-        default_logconfig_handler({ "bad_data": [[],[],[[1, 3, False, "foo" ]]] },
-                                  config_data)
-        default_logconfig_handler({ "bad_data": [ 1, 2, { "b": { "c": "d" } } ] },
-                                  config_data)
-
-        # Try a correct config
-        log_conf = {"loggers":
-                       [{"name": "b10-xfrout", "output_options":
-                           [{"output": "/tmp/bind10.log",
-                                       "destination": "file",
-                                       "flush": True}]}]}
-        default_logconfig_handler(log_conf, config_data)
-
-class fakeData:
-    def decode(self):
-        return "{}";
-
-class fakeAnswer:
-    def read(self):
-        return fakeData();
-
-class fakeUIConn():
-    def __init__(self):
-        self.get_answers = {}
-        self.post_answers = {}
-
-    def set_get_answer(self, name, answer):
-        self.get_answers[name] = answer
-
-    def set_post_answer(self, name, answer):
-        self.post_answers[name] = answer
-
-    def send_GET(self, name, arg = None):
-        if name in self.get_answers:
-            return self.get_answers[name]
-        else:
-            return {}
-
-    def send_POST(self, name, arg = None):
-        if name in self.post_answers:
-            return self.post_answers[name]
-        else:
-            return fakeAnswer()
-
-
-class TestUIModuleCCSession(unittest.TestCase):
-    def setUp(self):
-        if 'CONFIG_TESTDATA_PATH' in os.environ:
-            self.data_path = os.environ['CONFIG_TESTDATA_PATH']
-        else:
-            self.data_path = "../../../testdata"
-
-    def spec_file(self, file):
-        return self.data_path + os.sep + file
-
-    def create_uccs(self, fake_conn, specfile="spec2.spec"):
-        module_spec = isc.config.module_spec_from_file(self.spec_file(specfile))
-        fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
-        return UIModuleCCSession(fake_conn)
-
-    def create_uccs_named_set(self, fake_conn):
-        module_spec = isc.config.module_spec_from_file(self.spec_file("spec32.spec"))
-        fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
-        return UIModuleCCSession(fake_conn)
-
-    def create_uccs_listtest(self, fake_conn):
-        module_spec = isc.config.module_spec_from_file(self.spec_file("spec39.spec"))
-        fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
-        return UIModuleCCSession(fake_conn)
-
-    def test_init(self):
-        fake_conn = fakeUIConn()
-        fake_conn.set_get_answer('/module_spec', {})
-        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
-        uccs = UIModuleCCSession(fake_conn)
-        self.assertEqual({}, uccs._specifications)
-        self.assertEqual({ 'version': BIND10_CONFIG_DATA_VERSION}, uccs._current_config)
-
-        module_spec = isc.config.module_spec_from_file(self.spec_file("spec2.spec"))
-        fake_conn.set_get_answer('/module_spec', { module_spec.get_module_name(): module_spec.get_full_spec()})
-        fake_conn.set_get_answer('/config_data', { 'version': BIND10_CONFIG_DATA_VERSION })
-        uccs = UIModuleCCSession(fake_conn)
-        self.assertEqual(module_spec._module_spec, uccs._specifications['Spec2']._module_spec)
-
-        fake_conn.set_get_answer('/config_data', { 'version': 123123 })
-        self.assertRaises(ModuleCCSessionError, UIModuleCCSession, fake_conn)
-
-    def test_request_specifications(self):
-        module_spec1 = isc.config.module_spec_from_file(
-                          self.spec_file("spec1.spec"))
-        module_spec_dict1 = { "module_spec": module_spec1.get_full_spec() }
-        module_spec2 = isc.config.module_spec_from_file(
-                          self.spec_file("spec2.spec"))
-        module_spec_dict2 = { "module_spec": module_spec2.get_full_spec() }
-
-        fake_conn = fakeUIConn()
-        # Set the first one in the answer
-        fake_conn.set_get_answer('/module_spec', module_spec_dict1)
-        fake_conn.set_get_answer('/config_data',
-                                 { 'version': BIND10_CONFIG_DATA_VERSION })
-        uccs = UIModuleCCSession(fake_conn)
-
-        # We should now have the first one, but not the second.
-        self.assertTrue("Spec1" in uccs._specifications)
-        self.assertEqual(module_spec1.get_full_spec(),
-                         uccs._specifications["Spec1"].get_full_spec())
-        self.assertFalse("Spec2" in uccs._specifications)
-
-        # Now set an answer where only the second one is present
-        fake_conn.set_get_answer('/module_spec', module_spec_dict2)
-
-        uccs.request_specifications()
-
-        # Now Spec1 should have been removed, and spec2 should be there
-        self.assertFalse("Spec1" in uccs._specifications)
-        self.assertTrue("Spec2" in uccs._specifications)
-        self.assertEqual(module_spec2.get_full_spec(),
-                         uccs._specifications["Spec2"].get_full_spec())
-
-    def test_add_remove_value(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs(fake_conn)
-
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, 1, "a")
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "no_such_item", "a")
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, 1, "a")
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "no_such_item", "a")
-        # add and remove should raise DataNotFoundError when used with items
-        # that are not a list or named_set (more importantly, they should
-        # not raise TypeError)
-        self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec2/item1", "a")
-        self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec2/item1", "a")
-        self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec2", "")
-        self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec2", "")
-
-        self.assertEqual({}, uccs._local_changes)
-        uccs.add_value("Spec2/item5", "foo")
-        self.assertEqual({'Spec2': {'item5': ['a', 'b', 'foo']}}, uccs._local_changes)
-        uccs.remove_value("Spec2/item5", "foo")
-        self.assertEqual({'Spec2': {'item5': ['a', 'b']}}, uccs._local_changes)
-        uccs._local_changes = {'Spec2': {'item5': []}}
-        uccs.remove_value("Spec2/item5", "foo")
-        uccs.add_value("Spec2/item5", "foo")
-        self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
-        self.assertRaises(isc.cc.data.DataAlreadyPresentError,
-                          uccs.add_value, "Spec2/item5", "foo")
-        self.assertEqual({'Spec2': {'item5': ['foo']}}, uccs._local_changes)
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          uccs.remove_value, "Spec2/item5[123]", None)
-        uccs.remove_value("Spec2/item5[0]", None)
-        self.assertEqual({'Spec2': {'item5': []}}, uccs._local_changes)
-        uccs.add_value("Spec2/item5", None);
-        self.assertEqual({'Spec2': {'item5': ['']}}, uccs._local_changes)
-        # Intending to empty a list element, but forget specifying the index.
-        self.assertRaises(isc.cc.data.DataTypeError,
-                          uccs.remove_value, "Spec2/item5", None)
-
-
-    # Check that the difference between no default and default = null
-    # is recognized
-    def test_default_null(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs(fake_conn, "spec40.spec")
-        (value, status) = uccs.get_value("/Spec40/item2")
-        self.assertIsNone(value)
-        self.assertEqual(uccs.NONE, status)
-        (value, status) = uccs.get_value("/Spec40/item3")
-        self.assertIsNone(value)
-        self.assertEqual(uccs.DEFAULT, status)
-
-    # Test adding and removing values for type = any
-    def test_add_remove_value_any(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs(fake_conn, "spec40.spec")
-
-        # Test item set of basic types
-        items = [ 1234, "foo", True, False ]
-        items_as_str = [ '1234', 'foo', 'true', 'false' ]
-
-        def test_fails():
-            self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec40/item1", "foo")
-            self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec40/item1", "foo", "bar")
-            self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec40/item1", "foo")
-            self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec40/item1[0]", None)
-
-        # A few helper functions to perform a number of tests
-        # (to repeat the same test for nested data)
-        def check_list(identifier):
-            for item in items_as_str:
-                uccs.add_value(identifier, item)
-            self.assertEqual((items, 1), uccs.get_value(identifier))
-
-            # Removing from list should work in both ways
-            uccs.remove_value(identifier, "foo")
-            uccs.remove_value(identifier + "[1]", None)
-            self.assertEqual(([1234, False], 1), uccs.get_value(identifier))
-
-            # As should item indexing
-            self.assertEqual((1234, 1), uccs.get_value(identifier + "[0]"))
-            self.assertEqual((False, 1), uccs.get_value(identifier + "[1]"))
-
-        def check_named_set(identifier):
-            for item in items_as_str:
-                # use string version as key as well
-                uccs.add_value(identifier, item, item)
-
-            self.assertEqual((1234, 1), uccs.get_value(identifier + "/1234"))
-            self.assertEqual((True, 1), uccs.get_value(identifier + "/true"))
-
-            for item in items_as_str:
-                # use string version as key as well
-                uccs.remove_value(identifier, item)
-
-
-        # should fail when set to value of primitive type
-        for item in items:
-            uccs.set_value("Spec40/item1", item)
-            test_fails()
-
-        # When set to list, add and remove should work, and its elements
-        # should be considered of type 'any' themselves.
-        uccs.set_value("Spec40/item1", [])
-        check_list("Spec40/item1")
-
-        # When set to dict, it should have the behaviour of a named set
-        uccs.set_value("Spec40/item1", {})
-        check_named_set("Spec40/item1")
-
-        # And, or course, we may need nesting.
-        uccs.set_value("Spec40/item1", { "foo": {}, "bar": [] })
-        check_named_set("Spec40/item1/foo")
-        check_list("Spec40/item1/bar")
-        uccs.set_value("Spec40/item1", [ {}, [] ] )
-        check_named_set("Spec40/item1[0]")
-        check_list("Spec40/item1[1]")
-        uccs.set_value("Spec40/item1", [[[[[[]]]]]] )
-        check_list("Spec40/item1[0][0][0][0][0]")
-        uccs.set_value("Spec40/item1", { 'a': { 'a': { 'a': {} } } } )
-        check_named_set("Spec40/item1/a/a/a")
-
-    def test_add_dup_value(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs_listtest(fake_conn)
-
-        uccs.add_value("Spec39/list")
-        self.assertRaises(isc.cc.data.DataAlreadyPresentError, uccs.add_value,
-                          "Spec39/list")
-
-    def test_add_remove_value_named_set(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs_named_set(fake_conn)
-        value, status = uccs.get_value("/Spec32/named_set_item")
-        self.assertEqual({'a': 1, 'b': 2}, value)
-
-        # make sure that removing from default actually removes it
-        uccs.remove_value("/Spec32/named_set_item", "a")
-        value, status = uccs.get_value("/Spec32/named_set_item")
-        self.assertEqual({'b': 2}, value)
-        self.assertEqual(uccs.LOCAL, status)
-
-        # ok, put it back now
-        uccs.add_value("/Spec32/named_set_item", "a")
-        uccs.set_value("/Spec32/named_set_item/a", 1)
-
-        uccs.add_value("/Spec32/named_set_item", "foo")
-        value, status = uccs.get_value("/Spec32/named_set_item")
-        self.assertEqual({'a': 1, 'b': 2, 'foo': 3}, value)
-
-        uccs.remove_value("/Spec32/named_set_item", "a")
-        uccs.remove_value("/Spec32/named_set_item", "foo")
-        value, status = uccs.get_value("/Spec32/named_set_item")
-        self.assertEqual({'b': 2}, value)
-
-        uccs.set_value("/Spec32/named_set_item/c", 5)
-        value, status = uccs.get_value("/Spec32/named_set_item")
-        self.assertEqual({"b": 2, "c": 5}, value)
-
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          uccs.set_value,
-                          "/Spec32/named_set_item/no_such_item/a",
-                          4)
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          uccs.remove_value, "/Spec32/named_set_item",
-                          "no_such_item")
-        self.assertRaises(isc.cc.data.DataAlreadyPresentError,
-                          uccs.add_value, "/Spec32/named_set_item", "c")
-
-    def test_set_value_named_set(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs_named_set(fake_conn)
-        value, status = uccs.get_value("/Spec32/named_set_item2")
-        self.assertEqual({}, value)
-        self.assertEqual(status, uccs.DEFAULT)
-
-        # Try setting a value that is optional but has no default
-        uccs.add_value("/Spec32/named_set_item2", "new1")
-        uccs.set_value("/Spec32/named_set_item2/new1/first", 3)
-        # Different method to add a new element
-        uccs.set_value("/Spec32/named_set_item2/new2", { "second": 4 })
-
-        value, status = uccs.get_value("/Spec32/named_set_item2")
-        self.assertEqual({ "new1": {"first": 3 }, "new2": {"second": 4}},
-                         value)
-        self.assertEqual(status, uccs.LOCAL)
-
-        uccs.set_value("/Spec32/named_set_item2/new1/second", "foo")
-
-        value, status = uccs.get_value("/Spec32/named_set_item2")
-        self.assertEqual({ "new1": {"first": 3, "second": "foo" },
-                           "new2": {"second": 4}},
-                         value)
-        self.assertEqual(status, uccs.LOCAL)
-
-        # make sure using a bad name still fails
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.set_value,
-                          "/Spec32/named_set_item2/doesnotexist/first", 3)
-
-
-
-    def test_commit(self):
-        fake_conn = fakeUIConn()
-        uccs = self.create_uccs(fake_conn)
-        uccs.commit()
-        uccs._local_changes = {'Spec2': {'item5': [ 'a' ]}}
-        uccs.commit()
-
-if __name__ == '__main__':
-    isc.log.init("bind10")
-    unittest.main()
-
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
deleted file mode 100644 (file)
index ee6c98d..0000000
+++ /dev/null
@@ -1,669 +0,0 @@
-# Copyright (C) 2010  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 the configuration manager module
-#
-
-import unittest
-import os
-from isc.config.cfgmgr import *
-from isc.config import config_data
-from unittest_fakesession import FakeModuleCCSession
-
-class TestConfigManagerData(unittest.TestCase):
-    def setUp(self):
-        self.data_path = os.environ['CONFIG_TESTDATA_PATH']
-        self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
-        self.config_manager_data = ConfigManagerData(self.writable_data_path,
-                                                     file_name="b10-config.db")
-        self.assertTrue(self.config_manager_data)
-
-    def test_abs_file(self):
-        """
-        Test what happens if we give the config manager an absolute path.
-        It shouldn't append the data path to it.
-        """
-        abs_path = self.data_path + os.sep + "b10-config-imaginary.db"
-        data = ConfigManagerData(self.data_path, abs_path)
-        self.assertEqual(abs_path, data.db_filename)
-        self.assertEqual(self.data_path, data.data_path)
-
-    def test_init(self):
-        self.assertEqual(self.config_manager_data.data['version'],
-                         config_data.BIND10_CONFIG_DATA_VERSION)
-        self.assertEqual(self.config_manager_data.data_path,
-                         self.writable_data_path)
-        self.assertEqual(self.config_manager_data.db_filename,
-                         self.writable_data_path + os.sep + "b10-config.db")
-
-    def test_check_for_updates_up_to_date(self):
-        # This should automatically give an up-to-date version
-        file_config = ConfigManagerData.read_from_file(
-                        self.writable_data_path, "b10-config.db").data
-        updated_config = ConfigManagerData.check_for_updates(file_config)
-        self.assertEqual(file_config, updated_config)
-
-    def test_check_for_updates_from_1(self):
-        config = { "version": 1,
-                   "foo": "bar",
-                   "something": [ 1, 2, 3 ] }
-        updated = ConfigManagerData.check_for_updates(config)
-        config['version'] = config_data.BIND10_CONFIG_DATA_VERSION
-        self.assertEqual(config, updated)
-
-    def test_check_for_updates_from_2(self):
-        # No 'Boss' present, no change (except version)
-        config = { "version": 2,
-                   "foo": "bar",
-                   "something": [ 1, 2, 3 ] }
-        updated = ConfigManagerData.check_for_updates(config)
-        config['version'] = config_data.BIND10_CONFIG_DATA_VERSION
-        self.assertEqual(config, updated)
-
-        # With Boss, should be changed to 'Init'
-        config = { "version": 2,
-                   "Boss": { "some config": 1 },
-                   "something": [ 1, 2, 3 ] }
-        updated = ConfigManagerData.check_for_updates(config)
-        config = { "version": config_data.BIND10_CONFIG_DATA_VERSION,
-                   "Init": { "some config": 1 },
-                   "something": [ 1, 2, 3 ] }
-        self.assertEqual(config, updated)
-
-        # With Boss AND Init, no change
-        config = { "version": 2,
-                   "Boss": { "some config": 1 },
-                   "Init": { "some other config": 1 },
-                   "something": [ 1, 2, 3 ] }
-        updated = ConfigManagerData.check_for_updates(config)
-        config['version'] = config_data.BIND10_CONFIG_DATA_VERSION
-        self.assertEqual(config, updated)
-
-    def test_read_from_file(self):
-        ConfigManagerData.read_from_file(self.writable_data_path, "b10-config.db")
-        self.assertRaises(ConfigManagerDataEmpty,
-                          ConfigManagerData.read_from_file,
-                          "doesnotexist", "b10-config.db")
-        self.assertRaises(ConfigManagerDataReadError,
-                          ConfigManagerData.read_from_file,
-                          self.data_path, "b10-config-bad1.db")
-        self.assertRaises(ConfigManagerDataReadError,
-                          ConfigManagerData.read_from_file,
-                          self.data_path, "b10-config-bad2.db")
-        self.assertRaises(ConfigManagerDataReadError,
-                          ConfigManagerData.read_from_file,
-                          self.data_path, "b10-config-bad3.db")
-        self.assertRaises(ConfigManagerDataReadError,
-                          ConfigManagerData.read_from_file,
-                          self.data_path, "b10-config-bad4.db")
-
-    def test_write_to_file(self):
-        output_file_name = "b10-config-write-test"
-        self.config_manager_data.write_to_file(output_file_name)
-        new_config = ConfigManagerData(self.data_path, output_file_name)
-        self.assertEqual(self.config_manager_data, new_config)
-        os.remove(output_file_name)
-
-    def check_existence(self, files, should_exist=[], should_not_exist=[]):
-        """Helper function for test_rename_config_file.
-           Arguments:
-           files: array of file names to check.
-           should_exist: array of indices, the files in 'files' with these
-                         indices should exist.
-           should_not_exist: array of indices, the files in 'files' with
-                             these indices should not exist."""
-        for n in should_exist:
-            self.assertTrue(os.path.exists(files[n]))
-        for n in should_not_exist:
-            self.assertFalse(os.path.exists(files[n]))
-
-    def test_rename_config_file(self):
-        # test file names, put in array for easy cleanup
-        filenames = [ "b10-config-rename-test",
-                      "b10-config-rename-test.bak",
-                      "b10-config-rename-test.bak.1",
-                      "b10-config-rename-test.bak.2" ]
-
-        for filename in filenames:
-            if os.path.exists(filename):
-                os.remove(filename)
-
-        # The original does not exist, so the new one should not be created
-        self.config_manager_data.rename_config_file(filenames[0])
-        self.check_existence(filenames, [], [0, 1, 2, 3])
-
-        # now create a file to rename, and call rename again
-        self.config_manager_data.write_to_file(filenames[0])
-        self.config_manager_data.rename_config_file(filenames[0])
-        self.check_existence(filenames, [1], [0, 2, 3])
-
-        # If backup already exists, give it a new name automatically
-        self.config_manager_data.write_to_file(filenames[0])
-        self.config_manager_data.rename_config_file(filenames[0])
-        self.check_existence(filenames, [1, 2], [0, 3])
-
-        # If backup already exists, give it a new name automatically with
-        # increasing postfix
-        self.config_manager_data.write_to_file(filenames[0])
-        self.config_manager_data.rename_config_file(filenames[0])
-        self.check_existence(filenames, [1, 2, 3], [0])
-
-        # Test with explicit renamed file argument
-        self.config_manager_data.rename_config_file(filenames[1],
-                                                    filenames[0])
-        self.check_existence(filenames, [0, 2, 3], [1])
-
-        # clean up again to be nice
-        for filename in filenames:
-            if os.path.exists(filename):
-                os.remove(filename)
-
-    def test_equality(self):
-        # tests the __eq__ function. Equality is only defined
-        # by equality of the .data element. If data_path or db_filename
-        # are different, but the contents are the same, it's still
-        # considered equal
-        cfd1 = ConfigManagerData(self.data_path, file_name="b10-config.db")
-        cfd2 = ConfigManagerData(self.data_path, file_name="b10-config.db")
-        self.assertEqual(cfd1, cfd2)
-        cfd2.data_path = "some/unknown/path"
-        self.assertEqual(cfd1, cfd2)
-        cfd2.db_filename = "bad_file.name"
-        self.assertEqual(cfd1, cfd2)
-        cfd2.data['test'] = { 'a': [ 1, 2, 3]}
-        self.assertNotEqual(cfd1, cfd2)
-
-
-class TestConfigManager(unittest.TestCase):
-
-    def setUp(self):
-        self.data_path = os.environ['CONFIG_TESTDATA_PATH']
-        self.writable_data_path = os.environ['CONFIG_WR_TESTDATA_PATH']
-        self.fake_session = FakeModuleCCSession()
-        self.cm = ConfigManager(self.writable_data_path,
-                                database_filename="b10-config.db",
-                                session=self.fake_session)
-        self.name = "TestModule"
-        self.spec = isc.config.module_spec_from_file(self.data_path + os.sep + "/spec2.spec")
-
-    def test_paths(self):
-        """
-        Test data_path and database filename is passed through to
-        underlying ConfigManagerData.
-        """
-        cm = ConfigManager("datapath", "filename", self.fake_session)
-        self.assertEqual("datapath" + os.sep + "filename",
-                         cm.config.db_filename)
-        # It should preserve it while reading
-        cm.read_config()
-        self.assertEqual("datapath" + os.sep + "filename",
-                         cm.config.db_filename)
-
-    def test_init(self):
-        self.assertEqual(self.cm.module_specs, {})
-        self.assertEqual(self.cm.data_path, self.writable_data_path)
-        self.assertIsNotNone(self.cm.config)
-        self.assertTrue(self.fake_session.has_subscription("ConfigManager"))
-        self.assertTrue(self.fake_session.has_subscription("Init", "ConfigManager"))
-        self.assertFalse(self.cm.running)
-
-    def test_notify_b10_init(self):
-        self.cm.notify_b10_init()
-        msg = self.fake_session.get_message("Init", None)
-        self.assertTrue(msg)
-        # this one is actually wrong, but 'current status quo'
-        self.assertEqual(msg, {"running": "ConfigManager"})
-
-    def test_set_module_spec(self):
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
-
-    def test_remove_module_spec(self):
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.remove_module_spec(module_spec.get_module_name())
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
-
-    def test_add_remove_virtual_module(self):
-        module_spec = isc.config.module_spec.module_spec_from_file(
-            self.data_path + os.sep + "spec1.spec")
-        check_func = lambda: True
-        # Make sure it's not in there before
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
-        # Add it there
-        self.cm.set_virtual_module(module_spec, check_func)
-        # Check it's in there
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.assertEqual(self.cm.module_specs[module_spec.get_module_name()],
-                      module_spec)
-        self.assertEqual(self.cm.virtual_modules[module_spec.get_module_name()],
-                      check_func)
-        # Remove it again
-        self.cm.remove_module_spec(module_spec.get_module_name())
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.assertNotIn(module_spec.get_module_name(), self.cm.virtual_modules)
-
-    def test_get_module_spec(self):
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        module_spec2 = self.cm.get_module_spec(module_spec.get_module_name())
-        self.assertEqual(module_spec.get_full_spec(), module_spec2)
-
-        self.assertEqual({}, self.cm.get_module_spec("nosuchmodule"))
-
-    def test_get_config_spec(self):
-        config_spec = self.cm.get_config_spec()
-        self.assertEqual(config_spec, {})
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        config_spec = self.cm.get_config_spec()
-        self.assertEqual(config_spec, { 'Spec1': None })
-        self.cm.remove_module_spec('Spec1')
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        config_spec = self.cm.get_config_spec()
-        self.assertEqual(config_spec['Spec2'], module_spec.get_config_spec())
-        config_spec = self.cm.get_config_spec('Spec2')
-        self.assertEqual(config_spec['Spec2'], module_spec.get_config_spec())
-
-
-    def test_get_commands_spec(self):
-        commands_spec = self.cm.get_commands_spec()
-        self.assertEqual(commands_spec, {})
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        commands_spec = self.cm.get_commands_spec()
-        self.assertEqual(commands_spec, { 'Spec1': None })
-        self.cm.remove_module_spec('Spec1')
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        commands_spec = self.cm.get_commands_spec()
-        self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec())
-        commands_spec = self.cm.get_commands_spec('Spec2')
-        self.assertEqual(commands_spec['Spec2'], module_spec.get_commands_spec())
-
-    def test_get_statistics_spec(self):
-        statistics_spec = self.cm.get_statistics_spec()
-        self.assertEqual(statistics_spec, {})
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        statistics_spec = self.cm.get_statistics_spec()
-        self.assertEqual(statistics_spec, { 'Spec1': None })
-        self.cm.remove_module_spec('Spec1')
-        module_spec = isc.config.module_spec.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
-        self.assertNotIn(module_spec.get_module_name(), self.cm.module_specs)
-        self.cm.set_module_spec(module_spec)
-        self.assertIn(module_spec.get_module_name(), self.cm.module_specs)
-        statistics_spec = self.cm.get_statistics_spec()
-        self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec())
-        statistics_spec = self.cm.get_statistics_spec('Spec2')
-        self.assertEqual(statistics_spec['Spec2'], module_spec.get_statistics_spec())
-
-    def test_read_config(self):
-        self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
-        self.cm.data_path = "/no_such_path"
-        self.cm.read_config()
-        self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
-
-    def test_write_config(self):
-        # tested in ConfigManagerData tests
-        pass
-
-    def _handle_msg_helper(self, msg, expected_answer):
-        answer = self.cm.handle_msg(msg)
-        self.assertEqual(expected_answer, answer)
-
-    def test_handle_msg_basic_commands(self):
-        # Some basic commands, where not much interaction happens, just
-        # check the result
-        self._handle_msg_helper({},
-            { 'result': [ 1, 'Unknown message format: {}']})
-        self._handle_msg_helper("",
-            { 'result': [ 1, 'Unknown message format: ']})
-        self._handle_msg_helper({ "command": [ "badcommand" ] },
-            { 'result': [ 1, "Unknown command: badcommand"]})
-        self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
-                                { 'result': [ 0, {} ]})
-        self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
-                                { 'result': [ 0, {} ]})
-        self._handle_msg_helper({ "command": [ "get_module_spec" ] },
-                                { 'result': [ 0, {} ]})
-        self._handle_msg_helper({ "command": [ "get_module_spec",
-                                               { "module_name": "Spec2" } ] },
-                                { 'result': [ 0, {} ]})
-        self._handle_msg_helper({ "command": [ "get_module_spec", 1 ] },
-                                {'result': [1, 'Bad get_module_spec command, '+
-                                               'argument not a dict']})
-        self._handle_msg_helper({ "command": [ "get_module_spec", { } ] },
-                                {'result': [1, 'Bad module_name in '+
-                                               'get_module_spec command']})
-        self._handle_msg_helper({ "command": [ "get_config" ] },
-                                { 'result': [ 0, { 'version':
-                                    config_data.BIND10_CONFIG_DATA_VERSION }]})
-        self._handle_msg_helper({ "command": [ "get_config",
-                                    { "module_name": "nosuchmodule" } ] },
-                                {'result': [0, { 'version':
-                                    config_data.BIND10_CONFIG_DATA_VERSION }]})
-        self._handle_msg_helper({ "command": [ "get_config", 1 ] },
-                                {'result': [1, 'Bad get_config command, '+
-                                               'argument not a dict']})
-        self._handle_msg_helper({ "command": [ "get_config", { } ] },
-                                {'result': [1, 'Bad module_name in '+
-                                               'get_config command']})
-        self._handle_msg_helper({ "command": [ "set_config" ] },
-                                {'result': [1, 'Wrong number of arguments']})
-        self._handle_msg_helper({ "command": [ "set_config", [{}]] },
-                                {'result': [0]})
-
-        self.assertEqual(len(self.fake_session.message_queue), 0)
-
-    def test_handle_msg_module_and_stats_commands(self):
-        self._handle_msg_helper({ "command":
-                                  ["module_spec", self.spec.get_full_spec()]
-                                },
-                                {'result': [0]})
-        # There should be a message on the queue about the 'new' Spec2 module
-        # from ConfigManager to Cmdctl, containing its name and full
-        # specification
-        self.assertEqual(ccsession.create_command(
-                            ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
-                            [ self.spec.get_module_name(),
-                              self.spec.get_full_spec()]),
-                         self.fake_session.get_message("Cmdctl", None))
-
-        self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
-                                {'result': [1, 'Error in data definition: no '+
-                                               'module_name in module_spec']})
-
-        self._handle_msg_helper({ "command": [ "get_module_spec" ] },
-                                { 'result': [ 0, { self.spec.get_module_name():
-                                                 self.spec.get_full_spec() } ]})
-        self._handle_msg_helper({ "command": [ "get_module_spec",
-                                               { "module_name" : "Spec2" } ] },
-                                { 'result': [ 0, self.spec.get_full_spec() ] })
-        self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
-                                { 'result': [ 0, { self.spec.get_module_name():
-                                              self.spec.get_commands_spec()}]})
-        self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
-                                { 'result': [ 0, { self.spec.get_module_name():
-                                             self.spec.get_statistics_spec()}]})
-
-
-    def __test_handle_msg_update_config_helper(self, new_config):
-        # Helper function for the common pattern in
-        # test_handle_msg_update_config; send 'set config', check for
-        # update message, check if config has indeed been updated
-
-        my_ok_answer = { 'result': [ 0 ] }
-        # Send the 'ok' that cfgmgr expects back to the fake queue first
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-
-        config_version = config_data.BIND10_CONFIG_DATA_VERSION
-        self._handle_msg_helper({ "command": [ "set_config",
-                                               [ { "version": config_version,
-                                                 self.name: new_config } ] ] },
-                                my_ok_answer)
-
-        # The cfgmgr should have eaten the ok message, and sent out an update
-        # message
-        self.assertEqual(len(self.fake_session.message_queue), 1)
-        self.assertEqual({'command': [ 'config_update', new_config]},
-                         self.fake_session.get_message(self.name, None))
-
-        # Config should have been updated
-        self.assertEqual(self.cm.config.data, {self.name: new_config,
-                            'version': config_version})
-
-        # and the queue should now be empty again
-        self.assertEqual(len(self.fake_session.message_queue), 0)
-
-    def test_handle_msg_update_config(self):
-        # Update the configuration and check results a few times
-        # only work the first time
-        self.__test_handle_msg_update_config_helper({ "test": 123 })
-
-        self.__test_handle_msg_update_config_helper({ "test": 124 })
-
-        self.__test_handle_msg_update_config_helper({ "test": 125 })
-
-        self.__test_handle_msg_update_config_helper({ "test": 126 })
-
-        # Now send an error result (i.e. config not accepted)
-        my_bad_answer = { 'result': [1, "bad config"] }
-        self.fake_session.group_sendmsg(my_bad_answer, "ConfigManager")
-        self._handle_msg_helper({ "command": [ "set_config",
-                                               [self.name, { "test": 127 }] ] },
-                                my_bad_answer )
-        self.assertEqual(len(self.fake_session.message_queue), 1)
-        self.assertEqual({'command': [ 'config_update', {'test': 127}]},
-                         self.fake_session.get_message(self.name, None))
-        # Config should not be updated due to the error
-        self.cm.read_config()
-        self.assertEqual(self.cm.config.data, { self.name: {'test': 126},
-                            'version': config_data.BIND10_CONFIG_DATA_VERSION})
-
-        self.assertEqual(len(self.fake_session.message_queue), 0)
-
-        self.fake_session.group_sendmsg(None, 'ConfigManager')
-        self._handle_msg_helper({ "command": [ "set_config", [ ] ] },
-                                {'result': [1, 'Wrong number of arguments']} )
-        self._handle_msg_helper({ "command": [ "set_config",
-                                               [ self.name, { "test": 128 }]]},
-                                { 'result': [1, 'No answer message '+
-                                                'from TestModule']} )
-
-        # This command should leave a message to the TestModule to update its
-        # configuration (since the TestModule did not eat it)
-        self.assertEqual(len(self.fake_session.message_queue), 1)
-        self.assertEqual(
-            ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
-                                     { "test": 128 }),
-            self.fake_session.get_message("TestModule", None))
-
-        # Make sure queue is empty now
-        self.assertEqual(len(self.fake_session.message_queue), 0)
-
-        # Shutdown should result in 'ok' answer
-        self._handle_msg_helper({ "command":
-                                  ["shutdown"]
-                                },
-                                {'result': [0]})
-
-    def test_stopping_message(self):
-        # Update the system by announcing this module
-        self._handle_msg_helper({ "command":
-                                  ["module_spec", self.spec.get_full_spec()]
-                                },
-                                {'result': [0]})
-
-        # This causes a update to be sent from the ConfigManager to the CmdCtl
-        # channel, containing the new module's name and full specification
-        self.assertEqual(ccsession.create_command(
-                            ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
-                            [ self.spec.get_module_name(),
-                              self.spec.get_full_spec()]),
-                         self.fake_session.get_message("Cmdctl", None))
-
-        # A stopping message should get no response, but should cause another
-        # message to be sent, if it is a known module
-        self._handle_msg_helper({ "command": [ "stopping",
-                                               { "module_name": "Spec2"}] },
-                                None)
-        self.assertEqual(len(self.fake_session.message_queue), 1)
-        self.assertEqual({'command': [ 'module_specification_update',
-                                       ['Spec2', None] ] },
-                         self.fake_session.get_message("Cmdctl", None))
-
-        # but if the 'stopping' module is either unknown or not running,
-        # no follow-up message should be sent
-        self._handle_msg_helper({ "command":
-                                  [ "stopping",
-                                    { "module_name": "NoSuchModule" } ] },
-                                None)
-        self.assertEqual(len(self.fake_session.message_queue), 0)
-
-    def test_set_config_virtual(self):
-        """Test that if the module is virtual, we don't send it over the
-           message bus, but call the checking function.
-           """
-        # We run the same three times, with different return values
-        def single_test(value, returnFunc, expectedResult):
-            # Because closures can't assign to closed-in variables, we pass
-            # it through self
-            self.called_with = None
-            def check_test(new_data):
-                self.called_with = new_data
-                return returnFunc()
-
-            # Register our virtual module
-            self.cm.set_virtual_module(self.spec, check_test)
-            # The fake session will throw now if it tries to read a response.
-            # Handy, we don't need to find a complicated way to check for it.
-            result = self.cm.handle_msg(ccsession.create_command(
-                        ccsession.COMMAND_SET_CONFIG,
-                        [self.spec.get_module_name(), { "item1": value }]))
-            # Check the correct result is passed and our function was called
-            # With correct data
-            self.assertEqual(self.called_with['item1'], value)
-            self.assertEqual(result, {'result': expectedResult})
-            if expectedResult[0] == 0:
-                # Check it provided the correct notification
-                self.assertEqual(len(self.fake_session.message_queue), 1)
-                self.assertEqual({'command': [ 'config_update',
-                                 {'item1': value}]},
-                                 self.fake_session.get_message('Spec2', None))
-                # and the queue should now be empty again
-                self.assertEqual(len(self.fake_session.message_queue), 0)
-            else:
-                # It shouldn't send anything on error
-                self.assertEqual(len(self.fake_session.message_queue), 0)
-
-        # Success
-        single_test(5, lambda: None, [0])
-        # Graceful error
-        single_test(6, lambda: "Just error", [1, "Just error"])
-        # Exception from the checker
-        def raiser():
-            raise Exception("Just exception")
-        single_test(7, raiser, [1, "Exception: Just exception"])
-
-    def test_set_config_all(self):
-        my_ok_answer = { 'result': [ 0 ] }
-
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION},
-                         self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 123 }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value1": 123 }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 124 }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value1": 124 }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value2": True }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value1": 124,
-                                    "value2": True
-                                  }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1, 2, 3 ] }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value1": 124,
-                                    "value2": True,
-                                    "value3": [ 1, 2, 3 ]
-                                  }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value2": False }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value1": 124,
-                                    "value2": False,
-                                    "value3": [ 1, 2, 3 ]
-                                  }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value1": None }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value2": False,
-                                    "value3": [ 1, 2, 3 ]
-                                  }
-                         }, self.cm.config.data)
-
-        self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
-        self.cm.handle_msg(ccsession.create_command(
-            ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1 ] }]))
-        self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
-                          "test": { "value2": False,
-                                    "value3": [ 1 ]
-                                  }
-                         }, self.cm.config.data)
-
-
-    def test_run(self):
-        self.fake_session.group_sendmsg({ "command": [ "get_commands_spec" ] }, "ConfigManager")
-        self.fake_session.group_sendmsg({ "command": [ "get_statistics_spec" ] }, "ConfigManager")
-        self.fake_session.group_sendmsg({ "command": [ "stopping", { "module_name": "FooModule" } ] }, "ConfigManager")
-        self.fake_session.group_sendmsg({ "command": [ "shutdown" ] }, "ConfigManager")
-        self.assertEqual(len(self.fake_session.message_queue), 4)
-        self.cm.run()
-        # All commands should have been read out by run()
-        # Three of the commands should have been responded to, so the queue
-        # should now contain three answers
-        self.assertEqual(len(self.fake_session.message_queue), 3)
-
-
-if __name__ == '__main__':
-    if not 'CONFIG_TESTDATA_PATH' in os.environ or not 'CONFIG_WR_TESTDATA_PATH' in os.environ:
-        print("You need to set the environment variable CONFIG_TESTDATA_PATH and CONFIG_WR_TESTDATA_PATH to point to the directory containing the test data files")
-        exit(1)
-    isc.log.init("unittests")
-    isc.log.resetUnitTestRootLogger()
-    unittest.main()
index 14f10a2aa74683921db8580caf24fe90e3955d9b..5a37d10cf74446e5c656ccd7335595d0a931c495 100644 (file)
@@ -1,6 +1,6 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS_GEN = log_console.py
-PYTESTS_NOGEN = log_test.py
+PYTESTS_NOGEN = 
 noinst_SCRIPTS = $(PYTESTS_GEN)
 EXTRA_DIST = console.out check_output.sh $(PYTESTS_NOGEN)
 
diff --git a/src/lib/python/isc/log/tests/log_test.py b/src/lib/python/isc/log/tests/log_test.py
deleted file mode 100644 (file)
index 2059caa..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-# Copyright (C) 2011  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.
-
-# This tests it can be loaded, nothing more yet
-import isc.log
-import unittest
-import json
-import sys
-import bind10_config
-from isc.config.ccsession import path_search
-
-class LogDict(unittest.TestCase):
-    def setUp(self):
-        # We work on a test dictionary now.
-        isc.log.set_test_dictionary(True)
-    def tearDown(self):
-        # Return to the global dictionary
-        isc.log.set_test_dictionary(False)
-
-    def test_load_msgs(self):
-        # Try loading a message and see it's there, but nothing more
-        self.assertEqual(isc.log.create_message("ID", "Text"), "ID")
-        self.assertEqual(isc.log.get_message("ID"), "Text")
-        self.assertEqual(isc.log.get_message("no-ID"), None)
-
-class Manager(unittest.TestCase):
-    def tearDown(self):
-        isc.log.reset()
-
-    def test_init_debug(self):
-        # We try calling it now only, as we don't have any other functions
-        # to check the outcome by it. Once we add the logger class, we may
-        # check more.
-        isc.log.init("root", "DEBUG", 50, None)
-
-    def test_init_defaults(self):
-        # We try calling it now only, as we don't have any other functions
-        # to check the outcome by it. Once we add the logger class, we may
-        # check more.
-        isc.log.init("root")
-
-    def test_init_notfound(self):
-        # This should not throw, because the C++ one doesn't. Should we really
-        # ignore errors like missing file?
-        isc.log.init("root", "INFO", 0, "/no/such/file");
-
-    def test_init_keywords(self):
-        isc.log.init(name="root", severity="DEBUG", debuglevel=50, file=None,
-                     buffer=True)
-        # unknown keyword
-        self.assertRaises(TypeError, isc.log.init, name="root", foo="bar")
-        # Replace the values for each keyword by a wrong type, one by one
-        self.assertRaises(TypeError, isc.log.init, name=1,
-                          severity="DEBUG", debuglevel=50, file=None,
-                          buffer=True)
-        self.assertRaises(TypeError, isc.log.init, name="root",
-                          severity=2, debuglevel=50, file=None,
-                          buffer=True)
-        self.assertRaises(TypeError, isc.log.init, name="root",
-                          severity="DEBUG", debuglevel="50", file=None,
-                          buffer=True)
-        self.assertRaises(TypeError, isc.log.init, name="root",
-                          severity="DEBUG", debuglevel=50, file=1,
-                          buffer=True)
-        self.assertRaises(TypeError, isc.log.init, name="root",
-                          severity="DEBUG", debuglevel=50, file=None,
-                          buffer=None)
-
-    def test_log_config_update(self):
-        log_spec = json.dumps(isc.config.module_spec_from_file(path_search('logging.spec', bind10_config.PLUGIN_PATHS)).get_full_spec())
-
-        self.assertRaises(TypeError, isc.log.log_config_update)
-        self.assertRaises(TypeError, isc.log.log_config_update, 1)
-        self.assertRaises(TypeError, isc.log.log_config_update, 1, 1)
-        self.assertRaises(TypeError, isc.log.log_config_update, 1, 1, 1)
-
-        self.assertRaises(TypeError, isc.log.log_config_update, 1, log_spec)
-        self.assertRaises(TypeError, isc.log.log_config_update, [], log_spec)
-        self.assertRaises(TypeError, isc.log.log_config_update, "foo", log_spec)
-        self.assertRaises(TypeError, isc.log.log_config_update, "{ '", log_spec)
-
-        # empty should pass
-        isc.log.log_config_update("{}", log_spec)
-
-        # bad spec
-        self.assertRaises(TypeError, isc.log.log_config_update, "{}", json.dumps({"foo": "bar"}))
-
-        # Try a correct one
-        log_conf = json.dumps({"loggers":
-                                [{"name": "b10-xfrout", "output_options":
-                                    [{"output": "/tmp/bind10.log",
-                                       "destination": "file",
-                                       "flush": True}]}]})
-        isc.log.log_config_update(log_conf, log_spec)
-
-class Logger(unittest.TestCase):
-    def tearDown(self):
-        isc.log.reset()
-
-    def setUp(self):
-        isc.log.init("root", "DEBUG", 50)
-        self.sevs = ['INFO', 'WARN', 'ERROR', 'FATAL']
-        self.TEST_MSG = isc.log.create_message('TEST_MESSAGE', '%1')
-
-    # Checks defaults of the logger
-    def defaults(self, logger):
-        self.assertEqual(logger.get_effective_severity(), "DEBUG")
-        self.assertEqual(logger.get_effective_debug_level(), 50)
-
-    def test_default_severity(self):
-        logger = isc.log.Logger("child")
-        self.defaults(logger)
-
-    # Try changing the severities little bit
-    def test_severity(self):
-        logger = isc.log.Logger("child")
-        logger.set_severity('DEBUG', 25)
-        self.assertEqual(logger.get_effective_severity(), "DEBUG")
-        self.assertEqual(logger.get_effective_debug_level(), 25)
-        for sev in self.sevs:
-            logger.set_severity(sev)
-            self.assertEqual(logger.get_effective_severity(), sev)
-            self.assertEqual(logger.get_effective_debug_level(), 0)
-        # Return to default
-        logger.set_severity(None)
-        self.defaults(logger)
-
-    def test_enabled(self):
-        logger = isc.log.Logger("child")
-        self.sevs.insert(0, 'DEBUG')
-        methods = {
-            'DEBUG': logger.is_debug_enabled,
-            'INFO': logger.is_info_enabled,
-            'WARN': logger.is_warn_enabled,
-            'ERROR': logger.is_error_enabled,
-            'FATAL': logger.is_fatal_enabled
-        }
-        for sev in self.sevs:
-            logger.set_severity(sev)
-            enabled = False
-            for tested in self.sevs:
-                if tested == sev:
-                    enabled = True
-                self.assertEqual(methods[tested](), enabled)
-        logger.set_severity('DEBUG', 50)
-        self.assertTrue(logger.is_debug_enabled())
-        self.assertTrue(logger.is_debug_enabled(0))
-        self.assertTrue(logger.is_debug_enabled(50))
-        self.assertFalse(logger.is_debug_enabled(99))
-
-    def test_invalid_params(self):
-        """
-           Tests invalid arguments for logging functions. The output is tested
-           in check_output.sh.
-        """
-        logger = isc.log.Logger("child")
-        methods = [
-            logger.info,
-            logger.warn,
-            logger.error,
-            logger.fatal
-        ]
-        for meth in methods:
-            # Not enough arguments
-            self.assertRaises(TypeError, meth)
-            # Bad type
-            self.assertRaises(TypeError, meth, 1)
-        # Too few arguments
-        self.assertRaises(TypeError, logger.debug, 42)
-        self.assertRaises(TypeError, logger.debug)
-        # Bad type
-        self.assertRaises(TypeError, logger.debug, "42", "hello")
-
-    def test_dbglevel_constants(self):
-        """
-            Just check a constant to make sure it is defined and is the
-            correct value.  (The constant chosen has a non-zero value to
-            ensure that the code has both define the constant and set its
-            value correctly.)
-        """
-        logger = isc.log.Logger("child")
-        self.assertEqual(logger.DBGLVL_COMMAND, 10)
-
-    def test_param_reference(self):
-        """
-        Check whether passing a parameter to a logger causes a reference leak.
-        """
-        class LogParam:
-            def __str__(self):
-                return 'LogParam'
-        logger = isc.log.Logger("child")
-        param = LogParam()
-        orig_msgrefcnt = sys.getrefcount(param)
-        orig_idrefcnt = sys.getrefcount(self.TEST_MSG)
-        logger.info(self.TEST_MSG, param);
-        self.assertEqual(sys.getrefcount(self.TEST_MSG), orig_idrefcnt)
-        self.assertEqual(sys.getrefcount(param), orig_msgrefcnt)
-
-        # intentionally pass an invalid type for debug level.  It will
-        # result in TypeError.  The passed object still shouldn't leak a
-        # reference.
-        self.assertRaises(TypeError, logger.debug, param, self.TEST_MSG, param)
-        self.assertEqual(sys.getrefcount(param), orig_msgrefcnt)
-
-    def test_bad_parameter(self):
-        # a log parameter cannot be converted to a string object.
-        class LogParam:
-            def __str__(self):
-                raise ValueError("LogParam can't be converted to string")
-        logger = isc.log.Logger("child")
-        self.assertRaises(ValueError, logger.info, self.TEST_MSG, LogParam())
-
-if __name__ == '__main__':
-    unittest.main()