[Kimchi-devel] [PATCH 08/13] refactor model: Create a separated model for storagepool resource

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


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

To avoid duplicating code in model and mockmodel, the code related to
storagepool resource was added to model_/storagepools.py and
the specific code for each backend (libvirt or mock) was added to
model_/libvirtbackend.py and model_/mockbackend.py

Signed-off-by: Aline Manera <alinefm at br.ibm.com>
---
 src/kimchi/model_/libvirtbackend.py |  171 ++++++++++++++++++++++++++++++++++-
 src/kimchi/model_/mockbackend.py    |   51 +++++++++++
 src/kimchi/model_/storagepools.py   |   86 ++++++++++++++++++
 3 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 src/kimchi/model_/storagepools.py

diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py
index 4ab4db6..28bfc01 100644
--- a/src/kimchi/model_/libvirtbackend.py
+++ b/src/kimchi/model_/libvirtbackend.py
@@ -36,18 +36,33 @@ from cherrypy.process.plugins import BackgroundTask
 from collections import defaultdict
 
 from kimchi import config
+from kimchi import xmlutils
 from kimchi.asynctask import AsyncTask
 from kimchi.featuretests import FeatureTests
 from kimchi.exception import OperationFailed
+from kimchi.model_.libvirtconnection import LibvirtConnection
+from kimchi.model_.libvirtstoragepool import StoragePoolDef
 from kimchi.objectstore import ObjectStore
+from kimchi.scan import Scanner
 from kimchi.utils import kimchi_log
 
 HOST_STATS_INTERVAL = 1
+STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
+                             'path': '/pool/source/dir/@path'}}
 
 class LibvirtBackend(object):
-    def __init__(self, objstore_loc=None):
+    pool_state_map = {0: 'inactive',
+                      1: 'initializing',
+                      2: 'active',
+                      3: 'degraded',
+                      4: 'inaccessible'}
+
+    def __init__(self, libvirt_uri=None, objstore_loc=None):
+        self.libvirt_uri = libvirt_uri or 'qemu:///system'
+        self.conn = LibvirtConnection(self.libvirt_uri)
         self.objstore = ObjectStore(objstore_loc)
         self.next_taskid = 1
+        self.scanner = Scanner(self._clean_scan)
         self.host_stats = defaultdict(int)
         self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
                                                 self._update_host_stats)
@@ -228,3 +243,157 @@ class LibvirtBackend(object):
         res['os_codename'] = unicode(codename,"utf-8")
 
         return res
+
+    def get_storagepools(self):
+        try:
+            conn = self.conn.get()
+            names = conn.listStoragePools()
+            names += conn.listDefinedStoragePools()
+            return names
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _clean_scan(self, pool_name):
+        try:
+            self.deactivate_storagepool(pool_name)
+            with self.objstore as session:
+                session.delete('scanning', pool_name)
+        except Exception, e:
+            kimchi_log.debug("Error while cleaning deep scan results" %
+                             e.message)
+
+    def do_deep_scan(self, params):
+        scan_params = dict(ignore_list=[])
+        scan_params['scan_path'] = params['path']
+        params['type'] = 'dir'
+
+        for pool in self.get_storagepools():
+            try:
+                res = self.get_storagepool_by_name(pool)
+                if res['state'] == 'active':
+                    scan_params['ignore_list'].append(res['path'])
+            except Exception, e:
+                kimchi_log.debug("Error while preparing for deep scan: %s" %
+                                 e.message)
+
+        params['path']  = self.scanner.scan_dir_prepare(params['name'])
+        scan_params['pool_path'] = params['path']
+        task_id = self.add_task('', self.scanner.start_scan, scan_params)
+        # Record scanning-task/storagepool mapping for future querying
+        with self.objstore as session:
+                session.store('scanning', params['name'], task_id)
+        return task_id
+
+    def create_storagepool(self, params):
+        conn = self.conn.get()
+        try:
+            poolDef = StoragePoolDef.create(params)
+            poolDef.prepare(conn)
+            xml = poolDef.xml
+        except KeyError, key:
+            raise MissingParameter("You need to specify '%s' in order to "
+                                   "create storage pool" % key)
+
+        try:
+            if task_id:
+                # Create transient pool for deep scan
+                conn.storagePoolCreateXML(xml, 0)
+                return name
+
+            pool = conn.storagePoolDefineXML(xml, 0)
+            if params['type'] in ['logical', 'dir', 'netfs']:
+                pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW)
+                # autostart dir and logical storage pool created from kimchi
+                pool.setAutostart(1)
+            else:
+                # disable autostart for others
+                pool.setAutostart(0)
+        except libvirt.libvirtError as e:
+            msg = "Problem creating Storage Pool: %s"
+            kimchi_log.error(msg, e)
+            raise OperationFailed(e.get_error_message())
+
+    def get_storagepool_by_name(self, name):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(name)
+        info = pool.info()
+        nr_volumes = self._get_storagepool_vols_num(pool)
+        autostart = True if pool.autostart() else False
+        xml = pool.XMLDesc(0)
+        path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
+        pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0]
+        source = self._get_storage_source(pool_type, xml)
+        res = {'state': self.pool_state_map[info[0]],
+               'path': path,
+               'source': source,
+               'type': pool_type,
+               'autostart': autostart,
+               'capacity': info[1],
+               'allocated': info[2],
+               'available': info[3],
+               'nr_volumes': nr_volumes}
+
+        if not pool.isPersistent():
+            # Deal with deep scan generated pool
+            try:
+                with self.objstore as session:
+                    task_id = session.get('scanning', name)
+                res['task_id'] = str(task_id)
+                res['type'] = 'kimchi-iso'
+            except NotFoundError:
+                # User created normal pool
+                pass
+        return res
+
+    def _get_storagepool_vols_num(self, pool):
+        try:
+            if pool.isActive():
+                pool.refresh(0)
+                return pool.numOfVolumes()
+            else:
+                return 0
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _get_storage_source(self, pool_type, pool_xml):
+        source = {}
+        if pool_type not in STORAGE_SOURCES:
+            return source
+
+        for key, val in STORAGE_SOURCES[pool_type].items():
+            res = xmlutils.xpath_get_text(pool_xml, val)
+            source[key] = res[0] if len(res) == 1 else res
+
+        return source
+
+    def activate_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.create(0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def deactivate_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.destroy()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def delete_storagepool(self, name):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(name)
+            pool.undefine()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def autostart_storagepool(self, name, value):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(name)
+        if autostart:
+            pool.setAutostart(1)
+        else:
+            pool.setAutostart(0)
diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py
index 5fb332e..01f4c22 100644
--- a/src/kimchi/model_/mockbackend.py
+++ b/src/kimchi/model_/mockbackend.py
@@ -21,6 +21,7 @@
 # License along with this library; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
+import copy
 import os
 import random
 
@@ -33,6 +34,7 @@ class MockBackend(object):
         self.objstore = ObjectStore(objstore_loc)
         self.next_taskid = 1
         self.host_stats = self._get_host_stats()
+        self._storagepools = {}
 
     def _get_host_stats(self):
         memory_stats = {'total': 3934908416L,
@@ -85,3 +87,52 @@ class MockBackend(object):
         res['os_codename'] = 'Santiago'
 
         return res
+
+    def get_storagepools(self):
+        return self._storagepools.keys()
+
+    def do_deep_scan(self):
+        return self.add_task('', time.sleep, 25)
+
+    def create_storagepool(self, params):
+        name = params['name']
+        pool = MockStoragePool(name)
+        pool.info.update(params)
+        if params['type'] == 'dir':
+            pool.info['autostart'] = True
+        else:
+            pool.info['autostart'] = False
+
+        self._storagepools[name] = pool
+
+    def get_storagepool_by_name(self, name):
+        pool = self._storagepools[name]
+        pool.refresh()
+        return pool.info
+
+    def activate_storagepool(self, name):
+        self._storagepools[name].info['state'] = 'active'
+
+    def deactivate_storagepool(self, name):
+        self._storagepools[name].info['state'] = 'inactive'
+
+    def delete_storagepool(self, name):
+        del self._storagepools[name]
+
+    def autostart_storagepool(self, name, value):
+        self._storagepools[name].info['autostart'] = value
+
+class MockStoragePool(object):
+    def __init__(self, name):
+        self.name = name
+        self._volumes = {}
+        self.info = {'state': 'inactive', 'capacity': 1024 << 20,
+                     'allocated': 512 << 20, 'available': 512 << 20,
+                     'path': '/var/lib/libvirt/images', 'source': {},
+                     'type': 'dir', 'nr_volumes': 0, 'autostart': 0}
+
+    def refresh(self):
+        state = self.info['state']
+        self.info['nr_volumes'] = 0
+        if state == 'active':
+            self.info['nr_volumes'] = len(self._volumes)
diff --git a/src/kimchi/model_/storagepools.py b/src/kimchi/model_/storagepools.py
new file mode 100644
index 0000000..abdebd8
--- /dev/null
+++ b/src/kimchi/model_/storagepools.py
@@ -0,0 +1,86 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+ISO_POOL_NAME = u'kimchi_isos'
+
+from kimchi.exception import InvalidParameter
+
+class StoragePools(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def get_list(self):
+        return sorted(self.backend.get_storagepools())
+
+    def create(self, params):
+        name = params['name']
+        if name in self.get_list() or name in (ISO_POOL_NAME,):
+            raise InvalidParameter("Storage pool '%s' already exists" % name)
+
+        task_id = None
+        if params['type'] == 'kimchi-iso':
+            task_id = self.backend.do_deep_scan(params)
+
+        self.backend.create_storagepool(params)
+        return name
+
+class StoragePool(StoragePools):
+    def lookup(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        return self.backend.get_storagepool_by_name(name)
+
+    def activate(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        self.backend.activate_storagepool()
+
+    def deactivate(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        self.backend.deactivate_storagepool()
+
+    def delete(self, name):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        if self.get_storagepool_by_name(name)['state'] == 'active':
+            raise InvalidOperation("Unable to delete active storage pool '%s'" %
+                                   name)
+
+        self.backend.delete_storagepool()
+
+    def update(self, name, params):
+        if name not in self.get_list():
+            raise NotFoundError("Storage pool '%s' not found.")
+
+        autostart = params['autostart']
+        if autostart not in [True, False]:
+            raise InvalidOperation("Autostart flag must be true or false")
+
+        self.backend.autostart_storagepool(name, autostart)
+
+        return name
-- 
1.7.10.4




More information about the Kimchi-devel mailing list