This patch changes the Kimchi backend in order to support creation of
guests from Templates that have multiple disks from different storage
pools.
Before, it was only possible to create a guest with one, two or more
disks from only one storagepool. In order to add a disk from another
pool, user would have to edit the guest and add it.
Now users can do this in creation time, using pre-configured Template.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/wok/plugins/kimchi/i18n.py | 1 +
src/wok/plugins/kimchi/model/templates.py | 21 +++++++++++++++------
src/wok/plugins/kimchi/model/vms.py | 18 ++++++------------
src/wok/plugins/kimchi/vmtemplate.py | 30 ++++++++++++++++--------------
4 files changed, 38 insertions(+), 32 deletions(-)
diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py
index de7962a..b18048c 100644
--- a/src/wok/plugins/kimchi/i18n.py
+++ b/src/wok/plugins/kimchi/i18n.py
@@ -128,6 +128,7 @@ messages = {
"KCHVM0069E": _("Password field must be a string."),
"KCHVM0070E": _("Error creating local host ssh rsa key of user
'root'."),
"KCHVM0071E": _("Memory value %(mem)s must be aligned to
%(alignment)sMiB."),
+ "KCHVM0072E": _("Template given has multiple disks assigned to
different types of storage pools, conflicting with storage pool provided."),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned
host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to
directly assign to VM."),
diff --git a/src/wok/plugins/kimchi/model/templates.py
b/src/wok/plugins/kimchi/model/templates.py
index bac6e47..eae2714 100644
--- a/src/wok/plugins/kimchi/model/templates.py
+++ b/src/wok/plugins/kimchi/model/templates.py
@@ -147,6 +147,16 @@ class TemplateModel(object):
params = session.get('template', name)
if overrides:
params.update(overrides)
+ if 'storagepool' in params:
+ libvVMT = LibvirtVMTemplate(params, conn=conn)
+ poolType = libvVMT._get_storage_type(params['storagepool'])
+ for i, disk in enumerate(params['disks']):
+ if disk['pool']['type'] != poolType:
+ raise InvalidOperation('KCHVM0072E')
+ else:
+ params['disks'][i]['pool']['name'] = \
+ params['storagepool']
+
return LibvirtVMTemplate(params, False, conn)
def lookup(self, name):
@@ -274,8 +284,8 @@ class LibvirtVMTemplate(VMTemplate):
raise InvalidParameter("KCHTMPL0007E", {'network':
name,
'template': self.name})
- def _get_storage_path(self):
- pool = self._storage_validate()
+ def _get_storage_path(self, pool_uri=None):
+ pool = self._storage_validate(pool_uri)
xml = pool.XMLDesc(0)
return xpath_get_text(xml, "/pool/target/path")[0]
@@ -285,7 +295,7 @@ class LibvirtVMTemplate(VMTemplate):
return xpath_get_text(xml, "/pool/@type")[0]
def _get_volume_path(self, pool, vol):
- pool = self._storage_validate()
+ pool = self._storage_validate(pool)
try:
return pool.storageVolLookupByName(vol).path()
except:
@@ -293,12 +303,11 @@ class LibvirtVMTemplate(VMTemplate):
'pool': pool})
def fork_vm_storage(self, vm_uuid):
- # Provision storage:
- # TODO: Rebase on the storage API once upstream
- pool = self._storage_validate()
+ # Provision storages:
vol_list = self.to_volume_list(vm_uuid)
try:
for v in vol_list:
+ pool = self._storage_validate(v['pool'])
# outgoing text to libvirt, encode('utf-8')
pool.createXML(v['xml'].encode('utf-8'), 0)
except libvirt.libvirtError as e:
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py
index 366f1b4..ebe716e 100644
--- a/src/wok/plugins/kimchi/model/vms.py
+++ b/src/wok/plugins/kimchi/model/vms.py
@@ -151,28 +151,22 @@ class VMsModel(object):
wok_log.error('Error trying to update database with guest '
'icon information due error: %s', e.message)
- # If storagepool is SCSI, volumes will be LUNs and must be passed by
- # the user from UI or manually.
- cb('Provisioning storage for new VM')
- vol_list = []
- if t._get_storage_type() not in ["iscsi", "scsi"]:
- vol_list = t.fork_vm_storage(vm_uuid)
+ cb('Provisioning storages for new VM')
+ vol_list = t.fork_vm_storage(vm_uuid)
graphics = params.get('graphics', {})
stream_protocols = self.caps.libvirt_stream_protocols
xml = t.to_vm_xml(name, vm_uuid,
libvirt_stream_protocols=stream_protocols,
- graphics=graphics,
- volumes=vol_list)
+ graphics=graphics)
cb('Defining new VM')
try:
conn.defineXML(xml.encode('utf-8'))
except libvirt.libvirtError as e:
- if t._get_storage_type() not in READONLY_POOL_TYPE:
- for v in vol_list:
- vol = conn.storageVolLookupByPath(v['path'])
- vol.delete(0)
+ for v in vol_list:
+ vol = conn.storageVolLookupByPath(v['path'])
+ vol.delete(0)
raise OperationFailed("KCHVM0007E", {'name': name,
'err': e.get_error_message()})
diff --git a/src/wok/plugins/kimchi/vmtemplate.py b/src/wok/plugins/kimchi/vmtemplate.py
index c7635b0..69cc9b5 100644
--- a/src/wok/plugins/kimchi/vmtemplate.py
+++ b/src/wok/plugins/kimchi/vmtemplate.py
@@ -184,11 +184,6 @@ class VMTemplate(object):
return xml
def _get_disks_xml(self, vm_uuid):
- # Current implementation just allows to create disk in one single
- # storage pool, so we cannot mix the types (scsi volumes vs img file)
- storage_type = self._get_storage_type()
- storage_path = self._get_storage_path()
-
base_disk_params = {'type': 'disk', 'disk':
'file',
'bus': self.info['disk_bus']}
logical_disk_params = {'format': 'raw'}
@@ -199,37 +194,44 @@ class VMTemplate(object):
'format': 'raw', 'bus':
'scsi'}
disks_xml = ''
- pool_name = pool_name_from_uri(self.info['storagepool'])
for index, disk in enumerate(self.info['disks']):
params = dict(base_disk_params)
params['format'] = disk['format']
- params.update(locals().get('%s_disk_params' % storage_type, {}))
+ params.update(locals().get('%s_disk_params' %
+ disk['pool']['type'], {}))
params['index'] = index
volume = disk.get('volume')
if volume is not None:
- params['path'] = self._get_volume_path(pool_name, volume)
+ params['path'] =
self._get_volume_path(disk['pool']['name'],
+ volume)
else:
- volume = "%s-%s.img" % (vm_uuid, params['index'])
- params['path'] = os.path.join(storage_path, volume)
+ img = "%s-%s.img" % (vm_uuid, params['index'])
+ storage_path =
self._get_storage_path(disk['pool']['name'])
+ params['path'] = os.path.join(storage_path, img)
disks_xml += get_disk_xml(params)[1]
return unicode(disks_xml, 'utf-8')
def to_volume_list(self, vm_uuid):
- storage_path = self._get_storage_path()
ret = []
for i, d in enumerate(self.info['disks']):
+ # Create only .img. If storagepool is (i)SCSI, volumes will be LUNs
+ if d['pool']['type'] in ["iscsi",
"scsi"]:
+ continue
+
index = d.get('index', i)
volume = "%s-%s.img" % (vm_uuid, index)
+ storage_path = self._get_storage_path(d['pool']['name'])
info = {'name': volume,
'capacity': d['size'],
'format': d['format'],
- 'path': '%s/%s' % (storage_path, volume)}
+ 'path': '%s/%s' % (storage_path, volume),
+ 'pool': d['pool']['name']}
- if 'logical' == self._get_storage_type() or \
+ if 'logical' == d['pool']['type'] or \
info['format'] not in ['qcow2', 'raw']:
info['allocation'] = info['capacity']
else:
@@ -414,7 +416,7 @@ class VMTemplate(object):
def fork_vm_storage(self, vm_uuid):
pass
- def _get_storage_path(self):
+ def _get_storage_path(self, pool_uri=None):
return ''
def _get_storage_type(self, pool=None):
--
2.1.0