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

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


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

To avoid duplicating code in model and mockmodel, the code related to
storagevolume resource was added to model_/storagevolumes.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 |   96 ++++++++++++++++++++++++++++++++++-
 src/kimchi/model_/mockbackend.py    |   42 +++++++++++++++
 src/kimchi/model_/storagevolumes.py |   95 ++++++++++++++++++++++++++++++++++
 src/kimchi/vmtemplate.py            |   12 -----
 4 files changed, 232 insertions(+), 13 deletions(-)
 create mode 100644 src/kimchi/model_/storagevolumes.py

diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py
index 28bfc01..ee263c6 100644
--- a/src/kimchi/model_/libvirtbackend.py
+++ b/src/kimchi/model_/libvirtbackend.py
@@ -39,7 +39,8 @@ 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.isoinfo import IsoImage
+from kimchi.exception import IsoFormatError, OperationFailed
 from kimchi.model_.libvirtconnection import LibvirtConnection
 from kimchi.model_.libvirtstoragepool import StoragePoolDef
 from kimchi.objectstore import ObjectStore
@@ -57,6 +58,11 @@ class LibvirtBackend(object):
                       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)
@@ -397,3 +403,91 @@ class LibvirtBackend(object):
             pool.setAutostart(1)
         else:
             pool.setAutostart(0)
+
+    def create_storagevolume(self, pool, params):
+        storagevol_xml = """
+        <volume>
+          <name>%(name)s</name>
+          <allocation unit="MiB">%(allocation)s</allocation>
+          <capacity unit="MiB">%(capacity)s</capacity>
+          <target>
+            <format type='%(format)s'/>
+            <path>%(path)s</path>
+          </target>
+        </volume>"""
+
+        params.setdefault('allocation', 0)
+        params.setdefault('format', 'qcow2')
+        try:
+            xml = storagevol_xml % params
+        except KeyError, key:
+            raise MissingParameter("You need to specify '%s' in order to "
+                                   "create the storage volume." % key)
+
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(pool)
+        try:
+            pool.createXML(xml, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def _get_storagevolume(self, pool, name):
+        conn = self.conn.get()
+        pool = conn.storagePoolLookupByName(pool)
+        return pool.storageVolLookupByName(name)
+
+    def get_storagevolumes_by_pool(self, pool):
+        try:
+            conn = self.conn.get()
+            pool = conn.storagePoolLookupByName(pool)
+            pool.refresh(0)
+            return pool.listVolumes()
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def get_storagevolume(self, pool, name):
+        vol = self._get_storagevolume(pool, name)
+        path = vol.path()
+        info = vol.info()
+        xml = vol.XMLDesc(0)
+        fmt = xmlutils.xpath_get_text(xml, "/volume/target/format/@type")[0]
+        res = dict(type=self.volume_type_map[info[0]], capacity=info[1],
+                   allocation=info[2], path=path, format=fmt)
+
+        if fmt == 'iso':
+            if os.path.islink(path):
+                path = os.path.join(os.path.dirname(path), os.readlink(path))
+
+            try:
+                iso_img = IsoImage(path)
+                os_distro, os_version = iso_img.probe()
+                bootable = True
+            except IsoFormatError:
+                bootable = False
+
+            res.update(dict(os_distro=os_distro, os_version=os_version,
+                            path=path, bootable=bootable))
+
+        return res
+
+    def wipe_storagevolume(self, pool, name):
+        try:
+            vol = self._get_storagevolume(pool, name)
+            vol.wipePattern(libvirt.VIR_STORAGE_VOL_WIPE_ALG_ZERO, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def resize_storagevolume(self, pool, name, size):
+        size = size << 20
+        try:
+            vol = pool.storageVolLookupByName(name)
+            vol.resize(size, 0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
+
+    def delete_storagevolume(self, pool, name):
+        try:
+            vol = pool.storageVolLookupByName(name)
+            volume.delete(0)
+        except libvirt.libvirtError as e:
+            raise OperationFailed(e.get_error_message())
diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py
index 01f4c22..1f93d36 100644
--- a/src/kimchi/model_/mockbackend.py
+++ b/src/kimchi/model_/mockbackend.py
@@ -122,6 +122,34 @@ class MockBackend(object):
     def autostart_storagepool(self, name, value):
         self._storagepools[name].info['autostart'] = value
 
+    def create_storagevolume(self, pool, params):
+        try:
+            name = params['name']
+            volume = MockStorageVolume(pool, name, params)
+            volume.info['type'] = params['type']
+            volume.info['format'] = params['format']
+            volume.info['path'] = os.path.join(pool.info['path'], name)
+        except KeyError, item:
+            raise MissingParameter(item)
+
+        pool._volumes[name] = volume
+
+    def get_storagevolumes_by_pool(self, pool):
+        return self._storagepools[pool]._volumes.keys()
+
+    def get_storagevolume(self, pool, name):
+        vol = self._storagevolumes[pool]._volumes[name]
+        return vol.info
+
+    def wipe_storagevolume(self, pool, name):
+        self._storagepools[pool]._volumes[name].info['allocation'] = 0
+
+    def resize_storagevolume(self, pool, name, size):
+        self._storagepools[pool]._volumes[name].info['capacity'] = size
+
+    def delete_storagevolume(self, pool, name):
+        del self._storagepools[pool]._volumes[name]
+
 class MockStoragePool(object):
     def __init__(self, name):
         self.name = name
@@ -136,3 +164,17 @@ class MockStoragePool(object):
         self.info['nr_volumes'] = 0
         if state == 'active':
             self.info['nr_volumes'] = len(self._volumes)
+
+class MockStorageVolume(object):
+    def __init__(self, pool, name, params={}):
+        self.name = name
+        self.pool = pool
+        self.info = {'type': 'disk', 'allocation': 512,
+                     'capacity': params.get('capacity', 1024) << 20,
+                     'format': params.get('format', 'raw')}
+
+        if fmt == 'iso':
+            self.info['allocation'] = self.info['capacity']
+            self.info['os_version'] = '19'
+            self.info['os_distro'] = 'fedora'
+            self.info['bootable'] = True
diff --git a/src/kimchi/model_/storagevolumes.py b/src/kimchi/model_/storagevolumes.py
new file mode 100644
index 0000000..9f3c93b
--- /dev/null
+++ b/src/kimchi/model_/storagevolumes.py
@@ -0,0 +1,95 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+#  Adam Litke <agl at linux.vnet.ibm.com>
+#  Aline Manera <alinefm at linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
+from kimchi.model import storagepools
+
+class StorageVolumes(object):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def create(self, pool, params):
+        if name in self.get_list(pool):
+            raise InvalidParameter("Storage volume '%s' already exists.")
+
+        self.backend.create_storagevolume(pool, params)
+        return name
+
+    def get_list(self, pool):
+        info = self.backend.get_storagepool_by_name(pool)
+        if info['state'] != 'active':
+            raise InvalidOperation("Unable to list volumes in inactive "
+                                   "storagepool %s" % pool)
+
+        return self.backend.get_storagevolumes_by_pool(pool)
+
+class StorageVolume(StorageVolumes):
+    def __init__(self, backend):
+        self.backend = backend
+
+    def _storagevolume_exist(self, pool, name):
+        if name not in self.get_list(pool):
+            raise NotFoundError("Storage volume '%' not found in '%' pool" %
+                                (name, pool))
+        return True
+
+    def lookup(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            return self.backend.get_storagevolume(pool, name)
+
+    def resize(self, pool, name, size):
+        if self._storagevolume_exist(pool, name):
+            self.backend.resize_storagevolume(pool, name, size)
+
+    def wipe(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            self.backend.wipe_storagevolume(pool, name)
+
+    def delete(self, pool, name):
+        if self._storagevolume_exist(pool, name):
+            self.backend.delete_storagevolume(pool, name)
+
+class IsoVolumes(StorageVolumes):
+    def __init__(self, backend):
+        self.backend = backend
+        self.storagepools = storagepools.StoragePools(self.backend)
+
+    def get_list(self, pool):
+        iso_volumes = []
+
+        for pool in self.storagepools.get_list():
+            try:
+                volumes = self.get_list(pool)
+            except InvalidOperation:
+                # Skip inactive pools
+                continue
+
+            for volume in volumes:
+                res = self.lookup(pool, volume)
+                if res['format'] == 'iso':
+                    # prevent iso from different pool having same volume name
+                    res['name'] = '%s-%s' % (pool, volume)
+                    iso_volumes.append(res)
+
+        return iso_volumes
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index 58147e3..e7f6c81 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -192,18 +192,6 @@ class VMTemplate(object):
                     'type': 'disk',
                     'format': 'qcow2',
                     'path': '%s/%s' % (storage_path, volume)}
-
-            info['xml'] = """
-            <volume>
-              <name>%(name)s</name>
-              <allocation>0</allocation>
-              <capacity unit="G">%(capacity)s</capacity>
-              <target>
-                <format type='%(format)s'/>
-                <path>%(path)s</path>
-              </target>
-            </volume>
-            """ % info
             ret.append(info)
         return ret
 
-- 
1.7.10.4




More information about the Kimchi-devel mailing list