Browse Source

Merge branch 'master' into trac2053

Mukund Sivaraman 13 years ago
parent
commit
1fc2b06b57

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+452.	[func]*		muks
+	b10-showtech: An initial implementation of the b10-showtech tool
+	is now available. It gathers and outputs system information which
+	can be used by future tech support staff.
+	(Trac #2062, git 144e80212746f8d55e6a59edcf689fec9f32ae95)
+
 451.	[bug]		muks, jinmei
 451.	[bug]		muks, jinmei
 	libdatasrc: the database-based data source now correctly returns
 	libdatasrc: the database-based data source now correctly returns
 	glue records on (not under) a zone cut, such as in the case where
 	glue records on (not under) a zone cut, such as in the case where

+ 4 - 0
configure.ac

@@ -1027,6 +1027,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/dhcp4/tests/Makefile
                  src/bin/dhcp4/tests/Makefile
                  src/bin/resolver/Makefile
                  src/bin/resolver/Makefile
                  src/bin/resolver/tests/Makefile
                  src/bin/resolver/tests/Makefile
+                 src/bin/showtech/Makefile
                  src/bin/sockcreator/Makefile
                  src/bin/sockcreator/Makefile
                  src/bin/sockcreator/tests/Makefile
                  src/bin/sockcreator/tests/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/Makefile
@@ -1082,6 +1083,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/xfrin/tests/Makefile
                  src/lib/python/isc/xfrin/tests/Makefile
                  src/lib/python/isc/server_common/Makefile
                  src/lib/python/isc/server_common/Makefile
                  src/lib/python/isc/server_common/tests/Makefile
                  src/lib/python/isc/server_common/tests/Makefile
+                 src/lib/python/isc/sysinfo/Makefile
+                 src/lib/python/isc/sysinfo/tests/Makefile
                  src/lib/config/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
                  src/lib/config/tests/testdata/Makefile
@@ -1159,6 +1162,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/zonemgr.spec.pre
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/run_b10-zonemgr.sh
            src/bin/zonemgr/run_b10-zonemgr.sh
+           src/bin/showtech/showtech.py
            src/bin/stats/stats.py
            src/bin/stats/stats.py
            src/bin/stats/stats_httpd.py
            src/bin/stats/stats_httpd.py
            src/bin/bind10/bind10_src.py
            src/bin/bind10/bind10_src.py

+ 14 - 17
doc/guide/bind10-guide.xml

@@ -92,7 +92,7 @@
       <title>Supported Platforms</title>
       <title>Supported Platforms</title>
       <para>
       <para>
   BIND 10 builds have been tested on (in no particular order)
   BIND 10 builds have been tested on (in no particular order)
-  Debian GNU/Linux 5 and unstable, Ubuntu 9.10, NetBSD 5,
+  Debian GNU/Linux 6 and unstable, Ubuntu 9.10, NetBSD 5,
   Solaris 10 and 11, FreeBSD 7 and 8, CentOS Linux 5.3,
   Solaris 10 and 11, FreeBSD 7 and 8, CentOS Linux 5.3,
   MacOS 10.6 and 10.7, and OpenBSD 5.1.
   MacOS 10.6 and 10.7, and OpenBSD 5.1.
 
 
@@ -105,7 +105,17 @@
     </section>
     </section>
 
 
     <section id="required-software">
     <section id="required-software">
-      <title>Required Software</title>
+      <title>Required Software at Run-time</title>
+
+      <para>
+        Running BIND 10 uses various extra software which may
+        not be provided in some operating systems' default
+        installations nor standard packages collections. You may
+        need to install this required software separately.
+        (For the build requirements, also see
+        <xref linkend="build-requirements"/>.)
+      </para>
+
       <para>
       <para>
         BIND 10 requires at least Python 3.1
         BIND 10 requires at least Python 3.1
         (<ulink url="http://www.python.org/"/>).
         (<ulink url="http://www.python.org/"/>).
@@ -140,15 +150,6 @@
         Python modules need to be built for the corresponding Python 3.
         Python modules need to be built for the corresponding Python 3.
       </para>
       </para>
 <!-- TODO: this will change ... -->
 <!-- TODO: this will change ... -->
-
-      <note>
-        <para>
-          Some operating systems do not provide these dependencies
-          in their default installation nor standard packages
-          collections.
-          You may need to install them separately.
-        </para>
-      </note>
     </section>
     </section>
 
 
     <section id="starting_stopping">
     <section id="starting_stopping">
@@ -1058,9 +1059,6 @@ address, but the usual ones don't." mean? -->
         specifications and all current settings to the
         specifications and all current settings to the
         <command>bindctl</command> client (via
         <command>bindctl</command> client (via
         <command>b10-cmdctl</command>).
         <command>b10-cmdctl</command>).
-      </para>
-
-      <para>
         <command>b10-cfgmgr</command> relays configurations received
         <command>b10-cfgmgr</command> relays configurations received
         from <command>b10-cmdctl</command> to the appropriate modules.
         from <command>b10-cmdctl</command> to the appropriate modules.
       </para>
       </para>
@@ -1079,7 +1077,7 @@ config changes are actually commands to cfgmgr
       <para>
       <para>
         The stored configuration file is at
         The stored configuration file is at
         <filename>/usr/local/var/bind10-devel/b10-config.db</filename>.
         <filename>/usr/local/var/bind10-devel/b10-config.db</filename>.
-        (The full path is what was defined at build configure time for
+        (The directory is what was defined at build configure time for
         <option>--localstatedir</option>.
         <option>--localstatedir</option>.
         The default is <filename>/usr/local/var/</filename>.)
         The default is <filename>/usr/local/var/</filename>.)
         The format is loosely based on JSON and is directly parseable
         The format is loosely based on JSON and is directly parseable
@@ -1197,7 +1195,7 @@ but you might wanna check with likun
 
 
 <!-- TODO
 <!-- TODO
 openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
 openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
-but that is a single file, maybethis should go back to that format?
+but that is a single file, maybe this should go back to that format?
 -->
 -->
 
 
 <!--
 <!--
@@ -1264,7 +1262,6 @@ shutdown
 <!--
 <!--
 TODO
 TODO
 (12:21:30) jinmei: I'd like to have sample session using a command line www client such as wget
 (12:21:30) jinmei: I'd like to have sample session using a command line www client such as wget
-(12:21:33) jinmei: btw
 -->
 -->
 
 
   </chapter>
   </chapter>

+ 2 - 1
src/bin/Makefile.am

@@ -1,4 +1,5 @@
 SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
 SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
-	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 dbutil
+	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 \
+	dbutil showtech
 
 
 check-recursive: all-recursive
 check-recursive: all-recursive

+ 1 - 1
src/bin/cmdctl/cmdctl.py.in

@@ -541,7 +541,7 @@ class SecureHTTPServer(socketserver_mixin.NoPollMixIn,
                                       ssl_version = ssl.PROTOCOL_SSLv23)
                                       ssl_version = ssl.PROTOCOL_SSLv23)
             return ssl_sock
             return ssl_sock
         except (ssl.SSLError, CmdctlException) as err :
         except (ssl.SSLError, CmdctlException) as err :
-            logger.info(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
+            logger.error(CMDCTL_SSL_SETUP_FAILURE_USER_DENIED, err)
             self.close_request(sock)
             self.close_request(sock)
             # raise socket error to finish the request
             # raise socket error to finish the request
             raise socket.error
             raise socket.error

+ 2 - 0
src/bin/showtech/.gitignore

@@ -0,0 +1,2 @@
+/b10-showtech
+/showtech.py

+ 28 - 0
src/bin/showtech/Makefile.am

@@ -0,0 +1,28 @@
+bin_SCRIPTS = b10-showtech
+
+CLEANFILES = b10-showtech showtech.pyc
+
+# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
+b10-showtech: showtech.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" showtech.py >$@
+	chmod a+x $@
+
+MAN1_FILES = \
+	b10-showtech.xml
+
+man_MANS = \
+	$(MAN1_FILES:.xml=.1)
+
+if ENABLE_MAN
+
+.xml.1:
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
+
+endif
+
+EXTRA_DIST = $(man_MANS) $(MAN1_FILES)
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)

+ 66 - 0
src/bin/showtech/b10-showtech.1

@@ -0,0 +1,66 @@
+'\" t
+.\"     Title: b10-showtech
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
+.\"      Date: June 26, 2012
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-SHOWTECH" "1" "June 26, 2012" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-showtech \- BIND 10 system information display tool
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-showtech\fR\ 'u
+\fBb10\-showtech\fR
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-showtech\fR
+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\&.
+.SH "ARGUMENTS"
+.PP
+\-h
+.RS 4
+Displays usage instructions\&.
+.RE
+.PP
+\-o \fIoutput\-file\fR
+.RS 4
+If an output file is specified, the output of
+\fBb10\-showtech\fR
+is written to this file\&. By default, the output is written to standard output\&.
+.RE
+.SH "SEE ALSO"
+.PP
+
+\fBbind10\fR(8),
+BIND 10 Guide\&.
+.SH "HISTORY"
+.PP
+The
+\fBb10\-showtech\fR
+daemon was initially implemented by ISC staff in June, 2012\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2012 Internet Systems Consortium, Inc. ("ISC")
+.br

+ 106 - 0
src/bin/showtech/b10-showtech.xml

@@ -0,0 +1,106 @@
+<!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>b10-showtech</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-showtech</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>b10-showtech</command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>
+      The <command>b10-showtech</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>b10-showtech</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>b10-showtech</command> daemon was initially
+      implemented by ISC staff in June, 2012.
+    </para>
+  </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->

+ 127 - 0
src/bin/showtech/showtech.py.in

@@ -0,0 +1,127 @@
+#!@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.
+
+"""
+BIND 10 showtech program.
+
+"""
+
+import sys; sys.path.append ('@@PYTHONPATH@@')
+import getopt
+import isc.util.process
+from isc.sysinfo import *
+
+isc.util.process.rename()
+
+def usage():
+    print("Usage: %s [-h] [-o <output-file>]" % sys.argv[0], \
+              file=sys.stderr)
+    exit(1)
+
+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('BIND 10 ShowTech tool\n')
+    f.write('=====================\n')
+
+    f.write('\nCPU\n');
+    f.write(' + Number of processors: %d\n' % (s.get_num_processors()))
+    f.write(' + Endianness: %s\n' % (s.get_endianness()))
+
+    f.write('\nPlatform\n');
+    f.write(' + Operating system: %s\n' % (s.get_platform_name()))
+    f.write(' + Distribution: %s\n' % (s.get_platform_distro()))
+    f.write(' + Kernel version: %s\n' % (s.get_platform_version()))
+
+    f.write(' + SMP kernel: ')
+    if s.get_platform_is_smp():
+        f.write('yes')
+    else:
+        f.write('no')
+    f.write('\n')
+
+    f.write(' + Machine name: %s\n' % (s.get_platform_machine()))
+    f.write(' + Hostname: %s\n' % (s.get_platform_hostname()))
+    f.write(' + Uptime: %d seconds\n' % (s.get_uptime()))
+
+    l = s.get_loadavg()
+    f.write(' + Loadavg: %f %f %f\n' % (l[0], l[1], l[2]))
+
+    f.write('\nMemory\n');
+    f.write(' + Total: %d bytes\n' % (s.get_mem_total()))
+    f.write(' + Free: %d bytes\n' % (s.get_mem_free()))
+    f.write(' + Cached: %d bytes\n' % (s.get_mem_cached()))
+    f.write(' + Buffers: %d bytes\n' % (s.get_mem_buffers()))
+    f.write(' + Swap total: %d bytes\n' % (s.get_mem_swap_total()))
+    f.write(' + 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')
+
+    f.write(s.get_net_interfaces())
+
+    f.write('\nRouting table\n')
+    f.write('~~~~~~~~~~~~~\n\n')
+    f.write(s.get_net_routing_table())
+
+    f.write('\nStatistics\n')
+    f.write('~~~~~~~~~~\n\n')
+    f.write(s.get_net_stats())
+
+    f.write('\nConnections\n')
+    f.write('~~~~~~~~~~~\n\n')
+    f.write(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()

+ 6 - 5
src/lib/config/tests/ccsession_unittests.cc

@@ -33,7 +33,6 @@ using namespace isc::data;
 using namespace isc::config;
 using namespace isc::config;
 using namespace isc::cc;
 using namespace isc::cc;
 using namespace std;
 using namespace std;
-using namespace boost;
 
 
 namespace {
 namespace {
 std::string
 std::string
@@ -52,6 +51,7 @@ protected:
                       root_name(isc::log::getRootLoggerName())
                       root_name(isc::log::getRootLoggerName())
     {
     {
         // upon creation of a ModuleCCSession, the class
         // upon creation of a ModuleCCSession, the class
+
         // sends its specification to the config manager.
         // sends its specification to the config manager.
         // it expects an ok answer back, so everytime we
         // it expects an ok answer back, so everytime we
         // create a ModuleCCSession, we must set an initial
         // create a ModuleCCSession, we must set an initial
@@ -740,8 +740,9 @@ protected:
         registerCommand(const string& recipient)
         registerCommand(const string& recipient)
     {
     {
         return (mccs_.groupRecvMsgAsync(
         return (mccs_.groupRecvMsgAsync(
-            bind(&AsyncReceiveCCSessionTest::callback, this, next_flag_ ++, _1,
-                 _2, _3), false, -1, recipient));
+                    boost::bind(&AsyncReceiveCCSessionTest::callback, this,
+                                next_flag_++, _1, _2, _3), false, -1,
+                    recipient));
     }
     }
     /// \brief Convenience function to queue a request to get a reply
     /// \brief Convenience function to queue a request to get a reply
     ///     message.
     ///     message.
@@ -749,8 +750,8 @@ protected:
         registerReply(int seq)
         registerReply(int seq)
     {
     {
         return (mccs_.groupRecvMsgAsync(
         return (mccs_.groupRecvMsgAsync(
-            bind(&AsyncReceiveCCSessionTest::callback, this, next_flag_ ++, _1,
-                 _2, _3), true, seq));
+                    boost::bind(&AsyncReceiveCCSessionTest::callback, this,
+                                next_flag_++, _1, _2, _3), true, seq));
     }
     }
     /// \brief Check the next called callback was with this flag
     /// \brief Check the next called callback was with this flag
     void called(int flag) {
     void called(int flag) {

+ 3 - 0
src/lib/datasrc/rbtree.h

@@ -1260,6 +1260,9 @@ RBTree<T>::previousNode(RBTreeNodeChain<T>& node_path) const {
             // already, which located the exact node. The rest of the function
             // already, which located the exact node. The rest of the function
             // goes one domain left and returns it for us.
             // goes one domain left and returns it for us.
             break;
             break;
+        default:
+            // This must not happen as Name::compare() never returns NONE.
+            isc_throw(isc::Unexpected, "Name::compare() returned unexpected result");
     }
     }
 
 
     // So, the node_path now contains the path to a node we want previous for.
     // So, the node_path now contains the path to a node we want previous for.

+ 15 - 0
src/lib/dns/labelsequence.cc

@@ -71,6 +71,21 @@ LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
     return (true);
     return (true);
 }
 }
 
 
+NameComparisonResult
+LabelSequence::compare(const LabelSequence& other,
+                       bool case_sensitive) const {
+    if (isAbsolute() ^ other.isAbsolute()) {
+        return (NameComparisonResult(0, 0, NameComparisonResult::NONE));
+    }
+
+    return (name_.compare(other.name_,
+                          first_label_,
+                          other.first_label_,
+                          last_label_,
+                          other.last_label_,
+                          case_sensitive));
+}
+
 void
 void
 LabelSequence::stripLeft(size_t i) {
 LabelSequence::stripLeft(size_t i) {
     if (i >= getLabelCount()) {
     if (i >= getLabelCount()) {

+ 14 - 2
src/lib/dns/labelsequence.h

@@ -88,10 +88,10 @@ public:
     /// \return The length of the data of the label sequence in octets.
     /// \return The length of the data of the label sequence in octets.
     size_t getDataLength() const;
     size_t getDataLength() const;
 
 
-    /// \brief Compares two label sequences.
+    /// \brief Compares two label sequences for equality.
     ///
     ///
     /// Performs a (optionally case-insensitive) comparison between this
     /// Performs a (optionally case-insensitive) comparison between this
-    /// LabelSequence and another LabelSequence.
+    /// LabelSequence and another LabelSequence for equality.
     ///
     ///
     /// \param other The LabelSequence to compare with
     /// \param other The LabelSequence to compare with
     /// \param case_sensitive If true, comparison is case-insensitive
     /// \param case_sensitive If true, comparison is case-insensitive
@@ -99,6 +99,18 @@ public:
     ///         and contain the same data.
     ///         and contain the same data.
     bool equals(const LabelSequence& other, bool case_sensitive = false) const;
     bool equals(const LabelSequence& other, bool case_sensitive = false) const;
 
 
+    /// \brief Compares two label sequences.
+    ///
+    /// Performs a (optionally case-insensitive) comparison between this
+    /// LabelSequence and another LabelSequence.
+    ///
+    /// \param other The LabelSequence to compare with
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return a <code>NameComparisonResult</code> object representing the
+    /// comparison result.
+    NameComparisonResult compare(const LabelSequence& other,
+                                 bool case_sensitive = false) const;
+
     /// \brief Remove labels from the front of this LabelSequence
     /// \brief Remove labels from the front of this LabelSequence
     ///
     ///
     /// \note No actual memory is changed, this operation merely updates the
     /// \note No actual memory is changed, this operation merely updates the

+ 48 - 8
src/lib/dns/name.cc

@@ -435,13 +435,33 @@ Name::toText(bool omit_final_dot) const {
 
 
 NameComparisonResult
 NameComparisonResult
 Name::compare(const Name& other) const {
 Name::compare(const Name& other) const {
+    return (compare(other, 0, 0, labelcount_, other.labelcount_));
+}
+
+NameComparisonResult
+Name::compare(const Name& other,
+              unsigned int first_label,
+              unsigned int first_label_other,
+              unsigned int last_label,
+              unsigned int last_label_other,
+              bool case_sensitive) const {
     // Determine the relative ordering under the DNSSEC order relation of
     // Determine the relative ordering under the DNSSEC order relation of
     // 'this' and 'other', and also determine the hierarchical relationship
     // 'this' and 'other', and also determine the hierarchical relationship
     // of the names.
     // of the names.
 
 
+    if ((first_label > last_label) ||
+        (first_label_other > last_label_other)) {
+        isc_throw(BadValue, "Bad label index ranges were passed");
+    }
+
+    if ((first_label > labelcount_) ||
+        (first_label_other > other.labelcount_)) {
+        isc_throw(BadValue, "Bad first label indices were passed");
+    }
+
     unsigned int nlabels = 0;
     unsigned int nlabels = 0;
-    unsigned int l1 = labelcount_;
-    unsigned int l2 = other.labelcount_;
+    int l1 = last_label - first_label;
+    int l2 = last_label_other - first_label_other;
     int ldiff = (int)l1 - (int)l2;
     int ldiff = (int)l1 - (int)l2;
     unsigned int l = (ldiff < 0) ? l1 : l2;
     unsigned int l = (ldiff < 0) ? l1 : l2;
 
 
@@ -449,8 +469,8 @@ Name::compare(const Name& other) const {
         --l;
         --l;
         --l1;
         --l1;
         --l2;
         --l2;
-        size_t pos1 = offsets_[l1];
-        size_t pos2 = other.offsets_[l2];
+        size_t pos1 = offsets_[l1 + first_label];
+        size_t pos2 = other.offsets_[l2 + first_label_other];
         unsigned int count1 = ndata_[pos1++];
         unsigned int count1 = ndata_[pos1++];
         unsigned int count2 = other.ndata_[pos2++];
         unsigned int count2 = other.ndata_[pos2++];
 
 
@@ -464,19 +484,39 @@ Name::compare(const Name& other) const {
         while (count > 0) {
         while (count > 0) {
             uint8_t label1 = ndata_[pos1];
             uint8_t label1 = ndata_[pos1];
             uint8_t label2 = other.ndata_[pos2];
             uint8_t label2 = other.ndata_[pos2];
+            int chdiff;
+
+            if (case_sensitive) {
+                chdiff = (int)label1 - (int)label2;
+            } else {
+                chdiff = (int)maptolower[label1] - (int)maptolower[label2];
+            }
 
 
-            int chdiff = (int)maptolower[label1] - (int)maptolower[label2];
             if (chdiff != 0) {
             if (chdiff != 0) {
-                return (NameComparisonResult(chdiff, nlabels,
-                                         NameComparisonResult::COMMONANCESTOR));
+                if ((nlabels == 0) &&
+                    ((last_label < labelcount_) ||
+                     (last_label_other < other.labelcount_))) {
+                    return (NameComparisonResult(0, 0,
+                                                 NameComparisonResult::NONE));
+                } else {
+                    return (NameComparisonResult(chdiff, nlabels,
+                                                 NameComparisonResult::COMMONANCESTOR));
+                }
             }
             }
             --count;
             --count;
             ++pos1;
             ++pos1;
             ++pos2;
             ++pos2;
         }
         }
         if (cdiff != 0) {
         if (cdiff != 0) {
+            if ((nlabels == 0) &&
+                ((last_label < labelcount_) ||
+                 (last_label_other < other.labelcount_))) {
+                return (NameComparisonResult(0, 0,
+                                             NameComparisonResult::NONE));
+            } else {
                 return (NameComparisonResult(cdiff, nlabels,
                 return (NameComparisonResult(cdiff, nlabels,
-                                         NameComparisonResult::COMMONANCESTOR));
+                                             NameComparisonResult::COMMONANCESTOR));
+            }
         }
         }
         ++nlabels;
         ++nlabels;
     }
     }

+ 32 - 1
src/lib/dns/name.h

@@ -142,7 +142,8 @@ public:
         SUPERDOMAIN = 0,
         SUPERDOMAIN = 0,
         SUBDOMAIN = 1,
         SUBDOMAIN = 1,
         EQUAL = 2,
         EQUAL = 2,
-        COMMONANCESTOR = 3
+        COMMONANCESTOR = 3,
+        NONE = 4
     };
     };
 
 
     ///
     ///
@@ -404,6 +405,36 @@ public:
     /// comparison result.
     /// comparison result.
     NameComparisonResult compare(const Name& other) const;
     NameComparisonResult compare(const Name& other) const;
 
 
+private:
+    /// \brief Partially compare two <code>Name</code>s.
+    ///
+    /// This method performs a partial comparison of the
+    /// <code>Name</code> and <code>other</code> and returns the result
+    /// in the form of a <code>NameComparisonResult</code> object.
+    ///
+    /// This method can throw the BadValue exception if bad label
+    /// indices are passed.
+    ///
+    /// \param other the right-hand operand to compare against.
+    /// \param first_label the left-most label of <code>Name</code> to
+    /// begin comparing from.
+    /// \param first_label_other the left-most label of
+    /// <code>other</code> to begin comparing from.
+    /// \param last_label the right-most label of <code>Name</code> to
+    /// end comparing at.
+    /// \param last_label_other the right-most label of
+    /// <code>other</code> to end comparing at.
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return a <code>NameComparisonResult</code> object representing the
+    /// comparison result.
+    NameComparisonResult compare(const Name& other,
+                                 unsigned int first_label,
+                                 unsigned int first_label_other,
+                                 unsigned int last_label,
+                                 unsigned int last_label_other,
+                                 bool case_sensitive = false) const;
+
+public:
     /// \brief Return true iff two names are equal.
     /// \brief Return true iff two names are equal.
     ///
     ///
     /// Semantically this could be implemented based on the result of the
     /// Semantically this could be implemented based on the result of the

+ 217 - 0
src/lib/dns/tests/labelsequence_unittest.cc

@@ -142,6 +142,223 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
     EXPECT_TRUE(ls11.equals(ls12));
     EXPECT_TRUE(ls11.equals(ls12));
 }
 }
 
 
+// Compare tests
+TEST_F(LabelSequenceTest, compare) {
+    // "example.org." and "example.org.", case sensitive
+    NameComparisonResult result = ls1.compare(ls3, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "example.ORG.", case sensitive
+    result = ls3.compare(ls5, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    // "example.org." and "example.ORG.", case in-sensitive
+    result = ls3.compare(ls5);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    Name na("a.example.org");
+    Name nb("b.example.org");
+    LabelSequence lsa(na);
+    LabelSequence lsb(nb);
+
+    // "a.example.org." and "b.example.org.", case in-sensitive
+    result = lsa.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_GT(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "b.example.org.", case in-sensitive
+    lsa.stripLeft(1);
+    result = lsa.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
+              result.getRelation());
+    EXPECT_GT(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    Name nc("g.f.e.d.c.example.org");
+    LabelSequence lsc(nc);
+
+    // "g.f.e.d.c.example.org." and "b.example.org" (not absolute), case
+    // in-sensitive
+    lsb.stripRight(1);
+    result = lsc.compare(lsb);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(0, result.getCommonLabels());
+
+    // "g.f.e.d.c.example.org." and "example.org.", case in-sensitive
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "e.d.c.example.org." and "example.org.", case in-sensitive
+    lsc.stripLeft(2);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "example.org." and "example.org.", case in-sensitive
+    lsc.stripLeft(3);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "." and "example.org.", case in-sensitive
+    lsc.stripLeft(2);
+    result = lsc.compare(ls1);
+    EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN,
+              result.getRelation());
+    EXPECT_GT(0, result.getOrder());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    Name nd("a.b.c.isc.example.org");
+    LabelSequence lsd(nd);
+    Name ne("w.x.y.isc.EXAMPLE.org");
+    LabelSequence lse(ne);
+
+    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
+    // case sensitive
+    result = lsd.compare(lse, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.",
+    // case in-sensitive
+    result = lsd.compare(lse);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_GT(0, result.getOrder());
+    EXPECT_EQ(4, result.getCommonLabels());
+
+    // "isc.example.org." and "isc.EXAMPLE.org.", case sensitive
+    lsd.stripLeft(3);
+    lse.stripLeft(3);
+    result = lsd.compare(lse, true);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "isc.example.org." and "isc.EXAMPLE.org.", case in-sensitive
+    result = lsd.compare(lse);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(4, result.getCommonLabels());
+
+    Name nf("a.b.c.isc.example.org");
+    LabelSequence lsf(nf);
+    Name ng("w.x.y.isc.EXAMPLE.org");
+    LabelSequence lsg(ng);
+
+    // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org" (not
+    // absolute), case in-sensitive
+    lsg.stripRight(1);
+    result = lsg.compare(lsf);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(0, result.getCommonLabels());
+
+    // "a.b.c.isc.example.org" (not absolute) and
+    // "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive
+    lsf.stripRight(1);
+    result = lsg.compare(lsf);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // "a.b.c.isc.example" (not absolute) and
+    // "w.x.y.isc.EXAMPLE" (not absolute), case in-sensitive
+    lsf.stripRight(1);
+    lsg.stripRight(1);
+    result = lsg.compare(lsf);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_LT(0, result.getOrder());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "a.b.c" (not absolute) and
+    // "w.x.y" (not absolute), case in-sensitive
+    lsf.stripRight(2);
+    lsg.stripRight(2);
+    result = lsg.compare(lsf);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(0, result.getCommonLabels());
+
+    Name nh("aexample.org");
+    LabelSequence lsh(nh);
+    Name ni("bexample.org");
+    LabelSequence lsi(ni);
+
+    // "aexample.org" (not absolute) and
+    // "bexample.org" (not absolute), case in-sensitive
+    lsh.stripRight(1);
+    lsi.stripRight(1);
+    result = lsh.compare(lsi);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_GT(0, result.getOrder());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    // "aexample" (not absolute) and
+    // "bexample" (not absolute), case in-sensitive
+    lsh.stripRight(1);
+    lsi.stripRight(1);
+    result = lsh.compare(lsi);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(0, result.getCommonLabels());
+
+    Name nj("example.org");
+    LabelSequence lsj(nj);
+    Name nk("example.org");
+    LabelSequence lsk(nk);
+
+    // "example.org" (not absolute) and
+    // "example.org" (not absolute), case in-sensitive
+    lsj.stripRight(1);
+    lsk.stripRight(1);
+    result = lsj.compare(lsk);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    // "example" (not absolute) and
+    // "example" (not absolute), case in-sensitive
+    lsj.stripRight(1);
+    lsk.stripRight(1);
+    result = lsj.compare(lsk);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(1, result.getCommonLabels());
+}
+
 void
 void
 getDataCheck(const uint8_t* expected_data, size_t expected_len,
 getDataCheck(const uint8_t* expected_data, size_t expected_len,
              const LabelSequence& ls)
              const LabelSequence& ls)

+ 1 - 1
src/lib/python/isc/Makefile.am

@@ -1,5 +1,5 @@
 SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
 SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
-SUBDIRS += xfrin log_messages server_common ddns
+SUBDIRS += xfrin log_messages server_common ddns sysinfo
 
 
 python_PYTHON = __init__.py
 python_PYTHON = __init__.py
 
 

+ 11 - 0
src/lib/python/isc/sysinfo/Makefile.am

@@ -0,0 +1,11 @@
+SUBDIRS = . tests
+
+python_PYTHON = __init__.py
+python_PYTHON += sysinfo.py
+
+pythondir = $(pyexecdir)/isc/sysinfo
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)

+ 1 - 0
src/lib/python/isc/sysinfo/__init__.py

@@ -0,0 +1 @@
+from isc.sysinfo.sysinfo import *

+ 286 - 0
src/lib/python/isc/sysinfo/sysinfo.py

@@ -0,0 +1,286 @@
+# Copyright (C) 2012  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.
+
+'''This module returns system information.'''
+
+import os
+import sys
+import re
+import subprocess
+import os.path
+import platform
+
+class SysInfo:
+    def __init__(self):
+        self._num_processors = -1
+        self._endianness = 'Unknown'
+        self._hostname = ''
+        self._platform_name = 'Unknown'
+        self._platform_version = 'Unknown'
+        self._platform_machine = 'Unknown'
+        self._platform_is_smp = False
+        self._uptime = -1
+        self._loadavg = [-1.0, -1.0, -1.0]
+        self._mem_total = -1
+        self._mem_free = -1
+        self._mem_cached = -1
+        self._mem_buffers = -1
+        self._mem_swap_total = -1
+        self._mem_swap_free = -1
+        self._platform_distro = 'Unknown'
+        self._net_interfaces = 'Unknown'
+        self._net_routing_table = 'Unknown'
+        self._net_stats = 'Unknown'
+        self._net_connections = 'Unknown'
+
+    def get_num_processors(self):
+        """Returns the number of processors. This is the number of
+        hyperthreads when hyper-threading is enabled.
+        """
+        return self._num_processors
+
+    def get_endianness(self):
+        """Returns 'big' or 'little'."""
+        return self._endianness
+
+    def get_platform_hostname(self):
+        """Returns the hostname of the system."""
+        return self._hostname
+
+    def get_platform_name(self):
+        """Returns the platform name (uname -s)."""
+        return self._platform_name
+
+    def get_platform_version(self):
+        """Returns the platform version (uname -v)."""
+        return self._platform_version
+
+    def get_platform_machine(self):
+        """Returns the platform machine architecture."""
+        return self._platform_machine
+
+    def get_platform_is_smp(self):
+        """Returns True if an SMP kernel is being used, False otherwise."""
+        return self._platform_is_smp
+
+    def get_platform_distro(self):
+        """Returns the name of the OS distribution in use."""
+        return self._platform_distro
+
+    def get_uptime(self):
+        """Returns the uptime in seconds."""
+        return self._uptime
+
+    def get_loadavg(self):
+        """Returns the load average as 3 floating point values in an array."""
+        return self._loadavg
+
+    def get_mem_total(self):
+        """Returns the total amount of memory in bytes."""
+        return self._mem_total
+
+    def get_mem_free(self):
+        """Returns the amount of free memory in bytes."""
+        return self._mem_free
+
+    def get_mem_cached(self):
+        """Returns the amount of cached memory in bytes."""
+        return self._mem_cached
+
+    def get_mem_buffers(self):
+        """Returns the amount of buffer in bytes."""
+        return self._mem_buffers
+
+    def get_mem_swap_total(self):
+        """Returns the total amount of swap in bytes."""
+        return self._mem_swap_total
+
+    def get_mem_swap_free(self):
+        """Returns the amount of free swap in bytes."""
+        return self._mem_swap_free
+
+    def get_net_interfaces(self):
+        """Returns information about network interfaces (as a multi-line string)."""
+        return self._net_interfaces
+
+    def get_net_routing_table(self):
+        """Returns information about network routing table (as a multi-line string)."""
+        return self._net_routing_table
+
+    def get_net_stats(self):
+        """Returns network statistics (as a multi-line string)."""
+        return self._net_stats
+
+    def get_net_connections(self):
+        """Returns network connection information (as a multi-line string)."""
+        return self._net_connections
+
+class SysInfoLinux(SysInfo):
+    """Linux implementation of the SysInfo class.
+    See the base class documentation for more information.
+    """
+    def __init__(self):
+        super().__init__()
+
+        self._num_processors = os.sysconf('SC_NPROCESSORS_CONF')
+        self._endianness = sys.byteorder
+
+        with open('/proc/sys/kernel/hostname') as f:
+            self._hostname = f.read().strip()
+
+        u = os.uname()
+        self._platform_name = u[0]
+        self._platform_version = u[2]
+        self._platform_machine = u[4]
+
+        with open('/proc/version') as f:
+            self._platform_is_smp = ' SMP ' in f.read().strip()
+
+        with open('/proc/uptime') as f:
+            u = f.read().strip().split(' ')
+            if len(u) > 1:
+                self._uptime = int(round(float(u[0])))
+
+        with open('/proc/loadavg') as f:
+            l = f.read().strip().split(' ')
+            if len(l) >= 3:
+                self._loadavg = [float(l[0]), float(l[1]), float(l[2])]
+
+        with open('/proc/meminfo') as f:
+            m = f.readlines()
+            for line in m:
+                r = re.match('^MemTotal:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_total = int(r.group(1).strip()) * 1024
+                    continue
+                r = re.match('^MemFree:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_free = int(r.group(1).strip()) * 1024
+                    continue
+                r = re.match('^Cached:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_cached = int(r.group(1).strip()) * 1024
+                    continue
+                r = re.match('^Buffers:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_buffers = int(r.group(1).strip()) * 1024
+                    continue
+                r = re.match('^SwapTotal:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_swap_total = int(r.group(1).strip()) * 1024
+                    continue
+                r = re.match('^SwapFree:\s+(.*)\s*kB', line)
+                if r:
+                    self._mem_swap_free = int(r.group(1).strip()) * 1024
+                    continue
+
+        self._platform_distro = None
+
+        try:
+            s = subprocess.check_output(['lsb_release', '-a'])
+            for line in s.decode('utf-8').split('\n'):
+                r = re.match('^Description:(.*)', line)
+                if r:
+                    self._platform_distro = r.group(1).strip()
+                    break
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        if self._platform_distro is None:
+            files = ['/etc/debian_release',
+                     '/etc/debian_version',
+                     '/etc/SuSE-release',
+                     '/etc/UnitedLinux-release',
+                     '/etc/mandrake-release',
+                     '/etc/gentoo-release',
+                     '/etc/fedora-release',
+                     '/etc/redhat-release',
+                     '/etc/redhat_version',
+                     '/etc/slackware-release',
+                     '/etc/slackware-version',
+                     '/etc/arch-release',
+                     '/etc/lsb-release',
+                     '/etc/mageia-release']
+            for fn in files:
+                if os.path.exists(fn):
+                    with open(fn) as f:
+                        self._platform_distro = f.read().strip()
+                    break
+
+        if self._platform_distro is None:
+            self._platform_distro = 'Unknown'
+
+        self._net_interfaces = None
+
+        try:
+            s = subprocess.check_output(['ip', 'addr'])
+            self._net_interfaces = s.decode('utf-8')
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        if self._net_interfaces is None:
+            self._net_interfaces = 'Unknown'
+
+        self._net_routing_table = None
+
+        try:
+            s = subprocess.check_output(['ip', 'route'])
+            self._net_routing_table = s.decode('utf-8')
+            self._net_routing_table += '\n'
+            s = subprocess.check_output(['ip', '-f', 'inet6', 'route'])
+            self._net_routing_table += s.decode('utf-8')
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        if self._net_routing_table is None:
+            self._net_routing_table = 'Unknown'
+
+        self._net_stats = None
+
+        try:
+            s = subprocess.check_output(['netstat', '-s'])
+            self._net_stats = s.decode('utf-8')
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        if self._net_stats is None:
+            self._net_stats = 'Unknown'
+
+        self._net_connections = None
+
+        try:
+            s = subprocess.check_output(['netstat', '-apn'])
+            self._net_connections = s.decode('utf-8')
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        if self._net_connections is None:
+            self._net_connections = 'Unknown'
+
+class SysInfoTestcase(SysInfo):
+    def __init__(self):
+        super().__init__()
+        self._endianness = 'bigrastafarian'
+        self._platform_name = 'b10test'
+        self._uptime = 131072
+
+def SysInfoFromFactory():
+    osname = platform.system()
+    if osname == 'Linux':
+        return SysInfoLinux()
+    elif osname == 'BIND10Testcase':
+        return SysInfoTestcase()
+    else:
+        return SysInfo()

+ 23 - 0
src/lib/python/isc/sysinfo/tests/Makefile.am

@@ -0,0 +1,23 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = sysinfo_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+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/acl/.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) \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done

+ 197 - 0
src/lib/python/isc/sysinfo/tests/sysinfo_test.py

@@ -0,0 +1,197 @@
+# 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.sysinfo import *
+import os
+import unittest
+import platform
+import subprocess
+
+def _my_testcase_platform_system():
+    return 'BIND10Testcase'
+
+def _my_linux_platform_system():
+    return 'Linux'
+
+def _my_linux_os_sysconf(key):
+    if key == 'SC_NPROCESSORS_CONF':
+        return 42
+    assert False, 'Unhandled key'
+
+class MyFile:
+    def __init__(self, filename):
+        self._filename = filename
+
+    def read(self):
+        if self._filename == '/proc/sys/kernel/hostname':
+            return 'myhostname'
+        elif self._filename == '/proc/version':
+            return 'An SMP version string'
+        elif self._filename == '/proc/uptime':
+            return '86400.75 139993.71'
+        elif self._filename == '/proc/loadavg':
+            return '0.1 0.2 0.3 0.4'
+        else:
+            assert False, 'Unhandled filename'
+
+    def readlines(self):
+        if self._filename == '/proc/meminfo':
+            return ['MemTotal:        3083872 kB',
+                    'MemFree:          870492 kB',
+                    'Buffers:           27412 kB',
+                    'Cached:          1303860 kB',
+                    'SwapTotal:       4194300 kB',
+                    'SwapFree:        3999464 kB']
+        else:
+            assert False, 'Unhandled filename'
+
+    def close(self):
+        return
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        return
+
+def _my_linux_open(filename):
+    return MyFile(filename)
+
+def _my_linux_subprocess_check_output(command):
+    assert type(command) == list, 'command argument is not a list'
+    if command == ['lsb_release', '-a']:
+        return b'Description: My Distribution\n'
+    elif command == ['ip', 'addr']:
+        return b'qB2osV6vUOjqm3P/+tQ4d92xoYz8/U8P9v3KWRpNwlI=\n'
+    elif command == ['ip', 'route']:
+        return b'VGWAS92AlS14Pl2xqENJs5P2Ihe6Nv9g181Mu6Zz+aQ=\n'
+    elif command == ['ip', '-f', 'inet6', 'route']:
+        return b'XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+5Nc=\n'
+    elif command == ['netstat', '-s']:
+        return b'osuxbrcc1g9VgaF4yf3FrtfodrfATrbSnjhqhuQSAs8=\n'
+    elif command == ['netstat', '-apn']:
+        return b'Z+w0lwa02/T+5+EIio84rrst/Dtizoz/aL9Im7J7ESA=\n'
+    else:
+        assert False, 'Unhandled command'
+
+class SysInfoTest(unittest.TestCase):
+    def test_sysinfo(self):
+        """Test that the various methods on SysInfo exist and return data."""
+
+        s = SysInfo()
+        self.assertEqual(-1, s.get_num_processors())
+        self.assertEqual('Unknown', s.get_endianness())
+        self.assertEqual('', s.get_platform_hostname())
+        self.assertEqual('Unknown', s.get_platform_name())
+        self.assertEqual('Unknown', s.get_platform_version())
+        self.assertEqual('Unknown', s.get_platform_machine())
+        self.assertFalse(s.get_platform_is_smp())
+        self.assertEqual(-1, s.get_uptime())
+        self.assertEqual([-1.0, -1.0, -1.0], s.get_loadavg())
+        self.assertEqual(-1, s.get_mem_total())
+        self.assertEqual(-1, s.get_mem_free())
+        self.assertEqual(-1, s.get_mem_cached())
+        self.assertEqual(-1, s.get_mem_buffers())
+        self.assertEqual(-1, s.get_mem_swap_total())
+        self.assertEqual(-1, s.get_mem_swap_free())
+        self.assertEqual('Unknown', s.get_platform_distro())
+        self.assertEqual('Unknown', s.get_net_interfaces())
+        self.assertEqual('Unknown', s.get_net_routing_table())
+        self.assertEqual('Unknown', s.get_net_stats())
+        self.assertEqual('Unknown', s.get_net_connections())
+
+    def test_sysinfo_factory(self):
+        """Test that SysInfoFromFactory returns a valid system-specific
+        SysInfo implementation."""
+
+        old_platform_system = platform.system
+        platform.system = _my_testcase_platform_system
+
+        s = SysInfoFromFactory()
+        self.assertEqual(-1, s.get_num_processors())
+        self.assertEqual('bigrastafarian', s.get_endianness())
+        self.assertEqual('', s.get_platform_hostname())
+        self.assertEqual('b10test', s.get_platform_name())
+        self.assertEqual('Unknown', s.get_platform_version())
+        self.assertEqual('Unknown', s.get_platform_machine())
+        self.assertFalse(s.get_platform_is_smp())
+        self.assertEqual(131072, s.get_uptime())
+        self.assertEqual([-1.0, -1.0, -1.0], s.get_loadavg())
+        self.assertEqual(-1, s.get_mem_total())
+        self.assertEqual(-1, s.get_mem_free())
+        self.assertEqual(-1, s.get_mem_cached())
+        self.assertEqual(-1, s.get_mem_buffers())
+        self.assertEqual(-1, s.get_mem_swap_total())
+        self.assertEqual(-1, s.get_mem_swap_free())
+        self.assertEqual('Unknown', s.get_platform_distro())
+        self.assertEqual('Unknown', s.get_net_interfaces())
+        self.assertEqual('Unknown', s.get_net_routing_table())
+        self.assertEqual('Unknown', s.get_net_stats())
+        self.assertEqual('Unknown', s.get_net_connections())
+
+        platform.system = old_platform_system
+
+    def test_sysinfo_linux(self):
+        """Tests the Linux implementation of SysInfo. Note that this
+        tests deep into the implementation, and not just the
+        interfaces."""
+
+        # Don't run this test on platform other than Linux as some
+        # system calls may not even be available.
+        osname = platform.system()
+        if osname != 'Linux':
+            return
+
+        # Save and replace existing implementations of library functions
+        # with mock ones for testing.
+        old_platform_system = platform.system
+        platform.system = _my_linux_platform_system
+        old_os_sysconf = os.sysconf
+        os.sysconf = _my_linux_os_sysconf
+        old_open = __builtins__.open
+        __builtins__.open = _my_linux_open
+        old_subprocess_check_output = subprocess.check_output
+        subprocess.check_output = _my_linux_subprocess_check_output
+
+        s = SysInfoFromFactory()
+        self.assertEqual(42, s.get_num_processors())
+        self.assertEqual('myhostname', s.get_platform_hostname())
+        self.assertTrue(s.get_platform_is_smp())
+        self.assertEqual(86401, s.get_uptime())
+        self.assertEqual([0.1, 0.2, 0.3], s.get_loadavg())
+        self.assertEqual(3157884928, s.get_mem_total())
+        self.assertEqual(891383808, s.get_mem_free())
+        self.assertEqual(1335152640, s.get_mem_cached())
+        self.assertEqual(28069888, s.get_mem_buffers())
+        self.assertEqual(4294963200, s.get_mem_swap_total())
+        self.assertEqual(4095451136, s.get_mem_swap_free())
+        self.assertEqual('My Distribution', s.get_platform_distro())
+
+        # These test that the corresponding tools are being called (and
+        # no further processing is done on this data). Please see the
+        # implementation functions at the top of this file.
+        self.assertEqual('qB2osV6vUOjqm3P/+tQ4d92xoYz8/U8P9v3KWRpNwlI=\n', s.get_net_interfaces())
+        self.assertEqual('VGWAS92AlS14Pl2xqENJs5P2Ihe6Nv9g181Mu6Zz+aQ=\n\nXfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+5Nc=\n', s.get_net_routing_table())
+        self.assertEqual('osuxbrcc1g9VgaF4yf3FrtfodrfATrbSnjhqhuQSAs8=\n', s.get_net_stats())
+        self.assertEqual('Z+w0lwa02/T+5+EIio84rrst/Dtizoz/aL9Im7J7ESA=\n', s.get_net_connections())
+
+        # Restore original implementations.
+        platform.system = old_platform_system
+        os.sysconf = old_os_sysconf
+        __builtins__.open = old_open
+        subprocess.check_output = old_subprocess_check_output
+
+if __name__ == "__main__":
+    unittest.main()