[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