Parcourir la source

[master] Merge branch 'trac2172'

Jelte Jansen il y a 12 ans
Parent
commit
9276a6b36c

+ 38 - 29
src/bin/sysinfo/sysinfo.py.in

@@ -32,6 +32,15 @@ def usage():
               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", \
@@ -57,39 +66,39 @@ def main():
 
     s = SysInfoFromFactory()
 
-    f.write('BIND 10 ShowTech tool\n')
-    f.write('=====================\n')
+    f.write('ISC Sysinfo 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()))
+    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');
-    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')
+    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')
 
-    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()))
+    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\n', s.get_uptime)
 
-    l = s.get_loadavg()
-    f.write(' + Loadavg: %f %f %f\n' % (l[0], l[1], l[2]))
+    write_value(f, ' + Loadavg: %f %f %f\n', s.get_loadavg)
 
     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()))
+    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');
@@ -97,19 +106,19 @@ def main():
     f.write('Interfaces\n')
     f.write('~~~~~~~~~~\n\n')
 
-    f.write(s.get_net_interfaces())
+    write_value(f, '%s', s.get_net_interfaces)
 
     f.write('\nRouting table\n')
     f.write('~~~~~~~~~~~~~\n\n')
-    f.write(s.get_net_routing_table())
+    write_value(f, '%s', s.get_net_routing_table)
 
     f.write('\nStatistics\n')
     f.write('~~~~~~~~~~\n\n')
-    f.write(s.get_net_stats())
+    write_value(f, '%s', s.get_net_stats)
 
     f.write('\nConnections\n')
     f.write('~~~~~~~~~~~\n\n')
-    f.write(s.get_net_connections())
+    write_value(f, '%s', s.get_net_connections)
 
     try:
         if os.getuid() != 0:

+ 97 - 44
src/lib/python/isc/sysinfo/sysinfo.py

@@ -25,27 +25,31 @@ import time
 
 class SysInfo:
     def __init__(self):
-        self._num_processors = -1
+        self._num_processors = None
         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._platform_is_smp = None
+        self._uptime = None
+        self._loadavg = None
+        self._mem_total = None
+        self._mem_free = None
+        self._mem_swap_total = None
+        self._mem_swap_free = None
         self._net_interfaces = 'Unknown\n'
         self._net_routing_table = 'Unknown\n'
         self._net_stats = 'Unknown\n'
         self._net_connections = 'Unknown\n'
 
+        # The following are Linux speicific, and should eventually be removed
+        # from this level; for now we simply default to None (so they won't
+        # be printed)
+        self._platform_distro = None
+        self._mem_cached = None
+        self._mem_buffers = None
+
     def get_num_processors(self):
         """Returns the number of processors. This is the number of
         hyperthreads when hyper-threading is enabled.
@@ -77,7 +81,12 @@ class SysInfo:
         return self._platform_is_smp
 
     def get_platform_distro(self):
-        """Returns the name of the OS distribution in use."""
+        """Returns the name of the OS distribution in use.
+
+        Note: the concept of 'distribution' is Linux specific.  This shouldn't
+        be at this level.
+
+        """
         return self._platform_distro
 
     def get_uptime(self):
@@ -164,7 +173,7 @@ class SysInfoLinux(SysInfoPOSIX):
         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])]
+                self._loadavg = (float(l[0]), float(l[1]), float(l[2]))
 
         with open('/proc/meminfo') as f:
             m = f.readlines()
@@ -276,8 +285,6 @@ class SysInfoBSD(SysInfoPOSIX):
         except (subprocess.CalledProcessError, OSError):
             pass
 
-        self._platform_distro = self._platform_name + ' ' + self._platform_version
-
         try:
             s = subprocess.check_output(['ifconfig'])
             self._net_interfaces = s.decode('utf-8')
@@ -296,6 +303,12 @@ class SysInfoBSD(SysInfoPOSIX):
         except (subprocess.CalledProcessError, OSError):
             self._net_connections = 'Warning: "netstat -an" command failed.\n'
 
+        try:
+            s = subprocess.check_output(['netstat', '-nr'])
+            self._net_routing_table = s.decode('utf-8')
+        except (subprocess.CalledProcessError, OSError):
+            self._net_connections = 'Warning: "netstat -nr" command failed.\n'
+
 class SysInfoOpenBSD(SysInfoBSD):
     """OpenBSD implementation of the SysInfo class.
     See the SysInfo class documentation for more information.
@@ -303,11 +316,6 @@ class SysInfoOpenBSD(SysInfoBSD):
     def __init__(self):
         super().__init__()
 
-        # Don't know how to gather these
-        self._platform_is_smp = False
-        self._mem_cached = -1
-        self._mem_buffers = -1
-
         try:
             s = subprocess.check_output(['sysctl', '-n', 'kern.boottime'])
             t = s.decode('utf-8').strip()
@@ -320,7 +328,7 @@ class SysInfoOpenBSD(SysInfoBSD):
             s = subprocess.check_output(['sysctl', '-n', 'vm.loadavg'])
             l = s.decode('utf-8').strip().split(' ')
             if len(l) >= 3:
-                self._loadavg = [float(l[0]), float(l[1]), float(l[2])]
+                self._loadavg = (float(l[0]), float(l[1]), float(l[2]))
         except (subprocess.CalledProcessError, OSError):
             pass
 
@@ -343,29 +351,13 @@ class SysInfoOpenBSD(SysInfoBSD):
         except (subprocess.CalledProcessError, OSError):
             pass
 
-        try:
-            s = subprocess.check_output(['route', '-n', 'show'])
-            self._net_routing_table = s.decode('utf-8')
-        except (subprocess.CalledProcessError, OSError):
-            self._net_routing_table = 'Warning: "route -n show" command failed.\n'
-
-class SysInfoFreeBSD(SysInfoBSD):
-    """FreeBSD implementation of the SysInfo class.
-    See the SysInfo class documentation for more information.
+class SysInfoFreeBSDOSX(SysInfoBSD):
+    """Shared code for the FreeBSD and OS X implementations of the SysInfo
+    class. See the SysInfo class documentation for more information.
     """
     def __init__(self):
         super().__init__()
 
-        # Don't know how to gather these
-        self._mem_cached = -1
-        self._mem_buffers = -1
-
-        try:
-            s = subprocess.check_output(['sysctl', '-n', 'kern.smp.active'])
-            self._platform_is_smp = int(s.decode('utf-8').strip()) > 0
-        except (subprocess.CalledProcessError, OSError):
-            pass
-
         try:
             s = subprocess.check_output(['sysctl', '-n', 'kern.boottime'])
             t = s.decode('utf-8').strip()
@@ -385,7 +377,20 @@ class SysInfoFreeBSD(SysInfoBSD):
             else:
                 la = l.split(' ')
             if len(la) >= 3:
-                self._loadavg = [float(la[0]), float(la[1]), float(la[2])]
+                self._loadavg = (float(la[0]), float(la[1]), float(la[2]))
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+class SysInfoFreeBSD(SysInfoFreeBSDOSX):
+    """FreeBSD implementation of the SysInfo class.
+    See the SysInfo class documentation for more information.
+    """
+    def __init__(self):
+        super().__init__()
+
+        try:
+            s = subprocess.check_output(['sysctl', '-n', 'kern.smp.active'])
+            self._platform_is_smp = int(s.decode('utf-8').strip()) > 0
         except (subprocess.CalledProcessError, OSError):
             pass
 
@@ -408,11 +413,57 @@ class SysInfoFreeBSD(SysInfoBSD):
         except (subprocess.CalledProcessError, OSError):
             pass
 
+
+
+class SysInfoOSX(SysInfoFreeBSDOSX):
+    """OS X (Darwin) implementation of the SysInfo class.
+    See the SysInfo class documentation for more information.
+    """
+    def __init__(self):
+        super().__init__()
+
+        # note; this call overrides the value already set when hw.physmem
+        # was read. However, on OSX, physmem is not necessarily the correct
+        # value. But since it does not fail and does work on most BSD's, it's
+        # left in the base class and overwritten here
+        self._mem_total = None
         try:
-            s = subprocess.check_output(['netstat', '-nr'])
-            self._net_routing_table = s.decode('utf-8')
+            s = subprocess.check_output(['sysctl', '-n', 'hw.memsize'])
+            self._mem_total = int(s.decode('utf-8').strip())
         except (subprocess.CalledProcessError, OSError):
-            self._net_connections = 'Warning: "netstat -nr" command failed.\n'
+            pass
+
+        try:
+            s = subprocess.check_output(['vm_stat'])
+            lines = s.decode('utf-8').split('\n')
+            # store all values in a dict
+            values = {}
+            page_size = None
+            page_size_re = re.compile('.*page size of ([0-9]+) bytes')
+            for line in lines:
+                page_size_m = page_size_re.match(line)
+                if page_size_m:
+                    page_size = int(page_size_m.group(1))
+                else:
+                    key, _, value = line.partition(':')
+                    values[key] = value.strip()[:-1]
+            # Only calculate memory if page size is known
+            if page_size is not None:
+                self._mem_free = int(values['Pages free']) * page_size +\
+                                 int(values['Pages speculative']) * page_size
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
+        try:
+            s = subprocess.check_output(['sysctl', '-n', 'vm.swapusage'])
+            l = s.decode('utf-8').strip()
+            r = re.match('^total = (\d+\.\d+)M\s+used = (\d+\.\d+)M\s+free = (\d+\.\d+)M', l)
+            if r:
+                self._mem_swap_total = float(r.group(1).strip()) * 1024
+                self._mem_swap_free = float(r.group(3).strip()) * 1024
+        except (subprocess.CalledProcessError, OSError):
+            pass
+
 
 class SysInfoTestcase(SysInfo):
     def __init__(self):
@@ -429,6 +480,8 @@ def SysInfoFromFactory():
         return SysInfoOpenBSD()
     elif osname == 'FreeBSD':
         return SysInfoFreeBSD()
+    elif osname == 'Darwin':
+        return SysInfoOSX()
     elif osname == 'BIND10Testcase':
         return SysInfoTestcase()
     else:

+ 191 - 131
src/lib/python/isc/sysinfo/tests/sysinfo_test.py

@@ -20,6 +20,14 @@ import platform
 import subprocess
 import time
 
+# different fake 'number of processors' values used for the different
+# operating systems
+NPROCESSORS_LINUX = 42
+NPROCESSORS_OPENBSD = 43
+NPROCESSORS_FREEBSD = 44
+NPROCESSORS_OSX = 45
+
+
 def _my_testcase_platform_system():
     return 'BIND10Testcase'
 
@@ -28,7 +36,7 @@ def _my_linux_platform_system():
 
 def _my_linux_os_sysconf(key):
     if key == 'SC_NPROCESSORS_CONF':
-        return 42
+        return NPROCESSORS_LINUX
     assert False, 'Unhandled key'
 
 class MyLinuxFile:
@@ -92,90 +100,166 @@ def _my_openbsd_platform_system():
 
 def _my_openbsd_os_sysconf(key):
     if key == 'SC_NPROCESSORS_CONF':
-        return 53
+        return NPROCESSORS_OPENBSD
     assert False, 'Unhandled key'
 
-def _my_openbsd_subprocess_check_output(command):
+def _my_openbsd_platform_uname():
+    return ('OpenBSD', 'test.example.com', '5.0', '', 'amd64')
+
+# For the BSD types, there is a hierarchy that mostly resembles the
+# class hierarchy in the sysinfo library;
+# These are output strings of commands that sysinfo calls
+#
+# The test hierarchy is used as follows:
+# Each operating system has its own _my_<OS>_subprocess_check_output
+# call. If the call is not found, it calls it's 'parent' (e.g.
+# for openbsd that is my_bsd_subprocesses_check_output).
+#
+# If that returns None, the call had no test value and the test fails
+# (and needs to be updated).
+# The child classes are checked first so that they can override
+# output from the parents, if necessary.
+#
+# Some parents have their own parent
+# (e.g. _my_freebsd_osx_subprocess_check_output), in that case,
+# if they do not recognize the command, they simply return whatever
+# their parent returns
+
+def _my_bsd_subprocess_check_output(command):
+    '''subprocess output for all bsd types'''
     assert type(command) == list, 'command argument is not a list'
     if command == ['hostname']:
-        return b'blowfish.example.com\n'
-    elif command == ['sysctl', '-n', 'kern.boottime']:
-        return bytes(str(int(time.time() - 76632)), 'utf-8')
-    elif command == ['sysctl', '-n', 'vm.loadavg']:
-        return b'0.7 0.9 0.8\n'
+        return b'test.example.com\n'
     elif command == ['sysctl', '-n', 'hw.physmem']:
         return b'543214321\n'
-    elif command == ['vmstat']:
-        return b' procs    memory       page                    disks    traps          cpu\n r b w    avm     fre  flt  re  pi  po  fr  sr wd0 cd0  int   sys   cs us sy id\n 0 0 0   121212  123456   47   0   0   0   0   0   2   0    2    80   14  0  1 99\n'
-    elif command == ['swapctl', '-s', '-k']:
-        return b'total: 553507 1K-blocks allocated, 2 used, 553505 available'
     elif command == ['ifconfig']:
         return b'qB2osV6vUOjqm3P/+tQ4d92xoYz8/U8P9v3KWRpNwlI=\n'
-    elif command == ['route', '-n', 'show']:
-        return b'XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+5Nc=\n'
     elif command == ['netstat', '-s']:
         return b'osuxbrcc1g9VgaF4yf3FrtfodrfATrbSnjhqhuQSAs8=\n'
     elif command == ['netstat', '-an']:
         return b'Z+w0lwa02/T+5+EIio84rrst/Dtizoz/aL9Im7J7ESA=\n'
+    elif command == ['netstat', '-nr']:
+        return b'XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+5Nc=\n'
     else:
-        assert False, 'Unhandled command'
+        return None
+
+def _my_openbsd_subprocess_check_output(command):
+    assert type(command) == list, 'command argument is not a list'
+    if command == ['sysctl', '-n', 'kern.boottime']:
+        return bytes(str(int(time.time() - 76632)), 'utf-8')
+    elif command == ['sysctl', '-n', 'vm.loadavg']:
+        return b'0.7 0.9 0.8\n'
+    elif command == ['vmstat']:
+        return b' procs    memory       page                    disks    traps          cpu\n r b w    avm     fre  flt  re  pi  po  fr  sr wd0 cd0  int   sys   cs us sy id\n 0 0 0   121212  123456   47   0   0   0   0   0   2   0    2    80   14  0  1 99\n'
+    elif command == ['swapctl', '-s', '-k']:
+        return b'total: 553507 1K-blocks allocated, 2 used, 553505 available'
+    else:
+        bsd_output = _my_bsd_subprocess_check_output(command)
+        if bsd_output is not None:
+            return bsd_output
+        else:
+            assert False, 'Unhandled command'
 
 def _my_freebsd_platform_system():
     return 'FreeBSD'
 
 def _my_freebsd_os_sysconf(key):
     if key == 'SC_NPROCESSORS_CONF':
-        return 91
+        return NPROCESSORS_FREEBSD
     assert False, 'Unhandled key'
 
-def _my_freebsd_subprocess_check_output(command):
+def _my_freebsd_platform_uname():
+    return ('FreeBSD', 'freebsd', '8.2-RELEASE', '', 'i386')
+
+def _my_freebsd_osx_subprocess_check_output(command):
+    '''subprocess output shared for freebsd and osx'''
     assert type(command) == list, 'command argument is not a list'
-    if command == ['hostname']:
-        return b'daemon.example.com\n'
-    elif command == ['sysctl', '-n', 'kern.smp.active']:
-        return b'1\n'
-    elif command == ['sysctl', '-n', 'kern.boottime']:
+    if command == ['sysctl', '-n', 'kern.boottime']:
         return bytes('{ sec = ' + str(int(time.time() - 76632)) + ', usec = 0 }\n', 'utf-8')
     elif command == ['sysctl', '-n', 'vm.loadavg']:
         return b'{ 0.2 0.4 0.6 }\n'
-    elif command == ['sysctl', '-n', 'hw.physmem']:
-        return b'987654321\n'
+    else:
+        return _my_bsd_subprocess_check_output(command)
+
+def _my_freebsd_subprocess_check_output(command):
+    assert type(command) == list, 'command argument is not a list'
+    if command == ['sysctl', '-n', 'kern.smp.active']:
+        return b'1\n'
     elif command == ['vmstat', '-H']:
         return b' procs    memory       page                    disks    traps          cpu\n r b w    avm     fre  flt  re  pi  po  fr  sr wd0 cd0  int   sys   cs us sy id\n 0 0 0   343434  123456   47   0   0   0   0   0   2   0    2    80   14  0  1 99\n'
     elif command == ['swapctl', '-s', '-k']:
         return b'Total:         1013216    0\n'
-    elif command == ['ifconfig']:
-        return b'qB2osV6vUOjqm3P/+tQ4d92xoYz8/U8P9v3KWRpNwlI=\n'
-    elif command == ['netstat', '-nr']:
-        return b'XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+5Nc=\n'
-    elif command == ['netstat', '-s']:
-        return b'osuxbrcc1g9VgaF4yf3FrtfodrfATrbSnjhqhuQSAs8=\n'
-    elif command == ['netstat', '-an']:
-        return b'Z+w0lwa02/T+5+EIio84rrst/Dtizoz/aL9Im7J7ESA=\n'
     else:
-        assert False, 'Unhandled command'
+        freebsd_osx_output = _my_freebsd_osx_subprocess_check_output(command)
+        if freebsd_osx_output is not None:
+            return freebsd_osx_output
+        else:
+            assert False, 'Unhandled command'
+
+def _my_osx_platform_system():
+    return 'Darwin'
+
+def _my_osx_platform_uname():
+    return ('Darwin', 'test.example.com', '10.6.0', '', '')
+
+def _my_osx_os_sysconf(key):
+    if key == 'SC_NPROCESSORS_CONF':
+        return NPROCESSORS_OSX
+    assert False, 'Unhandled key'
+
+def _my_osx_subprocess_check_output(command):
+    assert type(command) == list, 'command argument is not a list'
+    if command == ['sysctl', '-n', 'hw.memsize']:
+        # Something different than physmem from bsd
+        return b'123456789\n'
+    elif command == ['vm_stat']:
+        return b'Mach Virtual Memory Statistics: (page size of 4096 bytes)\nPages free: 12345.\nPages speculative: 11111.\n'
+    elif command == ['sysctl', '-n', 'vm.swapusage']:
+        return b'total = 18432.00M  used = 17381.23M  free = 1050.77M\n'
+    else:
+        freebsd_osx_output = _my_freebsd_osx_subprocess_check_output(command)
+        if freebsd_osx_output is not None:
+            return freebsd_osx_output
+        else:
+            assert False, 'Unhandled command'
 
 class SysInfoTest(unittest.TestCase):
+
+    def setUp(self):
+        # Save existing implementations of library functions
+        # (they are replaced in the tests)
+        self.old_platform_system = platform.system
+        self.old_os_sysconf = os.sysconf
+        self.old_open = __builtins__.open
+        self.old_subprocess_check_output = subprocess.check_output
+
+    def tearDown(self):
+        # Restore the library functions
+        platform.system = self.old_platform_system
+        os.sysconf = self.old_os_sysconf
+        __builtins__.open = self.old_open
+        subprocess.check_output = self.old_subprocess_check_output
+
     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(None, 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(None, s.get_uptime())
+        self.assertEqual(None, s.get_loadavg())
+        self.assertEqual(None, s.get_mem_total())
+        self.assertEqual(None, s.get_mem_free())
+        self.assertEqual(None, s.get_mem_cached())
+        self.assertEqual(None, s.get_mem_buffers())
+        self.assertEqual(None, s.get_mem_swap_total())
+        self.assertEqual(None, s.get_mem_swap_free())
+        self.assertEqual(None, s.get_platform_distro())
         self.assertEqual('Unknown\n', s.get_net_interfaces())
         self.assertEqual('Unknown\n', s.get_net_routing_table())
         self.assertEqual('Unknown\n', s.get_net_stats())
@@ -189,7 +273,7 @@ class SysInfoTest(unittest.TestCase):
         platform.system = _my_testcase_platform_system
 
         s = SysInfoFromFactory()
-        self.assertEqual(-1, s.get_num_processors())
+        self.assertEqual(None, s.get_num_processors())
         self.assertEqual('bigrastafarian', s.get_endianness())
         self.assertEqual('', s.get_platform_hostname())
         self.assertEqual('b10test', s.get_platform_name())
@@ -197,14 +281,14 @@ class SysInfoTest(unittest.TestCase):
         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(None, s.get_loadavg())
+        self.assertEqual(None, s.get_mem_total())
+        self.assertEqual(None, s.get_mem_free())
+        self.assertEqual(None, s.get_mem_cached())
+        self.assertEqual(None, s.get_mem_buffers())
+        self.assertEqual(None, s.get_mem_swap_total())
+        self.assertEqual(None, s.get_mem_swap_free())
+        self.assertEqual(None, s.get_platform_distro())
         self.assertEqual('Unknown\n', s.get_net_interfaces())
         self.assertEqual('Unknown\n', s.get_net_routing_table())
         self.assertEqual('Unknown\n', s.get_net_stats())
@@ -217,29 +301,19 @@ class SysInfoTest(unittest.TestCase):
         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
+        # 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(NPROCESSORS_LINUX, 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((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())
@@ -256,107 +330,93 @@ class SysInfoTest(unittest.TestCase):
         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
+    def check_bsd_values(self, s):
+        # check values shared by all bsd implementations
+        self.assertEqual('test.example.com', s.get_platform_hostname())
+        self.assertLess(abs(76632 - s.get_uptime()), 4)
+        self.assertEqual(None, s.get_mem_cached())
+        self.assertEqual(None, s.get_mem_buffers())
+        self.assertEqual(None, 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('XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+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())
 
     def test_sysinfo_openbsd(self):
         """Tests the OpenBSD 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 OpenBSD as some
-        # system calls may not even be available.
-        osname = platform.system()
-        if osname != 'OpenBSD':
-            return
-
-        # Save and replace existing implementations of library functions
+        # Replace existing implementations of library functions
         # with mock ones for testing.
-        old_platform_system = platform.system
         platform.system = _my_openbsd_platform_system
-        old_os_sysconf = os.sysconf
         os.sysconf = _my_openbsd_os_sysconf
-        old_subprocess_check_output = subprocess.check_output
         subprocess.check_output = _my_openbsd_subprocess_check_output
+        os.uname = _my_openbsd_platform_uname
 
         s = SysInfoFromFactory()
-        self.assertEqual(53, s.get_num_processors())
-        self.assertEqual('blowfish.example.com', s.get_platform_hostname())
-        self.assertFalse(s.get_platform_is_smp())
+        self.assertEqual(NPROCESSORS_OPENBSD, s.get_num_processors())
 
-        self.assertLess(abs(76632 - s.get_uptime()), 4)
-        self.assertEqual([0.7, 0.9, 0.8], s.get_loadavg())
+        self.check_bsd_values(s)
+
+        self.assertEqual((0.7, 0.9, 0.8), s.get_loadavg())
+        self.assertFalse(s.get_platform_is_smp())
         self.assertEqual(543214321, s.get_mem_total())
         self.assertEqual(543214321 - (121212 * 1024), s.get_mem_free())
-        self.assertEqual(-1, s.get_mem_cached())
-        self.assertEqual(-1, s.get_mem_buffers())
         self.assertEqual(566791168, s.get_mem_swap_total())
         self.assertEqual(566789120, s.get_mem_swap_free())
-        self.assertRegexpMatches(s.get_platform_distro(), '^OpenBSD\s+.*')
-
-        # 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('XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+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
-        subprocess.check_output = old_subprocess_check_output
 
     def test_sysinfo_freebsd(self):
         """Tests the FreeBSD 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 FreeBSD as some
-        # system calls may not even be available.
-        osname = platform.system()
-        if osname != 'FreeBSD':
-            return
-
-        # Save and replace existing implementations of library functions
+        # Replace existing implementations of library functions
         # with mock ones for testing.
-        old_platform_system = platform.system
         platform.system = _my_freebsd_platform_system
-        old_os_sysconf = os.sysconf
         os.sysconf = _my_freebsd_os_sysconf
-        old_subprocess_check_output = subprocess.check_output
         subprocess.check_output = _my_freebsd_subprocess_check_output
+        os.uname = _my_freebsd_platform_uname
 
         s = SysInfoFromFactory()
-        self.assertEqual(91, s.get_num_processors())
-        self.assertEqual('daemon.example.com', s.get_platform_hostname())
+        self.assertEqual(NPROCESSORS_FREEBSD, s.get_num_processors())
         self.assertTrue(s.get_platform_is_smp())
 
-        self.assertLess(abs(76632 - s.get_uptime()), 4)
-        self.assertEqual([0.2, 0.4, 0.6], s.get_loadavg())
-        self.assertEqual(987654321, s.get_mem_total())
-        self.assertEqual(987654321 - (343434 * 1024), s.get_mem_free())
-        self.assertEqual(-1, s.get_mem_cached())
-        self.assertEqual(-1, s.get_mem_buffers())
+        self.check_bsd_values(s)
+
+        self.assertEqual((0.2, 0.4, 0.6), s.get_loadavg())
+        self.assertEqual(543214321, s.get_mem_total())
+        self.assertEqual(543214321 - (343434 * 1024), s.get_mem_free())
         self.assertEqual(1037533184, s.get_mem_swap_total())
         self.assertEqual(1037533184, s.get_mem_swap_free())
-        self.assertRegexpMatches(s.get_platform_distro(), '^FreeBSD\s+.*')
 
-        # 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('XfizswwNA9NkXz6K36ZExpjV08Y5IXkHI8jjDSV+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())
+    def test_sysinfo_osx(self):
+        """Tests the OS X implementation of SysInfo. Note that this
+        tests deep into the implementation, and not just the
+        interfaces."""
 
-        # Restore original implementations.
-        platform.system = old_platform_system
-        os.sysconf = old_os_sysconf
-        subprocess.check_output = old_subprocess_check_output
+        # Replace existing implementations of library functions
+        # with mock ones for testing.
+        platform.system = _my_osx_platform_system
+        os.sysconf = _my_osx_os_sysconf
+        subprocess.check_output = _my_osx_subprocess_check_output
+        os.uname = _my_osx_platform_uname
+
+        s = SysInfoFromFactory()
+        self.assertEqual(NPROCESSORS_OSX, s.get_num_processors())
+        self.assertFalse(s.get_platform_is_smp())
+
+        self.check_bsd_values(s)
+
+        self.assertEqual((0.2, 0.4, 0.6), s.get_loadavg())
+        self.assertEqual(123456789, s.get_mem_total())
+        self.assertEqual((23456 * 4096), s.get_mem_free())
+        self.assertEqual(18874368.0, s.get_mem_swap_total())
+        self.assertEqual(1075988.48, s.get_mem_swap_free())
 
 if __name__ == "__main__":
     unittest.main()