From 8179007c804d88595a346ad993791fd21a2a9c01 Mon Sep 17 00:00:00 2001 From: Wenhao Xu Date: Wed, 6 Mar 2013 16:51:33 +0800 Subject: [PATCH] Add support to import images into sheepdog volumes. Supporting import images from glance to sheepdog volumes. Unit test framework for sheepdog is added too. Fix Bug #1148784 Change-Id: I8ffd1bc1b2d719aed27ce0d98eaa41fef8027ca2 --- cinder/image/image_utils.py | 21 ++++++++++++++++ cinder/tests/test_sheepdog.py | 37 ++++++++++++++++++++++++++-- cinder/volume/drivers/sheepdog.py | 40 ++++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/cinder/image/image_utils.py b/cinder/image/image_utils.py index b5f67ffda..af6f887da 100644 --- a/cinder/image/image_utils.py +++ b/cinder/image/image_utils.py @@ -211,6 +211,27 @@ def fetch(context, image_service, image_id, path, _user_id, _project_id): image_service.download(context, image_id, image_file) +def fetch_verify_image(context, image_service, image_id, dest, + user_id=None, project_id=None): + fetch(context, image_service, image_id, dest, + None, None) + + with fileutils.remove_path_on_error(dest): + data = qemu_img_info(dest) + fmt = data.file_format + if fmt is None: + raise exception.ImageUnacceptable( + reason=_("'qemu-img info' parsing failed."), + image_id=image_id) + + backing_file = data.backing_file + if backing_file is not None: + raise exception.ImageUnacceptable( + image_id=image_id, + reason=_("fmt=%(fmt)s backed by:" + "%(backing_file)s") % locals()) + + def fetch_to_raw(context, image_service, image_id, dest, user_id=None, project_id=None): diff --git a/cinder/tests/test_sheepdog.py b/cinder/tests/test_sheepdog.py index 7fe3862da..b533516c9 100644 --- a/cinder/tests/test_sheepdog.py +++ b/cinder/tests/test_sheepdog.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2013 Wenhao Xu +# Copyright (c) 2013 Zelin.io # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -15,10 +15,17 @@ # License for the specific language governing permissions and limitations # under the License. + +import contextlib +import os +import tempfile + from cinder import exception +from cinder.image import image_utils from cinder import test from cinder.volume.drivers.sheepdog import SheepdogDriver + COLLIE_NODE_INFO = """ 0 107287605248 3623897354 3% Total 107287605248 3623897354 3% 54760833024 @@ -43,8 +50,12 @@ Epoch Time Version """ -class SheepdogTestCase(test.TestCase): +class FakeImageService: + def download(self, context, image_id, path): + pass + +class SheepdogTestCase(test.TestCase): def setUp(self): super(SheepdogTestCase, self).setUp() self.driver = SheepdogDriver() @@ -92,3 +103,25 @@ class SheepdogTestCase(test.TestCase): return COLLIE_CLUSTER_INFO_0_6, '' self.stubs.Set(self.driver, '_execute', fake_stats) self.driver.check_for_setup_error() + + def test_copy_image_to_volume(self): + @contextlib.contextmanager + def fake_temp_file(dir): + class FakeTmp: + def __init__(self, name): + self.name = name + yield FakeTmp('test') + + def fake_try_execute(obj, *command, **kwargs): + return True + + self.stubs.Set(tempfile, 'NamedTemporaryFile', fake_temp_file) + self.stubs.Set(os.path, 'exists', lambda x: True) + self.stubs.Set(image_utils, 'fetch_verify_image', + lambda w, x, y, z: None) + self.stubs.Set(image_utils, 'convert_image', + lambda x, y, z: None) + self.stubs.Set(SheepdogDriver, '_try_execute', fake_try_execute) + self.driver.copy_image_to_volume(None, {'name': 'test', + 'size': 1}, + FakeImageService(), None) diff --git a/cinder/volume/drivers/sheepdog.py b/cinder/volume/drivers/sheepdog.py index db3b64c4a..cbca091a8 100644 --- a/cinder/volume/drivers/sheepdog.py +++ b/cinder/volume/drivers/sheepdog.py @@ -1,4 +1,6 @@ # Copyright 2012 OpenStack LLC +# Copyright (c) 2013 Zelin.io +# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -16,17 +18,23 @@ SheepDog Volume Driver. """ +import os import re +import tempfile from oslo.config import cfg from cinder import exception +from cinder.image import image_utils from cinder.openstack.common import log as logging from cinder.volume import driver LOG = logging.getLogger(__name__) +CONF = cfg.CONF +CONF.import_opt("image_conversion_dir", "cinder.image.image_utils") + class SheepdogDriver(driver.VolumeDriver): """Executes commands relating to Sheepdog Volumes""" @@ -70,7 +78,37 @@ class SheepdogDriver(driver.VolumeDriver): def delete_volume(self, volume): """Deletes a logical volume""" - self._try_execute('collie', 'vdi', 'delete', volume['name']) + self._delete(volume) + + def _ensure_dir_exists(self, tmp_dir): + if tmp_dir and not os.path.exists(tmp_dir): + os.makedirs(tmp_dir) + + def _resize(self, volume): + size = int(volume['size']) * (1024 ** 3) + self._try_execute('collie', 'vdi', 'resize', + volume['name'], size) + + def _delete(self, volume): + self._try_execute('collie', 'vdi', 'delete', + volume['name']) + + def copy_image_to_volume(self, context, volume, image_service, image_id): + # use the image_conversion_dir as a temporary place to save the image + conversion_dir = CONF.image_conversion_dir + self._ensure_dir_exists(conversion_dir) + with tempfile.NamedTemporaryFile(dir=conversion_dir) as tmp: + # (wenhao): we don't need to convert to raw for sheepdog. + image_utils.fetch_verify_image(context, image_service, + image_id, tmp.name) + + # remove the image created by import before this function. + # see volume/drivers/manager.py:_create_volume + self._delete(volume) + # convert and store into sheepdog + image_utils.convert_image(tmp, 'sheepdog:%s' % volume['name'], + 'raw') + self._resize(volume) def create_snapshot(self, snapshot): """Creates a sheepdog snapshot""" -- 2.45.2