[Kimchi-devel] [PATCH 3/3] issue #544: Refactor storage volume download

Crístian Viana vianac at linux.vnet.ibm.com
Tue Dec 30 13:24:38 UTC 2014


The current storage volume download implementation relies on writing the
new volume as a file. But that's not the case for some storage pool
types (e.g. logical), which don't treat their volumes as plain files.

Use the libvirt API to write data to a storage volume instead of writing
it directly to a file. The libvirt API requires an existing volume to
write the data to it, and a volume requires a capacity to be created.
As some remote URLs don't provide the header 'Content-length', sometimes
we need to download a file without knowing its size in advance. Due to
those cases, instead of downloading and writing the data directly to the
storage volume, this patch makes it so a temporary file is always
created - so we can get its size - and then that file is transfered to a
storage volume.

Fix issue #544 ("Download to logical storage pool fails").

Signed-off-by: Crístian Viana <vianac at linux.vnet.ibm.com>
---
 src/kimchi/model/storagevolumes.py | 52 +++++++++++++++++++++++++++++++++++---
 1 file changed, 49 insertions(+), 3 deletions(-)

diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py
index 4bf029a..67fcaf0 100644
--- a/src/kimchi/model/storagevolumes.py
+++ b/src/kimchi/model/storagevolumes.py
@@ -20,6 +20,7 @@
 import contextlib
 import lxml.etree as ET
 import os
+import tempfile
 import time
 import urllib2
 from lxml.builder import E
@@ -203,7 +204,11 @@ class StorageVolumesModel(object):
         pool_model = StoragePoolModel(conn=self.conn,
                                       objstore=self.objstore)
         pool = pool_model.lookup(pool_name)
-        file_path = os.path.join(pool['path'], name)
+
+        if pool['type'] in ['dir', 'netfs']:
+            file_path = os.path.join(pool['path'], name)
+        else:
+            file_path = tempfile.mkstemp(prefix=name)[1]
 
         with contextlib.closing(urllib2.urlopen(url)) as response:
             with open(file_path, 'w') as volume_file:
@@ -219,14 +224,55 @@ class StorageVolumesModel(object):
                         volume_file.write(chunk_data)
                         downloaded_size += len(chunk_data)
                         cb('%s/%s' % (downloaded_size, remote_size))
-                except Exception, e:
+                except (IOError, libvirt.libvirtError) as e:
                     if os.path.isfile(file_path):
                         os.remove(file_path)
+
                     raise OperationFailed('KCHVOL0007E', {'name': name,
                                                           'pool': pool_name,
                                                           'err': e.message})
 
-        StoragePoolModel.get_storagepool(pool_name, self.conn).refresh(0)
+        if pool['type'] in ['dir', 'netfs']:
+            virt_pool = StoragePoolModel.get_storagepool(pool_name, self.conn)
+            virt_pool.refresh(0)
+        else:
+            def _stream_handler(stream, nbytes, fd):
+                return fd.read(nbytes)
+
+            virt_stream = virt_vol = None
+
+            try:
+                task = self.create(pool_name, {'name': name,
+                                               'format': 'raw',
+                                               'capacity': downloaded_size,
+                                               'allocation': downloaded_size})
+                self.task.wait(task['id'])
+                virt_vol = StorageVolumeModel.get_storagevolume(pool_name,
+                                                                name,
+                                                                self.conn)
+
+                virt_stream = self.conn.get().newStream(0)
+                virt_vol.upload(virt_stream, 0, downloaded_size, 0)
+
+                with open(file_path) as fd:
+                    virt_stream.sendAll(_stream_handler, fd)
+
+                virt_stream.finish()
+            except (IOError, libvirt.libvirtError) as e:
+                try:
+                    if virt_stream:
+                        virt_stream.abort()
+                    if virt_vol:
+                        virt_vol.delete(0)
+                except libvirt.libvirtError, virt_e:
+                    kimchi_log.error(virt_e.message)
+                finally:
+                    raise OperationFailed('KCHVOL0007E', {'name': name,
+                                                          'pool': pool_name,
+                                                          'err': e.message})
+            finally:
+                os.remove(file_path)
+
         cb('OK', True)
 
     def get_list(self, pool_name):
-- 
2.1.0




More information about the Kimchi-devel mailing list