[Kimchi-devel] [PATCH 22/22] Delete former model.py and rename model_ to model

Aline Manera alinefm at linux.vnet.ibm.com
Tue Jan 28 20:21:36 UTC 2014


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

model.py can be deleted as all resources have their own model
implementations
Also rename model_ to model

Signed-off-by: Aline Manera <alinefm at br.ibm.com>
---
 Makefile.am                             |    2 +-
 src/kimchi/control/storagepools.py      |    2 +-
 src/kimchi/mockmodel.py                 |    6 +-
 src/kimchi/model.py                     | 1708 -------------------------------
 src/kimchi/model/__init__.py            |   21 +
 src/kimchi/model/config.py              |   87 ++
 src/kimchi/model/debugreports.py        |  167 +++
 src/kimchi/model/host.py                |  201 ++++
 src/kimchi/model/interfaces.py          |   46 +
 src/kimchi/model/libvirtconnection.py   |  122 +++
 src/kimchi/model/libvirtstoragepool.py  |  257 +++++
 src/kimchi/model/model.py               |   53 +
 src/kimchi/model/networks.py            |  265 +++++
 src/kimchi/model/plugins.py             |   31 +
 src/kimchi/model/storagepools.py        |  246 +++++
 src/kimchi/model/storageservers.py      |   78 ++
 src/kimchi/model/storagetargets.py      |   86 ++
 src/kimchi/model/storagevolumes.py      |  176 ++++
 src/kimchi/model/tasks.py               |   39 +
 src/kimchi/model/templates.py           |  172 ++++
 src/kimchi/model/utils.py               |   33 +
 src/kimchi/model/vmifaces.py            |  135 +++
 src/kimchi/model/vms.py                 |  450 ++++++++
 src/kimchi/model_/__init__.py           |   21 -
 src/kimchi/model_/config.py             |   87 --
 src/kimchi/model_/debugreports.py       |  167 ---
 src/kimchi/model_/host.py               |  201 ----
 src/kimchi/model_/interfaces.py         |   46 -
 src/kimchi/model_/libvirtconnection.py  |  122 ---
 src/kimchi/model_/libvirtstoragepool.py |  257 -----
 src/kimchi/model_/model.py              |   53 -
 src/kimchi/model_/networks.py           |  265 -----
 src/kimchi/model_/plugins.py            |   31 -
 src/kimchi/model_/storagepools.py       |  246 -----
 src/kimchi/model_/storageservers.py     |   78 --
 src/kimchi/model_/storagetargets.py     |   86 --
 src/kimchi/model_/storagevolumes.py     |  176 ----
 src/kimchi/model_/tasks.py              |   39 -
 src/kimchi/model_/templates.py          |  172 ----
 src/kimchi/model_/utils.py              |   33 -
 src/kimchi/model_/vmifaces.py           |  135 ---
 src/kimchi/model_/vms.py                |  450 --------
 src/kimchi/server.py                    |    2 +-
 tests/test_model.py                     |    2 +-
 tests/test_storagepool.py               |    2 +-
 45 files changed, 2673 insertions(+), 4381 deletions(-)
 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/libvirtconnection.py
 create mode 100644 src/kimchi/model/libvirtstoragepool.py
 create mode 100644 src/kimchi/model/model.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/storageservers.py
 create mode 100644 src/kimchi/model/storagetargets.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/utils.py
 create mode 100644 src/kimchi/model/vmifaces.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_/libvirtconnection.py
 delete mode 100644 src/kimchi/model_/libvirtstoragepool.py
 delete mode 100644 src/kimchi/model_/model.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_/storageservers.py
 delete mode 100644 src/kimchi/model_/storagetargets.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_/utils.py
 delete mode 100644 src/kimchi/model_/vmifaces.py
 delete mode 100644 src/kimchi/model_/vms.py

diff --git a/Makefile.am b/Makefile.am
index 1ffe6db..c092f42 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -54,7 +54,7 @@ PEP8_WHITELIST = \
 	src/kimchi/iscsi.py \
 	src/kimchi/isoinfo.py \
 	src/kimchi/kvmusertests.py \
-	src/kimchi/model_/*.py \
+	src/kimchi/model/*.py \
 	src/kimchi/rollbackcontext.py \
 	src/kimchi/root.py \
 	src/kimchi/server.py \
diff --git a/src/kimchi/control/storagepools.py b/src/kimchi/control/storagepools.py
index 8a6823c..fc276cd 100644
--- a/src/kimchi/control/storagepools.py
+++ b/src/kimchi/control/storagepools.py
@@ -30,7 +30,7 @@ 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_.storagepools import ISO_POOL_NAME
+from kimchi.model.storagepools import ISO_POOL_NAME
 from kimchi.control.utils import UrlSubNode
 
 
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
index 79f6f79..e59d1f5 100644
--- a/src/kimchi/mockmodel.py
+++ b/src/kimchi/mockmodel.py
@@ -46,9 +46,9 @@ 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.model_.storagepools import ISO_POOL_NAME, STORAGE_SOURCES
-from kimchi.model_.utils import get_vm_name
-from kimchi.model_.vms import VM_STATIC_UPDATE_PARAMS
+from kimchi.model.storagepools import ISO_POOL_NAME, STORAGE_SOURCES
+from kimchi.model.utils import get_vm_name
+from kimchi.model.vms import VM_STATIC_UPDATE_PARAMS
 from kimchi.objectstore import ObjectStore
 from kimchi.screenshot import VMScreenshot
 from kimchi.utils import template_name_from_uri, pool_name_from_uri
diff --git a/src/kimchi/model.py b/src/kimchi/model.py
deleted file mode 100644
index ddb2d95..0000000
--- a/src/kimchi/model.py
+++ /dev/null
@@ -1,1708 +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 lxml.etree as ET
-import os
-import platform
-import psutil
-import random
-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 lxml import etree, objectify
-from lxml.builder import E
-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.utils import patch_find_nfs_target
-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.nfs_target_probe = FeatureTests.libvirt_support_nfs_probe()
-
-        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({'net': 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 vmifaces_create(self, vm, params):
-        def randomMAC():
-            mac = [0x52, 0x54, 0x00,
-                   random.randint(0x00, 0x7f),
-                   random.randint(0x00, 0xff),
-                   random.randint(0x00, 0xff)]
-            return ':'.join(map(lambda x: "%02x" % x, mac))
-
-        if (params["type"] == "network" and
-            params["network"] not in self.networks_get_list()):
-            raise InvalidParameter("%s is not an available network" %
-                                   params["network"])
-
-        dom = self._get_vm(vm)
-        if Model.dom_state_map[dom.info()[0]] != "shutoff":
-            raise InvalidOperation("do not support hot plugging attach "
-                                   "guest interface")
-
-        macs = (iface.mac.get('address')
-                for iface in self._get_vmifaces(vm))
-
-        mac = randomMAC()
-        while True:
-            if mac not in macs:
-                break
-            mac = randomMAC()
-
-        children = [E.mac(address=mac)]
-        ("network" in params.keys() and
-         children.append(E.source(network=params['network'])))
-        ("model" in params.keys() and
-         children.append(E.model(type=params['model'])))
-        attrib = {"type": params["type"]}
-
-        xml =  etree.tostring(E.interface(*children, **attrib))
-
-        dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)
-
-        return mac
-
-    def _get_vmifaces(self, vm):
-        dom = self._get_vm(vm)
-        xml = dom.XMLDesc(0)
-        root = objectify.fromstring(xml)
-
-        return root.devices.findall("interface")
-
-    def _get_vmiface(self, vm, mac):
-        ifaces = self._get_vmifaces(vm)
-
-        for iface in ifaces:
-           if iface.mac.get('address') == mac:
-               return iface
-        return None
-
-    def vmifaces_get_list(self, vm):
-        return [iface.mac.get('address') for iface in self._get_vmifaces(vm)]
-
-    def vmiface_lookup(self, vm, mac):
-        info = {}
-
-        iface = self._get_vmiface(vm, mac)
-        if iface is None:
-            raise NotFoundError('iface: "%s"' % mac)
-
-        info['type'] = iface.attrib['type']
-        info['mac'] = iface.mac.get('address')
-        if iface.find("model") is not None:
-            info['model'] = iface.model.get('type')
-        if info['type'] == 'network':
-            info['network'] = iface.source.get('network')
-        if info['type'] == 'bridge':
-            info['bridge'] = iface.source.get('bridge')
-
-        return info
-
-    def vmiface_delete(self, vm, mac):
-        dom = self._get_vm(vm)
-        iface = self._get_vmiface(vm, mac)
-
-        if Model.dom_state_map[dom.info()[0]] != "shutoff":
-            raise InvalidOperation("do not support hot plugging detach "
-                                   "guest interface")
-        if iface is None:
-            raise NotFoundError('iface: "%s"' % mac)
-
-        dom.detachDeviceFlags(etree.tostring(iface),
-                              libvirt.VIR_DOMAIN_AFFECT_CURRENT)
-
-    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 storageservers_get_list(self, _target_type=None):
-        target_type = STORAGE_SOURCES.keys() if not _target_type else [_target_type]
-        pools = self.storagepools_get_list()
-        server_list = []
-        for pool in pools:
-            try:
-                pool_info = self.storagepool_lookup(pool)
-                if (pool_info['type'] in target_type and
-                        pool_info['source']['addr'] not in server_list):
-                    # Avoid to add same server for multiple times
-                    # if it hosts more than one storage type
-                    server_list.append(pool_info['source']['addr'])
-            except NotFoundError:
-                pass
-
-        return server_list
-
-    def storageserver_lookup(self, server):
-        pools = self.storagepools_get_list()
-        for pool in pools:
-            try:
-                pool_info = self.storagepool_lookup(pool)
-                if pool_info['source'] and pool_info['source']['addr'] == server:
-                    return dict(host=server)
-            except NotFoundError:
-            # Avoid inconsistent pool result because of lease between list and lookup
-                pass
-
-        raise NotFoundError('server %s does not used by kimchi' % server)
-
-    def storagetargets_get_list(self, storage_server, _target_type=None):
-        target_types = STORAGE_SOURCES.keys() if not _target_type else [_target_type]
-        target_list = list()
-
-        for target_type in target_types:
-            if not self.nfs_target_probe and target_type == 'netfs':
-                targets = patch_find_nfs_target(storage_server)
-            else:
-                xml = _get_storage_server_spec(server=storage_server, target_type=target_type)
-                conn = self.conn.get()
-
-                try:
-                    ret = conn.findStoragePoolSources(target_type, xml, 0)
-                except libvirt.libvirtError as e:
-                    kimchi_log.warning("Query storage pool source fails because of %s",
-                        e.get_error_message())
-                    continue
-                targets = _parse_target_source_result(target_type, ret)
-
-            target_list.extend(targets)
-        return target_list
-
-    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 _parse_target_source_result(target_type, xml_str):
-    root = objectify.fromstring(xml_str)
-    ret = []
-    for source in root.getchildren():
-        if target_type == 'netfs':
-            host_name = source.host.get('name')
-            target_path = source.dir.get('path')
-            type = source.format.get('type')
-            ret.append(dict(host=host_name, target_type=type, target=target_path))
-    return ret
-
-
-def _get_storage_server_spec(**kwargs):
-    # Required parameters:
-    # server:
-    # target_type:
-    extra_args = []
-    if kwargs['target_type'] == 'netfs':
-        extra_args.append(E.format(type='nfs'))
-    obj = E.source(E.host(name=kwargs['server']), *extra_args)
-    xml = ET.tostring(obj)
-    return xml
-
-
-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..86a1167
--- /dev/null
+++ b/src/kimchi/model/config.py
@@ -0,0 +1,87 @@
+#
+# 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 cherrypy
+
+from kimchi.basemodel import Singleton
+from kimchi.distroloader import DistroLoader
+from kimchi.exception import NotFoundError
+from kimchi.featuretests import FeatureTests
+from kimchi.model.debugreports import DebugReportsModel
+from kimchi.screenshot import VMScreenshot
+from kimchi.utils import kimchi_log
+
+
+class CapabilitiesModel(object):
+    __metaclass__ = Singleton
+
+    def __init__(self, **kargs):
+        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)
+
+    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.nfs_target_probe = FeatureTests.libvirt_support_nfs_probe()
+
+        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 lookup(self, *ident):
+        report_tool = DebugReportsModel.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)}
+
+
+class DistrosModel(object):
+    def __init__(self, **kargs):
+        distroloader = DistroLoader()
+        self.distros = distroloader.get()
+
+    def get_list(self):
+        return self.distros.keys()
+
+
+class DistroModel(object):
+    def __init__(self, **kargs):
+        self._distros = DistrosModel()
+
+    def lookup(self, name):
+        try:
+            return self._distros.distros[name]
+        except KeyError:
+            raise NotFoundError("Distro '%s' not found." % name)
diff --git a/src/kimchi/model/debugreports.py b/src/kimchi/model/debugreports.py
new file mode 100644
index 0000000..a1cb19c
--- /dev/null
+++ b/src/kimchi/model/debugreports.py
@@ -0,0 +1,167 @@
+#
+# 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 fnmatch
+import glob
+import logging
+import os
+import shutil
+import subprocess
+import time
+
+from kimchi import config
+from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.model.tasks import TaskModel
+from kimchi.utils import add_task, kimchi_log
+
+
+class DebugReportsModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+        self.task = TaskModel(**kargs)
+
+    def create(self, params):
+        ident = params['name']
+        taskid = self._gen_debugreport_file(ident)
+        return self.task.lookup(taskid)
+
+    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
+
+    def _gen_debugreport_file(self, name):
+        gen_cmd = self.get_system_report_tool()
+
+        if gen_cmd is not None:
+            return add_task('', gen_cmd, self.objstore, name)
+
+        raise OperationFailed("debugreport tool not found")
+
+    @staticmethod
+    def sosreport_generate(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)
+
+    @staticmethod
+    def get_system_report_tool():
+        # Please add new possible debug report command here
+        # and implement the report generating function
+        # based on the new report command
+        report_tools = ({'cmd': 'sosreport --help',
+                         'fn': DebugReportsModel.sosreport_generate},)
+
+        # check if the command can be found by shell one by one
+        for helper_tool in 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
+
+
+class DebugReportModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def 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 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)
+
+
+class DebugReportContentModel(object):
+    def __init__(self, **kargs):
+        self._debugreport = DebugReportModel()
+
+    def lookup(self, name):
+        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..d5fc124
--- /dev/null
+++ b/src/kimchi/model/host.py
@@ -0,0 +1,201 @@
+#
+# 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 os
+import time
+import platform
+from collections import defaultdict
+
+import psutil
+from cherrypy.process.plugins import BackgroundTask
+
+from kimchi import disks
+from kimchi import netinfo
+from kimchi.basemodel import Singleton
+from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.model.vms import DOM_STATE_MAP
+from kimchi.utils import kimchi_log
+
+
+HOST_STATS_INTERVAL = 1
+
+
+class HostModel(object):
+    def __init__(self, **kargs):
+        self.host_info = self._get_host_info()
+
+    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 lookup(self, *name):
+        return self.host_info
+
+    def 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 reboot(self, args=None):
+        # Find running VMs
+        running_vms = self._get_vms_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')
+
+    def _get_vms_list_by_state(self, state):
+        ret_list = []
+        for name in self.vms_get_list():
+            info = self._get_vm(name).info()
+            if (DOM_STATE_MAP[info[0]]) == state:
+                ret_list.append(name)
+        return ret_list
+
+
+class HostStatsModel(object):
+    __metaclass__ = Singleton
+
+    def __init__(self, **kargs):
+        self.host_stats = defaultdict(int)
+        self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
+                                                self._update_host_stats)
+        self.host_stats_thread.start()
+
+    def 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 _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})
+
+
+class PartitionsModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def get_list(self):
+        result = disks.get_partitions_names()
+        return result
+
+
+class PartitionModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def 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)
diff --git a/src/kimchi/model/interfaces.py b/src/kimchi/model/interfaces.py
new file mode 100644
index 0000000..52c6bae
--- /dev/null
+++ b/src/kimchi/model/interfaces.py
@@ -0,0 +1,46 @@
+#
+# 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
+
+from kimchi import netinfo
+from kimchi.exception import NotFoundError
+from kimchi.model.networks import NetworksModel
+
+
+class InterfacesModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.networks = NetworksModel(**kargs)
+
+    def get_list(self):
+        return list(set(netinfo.all_favored_interfaces()) -
+                    set(self.networks.get_all_networks_interfaces()))
+
+
+class InterfaceModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def lookup(self, name):
+        try:
+            return netinfo.get_interface_info(name)
+        except ValueError, e:
+            raise NotFoundError(e)
diff --git a/src/kimchi/model/libvirtconnection.py b/src/kimchi/model/libvirtconnection.py
new file mode 100644
index 0000000..7bbb668
--- /dev/null
+++ b/src/kimchi/model/libvirtconnection.py
@@ -0,0 +1,122 @@
+#
+# 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 threading
+import time
+
+import cherrypy
+import libvirt
+
+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:
+                            err = 'Libvirt is not available, exiting.'
+                            kimchi_log.error(err)
+                            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..f4dbf2e
--- /dev/null
+++ b/src/kimchi/model/libvirtstoragepool.py
@@ -0,0 +1,257 @@
+#
+# 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 os
+import tempfile
+
+import libvirt
+
+from kimchi.exception import InvalidParameter, OperationFailed, TimeoutExpired
+from kimchi.iscsi import TargetClient
+from kimchi.rollbackcontext import RollbackContext
+from kimchi.utils import parse_cmd_output, run_command
+
+
+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):
+        mnt_point = tempfile.mkdtemp(dir='/tmp')
+        export_path = "%s:%s" % (
+            self.poolArgs['source']['host'], self.poolArgs['source']['path'])
+        mount_cmd = ["mount", "-o", 'soft,timeo=100,retrans=3,retry=0',
+                     export_path, mnt_point]
+        umount_cmd = ["umount", "-f", export_path]
+        mounted = False
+
+        with RollbackContext() as rollback:
+            rollback.prependDefer(os.rmdir, mnt_point)
+            try:
+                run_command(mount_cmd, 30)
+                rollback.prependDefer(run_command, umount_cmd)
+            except TimeoutExpired:
+                err = "Export path %s may block during nfs mount"
+                raise InvalidParameter(err % export_path)
+
+            with open("/proc/mounts", "rb") as f:
+                rawMounts = f.read()
+            output_items = ['dev_path', 'mnt_point', 'type']
+            mounts = parse_cmd_output(rawMounts, output_items)
+            for item in mounts:
+                if 'dev_path' in item and item['dev_path'] == export_path:
+                    mounted = True
+
+            if not mounted:
+                err = "Export path %s mount failed during nfs mount"
+                raise InvalidParameter(err % export_path)
+
+    @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/model.py b/src/kimchi/model/model.py
new file mode 100644
index 0000000..28f8be4
--- /dev/null
+++ b/src/kimchi/model/model.py
@@ -0,0 +1,53 @@
+#
+# 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 inspect
+import os
+
+from kimchi.basemodel import BaseModel
+from kimchi.model.libvirtconnection import LibvirtConnection
+from kimchi.objectstore import ObjectStore
+from kimchi.utils import import_module, listPathModules
+
+
+class Model(BaseModel):
+    def __init__(self, libvirt_uri='qemu:///system', objstore_loc=None):
+        self.objstore = ObjectStore(objstore_loc)
+        self.conn = LibvirtConnection(libvirt_uri)
+        kargs = {'objstore': self.objstore, 'conn': self.conn}
+
+        this = os.path.basename(__file__)
+        this_mod = os.path.splitext(this)[0]
+
+        models = []
+        for mod_name in listPathModules(os.path.dirname(__file__)):
+            if mod_name.startswith("_") or mod_name == this_mod:
+                continue
+
+            module = import_module('model.' + mod_name)
+            members = inspect.getmembers(module, inspect.isclass)
+            for cls_name, instance in members:
+                if inspect.getmodule(instance) == module:
+                    if cls_name.endswith('Model'):
+                        models.append(instance(**kargs))
+
+        return super(Model, self).__init__(models)
diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py
new file mode 100644
index 0000000..b164141
--- /dev/null
+++ b/src/kimchi/model/networks.py
@@ -0,0 +1,265 @@
+#
+# 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 ipaddr
+import libvirt
+
+from kimchi import netinfo
+from kimchi import network as knetwork
+from kimchi import networkxml
+from kimchi import xmlutils
+from kimchi.exception import InvalidOperation, InvalidParameter
+from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
+
+
+class NetworksModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def create(self, params):
+        conn = self.conn.get()
+        name = params['name']
+        if name in self.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 get_list(self):
+        conn = self.conn.get()
+        return sorted(conn.listNetworks() + conn.listDefinedNetworks())
+
+    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():
+                network = self._get_network(net_name)
+                xml = network.XMLDesc(0)
+                subnet = NetworkModel.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({'net': 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 get_all_networks_interfaces(self):
+        net_names = self.get_list()
+        interfaces = []
+        for name in net_names:
+            conn = self.conn.get()
+            network = conn.networkLookupByName(name)
+            xml = network.XMLDesc(0)
+            net_dict = NetworkModel.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 _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
+
+
+class NetworkModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def 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 _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 _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 activate(self, name):
+        network = self._get_network(name)
+        network.create()
+
+    def deactivate(self, name):
+        network = self._get_network(name)
+        network.destroy()
+
+    def 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_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()))
+
+    @staticmethod
+    def get_network_from_xml(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 _remove_vlan_tagged_bridge(self, network):
+        try:
+            bridge = network.bridgeName()
+        except libvirt.libvirtError:
+            pass
+        else:
+            if bridge.startswith('kimchi-'):
+                conn = self.conn.get()
+                iface = conn.interfaceLookupByName(bridge)
+                if iface.isActive():
+                    iface.destroy()
+                iface.undefine()
diff --git a/src/kimchi/model/plugins.py b/src/kimchi/model/plugins.py
new file mode 100644
index 0000000..d6756d0
--- /dev/null
+++ b/src/kimchi/model/plugins.py
@@ -0,0 +1,31 @@
+#
+# 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
+
+from kimchi.utils import get_enabled_plugins
+
+
+class PluginsModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def get_list(self):
+        return [plugin for (plugin, config) in get_enabled_plugins()]
diff --git a/src/kimchi/model/storagepools.py b/src/kimchi/model/storagepools.py
new file mode 100644
index 0000000..233a8a7
--- /dev/null
+++ b/src/kimchi/model/storagepools.py
@@ -0,0 +1,246 @@
+#
+# 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 libvirt
+
+from kimchi import xmlutils
+from kimchi.scan import Scanner
+from kimchi.exception import InvalidOperation, MissingParameter
+from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.model.libvirtstoragepool import StoragePoolDef
+from kimchi.utils import add_task, kimchi_log
+
+
+ISO_POOL_NAME = u'kimchi_isos'
+POOL_STATE_MAP = {0: 'inactive',
+                  1: 'initializing',
+                  2: 'active',
+                  3: 'degraded',
+                  4: 'inaccessible'}
+
+STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
+                             'path': '/pool/source/dir/@path'}}
+
+
+class StoragePoolsModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.objstore = kargs['objstore']
+        self.scanner = Scanner(self._clean_scan)
+        self.scanner.delete()
+
+    def 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 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.get_list():
+            err = "The name %s has been used by a pool"
+            raise InvalidOperation(err % 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 _clean_scan(self, pool_name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(pool_name)
+            pool.destroy()
+            with self.objstore as session:
+                session.delete('scanning', pool_name)
+        except Exception, e:
+            err = "Exception %s occured when cleaning scan result"
+            kimchi_log.debug(err % 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_list():
+            try:
+                res = self.storagepool_lookup(pool)
+                if res['state'] == 'active':
+                    scan_params['ignore_list'].append(res['path'])
+            except Exception, e:
+                err = "Exception %s occured when get ignore path"
+                kimchi_log.debug(err % e.message)
+
+        params['path'] = self.scanner.scan_dir_prepare(params['name'])
+        scan_params['pool_path'] = params['path']
+        task_id = add_task('', self.scanner.start_scan, self.objstore,
+                           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
+
+
+class StoragePoolModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.objstore = kargs['objstore']
+
+    @staticmethod
+    def get_storagepool(name, conn):
+        conn = 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 _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 lookup(self, name):
+        pool = self.get_storagepool(name, self.conn)
+        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': 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 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, self.conn)
+        if autostart:
+            pool.setAutostart(1)
+        else:
+            pool.setAutostart(0)
+        ident = pool.name()
+        return ident
+
+    def activate(self, name):
+        pool = self.get_storagepool(name, self.conn)
+        try:
+            pool.create(0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def deactivate(self, name):
+        pool = self.get_storagepool(name, self.conn)
+        try:
+            pool.destroy()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def delete(self, name):
+        pool = self.get_storagepool(name, self.conn)
+        if pool.isActive():
+            err = "Unable to delete the active storagepool %s"
+            raise InvalidOperation(err % name)
+        try:
+            pool.undefine()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+
+class IsoPoolModel(object):
+    def __init__(self, **kargs):
+        pass
+
+    def lookup(self, name):
+        return {'state': 'active',
+                'type': 'kimchi-iso'}
diff --git a/src/kimchi/model/storageservers.py b/src/kimchi/model/storageservers.py
new file mode 100644
index 0000000..6a7c14a
--- /dev/null
+++ b/src/kimchi/model/storageservers.py
@@ -0,0 +1,78 @@
+#
+# 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
+
+from kimchi.exception import NotFoundError
+from kimchi.model.storagepools import StoragePoolModel, STORAGE_SOURCES
+
+
+class StorageServersModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.pool = StoragePoolModel(**kargs)
+
+    def get_list(self, _target_type=None):
+        if not _target_type:
+            target_type = STORAGE_SOURCES.keys()
+        else:
+            target_type = [_target_type]
+        pools = self.pools.get_list()
+
+        conn = self.conn.get()
+        pools = conn.listStoragePools()
+        pools += conn.listDefinedStoragePools()
+
+        server_list = []
+        for pool in pools:
+            try:
+                pool_info = self.pool.lookup(pool)
+                if (pool_info['type'] in target_type and
+                        pool_info['source']['addr'] not in server_list):
+                    # Avoid to add same server for multiple times
+                    # if it hosts more than one storage type
+                    server_list.append(pool_info['source']['addr'])
+            except NotFoundError:
+                pass
+
+        return server_list
+
+
+class StorageServerModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.pool = StoragePoolModel(**kargs)
+
+    def lookup(self, server):
+        conn = self.conn.get()
+        pools = conn.listStoragePools()
+        pools += conn.listDefinedStoragePools()
+        for pool in pools:
+            try:
+                pool_info = self.pool.lookup(pool)
+                if pool_info['source'] and \
+                   pool_info['source']['addr'] == server:
+                    return dict(host=server)
+            except NotFoundError:
+                # Avoid inconsistent pool result because of lease between list
+                # lookup
+                pass
+
+        raise NotFoundError('server %s does not used by kimchi' % server)
diff --git a/src/kimchi/model/storagetargets.py b/src/kimchi/model/storagetargets.py
new file mode 100644
index 0000000..be7cdaf
--- /dev/null
+++ b/src/kimchi/model/storagetargets.py
@@ -0,0 +1,86 @@
+#
+# 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 libvirt
+import lxml.etree as ET
+from lxml import objectify
+from lxml.builder import E
+
+from kimchi.model.config import CapabilitiesModel
+from kimchi.model.storagepools import STORAGE_SOURCES
+from kimchi.utils import kimchi_log, patch_find_nfs_target
+
+
+class StorageTargetsModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.caps = CapabilitiesModel()
+
+    def get_list(self, storage_server, _target_type=None):
+        target_list = list()
+
+        if not _target_type:
+            target_types = STORAGE_SOURCES.keys()
+        else:
+            target_types = [_target_type]
+
+        for target_type in target_types:
+            if not self.caps.nfs_target_probe and target_type == 'netfs':
+                targets = patch_find_nfs_target(storage_server)
+            else:
+                xml = self._get_storage_server_spec(server=storage_server,
+                                                    target_type=target_type)
+                conn = self.conn.get()
+                try:
+                    ret = conn.findStoragePoolSources(target_type, xml, 0)
+                except libvirt.libvirtError as e:
+                    err = "Query storage pool source fails because of %s"
+                    kimchi_log.warning(err, e.get_error_message())
+                    continue
+
+                targets = self._parse_target_source_result(target_type, ret)
+
+            target_list.extend(targets)
+        return target_list
+
+    def _get_storage_server_spec(**kwargs):
+        # Required parameters:
+        # server:
+        # target_type:
+        extra_args = []
+        if kwargs['target_type'] == 'netfs':
+            extra_args.append(E.format(type='nfs'))
+        obj = E.source(E.host(name=kwargs['server']), *extra_args)
+        xml = ET.tostring(obj)
+        return xml
+
+    def _parse_target_source_result(target_type, xml_str):
+        root = objectify.fromstring(xml_str)
+        ret = []
+        for source in root.getchildren():
+            if target_type == 'netfs':
+                host_name = source.host.get('name')
+                target_path = source.dir.get('path')
+                type = source.format.get('type')
+                ret.append(dict(host=host_name, target_type=type,
+                                target=target_path))
+        return ret
diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py
new file mode 100644
index 0000000..7ff570b
--- /dev/null
+++ b/src/kimchi/model/storagevolumes.py
@@ -0,0 +1,176 @@
+#
+# 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 os
+
+import libvirt
+
+from kimchi import xmlutils
+from kimchi.exception import InvalidOperation, IsoFormatError
+from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
+from kimchi.isoinfo import IsoImage
+from kimchi.model.storagepools import StoragePoolModel
+
+
+VOLUME_TYPE_MAP = {0: 'file',
+                   1: 'block',
+                   2: 'directory',
+                   3: 'network'}
+
+
+class StorageVolumesModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def create(self, pool, params):
+        vol_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>
+        """
+        params.setdefault('allocation', 0)
+        params.setdefault('format', 'qcow2')
+
+        try:
+            pool = StoragePoolModel.get_storagepool(pool, self.conn)
+            name = params['name']
+            xml = vol_xml % params
+        except KeyError, key:
+            raise MissingParameter(key)
+
+        try:
+            pool.createXML(xml, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+        return name
+
+    def get_list(self, pool):
+        pool = StoragePoolModel.get_storagepool(pool, self.conn)
+        if not pool.isActive():
+            err = "Unable to list volumes in inactive storagepool %s"
+            raise InvalidOperation(err % pool.name())
+        try:
+            pool.refresh(0)
+            return pool.listVolumes()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+
+class StorageVolumeModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def _get_storagevolume(self, pool, name):
+        pool = StoragePoolModel.get_storagepool(pool, self.conn)
+        if not pool.isActive():
+            err = "Unable to list volumes in inactive storagepool %s"
+            raise InvalidOperation(err % 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 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=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 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 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 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())
+
+
+class IsoVolumesModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.storagevolume = StorageVolumeModel(**kargs)
+
+    def get_list(self):
+        iso_volumes = []
+        conn = self.conn.get()
+        pools = conn.listStoragePools()
+        pools += conn.listDefinedStoragePools()
+
+        for pool in pools:
+            try:
+                pool.refresh(0)
+                volumes = pool.listVolumes()
+            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
diff --git a/src/kimchi/model/tasks.py b/src/kimchi/model/tasks.py
new file mode 100644
index 0000000..40ca1d6
--- /dev/null
+++ b/src/kimchi/model/tasks.py
@@ -0,0 +1,39 @@
+#
+# 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
+
+
+class TasksModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+
+    def get_list(self):
+        with self.objstore as session:
+            return session.get_list('task')
+
+
+class TaskModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+
+    def lookup(self, id):
+        with self.objstore as session:
+            return session.get('task', str(id))
diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py
new file mode 100644
index 0000000..03632a6
--- /dev/null
+++ b/src/kimchi/model/templates.py
@@ -0,0 +1,172 @@
+#
+# 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 import xmlutils
+from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
+from kimchi.utils import pool_name_from_uri
+from kimchi.vmtemplate import VMTemplate
+
+
+class TemplatesModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+        self.conn = kargs['conn']
+
+    def create(self, params):
+        name = params['name']
+        conn = self.conn.get()
+
+        pool_uri = params.get(u'storagepool', '')
+        if pool_uri:
+            pool_name = pool_name_from_uri(pool_uri)
+            try:
+                conn.storagePoolLookupByName(pool_name)
+            except Exception as e:
+                err = "Storagepool specified is not valid: %s."
+                raise InvalidParameter(err % e.message)
+
+        for net_name in params.get(u'networks', []):
+            try:
+                conn.networkLookupByName(net_name)
+            except Exception, e:
+                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 get_list(self):
+        with self.objstore as session:
+            return session.get_list('template')
+
+
+class TemplateModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+        self.conn = kargs['conn']
+        self.templates = TemplatesModel(**kargs)
+
+    @staticmethod
+    def get_template(name, objstore, conn, overrides=None):
+        with objstore as session:
+            params = session.get('template', name)
+        if overrides:
+            params.update(overrides)
+        return LibvirtVMTemplate(params, False, conn)
+
+    def lookup(self, name):
+        t = self.get_template(name, self.objstore, self.conn)
+        return t.info
+
+    def delete(self, name):
+        with self.objstore as session:
+            session.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
+
+        pool_uri = new_t.get(u'storagepool', '')
+        pool_name = pool_name_from_uri(pool_uri)
+        try:
+            conn = self.conn.get()
+            conn.storagePoolLookupByName(pool_name)
+        except Exception as e:
+            err = "Storagepool specified is not valid: %s."
+            raise InvalidParameter(err % e.message)
+
+        for net_name in params.get(u'networks', []):
+            try:
+                conn = self.conn.get()
+                conn.networkLookupByName(net_name)
+            except Exception, e:
+                raise InvalidParameter("Network '%s' specified by template "
+                                       "does not exist" % net_name)
+
+        self.delete(name)
+        try:
+            ident = self.templates.create(new_t)
+        except:
+            ident = self.templates.create(old_t)
+            raise
+        return ident
+
+
+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:
+            err = 'Storage specified by template does not exist'
+            raise InvalidParameter(err)
+
+        if not pool.isActive():
+            err = 'Storage specified by template is not active'
+            raise InvalidParameter(err)
+
+        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:
+                err = 'Network specified by template does not exist'
+                raise InvalidParameter(err)
+
+            if not network.isActive():
+                err = 'Network specified by template is not active'
+                raise InvalidParameter(err)
+
+    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
diff --git a/src/kimchi/model/utils.py b/src/kimchi/model/utils.py
new file mode 100644
index 0000000..a27b867
--- /dev/null
+++ b/src/kimchi/model/utils.py
@@ -0,0 +1,33 @@
+#
+# 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
+
+from kimchi.exception import OperationFailed
+
+
+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")
diff --git a/src/kimchi/model/vmifaces.py b/src/kimchi/model/vmifaces.py
new file mode 100644
index 0000000..f3eddb2
--- /dev/null
+++ b/src/kimchi/model/vmifaces.py
@@ -0,0 +1,135 @@
+#
+# 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 random
+
+import libvirt
+from lxml import etree, objectify
+from lxml.builder import E
+
+from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
+from kimchi.model.vms import DOM_STATE_MAP, VMModel
+
+
+class VMIfacesModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def get_list(self, vm):
+        macs = []
+        for iface in self.get_vmifaces(vm, self.conn):
+            macs.append(iface.mac.get('address'))
+        return macs
+
+    def create(self, vm, params):
+        def randomMAC():
+            mac = [0x52, 0x54, 0x00,
+                   random.randint(0x00, 0x7f),
+                   random.randint(0x00, 0xff),
+                   random.randint(0x00, 0xff)]
+            return ':'.join(map(lambda x: "%02x" % x, mac))
+
+        conn = self.conn.get()
+        networks = conn.listNetworks() + conn.listDefinedNetworks()
+
+        if params["type"] == "network" and params["network"] not in networks:
+            raise InvalidParameter("%s is not an available network" %
+                                   params["network"])
+
+        dom = VMModel.get_vm(vm, self.conn)
+        if DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+            raise InvalidOperation("do not support hot plugging attach "
+                                   "guest interface")
+
+        macs = (iface.mac.get('address')
+                for iface in self.get_vmifaces(vm, self.conn))
+
+        mac = randomMAC()
+        while True:
+            if mac not in macs:
+                break
+            mac = randomMAC()
+
+        children = [E.mac(address=mac)]
+        ("network" in params.keys() and
+         children.append(E.source(network=params['network'])))
+        ("model" in params.keys() and
+         children.append(E.model(type=params['model'])))
+        attrib = {"type": params["type"]}
+
+        xml = etree.tostring(E.interface(*children, **attrib))
+
+        dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)
+
+        return mac
+
+    @staticmethod
+    def get_vmifaces(vm, conn):
+        dom = VMModel.get_vm(vm, conn)
+        xml = dom.XMLDesc(0)
+        root = objectify.fromstring(xml)
+
+        return root.devices.findall("interface")
+
+
+class VMIfaceModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+
+    def _get_vmiface(self, vm, mac):
+        ifaces = VMIfacesModel.get_vmifaces(vm, self.conn)
+
+        for iface in ifaces:
+            if iface.mac.get('address') == mac:
+                return iface
+        return None
+
+    def lookup(self, vm, mac):
+        info = {}
+
+        iface = self._get_vmiface(vm, mac)
+        if iface is None:
+            raise NotFoundError('iface: "%s"' % mac)
+
+        info['type'] = iface.attrib['type']
+        info['mac'] = iface.mac.get('address')
+        if iface.find("model") is not None:
+            info['model'] = iface.model.get('type')
+        if info['type'] == 'network':
+            info['network'] = iface.source.get('network')
+        if info['type'] == 'bridge':
+            info['bridge'] = iface.source.get('bridge')
+
+        return info
+
+    def delete(self, vm, mac):
+        dom = VMModel.get_vm(vm, self.conn)
+        iface = self._get_vmiface(vm, mac)
+
+        if DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+            raise InvalidOperation("do not support hot plugging detach "
+                                   "guest interface")
+        if iface is None:
+            raise NotFoundError('iface: "%s"' % mac)
+
+        dom.detachDeviceFlags(etree.tostring(iface),
+                              libvirt.VIR_DOMAIN_AFFECT_CURRENT)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
new file mode 100644
index 0000000..e62f886
--- /dev/null
+++ b/src/kimchi/model/vms.py
@@ -0,0 +1,450 @@
+#
+# 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 os
+import time
+import uuid
+from xml.etree import ElementTree
+
+import libvirt
+from cherrypy.process.plugins import BackgroundTask
+
+from kimchi import vnc
+from kimchi import xmlutils
+from kimchi.exception import InvalidOperation, InvalidParameter
+from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.model.config import CapabilitiesModel
+from kimchi.model.templates import TemplateModel
+from kimchi.model.utils import get_vm_name
+from kimchi.screenshot import VMScreenshot
+from kimchi.utils import template_name_from_uri
+
+
+DOM_STATE_MAP = {0: 'nostate',
+                 1: 'running',
+                 2: 'blocked',
+                 3: 'paused',
+                 4: 'shutdown',
+                 5: 'shutoff',
+                 6: 'crashed'}
+
+GUESTS_STATS_INTERVAL = 5
+VM_STATIC_UPDATE_PARAMS = {'name': './name'}
+VM_LIVE_UPDATE_PARAMS = {}
+
+stats = {}
+
+
+class VMsModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.objstore = kargs['objstore']
+        self.caps = CapabilitiesModel()
+        self.guests_stats_thread = BackgroundTask(GUESTS_STATS_INTERVAL,
+                                                  self._update_guests_stats)
+        self.guests_stats_thread.start()
+
+    def _update_guests_stats(self):
+        vm_list = self.get_list()
+
+        for name in vm_list:
+            dom = VMModel.get_vm(name, self.conn)
+            vm_uuid = dom.UUIDString()
+            info = dom.info()
+            state = DOM_STATE_MAP[info[0]]
+
+            if state != 'running':
+                stats[vm_uuid] = {}
+                continue
+
+            if stats.get(vm_uuid, None) is None:
+                stats[vm_uuid] = {}
+
+            timestamp = time.time()
+            prevStats = stats.get(vm_uuid, {})
+            seconds = timestamp - prevStats.get('timestamp', 0)
+            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_percentage_cpu_usage(self, vm_uuid, info, seconds):
+        prevCpuTime = 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))
+
+        stats[vm_uuid].update({'cputime': info[4], 'cpu': percentage})
+
+    def _get_network_io_rate(self, vm_uuid, dom, seconds):
+        prevNetRxKB = stats[vm_uuid].get('netRxKB', 0)
+        prevNetTxKB = stats[vm_uuid].get('netTxKB', 0)
+        currentMaxNetRate = 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)
+
+        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 = stats[vm_uuid].get('diskRdKB', 0)
+        prevDiskWrKB = stats[vm_uuid].get('diskWrKB', 0)
+        currentMaxDiskRate = 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)
+
+        stats[vm_uuid].update({'disk_io': rate,
+                               'max_disk_io': max_disk_io,
+                               'diskRdKB': diskRdKB,
+                               'diskWrKB': diskWrKB})
+
+    def create(self, params):
+        conn = self.conn.get()
+        t_name = template_name_from_uri(params['template'])
+        vm_uuid = str(uuid.uuid4())
+        vm_list = self.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 = TemplateModel.get_template(t_name, self.objstore, self.conn,
+                                       vm_overrides)
+
+        if not self.caps.qemu_stream and t.info.get('iso_stream', False):
+            err = "Remote ISO image is not supported by this server."
+            raise InvalidOperation(err)
+
+        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.caps.libvirt_stream_protocols) == 0:
+            libvirt_stream = True
+
+        graphics = params.get('graphics')
+        xml = t.to_vm_xml(name, vm_uuid,
+                          libvirt_stream=libvirt_stream,
+                          qemu_stream_dns=self.caps.qemu_stream_dns,
+                          graphics=graphics)
+
+        try:
+            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 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)
+
+
+class VMModel(object):
+    def __init__(self, **kargs):
+        self.conn = kargs['conn']
+        self.objstore = kargs['objstore']
+        self.vms = VMsModel(**kargs)
+        self.vmscreenshot = VMScreenshotModel(**kargs)
+
+    def update(self, name, params):
+        dom = self.get_vm(name, self.conn)
+        dom = self._static_vm_update(dom, params)
+        self._live_vm_update(dom, params)
+        return dom.name()
+
+    def _static_vm_update(self, dom, params):
+        state = 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:
+                xpath = VM_STATIC_UPDATE_PARAMS[key]
+                new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
+
+        try:
+            if 'name' in params:
+                if state == 'running':
+                    err = "VM name only can be updated when vm is powered off."
+                    raise InvalidParameter(err)
+                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 lookup(self, name):
+        dom = self.get_vm(name, self.conn)
+        info = dom.info()
+        state = DOM_STATE_MAP[info[0]]
+        screenshot = None
+        graphics = self._vm_get_graphics(name)
+        graphics_type, graphics_listen, graphics_port = graphics
+        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
+                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 = stats.get(dom.UUIDString(), {})
+        res = {}
+        res['cpu_utilization'] = vm_stats.get('cpu', 0)
+        res['net_throughput'] = vm_stats.get('net_io', 0)
+        res['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
+        res['io_throughput'] = vm_stats.get('disk_io', 0)
+        res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
+
+        return {'state': state,
+                'stats': str(res),
+                '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_exists(self, name):
+        try:
+            self.get_vm(name, self.conn)
+            return True
+        except NotFoundError:
+            return False
+        except Exception, e:
+            err = "Unable to retrieve VM '%s': %s"
+            raise OperationFailed(err % (name, e.message))
+
+    @staticmethod
+    def get_vm(name, conn):
+        conn = 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 delete(self, name):
+        if self._vm_exists(name):
+            conn = self.conn.get()
+            dom = self.get_vm(name, self.conn)
+            self._vmscreenshot_delete(dom.UUIDString())
+            paths = self._vm_get_disk_paths(dom)
+            info = self.lookup(name)
+
+            if info['state'] == 'running':
+                self.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 start(self, name):
+        dom = self.get_vm(name, self.conn)
+        dom.create()
+
+    def stop(self, name):
+        if self._vm_exists(name):
+            dom = self.get_vm(name, self.conn)
+            dom.destroy()
+
+    def _vm_get_graphics(self, name):
+        dom = self.get_vm(name, self.conn)
+        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 connect(self, name):
+        graphics = self._vm_get_graphics(name)
+        graphics_type, graphics_listen, graphics_port = graphics
+        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 _vmscreenshot_delete(self, vm_uuid):
+        screenshot = VMScreenshotModel.get_screenshot(vm_uuid, self.objstore,
+                                                      self.conn)
+        screenshot.delete()
+        with self.objstore as session:
+            session.delete('screenshot', vm_uuid)
+
+
+class VMScreenshotModel(object):
+    def __init__(self, **kargs):
+        self.objstore = kargs['objstore']
+        self.conn = kargs['conn']
+
+    def lookup(self, name):
+        dom = VMModel.get_vm(name, self.conn)
+        d_info = dom.info()
+        vm_uuid = dom.UUIDString()
+        if DOM_STATE_MAP[d_info[0]] != 'running':
+            raise NotFoundError('No screenshot for stopped vm')
+
+        screenshot = self.get_screenshot(vm_uuid, self.objstore, 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
+
+    @staticmethod
+    def get_screenshot(vm_uuid, objstore, conn):
+        with 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, conn)
+
+
+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)
+            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_/__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 933e37f..0000000
--- a/src/kimchi/model_/config.py
+++ /dev/null
@@ -1,87 +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 cherrypy
-
-from kimchi.basemodel import Singleton
-from kimchi.distroloader import DistroLoader
-from kimchi.exception import NotFoundError
-from kimchi.featuretests import FeatureTests
-from kimchi.model_.debugreports import DebugReportsModel
-from kimchi.screenshot import VMScreenshot
-from kimchi.utils import kimchi_log
-
-
-class CapabilitiesModel(object):
-    __metaclass__ = Singleton
-
-    def __init__(self, **kargs):
-        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)
-
-    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.nfs_target_probe = FeatureTests.libvirt_support_nfs_probe()
-
-        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 lookup(self, *ident):
-        report_tool = DebugReportsModel.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)}
-
-
-class DistrosModel(object):
-    def __init__(self, **kargs):
-        distroloader = DistroLoader()
-        self.distros = distroloader.get()
-
-    def get_list(self):
-        return self.distros.keys()
-
-
-class DistroModel(object):
-    def __init__(self, **kargs):
-        self._distros = DistrosModel()
-
-    def lookup(self, name):
-        try:
-            return self._distros.distros[name]
-        except KeyError:
-            raise NotFoundError("Distro '%s' not found." % name)
diff --git a/src/kimchi/model_/debugreports.py b/src/kimchi/model_/debugreports.py
deleted file mode 100644
index 39402aa..0000000
--- a/src/kimchi/model_/debugreports.py
+++ /dev/null
@@ -1,167 +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 fnmatch
-import glob
-import logging
-import os
-import shutil
-import subprocess
-import time
-
-from kimchi import config
-from kimchi.exception import NotFoundError, OperationFailed
-from kimchi.model_.tasks import TaskModel
-from kimchi.utils import add_task, kimchi_log
-
-
-class DebugReportsModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-        self.task = TaskModel(**kargs)
-
-    def create(self, params):
-        ident = params['name']
-        taskid = self._gen_debugreport_file(ident)
-        return self.task.lookup(taskid)
-
-    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
-
-    def _gen_debugreport_file(self, name):
-        gen_cmd = self.get_system_report_tool()
-
-        if gen_cmd is not None:
-            return add_task('', gen_cmd, self.objstore, name)
-
-        raise OperationFailed("debugreport tool not found")
-
-    @staticmethod
-    def sosreport_generate(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)
-
-    @staticmethod
-    def get_system_report_tool():
-        # Please add new possible debug report command here
-        # and implement the report generating function
-        # based on the new report command
-        report_tools = ({'cmd': 'sosreport --help',
-                         'fn': DebugReportsModel.sosreport_generate},)
-
-        # check if the command can be found by shell one by one
-        for helper_tool in 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
-
-
-class DebugReportModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def 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 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)
-
-
-class DebugReportContentModel(object):
-    def __init__(self, **kargs):
-        self._debugreport = DebugReportModel()
-
-    def lookup(self, name):
-        return self._debugreport.lookup(name)
diff --git a/src/kimchi/model_/host.py b/src/kimchi/model_/host.py
deleted file mode 100644
index d5f92ef..0000000
--- a/src/kimchi/model_/host.py
+++ /dev/null
@@ -1,201 +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 os
-import time
-import platform
-from collections import defaultdict
-
-import psutil
-from cherrypy.process.plugins import BackgroundTask
-
-from kimchi import disks
-from kimchi import netinfo
-from kimchi.basemodel import Singleton
-from kimchi.exception import NotFoundError, OperationFailed
-from kimchi.model_.vms import DOM_STATE_MAP
-from kimchi.utils import kimchi_log
-
-
-HOST_STATS_INTERVAL = 1
-
-
-class HostModel(object):
-    def __init__(self, **kargs):
-        self.host_info = self._get_host_info()
-
-    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 lookup(self, *name):
-        return self.host_info
-
-    def 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 reboot(self, args=None):
-        # Find running VMs
-        running_vms = self._get_vms_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')
-
-    def _get_vms_list_by_state(self, state):
-        ret_list = []
-        for name in self.vms_get_list():
-            info = self._get_vm(name).info()
-            if (DOM_STATE_MAP[info[0]]) == state:
-                ret_list.append(name)
-        return ret_list
-
-
-class HostStatsModel(object):
-    __metaclass__ = Singleton
-
-    def __init__(self, **kargs):
-        self.host_stats = defaultdict(int)
-        self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
-                                                self._update_host_stats)
-        self.host_stats_thread.start()
-
-    def 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 _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})
-
-
-class PartitionsModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def get_list(self):
-        result = disks.get_partitions_names()
-        return result
-
-
-class PartitionModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def 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)
diff --git a/src/kimchi/model_/interfaces.py b/src/kimchi/model_/interfaces.py
deleted file mode 100644
index b3b6ccd..0000000
--- a/src/kimchi/model_/interfaces.py
+++ /dev/null
@@ -1,46 +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
-
-from kimchi import netinfo
-from kimchi.exception import NotFoundError
-from kimchi.model_.networks import NetworksModel
-
-
-class InterfacesModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.networks = NetworksModel(**kargs)
-
-    def get_list(self):
-        return list(set(netinfo.all_favored_interfaces()) -
-                    set(self.networks.get_all_networks_interfaces()))
-
-
-class InterfaceModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def lookup(self, name):
-        try:
-            return netinfo.get_interface_info(name)
-        except ValueError, e:
-            raise NotFoundError(e)
diff --git a/src/kimchi/model_/libvirtconnection.py b/src/kimchi/model_/libvirtconnection.py
deleted file mode 100644
index 7bbb668..0000000
--- a/src/kimchi/model_/libvirtconnection.py
+++ /dev/null
@@ -1,122 +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 threading
-import time
-
-import cherrypy
-import libvirt
-
-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:
-                            err = 'Libvirt is not available, exiting.'
-                            kimchi_log.error(err)
-                            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 f4dbf2e..0000000
--- a/src/kimchi/model_/libvirtstoragepool.py
+++ /dev/null
@@ -1,257 +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 os
-import tempfile
-
-import libvirt
-
-from kimchi.exception import InvalidParameter, OperationFailed, TimeoutExpired
-from kimchi.iscsi import TargetClient
-from kimchi.rollbackcontext import RollbackContext
-from kimchi.utils import parse_cmd_output, run_command
-
-
-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):
-        mnt_point = tempfile.mkdtemp(dir='/tmp')
-        export_path = "%s:%s" % (
-            self.poolArgs['source']['host'], self.poolArgs['source']['path'])
-        mount_cmd = ["mount", "-o", 'soft,timeo=100,retrans=3,retry=0',
-                     export_path, mnt_point]
-        umount_cmd = ["umount", "-f", export_path]
-        mounted = False
-
-        with RollbackContext() as rollback:
-            rollback.prependDefer(os.rmdir, mnt_point)
-            try:
-                run_command(mount_cmd, 30)
-                rollback.prependDefer(run_command, umount_cmd)
-            except TimeoutExpired:
-                err = "Export path %s may block during nfs mount"
-                raise InvalidParameter(err % export_path)
-
-            with open("/proc/mounts", "rb") as f:
-                rawMounts = f.read()
-            output_items = ['dev_path', 'mnt_point', 'type']
-            mounts = parse_cmd_output(rawMounts, output_items)
-            for item in mounts:
-                if 'dev_path' in item and item['dev_path'] == export_path:
-                    mounted = True
-
-            if not mounted:
-                err = "Export path %s mount failed during nfs mount"
-                raise InvalidParameter(err % export_path)
-
-    @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_/model.py b/src/kimchi/model_/model.py
deleted file mode 100644
index 709e0bb..0000000
--- a/src/kimchi/model_/model.py
+++ /dev/null
@@ -1,53 +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 inspect
-import os
-
-from kimchi.basemodel import BaseModel
-from kimchi.model_.libvirtconnection import LibvirtConnection
-from kimchi.objectstore import ObjectStore
-from kimchi.utils import import_module, listPathModules
-
-
-class Model(BaseModel):
-    def __init__(self, libvirt_uri='qemu:///system', objstore_loc=None):
-        self.objstore = ObjectStore(objstore_loc)
-        self.conn = LibvirtConnection(libvirt_uri)
-        kargs = {'objstore': self.objstore, 'conn': self.conn}
-
-        this = os.path.basename(__file__)
-        this_mod = os.path.splitext(this)[0]
-
-        models = []
-        for mod_name in listPathModules(os.path.dirname(__file__)):
-            if mod_name.startswith("_") or mod_name == this_mod:
-                continue
-
-            module = import_module('model_.' + mod_name)
-            members = inspect.getmembers(module, inspect.isclass)
-            for cls_name, instance in members:
-                if inspect.getmodule(instance) == module:
-                    if cls_name.endswith('Model'):
-                        models.append(instance(**kargs))
-
-        return super(Model, self).__init__(models)
diff --git a/src/kimchi/model_/networks.py b/src/kimchi/model_/networks.py
deleted file mode 100644
index b164141..0000000
--- a/src/kimchi/model_/networks.py
+++ /dev/null
@@ -1,265 +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 ipaddr
-import libvirt
-
-from kimchi import netinfo
-from kimchi import network as knetwork
-from kimchi import networkxml
-from kimchi import xmlutils
-from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
-
-
-class NetworksModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def create(self, params):
-        conn = self.conn.get()
-        name = params['name']
-        if name in self.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 get_list(self):
-        conn = self.conn.get()
-        return sorted(conn.listNetworks() + conn.listDefinedNetworks())
-
-    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():
-                network = self._get_network(net_name)
-                xml = network.XMLDesc(0)
-                subnet = NetworkModel.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({'net': 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 get_all_networks_interfaces(self):
-        net_names = self.get_list()
-        interfaces = []
-        for name in net_names:
-            conn = self.conn.get()
-            network = conn.networkLookupByName(name)
-            xml = network.XMLDesc(0)
-            net_dict = NetworkModel.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 _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
-
-
-class NetworkModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def 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 _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 _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 activate(self, name):
-        network = self._get_network(name)
-        network.create()
-
-    def deactivate(self, name):
-        network = self._get_network(name)
-        network.destroy()
-
-    def 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_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()))
-
-    @staticmethod
-    def get_network_from_xml(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 _remove_vlan_tagged_bridge(self, network):
-        try:
-            bridge = network.bridgeName()
-        except libvirt.libvirtError:
-            pass
-        else:
-            if bridge.startswith('kimchi-'):
-                conn = self.conn.get()
-                iface = conn.interfaceLookupByName(bridge)
-                if iface.isActive():
-                    iface.destroy()
-                iface.undefine()
diff --git a/src/kimchi/model_/plugins.py b/src/kimchi/model_/plugins.py
deleted file mode 100644
index d6756d0..0000000
--- a/src/kimchi/model_/plugins.py
+++ /dev/null
@@ -1,31 +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
-
-from kimchi.utils import get_enabled_plugins
-
-
-class PluginsModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def get_list(self):
-        return [plugin for (plugin, config) in get_enabled_plugins()]
diff --git a/src/kimchi/model_/storagepools.py b/src/kimchi/model_/storagepools.py
deleted file mode 100644
index 2fca8e4..0000000
--- a/src/kimchi/model_/storagepools.py
+++ /dev/null
@@ -1,246 +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 libvirt
-
-from kimchi import xmlutils
-from kimchi.scan import Scanner
-from kimchi.exception import InvalidOperation, MissingParameter
-from kimchi.exception import NotFoundError, OperationFailed
-from kimchi.model_.libvirtstoragepool import StoragePoolDef
-from kimchi.utils import add_task, kimchi_log
-
-
-ISO_POOL_NAME = u'kimchi_isos'
-POOL_STATE_MAP = {0: 'inactive',
-                  1: 'initializing',
-                  2: 'active',
-                  3: 'degraded',
-                  4: 'inaccessible'}
-
-STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
-                             'path': '/pool/source/dir/@path'}}
-
-
-class StoragePoolsModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.objstore = kargs['objstore']
-        self.scanner = Scanner(self._clean_scan)
-        self.scanner.delete()
-
-    def 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 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.get_list():
-            err = "The name %s has been used by a pool"
-            raise InvalidOperation(err % 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 _clean_scan(self, pool_name):
-        try:
-            conn = self.conn.get()
-            pool = conn.storagePoolLookupByName(pool_name)
-            pool.destroy()
-            with self.objstore as session:
-                session.delete('scanning', pool_name)
-        except Exception, e:
-            err = "Exception %s occured when cleaning scan result"
-            kimchi_log.debug(err % 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_list():
-            try:
-                res = self.storagepool_lookup(pool)
-                if res['state'] == 'active':
-                    scan_params['ignore_list'].append(res['path'])
-            except Exception, e:
-                err = "Exception %s occured when get ignore path"
-                kimchi_log.debug(err % e.message)
-
-        params['path'] = self.scanner.scan_dir_prepare(params['name'])
-        scan_params['pool_path'] = params['path']
-        task_id = add_task('', self.scanner.start_scan, self.objstore,
-                           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
-
-
-class StoragePoolModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.objstore = kargs['objstore']
-
-    @staticmethod
-    def get_storagepool(name, conn):
-        conn = 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 _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 lookup(self, name):
-        pool = self.get_storagepool(name, self.conn)
-        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': 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 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, self.conn)
-        if autostart:
-            pool.setAutostart(1)
-        else:
-            pool.setAutostart(0)
-        ident = pool.name()
-        return ident
-
-    def activate(self, name):
-        pool = self.get_storagepool(name, self.conn)
-        try:
-            pool.create(0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def deactivate(self, name):
-        pool = self.get_storagepool(name, self.conn)
-        try:
-            pool.destroy()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-    def delete(self, name):
-        pool = self.get_storagepool(name, self.conn)
-        if pool.isActive():
-            err = "Unable to delete the active storagepool %s"
-            raise InvalidOperation(err % name)
-        try:
-            pool.undefine()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-
-class IsoPoolModel(object):
-    def __init__(self, **kargs):
-        pass
-
-    def lookup(self, name):
-        return {'state': 'active',
-                'type': 'kimchi-iso'}
diff --git a/src/kimchi/model_/storageservers.py b/src/kimchi/model_/storageservers.py
deleted file mode 100644
index 1728394..0000000
--- a/src/kimchi/model_/storageservers.py
+++ /dev/null
@@ -1,78 +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
-
-from kimchi.exception import NotFoundError
-from kimchi.model_.storagepools import StoragePoolModel, STORAGE_SOURCES
-
-
-class StorageServersModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.pool = StoragePoolModel(**kargs)
-
-    def get_list(self, _target_type=None):
-        if not _target_type:
-            target_type = STORAGE_SOURCES.keys()
-        else:
-            target_type = [_target_type]
-        pools = self.pools.get_list()
-
-        conn = self.conn.get()
-        pools = conn.listStoragePools()
-        pools += conn.listDefinedStoragePools()
-
-        server_list = []
-        for pool in pools:
-            try:
-                pool_info = self.pool.lookup(pool)
-                if (pool_info['type'] in target_type and
-                        pool_info['source']['addr'] not in server_list):
-                    # Avoid to add same server for multiple times
-                    # if it hosts more than one storage type
-                    server_list.append(pool_info['source']['addr'])
-            except NotFoundError:
-                pass
-
-        return server_list
-
-
-class StorageServerModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.pool = StoragePoolModel(**kargs)
-
-    def lookup(self, server):
-        conn = self.conn.get()
-        pools = conn.listStoragePools()
-        pools += conn.listDefinedStoragePools()
-        for pool in pools:
-            try:
-                pool_info = self.pool.lookup(pool)
-                if pool_info['source'] and \
-                   pool_info['source']['addr'] == server:
-                    return dict(host=server)
-            except NotFoundError:
-                # Avoid inconsistent pool result because of lease between list
-                # lookup
-                pass
-
-        raise NotFoundError('server %s does not used by kimchi' % server)
diff --git a/src/kimchi/model_/storagetargets.py b/src/kimchi/model_/storagetargets.py
deleted file mode 100644
index be73732..0000000
--- a/src/kimchi/model_/storagetargets.py
+++ /dev/null
@@ -1,86 +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 libvirt
-import lxml.etree as ET
-from lxml import objectify
-from lxml.builder import E
-
-from kimchi.model_.config import CapabilitiesModel
-from kimchi.model_.storagepools import STORAGE_SOURCES
-from kimchi.utils import kimchi_log, patch_find_nfs_target
-
-
-class StorageTargetsModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.caps = CapabilitiesModel()
-
-    def get_list(self, storage_server, _target_type=None):
-        target_list = list()
-
-        if not _target_type:
-            target_types = STORAGE_SOURCES.keys()
-        else:
-            target_types = [_target_type]
-
-        for target_type in target_types:
-            if not self.caps.nfs_target_probe and target_type == 'netfs':
-                targets = patch_find_nfs_target(storage_server)
-            else:
-                xml = self._get_storage_server_spec(server=storage_server,
-                                                    target_type=target_type)
-                conn = self.conn.get()
-                try:
-                    ret = conn.findStoragePoolSources(target_type, xml, 0)
-                except libvirt.libvirtError as e:
-                    err = "Query storage pool source fails because of %s"
-                    kimchi_log.warning(err, e.get_error_message())
-                    continue
-
-                targets = self._parse_target_source_result(target_type, ret)
-
-            target_list.extend(targets)
-        return target_list
-
-    def _get_storage_server_spec(**kwargs):
-        # Required parameters:
-        # server:
-        # target_type:
-        extra_args = []
-        if kwargs['target_type'] == 'netfs':
-            extra_args.append(E.format(type='nfs'))
-        obj = E.source(E.host(name=kwargs['server']), *extra_args)
-        xml = ET.tostring(obj)
-        return xml
-
-    def _parse_target_source_result(target_type, xml_str):
-        root = objectify.fromstring(xml_str)
-        ret = []
-        for source in root.getchildren():
-            if target_type == 'netfs':
-                host_name = source.host.get('name')
-                target_path = source.dir.get('path')
-                type = source.format.get('type')
-                ret.append(dict(host=host_name, target_type=type,
-                                target=target_path))
-        return ret
diff --git a/src/kimchi/model_/storagevolumes.py b/src/kimchi/model_/storagevolumes.py
deleted file mode 100644
index 0edac52..0000000
--- a/src/kimchi/model_/storagevolumes.py
+++ /dev/null
@@ -1,176 +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 os
-
-import libvirt
-
-from kimchi import xmlutils
-from kimchi.exception import InvalidOperation, IsoFormatError
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
-from kimchi.isoinfo import IsoImage
-from kimchi.model_.storagepools import StoragePoolModel
-
-
-VOLUME_TYPE_MAP = {0: 'file',
-                   1: 'block',
-                   2: 'directory',
-                   3: 'network'}
-
-
-class StorageVolumesModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def create(self, pool, params):
-        vol_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>
-        """
-        params.setdefault('allocation', 0)
-        params.setdefault('format', 'qcow2')
-
-        try:
-            pool = StoragePoolModel.get_storagepool(pool, self.conn)
-            name = params['name']
-            xml = vol_xml % params
-        except KeyError, key:
-            raise MissingParameter(key)
-
-        try:
-            pool.createXML(xml, 0)
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-        return name
-
-    def get_list(self, pool):
-        pool = StoragePoolModel.get_storagepool(pool, self.conn)
-        if not pool.isActive():
-            err = "Unable to list volumes in inactive storagepool %s"
-            raise InvalidOperation(err % pool.name())
-        try:
-            pool.refresh(0)
-            return pool.listVolumes()
-        except libvirt.libvirtError as e:
-            raise OperationFailed(e.get_error_message())
-
-
-class StorageVolumeModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def _get_storagevolume(self, pool, name):
-        pool = StoragePoolModel.get_storagepool(pool, self.conn)
-        if not pool.isActive():
-            err = "Unable to list volumes in inactive storagepool %s"
-            raise InvalidOperation(err % 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 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=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 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 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 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())
-
-
-class IsoVolumesModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.storagevolume = StorageVolumeModel(**kargs)
-
-    def get_list(self):
-        iso_volumes = []
-        conn = self.conn.get()
-        pools = conn.listStoragePools()
-        pools += conn.listDefinedStoragePools()
-
-        for pool in pools:
-            try:
-                pool.refresh(0)
-                volumes = pool.listVolumes()
-            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
diff --git a/src/kimchi/model_/tasks.py b/src/kimchi/model_/tasks.py
deleted file mode 100644
index 40ca1d6..0000000
--- a/src/kimchi/model_/tasks.py
+++ /dev/null
@@ -1,39 +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
-
-
-class TasksModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-
-    def get_list(self):
-        with self.objstore as session:
-            return session.get_list('task')
-
-
-class TaskModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-
-    def lookup(self, id):
-        with self.objstore as session:
-            return session.get('task', str(id))
diff --git a/src/kimchi/model_/templates.py b/src/kimchi/model_/templates.py
deleted file mode 100644
index 03632a6..0000000
--- a/src/kimchi/model_/templates.py
+++ /dev/null
@@ -1,172 +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 import xmlutils
-from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
-from kimchi.utils import pool_name_from_uri
-from kimchi.vmtemplate import VMTemplate
-
-
-class TemplatesModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-        self.conn = kargs['conn']
-
-    def create(self, params):
-        name = params['name']
-        conn = self.conn.get()
-
-        pool_uri = params.get(u'storagepool', '')
-        if pool_uri:
-            pool_name = pool_name_from_uri(pool_uri)
-            try:
-                conn.storagePoolLookupByName(pool_name)
-            except Exception as e:
-                err = "Storagepool specified is not valid: %s."
-                raise InvalidParameter(err % e.message)
-
-        for net_name in params.get(u'networks', []):
-            try:
-                conn.networkLookupByName(net_name)
-            except Exception, e:
-                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 get_list(self):
-        with self.objstore as session:
-            return session.get_list('template')
-
-
-class TemplateModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-        self.conn = kargs['conn']
-        self.templates = TemplatesModel(**kargs)
-
-    @staticmethod
-    def get_template(name, objstore, conn, overrides=None):
-        with objstore as session:
-            params = session.get('template', name)
-        if overrides:
-            params.update(overrides)
-        return LibvirtVMTemplate(params, False, conn)
-
-    def lookup(self, name):
-        t = self.get_template(name, self.objstore, self.conn)
-        return t.info
-
-    def delete(self, name):
-        with self.objstore as session:
-            session.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
-
-        pool_uri = new_t.get(u'storagepool', '')
-        pool_name = pool_name_from_uri(pool_uri)
-        try:
-            conn = self.conn.get()
-            conn.storagePoolLookupByName(pool_name)
-        except Exception as e:
-            err = "Storagepool specified is not valid: %s."
-            raise InvalidParameter(err % e.message)
-
-        for net_name in params.get(u'networks', []):
-            try:
-                conn = self.conn.get()
-                conn.networkLookupByName(net_name)
-            except Exception, e:
-                raise InvalidParameter("Network '%s' specified by template "
-                                       "does not exist" % net_name)
-
-        self.delete(name)
-        try:
-            ident = self.templates.create(new_t)
-        except:
-            ident = self.templates.create(old_t)
-            raise
-        return ident
-
-
-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:
-            err = 'Storage specified by template does not exist'
-            raise InvalidParameter(err)
-
-        if not pool.isActive():
-            err = 'Storage specified by template is not active'
-            raise InvalidParameter(err)
-
-        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:
-                err = 'Network specified by template does not exist'
-                raise InvalidParameter(err)
-
-            if not network.isActive():
-                err = 'Network specified by template is not active'
-                raise InvalidParameter(err)
-
-    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
diff --git a/src/kimchi/model_/utils.py b/src/kimchi/model_/utils.py
deleted file mode 100644
index a27b867..0000000
--- a/src/kimchi/model_/utils.py
+++ /dev/null
@@ -1,33 +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
-
-from kimchi.exception import OperationFailed
-
-
-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")
diff --git a/src/kimchi/model_/vmifaces.py b/src/kimchi/model_/vmifaces.py
deleted file mode 100644
index 4ec0c7b..0000000
--- a/src/kimchi/model_/vmifaces.py
+++ /dev/null
@@ -1,135 +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 random
-
-import libvirt
-from lxml import etree, objectify
-from lxml.builder import E
-
-from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
-from kimchi.model_.vms import DOM_STATE_MAP, VMModel
-
-
-class VMIfacesModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def get_list(self, vm):
-        macs = []
-        for iface in self.get_vmifaces(vm, self.conn):
-            macs.append(iface.mac.get('address'))
-        return macs
-
-    def create(self, vm, params):
-        def randomMAC():
-            mac = [0x52, 0x54, 0x00,
-                   random.randint(0x00, 0x7f),
-                   random.randint(0x00, 0xff),
-                   random.randint(0x00, 0xff)]
-            return ':'.join(map(lambda x: "%02x" % x, mac))
-
-        conn = self.conn.get()
-        networks = conn.listNetworks() + conn.listDefinedNetworks()
-
-        if params["type"] == "network" and params["network"] not in networks:
-            raise InvalidParameter("%s is not an available network" %
-                                   params["network"])
-
-        dom = VMModel.get_vm(vm, self.conn)
-        if DOM_STATE_MAP[dom.info()[0]] != "shutoff":
-            raise InvalidOperation("do not support hot plugging attach "
-                                   "guest interface")
-
-        macs = (iface.mac.get('address')
-                for iface in self.get_vmifaces(vm, self.conn))
-
-        mac = randomMAC()
-        while True:
-            if mac not in macs:
-                break
-            mac = randomMAC()
-
-        children = [E.mac(address=mac)]
-        ("network" in params.keys() and
-         children.append(E.source(network=params['network'])))
-        ("model" in params.keys() and
-         children.append(E.model(type=params['model'])))
-        attrib = {"type": params["type"]}
-
-        xml = etree.tostring(E.interface(*children, **attrib))
-
-        dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)
-
-        return mac
-
-    @staticmethod
-    def get_vmifaces(vm, conn):
-        dom = VMModel.get_vm(vm, conn)
-        xml = dom.XMLDesc(0)
-        root = objectify.fromstring(xml)
-
-        return root.devices.findall("interface")
-
-
-class VMIfaceModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-
-    def _get_vmiface(self, vm, mac):
-        ifaces = VMIfacesModel.get_vmifaces(vm, self.conn)
-
-        for iface in ifaces:
-            if iface.mac.get('address') == mac:
-                return iface
-        return None
-
-    def lookup(self, vm, mac):
-        info = {}
-
-        iface = self._get_vmiface(vm, mac)
-        if iface is None:
-            raise NotFoundError('iface: "%s"' % mac)
-
-        info['type'] = iface.attrib['type']
-        info['mac'] = iface.mac.get('address')
-        if iface.find("model") is not None:
-            info['model'] = iface.model.get('type')
-        if info['type'] == 'network':
-            info['network'] = iface.source.get('network')
-        if info['type'] == 'bridge':
-            info['bridge'] = iface.source.get('bridge')
-
-        return info
-
-    def delete(self, vm, mac):
-        dom = VMModel.get_vm(vm, self.conn)
-        iface = self._get_vmiface(vm, mac)
-
-        if DOM_STATE_MAP[dom.info()[0]] != "shutoff":
-            raise InvalidOperation("do not support hot plugging detach "
-                                   "guest interface")
-        if iface is None:
-            raise NotFoundError('iface: "%s"' % mac)
-
-        dom.detachDeviceFlags(etree.tostring(iface),
-                              libvirt.VIR_DOMAIN_AFFECT_CURRENT)
diff --git a/src/kimchi/model_/vms.py b/src/kimchi/model_/vms.py
deleted file mode 100644
index d2ab292..0000000
--- a/src/kimchi/model_/vms.py
+++ /dev/null
@@ -1,450 +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 os
-import time
-import uuid
-from xml.etree import ElementTree
-
-import libvirt
-from cherrypy.process.plugins import BackgroundTask
-
-from kimchi import vnc
-from kimchi import xmlutils
-from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import NotFoundError, OperationFailed
-from kimchi.model_.config import CapabilitiesModel
-from kimchi.model_.templates import TemplateModel
-from kimchi.model_.utils import get_vm_name
-from kimchi.screenshot import VMScreenshot
-from kimchi.utils import template_name_from_uri
-
-
-DOM_STATE_MAP = {0: 'nostate',
-                 1: 'running',
-                 2: 'blocked',
-                 3: 'paused',
-                 4: 'shutdown',
-                 5: 'shutoff',
-                 6: 'crashed'}
-
-GUESTS_STATS_INTERVAL = 5
-VM_STATIC_UPDATE_PARAMS = {'name': './name'}
-VM_LIVE_UPDATE_PARAMS = {}
-
-stats = {}
-
-
-class VMsModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.objstore = kargs['objstore']
-        self.caps = CapabilitiesModel()
-        self.guests_stats_thread = BackgroundTask(GUESTS_STATS_INTERVAL,
-                                                  self._update_guests_stats)
-        self.guests_stats_thread.start()
-
-    def _update_guests_stats(self):
-        vm_list = self.get_list()
-
-        for name in vm_list:
-            dom = VMModel.get_vm(name, self.conn)
-            vm_uuid = dom.UUIDString()
-            info = dom.info()
-            state = DOM_STATE_MAP[info[0]]
-
-            if state != 'running':
-                stats[vm_uuid] = {}
-                continue
-
-            if stats.get(vm_uuid, None) is None:
-                stats[vm_uuid] = {}
-
-            timestamp = time.time()
-            prevStats = stats.get(vm_uuid, {})
-            seconds = timestamp - prevStats.get('timestamp', 0)
-            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_percentage_cpu_usage(self, vm_uuid, info, seconds):
-        prevCpuTime = 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))
-
-        stats[vm_uuid].update({'cputime': info[4], 'cpu': percentage})
-
-    def _get_network_io_rate(self, vm_uuid, dom, seconds):
-        prevNetRxKB = stats[vm_uuid].get('netRxKB', 0)
-        prevNetTxKB = stats[vm_uuid].get('netTxKB', 0)
-        currentMaxNetRate = 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)
-
-        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 = stats[vm_uuid].get('diskRdKB', 0)
-        prevDiskWrKB = stats[vm_uuid].get('diskWrKB', 0)
-        currentMaxDiskRate = 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)
-
-        stats[vm_uuid].update({'disk_io': rate,
-                               'max_disk_io': max_disk_io,
-                               'diskRdKB': diskRdKB,
-                               'diskWrKB': diskWrKB})
-
-    def create(self, params):
-        conn = self.conn.get()
-        t_name = template_name_from_uri(params['template'])
-        vm_uuid = str(uuid.uuid4())
-        vm_list = self.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 = TemplateModel.get_template(t_name, self.objstore, self.conn,
-                                       vm_overrides)
-
-        if not self.caps.qemu_stream and t.info.get('iso_stream', False):
-            err = "Remote ISO image is not supported by this server."
-            raise InvalidOperation(err)
-
-        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.caps.libvirt_stream_protocols) == 0:
-            libvirt_stream = True
-
-        graphics = params.get('graphics')
-        xml = t.to_vm_xml(name, vm_uuid,
-                          libvirt_stream=libvirt_stream,
-                          qemu_stream_dns=self.caps.qemu_stream_dns,
-                          graphics=graphics)
-
-        try:
-            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 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)
-
-
-class VMModel(object):
-    def __init__(self, **kargs):
-        self.conn = kargs['conn']
-        self.objstore = kargs['objstore']
-        self.vms = VMsModel(**kargs)
-        self.vmscreenshot = VMScreenshotModel(**kargs)
-
-    def update(self, name, params):
-        dom = self.get_vm(name, self.conn)
-        dom = self._static_vm_update(dom, params)
-        self._live_vm_update(dom, params)
-        return dom.name()
-
-    def _static_vm_update(self, dom, params):
-        state = 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:
-                xpath = VM_STATIC_UPDATE_PARAMS[key]
-                new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
-
-        try:
-            if 'name' in params:
-                if state == 'running':
-                    err = "VM name only can be updated when vm is powered off."
-                    raise InvalidParameter(err)
-                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 lookup(self, name):
-        dom = self.get_vm(name, self.conn)
-        info = dom.info()
-        state = DOM_STATE_MAP[info[0]]
-        screenshot = None
-        graphics = self._vm_get_graphics(name)
-        graphics_type, graphics_listen, graphics_port = graphics
-        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
-                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 = stats.get(dom.UUIDString(), {})
-        res = {}
-        res['cpu_utilization'] = vm_stats.get('cpu', 0)
-        res['net_throughput'] = vm_stats.get('net_io', 0)
-        res['net_throughput_peak'] = vm_stats.get('max_net_io', 100)
-        res['io_throughput'] = vm_stats.get('disk_io', 0)
-        res['io_throughput_peak'] = vm_stats.get('max_disk_io', 100)
-
-        return {'state': state,
-                'stats': str(res),
-                '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_exists(self, name):
-        try:
-            self.get_vm(name, self.conn)
-            return True
-        except NotFoundError:
-            return False
-        except Exception, e:
-            err = "Unable to retrieve VM '%s': %s"
-            raise OperationFailed(err % (name, e.message))
-
-    @staticmethod
-    def get_vm(name, conn):
-        conn = 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 delete(self, name):
-        if self._vm_exists(name):
-            conn = self.conn.get()
-            dom = self.get_vm(name, self.conn)
-            self._vmscreenshot_delete(dom.UUIDString())
-            paths = self._vm_get_disk_paths(dom)
-            info = self.lookup(name)
-
-            if info['state'] == 'running':
-                self.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 start(self, name):
-        dom = self.get_vm(name, self.conn)
-        dom.create()
-
-    def stop(self, name):
-        if self._vm_exists(name):
-            dom = self.get_vm(name, self.conn)
-            dom.destroy()
-
-    def _vm_get_graphics(self, name):
-        dom = self.get_vm(name, self.conn)
-        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 connect(self, name):
-        graphics = self._vm_get_graphics(name)
-        graphics_type, graphics_listen, graphics_port = graphics
-        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 _vmscreenshot_delete(self, vm_uuid):
-        screenshot = VMScreenshotModel.get_screenshot(vm_uuid, self.objstore,
-                                                      self.conn)
-        screenshot.delete()
-        with self.objstore as session:
-            session.delete('screenshot', vm_uuid)
-
-
-class VMScreenshotModel(object):
-    def __init__(self, **kargs):
-        self.objstore = kargs['objstore']
-        self.conn = kargs['conn']
-
-    def lookup(self, name):
-        dom = VMModel.get_vm(name, self.conn)
-        d_info = dom.info()
-        vm_uuid = dom.UUIDString()
-        if DOM_STATE_MAP[d_info[0]] != 'running':
-            raise NotFoundError('No screenshot for stopped vm')
-
-        screenshot = self.get_screenshot(vm_uuid, self.objstore, 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
-
-    @staticmethod
-    def get_screenshot(vm_uuid, objstore, conn):
-        with 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, conn)
-
-
-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)
-            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/server.py b/src/kimchi/server.py
index 9cc4c3c..90b2a27 100644
--- a/src/kimchi/server.py
+++ b/src/kimchi/server.py
@@ -30,7 +30,7 @@ import sslcert
 
 from kimchi import auth
 from kimchi import config
-from kimchi.model_ import model
+from kimchi.model import model
 from kimchi import mockmodel
 from kimchi import vnc
 from kimchi.control import sub_nodes
diff --git a/tests/test_model.py b/tests/test_model.py
index 74f3dd9..51b216c 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -38,7 +38,7 @@ from kimchi import netinfo
 from kimchi.exception import InvalidOperation, InvalidParameter
 from kimchi.exception import NotFoundError, OperationFailed
 from kimchi.iscsi import TargetClient
-from kimchi.model_ import model
+from kimchi.model import model
 from kimchi.rollbackcontext import RollbackContext
 from kimchi.utils import add_task
 
diff --git a/tests/test_storagepool.py b/tests/test_storagepool.py
index 869b608..a3f4983 100644
--- a/tests/test_storagepool.py
+++ b/tests/test_storagepool.py
@@ -24,7 +24,7 @@ import libxml2
 import unittest
 
 
-from kimchi.model_.libvirtstoragepool import StoragePoolDef
+from kimchi.model.libvirtstoragepool import StoragePoolDef
 from kimchi.rollbackcontext import RollbackContext
 
 
-- 
1.7.10.4




More information about the Kimchi-devel mailing list