[Kimchi-devel] [PATCH 2/9 - v6] Enable create guests with multiple disks from different pools

Rodrigo Trujillo rodrigo.trujillo at linux.vnet.ibm.com
Wed Dec 2 15:59:19 UTC 2015


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 at 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 b3414e2..a01c3de 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




More information about the Kimchi-devel mailing list