Package: open-iscsi
Version: 2.0.873-3
Severity: wishlist
Tags: patch
User: yolanda.ro...@canonical.com
Usertags: origin-ubuntu  ubuntu-patch

Added autopkgtests


*** /tmp/tmpZ_VaLn/bug_body

In Ubuntu, the attached patch was applied to achieve the following:

Improve QA of packages


  * d/tests: added dep-8-tests


Thanks for considering the patch.


-- System Information:
Debian Release: wheezy/sid
  APT prefers saucy-updates
  APT policy: (500, 'saucy-updates'), (500, 'saucy-security'), (500, 'saucy')
Architecture: amd64 (x86_64)

Kernel: Linux 3.8.0-14-generic (SMP w/1 CPU core)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
=== modified file 'debian/changelog'

=== modified file 'debian/control'
--- debian/control	2012-07-10 13:53:52 +0000
+++ debian/control	2013-05-29 11:47:58 +0000
@@ -9,6 +9,7 @@
 Vcs-Git: git://git.debian.org/git/pkg-iscsi/open-iscsi.git
 Vcs-Browser: http://git.debian.org/?p=pkg-iscsi/open-iscsi.git
 Homepage: http://www.open-iscsi.org/
+XS-Testsuite: autopkgtest
 
 Package: open-iscsi
 Architecture: any

=== added directory 'debian/tests'
=== added file 'debian/tests/control'
--- debian/tests/control	1970-01-01 00:00:00 +0000
+++ debian/tests/control	2013-05-29 11:47:45 +0000
@@ -0,0 +1,3 @@
+Tests: daemon testsuite
+Depends: open-iscsi
+Restrictions: needs-root

=== added file 'debian/tests/daemon'
--- debian/tests/daemon	1970-01-01 00:00:00 +0000
+++ debian/tests/daemon	2013-05-29 11:47:45 +0000
@@ -0,0 +1,15 @@
+#!/bin/bash
+#--------------------------
+# Testing open-iscsi daemon
+#--------------------------
+set -e
+
+/etc/init.d/open-iscsi start > /dev/null 2>&1
+
+if pidof -x iscsid > /dev/null; then
+    echo "OK"
+    exit 0
+else
+    echo "ERROR: OPEN-ISCSI IS NOT RUNNING"
+    exit 1
+fi

=== added file 'debian/tests/test-open-iscsi.py'
--- debian/tests/test-open-iscsi.py	1970-01-01 00:00:00 +0000
+++ debian/tests/test-open-iscsi.py	2013-05-29 11:47:45 +0000
@@ -0,0 +1,195 @@
+#!/usr/bin/python
+#
+#    test-open-iscsi.py quality assurance test script for open-iscsi
+#    Copyright (C) 2011 Canonical Ltd.
+#    Author: Jamie Strandboge <ja...@canonical.com>
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 3,
+#    as published by the Free Software Foundation.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# packages required for test to run:
+# QRT-Packages: open-iscsi
+# packages where more than one package can satisfy a runtime requirement:
+# QRT-Alternates: 
+# files and directories required for the test to run:
+# QRT-Depends: 
+# privilege required for the test to run (remove line if running as user is okay):
+# QRT-Privilege: root
+
+'''
+    In general, this test should be run in a virtual machine (VM) or possibly
+    a chroot and not on a production machine. While efforts are made to make
+    these tests non-destructive, there is no guarantee this script will not
+    alter the machine. You have been warned.
+
+    How to run in a clean VM:
+    $ sudo apt-get -y install python-unit <QRT-Packages> && sudo ./test-PKG.py -v'
+
+    How to run in a clean schroot named 'lucid':
+    $ schroot -c lucid -u root -- sh -c 'apt-get -y install python-unit <QRT-Packages> && ./test-PKG.py -v'
+
+
+    NOTES:
+    - currently only tested on Ubuntu 8.04
+'''
+
+
+import unittest, subprocess, sys, os
+import testlib
+import time
+
+# There are setup based on README.multipurpose-vm. Feel free to override.
+remote_server = ''
+username = 'ubuntu'
+password = 'passwd'
+username_in = 'ubuntu'
+password_in = 'ubuntupasswd'
+initiatorname = 'iqn.2009-10.com.example.hardy-multi:iscsi-01'
+
+try:
+    from private.qrt.OpenIscsi import PrivateOpenIscsiTest
+except ImportError:
+    class PrivateOpenIscsiTest(object):
+        '''Empty class'''
+    print >>sys.stdout, "Skipping private tests"
+
+class OpenIscsiTest(testlib.TestlibCase, PrivateOpenIscsiTest):
+    '''Test my thing.'''
+
+    def setUp(self):
+        '''Set up prior to each test_* function'''
+        self.pidfile = "/var/run/iscsid.pid"
+        self.exe = "/sbin/iscsid"
+        self.daemon = testlib.TestDaemon("/etc/init.d/open-iscsi")
+        self.initiatorname_iscsi = '/etc/iscsi/initiatorname.iscsi'
+        self.iscsid_conf = '/etc/iscsi/iscsid.conf'
+
+    def tearDown(self):
+        '''Clean up after each test_* function'''
+        global remote_server
+        global initiatorname
+
+        # If remote server is setup, convert back to manual, logout, remove
+        # testlib configs and restart (in that order)
+        if remote_server != '':
+            testlib.cmd(['iscsiadm', '-m', 'node', '--targetname', initiatorname, '-p', '%s:3260' % remote_server, '--op=update', '--name', 'node.startup', '--value=manual'])
+            testlib.cmd(['iscsiadm', '-m', 'node', '--targetname', initiatorname, '-p', '%s:3260' % remote_server, '--op=update', '--name',  'node.conn[0].startup', '--value=manual'])
+            testlib.cmd(['iscsiadm', '-m', 'node', '--targetname', initiatorname, '-p', '%s:3260' % remote_server, '--logout'])
+
+        testlib.config_restore(self.initiatorname_iscsi)
+        testlib.config_restore(self.iscsid_conf)
+        self.daemon.restart()
+
+    def test_daemon(self):
+        '''Test iscsid'''
+        self.assertTrue(self.daemon.stop())
+        time.sleep(2)
+        self.assertFalse(testlib.check_pidfile(self.exe, self.pidfile))
+
+        self.assertTrue(self.daemon.start())
+        time.sleep(2)
+        self.assertTrue(testlib.check_pidfile(os.path.basename(self.exe), self.pidfile))
+
+        self.assertTrue(self.daemon.restart())
+        time.sleep(2)
+        self.assertTrue(testlib.check_pidfile(os.path.basename(self.exe), self.pidfile))
+
+    def test_discovery_with_server(self):
+        '''Test iscsi_discovery to remote server'''
+        global remote_server
+        global username
+        global password
+        global username_in
+        global password_in
+        global initiatorname
+
+        if remote_server == '':
+            return self._skipped("--remote-server not specified")
+
+        contents = '''
+InitiatorName=%s
+InitiatorAlias=ubuntu
+''' % (initiatorname)
+        testlib.config_replace(self.initiatorname_iscsi, contents, True)
+
+        contents = '''
+node.session.auth.authmethod = CHAP
+node.session.auth.username = %s
+node.session.auth.password = %s
+node.session.auth.username_in = %s
+node.session.auth.password_in = %s
+
+discovery.sendtargets.auth.authmethod = CHAP
+discovery.sendtargets.auth.username = %s
+discovery.sendtargets.auth.password = %s
+discovery.sendtargets.auth.username_in = %s
+discovery.sendtargets.auth.password_in = %s
+''' % (username, password, username_in, password_in, username, password, username_in, password_in)
+        testlib.config_replace(self.iscsid_conf, contents, True)
+
+        self.assertTrue(self.daemon.restart())
+        time.sleep(2)
+        self.assertTrue(testlib.check_pidfile(os.path.basename(self.exe), self.pidfile))
+
+        rc, report = testlib.cmd(["/sbin/iscsi_discovery", remote_server])
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        self.assertEquals(expected, rc, result + report)
+        for i in ['starting discovery to %s' % remote_server,
+                  'Testing iser-login to target %s portal %s' % (initiatorname, remote_server),
+                  'starting to test tcp-login to target %s portal %s' % (initiatorname, remote_server),
+                  'discovered 1 targets at %s, connected to 1' % remote_server]:
+            result = "Could not find '%s' in report:\n" % i
+            self.assertTrue(i in report, result + report)
+
+if __name__ == '__main__':
+    import optparse
+    parser = optparse.OptionParser()
+    parser.add_option("-v", "--verbose", dest="verbose", help="Verbose", action="store_true")
+    parser.add_option("-s", "--remote-server", dest="remote_server", help="Specify host with available iSCSI volumes", metavar="HOST")
+
+    parser.add_option("-n", "--initiatorname", dest="initiatorname", help="Specify initiatorname for use with --remote-server", metavar="NAME")
+
+    parser.add_option("--password", dest="password", help="Specify password for use with --remote-server", metavar="PASS")
+    parser.add_option("--password-in", dest="password_in", help="Specify password_in for use with --remote-server", metavar="PASS")
+
+    parser.add_option("--username", dest="username", help="Specify username for use with --remote-server", metavar="USER")
+    parser.add_option("--username-in", dest="username_in", help="Specify username_in for use with --remote-server", metavar="USER")
+
+    (options, args) = parser.parse_args()
+
+    if options.remote_server:
+        remote_server = options.remote_server
+
+        if options.username:
+            username = options.username
+        if options.password:
+            password = options.password
+        if options.username_in:
+            username_in = options.username_in
+        if options.password_in:
+            password_in = options.password_in
+        if options.initiatorname:
+            initiatorname = options.initiatorname
+        print "Connecting to remote server with:"
+        print " host = %s " % remote_server
+        print ' initiatorname = %s' % initiatorname
+        print ' username = %s' % username
+        print ' password = %s' % password
+        print ' username_in = %s' % username_in
+        print ' password_in = %s' % password_in
+
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(OpenIscsiTest))
+    rc = unittest.TextTestRunner(verbosity=2).run(suite)
+    if not rc.wasSuccessful():
+        sys.exit(1)

=== added file 'debian/tests/testlib.py'
--- debian/tests/testlib.py	1970-01-01 00:00:00 +0000
+++ debian/tests/testlib.py	2013-05-29 11:47:45 +0000
@@ -0,0 +1,1144 @@
+#
+#    testlib.py quality assurance test script
+#    Copyright (C) 2008-2011 Canonical Ltd.
+#
+#    This library is free software; you can redistribute it and/or
+#    modify it under the terms of the GNU Library General Public
+#    License as published by the Free Software Foundation; either
+#    version 2 of the License.
+#
+#    This library is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#    Library General Public License for more details.
+#
+#    You should have received a copy of the GNU Library General Public
+#    License along with this program.  If not, see
+#    <http://www.gnu.org/licenses/>.
+#
+
+'''Common classes and functions for package tests.'''
+
+import string, random, crypt, subprocess, pwd, grp,  signal, time, unittest, tempfile, shutil, os, os.path, re, glob
+import sys, socket, gzip
+from stat import *
+from encodings import string_escape
+
+import warnings
+warnings.filterwarnings('ignore', message=r'.*apt_pkg\.TagFile.*', category=DeprecationWarning)
+try:
+    import apt_pkg
+    apt_pkg.InitSystem();
+except:
+    # On non-Debian system, fall back to simple comparison without debianisms
+    class apt_pkg(object):
+        def VersionCompare(one, two):
+            list_one = one.split('.')
+            list_two = two.split('.')
+            while len(list_one)>0 and len(list_two)>0:
+                if list_one[0] > list_two[0]:
+                    return 1
+                if list_one[0] < list_two[0]:
+                    return -1
+                list_one.pop(0)
+                list_two.pop(0)
+            return 0
+
+bogus_nxdomain = "208.69.32.132"
+
+# http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html
+# This is needed so that the subprocesses that produce endless output
+# actually quit when the reader goes away.
+import signal
+def subprocess_setup():
+    # Python installs a SIGPIPE handler by default. This is usually not what
+    # non-Python subprocesses expect.
+    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+class TimedOutException(Exception):
+    def __init__(self, value = "Timed Out"):
+        self.value = value
+    def __str__(self):
+        return repr(self.value)
+
+def _restore_backup(path):
+    pathbackup = path + '.autotest'
+    if os.path.exists(pathbackup):
+        shutil.move(pathbackup, path)
+
+def _save_backup(path):
+    pathbackup = path + '.autotest'
+    if os.path.exists(path) and not os.path.exists(pathbackup):
+        shutil.copy2(path, pathbackup)
+        # copy2 does not copy ownership, so do it here.
+        # Reference: http://docs.python.org/library/shutil.html
+        a = os.stat(path)
+        os.chown(pathbackup, a[4], a[5])
+
+def config_copydir(path):
+    if os.path.exists(path) and not os.path.isdir(path):
+        raise OSError, "'%s' is not a directory" % (path)
+    _restore_backup(path)
+
+    pathbackup = path + '.autotest'
+    if os.path.exists(path):
+        shutil.copytree(path, pathbackup, symlinks=True)
+
+def config_replace(path,contents,append=False):
+    '''Replace (or append) to a config file'''
+    _restore_backup(path)
+    if os.path.exists(path):
+        _save_backup(path)
+        if append:
+            contents = file(path).read() + contents
+    open(path, 'w').write(contents)
+
+def config_comment(path, field):
+    _save_backup(path)
+    contents = ""
+    for line in file(path):
+        if re.search("^\s*%s\s*=" % (field), line):
+            line = "#" + line
+        contents += line
+
+    open(path+'.new', 'w').write(contents)
+    os.rename(path+'.new', path)
+
+def config_set(path, field, value, spaces=True):
+    _save_backup(path)
+    contents = ""
+    if spaces==True:
+        setting = '%s = %s\n' % (field, value)
+    else:
+        setting = '%s=%s\n' % (field, value)
+    found = False
+    for line in file(path):
+        if re.search("^\s*%s\s*=" % (field), line):
+            found = True
+            line = setting
+        contents += line
+    if not found:
+        contents += setting
+
+    open(path+'.new', 'w').write(contents)
+    os.rename(path+'.new', path)
+
+def config_patch(path, patch, depth=1):
+    '''Patch a config file'''
+    _restore_backup(path)
+    _save_backup(path)
+
+    handle, name = mkstemp_fill(patch)
+    rc = subprocess.call(['/usr/bin/patch', '-p%s' %(depth), path], stdin=handle, stdout=subprocess.PIPE)
+    os.unlink(name)
+    if rc != 0:
+        raise Exception("Patch failed")
+
+def config_restore(path):
+    '''Rename a replaced config file back to its initial state'''
+    _restore_backup(path)
+
+def timeout(secs, f, *args):
+    def handler(signum, frame):
+        raise TimedOutException()
+
+    old = signal.signal(signal.SIGALRM, handler)
+    result = None
+    signal.alarm(secs)
+    try:
+        result = f(*args)
+    finally:
+        signal.alarm(0)
+        signal.signal(signal.SIGALRM, old)
+
+    return result
+
+def require_nonroot():
+    if os.geteuid() == 0:
+        print >>sys.stderr, "This series of tests should be run as a regular user with sudo access, not as root."
+        sys.exit(1)
+
+def require_root():
+    if os.geteuid() != 0:
+        print >>sys.stderr, "This series of tests should be run with root privileges (e.g. via sudo)."
+        sys.exit(1)
+
+def require_sudo():
+    if os.geteuid() != 0 or os.environ.get('SUDO_USER', None) == None:
+        print >>sys.stderr, "This series of tests must be run under sudo."
+        sys.exit(1)
+    if os.environ['SUDO_USER'] == 'root':
+        print >>sys.stderr, 'Please run this test using sudo from a regular user. (You ran sudo from root.)'
+        sys.exit(1)
+
+def random_string(length,lower=False):
+    '''Return a random string, consisting of ASCII letters, with given
+    length.'''
+
+    s = ''
+    selection = string.letters
+    if lower:
+        selection = string.lowercase
+    maxind = len(selection)-1
+    for l in range(length):
+        s += selection[random.randint(0, maxind)]
+    return s
+
+def mkstemp_fill(contents,suffix='',prefix='testlib-',dir=None):
+    '''As tempfile.mkstemp does, return a (file, name) pair, but with
+    prefilled contents.'''
+
+    handle, name = tempfile.mkstemp(suffix=suffix,prefix=prefix,dir=dir)
+    os.close(handle)
+    handle = file(name,"w+")
+    handle.write(contents)
+    handle.flush()
+    handle.seek(0)
+
+    return handle, name
+
+def create_fill(path, contents, mode=0644):
+    '''Safely create a page'''
+    # make the temp file in the same dir as the destination file so we
+    # don't get invalid cross-device link errors when we rename
+    handle, name = mkstemp_fill(contents, dir=os.path.dirname(path))
+    handle.close()
+    os.rename(name, path)
+    os.chmod(path, mode)
+
+def login_exists(login):
+    '''Checks whether the given login exists on the system.'''
+
+    try:
+        pwd.getpwnam(login)
+        return True
+    except KeyError:
+        return False
+
+def group_exists(group):
+    '''Checks whether the given login exists on the system.'''
+
+    try:
+        grp.getgrnam(group)
+        return True
+    except KeyError:
+        return False
+
+def recursive_rm(dirPath, contents_only=False):
+    '''recursively remove directory'''
+    names = os.listdir(dirPath)
+    for name in names:
+        path = os.path.join(dirPath, name)
+        if os.path.islink(path) or not os.path.isdir(path):
+            os.unlink(path)
+        else:
+            recursive_rm(path)
+    if contents_only == False:
+        os.rmdir(dirPath)
+
+def check_pidfile(exe, pidfile):
+    '''Checks if pid in pidfile is running'''
+    if not os.path.exists(pidfile):
+        return False
+
+    # get the pid
+    try:
+        fd = open(pidfile, 'r')
+        pid = fd.readline().rstrip('\n')
+        fd.close()
+    except:
+        return False
+
+    return check_pid(exe, pid)
+
+def check_pid(exe, pid):
+    '''Checks if pid is running'''
+    cmdline = "/proc/%s/cmdline" % (str(pid))
+    if not os.path.exists(cmdline):
+        return False
+
+    # get the command line
+    try:
+        fd = open(cmdline, 'r')
+        tmp = fd.readline().split('\0')
+        fd.close()
+    except:
+        return False
+
+    # this allows us to match absolute paths or just the executable name
+    if re.match('^' + exe + '$', tmp[0]) or \
+       re.match('.*/' + exe + '$', tmp[0]) or \
+       re.match('^' + exe + ': ', tmp[0]) or \
+       re.match('^\(' + exe + '\)', tmp[0]):
+        return True
+
+    return False
+
+def check_port(port, proto, ver=4):
+    '''Check if something is listening on the specified port.
+       WARNING: for some reason this does not work with a bind mounted /proc
+    '''
+    assert (port >= 1)
+    assert (port <= 65535)
+    assert (proto.lower() == "tcp" or proto.lower() == "udp")
+    assert (ver == 4 or ver == 6)
+
+    fn = "/proc/net/%s" % (proto)
+    if ver == 6:
+        fn += str(ver)
+
+    rc, report = cmd(['cat', fn])
+    assert (rc == 0)
+
+    hport = "%0.4x" % port
+
+    if re.search(': [0-9a-f]{8}:%s [0-9a-f]' % str(hport).lower(), report.lower()):
+        return True
+    return False
+
+def get_arch():
+    '''Get the current architecture'''
+    rc, report = cmd(['uname', '-m'])
+    assert (rc == 0)
+    return report.strip()
+
+def get_memory():
+    '''Gets total ram and swap'''
+    meminfo = "/proc/meminfo"
+    memtotal = 0
+    swaptotal = 0
+    if not os.path.exists(meminfo):
+        return (False, False)
+
+    try:
+        fd = open(meminfo, 'r')
+        for line in fd.readlines():
+            splitline = line.split()
+            if splitline[0] == 'MemTotal:':
+                memtotal = int(splitline[1])
+            elif splitline[0] == 'SwapTotal:':
+                swaptotal = int(splitline[1])
+        fd.close()
+    except:
+        return (False, False)
+
+    return (memtotal,swaptotal)
+
+def is_running_in_vm():
+    '''Check if running under a VM'''
+    # add other virtualization environments here
+    for search in ['QEMU Virtual CPU']:
+        rc, report = cmd_pipe(['dmesg'], ['grep', search])
+        if rc == 0:
+            return True
+    return False
+
+def ubuntu_release():
+    '''Get the Ubuntu release'''
+    f = "/etc/lsb-release"
+    try:
+        size = os.stat(f)[ST_SIZE]
+    except:
+        return "UNKNOWN"
+
+    if size > 1024*1024:
+        raise IOError, 'Could not open "%s" (too big)' % f
+
+    try:
+        fh = open("/etc/lsb-release", 'r')
+    except:
+        raise
+
+    lines = fh.readlines()
+    fh.close()
+
+    pat = re.compile(r'DISTRIB_CODENAME')
+    for line in lines:
+        if pat.search(line):
+            return line.split('=')[1].rstrip('\n').rstrip('\r')
+
+    return "UNKNOWN"
+
+def cmd(command, input = None, stderr = subprocess.STDOUT, stdout = subprocess.PIPE, stdin = None, timeout = None):
+    '''Try to execute given command (array) and return its stdout, or return
+    a textual error if it failed.'''
+
+    try:
+        sp = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, close_fds=True, preexec_fn=subprocess_setup)
+    except OSError, e:
+        return [127, str(e)]
+
+    out, outerr = sp.communicate(input)
+    # Handle redirection of stdout
+    if out == None:
+        out = ''
+    # Handle redirection of stderr
+    if outerr == None:
+        outerr = ''
+    return [sp.returncode,out+outerr]
+
+def cmd_pipe(command1, command2, input = None, stderr = subprocess.STDOUT, stdin = None):
+    '''Try to pipe command1 into command2.'''
+    try:
+        sp1 = subprocess.Popen(command1, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, close_fds=True)
+        sp2 = subprocess.Popen(command2, stdin=sp1.stdout, stdout=subprocess.PIPE, stderr=stderr, close_fds=True)
+    except OSError, e:
+        return [127, str(e)]
+
+    out = sp2.communicate(input)[0]
+    return [sp2.returncode,out]
+
+def cwd_has_enough_space(cdir, total_bytes):
+    '''Determine if the partition of the current working directory has 'bytes'
+       free.'''
+    rc, df_output = cmd(['df'])
+    result = 'Got exit code %d, expected %d\n' % (rc, 0)
+    if rc != 0:
+        return False
+
+    kb = total_bytes / 1024
+
+    mounts = dict()
+    for line in df_output.splitlines():
+        if '/' not in line:
+            continue
+        tmp = line.split()
+        mounts[tmp[5]] = int(tmp[3])
+
+    cdir = os.getcwd()
+    while cdir != '/':
+        if not mounts.has_key(cdir):
+            cdir = os.path.dirname(cdir)
+            continue
+        if kb < mounts[cdir]:
+            return True
+        else:
+            return False
+
+    if kb < mounts['/']:
+        return True
+
+    return False
+
+def get_md5(filename):
+    '''Gets the md5sum of the file specified'''
+
+    (rc, report) = cmd(["/usr/bin/md5sum", "-b", filename])
+    expected = 0
+    assert (expected == rc)
+
+    return report.split(' ')[0]
+
+def dpkg_compare_installed_version(pkg, check, version):
+    '''Gets the version for the installed package, and compares it to the
+       specified version.
+    '''
+    (rc, report) = cmd(["/usr/bin/dpkg", "-s", pkg])
+    assert (rc == 0)
+    assert ("Status: install ok installed" in report)
+    installed_version = ""
+    for line in report.splitlines():
+        if line.startswith("Version: "):
+            installed_version = line.split()[1]
+
+    assert (installed_version != "")
+
+    (rc, report) = cmd(["/usr/bin/dpkg", "--compare-versions", installed_version, check, version])
+    assert (rc == 0 or rc == 1)
+    if rc == 0:
+        return True
+    return False
+
+def prepare_source(source, builder, cached_src, build_src, patch_system):
+    '''Download and unpack source package, installing necessary build depends,
+       adjusting the permissions for the 'builder' user, and returning the
+       directory of the unpacked source. Patch system can be one of:
+       - cdbs
+       - dpatch
+       - quilt
+       - quiltv3
+       - None (not the string)
+
+       This is normally used like this:
+
+       def setUp(self):
+           ...
+           self.topdir = os.getcwd()
+           self.cached_src = os.path.join(os.getcwd(), "source")
+           self.tmpdir = tempfile.mkdtemp(prefix='testlib', dir='/tmp')
+           self.builder = testlib.TestUser()
+           testlib.cmd(['chgrp', self.builder.login, self.tmpdir])
+           os.chmod(self.tmpdir, 0775)
+
+       def tearDown(self):
+           ...
+           self.builder = None
+           self.topdir = os.getcwd()
+           if os.path.exists(self.tmpdir):
+               testlib.recursive_rm(self.tmpdir)
+
+       def test_suite_build(self):
+           ...
+           build_dir = testlib.prepare_source('foo', \
+                                         self.builder, \
+                                         self.cached_src, \
+                                         os.path.join(self.tmpdir, \
+                                           os.path.basename(self.cached_src)),
+                                         "quilt")
+           os.chdir(build_dir)
+
+           # Example for typical build, adjust as necessary
+           print ""
+           print "  make clean"
+           rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make', 'clean'])
+
+           print "  configure"
+           rc, report = testlib.cmd(['sudo', '-u', self.builder.login, './configure', '--prefix=%s' % self.tmpdir, '--enable-debug'])
+
+           print "  make (will take a while)"
+           rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make'])
+
+           print "  make check (will take a while)",
+           rc, report = testlib.cmd(['sudo', '-u', self.builder.login, 'make', 'check'])
+           expected = 0
+           result = 'Got exit code %d, expected %d\n' % (rc, expected)
+           self.assertEquals(expected, rc, result + report)
+
+        def test_suite_cleanup(self):
+            ...
+            if os.path.exists(self.cached_src):
+                testlib.recursive_rm(self.cached_src)
+
+       It is up to the caller to clean up cached_src and build_src (as in the
+       above example, often the build_src is in a tmpdir that is cleaned in
+       tearDown() and the cached_src is cleaned in a one time clean-up
+       operation (eg 'test_suite_cleanup()) which must be run after the build
+       suite test (obviously).
+       '''
+
+    # Make sure we have a clean slate
+    assert (os.path.exists(os.path.dirname(build_src)))
+    assert (not os.path.exists(build_src))
+
+    cdir = os.getcwd()
+    if os.path.exists(cached_src):
+        shutil.copytree(cached_src, build_src)
+        os.chdir(build_src)
+    else:
+        # Only install the build dependencies on the initial setup
+        rc, report = cmd(['apt-get','-y','--force-yes','build-dep',source])
+        assert (rc == 0)
+
+        os.makedirs(build_src)
+        os.chdir(build_src)
+
+        # These are always needed
+        pkgs = ['build-essential', 'dpkg-dev', 'fakeroot']
+        rc, report = cmd(['apt-get','-y','--force-yes','install'] + pkgs)
+        assert (rc == 0)
+
+        rc, report = cmd(['apt-get','source',source])
+        assert (rc == 0)
+        shutil.copytree(build_src, cached_src)
+
+    unpacked_dir = os.path.join(build_src, glob.glob('%s-*' % source)[0])
+
+    # Now apply the patches. Do it here so that we don't mess up our cached
+    # sources.
+    os.chdir(unpacked_dir)
+    assert (patch_system in ['cdbs', 'dpatch', 'quilt', 'quiltv3', None])
+    if patch_system != None and patch_system != "quiltv3":
+        if patch_system == "quilt":
+            os.environ.setdefault('QUILT_PATCHES','debian/patches')
+            rc, report = cmd(['quilt', 'push', '-a'])
+            assert (rc == 0)
+        elif patch_system == "cdbs":
+            rc, report = cmd(['./debian/rules', 'apply-patches'])
+            assert (rc == 0)
+        elif patch_system == "dpatch":
+            rc, report = cmd(['dpatch', 'apply-all'])
+            assert (rc == 0)
+
+    cmd(['chown', '-R', '%s:%s' % (builder.uid, builder.gid), build_src])
+    os.chdir(cdir)
+
+    return unpacked_dir
+
+def _aa_status():
+    '''Get aa-status output'''
+    exe = "/usr/sbin/aa-status"
+    assert (os.path.exists(exe))
+    if os.geteuid() == 0:
+        return cmd([exe])
+    return cmd(['sudo', exe])
+
+def is_apparmor_loaded(path):
+    '''Check if profile is loaded'''
+    rc, report = _aa_status()
+    if rc != 0:
+        return False
+
+    for line in report.splitlines():
+        if line.endswith(path):
+            return True
+    return False
+
+def is_apparmor_confined(path):
+    '''Check if application is confined'''
+    rc, report = _aa_status()
+    if rc != 0:
+        return False
+
+    for line in report.splitlines():
+        if re.search('%s \(' % path, line):
+            return True
+    return False
+
+def check_apparmor(path, first_ubuntu_release, is_running=True):
+    '''Check if path is loaded and confined for everything higher than the
+       first Ubuntu release specified.
+
+       Usage:
+        rc, report = testlib.check_apparmor('/usr/sbin/foo', 8.04, is_running=True)
+        if rc < 0:
+            return self._skipped(report)
+
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        self.assertEquals(expected, rc, result + report)
+     '''
+    global manager
+    rc = -1
+
+    if manager.lsb_release["Release"] < first_ubuntu_release:
+        return (rc, "Skipped apparmor check")
+
+    if not os.path.exists('/sbin/apparmor_parser'):
+        return (rc, "Skipped (couldn't find apparmor_parser)")
+
+    rc = 0
+    msg = ""
+    if not is_apparmor_loaded(path):
+        rc = 1
+        msg = "Profile not loaded for '%s'" % path
+
+    # this check only makes sense it the 'path' is currently executing
+    if is_running and rc == 0 and not is_apparmor_confined(path):
+        rc = 1
+        msg = "'%s' is not running in enforce mode" % path
+
+    return (rc, msg)
+
+def get_gcc_version(gcc, full=True):
+    gcc_version = 'none'
+    if not gcc.startswith('/'):
+        gcc = '/usr/bin/%s' % (gcc)
+    if os.path.exists(gcc):
+        gcc_version = 'unknown'
+        lines = cmd([gcc,'-v'])[1].strip().splitlines()
+        version_lines = [x for x in lines if x.startswith('gcc version')]
+        if len(version_lines) == 1:
+            gcc_version = " ".join(version_lines[0].split()[2:])
+    if not full:
+        return gcc_version.split()[0]
+    return gcc_version
+
+def is_kdeinit_running():
+    '''Test if kdeinit is running'''
+    # applications that use kdeinit will spawn it if it isn't running in the
+    # test. This is a problem because it does not exit. This is a helper to
+    # check for it.
+    rc, report = cmd(['ps', 'x'])
+    if 'kdeinit4 Running' not in report:
+        print >>sys.stderr, ("kdeinit not running (you may start/stop any KDE application then run this script again)")
+        return False
+    return True
+
+def get_pkgconfig_flags(libs=[]):
+    '''Find pkg-config flags for libraries'''
+    assert (len(libs) > 0)
+    rc, pkg_config = cmd(['pkg-config', '--cflags', '--libs'] + libs)
+    expected = 0
+    if rc != expected:
+        print >>sys.stderr, 'Got exit code %d, expected %d\n' % (rc, expected)
+    assert(rc == expected)
+    return pkg_config.split()
+
+class TestDaemon:
+    '''Helper class to manage daemons consistently'''
+    def __init__(self, init):
+        '''Setup daemon attributes'''
+        self.initscript = init
+
+    def start(self):
+        '''Start daemon'''
+        rc, report = cmd([self.initscript, 'start'])
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        time.sleep(2)
+        if expected != rc:
+            return (False, result + report)
+
+        if "fail" in report:
+            return (False, "Found 'fail' in report\n" + report)
+
+        return (True, "")
+
+    def stop(self):
+        '''Stop daemon'''
+        rc, report = cmd([self.initscript, 'stop'])
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        if expected != rc:
+            return (False, result + report)
+
+        if "fail" in report:
+            return (False, "Found 'fail' in report\n" + report)
+
+        return (True, "")
+
+    def reload(self):
+        '''Reload daemon'''
+        rc, report = cmd([self.initscript, 'force-reload'])
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        if expected != rc:
+            return (False, result + report)
+
+        if "fail" in report:
+            return (False, "Found 'fail' in report\n" + report)
+
+        return (True, "")
+
+    def restart(self):
+        '''Restart daemon'''
+        (res, str) = self.stop()
+        if not res:
+            return (res, str)
+
+        (res, str) = self.start()
+        if not res:
+            return (res, str)
+
+        return (True, "")
+
+    def status(self):
+        '''Check daemon status'''
+        rc, report = cmd([self.initscript, 'status'])
+        expected = 0
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        if expected != rc:
+            return (False, result + report)
+
+        if "fail" in report:
+            return (False, "Found 'fail' in report\n" + report)
+
+        return (True, "")
+
+class TestlibManager(object):
+    '''Singleton class used to set up per-test-run information'''
+    def __init__(self):
+        # Set glibc aborts to dump to stderr instead of the tty so test output
+        # is more sane.
+        os.environ.setdefault('LIBC_FATAL_STDERR_','1')
+
+        # check verbosity
+        self.verbosity = False
+        if (len(sys.argv) > 1 and '-v' in sys.argv[1:]):
+            self.verbosity = True
+
+        # Load LSB release file
+        self.lsb_release = dict()
+        if not os.path.exists('/usr/bin/lsb_release') and not os.path.exists('/bin/lsb_release'):
+            raise OSError, "Please install 'lsb-release'"
+        for line in subprocess.Popen(['lsb_release','-a'],stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()[0].splitlines():
+            field, value = line.split(':',1)
+            value=value.strip()
+            field=field.strip()
+            # Convert numerics
+            try:
+                value = float(value)
+            except:
+                pass
+            self.lsb_release.setdefault(field,value)
+
+        # FIXME: hack OEM releases into known-Ubuntu versions
+        if self.lsb_release['Distributor ID'] == "HP MIE (Mobile Internet Experience)":
+            if self.lsb_release['Release'] == 1.0:
+                self.lsb_release['Distributor ID'] = "Ubuntu"
+                self.lsb_release['Release'] = 8.04
+            else:
+                raise OSError, "Unknown version of HP MIE"
+
+        # FIXME: hack to assume a most-recent release if we're not
+        # running under Ubuntu.
+        if self.lsb_release['Distributor ID'] not in ["Ubuntu","Linaro"]:
+            self.lsb_release['Release'] = 10000
+        # Adjust Linaro release to pretend to be Ubuntu
+        if self.lsb_release['Distributor ID'] in ["Linaro"]:
+       	    self.lsb_release['Distributor ID'] = "Ubuntu"
+            self.lsb_release['Release'] -= 0.01
+
+        # Load arch
+        if not os.path.exists('/usr/bin/dpkg'):
+            machine = cmd(['uname','-m'])[1].strip()
+            if machine.endswith('86'):
+                self.dpkg_arch = 'i386'
+            elif machine.endswith('_64'):
+                self.dpkg_arch = 'amd64'
+            elif machine.startswith('arm'):
+                self.dpkg_arch = 'armel'
+            else:
+                raise ValueError, "Unknown machine type '%s'" % (machine)
+        else:
+            self.dpkg_arch = cmd(['dpkg','--print-architecture'])[1].strip()
+
+        # Find kernel version
+        self.kernel_is_ubuntu = False
+        self.kernel_version_signature = None
+        self.kernel_version = cmd(["uname","-r"])[1].strip()
+        versig = '/proc/version_signature'
+        if os.path.exists(versig):
+            self.kernel_is_ubuntu = True
+            self.kernel_version_signature = file(versig).read().strip()
+            self.kernel_version_ubuntu = self.kernel_version
+        elif os.path.exists('/usr/bin/dpkg'):
+            # this can easily be inaccurate but is only an issue for Dapper
+            rc, out = cmd(['dpkg','-l','linux-image-%s' % (self.kernel_version)])
+            if rc == 0:
+                self.kernel_version_signature = out.strip().split('\n').pop().split()[2]
+                self.kernel_version_ubuntu = self.kernel_version_signature
+        if self.kernel_version_signature == None:
+            # Attempt to fall back to something for non-Debian-based
+            self.kernel_version_signature = self.kernel_version
+            self.kernel_version_ubuntu = self.kernel_version
+        # Build ubuntu version without hardware suffix
+        try:
+            self.kernel_version_ubuntu = "-".join([x for x in self.kernel_version_signature.split(' ')[1].split('-') if re.search('^[0-9]', x)])
+        except:
+            pass
+
+        # Find gcc version
+        self.gcc_version = get_gcc_version('gcc')
+
+        # Find libc
+        self.path_libc = [x.split()[2] for x in cmd(['ldd','/bin/ls'])[1].splitlines() if x.startswith('\tlibc.so.')][0]
+
+        # Report self
+        if self.verbosity:
+            kernel = self.kernel_version_ubuntu
+            if kernel != self.kernel_version_signature:
+                kernel += " (%s)" % (self.kernel_version_signature)
+            print >>sys.stdout, "Running test: '%s' distro: '%s %.2f' kernel: '%s' arch: '%s' uid: %d/%d SUDO_USER: '%s')" % ( \
+                sys.argv[0],
+                self.lsb_release['Distributor ID'],
+                self.lsb_release['Release'],
+                kernel,
+                self.dpkg_arch,
+                os.geteuid(), os.getuid(),
+                os.environ.get('SUDO_USER', ''))
+            sys.stdout.flush()
+
+        # Additional heuristics
+        #if os.environ.get('SUDO_USER', os.environ.get('USER', '')) in ['mdeslaur']:
+        #    sys.stdout.write("Replying to Marc Deslauriers in http://launchpad.net/bugs/%d: " % random.randint(600000, 980000))
+        #    sys.stdout.flush()
+        #    time.sleep(0.5)
+        #    sys.stdout.write("destroyed\n")
+        #    time.sleep(0.5)
+
+    def hello(self, msg):
+        print >>sys.stderr, "Hello from %s" % (msg)
+# The central instance
+manager = TestlibManager()
+
+class TestlibCase(unittest.TestCase):
+    def __init__(self, *args):
+        '''This is called for each TestCase test instance, which isn't much better
+           than SetUp.'''
+
+        unittest.TestCase.__init__(self, *args)
+
+        # Attach to and duplicate dicts from manager singleton
+        self.manager = manager
+        #self.manager.hello(repr(self) + repr(*args))
+        self.my_verbosity = self.manager.verbosity
+        self.lsb_release = self.manager.lsb_release
+        self.dpkg_arch = self.manager.dpkg_arch
+        self.kernel_version = self.manager.kernel_version
+        self.kernel_version_signature = self.manager.kernel_version_signature
+        self.kernel_version_ubuntu = self.manager.kernel_version_ubuntu
+        self.kernel_is_ubuntu = self.manager.kernel_is_ubuntu
+        self.gcc_version = self.manager.gcc_version
+        self.path_libc = self.manager.path_libc
+
+    def version_compare(self, one, two):
+        return apt_pkg.VersionCompare(one,two)
+
+    def assertFileType(self, filename, filetype):
+        '''Checks the file type of the file specified'''
+
+        (rc, report, out) = self._testlib_shell_cmd(["/usr/bin/file", "-b", filename])
+        out = out.strip()
+        expected = 0
+        # Absolutely no idea why this happens on Hardy
+        if self.lsb_release['Release'] == 8.04 and rc == 255 and len(out) > 0:
+            rc = 0
+        result = 'Got exit code %d, expected %d:\n%s\n' % (rc, expected, report)
+        self.assertEquals(expected, rc, result)
+
+        filetype = '^%s$' % (filetype)
+        result = 'File type reported by file: [%s], expected regex: [%s]\n' % (out, filetype)
+        self.assertNotEquals(None, re.search(filetype, out), result)
+
+    def yank_commonname_from_cert(self, certfile):
+        '''Extract the commonName from a given PEM'''
+        rc, out = cmd(['openssl','asn1parse','-in',certfile])
+        if rc == 0:
+            ready = False
+            for line in out.splitlines():
+                if ready:
+                    return line.split(':')[-1]
+                if ':commonName' in line:
+                    ready = True
+        return socket.getfqdn()
+
+    def announce(self, text):
+        if self.my_verbosity:
+            print >>sys.stdout, "(%s) " % (text),
+            sys.stdout.flush()
+
+    def make_clean(self):
+        rc, output = self.shell_cmd(['make','clean'])
+        self.assertEquals(rc, 0, output)
+
+    def get_makefile_compiler(self):
+        # Find potential compiler name
+        compiler = 'gcc'
+        if os.path.exists('Makefile'):
+            for line in open('Makefile'):
+                if line.startswith('CC') and '=' in line:
+                    items = [x.strip() for x in line.split('=')]
+                    if items[0] == 'CC':
+                        compiler = items[1]
+                        break
+        return compiler
+
+    def make_target(self, target, expected=0):
+        '''Compile a target and report output'''
+
+        compiler = self.get_makefile_compiler()
+        rc, output = self.shell_cmd(['make',target])
+        self.assertEquals(rc, expected, 'rc(%d)!=%d:\n' % (rc, expected) + output)
+        self.assertTrue('%s ' % (compiler) in output, 'Expected "%s":' % (compiler) + output)
+        return output
+
+    # call as   return testlib.skipped()
+    def _skipped(self, reason=""):
+        '''Provide a visible way to indicate that a test was skipped'''
+        if reason != "":
+            reason = ': %s' % (reason)
+        self.announce("skipped%s" % (reason))
+        return False
+
+    def _testlib_shell_cmd(self,args,stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT):
+        argstr = "'" + "', '".join(args).strip() + "'"
+        rc, out = cmd(args,stdin=stdin,stdout=stdout,stderr=stderr)
+        report = 'Command: ' + argstr + '\nOutput:\n' + out
+        return rc, report, out
+
+    def shell_cmd(self, args, stdin=None):
+        return cmd(args,stdin=stdin)
+
+    def assertShellExitEquals(self, expected, args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, msg=""):
+        '''Test a shell command matches a specific exit code'''
+        rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
+        result = 'Got exit code %d, expected %d\n' % (rc, expected)
+        self.assertEquals(expected, rc, msg + result + report)
+
+    def assertShellExitNotEquals(self, unwanted, args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, msg=""):
+        '''Test a shell command doesn't match a specific exit code'''
+        rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
+        result = 'Got (unwanted) exit code %d\n' % rc
+        self.assertNotEquals(unwanted, rc, msg + result + report)
+
+    def assertShellOutputContains(self, text, args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, msg="", invert=False):
+        '''Test a shell command contains a specific output'''
+        rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
+        result = 'Got exit code %d.  Looking for text "%s"\n' % (rc, text)
+        if not invert:
+            self.assertTrue(text in out, msg + result + report)
+        else:
+            self.assertFalse(text in out, msg + result + report)
+
+    def assertShellOutputEquals(self, text, args, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, msg="", invert=False, expected=None):
+        '''Test a shell command matches a specific output'''
+        rc, report, out = self._testlib_shell_cmd(args, stdin=stdin, stdout=stdout, stderr=stderr)
+        result = 'Got exit code %d. Looking for exact text "%s" (%s)\n' % (rc, text, " ".join(args))
+        if not invert:
+            self.assertEquals(text, out, msg + result + report)
+        else:
+            self.assertNotEquals(text, out, msg + result + report)
+        if expected != None:
+            result = 'Got exit code %d. Expected %d (%s)\n' % (rc, expected, " ".join(args))
+            self.assertEquals(rc, expected, msg + result + report)
+
+    def _word_find(self, report, content, invert=False):
+        '''Check for a specific string'''
+        if invert:
+            warning = 'Found "%s"\n' % content
+            self.assertTrue(content not in report, warning + report)
+        else:
+            warning = 'Could not find "%s"\n' % content
+            self.assertTrue(content in report, warning + report)
+
+    def _test_sysctl_value(self, path, expected, msg=None, exists=True):
+        sysctl = '/proc/sys/%s' % (path)
+        self.assertEquals(exists, os.path.exists(sysctl), sysctl)
+        value = None
+        if exists:
+            value = int(file(sysctl).read())
+            report = "%s is not %d: %d" % (sysctl, expected, value)
+            if msg:
+                report += " (%s)" % (msg)
+            self.assertEquals(value, expected, report)
+        return value
+
+    def set_sysctl_value(self, path, desired):
+        sysctl = '/proc/sys/%s' % (path)
+        self.assertTrue(os.path.exists(sysctl),"%s does not exist" % (sysctl))
+        file(sysctl,'w').write(str(desired))
+        self._test_sysctl_value(path, desired)
+
+    def kernel_at_least(self, introduced):
+        return self.version_compare(self.kernel_version_ubuntu,
+                                    introduced) >= 0
+
+    def kernel_claims_cve_fixed(self, cve):
+        changelog = "/usr/share/doc/linux-image-%s/changelog.Debian.gz" % (self.kernel_version)
+        if os.path.exists(changelog):
+            for line in gzip.open(changelog):
+                if cve in line and not "revert" in line and not "Revert" in line:
+                    return True
+        return False
+
+class TestGroup:
+    '''Create a temporary test group and remove it again in the dtor.'''
+
+    def __init__(self, group=None, lower=False):
+        '''Create a new group'''
+
+        self.group = None
+        if group:
+            if group_exists(group):
+                raise ValueError, 'group name already exists'
+        else:
+            while(True):
+                group = random_string(7,lower=lower)
+                if not group_exists(group):
+                    break
+
+        assert subprocess.call(['groupadd',group]) == 0
+        self.group = group
+        g = grp.getgrnam(self.group)
+        self.gid = g[2]
+
+    def __del__(self):
+        '''Remove the created group.'''
+
+        if self.group:
+            rc, report = cmd(['groupdel', self.group])
+            assert rc == 0
+
+class TestUser:
+    '''Create a temporary test user and remove it again in the dtor.'''
+
+    def __init__(self, login=None, home=True, group=None, uidmin=None, lower=False, shell=None):
+        '''Create a new user account with a random password.
+
+        By default, the login name is random, too, but can be explicitly
+        specified with 'login'. By default, a home directory is created, this
+        can be suppressed with 'home=False'.'''
+
+        self.login = None
+
+        if os.geteuid() != 0:
+            raise ValueError, "You must be root to run this test"
+
+        if login:
+            if login_exists(login):
+                raise ValueError, 'login name already exists'
+        else:
+            while(True):
+                login = 't' + random_string(7,lower=lower)
+                if not login_exists(login):
+                    break
+
+        self.salt = random_string(2)
+        self.password = random_string(8,lower=lower)
+        self.crypted = crypt.crypt(self.password, self.salt)
+
+        creation = ['useradd', '-p', self.crypted]
+        if home:
+            creation += ['-m']
+        if group:
+            creation += ['-G',group]
+        if uidmin:
+            creation += ['-K','UID_MIN=%d'%uidmin]
+        if shell:
+            creation += ['-s',shell]
+        creation += [login]
+        assert subprocess.call(creation) == 0
+        # Set GECOS
+        assert subprocess.call(['usermod','-c','Buddy %s' % (login),login]) == 0
+
+        self.login = login
+        p = pwd.getpwnam(self.login)
+        self.uid   = p[2]
+        self.gid   = p[3]
+        self.gecos = p[4]
+        self.home  = p[5]
+        self.shell = p[6]
+
+    def __del__(self):
+        '''Remove the created user account.'''
+
+        if self.login:
+            # sanity check the login name so we don't accidentally wipe too much
+            if len(self.login)>3 and not '/' in self.login:
+                subprocess.call(['rm','-rf', '/home/'+self.login, '/var/mail/'+self.login])
+            rc, report = cmd(['userdel', '-f', self.login])
+            assert rc == 0
+
+    def add_to_group(self, group):
+        '''Add user to the specified group name'''
+        rc, report = cmd(['usermod', '-G', group, self.login])
+        if rc != 0:
+            print report
+        assert rc == 0
+
+# Timeout handler using alarm() from John P. Speno's Pythonic Avocado
+class TimeoutFunctionException(Exception):
+    """Exception to raise on a timeout"""
+    pass
+class TimeoutFunction:
+    def __init__(self, function, timeout):
+        self.timeout = timeout
+        self.function = function
+
+    def handle_timeout(self, signum, frame):
+        raise TimeoutFunctionException()
+
+    def __call__(self, *args, **kwargs):
+        old = signal.signal(signal.SIGALRM, self.handle_timeout)
+        signal.alarm(self.timeout)
+        try:
+            result = self.function(*args, **kwargs)
+        finally:
+            signal.signal(signal.SIGALRM, old)
+        signal.alarm(0)
+        return result
+
+def main():
+    print "hi"
+    unittest.main()

=== added file 'debian/tests/testsuite'
--- debian/tests/testsuite	1970-01-01 00:00:00 +0000
+++ debian/tests/testsuite	2013-05-29 11:47:45 +0000
@@ -0,0 +1,7 @@
+#!/bin/bash
+#-------------------
+# Testing open-iscsi
+#-------------------
+set -e
+python `dirname $0`/test-open-iscsi.py 2>&1
+

Reply via email to