[Kimchi-devel] [PATCH 13/13] refactor model: Update server.py and root.py to use new models

Aline Manera alinefm at linux.vnet.ibm.com
Fri Jan 17 02:24:49 UTC 2014


From: Aline Manera <alinefm at br.ibm.com>

Also remove former model.py and mockmodel.py files.
And rename model_ to model

Signed-off-by: Aline Manera <alinefm at br.ibm.com>
---
 src/kimchi/control/base.py              |   32 +-
 src/kimchi/control/config.py            |   30 +-
 src/kimchi/control/debugreports.py      |   18 +-
 src/kimchi/control/host.py              |   24 +-
 src/kimchi/control/interfaces.py        |   11 +-
 src/kimchi/control/networks.py          |   11 +-
 src/kimchi/control/plugins.py           |   13 +-
 src/kimchi/control/storagepools.py      |   32 +-
 src/kimchi/control/storagevolumes.py    |   24 +-
 src/kimchi/control/tasks.py             |   11 +-
 src/kimchi/control/templates.py         |   11 +-
 src/kimchi/control/utils.py             |    2 +-
 src/kimchi/control/vms.py               |   17 +-
 src/kimchi/mockmodel.py                 |  784 ----------------
 src/kimchi/model.py                     | 1536 -------------------------------
 src/kimchi/model/__init__.py            |   21 +
 src/kimchi/model/config.py              |   52 ++
 src/kimchi/model/debugreports.py        |   86 ++
 src/kimchi/model/host.py                |   49 +
 src/kimchi/model/interfaces.py          |   48 +
 src/kimchi/model/libvirtbackend.py      |  955 +++++++++++++++++++
 src/kimchi/model/libvirtconnection.py   |  123 +++
 src/kimchi/model/libvirtstoragepool.py  |  225 +++++
 src/kimchi/model/mockbackend.py         |  338 +++++++
 src/kimchi/model/networks.py            |  115 +++
 src/kimchi/model/plugins.py             |   29 +
 src/kimchi/model/storagepools.py        |   86 ++
 src/kimchi/model/storagevolumes.py      |   95 ++
 src/kimchi/model/tasks.py               |   45 +
 src/kimchi/model/templates.py           |   89 ++
 src/kimchi/model/vms.py                 |  164 ++++
 src/kimchi/model_/__init__.py           |   21 -
 src/kimchi/model_/config.py             |   52 --
 src/kimchi/model_/debugreports.py       |   86 --
 src/kimchi/model_/host.py               |   49 -
 src/kimchi/model_/interfaces.py         |   48 -
 src/kimchi/model_/libvirtbackend.py     |  955 -------------------
 src/kimchi/model_/libvirtconnection.py  |  123 ---
 src/kimchi/model_/libvirtstoragepool.py |  225 -----
 src/kimchi/model_/mockbackend.py        |  338 -------
 src/kimchi/model_/networks.py           |  115 ---
 src/kimchi/model_/plugins.py            |   29 -
 src/kimchi/model_/storagepools.py       |   86 --
 src/kimchi/model_/storagevolumes.py     |   95 --
 src/kimchi/model_/tasks.py              |   45 -
 src/kimchi/model_/templates.py          |   89 --
 src/kimchi/model_/vms.py                |  164 ----
 src/kimchi/root.py                      |   24 +-
 src/kimchi/server.py                    |   16 +-
 49 files changed, 2669 insertions(+), 4967 deletions(-)
 delete mode 100644 src/kimchi/mockmodel.py
 delete mode 100644 src/kimchi/model.py
 create mode 100644 src/kimchi/model/__init__.py
 create mode 100644 src/kimchi/model/config.py
 create mode 100644 src/kimchi/model/debugreports.py
 create mode 100644 src/kimchi/model/host.py
 create mode 100644 src/kimchi/model/interfaces.py
 create mode 100644 src/kimchi/model/libvirtbackend.py
 create mode 100644 src/kimchi/model/libvirtconnection.py
 create mode 100644 src/kimchi/model/libvirtstoragepool.py
 create mode 100644 src/kimchi/model/mockbackend.py
 create mode 100644 src/kimchi/model/networks.py
 create mode 100644 src/kimchi/model/plugins.py
 create mode 100644 src/kimchi/model/storagepools.py
 create mode 100644 src/kimchi/model/storagevolumes.py
 create mode 100644 src/kimchi/model/tasks.py
 create mode 100644 src/kimchi/model/templates.py
 create mode 100644 src/kimchi/model/vms.py
 delete mode 100644 src/kimchi/model_/__init__.py
 delete mode 100644 src/kimchi/model_/config.py
 delete mode 100644 src/kimchi/model_/debugreports.py
 delete mode 100644 src/kimchi/model_/host.py
 delete mode 100644 src/kimchi/model_/interfaces.py
 delete mode 100644 src/kimchi/model_/libvirtbackend.py
 delete mode 100644 src/kimchi/model_/libvirtconnection.py
 delete mode 100644 src/kimchi/model_/libvirtstoragepool.py
 delete mode 100644 src/kimchi/model_/mockbackend.py
 delete mode 100644 src/kimchi/model_/networks.py
 delete mode 100644 src/kimchi/model_/plugins.py
 delete mode 100644 src/kimchi/model_/storagepools.py
 delete mode 100644 src/kimchi/model_/storagevolumes.py
 delete mode 100644 src/kimchi/model_/tasks.py
 delete mode 100644 src/kimchi/model_/templates.py
 delete mode 100644 src/kimchi/model_/vms.py

diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py
index 185c8d8..8ee9f89 100644
--- a/src/kimchi/control/base.py
+++ b/src/kimchi/control/base.py
@@ -52,8 +52,8 @@ class Resource(object):
     - Set the 'data' property to a JSON-serializable representation of the
       Resource.
     """
-    def __init__(self, model, ident=None):
-        self.model = model
+    def __init__(self, backend, ident=None):
+        self.backend = backend
         self.ident = ident
         self.model_args = (ident,)
         self.update_params = []
@@ -66,7 +66,7 @@ class Resource(object):
                 if action_args is not None:
                     model_args.extend(parse_request()[key]
                                       for key in action_args)
-                fn = getattr(self.model, model_fn(self, action_name))
+                fn = getattr(self.model, action_name)
                 fn(*model_args)
                 uri_params = tuple(self.model_args)
                 raise internal_redirect(self.uri_fmt % uri_params)
@@ -89,15 +89,13 @@ class Resource(object):
 
     def lookup(self):
         try:
-            lookup = getattr(self.model, model_fn(self, 'lookup'))
-            self.info = lookup(*self.model_args)
+            self.info = self.model.lookup(*self.model_args)
         except AttributeError:
             self.info = {}
 
     def delete(self):
         try:
-            fn = getattr(self.model, model_fn(self, 'delete'))
-            fn(*self.model_args)
+            self.model.delete(*self.model_args)
             cherrypy.response.status = 204
         except AttributeError:
             error = "Delete is not allowed for %s" % get_class_name(self)
@@ -136,7 +134,7 @@ class Resource(object):
 
     def update(self):
         try:
-            update = getattr(self.model, model_fn(self, 'update'))
+            update = getattr(self.model, 'update')
         except AttributeError:
             error = "%s does not implement update method"
             raise cherrypy.HTTPError(405, error % get_class_name(self))
@@ -189,15 +187,15 @@ class Collection(object):
 
     - Implement the base operations of 'create' and 'get_list' in the model.
     """
-    def __init__(self, model):
-        self.model = model
+    def __init__(self, backend):
+        self.backend = backend
         self.resource = Resource
         self.resource_args = []
         self.model_args = []
 
     def create(self, *args):
         try:
-            create = getattr(self.model, model_fn(self, 'create'))
+            create = getattr(self.model, 'create')
         except AttributeError:
             error = 'Create is not allowed for %s' % get_class_name(self)
             raise cherrypy.HTTPError(405, error)
@@ -208,19 +206,19 @@ class Collection(object):
         name = create(*args)
         cherrypy.response.status = 201
         args = self.resource_args + [name]
-        res = self.resource(self.model, *args)
+        res = self.resource(self.backend, *args)
 
         return res.get()
 
     def _get_resources(self):
         try:
-            get_list = getattr(self.model, model_fn(self, 'get_list'))
-            idents = get_list(*self.model_args)
+            get_list = getattr(self.model, 'get_list')
+            idents = self.model.get_list(*self.model_args)
             res_list = []
             for ident in idents:
                 # internal text, get_list changes ident to unicode for sorted
                 args = self.resource_args + [ident]
-                res = self.resource(self.model, *args)
+                res = self.resource(self.backend, *args)
                 res.lookup()
                 res_list.append(res)
             return res_list
@@ -232,7 +230,7 @@ class Collection(object):
             ident = vpath.pop(0)
             # incoming text, from URL, is not unicode, need decode
             args = self.resource_args + [ident.decode("utf-8")]
-            return self.resource(self.model, *args)
+            return self.resource(self.backend, *args)
 
     def get(self):
         resources = self._get_resources()
@@ -280,7 +278,7 @@ class AsyncCollection(Collection):
 
     def create(self, *args):
         try:
-            create = getattr(self.model, model_fn(self, 'create'))
+            create = getattr(self.model, 'create')
         except AttributeError:
             error = 'Create is not allowed for %s' % get_class_name(self)
             raise cherrypy.HTTPError(405, error)
diff --git a/src/kimchi/control/config.py b/src/kimchi/control/config.py
index 5186ddd..6720c1a 100644
--- a/src/kimchi/control/config.py
+++ b/src/kimchi/control/config.py
@@ -25,16 +25,17 @@
 import cherrypy
 
 
+from kimchi.model import config as model_config
 from kimchi.config import config
 from kimchi.control.base import Collection, Resource
 
 
 class Config(Resource):
-    def __init__(self, model, id=None):
-        super(Config, self).__init__(model, id)
-        self.capabilities = Capabilities(self.model)
+    def __init__(self, backend, id=None):
+        super(Config, self).__init__(backend, id)
+        self.capabilities = Capabilities(backend)
         self.capabilities.exposed = True
-        self.distros = Distros(model)
+        self.distros = Distros(backend)
         self.distros.exposed = True
 
     @property
@@ -44,27 +45,26 @@ class Config(Resource):
 
 
 class Capabilities(Resource):
-    def __init__(self, model, id=None):
-        super(Capabilities, self).__init__(model, id)
+    def __init__(self, backend, id=None):
+        super(Capabilities, self).__init__(backend, id)
+        self.model = model_config.Capabilities(backend)
 
     @property
     def data(self):
-        caps = ['libvirt_stream_protocols', 'qemu_stream',
-                'screenshot', 'system_report_tool']
-        ret = dict([(x, None) for x in caps])
-        ret.update(self.model.get_capabilities())
-        return ret
+        return self.info
 
 
 class Distros(Collection):
-    def __init__(self, model):
-        super(Distros, self).__init__(model)
+    def __init__(self, backend):
+        super(Distros, self).__init__(backend)
+        self.model = model_config.Distros()
         self.resource = Distro
 
 
 class Distro(Resource):
-    def __init__(self, model, ident):
-        super(Distro, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(Distro, self).__init__(backend, ident)
+        self.model = model_config.Distro()
 
     @property
     def data(self):
diff --git a/src/kimchi/control/debugreports.py b/src/kimchi/control/debugreports.py
index a55ba38..ef3c597 100644
--- a/src/kimchi/control/debugreports.py
+++ b/src/kimchi/control/debugreports.py
@@ -21,20 +21,23 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import debugreports
 from kimchi.control.base import AsyncCollection, Resource
 from kimchi.control.utils import internal_redirect
 
 
 class DebugReports(AsyncCollection):
-    def __init__(self, model):
-        super(DebugReports, self).__init__(model)
+    def __init__(self, backend):
+        super(DebugReports, self).__init__(backend)
+        self.model = debugreports.DebugReports(backend)
         self.resource = DebugReport
 
 
 class DebugReport(Resource):
-    def __init__(self, model, ident):
-        super(DebugReport, self).__init__(model, ident)
-        self.content = DebugReportContent(model, ident)
+    def __init__(self, backend, ident):
+        super(DebugReport, self).__init__(backend, ident)
+        self.model = debugreports.DebugReport(backend)
+        self.content = DebugReportContent(backend, ident)
 
     @property
     def data(self):
@@ -44,8 +47,9 @@ class DebugReport(Resource):
 
 
 class DebugReportContent(Resource):
-    def __init__(self, model, ident):
-        super(DebugReportContent, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(DebugReportContent, self).__init__(backend, ident)
+        self.model = debugreports.DebugReportContent(backend)
 
     def get(self):
         self.lookup()
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
index 9b19577..c638fc0 100644
--- a/src/kimchi/control/host.py
+++ b/src/kimchi/control/host.py
@@ -23,18 +23,20 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import host
 from kimchi.control.base import Collection, Resource
 
 
 class Host(Resource):
-    def __init__(self, model, id=None):
-        super(Host, self).__init__(model, id)
+    def __init__(self, backend, id=None):
+        super(Host, self).__init__(backend, id)
+        self.model = host.Host(backend)
         self.uri_fmt = '/host/%s'
         self.reboot = self.generate_action_handler('reboot')
         self.shutdown = self.generate_action_handler('shutdown')
-        self.stats = HostStats(self.model)
+        self.stats = HostStats(backend)
         self.stats.exposed = True
-        self.partitions = Partitions(self.model)
+        self.partitions = Partitions(backend)
         self.partitions.exposed = True
 
     @property
@@ -43,20 +45,26 @@ class Host(Resource):
 
 
 class HostStats(Resource):
+    def __init__(self, backend, id=None):
+        super(HostStats, self).__init__(backend, id)
+        self.model = host.HostStats(backend)
+
     @property
     def data(self):
         return self.info
 
 
 class Partitions(Collection):
-    def __init__(self, model):
-        super(Partitions, self).__init__(model)
+    def __init__(self, backend):
+        super(Partitions, self).__init__(backend)
+        self.model = host.Partitions()
         self.resource = Partition
 
 
 class Partition(Resource):
-    def __init__(self, model, id):
-        super(Partition, self).__init__(model, id)
+    def __init__(self, backend, id):
+        super(Partition, self).__init__(backend, id)
+        self.model = host.Partition()
 
     @property
     def data(self):
diff --git a/src/kimchi/control/interfaces.py b/src/kimchi/control/interfaces.py
index 28be26e..319b6a9 100644
--- a/src/kimchi/control/interfaces.py
+++ b/src/kimchi/control/interfaces.py
@@ -22,18 +22,21 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import interfaces
 from kimchi.control.base import Collection, Resource
 
 
 class Interfaces(Collection):
-    def __init__(self, model):
-        super(Interfaces, self).__init__(model)
+    def __init__(self, backend):
+        super(Interfaces, self).__init__(backend)
+        self.model = interfaces.Interfaces(backend)
         self.resource = Interface
 
 
 class Interface(Resource):
-    def __init__(self, model, ident):
-        super(Interface, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(Interface, self).__init__(backend, ident)
+        self.model = interfaces.Interface()
         self.uri_fmt = "/interfaces/%s"
 
     @property
diff --git a/src/kimchi/control/networks.py b/src/kimchi/control/networks.py
index f3f0b41..2fae93e 100644
--- a/src/kimchi/control/networks.py
+++ b/src/kimchi/control/networks.py
@@ -21,18 +21,21 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import networks
 from kimchi.control.base import Collection, Resource
 
 
 class Networks(Collection):
-    def __init__(self, model):
-        super(Networks, self).__init__(model)
+    def __init__(self, backend):
+        super(Networks, self).__init__(backend)
+        self.model = networks.Networks(backend)
         self.resource = Network
 
 
 class Network(Resource):
-    def __init__(self, model, ident):
-        super(Network, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(Network, self).__init__(backend, ident)
+        self.model = networks.Network(backend)
         self.uri_fmt = "/networks/%s"
         self.activate = self.generate_action_handler('activate')
         self.deactivate = self.generate_action_handler('deactivate')
diff --git a/src/kimchi/control/plugins.py b/src/kimchi/control/plugins.py
index af32709..24903ac 100644
--- a/src/kimchi/control/plugins.py
+++ b/src/kimchi/control/plugins.py
@@ -23,23 +23,20 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
 import kimchi.template
+from kimchi.model import plugins
 from kimchi.control.base import Collection, Resource
 from kimchi.control.utils import get_class_name, model_fn
 
 
 class Plugins(Collection):
-    def __init__(self, model):
-        super(Plugins, self).__init__(model)
+    def __init__(self, backend):
+        super(Plugins, self).__init__(backend)
+        self.model = plugins.Plugins()
 
     @property
     def data(self):
         return self.info
 
     def get(self):
-        res_list = []
-        try:
-            get_list = getattr(self.model, model_fn(self, 'get_list'))
-            res_list = get_list(*self.model_args)
-        except AttributeError:
-            pass
+        res_list = self.model.get_list()
         return kimchi.template.render(get_class_name(self), res_list)
diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py
index 782f5a6..fc847bb 100644
--- a/src/kimchi/control/storagepools.py
+++ b/src/kimchi/control/storagepools.py
@@ -25,25 +25,26 @@
 
 import cherrypy
 
-
+from kimchi.model import storagepools
 from kimchi.control.base import Collection, Resource
 from kimchi.control.storagevolumes import IsoVolumes, StorageVolumes
 from kimchi.control.utils import get_class_name, model_fn, parse_request
 from kimchi.control.utils import validate_params
-from kimchi.model import ISO_POOL_NAME
+from kimchi.model.storagepools import ISO_POOL_NAME
 
 
 class StoragePools(Collection):
-    def __init__(self, model):
-        super(StoragePools, self).__init__(model)
+    def __init__(self, backend):
+        super(StoragePools, self).__init__(backend)
+        self.model = storagepools.StoragePools(backend)
         self.resource = StoragePool
-        isos = IsoPool(model)
+        isos = IsoPool(backend)
         isos.exposed = True
         setattr(self, ISO_POOL_NAME, isos)
 
     def create(self, *args):
         try:
-            create = getattr(self.model, model_fn(self, 'create'))
+            create = self.model.create
         except AttributeError:
             error = 'Create is not allowed for %s' % get_class_name(self)
             raise cherrypy.HTTPError(405, error)
@@ -53,7 +54,7 @@ class StoragePools(Collection):
         args = self.model_args + [params]
         name = create(*args)
         args = self.resource_args + [name]
-        res = self.resource(self.model, *args)
+        res = self.resource(self.backend, *args)
         resp = res.get()
 
         if 'task_id' in res.data:
@@ -77,8 +78,9 @@ class StoragePools(Collection):
 
 
 class StoragePool(Resource):
-    def __init__(self, model, ident):
-        super(StoragePool, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(StoragePool, self).__init__(backend, ident)
+        self.model = storagepools.StoragePool(backend)
         self.update_params = ["autostart"]
         self.uri_fmt = "/storagepools/%s"
         self.activate = self.generate_action_handler('activate')
@@ -108,22 +110,22 @@ class StoragePool(Resource):
             subcollection = vpath.pop(0)
             if subcollection == 'storagevolumes':
                 # incoming text, from URL, is not unicode, need decode
-                return StorageVolumes(self.model, self.ident.decode("utf-8"))
+                return StorageVolumes(self.backend, self.ident.decode("utf-8"))
 
 
 class IsoPool(Resource):
-    def __init__(self, model):
-        super(IsoPool, self).__init__(model, ISO_POOL_NAME)
+    def __init__(self, backend):
+        super(IsoPool, self).__init__(backend, ISO_POOL_NAME)
 
     @property
     def data(self):
         return {'name': self.ident,
-                'state': self.info['state'],
-                'type': self.info['type']}
+                'state': 'active',
+                'type': 'kimchi-iso'}
 
     def _cp_dispatch(self, vpath):
         if vpath:
             subcollection = vpath.pop(0)
             if subcollection == 'storagevolumes':
                 # incoming text, from URL, is not unicode, need decode
-                return IsoVolumes(self.model, self.ident.decode("utf-8"))
+                return IsoVolumes(self.backend, self.ident.decode("utf-8"))
diff --git a/src/kimchi/control/storagevolumes.py b/src/kimchi/control/storagevolumes.py
index d541807..f07dfb9 100644
--- a/src/kimchi/control/storagevolumes.py
+++ b/src/kimchi/control/storagevolumes.py
@@ -24,13 +24,15 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
 import kimchi.template
+from kimchi.model import storagevolumes
 from kimchi.control.base import Collection, Resource
 from kimchi.control.utils import get_class_name, model_fn
 
 
 class StorageVolumes(Collection):
-    def __init__(self, model, pool):
-        super(StorageVolumes, self).__init__(model)
+    def __init__(self, backend, pool):
+        super(StorageVolumes, self).__init__(backend)
+        self.model = storagevolumes.StorageVolumes(backend)
         self.resource = StorageVolume
         self.pool = pool
         self.resource_args = [self.pool, ]
@@ -38,8 +40,9 @@ class StorageVolumes(Collection):
 
 
 class StorageVolume(Resource):
-    def __init__(self, model, pool, ident):
-        super(StorageVolume, self).__init__(model, ident)
+    def __init__(self, backend, pool, ident):
+        super(StorageVolume, self).__init__(backend, ident)
+        self.model = storagevolumes.StorageVolume(backend)
         self.pool = pool
         self.ident = ident
         self.info = {}
@@ -66,16 +69,11 @@ class StorageVolume(Resource):
 
 
 class IsoVolumes(Collection):
-    def __init__(self, model, pool):
-        super(IsoVolumes, self).__init__(model)
+    def __init__(self, backend, pool):
+        super(IsoVolumes, self).__init__(backend)
+        self.model = storagevolumes.IsoVolumes(backend)
         self.pool = pool
 
     def get(self):
-        res_list = []
-        try:
-            get_list = getattr(self.model, model_fn(self, 'get_list'))
-            res_list = get_list(*self.model_args)
-        except AttributeError:
-            pass
-
+        res_list = self.model.get_list(self.pool)
         return kimchi.template.render(get_class_name(self), res_list)
diff --git a/src/kimchi/control/tasks.py b/src/kimchi/control/tasks.py
index b799422..3bd63f9 100644
--- a/src/kimchi/control/tasks.py
+++ b/src/kimchi/control/tasks.py
@@ -21,18 +21,21 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import tasks
 from kimchi.control.base import Collection, Resource
 
 
 class Tasks(Collection):
-    def __init__(self, model):
-        super(Tasks, self).__init__(model)
+    def __init__(self, backend):
+        super(Tasks, self).__init__(backend)
+        self.model = tasks.Tasks(backend)
         self.resource = Task
 
 
 class Task(Resource):
-    def __init__(self, model, id):
-        super(Task, self).__init__(model, id)
+    def __init__(self, backend, id):
+        super(Task, self).__init__(backend, id)
+        self.model = tasks.Task(backend)
 
     @property
     def data(self):
diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py
index a77936e..bc65eb7 100644
--- a/src/kimchi/control/templates.py
+++ b/src/kimchi/control/templates.py
@@ -21,18 +21,21 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import templates
 from kimchi.control.base import Collection, Resource
 
 
 class Templates(Collection):
-    def __init__(self, model):
-        super(Templates, self).__init__(model)
+    def __init__(self, backend):
+        super(Templates, self).__init__(backend)
+        self.model = templates.Templates(backend)
         self.resource = Template
 
 
 class Template(Resource):
-    def __init__(self, model, ident):
-        super(Template, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(Template, self).__init__(backend, ident)
+        self.model = templates.Template(backend)
         self.update_params = ["name", "folder", "icon", "os_distro",
                               "storagepool", "os_version", "cpus",
                               "memory", "cdrom", "disks", "networks",
diff --git a/src/kimchi/control/utils.py b/src/kimchi/control/utils.py
index 814ba20..ee3ecce 100644
--- a/src/kimchi/control/utils.py
+++ b/src/kimchi/control/utils.py
@@ -94,7 +94,7 @@ def validate_params(params, instance, action):
     else:
         return
 
-    operation = model_fn(instance, action)
+    operation = "%s.%s" % (instance.__class__.__name__, action)
     validator = Draft3Validator(api_schema, format_checker=FormatChecker())
     request = {operation: params}
 
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 7843be7..c50b6b1 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -22,21 +22,24 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+from kimchi.model import vms
 from kimchi.control.base import Collection, Resource
 from kimchi.control.utils import internal_redirect
 
 
 class VMs(Collection):
-    def __init__(self, model):
-        super(VMs, self).__init__(model)
+    def __init__(self, backend):
+        super(VMs, self).__init__(backend)
+        self.model = vms.VMs(backend)
         self.resource = VM
 
 
 class VM(Resource):
-    def __init__(self, model, ident):
-        super(VM, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(VM, self).__init__(backend, ident)
+        self.model = vms.VM(self.backend)
         self.update_params = ["name"]
-        self.screenshot = VMScreenShot(model, ident)
+        self.screenshot = VMScreenShot(backend, ident)
         self.uri_fmt = '/vms/%s'
         self.start = self.generate_action_handler('start')
         self.stop = self.generate_action_handler('stop')
@@ -59,8 +62,8 @@ class VM(Resource):
 
 
 class VMScreenShot(Resource):
-    def __init__(self, model, ident):
-        super(VMScreenShot, self).__init__(model, ident)
+    def __init__(self, backend, ident):
+        super(VMScreenShot, self).__init__(backend, ident)
 
     def get(self):
         self.lookup()
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
deleted file mode 100644
index 4ef3fa6..0000000
--- a/src/kimchi/mockmodel.py
+++ /dev/null
@@ -1,784 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-import cherrypy
-import copy
-import disks
-import glob
-import ipaddr
-import os
-import psutil
-import random
-import subprocess
-import time
-import uuid
-
-
-try:
-    from PIL import Image
-    from PIL import ImageDraw
-except ImportError:
-    import Image
-    import ImageDraw
-
-
-import kimchi.model
-from kimchi import config
-from kimchi import network as knetwork
-from kimchi.asynctask import AsyncTask
-from kimchi.distroloader import DistroLoader
-from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
-from kimchi.objectstore import ObjectStore
-from kimchi.screenshot import VMScreenshot
-from kimchi.utils import is_digit
-from kimchi.vmtemplate import VMTemplate
-
-
-class MockModel(object):
-    def __init__(self, objstore_loc=None):
-        self.reset()
-        self.objstore = ObjectStore(objstore_loc)
-        self.distros = self._get_distros()
-
-    def get_capabilities(self):
-        return {'libvirt_stream_protocols': ['http', 'https', 'ftp', 'ftps', 'tftp'],
-                'qemu_stream': True,
-                'screenshot': True,
-                'system_report_tool': True}
-
-    def reset(self):
-        self._mock_vms = {}
-        self._mock_screenshots = {}
-        self._mock_templates = {}
-        self._mock_storagepools = {'default': MockStoragePool('default')}
-        self._mock_networks = {'default': MockNetwork('default')}
-        self._mock_interfaces = self.dummy_interfaces()
-        self.next_taskid = 1
-        self.storagepool_activate('default')
-
-    def _static_vm_update(self, dom, params):
-        state = dom.info['state']
-
-        if 'name' in params:
-            if state == 'running' or params['name'] in self.vms_get_list():
-                raise InvalidParameter("VM name existed or vm not shutoff.")
-            else:
-                del self._mock_vms[dom.name]
-                dom.name = params['name']
-                self._mock_vms[dom.name] = dom
-
-        for key, val in params.items():
-            if key in kimchi.model.VM_STATIC_UPDATE_PARAMS and key in dom.info:
-                dom.info[key] = val
-
-    def _live_vm_update(self, dom, params):
-        pass
-
-    def vm_update(self, name, params):
-        dom = self._get_vm(name)
-        self._static_vm_update(dom, params)
-        self._live_vm_update(dom, params)
-
-        return dom.name
-
-    def vm_lookup(self, name):
-        vm = self._get_vm(name)
-        if vm.info['state'] == 'running':
-            vm.info['screenshot'] = self.vmscreenshot_lookup(name)
-        else:
-            vm.info['screenshot'] = None
-        return vm.info
-
-    def vm_delete(self, name):
-        vm = self._get_vm(name)
-        self._vmscreenshot_delete(vm.uuid)
-        for disk in vm.disk_paths:
-            self.storagevolume_delete(disk['pool'], disk['volume'])
-
-        del self._mock_vms[vm.name]
-
-    def vm_start(self, name):
-        self._get_vm(name).info['state'] = 'running'
-        info = self._get_vm(name).info
-
-    def vm_stop(self, name):
-        self._get_vm(name).info['state'] = 'shutoff'
-
-    def vm_connect(self, name):
-        pass
-
-    def vms_create(self, params):
-        t_name = kimchi.model.template_name_from_uri(params['template'])
-        name = kimchi.model.get_vm_name(params.get('name'), t_name,
-                                        self._mock_vms.keys())
-        if name in self._mock_vms:
-            raise InvalidOperation("VM already exists")
-
-        vm_uuid = str(uuid.uuid4())
-        vm_overrides = dict()
-        pool_uri = params.get('storagepool')
-        if pool_uri:
-            vm_overrides['storagepool'] = pool_uri
-
-        t = self._get_template(t_name, vm_overrides)
-        t.validate()
-
-        t_info = copy.deepcopy(t.info)
-        graphics = params.get('graphics')
-        if graphics:
-                t_info.update({'graphics': graphics})
-
-        vm = MockVM(vm_uuid, name, t_info)
-        icon = t_info.get('icon')
-        if icon:
-            vm.info['icon'] = icon
-
-        vm.disk_paths = t.fork_vm_storage(vm_uuid)
-        self._mock_vms[name] = vm
-        return name
-
-    def vms_get_list(self):
-        names = self._mock_vms.keys()
-        return sorted(names, key=unicode.lower)
-
-    def vmscreenshot_lookup(self, name):
-        vm = self._get_vm(name)
-        if vm.info['state'] != 'running':
-            raise NotFoundError('No screenshot for stopped vm')
-        screenshot = self._mock_screenshots.setdefault(
-            vm.uuid, MockVMScreenshot({'uuid': vm.uuid}))
-        return screenshot.lookup()
-
-    def _vmscreenshot_delete(self, vm_uuid):
-        screenshot = self._mock_screenshots.get(vm_uuid)
-        if screenshot:
-            screenshot.delete()
-            del self._mock_screenshots[vm_uuid]
-
-    def template_lookup(self, name):
-        t = self._get_template(name)
-        return t.info
-
-    def template_delete(self, name):
-        try:
-            del self._mock_templates[name]
-        except KeyError:
-            raise NotFoundError()
-
-    def templates_create(self, params):
-        name = params['name']
-        if name in self._mock_templates:
-            raise InvalidOperation("Template already exists")
-        for net_name in params.get(u'networks', []):
-            try:
-                self._get_network(net_name)
-            except NotFoundError:
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist" % net_name)
-
-        t = MockVMTemplate(params, self)
-        self._mock_templates[name] = t
-        return name
-
-    def template_update(self, name, params):
-        old_t = self.template_lookup(name)
-        new_t = copy.copy(old_t)
-
-        new_t.update(params)
-        ident = name
-
-        new_storagepool = new_t.get(u'storagepool', '')
-        try:
-            self._get_storagepool(kimchi.model.pool_name_from_uri(new_storagepool))
-        except Exception as e:
-            raise InvalidParameter("Storagepool specified is not valid: %s." % e.message)
-
-        for net_name in params.get(u'networks', []):
-            try:
-                self._get_network(net_name)
-            except NotFoundError:
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist" % net_name)
-
-        self.template_delete(name)
-        try:
-            ident = self.templates_create(new_t)
-        except:
-            ident = self.templates_create(old_t)
-            raise
-        return ident
-
-    def templates_get_list(self):
-        return self._mock_templates.keys()
-
-    def _get_template(self, name, overrides=None):
-        try:
-            t = self._mock_templates[name]
-            if overrides:
-                args = copy.copy(t.info)
-                args.update(overrides)
-                return MockVMTemplate(args, self)
-            else:
-                return t
-        except KeyError:
-            raise NotFoundError()
-
-    def debugreport_lookup(self, name):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, name + '.txt')
-        try:
-            file_target = glob.glob(file_pattern)[0]
-        except IndexError:
-            raise NotFoundError('no such report')
-
-        ctime = os.stat(file_target).st_ctime
-        ctime = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime(ctime))
-        file_target = os.path.split(file_target)[-1]
-        file_target = os.path.join("/data/debugreports", file_target)
-        return {'file': file_target,
-                'ctime': ctime}
-
-    def debugreportcontent_lookup(self, name):
-        return self.debugreport_lookup(name)
-
-    def debugreport_delete(self, name):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, name + '.txt')
-        try:
-            file_target = glob.glob(file_pattern)[0]
-        except IndexError:
-            raise NotFoundError('no such report')
-
-        os.remove(file_target)
-
-    def debugreports_create(self, params):
-        ident = params['name']
-        taskid = self._gen_debugreport_file(ident)
-        return self.task_lookup(taskid)
-
-    def debugreports_get_list(self):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, '*.txt')
-        file_lists = glob.glob(file_pattern)
-        file_lists = [os.path.split(file)[1] for file in file_lists]
-        name_lists = [file.split('.', 1)[0] for file in file_lists]
-
-        return name_lists
-
-    def _get_vm(self, name):
-        try:
-            return self._mock_vms[name]
-        except KeyError:
-            raise NotFoundError()
-
-    def storagepools_create(self, params):
-        try:
-            name = params['name']
-            pool = MockStoragePool(name)
-            pool.info['type'] = params['type']
-            pool.info['path'] = params['path']
-            if params['type'] == 'dir':
-                pool.info['autostart'] = True
-            else:
-                pool.info['autostart'] = False
-        except KeyError, item:
-            raise MissingParameter(item)
-        if name in self._mock_storagepools or name in (kimchi.model.ISO_POOL_NAME,):
-            raise InvalidOperation("StoragePool already exists")
-        self._mock_storagepools[name] = pool
-        return name
-
-    def storagepool_lookup(self, name):
-        storagepool = self._get_storagepool(name)
-        storagepool.refresh()
-        return storagepool.info
-
-    def storagepool_update(self, name, params):
-        autostart = params['autostart']
-        if autostart not in [True, False]:
-            raise InvalidOperation("Autostart flag must be true or false")
-        storagepool = self._get_storagepool(name)
-        storagepool.info['autostart'] = autostart
-        ident = storagepool.name
-        return ident
-
-    def storagepool_activate(self, name):
-        self._get_storagepool(name).info['state'] = 'active'
-
-    def storagepool_deactivate(self, name):
-        self._get_storagepool(name).info['state'] = 'inactive'
-
-    def storagepool_delete(self, name):
-        # firstly, we should check the pool actually exists
-        pool = self._get_storagepool(name)
-        del self._mock_storagepools[pool.name]
-
-    def storagepools_get_list(self):
-        return sorted(self._mock_storagepools.keys())
-
-    def _get_storagepool(self, name):
-        try:
-            return self._mock_storagepools[name]
-        except KeyError:
-            raise NotFoundError()
-
-    def storagevolumes_create(self, pool_name, params):
-        pool = self._get_storagepool(pool_name)
-        if pool.info['state'] == 'inactive':
-            raise InvalidOperation("StoragePool not active")
-        try:
-            name = params['name']
-            volume = MockStorageVolume(pool, name, params)
-            volume.info['type'] = params['type']
-            volume.info['format'] = params['format']
-            volume.info['path'] = os.path.join(
-                pool.info['path'], name)
-        except KeyError, item:
-            raise MissingParameter(item)
-        if name in pool._volumes:
-            raise InvalidOperation("StorageVolume already exists")
-        pool._volumes[name] = volume
-        return name
-
-    def storagevolume_lookup(self, pool, name):
-        if self._get_storagepool(pool).info['state'] != 'active':
-            raise InvalidOperation("StoragePool %s is not active" % pool)
-        storagevolume = self._get_storagevolume(pool, name)
-        return storagevolume.info
-
-    def storagevolume_wipe(self, pool, name):
-        volume = self._get_storagevolume(pool, name)
-        volume.info['allocation'] = 0
-
-    def storagevolume_delete(self, pool, name):
-        # firstly, we should check the pool actually exists
-        volume = self._get_storagevolume(pool, name)
-        del self._get_storagepool(pool)._volumes[volume.name]
-
-    def storagevolume_resize(self, pool, name, size):
-        volume = self._get_storagevolume(pool, name)
-        volume.info['capacity'] = size
-
-    def storagevolumes_get_list(self, pool):
-        res = self._get_storagepool(pool)
-        if res.info['state'] == 'inactive':
-            raise InvalidOperation(
-                "Unable to list volumes of inactive storagepool %s" % pool)
-        return res._volumes.keys()
-
-    def isopool_lookup(self, name):
-        return {'state': 'active',
-                'type': 'kimchi-iso'}
-
-    def isovolumes_get_list(self):
-        iso_volumes = []
-        pools = self.storagepools_get_list()
-
-        for pool in pools:
-            try:
-                volumes = self.storagevolumes_get_list(pool)
-            except InvalidOperation:
-                # Skip inactive pools
-                continue
-            for volume in volumes:
-                res = self.storagevolume_lookup(pool, volume)
-                if res['format'] == 'iso':
-                    # prevent iso from different pool having same volume name
-                    res['name'] = '%s-%s' % (pool, volume)
-                    iso_volumes.append(res)
-        return iso_volumes
-
-    def dummy_interfaces(self):
-        interfaces = {}
-        ifaces = {"eth1": "nic", "bond0": "bonding",
-                  "eth1.10": "vlan", "bridge0": "bridge"}
-        for i, name in enumerate(ifaces.iterkeys()):
-            iface = Interface(name)
-            iface.info['type'] = ifaces[name]
-            iface.info['ipaddr'] = '192.168.%s.101' % (i + 1)
-            interfaces[name] = iface
-        interfaces['eth1'].info['ipaddr'] = '192.168.0.101'
-        return interfaces
-
-    def interfaces_get_list(self):
-        return self._mock_interfaces.keys()
-
-    def interface_lookup(self, name):
-        return self._mock_interfaces[name].info
-
-    def networks_create(self, params):
-        name = params['name']
-        if name in self.networks_get_list():
-            raise InvalidOperation("Network %s already exists" % name)
-        network = MockNetwork(name)
-        connection = params['connection']
-        network.info['connection'] = connection
-        if connection == "bridge":
-            try:
-                interface = params['interface']
-                network.info['interface'] = interface
-            except KeyError, key:
-                raise MissingParameter(key)
-
-        subnet = params.get('subnet', '')
-        if subnet:
-            network.info['subnet'] = subnet
-            try:
-                net = ipaddr.IPNetwork(subnet)
-            except ValueError, e:
-                raise InvalidParameter(e)
-
-            network.info['dhcp'] = {
-                'start': str(net.network + net.numhosts / 2),
-                'stop': str(net.network + net.numhosts - 2)}
-        if name in self._mock_networks:
-            raise InvalidOperation("Network already exists")
-        self._mock_networks[name] = network
-        return name
-
-    def _get_network(self, name):
-        try:
-            return self._mock_networks[name]
-        except KeyError:
-            raise NotFoundError("Network '%s'" % name)
-
-    def _get_vms_attach_to_a_network(self, network):
-        vms = []
-        for name, dom in self._mock_vms.iteritems():
-             if network in dom.networks:
-                 vms.append(name)
-        return vms
-
-    def network_lookup(self, name):
-        network = self._get_network(name)
-        network.info['vms'] = self._get_vms_attach_to_a_network(name)
-        return network.info
-
-    def network_activate(self, name):
-        self._get_network(name).info['state'] = 'active'
-
-    def network_deactivate(self, name):
-        self._get_network(name).info['state'] = 'inactive'
-
-    def network_delete(self, name):
-        # firstly, we should check the network actually exists
-        network = self._get_network(name)
-        del self._mock_networks[network.name]
-
-    def networks_get_list(self):
-        return sorted(self._mock_networks.keys())
-
-    def tasks_get_list(self):
-        with self.objstore as session:
-            return session.get_list('task')
-
-    def task_lookup(self, id):
-        with self.objstore as session:
-            return session.get('task', str(id))
-
-    def add_task(self, target_uri, fn, opaque=None):
-        id = self.next_taskid
-        self.next_taskid = self.next_taskid + 1
-        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
-
-        return id
-
-    def _get_storagevolume(self, pool, name):
-        try:
-            return self._get_storagepool(pool)._volumes[name]
-        except KeyError:
-            raise NotFoundError()
-
-    def _get_distros(self):
-        distroloader = DistroLoader()
-        return distroloader.get()
-
-    def distros_get_list(self):
-        return self.distros.keys()
-
-    def distro_lookup(self, name):
-        try:
-            return self.distros[name]
-        except KeyError:
-            raise NotFoundError("distro '%s' not found" % name)
-
-    def _gen_debugreport_file(self, ident):
-        return self.add_task('', self._create_log, ident)
-
-    def _create_log(self, cb, name):
-        path = config.get_debugreports_path()
-        tmpf = os.path.join(path, name + '.tmp')
-        realf = os.path.join(path, name + '.txt')
-        length = random.randint(1000, 10000)
-        with open(tmpf, 'w') as fd:
-            while length:
-                fd.write('I am logged')
-                length = length - 1
-        os.rename(tmpf, realf)
-        cb("OK", True)
-
-    def host_lookup(self, *name):
-        res = {}
-        res['memory'] = 6114058240
-        res['cpu'] = 'Intel(R) Core(TM) i5 CPU       M 560  @ 2.67GHz'
-        res['os_distro'] = 'Red Hat Enterprise Linux Server'
-        res['os_version'] = '6.4'
-        res['os_codename'] = 'Santiago'
-
-        return res
-
-    def hoststats_lookup(self, *name):
-        virt_mem = psutil.virtual_memory()
-        memory_stats = {'total': virt_mem.total,
-                        'free': virt_mem.free,
-                        'cached': virt_mem.cached,
-                        'buffers': virt_mem.buffers,
-                        'avail': virt_mem.available}
-        return {'cpu_utilization': round(random.uniform(0, 100), 1),
-                'memory': memory_stats,
-                'disk_read_rate': round(random.uniform(0, 4000), 1),
-                'disk_write_rate': round(random.uniform(0, 4000), 1),
-                'net_recv_rate': round(random.uniform(0, 4000), 1),
-                'net_sent_rate': round(random.uniform(0, 4000), 1)}
-
-    def vms_get_list_by_state(self, state):
-        ret_list = []
-        for name in self.vms_get_list():
-            if (self._mock_vms[name].info['state']) == state:
-                ret_list.append(name)
-        return ret_list
-
-    def host_shutdown(self, args=None):
-        # Check for running vms before shutdown
-        running_vms = self.vms_get_list_by_state('running')
-        if len(running_vms) > 0:
-            raise OperationFailed("Shutdown not allowed: VMs are running!")
-        cherrypy.engine.exit()
-
-    def host_reboot(self, args=None):
-        # Find running VMs
-        running_vms = self.vms_get_list_by_state('running')
-        if len(running_vms) > 0:
-            raise OperationFailed("Reboot not allowed: VMs are running!")
-        cherrypy.engine.stop()
-        time.sleep(10)
-        cherrypy.engine.start()
-
-    def partitions_get_list(self):
-        result = disks.get_partitions_names()
-        return result
-
-    def partition_lookup(self, name):
-        if name not in disks.get_partitions_names():
-            raise NotFoundError("Partition %s not found in the host"
-                                % name)
-        return disks.get_partition_details(name)
-
-class MockVMTemplate(VMTemplate):
-    def __init__(self, args, mockmodel_inst=None):
-        VMTemplate.__init__(self, args)
-        self.model = mockmodel_inst
-
-    def _storage_validate(self):
-        pool_uri = self.info['storagepool']
-        pool_name = kimchi.model.pool_name_from_uri(pool_uri)
-        try:
-            pool = self.model._get_storagepool(pool_name)
-        except NotFoundError:
-            raise InvalidParameter('Storage specified by template does not exist')
-        if pool.info['state'] != 'active':
-            raise InvalidParameter('Storage specified by template is not active')
-
-        return pool
-
-    def _get_storage_path(self):
-        pool = self._storage_validate()
-        return pool.info['path']
-
-    def fork_vm_storage(self, vm_name):
-        pool = self._storage_validate()
-        volumes = self.to_volume_list(vm_name)
-        disk_paths = []
-        for vol_info in volumes:
-            vol_info['capacity'] = vol_info['capacity'] << 10
-            self.model.storagevolumes_create(pool.name, vol_info)
-            disk_paths.append({'pool': pool.name, 'volume': vol_info['name']})
-        return disk_paths
-
-
-class MockVM(object):
-    def __init__(self, uuid, name, template_info):
-        self.uuid = uuid
-        self.name = name
-        self.disk_paths = []
-        self.networks = template_info['networks']
-        self.info = {'state': 'shutoff',
-                     'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \
-                                'net_throughput_peak': 100, 'io_throughput': 45, \
-                                'io_throughput_peak': 100}",
-                     'uuid': self.uuid,
-                     'memory': template_info['memory'],
-                     'cpus': template_info['cpus'],
-                     'icon': None,
-                     'graphics': {'type': 'vnc', 'listen': '0.0.0.0', 'port': None}
-                     }
-        self.info['graphics'].update(template_info['graphics'])
-
-
-class MockStoragePool(object):
-    def __init__(self, name):
-        self.name = name
-        self.info = {'state': 'inactive',
-                     'capacity': 1024 << 20,
-                     'allocated': 512 << 20,
-                     'available': 512 << 20,
-                     'path': '/var/lib/libvirt/images',
-                     'source': {},
-                     'type': 'dir',
-                     'nr_volumes': 0,
-                     'autostart': 0}
-        self._volumes = {}
-
-    def refresh(self):
-        state = self.info['state']
-        self.info['nr_volumes'] = len(self._volumes) \
-            if state == 'active' else 0
-
-
-class Interface(object):
-    def __init__(self, name):
-        self.name = name
-        self.info = {'type': 'nic',
-                     'ipaddr': '192.168.0.101',
-                     'netmask': '255.255.255.0',
-                     'status': 'active'}
-
-
-class MockNetwork(object):
-    def __init__(self, name):
-        self.name = name
-        self.info = {'state': 'inactive',
-                     'autostart': True,
-                     'connection': 'nat',
-                     'interface': 'virbr0',
-                     'subnet': '192.168.122.0/24',
-                     'dhcp': {'start': '192.168.122.128',
-                              'stop':  '192.168.122.254'},
-                     }
-
-
-class MockTask(object):
-    def __init__(self, id):
-        self.id = id
-
-class MockStorageVolume(object):
-    def __init__(self, pool, name, params={}):
-        self.name = name
-        self.pool = pool
-        fmt = params.get('format', 'raw')
-        capacity = params.get('capacity', 1024)
-        self.info = {'type': 'disk',
-                     'capacity': capacity << 20,
-                     'allocation': 512,
-                     'format': fmt}
-        if fmt == 'iso':
-            self.info['allocation'] = self.info['capacity']
-            self.info['os_version'] = '17'
-            self.info['os_distro'] = 'fedora'
-            self.info['bootable'] = True
-
-
-class MockVMScreenshot(VMScreenshot):
-    OUTDATED_SECS = 5
-    BACKGROUND_COLOR = ['blue', 'green', 'purple', 'red', 'yellow']
-    BOX_COORD = (50, 115, 206, 141)
-    BAR_COORD = (50, 115, 50, 141)
-
-    def __init__(self, vm_name):
-        VMScreenshot.__init__(self, vm_name)
-        self.coord = MockVMScreenshot.BAR_COORD
-        self.background = random.choice(MockVMScreenshot.BACKGROUND_COLOR)
-
-    def _generate_scratch(self, thumbnail):
-        self.coord = (self.coord[0],
-                      self.coord[1],
-                      min(MockVMScreenshot.BOX_COORD[2],
-                          self.coord[2]+random.randrange(50)),
-                      self.coord[3])
-
-        image = Image.new("RGB", (256, 256), self.background)
-        d = ImageDraw.Draw(image)
-        d.rectangle(MockVMScreenshot.BOX_COORD, outline='black')
-        d.rectangle(self.coord, outline='black', fill='black')
-        image.save(thumbnail)
-
-
-def get_mock_environment():
-    model = MockModel()
-    for i in xrange(5):
-        name = 'test-template-%i' % i
-        params = {'name': name}
-        t = MockVMTemplate(params, model)
-        model._mock_templates[name] = t
-
-    for name in ('test-template-1', 'test-template-3'):
-        model._mock_templates[name].info.update({'folder': ['rhel', '6']})
-
-    for i in xrange(10):
-        name = u'test-vm-%i' % i
-        vm_uuid = str(uuid.uuid4())
-        vm = MockVM(vm_uuid, name, model.template_lookup('test-template-0'))
-        model._mock_vms[name] = vm
-
-    #mock storagepool
-    for i in xrange(5):
-        name = 'default-pool-%i' % i
-        defaultstoragepool = MockStoragePool(name)
-        defaultstoragepool.info['path'] += '/%i' % i
-        model._mock_storagepools[name] = defaultstoragepool
-        for j in xrange(5):
-            vol_name = 'volume-%i' % j
-            defaultstoragevolume = MockStorageVolume(name, vol_name)
-            defaultstoragevolume.info['path'] = '%s/%s' % (
-                defaultstoragepool.info['path'], vol_name)
-            mockpool = model._mock_storagepools[name]
-            mockpool._volumes[vol_name] = defaultstoragevolume
-        vol_name = 'Fedora17.iso'
-        defaultstoragevolume = MockStorageVolume(name, vol_name,
-                                                 {'format': 'iso'})
-        defaultstoragevolume.info['path'] = '%s/%s' % (
-            defaultstoragepool.info['path'], vol_name)
-        mockpool = model._mock_storagepools[name]
-        mockpool._volumes[vol_name] = defaultstoragevolume
-
-    #mock network
-    for i in xrange(5):
-        name = 'test-network-%i' % i
-        testnetwork = MockNetwork(name)
-        testnetwork.info['interface'] = 'virbr%i' % (i + 1)
-        testnetwork.info['subnet'] = '192.168.%s.0/24' % (i + 1)
-        testnetwork.info['dhcp']['start'] = '192.168.%s.128' % (i + 1)
-        testnetwork.info['dhcp']['end'] = '192.168.%s.254' % (i + 1)
-        model._mock_networks[name] = testnetwork
-
-    return model
diff --git a/src/kimchi/model.py b/src/kimchi/model.py
deleted file mode 100644
index 7b0eafc..0000000
--- a/src/kimchi/model.py
+++ /dev/null
@@ -1,1536 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-import cherrypy
-import copy
-import disks
-import fnmatch
-import functools
-import glob
-import ipaddr
-import json
-import libvirt
-import logging
-import os
-import platform
-import psutil
-import re
-import shutil
-import subprocess
-import sys
-import threading
-import time
-import uuid
-
-
-from cherrypy.process.plugins import BackgroundTask
-from cherrypy.process.plugins import SimplePlugin
-from collections import defaultdict
-from xml.etree import ElementTree
-
-
-try:
-    from collections import OrderedDict
-except ImportError:
-    from ordereddict import OrderedDict
-
-
-from kimchi import config
-from kimchi import netinfo
-from kimchi import network as knetwork
-from kimchi import networkxml
-from kimchi import vnc
-from kimchi import xmlutils
-from kimchi.asynctask import AsyncTask
-from kimchi.distroloader import DistroLoader
-from kimchi.exception import InvalidOperation, InvalidParameter, IsoFormatError
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
-from kimchi.featuretests import FeatureTests
-from kimchi.isoinfo import IsoImage
-from kimchi.model_.libvirtconnection import LibvirtConnection
-from kimchi.model_.libvirtstoragepool import StoragePoolDef
-from kimchi.objectstore import ObjectStore
-from kimchi.scan import Scanner
-from kimchi.screenshot import VMScreenshot
-from kimchi.utils import get_enabled_plugins, is_digit, kimchi_log
-from kimchi.vmtemplate import VMTemplate
-
-
-ISO_POOL_NAME = u'kimchi_isos'
-GUESTS_STATS_INTERVAL = 5
-HOST_STATS_INTERVAL = 1
-VM_STATIC_UPDATE_PARAMS = {'name': './name'}
-VM_LIVE_UPDATE_PARAMS = {}
-STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
-                             'path': '/pool/source/dir/@path'}}
-
-
-def _uri_to_name(collection, uri):
-    expr = '/%s/(.*?)/?$' % collection
-    m = re.match(expr, uri)
-    if not m:
-        raise InvalidParameter(uri)
-    return m.group(1)
-
-def template_name_from_uri(uri):
-    return _uri_to_name('templates', uri)
-
-def pool_name_from_uri(uri):
-    return _uri_to_name('storagepools', uri)
-
-def get_vm_name(vm_name, t_name, name_list):
-    if vm_name:
-        return vm_name
-    for i in xrange(1, 1000):
-        vm_name = "%s-vm-%i" % (t_name, i)
-        if vm_name not in name_list:
-            return vm_name
-    raise OperationFailed("Unable to choose a VM name")
-
-class Model(object):
-    dom_state_map = {0: 'nostate',
-                     1: 'running',
-                     2: 'blocked',
-                     3: 'paused',
-                     4: 'shutdown',
-                     5: 'shutoff',
-                     6: 'crashed'}
-
-    pool_state_map = {0: 'inactive',
-                      1: 'initializing',
-                      2: 'active',
-                      3: 'degraded',
-                      4: 'inaccessible'}
-
-    volume_type_map = {0: 'file',
-                       1: 'block',
-                       2: 'directory',
-                       3: 'network'}
-
-    def __init__(self, libvirt_uri=None, objstore_loc=None):
-        self.libvirt_uri = libvirt_uri or 'qemu:///system'
-        self.conn = LibvirtConnection(self.libvirt_uri)
-        self.objstore = ObjectStore(objstore_loc)
-        self.next_taskid = 1
-        self.stats = {}
-        self.host_stats = defaultdict(int)
-        self.host_info = {}
-        self.qemu_stream = False
-        self.qemu_stream_dns = False
-        self.libvirt_stream_protocols = []
-        # Subscribe function to set host capabilities to be run when cherrypy
-        # server is up
-        # It is needed because some features tests depends on the server
-        cherrypy.engine.subscribe('start', self._set_capabilities)
-        self.scanner = Scanner(self._clean_scan)
-        self.scanner.delete()
-        self.guests_stats_thread = BackgroundTask(GUESTS_STATS_INTERVAL,
-                                                  self._update_guests_stats)
-        self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
-                                                self._update_host_stats)
-        self.guests_stats_thread.start()
-        self.host_stats_thread.start()
-
-        # Please add new possible debug report command here
-        # and implement the report generating function
-        # based on the new report command
-        self.report_tools = ({'cmd': 'sosreport --help', 'fn': self._sosreport_generate},
-                             {'cmd': 'supportconfig -h', 'fn':None},
-                             {'cmd': 'linuxexplorers --help', 'fn':None})
-
-        self.distros = self._get_distros()
-        if 'qemu:///' in self.libvirt_uri:
-            self.host_info = self._get_host_info()
-            self._default_pool_check()
-            self._default_network_check()
-
-    def _default_network_check(self):
-        conn = self.conn.get()
-        xml = """
-             <network>
-              <name>default</name>
-              <forward mode='nat'/>
-              <bridge name='virbr0' stp='on' delay='0' />
-              <ip address='192.168.122.1' netmask='255.255.255.0'>
-                <dhcp>
-                  <range start='192.168.122.2' end='192.168.122.254' />
-                </dhcp>
-              </ip>
-            </network>
-        """
-        try:
-            net = conn.networkLookupByName("default")
-        except libvirt.libvirtError:
-            try:
-                net = conn.networkDefineXML(xml)
-            except libvirt.libvirtError, e:
-                cherrypy.log.error(
-                    "Fatal: Cannot create default network because of %s, exit kimchid" % e.message,
-                    severity=logging.ERROR)
-                sys.exit(1)
-
-        if net.isActive() == 0:
-            try:
-                net.create()
-            except libvirt.libvirtError, e:
-                cherrypy.log.error(
-                    "Fatal: Cannot activate default network because of %s, exit kimchid" % e.message,
-                    severity=logging.ERROR)
-                sys.exit(1)
-
-    def _default_pool_check(self):
-        default_pool = {'name': 'default',
-                        'path': '/var/lib/libvirt/images',
-                        'type': 'dir'}
-        try:
-            self.storagepools_create(default_pool)
-        except InvalidOperation:
-            # ignore error when pool existed
-            pass
-        except OperationFailed as e:
-            # path used by other pool or other reasons of failure, exit
-            cherrypy.log.error(
-                "Fatal: Cannot create default pool because of %s, exit kimchid" % e.message,
-                severity=logging.ERROR)
-            sys.exit(1)
-
-        if self.storagepool_lookup('default')['state'] == 'inactive':
-            try:
-                self.storagepool_activate('default')
-            except OperationFailed:
-                cherrypy.log.error(
-                    "Fatal: Default pool cannot be activated, exit kimchid",
-                    severity=logging.ERROR)
-                sys.exit(1)
-
-    def _set_capabilities(self):
-        kimchi_log.info("*** Running feature tests ***")
-        self.qemu_stream = FeatureTests.qemu_supports_iso_stream()
-        self.qemu_stream_dns = FeatureTests.qemu_iso_stream_dns()
-
-        self.libvirt_stream_protocols = []
-        for p in ['http', 'https', 'ftp', 'ftps', 'tftp']:
-            if FeatureTests.libvirt_supports_iso_stream(p):
-                self.libvirt_stream_protocols.append(p)
-
-        kimchi_log.info("*** Feature tests completed ***")
-    _set_capabilities.priority = 90
-
-    def get_capabilities(self):
-        report_tool = self._get_system_report_tool()
-
-        return {'libvirt_stream_protocols': self.libvirt_stream_protocols,
-                'qemu_stream': self.qemu_stream,
-                'screenshot': VMScreenshot.get_stream_test_result(),
-                'system_report_tool': bool(report_tool)}
-
-    def _update_guests_stats(self):
-        vm_list = self.vms_get_list()
-
-        for name in vm_list:
-            dom = self._get_vm(name)
-            vm_uuid = dom.UUIDString()
-            info = dom.info()
-            state = Model.dom_state_map[info[0]]
-
-            if state != 'running':
-                self.stats[vm_uuid] = {}
-                continue
-
-            if self.stats.get(vm_uuid, None) is None:
-                self.stats[vm_uuid] = {}
-
-            timestamp = time.time()
-            prevStats = self.stats.get(vm_uuid, {})
-            seconds = timestamp - prevStats.get('timestamp', 0)
-            self.stats[vm_uuid].update({'timestamp': timestamp})
-
-            self._get_percentage_cpu_usage(vm_uuid, info, seconds)
-            self._get_network_io_rate(vm_uuid, dom, seconds)
-            self._get_disk_io_rate(vm_uuid, dom, seconds)
-
-    def _get_host_info(self):
-        res = {}
-        with open('/proc/cpuinfo') as f:
-            for line in f.xreadlines():
-                if "model name" in line:
-                    res['cpu'] = line.split(':')[1].strip()
-                    break
-
-        res['memory'] = psutil.TOTAL_PHYMEM
-        # 'fedora' '17' 'Beefy Miracle'
-        distro, version, codename = platform.linux_distribution()
-        res['os_distro'] = distro
-        res['os_version'] = version
-        res['os_codename'] = unicode(codename,"utf-8")
-
-        return res
-
-    def _get_percentage_cpu_usage(self, vm_uuid, info, seconds):
-        prevCpuTime = self.stats[vm_uuid].get('cputime', 0)
-
-        cpus = info[3]
-        cpuTime = info[4] - prevCpuTime
-
-        base = (((cpuTime) * 100.0) / (seconds * 1000.0 * 1000.0 * 1000.0))
-        percentage = max(0.0, min(100.0, base / cpus))
-
-        self.stats[vm_uuid].update({'cputime': info[4], 'cpu': percentage})
-
-    def _get_network_io_rate(self, vm_uuid, dom, seconds):
-        prevNetRxKB = self.stats[vm_uuid].get('netRxKB', 0)
-        prevNetTxKB = self.stats[vm_uuid].get('netTxKB', 0)
-        currentMaxNetRate = self.stats[vm_uuid].get('max_net_io', 100)
-
-        rx_bytes = 0
-        tx_bytes = 0
-
-        tree = ElementTree.fromstring(dom.XMLDesc(0))
-        for target in tree.findall('devices/interface/target'):
-            dev = target.get('dev')
-            io = dom.interfaceStats(dev)
-            rx_bytes += io[0]
-            tx_bytes += io[4]
-
-        netRxKB = float(rx_bytes) / 1000
-        netTxKB = float(tx_bytes) / 1000
-
-        rx_stats = (netRxKB - prevNetRxKB) / seconds
-        tx_stats = (netTxKB - prevNetTxKB) / seconds
-
-        rate = rx_stats + tx_stats
-        max_net_io = round(max(currentMaxNetRate, int(rate)), 1)
-
-        self.stats[vm_uuid].update({'net_io': rate, 'max_net_io': max_net_io,
-                                 'netRxKB': netRxKB, 'netTxKB': netTxKB})
-
-    def _get_disk_io_rate(self, vm_uuid, dom, seconds):
-        prevDiskRdKB = self.stats[vm_uuid].get('diskRdKB', 0)
-        prevDiskWrKB = self.stats[vm_uuid].get('diskWrKB', 0)
-        currentMaxDiskRate = self.stats[vm_uuid].get('max_disk_io', 100)
-
-        rd_bytes = 0
-        wr_bytes = 0
-
-        tree = ElementTree.fromstring(dom.XMLDesc(0))
-        for target in tree.findall("devices/disk/target"):
-            dev = target.get("dev")
-            io = dom.blockStats(dev)
-            rd_bytes += io[1]
-            wr_bytes += io[3]
-
-        diskRdKB = float(rd_bytes) / 1024
-        diskWrKB = float(wr_bytes) / 1024
-
-        rd_stats = (diskRdKB - prevDiskRdKB) / seconds
-        wr_stats = (diskWrKB - prevDiskWrKB) / seconds
-
-        rate = rd_stats + wr_stats
-        max_disk_io = round(max(currentMaxDiskRate, int(rate)), 1)
-
-        self.stats[vm_uuid].update({'disk_io': rate, 'max_disk_io': max_disk_io,
-                                 'diskRdKB': diskRdKB, 'diskWrKB': diskWrKB})
-
-    def debugreport_lookup(self, name):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, name)
-        file_pattern = file_pattern + '.*'
-        try:
-            file_target = glob.glob(file_pattern)[0]
-        except IndexError:
-            raise NotFoundError('no such report')
-
-        ctime = os.stat(file_target).st_ctime
-        ctime = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime(ctime))
-        file_target = os.path.split(file_target)[-1]
-        file_target = os.path.join("/data/debugreports", file_target)
-        return {'file': file_target,
-                'ctime': ctime}
-
-    def debugreportcontent_lookup(self, name):
-        return self.debugreport_lookup(name)
-
-    def debugreport_delete(self, name):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, name + '.*')
-        try:
-            file_target = glob.glob(file_pattern)[0]
-        except IndexError:
-            raise NotFoundError('no such report')
-
-        os.remove(file_target)
-
-    def debugreports_create(self, params):
-        ident = params['name']
-        taskid = self._gen_debugreport_file(ident)
-        return self.task_lookup(taskid)
-
-    def debugreports_get_list(self):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, '*.*')
-        file_lists = glob.glob(file_pattern)
-        file_lists = [os.path.split(file)[1] for file in file_lists]
-        name_lists = [file.split('.', 1)[0] for file in file_lists]
-
-        return name_lists
-
-    def  _update_host_stats(self):
-        preTimeStamp = self.host_stats['timestamp']
-        timestamp = time.time()
-        # FIXME when we upgrade psutil, we can get uptime by psutil.uptime
-        # we get uptime by float(open("/proc/uptime").readline().split()[0])
-        # and calculate the first io_rate after the OS started.
-        seconds = (timestamp - preTimeStamp if preTimeStamp else
-                   float(open("/proc/uptime").readline().split()[0]))
-
-        self.host_stats['timestamp'] = timestamp
-        self._get_host_disk_io_rate(seconds)
-        self._get_host_network_io_rate(seconds)
-
-        self._get_percentage_host_cpu_usage()
-        self._get_host_memory_stats()
-
-    def _get_percentage_host_cpu_usage(self):
-        # This is cpu usage producer. This producer will calculate the usage
-        # at an interval of HOST_STATS_INTERVAL.
-        # The psutil.cpu_percent works as non blocking.
-        # psutil.cpu_percent maintains a cpu time sample.
-        # It will update the cpu time sample when it is called.
-        # So only this producer can call psutil.cpu_percent in kimchi.
-        self.host_stats['cpu_utilization'] = psutil.cpu_percent(None)
-
-    def _get_host_memory_stats(self):
-        virt_mem = psutil.virtual_memory()
-        # available:
-        #  the actual amount of available memory that can be given
-        #  instantly to processes that request more memory in bytes; this
-        #  is calculated by summing different memory values depending on
-        #  the platform (e.g. free + buffers + cached on Linux)
-        memory_stats = {'total': virt_mem.total,
-                        'free': virt_mem.free,
-                        'cached': virt_mem.cached,
-                        'buffers': virt_mem.buffers,
-                        'avail': virt_mem.available}
-        self.host_stats['memory'] = memory_stats
-
-    def _get_host_disk_io_rate(self, seconds):
-        prev_read_bytes = self.host_stats['disk_read_bytes']
-        prev_write_bytes = self.host_stats['disk_write_bytes']
-
-        disk_io = psutil.disk_io_counters(False)
-        read_bytes = disk_io.read_bytes
-        write_bytes = disk_io.write_bytes
-
-        rd_rate = int(float(read_bytes - prev_read_bytes) / seconds + 0.5)
-        wr_rate = int(float(write_bytes - prev_write_bytes) / seconds + 0.5)
-
-        self.host_stats.update({'disk_read_rate': rd_rate,
-                                'disk_write_rate': wr_rate,
-                                'disk_read_bytes': read_bytes,
-                                'disk_write_bytes': write_bytes})
-
-    def _get_host_network_io_rate(self, seconds):
-        prev_recv_bytes = self.host_stats['net_recv_bytes']
-        prev_sent_bytes = self.host_stats['net_sent_bytes']
-
-        net_ios = psutil.network_io_counters(True)
-        recv_bytes = 0
-        sent_bytes = 0
-        for key in set(netinfo.nics() +
-                       netinfo.wlans()) & set(net_ios.iterkeys()):
-            recv_bytes = recv_bytes + net_ios[key].bytes_recv
-            sent_bytes = sent_bytes + net_ios[key].bytes_sent
-
-        rx_rate = int(float(recv_bytes - prev_recv_bytes) / seconds + 0.5)
-        tx_rate = int(float(sent_bytes - prev_sent_bytes) / seconds + 0.5)
-
-        self.host_stats.update({'net_recv_rate': rx_rate,
-                                'net_sent_rate': tx_rate,
-                                'net_recv_bytes': recv_bytes,
-                                'net_sent_bytes': sent_bytes})
-
-    def _static_vm_update(self, dom, params):
-        state = Model.dom_state_map[dom.info()[0]]
-
-        old_xml = new_xml = dom.XMLDesc(0)
-
-        for key, val in params.items():
-            if key in VM_STATIC_UPDATE_PARAMS:
-                new_xml = xmlutils.xml_item_update(new_xml, VM_STATIC_UPDATE_PARAMS[key], val)
-
-        try:
-            if 'name' in params:
-                if state == 'running':
-                    raise InvalidParameter("vm name can just updated when vm shutoff")
-                else:
-                    dom.undefine()
-            conn = self.conn.get()
-            dom = conn.defineXML(new_xml)
-        except libvirt.libvirtError as e:
-            dom = conn.defineXML(old_xml)
-            raise OperationFailed(e.get_error_message())
-        return dom
-
-    def _live_vm_update(self, dom, params):
-        pass
-
-    def vm_update(self, name, params):
-        dom = self._get_vm(name)
-        dom = self._static_vm_update(dom, params)
-        self._live_vm_update(dom, params)
-        return dom.name()
-
-    def vm_lookup(self, name):
-        dom = self._get_vm(name)
-        info = dom.info()
-        state = Model.dom_state_map[info[0]]
-        screenshot = None
-        graphics_type, graphics_listen, graphics_port = self._vm_get_graphics(name)
-        graphics_port = graphics_port if state == 'running' else None
-        try:
-            if state == 'running':
-                screenshot = self.vmscreenshot_lookup(name)
-            elif state == 'shutoff':
-                # reset vm stats when it is powered off to avoid sending
-                # incorrect (old) data
-                self.stats[dom.UUIDString()] = {}
-        except NotFoundError:
-            pass
-
-        with self.objstore as session:
-            try:
-                extra_info = session.get('vm', dom.UUIDString())
-            except NotFoundError:
-                extra_info = {}
-        icon = extra_info.get('icon')
-
-        vm_stats = self.stats.get(dom.UUIDString(), {})
-        stats = {}
-        stats['cpu_utilization'] = vm_stats.get('cpu', 0)
-        stats['net_throughput'] = vm_stats.get('net_io', 0)
-        stats['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
-        stats['io_throughput'] = vm_stats.get('disk_io', 0)
-        stats['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
-
-        return {'state': state,
-                'stats': str(stats),
-                'uuid': dom.UUIDString(),
-                'memory': info[2] >> 10,
-                'cpus': info[3],
-                'screenshot': screenshot,
-                'icon': icon,
-                'graphics': {"type": graphics_type,
-                             "listen": graphics_listen,
-                             "port": graphics_port}
-                }
-
-    def _vm_get_disk_paths(self, dom):
-        xml = dom.XMLDesc(0)
-        xpath = "/domain/devices/disk[@device='disk']/source/@file"
-        return xmlutils.xpath_get_text(xml, xpath)
-
-    def _vm_get_networks(self, dom):
-        xml = dom.XMLDesc(0)
-        xpath = "/domain/devices/interface[@type='network']/source/@network"
-        return xmlutils.xpath_get_text(xml, xpath)
-
-    def vm_delete(self, name):
-        if self._vm_exists(name):
-            conn = self.conn.get()
-            dom = self._get_vm(name)
-            self._vmscreenshot_delete(dom.UUIDString())
-            paths = self._vm_get_disk_paths(dom)
-            info = self.vm_lookup(name)
-
-            if info['state'] == 'running':
-                self.vm_stop(name)
-
-            dom.undefine()
-
-            for path in paths:
-                vol = conn.storageVolLookupByPath(path)
-                vol.delete(0)
-
-            with self.objstore as session:
-                session.delete('vm', dom.UUIDString(), ignore_missing=True)
-
-            vnc.remove_proxy_token(name)
-
-    def vm_start(self, name):
-        dom = self._get_vm(name)
-        dom.create()
-
-    def vm_stop(self, name):
-        if self._vm_exists(name):
-            dom = self._get_vm(name)
-            dom.destroy()
-
-    def _vm_get_graphics(self, name):
-        dom = self._get_vm(name)
-        xml = dom.XMLDesc(0)
-        expr = "/domain/devices/graphics/@type"
-        res = xmlutils.xpath_get_text(xml, expr)
-        graphics_type = res[0] if res else None
-        expr = "/domain/devices/graphics/@listen"
-        res = xmlutils.xpath_get_text(xml, expr)
-        graphics_listen = res[0] if res else None
-        graphics_port = None
-        if graphics_type:
-            expr = "/domain/devices/graphics[@type='%s']/@port" % graphics_type
-            res = xmlutils.xpath_get_text(xml, expr)
-            graphics_port = int(res[0]) if res else None
-        return graphics_type, graphics_listen, graphics_port
-
-    def vm_connect(self, name):
-        graphics_type, graphics_listen, graphics_port \
-            = self._vm_get_graphics(name)
-        if graphics_port is not None:
-            vnc.add_proxy_token(name, graphics_port)
-        else:
-            raise OperationFailed("Only able to connect to running vm's vnc "
-                                  "graphics.")
-
-    def vms_create(self, params):
-        conn = self.conn.get()
-        t_name = template_name_from_uri(params['template'])
-        vm_uuid = str(uuid.uuid4())
-        vm_list = self.vms_get_list()
-        name = get_vm_name(params.get('name'), t_name, vm_list)
-        # incoming text, from js json, is unicode, do not need decode
-        if name in vm_list:
-            raise InvalidOperation("VM already exists")
-
-        vm_overrides = dict()
-        pool_uri = params.get('storagepool')
-        if pool_uri:
-            vm_overrides['storagepool'] = pool_uri
-        t = self._get_template(t_name, vm_overrides)
-
-        if not self.qemu_stream and t.info.get('iso_stream', False):
-            raise InvalidOperation("Remote ISO image is not supported by this server.")
-
-        t.validate()
-        vol_list = t.fork_vm_storage(vm_uuid)
-
-        # Store the icon for displaying later
-        icon = t.info.get('icon')
-        if icon:
-            with self.objstore as session:
-                session.store('vm', vm_uuid, {'icon': icon})
-
-        libvirt_stream = False if len(self.libvirt_stream_protocols) == 0 else True
-        graphics = params.get('graphics')
-
-        xml = t.to_vm_xml(name, vm_uuid,
-                          libvirt_stream=libvirt_stream,
-                          qemu_stream_dns=self.qemu_stream_dns,
-                          graphics=graphics)
-        try:
-            dom = conn.defineXML(xml.encode('utf-8'))
-        except libvirt.libvirtError as e:
-            for v in vol_list:
-                vol = conn.storageVolLookupByPath(v['path'])
-                vol.delete(0)
-            raise OperationFailed(e.get_error_message())
-        return name
-
-    def vms_get_list(self):
-        conn = self.conn.get()
-        ids = conn.listDomainsID()
-        names = map(lambda x: conn.lookupByID(x).name(), ids)
-        names += conn.listDefinedDomains()
-        names = map(lambda x: x.decode('utf-8'), names)
-        return sorted(names, key=unicode.lower)
-
-    def vmscreenshot_lookup(self, name):
-        dom = self._get_vm(name)
-        d_info = dom.info()
-        vm_uuid = dom.UUIDString()
-        if Model.dom_state_map[d_info[0]] != 'running':
-            raise NotFoundError('No screenshot for stopped vm')
-
-        screenshot = self._get_screenshot(vm_uuid)
-        img_path = screenshot.lookup()
-        # screenshot info changed after scratch generation
-        with self.objstore as session:
-            session.store('screenshot', vm_uuid, screenshot.info)
-        return img_path
-
-    def _vmscreenshot_delete(self, vm_uuid):
-        screenshot = self._get_screenshot(vm_uuid)
-        screenshot.delete()
-        with self.objstore as session:
-            session.delete('screenshot', vm_uuid)
-
-    def template_lookup(self, name):
-        t = self._get_template(name)
-        return t.info
-
-    def template_delete(self, name):
-        with self.objstore as session:
-            session.delete('template', name)
-
-    def templates_create(self, params):
-        name = params['name']
-        for net_name in params.get(u'networks', []):
-            try:
-                self._get_network(net_name)
-            except NotFoundError:
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist" % net_name)
-
-        with self.objstore as session:
-            if name in session.get_list('template'):
-                raise InvalidOperation("Template already exists")
-            t = LibvirtVMTemplate(params, scan=True)
-            session.store('template', name, t.info)
-        return name
-
-    def template_update(self, name, params):
-        old_t = self.template_lookup(name)
-        new_t = copy.copy(old_t)
-
-        new_t.update(params)
-        ident = name
-
-        new_storagepool = new_t.get(u'storagepool', '')
-        try:
-            self._get_storagepool(pool_name_from_uri(new_storagepool))
-        except Exception as e:
-            raise InvalidParameter("Storagepool specified is not valid: %s." % e.message)
-
-        for net_name in params.get(u'networks', []):
-            try:
-                self._get_network(net_name)
-            except NotFoundError:
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist" % net_name)
-
-        self.template_delete(name)
-        try:
-            ident = self.templates_create(new_t)
-        except:
-            ident = self.templates_create(old_t)
-            raise
-        return ident
-
-    def templates_get_list(self):
-        with self.objstore as session:
-            return session.get_list('template')
-
-    def interfaces_get_list(self):
-        return list(set(netinfo.all_favored_interfaces()) -
-                    set(self._get_all_networks_interfaces()))
-
-    def interface_lookup(self, name):
-        try:
-            return netinfo.get_interface_info(name)
-        except ValueError, e:
-            raise NotFoundError(e)
-
-    def _get_network(self, name):
-        conn = self.conn.get()
-        try:
-            return conn.networkLookupByName(name)
-        except libvirt.libvirtError as e:
-            raise NotFoundError("Network '%s' not found: %s" %
-                                (name, e.get_error_message()))
-
-    def _get_network_from_xml(self, xml):
-        address = xmlutils.xpath_get_text(xml, "/network/ip/@address")
-        address = address and address[0] or ''
-        netmask = xmlutils.xpath_get_text(xml, "/network/ip/@netmask")
-        netmask = netmask and netmask[0] or ''
-        net = address and netmask and "/".join([address, netmask]) or ''
-
-        dhcp_start = xmlutils.xpath_get_text(xml,
-                                             "/network/ip/dhcp/range/@start")
-        dhcp_start = dhcp_start and dhcp_start[0] or ''
-        dhcp_end = xmlutils.xpath_get_text(xml, "/network/ip/dhcp/range/@end")
-        dhcp_end = dhcp_end and dhcp_end[0] or ''
-        dhcp = {'start': dhcp_start, 'end': dhcp_end}
-
-        forward_mode = xmlutils.xpath_get_text(xml, "/network/forward/@mode")
-        forward_mode = forward_mode and forward_mode[0] or ''
-        forward_if = xmlutils.xpath_get_text(xml,
-                                             "/network/forward/interface/@dev")
-        forward_pf = xmlutils.xpath_get_text(xml, "/network/forward/pf/@dev")
-        bridge = xmlutils.xpath_get_text(xml, "/network/bridge/@name")
-        bridge = bridge and bridge[0] or ''
-        return {'subnet': net, 'dhcp': dhcp, 'bridge': bridge,
-                'forward': {'mode': forward_mode,
-                            'interface': forward_if,
-                            'pf': forward_pf}}
-
-    def _get_all_networks_interfaces(self):
-        net_names = self.networks_get_list()
-        interfaces = []
-        for name in net_names:
-            network = self._get_network(name)
-            xml = network.XMLDesc(0)
-            net_dict = self._get_network_from_xml(xml)
-            forward = net_dict['forward']
-            (forward['mode'] == 'bridge' and forward['interface'] and
-             interfaces.append(forward['interface'][0]) is None or
-             interfaces.extend(forward['interface'] + forward['pf']))
-            net_dict['bridge'] and interfaces.append(net_dict['bridge'])
-        return interfaces
-
-    def _set_network_subnet(self, params):
-        netaddr = params.get('subnet', '')
-        net_addrs = []
-        # lookup a free network address for nat and isolated automatically
-        if not netaddr:
-            for net_name in self.networks_get_list():
-                network = self._get_network(net_name)
-                xml = network.XMLDesc(0)
-                subnet = self._get_network_from_xml(xml)['subnet']
-                subnet and net_addrs.append(ipaddr.IPNetwork(subnet))
-            netaddr = knetwork.get_one_free_network(net_addrs)
-            if not netaddr:
-                raise OperationFailed("can not find a free IP address "
-                                      "for network '%s'" %
-                                      params['name'])
-        try:
-            ip = ipaddr.IPNetwork(netaddr)
-        except ValueError as e:
-            raise InvalidParameter("%s" % e)
-        if ip.ip == ip.network:
-            ip.ip = ip.ip + 1
-        dhcp_start = str(ip.ip + ip.numhosts / 2)
-        dhcp_end = str(ip.ip + ip.numhosts - 2)
-        params.update({'subnet': str(ip),
-                       'dhcp': {'range': {'start': dhcp_start,
-                                'end': dhcp_end}}})
-
-    def _set_network_bridge(self, params):
-        try:
-            iface = params['interface']
-            if iface in self._get_all_networks_interfaces():
-                raise InvalidParameter("interface '%s' already in use." %
-                                       iface)
-        except KeyError, e:
-            raise MissingParameter(e)
-        if netinfo.is_bridge(iface):
-            params['bridge'] = iface
-        elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface):
-            if params.get('vlan_id') is None:
-                params['forward']['dev'] = iface
-            else:
-                params['bridge'] = \
-                    self._create_vlan_tagged_bridge(str(iface),
-                                                    str(params['vlan_id']))
-        else:
-            raise InvalidParameter("the interface should be bare nic, "
-                                   "bonding or bridge device.")
-
-    def networks_create(self, params):
-        conn = self.conn.get()
-        name = params['name']
-        if name in self.networks_get_list():
-            raise InvalidOperation("Network %s already exists" % name)
-
-        connection = params["connection"]
-        # set forward mode, isolated do not need forward
-        if connection != 'isolated':
-            params['forward'] = {'mode': connection}
-
-        # set subnet, bridge network do not need subnet
-        if connection in ["nat", 'isolated']:
-            self._set_network_subnet(params)
-
-        # only bridge network need bridge(linux bridge) or interface(macvtap)
-        if connection == 'bridge':
-            self._set_network_bridge(params)
-
-        xml = networkxml.to_network_xml(**params)
-
-        try:
-            network = conn.networkDefineXML(xml)
-            network.setAutostart(True)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-        return name
-
-    def networks_get_list(self):
-        conn = self.conn.get()
-        return sorted(conn.listNetworks() + conn.listDefinedNetworks())
-
-    def _get_vms_attach_to_a_network(self, network):
-        vms = []
-        conn = self.conn.get()
-        for dom in conn.listAllDomains(0):
-            networks = self._vm_get_networks(dom)
-            if network in networks:
-                vms.append(dom.name())
-        return vms
-
-    def network_lookup(self, name):
-        network = self._get_network(name)
-        xml = network.XMLDesc(0)
-        net_dict = self._get_network_from_xml(xml)
-        subnet = net_dict['subnet']
-        dhcp = net_dict['dhcp']
-        forward = net_dict['forward']
-        interface = net_dict['bridge']
-
-        connection = forward['mode'] or "isolated"
-        # FIXME, if we want to support other forward mode well.
-        if connection == 'bridge':
-            # macvtap bridge
-            interface = interface or forward['interface'][0]
-            # exposing the network on linux bridge or macvtap interface
-            interface_subnet = knetwork.get_dev_netaddr(interface)
-            subnet = subnet if subnet else interface_subnet
-
-        # libvirt use format 192.168.0.1/24, standard should be 192.168.0.0/24
-        # http://www.ovirt.org/File:Issue3.png
-        if subnet:
-            subnet = ipaddr.IPNetwork(subnet)
-            subnet = "%s/%s" % (subnet.network, subnet.prefixlen)
-
-        return {'connection': connection,
-                'interface': interface,
-                'subnet': subnet,
-                'dhcp': dhcp,
-                'vms': self._get_vms_attach_to_a_network(name),
-                'autostart': network.autostart() == 1,
-                'state':  network.isActive() and "active" or "inactive"}
-
-    def network_activate(self, name):
-        network = self._get_network(name)
-        network.create()
-
-    def network_deactivate(self, name):
-        network = self._get_network(name)
-        network.destroy()
-
-    def network_delete(self, name):
-        network = self._get_network(name)
-        if network.isActive():
-            raise InvalidOperation(
-                "Unable to delete the active network %s" % name)
-        self._remove_vlan_tagged_bridge(network)
-        network.undefine()
-
-    def _get_vlan_tagged_bridge_name(self, interface, vlan_id):
-        return '-'.join(('kimchi', interface, vlan_id))
-
-    def _is_vlan_tagged_bridge(self, bridge):
-        return  bridge.startswith('kimchi-')
-
-    def _create_vlan_tagged_bridge(self, interface, vlan_id):
-        br_name = self._get_vlan_tagged_bridge_name(interface, vlan_id)
-        br_xml = networkxml.create_vlan_tagged_bridge_xml(br_name, interface,
-                                                          vlan_id)
-        conn = self.conn.get()
-        conn.changeBegin()
-        try:
-            vlan_tagged_br = conn.interfaceDefineXML(br_xml)
-            vlan_tagged_br.create()
-        except libvirt.libvirtError as e:
-            conn.changeRollback()
-            raise OperationFailed(e.message)
-        else:
-            conn.changeCommit()
-            return br_name
-
-    def _remove_vlan_tagged_bridge(self, network):
-        try:
-            bridge = network.bridgeName()
-        except libvirt.libvirtError:
-            pass
-        else:
-            if self._is_vlan_tagged_bridge(bridge):
-                conn = self.conn.get()
-                iface = conn.interfaceLookupByName(bridge)
-                if iface.isActive():
-                    iface.destroy()
-                iface.undefine()
-
-    def add_task(self, target_uri, fn, opaque=None):
-        id = self.next_taskid
-        self.next_taskid = self.next_taskid + 1
-
-        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
-
-        return id
-
-    def tasks_get_list(self):
-        with self.objstore as session:
-            return session.get_list('task')
-
-    def task_lookup(self, id):
-        with self.objstore as session:
-            return session.get('task', str(id))
-
-    def _vm_exists(self, name):
-        try:
-            self._get_vm(name)
-            return True
-        except NotFoundError:
-            return False
-        except:
-            raise
-
-
-    def _get_vm(self, name):
-        conn = self.conn.get()
-        try:
-            # outgoing text to libvirt, encode('utf-8')
-            return conn.lookupByName(name.encode("utf-8"))
-        except libvirt.libvirtError as e:
-            if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
-                raise NotFoundError("Virtual Machine '%s' not found" % name)
-            else:
-                raise
-
-    def _get_template(self, name, overrides=None):
-        with self.objstore as session:
-            params = session.get('template', name)
-        if overrides:
-            params.update(overrides)
-        return LibvirtVMTemplate(params, False, self.conn)
-
-    def isopool_lookup(self, name):
-        return {'state': 'active',
-                'type': 'kimchi-iso'}
-
-    def isovolumes_get_list(self):
-        iso_volumes = []
-        pools = self.storagepools_get_list()
-
-        for pool in pools:
-            try:
-                volumes = self.storagevolumes_get_list(pool)
-            except InvalidOperation:
-                # Skip inactive pools
-                continue
-            for volume in volumes:
-                res = self.storagevolume_lookup(pool, volume)
-                if res['format'] == 'iso':
-                    res['name'] = '%s' % volume
-                    iso_volumes.append(res)
-        return iso_volumes
-
-    def _clean_scan(self, pool_name):
-        try:
-            self.storagepool_deactivate(pool_name)
-            with self.objstore as session:
-                session.delete('scanning', pool_name)
-        except Exception, e:
-            kimchi_log.debug("Exception %s occured when cleaning scan result" % e.message)
-
-    def _do_deep_scan(self, params):
-        scan_params = dict(ignore_list=[])
-        scan_params['scan_path'] = params['path']
-        params['type'] = 'dir'
-
-        for pool in self.storagepools_get_list():
-            try:
-                res = self.storagepool_lookup(pool)
-                if res['state'] == 'active':
-                    scan_params['ignore_list'].append(res['path'])
-            except Exception, e:
-                kimchi_log.debug("Exception %s occured when get ignore path" % e.message)
-
-        params['path'] = scan_params['pool_path'] = self.scanner.scan_dir_prepare(
-            params['name'])
-        task_id = self.add_task('', self.scanner.start_scan, scan_params)
-        # Record scanning-task/storagepool mapping for future querying
-        with self.objstore as session:
-                session.store('scanning', params['name'], task_id)
-        return task_id
-
-    def storagepools_create(self, params):
-        task_id = None
-        conn = self.conn.get()
-        try:
-            name = params['name']
-            if name in (ISO_POOL_NAME, ):
-                raise InvalidOperation("StoragePool already exists")
-
-            if params['type'] == 'kimchi-iso':
-                task_id = self._do_deep_scan(params)
-            poolDef = StoragePoolDef.create(params)
-            poolDef.prepare(conn)
-            xml = poolDef.xml
-        except KeyError, key:
-            raise MissingParameter(key)
-
-        if name in self.storagepools_get_list():
-            raise InvalidOperation(
-                        "The name %s has been used by a pool" % name)
-
-        try:
-            if task_id:
-                # Create transient pool for deep scan
-                conn.storagePoolCreateXML(xml, 0)
-                return name
-
-            pool = conn.storagePoolDefineXML(xml, 0)
-            if params['type'] in ['logical', 'dir', 'netfs']:
-                pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW)
-                # autostart dir and logical storage pool created from kimchi
-                pool.setAutostart(1)
-            else:
-                # disable autostart for others
-                pool.setAutostart(0)
-        except libvirt.libvirtError as e:
-            msg = "Problem creating Storage Pool: %s"
-            kimchi_log.error(msg, e)
-            raise OperationFailed(e.get_error_message())
-        return name
-
-    def _get_storage_source(self, pool_type, pool_xml):
-        source = {}
-        if pool_type not in STORAGE_SOURCES:
-            return source
-
-        for key, val in STORAGE_SOURCES[pool_type].items():
-            res = xmlutils.xpath_get_text(pool_xml, val)
-            source[key] = res[0] if len(res) == 1 else res
-
-        return source
-
-    def storagepool_lookup(self, name):
-        pool = self._get_storagepool(name)
-        info = pool.info()
-        nr_volumes = self._get_storagepool_vols_num(pool)
-        autostart = True if pool.autostart() else False
-        xml = pool.XMLDesc(0)
-        path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
-        pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0]
-        source = self._get_storage_source(pool_type, xml)
-        res = {'state': Model.pool_state_map[info[0]],
-               'path': path,
-               'source': source,
-               'type': pool_type,
-               'autostart': autostart,
-               'capacity': info[1],
-               'allocated': info[2],
-               'available': info[3],
-               'nr_volumes': nr_volumes}
-
-        if not pool.isPersistent():
-            # Deal with deep scan generated pool
-            try:
-                with self.objstore as session:
-                    task_id = session.get('scanning', name)
-                res['task_id'] = str(task_id)
-                res['type'] = 'kimchi-iso'
-            except NotFoundError:
-                # User created normal pool
-                pass
-        return res
-
-    def storagepool_update(self, name, params):
-        autostart = params['autostart']
-        if autostart not in [True, False]:
-            raise InvalidOperation("Autostart flag must be true or false")
-        pool = self._get_storagepool(name)
-        if autostart:
-            pool.setAutostart(1)
-        else:
-            pool.setAutostart(0)
-        ident = pool.name()
-        return ident
-
-    def storagepool_activate(self, name):
-        pool = self._get_storagepool(name)
-        try:
-            pool.create(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagepool_deactivate(self, name):
-        pool = self._get_storagepool(name)
-        try:
-            pool.destroy()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _pool_refresh(self, pool):
-        try:
-            pool.refresh(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _get_storagepool_vols_num(self, pool):
-        try:
-            if pool.isActive():
-                self._pool_refresh(pool)
-                return pool.numOfVolumes()
-            else:
-                return 0
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagepool_delete(self, name):
-        pool = self._get_storagepool(name)
-        if pool.isActive():
-            raise InvalidOperation(
-                        "Unable to delete the active storagepool %s" % name)
-        try:
-            pool.undefine()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagepools_get_list(self):
-        try:
-            conn = self.conn.get()
-            names = conn.listStoragePools()
-            names += conn.listDefinedStoragePools()
-            return sorted(names)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _get_storagepool(self, name):
-        conn = self.conn.get()
-        try:
-            return conn.storagePoolLookupByName(name)
-        except libvirt.libvirtError as e:
-            if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_POOL:
-                raise NotFoundError("Storage Pool '%s' not found" % name)
-            else:
-                raise
-
-    def storagevolumes_create(self, pool, params):
-        info = self.storagepool_lookup(pool)
-        try:
-            name = params['name']
-            xml = _get_volume_xml(**params)
-        except KeyError, key:
-            raise MissingParameter(key)
-        pool = self._get_storagepool(pool)
-        try:
-            pool.createXML(xml, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-        return name
-
-    def storagevolume_lookup(self, pool, name):
-        vol = self._get_storagevolume(pool, name)
-        path = vol.path()
-        info = vol.info()
-        xml = vol.XMLDesc(0)
-        fmt = xmlutils.xpath_get_text(xml, "/volume/target/format/@type")[0]
-        res = dict(type=Model.volume_type_map[info[0]],
-                   capacity=info[1],
-                   allocation=info[2],
-                   path=path,
-                   format=fmt)
-        if fmt == 'iso':
-            if os.path.islink(path):
-                path = os.path.join(os.path.dirname(path), os.readlink(path))
-            os_distro = os_version = 'unknown'
-            try:
-                iso_img = IsoImage(path)
-                os_distro, os_version = iso_img.probe()
-                bootable = True
-            except IsoFormatError:
-                bootable = False
-            res.update(
-                dict(os_distro=os_distro, os_version=os_version, path=path, bootable=bootable))
-
-        return res
-
-    def storagevolume_wipe(self, pool, name):
-        volume = self._get_storagevolume(pool, name)
-        try:
-            volume.wipePattern(libvirt.VIR_STORAGE_VOL_WIPE_ALG_ZERO, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagevolume_delete(self, pool, name):
-        volume = self._get_storagevolume(pool, name)
-        try:
-            volume.delete(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagevolume_resize(self, pool, name, size):
-        size = size << 20
-        volume = self._get_storagevolume(pool, name)
-        try:
-            volume.resize(size, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def storagevolumes_get_list(self, pool):
-        pool = self._get_storagepool(pool)
-        if not pool.isActive():
-            raise InvalidOperation(
-            "Unable to list volumes in inactive storagepool %s" % pool.name())
-        try:
-            self._pool_refresh(pool)
-            return pool.listVolumes()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _get_storagevolume(self, pool, name):
-        pool = self._get_storagepool(pool)
-        if not pool.isActive():
-            raise InvalidOperation(
-            "Unable to list volumes in inactive storagepool %s" % pool.name())
-        try:
-            return pool.storageVolLookupByName(name)
-        except libvirt.libvirtError as e:
-            if e.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL:
-                raise NotFoundError("Storage Volume '%s' not found" % name)
-            else:
-                raise
-
-    def _get_screenshot(self, vm_uuid):
-        with self.objstore as session:
-            try:
-                params = session.get('screenshot', vm_uuid)
-            except NotFoundError:
-                params = {'uuid': vm_uuid}
-                session.store('screenshot', vm_uuid, params)
-        return LibvirtVMScreenshot(params, self.conn)
-
-    def _sosreport_generate(self, cb, name):
-        command = 'sosreport --batch --name "%s"' % name
-        try:
-            retcode = subprocess.call(command, shell=True, stdout=subprocess.PIPE)
-            if retcode < 0:
-                raise OperationFailed('Command terminated with signal')
-            elif retcode > 0:
-                raise OperationFailed('Command failed: rc = %i' % retcode)
-            pattern = '/tmp/sosreport-%s-*' % name
-            for reportFile in glob.glob(pattern):
-                if not fnmatch.fnmatch(reportFile, '*.md5'):
-                    output = reportFile
-                    break
-            else:
-                # sosreport tends to change the name mangling rule and
-                # compression file format between different releases.
-                # It's possible to fail to match a report file even sosreport
-                # runs successfully. In future we might have a general name
-                # mangling function in kimchi to format the name before passing
-                # it to sosreport. Then we can delete this exception.
-                raise OperationFailed('Can not find generated debug report '
-                                      'named by %s' % pattern)
-            ext = output.split('.', 1)[1]
-            path = config.get_debugreports_path()
-            target = os.path.join(path, name)
-            target_file = '%s.%s' % (target, ext)
-            shutil.move(output, target_file)
-            os.remove('%s.md5' % output)
-            cb('OK', True)
-
-            return
-
-        except OSError:
-            raise
-
-        except Exception, e:
-            # No need to call cb to update the task status here.
-            # The task object will catch the exception rasied here
-            # and update the task status there
-            log = logging.getLogger('Model')
-            log.warning('Exception in generating debug file: %s', e)
-            raise OperationFailed(e)
-
-    def _get_system_report_tool(self):
-        # check if the command can be found by shell one by one
-        for helper_tool in self.report_tools:
-            try:
-                retcode = subprocess.call(helper_tool['cmd'], shell=True,
-                                          stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-                if retcode == 0:
-                    return helper_tool['fn']
-            except Exception, e:
-                kimchi_log.info('Exception running command: %s', e)
-
-        return None
-
-    def _gen_debugreport_file(self, name):
-        gen_cmd = self._get_system_report_tool()
-
-        if gen_cmd is not None:
-            return self.add_task('', gen_cmd, name)
-
-        raise OperationFailed("debugreport tool not found")
-
-    def _get_distros(self):
-        distroloader = DistroLoader()
-        return  distroloader.get()
-
-    def distros_get_list(self):
-        return self.distros.keys()
-
-    def distro_lookup(self, name):
-        try:
-            return self.distros[name]
-        except KeyError:
-            raise NotFoundError("distro '%s' not found" % name)
-
-    def host_lookup(self, *name):
-        return self.host_info
-
-    def hoststats_lookup(self, *name):
-        return {'cpu_utilization': self.host_stats['cpu_utilization'],
-                'memory': self.host_stats.get('memory'),
-                'disk_read_rate': self.host_stats['disk_read_rate'],
-                'disk_write_rate': self.host_stats['disk_write_rate'],
-                'net_recv_rate': self.host_stats['net_recv_rate'],
-                'net_sent_rate': self.host_stats['net_sent_rate']}
-
-    def plugins_get_list(self):
-        return [plugin for (plugin, config) in get_enabled_plugins()]
-
-    def partitions_get_list(self):
-        result = disks.get_partitions_names()
-        return result
-
-    def partition_lookup(self, name):
-        if name not in disks.get_partitions_names():
-            raise NotFoundError("Partition %s not found in the host"
-                                % name)
-        return disks.get_partition_details(name)
-
-    def vms_get_list_by_state(self, state):
-        ret_list = []
-        for name in self.vms_get_list():
-            info = self._get_vm(name).info()
-            if (Model.dom_state_map[info[0]]) == state:
-                ret_list.append(name)
-        return ret_list
-
-    def host_shutdown(self, args=None):
-        # Check for running vms before shutdown
-        running_vms = self.vms_get_list_by_state('running')
-        if len(running_vms) > 0:
-            raise OperationFailed("Shutdown not allowed: VMs are running!")
-        kimchi_log.info('Host is going to shutdown.')
-        os.system('shutdown -h now')
-
-    def host_reboot(self, args=None):
-        # Find running VMs
-        running_vms = self.vms_get_list_by_state('running')
-        if len(running_vms) > 0:
-            raise OperationFailed("Reboot not allowed: VMs are running!")
-        kimchi_log.info('Host is going to reboot.')
-        os.system('reboot')
-
-
-class LibvirtVMTemplate(VMTemplate):
-    def __init__(self, args, scan=False, conn=None):
-        VMTemplate.__init__(self, args, scan)
-        self.conn = conn
-
-    def _storage_validate(self):
-        pool_uri = self.info['storagepool']
-        pool_name = pool_name_from_uri(pool_uri)
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(pool_name)
-        except libvirt.libvirtError:
-            raise InvalidParameter('Storage specified by template does not exist')
-        if not pool.isActive():
-            raise InvalidParameter('Storage specified by template is not active')
-
-        return pool
-
-    def _network_validate(self):
-        names = self.info['networks']
-        for name in names:
-            try:
-                conn = self.conn.get()
-                network = conn.networkLookupByName(name)
-            except libvirt.libvirtError:
-                raise InvalidParameter('Network specified by template does not exist')
-            if not network.isActive():
-                raise InvalidParameter('Network specified by template is not active')
-
-    def _get_storage_path(self):
-        pool = self._storage_validate()
-        xml = pool.XMLDesc(0)
-        return xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
-
-    def fork_vm_storage(self, vm_uuid):
-        # Provision storage:
-        # TODO: Rebase on the storage API once upstream
-        pool = self._storage_validate()
-        vol_list = self.to_volume_list(vm_uuid)
-        for v in vol_list:
-            # outgoing text to libvirt, encode('utf-8')
-            pool.createXML(v['xml'].encode('utf-8'), 0)
-        return vol_list
-
-
-class LibvirtVMScreenshot(VMScreenshot):
-    def __init__(self, vm_uuid, conn):
-        VMScreenshot.__init__(self, vm_uuid)
-        self.conn = conn
-
-    def _generate_scratch(self, thumbnail):
-        def handler(stream, buf, opaque):
-            fd = opaque
-            os.write(fd, buf)
-
-        fd = os.open(thumbnail, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0644)
-        try:
-            conn = self.conn.get()
-            dom = conn.lookupByUUIDString(self.vm_uuid)
-            vm_name = dom.name()
-            stream = conn.newStream(0)
-            mimetype = dom.screenshot(stream, 0, 0)
-            stream.recvAll(handler, fd)
-        except libvirt.libvirtError:
-            try:
-                stream.abort()
-            except:
-                pass
-            raise NotFoundError("Screenshot not supported for %s" % vm_name)
-        else:
-            stream.finish()
-        finally:
-            os.close(fd)
-
-def _get_volume_xml(**kwargs):
-    # Required parameters
-    # name:
-    # capacity:
-    #
-    # Optional:
-    # allocation:
-    # format:
-    kwargs.setdefault('allocation', 0)
-    kwargs.setdefault('format', 'qcow2')
-
-    xml = """
-    <volume>
-      <name>%(name)s</name>
-      <allocation unit="MiB">%(allocation)s</allocation>
-      <capacity unit="MiB">%(capacity)s</capacity>
-      <source>
-      </source>
-      <target>
-        <format type='%(format)s'/>
-      </target>
-    </volume>
-    """ % kwargs
-    return xml
diff --git a/src/kimchi/model/__init__.py b/src/kimchi/model/__init__.py
new file mode 100644
index 0000000..8a37cc4
--- /dev/null
+++ b/src/kimchi/model/__init__.py
@@ -0,0 +1,21 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
diff --git a/src/kimchi/model/config.py b/src/kimchi/model/config.py
new file mode 100644
index 0000000..7c0a5d6
--- /dev/null
+++ b/src/kimchi/model/config.py
@@ -0,0 +1,52 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi.distroloader import DistroLoader
+from kimchi.exception import NotFoundError
+
+ERR_DISTRO_NOT_FOUND = "Distro '%s' not found."
+
+class Config(object):
+    pass
+
+class Capabilities(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def lookup(self, ident):
+        return self.backend.get_capabilities()
+
+class Distros(object):
+    def __init__(self):
+        self._distroloader = DistroLoader()
+        self._distros = self._distroloader.get()
+
+    def get_list(self):
+        return self._distros.keys()
+
+class Distro(Distros):
+    def lookup(self, ident):
+        if ident not in self.get_list():
+            raise NotFoundError(ERR_DISTRO_NOT_FOUND % ident)
+
+        return self._distros[ident]
diff --git a/src/kimchi/model/debugreports.py b/src/kimchi/model/debugreports.py
new file mode 100644
index 0000000..4db1ace
--- /dev/null
+++ b/src/kimchi/model/debugreports.py
@@ -0,0 +1,86 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import glob
+import os
+import time
+
+from kimchi import config
+from kimchi.exception import NotFoundError
+
+class DebugReports(object):
+    def __init__(self, backend):
+        pass
+
+    def get_list(self):
+        path = config.get_debugreports_path()
+        file_pattern = os.path.join(path, '*.*')
+        file_lists = glob.glob(file_pattern)
+        file_lists = [os.path.split(file)[1] for file in file_lists]
+        name_lists = [file.split('.', 1)[0] for file in file_lists]
+
+        return name_lists
+
+class DebugReport(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def lookup(self, name):
+        file_target = self._get_debugreport(name)
+        ctime = os.stat(file_target).st_ctime
+        ctime = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime(ctime))
+        file_target = os.path.split(file_target)[-1]
+        file_target = os.path.join("/data/debugreports", file_target)
+        return {'file': file_target,
+                'ctime': ctime}
+
+    def create(self, params):
+        ident = params['name']
+        taskid = self.backend.gen_debugreport_file(ident)
+        if taskid is None:
+            raise OperationFailed("Debug report tool not found.")
+
+        with self.backend.objstore as session:
+            return session.get('task', str(taskid))
+
+    def delete(self, name):
+        file_target = self._get_debugreports(name)
+        os.remove(file_target)
+
+    def _get_debugreport(self, name):
+        path = config.get_debugreports_path()
+        file_pattern = os.path.join(path, name + '.*')
+        try:
+            file_target = glob.glob(file_pattern)[0]
+        except IndexError:
+            raise NotFoundError("Debug report '%s' not found.")
+
+        return file_target
+
+class DebugReportContent(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def lookup(self, name):
+        debugreport = DebugReports(backend)
+        return self.debugreport.lookup(name)
diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
new file mode 100644
index 0000000..f5ef0d7
--- /dev/null
+++ b/src/kimchi/model/host.py
@@ -0,0 +1,49 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi import disks
+
+class Host(object):
+    def __init__(self, backend):
+        self.info = backend.get_host()
+
+    def lookup(self, ident):
+        return self.info
+
+class HostStats(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def lookup(self, ident):
+        return self.backend.host_stats
+
+class Partitions(object):
+    def get_list(self):
+        return disks.get_partitions_names()
+
+class Partition(object):
+    def lookup(self, ident):
+        if ident not in disks.get_partitions_names():
+            raise NotFoundError("Partition %s not found in the host" % name)
+
+        return disks.get_partition_details(ident)
diff --git a/src/kimchi/model/interfaces.py b/src/kimchi/model/interfaces.py
new file mode 100644
index 0000000..7b1d156
--- /dev/null
+++ b/src/kimchi/model/interfaces.py
@@ -0,0 +1,48 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi import netinfo
+
+class Interfaces(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def get_list(self):
+        return list(set(netinfo.all_favored_interfaces()) -
+                    set(self.get_used_ifaces()))
+
+    def get_used_ifaces(self):
+        net_names = self.backend.get_networks()
+        interfaces = []
+        for name in net_names:
+            net_dict = self.backend.get_network_by_name(name)
+            net_dict['interface'] and interfaces.append(net_dict['interface'])
+
+        return interfaces
+
+class Interface(object):
+    def lookup(self, name):
+        try:
+            return netinfo.get_interface_info(name)
+        except ValueError, e:
+            raise NotFoundError(e)
diff --git a/src/kimchi/model/libvirtbackend.py b/src/kimchi/model/libvirtbackend.py
new file mode 100644
index 0000000..3fb1de2
--- /dev/null
+++ b/src/kimchi/model/libvirtbackend.py
@@ -0,0 +1,955 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import cherrypy
+import fnmatch
+import glob
+import ipaddr
+import logging
+import os
+import platform
+import psutil
+import shutil
+import subprocess
+import time
+
+from cherrypy.process.plugins import BackgroundTask
+from collections import defaultdict
+
+from kimchi import config
+from kimchi import network
+from kimchi import netinfo
+from kimchi import networkxml
+from kimchi import xmlutils
+from kimchi.asynctask import AsyncTask
+from kimchi.exception import IsoFormatError, NotFoundError, OperationFailed
+from kimchi.featuretests import FeatureTests
+from kimchi.isoinfo import IsoImage
+from kimchi.model.libvirtconnection import LibvirtConnection
+from kimchi.model.libvirtstoragepool import StoragePoolDef
+from kimchi.objectstore import ObjectStore
+from kimchi.scan import Scanner
+from kimchi.screenshot import VMScreenshot
+from kimchi.utils import kimchi_log
+
+GUESTS_STATS_INTERVAL = 5
+HOST_STATS_INTERVAL = 1
+STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
+                             'path': '/pool/source/dir/@path'}}
+
+class LibvirtBackend(object):
+    dom_state_map = {0: 'nostate',
+                     1: 'running',
+                     2: 'blocked',
+                     3: 'paused',
+                     4: 'shutdown',
+                     5: 'shutoff',
+                     6: 'crashed'}
+
+    pool_state_map = {0: 'inactive',
+                      1: 'initializing',
+                      2: 'active',
+                      3: 'degraded',
+                      4: 'inaccessible'}
+
+    volume_type_map = {0: 'file',
+                       1: 'block',
+                       2: 'directory',
+                       3: 'network'}
+
+    TEMPLATE_SCAN = True
+
+    def __init__(self, libvirt_uri=None, objstore_loc=None):
+        self.libvirt_uri = libvirt_uri or 'qemu:///system'
+        self.conn = LibvirtConnection(self.libvirt_uri)
+        self.objstore = ObjectStore(objstore_loc)
+        self.next_taskid = 1
+        self.scanner = Scanner(self._clean_scan)
+        self.stats = {}
+        self.host_stats = defaultdict(int)
+        self.guests_stats_thread = BackgroundTask(GUESTS_STATS_INTERVAL,
+                                                  self._update_guests_stats)
+        self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
+                                                self._update_host_stats)
+        self.guests_stats_thread.start()
+        self.host_stats_thread.start()
+
+        # Subscribe function to set host capabilities to be run when cherrypy
+        # server is up
+        # It is needed because some features tests depends on the server
+        cherrypy.engine.subscribe('start', self._set_capabilities)
+
+        # Please add new possible debug report command here
+        # and implement the report generating function
+        # based on the new report command
+        self.report_tools = ({'cmd': 'sosreport --help',
+                              'fn': self._sosreport_generate},)
+
+    def _set_capabilities(self):
+        kimchi_log.info("*** Running feature tests ***")
+        self.qemu_stream = FeatureTests.qemu_supports_iso_stream()
+        self.qemu_stream_dns = FeatureTests.qemu_iso_stream_dns()
+
+        self.libvirt_stream_protocols = []
+        for p in ['http', 'https', 'ftp', 'ftps', 'tftp']:
+            if FeatureTests.libvirt_supports_iso_stream(p):
+                self.libvirt_stream_protocols.append(p)
+
+        kimchi_log.info("*** Feature tests completed ***")
+    _set_capabilities.priority = 90
+
+    def get_capabilities(self):
+        report_tool = self._get_system_report_tool()
+
+        return {'libvirt_stream_protocols': self.libvirt_stream_protocols,
+                'qemu_stream': self.qemu_stream,
+                'screenshot': VMScreenshot.get_stream_test_result(),
+                'system_report_tool': bool(report_tool)}
+
+    def _update_guests_stats(self):
+        conn = self.conn.get()
+        vm_list = self.get_vms()
+
+        for name in vm_list:
+            info = self.get_vm_by_name(name)
+            vm_uuid = info['uuid']
+            state = info['state']
+            if state != 'running':
+                self.stats[vm_uuid] = {}
+                continue
+
+            if self.stats.get(vm_uuid, None) is None:
+                self.stats[vm_uuid] = {}
+
+            timestamp = time.time()
+            prevStats = self.stats.get(vm_uuid, {})
+            seconds = timestamp - prevStats.get('timestamp', 0)
+            self.stats[vm_uuid].update({'timestamp': timestamp})
+
+            dom = conn.lookupByName(name.encode("utf-8"))
+            self._get_percentage_cpu_usage(vm_uuid, dom.info, seconds)
+            self._get_network_io_rate(vm_uuid, dom, seconds)
+            self._get_disk_io_rate(vm_uuid, dom, seconds)
+
+    def _get_percentage_cpu_usage(self, vm_uuid, info, seconds):
+        prevCpuTime = self.stats[vm_uuid].get('cputime', 0)
+
+        cpus = info[3]
+        cpuTime = info[4] - prevCpuTime
+
+        base = (((cpuTime) * 100.0) / (seconds * 1000.0 * 1000.0 * 1000.0))
+        percentage = max(0.0, min(100.0, base / cpus))
+
+        self.stats[vm_uuid].update({'cputime': info[4], 'cpu': percentage})
+
+    def _get_network_io_rate(self, vm_uuid, dom, seconds):
+        prevNetRxKB = self.stats[vm_uuid].get('netRxKB', 0)
+        prevNetTxKB = self.stats[vm_uuid].get('netTxKB', 0)
+        currentMaxNetRate = self.stats[vm_uuid].get('max_net_io', 100)
+
+        rx_bytes = 0
+        tx_bytes = 0
+
+        tree = ElementTree.fromstring(dom.XMLDesc(0))
+        for target in tree.findall('devices/interface/target'):
+            dev = target.get('dev')
+            io = dom.interfaceStats(dev)
+            rx_bytes += io[0]
+            tx_bytes += io[4]
+
+        netRxKB = float(rx_bytes) / 1000
+        netTxKB = float(tx_bytes) / 1000
+
+        rx_stats = (netRxKB - prevNetRxKB) / seconds
+        tx_stats = (netTxKB - prevNetTxKB) / seconds
+
+        rate = rx_stats + tx_stats
+        max_net_io = round(max(currentMaxNetRate, int(rate)), 1)
+
+        self.stats[vm_uuid].update({'net_io': rate, 'max_net_io': max_net_io,
+                                    'netRxKB': netRxKB, 'netTxKB': netTxKB})
+
+    def _get_disk_io_rate(self, vm_uuid, dom, seconds):
+        prevDiskRdKB = self.stats[vm_uuid].get('diskRdKB', 0)
+        prevDiskWrKB = self.stats[vm_uuid].get('diskWrKB', 0)
+        currentMaxDiskRate = self.stats[vm_uuid].get('max_disk_io', 100)
+
+        rd_bytes = 0
+        wr_bytes = 0
+
+        tree = ElementTree.fromstring(dom.XMLDesc(0))
+        for target in tree.findall("devices/disk/target"):
+            dev = target.get("dev")
+            io = dom.blockStats(dev)
+            rd_bytes += io[1]
+            wr_bytes += io[3]
+
+        diskRdKB = float(rd_bytes) / 1024
+        diskWrKB = float(wr_bytes) / 1024
+
+        rd_stats = (diskRdKB - prevDiskRdKB) / seconds
+        wr_stats = (diskWrKB - prevDiskWrKB) / seconds
+
+        rate = rd_stats + wr_stats
+        max_disk_io = round(max(currentMaxDiskRate, int(rate)), 1)
+
+        self.stats[vm_uuid].update({'disk_io': rate, 'max_disk_io': max_disk_io,
+                                    'diskRdKB': diskRdKB, 'diskWrKB': diskWrKB})
+
+    def _update_host_stats(self):
+        preTimeStamp = self.host_stats['timestamp']
+        timestamp = time.time()
+        # FIXME when we upgrade psutil, we can get uptime by psutil.uptime
+        # we get uptime by float(open("/proc/uptime").readline().split()[0])
+        # and calculate the first io_rate after the OS started.
+        seconds = (timestamp - preTimeStamp if preTimeStamp else
+                   float(open("/proc/uptime").readline().split()[0]))
+
+        self.host_stats['timestamp'] = timestamp
+        self._get_host_disk_io_rate(seconds)
+        self._get_host_network_io_rate(seconds)
+
+        # get cpu utilization
+        self.host_stats['cpu_utilization'] = psutil.cpu_percent(None)
+
+        # get memory stats
+        virt_mem = psutil.virtual_memory()
+        self.host_stats['memory'] = {'total': virt_mem.total,
+                                     'free': virt_mem.free,
+                                     'cached': virt_mem.cached,
+                                     'buffers': virt_mem.buffers,
+                                     'avail': virt_mem.available}
+
+    def _get_host_disk_io_rate(self, seconds):
+        prev_read_bytes = self.host_stats['disk_read_bytes']
+        prev_write_bytes = self.host_stats['disk_write_bytes']
+
+        disk_io = psutil.disk_io_counters(False)
+        read_bytes = disk_io.read_bytes
+        write_bytes = disk_io.write_bytes
+
+        rd_rate = int(float(read_bytes - prev_read_bytes) / seconds + 0.5)
+        wr_rate = int(float(write_bytes - prev_write_bytes) / seconds + 0.5)
+
+        self.host_stats.update({'disk_read_rate': rd_rate,
+                                'disk_write_rate': wr_rate,
+                                'disk_read_bytes': read_bytes,
+                                'disk_write_bytes': write_bytes})
+
+    def _get_host_network_io_rate(self, seconds):
+        prev_recv_bytes = self.host_stats['net_recv_bytes']
+        prev_sent_bytes = self.host_stats['net_sent_bytes']
+
+        net_ios = psutil.network_io_counters(True)
+        recv_bytes = 0
+        sent_bytes = 0
+
+        ifaces = set(netinfo.nics() + netinfo.wlans()) & set(net_ios.iterkeys())
+        for key in ifaces:
+            recv_bytes = recv_bytes + net_ios[key].bytes_recv
+            sent_bytes = sent_bytes + net_ios[key].bytes_sent
+
+        rx_rate = int(float(recv_bytes - prev_recv_bytes) / seconds + 0.5)
+        tx_rate = int(float(sent_bytes - prev_sent_bytes) / seconds + 0.5)
+
+        self.host_stats.update({'net_recv_rate': rx_rate,
+                                'net_sent_rate': tx_rate,
+                                'net_recv_bytes': recv_bytes,
+                                'net_sent_bytes': sent_bytes})
+
+    def gen_debugreport_file(self, name):
+        gen_cmd = self._get_system_report_tool()
+        if gen_cmd is None:
+            return None
+
+        return self.add_task('', gen_cmd, name)
+
+    def _get_system_report_tool(self):
+        # check if the command can be found by shell one by one
+        for helper_tool in self.report_tools:
+            try:
+                retcode = subprocess.call(helper_tool['cmd'], shell=True,
+                                          stdout=subprocess.PIPE,
+                                          stderr=subprocess.PIPE)
+                if retcode == 0:
+                    return helper_tool['fn']
+            except Exception, e:
+                kimchi_log.info('Exception running command: %s', e)
+
+        return None
+
+    def _sosreport_generate(self, cb, name):
+        command = 'sosreport --batch --name "%s"' % name
+        try:
+            retcode = subprocess.call(command, shell=True,
+                                      stdout=subprocess.PIPE,
+                                      stderr=subprocess.PIPE)
+            if retcode < 0:
+                raise OperationFailed('Command terminated with signal')
+            elif retcode > 0:
+                raise OperationFailed('Command failed: rc = %i' % retcode)
+
+            pattern = '/tmp/sosreport-%s-*' % name
+            for reportFile in glob.glob(pattern):
+                if not fnmatch.fnmatch(reportFile, '*.md5'):
+                    output = reportFile
+                    break
+            else:
+                # sosreport tends to change the name mangling rule and
+                # compression file format between different releases.
+                # It's possible to fail to match a report file even sosreport
+                # runs successfully. In future we might have a general name
+                # mangling function in kimchi to format the name before passing
+                # it to sosreport. Then we can delete this exception.
+                raise OperationFailed('Can not find generated debug report '
+                                      'named by %s' % pattern)
+            ext = output.split('.', 1)[1]
+            path = config.get_debugreports_path()
+            target = os.path.join(path, name)
+            target_file = '%s.%s' % (target, ext)
+            shutil.move(output, target_file)
+            os.remove('%s.md5' % output)
+            cb('OK', True)
+        except Exception, e:
+            # No need to call cb to update the task status here.
+            # The task object will catch the exception rasied here
+            # and update the task status there
+            log = logging.getLogger('Model')
+            log.warning('Exception in generating debug file: %s', e)
+            raise OperationFailed(e)
+
+    def add_task(self, target_uri, fn, opaque=None):
+        id = self.next_taskid
+        self.next_taskid += 1
+        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
+        return id
+
+    def get_host(self):
+        res = {}
+        with open('/proc/cpuinfo') as f:
+            for line in f.xreadlines():
+                if "model name" in line:
+                    res['cpu'] = line.split(':')[1].strip()
+                    break
+
+        res['memory'] = psutil.TOTAL_PHYMEM
+        distro, version, codename = platform.linux_distribution()
+        res['os_distro'] = distro
+        res['os_version'] = version
+        res['os_codename'] = unicode(codename,"utf-8")
+
+        return res
+
+    def get_storagepools(self):
+        try:
+            conn = self.conn.get()
+            names = conn.listStoragePools()
+            names += conn.listDefinedStoragePools()
+            return names
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _clean_scan(self, pool_name):
+        try:
+            self.deactivate_storagepool(pool_name)
+            with self.objstore as session:
+                session.delete('scanning', pool_name)
+        except Exception, e:
+            kimchi_log.debug("Error while cleaning deep scan results" %
+                             e.message)
+
+    def do_deep_scan(self, params):
+        scan_params = dict(ignore_list=[])
+        scan_params['scan_path'] = params['path']
+        params['type'] = 'dir'
+
+        for pool in self.get_storagepools():
+            try:
+                res = self.get_storagepool_by_name(pool)
+                if res['state'] == 'active':
+                    scan_params['ignore_list'].append(res['path'])
+            except Exception, e:
+                kimchi_log.debug("Error while preparing for deep scan: %s" %
+                                 e.message)
+
+        params['path']  = self.scanner.scan_dir_prepare(params['name'])
+        scan_params['pool_path'] = params['path']
+        task_id = self.add_task('', self.scanner.start_scan, scan_params)
+        # Record scanning-task/storagepool mapping for future querying
+        with self.objstore as session:
+                session.store('scanning', params['name'], task_id)
+        return task_id
+
+    def create_storagepool(self, params):
+        conn = self.conn.get()
+        try:
+            poolDef = StoragePoolDef.create(params)
+            poolDef.prepare(conn)
+            xml = poolDef.xml
+        except KeyError, key:
+            raise MissingParameter("You need to specify '%s' in order to "
+                                   "create storage pool" % key)
+
+        try:
+            if task_id:
+                # Create transient pool for deep scan
+                conn.storagePoolCreateXML(xml, 0)
+                return name
+
+            pool = conn.storagePoolDefineXML(xml, 0)
+            if params['type'] in ['logical', 'dir', 'netfs']:
+                pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW)
+                # autostart dir and logical storage pool created from kimchi
+                pool.setAutostart(1)
+            else:
+                # disable autostart for others
+                pool.setAutostart(0)
+        except libvirt.libvirtError as e:
+            msg = "Problem creating Storage Pool: %s"
+            kimchi_log.error(msg, e)
+            raise OperationFailed(e.get_error_message())
+
+    def get_storagepool_by_name(self, name):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(name)
+        info = pool.info()
+        nr_volumes = self._get_storagepool_vols_num(pool)
+        autostart = True if pool.autostart() else False
+        xml = pool.XMLDesc(0)
+        path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
+        pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0]
+        source = self._get_storage_source(pool_type, xml)
+        res = {'state': self.pool_state_map[info[0]],
+               'path': path,
+               'source': source,
+               'type': pool_type,
+               'autostart': autostart,
+               'capacity': info[1],
+               'allocated': info[2],
+               'available': info[3],
+               'nr_volumes': nr_volumes}
+
+        if not pool.isPersistent():
+            # Deal with deep scan generated pool
+            try:
+                with self.objstore as session:
+                    task_id = session.get('scanning', name)
+                res['task_id'] = str(task_id)
+                res['type'] = 'kimchi-iso'
+            except NotFoundError:
+                # User created normal pool
+                pass
+        return res
+
+    def _get_storagepool_vols_num(self, pool):
+        try:
+            if pool.isActive():
+                pool.refresh(0)
+                return pool.numOfVolumes()
+            else:
+                return 0
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _get_storage_source(self, pool_type, pool_xml):
+        source = {}
+        if pool_type not in STORAGE_SOURCES:
+            return source
+
+        for key, val in STORAGE_SOURCES[pool_type].items():
+            res = xmlutils.xpath_get_text(pool_xml, val)
+            source[key] = res[0] if len(res) == 1 else res
+
+        return source
+
+    def activate_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.create(0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def deactivate_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.destroy()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def delete_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.undefine()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def autostart_storagepool(self, name, value):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(name)
+        if autostart:
+            pool.setAutostart(1)
+        else:
+            pool.setAutostart(0)
+
+    def create_storagevolume(self, pool, params):
+        storagevol_xml = """
+        <volume>
+          <name>%(name)s</name>
+          <allocation unit="MiB">%(allocation)s</allocation>
+          <capacity unit="MiB">%(capacity)s</capacity>
+          <target>
+            <format type='%(format)s'/>
+            <path>%(path)s</path>
+          </target>
+        </volume>"""
+
+        params.setdefault('allocation', 0)
+        params.setdefault('format', 'qcow2')
+        try:
+            xml = storagevol_xml % params
+        except KeyError, key:
+            raise MissingParameter("You need to specify '%s' in order to "
+                                   "create the storage volume." % key)
+
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(pool)
+        try:
+            pool.createXML(xml, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _get_storagevolume(self, pool, name):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(pool)
+        return pool.storageVolLookupByName(name)
+
+    def get_storagevolumes_by_pool(self, pool):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(pool)
+            pool.refresh(0)
+            return pool.listVolumes()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def get_storagevolume(self, pool, name):
+        vol = self._get_storagevolume(pool, name)
+        path = vol.path()
+        info = vol.info()
+        xml = vol.XMLDesc(0)
+        fmt = xmlutils.xpath_get_text(xml, "/volume/target/format/@type")[0]
+        res = dict(type=self.volume_type_map[info[0]], capacity=info[1],
+                   allocation=info[2], path=path, format=fmt)
+
+        if fmt == 'iso':
+            if os.path.islink(path):
+                path = os.path.join(os.path.dirname(path), os.readlink(path))
+
+            try:
+                iso_img = IsoImage(path)
+                os_distro, os_version = iso_img.probe()
+                bootable = True
+            except IsoFormatError:
+                bootable = False
+
+            res.update(dict(os_distro=os_distro, os_version=os_version,
+                            path=path, bootable=bootable))
+
+        return res
+
+    def wipe_storagevolume(self, pool, name):
+        try:
+            vol = self._get_storagevolume(pool, name)
+            vol.wipePattern(libvirt.VIR_STORAGE_VOL_WIPE_ALG_ZERO, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def resize_storagevolume(self, pool, name, size):
+        size = size << 20
+        try:
+            vol = pool.storageVolLookupByName(name)
+            vol.resize(size, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def delete_storagevolume(self, pool, name):
+        try:
+            vol = pool.storageVolLookupByName(name)
+            volume.delete(0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def create_network(self, params):
+        connection = params["connection"]
+        # set forward mode, isolated do not need forward
+        if connection != 'isolated':
+            params['forward'] = {'mode': connection}
+
+        if connection == 'bridge':
+            if netinfo.is_bridge(iface):
+                params['bridge'] = iface
+            elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface):
+                if params.get('vlan_id') is None:
+                    params['forward']['dev'] = iface
+                else:
+                    params['bridge'] = \
+                        self._create_vlan_tagged_bridge(str(iface),
+                                                        str(params['vlan_id']))
+
+        xml = networkxml.to_network_xml(**params)
+        try:
+            conn = self.conn.get()
+            network = conn.networkDefineXML(xml)
+            network.setAutostart(True)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _create_vlan_tagged_bridge(self, interface, vlan_id):
+        br_name = '-'.join(('kimchi', interface, vlan_id))
+        br_xml = networkxml.create_vlan_tagged_bridge_xml(br_name, interface,
+                                                          vlan_id)
+        conn = self.conn.get()
+        conn.changeBegin()
+        try:
+            vlan_tagged_br = conn.interfaceDefineXML(br_xml)
+            vlan_tagged_br.create()
+        except libvirt.libvirtError as e:
+            conn.changeRollback()
+            raise OperationFailed(e.message)
+        else:
+            conn.changeCommit()
+            return br_name
+
+    def get_networks(self):
+        conn = self.conn.get()
+        return conn.listNetworks() + conn.listDefinedNetworks()
+
+    def get_network_by_name(self, name):
+        conn = self.conn.get()
+        net = conn.networkLookupByName(name)
+        xml = net.XMLDesc(0)
+        net_dict = self._get_network_from_xml(xml)
+        subnet = net_dict['subnet']
+        dhcp = net_dict['dhcp']
+        forward = net_dict['forward']
+        interface = net_dict['bridge']
+
+        connection = forward['mode'] or "isolated"
+        # FIXME, if we want to support other forward mode well.
+        if connection == 'bridge':
+            # macvtap bridge
+            interface = interface or forward['interface'][0]
+            # exposing the network on linux bridge or macvtap interface
+            interface_subnet = network.get_dev_netaddr(interface)
+            subnet = subnet if subnet else interface_subnet
+
+        # libvirt use format 192.168.0.1/24, standard should be 192.168.0.0/24
+        # http://www.ovirt.org/File:Issue3.png
+        if subnet:
+            subnet = ipaddr.IPNetwork(subnet)
+            subnet = "%s/%s" % (subnet.network, subnet.prefixlen)
+
+        return {'connection': connection,
+                'interface': interface,
+                'subnet': subnet,
+                'dhcp': dhcp,
+                'vms': self._get_vms_attach_to_network(name),
+                'autostart': net.autostart() == 1,
+                'state':  net.isActive() and "active" or "inactive"}
+
+    def _get_network_from_xml(self, xml):
+        address = xmlutils.xpath_get_text(xml, "/network/ip/@address")
+        address = address and address[0] or ''
+        netmask = xmlutils.xpath_get_text(xml, "/network/ip/@netmask")
+        netmask = netmask and netmask[0] or ''
+        net = address and netmask and "/".join([address, netmask]) or ''
+
+        dhcp_start = xmlutils.xpath_get_text(xml,
+                                             "/network/ip/dhcp/range/@start")
+        dhcp_start = dhcp_start and dhcp_start[0] or ''
+        dhcp_end = xmlutils.xpath_get_text(xml, "/network/ip/dhcp/range/@end")
+        dhcp_end = dhcp_end and dhcp_end[0] or ''
+        dhcp = {'start': dhcp_start, 'end': dhcp_end}
+
+        forward_mode = xmlutils.xpath_get_text(xml, "/network/forward/@mode")
+        forward_mode = forward_mode and forward_mode[0] or ''
+        forward_if = xmlutils.xpath_get_text(xml,
+                                             "/network/forward/interface/@dev")
+        forward_pf = xmlutils.xpath_get_text(xml, "/network/forward/pf/@dev")
+        bridge = xmlutils.xpath_get_text(xml, "/network/bridge/@name")
+        bridge = bridge and bridge[0] or ''
+        return {'subnet': net, 'dhcp': dhcp, 'bridge': bridge,
+                'forward': {'mode': forward_mode, 'interface': forward_if,
+                            'pf': forward_pf}}
+
+    def _get_vms_attach_to_network(self, network):
+        vms = []
+        xpath = "/domain/devices/interface[@type='network']/source/@network"
+        conn = self.conn.get()
+        for dom in conn.listAllDomains(0):
+            xml = dom.XMLDesc(0)
+            networks =  xmlutils.xpath_get_text(xml, xpath)
+            if network in networks:
+                vms.append(dom.name())
+        return vms
+
+    def activate_network(self, name):
+        conn = self.conn.get()
+        net = conn.networkLookupByName(name)
+        net.create()
+
+    def deactivate_network(self, name):
+        conn = self.conn.get()
+        net = conn.networkLookupByName(name)
+        net.destroy()
+
+    def delete_network(self, name):
+        conn = self.conn.get()
+        net = conn.networkLookupByName(name)
+        self._remove_vlan_tagged_bridge(net)
+        net.undefine()
+
+    def _remove_vlan_tagged_bridge(self, network):
+        try:
+            bridge = network.bridgeName()
+        except libvirt.libvirtError:
+            pass
+        else:
+            if self._is_vlan_tagged_bridge(bridge):
+                conn = self.conn.get()
+                iface = conn.interfaceLookupByName(bridge)
+                if iface.isActive():
+                    iface.destroy()
+                iface.undefine()
+
+    def create_template(self, name, tmpl):
+        with self.objstore as session:
+            session.store('template', name, tmpl.info)
+
+    def get_templates(self):
+        with self.objstore as session:
+            return session.get_list('template')
+
+    def get_template_by_name(self, name):
+        with self.objstore as session:
+            return session.get('template', name)
+
+    def delete_template(self, name):
+        with self.objstore as session:
+            session.delete('template', name)
+
+    def create_vm(self, name, uuid, tmpl, vol_list):
+        # Store the icon for displaying later
+        icon = tmpl.info.get('icon', None)
+        if icon is not None:
+            with self.objstore as session:
+                session.store('vm', vm_uuid, {'icon': icon})
+
+        libvirt_stream = False
+        if len(self.libvirt_stream_protocols) != 0:
+            libvirt_stream = True
+
+        xml = tmpl.to_vm_xml(name, vm_uuid, libvirt_stream,
+                             self.qemu_stream_dns)
+        try:
+            dom = conn.defineXML(xml.encode('utf-8'))
+        except libvirt.libvirtError as e:
+            for v in vol_list:
+                vol = conn.storageVolLookupByPath(v['path'])
+                vol.delete(0)
+            raise OperationFailed(e.get_error_message())
+
+    def get_vms(self):
+        conn = self.conn.get()
+        ids = conn.listDomainsID()
+        names = map(lambda x: conn.lookupByID(x).name(), ids)
+        names += conn.listDefinedDomains()
+        names = map(lambda x: x.decode('utf-8'), names)
+        return sorted(names, key=unicode.lower)
+
+    def get_screenshot_by_name(self, vm_uuid):
+        with self.objstore as session:
+            try:
+                params = session.get('screenshot', vm_uuid)
+            except NotFoundError:
+                params = {'uuid': vm_uuid}
+                session.store('screenshot', vm_uuid, params)
+
+        screenshot = LibvirtVMScreenshot(params, self.conn)
+        img_path = screenshot.lookup()
+        # screenshot info changed after scratch generation
+        with self.objstore as session:
+            session.store('screenshot', vm_uuid, screenshot.info)
+
+        return img_path
+
+    def delete_screenshot(self, vm_uuid):
+        os.remove(self.get_screenshot_by_name(vm_uuid))
+        with self.objstore as session:
+            session.delete('screenshot', vm_uuid)
+
+    def get_vm_by_name(self, name):
+        conn = self.conn.get()
+        dom = conn.lookupByName(name.encode("utf-8"))
+        info = dom.info()
+        state = self.dom_state_map[info[0]]
+        screenshot = None
+        graphics = self._get_vm_graphics(dom)
+        graphics_type, graphics_listen, graphics_port = graphics
+        graphics_port = graphics_port if state == 'running' else None
+        if state == 'running':
+            screenshot = self.get_screenshot_by_name(name)
+        elif state == 'shutoff':
+            # reset vm stats when it is powered off to avoid sending
+            # incorrect (old) data
+            self.stats[dom.UUIDString()] = {}
+
+        with self.objstore as session:
+            try:
+                extra_info = session.get('vm', dom.UUIDString())
+            except NotFoundError:
+                extra_info = {}
+        icon = extra_info.get('icon')
+
+        vm_stats = self.stats.get(dom.UUIDString(), {})
+        stats = {}
+        stats['cpu_utilization'] = vm_stats.get('cpu', 0)
+        stats['net_throughput'] = vm_stats.get('net_io', 0)
+        stats['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
+        stats['io_throughput'] = vm_stats.get('disk_io', 0)
+        stats['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
+
+        return {'state': state, 'stats': str(stats), 'uuid': dom.UUIDString(),
+                'memory': info[2] >> 10, 'cpus': info[3], 'icon': icon,
+                'screenshot': screenshot,
+                'graphics': {'type': graphics_type, 'listen': graphics_listen,
+                             'port': graphics_port}
+                }
+
+    def _get_vm_graphics(self, dom):
+        xml = dom.XMLDesc(0)
+        expr = "/domain/devices/graphics/@type"
+        res = xmlutils.xpath_get_text(xml, expr)
+        graphics_type = res[0] if res else None
+        expr = "/domain/devices/graphics/@listen"
+        res = xmlutils.xpath_get_text(xml, expr)
+        graphics_listen = res[0] if res else None
+        graphics_port = None
+        if graphics_type:
+            expr = "/domain/devices/graphics[@type='%s']/@port" % graphics_type
+            res = xmlutils.xpath_get_text(xml, expr)
+            graphics_port = int(res[0]) if res else None
+        return graphics_type, graphics_listen, graphics_port
+
+    def static_vm_update(self, name, params):
+        conn = self.conn.get()
+        dom = conn.lookupByName(name.encode("utf-8"))
+        old_xml = new_xml = dom.XMLDesc(0)
+
+        for key, val in params.items():
+            if key in VM_STATIC_UPDATE_PARAMS:
+                new_xml = xmlutils.xml_item_update(new_xml,
+                                                   VM_STATIC_UPDATE_PARAMS[key],
+                                                   val)
+
+        try:
+            dom.undefine()
+            conn.defineXML(new_xml)
+        except libvirt.libvirtError as e:
+            conn.defineXML(old_xml)
+            raise OperationFailed(e.get_error_message())
+
+    def live_vm_update(self, name, params):
+        pass
+
+    def delete_vm(self, name):
+        info = self.get_vm_by_name(name)
+        if info['state'] == 'running':
+            self.stop_vm(name)
+
+        conn = self.conn.get()
+        dom = conn.lookupByName(name.encode("utf-8"))
+        dom.undefine()
+
+        xml = dom.XMLDesc(0)
+        xpath = "/domain/devices/disk[@device='disk']/source/@file"
+        paths = xmlutils.xpath_get_text(xml, xpath)
+        for path in paths:
+            vol = conn.storageVolLookupByPath(path)
+            vol.delete(0)
+
+        with self.objstore as session:
+            session.delete('vm', dom.UUIDString(), ignore_missing=True)
+
+        self.delete_screenshot(dom.UUIDString())
+        vnc.remove_proxy_token(name)
+
+    def start_vm(self, name):
+        conn = self.conn.get()
+        dom = conn.lookupByName(name.encode("utf-8"))
+        dom.create()
+
+    def stop_vm(self, name):
+        conn = self.conn.get()
+        dom = conn.lookupByName(name.encode("utf-8"))
+        dom.destroy()
+
+    def connect_vm(self, name):
+        graphics = self._get_vm_graphics(name)
+        graphics_type, graphics_listen, graphics_port = get_graphics
+        if graphics_port is None:
+            raise OperationFailed("Only able to connect to running vm's vnc "
+                                  "graphics.")
+        vnc.add_proxy_token(name, graphics_port)
+
+class LibvirtVMScreenshot(VMScreenshot):
+    def __init__(self, vm_uuid, conn):
+        VMScreenshot.__init__(self, vm_uuid)
+        self.conn = conn
+
+    def _generate_scratch(self, thumbnail):
+        def handler(stream, buf, opaque):
+            fd = opaque
+            os.write(fd, buf)
+
+        fd = os.open(thumbnail, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0644)
+        try:
+            conn = self.conn.get()
+            dom = conn.lookupByUUIDString(self.vm_uuid)
+            vm_name = dom.name()
+            stream = conn.newStream(0)
+            mimetype = dom.screenshot(stream, 0, 0)
+            stream.recvAll(handler, fd)
+        except libvirt.libvirtError:
+            try:
+                stream.abort()
+            except:
+                pass
+            raise NotFoundError("Screenshot not supported for %s" % vm_name)
+        else:
+            stream.finish()
+        finally:
+            os.close(fd)
diff --git a/src/kimchi/model/libvirtconnection.py b/src/kimchi/model/libvirtconnection.py
new file mode 100644
index 0000000..9276acc
--- /dev/null
+++ b/src/kimchi/model/libvirtconnection.py
@@ -0,0 +1,123 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import cherrypy
+import libvirt
+import threading
+import time
+
+
+from kimchi.utils import kimchi_log
+
+
+class LibvirtConnection(object):
+    def __init__(self, uri):
+        self.uri = uri
+        self._connections = {}
+        self._connectionLock = threading.Lock()
+        self.wrappables = self.get_wrappable_objects()
+
+    def get_wrappable_objects(self):
+        """
+        When a wrapped function returns an instance of another libvirt object,
+        we also want to wrap that object so we can catch errors that happen
+        when calling its methods.
+        """
+        objs = []
+        for name in ('virDomain', 'virDomainSnapshot', 'virInterface',
+                     'virNWFilter', 'virNetwork', 'virNodeDevice', 'virSecret',
+                     'virStoragePool', 'virStorageVol', 'virStream'):
+            try:
+                attr = getattr(libvirt, name)
+            except AttributeError:
+                pass
+            objs.append(attr)
+        return tuple(objs)
+
+    def get(self, conn_id=0):
+        """
+        Return current connection to libvirt or open a new one.  Wrap all
+        callable libvirt methods so we can catch connection errors and handle
+        them by restarting the server.
+        """
+        def wrapMethod(f):
+            def wrapper(*args, **kwargs):
+                try:
+                    ret = f(*args, **kwargs)
+                    return ret
+                except libvirt.libvirtError as e:
+                    edom = e.get_error_domain()
+                    ecode = e.get_error_code()
+                    EDOMAINS = (libvirt.VIR_FROM_REMOTE,
+                                libvirt.VIR_FROM_RPC)
+                    ECODES = (libvirt.VIR_ERR_SYSTEM_ERROR,
+                              libvirt.VIR_ERR_INTERNAL_ERROR,
+                              libvirt.VIR_ERR_NO_CONNECT,
+                              libvirt.VIR_ERR_INVALID_CONN)
+                    if edom in EDOMAINS and ecode in ECODES:
+                        kimchi_log.error('Connection to libvirt broken. '
+                                         'Recycling. ecode: %d edom: %d' %
+                                         (ecode, edom))
+                        with self._connectionLock:
+                            self._connections[conn_id] = None
+                    raise
+            wrapper.__name__ = f.__name__
+            wrapper.__doc__ = f.__doc__
+            return wrapper
+
+        with self._connectionLock:
+            conn = self._connections.get(conn_id)
+            if not conn:
+                retries = 5
+                while True:
+                    retries = retries - 1
+                    try:
+                        conn = libvirt.open(self.uri)
+                        break
+                    except libvirt.libvirtError:
+                        kimchi_log.error('Unable to connect to libvirt.')
+                        if not retries:
+                            msg = 'Libvirt is not available, exiting.'
+                            kimchi_log.error(msg)
+                            cherrypy.engine.stop()
+                            raise
+                    time.sleep(2)
+
+                for name in dir(libvirt.virConnect):
+                    method = getattr(conn, name)
+                    if callable(method) and not name.startswith('_'):
+                        setattr(conn, name, wrapMethod(method))
+
+                for cls in self.wrappables:
+                    for name in dir(cls):
+                        method = getattr(cls, name)
+                        if callable(method) and not name.startswith('_'):
+                            setattr(cls, name, wrapMethod(method))
+
+                self._connections[conn_id] = conn
+                # In case we're running into troubles with keeping the
+                # connections alive we should place here:
+                # conn.setKeepAlive(interval=5, count=3)
+                # However the values need to be considered wisely to not affect
+                # hosts which are hosting a lot of virtual machines
+            return conn
diff --git a/src/kimchi/model/libvirtstoragepool.py b/src/kimchi/model/libvirtstoragepool.py
new file mode 100644
index 0000000..e9b9aa8
--- /dev/null
+++ b/src/kimchi/model/libvirtstoragepool.py
@@ -0,0 +1,225 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+import copy
+import libvirt
+
+from kimchi.exception import OperationFailed
+from kimchi.iscsi import TargetClient
+
+class StoragePoolDef(object):
+    @classmethod
+    def create(cls, poolArgs):
+        for klass in cls.__subclasses__():
+            if poolArgs['type'] == klass.poolType:
+                return klass(poolArgs)
+        raise OperationFailed('Unsupported pool type: %s' % poolArgs['type'])
+
+    def __init__(self, poolArgs):
+        self.poolArgs = poolArgs
+
+    def prepare(self, conn):
+        ''' Validate pool arguments and perform preparations. Operation which
+        would cause side effect should be put here. Subclasses can optionally
+        override this method, or it always succeeds by default. '''
+        pass
+
+    @property
+    def xml(self):
+        ''' Subclasses have to override this method to actually generate the
+        storage pool XML definition. Should cause no side effect and be
+        idempotent'''
+        # TODO: When add new pool type, should also add the related test in
+        # tests/test_storagepool.py
+        raise OperationFailed('self.xml is not implemented: %s' % self)
+
+
+class DirPoolDef(StoragePoolDef):
+    poolType = 'dir'
+
+    @property
+    def xml(self):
+        # Required parameters
+        # name:
+        # type:
+        # path:
+        xml = """
+        <pool type='dir'>
+          <name>{name}</name>
+          <target>
+            <path>{path}</path>
+          </target>
+        </pool>
+        """.format(**self.poolArgs)
+        return xml
+
+
+class NetfsPoolDef(StoragePoolDef):
+    poolType = 'netfs'
+
+    def __init__(self, poolArgs):
+        super(NetfsPoolDef, self).__init__(poolArgs)
+        self.path = '/var/lib/kimchi/nfs_mount/' + self.poolArgs['name']
+
+    def prepare(self, conn):
+        # TODO: Verify the NFS export can be actually mounted.
+        pass
+
+    @property
+    def xml(self):
+        # Required parameters
+        # name:
+        # type:
+        # source[host]:
+        # source[path]:
+        poolArgs = copy.deepcopy(self.poolArgs)
+        poolArgs['path'] = self.path
+        xml = """
+        <pool type='netfs'>
+          <name>{name}</name>
+          <source>
+            <host name='{source[host]}'/>
+            <dir path='{source[path]}'/>
+          </source>
+          <target>
+            <path>{path}</path>
+          </target>
+        </pool>
+        """.format(**poolArgs)
+        return xml
+
+
+class LogicalPoolDef(StoragePoolDef):
+    poolType = 'logical'
+
+    def __init__(self, poolArgs):
+        super(LogicalPoolDef, self).__init__(poolArgs)
+        self.path = '/var/lib/kimchi/logical_mount/' + self.poolArgs['name']
+
+    @property
+    def xml(self):
+        # Required parameters
+        # name:
+        # type:
+        # source[devices]:
+        poolArgs = copy.deepcopy(self.poolArgs)
+        devices = []
+        for device_path in poolArgs['source']['devices']:
+            devices.append('<device path="%s" />' % device_path)
+
+        poolArgs['source']['devices'] = ''.join(devices)
+        poolArgs['path'] = self.path
+
+        xml = """
+        <pool type='logical'>
+        <name>{name}</name>
+            <source>
+            {source[devices]}
+            </source>
+        <target>
+            <path>{path}</path>
+        </target>
+        </pool>
+        """.format(**poolArgs)
+        return xml
+
+
+class IscsiPoolDef(StoragePoolDef):
+    poolType = 'iscsi'
+
+    def prepare(self, conn):
+        source = self.poolArgs['source']
+        if not TargetClient(**source).validate():
+            raise OperationFailed("Can not login to iSCSI host %s target %s" %
+                                  (source['host'], source['target']))
+        self._prepare_auth(conn)
+
+    def _prepare_auth(self, conn):
+        try:
+            auth = self.poolArgs['source']['auth']
+        except KeyError:
+            return
+
+        try:
+            virSecret = conn.secretLookupByUsage(
+                libvirt.VIR_SECRET_USAGE_TYPE_ISCSI, self.poolArgs['name'])
+        except libvirt.libvirtError:
+            xml = '''
+            <secret ephemeral='no' private='yes'>
+              <description>Secret for iSCSI storage pool {name}</description>
+              <auth type='chap' username='{username}'/>
+              <usage type='iscsi'>
+                <target>{name}</target>
+              </usage>
+            </secret>'''.format(name=self.poolArgs['name'],
+                                username=auth['username'])
+            virSecret = conn.secretDefineXML(xml)
+
+        virSecret.setValue(auth['password'])
+
+    def _format_port(self, poolArgs):
+        try:
+            port = poolArgs['source']['port']
+        except KeyError:
+            return ""
+        return "port='%s'" % port
+
+    def _format_auth(self, poolArgs):
+        try:
+            auth = poolArgs['source']['auth']
+        except KeyError:
+            return ""
+
+        return '''
+        <auth type='chap' username='{username}'>
+          <secret type='iscsi' usage='{name}'/>
+        </auth>'''.format(name=poolArgs['name'], username=auth['username'])
+
+    @property
+    def xml(self):
+        # Required parameters
+        # name:
+        # type:
+        # source[host]:
+        # source[target]:
+        #
+        # Optional parameters
+        # source[port]:
+        poolArgs = copy.deepcopy(self.poolArgs)
+        poolArgs['source'].update({'port': self._format_port(poolArgs),
+                                   'auth': self._format_auth(poolArgs)})
+        poolArgs['path'] = '/dev/disk/by-id'
+
+        xml = """
+        <pool type='iscsi'>
+          <name>{name}</name>
+          <source>
+            <host name='{source[host]}' {source[port]}/>
+            <device path='{source[target]}'/>
+            {source[auth]}
+          </source>
+          <target>
+            <path>{path}</path>
+          </target>
+        </pool>
+        """.format(**poolArgs)
+        return xml
diff --git a/src/kimchi/model/mockbackend.py b/src/kimchi/model/mockbackend.py
new file mode 100644
index 0000000..d4314ca
--- /dev/null
+++ b/src/kimchi/model/mockbackend.py
@@ -0,0 +1,338 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import copy
+import os
+import random
+
+from kimchi import config
+from kimchi.asynctask import AsyncTask
+from kimchi.objectstore import ObjectStore
+from kimchi.screenshot import VMScreenshot
+
+class MockBackend(object):
+    _network_info = {'state': 'inactive', 'autostart': True, 'connection': '',
+                     'interface': '', 'subnet': '',
+                     'dhcp': {'start': '', 'stop':  ''}}
+
+    TEMPLATE_SCAN = False
+
+    def __init__(self, objstore_loc=None):
+        self.objstore = ObjectStore(objstore_loc)
+        self.next_taskid = 1
+        self.host_stats = self._get_host_stats()
+        self._storagepools = {}
+        self._networks = {}
+        self._templates = {}
+        self._vms = {}
+        self._screenshots = {}
+
+    def _get_host_stats(self):
+        memory_stats = {'total': 3934908416L,
+                        'free': round(random.uniform(0, 3934908416L), 1),
+                        'cached': round(random.uniform(0, 3934908416L), 1),
+                        'buffers': round(random.uniform(0, 3934908416L), 1),
+                        'avail': round(random.uniform(0, 3934908416L), 1)}
+
+        return {'cpu_utilization': round(random.uniform(0, 100), 1),
+                'memory': memory_stats,
+                'disk_read_rate': round(random.uniform(0, 4000), 1),
+                'disk_write_rate': round(random.uniform(0, 4000), 1),
+                'net_recv_rate': round(random.uniform(0, 4000), 1),
+                'net_sent_rate': round(random.uniform(0, 4000), 1)}
+
+    def get_capabilities(self):
+        protocols = ['http', 'https', 'ftp', 'ftps', 'tftp']
+        return {'libvirt_stream_protocols': protocols,
+                'qemu_stream': True,
+                'screenshot': True,
+                'system_report_tool': True}
+
+    def gen_debugreport_file(self, ident):
+        return self.add_task('', self._create_debugreport, ident)
+
+    def _create_debugreport(self, cb, name):
+        path = config.get_debugreports_path()
+        tmpf = os.path.join(path, name + '.tmp')
+        realf = os.path.join(path, name + '.txt')
+        length = random.randint(1000, 10000)
+        with open(tmpf, 'w') as fd:
+            while length:
+                fd.write('mock debug report\n')
+                length = length - 1
+        os.rename(tmpf, realf)
+        cb("OK", True)
+
+    def add_task(self, target_uri, fn, opaque=None):
+        id = self.next_taskid
+        self.next_taskid += 1
+        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
+        return id
+
+    def get_host(self):
+        res = {}
+        res['memory'] = 6114058240
+        res['cpu'] = 'Intel(R) Core(TM) i5 CPU       M 560  @ 2.67GHz'
+        res['os_distro'] = 'Red Hat Enterprise Linux Server'
+        res['os_version'] = '6.4'
+        res['os_codename'] = 'Santiago'
+
+        return res
+
+    def get_storagepools(self):
+        return self._storagepools.keys()
+
+    def do_deep_scan(self):
+        return self.add_task('', time.sleep, 25)
+
+    def create_storagepool(self, params):
+        name = params['name']
+        pool = MockStoragePool(name)
+        pool.info.update(params)
+        if params['type'] == 'dir':
+            pool.info['autostart'] = True
+        else:
+            pool.info['autostart'] = False
+
+        self._storagepools[name] = pool
+
+    def get_storagepool_by_name(self, name):
+        pool = self._storagepools[name]
+        pool.refresh()
+        return pool.info
+
+    def activate_storagepool(self, name):
+        self._storagepools[name].info['state'] = 'active'
+
+    def deactivate_storagepool(self, name):
+        self._storagepools[name].info['state'] = 'inactive'
+
+    def delete_storagepool(self, name):
+        del self._storagepools[name]
+
+    def autostart_storagepool(self, name, value):
+        self._storagepools[name].info['autostart'] = value
+
+    def create_storagevolume(self, pool, params):
+        try:
+            name = params['name']
+            volume = MockStorageVolume(pool, name, params)
+            volume.info['type'] = params['type']
+            volume.info['format'] = params['format']
+            volume.info['path'] = os.path.join(pool.info['path'], name)
+        except KeyError, item:
+            raise MissingParameter(item)
+
+        pool._volumes[name] = volume
+
+    def get_storagevolumes_by_pool(self, pool):
+        return self._storagepools[pool]._volumes.keys()
+
+    def get_storagevolume(self, pool, name):
+        vol = self._storagevolumes[pool]._volumes[name]
+        return vol.info
+
+    def wipe_storagevolume(self, pool, name):
+        self._storagepools[pool]._volumes[name].info['allocation'] = 0
+
+    def resize_storagevolume(self, pool, name, size):
+        self._storagepools[pool]._volumes[name].info['capacity'] = size
+
+    def delete_storagevolume(self, pool, name):
+        del self._storagepools[pool]._volumes[name]
+
+    def create_network(self, params):
+        name = params['name']
+        info = copy.deepcopy(self._network_info)
+        info.update(params)
+        self._networks[name] = info
+
+    def get_networks(self):
+        return self._networks.keys()
+
+    def get_network_by_name(self, name):
+        info = self._networks[name]
+        info['vms'] = self._get_vms_attach_to_network(name)
+        return info
+
+    def _get_vms_attach_to_network(self, network):
+        vms = []
+        for name, dom in self._vms.iteritems():
+             if network in dom.networks:
+                 vms.append(name)
+        return vms
+
+    def activate_network(self, name):
+        self._networks[name]['state'] = 'active'
+
+    def deactivate_network(self, name):
+        self._networks[name]['state'] = 'inactive'
+
+    def delete_network(self, name):
+        del self._networks[name]
+
+    def create_template(self, name, tmpl):
+        self._templates[name] = tmpl.info
+
+    def get_templates(self):
+        return self._templates.keys()
+
+    def get_template_by_name(self, name):
+        return self._templates[name]
+
+    def delete_template(self, name):
+        del self._templates[name]
+
+    def create_vm(self, name, uuid, tmpl, vol_list):
+        vm = MockVM(vm_uuid, name, tmpl.info)
+        icon = tmpl.info.get('icon', None)
+        if icon is not None:
+            vm.info['icon'] = icon
+
+        disk_paths = []
+        for vol in vol_list:
+            disk_paths.append({'pool': pool.name, 'volume': vol_info['name']})
+
+        vm.disk_paths = disk_paths
+        self._vms[name] = vm
+
+    def get_vms(self):
+        return self._vms.keys()
+
+    def get_screenshot_by_name(self, vm_uuid):
+        mockscreenshot = MockVMScreenshot({'uuid': vm_uuid})
+        screenshot = self._screenshots.setdefault(vm_uuid, mockscreenshot)
+        return screenshot.lookup()
+
+    def get_vm_by_name(self, name):
+        vm = self._vms[name]
+        if vm.info['state'] == 'running':
+            vm.info['screenshot'] = self.get_screenshot_by_name(name)
+        else:
+            vm.info['screenshot'] = None
+        return vm.info
+
+    def static_vm_update(self, name, params):
+        vm_info = copy.copy(self._vms[name])
+        for key, val in params.items():
+            if key in VM_STATIC_UPDATE_PARAMS and key in vm_info:
+                vm_info[key] = val
+
+        if 'name' in params:
+            del self._vms[name]
+            self._vms[params['name']] = vm_info
+
+    def live_vm_update(self, name, params):
+        pass
+
+    def delete_vm(self, name):
+        vm = self._vms[name]
+        screenshot = self._screenshots.get(vm.uuid, None)
+        if screenshot is not None:
+            screenshot.delete()
+            del self._screenshots[vm_uuid]
+
+        for disk in vm.disk_paths:
+            self.delete_storagevolume(disk['pool'], disk['volume'])
+
+        del self._vms[name]
+
+    def start_vm(self, name):
+        self._vms[name].info['state'] = 'running'
+
+    def stop_vm(self, name):
+        self._vms[name].info['state'] = 'shutoff'
+
+    def connect_vm(self, name):
+        pass
+
+class MockStoragePool(object):
+    def __init__(self, name):
+        self.name = name
+        self._volumes = {}
+        self.info = {'state': 'inactive', 'capacity': 1024 << 20,
+                     'allocated': 512 << 20, 'available': 512 << 20,
+                     'path': '/var/lib/libvirt/images', 'source': {},
+                     'type': 'dir', 'nr_volumes': 0, 'autostart': 0}
+
+    def refresh(self):
+        state = self.info['state']
+        self.info['nr_volumes'] = 0
+        if state == 'active':
+            self.info['nr_volumes'] = len(self._volumes)
+
+class MockStorageVolume(object):
+    def __init__(self, pool, name, params={}):
+        self.name = name
+        self.pool = pool
+        self.info = {'type': 'disk', 'allocation': 512,
+                     'capacity': params.get('capacity', 1024) << 20,
+                     'format': params.get('format', 'raw')}
+
+        if fmt == 'iso':
+            self.info['allocation'] = self.info['capacity']
+            self.info['os_version'] = '19'
+            self.info['os_distro'] = 'fedora'
+            self.info['bootable'] = True
+
+class MockVM(object):
+    def __init__(self, uuid, name, template_info):
+        self.uuid = uuid
+        self.name = name
+        self.disk_paths = []
+        self.networks = template_info['networks']
+        self.info = {'state': 'shutoff',
+                     'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \
+                                'net_throughput_peak': 100, 'io_throughput': 45, \
+                                'io_throughput_peak': 100}",
+                     'uuid': self.uuid,
+                     'memory': template_info['memory'],
+                     'cpus': template_info['cpus'],
+                     'icon': None,
+                     'graphics': {'type': 'vnc', 'listen': '0.0.0.0', 'port': None}
+                     }
+        self.info['graphics'].update(template_info['graphics'])
+
+class MockVMScreenshot(VMScreenshot):
+    OUTDATED_SECS = 5
+    BACKGROUND_COLOR = ['blue', 'green', 'purple', 'red', 'yellow']
+    BOX_COORD = (50, 115, 206, 141)
+    BAR_COORD = (50, 115, 50, 141)
+
+    def __init__(self, vm_name):
+        VMScreenshot.__init__(self, vm_name)
+        self.coord = MockVMScreenshot.BAR_COORD
+        self.background = random.choice(MockVMScreenshot.BACKGROUND_COLOR)
+
+    def _generate_scratch(self, thumbnail):
+        self.coord = (self.coord[0],
+                      self.coord[1],
+                      min(MockVMScreenshot.BOX_COORD[2],
+                          self.coord[2]+random.randrange(50)),
+                      self.coord[3])
+
+        image = Image.new("RGB", (256, 256), self.background)
+        d = ImageDraw.Draw(image)
+        d.rectangle(MockVMScreenshot.BOX_COORD, outline='black')
+        d.rectangle(self.coord, outline='black', fill='black')
+        image.save(thumbnail)
diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py
new file mode 100644
index 0000000..dfefa81
--- /dev/null
+++ b/src/kimchi/model/networks.py
@@ -0,0 +1,115 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi.model import interfaces
+
+class Networks(object):
+    def __init__(self, backend):
+        self.backend = backend
+        self.ifaces = interfaces.Interfaces(backend)
+
+    def create(self, params):
+        name = params['name']
+        if name in self.get_list():
+            raise InvalidOperation("Network %s already exists" % name)
+
+        connection = params['connection']
+        # set subnet, bridge network do not need subnet
+        if connection in ["nat", 'isolated']:
+            self._set_network_subnet(params)
+
+        # only bridge network need bridge(linux bridge) or interface(macvtap)
+        if connection == 'bridge':
+            iface = params.get('interface', None)
+            if iface is None:
+                raise MissingParameter("You need to specify interface to create"
+                                       " a bridged network.")
+
+            if iface in self.ifaces.get_used_ifaces():
+                raise InvalidParameter("interface '%s' already in use." % iface)
+
+            if not (netinfo.is_bridge(iface) or netinfo.is_bare_nic(iface) or
+                    netinfo.is_bonding(iface)):
+                raise InvalidParameter("The interface should be bare nic, bonding "
+                                       "or bridge device.")
+
+        self.backend.create_network(params)
+        return name
+
+    def get_list(self):
+        return sorted(self.backend.get_networks())
+
+    def _set_network_subnet(self, params):
+        netaddr = params.get('subnet', '')
+        net_addrs = []
+        # lookup a free network address for nat and isolated automatically
+        if not netaddr:
+            for net_name in self.get_list():
+                net = self.backend.get_network_by_name(net_name)
+                subnet = net['subnet']
+                subnet and net_addrs.append(ipaddr.IPNetwork(subnet))
+
+            netaddr = network.get_one_free_network(net_addrs)
+            if not netaddr:
+                raise OperationFailed("can not find a free IP address for "
+                                      "network '%s'" % params['name'])
+        try:
+            ip = ipaddr.IPNetwork(netaddr)
+        except ValueError as e:
+            raise InvalidParameter("%s" % e)
+
+        if ip.ip == ip.network:
+            ip.ip = ip.ip + 1
+
+        dhcp_start = str(ip.ip + ip.numhosts / 2)
+        dhcp_end = str(ip.ip + ip.numhosts - 2)
+
+        params.update({'subnet': str(ip),
+                       'dhcp': {'range': {'start': dhcp_start,
+                                'end': dhcp_end}}})
+
+class Network(Networks):
+    def _network_exist(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Network '%s' not found.")
+
+        return True
+
+    def lookup(self, name):
+        if self._network_exist(name):
+            return self.backend.get_network_by_name(name)
+
+    def activate(self, name):
+        if self._network_exist(name):
+            return self.backend.activate_network(name)
+
+    def deactivate(self, name):
+        if self._network_exist(name):
+            return self.backend.deactivate_network(name)
+
+    def delete(self, name):
+        if self.lookup(name)['state'] == 'active':
+            raise InvalidOperation("Unable to delete the active network %s" %
+                                    name)
+
+        return self.backend.delete_network(name)
diff --git a/src/kimchi/model/plugins.py b/src/kimchi/model/plugins.py
new file mode 100644
index 0000000..3cbae77
--- /dev/null
+++ b/src/kimchi/model/plugins.py
@@ -0,0 +1,29 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi import utils
+
+class Plugins(object):
+    def get_list(self):
+        return [plugin for (plugin, config) in utils.get_enabled_plugins()]
+
diff --git a/src/kimchi/model/storagepools.py b/src/kimchi/model/storagepools.py
new file mode 100644
index 0000000..abdebd8
--- /dev/null
+++ b/src/kimchi/model/storagepools.py
@@ -0,0 +1,86 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+ISO_POOL_NAME = u'kimchi_isos'
+
+from kimchi.exception import InvalidParameter
+
+class StoragePools(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def get_list(self):
+        return sorted(self.backend.get_storagepools())
+
+    def create(self, params):
+        name = params['name']
+        if name in self.get_list() or name in (ISO_POOL_NAME,):
+            raise InvalidParameter("Storage pool '%s' already exists" % name)
+
+        task_id = None
+        if params['type'] == 'kimchi-iso':
+            task_id = self.backend.do_deep_scan(params)
+
+        self.backend.create_storagepool(params)
+        return name
+
+class StoragePool(StoragePools):
+    def lookup(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        return self.backend.get_storagepool_by_name(name)
+
+    def activate(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        self.backend.activate_storagepool()
+
+    def deactivate(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        self.backend.deactivate_storagepool()
+
+    def delete(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        if self.get_storagepool_by_name(name)['state'] == 'active':
+            raise InvalidOperation("Unable to delete active storage pool '%s'" %
+                                   name)
+
+        self.backend.delete_storagepool()
+
+    def update(self, name, params):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        autostart = params['autostart']
+        if autostart not in [True, False]:
+            raise InvalidOperation("Autostart flag must be true or false")
+
+        self.backend.autostart_storagepool(name, autostart)
+
+        return name
diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py
new file mode 100644
index 0000000..9f3c93b
--- /dev/null
+++ b/src/kimchi/model/storagevolumes.py
@@ -0,0 +1,95 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
+from kimchi.model import storagepools
+
+class StorageVolumes(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def create(self, pool, params):
+        if name in self.get_list(pool):
+            raise InvalidParameter("Storage volume '%s' already exists.")
+
+        self.backend.create_storagevolume(pool, params)
+        return name
+
+    def get_list(self, pool):
+        info = self.backend.get_storagepool_by_name(pool)
+        if info['state'] != 'active':
+            raise InvalidOperation("Unable to list volumes in inactive "
+                                   "storagepool %s" % pool)
+
+        return self.backend.get_storagevolumes_by_pool(pool)
+
+class StorageVolume(StorageVolumes):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def _storagevolume_exist(self, pool, name):
+        if name not in self.get_list(pool):
+            raise NotFoundError("Storage volume '%' not found in '%' pool" %
+                                (name, pool))
+        return True
+
+    def lookup(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            return self.backend.get_storagevolume(pool, name)
+
+    def resize(self, pool, name, size):
+        if self._storagevolume_exist(pool, name):
+            self.backend.resize_storagevolume(pool, name, size)
+
+    def wipe(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            self.backend.wipe_storagevolume(pool, name)
+
+    def delete(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            self.backend.delete_storagevolume(pool, name)
+
+class IsoVolumes(StorageVolumes):
+    def __init__(self, backend):
+        self.backend = backend
+        self.storagepools = storagepools.StoragePools(self.backend)
+
+    def get_list(self, pool):
+        iso_volumes = []
+
+        for pool in self.storagepools.get_list():
+            try:
+                volumes = self.get_list(pool)
+            except InvalidOperation:
+                # Skip inactive pools
+                continue
+
+            for volume in volumes:
+                res = self.lookup(pool, volume)
+                if res['format'] == 'iso':
+                    # prevent iso from different pool having same volume name
+                    res['name'] = '%s-%s' % (pool, volume)
+                    iso_volumes.append(res)
+
+        return iso_volumes
diff --git a/src/kimchi/model/tasks.py b/src/kimchi/model/tasks.py
new file mode 100644
index 0000000..29eaddf
--- /dev/null
+++ b/src/kimchi/model/tasks.py
@@ -0,0 +1,45 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi.exception import NotFoundError
+
+ERROR_TASK_NOT_FOUND = "Task id '%s' not found."
+
+class Tasks(object):
+    def __init__(self, backend):
+        self.objstore = backend.objstore
+
+    def get_list(self):
+        with self.objstore as session:
+            return session.get_list('task')
+
+class Task(object):
+    def __init__(self, backend):
+        self.objstore = backend.objstore
+
+    def lookup(self, ident):
+        if ident not in self.get_list():
+            raise NotFoundError(ERROR_TASK_NOT_FOUND % ident)
+
+        with self.objstore as session:
+            return session.get('task', str(ident))
diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py
new file mode 100644
index 0000000..86cd54f
--- /dev/null
+++ b/src/kimchi/model/templates.py
@@ -0,0 +1,89 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+from kimchi.vmtemplate import VMTemplate
+from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
+from kimchi.exception import OperationFailed
+
+class Templates(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def create(self, params):
+        name = params['name']
+        if name in self.get_list:
+            raise InvalidOperation("Template '%s' already exists." % name)
+
+        for net_name in params.get(u'networks', []):
+            if net_name not in self.backend.get_networks():
+                raise InvalidParameter("Network '%s' not found," % net_name)
+
+        try:
+            tmpl = VMTemplate(params, self.backend.TEMPLATE_SCAN)
+            self.backend.create_template(name, tmpl)
+        except Exception, e:
+            raise OperationFailed("Unable to create template '%s': %s" %
+                                  (name, e.message))
+
+        return name
+
+    def get_list(self):
+        return sorted(self.backend.get_templates())
+
+class Template(Templates):
+    def lookup(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Template '%s' not found." % name)
+
+        params = self.backend.get_template_by_name(name)
+        tmpl = VMTemplate(params, False)
+        return tmpl.info
+
+    def delete(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Template '%s' not found." % name)
+
+        self.backend.delete_template(name)
+
+    def update(self, name, params):
+        old_t = self.lookup(name)
+        new_t = copy.copy(old_t)
+
+        new_t.update(params)
+        ident = name
+
+        new_storagepool = new_t.get(u'storagepool', '')
+        if new_storagepool not in self.backend.get_storagepools():
+            raise InvalidParameter("Storage pool '%s' not found." % name)
+
+        for net_name in params.get(u'networks', []):
+            if net_name not in self.backend.get_networks():
+                raise InvalidParameter("Network '%s' not found." % net_name)
+
+        self.delete(name)
+        try:
+            ident = self.create(new_t)
+        except:
+            ident = self.create(old_t)
+
+        return ident
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
new file mode 100644
index 0000000..3f1b6c3
--- /dev/null
+++ b/src/kimchi/model/vms.py
@@ -0,0 +1,164 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+import uuid
+
+VM_STATIC_UPDATE_PARAMS = {'name': './name'}
+
+class VMs(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def create(self, params):
+        vm_uuid = str(uuid.uuid4())
+        vm_list = self.get_list()
+        name = self._get_vm_name(params.get('name'), t_name, vm_list)
+        # incoming text, from js json, is unicode, do not need decode
+        if name in vm_list:
+            raise InvalidOperation("VM '%s' already exists" % name)
+
+        vm_overrides = dict()
+        override_params = ['storagepool', 'graphics']
+        for param in override_params:
+            value = params.get(param, None)
+            if value is not None:
+                vm_overrides[param] = value
+
+        t_name = self._uri_to_name('templates', params['template'])
+        t_params = self.backend.get_template_by_name(t_name)
+        t_params.update(vm_overrides)
+        tmpl = VMTemplate(t_params, False)
+
+        caps = self.backend.get_capabilities()
+        if not caps.qemu_stream and t.info.get('iso_stream', False):
+            raise InvalidOperation("Remote ISO image is not supported by this"
+                                   " server.")
+
+        pool_name = self._uri_to_name('storagepools', pool_uri)
+        self._validate_storagepool(pool_name)
+        self._validate_network(tmpl)
+        vol_list = tmpl.to_volume_list(vm_uuid)
+        for vol_info in vol_list:
+            self.backend.create_storagevolume(pool_name, vol_info)
+
+        self.backend.create_vm(name, vm_uuid, tmpl, vol_list)
+        return name
+
+    def _validate_storagepool(self, pool_name):
+        try:
+            pool_info = self.backend.get_storagepool_by_name(pool_name)
+        except Exception:
+            raise InvalidParameter("Storage pool '%s' specified by template "
+                                   "does not exist" % pool_name)
+
+        if not pool_info['state'] != 'active':
+            raise InvalidParameter("Storage pool '%s' specified by template "
+                                   "is not active" % pool_name)
+
+    def _validate_network(self, tmpl):
+        names = tmpl.info['networks']
+        for name in names:
+            if name not in self.backend.get_networks():
+                raise InvalidParameter("Network '%s' specified by template "
+                                       "does not exist.")
+
+            net_info = self.backend.get_network_by_name(name)
+            if net_info['state'] != 'active':
+                raise InvalidParameter("Network '%s' specified by template is "
+                                       "not active.")
+
+    def _uri_to_name(self, collection, uri):
+        expr = '/%s/(.*?)/?$' % collection
+        m = re.match(expr, uri)
+        if not m:
+            raise InvalidParameter(uri)
+        return m.group(1)
+
+    def _get_vm_name(self, vm_name, t_name, name_list):
+        if vm_name:
+            return vm_name
+
+        for i in xrange(1, 1000):
+            vm_name = "%s-vm-%i" % (t_name, i)
+            if vm_name not in name_list:
+                return vm_name
+
+        raise OperationFailed("Unable to choose a VM name")
+
+    def get_list(self):
+        return sorted(self.backend.get_vms())
+
+class VM(VMs):
+    def _vm_exists(self, name):
+        if name not in self.backend.get_vms():
+            raise NotFoundError("VM '%s' not found." % name)
+
+        return True
+
+    def lookup(self, name):
+        if self._vm_exists(name):
+            return self.backend.get_vm_by_name(name)
+
+    def update(self, name, params):
+        if self._vm_exists(name):
+            if 'name' in params:
+                state = self.get_vm_by_name(name)['state']
+                if state == 'running':
+                    raise InvalidParameter("The VM needs to be shutted off for"
+                                           "renaming.")
+
+                if params['name'] in self.get_list():
+                    raise InvalidParameter("VM name '%s' already exists" %
+                                            params['name'])
+
+            self.backend.static_vm_update(name, params)
+            self.backend.live_vm_update(name, params)
+
+            return params.get('name', None) or name
+
+    def delete(self, name):
+        if self._vm_exists(name):
+            self.backend.delete_vm(name)
+
+    def start(self, name):
+        if self._vm_exists(name):
+            self.backend.start_vm(self, name)
+
+    def stop(self, name):
+        if self._vm_exists(name):
+            self.backend.stop_vm(self, name)
+
+    def connect(self, name):
+        if self._vm_exists(name):
+            self.backend.connect_vm(self, name)
+
+class VMScreenshot(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def lookup(self, name):
+        vm_info = self.backend.get_vm_by_name(name)
+        if vm_info['state'] != 'running':
+            raise NotFoundError('No screenshot for stopped vm')
+
+        return self.backend.get_screenshot_by_name(vm_info['uuid'])
diff --git a/src/kimchi/model_/__init__.py b/src/kimchi/model_/__init__.py
deleted file mode 100644
index 8a37cc4..0000000
--- a/src/kimchi/model_/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
diff --git a/src/kimchi/model_/config.py b/src/kimchi/model_/config.py
deleted file mode 100644
index 7c0a5d6..0000000
--- a/src/kimchi/model_/config.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi.distroloader import DistroLoader
-from kimchi.exception import NotFoundError
-
-ERR_DISTRO_NOT_FOUND = "Distro '%s' not found."
-
-class Config(object):
-    pass
-
-class Capabilities(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def lookup(self, ident):
-        return self.backend.get_capabilities()
-
-class Distros(object):
-    def __init__(self):
-        self._distroloader = DistroLoader()
-        self._distros = self._distroloader.get()
-
-    def get_list(self):
-        return self._distros.keys()
-
-class Distro(Distros):
-    def lookup(self, ident):
-        if ident not in self.get_list():
-            raise NotFoundError(ERR_DISTRO_NOT_FOUND % ident)
-
-        return self._distros[ident]
diff --git a/src/kimchi/model_/debugreports.py b/src/kimchi/model_/debugreports.py
deleted file mode 100644
index 4db1ace..0000000
--- a/src/kimchi/model_/debugreports.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-import glob
-import os
-import time
-
-from kimchi import config
-from kimchi.exception import NotFoundError
-
-class DebugReports(object):
-    def __init__(self, backend):
-        pass
-
-    def get_list(self):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, '*.*')
-        file_lists = glob.glob(file_pattern)
-        file_lists = [os.path.split(file)[1] for file in file_lists]
-        name_lists = [file.split('.', 1)[0] for file in file_lists]
-
-        return name_lists
-
-class DebugReport(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def lookup(self, name):
-        file_target = self._get_debugreport(name)
-        ctime = os.stat(file_target).st_ctime
-        ctime = time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime(ctime))
-        file_target = os.path.split(file_target)[-1]
-        file_target = os.path.join("/data/debugreports", file_target)
-        return {'file': file_target,
-                'ctime': ctime}
-
-    def create(self, params):
-        ident = params['name']
-        taskid = self.backend.gen_debugreport_file(ident)
-        if taskid is None:
-            raise OperationFailed("Debug report tool not found.")
-
-        with self.backend.objstore as session:
-            return session.get('task', str(taskid))
-
-    def delete(self, name):
-        file_target = self._get_debugreports(name)
-        os.remove(file_target)
-
-    def _get_debugreport(self, name):
-        path = config.get_debugreports_path()
-        file_pattern = os.path.join(path, name + '.*')
-        try:
-            file_target = glob.glob(file_pattern)[0]
-        except IndexError:
-            raise NotFoundError("Debug report '%s' not found.")
-
-        return file_target
-
-class DebugReportContent(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def lookup(self, name):
-        debugreport = DebugReports(backend)
-        return self.debugreport.lookup(name)
diff --git a/src/kimchi/model_/host.py b/src/kimchi/model_/host.py
deleted file mode 100644
index f5ef0d7..0000000
--- a/src/kimchi/model_/host.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi import disks
-
-class Host(object):
-    def __init__(self, backend):
-        self.info = backend.get_host()
-
-    def lookup(self, ident):
-        return self.info
-
-class HostStats(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def lookup(self, ident):
-        return self.backend.host_stats
-
-class Partitions(object):
-    def get_list(self):
-        return disks.get_partitions_names()
-
-class Partition(object):
-    def lookup(self, ident):
-        if ident not in disks.get_partitions_names():
-            raise NotFoundError("Partition %s not found in the host" % name)
-
-        return disks.get_partition_details(ident)
diff --git a/src/kimchi/model_/interfaces.py b/src/kimchi/model_/interfaces.py
deleted file mode 100644
index 7b1d156..0000000
--- a/src/kimchi/model_/interfaces.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi import netinfo
-
-class Interfaces(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def get_list(self):
-        return list(set(netinfo.all_favored_interfaces()) -
-                    set(self.get_used_ifaces()))
-
-    def get_used_ifaces(self):
-        net_names = self.backend.get_networks()
-        interfaces = []
-        for name in net_names:
-            net_dict = self.backend.get_network_by_name(name)
-            net_dict['interface'] and interfaces.append(net_dict['interface'])
-
-        return interfaces
-
-class Interface(object):
-    def lookup(self, name):
-        try:
-            return netinfo.get_interface_info(name)
-        except ValueError, e:
-            raise NotFoundError(e)
diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py
deleted file mode 100644
index bf29c78..0000000
--- a/src/kimchi/model_/libvirtbackend.py
+++ /dev/null
@@ -1,955 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-import cherrypy
-import fnmatch
-import glob
-import ipaddr
-import logging
-import os
-import platform
-import psutil
-import shutil
-import subprocess
-import time
-
-from cherrypy.process.plugins import BackgroundTask
-from collections import defaultdict
-
-from kimchi import config
-from kimchi import network
-from kimchi import netinfo
-from kimchi import networkxml
-from kimchi import xmlutils
-from kimchi.asynctask import AsyncTask
-from kimchi.exception import IsoFormatError, NotFoundError, OperationFailed
-from kimchi.featuretests import FeatureTests
-from kimchi.isoinfo import IsoImage
-from kimchi.model_.libvirtconnection import LibvirtConnection
-from kimchi.model_.libvirtstoragepool import StoragePoolDef
-from kimchi.objectstore import ObjectStore
-from kimchi.scan import Scanner
-from kimchi.screenshot import VMScreenshot
-from kimchi.utils import kimchi_log
-
-GUESTS_STATS_INTERVAL = 5
-HOST_STATS_INTERVAL = 1
-STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
-                             'path': '/pool/source/dir/@path'}}
-
-class LibvirtBackend(object):
-    dom_state_map = {0: 'nostate',
-                     1: 'running',
-                     2: 'blocked',
-                     3: 'paused',
-                     4: 'shutdown',
-                     5: 'shutoff',
-                     6: 'crashed'}
-
-    pool_state_map = {0: 'inactive',
-                      1: 'initializing',
-                      2: 'active',
-                      3: 'degraded',
-                      4: 'inaccessible'}
-
-    volume_type_map = {0: 'file',
-                       1: 'block',
-                       2: 'directory',
-                       3: 'network'}
-
-    TEMPLATE_SCAN = True
-
-    def __init__(self, libvirt_uri=None, objstore_loc=None):
-        self.libvirt_uri = libvirt_uri or 'qemu:///system'
-        self.conn = LibvirtConnection(self.libvirt_uri)
-        self.objstore = ObjectStore(objstore_loc)
-        self.next_taskid = 1
-        self.scanner = Scanner(self._clean_scan)
-        self.stats = {}
-        self.host_stats = defaultdict(int)
-        self.guests_stats_thread = BackgroundTask(GUESTS_STATS_INTERVAL,
-                                                  self._update_guests_stats)
-        self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
-                                                self._update_host_stats)
-        self.guests_stats_thread.start()
-        self.host_stats_thread.start()
-
-        # Subscribe function to set host capabilities to be run when cherrypy
-        # server is up
-        # It is needed because some features tests depends on the server
-        cherrypy.engine.subscribe('start', self._set_capabilities)
-
-        # Please add new possible debug report command here
-        # and implement the report generating function
-        # based on the new report command
-        self.report_tools = ({'cmd': 'sosreport --help',
-                              'fn': self._sosreport_generate},)
-
-    def _set_capabilities(self):
-        kimchi_log.info("*** Running feature tests ***")
-        self.qemu_stream = FeatureTests.qemu_supports_iso_stream()
-        self.qemu_stream_dns = FeatureTests.qemu_iso_stream_dns()
-
-        self.libvirt_stream_protocols = []
-        for p in ['http', 'https', 'ftp', 'ftps', 'tftp']:
-            if FeatureTests.libvirt_supports_iso_stream(p):
-                self.libvirt_stream_protocols.append(p)
-
-        kimchi_log.info("*** Feature tests completed ***")
-    _set_capabilities.priority = 90
-
-    def get_capabilities(self):
-        report_tool = self._get_system_report_tool()
-
-        return {'libvirt_stream_protocols': self.libvirt_stream_protocols,
-                'qemu_stream': self.qemu_stream,
-                'screenshot': VMScreenshot.get_stream_test_result(),
-                'system_report_tool': bool(report_tool)}
-
-    def _update_guests_stats(self):
-        conn = self.conn.get()
-        vm_list = self.get_vms()
-
-        for name in vm_list:
-            info = self.get_vm_by_name(name)
-            vm_uuid = info['uuid']
-            state = info['state']
-            if state != 'running':
-                self.stats[vm_uuid] = {}
-                continue
-
-            if self.stats.get(vm_uuid, None) is None:
-                self.stats[vm_uuid] = {}
-
-            timestamp = time.time()
-            prevStats = self.stats.get(vm_uuid, {})
-            seconds = timestamp - prevStats.get('timestamp', 0)
-            self.stats[vm_uuid].update({'timestamp': timestamp})
-
-            dom = conn.lookupByName(name.encode("utf-8"))
-            self._get_percentage_cpu_usage(vm_uuid, dom.info, seconds)
-            self._get_network_io_rate(vm_uuid, dom, seconds)
-            self._get_disk_io_rate(vm_uuid, dom, seconds)
-
-    def _get_percentage_cpu_usage(self, vm_uuid, info, seconds):
-        prevCpuTime = self.stats[vm_uuid].get('cputime', 0)
-
-        cpus = info[3]
-        cpuTime = info[4] - prevCpuTime
-
-        base = (((cpuTime) * 100.0) / (seconds * 1000.0 * 1000.0 * 1000.0))
-        percentage = max(0.0, min(100.0, base / cpus))
-
-        self.stats[vm_uuid].update({'cputime': info[4], 'cpu': percentage})
-
-    def _get_network_io_rate(self, vm_uuid, dom, seconds):
-        prevNetRxKB = self.stats[vm_uuid].get('netRxKB', 0)
-        prevNetTxKB = self.stats[vm_uuid].get('netTxKB', 0)
-        currentMaxNetRate = self.stats[vm_uuid].get('max_net_io', 100)
-
-        rx_bytes = 0
-        tx_bytes = 0
-
-        tree = ElementTree.fromstring(dom.XMLDesc(0))
-        for target in tree.findall('devices/interface/target'):
-            dev = target.get('dev')
-            io = dom.interfaceStats(dev)
-            rx_bytes += io[0]
-            tx_bytes += io[4]
-
-        netRxKB = float(rx_bytes) / 1000
-        netTxKB = float(tx_bytes) / 1000
-
-        rx_stats = (netRxKB - prevNetRxKB) / seconds
-        tx_stats = (netTxKB - prevNetTxKB) / seconds
-
-        rate = rx_stats + tx_stats
-        max_net_io = round(max(currentMaxNetRate, int(rate)), 1)
-
-        self.stats[vm_uuid].update({'net_io': rate, 'max_net_io': max_net_io,
-                                    'netRxKB': netRxKB, 'netTxKB': netTxKB})
-
-    def _get_disk_io_rate(self, vm_uuid, dom, seconds):
-        prevDiskRdKB = self.stats[vm_uuid].get('diskRdKB', 0)
-        prevDiskWrKB = self.stats[vm_uuid].get('diskWrKB', 0)
-        currentMaxDiskRate = self.stats[vm_uuid].get('max_disk_io', 100)
-
-        rd_bytes = 0
-        wr_bytes = 0
-
-        tree = ElementTree.fromstring(dom.XMLDesc(0))
-        for target in tree.findall("devices/disk/target"):
-            dev = target.get("dev")
-            io = dom.blockStats(dev)
-            rd_bytes += io[1]
-            wr_bytes += io[3]
-
-        diskRdKB = float(rd_bytes) / 1024
-        diskWrKB = float(wr_bytes) / 1024
-
-        rd_stats = (diskRdKB - prevDiskRdKB) / seconds
-        wr_stats = (diskWrKB - prevDiskWrKB) / seconds
-
-        rate = rd_stats + wr_stats
-        max_disk_io = round(max(currentMaxDiskRate, int(rate)), 1)
-
-        self.stats[vm_uuid].update({'disk_io': rate, 'max_disk_io': max_disk_io,
-                                    'diskRdKB': diskRdKB, 'diskWrKB': diskWrKB})
-
-    def _update_host_stats(self):
-        preTimeStamp = self.host_stats['timestamp']
-        timestamp = time.time()
-        # FIXME when we upgrade psutil, we can get uptime by psutil.uptime
-        # we get uptime by float(open("/proc/uptime").readline().split()[0])
-        # and calculate the first io_rate after the OS started.
-        seconds = (timestamp - preTimeStamp if preTimeStamp else
-                   float(open("/proc/uptime").readline().split()[0]))
-
-        self.host_stats['timestamp'] = timestamp
-        self._get_host_disk_io_rate(seconds)
-        self._get_host_network_io_rate(seconds)
-
-        # get cpu utilization
-        self.host_stats['cpu_utilization'] = psutil.cpu_percent(None)
-
-        # get memory stats
-        virt_mem = psutil.virtual_memory()
-        self.host_stats['memory'] = {'total': virt_mem.total,
-                                     'free': virt_mem.free,
-                                     'cached': virt_mem.cached,
-                                     'buffers': virt_mem.buffers,
-                                     'avail': virt_mem.available}
-
-    def _get_host_disk_io_rate(self, seconds):
-        prev_read_bytes = self.host_stats['disk_read_bytes']
-        prev_write_bytes = self.host_stats['disk_write_bytes']
-
-        disk_io = psutil.disk_io_counters(False)
-        read_bytes = disk_io.read_bytes
-        write_bytes = disk_io.write_bytes
-
-        rd_rate = int(float(read_bytes - prev_read_bytes) / seconds + 0.5)
-        wr_rate = int(float(write_bytes - prev_write_bytes) / seconds + 0.5)
-
-        self.host_stats.update({'disk_read_rate': rd_rate,
-                                'disk_write_rate': wr_rate,
-                                'disk_read_bytes': read_bytes,
-                                'disk_write_bytes': write_bytes})
-
-    def _get_host_network_io_rate(self, seconds):
-        prev_recv_bytes = self.host_stats['net_recv_bytes']
-        prev_sent_bytes = self.host_stats['net_sent_bytes']
-
-        net_ios = psutil.network_io_counters(True)
-        recv_bytes = 0
-        sent_bytes = 0
-
-        ifaces = set(netinfo.nics() + netinfo.wlans()) & set(net_ios.iterkeys())
-        for key in ifaces:
-            recv_bytes = recv_bytes + net_ios[key].bytes_recv
-            sent_bytes = sent_bytes + net_ios[key].bytes_sent
-
-        rx_rate = int(float(recv_bytes - prev_recv_bytes) / seconds + 0.5)
-        tx_rate = int(float(sent_bytes - prev_sent_bytes) / seconds + 0.5)
-
-        self.host_stats.update({'net_recv_rate': rx_rate,
-                                'net_sent_rate': tx_rate,
-                                'net_recv_bytes': recv_bytes,
-                                'net_sent_bytes': sent_bytes})
-
-    def gen_debugreport_file(self, name):
-        gen_cmd = self._get_system_report_tool()
-        if gen_cmd is None:
-            return None
-
-        return self.add_task('', gen_cmd, name)
-
-    def _get_system_report_tool(self):
-        # check if the command can be found by shell one by one
-        for helper_tool in self.report_tools:
-            try:
-                retcode = subprocess.call(helper_tool['cmd'], shell=True,
-                                          stdout=subprocess.PIPE,
-                                          stderr=subprocess.PIPE)
-                if retcode == 0:
-                    return helper_tool['fn']
-            except Exception, e:
-                kimchi_log.info('Exception running command: %s', e)
-
-        return None
-
-    def _sosreport_generate(self, cb, name):
-        command = 'sosreport --batch --name "%s"' % name
-        try:
-            retcode = subprocess.call(command, shell=True,
-                                      stdout=subprocess.PIPE,
-                                      stderr=subprocess.PIPE)
-            if retcode < 0:
-                raise OperationFailed('Command terminated with signal')
-            elif retcode > 0:
-                raise OperationFailed('Command failed: rc = %i' % retcode)
-
-            pattern = '/tmp/sosreport-%s-*' % name
-            for reportFile in glob.glob(pattern):
-                if not fnmatch.fnmatch(reportFile, '*.md5'):
-                    output = reportFile
-                    break
-            else:
-                # sosreport tends to change the name mangling rule and
-                # compression file format between different releases.
-                # It's possible to fail to match a report file even sosreport
-                # runs successfully. In future we might have a general name
-                # mangling function in kimchi to format the name before passing
-                # it to sosreport. Then we can delete this exception.
-                raise OperationFailed('Can not find generated debug report '
-                                      'named by %s' % pattern)
-            ext = output.split('.', 1)[1]
-            path = config.get_debugreports_path()
-            target = os.path.join(path, name)
-            target_file = '%s.%s' % (target, ext)
-            shutil.move(output, target_file)
-            os.remove('%s.md5' % output)
-            cb('OK', True)
-        except Exception, e:
-            # No need to call cb to update the task status here.
-            # The task object will catch the exception rasied here
-            # and update the task status there
-            log = logging.getLogger('Model')
-            log.warning('Exception in generating debug file: %s', e)
-            raise OperationFailed(e)
-
-    def add_task(self, target_uri, fn, opaque=None):
-        id = self.next_taskid
-        self.next_taskid += 1
-        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
-        return id
-
-    def get_host(self):
-        res = {}
-        with open('/proc/cpuinfo') as f:
-            for line in f.xreadlines():
-                if "model name" in line:
-                    res['cpu'] = line.split(':')[1].strip()
-                    break
-
-        res['memory'] = psutil.TOTAL_PHYMEM
-        distro, version, codename = platform.linux_distribution()
-        res['os_distro'] = distro
-        res['os_version'] = version
-        res['os_codename'] = unicode(codename,"utf-8")
-
-        return res
-
-    def get_storagepools(self):
-        try:
-            conn = self.conn.get()
-            names = conn.listStoragePools()
-            names += conn.listDefinedStoragePools()
-            return names
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _clean_scan(self, pool_name):
-        try:
-            self.deactivate_storagepool(pool_name)
-            with self.objstore as session:
-                session.delete('scanning', pool_name)
-        except Exception, e:
-            kimchi_log.debug("Error while cleaning deep scan results" %
-                             e.message)
-
-    def do_deep_scan(self, params):
-        scan_params = dict(ignore_list=[])
-        scan_params['scan_path'] = params['path']
-        params['type'] = 'dir'
-
-        for pool in self.get_storagepools():
-            try:
-                res = self.get_storagepool_by_name(pool)
-                if res['state'] == 'active':
-                    scan_params['ignore_list'].append(res['path'])
-            except Exception, e:
-                kimchi_log.debug("Error while preparing for deep scan: %s" %
-                                 e.message)
-
-        params['path']  = self.scanner.scan_dir_prepare(params['name'])
-        scan_params['pool_path'] = params['path']
-        task_id = self.add_task('', self.scanner.start_scan, scan_params)
-        # Record scanning-task/storagepool mapping for future querying
-        with self.objstore as session:
-                session.store('scanning', params['name'], task_id)
-        return task_id
-
-    def create_storagepool(self, params):
-        conn = self.conn.get()
-        try:
-            poolDef = StoragePoolDef.create(params)
-            poolDef.prepare(conn)
-            xml = poolDef.xml
-        except KeyError, key:
-            raise MissingParameter("You need to specify '%s' in order to "
-                                   "create storage pool" % key)
-
-        try:
-            if task_id:
-                # Create transient pool for deep scan
-                conn.storagePoolCreateXML(xml, 0)
-                return name
-
-            pool = conn.storagePoolDefineXML(xml, 0)
-            if params['type'] in ['logical', 'dir', 'netfs']:
-                pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW)
-                # autostart dir and logical storage pool created from kimchi
-                pool.setAutostart(1)
-            else:
-                # disable autostart for others
-                pool.setAutostart(0)
-        except libvirt.libvirtError as e:
-            msg = "Problem creating Storage Pool: %s"
-            kimchi_log.error(msg, e)
-            raise OperationFailed(e.get_error_message())
-
-    def get_storagepool_by_name(self, name):
-        conn = self.conn.get()
-        pool = conn.storagePoolLookupByName(name)
-        info = pool.info()
-        nr_volumes = self._get_storagepool_vols_num(pool)
-        autostart = True if pool.autostart() else False
-        xml = pool.XMLDesc(0)
-        path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
-        pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0]
-        source = self._get_storage_source(pool_type, xml)
-        res = {'state': self.pool_state_map[info[0]],
-               'path': path,
-               'source': source,
-               'type': pool_type,
-               'autostart': autostart,
-               'capacity': info[1],
-               'allocated': info[2],
-               'available': info[3],
-               'nr_volumes': nr_volumes}
-
-        if not pool.isPersistent():
-            # Deal with deep scan generated pool
-            try:
-                with self.objstore as session:
-                    task_id = session.get('scanning', name)
-                res['task_id'] = str(task_id)
-                res['type'] = 'kimchi-iso'
-            except NotFoundError:
-                # User created normal pool
-                pass
-        return res
-
-    def _get_storagepool_vols_num(self, pool):
-        try:
-            if pool.isActive():
-                pool.refresh(0)
-                return pool.numOfVolumes()
-            else:
-                return 0
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _get_storage_source(self, pool_type, pool_xml):
-        source = {}
-        if pool_type not in STORAGE_SOURCES:
-            return source
-
-        for key, val in STORAGE_SOURCES[pool_type].items():
-            res = xmlutils.xpath_get_text(pool_xml, val)
-            source[key] = res[0] if len(res) == 1 else res
-
-        return source
-
-    def activate_storagepool(self, name):
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(name)
-            pool.create(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def deactivate_storagepool(self, name):
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(name)
-            pool.destroy()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def delete_storagepool(self, name):
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(name)
-            pool.undefine()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def autostart_storagepool(self, name, value):
-        conn = self.conn.get()
-        pool = conn.storagePoolLookupByName(name)
-        if autostart:
-            pool.setAutostart(1)
-        else:
-            pool.setAutostart(0)
-
-    def create_storagevolume(self, pool, params):
-        storagevol_xml = """
-        <volume>
-          <name>%(name)s</name>
-          <allocation unit="MiB">%(allocation)s</allocation>
-          <capacity unit="MiB">%(capacity)s</capacity>
-          <target>
-            <format type='%(format)s'/>
-            <path>%(path)s</path>
-          </target>
-        </volume>"""
-
-        params.setdefault('allocation', 0)
-        params.setdefault('format', 'qcow2')
-        try:
-            xml = storagevol_xml % params
-        except KeyError, key:
-            raise MissingParameter("You need to specify '%s' in order to "
-                                   "create the storage volume." % key)
-
-        conn = self.conn.get()
-        pool = conn.storagePoolLookupByName(pool)
-        try:
-            pool.createXML(xml, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _get_storagevolume(self, pool, name):
-        conn = self.conn.get()
-        pool = conn.storagePoolLookupByName(pool)
-        return pool.storageVolLookupByName(name)
-
-    def get_storagevolumes_by_pool(self, pool):
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(pool)
-            pool.refresh(0)
-            return pool.listVolumes()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def get_storagevolume(self, pool, name):
-        vol = self._get_storagevolume(pool, name)
-        path = vol.path()
-        info = vol.info()
-        xml = vol.XMLDesc(0)
-        fmt = xmlutils.xpath_get_text(xml, "/volume/target/format/@type")[0]
-        res = dict(type=self.volume_type_map[info[0]], capacity=info[1],
-                   allocation=info[2], path=path, format=fmt)
-
-        if fmt == 'iso':
-            if os.path.islink(path):
-                path = os.path.join(os.path.dirname(path), os.readlink(path))
-
-            try:
-                iso_img = IsoImage(path)
-                os_distro, os_version = iso_img.probe()
-                bootable = True
-            except IsoFormatError:
-                bootable = False
-
-            res.update(dict(os_distro=os_distro, os_version=os_version,
-                            path=path, bootable=bootable))
-
-        return res
-
-    def wipe_storagevolume(self, pool, name):
-        try:
-            vol = self._get_storagevolume(pool, name)
-            vol.wipePattern(libvirt.VIR_STORAGE_VOL_WIPE_ALG_ZERO, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def resize_storagevolume(self, pool, name, size):
-        size = size << 20
-        try:
-            vol = pool.storageVolLookupByName(name)
-            vol.resize(size, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def delete_storagevolume(self, pool, name):
-        try:
-            vol = pool.storageVolLookupByName(name)
-            volume.delete(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def create_network(self, params):
-        connection = params["connection"]
-        # set forward mode, isolated do not need forward
-        if connection != 'isolated':
-            params['forward'] = {'mode': connection}
-
-        if connection == 'bridge':
-            if netinfo.is_bridge(iface):
-                params['bridge'] = iface
-            elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface):
-                if params.get('vlan_id') is None:
-                    params['forward']['dev'] = iface
-                else:
-                    params['bridge'] = \
-                        self._create_vlan_tagged_bridge(str(iface),
-                                                        str(params['vlan_id']))
-
-        xml = networkxml.to_network_xml(**params)
-        try:
-            conn = self.conn.get()
-            network = conn.networkDefineXML(xml)
-            network.setAutostart(True)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def _create_vlan_tagged_bridge(self, interface, vlan_id):
-        br_name = '-'.join(('kimchi', interface, vlan_id))
-        br_xml = networkxml.create_vlan_tagged_bridge_xml(br_name, interface,
-                                                          vlan_id)
-        conn = self.conn.get()
-        conn.changeBegin()
-        try:
-            vlan_tagged_br = conn.interfaceDefineXML(br_xml)
-            vlan_tagged_br.create()
-        except libvirt.libvirtError as e:
-            conn.changeRollback()
-            raise OperationFailed(e.message)
-        else:
-            conn.changeCommit()
-            return br_name
-
-    def get_networks(self):
-        conn = self.conn.get()
-        return conn.listNetworks() + conn.listDefinedNetworks()
-
-    def get_network_by_name(self, name):
-        conn = self.conn.get()
-        net = conn.networkLookupByName(name)
-        xml = net.XMLDesc(0)
-        net_dict = self._get_network_from_xml(xml)
-        subnet = net_dict['subnet']
-        dhcp = net_dict['dhcp']
-        forward = net_dict['forward']
-        interface = net_dict['bridge']
-
-        connection = forward['mode'] or "isolated"
-        # FIXME, if we want to support other forward mode well.
-        if connection == 'bridge':
-            # macvtap bridge
-            interface = interface or forward['interface'][0]
-            # exposing the network on linux bridge or macvtap interface
-            interface_subnet = network.get_dev_netaddr(interface)
-            subnet = subnet if subnet else interface_subnet
-
-        # libvirt use format 192.168.0.1/24, standard should be 192.168.0.0/24
-        # http://www.ovirt.org/File:Issue3.png
-        if subnet:
-            subnet = ipaddr.IPNetwork(subnet)
-            subnet = "%s/%s" % (subnet.network, subnet.prefixlen)
-
-        return {'connection': connection,
-                'interface': interface,
-                'subnet': subnet,
-                'dhcp': dhcp,
-                'vms': self._get_vms_attach_to_network(name),
-                'autostart': net.autostart() == 1,
-                'state':  net.isActive() and "active" or "inactive"}
-
-    def _get_network_from_xml(self, xml):
-        address = xmlutils.xpath_get_text(xml, "/network/ip/@address")
-        address = address and address[0] or ''
-        netmask = xmlutils.xpath_get_text(xml, "/network/ip/@netmask")
-        netmask = netmask and netmask[0] or ''
-        net = address and netmask and "/".join([address, netmask]) or ''
-
-        dhcp_start = xmlutils.xpath_get_text(xml,
-                                             "/network/ip/dhcp/range/@start")
-        dhcp_start = dhcp_start and dhcp_start[0] or ''
-        dhcp_end = xmlutils.xpath_get_text(xml, "/network/ip/dhcp/range/@end")
-        dhcp_end = dhcp_end and dhcp_end[0] or ''
-        dhcp = {'start': dhcp_start, 'end': dhcp_end}
-
-        forward_mode = xmlutils.xpath_get_text(xml, "/network/forward/@mode")
-        forward_mode = forward_mode and forward_mode[0] or ''
-        forward_if = xmlutils.xpath_get_text(xml,
-                                             "/network/forward/interface/@dev")
-        forward_pf = xmlutils.xpath_get_text(xml, "/network/forward/pf/@dev")
-        bridge = xmlutils.xpath_get_text(xml, "/network/bridge/@name")
-        bridge = bridge and bridge[0] or ''
-        return {'subnet': net, 'dhcp': dhcp, 'bridge': bridge,
-                'forward': {'mode': forward_mode, 'interface': forward_if,
-                            'pf': forward_pf}}
-
-    def _get_vms_attach_to_network(self, network):
-        vms = []
-        xpath = "/domain/devices/interface[@type='network']/source/@network"
-        conn = self.conn.get()
-        for dom in conn.listAllDomains(0):
-            xml = dom.XMLDesc(0)
-            networks =  xmlutils.xpath_get_text(xml, xpath)
-            if network in networks:
-                vms.append(dom.name())
-        return vms
-
-    def activate_network(self, name):
-        conn = self.conn.get()
-        net = conn.networkLookupByName(name)
-        net.create()
-
-    def deactivate_network(self, name):
-        conn = self.conn.get()
-        net = conn.networkLookupByName(name)
-        net.destroy()
-
-    def delete_network(self, name):
-        conn = self.conn.get()
-        net = conn.networkLookupByName(name)
-        self._remove_vlan_tagged_bridge(net)
-        net.undefine()
-
-    def _remove_vlan_tagged_bridge(self, network):
-        try:
-            bridge = network.bridgeName()
-        except libvirt.libvirtError:
-            pass
-        else:
-            if self._is_vlan_tagged_bridge(bridge):
-                conn = self.conn.get()
-                iface = conn.interfaceLookupByName(bridge)
-                if iface.isActive():
-                    iface.destroy()
-                iface.undefine()
-
-    def create_template(self, name, tmpl):
-        with self.objstore as session:
-            session.store('template', name, tmpl.info)
-
-    def get_templates(self):
-        with self.objstore as session:
-            return session.get_list('template')
-
-    def get_template_by_name(self, name):
-        with self.objstore as session:
-            return session.get('template', name)
-
-    def delete_template(self, name):
-        with self.objstore as session:
-            session.delete('template', name)
-
-    def create_vm(self, name, uuid, tmpl, vol_list):
-        # Store the icon for displaying later
-        icon = tmpl.info.get('icon', None)
-        if icon is not None:
-            with self.objstore as session:
-                session.store('vm', vm_uuid, {'icon': icon})
-
-        libvirt_stream = False
-        if len(self.libvirt_stream_protocols) != 0:
-            libvirt_stream = True
-
-        xml = tmpl.to_vm_xml(name, vm_uuid, libvirt_stream,
-                             self.qemu_stream_dns)
-        try:
-            dom = conn.defineXML(xml.encode('utf-8'))
-        except libvirt.libvirtError as e:
-            for v in vol_list:
-                vol = conn.storageVolLookupByPath(v['path'])
-                vol.delete(0)
-            raise OperationFailed(e.get_error_message())
-
-    def get_vms(self):
-        conn = self.conn.get()
-        ids = conn.listDomainsID()
-        names = map(lambda x: conn.lookupByID(x).name(), ids)
-        names += conn.listDefinedDomains()
-        names = map(lambda x: x.decode('utf-8'), names)
-        return sorted(names, key=unicode.lower)
-
-    def get_screenshot_by_name(self, vm_uuid):
-        with self.objstore as session:
-            try:
-                params = session.get('screenshot', vm_uuid)
-            except NotFoundError:
-                params = {'uuid': vm_uuid}
-                session.store('screenshot', vm_uuid, params)
-
-        screenshot = LibvirtVMScreenshot(params, self.conn)
-        img_path = screenshot.lookup()
-        # screenshot info changed after scratch generation
-        with self.objstore as session:
-            session.store('screenshot', vm_uuid, screenshot.info)
-
-        return img_path
-
-    def delete_screenshot(self, vm_uuid):
-        os.remove(self.get_screenshot_by_name(vm_uuid))
-        with self.objstore as session:
-            session.delete('screenshot', vm_uuid)
-
-    def get_vm_by_name(self, name):
-        conn = self.conn.get()
-        dom = conn.lookupByName(name.encode("utf-8"))
-        info = dom.info()
-        state = self.dom_state_map[info[0]]
-        screenshot = None
-        graphics = self._get_vm_graphics(dom)
-        graphics_type, graphics_listen, graphics_port = graphics
-        graphics_port = graphics_port if state == 'running' else None
-        if state == 'running':
-            screenshot = self.get_screenshot_by_name(name)
-        elif state == 'shutoff':
-            # reset vm stats when it is powered off to avoid sending
-            # incorrect (old) data
-            self.stats[dom.UUIDString()] = {}
-
-        with self.objstore as session:
-            try:
-                extra_info = session.get('vm', dom.UUIDString())
-            except NotFoundError:
-                extra_info = {}
-        icon = extra_info.get('icon')
-
-        vm_stats = self.stats.get(dom.UUIDString(), {})
-        stats = {}
-        stats['cpu_utilization'] = vm_stats.get('cpu', 0)
-        stats['net_throughput'] = vm_stats.get('net_io', 0)
-        stats['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
-        stats['io_throughput'] = vm_stats.get('disk_io', 0)
-        stats['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
-
-        return {'state': state, 'stats': str(stats), 'uuid': dom.UUIDString(),
-                'memory': info[2] >> 10, 'cpus': info[3], 'icon': icon,
-                'screenshot': screenshot,
-                'graphics': {'type': graphics_type, 'listen': graphics_listen,
-                             'port': graphics_port}
-                }
-
-    def _get_vm_graphics(self, dom):
-        xml = dom.XMLDesc(0)
-        expr = "/domain/devices/graphics/@type"
-        res = xmlutils.xpath_get_text(xml, expr)
-        graphics_type = res[0] if res else None
-        expr = "/domain/devices/graphics/@listen"
-        res = xmlutils.xpath_get_text(xml, expr)
-        graphics_listen = res[0] if res else None
-        graphics_port = None
-        if graphics_type:
-            expr = "/domain/devices/graphics[@type='%s']/@port" % graphics_type
-            res = xmlutils.xpath_get_text(xml, expr)
-            graphics_port = int(res[0]) if res else None
-        return graphics_type, graphics_listen, graphics_port
-
-    def static_vm_update(self, name, params):
-        conn = self.conn.get()
-        dom = conn.lookupByName(name.encode("utf-8"))
-        old_xml = new_xml = dom.XMLDesc(0)
-
-        for key, val in params.items():
-            if key in VM_STATIC_UPDATE_PARAMS:
-                new_xml = xmlutils.xml_item_update(new_xml,
-                                                   VM_STATIC_UPDATE_PARAMS[key],
-                                                   val)
-
-        try:
-            dom.undefine()
-            conn.defineXML(new_xml)
-        except libvirt.libvirtError as e:
-            conn.defineXML(old_xml)
-            raise OperationFailed(e.get_error_message())
-
-    def live_vm_update(self, name, params):
-        pass
-
-    def delete_vm(self, name):
-        info = self.get_vm_by_name(name)
-        if info['state'] == 'running':
-            self.stop_vm(name)
-
-        conn = self.conn.get()
-        dom = conn.lookupByName(name.encode("utf-8"))
-        dom.undefine()
-
-        xml = dom.XMLDesc(0)
-        xpath = "/domain/devices/disk[@device='disk']/source/@file"
-        paths = xmlutils.xpath_get_text(xml, xpath)
-        for path in paths:
-            vol = conn.storageVolLookupByPath(path)
-            vol.delete(0)
-
-        with self.objstore as session:
-            session.delete('vm', dom.UUIDString(), ignore_missing=True)
-
-        self.delete_screenshot(dom.UUIDString())
-        vnc.remove_proxy_token(name)
-
-    def start_vm(self, name):
-        conn = self.conn.get()
-        dom = conn.lookupByName(name.encode("utf-8"))
-        dom.create()
-
-    def stop_vm(self, name):
-        conn = self.conn.get()
-        dom = conn.lookupByName(name.encode("utf-8"))
-        dom.destroy()
-
-    def connect_vm(self, name):
-        graphics = self._get_vm_graphics(name)
-        graphics_type, graphics_listen, graphics_port = get_graphics
-        if graphics_port is None:
-            raise OperationFailed("Only able to connect to running vm's vnc "
-                                  "graphics.")
-        vnc.add_proxy_token(name, graphics_port)
-
-class LibvirtVMScreenshot(VMScreenshot):
-    def __init__(self, vm_uuid, conn):
-        VMScreenshot.__init__(self, vm_uuid)
-        self.conn = conn
-
-    def _generate_scratch(self, thumbnail):
-        def handler(stream, buf, opaque):
-            fd = opaque
-            os.write(fd, buf)
-
-        fd = os.open(thumbnail, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, 0644)
-        try:
-            conn = self.conn.get()
-            dom = conn.lookupByUUIDString(self.vm_uuid)
-            vm_name = dom.name()
-            stream = conn.newStream(0)
-            mimetype = dom.screenshot(stream, 0, 0)
-            stream.recvAll(handler, fd)
-        except libvirt.libvirtError:
-            try:
-                stream.abort()
-            except:
-                pass
-            raise NotFoundError("Screenshot not supported for %s" % vm_name)
-        else:
-            stream.finish()
-        finally:
-            os.close(fd)
diff --git a/src/kimchi/model_/libvirtconnection.py b/src/kimchi/model_/libvirtconnection.py
deleted file mode 100644
index 9276acc..0000000
--- a/src/kimchi/model_/libvirtconnection.py
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-import cherrypy
-import libvirt
-import threading
-import time
-
-
-from kimchi.utils import kimchi_log
-
-
-class LibvirtConnection(object):
-    def __init__(self, uri):
-        self.uri = uri
-        self._connections = {}
-        self._connectionLock = threading.Lock()
-        self.wrappables = self.get_wrappable_objects()
-
-    def get_wrappable_objects(self):
-        """
-        When a wrapped function returns an instance of another libvirt object,
-        we also want to wrap that object so we can catch errors that happen
-        when calling its methods.
-        """
-        objs = []
-        for name in ('virDomain', 'virDomainSnapshot', 'virInterface',
-                     'virNWFilter', 'virNetwork', 'virNodeDevice', 'virSecret',
-                     'virStoragePool', 'virStorageVol', 'virStream'):
-            try:
-                attr = getattr(libvirt, name)
-            except AttributeError:
-                pass
-            objs.append(attr)
-        return tuple(objs)
-
-    def get(self, conn_id=0):
-        """
-        Return current connection to libvirt or open a new one.  Wrap all
-        callable libvirt methods so we can catch connection errors and handle
-        them by restarting the server.
-        """
-        def wrapMethod(f):
-            def wrapper(*args, **kwargs):
-                try:
-                    ret = f(*args, **kwargs)
-                    return ret
-                except libvirt.libvirtError as e:
-                    edom = e.get_error_domain()
-                    ecode = e.get_error_code()
-                    EDOMAINS = (libvirt.VIR_FROM_REMOTE,
-                                libvirt.VIR_FROM_RPC)
-                    ECODES = (libvirt.VIR_ERR_SYSTEM_ERROR,
-                              libvirt.VIR_ERR_INTERNAL_ERROR,
-                              libvirt.VIR_ERR_NO_CONNECT,
-                              libvirt.VIR_ERR_INVALID_CONN)
-                    if edom in EDOMAINS and ecode in ECODES:
-                        kimchi_log.error('Connection to libvirt broken. '
-                                         'Recycling. ecode: %d edom: %d' %
-                                         (ecode, edom))
-                        with self._connectionLock:
-                            self._connections[conn_id] = None
-                    raise
-            wrapper.__name__ = f.__name__
-            wrapper.__doc__ = f.__doc__
-            return wrapper
-
-        with self._connectionLock:
-            conn = self._connections.get(conn_id)
-            if not conn:
-                retries = 5
-                while True:
-                    retries = retries - 1
-                    try:
-                        conn = libvirt.open(self.uri)
-                        break
-                    except libvirt.libvirtError:
-                        kimchi_log.error('Unable to connect to libvirt.')
-                        if not retries:
-                            msg = 'Libvirt is not available, exiting.'
-                            kimchi_log.error(msg)
-                            cherrypy.engine.stop()
-                            raise
-                    time.sleep(2)
-
-                for name in dir(libvirt.virConnect):
-                    method = getattr(conn, name)
-                    if callable(method) and not name.startswith('_'):
-                        setattr(conn, name, wrapMethod(method))
-
-                for cls in self.wrappables:
-                    for name in dir(cls):
-                        method = getattr(cls, name)
-                        if callable(method) and not name.startswith('_'):
-                            setattr(cls, name, wrapMethod(method))
-
-                self._connections[conn_id] = conn
-                # In case we're running into troubles with keeping the
-                # connections alive we should place here:
-                # conn.setKeepAlive(interval=5, count=3)
-                # However the values need to be considered wisely to not affect
-                # hosts which are hosting a lot of virtual machines
-            return conn
diff --git a/src/kimchi/model_/libvirtstoragepool.py b/src/kimchi/model_/libvirtstoragepool.py
deleted file mode 100644
index e9b9aa8..0000000
--- a/src/kimchi/model_/libvirtstoragepool.py
+++ /dev/null
@@ -1,225 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-import copy
-import libvirt
-
-from kimchi.exception import OperationFailed
-from kimchi.iscsi import TargetClient
-
-class StoragePoolDef(object):
-    @classmethod
-    def create(cls, poolArgs):
-        for klass in cls.__subclasses__():
-            if poolArgs['type'] == klass.poolType:
-                return klass(poolArgs)
-        raise OperationFailed('Unsupported pool type: %s' % poolArgs['type'])
-
-    def __init__(self, poolArgs):
-        self.poolArgs = poolArgs
-
-    def prepare(self, conn):
-        ''' Validate pool arguments and perform preparations. Operation which
-        would cause side effect should be put here. Subclasses can optionally
-        override this method, or it always succeeds by default. '''
-        pass
-
-    @property
-    def xml(self):
-        ''' Subclasses have to override this method to actually generate the
-        storage pool XML definition. Should cause no side effect and be
-        idempotent'''
-        # TODO: When add new pool type, should also add the related test in
-        # tests/test_storagepool.py
-        raise OperationFailed('self.xml is not implemented: %s' % self)
-
-
-class DirPoolDef(StoragePoolDef):
-    poolType = 'dir'
-
-    @property
-    def xml(self):
-        # Required parameters
-        # name:
-        # type:
-        # path:
-        xml = """
-        <pool type='dir'>
-          <name>{name}</name>
-          <target>
-            <path>{path}</path>
-          </target>
-        </pool>
-        """.format(**self.poolArgs)
-        return xml
-
-
-class NetfsPoolDef(StoragePoolDef):
-    poolType = 'netfs'
-
-    def __init__(self, poolArgs):
-        super(NetfsPoolDef, self).__init__(poolArgs)
-        self.path = '/var/lib/kimchi/nfs_mount/' + self.poolArgs['name']
-
-    def prepare(self, conn):
-        # TODO: Verify the NFS export can be actually mounted.
-        pass
-
-    @property
-    def xml(self):
-        # Required parameters
-        # name:
-        # type:
-        # source[host]:
-        # source[path]:
-        poolArgs = copy.deepcopy(self.poolArgs)
-        poolArgs['path'] = self.path
-        xml = """
-        <pool type='netfs'>
-          <name>{name}</name>
-          <source>
-            <host name='{source[host]}'/>
-            <dir path='{source[path]}'/>
-          </source>
-          <target>
-            <path>{path}</path>
-          </target>
-        </pool>
-        """.format(**poolArgs)
-        return xml
-
-
-class LogicalPoolDef(StoragePoolDef):
-    poolType = 'logical'
-
-    def __init__(self, poolArgs):
-        super(LogicalPoolDef, self).__init__(poolArgs)
-        self.path = '/var/lib/kimchi/logical_mount/' + self.poolArgs['name']
-
-    @property
-    def xml(self):
-        # Required parameters
-        # name:
-        # type:
-        # source[devices]:
-        poolArgs = copy.deepcopy(self.poolArgs)
-        devices = []
-        for device_path in poolArgs['source']['devices']:
-            devices.append('<device path="%s" />' % device_path)
-
-        poolArgs['source']['devices'] = ''.join(devices)
-        poolArgs['path'] = self.path
-
-        xml = """
-        <pool type='logical'>
-        <name>{name}</name>
-            <source>
-            {source[devices]}
-            </source>
-        <target>
-            <path>{path}</path>
-        </target>
-        </pool>
-        """.format(**poolArgs)
-        return xml
-
-
-class IscsiPoolDef(StoragePoolDef):
-    poolType = 'iscsi'
-
-    def prepare(self, conn):
-        source = self.poolArgs['source']
-        if not TargetClient(**source).validate():
-            raise OperationFailed("Can not login to iSCSI host %s target %s" %
-                                  (source['host'], source['target']))
-        self._prepare_auth(conn)
-
-    def _prepare_auth(self, conn):
-        try:
-            auth = self.poolArgs['source']['auth']
-        except KeyError:
-            return
-
-        try:
-            virSecret = conn.secretLookupByUsage(
-                libvirt.VIR_SECRET_USAGE_TYPE_ISCSI, self.poolArgs['name'])
-        except libvirt.libvirtError:
-            xml = '''
-            <secret ephemeral='no' private='yes'>
-              <description>Secret for iSCSI storage pool {name}</description>
-              <auth type='chap' username='{username}'/>
-              <usage type='iscsi'>
-                <target>{name}</target>
-              </usage>
-            </secret>'''.format(name=self.poolArgs['name'],
-                                username=auth['username'])
-            virSecret = conn.secretDefineXML(xml)
-
-        virSecret.setValue(auth['password'])
-
-    def _format_port(self, poolArgs):
-        try:
-            port = poolArgs['source']['port']
-        except KeyError:
-            return ""
-        return "port='%s'" % port
-
-    def _format_auth(self, poolArgs):
-        try:
-            auth = poolArgs['source']['auth']
-        except KeyError:
-            return ""
-
-        return '''
-        <auth type='chap' username='{username}'>
-          <secret type='iscsi' usage='{name}'/>
-        </auth>'''.format(name=poolArgs['name'], username=auth['username'])
-
-    @property
-    def xml(self):
-        # Required parameters
-        # name:
-        # type:
-        # source[host]:
-        # source[target]:
-        #
-        # Optional parameters
-        # source[port]:
-        poolArgs = copy.deepcopy(self.poolArgs)
-        poolArgs['source'].update({'port': self._format_port(poolArgs),
-                                   'auth': self._format_auth(poolArgs)})
-        poolArgs['path'] = '/dev/disk/by-id'
-
-        xml = """
-        <pool type='iscsi'>
-          <name>{name}</name>
-          <source>
-            <host name='{source[host]}' {source[port]}/>
-            <device path='{source[target]}'/>
-            {source[auth]}
-          </source>
-          <target>
-            <path>{path}</path>
-          </target>
-        </pool>
-        """.format(**poolArgs)
-        return xml
diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py
deleted file mode 100644
index d4314ca..0000000
--- a/src/kimchi/model_/mockbackend.py
+++ /dev/null
@@ -1,338 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-import copy
-import os
-import random
-
-from kimchi import config
-from kimchi.asynctask import AsyncTask
-from kimchi.objectstore import ObjectStore
-from kimchi.screenshot import VMScreenshot
-
-class MockBackend(object):
-    _network_info = {'state': 'inactive', 'autostart': True, 'connection': '',
-                     'interface': '', 'subnet': '',
-                     'dhcp': {'start': '', 'stop':  ''}}
-
-    TEMPLATE_SCAN = False
-
-    def __init__(self, objstore_loc=None):
-        self.objstore = ObjectStore(objstore_loc)
-        self.next_taskid = 1
-        self.host_stats = self._get_host_stats()
-        self._storagepools = {}
-        self._networks = {}
-        self._templates = {}
-        self._vms = {}
-        self._screenshots = {}
-
-    def _get_host_stats(self):
-        memory_stats = {'total': 3934908416L,
-                        'free': round(random.uniform(0, 3934908416L), 1),
-                        'cached': round(random.uniform(0, 3934908416L), 1),
-                        'buffers': round(random.uniform(0, 3934908416L), 1),
-                        'avail': round(random.uniform(0, 3934908416L), 1)}
-
-        return {'cpu_utilization': round(random.uniform(0, 100), 1),
-                'memory': memory_stats,
-                'disk_read_rate': round(random.uniform(0, 4000), 1),
-                'disk_write_rate': round(random.uniform(0, 4000), 1),
-                'net_recv_rate': round(random.uniform(0, 4000), 1),
-                'net_sent_rate': round(random.uniform(0, 4000), 1)}
-
-    def get_capabilities(self):
-        protocols = ['http', 'https', 'ftp', 'ftps', 'tftp']
-        return {'libvirt_stream_protocols': protocols,
-                'qemu_stream': True,
-                'screenshot': True,
-                'system_report_tool': True}
-
-    def gen_debugreport_file(self, ident):
-        return self.add_task('', self._create_debugreport, ident)
-
-    def _create_debugreport(self, cb, name):
-        path = config.get_debugreports_path()
-        tmpf = os.path.join(path, name + '.tmp')
-        realf = os.path.join(path, name + '.txt')
-        length = random.randint(1000, 10000)
-        with open(tmpf, 'w') as fd:
-            while length:
-                fd.write('mock debug report\n')
-                length = length - 1
-        os.rename(tmpf, realf)
-        cb("OK", True)
-
-    def add_task(self, target_uri, fn, opaque=None):
-        id = self.next_taskid
-        self.next_taskid += 1
-        task = AsyncTask(id, target_uri, fn, self.objstore, opaque)
-        return id
-
-    def get_host(self):
-        res = {}
-        res['memory'] = 6114058240
-        res['cpu'] = 'Intel(R) Core(TM) i5 CPU       M 560  @ 2.67GHz'
-        res['os_distro'] = 'Red Hat Enterprise Linux Server'
-        res['os_version'] = '6.4'
-        res['os_codename'] = 'Santiago'
-
-        return res
-
-    def get_storagepools(self):
-        return self._storagepools.keys()
-
-    def do_deep_scan(self):
-        return self.add_task('', time.sleep, 25)
-
-    def create_storagepool(self, params):
-        name = params['name']
-        pool = MockStoragePool(name)
-        pool.info.update(params)
-        if params['type'] == 'dir':
-            pool.info['autostart'] = True
-        else:
-            pool.info['autostart'] = False
-
-        self._storagepools[name] = pool
-
-    def get_storagepool_by_name(self, name):
-        pool = self._storagepools[name]
-        pool.refresh()
-        return pool.info
-
-    def activate_storagepool(self, name):
-        self._storagepools[name].info['state'] = 'active'
-
-    def deactivate_storagepool(self, name):
-        self._storagepools[name].info['state'] = 'inactive'
-
-    def delete_storagepool(self, name):
-        del self._storagepools[name]
-
-    def autostart_storagepool(self, name, value):
-        self._storagepools[name].info['autostart'] = value
-
-    def create_storagevolume(self, pool, params):
-        try:
-            name = params['name']
-            volume = MockStorageVolume(pool, name, params)
-            volume.info['type'] = params['type']
-            volume.info['format'] = params['format']
-            volume.info['path'] = os.path.join(pool.info['path'], name)
-        except KeyError, item:
-            raise MissingParameter(item)
-
-        pool._volumes[name] = volume
-
-    def get_storagevolumes_by_pool(self, pool):
-        return self._storagepools[pool]._volumes.keys()
-
-    def get_storagevolume(self, pool, name):
-        vol = self._storagevolumes[pool]._volumes[name]
-        return vol.info
-
-    def wipe_storagevolume(self, pool, name):
-        self._storagepools[pool]._volumes[name].info['allocation'] = 0
-
-    def resize_storagevolume(self, pool, name, size):
-        self._storagepools[pool]._volumes[name].info['capacity'] = size
-
-    def delete_storagevolume(self, pool, name):
-        del self._storagepools[pool]._volumes[name]
-
-    def create_network(self, params):
-        name = params['name']
-        info = copy.deepcopy(self._network_info)
-        info.update(params)
-        self._networks[name] = info
-
-    def get_networks(self):
-        return self._networks.keys()
-
-    def get_network_by_name(self, name):
-        info = self._networks[name]
-        info['vms'] = self._get_vms_attach_to_network(name)
-        return info
-
-    def _get_vms_attach_to_network(self, network):
-        vms = []
-        for name, dom in self._vms.iteritems():
-             if network in dom.networks:
-                 vms.append(name)
-        return vms
-
-    def activate_network(self, name):
-        self._networks[name]['state'] = 'active'
-
-    def deactivate_network(self, name):
-        self._networks[name]['state'] = 'inactive'
-
-    def delete_network(self, name):
-        del self._networks[name]
-
-    def create_template(self, name, tmpl):
-        self._templates[name] = tmpl.info
-
-    def get_templates(self):
-        return self._templates.keys()
-
-    def get_template_by_name(self, name):
-        return self._templates[name]
-
-    def delete_template(self, name):
-        del self._templates[name]
-
-    def create_vm(self, name, uuid, tmpl, vol_list):
-        vm = MockVM(vm_uuid, name, tmpl.info)
-        icon = tmpl.info.get('icon', None)
-        if icon is not None:
-            vm.info['icon'] = icon
-
-        disk_paths = []
-        for vol in vol_list:
-            disk_paths.append({'pool': pool.name, 'volume': vol_info['name']})
-
-        vm.disk_paths = disk_paths
-        self._vms[name] = vm
-
-    def get_vms(self):
-        return self._vms.keys()
-
-    def get_screenshot_by_name(self, vm_uuid):
-        mockscreenshot = MockVMScreenshot({'uuid': vm_uuid})
-        screenshot = self._screenshots.setdefault(vm_uuid, mockscreenshot)
-        return screenshot.lookup()
-
-    def get_vm_by_name(self, name):
-        vm = self._vms[name]
-        if vm.info['state'] == 'running':
-            vm.info['screenshot'] = self.get_screenshot_by_name(name)
-        else:
-            vm.info['screenshot'] = None
-        return vm.info
-
-    def static_vm_update(self, name, params):
-        vm_info = copy.copy(self._vms[name])
-        for key, val in params.items():
-            if key in VM_STATIC_UPDATE_PARAMS and key in vm_info:
-                vm_info[key] = val
-
-        if 'name' in params:
-            del self._vms[name]
-            self._vms[params['name']] = vm_info
-
-    def live_vm_update(self, name, params):
-        pass
-
-    def delete_vm(self, name):
-        vm = self._vms[name]
-        screenshot = self._screenshots.get(vm.uuid, None)
-        if screenshot is not None:
-            screenshot.delete()
-            del self._screenshots[vm_uuid]
-
-        for disk in vm.disk_paths:
-            self.delete_storagevolume(disk['pool'], disk['volume'])
-
-        del self._vms[name]
-
-    def start_vm(self, name):
-        self._vms[name].info['state'] = 'running'
-
-    def stop_vm(self, name):
-        self._vms[name].info['state'] = 'shutoff'
-
-    def connect_vm(self, name):
-        pass
-
-class MockStoragePool(object):
-    def __init__(self, name):
-        self.name = name
-        self._volumes = {}
-        self.info = {'state': 'inactive', 'capacity': 1024 << 20,
-                     'allocated': 512 << 20, 'available': 512 << 20,
-                     'path': '/var/lib/libvirt/images', 'source': {},
-                     'type': 'dir', 'nr_volumes': 0, 'autostart': 0}
-
-    def refresh(self):
-        state = self.info['state']
-        self.info['nr_volumes'] = 0
-        if state == 'active':
-            self.info['nr_volumes'] = len(self._volumes)
-
-class MockStorageVolume(object):
-    def __init__(self, pool, name, params={}):
-        self.name = name
-        self.pool = pool
-        self.info = {'type': 'disk', 'allocation': 512,
-                     'capacity': params.get('capacity', 1024) << 20,
-                     'format': params.get('format', 'raw')}
-
-        if fmt == 'iso':
-            self.info['allocation'] = self.info['capacity']
-            self.info['os_version'] = '19'
-            self.info['os_distro'] = 'fedora'
-            self.info['bootable'] = True
-
-class MockVM(object):
-    def __init__(self, uuid, name, template_info):
-        self.uuid = uuid
-        self.name = name
-        self.disk_paths = []
-        self.networks = template_info['networks']
-        self.info = {'state': 'shutoff',
-                     'stats': "{'cpu_utilization': 20, 'net_throughput' : 35, \
-                                'net_throughput_peak': 100, 'io_throughput': 45, \
-                                'io_throughput_peak': 100}",
-                     'uuid': self.uuid,
-                     'memory': template_info['memory'],
-                     'cpus': template_info['cpus'],
-                     'icon': None,
-                     'graphics': {'type': 'vnc', 'listen': '0.0.0.0', 'port': None}
-                     }
-        self.info['graphics'].update(template_info['graphics'])
-
-class MockVMScreenshot(VMScreenshot):
-    OUTDATED_SECS = 5
-    BACKGROUND_COLOR = ['blue', 'green', 'purple', 'red', 'yellow']
-    BOX_COORD = (50, 115, 206, 141)
-    BAR_COORD = (50, 115, 50, 141)
-
-    def __init__(self, vm_name):
-        VMScreenshot.__init__(self, vm_name)
-        self.coord = MockVMScreenshot.BAR_COORD
-        self.background = random.choice(MockVMScreenshot.BACKGROUND_COLOR)
-
-    def _generate_scratch(self, thumbnail):
-        self.coord = (self.coord[0],
-                      self.coord[1],
-                      min(MockVMScreenshot.BOX_COORD[2],
-                          self.coord[2]+random.randrange(50)),
-                      self.coord[3])
-
-        image = Image.new("RGB", (256, 256), self.background)
-        d = ImageDraw.Draw(image)
-        d.rectangle(MockVMScreenshot.BOX_COORD, outline='black')
-        d.rectangle(self.coord, outline='black', fill='black')
-        image.save(thumbnail)
diff --git a/src/kimchi/model_/networks.py b/src/kimchi/model_/networks.py
deleted file mode 100644
index dfefa81..0000000
--- a/src/kimchi/model_/networks.py
+++ /dev/null
@@ -1,115 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi.model import interfaces
-
-class Networks(object):
-    def __init__(self, backend):
-        self.backend = backend
-        self.ifaces = interfaces.Interfaces(backend)
-
-    def create(self, params):
-        name = params['name']
-        if name in self.get_list():
-            raise InvalidOperation("Network %s already exists" % name)
-
-        connection = params['connection']
-        # set subnet, bridge network do not need subnet
-        if connection in ["nat", 'isolated']:
-            self._set_network_subnet(params)
-
-        # only bridge network need bridge(linux bridge) or interface(macvtap)
-        if connection == 'bridge':
-            iface = params.get('interface', None)
-            if iface is None:
-                raise MissingParameter("You need to specify interface to create"
-                                       " a bridged network.")
-
-            if iface in self.ifaces.get_used_ifaces():
-                raise InvalidParameter("interface '%s' already in use." % iface)
-
-            if not (netinfo.is_bridge(iface) or netinfo.is_bare_nic(iface) or
-                    netinfo.is_bonding(iface)):
-                raise InvalidParameter("The interface should be bare nic, bonding "
-                                       "or bridge device.")
-
-        self.backend.create_network(params)
-        return name
-
-    def get_list(self):
-        return sorted(self.backend.get_networks())
-
-    def _set_network_subnet(self, params):
-        netaddr = params.get('subnet', '')
-        net_addrs = []
-        # lookup a free network address for nat and isolated automatically
-        if not netaddr:
-            for net_name in self.get_list():
-                net = self.backend.get_network_by_name(net_name)
-                subnet = net['subnet']
-                subnet and net_addrs.append(ipaddr.IPNetwork(subnet))
-
-            netaddr = network.get_one_free_network(net_addrs)
-            if not netaddr:
-                raise OperationFailed("can not find a free IP address for "
-                                      "network '%s'" % params['name'])
-        try:
-            ip = ipaddr.IPNetwork(netaddr)
-        except ValueError as e:
-            raise InvalidParameter("%s" % e)
-
-        if ip.ip == ip.network:
-            ip.ip = ip.ip + 1
-
-        dhcp_start = str(ip.ip + ip.numhosts / 2)
-        dhcp_end = str(ip.ip + ip.numhosts - 2)
-
-        params.update({'subnet': str(ip),
-                       'dhcp': {'range': {'start': dhcp_start,
-                                'end': dhcp_end}}})
-
-class Network(Networks):
-    def _network_exist(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Network '%s' not found.")
-
-        return True
-
-    def lookup(self, name):
-        if self._network_exist(name):
-            return self.backend.get_network_by_name(name)
-
-    def activate(self, name):
-        if self._network_exist(name):
-            return self.backend.activate_network(name)
-
-    def deactivate(self, name):
-        if self._network_exist(name):
-            return self.backend.deactivate_network(name)
-
-    def delete(self, name):
-        if self.lookup(name)['state'] == 'active':
-            raise InvalidOperation("Unable to delete the active network %s" %
-                                    name)
-
-        return self.backend.delete_network(name)
diff --git a/src/kimchi/model_/plugins.py b/src/kimchi/model_/plugins.py
deleted file mode 100644
index 3cbae77..0000000
--- a/src/kimchi/model_/plugins.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi import utils
-
-class Plugins(object):
-    def get_list(self):
-        return [plugin for (plugin, config) in utils.get_enabled_plugins()]
-
diff --git a/src/kimchi/model_/storagepools.py b/src/kimchi/model_/storagepools.py
deleted file mode 100644
index abdebd8..0000000
--- a/src/kimchi/model_/storagepools.py
+++ /dev/null
@@ -1,86 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-ISO_POOL_NAME = u'kimchi_isos'
-
-from kimchi.exception import InvalidParameter
-
-class StoragePools(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def get_list(self):
-        return sorted(self.backend.get_storagepools())
-
-    def create(self, params):
-        name = params['name']
-        if name in self.get_list() or name in (ISO_POOL_NAME,):
-            raise InvalidParameter("Storage pool '%s' already exists" % name)
-
-        task_id = None
-        if params['type'] == 'kimchi-iso':
-            task_id = self.backend.do_deep_scan(params)
-
-        self.backend.create_storagepool(params)
-        return name
-
-class StoragePool(StoragePools):
-    def lookup(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Storage pool '%s' not found.")
-
-        return self.backend.get_storagepool_by_name(name)
-
-    def activate(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Storage pool '%s' not found.")
-
-        self.backend.activate_storagepool()
-
-    def deactivate(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Storage pool '%s' not found.")
-
-        self.backend.deactivate_storagepool()
-
-    def delete(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Storage pool '%s' not found.")
-
-        if self.get_storagepool_by_name(name)['state'] == 'active':
-            raise InvalidOperation("Unable to delete active storage pool '%s'" %
-                                   name)
-
-        self.backend.delete_storagepool()
-
-    def update(self, name, params):
-        if name not in self.get_list():
-            raise NotFoundError("Storage pool '%s' not found.")
-
-        autostart = params['autostart']
-        if autostart not in [True, False]:
-            raise InvalidOperation("Autostart flag must be true or false")
-
-        self.backend.autostart_storagepool(name, autostart)
-
-        return name
diff --git a/src/kimchi/model_/storagevolumes.py b/src/kimchi/model_/storagevolumes.py
deleted file mode 100644
index 9f3c93b..0000000
--- a/src/kimchi/model_/storagevolumes.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-
-from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
-from kimchi.model import storagepools
-
-class StorageVolumes(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def create(self, pool, params):
-        if name in self.get_list(pool):
-            raise InvalidParameter("Storage volume '%s' already exists.")
-
-        self.backend.create_storagevolume(pool, params)
-        return name
-
-    def get_list(self, pool):
-        info = self.backend.get_storagepool_by_name(pool)
-        if info['state'] != 'active':
-            raise InvalidOperation("Unable to list volumes in inactive "
-                                   "storagepool %s" % pool)
-
-        return self.backend.get_storagevolumes_by_pool(pool)
-
-class StorageVolume(StorageVolumes):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def _storagevolume_exist(self, pool, name):
-        if name not in self.get_list(pool):
-            raise NotFoundError("Storage volume '%' not found in '%' pool" %
-                                (name, pool))
-        return True
-
-    def lookup(self, pool, name):
-        if self._storagevolume_exist(pool, name):
-            return self.backend.get_storagevolume(pool, name)
-
-    def resize(self, pool, name, size):
-        if self._storagevolume_exist(pool, name):
-            self.backend.resize_storagevolume(pool, name, size)
-
-    def wipe(self, pool, name):
-        if self._storagevolume_exist(pool, name):
-            self.backend.wipe_storagevolume(pool, name)
-
-    def delete(self, pool, name):
-        if self._storagevolume_exist(pool, name):
-            self.backend.delete_storagevolume(pool, name)
-
-class IsoVolumes(StorageVolumes):
-    def __init__(self, backend):
-        self.backend = backend
-        self.storagepools = storagepools.StoragePools(self.backend)
-
-    def get_list(self, pool):
-        iso_volumes = []
-
-        for pool in self.storagepools.get_list():
-            try:
-                volumes = self.get_list(pool)
-            except InvalidOperation:
-                # Skip inactive pools
-                continue
-
-            for volume in volumes:
-                res = self.lookup(pool, volume)
-                if res['format'] == 'iso':
-                    # prevent iso from different pool having same volume name
-                    res['name'] = '%s-%s' % (pool, volume)
-                    iso_volumes.append(res)
-
-        return iso_volumes
diff --git a/src/kimchi/model_/tasks.py b/src/kimchi/model_/tasks.py
deleted file mode 100644
index 29eaddf..0000000
--- a/src/kimchi/model_/tasks.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi.exception import NotFoundError
-
-ERROR_TASK_NOT_FOUND = "Task id '%s' not found."
-
-class Tasks(object):
-    def __init__(self, backend):
-        self.objstore = backend.objstore
-
-    def get_list(self):
-        with self.objstore as session:
-            return session.get_list('task')
-
-class Task(object):
-    def __init__(self, backend):
-        self.objstore = backend.objstore
-
-    def lookup(self, ident):
-        if ident not in self.get_list():
-            raise NotFoundError(ERROR_TASK_NOT_FOUND % ident)
-
-        with self.objstore as session:
-            return session.get('task', str(ident))
diff --git a/src/kimchi/model_/templates.py b/src/kimchi/model_/templates.py
deleted file mode 100644
index 86cd54f..0000000
--- a/src/kimchi/model_/templates.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-from kimchi.vmtemplate import VMTemplate
-from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
-from kimchi.exception import OperationFailed
-
-class Templates(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def create(self, params):
-        name = params['name']
-        if name in self.get_list:
-            raise InvalidOperation("Template '%s' already exists." % name)
-
-        for net_name in params.get(u'networks', []):
-            if net_name not in self.backend.get_networks():
-                raise InvalidParameter("Network '%s' not found," % net_name)
-
-        try:
-            tmpl = VMTemplate(params, self.backend.TEMPLATE_SCAN)
-            self.backend.create_template(name, tmpl)
-        except Exception, e:
-            raise OperationFailed("Unable to create template '%s': %s" %
-                                  (name, e.message))
-
-        return name
-
-    def get_list(self):
-        return sorted(self.backend.get_templates())
-
-class Template(Templates):
-    def lookup(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Template '%s' not found." % name)
-
-        params = self.backend.get_template_by_name(name)
-        tmpl = VMTemplate(params, False)
-        return tmpl.info
-
-    def delete(self, name):
-        if name not in self.get_list():
-            raise NotFoundError("Template '%s' not found." % name)
-
-        self.backend.delete_template(name)
-
-    def update(self, name, params):
-        old_t = self.lookup(name)
-        new_t = copy.copy(old_t)
-
-        new_t.update(params)
-        ident = name
-
-        new_storagepool = new_t.get(u'storagepool', '')
-        if new_storagepool not in self.backend.get_storagepools():
-            raise InvalidParameter("Storage pool '%s' not found." % name)
-
-        for net_name in params.get(u'networks', []):
-            if net_name not in self.backend.get_networks():
-                raise InvalidParameter("Network '%s' not found." % net_name)
-
-        self.delete(name)
-        try:
-            ident = self.create(new_t)
-        except:
-            ident = self.create(old_t)
-
-        return ident
diff --git a/src/kimchi/model_/vms.py b/src/kimchi/model_/vms.py
deleted file mode 100644
index 3f1b6c3..0000000
--- a/src/kimchi/model_/vms.py
+++ /dev/null
@@ -1,164 +0,0 @@
-#
-# Project Kimchi
-#
-# Copyright IBM, Corp. 2013
-#
-# Authors:
-#  Adam Litke <agl at linux.vnet.ibm.com>
-#  Aline Manera <alinefm at linux.vnet.ibm.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
-
-import uuid
-
-VM_STATIC_UPDATE_PARAMS = {'name': './name'}
-
-class VMs(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def create(self, params):
-        vm_uuid = str(uuid.uuid4())
-        vm_list = self.get_list()
-        name = self._get_vm_name(params.get('name'), t_name, vm_list)
-        # incoming text, from js json, is unicode, do not need decode
-        if name in vm_list:
-            raise InvalidOperation("VM '%s' already exists" % name)
-
-        vm_overrides = dict()
-        override_params = ['storagepool', 'graphics']
-        for param in override_params:
-            value = params.get(param, None)
-            if value is not None:
-                vm_overrides[param] = value
-
-        t_name = self._uri_to_name('templates', params['template'])
-        t_params = self.backend.get_template_by_name(t_name)
-        t_params.update(vm_overrides)
-        tmpl = VMTemplate(t_params, False)
-
-        caps = self.backend.get_capabilities()
-        if not caps.qemu_stream and t.info.get('iso_stream', False):
-            raise InvalidOperation("Remote ISO image is not supported by this"
-                                   " server.")
-
-        pool_name = self._uri_to_name('storagepools', pool_uri)
-        self._validate_storagepool(pool_name)
-        self._validate_network(tmpl)
-        vol_list = tmpl.to_volume_list(vm_uuid)
-        for vol_info in vol_list:
-            self.backend.create_storagevolume(pool_name, vol_info)
-
-        self.backend.create_vm(name, vm_uuid, tmpl, vol_list)
-        return name
-
-    def _validate_storagepool(self, pool_name):
-        try:
-            pool_info = self.backend.get_storagepool_by_name(pool_name)
-        except Exception:
-            raise InvalidParameter("Storage pool '%s' specified by template "
-                                   "does not exist" % pool_name)
-
-        if not pool_info['state'] != 'active':
-            raise InvalidParameter("Storage pool '%s' specified by template "
-                                   "is not active" % pool_name)
-
-    def _validate_network(self, tmpl):
-        names = tmpl.info['networks']
-        for name in names:
-            if name not in self.backend.get_networks():
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist.")
-
-            net_info = self.backend.get_network_by_name(name)
-            if net_info['state'] != 'active':
-                raise InvalidParameter("Network '%s' specified by template is "
-                                       "not active.")
-
-    def _uri_to_name(self, collection, uri):
-        expr = '/%s/(.*?)/?$' % collection
-        m = re.match(expr, uri)
-        if not m:
-            raise InvalidParameter(uri)
-        return m.group(1)
-
-    def _get_vm_name(self, vm_name, t_name, name_list):
-        if vm_name:
-            return vm_name
-
-        for i in xrange(1, 1000):
-            vm_name = "%s-vm-%i" % (t_name, i)
-            if vm_name not in name_list:
-                return vm_name
-
-        raise OperationFailed("Unable to choose a VM name")
-
-    def get_list(self):
-        return sorted(self.backend.get_vms())
-
-class VM(VMs):
-    def _vm_exists(self, name):
-        if name not in self.backend.get_vms():
-            raise NotFoundError("VM '%s' not found." % name)
-
-        return True
-
-    def lookup(self, name):
-        if self._vm_exists(name):
-            return self.backend.get_vm_by_name(name)
-
-    def update(self, name, params):
-        if self._vm_exists(name):
-            if 'name' in params:
-                state = self.get_vm_by_name(name)['state']
-                if state == 'running':
-                    raise InvalidParameter("The VM needs to be shutted off for"
-                                           "renaming.")
-
-                if params['name'] in self.get_list():
-                    raise InvalidParameter("VM name '%s' already exists" %
-                                            params['name'])
-
-            self.backend.static_vm_update(name, params)
-            self.backend.live_vm_update(name, params)
-
-            return params.get('name', None) or name
-
-    def delete(self, name):
-        if self._vm_exists(name):
-            self.backend.delete_vm(name)
-
-    def start(self, name):
-        if self._vm_exists(name):
-            self.backend.start_vm(self, name)
-
-    def stop(self, name):
-        if self._vm_exists(name):
-            self.backend.stop_vm(self, name)
-
-    def connect(self, name):
-        if self._vm_exists(name):
-            self.backend.connect_vm(self, name)
-
-class VMScreenshot(object):
-    def __init__(self, backend):
-        self.backend = backend
-
-    def lookup(self, name):
-        vm_info = self.backend.get_vm_by_name(name)
-        if vm_info['state'] != 'running':
-            raise NotFoundError('No screenshot for stopped vm')
-
-        return self.backend.get_screenshot_by_name(vm_info['uuid'])
diff --git a/src/kimchi/root.py b/src/kimchi/root.py
index 3cc6321..a064ccd 100644
--- a/src/kimchi/root.py
+++ b/src/kimchi/root.py
@@ -44,7 +44,7 @@ from kimchi.exception import OperationFailed
 
 
 class Root(Resource):
-    def __init__(self, model, dev_env):
+    def __init__(self, backend, dev_env):
         self._handled_error = ['error_page.400', 'error_page.404',
                                'error_page.405', 'error_page.406',
                                'error_page.415', 'error_page.500']
@@ -56,17 +56,17 @@ class Root(Resource):
             self._cp_config = dict([(key, self.error_development_handler)
                                     for key in self._handled_error])
 
-        Resource.__init__(self, model)
-        self.vms = VMs(model)
-        self.templates = Templates(model)
-        self.storagepools = StoragePools(model)
-        self.interfaces = Interfaces(model)
-        self.networks = Networks(model)
-        self.tasks = Tasks(model)
-        self.config = Config(model)
-        self.host = Host(model)
-        self.debugreports = DebugReports(model)
-        self.plugins = Plugins(model)
+        Resource.__init__(self, backend)
+        self.vms = VMs(backend)
+        self.templates = Templates(backend)
+        self.storagepools = StoragePools(backend)
+        self.interfaces = Interfaces(backend)
+        self.networks = Networks(backend)
+        self.tasks = Tasks(backend)
+        self.config = Config(backend)
+        self.host = Host(backend)
+        self.debugreports = DebugReports(backend)
+        self.plugins = Plugins(backend)
         self.api_schema = json.load(open(get_api_schema_file()))
 
     def error_production_handler(self, status, message, traceback, version):
diff --git a/src/kimchi/server.py b/src/kimchi/server.py
index b820263..ffa9324 100644
--- a/src/kimchi/server.py
+++ b/src/kimchi/server.py
@@ -30,9 +30,9 @@ import sslcert
 
 from kimchi import auth
 from kimchi import config
-from kimchi import model
-from kimchi import mockmodel
 from kimchi import vnc
+from kimchi.model.libvirtbackend import LibvirtBackend
+from kimchi.model.mockbackend import MockBackend
 from kimchi.root import Root
 from kimchi.utils import get_enabled_plugins, import_class
 
@@ -182,18 +182,18 @@ class Server(object):
         if not dev_env:
             cherrypy.config.update({'environment': 'production'})
 
-        if hasattr(options, 'model'):
-            model_instance = options.model
+        if hasattr(options, 'backend'):
+            backend_instance = options.backend
         elif options.test:
-            model_instance = mockmodel.get_mock_environment()
+            backend_instance = MockBackend()
         else:
-            model_instance = model.Model()
+            backend_instance = LibvirtBackend()
 
-        if isinstance(model_instance, model.Model):
+        if isinstance(backend_instance, LibvirtBackend):
             vnc_ws_proxy = vnc.new_ws_proxy()
             cherrypy.engine.subscribe('exit', vnc_ws_proxy.kill)
 
-        self.app = cherrypy.tree.mount(Root(model_instance, dev_env),
+        self.app = cherrypy.tree.mount(Root(backend_instance, dev_env),
                                        config=self.configObj)
         self._load_plugins()
 
-- 
1.7.10.4




More information about the Kimchi-devel mailing list