cinder/cinder/tests/test_sheepdog.py

296 lines
11 KiB
Python

# 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import mock
from oslo_concurrency import processutils
from oslo_utils import importutils
from oslo_utils import units
import six
from cinder import exception
from cinder.image import image_utils
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers import sheepdog
COLLIE_NODE_INFO = """
0 107287605248 3623897354 3%
Total 107287605248 3623897354 3% 54760833024
"""
COLLIE_CLUSTER_INFO_0_5 = """
Cluster status: running
Cluster created at Tue Jun 25 19:51:41 2013
Epoch Time Version
2013-06-25 19:51:41 1 [127.0.0.1:7000, 127.0.0.1:7001, 127.0.0.1:7002]
"""
COLLIE_CLUSTER_INFO_0_6 = """
Cluster status: running, auto-recovery enabled
Cluster created at Tue Jun 25 19:51:41 2013
Epoch Time Version
2013-06-25 19:51:41 1 [127.0.0.1:7000, 127.0.0.1:7001, 127.0.0.1:7002]
"""
class FakeImageService:
def download(self, context, image_id, path):
pass
class SheepdogTestCase(test.TestCase):
def setUp(self):
super(SheepdogTestCase, self).setUp()
self.driver = sheepdog.SheepdogDriver(
configuration=conf.Configuration(None))
db_driver = self.driver.configuration.db_driver
self.db = importutils.import_module(db_driver)
self.driver.db = self.db
self.driver.do_setup(None)
def test_update_volume_stats(self):
def fake_stats(*args):
return COLLIE_NODE_INFO, ''
self.stubs.Set(self.driver, '_execute', fake_stats)
expected = dict(
volume_backend_name='sheepdog',
vendor_name='Open Source',
dirver_version=self.driver.VERSION,
storage_protocol='sheepdog',
total_capacity_gb=float(107287605248) / units.Gi,
free_capacity_gb=float(107287605248 - 3623897354) / units.Gi,
reserved_percentage=0,
QoS_support=False)
actual = self.driver.get_volume_stats(True)
self.assertDictMatch(expected, actual)
def test_update_volume_stats_error(self):
def fake_stats(*args):
raise processutils.ProcessExecutionError()
self.stubs.Set(self.driver, '_execute', fake_stats)
expected = dict(
volume_backend_name='sheepdog',
vendor_name='Open Source',
dirver_version=self.driver.VERSION,
storage_protocol='sheepdog',
total_capacity_gb='unknown',
free_capacity_gb='unknown',
reserved_percentage=0,
QoS_support=False)
actual = self.driver.get_volume_stats(True)
self.assertDictMatch(expected, actual)
def test_check_for_setup_error_0_5(self):
def fake_stats(*args):
return COLLIE_CLUSTER_INFO_0_5, ''
self.stubs.Set(self.driver, '_execute', fake_stats)
self.driver.check_for_setup_error()
def test_check_for_setup_error_0_6(self):
def fake_stats(*args):
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():
class FakeTmp:
def __init__(self, name):
self.name = name
yield FakeTmp('test').name
def fake_try_execute(obj, *command, **kwargs):
return True
self.stubs.Set(image_utils, 'temporary_file', fake_temp_file)
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(sheepdog.SheepdogDriver,
'_try_execute',
fake_try_execute)
self.driver.copy_image_to_volume(None, {'name': 'test',
'size': 1},
FakeImageService(), None)
def test_create_cloned_volume(self):
src_vol = {
'project_id': 'testprjid',
'name': six.text_type('volume-00000001'),
'size': '20',
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
}
target_vol = {
'project_id': 'testprjid',
'name': six.text_type('volume-00000002'),
'size': '20',
'id': '582a1efa-be6a-11e4-a73b-0aa186c60fe0',
}
with mock.patch.object(self.driver,
'_try_execute') as mock_exe:
self.driver.create_cloned_volume(target_vol, src_vol)
snapshot_name = src_vol['name'] + '-temp-snapshot'
qemu_src_volume_name = "sheepdog:%s" % src_vol['name']
qemu_snapshot_name = '%s:%s' % (qemu_src_volume_name,
snapshot_name)
qemu_target_volume_name = "sheepdog:%s" % target_vol['name']
calls = [
mock.call('qemu-img', 'snapshot', '-c',
snapshot_name, qemu_src_volume_name),
mock.call('qemu-img', 'create', '-b',
qemu_snapshot_name,
qemu_target_volume_name,
'%sG' % target_vol['size']),
]
mock_exe.assert_has_calls(calls)
def test_create_cloned_volume_failure(self):
fake_name = six.text_type('volume-00000001')
fake_size = '20'
fake_vol = {'project_id': 'testprjid', 'name': fake_name,
'size': fake_size,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
src_vol = fake_vol
patch = mock.patch.object
with patch(self.driver, '_try_execute',
side_effect=processutils.ProcessExecutionError):
with patch(self.driver, 'create_snapshot'):
with patch(self.driver, 'delete_snapshot'):
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cloned_volume,
fake_vol,
src_vol)
def test_clone_image_success(self):
context = {}
fake_name = six.text_type('volume-00000001')
fake_size = '2'
fake_vol = {'project_id': 'testprjid', 'name': fake_name,
'size': fake_size,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
image_location = ('sheepdog:192.168.1.111:7000:Alice', None)
image_id = "caa4ffd0-fake-fake-fake-f8631a807f5a"
image_meta = {'id': image_id, 'size': 1, 'disk_format': 'raw'}
image_service = ''
patch = mock.patch.object
with patch(self.driver, '_try_execute', return_value=True):
with patch(self.driver, 'create_cloned_volume'):
with patch(self.driver, '_resize'):
model_updated, cloned = self.driver.clone_image(
context, fake_vol, image_location,
image_meta, image_service)
self.assertTrue(cloned)
self.assertEqual("sheepdog:%s" % fake_name,
model_updated['provider_location'])
def test_clone_image_failure(self):
context = {}
fake_vol = {}
image_location = ('image_location', None)
image_meta = {}
image_service = ''
with mock.patch.object(self.driver, '_is_cloneable',
lambda *args: False):
result = self.driver.clone_image(
context, fake_vol, image_location, image_meta, image_service)
self.assertEqual(({}, False), result)
def test_is_cloneable(self):
uuid = '87f1b01c-f46c-4537-bd5d-23962f5f4316'
location = 'sheepdog:ip:port:%s' % uuid
image_meta = {'id': uuid, 'size': 1, 'disk_format': 'raw'}
invalid_image_meta = {'id': uuid, 'size': 1, 'disk_format': 'iso'}
with mock.patch.object(self.driver, '_try_execute') as try_execute:
self.assertTrue(
self.driver._is_cloneable(location, image_meta))
expected_cmd = ('collie', 'vdi', 'list',
'--address', 'ip',
'--port', 'port',
uuid)
try_execute.assert_called_once_with(*expected_cmd)
# check returning False without executing a command
self.assertFalse(
self.driver._is_cloneable('invalid-location', image_meta))
self.assertFalse(
self.driver._is_cloneable(location, invalid_image_meta))
self.assertEqual(1, try_execute.call_count)
error = processutils.ProcessExecutionError
with mock.patch.object(self.driver, '_try_execute',
side_effect=error) as fail_try_execute:
self.assertFalse(
self.driver._is_cloneable(location, image_meta))
fail_try_execute.assert_called_once_with(*expected_cmd)
def test_extend_volume(self):
fake_name = u'volume-00000001'
fake_size = '20'
fake_vol = {'project_id': 'testprjid', 'name': fake_name,
'size': fake_size,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
self.mox.StubOutWithMock(self.driver, '_resize')
size = int(fake_size) * units.Gi
self.driver._resize(fake_vol, size=size)
self.mox.ReplayAll()
self.driver.extend_volume(fake_vol, fake_size)
self.mox.VerifyAll()
def test_create_volume_from_snapshot(self):
fake_name = u'volume-00000001'
fake_size = '10'
fake_vol = {'project_id': 'testprjid', 'name': fake_name,
'size': fake_size,
'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66'}
ss_uuid = '00000000-0000-0000-0000-c3aa7ee01536'
fake_snapshot = {'volume_name': fake_name,
'name': 'volume-%s' % ss_uuid,
'id': ss_uuid,
'size': fake_size}
with mock.patch.object(sheepdog.SheepdogDriver,
'_try_execute') as mock_exe:
self.driver.create_volume_from_snapshot(fake_vol, fake_snapshot)
args = ['qemu-img', 'create', '-b',
"sheepdog:%s:%s" % (fake_snapshot['volume_name'],
fake_snapshot['name']),
"sheepdog:%s" % fake_vol['name'],
"%sG" % fake_vol['size']]
mock_exe.assert_called_once_with(*args)