← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:remove-hwdb into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:remove-hwdb into launchpad:master.

Commit message:
Remove the hardware database

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/390183

Since Ubuntu xenial, checkbox no longer uses Launchpad's hardware database, and the only new submissions are coming from older systems; the certification teams no longer pay attention to it.  We've been refusing new submissions since 2020-05-13 and have had no comments.

The actual data remains in the Launchpad production database and we can extract it manually if need be, but there's no longer any need to keep the application code around.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:remove-hwdb into launchpad:master.
diff --git a/lib/lp/_schema_circular_imports.py b/lib/lp/_schema_circular_imports.py
index 19bae81..6e2cbfc 100644
--- a/lib/lp/_schema_circular_imports.py
+++ b/lib/lp/_schema_circular_imports.py
@@ -84,17 +84,6 @@ from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
 from lp.code.interfaces.sourcepackagerecipebuild import (
     ISourcePackageRecipeBuild,
     )
-from lp.hardwaredb.interfaces.hwdb import (
-    IHWDBApplication,
-    IHWDevice,
-    IHWDeviceClass,
-    IHWDriver,
-    IHWDriverName,
-    IHWDriverPackageName,
-    IHWSubmission,
-    IHWSubmissionDevice,
-    IHWVendorID,
-    )
 from lp.registry.interfaces.commercialsubscription import (
     ICommercialSubscription,
     )
@@ -897,44 +886,6 @@ patch_entry_explicit_version(IDistroSeriesDifferenceComment, 'beta')
 # IGPGKey
 patch_entry_explicit_version(IGPGKey, 'beta')
 
-# IHWDBApplication
-patch_entry_explicit_version(IHWDBApplication, 'beta')
-patch_operations_explicit_version(
-    IHWDBApplication, 'beta', "deviceDriverOwnersAffectedByBugs", "devices",
-    "drivers", "hwInfoByBugRelatedUsers", "numDevicesInSubmissions",
-    "numOwnersOfDevice", "numSubmissionsWithDevice", "search", "vendorIDs")
-
-# IHWDevice
-patch_entry_explicit_version(IHWDevice, 'beta')
-patch_operations_explicit_version(
-    IHWDevice, 'beta', "getOrCreateDeviceClass", "getSubmissions",
-    "removeDeviceClass")
-
-# IHWDeviceClass
-patch_entry_explicit_version(IHWDeviceClass, 'beta')
-patch_operations_explicit_version(
-    IHWDeviceClass, 'beta', "delete")
-
-# IHWDriver
-patch_entry_explicit_version(IHWDriver, 'beta')
-patch_operations_explicit_version(
-    IHWDriver, 'beta', "getSubmissions")
-
-# IHWDriverName
-patch_entry_explicit_version(IHWDriverName, 'beta')
-
-# IHWDriverPackageName
-patch_entry_explicit_version(IHWDriverPackageName, 'beta')
-
-# IHWSubmission
-patch_entry_explicit_version(IHWSubmission, 'beta')
-
-# IHWSubmissionDevice
-patch_entry_explicit_version(IHWSubmissionDevice, 'beta')
-
-# IHWVendorID
-patch_entry_explicit_version(IHWVendorID, 'beta')
-
 # IHasBugs
 patch_entry_explicit_version(IHasBugs, 'beta')
 
diff --git a/lib/lp/app/browser/launchpad.py b/lib/lp/app/browser/launchpad.py
index e1a5c71..d31df20 100644
--- a/lib/lp/app/browser/launchpad.py
+++ b/lib/lp/app/browser/launchpad.py
@@ -105,7 +105,6 @@ from lp.code.interfaces.codehosting import IBazaarApplication
 from lp.code.interfaces.codeimport import ICodeImportSet
 from lp.code.interfaces.gitlookup import IGitLookup
 from lp.code.interfaces.gitrepository import IGitRepositorySet
-from lp.hardwaredb.interfaces.hwdb import IHWDBApplication
 from lp.layers import WebServiceLayer
 from lp.registry.enums import VCSType
 from lp.registry.interfaces.announcement import IAnnouncementSet
@@ -856,7 +855,6 @@ class LaunchpadRootNavigation(Navigation):
         '+countries': ICountrySet,
         'distros': IDistributionSet,
         '+git': IGitRepositorySet,
-        '+hwdb': IHWDBApplication,
         'karmaaction': IKarmaActionSet,
         '+imports': ITranslationImportQueue,
         '+languages': ILanguageSet,
diff --git a/lib/lp/app/interfaces/launchpad.py b/lib/lp/app/interfaces/launchpad.py
index 96a2f95..2ab8033 100644
--- a/lib/lp/app/interfaces/launchpad.py
+++ b/lib/lp/app/interfaces/launchpad.py
@@ -48,7 +48,6 @@ class ILaunchpadCelebrities(Interface):
     debian = Attribute("The Debian Distribution.")
     english = Attribute("The English language.")
     gnome_bugzilla = Attribute("The Gnome Bugzilla.")
-    hwdb_team = Attribute("The HWDB team.")
     janitor = Attribute("The Launchpad Janitor.")
     katie = Attribute("The Debian Auto-sync user.")
     launchpad = Attribute("The Launchpad project.")
diff --git a/lib/lp/app/utilities/celebrities.py b/lib/lp/app/utilities/celebrities.py
index cb1de65..35e6d8a 100644
--- a/lib/lp/app/utilities/celebrities.py
+++ b/lib/lp/app/utilities/celebrities.py
@@ -138,7 +138,6 @@ class LaunchpadCelebrities:
     debian = CelebrityDescriptor(IDistributionSet, 'debian')
     english = LanguageCelebrityDescriptor(ILanguageSet, 'en')
     gnome_bugzilla = CelebrityDescriptor(IBugTrackerSet, 'gnome-bugs')
-    hwdb_team = PersonCelebrityDescriptor('hwdb-team')
     janitor = PersonCelebrityDescriptor('janitor')
     katie = PersonCelebrityDescriptor('katie')
     launchpad = CelebrityDescriptor(IProductSet, 'launchpad')
diff --git a/lib/lp/configure.zcml b/lib/lp/configure.zcml
index a3e7c08..b77284d 100644
--- a/lib/lp/configure.zcml
+++ b/lib/lp/configure.zcml
@@ -29,7 +29,6 @@
     <include package="lp.buildmaster" />
     <include package="lp.code" />
     <include package="lp.coop.answersbugs" />
-    <include package="lp.hardwaredb" />
     <include package="lp.oci" />
     <include package="lp.snappy" />
     <include package="lp.soyuz" />
diff --git a/lib/lp/hardwaredb/__init__.py b/lib/lp/hardwaredb/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/browser/__init__.py b/lib/lp/hardwaredb/browser/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/browser/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/browser/configure.zcml b/lib/lp/hardwaredb/browser/configure.zcml
deleted file mode 100644
index ed130e5..0000000
--- a/lib/lp/hardwaredb/browser/configure.zcml
+++ /dev/null
@@ -1,65 +0,0 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope";
-    xmlns:browser="http://namespaces.zope.org/browser";
-    xmlns:i18n="http://namespaces.zope.org/i18n";
-    xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
-    i18n_domain="launchpad">
-    <browser:navigation
-        module="lp.hardwaredb.browser.hwdb"
-        classes="
-            HWDBApplicationNavigation"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"
-        path_expression="string:+hwdb"
-        parent_utility="lp.services.webapp.interfaces.ILaunchpadRoot"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWSystemFingerprint"
-        path_expression="string:+fingerprint/${fingerprint}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWSubmission"
-        path_expression="string:+submission/${submission_key}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:defaultView
-        for="lp.hardwaredb.interfaces.hwdb.IHWSubmission"
-        name="+text"/>
-    <browser:page
-        for="lp.hardwaredb.interfaces.hwdb.IHWSubmission"
-        class="lp.hardwaredb.browser.hwdb.HWDBSubmissionTextView"
-        permission="zope.Public"
-        name="+text"/>
-    <browser:page
-        for="lp.registry.interfaces.person.IPerson"
-        permission="zope.Public"
-        class="lp.hardwaredb.browser.hwdb.HWDBPersonSubmissionsView"
-        template="../templates/person-hwdb-submissions.pt"
-        name="+hwdb-submissions"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWVendorID"
-        path_expression="string:+hwvendorid/${id}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDevice"
-        path_expression="string:+device/${id}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDriver"
-        path_expression="string:+driver/${id}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDriverName"
-        path_expression="string:+drivername/${name}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDriverPackageName"
-        path_expression="string:+driverpackagename/${package_name}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWDeviceClass"
-        path_expression="string:+deviceclass/${id}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    <browser:url
-        for="lp.hardwaredb.interfaces.hwdb.IHWSubmissionDevice"
-        path_expression="string:+submissiondevice/${id}"
-        parent_utility="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-</configure>
diff --git a/lib/lp/hardwaredb/browser/hwdb.py b/lib/lp/hardwaredb/browser/hwdb.py
deleted file mode 100644
index 03bf04c..0000000
--- a/lib/lp/hardwaredb/browser/hwdb.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-__metaclass__ = type
-
-__all__ = [
-    'HWDBApplicationNavigation',
-    'HWDBFingerprintSetView',
-    'HWDBPersonSubmissionsView',
-    'HWDBSubmissionTextView',
-    ]
-
-from textwrap import dedent
-
-from zope.browserpage import ViewPageTemplateFile
-from zope.component import getUtility
-from zope.interface import implementer
-from zope.publisher.interfaces.browser import IBrowserPublisher
-
-from lp.app.errors import NotFoundError
-from lp.hardwaredb.interfaces.hwdb import (
-    IHWDBApplication,
-    IHWDeviceClassSet,
-    IHWDeviceSet,
-    IHWDriverSet,
-    IHWSubmissionDeviceSet,
-    IHWSubmissionSet,
-    IHWVendorIDSet,
-    )
-from lp.services.webapp import (
-    LaunchpadView,
-    Navigation,
-    stepthrough,
-    )
-from lp.services.webapp.batching import BatchNavigator
-from lp.services.webapp.interfaces import ILaunchBag
-
-
-class HWDBPersonSubmissionsView(LaunchpadView):
-    """View class for preseting HWDB submissions by a person."""
-
-    @property
-    def label(self):
-        return 'Hardware submissions for %s' % (self.context.title,)
-
-    @property
-    def page_title(self):
-        return "Hardware Database submissions by %s" % (self.context.title,)
-
-    def getAllBatched(self):
-        """Return the list of HWDB submissions made by this person."""
-        hw_submissionset = getUtility(IHWSubmissionSet)
-        submissions = hw_submissionset.getByOwner(self.context, self.user)
-        return BatchNavigator(submissions, self.request)
-
-    def userIsOwner(self):
-        """Return true, if self.context == self.user"""
-        return self.context == self.user
-
-
-class HWDBSubmissionTextView(LaunchpadView):
-    """Renders a HWDBSubmission in parseable text."""
-    def render(self):
-        data = {}
-        data["date_created"] = self.context.date_created
-        data["date_submitted"] = self.context.date_submitted
-        data["format"] = self.context.format.name
-
-        dar = self.context.distroarchseries
-        if dar:
-            data["distribution"] = dar.distroseries.distribution.name
-            data["distribution_series"] = dar.distroseries.version
-            data["architecture"] = dar.architecturetag
-        else:
-            data["distribution"] = "(unknown)"
-            data["distribution_series"] = "(unknown)"
-            data["architecture"] = "(unknown)"
-
-        data["system_fingerprint"] = (
-            self.context.system_fingerprint.fingerprint)
-        data["url"] = self.context.raw_submission.http_url
-
-        return dedent("""
-            Date-Created: %(date_created)s
-            Date-Submitted: %(date_submitted)s
-            Format: %(format)s
-            Distribution: %(distribution)s
-            Distribution-Series: %(distribution_series)s
-            Architecture: %(architecture)s
-            System: %(system_fingerprint)s
-            Submission URL: %(url)s""" % data)
-
-
-class HWDBApplicationNavigation(Navigation):
-    """Navigation class for HWDBSubmissionSet."""
-
-    usedfor = IHWDBApplication
-
-    @stepthrough('+submission')
-    def traverse_submission(self, name):
-        user = getUtility(ILaunchBag).user
-        submission = getUtility(IHWSubmissionSet).getBySubmissionKey(
-            name, user=user)
-        return submission
-
-    @stepthrough('+fingerprint')
-    def traverse_hwdb_fingerprint(self, name):
-        return HWDBFingerprintSetView(self.context, self.request, name)
-
-    @stepthrough('+device')
-    def traverse_device(self, id):
-        try:
-            id = int(id)
-        except ValueError:
-            raise NotFoundError('invalid value for ID: %r' % id)
-        return getUtility(IHWDeviceSet).getByID(id)
-
-    @stepthrough('+deviceclass')
-    def traverse_device_class(self, id):
-        try:
-            id = int(id)
-        except ValueError:
-            raise NotFoundError('invalid value for ID: %r' % id)
-        return getUtility(IHWDeviceClassSet).get(id)
-
-    @stepthrough('+driver')
-    def traverse_driver(self, id):
-        try:
-            id = int(id)
-        except ValueError:
-            raise NotFoundError('invalid value for ID: %r' % id)
-        return getUtility(IHWDriverSet).getByID(id)
-
-    @stepthrough('+submissiondevice')
-    def traverse_submissiondevice(self, id):
-        try:
-            id = int(id)
-        except ValueError:
-            raise NotFoundError('invalid value for ID: %r' % id)
-        return getUtility(IHWSubmissionDeviceSet).get(id)
-
-    @stepthrough('+hwvendorid')
-    def traverse_hw_vendor_id(self, id):
-        try:
-            id = int(id)
-        except ValueError:
-            raise NotFoundError('invalid value for ID: %r' % id)
-        return getUtility(IHWVendorIDSet).get(id)
-
-
-@implementer(IBrowserPublisher)
-class HWDBFingerprintSetView(LaunchpadView):
-    """View class for lists of HWDB submissions for a system fingerprint."""
-    label = page_title = "Hardware Database submissions for a fingerprint"
-
-    template = ViewPageTemplateFile(
-        '../templates/hwdb-fingerprint-submissions.pt')
-
-    def __init__(self, context,  request, system_name):
-        LaunchpadView.__init__(self, context, request)
-        self.system_name = system_name
-
-    def getAllBatched(self):
-        """A BatchNavigator instance with the submissions."""
-        submissions = getUtility(IHWSubmissionSet).getByFingerprintName(
-            self.system_name, self.user)
-        return BatchNavigator(submissions, self.request)
-
-    def browserDefault(self, request):
-        """See `IBrowserPublisher`."""
-        return self, ()
-
-    def showOwner(self, submission):
-        """Check if the owner can be shown in the list.
-        """
-        return (submission.owner is not None
-                and (submission.contactable
-                     or (submission.owner == self.user)))
diff --git a/lib/lp/hardwaredb/browser/tests/__init__.py b/lib/lp/hardwaredb/browser/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/browser/tests/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/browser/tests/test_views.py b/lib/lp/hardwaredb/browser/tests/test_views.py
deleted file mode 100644
index 5bc7dfa..0000000
--- a/lib/lp/hardwaredb/browser/tests/test_views.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""
-Run the view tests.
-"""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-import logging
-import os
-import unittest
-
-from lp.testing.layers import LaunchpadFunctionalLayer
-from lp.testing.systemdocs import (
-    LayeredDocFileSuite,
-    setUp,
-    tearDown,
-    )
-
-
-here = os.path.dirname(os.path.realpath(__file__))
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    testsdir = os.path.abspath(here)
-
-    # Add tests using default setup/teardown
-    filenames = [filename
-                 for filename in os.listdir(testsdir)
-                 if filename.endswith('.txt')]
-    # Sort the list to give a predictable order.
-    filenames.sort()
-    for filename in filenames:
-        path = filename
-        one_test = LayeredDocFileSuite(
-            path,
-            setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
-            layer=LaunchpadFunctionalLayer,
-            stdout_logging_level=logging.WARNING
-            )
-        suite.addTest(one_test)
-
-    return suite
diff --git a/lib/lp/hardwaredb/configure.zcml b/lib/lp/hardwaredb/configure.zcml
deleted file mode 100644
index ffefc0c..0000000
--- a/lib/lp/hardwaredb/configure.zcml
+++ /dev/null
@@ -1,193 +0,0 @@
-<configure
-    xmlns="http://namespaces.zope.org/zope";
-    xmlns:browser="http://namespaces.zope.org/browser";
-    xmlns:i18n="http://namespaces.zope.org/i18n";
-    xmlns:webservice="http://namespaces.canonical.com/webservice";
-    xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
-    i18n_domain="launchpad">
-    <include
-        package=".browser"/>
-    
-    <!-- HWDBApplication -->
-    
-    <class
-        class="lp.systemhomes.HWDBApplication">
-        <allow
-            interface="lazr.restful.interfaces.ITopLevelEntryLink"/>
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    </class>
-    <securedutility
-        class="lp.systemhomes.HWDBApplication"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDBApplication">
-        <allow
-            interface="lazr.restful.interfaces.ITopLevelEntryLink"/>
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDBApplication"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWSubmission">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmission"
-            attributes="
-                id"/>
-        <require
-            permission="launchpad.Edit"
-            set_attributes="status"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWSubmissionSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWSubmissionSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmissionSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWSystemFingerprint">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSystemFingerprint"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWSystemFingerprintSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWSystemFingerprintSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSystemFingerprintSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.browser.hwdb.HWDBFingerprintSetView">
-        <allow
-            interface="zope.publisher.interfaces.browser.IBrowserPublisher"/>
-        <allow
-            attributes="
-                __call__"/>
-    </class>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWVendorName">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWVendorName"/>
-        <allow
-            attributes="
-                id"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWVendorNameSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWVendorNameSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWVendorNameSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWVendorID">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWVendorID"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWVendorIDSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWVendorIDSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWVendorIDSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDevice">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDevice"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWDeviceSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDeviceSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDeviceNameVariant">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceNameVariant"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWDeviceNameVariantSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDeviceNameVariantSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceNameVariantSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDriver">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDriver"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWDriverSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDriverSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDriverSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDriverName">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDriverName"/>
-    </class>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDriverPackageName">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDriverPackageName"/>
-    </class>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDeviceDriverLink">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceDriverLink"/>
-        <allow
-            attributes="
-                id"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWDeviceDriverLinkSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDeviceDriverLinkSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceDriverLinkSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWDeviceClass">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceClass"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWDeviceClassSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWDeviceClassSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWDeviceClassSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWSubmissionDevice">
-        <require
-            permission="launchpad.View"
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmissionDevice"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWSubmissionDeviceSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWSubmissionDeviceSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmissionDeviceSet"/>
-    </securedutility>
-    <class
-        class="lp.hardwaredb.model.hwdb.HWSubmissionBug">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmissionBug"/>
-        <allow
-            attributes="
-                id"/>
-    </class>
-    <securedutility
-        class="lp.hardwaredb.model.hwdb.HWSubmissionBugSet"
-        provides="lp.hardwaredb.interfaces.hwdb.IHWSubmissionBugSet">
-        <allow
-            interface="lp.hardwaredb.interfaces.hwdb.IHWSubmissionBugSet"/>
-    </securedutility>
-
-   <webservice:register module="lp.hardwaredb.interfaces.webservice" />
-</configure>
diff --git a/lib/lp/hardwaredb/doc/__init__.py b/lib/lp/hardwaredb/doc/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/doc/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/doc/hwdb-access.txt b/lib/lp/hardwaredb/doc/hwdb-access.txt
deleted file mode 100644
index 47c17f9..0000000
--- a/lib/lp/hardwaredb/doc/hwdb-access.txt
+++ /dev/null
@@ -1,131 +0,0 @@
-Access to HWDB tables
-=====================
-
-The access to most HWDB tables is restricted to members of the team
-"canonical".
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> hwdb_team = getUtility(IPersonSet).getByName('hwdb-team')
-    >>> no_priv = getUtility(IPersonSet).getByName('no-priv')
-    >>> no_priv.inTeam(hwdb_team)
-    False
-    >>> sample_person = getUtility(IPersonSet).getByName('name12')
-    >>> sample_person.inTeam(hwdb_team)
-    True
-
-    >>> from zope.security.interfaces import Unauthorized
-    >>> def check_authorized_for(obj, attribute, person_name_or_address):
-    ...     login(person_name_or_address)
-    ...     has_access = True
-    ...     try:
-    ...         getattr(obj, attribute)
-    ...     except Unauthorized:
-    ...         has_access = False
-    ...         print("Access for %s denied" % person_name_or_address)
-    ...     if has_access:
-    ...         print("Access for %s allowed" % person_name_or_address)
-    ...     logout()
-
-    >>> def check_authorized_only_for_hwdb_team(obj, attribute):
-    ...     check_authorized_for(obj, attribute, ANONYMOUS)
-    ...     check_authorized_for(obj, attribute, 'no-priv@xxxxxxxxxxxxx')
-    ...     check_authorized_for(obj, attribute, 'test@xxxxxxxxxxxxx')
-
-HWDriver
---------
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDriverSet
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> driver = getUtility(IHWDriverSet).search(name='sd')[0]
-    >>> check_authorized_only_for_hwdb_team(driver, 'name')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWDriverName
-------------
-
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> driver_name = getUtility(IHWDriverSet).all_driver_names()[0]
-    >>> check_authorized_only_for_hwdb_team(driver_name, 'name')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWDriverPackageName
--------------------
-
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> package_name = getUtility(IHWDriverSet).all_package_names()[0]
-    >>> logout()
-    >>> check_authorized_only_for_hwdb_team(package_name, 'package_name')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWVendorID
-----------
-
-    >>> from lp.hardwaredb.interfaces.hwdb import HWBus, IHWVendorIDSet
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> vendor_id = getUtility(IHWVendorIDSet).getByBusAndVendorID(
-    ...     HWBus.PCI, '0x10de')
-    >>> check_authorized_only_for_hwdb_team(vendor_id, 'bus')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWDevice
---------
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDeviceSet
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> device = getUtility(IHWDeviceSet).getByDeviceID(
-    ...     HWBus.PCI, '0x10de', '0x0455')
-    >>> check_authorized_only_for_hwdb_team(device, 'name')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWDeviceClass
--------------
-
-The access to most HWDB tables is restricted to members of the team
-"canonical". the current user, Sample Person, is a member of this team,
-No-Priv is not (see above).
-
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> device_class = device.getOrCreateDeviceClass(12, 3)
-    >>> check_authorized_only_for_hwdb_team(device_class, 'main_class')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-HWSubmissionDevice
-------------------
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionDeviceSet
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> submission_device = getUtility(IHWSubmissionDeviceSet).get(1)
-    >>> check_authorized_only_for_hwdb_team(
-    ...     submission_device, 'hal_device_id')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
-
-
-Methods of IHWDBApplication
----------------------------
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDBApplication
-    >>> app = getUtility(IHWDBApplication)
-    >>> check_authorized_only_for_hwdb_team(app, 'numOwnersOfDevice')
-    Access for launchpad.anonymous denied
-    Access for no-priv@xxxxxxxxxxxxx denied
-    Access for test@xxxxxxxxxxxxx allowed
diff --git a/lib/lp/hardwaredb/doc/hwdb-device-tables.txt b/lib/lp/hardwaredb/doc/hwdb-device-tables.txt
deleted file mode 100644
index 7b5cb68..0000000
--- a/lib/lp/hardwaredb/doc/hwdb-device-tables.txt
+++ /dev/null
@@ -1,2756 +0,0 @@
-Hardware Database Device Tables
-===============================
-
-These tables represent devices and complete systems in the database. They
-allow look up of devices by their bus IDs and by their human readable
-vendor and product names. They also link devices to drivers.
-
-HWVendorName
-------------
-
-HWVendorName is a simple list of vendor names. A new entry is created by
-IHWVendorNameSet.create().
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWVendorNameSet
-    >>> vendor_name_set = getUtility(IHWVendorNameSet)
-    >>> intel_name = vendor_name_set.create(name='Intel')
-    >>> print(intel_name.name)
-    Intel
-
-Each name in the table must be unique. The attempt to create a second
-row with the same name raises an IntegrityError error.
-
-    >>> from storm.store import Store
-    >>> store = Store.of(intel_name)
-    >>> from lp.testing.layers import LaunchpadZopelessLayer
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> vendor_name_set.create('Intel')
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-The database checks the uniqueness of the lower-case name, hence we
-cannot insert another name like "INTEL" or "intel".
-
-    >>> vendor_name_set.create('INTEL')
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-    >>> vendor_name_set.create('intel')
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-The lower-case uniqueness constraint works for latin characters with
-diacritical marks too...
-
-    >>> a_umlaut_upper = u'\N{LATIN CAPITAL LETTER A WITH DIAERESIS}'
-    >>> a_umlaut_lower = u'\N{LATIN SMALL LETTER A WITH DIAERESIS}'
-    >>> a_umlaut_name = vendor_name_set.create(name=a_umlaut_upper)
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> print(repr(a_umlaut_name.name))
-    u'\xc4'
-
-    >>> vendor_name_set.create(name=a_umlaut_lower)
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-...as well as for Cyrillic characters...
-
-    >>> cyrillic_de_upper = u'\N{CYRILLIC CAPITAL LETTER DE}'
-    >>> cyrillic_de_lower = u'\N{CYRILLIC SMALL LETTER DE}'
-    >>> cyrillic_de_name = vendor_name_set.create(name=cyrillic_de_upper)
-    >>> print(repr(cyrillic_de_name.name))
-    u'\u0414'
-
-    >>> vendor_name_set.create(name=cyrillic_de_lower)
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-...and Greek characters.
-
-    >>> alpha_upper = u'\N{GREEK CAPITAL LETTER ALPHA}'
-    >>> alpha_lower = u'\N{GREEK SMALL LETTER ALPHA}'
-    >>> cyrillic_de_name = vendor_name_set.create(name=alpha_upper)
-    >>> print(repr(cyrillic_de_name.name))
-    u'\u0391'
-
-    >>> vendor_name_set.create(name=alpha_lower)
-    <HWVendorName ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-Existing IHWVendorName records are retrieved by IHWVendorName.getByName().
-
-    >>> intel_name = vendor_name_set.getByName('Intel')
-    >>> print(intel_name.name)
-    Intel
-
-The capitalization of the name does not metter.
-
-    >>> intel_name = vendor_name_set.getByName('INTEL')
-    >>> print(intel_name.name)
-    Intel
-
-    >>> intel_name = vendor_name_set.getByName('intel')
-    >>> print(intel_name.name)
-    Intel
-
-    >>> umlaut_name = vendor_name_set.getByName(
-    ...     u'\N{LATIN CAPITAL LETTER A WITH DIAERESIS}')
-    >>> print(repr(umlaut_name.name))
-    u'\xc4'
-
-    >>> umlaut_name = vendor_name_set.getByName(
-    ...     u'\N{LATIN SMALL LETTER A WITH DIAERESIS}')
-    >>> print(repr(umlaut_name.name))
-    u'\xc4'
-
-If no record matching the given name exists, IHWVendorName.getByName()
-returns None.
-
-    >>> print(vendor_name_set.getByName('Babbage Computers'))
-    None
-
-
-HWVendorID
-----------
-
-HWVendorID associates a bus, as enumerated by HWBus, with a bus-specific
-vendor ID and a vendor name. We store the IDs as a string, because not all
-all busses use numeric IDs. Numbers are represented as strings with
-hexadecimal digits, prepended by '0x'.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import HWBus, IHWVendorIDSet
-    >>> vendor_id_set = getUtility(IHWVendorIDSet)
-    >>> intel_pci_id = vendor_id_set.create(bus=HWBus.PCI,
-    ...                                     vendor_id='0x8086',
-    ...                                     vendor_name=intel_name)
-    >>> print(intel_pci_id.bus.title, intel_pci_id.vendor_id_for_bus)
-    PCI 0x8086
-    >>> print(intel_pci_id.vendor_name.name)
-    Intel
-
-The tuple (bus, vendor id, vendor name) must be unique.
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='0x8086',
-    ...                      vendor_name=intel_name)
-    <HWVendorID ...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-We store the (bus specific) vendor ID as a string, but several busses
-have stricter constraints for their vendor ID. The PCI and USB busses use
-16 bit integers, the IEEE1394 bus uses 24 bit integers, the SCSI bus
-uses ASCII strings with exactly 8 characters. The constructor of
-HWVendorID ensures that the vendor IDs match the bus-specific format.
-
-USB, PCI and PCCard IDs are represented as strings with a four-digit
-hexadecimal number, prefixed by '0x'; the digits a..f must be lower
-cases characters. Other ID values raise a ParameterError.
-
-The characters a..f are accepted as digits.
-
-    >>> another_pci_vendor_id = vendor_id_set.create(bus=HWBus.PCI,
-    ...                                              vendor_id='0x10ae',
-    ...                                              vendor_name=intel_name)
-    >>> print(another_pci_vendor_id.bus.title)
-    PCI
-    >>> print(another_pci_vendor_id.vendor_id_for_bus)
-    0x10ae
-    >>> print(another_pci_vendor_id.vendor_name.name)
-    Intel
-
-    >>> another_pci_vendor_id = vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                                              vendor_id='0x10ae',
-    ...                                              vendor_name=intel_name)
-    >>> print(another_pci_vendor_id.bus.title)
-    PC Card (32 bit)
-    >>> print(another_pci_vendor_id.vendor_id_for_bus)
-    0x10ae
-    >>> print(another_pci_vendor_id.vendor_name.name)
-    Intel
-
-    >>> another_usb_vendor_id = vendor_id_set.create(bus=HWBus.USB,
-    ...                                              vendor_id='0x10ae',
-    ...                                              vendor_name=intel_name)
-    >>> print(another_usb_vendor_id.bus.title)
-    USB
-    >>> print(another_usb_vendor_id.vendor_id_for_bus)
-    0x10ae
-    >>> print(another_usb_vendor_id.vendor_name.name)
-    Intel
-
-A..F is rejected.
-
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='0x10AE',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x10AE' is not a valid vendor ID for PCI
-    >>> from storm.tracer import debug; debug(True)
-    >>> store.flush()
-    >>> debug(False)
-
-    >>> vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                      vendor_id='0x10AE',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x10AE' is not a valid vendor ID for PC Card (32 bit)
-
-    >>> vendor_id_set.create(bus=HWBus.USB,
-    ...                      vendor_id='0x10AE',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x10AE' is not a valid vendor ID for USB
-
-The ID must have the prefix "0x".
-
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='8086',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'8086' is not a valid vendor ID for PCI
-
-    >>> vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                      vendor_id='8086',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'8086' is not a valid vendor ID for PC Card (32 bit)
-
-    >>> vendor_id_set.create(bus=HWBus.USB,
-    ...                      vendor_id='8086',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'8086' is not a valid vendor ID for USB
-
-The number must have four digits.
-
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='0x123',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid vendor ID for PCI
-
-    >>> vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                      vendor_id='0x123',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid vendor ID for PC Card (32 bit)
-
-    >>> vendor_id_set.create(bus=HWBus.USB,
-    ...                      vendor_id='0x123',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid vendor ID for USB
-
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='0x12345',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid vendor ID for PCI
-
-    >>> vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                      vendor_id='0x12345',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid vendor ID for PC Card (32 bit)
-
-    >>> vendor_id_set.create(bus=HWBus.USB,
-    ...                      vendor_id='0x12345',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid vendor ID for USB
-
-Only hex digits are allowed.
-
-    >>> vendor_id_set.create(bus=HWBus.PCI,
-    ...                      vendor_id='0xblah',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid vendor ID for PCI
-
-    >>> vendor_id_set.create(bus=HWBus.PCCARD,
-    ...                      vendor_id='0xblah',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid vendor ID for PC Card (32 bit)
-
-    >>> vendor_id_set.create(bus=HWBus.USB,
-    ...                      vendor_id='0xblah',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid vendor ID for USB
-
-IEEE1394 IDs are represented as strings with a six-digit hexadecimal
-number, prefixed by '0x'; the digits a..f must be lower cases characters.
-Other ID values raise a value error.
-
-    >>> vendor_id_1394 = vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                                       vendor_id='0x0010e0',
-    ...                                       vendor_name=intel_name)
-    >>> print(vendor_id_1394.bus.title)
-    IEEE1394
-    >>> print(vendor_id_1394.vendor_id_for_bus)
-    0x0010e0
-    >>> print(vendor_id_1394.vendor_name.name)
-    Intel
-
-A..F is rejected.
-
-    >>> vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                      vendor_id='0x0010E0',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x0010E0' is not a valid vendor ID for IEEE1394
-
-The ID must have the prefix "0x".
-
-    >>> vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                      vendor_id='0010E0',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0010E0' is not a valid vendor ID for IEEE1394
-
-The number must have six digits.
-
-    >>> vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                      vendor_id='0x12345',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid vendor ID for IEEE1394
-
-    >>> vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                      vendor_id='0x1234567',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x1234567' is not a valid vendor ID for IEEE1394
-
-Only hex digits are allowed.
-
-    >>> vendor_id_set.create(bus=HWBus.IEEE1394,
-    ...                      vendor_id='0xfoobar',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xfoobar' is not a valid vendor ID for IEEE1394
-
-SCSI vendor IDs are ASCII strings with exactly eight characters.
-
-    >>> intel_scsi_id = vendor_id_set.create(bus=HWBus.SCSI,
-    ...                                      vendor_id='INTEL   ',
-    ...                                      vendor_name=intel_name)
-    >>> print(intel_scsi_id.bus.title)
-    SCSI
-    >>> intel_scsi_id.vendor_id_for_bus
-    u'INTEL   '
-    >>> print(intel_scsi_id.vendor_name.name)
-    Intel
-
-Strings with less than eight characters are not allowed as SCSI vendor IDs...
-
-    >>> vendor_id_set.create(bus=HWBus.SCSI,
-    ...                      vendor_id='1234567',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'1234567' is not a valid vendor ID for SCSI
-
-...as well as strings with more than eight characters.
-
-    >>> vendor_id_set.create(bus=HWBus.SCSI,
-    ...                      vendor_id='123456789',
-    ...                      vendor_name=intel_name)
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'123456789' is not a valid vendor ID for SCSI
-
-HWVendorIDSet.getByBusAndVendorID() is used to look up a HWVendorID record.
-
-    >>> vendor_id = vendor_id_set.getByBusAndVendorID(bus=HWBus.PCI,
-    ...                                               vendor_id='0x8086')
-    >>> print(vendor_id.bus.title)
-    PCI
-    >>> print(vendor_id.vendor_name.name)
-    Intel
-
-If no record exists for the given bus and vendor ID,
-HWVendorIDSet.getByBusAndVendorID() returns None.
-
-    >>> vendor_id = vendor_id_set.getByBusAndVendorID(bus=HWBus.PCI,
-    ...                                               vendor_id='0xffff')
-    >>> print(vendor_id)
-    None
-
-HWVendorIDSet.getByBusAndVendorID() performs the same validity tests of
-the vendor ID as HWVendorSet.create(): The vendor ID passed to
-HWVendorIDSet.getByBusAndVendorID() must be a valid ID for the given bus.
-
-    >>> vendor_id_set.getByBusAndVendorID(bus=HWBus.PCI, vendor_id='8086')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'8086' is not a valid vendor ID for PCI
-
-HWVendorIDSet.get() returns the HWVendorID record with the given ID...
-
-    >>> vendor_id = vendor_id_set.get(1)
-    >>> print(vendor_id.bus.name, vendor_id.vendor_id_for_bus)
-    SYSTEM MSI
-
-...or None, if no such record exists.
-
-    >>> print(vendor_id_set.get(1000000))
-    None
-
-HWVendorIDSet.idsForBus() returns all known HWVendorIDs for a given bus.
-
-    >>> for vendor_id in vendor_id_set.idsForBus(HWBus.PCI):
-    ...     print(vendor_id.bus.name, vendor_id.vendor_id_for_bus)
-    PCI 0x10ae
-    PCI 0x10de
-    PCI 0x8086
-
-
-HWDevice
---------
-
-A HWDevice instance stores core data about a hardware device: The bus
-it can connect to, the vendor ID, the product ID, the human readable
-product name, variant (see below), and the number of submissions with
-a device.
-
-Aside from "real" devices like keyboards, mice, hard disks, PCI cards,
-i.e., "thingies" one can connect to and remove from a computer, we also
-store data about "systems" (computers) and "components" in the HWDevice
-table. A component is a part that is "permanently built" into a system,
-like a hard disk controller that is soldered onto the main board.
-
-A new device record is created by IHWDeviceSet.create.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDeviceSet
-    >>> device_set = getUtility(IHWDeviceSet)
-    >>> product_name='82801GBM/GHM (ICH7 Family) USB2 EHCI Controller'
-    >>> usb_controller = device_set.create(bus=HWBus.PCI,
-    ...                                    vendor_id='0x8086',
-    ...                                    product_id='0x27cc',
-    ...                                    product_name=product_name)
-
-Bus and vendor ID are not directly stored as HWDevice attributes; we can
-access them, as well as the vendor name, either via the attribute
-bus_vendor, which references HWVendorID...
-
-    >>> print(usb_controller.bus_vendor.bus.title)
-    PCI
-    >>> print(usb_controller.bus_vendor.vendor_id_for_bus)
-    0x8086
-    >>> print(usb_controller.bus_vendor.vendor_name.name)
-    Intel
-
-...or via the (read-only) HWDevice properties bus, vendor_id, vendor_name.
-
-    >>> print(usb_controller.bus.title)
-    PCI
-    >>> print(usb_controller.vendor_id)
-    0x8086
-    >>> print(usb_controller.vendor_name)
-    Intel
-
-Other attributes stored in HWDevice are the product ID, the product name,
-the product variant and submissions. submissions is a counter of the
-number of submissions with this device.
-
-    >>> print(usb_controller.bus_product_id)
-    0x27cc
-    >>> print(usb_controller.name)
-    82801GBM/GHM (ICH7 Family) USB2 EHCI Controller
-    >>> print(usb_controller.variant)
-    None
-    >>> print(usb_controller.submissions)
-    0
-
-Like vendor IDs, product IDs of some busses have certain constraints.
-USB and PCI product IDs are 16 bit integers; SCSI IDs are strings
-with 16 characters. (Note that the IEEE1394 does _not_ define a product
-ID.)
-
-USB, PCI and PCCard IDs are represented as strings with a four-digit
-hexadecimal number, prefixed by '0x'; the digits a..f must be lower
-cases characters. Other ID values raise a ParameterError.
-
-    >>> another_pci_product = device_set.create(bus=HWBus.PCI,
-    ...                                         vendor_id='0x8086',
-    ...                                         product_id='0xabcd',
-    ...                                         product_name='A PCI card')
-    >>> print(another_pci_product.bus_product_id)
-    0xabcd
-
-    >>> another_pci_product = device_set.create(bus=HWBus.PCCARD,
-    ...                                         vendor_id='0x8086',
-    ...                                         product_id='0xabcd',
-    ...                                         product_name='A PC Card')
-    >>> print(another_pci_product.bus_product_id)
-    0xabcd
-
-    >>> another_usb_product = device_set.create(bus=HWBus.USB,
-    ...                                         vendor_id='0x8086',
-    ...                                         product_id='0xabcd',
-    ...                                         product_name='A USB device')
-    >>> print(another_usb_product.bus_product_id)
-    0xabcd
-
-A..F is rejected.
-
-    >>> device_set.create(bus=HWBus.PCI,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xABCD',
-    ...                   product_name='A PCI card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xABCD' is not a valid product ID for PCI
-
-    >>> device_set.create(bus=HWBus.PCCARD,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xABCD',
-    ...                   product_name='A PC Card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xABCD' is not a valid product ID for PC Card (32 bit)
-
-    >>> device_set.create(bus=HWBus.USB,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xABCD',
-    ...                   product_name='A USB device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xABCD' is not a valid product ID for USB
-
-The ID must have the prefix "0x".
-
-    >>> device_set.create(bus=HWBus.PCI,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='1234',
-    ...                   product_name='A PCI card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'1234' is not a valid product ID for PCI
-
-    >>> device_set.create(bus=HWBus.PCCARD,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='1234',
-    ...                   product_name='A PC Card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'1234' is not a valid product ID for PC Card (32 bit)
-
-    >>> device_set.create(bus=HWBus.USB,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='1234',
-    ...                   product_name='A USB device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'1234' is not a valid product ID for USB
-
-The number must have four digits.
-
-    >>> device_set.create(bus=HWBus.PCI,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x123',
-    ...                   product_name='A PCI card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid product ID for PCI
-
-    >>> device_set.create(bus=HWBus.PCCARD,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x123',
-    ...                   product_name='A PC Card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid product ID for PC Card (32 bit)
-
-    >>> device_set.create(bus=HWBus.USB,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x123',
-    ...                   product_name='A USB device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x123' is not a valid product ID for USB
-
-    >>> device_set.create(bus=HWBus.PCI,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x12345',
-    ...                   product_name='A PCI card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid product ID for PCI
-
-    >>> device_set.create(bus=HWBus.PCCARD,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x12345',
-    ...                   product_name='A PC Card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid product ID for PC Card (32 bit)
-
-    >>> device_set.create(bus=HWBus.USB,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0x12345',
-    ...                   product_name='A USB device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0x12345' is not a valid product ID for USB
-
-Only hex digits are allowed.
-
-    >>> device_set.create(bus=HWBus.PCI,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xblah',
-    ...                   product_name='A PCI card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid product ID for PCI
-
-    >>> device_set.create(bus=HWBus.PCCARD,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xblah',
-    ...                   product_name='A PC Card')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid product ID for PC Card (32 bit)
-
-    >>> device_set.create(bus=HWBus.USB,
-    ...                   vendor_id='0x8086',
-    ...                   product_id='0xblah',
-    ...                   product_name='A USB device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0xblah' is not a valid product ID for USB
-
-SCSI product IDs are ASCII strings with 16 characters.
-
-    >>> scsi_device = device_set.create(bus=HWBus.SCSI,
-    ...                                 vendor_id='INTEL   ',
-    ...                                 product_id='12345678901234  ',
-    ...                                 product_name='A SCSI device')
-    >>> scsi_device.bus_product_id
-    u'12345678901234  '
-
-Strings with less than 16 characters are not allowed as SCSI product IDs...
-
-    >>> device_set.create(bus=HWBus.SCSI,
-    ...                   vendor_id='INTEL   ',
-    ...                   product_id='123456789012345',
-    ...                   product_name='A SCSI device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'123456789012345' is not a valid product ID for SCSI
-
-...as well as strings with more than 16 characters.
-
-    >>> device_set.create(bus=HWBus.SCSI,
-    ...                   vendor_id='INTEL   ',
-    ...                   product_id='12345678901234567',
-    ...                   product_name='A SCSI device')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'12345678901234567' is not a valid product ID for SCSI
-
-
-Unknown Vendor IDs
-..................
-
-If IHWDevice.create is called with a vendor ID that for which there
-is no IHWVendorID record, the latter is automatically created. Since
-we do not pass the vendor name to IHWDevice.create, this method
-cannot set the real vendor name for the new IHWVendorID record.
-Instead, the vendor name is set to "Unknown".
-
-Reason: IHWDevice will be populated with data collected by the
-HWDB client, which gets most of the device data from HAL, and HAL
-quite often does not know the vendor name. Hence we will populate
-and update IHWVendorID from sources like
-
-http://www.linux-usb.org/usb.ids
-http://www.pcidatabase.com/reports.php?type=csv
-http://standards.ieee.org/regauth/oui/oui.txt
-
-    >>> new_vendor_device = device_set.create(bus=HWBus.PCI,
-    ...                                       vendor_id='0x4321',
-    ...                                       product_id='0x8765',
-    ...                                       product_name='mind sensor')
-    >>> print(new_vendor_device.bus_vendor.vendor_name.name)
-    Unknown
-
-
-Device Variants
-...............
-
-While most devices can be uniquely identified by their vendor and product
-IDs, there are some cases, where different devices have identical IDs:
-
-(a) The IDs are assigned to a USB chipset that is used in different
-    devices.
-(b) A vendor may inadvertently "recycle" a product ID
-(c) A chip manufacturer who does not have its own vendor ID may "forge"
-    the vendor ID.
-
-A real world example of the first case is the scanner chipset from
-Plustek with the USB ID 0x07b3/0x0017. This chipset is used in devices
-that differ for example in the maximum scan window size.
-
-We do not necessarily know in advance that there are different devices
-with identical IDs, so we may have a HWDevice record without the variant
-attribute set.
-
-    >>> plustek_name = vendor_name_set.create('Plustek')
-    >>> plustek_usb_id = vendor_id_set.create(bus=HWBus.USB,
-    ...                                       vendor_id='0x07b3',
-    ...                                       vendor_name=plustek_name)
-    >>> some_plustek_scanner = device_set.create(bus=HWBus.USB,
-    ...                                          vendor_id='0x07b3',
-    ...                                          product_id='0x0017',
-    ...                                          product_name='some scanner')
-    >>> print(some_plustek_scanner.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(some_plustek_scanner.bus_vendor.vendor_name.name)
-    Plustek
-    >>> print(some_plustek_scanner.name)
-    some scanner
-    >>> print(some_plustek_scanner.variant)
-    None
-
-Once we know that (bus, vendor ID, product ID) does not uniquely
-identify a device, we can disambiguate these devices by creating
-IHWDevice records, where the attribute variant is, for example, set
-to the product name.
-
-    >>> optic_pro_ut12 = device_set.create(bus=HWBus.USB,
-    ...                                    vendor_id='0x07b3',
-    ...                                    product_id='0x0017',
-    ...                                    variant='OpticPro UT12',
-    ...                                    product_name='OpticPro UT12')
-    >>> optic_pro_ut16 = device_set.create(bus=HWBus.USB,
-    ...                                    vendor_id='0x07b3',
-    ...                                    product_id='0x0017',
-    ...                                    variant='OpticPro UT16',
-    ...                                    product_name='OpticPro UT16')
-
-For systems, we use HWBus.SYSTEM as the "bus name"; the HAL property
-system.vendor is stored as the vendor ID and vendor name, and the HAL
-property system.product is stored as the product ID and the product
-name.
-
-    >>> hal_vendor_name = 'Tonka'
-    >>> hal_product_name = 'Tuffbook 2600'
-    >>> tonka_name = vendor_name_set.create(name=hal_vendor_name)
-    >>> tonka_system_id = vendor_id_set.create(bus=HWBus.SYSTEM,
-    ...                                        vendor_id=hal_vendor_name,
-    ...                                        vendor_name=tonka_name)
-    >>> tuffbook_2600 = device_set.create(bus=HWBus.SYSTEM,
-    ...                                   vendor_id=hal_vendor_name,
-    ...                                   product_id=hal_product_name,
-    ...                                   product_name=hal_product_name)
-    >>> print(tuffbook_2600.bus_vendor.bus.title)
-    System
-    >>> print(tuffbook_2600.bus_vendor.vendor_id_for_bus)
-    Tonka
-    >>> print(tuffbook_2600.bus_vendor.vendor_name.name)
-    Tonka
-    >>> print(tuffbook_2600.bus_product_id)
-    Tuffbook 2600
-    >>> print(tuffbook_2600.name)
-    Tuffbook 2600
-
-The tuple (bus_vendor, bus_product_id, variant) must be unique.
-The attempt to create two rows with the same data raises a
-IntegrityError error.
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> another_scanner = device_set.create(bus=HWBus.USB,
-    ...                                     vendor_id='0x07b3',
-    ...                                     product_id='0x0017',
-    ...                                     product_name='some scanner')
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-    >>> another_scanner = device_set.create(bus=HWBus.USB,
-    ...                                     vendor_id='0x07b3',
-    ...                                     product_id='0x0017',
-    ...                                     variant='OpticPro UT16',
-    ...                                     product_name='OpticPro UT16')
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-Retrieving HWDevice records
-...........................
-
-Existing HWDevice records can be retrieved by calling
-HWDeviceSet.getByDeviceID()
-
-    >>> device = device_set.getByDeviceID(HWBus.USB, '0x07b3', '0x0017')
-    >>> print(device.bus_vendor.bus.title)
-    USB
-    >>> print(device.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(device.bus_product_id)
-    0x0017
-    >>> print(device.name)
-    some scanner
-
-The call to HWDeviceSet.getByDeviceID() above did not specify a product
-variant. In such a case, the record having variant==None is returned
-
-    >>> print(device.variant)
-    None
-
-If a variant name is given, we get the HWDevice record of that variant.
-
-    >>> device = device_set.getByDeviceID(HWBus.USB, '0x07b3', '0x0017',
-    ...                                   'OpticPro UT16')
-    >>> print(device.bus_vendor.bus.title)
-    USB
-    >>> print(device.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(device.bus_product_id)
-    0x0017
-    >>> print(device.name)
-    OpticPro UT16
-    >>> print(device.variant)
-    OpticPro UT16
-
-If the given parameters do not match any existing record, None is
-returned.
-
-    >>> print(device_set.getByDeviceID(HWBus.PCI, '0x07b3', '0x0017',
-    ...                                'OpticPro UT16'))
-    None
-    >>> print(device_set.getByDeviceID(HWBus.USB, '0xffff', '0x0017',
-    ...                                'OpticPro UT16'))
-    None
-    >>> print(device_set.getByDeviceID(HWBus.PCI, '0x07b3', '0xffff',
-    ...                                'OpticPro UT16'))
-    None
-    >>> print(device_set.getByDeviceID(HWBus.PCI, '0x07b3', '0x0017',
-    ...                                'nonsense'))
-    None
-
-The parameters vendor_id and product_id must be valid IDs for the
-given bus.
-
-    >>> device_set.getByDeviceID(HWBus.USB, '07b3', '0x0017')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'07b3' is not a valid vendor ID for USB
-
-    >>> device_set.getByDeviceID(HWBus.USB, '0x07b3', '0017')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'0017' is not a valid product ID for USB
-
-HWDeviceSet.getOrCreate() returns an existing record matching the given
-parameters or creates a new one, if no existing record matches.
-
-    >>> device2 = device_set.getOrCreate(bus=HWBus.USB,
-    ...                                  vendor_id='0x07b3',
-    ...                                  product_id='0x0017',
-    ...                                  product_name='OpticPro UT16',
-    ...                                  variant='OpticPro UT16')
-    >>> print(device2.bus_vendor.bus.title)
-    USB
-    >>> print(device2.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(device2.bus_product_id)
-    0x0017
-    >>> print(device2.name)
-    OpticPro UT16
-    >>> print(device2.variant)
-    OpticPro UT16
-    >>> print(device2.id == device.id)
-    True
-
-    >>> device3 = device_set.getByDeviceID(bus=HWBus.USB,
-    ...                                    vendor_id='0x07b3',
-    ...                                    product_id='0x0017',
-    ...                                    variant='Some other scanner')
-    >>> print(device3)
-    None
-    >>> device3 = device_set.getOrCreate(bus=HWBus.USB,
-    ...                                  vendor_id='0x07b3',
-    ...                                  product_id='0x0017',
-    ...                                  product_name='Some other scanner',
-    ...                                  variant='Some other scanner')
-    >>> print(device3.bus_vendor.bus.title)
-    USB
-    >>> print(device3.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(device3.bus_product_id)
-    0x0017
-    >>> print(device3.name)
-    Some other scanner
-    >>> print(device3.variant)
-    Some other scanner
-
-    >>> device4 = device_set.getOrCreate(bus=HWBus.USB,
-    ...                                  vendor_id='0x07b3',
-    ...                                  product_id='0x0015',
-    ...                                  product_name='OpticPro U24')
-    >>> print(device4.bus_vendor.bus.title)
-    USB
-    >>> print(device4.bus_vendor.vendor_id_for_bus)
-    0x07b3
-    >>> print(device4.bus_product_id)
-    0x0015
-    >>> print(device4.name)
-    OpticPro U24
-    >>> print(device4.variant)
-    None
-
-
-HWDeviceSet.getByID() returns a HWDevice record with the given database ID.
-
-    >>> device = device_set.getByID(1)
-    >>> print(device.id)
-    1
-    >>> print(device.vendor_id)
-    MSI
-    >>> print(device.bus_product_id)
-    MS-7369
-
-HWDeviceSet.getByID() returns None if no record with the passed ID exists.
-
-    >>> device = device_set.getByID(1000000)
-    >>> print(device)
-    None
-
-HWDeviceSet.search() returns all devices with a given bus and vendor ID.
-
-    >>> devices = device_set.search(bus=HWBus.USB, vendor_id='0x07b3')
-    >>> for device in devices:
-    ...     print(device.bus.title, device.vendor_id, device.bus_product_id,
-    ...           device.variant)
-    USB 0x07b3 0x0017 None
-    USB 0x07b3 0x0017 OpticPro UT12
-    USB 0x07b3 0x0017 OpticPro UT16
-    USB 0x07b3 0x0017 Some other scanner
-    USB 0x07b3 0x0015 None
-
-Searching can be limited to a single product ID.
-
-    >>> devices = device_set.search(
-    ...     bus=HWBus.USB, vendor_id='0x07b3', product_id='0x0017')
-    >>> for device in devices:
-    ...     print(device.bus.title, device.vendor_id, device.bus_product_id,
-    ...           device.variant)
-    USB 0x07b3 0x0017 None
-    USB 0x07b3 0x0017 OpticPro UT12
-    USB 0x07b3 0x0017 OpticPro UT16
-    USB 0x07b3 0x0017 Some other scanner
-
-HWDeviceSet.search() raises a ParameterError for vendor or product IDs
-that are not valid for the given bus.
-
-    >>> devices = device_set.search(
-    ...     bus=HWBus.USB, vendor_id='invalid', product_id='0x0017')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'invalid' is not a valid vendor ID for USB
-
-    >>> devices = device_set.search(
-    ...     bus=HWBus.USB, vendor_id='0x07b3', product_id='nonsense')
-    Traceback (most recent call last):
-    ...
-    ParameterError: u'nonsense' is not a valid product ID for USB
-
-HWDevice.getSubmissions() returns submissions which contain this device.
-
-We'll switch the DB user from hwdb-submission-processor to the ordinary
-Launchpad user, because the former has no access to the table
-distribution, which we want to access in this test.
-
-    >>> from lp.testing.dbuser import (
-    ...     lp_dbuser,
-    ...     switch_dbuser,
-    ...     )
-
-    >>> switch_dbuser('launchpad')
-    >>> sata_controller = device_set.getByDeviceID(
-    ...     bus=HWBus.PCI, vendor_id='0x10de', product_id='0x045d')
-    >>> for submission in sata_controller.getSubmissions():
-    ...     print(submission.submission_key)
-    sample-submission
-
-We can limit the results to a specific driver. (See below for details about
-HWDriver.)
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDriverSet
-    >>> ahci_driver = getUtility(IHWDriverSet).getByPackageAndName(
-    ...     'linux-image-2.6.24-19-generic', 'ahci')
-    >>> for submission in sata_controller.getSubmissions(
-    ...     driver=ahci_driver):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> usb_storage_driver = getUtility(IHWDriverSet).getByPackageAndName(
-    ...     'linux-image-2.6.24-19-generic', 'usb-storage')
-    >>> print(list(sata_controller.getSubmissions(
-    ...     driver=usb_storage_driver)))
-    []
-
-We can limit the results to a specific distribution.
-
-    >>> from lp.registry.interfaces.distribution import (
-    ...     IDistributionSet)
-    >>> distribution_set = getUtility(IDistributionSet)
-    >>> ubuntu = distribution_set.getByName('ubuntu')
-    >>> for submission in sata_controller.getSubmissions(
-    ...     distribution=ubuntu):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> debian = distribution_set.getByName('debian')
-    >>> sata_controller.getSubmissions(distribution=debian).count()
-    0
-
-We can limit the results to a specific architecture.
-
-    >>> for submission in sata_controller.getSubmissions(
-    ...     architecture='i386'):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> sata_controller.getSubmissions(architecture='amd64').count()
-    0
-
-We can limit the results to a specific distro series.
-
-    >>> for submission in sata_controller.getSubmissions(
-    ...     distroseries=ubuntu['hoary']):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> sata_controller.getSubmissions(distroseries=ubuntu['warty']).count()
-    0
-
-We can even limit the results to a specific distro series and architecture.
-
-    >>> for submission in sata_controller.getSubmissions(
-    ...     distroseries=ubuntu['hoary'], architecture='i386'):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> sata_controller.getSubmissions(
-    ...     distroseries=ubuntu['hoary'], architecture='powerpc').count()
-    0
-
-We can also search for submissions from a particular user.
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> owner = getUtility(IPersonSet).getByName('name12')
-    >>> for submission in sata_controller.getSubmissions(owner=owner):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> not_owner = getUtility(IPersonSet).getByName('name20')
-    >>> sata_controller.getSubmissions(owner=not_owner).count()
-    0
-
-    >>> import transaction
-    >>> transaction.abort()
-    >>> switch_dbuser('hwdb-submission-processor')
-
-HWDevice.drivers is the set of drivers that are associated via
-HWDeviceDriverLink (see below) with this device.
-
-    >>> for driver in sata_controller.drivers:
-    ...     print(driver.package_name, driver.name)
-    linux-image-2.6.24-19-generic ahci
-
-
-HWDeviceNameVariant
--------------------
-
-Many OEM products are sold by more than one vendor under different
-product names; some manufacturers sell the same device under
-different names in different parts of the world. The support status
-of such devices does not depend on the "publicly visible" vendor and
-product name, so we consider these devices to be identical. Users will
-nevertheless want to look up devices by the vendor and product name
-they see in a store. HWDeviceNameVariant allows us to assign alternative
-vendor and product names to a device.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDeviceNameVariantSet
-    >>> device_name_variant_set = getUtility(IHWDeviceNameVariantSet)
-    >>> variant = device_name_variant_set.create(device=optic_pro_ut16,
-    ...                                          vendor_name='Medion',
-    ...                                          product_name='MD 1234')
-    >>> print(variant.device.bus_vendor.vendor_name.name)
-    Plustek
-    >>> print(variant.vendor_name.name)
-    Medion
-    >>> print(variant.product_name)
-    MD 1234
-
-We count the number of submissions which told us an alternative device
-name.
-
-    >>> print(variant.submissions)
-    0
-
-The tuple (device, vendor_name, product_name) must be unique.
-The attempt to create two rows with the same data raises an
-IntegrityError error.
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> same_variant = device_name_variant_set.create(device=optic_pro_ut16,
-    ...                                               vendor_name='Medion',
-    ...                                               product_name='MD 1234')
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-
-HWDriver
---------
-
-If a device is reported as having problems, then we are more
-interested in which drivers are involved rather than the fact that
-there are problems. The table HWDriver stores minimal data about
-drivers: its package name, the driver name itself and the driver's
-license. This data is linked to HWDevice records via the table
-HWDeviceDriverLink (see below).
-
-The driver is not in every case a kernel driver, it may also be for
-example a Ghostscript driver of a printer or a Sane scanner backend.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDriverSet
-    >>> from lp.registry.interfaces.product import License
-    >>> driver_set = getUtility(IHWDriverSet)
-    >>> usb_driver = driver_set.create(package_name='linux-image-generic',
-    ...                                name='usb',
-    ...                                license=License.GNU_GPL_V2)
-    >>> print(usb_driver.name)
-    usb
-    >>> print(usb_driver.package_name)
-    linux-image-generic
-    >>> print(usb_driver.license.title)
-    GNU GPL v2
-
-The submitted data does not need to contain package information for all
-drivers, hence the field package_name may be None or the empty string
-(see below). In both cases, a HWDriver record is created where
-package_name is the empty string.
-
-    >>> driver2 = driver_set.create(package_name=None,
-    ...                             name='whatever',
-    ...                             license=License.GNU_GPL_V2)
-    >>> print(driver2.name)
-    whatever
-    >>> print(repr(driver2.package_name))
-    u''
-    >>> print(driver2.license.title)
-    GNU GPL v2
-
-    >>> driver_blank_package_name = driver_set.create(package_name='',
-    ...                                               name='bar',
-    ...                             license=License.GNU_GPL_V3)
-    >>> print(driver_blank_package_name.name)
-    bar
-    >>> print(repr(driver_blank_package_name.package_name))
-    u''
-    >>> print(driver_blank_package_name.license.title)
-    GNU GPL v3
-
-Since we also do not always know the license of a driver, the license
-may too be None.
-
-    >>> driver3 = driver_set.create(package_name='',
-    ...                             name='something_else',
-    ...                             license=None)
-    >>> print(driver3.name)
-    something_else
-    >>> print(repr(driver3.package_name))
-    u''
-    >>> print(driver3.license)
-    None
-
-The tuple (driver name, package name) must be unique.
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> driver = driver_set.create(package_name='linux-image-generic',
-    ...                            name='usb',
-    ...                            license=License.GNU_GPL_V2)
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-    >>> driver = driver_set.create(package_name=None,
-    ...                            name='whatever',
-    ...                            license=License.GNU_GPL_V2)
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-An IHWDriver record is retrieved by calling
-IHWDriverSet.getByPackageAndName().
-
-    >>> driver = driver_set.getByPackageAndName('linux-image-generic', 'usb')
-    >>> print(driver.package_name)
-    linux-image-generic
-    >>> print(driver.name)
-    usb
-
-If we want to search for a driver without a known package, we can
-pass None as well as the empty string.
-
-    >>> driver = driver_set.getByPackageAndName('', 'whatever')
-    >>> print(repr(driver.package_name))
-    u''
-    >>> print(driver.name)
-    whatever
-
-    >>> driver2 = driver_set.getByPackageAndName(None, 'whatever')
-    >>> driver2.id == driver.id
-    True
-
-If no existing record matches the parameters of the
-IHWDriverSet.getByPackageAndName() call, None is returned.
-
-    >>> driver = driver_set.getByPackageAndName('nonsense', 'more nonsense')
-    >>> print(driver)
-    None
-
-IHWDriverSet.getOrCreate() returns an existing record matching
-the given parameters or creates a new one, if no existing record matches.
-
-    >>> driver = driver_set.getOrCreate('linux-image-generic', 'usb')
-    >>> print(driver.package_name)
-    linux-image-generic
-    >>> print(driver.name)
-    usb
-
-    >>> driver = driver_set.getByPackageAndName('linux-image-generic', 'foo')
-    >>> print(driver)
-    None
-    >>> driver = driver_set.getOrCreate('linux-image-generic', 'foo')
-    >>> print(driver.name)
-    foo
-    >>> print(driver.package_name)
-    linux-image-generic
-
-If we pass None as the value for package_name, we get records where
-package_name is None or the empty string.
-
-    >>> driver = driver_set.getOrCreate(None, 'whatever')
-    >>> print(repr(driver.package_name))
-    u''
-    >>> print(driver.name)
-    whatever
-
-Older HWDriver records may store None as the package name. We can
-retrieve these records by passing None for package_name...
-
-    >>> from lp.hardwaredb.model.hwdb import HWDriver
-    >>> old_driver = HWDriver(package_name=None, name=u'foo', license=None)
-    >>> print(old_driver.name)
-    foo
-    >>> print(old_driver.package_name)
-    None
-
-    >>> driver = driver_set.getOrCreate(None, 'foo')
-    >>> driver.id == old_driver.id
-    True
-
-...as well as the empty string.
-
-    >>> driver = driver_set.getOrCreate('', 'foo')
-    >>> driver.id == old_driver.id
-    True
-
-IHWDriverSet.search() returns a sequence of driver records matching the
-given parameters.
-
-    >>> drivers = driver_set.search(package_name='linux-image-generic')
-    >>> for driver in drivers:
-    ...    print(driver.package_name, driver.name)
-    linux-image-generic usb
-    linux-image-generic foo
-
-    >>> drivers = driver_set.search(name='usb')
-    >>> for driver in drivers:
-    ...    print(driver.package_name, driver.name)
-    linux-image-2.6.24-19-generic usb
-    linux-image-generic usb
-
-    >>> drivers = driver_set.search(
-    ...     package_name='linux-image-2.6.24-19-generic', name='ahci')
-    >>> for driver in drivers:
-    ...    print(driver.package_name, driver.name)
-    linux-image-2.6.24-19-generic ahci
-
-If package_name is an empty string, driver records are returned where
-the package name is not recorded (which is denoted by None or the
-empty string. See also bug 306265: Disallow the column value
-HWDriver.package_name=null).
-
-    >>> drivers = driver_set.search(package_name='')
-    >>> for driver in drivers:
-    ...    print(repr(driver.package_name), driver.name)
-    u'' whatever
-    u'' bar
-    u'' something_else
-    None foo
-
-    >>> drivers = driver_set.search(package_name='', name='whatever')
-    >>> for driver in drivers:
-    ...    print(repr(driver.package_name), driver.name)
-    u'' whatever
-
-    >>> drivers = driver_set.search(package_name='', name='foo')
-    >>> for driver in drivers:
-    ...    print(repr(driver.package_name), driver.name)
-    None foo
-
-If no parameters are specified, IHWDriverSet.search() returns all driver
-records.
-
-    >>> drivers = driver_set.search()
-    >>> for driver in drivers:
-    ...    print(repr(driver.package_name), driver.name)
-    u'linux-image-2.6.24-19-generic' ehci_hcd
-    u'linux-image-2.6.24-19-generic' usb
-    u'linux-image-2.6.24-19-generic' usb-storage
-    u'linux-image-2.6.24-19-generic' sd
-    u'linux-image-2.6.24-19-generic' hub
-    u'linux-image-2.6.24-19-generic' ahci
-    u'linux-image-2.6.24-19-generic' sr
-    u'linux-image-generic' usb
-    u'' whatever
-    u'' bar
-    u'' something_else
-    u'linux-image-generic' foo
-    None foo
-
-HWDriverSet.getByID() returns a HWDriver record with the given database ID.
-
-    >>> driver = driver_set.getByID(1)
-    >>> print(driver.id)
-    1
-    >>> print(driver.package_name)
-    linux-image-2.6.24-19-generic
-    >>> print(driver.name)
-    ehci_hcd
-
-HWDriverSet.getByID() returns None if no record with the passed ID exists.
-
-    >>> driver = driver_set.getByID(1000000)
-    >>> print(driver)
-    None
-
-    >>> transaction.abort()
-    >>> switch_dbuser('launchpad')
-
-We can search all the submissions related to a driver.
-
-    >>> driver = driver_set.getByID(1)
-    >>> for submission in driver.getSubmissions():
-    ...     print(submission.submission_key)
-    sample-submission
-
-We can limit our search to submissions for a particular distribution.
-
-    >>> for submission in driver.getSubmissions(distribution=ubuntu):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> print(driver.getSubmissions(distribution=debian).count())
-    0
-
-We can limit the results to a specific distro series.
-
-    >>> for submission in driver.getSubmissions(
-    ...     distroseries=ubuntu['hoary']):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> driver.getSubmissions(distroseries=ubuntu['warty']).count()
-    0
-
-We can also limit the results to a specific distro series and architecture.
-
-    >>> for submission in driver.getSubmissions(
-    ...     distroseries=ubuntu['hoary'], architecture='i386'):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> driver.getSubmissions(
-    ...     distroseries=ubuntu['hoary'], architecture='powerpc').count()
-    0
-
-And We can search for submissions from a particular user.
-
-    >>> for submission in driver.getSubmissions(owner=owner):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> driver.getSubmissions(owner=not_owner).count()
-    0
-
-    >>> transaction.abort()
-    >>> switch_dbuser('hwdb-submission-processor')
-
-
-Driver names and package names
-------------------------------
-
-The same driver names can appear multiple times in HWDriver.
-HWDriverSet.all_driver_names() returns a list of distinct driver
-names used in the table HWDriver.
-
-    >>> switch_dbuser('launchpad')
-    >>> for driver_name in driver_set.all_driver_names():
-    ...     print(driver_name.name)
-    ahci
-    bar
-    ehci_hcd
-    hub
-    sd
-    something_else
-    sr
-    usb
-    usb-storage
-    whatever
-
-Similary, the same package names appear more than once in HWDriver.
-HWDriverSet.all_package_names() returns a list of distinct package
-names. Note that the package name value None (used in older submissions)
-is not included.
-
-    >>> from lp.hardwaredb.model.hwdb import HWDriver
-    >>> store.add(HWDriver(name='foo', package_name=None))
-    <HWDriver at...
-    >>> for package_name in driver_set.all_package_names():
-    ...     print(package_name.package_name)
-    <BLANKLINE>
-    linux-image-2.6.24-19-generic
-    linux-image-generic
-
-    >>> switch_dbuser('hwdb-submission-processor')
-
-
-HWDeviceDriverLink
-------------------
-
-This table links devices and drivers.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDeviceDriverLinkSet
-    >>> device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
-    >>> usb_controller_usb_link = device_driver_link_set.create(
-    ...     device=usb_controller, driver=usb_driver)
-    >>> print(usb_controller_usb_link.device.name)
-    82801GBM/GHM (ICH7 Family) USB2 EHCI Controller
-    >>> print(usb_controller_usb_link.driver.name)
-    usb
-
-A device may be linked to more than one driver. A USB2 host controller
-for example has two drivers, the "generic" USB driver and the ehci-hcd
-driver.
-
-    >>> ehci_hcd_driver = driver_set.create(
-    ...     package_name='linux-image-generic', name='ehci_hcd',
-    ...     license=License.GNU_GPL_V2)
-    >>> usb_controller_ehci_hcd_link = device_driver_link_set.create(
-    ...     device=usb_controller, driver=ehci_hcd_driver)
-    >>> print(usb_controller_ehci_hcd_link.device.name)
-    82801GBM/GHM (ICH7 Family) USB2 EHCI Controller
-    >>> print(usb_controller_ehci_hcd_link.driver.name)
-    ehci_hcd
-
-A scanner can be linked to the kernel driver for the physical interface
-and to its Sane backend.
-
-    >>> scanner_usb_driver_link = device_driver_link_set.create(
-    ...     device=optic_pro_ut12, driver=usb_driver)
-    >>> print(scanner_usb_driver_link.device.name)
-    OpticPro UT12
-    >>> print(scanner_usb_driver_link.driver.name)
-    usb
-
-    >>> sane_plustek_driver = driver_set.create(package_name='libsane',
-    ...                                         name='plustek',
-    ...                                         license=License.GNU_GPL_V2)
-    >>> scanner_sane_plustek_link = device_driver_link_set.create(
-    ...     device=optic_pro_ut12, driver=sane_plustek_driver)
-    >>> print(scanner_sane_plustek_link.device.name)
-    OpticPro UT12
-    >>> print(scanner_sane_plustek_link.driver.name)
-    plustek
-
-Devices can have alternative drivers. Let's assume that Plustek
-provides a closed-source driver for its scanners.
-
-    >>> closed_driver = driver_set.create(package_name='plustek-scanner',
-    ...                                   name='ut12',
-    ...                                   license=License.OTHER_PROPRIETARY)
-
-Now we can link the OpticPro UT12 to the closed-source driver too.
-
-    >>> link3 = device_driver_link_set.create(device=optic_pro_ut12,
-    ...                                         driver=closed_driver)
-    >>> print(link3.device.name)
-    OpticPro UT12
-    >>> print(link3.driver.name)
-    ut12
-
-We have two cases, where we do not (or can not) store driver information
-for a HWDevice record:
-  - it does not make much sense to assign drivers to an entire system;
-  - HAL can list a device without providing driver information. This is
-    for example the case for an unsupported device.
-
-Since the tables HWSubmissionDevice, HWTestAnswer and HWTestAnswerCount
-(see below) link to HWDeviceDriverLink, we need a record in the latter
-table, even when we do not know about a driver of a device or system.
-Thus we can create a HWDeviceDriverLink record, where driver is None.
-
-    >>> tuffbook_2600_device_driver_link = device_driver_link_set.create(
-    ...     device=tuffbook_2600, driver=None)
-
-The tuple (device, driver) must be unique.
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> device_driver_link_set.create(device=optic_pro_ut12,
-    ...                               driver=closed_driver)
-    <HWDeviceDriverLink...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-    >>> device_driver_link_set.create(device=tuffbook_2600, driver=None)
-    <HWDeviceDriverLink...
-    >>> store.flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ...
-    <BLANKLINE>
-    >>> LaunchpadZopelessLayer.txn.abort()
-
-An IHWDeviceDriverLink record is retrieved by
-IHWDeviceDriverSet.getByDeviceAndDriver().
-
-    >>> link = device_driver_link_set.getByDeviceAndDriver(optic_pro_ut12,
-    ...                                                    closed_driver)
-    >>> print(link.device.name)
-    OpticPro UT12
-    >>> print(link.driver.name)
-    ut12
-
-If no record exists for the given tuple (device, driver),
-IHWDeviceDriverSet.getByDeviceAndDriver() returns None.
-
-    >>> link = device_driver_link_set.getByDeviceAndDriver(optic_pro_ut12,
-    ...                                                    ehci_hcd_driver)
-    >>> print(link)
-    None
-
-HWDeviceDriverLinkSet.getOrCreate() returns an existing record matching
-the given parameters or creates a new one, if no existing record matches.
-
-    >>> link = device_driver_link_set.getOrCreate(optic_pro_ut12,
-    ...                                           closed_driver)
-    >>> print(link.device.name)
-    OpticPro UT12
-    >>> print(link.driver.name)
-    ut12
-    >>> print(link.id == link3.id)
-    True
-    >>> link = device_driver_link_set.getByDeviceAndDriver(optic_pro_ut12,
-    ...                                                    None)
-    >>> print(link)
-    None
-    >>> link = device_driver_link_set.getOrCreate(optic_pro_ut12, None)
-    >>> print(link.device.name)
-    OpticPro UT12
-    >>> print(link.driver)
-    None
-
-
-HWDeviceClass
--------------
-
-This table specifies the class or classes of a device. A device class
-describes the capabilities of a device, i.e, if it is a printer,
-input device, sound device etc. HWDeviceClass stores the main class of
-a device, enumerated by HWMainClass, as well as a sub-class, enumerated
-by HWSubClass. The required main class specifies the generic type of
-a device, the optional sub-class a more detailed type.
-
-Note that this information is not very reliable for some devices.
-For example, the USB bus has no specification at all for scanners;
-many vendors use the "generic" class/sub-class 0/0 for their scanners,
-while other vendors use 0x10/0, at least for some of their scanners.
-
-We have also hardware reports with obviously broken devices. For example
-some USB/WLAN adapters with vendor/product IDs 0x050d/0x705a and
-0x050d/0x905b claim that they implement the USB class 6, imaging.
-Similary, a number of PCI devices return clearly invalid class/subclass
-data.
-
-Let's pretend that Plustek uses the inofficial class/subclass 0x10/0
-for their Optic Pro 12 scanner.
-
-    >>> device_class_optic_pro_ut12 = optic_pro_ut12.getOrCreateDeviceClass(
-    ...     main_class=0x10, sub_class=0)
-    >>> device_class_optic_pro_ut12.main_class
-    16
-    >>> device_class_optic_pro_ut12.sub_class
-    0
-
-We can assign more than one class to devices. This should only be
-done for USB devices, which may have different classes for each
-of their interfaces. Let's add a second device class, "USB printer",
-for the scanner.
-
-    >>> device_class_printer = optic_pro_ut12.getOrCreateDeviceClass(
-    ...     main_class=0x07, sub_class=0x01)
-    >>> print(device_class_printer.device.name)
-    OpticPro UT12
-    >>> print(device_class_printer.main_class)
-    7
-    >>> print(device_class_printer.sub_class)
-    1
-
-IHWDdevice.classes contains the set of classes defined for this device.
-
-    >>> for device_class in optic_pro_ut12.classes:
-    ...     print(device_class.main_class, device_class.sub_class)
-    7 1
-    16 0
-
-We get the device class data from HWDB submissiions. Some submissions
-contain devices with obviously broken device class information, like
-a PCI SCSI controller claiming to be a graphics card, or a USB WLAN
-adapter claiming to be an imaging device. If we notice such kind of
-broken device class data, we can remove it by calling
-IHWDevice.removeDeviceClass().
-
-    >>> LaunchpadZopelessLayer.txn.commit()
-    >>> switch_dbuser('launchpad')
-    >>> optic_pro_ut12.removeDeviceClass(main_class=0x07, sub_class=0x01)
-    >>> for device_class in optic_pro_ut12.classes:
-    ...     print(device_class.main_class, device_class.sub_class)
-    16 0
-
-    >>> switch_dbuser('hwdb-submission-processor')
-
-
-HWSubmissionDevice
-------------------
-
-This table links devices listed in a submission to the
-HWDeviceDriverLink table. Additionally it links a device of a
-submission to its parent device, as modeled by HAL. A submission
-may contain two or more identical devices. In order to unambiugously
-relate HWSubmissionDevice records to devices as represented in the
-submitted data, we store the attribute "id" of the HAL device node
-too.
-
-We need a submission to which we can link HWSubmissionDevice entries.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
-    >>> sample_submission = getUtility(IHWSubmissionSet).getBySubmissionKey(
-    ...     'test_submission_id_1')
-
-A HWSubmissionDevice record for a system does not have any parent...
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionDeviceSet
-    >>> submission_device_set = getUtility(IHWSubmissionDeviceSet)
-    >>> submitted_tuffbook_2600 = submission_device_set.create(
-    ...     device_driver_link=tuffbook_2600_device_driver_link,
-    ...     submission=sample_submission, parent=None, hal_device_id=1)
-    >>> print(submitted_tuffbook_2600.device_driver_link.device.name)
-    Tuffbook 2600
-    >>> print(submitted_tuffbook_2600.device.name)
-    Tuffbook 2600
-    >>> print(submitted_tuffbook_2600.driver)
-    None
-    >>> print(submitted_tuffbook_2600.submission.submission_key)
-    test_submission_id_1
-    >>> print(submitted_tuffbook_2600.parent)
-    None
-    >>> print(submitted_tuffbook_2600.hal_device_id)
-    1
-
-...while ordinary device entries have the attribute parent set. The
-parent/child relationship may extend to several "generations". For
-example, our Inspiron 1234 has a USB host controller; its parent
-is the system itself.
-
-    >>> submitted_usb_ehci_hcd_controller = submission_device_set.create(
-    ...      device_driver_link=usb_controller_ehci_hcd_link,
-    ...      submission=sample_submission,
-    ...      parent=submitted_tuffbook_2600,
-    ...      hal_device_id=2)
-
-For the "output aspect" of the USB controller, we have
-usb_controller_usb_link; its parent is submitted_usb_ehci_hcd_controller
-that we just have created.
-
-    >>> submitted_usb_controller = submission_device_set.create(
-    ...      device_driver_link=usb_controller_usb_link,
-    ...      submission=sample_submission,
-    ...      parent=submitted_usb_ehci_hcd_controller,
-    ...      hal_device_id=3)
-    >>> print(submitted_usb_controller.device_driver_link.device.name)
-    82801GBM/GHM (ICH7 Family) USB2 EHCI Controller
-    >>> print(submitted_usb_controller.device_driver_link.driver.name)
-    usb
-    >>> print(submitted_usb_controller.device.name)
-    82801GBM/GHM (ICH7 Family) USB2 EHCI Controller
-    >>> print(submitted_usb_controller.driver.name)
-    usb
-    >>> print(submitted_usb_controller.hal_device_id)
-    3
-
-The USB controller is connected to an (often internal) USB hub. We don't
-yet have HWDevice and HWDeviceDriverLink records for this hub, so we must
-create them first.
-
-    >>> usb_hub = device_set.create(bus=HWBus.USB,
-    ...                             vendor_id='0x8086',
-    ...                             product_id='0x1234',
-    ...                             product_name='Intel USB hub')
-    >>> usb_hub_driver_link = device_driver_link_set.create(
-    ...     device=usb_hub, driver=usb_driver)
-    >>> submitted_usb_hub = submission_device_set.create(
-    ...     device_driver_link=usb_hub_driver_link,
-    ...     submission=sample_submission,
-    ...     parent=submitted_usb_controller,
-    ...     hal_device_id=4)
-
-Finally, a scanner may be connected to the USB hub.
-
-    >>> submitted_scanner_usb = submission_device_set.create(
-    ...     device_driver_link=scanner_usb_driver_link,
-    ...     submission=sample_submission,
-    ...     parent=submitted_usb_hub,
-    ...     hal_device_id=5)
-
-    >>> submitted_scanner_sane = submission_device_set.create(
-    ...     device_driver_link=scanner_sane_plustek_link,
-    ...     submission=sample_submission,
-    ...     parent=submitted_scanner_usb,
-    ...     hal_device_id=6)
-
-IHWSubmissionDevice records belonging to a submission are retrieved by
-IHWSubmissionDeviceSet.getDevices().
-
-    >>> submitted_devices = submission_device_set.getDevices(
-    ...     submission=sample_submission)
-    >>> def print_device(submission_device, indent=0):
-    ...     if indent > 0:
-    ...         print('-' * indent + '>', end=' ')
-    ...     device = submission_device.device_driver_link.device
-    ...     driver = submission_device.device_driver_link.driver
-    ...     print(device.bus_vendor.vendor_name.name, device.name, end=' ')
-    ...     if driver is not None:
-    ...         print(driver.name)
-    ...     else:
-    ...         print('(no driver)')
-    ...     for sub_device in submitted_devices:
-    ...         if sub_device.parent == submission_device:
-    ...             print_device(sub_device, indent+1)
-    >>> devices_without_parent = [
-    ...     device for device in submitted_devices if device.parent is None]
-    >>> root_device, = devices_without_parent
-    >>> print_device(root_device)
-    Tonka Tuffbook 2600 (no driver)
-    -> Intel 82801GBM/GHM (ICH7 Family) USB2 EHCI Controller ehci_hcd
-    --> Intel 82801GBM/GHM (ICH7 Family) USB2 EHCI Controller usb
-    ---> Unknown Intel USB hub usb
-    ----> Plustek OpticPro UT12 usb
-    -----> Plustek OpticPro UT12 plustek
-
-A single IHWSubmissionDevice record is retrieved by
-IHWSubmissionDeviceSet.get().
-
-    >>> submitted_device = submission_device_set.get(1)
-    >>> print_device(submitted_device)
-    MSI MS-7369 (no driver)
-
-This method returns None if a nonexistent ID is passed.
-
-    >>> print(submission_device_set.get(1000000))
-    None
-
-
-Statistical functions
----------------------
-
-HWSubmissionDeviceSet.numDevicesInSubmissions() returns how often a
-device appears in HWDB submissions.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE',
-    ...     product_id='ST3250820NS     '))
-    1
-
-If a device appears more than once in a submission, it is counted as often
-as it appears in the submission. We have currently only unique devices
-in the existing data, so let's add another IDE disk to submission 2.
-
-    >>> ide_disk = device_set.getByDeviceID(
-    ...     HWBus.IDE, 'SEAGATE', 'ST3250820NS     ')
-    >>> # We'll use the same parent as that of the existing IDE disk.
-    >>> parent = getUtility(IHWSubmissionDeviceSet).get(17)
-    >>> submission = getUtility(IHWSubmissionSet).getBySubmissionKey(
-    ...     'sample-submission')
-    >>> # The value of hal_device_id is arbitrary: It is intended for
-    >>> # for consistency checks and debugging.
-    >>> factory.makeHWSubmissionDevice(submission, ide_disk, None, parent, 1)
-    <HWSubmissionDevice at...
-
-The number returned for the query "count the number of Seagate
-ST3250820NS" disks increased.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE',
-    ...     product_id='ST3250820NS     '))
-    2
-
-Note that we did _not_ add a HWSubmissionDevice entry for the device
-driven by the kernel's sd driver. So, when we query the number of
-disks driven by the sd driver, we'll get 1 as without the recently
-added device.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd'))
-    1
-
-Let's now create another HWDB submission, and let's add
-HWSubmissionDevice entries for the disk and the new submission, both
-for the "plain" disk and for the disk driven by the sd driver.
-
-    >>> with lp_dbuser():
-    ...     # Set the emailaddress and hence the owner to a non-default
-    ...     # value so that we have submissions with different owners.
-    ...     submission = factory.makeHWSubmission(
-    ...         emailaddress='foo.bar@xxxxxxxxxxxxx')
-    >>> first_device = factory.makeHWSubmissionDevice(
-    ...     submission, ide_disk, None, None, 1)
-    >>> driver = getUtility(IHWDriverSet).getOrCreate(
-    ...     'linux-image-generic', 'sd')
-    >>> factory.makeHWSubmissionDevice(
-    ...     submission, ide_disk, driver, first_device, 2)
-    <HWSubmissionDevice at...
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...    HWSubmissionProcessingStatus)
-    >>> # We consider only submissions in the status PROCESSED for
-    >>> # statistics.
-    >>> submission.status = HWSubmissionProcessingStatus.PROCESSED
-    >>> LaunchpadZopelessLayer.txn.commit()
-
-The number of all ST3250820NS disks and of those disks driven by
-the sd driver has now increased by 1.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE',
-    ...     product_id='ST3250820NS     '))
-    3
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd'))
-    2
-
-We can optionally specify a driver's package name too.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic'))
-    1
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='nonsense'))
-    0
-
-And we can query for devices having any driver from a given package too.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic'))
-    1
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense'))
-    0
-
-We can also get the number of all devices controlled by a given driver.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     driver_name='sd'))
-    6
-
-We can limit this count to a given package.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic'))
-    5
-
-While the parameters for a device or a driver are optional, specifying
-neither of them leads to an error.
-
-    >>> submission_device_set.numDevicesInSubmissions()
-    Traceback (most recent call last):
-    ...
-    ParameterError: Specify (bus, vendor_id, product_id) or driver_name.
-
-We can additionally query for devices in submissions made for a given
-distribution. The device from the new submission we just created is
-not counted here, because we created the new submission for a new
-distribution.
-
-    >>> switch_dbuser('launchpad')
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu))
-    2
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=debian))
-    0
-
-Similary, we can limit the count to devices mentioned in submissions
-made for a given distroseries.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']))
-    2
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['warty']))
-    0
-
-And we can also search for devices mentioned in submissions made on a
-given processor architecture.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    2
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['hppa']))
-    0
-
-We can also query the number of devices controlled by a given driver
-for a distrotarget.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', distro_target=ubuntu['hoary']['i386']))
-    1
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense', distro_target=ubuntu['hoary']['i386']))
-    0
-
-And we can additionally specify a package name.
-
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    1
-    >>> print(submission_device_set.numDevicesInSubmissions(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='nonsense',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    0
-
-HWSubmissionSet.numSubmissionsWithDevice() returns all submissions
-mentioning a given device and all processed submissions.
-
-We have currently two processed submissions, both containing the
-Seagate ST3250820NS disk. Let's add another submission for hoary/i386
-and mark it as "processed" so that we have one processed submission
-that does not contain this disk.
-
-    >>> submission = factory.makeHWSubmission(
-    ...     distroarchseries=ubuntu['hoary']['i386'])
-    >>> submission.status = HWSubmissionProcessingStatus.PROCESSED
-
-    >>> submission_set = getUtility(IHWSubmissionSet)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE',
-    ...     product_id='ST3250820NS     '))
-    (2L, 3L)
-
-We can limit the results to a given driver...
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd'))
-    (2L, 3L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='usb'))
-    (0L, 3L)
-
-...or we can ask for all submissions containing any device controlled
-by a given driver.
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     driver_name='sd'))
-    (2L, 3L)
-
-While the parameters for a device or a driver are optional, specifying
-neither of them leads to an error.
-
-    >>> print(submission_set.numSubmissionsWithDevice())
-    Traceback (most recent call last):
-    ...
-    ParameterError: Specify (bus, vendor_id, product_id) or driver_name.
-
-This count can be limited to a given package.
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic'))
-    (1L, 3L)
-
-We can also limit the count to a distibution...
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=debian))
-    (0L, 0L)
-
-...to a distroseries...
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['warty']))
-    (0L, 0L)
-
-...or to a distroarchseries.
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['hppa']))
-    (0L, 0L)
-
-We can specify a distro target as well as a driver or package name.
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nosense', distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense', distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numSubmissionsWithDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense',
-    ...     package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-HWSubmission.numDeviceOwners() returns the number of device owners.
-Note that we identify owners by the email address as queried by the
-HWDB client, not by HWSubmission.owner.
-
-We have currently three submissions from two submitters, and submissions
-from both submitters mention the Seagate ST3250820NS disk. Let's add
-another, empty, submission for hoary/i386 from another submitter.
-
-    >>> submission = factory.makeHWSubmission(
-    ...     emailaddress='charles@xxxxxxxxxxx',
-    ...     distroarchseries=ubuntu['hoary']['i386'])
-    >>> submission.status = HWSubmissionProcessingStatus.PROCESSED
-
-Now we have three submitters, two of them own our Seagate disk.
-
-    >>> submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ')
-    (2L, 3L)
-
-We can limit the results to a given driver.
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd'))
-    (2L, 3L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='usb'))
-    (0L, 3L)
-
-We can also ask for submitters who use a given driver with any device.
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     driver_name='sd'))
-    (2L, 3L)
-
-This count can be limited to a given package.
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic'))
-    (1L, 3L)
-
-While the parameters for a device or a driver are optional, specifying
-neither of them leads to an error.
-
-    >>> print(submission_set.numOwnersOfDevice())
-    Traceback (most recent call last):
-    ...
-    ParameterError: Specify (bus, vendor_id, product_id) or driver_name.
-
-We can limit the count to a distibution...
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=debian))
-    (0L, 0L)
-
-...to a distroseries...
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['warty']))
-    (0L, 0L)
-
-...or to a distroarchseries.
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distro_target=ubuntu['hoary']['hppa']))
-    (0L, 0L)
-
-We can specify a distro target as well as a driver or package name.
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nosense', distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense', distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (1L, 2L)
-    >>> print(submission_set.numOwnersOfDevice(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense',
-    ...     package_name='linux-image-2.6.24-19-generic',
-    ...     distro_target=ubuntu['hoary']['i386']))
-    (0L, 2L)
-
-
-Relations between bugs and HWDB submissions
--------------------------------------------
-
-We can query which owners of a device, or which people owning a device
-controlled by a given driver, are related to a set of bugs. We must
-specify a device's bus, vendor ID and product ID and one or more bugs.
-
-By default, only bug reporters are looked up. Sample Person has made
-a hardware report containing the IDE disk Seagate ST3250820NS, and
-they have filed bug 1.
-
-    >>> from lp.bugs.interfaces.bug import IBugSet
-    >>> bug_set = getUtility(IBugSet)
-    >>> bug_one = bug_set.get(1)
-    >>> print(bug_one.owner.displayname)
-    Sample Person
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1]):
-    ...     print(person.displayname)
-    Sample Person
-
-If Foo Bar says that they are affected by this bug, they will be listed
-too, since they too own the Seagate disk.
-
-    >>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
-    >>> bug_one = bug_set.get(1)
-    >>> bug_one.markUserAffected(foo_bar)
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], affected_by_bug=True):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-When they say that they are not affected by the bug, they will no longer
-be listed.
-
-    >>> bug_one.markUserAffected(foo_bar, False)
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], affected_by_bug=True):
-    ...     print(person.displayname)
-    Sample Person
-
-By setting the parameter subscribed_to_bug to True, we can also look
-for bug subscribers who own a given device.
-
-    >>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-    >>> bug_one.unsubscribe(foo_bar, unsubscribed_by=foo_bar)
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True):
-    ...     print(person.displayname)
-    Sample Person
-
-We can specify more than one bug ID. If we specify bugs 1 and 3,
-deviceDriverOwnersAffectedByBugs() returns again Sample Person and
-Foo Bar.
-
-    >>> bug_three = bug_set.get(3)
-    >>> print(bug_three.owner.displayname)
-    Foo Bar
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1, 3]):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-Instead of passing a seqence of bug IDs, we can also specify one or
-more bug tags.
-
-    >>> from lp.services.searchbuilder import any
-    >>> from lp.bugs.interfaces.bugtask import IBugTaskSet
-    >>> from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
-
-    >>> bugtask_set = getUtility(IBugTaskSet)
-    >>> bugtasks = bugtask_set.search(
-    ...     BugTaskSearchParams(user=foo_bar, tag=any('pebcak')))
-    >>> owners = set(bugtask.bug.owner for bugtask in bugtasks)
-    >>> for owner in owners:
-    ...     print(owner.displayname)
-    Sample Person
-    >>> bugtasks = bugtask_set.search(
-    ...     BugTaskSearchParams(user=foo_bar, tag=any('crash')))
-    >>> owners = set(bugtask.bug.owner for bugtask in bugtasks)
-    >>> for owner in owners:
-    ...     print(owner.displayname)
-    Foo Bar
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_tags=['pebcak']):
-    ...     print(person.displayname)
-    Sample Person
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_tags=['pebcak', 'crash']):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-    >>> submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_tags=['lunch-money']).count()
-    0
-
-We can limit the result to owners who use a device with a given driver
-by providing a driver_name parameter...
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd', bug_tags=['pebcak', 'crash']):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-    >>> print(submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense', bug_tags=['pebcak', 'crash']).count())
-    0
-
-...or a given package name by providing a package_name parameter.
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic',
-    ...     bug_tags=['pebcak', 'crash']):
-    ...     print(person.displayname)
-    Sample Person
-
-    >>> print(submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense', bug_tags=['pebcak', 'crash']).count())
-    0
-
-Owners of private submissions are only included if the optional parameter
-`user` is the owner or an admin.
-
-    >>> private_submission = factory.makeHWSubmission(
-    ...     emailaddress='no-priv@xxxxxxxxxxxxx', private=True)
-    >>> switch_dbuser('hwdb-submission-processor')
-    >>> first_device = factory.makeHWSubmissionDevice(
-    ...     private_submission, ide_disk, None, None, 1)
-    >>> switch_dbuser('launchpad')
-    >>> no_priv = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
-    >>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True, user=no_priv):
-    ...     print(person.displayname)
-    No Privileges Person
-    Sample Person
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True, user=foo_bar):
-    ...     print(person.displayname)
-    No Privileges Person
-    Sample Person
-
-    >>> sample_person = getUtility(IPersonSet).getByEmail(
-    ...     'test@xxxxxxxxxxxxx')
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True, user=sample_person):
-    ...     print(person.displayname)
-    Sample Person
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], subscribed_to_bug=True):
-    ...     print(person.displayname)
-    Sample Person
-
-
-Searching for drivers instead of devices
-........................................
-
-We can also look for people using a given driver and being affected by a
-bug.
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[1]):
-    ...     print(person.displayname)
-    Sample Person
-
-If Foo Bar says that they are affected by this bug, they will be listed
-too, since their machine too uses the sd driver.
-
-    >>> bug_one.markUserAffected(foo_bar)
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[1], affected_by_bug=True):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-They are also listed if they subscribe to bug 1.
-
-    >>> bug_one.markUserAffected(foo_bar, False)
-    >>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[1], subscribed_to_bug=True):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-We can specify more than one bug ID. If we specify bugs 1 and 3,
-deviceDriverOwnersAffectedByBugs() returns again Sample Person and
-Foo Bar.
-
-    >>> bug_one.unsubscribe(foo_bar, unsubscribed_by=foo_bar)
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[1, 3]):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-Instead of passing a sequence of bug IDs, we can also specify one or
-more bug tags.
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_tags=['pebcak']):
-    ...     print(person.displayname)
-    Sample Person
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_tags=['pebcak', 'crash']):
-    ...     print(person.displayname)
-    Foo Bar
-    Sample Person
-
-    >>> submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_tags=['lunch-money']).count()
-    0
-
-We can also query for both bug ids and bug tags together.
-
-    >>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[2], bug_tags=['pebcak']):
-    ...     print(person.displayname)
-    Sample Person
-
-Also, confirm that nothing is returned where there is no matching
-tag on a given bug id.
-
-    >>> print(len(list(submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     driver_name='sd', bug_ids=[3], bug_tags=['pebcak']))))
-    0
-
-If neither a device nor a driver name is specified,
-deviceDriverOwnersAffectedByBugs() raises an error.
-
-    >>> submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bug_tags=['lunch-money'])
-    Traceback (most recent call last):
-    ...
-    ParameterError: Specify (bus, vendor_id, product_id) or driver_name.
-
-If one of the parameters bus, vendor_id, product_id is supplied, the
-others must be supplied too.
-
-    >>> submission_set.deviceDriverOwnersAffectedByBugs(
-    ...     bus=HWBus.IDE, bug_ids=[1], subscribed_to_bug=True)
-    Traceback (most recent call last):
-    ...
-    ParameterError: Either specify bus, vendor_id and product_id or none
-    of them.
-
-For a given bug, we can get a list of devices by device owner.
-The result is a tuple of (owner_id, bus_number, vender_id, product_id).
-
-    >>> sample_person = getUtility(IPersonSet).getByName('name12')
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1]):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-This method can also take a list of bugs.
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1, 3]):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'name16', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-We can also query for bug tags instead of bug numbers.
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_tags=['pebcak']):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_tags=['pebcak', 'crash']):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'name16', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-We can also query for both bug ids and bug tags together.
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[2], bug_tags=['pebcak']):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-Also, confirm that nothing is returned where there is no matching
-tag on a given bug id.
-
-    >>> print(len(submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[3], bug_tags=['pebcak'])))
-    0
-
-This method can also be used to list devices where the
-device owner is affected by the bug.
-
-    >>> bug_one.markUserAffected(foo_bar)
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1], affected_by_bug=True):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'name16', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-    >>> bug_one.markUserAffected(foo_bar, False)
-
-We can also check for when the user is subscribed to the bug.
-
-    >>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1], subscribed_to_bug=True):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'name16', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-    >>> bug_one.unsubscribe(foo_bar, unsubscribed_by=foo_bar)
-
-Data from private submissions is not included by default.
-The owner of a private submission can see their submission.
-
-    >>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1], subscribed_to_bug=True, user=no_priv):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'no-priv', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-An admin can also see no_priv's private entry.
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1], subscribed_to_bug=True, user=foo_bar):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-    (u'no-priv', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-But sample_person cannot see the private entry because they
-are not the owner or an admin.
-
-    >>> for entry in submission_set.hwInfoByBugRelatedUsers(
-    ...     bug_ids=[1], subscribed_to_bug=True, user=sample_person):
-    ...     print(entry)
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'MSI', u'MS-7369')
-    (u'name12', <DBItem HWBus.SYSTEM, (0) System>, u'Tonka', u'Tuffbook 2600')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x0455')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x10de', u'0x045d')
-    (u'name12', <DBItem HWBus.PCI, (1) PCI>, u'0x8086', u'0x27cc')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x04b4', u'0x6560')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x07b3', u'0x0017')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x0dda', u'0x2026')
-    (u'name12', <DBItem HWBus.USB, (2) USB>, u'0x8086', u'0x1234')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'Optiarc', u'DVD RW AD-7170S ')
-    (u'name12', <DBItem HWBus.IDE, (7) IDE>, u'SEAGATE', u'ST3250820NS     ')
-
-    >>> bug_one.unsubscribe(no_priv, unsubscribed_by=no_priv)
diff --git a/lib/lp/hardwaredb/doc/hwdb.txt b/lib/lp/hardwaredb/doc/hwdb.txt
deleted file mode 100644
index a29c585..0000000
--- a/lib/lp/hardwaredb/doc/hwdb.txt
+++ /dev/null
@@ -1,739 +0,0 @@
-= Hardware database =
-
-== Storage of raw submissions for the hardware database ==
-
-The hardware database clients submits an XML file with collected hardware
-information and other data. This file is stored without any parsing in a
-librarian file. A reference to this file is stored in the table HWSubmission,
-together with the following data:
-
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionSet
-    >>> sample_submission = getUtility(IHWSubmissionSet).getBySubmissionKey(
-    ...     'sample-submission')
-
-  * reference to the librarian file.
-
-    >>> print(sample_submission.raw_submission)
-     <...LibraryFileAlias instance at ...>
-
-  * date_created: Date and time of the submission (generated by the client).
-
-    This value is not very reliable:
-    * Some users do not care to set the correct time, date and time zone.
-    * Some machines have a broken CMOS battery and the user does not set
-      date and time after every boot.
-
-    >>> print(sample_submission.date_created)
-    2008-09-26 17:19:18+00:00
-
-  * date_submitted: Date and time of the submission (generated by the server).
-
-    >>> print(sample_submission.date_submitted)
-    2008-09-30 08:19:00.222131+00:00
-
-  * format: The format version of the submitted data, as given by the HWDB
-    client. See HWSubmissionFormat for valid values.
-
-    >>> print(sample_submission.format.name)
-    VERSION_1
-
-  * status: The status of the submission. See HWSubmissionProcessingStatus for
-    valid values.
-
-    >>> print(sample_submission.status.name)
-    PROCESSED
-
-  * private: If True, the submitter allows public access to the data. If
-    false, the data may be used only for statistical purposes.
-
-    >>> print(sample_submission.private)
-    False
-
-  * contactable: If True, the submitter agrees to be contacted by upstream
-    developers and package maintainers for tests etc.
-
-    >>> print(sample_submission.contactable)
-    False
-
-  * submission_key: A unique submission ID.
-
-    >>> print(sample_submission.submission_key)
-    sample-submission
-
-  * raw_emailaddress: The email address of the submitter.
-
-    >>> print(sample_submission.raw_emailaddress)
-    test@xxxxxxxxxxxxx
-
-  * owner: A reference to the Person table. This value is null, if the email
-    address does not belong (yet) to an entry in Person table.
-
-    >>> print(sample_submission.owner.displayname)
-    Sample Person
-
-  * distroarchseries: A reference to the distroarchseries of the submission.
-    This value is null, if the submitted values for distribution,
-    distroseries and architecture do not match an existing entry in the
-    Distroarchrelease table.
-
-    >>> print(sample_submission.distroarchseries.architecturetag)
-    i386
-
-    >>> print(sample_submission.distroarchseries.distroseries.name)
-    hoary
-
-    >>> print(
-    ...     sample_submission.distroarchseries.distroseries.distribution.name)
-    ubuntu
-
-  * system_fingerprint: A reference to an entry of the HWSystemFingerprint
-    table. This table stores the system name as returned by HAL
-    (system.vendor, system.product).
-
-    >>> print(sample_submission.system_fingerprint.fingerprint)
-    MSI MS-7369
-
-  * devices: The set set of HWSubmissionDevice records which belong to
-    this submission.
-
-    Access to all data except submissions is restricted to members of
-    the Canonical team.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> for submission_device in sample_submission.devices[:2]:
-    ...     print(submission_device.device.name)
-    MCP65 USB Controller
-    MCP65 SATA Controller
-    >>> login(ANONYMOUS)
-
-Limitations:
-  * "No name" products like mainboards from companies like ASRock
-     or Asus that are directly sold to end users have fingerprints like
-     "American Megatrends Inc. Unknown 1.0".
-  * A manufacturer may erroneously assign identical DMI values for product
-    and vendor to different systems.
-  * submissions for "counterfeit systems".
-  * users submitting bogus values using a patched HWDB client.
-
-The system fingerprint is stored in a separate table, HWSystemFingerprint.
-IHWSystemFingerPrintSet.createFingerprint creates a new row in this table.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSystemFingerprintSet
-    >>> from storm.store import Store
-    >>> hw_fingerprint_set = getUtility(IHWSystemFingerprintSet)
-    >>> fp = hw_fingerprint_set.createFingerprint(u'IBM T41')
-    >>> fp.fingerprint
-    u'IBM T41'
-
-Each fingerprint string in HWSystemFingerprint must be unique.
-
-    >>> import transaction
-    >>> transaction.commit()
-    >>> fp = hw_fingerprint_set.createFingerprint(u'IBM T41')
-    >>> Store.of(fp).flush()
-    Traceback (most recent call last):
-    ...
-    IntegrityError: ... violates unique constraint "hwsystemfingerprint...
-    <BLANKLINE>
-
-    >>> transaction.abort()
-
-IHWSystemFingerprint.getByName returns a fingerprint...
-
-    >>> fp = hw_fingerprint_set.getByName(u'IBM T41')
-    >>> fp.fingerprint
-    u'IBM T41'
-
-...or None, if no entry exists.
-
-    >>> print(hw_fingerprint_set.getByName(u'DEC PDP11'))
-    None
-
-IHWSubmissionSet.createSubmission creates a new entry in the
-HWSubmission table.
-
-    >>> import pytz
-    >>> from StringIO import StringIO
-    >>> from datetime import datetime
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...     HWSubmissionFormat,
-    ...     IHWSubmissionSet,
-    ...     )
-    >>> date_created = datetime(2007, 4, 1, tzinfo=pytz.timezone('UTC'))
-    >>> date_submitted = datetime(2007, 4, 2, tzinfo=pytz.timezone('UTC'))
-    >>> submission_data = 'submission data'
-    >>> hw_submission_set = getUtility(IHWSubmissionSet)
-    >>> submission = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=False,
-    ...     contactable=False,
-    ...     submission_key=u'unique-id-1',
-    ...     emailaddress=u'test@xxxxxxxxxxxxx',
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Dell Inspiron 1234')
-
-submission_key must be unique. The attempt to create a HWSubmission entry
-with an already existing submission_key raises HWSubmissionError.
-
-    >>> submission = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=False,
-    ...     contactable=False,
-    ...     submission_key=u'unique-id-1',
-    ...     emailaddress=u'test@xxxxxxxxxxxxx',
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Dell Inspiron 1234')
-    Traceback (most recent call last):
-    ...
-    HWSubmissionKeyNotUnique: A submission with this ID already exists
-
-The field `owner` points to the Person record of the submitter, if the
-submitted emailaddress belongs to an existing Launchpad account.
-
-    >>> submission.owner.displayname
-    u'Sample Person'
-
-If no Person record is yet known for the submitter's email address,
-the field `owner` is None.
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> beeblebrox = getUtility(IPersonSet).getByEmail(
-    ...     'beeblebrox@xxxxxxxxxxx')
-    >>> print(beeblebrox)
-    None
-
-    >>> submission = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=False,
-    ...     contactable=False,
-    ...     submission_key=u'unique-id-2',
-    ...     emailaddress=u'beeblebrox@xxxxxxxxxxx',
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Acer 6789')
-    >>> print(submission.owner)
-    None
-
-The submitted data can be retrieved via IHWSubmissionSet.getBySubmissionKey.
-
-    >>> submission = hw_submission_set.getBySubmissionKey(u'unique-id-1')
-    >>> submission.submission_key, submission.system_fingerprint.fingerprint
-    (u'unique-id-1', u'Dell Inspiron 1234')
-
-If a submission is marked as private, it is only returned by
-IHWSubmissionSet.getBySubmissionKey if the parameter `user` matches the
-owner of the submission...
-
-    >>> submission = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=True,
-    ...     contactable=False,
-    ...     submission_key=u'private-submission',
-    ...     emailaddress=u'test@xxxxxxxxxxxxx',
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Dell Inspiron 1234')
-    >>> print(hw_submission_set.getBySubmissionKey(u'private-submission'))
-    None
-    >>> sample_person = getUtility(IPersonSet).getByEmail('test@xxxxxxxxxxxxx')
-    >>> sample_person.displayname
-    u'Sample Person'
-    >>> submission = hw_submission_set.getBySubmissionKey(
-    ...     u'private-submission', sample_person)
-    >>> submission
-    <HWSubmission at ...>
-
-... or if `user` is an admin.
-
-    >>> foobar = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
-    >>> submission = hw_submission_set.getBySubmissionKey(
-    ...     u'private-submission', foobar)
-    >>> submission
-    <HWSubmission at ...>
-
-Only the owner and admins can view the submission details.
-
-    >>> submission.submission_key, submission.system_fingerprint.fingerprint
-    Traceback (most recent call last):
-    ...
-    Unauthorized: (...'submission_key', 'launchpad.View')
-
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> submission.submission_key, submission.system_fingerprint.fingerprint
-    (u'private-submission', u'Dell Inspiron 1234')
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> submission.submission_key, submission.system_fingerprint.fingerprint
-    (u'private-submission', u'Dell Inspiron 1234')
-
-Passing another person than the owner or an admin as the parameter `user`
-does not return private submissions.
-
-    >>> no_priv = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
-    >>> no_priv.displayname
-    u'No Privileges Person'
-    >>> print(hw_submission_set.getBySubmissionKey(u'private-submission',
-    ...     no_priv))
-    None
-
-    >>> login(ANONYMOUS)
-
-If no submission exists for a given ID, None is returned.
-
-    >>> print(hw_submission_set.getBySubmissionKey(u'unknown-ID'))
-    None
-
-IHWSubmissionSet.getByFingerprintName returns all submissions for a
-given fingerprint name. Private submissions are excluded, if the parameter
-`user` is omitted or if user is neither the owner nor an admin.
-
-    >>> submissions = hw_submission_set.getByFingerprintName(
-    ...     u'Dell Inspiron 1234')
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint, s.private)
-    ...  for s in submissions]
-    [(u'unique-id-1', u'Dell Inspiron 1234', False)]
-    >>> submissions = hw_submission_set.getByFingerprintName(
-    ...     u'Dell Inspiron 1234', no_priv)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint, s.private)
-    ...  for s in submissions]
-    [(u'unique-id-1', u'Dell Inspiron 1234', False)]
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> submissions = hw_submission_set.getByFingerprintName(
-    ...     u'Dell Inspiron 1234', sample_person)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint, s.private)
-    ...  for s in submissions]
-    [(u'private-submission', u'Dell Inspiron 1234', True),
-     (u'unique-id-1', u'Dell Inspiron 1234', False)]
-    >>> submissions = hw_submission_set.getByFingerprintName(
-    ...     u'Dell Inspiron 1234', foobar)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint, s.private)
-    ...  for s in submissions]
-    [(u'private-submission', u'Dell Inspiron 1234', True),
-     (u'unique-id-1', u'Dell Inspiron 1234', False)]
-
-If no submissions exist for the given system fingerprint, an empty result
-set is returned.
-
-    >>> list(hw_submission_set.getByFingerprintName('DEC PDP11'))
-    []
-
-IHWSubmissionSet.getByOwner returns all submissions of a person. Again,
-only non-private submissions are returned if no parameter `user` is passed
-to the method, or if the passed parameter is neither the owner nor an admin.
-
-    >>> submissions = hw_submission_set.getByOwner(sample_person)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint)
-    ...  for s in submissions]
-    [(u'unique-id-1', u'Dell Inspiron 1234'),
-     (u'sample-submission', u'MSI MS-7369'),
-     (u'test_submission_id_1', u'TONKA TUFFBOOK2600')]
-    >>> submissions = hw_submission_set.getByOwner(sample_person,
-    ...                                            user=no_priv)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint)
-    ...  for s in submissions]
-    [(u'unique-id-1', u'Dell Inspiron 1234'),
-     (u'sample-submission', u'MSI MS-7369'),
-     (u'test_submission_id_1', u'TONKA TUFFBOOK2600')]
-    >>> submissions = hw_submission_set.getByOwner(sample_person,
-    ...                                            user=sample_person)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint)
-    ...  for s in submissions]
-    [(u'private-submission', u'Dell Inspiron 1234'),
-     (u'unique-id-1', u'Dell Inspiron 1234'),
-     (u'sample-submission', u'MSI MS-7369'),
-     (u'test_submission_id_1', u'TONKA TUFFBOOK2600')]
-    >>> submissions = hw_submission_set.getByOwner(sample_person,
-    ...                                            user=foobar)
-    >>> [(s.submission_key, s.system_fingerprint.fingerprint)
-    ...  for s in submissions]
-    [(u'private-submission', u'Dell Inspiron 1234'),
-     (u'unique-id-1', u'Dell Inspiron 1234'),
-     (u'sample-submission', u'MSI MS-7369'),
-     (u'test_submission_id_1', u'TONKA TUFFBOOK2600')]
-
-When an anonymous submitter later registers a Launchpad account, the field
-`owner` is updated.
-
-    >>> from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
-    >>> from lp.registry.interfaces.person import PersonCreationRationale
-    >>> user = getUtility(IPersonSet).ensurePerson(
-    ...     u'beeblebrox@xxxxxxxxxxx', u'Beeblebrox',
-    ...     PersonCreationRationale.OWNER_CREATED_LAUNCHPAD)
-    >>> email = getUtility(IEmailAddressSet).getByEmail(
-    ...     u'beeblebrox@xxxxxxxxxxx')
-    >>> user.validateAndEnsurePreferredEmail(email)
-    >>> transaction.commit()
-    >>> from lp.scripts.garbo import HWSubmissionEmailLinker
-    >>> from lp.services.log.logger import DevNullLogger
-    >>> HWSubmissionEmailLinker(log=DevNullLogger()).run()
-    >>> submission = hw_submission_set.getBySubmissionKey(u'unique-id-2')
-    >>> print(submission.owner.displayname)
-    Beeblebrox
-
-If somebody has a Launchpad account, but submits HWDB test data using
-an email address which they have not added to their account, the submission
-will have the `owner` field set to None.
-
-    >>> submission = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=False,
-    ...     contactable=False,
-    ...     submission_key=u'unique-id-4',
-    ...     emailaddress=u'beeblebrox@xxxxxxxxxxxxxx',
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Acer 6789')
-    >>> Store.of(submission).flush()
-    >>> print(submission.owner)
-    None
-
-When they add this email address to their list of addresses in Launchpad,
-the field `owner` is updated.
-
-    >>> email = getUtility(IEmailAddressSet).new(
-    ...     u'beeblebrox@xxxxxxxxxxxxxx', user)
-    >>> transaction.commit()
-    >>> ignored = login_person(user)
-    >>> user.validateAndEnsurePreferredEmail(email)
-    >>> transaction.commit()
-    >>> HWSubmissionEmailLinker(log=DevNullLogger()).run()
-    >>> submission = hw_submission_set.getBySubmissionKey(u'unique-id-2')
-    >>> print(submission.owner.displayname)
-    Beeblebrox
-
-=== Submission Records without an email address ===
-
-A submission does not necessarily have an email address (anonymous
-submission). In this case the owner is always None.
-
-    >>> submission_no_owner = hw_submission_set.createSubmission(
-    ...     date_created=date_created,
-    ...     format=HWSubmissionFormat.VERSION_1,
-    ...     private=False,
-    ...     contactable=False,
-    ...     submission_key=u'unique-id-5',
-    ...     emailaddress=None,
-    ...     distroarchseries=None,
-    ...     raw_submission=StringIO(submission_data),
-    ...     filename=u'hwinfo.txt',
-    ...     filesize=len(submission_data),
-    ...     system_fingerprint=u'Dell Inspiron 1234')
-    >>> Store.of(submission_no_owner).flush()
-    >>> print(submission_no_owner.raw_emailaddress)
-    None
-    >>> print(submission_no_owner.owner)
-    None
-
-== Retrieving Submissions by Status ==
-
-The set of submissions with a given status can be retrieved by
-IHWSubmissionSet.getByStatus(). Only the publicly visible
-submissions are returned if no user is specified.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...     HWSubmissionProcessingStatus)
-    >>> def print_submissions(submissions):
-    ...     for submission in submissions:
-    ...         print(submission.submission_key, submission.private, end=' ')
-    ...         if submission.owner is not None:
-    ...             print(submission.owner.displayname, end=' ')
-    ...         else:
-    ...             print('(no owner)', end=' ')
-    ...         print(submission.status.name)
-    >>> new_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.SUBMITTED)
-    >>> print_submissions(new_submissions)
-    test_submission_id_1 False Sample Person SUBMITTED
-    unique-id-1 False Sample Person SUBMITTED
-    unique-id-2 False Beeblebrox SUBMITTED
-    unique-id-4 False Beeblebrox SUBMITTED
-    unique-id-5 False (no owner) SUBMITTED
-
-If a user is passed, private submissions of this user are returned too.
-
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...     HWSubmissionProcessingStatus)
-    >>> new_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.SUBMITTED, user=sample_person)
-    >>> print_submissions(new_submissions)
-    test_submission_id_1 False Sample Person SUBMITTED
-    unique-id-1 False Sample Person SUBMITTED
-    unique-id-2 False Beeblebrox SUBMITTED
-    private-submission True Sample Person SUBMITTED
-    unique-id-4 False Beeblebrox SUBMITTED
-    unique-id-5 False (no owner) SUBMITTED
-
-Submissions of other users are not returned.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...     HWSubmissionProcessingStatus)
-    >>> new_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.SUBMITTED, user=no_priv)
-    >>> print_submissions(new_submissions)
-    test_submission_id_1 False Sample Person SUBMITTED
-    unique-id-1 False Sample Person SUBMITTED
-    unique-id-2 False Beeblebrox SUBMITTED
-    unique-id-4 False Beeblebrox SUBMITTED
-    unique-id-5 False (no owner) SUBMITTED
-
-For admins, IHWSubmissionSet.getByStatus() returns private and public
-submissions...
-
-    >>> foobar = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> from lp.hardwaredb.interfaces.hwdb import (
-    ...     HWSubmissionProcessingStatus)
-    >>> new_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.SUBMITTED, user=foobar)
-    >>> print_submissions(new_submissions)
-    test_submission_id_1 False Sample Person SUBMITTED
-    unique-id-1 False Sample Person SUBMITTED
-    unique-id-2 False Beeblebrox SUBMITTED
-    private-submission True Sample Person SUBMITTED
-    unique-id-4 False Beeblebrox SUBMITTED
-    unique-id-5 False (no owner) SUBMITTED
-
-...as well as for the janitor.
-
-    >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-    >>> janitor = getUtility(ILaunchpadCelebrities).janitor
-    >>> new_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.SUBMITTED, user=janitor)
-    >>> print_submissions(new_submissions)
-    test_submission_id_1 False Sample Person SUBMITTED
-    unique-id-1 False Sample Person SUBMITTED
-    unique-id-2 False Beeblebrox SUBMITTED
-    private-submission True Sample Person SUBMITTED
-    unique-id-4 False Beeblebrox SUBMITTED
-    unique-id-5 False (no owner) SUBMITTED
-
-Searching for other statuses is possible too.
-
-    >>> submission_no_owner.status = HWSubmissionProcessingStatus.PROCESSED
-    >>> processed_submissions = hw_submission_set.getByStatus(
-    ...     HWSubmissionProcessingStatus.PROCESSED, user=foobar)
-    >>> print_submissions(processed_submissions)
-    sample-submission False Sample Person PROCESSED
-    unique-id-5 False (no owner) PROCESSED
-
-== Links to Bugs ==
-
-HWDB submissions can be linked to bugs. These links are created by
-IHWSubmissionBugSet.create().
-
-    >>> from lp.bugs.interfaces.bug import IBugSet
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet
-    >>> bug_one = getUtility(IBugSet).get(1)
-    >>> submission_bug_set = getUtility(IHWSubmissionBugSet)
-    >>> submission_bug = submission_bug_set.create(
-    ...     submission_no_owner, bug_one)
-    >>> transaction.commit()
-    >>> print(submission_bug.submission.submission_key)
-    unique-id-5
-    >>> submission_bug.bug.id
-    1
-
-Links between a bug and a submission are unique. If we call
-IHWSubmissionBugSet.create() twice for the same bug and HWDB submission,
-the method returns the existing record.
-
-    >>> second_link = submission_bug_set.create(submission_no_owner, bug_one)
-    >>> second_link.id == submission_bug.id
-    True
-
-HWSubmissionBugSet.submissionsForBug() returns all submissions linked to
-a given bug.
-
-    >>> for submission in submission_bug_set.submissionsForBug(bug_one):
-    ...     print(submission.submission_key)
-    unique-id-5
-
-Private submissions are only returned if the current user is an admin
-or the owner of the private submission.
-
-    >>> submission = hw_submission_set.getBySubmissionKey(
-    ...     u'private-submission', sample_person)
-    >>> submission_bug_set.create(submission, bug_one)
-    <HWSubmissionBug at...
-    >>> for submission in submission_bug_set.submissionsForBug(
-    ...     bug_one, user=sample_person):
-    ...     print(submission.submission_key)
-    private-submission
-    unique-id-5
-
-    >>> for submission in submission_bug_set.submissionsForBug(
-    ...     bug_one, user=foobar):
-    ...     print(submission.submission_key)
-    private-submission
-    unique-id-5
-
-Other registered persons can see only public submissions...
-
-    >>> for submission in submission_bug_set.submissionsForBug(
-    ...     bug_one, user=no_priv):
-    ...     print(submission.submission_key)
-    unique-id-5
-
-...as well as anonymous users.
-
-    >>> for submission in submission_bug_set.submissionsForBug(
-    ...     bug_one):
-    ...     print(submission.submission_key)
-    unique-id-5
-
-Existing links can also be removed.
-
-    >>> submission_bug_set.remove(submission_no_owner, bug_one)
-    >>> submission_bug_set.submissionsForBug(bug_one).count()
-    0
-
-    >>> for submission in submission_bug_set.submissionsForBug(
-    ...     bug_one, user=foobar):
-    ...     print(submission.submission_key)
-    private-submission
-
-
-== General Search Method ==
-
-IHWSubmissionSet.search() allows the look up of submissions which
-contain a specfic device, a specific driver or which are made for a
-specific distribution or processor architecture.
-
-All parameters are optional; if none are given, all publicly visible
-submissions are returned.
-
-    >>> for submission in hw_submission_set.search():
-    ...     print(submission.submission_key)
-    test_submission_id_1
-    sample-submission
-    unique-id-1
-    unique-id-2
-    unique-id-4
-    unique-id-5
-
-If the parameter `user` is specified, private submissions from this user
-are returned too.
-
-    >>> for submission in hw_submission_set.search(user=sample_person):
-    ...     print(submission.submission_key)
-    test_submission_id_1
-    sample-submission
-    unique-id-1
-    unique-id-2
-    private-submission
-    unique-id-4
-    unique-id-5
-
-Admins have access to all submissions.
-
-    >>> for submission in hw_submission_set.search(user=foobar):
-    ...     print(submission.submission_key)
-    test_submission_id_1
-    sample-submission
-    unique-id-1
-    unique-id-2
-    private-submission
-    unique-id-4
-    unique-id-5
-
-If a distribution is specified, only those submissions that were made
-for that distribution are returned.
-
-    >>> from lp.registry.interfaces.distribution import (
-    ...     IDistributionSet)
-    >>> distribution_set = getUtility(IDistributionSet)
-    >>> ubuntu = distribution_set.getByName('ubuntu')
-    >>> for submission in hw_submission_set.search(distribution=ubuntu):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> debian = distribution_set.getByName('debian')
-    >>> print(hw_submission_set.search(distribution=debian).count())
-    0
-
-If an architecture is specified, only those submissions that were made
-for that architecture are returned.
-
-    >>> for submission in hw_submission_set.search(architecture='i386'):
-    ...     print(submission.submission_key)
-    sample-submission
-
-    >>> print(hw_submission_set.search(architecture='amd64').count())
-    0
-
-
-If a device is specified, only those submissions that include
-the device are returned.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import HWBus, IHWDeviceSet
-    >>> device_set = getUtility(IHWDeviceSet)
-    >>> msi_mainboard = device_set.getByDeviceID(
-    ...     HWBus.SYSTEM, 'MSI', 'MS-7369')
-    >>> for submission in hw_submission_set.search(device=msi_mainboard):
-    ...     print(submission.submission_key)
-    sample-submission
-
-If a driver is specified, only those submissions for that driver
-are returned.
-
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDriverSet
-    >>> driver_set = getUtility(IHWDriverSet)
-    >>> ehci_driver = driver_set.getByPackageAndName(
-    ...     'linux-image-2.6.24-19-generic', 'ehci_hcd')
-    >>> for submission in hw_submission_set.search(driver=ehci_driver):
-    ...     print(submission.submission_key)
-    sample-submission
-
-If a distroseries is specified, only submissions for that distroseries
-are returned.
-
-    >>> from lp.testing.factory import LaunchpadObjectFactory
-    >>> factory = LaunchpadObjectFactory()
-
-    >>> warty = ubuntu['warty']
-    >>> warty_arch_series = factory.makeDistroArchSeries(distroseries=warty)
-    >>> submission = factory.makeHWSubmission(distroarchseries=warty_arch_series)
-    >>> for submission in hw_submission_set.search(distroseries=warty):
-    ...     print(submission.distroarchseries.distroseries.name)
-    warty
-
-It is also possible to search for a distroseries and architecture.
-
-    >>> from lp.buildmaster.interfaces.processor import IProcessorSet
-    >>> amd64 = getUtility(IProcessorSet).getByName('amd64')
-    >>> warty_amd64 = factory.makeDistroArchSeries(
-    ...     distroseries=warty, architecturetag='amd64',
-    ...     processor=amd64)
-    >>> submission = factory.makeHWSubmission(distroarchseries=warty_amd64)
-    >>> for submission in hw_submission_set.search(distroseries=warty, architecture='amd64'):
-    ...     print('%s %s ' % (
-    ...         submission.distroarchseries.distroseries.name,
-    ...         submission.distroarchseries.architecturetag))
-    warty amd64
-
-And we can also search for submissions from a particular user.
-
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> owner = getUtility(IPersonSet).getByName('name12')
-    >>> set(submission.owner.name for submission
-    ...     in hw_submission_set.search(owner=owner))
-    set([u'name12'])
diff --git a/lib/lp/hardwaredb/interfaces/__init__.py b/lib/lp/hardwaredb/interfaces/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/interfaces/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/interfaces/hwdb.py b/lib/lp/hardwaredb/interfaces/hwdb.py
deleted file mode 100644
index e418abd..0000000
--- a/lib/lp/hardwaredb/interfaces/hwdb.py
+++ /dev/null
@@ -1,1717 +0,0 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Interfaces related to the hardware database."""
-
-__metaclass__ = type
-
-__all__ = [
-    'HWBus',
-    'HWDB_SUBMISSIONS_DISABLED_FEATURE_FLAG',
-    'HWSubmissionFormat',
-    'HWSubmissionKeyNotUnique',
-    'HWSubmissionProcessingStatus',
-    'HWSubmissionsDisabledError',
-    'IHWDBApplication',
-    'IHWDevice',
-    'IHWDeviceClass',
-    'IHWDeviceClassSet',
-    'IHWDeviceDriverLink',
-    'IHWDeviceDriverLinkSet',
-    'IHWDeviceNameVariant',
-    'IHWDeviceNameVariantSet',
-    'IHWDeviceSet',
-    'IHWDriver',
-    'IHWDriverName',
-    'IHWDriverPackageName',
-    'IHWDriverSet',
-    'IHWSubmission',
-    'IHWSubmissionBug',
-    'IHWSubmissionBugSet',
-    'IHWSubmissionForm',
-    'IHWSubmissionSet',
-    'IHWSubmissionDevice',
-    'IHWSubmissionDeviceSet',
-    'IHWSystemFingerprint',
-    'IHWSystemFingerprintSet',
-    'IHWVendorID',
-    'IHWVendorIDSet',
-    'IHWVendorName',
-    'IHWVendorNameSet',
-    'IllegalQuery',
-    'ParameterError',
-    ]
-
-from lazr.enum import (
-    DBEnumeratedType,
-    DBItem,
-    )
-from lazr.restful.declarations import (
-    call_with,
-    error_status,
-    export_destructor_operation,
-    export_read_operation,
-    export_write_operation,
-    exported,
-    exported_as_webservice_entry,
-    operation_parameters,
-    operation_returns_collection_of,
-    operation_returns_entry,
-    REQUEST_USER,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    )
-from lazr.restful.interface import copy_field
-from six.moves import http_client
-from zope.component import getUtility
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
-from zope.schema import (
-    ASCIILine,
-    Bool,
-    Bytes,
-    Choice,
-    Datetime,
-    Int,
-    List,
-    TextLine,
-    )
-
-from lp import _
-from lp.app.interfaces.launchpad import IPrivacy
-from lp.app.validators import LaunchpadValidationError
-from lp.app.validators.email import valid_email
-from lp.app.validators.name import valid_name
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.person import IPerson
-from lp.registry.interfaces.product import License
-from lp.services.webapp.interfaces import ILaunchpadApplication
-from lp.services.webservice.apihelpers import (
-    patch_collection_property,
-    patch_reference_property,
-    )
-from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
-
-
-HWDB_SUBMISSIONS_DISABLED_FEATURE_FLAG = 'hardwaredb.submissions.disabled'
-
-
-@error_status(http_client.GONE)
-class HWSubmissionsDisabledError(Exception):
-    """An exception saying that hardware database submissions are disabled."""
-
-    def __init__(self, message=None):
-        if message is None:
-            message = (
-                "Launchpad's hardware database is obsolete and is no longer "
-                "accepting submissions.  Please use "
-                "https://answers.launchpad.net/launchpad/+addquestion to tell "
-                "us about your requirements if you still need it.")
-        super(HWSubmissionsDisabledError, self).__init__(message)
-
-
-def validate_new_submission_key(submission_key):
-    """Check, if submission_key already exists in HWDBSubmission."""
-    if not valid_name(submission_key):
-        raise LaunchpadValidationError(
-            'Submission key can contain only lowercase alphanumerics.')
-    submission_set = getUtility(IHWSubmissionSet)
-    if submission_set.submissionIdExists(submission_key):
-        raise LaunchpadValidationError(
-            'Submission key already exists.')
-    return True
-
-
-def validate_email_address(emailaddress):
-    """Validate an email address.
-
-    Returns True for valid addresses, else raises LaunchpadValidationError.
-    The latter allows convenient error handling by LaunchpadFormView.
-    """
-    if not valid_email(emailaddress):
-        raise LaunchpadValidationError(
-            'Invalid email address')
-    return True
-
-
-class HWSubmissionKeyNotUnique(Exception):
-    """Prevent two or more submission with identical submission_key."""
-
-
-class HWSubmissionProcessingStatus(DBEnumeratedType):
-    """The status of a submission to the hardware database."""
-
-    INVALID = DBItem(0, """
-        Invalid submission
-
-        The submitted data could not be parsed.
-        """)
-
-    SUBMITTED = DBItem(1, """
-        Submitted
-
-        The submitted data has not yet been processed.
-        """)
-
-    PROCESSED = DBItem(2, """
-        Processed
-
-        The submitted data has been processed.
-        """)
-
-
-class HWSubmissionFormat(DBEnumeratedType):
-    """The format version of the submitted data."""
-
-    VERSION_1 = DBItem(1, "Version 1")
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWSubmission(Interface, IPrivacy):
-    """Raw submission data for the hardware database.
-
-    See doc/hwdb.txt for details about the attributes.
-    """
-
-    date_created = exported(
-        Datetime(
-            title=_(u'Date Created'), required=True, readonly=True))
-    date_submitted = exported(
-        Datetime(
-            title=_(u'Date Submitted'), required=True, readonly=True))
-    format = exported(
-        Choice(
-            title=_(u'Format Version'), required=True,
-            vocabulary=HWSubmissionFormat, readonly=True))
-    status = exported(
-        Choice(
-            title=_(u'Submission Status'), required=True,
-            vocabulary=HWSubmissionProcessingStatus, readonly=True))
-    # This is redefined from IPrivacy.private because the attribute is
-    # is required.
-    private = exported(
-        Bool(
-            title=_(u'Private Submission'), required=True))
-    contactable = exported(
-        Bool(
-            title=_(u'Contactable'), required=True, readonly=True))
-    submission_key = exported(
-        TextLine(
-            title=_(u'Unique Submission ID'), required=True, readonly=True))
-    owner = exported(
-        Reference(
-            IPerson, title=_(u"The owner of this submission"), readonly=True))
-    distroarchseries = Attribute(
-        _(u'The DistroArchSeries'))
-    raw_submission = exported(
-        Bytes(title=_(u'The raw submission data'), required=True,
-              readonly=True))
-    system_fingerprint = Attribute(
-        _(u'The system this submmission was made on'))
-    raw_emailaddress = TextLine(
-        title=_('Email address'), required=True)
-
-    devices = exported(
-        CollectionField(
-            title=_(u"The HWSubmissionDevice records for this submission."),
-            value_type=Reference(schema=Interface)))
-
-
-class IHWSubmissionForm(Interface):
-    """The schema used to build the HW submission form."""
-
-    date_created = Datetime(
-        title=_(u'Date Created'), required=True)
-    format = Choice(
-        title=_(u'Format Version'), required=True,
-        vocabulary=HWSubmissionFormat)
-    private = Bool(
-        title=_(u'Private Submission'), required=True)
-    contactable = Bool(
-        title=_(u'Contactable'), required=True)
-    submission_key = ASCIILine(
-        title=_(u'Unique Submission Key'), required=True,
-        constraint=validate_new_submission_key)
-    emailaddress = TextLine(
-            title=_(u'Email address'), required=True,
-            constraint=validate_email_address)
-    distribution = TextLine(
-        title=_(u'Distribution'), required=True)
-    distroseries = TextLine(
-        title=_(u'Distribution Release'), required=True)
-    architecture = TextLine(
-        title=_(u'Processor Architecture'), required=True)
-    system = TextLine(
-        title=_(u'System name'), required=True)
-    submission_data = Bytes(
-        title=_(u'Submission data'), required=True)
-
-
-class IHWSubmissionSet(Interface):
-    """The set of HWDBSubmissions."""
-
-    def createSubmission(date_created, format, private, contactable,
-                         submission_key, emailaddress, distroarchseries,
-                         raw_submission, filename, filesize, system):
-        """Store submitted raw hardware information in a Librarian file.
-
-        If a submission with an identical submission_key already exists,
-        an HWSubmissionKeyNotUnique exception is raised."""
-
-    def getBySubmissionKey(submission_key, user=None):
-        """Return the submission with the given submission key, or None.
-
-        If a submission is marked as private, it is only returned if
-        user == HWSubmission.owner, of if user is an admin.
-        """
-
-    def getByFingerprintName(name, user=None):
-        """Return the submissions for the given system fingerprint string.
-
-        If a submission is marked as private, it is only returned if
-        user == HWSubmission.owner, or if user is an admin.
-        """
-
-    def getByOwner(owner, user=None):
-        """Return the submissions for the given person.
-
-        If a submission is marked as private, it is only returned if
-        user == HWSubmission.owner, or if user is an admin.
-        """
-
-    def submissionIdExists(submission_key):
-        """Return True, if a record with ths ID exists, else return False."""
-
-    def getByStatus(status, user=None):
-        """Return the submissions with the given status.
-
-        :param status: A status as enumerated in
-            `HWSubmissionProcessingStatus`.
-        :param user: The `IPerson` running the query.
-        :return: The submissions having the given status.
-
-        If no user is specified, only public submissions are returned.
-        If a regular user is specified, public submissions and private
-        submissions owned by the user are returned.
-        For admins and for the janitor, all submissions with the given
-        status are returned.
-        """
-
-    def search(user=None, device=None, driver=None, distribution=None,
-               distroseries=None, architecture=None, owner=None,
-               created_before=None, created_after=None,
-               submitted_before=None, submitted_after=None):
-        """Return the submissions matiching the given parmeters.
-
-        :param user: The `IPerson` running the query. Private submissions
-            are returned only if the person running the query is the
-            owner or an admin.
-        :param device: Limit results to submissions containing this
-            `IHWDevice`.
-        :param driver: Limit results to submissions containing devices
-            that use this `IHWDriver`.
-        :param distribution: Limit results to submissions made for
-            this `IDistribution`.
-        :param distroseries: Limit results to submissions made for
-            this `IDistroSeries`.
-        :param architecture: Limit results to submissions made for
-            a specific architecture.
-        :param owner: Limit results to submissions from this person.
-        :param created_before: Exclude results created after this
-            date.
-        :param created_after: Exclude results created before or on
-            this date.
-        :param submitted_before: Exclude results submitted after this
-            date.
-        :param submitted_after: Exclude results submitted before or on
-            this date.
-
-        Only one of :distribution: or :distroseries: may be supplied.
-        """
-
-    def numSubmissionsWithDevice(bus=None, vendor_id=None, product_id=None,
-                                 driver_name=None, package_name=None,
-                                 distro_target=None):
-        """Count the number of submissions mentioning a device or a driver.
-
-        :return: A tuple (submissions_with_device, all_submissions)
-            where submissions_with_device is the number of submissions having
-            the given device or driver and matching the distro_target
-            criterion and where all_submissions is the number of submissions
-            matching the distro_target criterion.
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: The name of the driver used for the device
-            (optional).
-        :param package_name: The name of the package the driver is a part of.
-            (optional).
-        :param distro_target: Limit the count to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-    def numOwnersOfDevice(bus=None, vendor_id=None, product_id=None,
-                          driver_name=None, package_name=None,
-                          distro_target=None):
-        """The number of people owning a device or using a driver.
-
-        :return: A tuple (device_owners, all_hardware_reporters)
-            where device_owners is the number of people who made a HWDB
-            submission containing the given device or driver, optionally
-            limited to submissions made for the given distro_target.
-            all_hardware_reporters is the number of persons who made
-            a HWDB submission, optionally limited to submission made
-            on the given distro_target installation.
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: The name of the driver used for the device
-            (optional).
-        :param package_name: The name of the package the driver is a part of.
-            (optional).
-        :param distro_target: Limit the count to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-    def deviceDriverOwnersAffectedByBugs(
-        bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """Return persons affected by given bugs and owning a given device.
-
-        :param bus: The `HWBus` of the device.
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :param driver_name: Limit the search to devices controlled by the
-            given driver.
-        :param package_name: Limit the search to devices controlled by a
-            driver from the given package.
-        :param bug_ids: A sequence of bug IDs for which affected device
-            owners are looked up.
-        :param bug_tags: A sequence of bug tags.
-        :param affected_by_bug: If True, those persons are looked up that
-            have marked themselves as being affected by a one of the bugs
-            matching the bug criteria.
-        :param subscribed_to_bug: If True, those persons are looked up that
-            are subscribed to a bug matching one of the bug criteria.
-        :param user: The person making the query.
-
-        `bug_ids` must be a non-empty sequence of bug IDs, or `bug_tags`
-        must be a non-empty sequence of bug tags.
-
-        The parameters `bus`, `vendor_id`, `product_id` must not be None, or
-        `driver_name` must not be None.
-
-        By default, only those persons are returned which have reported a
-        bug matching the given bug conditions.
-
-        Owners of private submissions are returned only if user is the
-        owner of the private submission or if user is an admin.
-        """
-
-    def hwInfoByBugRelatedUsers(
-        bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """Return a list of owners and devices related to given bugs.
-
-        Actually returns a list of tuples where the tuple is of the form,
-        (person name, bus name, vendor id, product id).`
-
-        :param bug_ids: A sequence of bug IDs for which affected
-            are looked up.
-        :param bug_tags: A sequence of bug tags
-        :param affected_by_bug: If True, those persons are looked up that
-            have marked themselves as being affected by a one of the bugs
-            matching the bug criteria.
-        :param subscribed_to_bug: If True, those persons are looked up that
-            are subscribed to a bug matching one of the bug criteria.
-        :param user: The person making the query.
-        """
-
-
-class IHWSystemFingerprint(Interface):
-    """Identifiers of a computer system."""
-
-    fingerprint = Attribute(u'A unique identifier of a system')
-
-
-class IHWSystemFingerprintSet(Interface):
-    """The set of HWSystemFingerprints."""
-
-    def getByName(fingerprint):
-        """Lookup an IHWSystemFingerprint by its value.
-
-        Return None, if a fingerprint `fingerprint` does not exist."""
-
-    def createFingerprint(fingerprint):
-        """Create an entry in the fingerprint list.
-
-        Return the new entry."""
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWDriver(Interface):
-    """Information about a device driver."""
-
-    id = exported(
-        Int(title=u'Driver ID', required=True, readonly=True))
-
-    package_name = exported(
-        TextLine(
-            title=u'Package Name', required=False,
-            description=_("The name of the package written without spaces in "
-                          "lowercase letters and numbers."),
-            default=u''))
-
-    name = exported(
-        TextLine(
-            title=u'Driver Name', required=True,
-            description=_("The name of the driver written without spaces in "
-                          "lowercase letters and numbers.")))
-
-    license = exported(
-        Choice(
-            title=u'License of the Driver', required=False,
-            vocabulary=License))
-
-    @operation_parameters(
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given distribution series.'),
-            required=False),
-        architecture=TextLine(
-            title=u'A processor architecture',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given architecture.'),
-            required=False),
-        owner=copy_field(IHWSubmission['owner']))
-    @operation_returns_collection_of(IHWSubmission)
-    @export_read_operation()
-    def getSubmissions(distribution=None, distroseries=None,
-                       architecture=None, owner=None):
-        """List all submissions which mention this driver.
-
-        :param distribution: Limit results to submissions for this
-            `IDistribution`.
-        :param distroseries: Limit results to submissions for this
-            `IDistroSeries`.
-        :param architecture: Limit results to submissions for this
-            architecture.
-        :param owner: Limit results to submissions from this person.
-
-        Only submissions matching all given criteria are returned.
-        Only one of :distribution: or :distroseries: may be supplied.
-        """
-
-
-class IHWDriverSet(Interface):
-    """The set of device drivers."""
-
-    def create(package_name, name, license):
-        """Create a new IHWDriver instance.
-
-        :param package_name: The name of the packages containing the driver.
-        :param name: The name of the driver.
-        :param license: The license of the driver.
-        :return: The new IHWDriver instance.
-        """
-
-    def getByPackageAndName(package_name, name):
-        """Return an IHWDriver instance for the given parameters.
-
-        :param package_name: The name of the packages containing the driver.
-        :param name: The name of the driver.
-        :return: An IHWDriver instance or None, if no record exists for
-            the given parameters.
-        """
-
-    def getOrCreate(package_name, name, license=None):
-        """Return an IHWDriver instance or create one.
-
-        :param package_name: The name of the packages containing the driver.
-        :param name: The name of the driver.
-        :param license: The license of the driver.
-        :return: An IHWDriver instance or None, if no record exists for
-            the given parameters.
-        """
-
-    def search(package_name=None, name=None):
-        """Return the drivers matching the given parameters.
-
-        :param package_name: The name of the packages containing the driver.
-            If package_name is not given or None, the result set is
-            not limited to a specific package name.
-            If package_name == '', those records are returned where
-            record.package_name == '' or record.package_name is None.
-            Otherwise only records matching the given name are returned.
-        :param name: The name of the driver.
-            If name is not given or None, the result set is not limited to
-            a specific driver name.
-            Otherwise only records matching the given name are returned.
-        :return: A sequence of IHWDriver instances.
-        """
-
-    def getByID(id):
-        """Return an IHWDriver record with the given database ID.
-
-        :param id: The database ID.
-        :return: An IHWDriver instance.
-        """
-
-    def all_driver_names():
-        """Return all known distinct driver names appearing in HWDriver."""
-
-    def all_package_names():
-        """Return all known distinct package names appearing in HWDriver."""
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWDriverName(Interface):
-    """A driver name as appearing in `IHWDriver`.
-    """
-
-    name = exported(
-        TextLine(
-            title=u'Driver Name', required=True, readonly=True,
-            description=_("The name of a driver as it appears in "
-                          "IHWDriver.")))
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWDriverPackageName(Interface):
-    """A driver name as appearing in `IHWDriver`.
-    """
-
-    package_name = exported(
-        TextLine(
-            title=u'Package Name', required=True, readonly=True,
-            description=_("The name of a package as it appears in "
-                          "IHWDriver.")))
-
-
-# Identification of a hardware device.
-#
-# In theory, a tuple (bus, vendor ID, product ID) should identify
-# a device unambiguously. In practice, there are several cases where
-# this tuple can identify more than one device:
-#
-# - A USB chip or chipset may be used in different devices.
-#   A real world example:
-#     - Plustek sold different scanner models with the USB ID
-#       0x7b3/0x0017. Some of these scanners have for example a
-#       different maximum scan size.
-#
-# Hence we identify a device by tuple (bus, vendor ID, product ID,
-# variant). In the example above, we might use (HWBus.USB, '0x7b3',
-# '0x0017', 'OpticPro UT12') and (HWBus.USB, '0x7b3', '0x0017',
-# 'OpticPro UT16')
-
-class HWBus(DBEnumeratedType):
-    """The bus that connects a device to a computer."""
-
-    SYSTEM = DBItem(0, 'System')
-
-    PCI = DBItem(1, 'PCI')
-
-    USB = DBItem(2, 'USB')
-
-    IEEE1394 = DBItem(3, 'IEEE1394')
-
-    SCSI = DBItem(4, 'SCSI')
-
-    PARALLEL = DBItem(5, 'Parallel Port')
-
-    SERIAL = DBItem(6, 'Serial port')
-
-    IDE = DBItem(7, 'IDE')
-
-    ATA = DBItem(8, 'ATA')
-
-    FLOPPY = DBItem(9, 'Floppy')
-
-    IPI = DBItem(10, 'IPI')
-
-    SATA = DBItem(11, 'SATA')
-
-    SAS = DBItem(12, 'SAS')
-
-    PCCARD = DBItem(13, 'PC Card (32 bit)')
-
-    PCMCIA = DBItem(14, 'PCMCIA (16 bit)')
-
-
-class IHWVendorName(Interface):
-    """A list of vendor names."""
-    name = TextLine(title=u'Vendor Name', required=True)
-
-
-class IHWVendorNameSet(Interface):
-    """The set of vendor names."""
-    def create(name):
-        """Create and return a new vendor name.
-
-        :return: A new IHWVendorName instance.
-
-        An IntegrityError is raised, if the name already exists.
-        """
-
-    def getByName(name):
-        """Return the IHWVendorName record for the given name.
-
-        :param name: The vendor name.
-        :return: An IHWVendorName instance where IHWVendorName.name==name
-            or None, if no such instance exists.
-        """
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWVendorID(Interface):
-    """A list of vendor IDs for different busses associated with vendor names.
-    """
-    id = exported(
-        Int(title=u'The Database ID', required=True, readonly=True))
-
-    bus = exported(
-        Choice(
-            title=u'The bus that connects a device to a computer',
-            required=True, vocabulary=HWBus))
-
-    vendor_id_for_bus = exported(
-        TextLine(title=u'Vendor ID', required=True),
-        exported_as='vendor_id')
-
-    vendor_name = Attribute('Vendor Name')
-
-
-class IHWVendorIDSet(Interface):
-    """The set of vendor IDs."""
-    def create(bus, vendor_id, name):
-        """Create a vendor ID.
-
-        :param bus: the HWBus instance for this bus.
-        :param vendor_id: a string containing the bus ID. Numeric IDs
-            are represented as a hexadecimal string, prepended by '0x'.
-        :param name: The IHWVendorName instance with the vendor name.
-        :return: A new IHWVendorID instance.
-        """
-
-    def getByBusAndVendorID(bus, vendor_id):
-        """Return an IHWVendorID instance for the given bus and vendor_id.
-
-        :param bus: An HWBus instance.
-        :param vendor_id: A string containing the vendor ID. Numeric IDs
-            must be represented as a hexadecimal string, prepended by '0x'.
-        :return: The found IHWVendorID instance or None, if no instance
-            for the given bus and vendor ID exists.
-        """
-
-    def get(id):
-        """Return an IHWVendorID record with the given database ID.
-
-        :param id: The database ID.
-        :return: An IHWVendorID instance.
-        """
-
-    def idsForBus(bus):
-        """Return all known IHWVendorID records with the given bus.
-
-        :param bus: A HWBus instance.
-        :return: A sequence of IHWVendorID instances.
-        """
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWDeviceClass(Interface):
-    """The capabilities of a device."""
-
-    id = Int(title=u'Device class ID', required=True, readonly=True)
-    device = Reference(schema=Interface)
-    main_class = exported(
-        Int(
-            title=u'The main class of this device', required=True,
-            readonly=True))
-    sub_class = exported(
-        Int(
-            title=u'The sub class of this device', required=False,
-            readonly=True))
-
-    @export_destructor_operation()
-    def delete():
-        """Delete this record."""
-
-
-class IHWDeviceClassSet(Interface):
-    """The set of IHWDeviceClass records."""
-
-    def get(id):
-        """Return an `IHWDeviceClass` record with the given database ID.
-
-        :param id: The database ID.
-        :return: An `IHWDeviceClass` instance.
-        """
-
-
-VENDOR_ID_DESCRIPTION = u"""Allowed values of the vendor ID depend on the
-bus of the device.
-
-Vendor IDs of PCI, PCCard and USB devices are hexadecimal string
-representations of 16 bit integers in the format '0x01ab': The prefix
-'0x', followed by exactly 4 digits; where a digit is one of the
-characters 0..9, a..f. The characters A..F are not allowed.
-
-SCSI vendor IDs are strings with exactly 8 characters. Shorter names are
-right-padded with space (0x20) characters.
-
-IDs for other buses may be arbitrary strings.
-"""
-
-PRODUCT_ID_DESCRIPTION = u"""Allowed values of the product ID depend on the
-bus of the device.
-
-Product IDs of PCI, PCCard and USB devices are hexadecimal string
-representations of 16 bit integers in the format '0x01ab': The prefix
-'0x', followed by exactly 4 digits; where a digit is one of the
-characters 0..9, a..f. The characters A..F are not allowed.
-
-SCSI product IDs are strings with exactly 16 characters. Shorter names are
-right-padded with space (0x20) characters.
-
-IDs for other buses may be arbitrary strings.
-"""
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWDevice(Interface):
-    """Core information to identify a device."""
-
-    id = exported(
-        Int(title=u'Device ID', required=True, readonly=True))
-
-    bus_vendor = Attribute(u'Ths bus and vendor of the device')
-
-    bus_product_id = exported(
-        TextLine(title=u'The product identifier of the device',
-                 required=True, description=PRODUCT_ID_DESCRIPTION))
-
-    variant = exported(
-        TextLine(title=u'A string that distiguishes different '
-                        'devices with identical vendor/product IDs',
-                 required=True))
-
-    name = exported(
-        TextLine(title=u'The human readable name of the device.',
-                 required=True))
-
-    submissions = Int(title=u'The number of submissions with the device',
-                      required=True)
-
-    bus = exported(
-        Choice(title=u'The bus of the device.', vocabulary=HWBus,
-               readonly=True))
-
-    vendor_id = exported(
-        TextLine(title=u'The vendor iD.', readonly=True,
-                 description=VENDOR_ID_DESCRIPTION))
-
-    vendor_name = exported(
-        TextLine(title=u'The vendor name.', readonly=True))
-
-    @operation_parameters(
-        driver=Reference(
-            IHWDriver,
-            title=u'A driver used for this device in a submission',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given distribution, distroseries or '
-                'distroarchseries.'),
-            required=False),
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for the given distribution series.'),
-            required=False),
-        architecture=TextLine(
-            title=u'A processor architecture',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                'made for the given architecture.'),
-            required=False),
-        owner=copy_field(IHWSubmission['owner']))
-    @operation_returns_collection_of(IHWSubmission)
-    @export_read_operation()
-    def getSubmissions(driver=None, distribution=None,
-                       distroseries=None, architecture=None, owner=None):
-        """List all submissions which mention this device.
-
-        :param driver: Limit results to devices that use the given
-            `IHWDriver`.
-        :param distribution: Limit results to submissions for this
-            `IDistribution`.
-        :param distroseries: Limit results to submissions for this
-            `IDistroSeries`.
-        :param architecture: Limit results to submissions for this
-            architecture.
-        :param owner: Limit results to submissions from this person.
-
-        Only submissions matching all given criteria are returned.
-        Only one of :distribution: or :distroseries: may be supplied.
-        """
-
-    drivers = exported(
-        CollectionField(
-            title=_(u"The IHWDriver records related to this device."),
-            value_type=Reference(schema=IHWDriver)))
-
-    classes = exported(
-        CollectionField(
-            title=_(u"The device classes this device belongs to."),
-            value_type=Reference(schema=IHWDeviceClass)))
-
-    @operation_parameters(
-        main_class=copy_field(IHWDeviceClass['main_class']),
-        sub_class=copy_field(IHWDeviceClass['sub_class']))
-    @export_write_operation()
-    @operation_returns_entry(IHWDeviceClass)
-    def getOrCreateDeviceClass(main_class, sub_class=None):
-        """Return an `IHWDeviceClass` record or create a new one.
-
-        :param main_class: The main class to be added.
-        :param sub_class: The sub-class to added (otpional).
-        :return: An `IHWDeviceClass` record.
-
-        main_class and sub_class are integers specifying the class
-        of the device, or, in the case of USB devices, the class
-        of an interface.
-
-        `IHWDeviceClass` records must be unique; if this method is called
-        to create a new record with data of an already existing record,
-        the existing record is returned.
-        """
-
-    @operation_parameters(
-        main_class=copy_field(IHWDeviceClass['main_class']),
-        sub_class=copy_field(IHWDeviceClass['sub_class']))
-    @export_write_operation()
-    def removeDeviceClass(main_class, sub_class=None):
-        """Add an `IHWDeviceClass` record.
-
-        :param main_class: The main class to be added.
-        :param sub_class: The sub-class to added.
-        """
-
-
-# Fix cyclic reference.
-patch_reference_property(IHWDeviceClass, 'device', IHWDevice)
-
-
-class IHWDeviceSet(Interface):
-    """The set of devices."""
-
-    def create(bus, vendor_id, product_id, product_name, variant=None):
-        """Create a new device entry.
-
-        :param bus: A bus name as enumerated in HWBus.
-        :param vendor_id: The vendor ID for the bus.
-        :param product_id: The product ID.
-        :param product_name: The human readable product name.
-        :param variant: A string that allows to distinguish different devices
-                        with identical product/vendor IDs.
-        :return: A new IHWDevice instance.
-        """
-
-    def getByDeviceID(bus, vendor_id, product_id, variant=None):
-        """Return an IHWDevice record.
-
-        :param bus: The bus name of the device as enumerated in HWBus.
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :param variant: A string that allows to distinguish different devices
-                        with identical product/vendor IDs.
-        :return: An IHWDevice instance.
-        """
-
-    def getOrCreate(bus, vendor_id, product_id, product_name, variant=None):
-        """Return an IHWDevice record or create one.
-
-        :param bus: The bus name of the device as enumerated in HWBus.
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :param product_name: The human readable product name.
-        :param variant: A string that allows to distinguish different devices
-                        with identical product/vendor IDs.
-        :return: An IHWDevice instance.
-
-        Return an existing IHWDevice record matching the given
-        parameters or create a new one, if no existing record
-        matches.
-        """
-
-    def getByID(id):
-        """Return an IHWDevice record with the given database ID.
-
-        :param id: The database ID.
-        :return: An IHWDevice instance.
-        """
-
-    def search(bus, vendor_id, product_id=None):
-        """Return HWDevice records matching the given parameters.
-
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :return: A sequence of IHWDevice instances.
-        """
-
-
-class IHWDeviceNameVariant(Interface):
-    """Variants of a device name.
-
-    We identify devices by (bus, vendor_id, product_id[, variant]),
-    but many OEM products are sold by different vendors under different
-    names. Users might want to look up device data by giving the
-    vendor and product name as seen in a store; this table provides
-    the "alias names" required for such a lookup.
-    """
-    vendor_name = Attribute(u'Vendor Name')
-
-    product_name = TextLine(title=u'Product Name', required=True)
-
-    device = Attribute(u'The device which has this name')
-
-    submissions = Int(
-        title=u'The number of submissions with this name variant',
-        required=True)
-
-
-class IHWDeviceNameVariantSet(Interface):
-    """The set of device name variants."""
-
-    def create(device, vendor_name, product_name):
-        """Create a new IHWDeviceNameVariant instance.
-
-        :param device: An IHWDevice instance.
-        :param vendor_name: The alternative vendor name for the device.
-        :param product_name: The alternative product name for the device.
-        :return: The new IHWDeviceNameVariant.
-        """
-
-
-class IHWDeviceDriverLink(Interface):
-    """Link a device with a driver."""
-
-    device = Attribute(u'The Device.')
-
-    driver = Attribute(u'The Driver.')
-
-
-class IHWDeviceDriverLinkSet(Interface):
-    """The set of device driver links."""
-
-    def create(device, driver):
-        """Create a new IHWDeviceDriver instance.
-
-        :param device: The IHWDevice instance to be linked.
-        :param driver: The IHWDriver instance to be linked.
-        :return: The new IHWDeviceDriver instance.
-        """
-
-    def getByDeviceAndDriver(device, driver):
-        """Return an IHWDeviceDriver instance.
-
-        :param device: An IHWDevice instance.
-        :param driver: An IHWDriver instance.
-        :return: The IHWDeviceDriver instance matching the given
-            parameters or None, if no existing instance matches.
-        """
-    def getOrCreate(device, driver):
-        """Return an IHWDeviceDriverLink record or create one.
-
-        :param device: The IHWDevice instance to be linked.
-        :param driver: The IHWDriver instance to be linked.
-        :return: An IHWDeviceDriverLink instance.
-
-        Return an existing IHWDeviceDriverLink record matching te given
-        parameters or create a new one, if no exitsing record
-        matches.
-        """
-
-
-@exported_as_webservice_entry(publish_web_link=False)
-class IHWSubmissionDevice(Interface):
-    """Link a submission to a IHWDeviceDriver row."""
-
-    id = exported(
-        Int(title=u'HWSubmissionDevice ID', required=True, readonly=True))
-
-    device_driver_link = Attribute(u'A device and driver appearing in a '
-                                    'submission.')
-
-    submission = Attribute(u'The submission the device and driver are '
-                            'mentioned in.')
-
-    parent = exported(
-        # This is a reference to IHWSubmissionDevice itself, but we can
-        # access the class only when the class has been defined.
-        Reference(Interface, required=True))
-
-    hal_device_id = exported(
-        Int(
-            title=u'The ID of the HAL node of this device in the submitted '
-                'data',
-            required=True))
-
-    device = exported(
-        Reference(
-            IHWDevice,
-            title=u'The device'))
-
-    driver = exported(
-        Reference(
-            IHWDriver,
-            title=u'The driver used for this device in this submission'))
-
-
-# Fix cyclic references.
-patch_reference_property(IHWSubmissionDevice, 'parent', IHWSubmissionDevice)
-patch_collection_property(IHWSubmission, 'devices', IHWSubmissionDevice)
-
-
-class IHWSubmissionDeviceSet(Interface):
-    """The set of IHWSubmissionDevices."""
-
-    def create(device_driver_link, submission, parent):
-        """Create a new IHWSubmissionDevice instance.
-
-        :param device_driver_link: An IHWDeviceDriverLink instance.
-        :param submission: The submission the device/driver combination
-            is mentioned in.
-        :param parent: The parent of this device in the device tree in
-            the submission.
-        :return: The new IHWSubmissionDevice instance.
-        """
-
-    def getDevices(submission):
-        """Return the IHWSubmissionDevice records of a submission
-
-        :return: A sequence of IHWSubmissionDevice records.
-        :param submission: An IHWSubmission instance.
-        """
-
-    def get(id):
-        """Return an IHWSubmissionDevice record with the given database ID.
-
-        :param id: The database ID.
-        :return: An IHWSubmissionDevice instance.
-        """
-
-    def numDevicesInSubmissions(
-        bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distro_target=None):
-        """Count how often a device or a driver appears in HWDB submissions.
-
-        :return: The number how often the given device appears in HWDB
-            submissions.
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: Limit the count to devices controlled by the given
-            driver (optional).
-        :param package_name: Limit the count to devices controlled by a driver
-            from the given package (optional).
-        :param distro_target: Limit the count to devices appearing in HWDB
-            submissions made for the given distribution, distroseries
-            or distroarchseries (optional).
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-
-class IHWSubmissionBug(Interface):
-    """Link a HWDB submission to a bug."""
-
-    submission = Attribute(u'The HWDB submission referenced in a bug '
-                              'report.')
-
-    bug = Attribute(u'The bug the HWDB submission is referenced in.')
-
-
-class IHWSubmissionBugSet(Interface):
-    """The set of IHWSubmissionBugs."""
-
-    def create(hwsubmission, bug):
-        """Create a new IHWSubmissionBug instance.
-
-        :return: The new IHWSubmissionBug instance.
-        :param hwsubmission: An IHWSubmission instance.
-        :param bug: An IBug instance.
-        """
-
-    def remove(hwsubmission, bug):
-        """Remove the link between `hwsubmission` and `bug`.
-
-        :param hwsubmission: An IHWSubmission instance.
-        :param bug: An IBug instance.
-        """
-
-    def submissionsForBug(bug, user=None):
-        """Return the HWDB submissions linked to the bug `bug`.
-
-        :return: A sequence of HWDB submissions linked to `bug`.
-        :param user: The user making the request.
-
-        Only those submissions are returned which the user can access.
-        Public submissions are always included; private submisisons only
-        if the user is the owner or an admin.
-        """
-
-
-@exported_as_webservice_entry('hwdb', publish_web_link=False)
-class IHWDBApplication(ILaunchpadApplication):
-    """Hardware database application application root."""
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'The device bus', vocabulary=HWBus, required=True),
-        vendor_id=TextLine(
-            title=u'The vendor ID', required=True,
-            description=VENDOR_ID_DESCRIPTION),
-        product_id=TextLine(
-            title=u'The product ID', required=False,
-            description=PRODUCT_ID_DESCRIPTION))
-    @operation_returns_collection_of(IHWDevice)
-    @export_read_operation()
-    def devices(bus, vendor_id, product_id=None):
-        """Return the set of devices."""
-
-    @operation_parameters(
-        package_name=TextLine(
-            title=u'The name of the package containing the driver.',
-            required=False,
-            description=(
-                u'If package_name is omitted, all driver records '
-                'returned, optionally limited to those matching the '
-                'parameter name. If package_name is '' (empty string), '
-                'those records are returned where package_name is '' or '
-                'None.')),
-        name=TextLine(
-            title=u'The name of the driver.', required=False,
-            description=(
-                u'If name is omitted, all driver records are '
-                'returned, optionally limited to those matching the '
-                'parameter package_name.')))
-    @operation_returns_collection_of(IHWDriver)
-    @export_read_operation()
-    def drivers(package_name=None, name=None):
-        """Return the set of drivers."""
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'A Device Bus.', vocabulary=HWBus, required=True))
-    @operation_returns_collection_of(IHWVendorID)
-    @export_read_operation()
-    def vendorIDs(bus):
-        """Return the known vendor IDs for the given bus.
-
-        :param bus: A `HWBus` value.
-        :return: A list of strings with vendor IDs fr this bus,
-        """
-
-    driver_names = exported(
-        CollectionField(
-            title=u'Driver Names',
-            description=(
-                u'All known distinct driver names appearing in HWDriver'),
-            value_type=Reference(schema=IHWDriverName),
-            readonly=True))
-
-    package_names = exported(
-        CollectionField(
-            title=u'Package Names',
-            description=(
-                u'All known distinct package names appearing in '
-                'HWDriver.'),
-            value_type=Reference(schema=IHWDriverPackageName),
-            readonly=True))
-
-    @operation_parameters(
-        device=Reference(
-            IHWDevice,
-            title=u'A Device',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'containing this device.'),
-            required=False),
-        driver=Reference(
-            IHWDriver,
-            title=u'A Driver',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'containing devices that use this driver.'),
-            required=False),
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for this distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distribution series.'),
-            required=False),
-        architecture=TextLine(
-            title=u'A processor architecture',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'made for a specific architecture.'),
-            required=False),
-        owner=Reference(
-            IPerson,
-            title=u'Person',
-            description=(
-                u'If specified, the result set is limited to sumbissions '
-                'from this person.'),
-            required=False),
-        created_before=Datetime(
-            title=u'Created Before',
-            description=(
-                u'If specified, exclude results created after this date.'),
-            required=False),
-        created_after=Datetime(
-            title=u'Created After',
-            description=(
-                u'If specified, exclude results created before or on '
-                'this date.'),
-            required=False),
-        submitted_before=Datetime(
-            title=u'Created Before',
-            description=(
-                u'If specified, exclude results submitted after this date.'),
-            required=False),
-        submitted_after=Datetime(
-            title=u'Created After',
-            description=(
-                u'If specified, Exclude results submitted before or on '
-                'this date.'),
-            required=False))
-    @call_with(user=REQUEST_USER)
-    @operation_returns_collection_of(IHWSubmission)
-    @export_read_operation()
-    def search(user=None, device=None, driver=None, distribution=None,
-               distroseries=None, architecture=None, owner=None,
-               created_before=None, created_after=None,
-               submitted_before=None, submitted_after=None):
-        """Return the submissions matiching the given parmeters.
-
-        :param user: The `IPerson` running the query. Private submissions
-            are returned only if the person running the query is the
-            owner or an admin.
-        :param device: Limit results to submissions containing this
-            `IHWDevice`.
-        :param driver: Limit results to submissions containing devices
-            that use this `IHWDriver`.
-        :param distribution: Limit results to submissions made for
-            this `IDistribution`.
-        :param distroseries: Limit results to submissions made for
-            this `IDistroSeries`.
-        :param architecture: Limit results to submissions made for
-            a specific architecture.
-        :param owner: Limit results to submissions from this person.
-        :param created_before: Exclude results created after this
-            date.
-        :param created_after: Exclude results created before or on
-            this date.
-        :param submitted_before: Exclude results submitted after this
-            date.
-        :param submitted_after: Exclude results submitted before or on
-            this date.
-
-        Only one of :distribution: or :distroseries: may be supplied.
-        """
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'The device bus', vocabulary=HWBus, required=False),
-        vendor_id=TextLine(
-            title=u'The vendor ID', description=VENDOR_ID_DESCRIPTION,
-             required=False),
-        product_id=TextLine(
-            title=u'The product ID', description=PRODUCT_ID_DESCRIPTION,
-            required=False),
-        driver_name=TextLine(
-            title=u'A driver name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        'controlled by this driver.'),
-        package_name=TextLine(
-            title=u'A package name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        u'controlled by a driver from this package.'),
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for this distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distribution series.'),
-            required=False),
-        distroarchseries=Reference(
-            IDistroArchSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distroarchseries.'),
-            required=False))
-    @export_read_operation()
-    def numSubmissionsWithDevice(
-        bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """Count the number of submissions mentioning a device  or a driver.
-
-        Returns a dictionary {'submissions_with_device: n1,
-        'all_submissions': n2}, where submissions_with_device is the number
-        of submissions having the given device or driver and matching the
-        distro target criterion and where all_submissions is the number of
-        submissions matching the distro target criterion.
-
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: The name of the driver used for the device
-            (optional).
-        :param package_name: The name of the package the driver is a part of.
-            (optional).
-        :param distribution: Limit the count to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-        :param distroseries: Limit the count to submissions made for the
-            given distroseries.
-            (optional).
-        :param distroarchseries: Limit the count to submissions made for the
-            given distroarchseries.
-            (optional).
-
-        You may specify at most one of the parameters distribution,
-        distroseries or distroarchseries.
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'The device bus', vocabulary=HWBus, required=False),
-        vendor_id=TextLine(
-            title=u'The vendor ID', description=VENDOR_ID_DESCRIPTION,
-             required=False),
-        product_id=TextLine(
-            title=u'The product ID', description=PRODUCT_ID_DESCRIPTION,
-            required=False),
-        driver_name=TextLine(
-            title=u'A driver name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        u'controlled by this driver.'),
-        package_name=TextLine(
-            title=u'A package name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        u'controlled by a driver from this package.'),
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for this distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distribution series.'),
-            required=False),
-        distroarchseries=Reference(
-            IDistroArchSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distroarchseries.'),
-            required=False))
-    @export_read_operation()
-    def numOwnersOfDevice(
-        bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """The number of people owning a device or using a driver.
-
-        Returns a dictionary {'owners': n1, 'all_submitters': n2}
-        where owners is the number of people who made a HWDB
-        submission containing the given device or driver, optionally
-        limited to submissions made for the given distro target.
-        all_submitters is the number of persons who made
-        a HWDB submission, optionally limited to submission made
-        on the given distro target installation.
-
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: The name of the driver used for the device
-            (optional).
-        :param package_name: The name of the package the driver is a part of.
-            (optional).
-        :param distribution: Limit the count to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-        :param distroseries: Limit the count to submissions made for the
-            given distroseries.
-            (optional).
-        :param distroarchseries: Limit the count to submissions made for the
-            given distroarchseries.
-            (optional).
-
-        You may specify at most one of the parameters distribution,
-        distroseries or distroarchseries.
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'The device bus', vocabulary=HWBus, required=False),
-        vendor_id=TextLine(
-            title=u'The vendor ID', description=VENDOR_ID_DESCRIPTION,
-             required=False),
-        product_id=TextLine(
-            title=u'The product ID', description=PRODUCT_ID_DESCRIPTION,
-            required=False),
-        driver_name=TextLine(
-            title=u'A driver name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        u'controlled by this driver.'),
-        package_name=TextLine(
-            title=u'A package name', required=False,
-            description=u'If specified, the count is limited to devices '
-                        u'controlled by a driver from this package.'),
-        distribution=Reference(
-            IDistribution,
-            title=u'A Distribution',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for this distribution.'),
-            required=False),
-        distroseries=Reference(
-            IDistroSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distribution series.'),
-            required=False),
-        distroarchseries=Reference(
-            IDistroArchSeries,
-            title=u'A Distribution Series',
-            description=(
-                u'If specified, the result set is limited to submissions '
-                u'made for the given distroarchseries.'),
-            required=False))
-    @export_read_operation()
-    def numDevicesInSubmissions(
-        bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """Count how often a device or a driver appears in HWDB submissions.
-
-        :return: The number how often the given device appears in HWDB
-            submissions.
-        :param bus: The `HWBus` of the device (optional).
-        :param vendor_id: The vendor ID of the device (optional).
-        :param product_id: The product ID of the device (optional).
-        :param driver_name: Limit the count to devices controlled by the given
-            driver (optional).
-        :param package_name: Limit the count to devices controlled by a driver
-            from the given package (optional).
-        :param distribution: Limit the count to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-        :param distroseries: Limit the count to submissions made for the
-            given distroseries.
-            (optional).
-        :param distroarchseries: Limit the count to submissions made for the
-            given distroarchseries.
-            (optional).
-
-        You may specify at most one of the parameters distribution,
-        distroseries or distroarchseries.
-
-        At least each of bus, vendor_id, product_id must not be None or
-        driver_name must not be None.
-        """
-
-    @operation_parameters(
-        bus=Choice(
-            title=u'The device bus', vocabulary=HWBus, required=False),
-        vendor_id=TextLine(
-            title=u'The vendor ID', description=VENDOR_ID_DESCRIPTION,
-             required=False),
-        product_id=TextLine(
-            title=u'The product ID', description=PRODUCT_ID_DESCRIPTION,
-            required=False),
-        driver_name=TextLine(
-            title=u'A driver name', required=False,
-            description=u'If specified, the search is limited to devices '
-                        u'controlled by this driver.'),
-        package_name=TextLine(
-            title=u'A package name', required=False,
-            description=u'If specified, the search is limited to devices '
-                        u'controlled by a driver from this package.'),
-        bug_ids=List(title=u'A set of bug IDs',
-             description=u'Search submitters, subscribers or affected users '
-                         u'of bugs with these IDs.',
-             value_type=Int(),
-             required=False),
-        bug_tags=List(title=u'A set of bug tags',
-             description=u'Search submitters, subscribers or affected users '
-                         u'of bugs having one of these tags.',
-             value_type=TextLine(),
-             required=False),
-        affected_by_bug=Bool(
-            title=u'Search for users affected by a bug',
-            description=u'If true, those device owners are looked up which '
-                        u'are affected by one of the selected bugs.',
-            required=False),
-        subscribed_to_bug=Bool(
-            title=u'Search for users who subscribed to a bug',
-            description=u'If true, those device owners are looked up which '
-                        u'to one of the selected bugs.',
-            required=False))
-    @call_with(user=REQUEST_USER)
-    @operation_returns_collection_of(IPerson)
-    @export_read_operation()
-    def deviceDriverOwnersAffectedByBugs(
-        bus, vendor_id, product_id, driver_name=None, package_name=None,
-        bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """Return persons affected by given bugs and owning a given device.
-
-        :param bus: The `HWBus` of the device.
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :param driver_name: Limit the search to devices controlled by the
-            given driver.
-        :param package_name: Limit the search to devices controlled by a
-            driver from the given package.
-        :param bug_ids: A sequence of bug IDs for which affected
-            are looked up.
-        :param bug_tags: A sequence of bug tags
-        :param affected_by_bug: If True, those persons are looked up that
-            have marked themselves as being affected by a one of the bugs
-            matching the bug criteria.
-        :param subscribed_to_bug: If True, those persons are looked up that
-            are subscribed to a bug matching one of the bug criteria.
-        :param user: The person making the query.
-
-        bug_ids must be a non-empty sequence of bug IDs, or bug_tags
-        must be a non-empty sequence of bug tags.
-
-        The parameters bus, vendor_id, product_id must not be None, or
-        driver_name must not be None.
-
-        By default, only those persons are returned which have reported a
-        bug matching the given bug conditions.
-
-        Owners of private submissions are returned only if user is the
-        owner of the private submission or if user is an admin.
-        """
-
-    @operation_parameters(
-        bug_ids=List(title=u'A set of bug IDs',
-             description=u'Search for devices and their owners related to '
-                         u'bugs with these IDs.',
-             value_type=Int(),
-             required=False),
-        bug_tags=List(title=u'A set of bug tags',
-             description=u'Search for devices and their owners related to '
-                         u'bugs having one of these tags.',
-             value_type=TextLine(),
-             required=False),
-        affected_by_bug=Bool(
-            title=u'Search for users affected by a bug',
-            description=u'If true, those device owners are looked up which '
-                        u'are affected by one of the selected bugs.',
-            required=False),
-        subscribed_to_bug=Bool(
-            title=u'Search for users who subscribed to a bug',
-            description=u'If true, those device owners are looked up which '
-                        u'to one of the selected bugs.',
-            required=False))
-    @call_with(user=REQUEST_USER)
-    @export_read_operation()
-    def hwInfoByBugRelatedUsers(
-        bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """Return a list of owners and devices related to given bugs.
-
-        Actually returns a list of tuples where the tuple is of the form,
-        (person name, bus name, vendor id, product id).`
-
-        :param bug_ids: A sequence of bug IDs for which affected
-            are looked up.
-        :param bug_tags: A sequence of bug tags
-        :param affected_by_bug: If True, those persons are looked up that
-            have marked themselves as being affected by a one of the bugs
-            matching the bug criteria.
-        :param subscribed_to_bug: If True, those persons are looked up that
-            are subscribed to a bug matching one of the bug criteria.
-        :param user: The person making the query.
-        """
-
-
-@error_status(http_client.BAD_REQUEST)
-class IllegalQuery(Exception):
-    """Exception raised when trying to run an illegal submissions query."""
-
-
-@error_status(http_client.BAD_REQUEST)
-class ParameterError(Exception):
-    """Exception raised when a method parameter does not match a constrint."""
diff --git a/lib/lp/hardwaredb/interfaces/webservice.py b/lib/lp/hardwaredb/interfaces/webservice.py
deleted file mode 100644
index 8d27460..0000000
--- a/lib/lp/hardwaredb/interfaces/webservice.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""All the interfaces that are exposed through the webservice.
-
-There is a declaration in ZCML somewhere that looks like:
-  <webservice:register module="lp.hardwaredb.interfaces.hwdb" />
-
-which tells `lazr.restful` that it should look for webservice exports here.
-"""
-
-__all__ = [
-    'IHWDBApplication',
-    'IHWDevice',
-    'IHWDeviceClass',
-    'IHWDriver',
-    'IHWDriverName',
-    'IHWDriverPackageName',
-    'IHWSubmission',
-    'IHWSubmissionDevice',
-    'IHWVendorID',
-    'IllegalQuery',
-    'ParameterError',
-    ]
-
-# XXX: JonathanLange 2010-11-09 bug=673083: Legacy work-around for circular
-# import bugs.  Break this up into a per-package thing.
-from lp import _schema_circular_imports
-from lp.hardwaredb.interfaces.hwdb import (
-    IHWDBApplication,
-    IHWDevice,
-    IHWDeviceClass,
-    IHWDriver,
-    IHWDriverName,
-    IHWDriverPackageName,
-    IHWSubmission,
-    IHWSubmissionDevice,
-    IHWVendorID,
-    IllegalQuery,
-    ParameterError,
-    )
-
-
-_schema_circular_imports
diff --git a/lib/lp/hardwaredb/model/__init__.py b/lib/lp/hardwaredb/model/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/model/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/model/hwdb.py b/lib/lp/hardwaredb/model/hwdb.py
deleted file mode 100644
index b61b3e6..0000000
--- a/lib/lp/hardwaredb/model/hwdb.py
+++ /dev/null
@@ -1,1308 +0,0 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Hardware database related table classes."""
-
-__all__ = [
-    'HWDevice',
-    'HWDeviceClass',
-    'HWDeviceSet',
-    'HWDeviceDriverLink',
-    'HWDeviceDriverLinkSet',
-    'HWDeviceNameVariant',
-    'HWDeviceNameVariantSet',
-    'HWDriver',
-    'HWDriverName',
-    'HWDriverPackageName',
-    'HWDriverSet',
-    'HWSubmission',
-    'HWSubmissionBug',
-    'HWSubmissionBugSet',
-    'HWSubmissionSet',
-    'HWSubmissionDevice',
-    'HWSubmissionDeviceSet',
-    'HWSystemFingerprint',
-    'HWSystemFingerprintSet',
-    'HWVendorID',
-    'HWVendorIDSet',
-    'HWVendorName',
-    'HWVendorNameSet',
-    'make_submission_device_statistics_clause',
-    '_userCanAccessSubmissionStormClause',
-    ]
-
-import re
-
-from sqlobject import (
-    BoolCol,
-    ForeignKey,
-    IntCol,
-    StringCol,
-    )
-from storm.expr import (
-    Alias,
-    And,
-    Count,
-    Not,
-    Or,
-    Select,
-    )
-from storm.store import Store
-from zope.component import getUtility
-from zope.interface import implementer
-
-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-from lp.app.validators.name import valid_name
-from lp.bugs.model.bug import (
-    Bug,
-    BugAffectsPerson,
-    BugTag,
-    )
-from lp.bugs.model.bugsubscription import BugSubscription
-from lp.hardwaredb.interfaces.hwdb import (
-    HWBus,
-    HWDB_SUBMISSIONS_DISABLED_FEATURE_FLAG,
-    HWSubmissionFormat,
-    HWSubmissionKeyNotUnique,
-    HWSubmissionProcessingStatus,
-    HWSubmissionsDisabledError,
-    IHWDevice,
-    IHWDeviceClass,
-    IHWDeviceClassSet,
-    IHWDeviceDriverLink,
-    IHWDeviceDriverLinkSet,
-    IHWDeviceNameVariant,
-    IHWDeviceNameVariantSet,
-    IHWDeviceSet,
-    IHWDriver,
-    IHWDriverName,
-    IHWDriverPackageName,
-    IHWDriverSet,
-    IHWSubmission,
-    IHWSubmissionBug,
-    IHWSubmissionBugSet,
-    IHWSubmissionDevice,
-    IHWSubmissionDeviceSet,
-    IHWSubmissionSet,
-    IHWSystemFingerprint,
-    IHWSystemFingerprintSet,
-    IHWVendorID,
-    IHWVendorIDSet,
-    IHWVendorName,
-    IHWVendorNameSet,
-    IllegalQuery,
-    ParameterError,
-    )
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.person import (
-    IPersonSet,
-    validate_public_person,
-    )
-from lp.registry.interfaces.product import License
-from lp.registry.model.distribution import Distribution
-from lp.registry.model.distroseries import DistroSeries
-from lp.registry.model.person import Person
-from lp.registry.model.teammembership import TeamParticipation
-from lp.services.database.constants import (
-    DEFAULT,
-    UTC_NOW,
-    )
-from lp.services.database.datetimecol import UtcDateTimeCol
-from lp.services.database.enumcol import EnumCol
-from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import (
-    SQLBase,
-    sqlvalues,
-    )
-from lp.services.features import getFeatureFlag
-from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
-from lp.soyuz.model.distroarchseries import DistroArchSeries
-
-# The vendor name assigned to new, unknown vendor IDs. See
-# HWDeviceSet.create().
-UNKNOWN = 'Unknown'
-
-
-@implementer(IHWSubmission)
-class HWSubmission(SQLBase):
-    """See `IHWSubmission`."""
-
-    _table = 'HWSubmission'
-
-    date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW)
-    date_submitted = UtcDateTimeCol(notNull=True, default=UTC_NOW)
-    format = EnumCol(enum=HWSubmissionFormat, notNull=True)
-    status = EnumCol(enum=HWSubmissionProcessingStatus, notNull=True)
-    private = BoolCol(notNull=True)
-    contactable = BoolCol(notNull=True)
-    submission_key = StringCol(notNull=True)
-    owner = ForeignKey(dbName='owner', foreignKey='Person',
-                       storm_validator=validate_public_person)
-    distroarchseries = ForeignKey(dbName='distroarchseries',
-                                  foreignKey='DistroArchSeries')
-    raw_submission = ForeignKey(dbName='raw_submission',
-                                foreignKey='LibraryFileAlias',
-                                notNull=False, default=DEFAULT)
-    system_fingerprint = ForeignKey(dbName='system_fingerprint',
-                                    foreignKey='HWSystemFingerprint',
-                                    notNull=True)
-    raw_emailaddress = StringCol()
-
-    @property
-    def devices(self):
-        return HWSubmissionDeviceSet().getDevices(submission=self)
-
-
-@implementer(IHWSubmissionSet)
-class HWSubmissionSet:
-    """See `IHWSubmissionSet`."""
-
-    def createSubmission(self, date_created, format, private, contactable,
-                         submission_key, emailaddress, distroarchseries,
-                         raw_submission, filename, filesize,
-                         system_fingerprint):
-        """See `IHWSubmissionSet`."""
-        if getFeatureFlag(HWDB_SUBMISSIONS_DISABLED_FEATURE_FLAG):
-            raise HWSubmissionsDisabledError()
-
-        assert valid_name(submission_key), "Invalid key %s" % submission_key
-
-        submission_exists = HWSubmission.selectOneBy(
-            submission_key=submission_key)
-        if submission_exists is not None:
-            raise HWSubmissionKeyNotUnique(
-                'A submission with this ID already exists')
-
-        personset = getUtility(IPersonSet)
-        if emailaddress is not None:
-            owner = personset.getByEmail(emailaddress)
-        else:
-            owner = None
-
-        fingerprint = HWSystemFingerprint.selectOneBy(
-            fingerprint=system_fingerprint)
-        if fingerprint is None:
-            fingerprint = HWSystemFingerprint(fingerprint=system_fingerprint)
-
-        libraryfileset = getUtility(ILibraryFileAliasSet)
-        libraryfile = libraryfileset.create(
-            name=filename,
-            size=filesize,
-            file=raw_submission,
-            # XXX: kiko 2007-09-20: The hwdb client sends us bzipped XML, but
-            # arguably other clients could send us other formats. The right
-            # way to do this is either to enforce the format in the browser
-            # code, allow the client to specify the format, or use a
-            # magic module to sniff what it is we got.
-            contentType='application/x-bzip2',
-            expires=None)
-
-        return HWSubmission(
-            date_created=date_created,
-            format=format,
-            status=HWSubmissionProcessingStatus.SUBMITTED,
-            private=private,
-            contactable=contactable,
-            submission_key=submission_key,
-            owner=owner,
-            distroarchseries=distroarchseries,
-            raw_submission=libraryfile,
-            system_fingerprint=fingerprint,
-            raw_emailaddress=emailaddress)
-
-    def _userHasAccessClause(self, user):
-        """Limit results of HWSubmission queries to rows the user can access.
-        """
-        admins = getUtility(ILaunchpadCelebrities).admin
-        if user is None:
-            return " AND NOT HWSubmission.private"
-        elif not user.inTeam(admins):
-            return """
-                AND (NOT HWSubmission.private
-                     OR EXISTS
-                         (SELECT 1
-                             FROM HWSubmission as HWAccess, TeamParticipation
-                             WHERE HWAccess.id=HWSubmission.id
-                                 AND HWAccess.private
-                                 AND HWAccess.owner=TeamParticipation.team
-                                 AND TeamParticipation.person=%i
-                                 ))
-                """ % user.id
-        else:
-            return ""
-
-    def getBySubmissionKey(self, submission_key, user=None):
-        """See `IHWSubmissionSet`."""
-        return IStore(HWSubmission).find(
-            HWSubmission,
-            And(HWSubmission.submission_key == submission_key,
-                _userCanAccessSubmissionStormClause(user))).one()
-
-    def getByFingerprintName(self, name, user=None):
-        """See `IHWSubmissionSet`."""
-        fp = HWSystemFingerprintSet().getByName(name)
-        query = """
-            system_fingerprint=%s
-            AND HWSystemFingerprint.id = HWSubmission.system_fingerprint
-            """ % sqlvalues(fp)
-        query = query + self._userHasAccessClause(user)
-
-        return HWSubmission.select(
-            query,
-            clauseTables=['HWSystemFingerprint'],
-            prejoinClauseTables=['HWSystemFingerprint'],
-            orderBy=['-date_submitted',
-                     'HWSystemFingerprint.fingerprint',
-                     'submission_key'])
-
-    def getByOwner(self, owner, user=None):
-        """See `IHWSubmissionSet`."""
-        query = """
-            owner=%i
-            AND HWSystemFingerprint.id = HWSubmission.system_fingerprint
-            """ % owner.id
-        query = query + self._userHasAccessClause(user)
-
-        return HWSubmission.select(
-            query,
-            clauseTables=['HWSystemFingerprint'],
-            prejoinClauseTables=['HWSystemFingerprint'],
-            orderBy=['-date_submitted',
-                     'HWSystemFingerprint.fingerprint',
-                     'submission_key'])
-
-    def submissionIdExists(self, submission_key):
-        """See `IHWSubmissionSet`."""
-        rows = HWSubmission.selectBy(submission_key=submission_key)
-        return not rows.is_empty()
-
-    def getByStatus(self, status, user=None):
-        """See `IHWSubmissionSet`."""
-        # Provide a stable order. Sorting by id, to get the oldest
-        # submissions first. When date_submitted has an index, we could
-        # sort by that first.
-        return IStore(HWSubmission).find(
-            HWSubmission, HWSubmission.status == status,
-            _userCanAccessSubmissionStormClause(user)).order_by(
-                HWSubmission.id)
-
-    def search(self, user=None, device=None, driver=None, distribution=None,
-               distroseries=None, architecture=None, owner=None,
-               created_before=None, created_after=None,
-               submitted_before=None, submitted_after=None):
-        """See `IHWSubmissionSet`."""
-        args = []
-        if device is not None:
-            args.append(HWDeviceDriverLink.device == HWDevice.id)
-            args.append(HWDevice.id == device.id)
-        if driver is not None:
-            args.append(HWDeviceDriverLink.driver == HWDriver.id)
-            args.append(HWDriver.id == driver.id)
-        # HWDevice and HWDriver are linked to submissions via
-        # HWDeviceDriverLink and HWSubmissionDevice.
-        if args:
-            args.append(HWSubmissionDevice.device_driver_link ==
-                        HWDeviceDriverLink.id)
-            args.append(HWSubmissionDevice.submission == HWSubmission.id)
-
-        if (distribution is not None or distroseries is not None
-            or architecture is not None):
-            # We need to select a specific distribution, distroseries,
-            # and/or processor architecture.
-            if distribution and distroseries:
-                raise IllegalQuery(
-                    'Only one of `distribution` or '
-                    '`distroseries` can be present.')
-            args.append(HWSubmission.distroarchseries == DistroArchSeries.id)
-            if architecture is not None:
-                args.append(DistroArchSeries.architecturetag == architecture)
-            if distribution is not None:
-                args.append(DistroArchSeries.distroseries == DistroSeries.id)
-                args.append(DistroSeries.distribution == Distribution.id)
-                args.append(Distribution.id == distribution.id)
-            if distroseries is not None:
-                args.append(DistroArchSeries.distroseries == distroseries.id)
-        if owner is not None:
-            args.append(HWSubmission.owner == owner.id)
-        if created_before is not None:
-            args.append(HWSubmission.date_created <= created_before)
-        if created_after is not None:
-            args.append(HWSubmission.date_created > created_after)
-        if submitted_before is not None:
-            args.append(HWSubmission.date_submitted <= submitted_before)
-        if submitted_after is not None:
-            args.append(HWSubmission.date_submitted > submitted_after)
-
-        # Many devices are associated with more than one driver, even
-        # for one submission, hence we may have more than one
-        # HWSubmissionDevice record and more than one HWDeviceDriverLink
-        # for one device and one submission matching the WHERE clause
-        # defined above. This leads to duplicate results without a
-        # DISTINCT clause.
-        return IStore(HWSubmission).find(
-            HWSubmission,
-            _userCanAccessSubmissionStormClause(user),
-            *args).config(distinct=True).order_by(HWSubmission.id)
-
-    def _submissionsSubmitterSelects(
-        self, target_column, bus, vendor_id, product_id, driver_name,
-        package_name, distro_target):
-        """Return Select objects for statistical queries.
-
-        :return: A tuple
-            (select_device_related_records, select_all_records)
-            where select_device_related_records is a Select instance
-            returning target_column matching all other method
-            parameters, and where select_all_records is a Select
-            instance returning target_column and matching distro_target,
-        :param target_column: The records returned by the Select instance.
-        :param bus: The `HWBus` of the device.
-        :param vendor_id: The vendor ID of the device.
-        :param product_id: The product ID of the device.
-        :param driver_name: The name of the driver used for the device
-            (optional).
-        :param package_name: The name of the package the driver is a part of.
-            (optional).
-        :param distro_target: Limit the result to submissions made for the
-            given distribution, distroseries or distroarchseries.
-            (optional).
-        """
-        tables, clauses = make_distro_target_clause(distro_target)
-        if HWSubmission not in tables:
-            tables.append(HWSubmission)
-        clauses.append(
-            HWSubmission.status == HWSubmissionProcessingStatus.PROCESSED)
-
-        all_submissions = Select(
-            columns=[target_column], tables=tables, where=And(*clauses),
-            distinct=True)
-
-        device_tables, device_clauses = (
-            make_submission_device_statistics_clause(
-                bus, vendor_id, product_id, driver_name, package_name, False))
-        submission_ids = Select(
-            columns=[HWSubmissionDevice.submissionID],
-            tables=device_tables, where=And(*device_clauses))
-
-        clauses.append(HWSubmission.id.is_in(submission_ids))
-        submissions_with_device = Select(
-            columns=[target_column], tables=tables, where=And(*clauses),
-            distinct=True)
-
-        return (submissions_with_device, all_submissions)
-
-    def numSubmissionsWithDevice(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distro_target=None):
-        """See `IHWSubmissionSet`."""
-        store = IStore(HWSubmission)
-        submissions_with_device_select, all_submissions_select = (
-            self._submissionsSubmitterSelects(
-                Count(), bus, vendor_id, product_id, driver_name,
-                package_name, distro_target))
-        submissions_with_device = store.execute(
-            submissions_with_device_select)
-        all_submissions = store.execute(all_submissions_select)
-        return (submissions_with_device.get_one()[0],
-                all_submissions.get_one()[0])
-
-    def numOwnersOfDevice(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distro_target=None):
-        """See `IHWSubmissionSet`."""
-        store = IStore(HWSubmission)
-        submitters_with_device_select, all_submitters_select = (
-            self._submissionsSubmitterSelects(
-                HWSubmission.raw_emailaddress, bus, vendor_id, product_id,
-                driver_name, package_name, distro_target))
-
-        submitters_with_device = store.execute(
-            Select(
-                columns=[Count()],
-                tables=[Alias(submitters_with_device_select, 'addresses')]))
-        all_submitters = store.execute(
-            Select(
-                columns=[Count()],
-                tables=[Alias(all_submitters_select, 'addresses')]))
-
-        return (submitters_with_device.get_one()[0],
-                all_submitters.get_one()[0])
-
-    def deviceDriverOwnersAffectedByBugs(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """See `IHWSubmissionSet`."""
-        store = IStore(HWSubmission)
-        tables, clauses = make_submission_device_statistics_clause(
-                bus, vendor_id, product_id, driver_name, package_name, False)
-        tables.append(HWSubmission)
-        clauses.append(HWSubmissionDevice.submission == HWSubmission.id)
-        clauses.append(_userCanAccessSubmissionStormClause(user))
-
-        if ((bug_ids is None or len(bug_ids) == 0) and
-            (bug_tags is None or len(bug_tags) == 0)):
-            raise ParameterError('bug_ids or bug_tags must be supplied.')
-
-        tables.append(Bug)
-        if bug_ids is not None and bug_ids is not []:
-            clauses.append(Bug.id.is_in(bug_ids))
-
-        if bug_tags is not None and bug_tags is not []:
-            clauses.extend([
-                Bug.id == BugTag.bugID, BugTag.tag.is_in(bug_tags)])
-            tables.append(BugTag)
-
-        # If we OR-combine the search for bug owners, subscribers
-        # and affected people on SQL level, the query runs very slow.
-        # So let's run the queries separately and join the results
-        # on Python level.
-
-        # This would be quicker still if we did it as a single query
-        # using UNION.
-
-        owner_query = Select(
-            columns=[HWSubmission.ownerID], tables=tables,
-            where=And(*(clauses + [Bug.ownerID == HWSubmission.ownerID])))
-        user_ids = set(store.execute(owner_query))
-
-        if subscribed_to_bug:
-            subscriber_clauses = [
-                BugSubscription.person_id == HWSubmission.ownerID,
-                BugSubscription.bug == Bug.id,
-                ]
-            subscriber_query = Select(
-                columns=[HWSubmission.ownerID],
-                tables=tables + [BugSubscription],
-                where=And(*(clauses + subscriber_clauses)))
-            user_ids.update(store.execute(subscriber_query))
-
-        if affected_by_bug:
-            affected_clauses = [
-                BugAffectsPerson.person_id == HWSubmission.ownerID,
-                BugAffectsPerson.bug == Bug.id,
-                BugAffectsPerson.affected,
-                ]
-            affected_query = Select(
-                columns=[HWSubmission.ownerID],
-                tables=tables + [BugAffectsPerson],
-                where=And(*(clauses + affected_clauses)))
-            user_ids.update(store.execute(affected_query))
-
-        # A "WHERE x IN (y, z...)" query needs at least one element
-        # on the right side of IN.
-        if len(user_ids) == 0:
-            result = store.find(Person, False)
-        else:
-            user_ids = [row[0] for row in user_ids]
-            result = store.find(Person, Person.id.is_in(user_ids))
-        result.order_by(Person.display_name)
-        return result
-
-    def hwInfoByBugRelatedUsers(
-        self, bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """See `IHWSubmissionSet`."""
-        if ((bug_ids is None or len(bug_ids) == 0) and
-            (bug_tags is None or len(bug_tags) == 0)):
-            raise ParameterError('bug_ids or bug_tags must be supplied.')
-
-        tables = [
-            Person, HWSubmission, HWSubmissionDevice, HWDeviceDriverLink,
-            HWDevice, HWVendorID, Bug, BugTag,
-            ]
-
-        clauses = [
-            Person.id == HWSubmission.ownerID,
-            HWSubmissionDevice.submission == HWSubmission.id,
-            HWSubmissionDevice.device_driver_link == HWDeviceDriverLink.id,
-            HWDeviceDriverLink.device == HWDevice.id,
-            HWDevice.bus_vendor == HWVendorID.id]
-
-        if bug_ids is not None and bug_ids is not []:
-            clauses.append(Bug.id.is_in(bug_ids))
-
-        if bug_tags is not None and bug_tags is not []:
-            clauses.extend(
-                [Bug.id == BugTag.bugID, BugTag.tag.is_in(bug_tags)])
-
-        clauses.append(_userCanAccessSubmissionStormClause(user))
-
-        person_clauses = [Bug.ownerID == HWSubmission.ownerID]
-        if subscribed_to_bug:
-            person_clauses.append(
-                And(BugSubscription.person_id == HWSubmission.ownerID,
-                    BugSubscription.bug == Bug.id))
-            tables.append(BugSubscription)
-        if affected_by_bug:
-            person_clauses.append(
-                And(BugAffectsPerson.person_id == HWSubmission.ownerID,
-                    BugAffectsPerson.bug == Bug.id,
-                    BugAffectsPerson.affected))
-            tables.append(BugAffectsPerson)
-        clauses.append(Or(person_clauses))
-
-        query = Select(
-            columns=[
-                Person.name, HWVendorID.bus,
-                HWVendorID.vendor_id_for_bus, HWDevice.bus_product_id],
-            tables=tables, where=And(*clauses), distinct=True,
-            order_by=[HWVendorID.bus, HWVendorID.vendor_id_for_bus,
-                      HWDevice.bus_product_id, Person.name])
-
-        return [
-            (person_name, HWBus.items[bus_id], vendor_id, product_id)
-             for person_name, bus_id, vendor_id, product_id
-             in IStore(HWSubmission).execute(query)]
-
-
-@implementer(IHWSystemFingerprint)
-class HWSystemFingerprint(SQLBase):
-    """Identifiers of a computer system."""
-
-    _table = 'HWSystemFingerprint'
-
-    fingerprint = StringCol(notNull=True)
-
-
-@implementer(IHWSystemFingerprintSet)
-class HWSystemFingerprintSet:
-    """A set of identifiers of a computer system."""
-
-    def getByName(self, fingerprint):
-        """See `IHWSystemFingerprintSet`."""
-        return HWSystemFingerprint.selectOneBy(fingerprint=fingerprint)
-
-    def createFingerprint(self, fingerprint):
-        """See `IHWSystemFingerprintSet`."""
-        return HWSystemFingerprint(fingerprint=fingerprint)
-
-
-@implementer(IHWVendorName)
-class HWVendorName(SQLBase):
-    """See `IHWVendorName`."""
-
-    _table = 'HWVendorName'
-
-    name = StringCol(notNull=True)
-
-
-@implementer(IHWVendorNameSet)
-class HWVendorNameSet:
-    """See `IHWVendorNameSet`."""
-
-    def create(self, name):
-        """See `IHWVendorNameSet`."""
-        return HWVendorName(name=name)
-
-    def getByName(self, name):
-        """See `IHWVendorNameSet`."""
-        return HWVendorName.selectOne(
-            'ulower(name)=ulower(%s)' % sqlvalues(name))
-
-
-four_hex_digits = re.compile('^0x[0-9a-f]{4}$')
-six_hex_digits = re.compile('^0x[0-9a-f]{6}$')
-# The regular expressions for the SCSI vendor and product IDs are not as
-# "picky" as the specification requires. Considering the fact that for
-# example Microtek sold at least one scanner model that returns '        '
-# as the vendor ID, it seems reasonable to allows also somewhat broken
-# looking IDs.
-scsi_vendor = re.compile('^.{8}$')
-scsi_product = re.compile('^.{16}$')
-
-validVendorID = {
-    HWBus.PCI: four_hex_digits,
-    HWBus.PCCARD: four_hex_digits,
-    HWBus.USB: four_hex_digits,
-    HWBus.IEEE1394: six_hex_digits,
-    HWBus.SCSI: scsi_vendor,
-    }
-
-validProductID = {
-    HWBus.PCI: four_hex_digits,
-    HWBus.PCCARD: four_hex_digits,
-    HWBus.USB: four_hex_digits,
-    HWBus.SCSI: scsi_product,
-    }
-
-
-def isValidVendorID(bus, id):
-    """Check that the string id is a valid vendor ID for this bus.
-
-    :return: True, if id is valid, otherwise False
-    :param bus: A `HWBus` indicating the bus type of "id"
-    :param id: A string with the ID
-
-    Some busses have constraints for IDs, while some can use arbitrary
-    values, for example the "fake" busses HWBus.SYSTEM and HWBus.SERIAL.
-
-    We use a hexadecimal representation of integers like "0x123abc",
-    i.e., the numbers have the prefix "0x"; for the digits > 9 we
-    use the lower case characters a to f.
-
-    USB and PCI IDs have always four digits; IEEE1394 IDs have always
-    six digits.
-
-    SCSI vendor IDs consist of eight bytes of ASCII data (0x20..0x7e);
-    if a vendor name has less than eight characters, it is padded on the
-    right with spaces (See http://t10.org/ftp/t10/drafts/spc4/spc4r14.pdf,
-    page 45).
-    """
-    if bus not in validVendorID:
-        return True
-    return validVendorID[bus].search(id) is not None
-
-
-def isValidProductID(bus, id):
-    """Check that the string id is a valid product for this bus.
-
-    :return: True, if id is valid, otherwise False
-    :param bus: A `HWBus` indicating the bus type of "id"
-    :param id: A string with the ID
-
-    Some busses have constraints for IDs, while some can use arbitrary
-    values, for example the "fake" busses HWBus.SYSTEM and HWBus.SERIAL.
-
-    We use a hexadecimal representation of integers like "0x123abc",
-    i.e., the numbers have the prefix "0x"; for the digits > 9 we
-    use the lower case characters a to f.
-
-    USB and PCI IDs have always four digits.
-
-    Since IEEE1394 does not specify product IDs, there is no formal
-    check of them.
-
-    SCSI product IDs consist of 16 bytes of ASCII data (0x20..0x7e);
-    if a product name has less than 16 characters, it is padded on the
-    right with spaces.
-    """
-    if bus not in validProductID:
-        return True
-    return validProductID[bus].search(id) is not None
-
-
-@implementer(IHWVendorID)
-class HWVendorID(SQLBase):
-    """See `IHWVendorID`."""
-
-    _table = 'HWVendorID'
-
-    bus = EnumCol(enum=HWBus, notNull=True)
-    vendor_id_for_bus = StringCol(notNull=True)
-    vendor_name = ForeignKey(dbName='vendor_name', foreignKey='HWVendorName',
-                             notNull=True)
-
-    def _create(self, id, **kw):
-        bus = kw.get('bus')
-        if bus is None:
-            raise TypeError('HWVendorID() did not get expected keyword '
-                            'argument bus')
-        vendor_id_for_bus = kw.get('vendor_id_for_bus')
-        if vendor_id_for_bus is None:
-            raise TypeError('HWVendorID() did not get expected keyword '
-                            'argument vendor_id_for_bus')
-        if not isValidVendorID(bus, vendor_id_for_bus):
-            raise ParameterError(
-                '%s is not a valid vendor ID for %s'
-                % (repr(vendor_id_for_bus), bus.title))
-        SQLBase._create(self, id, **kw)
-
-
-@implementer(IHWVendorIDSet)
-class HWVendorIDSet:
-    """See `IHWVendorIDSet`."""
-
-    def create(self, bus, vendor_id, vendor_name):
-        """See `IHWVendorIDSet`."""
-        vendor_name = HWVendorName.selectOneBy(name=vendor_name.name)
-        return HWVendorID(bus=bus, vendor_id_for_bus=vendor_id,
-                          vendor_name=vendor_name)
-
-    def getByBusAndVendorID(self, bus, vendor_id):
-        """See `IHWVendorIDSet`."""
-        if not isValidVendorID(bus, vendor_id):
-            raise ParameterError(
-                '%s is not a valid vendor ID for %s'
-                % (repr(vendor_id), bus.title))
-        return HWVendorID.selectOneBy(bus=bus, vendor_id_for_bus=vendor_id)
-
-    def get(self, id):
-        """See `IHWVendorIDSet`."""
-        return IStore(HWVendorID).find(HWVendorID, HWVendorID.id == id).one()
-
-    def idsForBus(self, bus):
-        """See `IHWVendorIDSet`."""
-        return IStore(HWVendorID).find(HWVendorID, bus=bus).order_by(
-            HWVendorID.vendor_id_for_bus)
-
-
-@implementer(IHWDevice)
-class HWDevice(SQLBase):
-    """See `IHWDevice.`"""
-    _table = 'HWDevice'
-
-    # XXX Abel Deuring 2008-05-02: The columns bus_vendor and
-    # bus_product_id are supposed to be immutable. However, if they
-    # are defined as "immutable=True", the creation of a new HWDevice
-    # instance leads to an AttributeError in sqlobject/main.py, line 814.
-    bus_vendor = ForeignKey(dbName='bus_vendor_id', foreignKey='HWVendorID',
-                            notNull=True, immutable=False)
-    bus_product_id = StringCol(notNull=True, dbName='bus_product_id',
-                               immutable=False)
-    variant = StringCol(notNull=False)
-    name = StringCol(notNull=True)
-    submissions = IntCol(notNull=True)
-
-    @property
-    def bus(self):
-        return self.bus_vendor.bus
-
-    @property
-    def vendor_id(self):
-        return self.bus_vendor.vendor_id_for_bus
-
-    @property
-    def vendor_name(self):
-        return self.bus_vendor.vendor_name.name
-
-    def _create(self, id, **kw):
-        bus_vendor = kw.get('bus_vendor')
-        if bus_vendor is None:
-            raise TypeError('HWDevice() did not get expected keyword '
-                            'argument bus_vendor')
-        bus_product_id = kw.get('bus_product_id')
-        if bus_product_id is None:
-            raise TypeError('HWDevice() did not get expected keyword '
-                            'argument bus_product_id')
-        if not isValidProductID(bus_vendor.bus, bus_product_id):
-            raise ParameterError(
-                '%s is not a valid product ID for %s'
-                % (repr(bus_product_id), bus_vendor.bus.title))
-        SQLBase._create(self, id, **kw)
-
-    def getSubmissions(self, driver=None, distribution=None,
-                       distroseries=None, architecture=None, owner=None):
-        """See `IHWDevice.`"""
-        return HWSubmissionSet().search(
-            device=self, driver=driver, distribution=distribution,
-            distroseries=distroseries, architecture=architecture, owner=owner)
-
-    @property
-    def drivers(self):
-        """See `IHWDevice.`"""
-        return IStore(HWDriver).find(
-            HWDriver, HWDeviceDriverLink.driver == HWDriver.id,
-            HWDeviceDriverLink.device == self).order_by(
-                HWDriver.package_name, HWDriver.name)
-
-    @property
-    def classes(self):
-        """See `IHWDevice.`"""
-        return IStore(HWDeviceClass).find(
-            HWDeviceClass,
-            HWDeviceClass.device == self.id).order_by(
-                HWDeviceClass.main_class, HWDeviceClass.sub_class)
-
-    def getOrCreateDeviceClass(self, main_class, sub_class=None):
-        """See `IHWDevice.`"""
-        result_set = IStore(HWDeviceClass).find(
-            HWDeviceClass,
-            HWDeviceClass.device == self.id,
-            HWDeviceClass.main_class == main_class,
-            HWDeviceClass.sub_class == sub_class)
-        existing_record = result_set.one()
-        if existing_record is not None:
-            return existing_record
-        return HWDeviceClass(
-            device=self, main_class=main_class, sub_class=sub_class)
-
-    def removeDeviceClass(self, main_class, sub_class=None):
-        """See `IHWDevice.`"""
-        store = IStore(HWDeviceClass)
-        result_set = store.find(
-            HWDeviceClass,
-            HWDeviceClass.device == self.id,
-            HWDeviceClass.main_class == main_class,
-            HWDeviceClass.sub_class == sub_class)
-        existing_record = result_set.one()
-        if existing_record is not None:
-            store.remove(existing_record)
-
-
-@implementer(IHWDeviceSet)
-class HWDeviceSet:
-    """See `IHWDeviceSet`."""
-
-    def create(self, bus, vendor_id, product_id, product_name, variant=None):
-        """See `IHWDeviceSet`."""
-        vendor_id_record = HWVendorID.selectOneBy(bus=bus,
-                                                  vendor_id_for_bus=vendor_id)
-        if vendor_id_record is None:
-            # The vendor ID may be unknown for two reasons:
-            #   - we do not have anything like a subscription to newly
-            #     assigned PCI or USB vendor IDs, so we may get submissions
-            #     with IDs we don't know about yet.
-            #   - we may get submissions with invalid IDs.
-            # In both cases, we create a new HWVendorID entry with the
-            # vendor name 'Unknown'.
-            unknown_vendor = HWVendorName.selectOneBy(name=UNKNOWN)
-            if unknown_vendor is None:
-                unknown_vendor = HWVendorName(name=UNKNOWN)
-            vendor_id_record = HWVendorID(bus=bus,
-                                          vendor_id_for_bus=vendor_id,
-                                          vendor_name=unknown_vendor)
-        return HWDevice(bus_vendor=vendor_id_record,
-                        bus_product_id=product_id, name=product_name,
-                        variant=variant, submissions=0)
-
-    def getByDeviceID(self, bus, vendor_id, product_id, variant=None):
-        """See `IHWDeviceSet`."""
-        if not isValidProductID(bus, product_id):
-            raise ParameterError(
-                '%s is not a valid product ID for %s'
-                % (repr(product_id), bus.title))
-        bus_vendor = HWVendorIDSet().getByBusAndVendorID(bus, vendor_id)
-        return HWDevice.selectOneBy(bus_vendor=bus_vendor,
-                                    bus_product_id=product_id,
-                                    variant=variant)
-
-    def getOrCreate(self, bus, vendor_id, product_id, product_name,
-                    variant=None):
-        """See `IHWDeviceSet`."""
-        device = self.getByDeviceID(bus, vendor_id, product_id, variant)
-        if device is None:
-            return self.create(bus, vendor_id, product_id, product_name,
-                               variant)
-        return device
-
-    def getByID(self, id):
-        """See `IHWDeviceSet`."""
-        return IStore(HWDevice).find(HWDevice, HWDevice.id == id).one()
-
-    def search(self, bus, vendor_id, product_id=None):
-        """See `IHWDeviceSet`."""
-        bus_vendor = HWVendorIDSet().getByBusAndVendorID(bus, vendor_id)
-        args = []
-        if product_id is not None:
-            if not isValidProductID(bus, product_id):
-                raise ParameterError(
-                    '%s is not a valid product ID for %s'
-                    % (repr(product_id), bus.title))
-            args.append(HWDevice.bus_product_id == product_id)
-        return IStore(HWDevice).find(
-            HWDevice, HWDevice.bus_vendor == bus_vendor, *args).order_by(
-                HWDevice.id)
-
-
-@implementer(IHWDeviceNameVariant)
-class HWDeviceNameVariant(SQLBase):
-    """See `IHWDeviceNameVariant`."""
-    _table = 'HWDeviceNameVariant'
-
-    vendor_name = ForeignKey(dbName='vendor_name', foreignKey='HWVendorName',
-                             notNull=True)
-    product_name = StringCol(notNull=True)
-    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
-    submissions = IntCol(notNull=True)
-
-
-@implementer(IHWDeviceNameVariantSet)
-class HWDeviceNameVariantSet:
-    """See `IHWDeviceNameVariantSet`."""
-
-    def create(self, device, vendor_name, product_name):
-        """See `IHWDeviceNameVariantSet`."""
-        vendor_name_record = HWVendorName.selectOneBy(name=vendor_name)
-        if vendor_name_record is None:
-            vendor_name_record = HWVendorName(name=vendor_name)
-        return HWDeviceNameVariant(device=device,
-                                   vendor_name=vendor_name_record,
-                                   product_name=product_name,
-                                   submissions=0)
-
-
-@implementer(IHWDriver)
-class HWDriver(SQLBase):
-    """See `IHWDriver`."""
-    _table = 'HWDriver'
-
-    # XXX: Abel Deuring 2008-12-10 bug=306265: package_name should
-    # be declared notNull=True. This fixes the ambiguity that
-    # "package_name is None" as well as "package_name == ''" can
-    # indicate "we don't know to which package this driver belongs",
-    # moreover, it gives a more clear meaning to the parameter value
-    #package_name='' in webservice API calls.
-    package_name = StringCol(notNull=False)
-    name = StringCol(notNull=True)
-    license = EnumCol(enum=License, notNull=False)
-
-    def getSubmissions(self, distribution=None, distroseries=None,
-                       architecture=None, owner=None):
-        """See `IHWDriver.`"""
-        return HWSubmissionSet().search(
-            driver=self, distribution=distribution,
-            distroseries=distroseries, architecture=architecture, owner=owner)
-
-
-@implementer(IHWDriverSet)
-class HWDriverSet:
-    """See `IHWDriver`."""
-
-    def create(self, package_name, name, license):
-        """See `IHWDriverSet`."""
-        if package_name is None:
-            package_name = ''
-        return HWDriver(package_name=package_name, name=name, license=license)
-
-    def getByPackageAndName(self, package_name, name):
-        """See `IHWDriverSet`."""
-        store = IStore(HWDriver)
-        if package_name in (None, ''):
-            return store.find(
-                HWDriver,
-                Or(HWDriver.package_name == None,
-                   HWDriver.package_name == ''),
-                HWDriver.name == name).one()
-        else:
-            return store.find(
-                HWDriver, HWDriver.package_name == package_name,
-                HWDriver.name == name).one()
-
-    def getOrCreate(self, package_name, name, license=None):
-        """See `IHWDriverSet`."""
-        # Bugs 306265, 369769: If the method parameter package_name is
-        # None, and if no matching record exists, we create new records
-        # with package_name = '', but we must also search for old records
-        # where package_name == None in order to avoid the creation of
-        # two records where on rcord has package_name=None and the other
-        # package_name=''.
-        driver = self.getByPackageAndName(package_name, name)
-
-        if driver is None:
-            return self.create(package_name, name, license)
-        return driver
-
-    def search(self, package_name=None, name=None):
-        """See `IHWDriverSet`."""
-        args = []
-        if package_name is not None:
-            if len(package_name) == 0:
-                args.append(Or(HWDriver.package_name == None,
-                               HWDriver.package_name == ''))
-            else:
-                args.append(HWDriver.package_name == package_name)
-        if name != None:
-            args.append(HWDriver.name == name)
-        return IStore(HWDriver).find(HWDriver, *args).order_by(HWDriver.id)
-
-    def getByID(self, id):
-        """See `IHWDriverSet`."""
-        return IStore(HWDriver).find(HWDriver, HWDriver.id == id).one()
-
-    def all_driver_names(self):
-        """See `IHWDriverSet`."""
-        return IStore(HWDriverName).find(
-            HWDriverName).order_by(HWDriverName.name)
-
-    def all_package_names(self):
-        """See `IHWDriverSet`."""
-        # XXX Abel Deuring 2009-06-19 The clause package_name != None
-        # can be removed once bug #306265 is fixed.
-        result = IStore(HWDriverPackageName).find(
-            HWDriverPackageName, HWDriverPackageName.package_name != None)
-        result.order_by(HWDriverPackageName.package_name)
-        return result
-
-
-@implementer(IHWDriverName)
-class HWDriverName(SQLBase):
-    """See `IHWDriverName`."""
-    _table = 'HWDriverNames'
-
-    name = StringCol(notNull=True)
-
-
-@implementer(IHWDriverPackageName)
-class HWDriverPackageName(SQLBase):
-    """See `IHWDriverPackageName`."""
-    _table = 'HWDriverPackageNames'
-
-    package_name = StringCol(notNull=True)
-
-
-@implementer(IHWDeviceDriverLink)
-class HWDeviceDriverLink(SQLBase):
-    """See `IHWDeviceDriverLink`."""
-    _table = 'HWDeviceDriverLink'
-
-    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
-    driver = ForeignKey(dbName='driver', foreignKey='HWDriver', notNull=False)
-
-
-@implementer(IHWDeviceDriverLinkSet)
-class HWDeviceDriverLinkSet:
-    """See `IHWDeviceDriverLinkSet`."""
-
-    def create(self, device, driver):
-        """See `IHWDeviceDriverLinkSet`."""
-        return HWDeviceDriverLink(device=device, driver=driver)
-
-    def getByDeviceAndDriver(self, device, driver):
-        """See `IHWDeviceDriverLink`."""
-        return HWDeviceDriverLink.selectOneBy(device=device, driver=driver)
-
-    def getOrCreate(self, device, driver):
-        """See `IHWDeviceDriverLink`."""
-        device_driver_link = self.getByDeviceAndDriver(device, driver)
-        if device_driver_link is None:
-            return self.create(device, driver)
-        return device_driver_link
-
-
-@implementer(IHWDeviceClass)
-class HWDeviceClass(SQLBase):
-    """See `IHWDeviceClass`."""
-
-    device = ForeignKey(dbName='device', foreignKey='HWDevice', notNull=True)
-    main_class = IntCol(notNull=True)
-    sub_class = IntCol(notNull=False)
-
-    def delete(self):
-        """See `IHWDeviceClass`."""
-        store = Store.of(self)
-        store.remove(self)
-
-
-@implementer(IHWDeviceClassSet)
-class HWDeviceClassSet:
-    """See `IHWDeviceClassSet`."""
-
-    def get(self, id):
-        """See `IHWDeviceClassSet`."""
-        return IStore(HWDeviceClass).find(
-            HWDeviceClass, HWDeviceClass.id == id).one()
-
-
-@implementer(IHWSubmissionDevice)
-class HWSubmissionDevice(SQLBase):
-    """See `IHWSubmissionDevice`."""
-    _table = 'HWSubmissionDevice'
-
-    device_driver_link = ForeignKey(dbName='device_driver_link',
-                                    foreignKey='HWDeviceDriverLink',
-                                    notNull=True)
-    submission = ForeignKey(dbName='submission', foreignKey='HWSubmission',
-                            notNull=True)
-    parent = ForeignKey(dbName='parent', foreignKey='HWSubmissionDevice',
-                        notNull=False)
-
-    hal_device_id = IntCol(notNull=True)
-
-    @property
-    def device(self):
-        """See `IHWSubmissionDevice`."""
-        return self.device_driver_link.device
-
-    @property
-    def driver(self):
-        """See `IHWSubmissionDevice`."""
-        return self.device_driver_link.driver
-
-
-@implementer(IHWSubmissionDeviceSet)
-class HWSubmissionDeviceSet:
-    """See `IHWSubmissionDeviceSet`."""
-
-    def create(self, device_driver_link, submission, parent, hal_device_id):
-        """See `IHWSubmissionDeviceSet`."""
-        return HWSubmissionDevice(device_driver_link=device_driver_link,
-                                  submission=submission, parent=parent,
-                                  hal_device_id=hal_device_id)
-
-    def getDevices(self, submission):
-        """See `IHWSubmissionDeviceSet`."""
-        return HWSubmissionDevice.selectBy(
-            submission=submission,
-            orderBy=['parent', 'device_driver_link', 'hal_device_id'])
-
-    def get(self, id):
-        """See `IHWSubmissionDeviceSet`."""
-        return IStore(HWSubmissionDevice).find(
-            HWSubmissionDevice, HWSubmissionDevice.id == id).one()
-
-    def numDevicesInSubmissions(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distro_target=None):
-        """See `IHWSubmissionDeviceSet`."""
-        tables, where_clauses = make_submission_device_statistics_clause(
-            bus, vendor_id, product_id, driver_name, package_name, False)
-
-        distro_tables, distro_clauses = make_distro_target_clause(
-            distro_target)
-        if distro_clauses:
-            tables.extend(distro_tables)
-            where_clauses.extend(distro_clauses)
-            where_clauses.append(
-                HWSubmissionDevice.submission == HWSubmission.id)
-
-        result = IStore(HWSubmissionDevice).execute(
-            Select(
-                columns=[Count()], tables=tables, where=And(*where_clauses)))
-        return result.get_one()[0]
-
-
-@implementer(IHWSubmissionBug)
-class HWSubmissionBug(SQLBase):
-    """See `IHWSubmissionBug`."""
-    _table = 'HWSubmissionBug'
-
-    submission = ForeignKey(dbName='submission', foreignKey='HWSubmission',
-                              notNull=True)
-    bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True)
-
-
-@implementer(IHWSubmissionBugSet)
-class HWSubmissionBugSet:
-    """See `IHWSubmissionBugSet`."""
-
-    def create(self, submission, bug):
-        """See `IHWSubmissionBugSet`."""
-        store = Store.of(bug)
-        existing_link = store.find(
-            HWSubmissionBug,
-            And(HWSubmissionBug.submission == submission,
-                HWSubmissionBug.bug == bug)).one()
-        if existing_link is not None:
-            return existing_link
-        return HWSubmissionBug(submission=submission, bug=bug)
-
-    def remove(self, submission, bug):
-        """See `IHWSubmissionBugSet`."""
-        store = Store.of(bug)
-        link = store.find(
-            HWSubmissionBug,
-            And(HWSubmissionBug.bug == bug,
-                HWSubmissionBug.submission == submission.id)).one()
-        if link is not None:
-            store.remove(link)
-
-    def submissionsForBug(self, bug, user=None):
-        """See `IHWSubmissionBugSet`."""
-        store = Store.of(bug)
-        result = store.find(
-            HWSubmission, And(HWSubmissionBug.bug == bug,
-                              HWSubmissionBug.submission == HWSubmission.id,
-                              _userCanAccessSubmissionStormClause(user)))
-        result.order_by(HWSubmission.submission_key)
-        return result
-
-
-def make_submission_device_statistics_clause(
-    bus, vendor_id, product_id, driver_name, package_name,
-    device_ids_required):
-    """Create a where expression and a table list for selecting devices.
-    """
-    tables = [HWSubmissionDevice, HWDeviceDriverLink]
-    where_clauses = [
-        HWSubmissionDevice.device_driver_link == HWDeviceDriverLink.id,
-        ]
-
-    if device_ids_required:
-        if bus is None or vendor_id is None or product_id is None:
-            raise ParameterError("Device IDs are required.")
-    else:
-        device_specified = [
-            param
-            for param in (bus, vendor_id, product_id)
-            if param is not None]
-
-        if len(device_specified) not in (0, 3):
-            raise ParameterError(
-                'Either specify bus, vendor_id and product_id or none of '
-                'them.')
-        if bus is None and driver_name is None:
-            raise ParameterError(
-                'Specify (bus, vendor_id, product_id) or driver_name.')
-    if bus is not None:
-        tables.extend([HWVendorID, HWDevice])
-        where_clauses.extend([
-            HWVendorID.bus == bus,
-            HWVendorID.vendor_id_for_bus == vendor_id,
-            HWDevice.bus_vendor == HWVendorID.id,
-            HWDeviceDriverLink.device == HWDevice.id,
-            HWDevice.bus_product_id == product_id])
-
-    if driver_name is None and package_name is None:
-        where_clauses.append(HWDeviceDriverLink.driver == None)
-    else:
-        tables.append(HWDriver)
-        where_clauses.append(HWDeviceDriverLink.driver == HWDriver.id)
-        if driver_name is not None:
-            where_clauses.append(HWDriver.name == driver_name)
-        if package_name is not None:
-            if package_name == '':
-                # XXX Abel Deuring, 2009-05-07, bug=306265. package_name
-                # should be declared notNull=True. For now, we must query
-                # for the empty string as well as for None.
-                where_clauses.append(
-                    Or(HWDriver.package_name == package_name,
-                       HWDriver.package_name == None))
-            else:
-                where_clauses.append(HWDriver.package_name == package_name)
-
-    return tables, where_clauses
-
-
-def make_distro_target_clause(distro_target):
-    """Create a where expression and a table list to limit results to a
-    distro target.
-    """
-    if distro_target is not None:
-        if IDistroArchSeries.providedBy(distro_target):
-            return (
-                [HWSubmission],
-                [HWSubmission.distroarchseries == distro_target.id])
-        elif IDistroSeries.providedBy(distro_target):
-            return (
-                [DistroArchSeries, HWSubmission],
-                [
-                    HWSubmission.distroarchseries == DistroArchSeries.id,
-                    DistroArchSeries.distroseries == distro_target.id,
-                    ])
-        elif IDistribution.providedBy(distro_target):
-            return (
-                [DistroArchSeries, DistroSeries, HWSubmission],
-                [
-                    HWSubmission.distroarchseries == DistroArchSeries.id,
-                    DistroArchSeries.distroseries == DistroSeries.id,
-                    DistroSeries.distribution == distro_target.id,
-                    ])
-        else:
-            raise ValueError(
-                'Parameter distro_target must be an IDistribution, '
-                'IDistroSeries or IDistroArchSeries')
-    return ([], [])
-
-
-def _userCanAccessSubmissionStormClause(user):
-    """Limit results of HWSubmission queries to rows the user can access.
-    """
-    submission_is_public = Not(HWSubmission.private)
-    admins = getUtility(ILaunchpadCelebrities).admin
-    janitor = getUtility(ILaunchpadCelebrities).janitor
-    if user is None:
-        return submission_is_public
-    elif user.inTeam(admins) or user == janitor:
-        return True
-    else:
-        public = Not(HWSubmission.private)
-        subselect = Select(
-            TeamParticipation.teamID,
-            And(HWSubmission.ownerID == TeamParticipation.teamID,
-                TeamParticipation.personID == user.id,
-                HWSubmission.private))
-        has_access = HWSubmission.ownerID.is_in(subselect)
-        return Or(public, has_access)
diff --git a/lib/lp/hardwaredb/scripts/tests/simple_valid_hwdb_submission.xml b/lib/lp/hardwaredb/scripts/tests/simple_valid_hwdb_submission.xml
deleted file mode 100644
index bb48776..0000000
--- a/lib/lp/hardwaredb/scripts/tests/simple_valid_hwdb_submission.xml
+++ /dev/null
@@ -1,168 +0,0 @@
-<?xml version="1.0" ?>
-<system version="1.0">
-  <summary>
-    <live_cd value="False"/>
-    <system_id value="f982bb1ab536469cebfd6eaadcea0ffc"/>
-    <distribution value="Ubuntu"/>
-    <distroseries value="8.04"/>
-    <architecture value="amd64"/>
-    <private value="False"/>
-    <contactable value="False"/>
-    <date_created value="2008-09-08T12:05:23.187453"/>
-    <client name="hwtest" version="0.9"/>
-  </summary>
-
-  <hardware>
-    <hal version="0.5.11rc2">
-      <device id="287" udi="/org/freedesktop/Hal/devices/computer">
-        <property name="info.subsystem" type="str">
-          unknown
-        </property>
-        <property name="info.product" type="str">
-          Computer
-        </property>
-        <property name="info.udi" type="str">
-          /org/freedesktop/Hal/devices/computer
-        </property>
-        <property name="info.bus" type="str">
-          unknown
-        </property>
-        <property name="system.product" type="str">
-          LIFEBOOK E8210 
-        </property>
-        <property name="system.vendor" type="str">
-          FUJITSU SIEMENS
-        </property>
-        <property name="system.kernel.machine" type="str">
-          x86_64
-        </property>
-        <property name="system.kernel.version" type="str">
-          2.6.24-19-generic
-        </property>
-        <property name="system.kernel.name" type="str">
-          Linux
-        </property>
-        <property name="system.hardware.product" type="str">
-          LIFEBOOK E8210
-        </property>
-        <property name="system.hardware.version" type="str">
-        </property>
-        <property name="system.hardware.vendor" type="str">
-          FUJITSU SIEMENS
-        </property>
-      </device>
-      <device id="333" udi="/org/freedesktop/Hal/devices/pci_8086_27c5">
-        <property name="info.subsystem" type="str">
-          pci
-        </property>
-        <property name="info.product" type="str">
-          82801GBM/GHM (ICH7 Family) SATA AHCI Controller
-        </property>
-        <property name="info.vendor" type="str">
-          Intel Corporation
-        </property>
-        <property name="info.parent" type="str">
-          /org/freedesktop/Hal/devices/computer
-        </property>
-        <property name="info.bus" type="str">
-          pci
-        </property>
-        <property name="info.linux.driver" type="str">
-          ahci
-        </property>
-        <property name="info.udi" type="str">
-          /org/freedesktop/Hal/devices/pci_8086_27c5
-        </property>
-        <property name="pci.product" type="str">
-          82801GBM/GHM (ICH7 Family) SATA AHCI Controller
-        </property>
-        <property name="pci.vendor_id" type="int">
-          32902
-        </property>
-        <property name="pci.vendor" type="str">
-          Intel Corporation
-        </property>
-        <property name="pci.product_id" type="int">
-          10181
-        </property>
-        <property name="pci.device_protocol" type="int">
-          1
-        </property>
-        <property name="pci.subsys_vendor" type="str">
-          Fujitsu Limited.
-        </property>
-        <property name="pci.device_class" type="int">
-          1
-        </property>
-      </device>
-    </hal>
-    <processors>
-      <processor id="123" name="0">
-        <property name="wp" type="bool">
-                True
-        </property>
-        <property name="flags" type="list">
-          <value type="str">
-                  fpu
-          </value>
-          <value type="str">
-                  vme
-          </value>
-          <value type="str">
-                  de
-          </value>
-        </property>
-        <property name="cpu_mhz" type="float">
-                1000.0
-        </property>
-      </processor>
-    </processors>
-  </hardware>
-  <software>
-    <lsbrelease>
-      <property name="release" type="str">
-        8.04
-      </property>
-      <property name="codename" type="str">
-        hardy
-      </property>
-      <property name="distributor-id" type="str">
-        Ubuntu
-      </property>
-      <property name="description" type="str">
-        Ubuntu 8.04.1
-      </property>
-    </lsbrelease>
-    <packages>
-      <package id="2025" name="linux-image-2.6.24-19-generic">
-        <property name="description" type="str">
-          Linux kernel image for version 2.6.24 on x86/x86_64
-        </property>
-        <property name="version" type="str">
-          2.6.24-19-generic
-        </property>
-      </package>
-    </packages>
-  </software>
-  <questions>
-    <question name="resolution">
-      <answer type="multiple_choice">
-        yes
-      </answer>
-      <answer_choices>
-      <value type="str">
-        yes
-      </value>
-      <value type="str">
-        no
-      </value>
-      <value type="str">
-        skip
-      </value>
-      </answer_choices>
-      <comment>
-
-      </comment>
-    </question>
-  </questions>
-</system>
diff --git a/lib/lp/hardwaredb/stories/__init__.py b/lib/lp/hardwaredb/stories/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/stories/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt b/lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt
deleted file mode 100644
index 9665a6c..0000000
--- a/lib/lp/hardwaredb/stories/hwdb/xx-hwdb.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-= Views for raw submission data. =
-
-The raw submission data is listed by submitter and by system fingerprint.
-
-== Submissions listed by person ==
-
-The pages http://launchpad.test/~(name)/+hwdb-submissions lists the hardware
-database submissions by this person.
-
-    >>> anon_browser.open('http://launchpad.test/~name12/+hwdb-submissions')
-    >>> [content_table] = find_tags_by_class(anon_browser.contents, 'listing')
-    >>> print(extract_text(content_table))
-    System        Date submitted  Download          Raw
-    MSI MS-7369   ...             sample-submission-2.xml ...
-    TONKA TUFFBOOK2600 ...        hwsubmission1.xml ...
-
-The 'System', 'Download' and 'Raw' columns should contain valid links for that
-submission. All links should resolve to valid pages, but the individual pages
-are tested in detail later.
-
-    >>> system_link = anon_browser.getLink('MSI MS-7369')
-    >>> system_link.url
-    'http://launchpad.test/+hwdb/+fingerprint/MSI MS-7369'
-    >>> system_link.click()
-    >>> anon_browser.headers['status']
-    '200 Ok'
-    >>> anon_browser.goBack(1)
-
-    >>> download_link = anon_browser.getLink('sample-submission-2.xml')
-    >>> download_link.url
-    'http://.../sample-submission-2.xml'
-
-    >>> raw_link = anon_browser.getLink('text')
-    >>> raw_link.url
-    'http://launchpad.test/+hwdb/+submission/sample-submission'
-    >>> raw_link.click()
-    >>> anon_browser.headers['status']
-    '200 Ok'
-    >>> anon_browser.goBack(1)
-
-The owner of a submission sees a complete list of their own submissions.
-They also get an additional column in the listing, showing the "private"
-status of each submission.
-
-    >>> browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.test/~name12/+hwdb-submissions')
-    >>> [content_table] = find_tags_by_class(browser.contents, 'listing')
-    >>> print(extract_text(content_table))
-    System        Date submitted  Download          Private     Raw
-    MSI MS-7369   ...             sample-submission-2.xml ...
-    TONKA TUFFBOOK2600 ...        hwsubmission1.xml    no       ...
-
-If a person has not submitted any hardware data, an according message is
-displayed.
-
-    >>> anon_browser.open('http://launchpad.test/~name16/+hwdb-submissions')
-    >>> print(extract_text(find_main_content(anon_browser.contents)))
-    Hardware submissions for Foo Bar...
-    Foo Bar has posted no submissions.
-
-== Viewing individual submissions ==
-
-Individual submissions can be rendered in an RFC-822-formatted file.
-
-    >>> browser.open(
-    ...     'http://launchpad.test/+hwdb/+submission/sample-submission/+text')
-    >>> print(browser.contents)
-    Date-Created: 2008-09-26 17:19:18+00:00
-    Date-Submitted: ...
-    Format: VERSION_1
-    Distribution: ubuntu
-    Distribution-Series: 5.04
-    Architecture: i386
-    System: MSI MS-7369
-    Submission URL: http://.../sample-submission-2.xml
-
-
-== Submissions listed by fingerprint ==
-
-The pages http://launchpad.test/+hwdb/+fingerprint/(fingerprint) list
-all submissions for a fingerprint/system name.
-
-    >>> anon_browser.open(
-    ...     'http://launchpad.test/+hwdb/+fingerprint/MSI%20MS-7369')
-    >>> [content_table] = find_tags_by_class(anon_browser.contents, 'listing')
-    >>> print(extract_text(content_table))
-    Date submitted     Submitted by    Download     Raw
-    ...                     n/a        sample-submission-2.xml ...
-
-A page for an unknown fingerprint displays an according message.
-
-    >>> anon_browser.open(
-    ...     'http://launchpad.test/+hwdb/+fingerprint/unknownfingerprint')
-    >>> print(extract_text(find_main_content(anon_browser.contents)))
-    Hardware Database submissions for a fingerprint
-    Submissions for system: unknownfingerprint
-    There are no submissions for this system.
diff --git a/lib/lp/hardwaredb/stories/webservice/xx-hwdb.txt b/lib/lp/hardwaredb/stories/webservice/xx-hwdb.txt
deleted file mode 100644
index 48cb6d4..0000000
--- a/lib/lp/hardwaredb/stories/webservice/xx-hwdb.txt
+++ /dev/null
@@ -1,1430 +0,0 @@
-= The HWDB API =
-
-The HWDB API allows access to the HWDB tables.
-
-
-== The HWDB application root ==
-
-The HWDB application root provides the core methods to access HWDB
-tables. It also provides the set of known package names appearing
-in the table HWDriver.
-
-    >>> from lazr.restful.testing.webservice import (
-    ...     pprint_collection, pprint_entry)
-    >>> hwdb_root = webservice.get('/+hwdb').jsonBody()
-    >>> pprint_entry(hwdb_root)
-    driver_names_collection_link:
-        u'http://api.launchpad.test/beta/+hwdb/driver_names'
-    package_names_collection_link:
-        u'http://api.launchpad.test/beta/+hwdb/package_names'
-    resource_type_link: u'http://api.launchpad.test/beta/#hwdb'
-    self_link: u'http://api.launchpad.test/beta/+hwdb'
-
-
-=== Device Queries and Devices ===
-
-The method HWDBApplication.devices returns known devices matching
-the given parameters bus and vendor ID. (See
-doc/hwdb-device-tables.txt for details about the underlying table
-HWDevice.)
-
-    >>> devices = webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=System&vendor_id=MSI').jsonBody()
-
-A device entry provides the attributes bus_product_id, name and variant.
-
-    >>> for entry in devices['entries']:
-    ...     pprint_entry(entry)
-    bus: u'System'
-    bus_product_id: u'MS-7369'
-    classes_collection_link: u'.../+hwdb/+device/1/classes'
-    drivers_collection_link: u'.../+hwdb/+device/1/drivers'
-    id: 1
-    name: u'MS-7369'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_device'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+device/1'
-    variant: None
-    vendor_id: u'MSI'
-    vendor_name: u'MSI'
-
-The query parameter `bus` must be a bus name as enumerated by HWBus.
-
-    >>> print(webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=NoSuchBus&vendor_id=MSI'))
-    HTTP/1.1 400 Bad Request
-    ...
-    bus: Invalid value "NoSuchBus". Acceptable values are: System, PCI,
-    USB, IEEE1394, SCSI, Parallel Port, Serial port, IDE, ATA, Floppy, ...
-
-Omitting the query parameters `bus` or `vendor_id` leads to an error.
-
-    >>> print(webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=System'))
-    HTTP/1.1 400 Bad Request
-    ...
-    vendor_id: Required input is missing.
-
-    >>> print(webservice.get(
-    ...     '/+hwdb?ws.op=devices&vendor_id=MSI'))
-    HTTP/1.1 400 Bad Request
-    ...
-    bus: Required input is missing.
-
-HWDBApplication.devices() allows the optional parameters product_id. Only
-devices matching the given value are returned.
-
-    >>> devices = webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=System&vendor_id=MSI&product_id=MS-7369')
-    >>> for entry in devices.jsonBody()['entries']:
-    ...     pprint_entry(entry)
-    bus: u'System'
-    bus_product_id: u'MS-7369'
-    classes_collection_link: u'.../+hwdb/+device/1/classes'
-    drivers_collection_link: u'.../+hwdb/+device/1/drivers'
-    id: 1
-    name: u'MS-7369'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_device'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+device/1'
-    variant: None
-    vendor_id: u'MSI'
-    vendor_name: u'MSI'
-
-    >>> devices = webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=System&vendor_id=MSI&product_id=nothing')
-    >>> print(devices.jsonBody()['entries'])
-    []
-
-Some buses have constraints for the values of vendor and product IDs.
-IDs for PCI devices, for example, must have the form 0x12ab. (For
-an overview of these constraints, see doc/hwdb-device-tables.txt.)
-Queries with invalid parameter values return a 400 error.
-
-    >>> print(webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=PCI&vendor_id=WRONG'))
-    HTTP/1.1 400 Bad Request
-    ...
-    u'WRONG' is not a valid vendor ID for PCI
-
-    >>> print(webservice.get(
-    ...     '/+hwdb?ws.op=devices&bus=PCI&vendor_id=0x1234&product_id=BAD'))
-    HTTP/1.1 400 Bad Request
-    ...
-    u'BAD' is not a valid product ID for PCI
-
-Single device records can be accessed too.
-
-    >>> device = webservice.get('/+hwdb/+device/2').jsonBody()
-    >>> pprint_entry(device)
-    bus: u'PCI'
-    bus_product_id: u'0x0455'
-    classes_collection_link: u'.../+hwdb/+device/2/classes'
-    drivers_collection_link: u'.../+hwdb/+device/2/drivers'
-    id: 2
-    name: u'MCP65 USB Controller'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_device'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+device/2'
-    variant: None
-    vendor_id: u'0x10de'
-    vendor_name: u'nVidia Corporation'
-
-A 404 error is returned, when a non-existent ID is passed in the URL...
-
-    >>> print(webservice.get('/+hwdb/+device/1000000'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'1000000'
-
-...and when an otherwise invalid ID is passed.
-
-    >>> print(webservice.get('/+hwdb/+device/nonsense'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'nonsense'
-
-HWDevice.drivers returns the list of drivers associtaed with this device.
-
-    >>> drivers = webservice.get('/+hwdb/+device/2/drivers').jsonBody()
-    >>> for driver in drivers['entries']:
-    ...     pprint_entry(driver)
-    id: 1
-    license: None
-    name: u'ehci_hcd'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/1'
-    id: 5
-    license: None
-    name: u'hub'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/5'
-    id: 2
-    license: None
-    name: u'usb'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/2'
-
-We can add and retrieve class data, i.e., the type of a device (printer,
-scanner, sound device) for a device. Device 2 is a PCI USB controller,
-having the main PCI class 12 and sub-class 3. Let's add a new device
-class record for this device by calling device.getOrCreateDevice(). This
-call returns a reference to a HWDeviceClass record.
-
-    >>> class_info = webservice.named_post(
-    ...     '/+hwdb/+device/2', 'getOrCreateDeviceClass', {}, main_class=12,
-    ...     sub_class=3).jsonBody()
-    >>> pprint_entry(class_info)
-    main_class: 12
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_device_class'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+deviceclass/...'
-    sub_class: 3
-
-A second call of this method returns the same record.
-
-    >>> class_info = webservice.named_post(
-    ...     '/+hwdb/+device/2', 'getOrCreateDeviceClass', {}, main_class=12,
-    ...     sub_class=3).jsonBody()
-    >>> print(class_info['self_link'])
-    http://api.launchpad.test/beta/+hwdb/+deviceclass/...
-
-The property device.classes returns the collection of all classes
-assigned to the device.
-
-    >>> class_info = webservice.get('/+hwdb/+device/2/classes').jsonBody()
-    >>> for entry in class_info['entries']:
-    ...     pprint_entry(entry)
-    main_class: 12
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_device_class'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+deviceclass/...'
-    sub_class: 3
-
-We can delete existing class data by calling deviceclass.delete().
-
-    >>> print(webservice.delete(class_info['entries'][0]['self_link']))
-    HTTP/1.1 200 Ok
-    ...
-
-    >>> class_info = webservice.get('/+hwdb/+device/2/classes').jsonBody()
-    >>> print(class_info['total_size'])
-    0
-
-
-=== Drivers and Driver Queries ===
-
-The method HWDBApplication.drivers() returns known drivers matching
-the given optional parameters package_name and name. (See
-doc/hwdb-device-tables.txt for details about the underlying table
-HWDriver.)
-
-All driver records from the sample data have package_name ==
-'linux-image-2.6.24-19-generic'; let's add some driver records
-with different package names, so that we can see how filtering
-by name works.
-
-    >>> from zope.component import getUtility
-    >>> from lp.testing import login, logout, ANONYMOUS
-    >>> from lp.hardwaredb.interfaces.hwdb import IHWDriverSet
-    >>> login(ANONYMOUS)
-    >>> driver_set = getUtility(IHWDriverSet)
-    >>> # A typical graphics card driver
-    >>> driver_set.create('xorg', 'nv', None)
-    <HWDriver at ...>
-    >>> # A driver without an associated package.
-    >>> driver_set.create(None, 'usb', None)
-    <HWDriver at ...>
-    >>> # And another driver without an associated package.
-    >>> driver_set.create('', 'foo', None)
-    <HWDriver at ...>
-    >>> logout()
-
-If no parameters are specified, HWDBApplication.drivers() returns all
-known drivers. A device entry provides the attributes bus_product_id,
-name, package_name, variant and license.
-
-    >>> drivers = webservice.get('/+hwdb?ws.op=drivers&ws.size=15').jsonBody()
-    >>> for entry in drivers['entries']:
-    ...     pprint_entry(entry)
-    id: ...
-    license: None
-    name: u'ehci_hcd'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'usb'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'usb-storage'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'sd'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'hub'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'ahci'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'sr'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'nv'
-    package_name: u'xorg'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'usb'
-    package_name: u''
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'foo'
-    package_name: u''
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-
-If we specify a package name, only drivers from this package are
-returned.
-
-    >>> drivers = webservice.get(
-    ...     '/+hwdb?ws.op=drivers&package_name=xorg').jsonBody()
-    >>> [nv_xorg_driver] = drivers['entries']
-    >>> pprint_entry(nv_xorg_driver)
-    id: ...
-    license: None
-    name: u'nv'
-    package_name: u'xorg'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-
-If we specify an empty package name, those drivers are returned where
-a package_name is not recorded.
-
-    >>> drivers = webservice.get(
-    ...     '/+hwdb?ws.op=drivers&package_name=').jsonBody()
-    >>> for entry in drivers['entries']:
-    ...     pprint_entry(entry)
-    id: ...
-    license: None
-    name: u'usb'
-    package_name: u''
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'foo'
-    package_name: u''
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-
-If the parameter name is specified but no package_name, all drivers with
-the given name are returned.
-
-    >>> drivers = webservice.get(
-    ...     '/+hwdb?ws.op=drivers&name=usb').jsonBody()
-    >>> for entry in drivers['entries']:
-    ...     pprint_entry(entry)
-    id: ...
-    license: None
-    name: u'usb'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-    id: ...
-    license: None
-    name: u'usb'
-    package_name: u''
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/...'
-
-If package_name and name are specified, those records are returned that
-match both values.
-
-    >>> drivers = webservice.get(
-    ...     '/+hwdb?ws.op=drivers&name=usb'
-    ...     '&package_name=linux-image-2.6.24-19-generic').jsonBody()
-    >>> [usb_driver] = drivers['entries']
-    >>> pprint_entry(usb_driver)
-    id: 2
-    license: None
-    name: u'usb'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/2'
-
-Single drivers can be retrieved too.
-
-    >>> pprint_entry(webservice.get('/+hwdb/+driver/1').jsonBody())
-    id: 1
-    license: None
-    name: u'ehci_hcd'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driver/1'
-
-A 404 error is returned, when a non-existent ID is passed in the URL...
-
-    >>> print(webservice.get('/+hwdb/+driver/1000000'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'1000000'
-
-...and when an otherwise invalid ID is passed.
-
-    >>> print(webservice.get('/+hwdb/+driver/nonsense'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'nonsense'
-
-Once we've got a driver, we can search for submissions associated with it.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions').jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-We can filter the list of drivers by distribution.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     distribution=webservice.getAbsoluteUrl('/ubuntu')).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     distribution=webservice.getAbsoluteUrl('/debian')).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-We can filter the list of drivers by distribution series.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary')).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/warty')).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-We can filter the list of drivers by processor architecture.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     architecture='i386').jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     architecture='powerpc').jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-We can filter by distribution series and processor architecture.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+driver/1', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary'),
-    ...     architecture='i386').jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-
-=== Driver names and package names ===
-
-The HWDB application object provides a list of known driver names...
-
-    >>> driver_names = webservice.get(
-    ...     hwdb_root['driver_names_collection_link']).jsonBody()
-    >>> for driver_name in driver_names['entries']:
-    ...     pprint_entry(driver_name)
-    name: u'ahci'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+drivername/ahci'
-    name: u'ehci_hcd'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+drivername/ehci_hcd'
-    name: u'foo'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+drivername/foo'
-    name: u'hub'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+drivername/hub'
-    name: u'nv'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_driver_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+drivername/nv'
-
-...and of package names.
-
-    >>> package_names = webservice.get(
-    ...     hwdb_root['package_names_collection_link']).jsonBody()
-    >>> for package_name in package_names['entries']:
-    ...     pprint_entry(package_name)
-    package_name: u''
-    resource_type_link:
-        u'http://api.launchpad.test/beta/#h_w_driver_package_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driverpackagename/'
-    package_name: u'linux-image-2.6.24-19-generic'
-    resource_type_link:
-        u'http://api.launchpad.test/beta/#h_w_driver_package_name'
-    self_link:
-        u'http://api.launchpad.test/beta/+hwdb/+driverpackagename/linux-...
-    package_name: u'xorg'
-    resource_type_link:
-        u'http://api.launchpad.test/beta/#h_w_driver_package_name'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+driverpackagename/xorg'
-
-
-=== HWDB submissions and submission queries ===
-
-A single submission can be accessed via its submission key. (See below
-for the contents of devices_collection_link.)
-
-    >>> submission = webservice.get(
-    ...     '/+hwdb/+submission/sample-submission').jsonBody()
-    >>> pprint_entry(submission)
-    contactable: False
-    date_created: u'2008-09-26T17:19:18+00:00'
-    date_submitted: u'2008-09-30T08:19:00.222131+00:00'
-    devices_collection_link: u'.../+hwdb/+submission/sample-submission/devices'
-    format: u'Version 1'
-    owner_link: u'http://api.launchpad.test/beta/~name12'
-    private: False
-    raw_submission_link:
-        u'.../+hwdb/+submission/sample-submission/raw_submission'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_submission'
-    self_link: u'.../+hwdb/+submission/sample-submission'
-    status: u'Processed'
-    submission_key: u'sample-submission'
-
-Accessing the raw submission data yields a redirect to a Librarian URL.
-
-    >>> print(webservice.get(submission['raw_submission_link']))
-    HTTP/1.1 303 See Other...
-    Content-Length: 0
-    ...
-    Content-Type: text/plain
-    Location: http://.../92/sample-submission-2.xml
-    ...
-
-A 404 error is returned when a client tries to access a non-existent
-submission.
-
-    >>> print(webservice.get('/+hwdb/+submission/nonsense'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <...systemhomes.HWDBApplication object at ...>, name: u'nonsense'
-
-Submissions which mention a specific device can be retrieved by
-using the getSubmissions operation.
-
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?ws.op=getSubmissions').jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-We can limit the result set to submissions where the device is accessed
-by a specific driver. Device 2 is a USB controller, so will get the sample
-submission when we set the parameter driver to the usb driver...
-
-    >>> from six.moves.urllib.parse import urlencode
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'driver': usb_driver['self_link'],
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-...but when we set it to nv_xorg_driver, we get an empty result set.
-
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'driver': nv_xorg_driver['self_link'],
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-We can also limit the result set to submissions made for a specific
-distribution...
-
-    >>> distros = webservice.get("/distros").jsonBody()
-    >>> ubuntu = distros['entries'][0]
-    >>> ubuntu['name']
-    u'ubuntu'
-    >>> debian = distros['entries'][3]
-    >>> debian['name']
-    u'debian'
-
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'distribution': ubuntu['self_link'],
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'distribution': debian['self_link'],
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...for a specific processor architecture...
-
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'architecture': 'i386',
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> parameters = {
-    ...     'ws.op': 'getSubmissions',
-    ...     'architecture': 'amd64',
-    ...     }
-    >>> submissions = webservice.get(
-    ...     '/+hwdb/+device/2?' + urlencode(parameters)).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...for a specific distro series...
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary')).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/warty')).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...and the combination of a distro series and processor architecture.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary'),
-    ...     architecture='i386').jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary'),
-    ...     architecture='powerpc').jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-But we can't supply both distribution and distroseries.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     distribution=webservice.getAbsoluteUrl('/ubuntu'),
-    ...     distroseries=webservice.getAbsoluteUrl('/ubuntu/hoary')))
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution` or `distroseries` can be present.
-
-We can query for submissions from a particular user.
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     owner=webservice.getAbsoluteUrl('/~name12')).jsonBody()
-    >>> for submission in submissions['entries']:
-    ...     print(submission['submission_key'])
-    sample-submission
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb/+device/2', 'getSubmissions',
-    ...     owner=webservice.getAbsoluteUrl('/~name20')).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-=== Searching for submissions ===
-
-Alternatively, we can also search for hardware submissions by user:
-
-    >>> owner = webservice.getAbsoluteUrl('/~name12')
-    >>> submissions = webservice.get(
-    ...     '/+hwdb?ws.op=search&owner=%s' % owner).jsonBody()
-    >>> print(submissions['total_size'])
-    2
-
-    >>> owner = webservice.getAbsoluteUrl('/~name20')
-    >>> submissions = webservice.get(
-    ...     '/+hwdb?ws.op=search&owner=%s' % owner).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...and by device:
-
-    >>> device = webservice.getAbsoluteUrl('/+hwdb/+device/1')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', device=device).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-
-...and by driver:
-
-    >>> driver = webservice.getAbsoluteUrl('/+hwdb/+driver/1')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', driver=driver).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-
-...and by distribution:
-
-    >>> ubuntu_url = webservice.getAbsoluteUrl('/ubuntu')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', distribution=ubuntu_url).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-    >>> debian_url = webservice.getAbsoluteUrl('/debian')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', distribution=debian_url).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...and by distroseries:
-
-    >>> hoary_url = webservice.getAbsoluteUrl('/ubuntu/hoary')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', distroseries=hoary_url).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-    >>> warty_url = webservice.getAbsoluteUrl('/ubuntu/warty')
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', distroseries=warty_url).jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...and by architecture:
-
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', architecture='i386').jsonBody()
-    >>> print(submissions['total_size'])
-    1
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', architecture='powerpc').jsonBody()
-    >>> print(submissions['total_size'])
-    0
-
-...and by date created:
-
-    >>> date_created = u'2007-09-11T00:00:00+00:00'
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', created_before=date_created).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', created_after=date_created).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-
-...and by date submitted:
-
-    >>> date_submitted = u'2007-09-11T15:23:45.653316+00:00'
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', submitted_before=date_submitted).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-    >>> submissions = webservice.named_get(
-    ...     '/+hwdb', 'search', submitted_after=date_submitted).jsonBody()
-    >>> print(submissions['total_size'])
-    1
-
-
-=== Submission Devices ===
-
-The table HWSubmissionDevice associates devices with submissions.
-(See hwdb-device-tables.txt for details.)
-
-A single HWSubmissionDevice record can be accessed via its database ID.
-
-    >>> submission_device = webservice.get(
-    ...     '/+hwdb/+submissiondevice/3').jsonBody()
-    >>> pprint_entry(submission_device)
-    device_link: u'http://api.launchpad.test/beta/+hwdb/+device/2'
-    driver_link: u'http://api.launchpad.test/beta/+hwdb/+driver/1'
-    hal_device_id: 121
-    id: 3
-    parent_link: u'http://api.launchpad.test/beta/+hwdb/+submissiondevice/2'
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_submission_device'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+submissiondevice/3'
-
-The HWSubmissionDevice records belonging to a given submission can
-are stored in HWSubmission.devices.
-
-    >>> url = '/+hwdb/+submission/sample-submission/devices?ws.size=3'
-    >>> submission_devices = webservice.get(url).jsonBody()
-    >>> for submission_device in submission_devices['entries']:
-    ...     pprint_entry(submission_device)
-    device_link: u'http://api.launchpad.test/beta/+hwdb/+device/2'
-    driver_link: None
-    hal_device_id: 121
-    id: 2
-    parent_link: u'.../+hwdb/+submissiondevice/1'
-    resource_type_link: u'.../#h_w_submission_device'
-    self_link: u'.../+hwdb/+submissiondevice/2'
-    device_link: u'.../+hwdb/+device/5'
-    driver_link: None
-    hal_device_id: 65
-    id: 16
-    parent_link: u'.../+hwdb/+submissiondevice/1'
-    resource_type_link: u'.../#h_w_submission_device'
-    self_link: u'.../+hwdb/+submissiondevice/16'
-    device_link: u'.../+hwdb/+device/2'
-    driver_link: u'.../+hwdb/+driver/1'
-    hal_device_id: 121
-    id: 3
-    parent_link: u'.../+hwdb/+submissiondevice/2'
-    resource_type_link: u'.../#h_w_submission_device'
-    self_link: u'.../+hwdb/+submissiondevice/3'
-
-
-== Vendor ID queries ==
-
-A vendor ID can be accessed by via its database ID.
-
-    >>> vendor_id = webservice.get('/+hwdb/+hwvendorid/1').jsonBody()
-    >>> pprint_entry(vendor_id)
-    bus: u'System'
-    id: 1
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_vendor_i_d'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+hwvendorid/1'
-    vendor_id: u'MSI'
-
-A 404 error is returned, when a non-existent ID is passed in the URL...
-
-    >>> print(webservice.get('/+hwdb/+hwvendorid/1000000'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'1000000'
-
-...and when an otherwise invalid ID is passed.
-
-    >>> print(webservice.get('/+hwdb/+hwvendorid/nonsense'))
-    HTTP/1.1 404 Not Found
-    ...
-    Object: <lp.systemhomes.HWDBApplication
-    ... name: u'nonsense'
-
-Known vendor IDs for a given bus can be queried with
-HWDBApplication.vendorIDs().
-
-    >>> vendor_ids = webservice.get(
-    ...     '/+hwdb?ws.op=vendorIDs&bus=USB').jsonBody()
-    >>> for vendor_id in vendor_ids['entries']:
-    ...     pprint_entry(vendor_id)
-    bus: u'USB'
-    id: 3
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_vendor_i_d'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+hwvendorid/3'
-    vendor_id: u'0x04b4'
-    bus: u'USB'
-    id: 4
-    resource_type_link: u'http://api.launchpad.test/beta/#h_w_vendor_i_d'
-    self_link: u'http://api.launchpad.test/beta/+hwdb/+hwvendorid/4'
-    vendor_id: u'0x0dda'
-
-
-== Statistical methods ==
-
-We can query how often a given device or a given driver appears in
-HWDB submissions.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     '
-    ...     ).jsonBody())
-    1
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions', driver_name='sd').jsonBody())
-    5
-
-We can ask how many given devices controlled by a given driver appear
-in HWDB submissions.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd').jsonBody())
-    1
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense').jsonBody())
-    0
-
-This count can be limited to a package_name.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic').jsonBody())
-    1
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense').jsonBody())
-    0
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions', driver_name='sd',
-    ...     package_name='linux-image-2.6.24-19-generic').jsonBody())
-    5
-
-While the parameters for a device and for a driver are optional,
-specifying neither leads to an error.
-
-    >>> print(webservice.named_get('/+hwdb', 'numDevicesInSubmissions'))
-    HTTP/1.1 400 Bad Request
-    ...
-    Specify (bus, vendor_id, product_id) or driver_name.
-
-We can additionally pass a reference to a distribution...
-
-    >>> ubuntu_url = webservice.getAbsoluteUrl('/ubuntu')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url).jsonBody())
-    1
-    >>> debian_url = webservice.getAbsoluteUrl('/debian')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=debian_url).jsonBody())
-    0
-
-...a reference to a distroseries...
-
-    >>> hoary_url = webservice.getAbsoluteUrl('/ubuntu/hoary')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url).jsonBody())
-    1
-    >>> warty_url = webservice.getAbsoluteUrl('/ubuntu/warty')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=warty_url).jsonBody())
-    0
-
-...or a reference to a distroarchseries.
-
-    >>> hoary_i386_url = webservice.getAbsoluteUrl('/ubuntu/hoary/i386')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_i386_url).jsonBody())
-    1
-    >>> hoary_hppa_url = webservice.getAbsoluteUrl('/ubuntu/hoary/hppa')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_hppa_url).jsonBody())
-    0
-
-But at most one of the parameters distribution, distroseries,
-distroarchseries may be specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url, distroseries=hoary_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url,
-    ...     distroarchseries=hoary_i386_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numDevicesInSubmissions',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url, distroarchseries=hoary_i386_url))
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-numSubmissionsWithDevice() returns a dicitionary containing the
-number of submissions mentioning a given device or driver, and the
-number of all processed submissions, limited to the given distribution,
-distroseries or distroarchseries, if one of these parameters is
-specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     '
-    ...     ).jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     driver_name='sd').jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-
-The parameters for a device and a driver can be combined.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd').jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense').jsonBody())
-    {u'submissions_with_device': 0, u'all_submissions': 1}
-
-Additionally, a package_name can be passed.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic').jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense').jsonBody())
-    {u'submissions_with_device': 0, u'all_submissions': 1}
-
-If neither a device nor a driver is specified, an error is returned.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice'))
-    HTTP/1.1 400 Bad Request
-    ...
-    Specify (bus, vendor_id, product_id) or driver_name.
-
-The count can be limited to a distribution...
-
-    >>> ubuntu_url = webservice.getAbsoluteUrl('/ubuntu')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url).jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-    >>> debian_url = webservice.getAbsoluteUrl('/debian')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=debian_url).jsonBody())
-    {u'submissions_with_device': 0, u'all_submissions': 0}
-
-...or a distroseries...
-
-    >>> hoary_url = webservice.getAbsoluteUrl('/ubuntu/hoary')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url).jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-    >>> warty_url = webservice.getAbsoluteUrl('/ubuntu/warty')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=warty_url).jsonBody())
-    {u'submissions_with_device': 0, u'all_submissions': 0}
-
-...or a distroarchseries.
-
-    >>> hoary_i386_url = webservice.getAbsoluteUrl('/ubuntu/hoary/i386')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_i386_url).jsonBody())
-    {u'submissions_with_device': 1, u'all_submissions': 1}
-    >>> hoary_hppa_url = webservice.getAbsoluteUrl('/ubuntu/hoary/hppa')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_hppa_url).jsonBody())
-    {u'submissions_with_device': 0, u'all_submissions': 0}
-
-But at most one of the parameters distribution, distroseries,
-distroarchseries may be specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url, distroseries=hoary_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url,
-    ...     distroarchseries=hoary_i386_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numSubmissionsWithDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url, distroarchseries=hoary_i386_url))
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-numOwnersOfDevice() returns a dictionary containing the number of
-people which submitted at least one hardware report mentioning the
-given device or driver, and the number of all people who submitted
-a hardware report. The result can be limited to hardware reports for
-the given distribution, distroseries or distroarchseries, if one
-of these parameters is specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     '
-    ...     ).jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice', driver_name='sd').jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-
-A device and a driver can be specified simultaneously.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='sd').jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     driver_name='nonsense').jsonBody())
-    {u'owners': 0, u'all_submitters': 1}
-
-Specifying neiher a device nor a driver leads to an error.
-
-    >>> print(webservice.named_get('/+hwdb', 'numOwnersOfDevice'))
-    HTTP/1.1 400 Bad Request
-    ...
-    Specify (bus, vendor_id, product_id) or driver_name.
-
-Additionally, a package_name can be passed...
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='linux-image-2.6.24-19-generic').jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     package_name='nonsense').jsonBody())
-    {u'owners': 0, u'all_submitters': 1}
-
-...a reference to a distribution...
-
-    >>> ubuntu_url = webservice.getAbsoluteUrl('/ubuntu')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url).jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-    >>> debian_url = webservice.getAbsoluteUrl('/debian')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=debian_url).jsonBody())
-    {u'owners': 0, u'all_submitters': 0}
-
-...a reference to a distroseries...
-
-    >>> hoary_url = webservice.getAbsoluteUrl('/ubuntu/hoary')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url).jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-    >>> warty_url = webservice.getAbsoluteUrl('/ubuntu/warty')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=warty_url).jsonBody())
-    {u'owners': 0, u'all_submitters': 0}
-
-...or a reference to a distroarchseries.
-
-    >>> hoary_i386_url = webservice.getAbsoluteUrl('/ubuntu/hoary/i386')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_i386_url).jsonBody())
-    {u'owners': 1, u'all_submitters': 1}
-    >>> hoary_hppa_url = webservice.getAbsoluteUrl('/ubuntu/hoary/hppa')
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroarchseries=hoary_hppa_url).jsonBody())
-    {u'owners': 0, u'all_submitters': 0}
-
-But at most one of the parameters distribution, distroseries,
-distroarchseries may be specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url, distroseries=hoary_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distribution=ubuntu_url,
-    ...     distroarchseries=hoary_i386_url))
-    ...
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'numOwnersOfDevice',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     distroseries=hoary_url, distroarchseries=hoary_i386_url))
-    HTTP/1.1 400 Bad Request
-    ...
-    Only one of `distribution`, `distroseries` or `distroarchseries` can
-    be present.
-
-
-== Relations between bugs and HWDB submissions ==
-
-We can query which owners of a device are related to a set of bugs. We
-must specify a device's bus, vendor ID and product ID and one or more
-bugs.
-
-By default, only bug reporters are looked up.
-
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1]).jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-We can additionally ask for bugsubscribers...
-
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[9], subscribed_to_bug=True).jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-or users affected by a bug.
-
-    >>> from lp.testing.pages import webservice_for_person
-    >>> from lp.services.webapp.interfaces import OAuthPermission
-    >>> from lp.registry.interfaces.person import IPersonSet
-    >>> login('test@xxxxxxxxxxxxx')
-    >>> sample_person = getUtility(IPersonSet).getByEmail('test@xxxxxxxxxxxxx')
-    >>> logout()
-    >>> sample_person_webservice = webservice_for_person(
-    ...     sample_person, permission=OAuthPermission.WRITE_PUBLIC)
-    >>> bug_15 = webservice.get('/bugs/15').jsonBody()
-    >>> sample_person_webservice.named_post(
-    ...     bug_15['self_link'], 'markUserAffected',
-    ...     affected=True).jsonBody()
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[15], affected_by_bug=True).jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-We can limit the search to a driver...
-
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], driver_name='sd').jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[9], driver_name='nonsense', subscribed_to_bug=True
-    ...     ).jsonBody()['total_size'])
-    0
-
-...or a package name.
-
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[1], package_name='linux-image-2.6.24-19-generic'
-    ...     ).jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', vendor_id='SEAGATE', product_id='ST3250820NS     ',
-    ...     bug_ids=[9], package_name='nonsense', subscribed_to_bug=True
-    ...     ).jsonBody()['total_size'])
-    0
-
-And we can search for people using a given driver and being affected by
-a bug.
-
-    >>> for person in webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     driver_name='sd', bug_ids=[1]).jsonBody()['entries']:
-    ...     print(person['display_name'])
-    Sample Person
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     driver_name='nonsense', bug_ids=[1]).jsonBody()['total_size'])
-    0
-
-A 400 error is returned, if neither a device nor a driver name is specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs', bug_ids=[1]))
-    HTTP/1.1 400 Bad Request
-    ...
-    Specify (bus, vendor_id, product_id) or driver_name.
-
-If one of the parameters bus, vendor_id, product_id is specified, all of
-them have to be specified.
-
-    >>> print(webservice.named_get(
-    ...     '/+hwdb', 'deviceDriverOwnersAffectedByBugs',
-    ...     bus='IDE', bug_ids=[1]))
-    HTTP/1.1 400 Bad Request
-    ...
-    Either specify bus, vendor_id and product_id or none of them.
-
-For a given bug, we can get a list of devices by device owner.
-The result is a tuple of (owner_name, bus_name, vendor_id, product_id).
-
-    >>> for entry in webservice.named_get(
-    ...     '/+hwdb', 'hwInfoByBugRelatedUsers',
-    ...     bug_ids=[1]).jsonBody():
-    ...     print(entry)
-    [u'name12', u'IDE', u'Optiarc', u'DVD RW AD-7170S ']
-    [u'name12', u'IDE', u'SEAGATE', u'ST3250820NS     ']
-    [u'name12', u'PCI', u'0x10de', u'0x0455']
-    [u'name12', u'PCI', u'0x10de', u'0x045d']
-    [u'name12', u'System', u'MSI', u'MS-7369']
-    [u'name12', u'USB', u'0x04b4', u'0x6560']
-    [u'name12', u'USB', u'0x0dda', u'0x2026']
-
-We can also get a list of devices and owners where the owner
-is subscribed to the bug.
-
-    >>> for entry in webservice.named_get(
-    ...     '/+hwdb', 'hwInfoByBugRelatedUsers',
-    ...     bug_ids=[9], subscribed_to_bug=True).jsonBody():
-    ...     print(entry)
-    [u'name12', u'IDE', u'Optiarc', u'DVD RW AD-7170S ']
-    [u'name12', u'IDE', u'SEAGATE', u'ST3250820NS     ']
-    [u'name12', u'PCI', u'0x10de', u'0x0455']
-    [u'name12', u'PCI', u'0x10de', u'0x045d']
-    [u'name12', u'System', u'MSI', u'MS-7369']
-    [u'name12', u'USB', u'0x04b4', u'0x6560']
-    [u'name12', u'USB', u'0x0dda', u'0x2026']
-
-And the same with affected users...
-
-    >>> for entry in webservice.named_get(
-    ...     '/+hwdb', 'hwInfoByBugRelatedUsers',
-    ...     bug_ids=[15], affected_by_bug=True).jsonBody():
-    ...     print(entry)
-    [u'name12', u'IDE', u'Optiarc', u'DVD RW AD-7170S ']
-    [u'name12', u'IDE', u'SEAGATE', u'ST3250820NS     ']
-    [u'name12', u'PCI', u'0x10de', u'0x0455']
-    [u'name12', u'PCI', u'0x10de', u'0x045d']
-    [u'name12', u'System', u'MSI', u'MS-7369']
-    [u'name12', u'USB', u'0x04b4', u'0x6560']
-    [u'name12', u'USB', u'0x0dda', u'0x2026']
diff --git a/lib/lp/hardwaredb/templates/__init__.py b/lib/lp/hardwaredb/templates/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/templates/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/templates/hwdb-fingerprint-submissions.pt b/lib/lp/hardwaredb/templates/hwdb-fingerprint-submissions.pt
deleted file mode 100644
index ae27e45..0000000
--- a/lib/lp/hardwaredb/templates/hwdb-fingerprint-submissions.pt
+++ /dev/null
@@ -1,55 +0,0 @@
-<html
-  xmlns="http://www.w3.org/1999/xhtml";
-  xmlns:tal="http://xml.zope.org/namespaces/tal";
-  xmlns:metal="http://xml.zope.org/namespaces/metal";
-  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
-  metal:use-macro="view/macro:page/main_only"
-  i18n:domain="launchpad"
->
-  <body>
-
-    <div metal:fill-slot="main"
-       tal:define="batchnav view/getAllBatched;
-             batch batchnav/currentBatch">
-      <h1>Submissions for system: <span tal:replace="view/system_name" /></h1>
-      <tal:results condition="batch">
-        <table class="listing">
-          <thead>
-            <tr>
-              <th>Date submitted</th>
-              <th>Submitted by</th>
-              <th>Download</th>
-              <th>Raw</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr tal:repeat="submission batch">
-              <td tal:content="submission/date_submitted/fmt:datetime" />
-              <td tal:define="owner submission/owner;
-                      show_owner python: view.showOwner(submission)">
-                <a tal:condition="show_owner"
-                   tal:content="string: ${owner/displayname} (${owner/name})"
-                   tal:attributes="href owner/fmt:url" />
-                <tal:hide_owner tal:condition="not: show_owner">
-                   n/a
-                </tal:hide_owner>
-              </td>
-              <td>
-                <a tal:attributes="href submission/raw_submission/http_url"
-                   tal:content="submission/raw_submission/filename" />
-              </td>
-              <td>
-                <a tal:attributes="href submission/fmt:url">text</a>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-        <div tal:replace="structure batchnav/@@+navigation-links-lower" />
-      </tal:results>
-
-      <p tal:condition="not: batch">
-        There are no submissions for this system.
-      </p>
-    </div>
-  </body>
-</html>
diff --git a/lib/lp/hardwaredb/templates/person-hwdb-submissions.pt b/lib/lp/hardwaredb/templates/person-hwdb-submissions.pt
deleted file mode 100644
index 2f972ea..0000000
--- a/lib/lp/hardwaredb/templates/person-hwdb-submissions.pt
+++ /dev/null
@@ -1,56 +0,0 @@
-<html
-  xmlns="http://www.w3.org/1999/xhtml";
-  xmlns:tal="http://xml.zope.org/namespaces/tal";
-  xmlns:metal="http://xml.zope.org/namespaces/metal";
-  xmlns:i18n="http://xml.zope.org/namespaces/i18n";
-  metal:use-macro="view/macro:page/main_only"
-  i18n:domain="launchpad"
->
-  <body>
-
-    <div metal:fill-slot="main"
-       tal:define="batchnav view/getAllBatched;
-             batch batchnav/currentBatch;
-             show_private view/userIsOwner">
-      <tal:results condition="batch">
-        <table class="listing">
-          <thead>
-            <tr>
-              <th>System</th>
-              <th>Date submitted</th>
-              <th>Download</th>
-              <th tal:condition="show_private">Private</th>
-              <th>Raw</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr tal:repeat="submission batch">
-              <td>
-                <a tal:attributes="href submission/system_fingerprint/fmt:url"
-                  tal:content="submission/system_fingerprint/fingerprint" />
-              </td>
-              <td tal:content="submission/date_submitted/fmt:datetime" />
-              <td>
-                <a href=""
-                  tal:attributes="href submission/raw_submission/http_url"
-                  tal:content="submission/raw_submission/filename" />
-              </td>
-              <td tal:condition="show_private">
-                <span tal:condition="submission/private">yes</span>
-                <span tal:condition="not:submission/private">no</span>
-              </td>
-              <td>
-                <a tal:attributes="href submission/fmt:url">text</a>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-        <div tal:replace="structure batchnav/@@+navigation-links-lower" />
-      </tal:results>
-      <p tal:condition="not: batch">
-        <span tal:replace="context/fmt:displayname" />
-        has posted no submissions.
-      </p>
-    </div>
-  </body>
-</html>
diff --git a/lib/lp/hardwaredb/tests/__init__.py b/lib/lp/hardwaredb/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/lp/hardwaredb/tests/__init__.py
+++ /dev/null
diff --git a/lib/lp/hardwaredb/tests/real_hwdb_submission.xml.bz2 b/lib/lp/hardwaredb/tests/real_hwdb_submission.xml.bz2
deleted file mode 100644
index c3ecf0f..0000000
Binary files a/lib/lp/hardwaredb/tests/real_hwdb_submission.xml.bz2 and /dev/null differ
diff --git a/lib/lp/hardwaredb/tests/test_doc.py b/lib/lp/hardwaredb/tests/test_doc.py
deleted file mode 100644
index efe77e3..0000000
--- a/lib/lp/hardwaredb/tests/test_doc.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""
-Run the doctests and pagetests.
-"""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-import os
-
-from lp.services.testing import build_test_suite
-from lp.testing.dbuser import switch_dbuser
-from lp.testing.layers import (
-    LaunchpadFunctionalLayer,
-    LaunchpadZopelessLayer,
-    )
-from lp.testing.pages import setUpGlobs
-from lp.testing.systemdocs import (
-    LayeredDocFileSuite,
-    setUp,
-    tearDown,
-    )
-
-
-here = os.path.dirname(os.path.realpath(__file__))
-
-
-def hwdbDeviceTablesSetup(test):
-    setUp(test, future=True)
-    switch_dbuser('hwdb-submission-processor')
-
-
-special = {
-    'hwdb-device-tables.txt': LayeredDocFileSuite(
-        '../doc/hwdb-device-tables.txt',
-        setUp=hwdbDeviceTablesSetup,
-        tearDown=tearDown,
-        layer=LaunchpadZopelessLayer),
-    }
-
-
-def test_suite():
-    return build_test_suite(
-        here, special, layer=LaunchpadFunctionalLayer,
-        setUp=lambda test: setUp(test, future=True),
-        pageTestsSetUp=lambda test: setUpGlobs(test, future=True))
diff --git a/lib/lp/registry/interfaces/role.py b/lib/lp/registry/interfaces/role.py
index e2234a7..92f8872 100644
--- a/lib/lp/registry/interfaces/role.py
+++ b/lib/lp/registry/interfaces/role.py
@@ -85,9 +85,6 @@ class IPersonRoles(Interface):
     in_commercial_admin = Bool(
         title=_("True if this person is a commercial admin."),
         required=True, readonly=True)
-    in_hwdb_team = Bool(
-        title=_("True if this person is on the hwdb team."),
-        required=True, readonly=True)
     in_janitor = Bool(
         title=_("True if this person is the janitor."),
         required=True, readonly=True)
diff --git a/lib/lp/scripts/garbo.py b/lib/lp/scripts/garbo.py
index 6b27f02..09a64ed 100644
--- a/lib/lp/scripts/garbo.py
+++ b/lib/lp/scripts/garbo.py
@@ -72,7 +72,6 @@ from lp.code.model.revision import (
     RevisionAuthor,
     RevisionCache,
     )
-from lp.hardwaredb.model.hwdb import HWSubmission
 from lp.oci.model.ocirecipebuild import OCIFile
 from lp.registry.model.person import Person
 from lp.registry.model.product import Product
@@ -903,67 +902,6 @@ class RevisionAuthorEmailLinker(TunableLoop):
         transaction.commit()
 
 
-class HWSubmissionEmailLinker(TunableLoop):
-    """A TunableLoop that links `HWSubmission` objects to `Person` objects.
-
-    `EmailAddress` objects are looked up for `HWSubmission` objects
-    that have not yet been linked to a `Person`.  If the
-    `EmailAddress` is linked to a person, then the `HWSubmission` is
-    linked to the same.
-    """
-    maximum_chunk_size = 50000
-
-    def __init__(self, log, abort_time=None):
-        super(HWSubmissionEmailLinker, self).__init__(log, abort_time)
-        self.submission_store = IMasterStore(HWSubmission)
-        self.submission_store.execute(
-            "DROP TABLE IF EXISTS NewlyMatchedSubmission")
-        # The join with the Person table is to avoid any replication
-        # lag issues - EmailAddress.person might reference a Person
-        # that does not yet exist.
-        self.submission_store.execute("""
-            CREATE TEMPORARY TABLE NewlyMatchedSubmission AS
-            SELECT
-                HWSubmission.id AS submission,
-                EmailAddress.person AS owner
-            FROM HWSubmission, EmailAddress, Person
-            WHERE HWSubmission.owner IS NULL
-                AND EmailAddress.person = Person.id
-                AND EmailAddress.status IN %s
-                AND lower(HWSubmission.raw_emailaddress)
-                    = lower(EmailAddress.email)
-            """ % sqlvalues(
-                [EmailAddressStatus.VALIDATED, EmailAddressStatus.PREFERRED]),
-            noresult=True)
-        self.submission_store.execute("""
-            CREATE INDEX newlymatchsubmission__submission__idx
-            ON NewlyMatchedSubmission(submission)
-            """, noresult=True)
-        self.matched_submission_count = self.submission_store.execute("""
-            SELECT COUNT(*) FROM NewlyMatchedSubmission
-            """).get_one()[0]
-        self.offset = 0
-
-    def isDone(self):
-        return self.offset >= self.matched_submission_count
-
-    def __call__(self, chunk_size):
-        self.submission_store.execute("""
-            UPDATE HWSubmission
-            SET owner=NewlyMatchedSubmission.owner
-            FROM (
-                SELECT submission, owner
-                FROM NewlyMatchedSubmission
-                ORDER BY submission
-                OFFSET %d
-                LIMIT %d
-                ) AS NewlyMatchedSubmission
-            WHERE HWSubmission.id = NewlyMatchedSubmission.submission
-            """ % (self.offset, chunk_size), noresult=True)
-        self.offset += chunk_size
-        transaction.commit()
-
-
 class PersonPruner(TunableLoop):
 
     maximum_chunk_size = 1000
@@ -1894,7 +1832,6 @@ class DailyDatabaseGarbageCollector(BaseDatabaseGarbageCollector):
         CodeImportResultPruner,
         DiffPruner,
         GitJobPruner,
-        HWSubmissionEmailLinker,
         LiveFSFilePruner,
         LoginTokenPruner,
         OCIFilePruner,
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 405d03f..2fbc80d 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -713,37 +713,6 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
         switch_dbuser('testadmin')
         self.assertEqual(rev2.revision_author.person, person2)
 
-    def test_HWSubmissionEmailLinker(self):
-        switch_dbuser('testadmin')
-        sub1 = self.factory.makeHWSubmission(
-            emailaddress='author-1@xxxxxxxxxxx')
-        sub2 = self.factory.makeHWSubmission(
-            emailaddress='author-2@xxxxxxxxxxx')
-
-        person1 = self.factory.makePerson(email='Author-1@xxxxxxxxxxx')
-        person2 = self.factory.makePerson(
-            email='Author-2@xxxxxxxxxxx',
-            email_address_status=EmailAddressStatus.NEW)
-
-        self.assertEqual(sub1.owner, None)
-        self.assertEqual(sub2.owner, None)
-
-        self.runDaily()
-
-        # Only the validated email address associated with a Person
-        # causes a linkage.
-        switch_dbuser('testadmin')
-        self.assertEqual(sub1.owner, person1)
-        self.assertEqual(sub2.owner, None)
-
-        # Validating an email address creates a linkage.
-        person2.validateAndEnsurePreferredEmail(person2.guessedemails[0])
-        self.assertEqual(sub2.owner, None)
-
-        self.runDaily()
-        switch_dbuser('testadmin')
-        self.assertEqual(sub2.owner, person2)
-
     def test_PersonPruner(self):
         personset = getUtility(IPersonSet)
         # Switch the DB user because the garbo_daily user isn't allowed to
diff --git a/lib/lp/security.py b/lib/lp/security.py
index 599b717..243c1a8 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -101,17 +101,6 @@ from lp.code.interfaces.sourcepackagerecipe import ISourcePackageRecipe
 from lp.code.interfaces.sourcepackagerecipebuild import (
     ISourcePackageRecipeBuild,
     )
-from lp.hardwaredb.interfaces.hwdb import (
-    IHWDBApplication,
-    IHWDevice,
-    IHWDeviceClass,
-    IHWDriver,
-    IHWDriverName,
-    IHWDriverPackageName,
-    IHWSubmission,
-    IHWSubmissionDevice,
-    IHWVendorID,
-    )
 from lp.oci.interfaces.ocipushrule import IOCIPushRule
 from lp.oci.interfaces.ocirecipe import (
     IOCIRecipe,
@@ -2680,77 +2669,6 @@ class AdminLanguagePack(OnlyRosettaExpertsAndAdmins):
     usedfor = ILanguagePack
 
 
-class ViewHWSubmission(AuthorizationBase):
-    permission = 'launchpad.View'
-    usedfor = IHWSubmission
-
-    def checkAuthenticated(self, user):
-        """Can the user view the submission details?
-
-        Submissions that are not marked private are publicly visible,
-        private submissions may only be accessed by their owner and by
-        admins.
-        """
-        if not self.obj.private:
-            return True
-
-        return user.inTeam(self.obj.owner) or user.in_admin
-
-    def checkUnauthenticated(self):
-        return not self.obj.private
-
-
-class EditHWSubmission(AdminByAdminsTeam):
-    permission = 'launchpad.Edit'
-    usedfor = IHWSubmission
-
-
-class ViewHWDBBase(AuthorizationBase):
-    """Base class to restrict access to HWDB data to members of the HWDB team.
-    """
-    permission = 'launchpad.View'
-
-    def checkAuthenticated(self, user):
-        """We give for now access only to Canonical employees."""
-        return user.in_hwdb_team
-
-    def checkUnauthenticated(self):
-        """No access for anonymous users."""
-        return False
-
-
-class ViewHWDriver(ViewHWDBBase):
-    usedfor = IHWDriver
-
-
-class ViewHWDriverName(ViewHWDBBase):
-    usedfor = IHWDriverName
-
-
-class ViewHWDriverPackageName(ViewHWDBBase):
-    usedfor = IHWDriverPackageName
-
-
-class ViewHWVendorID(ViewHWDBBase):
-    usedfor = IHWVendorID
-
-
-class ViewHWDevice(ViewHWDBBase):
-    usedfor = IHWDevice
-
-
-class ViewHWSubmissionDevice(ViewHWDBBase):
-    usedfor = IHWSubmissionDevice
-
-
-class ViewHWDBApplication(ViewHWDBBase):
-    usedfor = IHWDBApplication
-
-
-class ViewHWDeviceClass(ViewHWDBBase):
-    usedfor = IHWDeviceClass
-
-
 class ViewArchive(AuthorizationBase):
     """Restrict viewing of private archives.
 
diff --git a/lib/lp/services/webservice/wadl-to-refhtml.xsl b/lib/lp/services/webservice/wadl-to-refhtml.xsl
index 86d8467..b3dc3e6 100644
--- a/lib/lp/services/webservice/wadl-to-refhtml.xsl
+++ b/lib/lp/services/webservice/wadl-to-refhtml.xsl
@@ -382,41 +382,6 @@
                 <xsl:text>/+gpg-keys/</xsl:text>
                 <var>&lt;fingerprint&gt;</var>
             </xsl:when>
-            <xsl:when test="@id = 'hwdb'">
-                <xsl:text>/+hwdb</xsl:text>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_device'">
-                <xsl:text>/+hwdb/+device/</xsl:text>
-                <var>&lt;id&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_device_class'">
-                <xsl:text>/+hwdb/+deviceclass/</xsl:text>
-                <var>&lt;id&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_driver'">
-                <xsl:text>/+hwdb/+driver/</xsl:text>
-                <var>&lt;id&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_submission'">
-                <xsl:text>/+hwdb/+submission/</xsl:text>
-                <var>&lt;submission-key&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_submission_device'">
-                <xsl:text>/+hwdb/+submissiondevice/</xsl:text>
-                <var>&lt;id&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_vendor_i_d'">
-                <xsl:text>/+hwdb/+hwvendorid/</xsl:text>
-                <var>&lt;id&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_driver_name'">
-                <xsl:text>/+hwdb/+drivername/</xsl:text>
-                <var>&lt;name&gt;</var>
-            </xsl:when>
-            <xsl:when test="@id = 'h_w_driver_package_name'">
-                <xsl:text>/+hwdb/+driverpackagename/</xsl:text>
-                <var>&lt;package_name&gt;</var>
-            </xsl:when>
             <xsl:when test="@id = 'jabber_id'">
                 <xsl:text>/</xsl:text>
                 <var>&lt;person.name&gt;</var>
@@ -767,9 +732,6 @@
     <xsl:template name="find-root-object-uri">
         <xsl:value-of select="$base"/>
         <xsl:choose>
-            <xsl:when test="@id = 'hwdb'">
-                <xsl:text>/+hwdb</xsl:text>
-            </xsl:when>
             <xsl:when test="@id = 'snap_bases'">
                 <xsl:text>/+snap-bases</xsl:text>
             </xsl:when>
@@ -802,14 +764,13 @@
 
                              https://api.launchpad.net/beta/bugs
 
-                         while the HWDB application root's URL is
+                         while some objects have URLs more like
 
-                            https://api.launchpad.net/beta/+hwdb
+                            https://api.launchpad.net/beta/+snaps
 
-                         In other words, the URL for the HWDB application
-                         root needs to be mangled in a form similar to
-                         that used for non-root objects in the template
-                         "find-entry-uri".
+                         In other words, some root object URLs need to be
+                         mangled in a form similar to that used for non-root
+                         objects in the template "find-entry-uri".
                     -->
 
                     <xsl:call-template name="find-root-object-uri"/>
diff --git a/lib/lp/systemhomes.py b/lib/lp/systemhomes.py
index 9154fb9..f886587 100644
--- a/lib/lp/systemhomes.py
+++ b/lib/lp/systemhomes.py
@@ -20,7 +20,6 @@ import codecs
 import os
 
 from lazr.restful import ServiceRootResource
-from lazr.restful.interfaces import ITopLevelEntryLink
 from storm.expr import Max
 from zope.component import getUtility
 from zope.interface import implementer
@@ -49,15 +48,6 @@ from lp.code.interfaces.codeimportscheduler import (
     ICodeImportSchedulerApplication,
     )
 from lp.code.interfaces.gitapi import IGitApplication
-from lp.hardwaredb.interfaces.hwdb import (
-    IHWDBApplication,
-    IHWDeviceSet,
-    IHWDriverSet,
-    IHWSubmissionDeviceSet,
-    IHWSubmissionSet,
-    IHWVendorIDSet,
-    ParameterError,
-    )
 from lp.registry.interfaces.distroseries import IDistroSeriesSet
 from lp.registry.interfaces.mailinglist import IMailingListApplication
 from lp.registry.interfaces.product import IProductSet
@@ -283,119 +273,6 @@ class RosettaApplication:
         return stats.value('translator_count')
 
 
-@implementer(IHWDBApplication, ITopLevelEntryLink)
-class HWDBApplication:
-    """See `IHWDBApplication`."""
-
-    link_name = 'hwdb'
-    entry_type = IHWDBApplication
-
-    def devices(self, bus, vendor_id, product_id=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWDeviceSet).search(bus, vendor_id, product_id)
-
-    def drivers(self, package_name=None, name=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWDriverSet).search(package_name, name)
-
-    def vendorIDs(self, bus):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWVendorIDSet).idsForBus(bus)
-
-    @property
-    def driver_names(self):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWDriverSet).all_driver_names()
-
-    @property
-    def package_names(self):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWDriverSet).all_package_names()
-
-    def search(self, user=None, device=None, driver=None, distribution=None,
-               distroseries=None, architecture=None, owner=None,
-               created_before=None, created_after=None,
-               submitted_before=None, submitted_after=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWSubmissionSet).search(
-            user=user, device=device, driver=driver,
-            distribution=distribution, distroseries=distroseries,
-            architecture=architecture, owner=owner,
-            created_before=created_before, created_after=created_after,
-            submitted_before=submitted_before,
-            submitted_after=submitted_after)
-
-    def getDistroTarget(self, distribution, distroseries, distroarchseries):
-        distro_targets = [
-            target for target in (
-                distribution, distroseries, distroarchseries)
-            if target is not None]
-        if len(distro_targets) == 0:
-            return None
-        elif len(distro_targets) == 1:
-            return distro_targets[0]
-        else:
-            raise ParameterError(
-                'Only one of `distribution`, `distroseries` or '
-                '`distroarchseries` can be present.')
-
-    def numSubmissionsWithDevice(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """See `IHWDBApplication`."""
-        submissions_with_device, all_submissions = (
-            getUtility(IHWSubmissionSet).numSubmissionsWithDevice(
-                bus, vendor_id, product_id, driver_name, package_name,
-                distro_target=self.getDistroTarget(
-                    distribution, distroseries, distroarchseries)))
-        return {
-            'submissions_with_device': submissions_with_device,
-            'all_submissions': all_submissions,
-            }
-
-    def numOwnersOfDevice(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """See `IHWDBApplication`."""
-        owners, all_submitters = (
-            getUtility(IHWSubmissionSet).numOwnersOfDevice(
-                bus, vendor_id, product_id, driver_name, package_name,
-                distro_target=self.getDistroTarget(
-                    distribution, distroseries, distroarchseries)))
-        return {
-            'owners': owners,
-            'all_submitters': all_submitters,
-            }
-
-    def numDevicesInSubmissions(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, distribution=None, distroseries=None,
-        distroarchseries=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWSubmissionDeviceSet).numDevicesInSubmissions(
-                bus, vendor_id, product_id, driver_name, package_name,
-                distro_target=self.getDistroTarget(
-                    distribution, distroseries, distroarchseries))
-
-    def deviceDriverOwnersAffectedByBugs(
-        self, bus=None, vendor_id=None, product_id=None, driver_name=None,
-        package_name=None, bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWSubmissionSet).deviceDriverOwnersAffectedByBugs(
-            bus, vendor_id, product_id, driver_name, package_name, bug_ids,
-            bug_tags, affected_by_bug, subscribed_to_bug, user)
-
-    def hwInfoByBugRelatedUsers(
-        self, bug_ids=None, bug_tags=None, affected_by_bug=False,
-        subscribed_to_bug=False, user=None):
-        """See `IHWDBApplication`."""
-        return getUtility(IHWSubmissionSet).hwInfoByBugRelatedUsers(
-            bug_ids, bug_tags, affected_by_bug, subscribed_to_bug, user)
-
-
 @implementer(IWebServiceApplication, ICanonicalUrlData)
 class WebServiceApplication(ServiceRootResource):
     """See `IWebServiceApplication`.
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index e8fd8ed..f5ad237 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -150,12 +150,6 @@ from lp.code.model.diff import (
     Diff,
     PreviewDiff,
     )
-from lp.hardwaredb.interfaces.hwdb import (
-    HWSubmissionFormat,
-    IHWDeviceDriverLinkSet,
-    IHWSubmissionDeviceSet,
-    IHWSubmissionSet,
-    )
 from lp.oci.interfaces.ocipushrule import IOCIPushRuleSet
 from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
@@ -4376,49 +4370,6 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                 msg.attach(attachment)
         return msg
 
-    def makeHWSubmission(self, date_created=None, submission_key=None,
-                         emailaddress=u'test@xxxxxxxxxxxxx',
-                         distroarchseries=None, private=False,
-                         contactable=False, system=None,
-                         submission_data=None, status=None):
-        """Create a new HWSubmission."""
-        if date_created is None:
-            date_created = datetime.now(pytz.UTC)
-        if submission_key is None:
-            submission_key = self.getUniqueString('submission-key')
-        if distroarchseries is None:
-            distroarchseries = self.makeDistroArchSeries()
-        if system is None:
-            system = self.getUniqueString('system-fingerprint')
-        if submission_data is None:
-            sample_data_path = os.path.join(
-                config.root, 'lib', 'lp', 'hardwaredb', 'scripts',
-                'tests', 'simple_valid_hwdb_submission.xml')
-            submission_data = open(sample_data_path).read()
-        filename = self.getUniqueString('submission-file')
-        filesize = len(submission_data)
-        raw_submission = StringIO(submission_data)
-        format = HWSubmissionFormat.VERSION_1
-        submission_set = getUtility(IHWSubmissionSet)
-
-        submission = submission_set.createSubmission(
-            date_created, format, private, contactable,
-            submission_key, emailaddress, distroarchseries,
-            raw_submission, filename, filesize, system)
-
-        if status is not None:
-            removeSecurityProxy(submission).status = status
-        return submission
-
-    def makeHWSubmissionDevice(self, submission, device, driver, parent,
-                               hal_device_id):
-        """Create a new HWSubmissionDevice."""
-        device_driver_link_set = getUtility(IHWDeviceDriverLinkSet)
-        device_driver_link = device_driver_link_set.getOrCreate(
-            device, driver)
-        return getUtility(IHWSubmissionDeviceSet).create(
-            device_driver_link, submission, parent, hal_device_id)
-
     def makeSSHKeyText(self, key_type="ssh-rsa", comment=None):
         """Create new SSH public key text.
 
diff --git a/utilities/snakefood/lp-sfood-packages b/utilities/snakefood/lp-sfood-packages
index a29d11c..b21a1ba 100644
--- a/utilities/snakefood/lp-sfood-packages
+++ b/utilities/snakefood/lp-sfood-packages
@@ -6,7 +6,6 @@ lp/snappy
 lp/services
 lp/scripts
 lp/registry
-lp/hardwaredb
 lp/coop/answersbugs
 lp/codehosting
 lp/code