Restructure reclass into a Python module

By moving all code into ./reclass/, reclass can now be used as a Python
module. The Ansible adapter and the CLI have been updated accordingly.

Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/adapters/ansible.in b/adapters/ansible.in
index 53b8f52..d225c9e 100644
--- a/adapters/ansible.in
+++ b/adapters/ansible.in
@@ -6,86 +6,5 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-import os, sys, posix, stat
-
-def exit_error(msg, rc):
-    print >>sys.stderr, msg
-    sys.exit(rc)
-
-if len(sys.argv) == 1:
-    exit_error('Need to specify --list or --host.', posix.EX_USAGE)
-
-ansible_dir = os.path.dirname(sys.argv[0])
-
-# In order to be able to use reclass as modules, manipulate the search path,
-# starting from the location of the adapter. Realpath will make sure that
-# symlinks are resolved.
-realpath = os.path.realpath(sys.argv[0] + '/../../')
-sys.path.insert(0, realpath)
-import reclass, config, errors
-
-# The adapter resides in the Ansible directory, so let's look there for an
-# optional configuration file called reclass-config.yml.
-options = {'output':'json', 'pretty_print':True}
-config_path = os.path.join(ansible_dir, 'reclass-config.yml')
-if os.path.exists(config_path) and os.access(config_path, os.R_OK):
-    options.update(config.read_config_file(config_path))
-
-# Massage options into shape
-if 'storage_type' not in options:
-    options['storage_type'] = 'yaml_fs'
-
-if 'nodes_uri' not in options:
-    nodes_uri = os.path.join(ansible_dir, 'nodes')
-    if stat.S_ISDIR(os.stat(nodes_uri).st_mode):
-        options['nodes_uri'] = nodes_uri
-    else:
-        exit_error('nodes_uri not specified', posix.EX_USAGE)
-
-if 'classes_uri' not in options:
-    classes_uri = os.path.join(ansible_dir, 'classes')
-    if not stat.S_ISDIR(os.stat(classes_uri).st_mode):
-        classes_uri = options['nodes_uri']
-    options['classes_uri'] = classes_uri
-
-if 'applications_postfix' not in options:
-    options['applications_postfix'] = '_hosts'
-
-# Invoke reclass according to what Ansible wants.
-# If the 'node' option is set, we want node information. If the option is
-# False instead, we print the inventory. Yeah for option abuse!
-if sys.argv[1] == '--list':
-    if len(sys.argv) > 2:
-        exit_error('Unknown arguments: ' + ' '.join(sys.argv[2:]),
-                    posix.EX_USAGE)
-    options['node'] = False
-
-elif sys.argv[1] == '--host':
-    if len(sys.argv) < 3:
-        exit_error('Missing hostname.', posix.EX_USAGE)
-    elif len(sys.argv) > 3:
-        exit_error('Unknown arguments: ' + ' '.join(sys.argv[3:]),
-                    posix.EX_USAGE)
-    options['node'] = sys.argv[2]
-
-else:
-    exit_error('Unknown mode (--list or --host required).', posix.EX_USAGE)
-
-try:
-    data = reclass.get_data(options['storage_type'], options['nodes_uri'],
-                            options['classes_uri'],
-                            options['applications_postfix'], options['node'])
-
-    if options['node']:
-        # Massage and shift the data like Ansible wants it
-        data['parameters']['RECLASS'] = data['RECLASS']
-        for i in ('classes', 'applications'):
-            data['parameters']['RECLASS'][i] = data[i]
-        data = data['parameters']
-
-    print reclass.output(data, options['output'], options['pretty_print'])
-
-    sys.exit(posix.EX_OK)
-
-except errors.ReclassException, e:
-    exit_error(e.message, e.rc)
+from reclass.adapters.ansible import ansible_adapter
+ansible_adapter()
diff --git a/reclass.py.in b/reclass.py.in
index dbb916f..061aa60 100644
--- a/reclass.py.in
+++ b/reclass.py.in
@@ -6,21 +6,21 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-__prog__ = 'reclass'
+__name__ = 'reclass'
 __description__ = 'classify nodes based on an external data source'
 __version__ = '1.0'
 __author__ = 'martin f. krafft <madduck@madduck.net>'
 __copyright__ = 'Copyright © 2007–13 ' + __author__
 __licence__ = 'Artistic Licence 2.0'
 
-import sys, os, posix, time
-import config
-from output import OutputLoader
-from storage import StorageBackendLoader
-import errors
+import sys, os, posix
+import reclass.config
+from reclass.output import OutputLoader
+from reclass.storage import StorageBackendLoader
+import reclass.errors
 
 def get_options(config_file=None):
-    return config.get_options(__name__, __version__, __description__, config_file)
+    return reclass.config.get_options(__name__, __version__, __description__, config_file)
 
 def get_data(storage_type, nodes_uri, classes_uri, applications_postfix, node):
     storage_class = StorageBackendLoader(storage_type).load()
@@ -52,13 +52,13 @@
             break
     try:
         options = get_options(config_file)
-        nodes_uri, classes_uri = config.path_mangler(options.inventory_base_uri,
-                                                     options.nodes_uri,
-                                                     options.classes_uri)
+        nodes_uri, classes_uri = reclass.config.path_mangler(options.inventory_base_uri,
+                                                             options.nodes_uri,
+                                                             options.classes_uri)
         data = get_data(options.storage_type, nodes_uri, classes_uri,
                         options.applications_postfix, options.node)
         print output(data, options.output, options.pretty_print)
         sys.exit(posix.EX_OK)
 
-    except errors.ReclassException, e:
+    except reclass.errors.ReclassException, e:
         _error(e.message, e.rc)
diff --git a/reclass/__init__.py b/reclass/__init__.py
new file mode 100644
index 0000000..a2d9f97
--- /dev/null
+++ b/reclass/__init__.py
@@ -0,0 +1,32 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+
+import time
+import config
+from output import OutputLoader
+from storage import StorageBackendLoader
+
+def get_options(config_file=None):
+    return config.get_options(__name__, __version__, __description__, config_file)
+
+def get_data(storage_type, nodes_uri, classes_uri, applications_postfix, node):
+    storage_class = StorageBackendLoader(storage_type).load()
+    storage = storage_class(nodes_uri, classes_uri, applications_postfix)
+    if node is False:
+        ret = storage.inventory()
+    else:
+        ret = storage.nodeinfo(node)
+        ret['RECLASS']['timestamp'] = time.strftime('%c')
+
+    return ret
+
+def output(data, fmt, pretty_print=False):
+    output_class = OutputLoader(fmt).load()
+    outputter = output_class()
+    return outputter.dump(data, pretty_print=pretty_print)
diff --git a/mergers/__init__.py b/reclass/adapters/__init__.py
similarity index 99%
rename from mergers/__init__.py
rename to reclass/adapters/__init__.py
index ada8bd8..6329757 100644
--- a/mergers/__init__.py
+++ b/reclass/adapters/__init__.py
@@ -6,4 +6,3 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-
diff --git a/reclass/adapters/ansible.py b/reclass/adapters/ansible.py
new file mode 100644
index 0000000..3bb6d4e
--- /dev/null
+++ b/reclass/adapters/ansible.py
@@ -0,0 +1,94 @@
+#
+# -*- coding: utf-8 -*-
+#
+# This file is part of reclass (http://github.com/madduck/reclass)
+#
+# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+import os, sys, posix, stat
+
+def exit_error(msg, rc):
+    print >>sys.stderr, msg
+    sys.exit(rc)
+
+def ansible_adapter():
+    if len(sys.argv) == 1:
+        exit_error('Need to specify --list or --host.', posix.EX_USAGE)
+
+    ansible_dir = os.path.dirname(sys.argv[0])
+
+    # In order to be able to use reclass as modules, manipulate the search path,
+    # starting from the location of the adapter. Realpath will make sure that
+    # symlinks are resolved.
+    realpath = os.path.realpath(sys.argv[0] + '/../../')
+    sys.path.insert(0, realpath)
+    import reclass.config
+    import reclass.errors
+    from reclass import get_data, output
+
+    # The adapter resides in the Ansible directory, so let's look there for an
+    # optional configuration file called reclass-config.yml.
+    options = {'output':'json', 'pretty_print':True}
+    config_path = os.path.join(ansible_dir, 'reclass-config.yml')
+    if os.path.exists(config_path) and os.access(config_path, os.R_OK):
+        options.update(reclass.config.read_config_file(config_path))
+
+    # Massage options into shape
+    if 'storage_type' not in options:
+        options['storage_type'] = 'yaml_fs'
+
+    if 'nodes_uri' not in options:
+        nodes_uri = os.path.join(ansible_dir, 'nodes')
+        if stat.S_ISDIR(os.stat(nodes_uri).st_mode):
+            options['nodes_uri'] = nodes_uri
+        else:
+            exit_error('nodes_uri not specified', posix.EX_USAGE)
+
+    if 'classes_uri' not in options:
+        classes_uri = os.path.join(ansible_dir, 'classes')
+        if not stat.S_ISDIR(os.stat(classes_uri).st_mode):
+            classes_uri = options['nodes_uri']
+        options['classes_uri'] = classes_uri
+
+    if 'applications_postfix' not in options:
+        options['applications_postfix'] = '_hosts'
+
+    # Invoke reclass according to what Ansible wants.
+    # If the 'node' option is set, we want node information. If the option is
+    # False instead, we print the inventory. Yeah for option abuse!
+    if sys.argv[1] == '--list':
+        if len(sys.argv) > 2:
+            exit_error('Unknown arguments: ' + ' '.join(sys.argv[2:]),
+                        posix.EX_USAGE)
+        options['node'] = False
+
+    elif sys.argv[1] == '--host':
+        if len(sys.argv) < 3:
+            exit_error('Missing hostname.', posix.EX_USAGE)
+        elif len(sys.argv) > 3:
+            exit_error('Unknown arguments: ' + ' '.join(sys.argv[3:]),
+                        posix.EX_USAGE)
+        options['node'] = sys.argv[2]
+
+    else:
+        exit_error('Unknown mode (--list or --host required).', posix.EX_USAGE)
+
+    try:
+        data = get_data(options['storage_type'], options['nodes_uri'],
+                        options['classes_uri'], options['applications_postfix'],
+                        options['node'])
+
+        if options['node']:
+            # Massage and shift the data like Ansible wants it
+            data['parameters']['RECLASS'] = data['RECLASS']
+            for i in ('classes', 'applications'):
+                data['parameters']['RECLASS'][i] = data[i]
+            data = data['parameters']
+
+        print output(data, options['output'], options['pretty_print'])
+
+        sys.exit(posix.EX_OK)
+
+    except reclass.errors.ReclassException, e:
+        exit_error(e.message, e.rc)
diff --git a/config.py b/reclass/config.py
similarity index 100%
rename from config.py
rename to reclass/config.py
diff --git a/datatypes/__init__.py b/reclass/datatypes/__init__.py
similarity index 100%
rename from datatypes/__init__.py
rename to reclass/datatypes/__init__.py
diff --git a/datatypes/applications.py b/reclass/datatypes/applications.py
similarity index 95%
rename from datatypes/applications.py
rename to reclass/datatypes/applications.py
index 0a8f402..92411b6 100644
--- a/datatypes/applications.py
+++ b/reclass/datatypes/applications.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.list import SetExtend
+from reclass.mergers.list import SetExtend
 
 class ApplicationsMerger(SetExtend):
 
diff --git a/datatypes/classes.py b/reclass/datatypes/classes.py
similarity index 92%
rename from datatypes/classes.py
rename to reclass/datatypes/classes.py
index facaf93..e750339 100644
--- a/datatypes/classes.py
+++ b/reclass/datatypes/classes.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.list import SetExtend
+from reclass.mergers.list import SetExtend
 
 class Classes(list):
 
diff --git a/datatypes/entity.py b/reclass/datatypes/entity.py
similarity index 100%
rename from datatypes/entity.py
rename to reclass/datatypes/entity.py
diff --git a/datatypes/parameters.py b/reclass/datatypes/parameters.py
similarity index 90%
rename from datatypes/parameters.py
rename to reclass/datatypes/parameters.py
index d5c25d6..43dcb02 100644
--- a/datatypes/parameters.py
+++ b/reclass/datatypes/parameters.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.dict import DictRecursivePolicyUpdate
+from reclass.mergers.dict import DictRecursivePolicyUpdate
 
 class Parameters(dict):
 
diff --git a/datatypes/tests/test_applications.py b/reclass/datatypes/tests/test_applications.py
similarity index 97%
rename from datatypes/tests/test_applications.py
rename to reclass/datatypes/tests/test_applications.py
index b18c07e..f7a7254 100644
--- a/datatypes/tests/test_applications.py
+++ b/reclass/datatypes/tests/test_applications.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from datatypes import Applications
+from reclass.datatypes import Applications
 
 class TestApplications:
 
diff --git a/datatypes/tests/test_classes.py b/reclass/datatypes/tests/test_classes.py
similarity index 95%
rename from datatypes/tests/test_classes.py
rename to reclass/datatypes/tests/test_classes.py
index deca77b..b095839 100644
--- a/datatypes/tests/test_classes.py
+++ b/reclass/datatypes/tests/test_classes.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from datatypes import Classes
+from reclass.datatypes import Classes
 
 class TestClasses:
 
diff --git a/datatypes/tests/test_entity.py b/reclass/datatypes/tests/test_entity.py
similarity index 91%
rename from datatypes/tests/test_entity.py
rename to reclass/datatypes/tests/test_entity.py
index c9e11ab..7e7f3b7 100644
--- a/datatypes/tests/test_entity.py
+++ b/reclass/datatypes/tests/test_entity.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from datatypes import Entity, Classes, Parameters, Applications
+from reclass.datatypes import Entity, Classes, Parameters, Applications
 
 class TestEntity:
 
diff --git a/datatypes/tests/test_parameters.py b/reclass/datatypes/tests/test_parameters.py
similarity index 92%
rename from datatypes/tests/test_parameters.py
rename to reclass/datatypes/tests/test_parameters.py
index e6efbb8..504b2e7 100644
--- a/datatypes/tests/test_parameters.py
+++ b/reclass/datatypes/tests/test_parameters.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from datatypes import Parameters
+from reclass.datatypes import Parameters
 
 class TestParameters:
 
diff --git a/errors.py b/reclass/errors.py
similarity index 100%
rename from errors.py
rename to reclass/errors.py
diff --git a/mergers/__init__.py b/reclass/mergers/__init__.py
similarity index 99%
copy from mergers/__init__.py
copy to reclass/mergers/__init__.py
index ada8bd8..6329757 100644
--- a/mergers/__init__.py
+++ b/reclass/mergers/__init__.py
@@ -6,4 +6,3 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-
diff --git a/mergers/base.py b/reclass/mergers/base.py
similarity index 100%
rename from mergers/base.py
rename to reclass/mergers/base.py
diff --git a/mergers/dict/__init__.py b/reclass/mergers/dict/__init__.py
similarity index 100%
rename from mergers/dict/__init__.py
rename to reclass/mergers/dict/__init__.py
diff --git a/mergers/dict/base.py b/reclass/mergers/dict/base.py
similarity index 85%
rename from mergers/dict/base.py
rename to reclass/mergers/dict/base.py
index 92fdd1b..ddf764d 100644
--- a/mergers/dict/base.py
+++ b/reclass/mergers/dict/base.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.base import BaseMerger
+from reclass.mergers.base import BaseMerger
 
 class BaseDictMerger(BaseMerger):
     pass
diff --git a/mergers/dict/recursive_policy_update.py b/reclass/mergers/dict/recursive_policy_update.py
similarity index 100%
rename from mergers/dict/recursive_policy_update.py
rename to reclass/mergers/dict/recursive_policy_update.py
diff --git a/mergers/dict/recursive_update.py b/reclass/mergers/dict/recursive_update.py
similarity index 100%
rename from mergers/dict/recursive_update.py
rename to reclass/mergers/dict/recursive_update.py
diff --git a/mergers/dict/tests/test_recursive_policy_update.py b/reclass/mergers/dict/tests/test_recursive_policy_update.py
similarity index 93%
rename from mergers/dict/tests/test_recursive_policy_update.py
rename to reclass/mergers/dict/tests/test_recursive_policy_update.py
index 4b9486b..e9e1f31 100644
--- a/mergers/dict/tests/test_recursive_policy_update.py
+++ b/reclass/mergers/dict/tests/test_recursive_policy_update.py
@@ -7,7 +7,7 @@
 # Released under the terms of the Artistic Licence 2.0
 #
 from test_recursive_update import TestDictRecursiveUpdate
-from mergers.dict import DictRecursivePolicyUpdate
+from reclass.mergers.dict import DictRecursivePolicyUpdate
 
 class TestDictRecursivePolicyUpdate(TestDictRecursiveUpdate):
 
diff --git a/mergers/dict/tests/test_recursive_update.py b/reclass/mergers/dict/tests/test_recursive_update.py
similarity index 96%
rename from mergers/dict/tests/test_recursive_update.py
rename to reclass/mergers/dict/tests/test_recursive_update.py
index 4410b68..12d2348 100644
--- a/mergers/dict/tests/test_recursive_update.py
+++ b/reclass/mergers/dict/tests/test_recursive_update.py
@@ -7,7 +7,7 @@
 # Released under the terms of the Artistic Licence 2.0
 #
 from test_update import TestDictUpdate
-from mergers.dict import DictRecursiveUpdate
+from reclass.mergers.dict import DictRecursiveUpdate
 
 class TestDictRecursiveUpdate(TestDictUpdate):
 
diff --git a/mergers/dict/tests/test_update.py b/reclass/mergers/dict/tests/test_update.py
similarity index 93%
rename from mergers/dict/tests/test_update.py
rename to reclass/mergers/dict/tests/test_update.py
index 02702f7..3dcbfd9 100644
--- a/mergers/dict/tests/test_update.py
+++ b/reclass/mergers/dict/tests/test_update.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.dict import DictUpdate
+from reclass.mergers.dict import DictUpdate
 
 class TestDictUpdate:
 
diff --git a/mergers/dict/update.py b/reclass/mergers/dict/update.py
similarity index 100%
rename from mergers/dict/update.py
rename to reclass/mergers/dict/update.py
diff --git a/mergers/list/__init__.py b/reclass/mergers/list/__init__.py
similarity index 100%
rename from mergers/list/__init__.py
rename to reclass/mergers/list/__init__.py
diff --git a/mergers/list/base.py b/reclass/mergers/list/base.py
similarity index 92%
rename from mergers/list/base.py
rename to reclass/mergers/list/base.py
index ea73cdd..9b2388c 100644
--- a/mergers/list/base.py
+++ b/reclass/mergers/list/base.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.base import BaseMerger
+from reclass.mergers.base import BaseMerger
 
 class BaseListMerger(BaseMerger):
 
diff --git a/mergers/list/extend.py b/reclass/mergers/list/extend.py
similarity index 100%
rename from mergers/list/extend.py
rename to reclass/mergers/list/extend.py
diff --git a/mergers/list/set.py b/reclass/mergers/list/set.py
similarity index 100%
rename from mergers/list/set.py
rename to reclass/mergers/list/set.py
diff --git a/mergers/list/tests/test_extend.py b/reclass/mergers/list/tests/test_extend.py
similarity index 97%
rename from mergers/list/tests/test_extend.py
rename to reclass/mergers/list/tests/test_extend.py
index 7484e8e..e65f77a 100644
--- a/mergers/list/tests/test_extend.py
+++ b/reclass/mergers/list/tests/test_extend.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from mergers.list import ListExtend
+from reclass.mergers.list import ListExtend
 
 class TestListExtend:
 
diff --git a/mergers/list/tests/test_set.py b/reclass/mergers/list/tests/test_set.py
similarity index 94%
rename from mergers/list/tests/test_set.py
rename to reclass/mergers/list/tests/test_set.py
index fa0e118..0dc965a 100644
--- a/mergers/list/tests/test_set.py
+++ b/reclass/mergers/list/tests/test_set.py
@@ -7,7 +7,7 @@
 # Released under the terms of the Artistic Licence 2.0
 #
 from test_extend import TestListExtend
-from mergers.list import SetExtend
+from reclass.mergers.list import SetExtend
 
 class TestSetExtend(TestListExtend):
 
diff --git a/output/__init__.py b/reclass/output/__init__.py
similarity index 89%
rename from output/__init__.py
rename to reclass/output/__init__.py
index 75f6f4a..8b3326a 100644
--- a/output/__init__.py
+++ b/reclass/output/__init__.py
@@ -18,10 +18,9 @@
 class OutputLoader(object):
 
     def __init__(self, outputter):
-        self._name = outputter + '_outputter'
+        self._name = 'reclass.output.' + outputter + '_outputter'
         try:
-            self._module = __import__(self._name, globals(), locals(),
-                                      self._name)
+            self._module = __import__(self._name, globals(), locals(), self._name)
         except ImportError:
             raise NotImplementedError
 
diff --git a/output/json_outputter.py b/reclass/output/json_outputter.py
similarity index 91%
rename from output/json_outputter.py
rename to reclass/output/json_outputter.py
index 25fa8bc..9ac6222 100644
--- a/output/json_outputter.py
+++ b/reclass/output/json_outputter.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from output import OutputterBase
+from reclass.output import OutputterBase
 import json
 
 class Outputter(OutputterBase):
diff --git a/output/yaml_outputter.py b/reclass/output/yaml_outputter.py
similarity index 89%
rename from output/yaml_outputter.py
rename to reclass/output/yaml_outputter.py
index 493f5cb..dbf95a6 100644
--- a/output/yaml_outputter.py
+++ b/reclass/output/yaml_outputter.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from output import OutputterBase
+from reclass.output import OutputterBase
 import yaml
 
 class Outputter(OutputterBase):
diff --git a/storage/__init__.py b/reclass/storage/__init__.py
similarity index 92%
rename from storage/__init__.py
rename to reclass/storage/__init__.py
index 27cbf7d..f6908d9 100644
--- a/storage/__init__.py
+++ b/reclass/storage/__init__.py
@@ -39,9 +39,9 @@
 class StorageBackendLoader(object):
 
     def __init__(self, storage_type):
-        self._name = storage_type
+        self._name = 'reclass.storage.' + storage_type
         try:
-            self._module = __import__(storage_type, globals(), locals(), storage_type)
+            self._module = __import__(self._name, globals(), locals(), self._name)
         except ImportError:
             raise NotImplementedError
 
diff --git a/storage/yaml_fs/__init__.py b/reclass/storage/yaml_fs/__init__.py
similarity index 92%
rename from storage/yaml_fs/__init__.py
rename to reclass/storage/yaml_fs/__init__.py
index 7ea4ae8..026c68f 100644
--- a/storage/yaml_fs/__init__.py
+++ b/reclass/storage/yaml_fs/__init__.py
@@ -7,10 +7,10 @@
 # Released under the terms of the Artistic Licence 2.0
 #
 import os
-from storage import NodeStorageBase
+from reclass.storage import NodeStorageBase
 from yamlfile import YamlFile
 from directory import Directory
-import errors
+import reclass.errors
 
 FILE_EXTENSION = '.yml'
 
@@ -52,9 +52,9 @@
 
         except IOError:
             if base_uri == self.classes_uri:
-                raise errors.ClassNotFound('yaml_fs', name, base_uri, nodename)
+                raise reclass.errors.ClassNotFound('yaml_fs', name, base_uri, nodename)
             else:
-                raise errors.NodeNotFound('yaml_fs', name, base_uri)
+                raise reclass.errors.NodeNotFound('yaml_fs', name, base_uri)
 
     def _list_inventory(self):
         d = Directory(self.nodes_uri)
diff --git a/storage/yaml_fs/directory.py b/reclass/storage/yaml_fs/directory.py
similarity index 100%
rename from storage/yaml_fs/directory.py
rename to reclass/storage/yaml_fs/directory.py
diff --git a/storage/yaml_fs/tests/classes/basenode.yml b/reclass/storage/yaml_fs/tests/classes/basenode.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/basenode.yml
rename to reclass/storage/yaml_fs/tests/classes/basenode.yml
diff --git a/storage/yaml_fs/tests/classes/debiannode.yml b/reclass/storage/yaml_fs/tests/classes/debiannode.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/debiannode.yml
rename to reclass/storage/yaml_fs/tests/classes/debiannode.yml
diff --git a/storage/yaml_fs/tests/classes/debiannode@sid.yml b/reclass/storage/yaml_fs/tests/classes/debiannode@sid.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/debiannode@sid.yml
rename to reclass/storage/yaml_fs/tests/classes/debiannode@sid.yml
diff --git a/storage/yaml_fs/tests/classes/debiannode@squeeze.yml b/reclass/storage/yaml_fs/tests/classes/debiannode@squeeze.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/debiannode@squeeze.yml
rename to reclass/storage/yaml_fs/tests/classes/debiannode@squeeze.yml
diff --git a/storage/yaml_fs/tests/classes/debiannode@wheezy.yml b/reclass/storage/yaml_fs/tests/classes/debiannode@wheezy.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/debiannode@wheezy.yml
rename to reclass/storage/yaml_fs/tests/classes/debiannode@wheezy.yml
diff --git a/storage/yaml_fs/tests/classes/hosted@munich.yml b/reclass/storage/yaml_fs/tests/classes/hosted@munich.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/hosted@munich.yml
rename to reclass/storage/yaml_fs/tests/classes/hosted@munich.yml
diff --git a/storage/yaml_fs/tests/classes/hosted@zurich.yml b/reclass/storage/yaml_fs/tests/classes/hosted@zurich.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/hosted@zurich.yml
rename to reclass/storage/yaml_fs/tests/classes/hosted@zurich.yml
diff --git a/storage/yaml_fs/tests/classes/mailserver.yml b/reclass/storage/yaml_fs/tests/classes/mailserver.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/mailserver.yml
rename to reclass/storage/yaml_fs/tests/classes/mailserver.yml
diff --git a/storage/yaml_fs/tests/classes/subdir/subclass.yml b/reclass/storage/yaml_fs/tests/classes/subdir/subclass.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/subdir/subclass.yml
rename to reclass/storage/yaml_fs/tests/classes/subdir/subclass.yml
diff --git a/storage/yaml_fs/tests/classes/webserver.yml b/reclass/storage/yaml_fs/tests/classes/webserver.yml
similarity index 100%
rename from storage/yaml_fs/tests/classes/webserver.yml
rename to reclass/storage/yaml_fs/tests/classes/webserver.yml
diff --git a/storage/yaml_fs/tests/nodes/blue.yml b/reclass/storage/yaml_fs/tests/nodes/blue.yml
similarity index 100%
rename from storage/yaml_fs/tests/nodes/blue.yml
rename to reclass/storage/yaml_fs/tests/nodes/blue.yml
diff --git a/storage/yaml_fs/tests/nodes/empty.yml b/reclass/storage/yaml_fs/tests/nodes/empty.yml
similarity index 100%
rename from storage/yaml_fs/tests/nodes/empty.yml
rename to reclass/storage/yaml_fs/tests/nodes/empty.yml
diff --git a/storage/yaml_fs/tests/nodes/green.yml b/reclass/storage/yaml_fs/tests/nodes/green.yml
similarity index 100%
rename from storage/yaml_fs/tests/nodes/green.yml
rename to reclass/storage/yaml_fs/tests/nodes/green.yml
diff --git a/storage/yaml_fs/tests/nodes/null.yml b/reclass/storage/yaml_fs/tests/nodes/null.yml
similarity index 100%
rename from storage/yaml_fs/tests/nodes/null.yml
rename to reclass/storage/yaml_fs/tests/nodes/null.yml
diff --git a/storage/yaml_fs/tests/nodes/red.yml b/reclass/storage/yaml_fs/tests/nodes/red.yml
similarity index 100%
rename from storage/yaml_fs/tests/nodes/red.yml
rename to reclass/storage/yaml_fs/tests/nodes/red.yml
diff --git a/storage/yaml_fs/tests/test_directory.py b/reclass/storage/yaml_fs/tests/test_directory.py
similarity index 93%
rename from storage/yaml_fs/tests/test_directory.py
rename to reclass/storage/yaml_fs/tests/test_directory.py
index 4c2604a..9d6049d 100644
--- a/storage/yaml_fs/tests/test_directory.py
+++ b/reclass/storage/yaml_fs/tests/test_directory.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-import storage.yaml_fs.directory as directory
+from reclass.storage.yaml_fs import directory
 import os, sys
 
 TESTDIR = os.path.join(sys.path[0], 'classes')
diff --git a/storage/yaml_fs/tests/test_yaml_fs.py b/reclass/storage/yaml_fs/tests/test_yaml_fs.py
similarity index 97%
rename from storage/yaml_fs/tests/test_yaml_fs.py
rename to reclass/storage/yaml_fs/tests/test_yaml_fs.py
index eae2f47..d76b6f0 100644
--- a/storage/yaml_fs/tests/test_yaml_fs.py
+++ b/reclass/storage/yaml_fs/tests/test_yaml_fs.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-from storage.yaml_fs import ExternalNodeStorage
+from reclass.storage.yaml_fs import ExternalNodeStorage
 
 import os
 
diff --git a/storage/yaml_fs/tests/test_yamlfile.py b/reclass/storage/yaml_fs/tests/test_yamlfile.py
similarity index 93%
rename from storage/yaml_fs/tests/test_yamlfile.py
rename to reclass/storage/yaml_fs/tests/test_yamlfile.py
index 1700f30..fe34396 100644
--- a/storage/yaml_fs/tests/test_yamlfile.py
+++ b/reclass/storage/yaml_fs/tests/test_yamlfile.py
@@ -6,9 +6,9 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-import storage.yaml_fs.yamlfile as yamlfile
+from reclass.storage.yaml_fs import yamlfile
 import os, sys
-from datatypes import Entity, Classes, Parameters, Applications
+from reclass.datatypes import Entity, Classes, Parameters, Applications
 
 TESTFILE = os.path.join(sys.path[0], 'nodes', 'blue.yml')
 EMPTYFILE = os.path.join(sys.path[0], 'nodes', 'empty.yml')
diff --git a/storage/yaml_fs/yamlfile.py b/reclass/storage/yaml_fs/yamlfile.py
similarity index 97%
rename from storage/yaml_fs/yamlfile.py
rename to reclass/storage/yaml_fs/yamlfile.py
index b8cb154..14bee86 100644
--- a/storage/yaml_fs/yamlfile.py
+++ b/reclass/storage/yaml_fs/yamlfile.py
@@ -6,7 +6,7 @@
 # Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
 # Released under the terms of the Artistic Licence 2.0
 #
-import datatypes
+from reclass import datatypes
 import yaml
 import os