Sheepdog: Improve snapshot and clone operation
This patch changes SheepdogDriver Class methods to use SheepdogClient Class methods for detailed Error logs. We change the following SheepdogDriver methods. - create_snapshot - delete_snapshot - create_volume_from_snapshot - create_cloned_volume - extend_volume SheepdogClient Class methods are implemented to enable fine grained Error handling. So, the administrators are enable to do error recovery more easily using those logs. Change-Id: Ib126088e4100739db013b21970051af1f29790d5
This commit is contained in:
parent
77d6bf6550
commit
34a28c302c
|
@ -23,7 +23,6 @@ import mock
|
|||
from oslo_concurrency import processutils
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import units
|
||||
import six
|
||||
|
||||
from cinder.backup import driver as backup_driver
|
||||
from cinder import context
|
||||
|
@ -32,6 +31,8 @@ from cinder import exception
|
|||
from cinder.i18n import _
|
||||
from cinder.image import image_utils
|
||||
from cinder import test
|
||||
from cinder.tests.unit import fake_backup
|
||||
from cinder.tests.unit import fake_snapshot
|
||||
from cinder.tests.unit import fake_volume
|
||||
from cinder import utils
|
||||
from cinder.volume import configuration as conf
|
||||
|
@ -44,6 +45,12 @@ SHEEP_PORT = 7000
|
|||
class SheepdogDriverTestDataGenerator(object):
|
||||
def __init__(self):
|
||||
self.TEST_VOLUME = self._make_fake_volume(self.TEST_VOL_DATA)
|
||||
self.TEST_CLONED_VOLUME = self._make_fake_volume(
|
||||
self.TEST_CLONED_VOL_DATA)
|
||||
self.TEST_SNAPSHOT = self._make_fake_snapshot(
|
||||
self.TEST_SNAPSHOT_DATA, self.TEST_VOLUME)
|
||||
self.TEST_BACKUP_VOLUME = self._make_fake_backup_volume(
|
||||
self.TEST_BACKUP_VOL_DATA)
|
||||
|
||||
def sheepdog_cmd_error(self, cmd, exit_code, stdout, stderr):
|
||||
return (('(Command: %(cmd)s) '
|
||||
|
@ -59,16 +66,48 @@ class SheepdogDriverTestDataGenerator(object):
|
|||
return fake_volume.fake_volume_obj(context.get_admin_context(),
|
||||
**volume_data)
|
||||
|
||||
def _make_fake_snapshot(self, snapshot_data, src_volume):
|
||||
snapshot_obj = fake_snapshot.fake_snapshot_obj(
|
||||
context.get_admin_context(), **snapshot_data)
|
||||
snapshot_obj.volume = src_volume
|
||||
return snapshot_obj
|
||||
|
||||
def _make_fake_backup_volume(self, backup_data):
|
||||
return fake_backup.fake_backup_obj(context.get_admin_context(),
|
||||
**backup_data)
|
||||
|
||||
def cmd_dog_vdi_create(self, name, size):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'create', name,
|
||||
'%sG' % size, '-a', SHEEP_ADDR, '-p', str(SHEEP_PORT))
|
||||
'%sG' % size, '-a', SHEEP_ADDR, '-p', SHEEP_PORT)
|
||||
|
||||
def cmd_dog_vdi_delete(self, name):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'delete', name,
|
||||
'-a', SHEEP_ADDR, '-p', str(SHEEP_PORT))
|
||||
'-a', SHEEP_ADDR, '-p', SHEEP_PORT)
|
||||
|
||||
def cmd_dog_vdi_create_snapshot(self, vdiname, snapname):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'snapshot', '-s',
|
||||
snapname, '-a', SHEEP_ADDR, '-p', SHEEP_PORT, vdiname)
|
||||
|
||||
def cmd_dog_vdi_delete_snapshot(self, vdiname, snapname):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'delete', '-s',
|
||||
snapname, '-a', SHEEP_ADDR, '-p', SHEEP_PORT, vdiname)
|
||||
|
||||
def cmd_qemuimg_vdi_clone(self, src_vdiname, src_snapname, dst_vdiname,
|
||||
size):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'qemu-img', 'create', '-b',
|
||||
'sheepdog:%(addr)s:%(port)s:%(src_vdiname)s:%(src_snapname)s' %
|
||||
{'addr': SHEEP_ADDR, 'port': SHEEP_PORT,
|
||||
'src_vdiname': src_vdiname, 'src_snapname': src_snapname},
|
||||
'sheepdog:%(addr)s:%(port)s:%(dst_vdiname)s' %
|
||||
{'addr': SHEEP_ADDR, 'port': SHEEP_PORT,
|
||||
'dst_vdiname': dst_vdiname}, '%sG' % size)
|
||||
|
||||
def cmd_dog_vdi_resize(self, name, size):
|
||||
return ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'vdi', 'resize', name,
|
||||
size, '-a', SHEEP_ADDR, '-p', SHEEP_PORT)
|
||||
|
||||
CMD_DOG_CLUSTER_INFO = ('env', 'LC_ALL=C', 'LANG=C', 'dog', 'cluster',
|
||||
'info', '-a', SHEEP_ADDR, '-p', str(SHEEP_PORT))
|
||||
'info', '-a', SHEEP_ADDR, '-p', SHEEP_PORT)
|
||||
|
||||
TEST_VOL_DATA = {
|
||||
'size': 1,
|
||||
|
@ -83,6 +122,27 @@ class SheepdogDriverTestDataGenerator(object):
|
|||
'consistencygroup_id': None,
|
||||
}
|
||||
|
||||
TEST_CLONED_VOL_DATA = {
|
||||
'size': 2,
|
||||
'id': '00000000-0000-0000-0000-000000000003',
|
||||
'provider_auth': None,
|
||||
'host': 'host@backendsec#unit_test_pool',
|
||||
'project_id': 'project',
|
||||
'provider_location': 'location',
|
||||
'display_name': 'vol3',
|
||||
'display_description': 'unit test cloned volume',
|
||||
'volume_type_id': None,
|
||||
'consistencygroup_id': None,
|
||||
}
|
||||
|
||||
TEST_SNAPSHOT_DATA = {
|
||||
'id': '00000000-0000-0000-0000-000000000002',
|
||||
}
|
||||
|
||||
TEST_BACKUP_VOL_DATA = {
|
||||
'volume_id': '00000000-0000-0000-0000-000000000001',
|
||||
}
|
||||
|
||||
COLLIE_NODE_INFO = """
|
||||
0 107287605248 3623897354 3%
|
||||
Total 107287605248 3623897354 3% 54760833024
|
||||
|
@ -137,7 +197,38 @@ Cluster status: System is shutting down
|
|||
Failed to create VDI %(vdiname)s: VDI exists already
|
||||
"""
|
||||
|
||||
DOG_VDI_DELETE_VDI_NOT_EXISTS = """\
|
||||
DOG_VDI_SNAPSHOT_VDI_NOT_FOUND = """\
|
||||
Failed to create snapshot for volume-00000000-0000-0000-0000-000000000001: \
|
||||
No VDI found
|
||||
"""
|
||||
|
||||
DOG_VDI_SNAPSHOT_ALREADY_EXISTED = """\
|
||||
Failed to create snapshot for volume-00000000-0000-0000-0000-000000000001, \
|
||||
maybe snapshot id (0) or tag (snapshot-00000000-0000-0000-0000-000000000002) \
|
||||
is existed
|
||||
"""
|
||||
|
||||
DOG_VDI_SNAPSHOT_TAG_NOT_FOUND = """\
|
||||
Failed to open VDI volume-00000000-0000-0000-0000-000000000001 \
|
||||
(snapshot id: 0 snapshot tag: snapshot-00000000-0000-0000-0000-000000000002): \
|
||||
Failed to find requested tag
|
||||
"""
|
||||
|
||||
DOG_VDI_SNAPSHOT_VOLUME_NOT_FOUND = """\
|
||||
Failed to open VDI volume-00000000-0000-0000-0000-000000000001 \
|
||||
(snapshot id: 0 snapshot tag: snapshot-00000000-0000-0000-0000-000000000002): \
|
||||
No VDI found
|
||||
"""
|
||||
|
||||
DOG_VDI_RESIZE_SIZE_SHRINK = """\
|
||||
Shrinking VDIs is not implemented
|
||||
"""
|
||||
|
||||
DOG_VDI_RESIZE_TOO_LARGE = """\
|
||||
New VDI size is too large. This volume's max size is 4398046511104
|
||||
"""
|
||||
|
||||
DOG_COMMAND_ERROR_VDI_NOT_EXISTS = """\
|
||||
Failed to open VDI %(vdiname)s (snapshot id: 0 snapshot tag: ): No VDI found
|
||||
"""
|
||||
|
||||
|
@ -147,6 +238,35 @@ failed to connect to 127.0.0.1:7000: Connection refused
|
|||
Failed to get node list
|
||||
"""
|
||||
|
||||
QEMU_IMG_VDI_ALREADY_EXISTS = """\
|
||||
qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000001: \
|
||||
VDI exists already,
|
||||
"""
|
||||
|
||||
QEMU_IMG_VDI_NOT_FOUND = """\
|
||||
qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000003: \
|
||||
cannot get vdi info, No vdi found, \
|
||||
volume-00000000-0000-0000-0000-000000000001 \
|
||||
snapshot-00000000-0000-0000-0000-000000000002
|
||||
"""
|
||||
|
||||
QEMU_IMG_SNAPSHOT_NOT_FOUND = """\
|
||||
qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000003: \
|
||||
cannot get vdi info, Failed to find the requested tag, \
|
||||
volume-00000000-0000-0000-0000-000000000001 \
|
||||
snapshot-00000000-0000-0000-0000-000000000002
|
||||
"""
|
||||
|
||||
QEMU_IMG_SIZE_TOO_LARGE = """\
|
||||
qemu-img: sheepdog:volume-00000000-0000-0000-0000-000000000001: \
|
||||
An image is too large. The maximum image size is 4096GB
|
||||
"""
|
||||
|
||||
QEMU_IMG_FAILED_TO_CONNECT = """\
|
||||
qemu-img: sheepdog::volume-00000000-0000-0000-0000-000000000001: \
|
||||
Failed to connect socket: Connection refused
|
||||
"""
|
||||
|
||||
|
||||
class FakeImageService(object):
|
||||
def download(self, context, image_id, path):
|
||||
|
@ -267,6 +387,10 @@ class SheepdogClientTestCase(test.TestCase):
|
|||
self.client = self.driver.client
|
||||
self._vdiname = self.test_data.TEST_VOLUME.name
|
||||
self._vdisize = self.test_data.TEST_VOLUME.size
|
||||
self._src_vdiname = self.test_data.TEST_SNAPSHOT.volume_name
|
||||
self._snapname = self.test_data.TEST_SNAPSHOT.name
|
||||
self._dst_vdiname = self.test_data.TEST_CLONED_VOLUME.name
|
||||
self._dst_vdisize = self.test_data.TEST_CLONED_VOLUME.size
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_run_dog_success(self, fake_execute):
|
||||
|
@ -301,6 +425,7 @@ class SheepdogClientTestCase(test.TestCase):
|
|||
def test_run_dog_unknown_error(self, fake_logger, fake_execute):
|
||||
args = ('cluster', 'info')
|
||||
cmd = self.test_data.CMD_DOG_CLUSTER_INFO
|
||||
cmd = self.test_data.CMD_DOG_CLUSTER_INFO
|
||||
exit_code = 1
|
||||
stdout = 'stdout dummy'
|
||||
stderr = 'stderr dummy'
|
||||
|
@ -312,6 +437,57 @@ class SheepdogClientTestCase(test.TestCase):
|
|||
self.client._run_dog, *args)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_run_qemu_img_success(self, fake_execute):
|
||||
# multiple part of args match the prefix and
|
||||
# volume name is matched the prefix unfortunately
|
||||
expected_cmd = ('env', 'LC_ALL=C', 'LANG=C',
|
||||
'qemu-img', 'create', '-b',
|
||||
'sheepdog:%(addr)s:%(port)s:sheepdog:snap' %
|
||||
{'addr': SHEEP_ADDR, 'port': SHEEP_PORT},
|
||||
'sheepdog:%(addr)s:%(port)s:clone' %
|
||||
{'addr': SHEEP_ADDR, 'port': SHEEP_PORT}, '10G')
|
||||
fake_execute.return_value = ('', '')
|
||||
self.client._run_qemu_img('create', '-b', 'sheepdog:sheepdog:snap',
|
||||
'sheepdog:clone', '10G')
|
||||
fake_execute.assert_called_once_with(*expected_cmd)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_run_qemu_img_command_not_found(self, fake_logger, fake_execute):
|
||||
args = ('create', 'dummy')
|
||||
expected_msg = 'No such file or directory'
|
||||
expected_errno = errno.ENOENT
|
||||
fake_execute.side_effect = OSError(expected_errno, expected_msg)
|
||||
self.assertRaises(OSError, self.client._run_qemu_img, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_run_qemu_img_unknown_os_error(self, fake_logger, fake_execute):
|
||||
args = ('create', 'dummy')
|
||||
expected_msg = 'unknown'
|
||||
expected_errno = errno.EPERM
|
||||
fake_execute.side_effect = OSError(expected_errno, expected_msg)
|
||||
self.assertRaises(OSError, self.client._run_qemu_img, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
|
||||
@mock.patch.object(utils, 'execute')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_run_qemu_img_execution_error(self, fake_logger, fake_execute):
|
||||
args = ('create', 'dummy')
|
||||
cmd = ('qemu-img', 'create', 'dummy')
|
||||
exit_code = 1
|
||||
stdout = 'stdout dummy'
|
||||
stderr = 'stderr dummy'
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr)
|
||||
fake_execute.side_effect = processutils.ProcessExecutionError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout, stderr=stderr)
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client._run_qemu_img, *args)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_check_cluster_status_success(self, fake_logger, fake_execute):
|
||||
|
@ -484,7 +660,7 @@ class SheepdogClientTestCase(test.TestCase):
|
|||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_vdi_not_found(self, fake_logger, fake_execute):
|
||||
stdout = ''
|
||||
stderr = (self.test_data.DOG_VDI_DELETE_VDI_NOT_EXISTS %
|
||||
stderr = (self.test_data.DOG_COMMAND_ERROR_VDI_NOT_EXISTS %
|
||||
{'vdiname': self._vdiname})
|
||||
fake_execute.return_value = (stdout, stderr)
|
||||
self.client.delete(self._vdiname)
|
||||
|
@ -542,6 +718,425 @@ class SheepdogClientTestCase(test.TestCase):
|
|||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
def test_create_snapshot_success(self, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
expected_cmd = ('vdi', 'snapshot', '-s', self._snapname,
|
||||
self._src_vdiname)
|
||||
fake_execute.return_value = ('', '')
|
||||
self.client.create_snapshot(*args)
|
||||
fake_execute.assert_called_once_with(*expected_cmd)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_create_snapshot_fail_to_connect(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args)
|
||||
exit_code = 2
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.create_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_create_snapshot_vdi_not_found(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args)
|
||||
exit_code = 1
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_VDI_SNAPSHOT_VDI_NOT_FOUND
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.create_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_create_snapshot_snap_name_already_used(self, fake_logger,
|
||||
fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args)
|
||||
exit_code = 1
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.DOG_VDI_SNAPSHOT_ALREADY_EXISTED
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.create_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_create_snapshot_unknown_error(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_create_snapshot(*args)
|
||||
exit_code = 1
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = 'unknown_error'
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.create_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_success(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
expected_cmd = ('vdi', 'delete', '-s', self._snapname,
|
||||
self._src_vdiname)
|
||||
fake_execute.return_value = ('', '')
|
||||
self.client.delete_snapshot(*args)
|
||||
fake_execute.assert_called_once_with(*expected_cmd)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_not_found(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_VDI_SNAPSHOT_TAG_NOT_FOUND
|
||||
fake_execute.return_value = (stdout, stderr)
|
||||
self.client.delete_snapshot(*args)
|
||||
self.assertTrue(fake_logger.warning.called)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_vdi_not_found(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_VDI_SNAPSHOT_VOLUME_NOT_FOUND
|
||||
fake_execute.return_value = (stdout, stderr)
|
||||
self.client.delete_snapshot(*args)
|
||||
self.assertTrue(fake_logger.warning.called)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_fail_to_connect_bugcase(self, fake_logger,
|
||||
fake_execute):
|
||||
# NOTE(tishizaki): Sheepdog's bug case.
|
||||
# details are written to Sheepdog driver code.
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT
|
||||
expected_reason = (_('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s'),
|
||||
{'addr': SHEEP_ADDR, 'port': SHEEP_PORT})
|
||||
fake_execute.return_value = (stdout, stderr)
|
||||
ex = self.assertRaises(exception.SheepdogError,
|
||||
self.client.delete_snapshot, *args)
|
||||
self.assertEqual(expected_reason, ex.kwargs['reason'])
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_fail_to_connect(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_delete_snapshot(*args)
|
||||
exit_code = 2
|
||||
stdout = ''
|
||||
stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.delete_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_delete_snapshot_unknown_error(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname)
|
||||
cmd = self.test_data.cmd_dog_vdi_delete_snapshot(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = 'unknown_error'
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.delete_snapshot, *args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
def test_clone_success(self, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
src_volume = 'sheepdog:%(src_vdiname)s:%(snapname)s' % {
|
||||
'src_vdiname': self._src_vdiname, 'snapname': self._snapname}
|
||||
dst_volume = 'sheepdog:%s' % self._dst_vdiname
|
||||
expected_cmd = ('create', '-b', src_volume, dst_volume,
|
||||
'%sG' % self._dst_vdisize)
|
||||
fake_execute.return_code = ("", "")
|
||||
self.client.clone(*args)
|
||||
fake_execute.assert_called_once_with(*expected_cmd)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_fail_to_connect(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.QEMU_IMG_FAILED_TO_CONNECT
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_dst_vdi_already_exists(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.QEMU_IMG_VDI_ALREADY_EXISTS
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_src_vdi_not_found(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.QEMU_IMG_VDI_NOT_FOUND
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_src_snapshot_not_found(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.QEMU_IMG_SNAPSHOT_NOT_FOUND
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_too_large_volume_size(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.QEMU_IMG_SIZE_TOO_LARGE
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_qemu_img')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_clone_unknown_error(self, fake_logger, fake_execute):
|
||||
args = (self._src_vdiname, self._snapname,
|
||||
self._dst_vdiname, self._dst_vdisize)
|
||||
cmd = self.test_data.cmd_qemuimg_vdi_clone(*args)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = 'stderr_dummy'
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError, self.client.clone,
|
||||
*args)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
def test_resize_success(self, fake_execute):
|
||||
expected_cmd = ('vdi', 'resize', self._vdiname, 10 * 1024 ** 3)
|
||||
fake_execute.return_value = ('', '')
|
||||
self.client.resize(self._vdiname, 10)
|
||||
fake_execute.assert_called_once_with(*expected_cmd)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_resize_fail_to_connect(self, fake_logger, fake_execute):
|
||||
cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 10 * 1024 ** 3)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.DOG_COMMAND_ERROR_FAIL_TO_CONNECT
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.resize, self._vdiname, 10)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_resize_vdi_not_found(self, fake_logger, fake_execute):
|
||||
cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 10 * 1024 ** 3)
|
||||
exit_code = 1
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = (self.test_data.DOG_COMMAND_ERROR_VDI_NOT_EXISTS %
|
||||
{'vdiname': self._vdiname})
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.resize, self._vdiname, 1)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_resize_shrinking_not_supported(self, fake_logger, fake_execute):
|
||||
cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 1 * 1024 ** 3)
|
||||
exit_code = 1
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.DOG_VDI_RESIZE_SIZE_SHRINK
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.resize, self._vdiname, 1)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_resize_too_large_size(self, fake_logger, fake_execute):
|
||||
cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 5 * 1024 ** 4)
|
||||
exit_code = 64
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = self.test_data.DOG_VDI_RESIZE_TOO_LARGE
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.resize, self._vdiname, 5120)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, '_run_dog')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_resize_unknown_error(self, fake_logger, fake_execute):
|
||||
cmd = self.test_data.cmd_dog_vdi_resize(self._vdiname, 10 * 1024 ** 3)
|
||||
exit_code = 2
|
||||
stdout = 'stdout_dummy'
|
||||
stderr = 'stderr_dummy'
|
||||
expected_msg = self.test_data.sheepdog_cmd_error(cmd=cmd,
|
||||
exit_code=exit_code,
|
||||
stdout=stdout,
|
||||
stderr=stderr)
|
||||
fake_execute.side_effect = exception.SheepdogCmdError(
|
||||
cmd=cmd, exit_code=exit_code, stdout=stdout.replace('\n', '\\n'),
|
||||
stderr=stderr.replace('\n', '\\n'))
|
||||
ex = self.assertRaises(exception.SheepdogCmdError,
|
||||
self.client.resize, self._vdiname, 10)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
self.assertEqual(expected_msg, ex.msg)
|
||||
|
||||
|
||||
class SheepdogDriverTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
|
@ -558,6 +1153,10 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
self.client = self.driver.client
|
||||
self._vdiname = self.test_data.TEST_VOLUME.name
|
||||
self._vdisize = self.test_data.TEST_VOLUME.size
|
||||
self._src_vdiname = self.test_data.TEST_SNAPSHOT.volume_name
|
||||
self._snapname = self.test_data.TEST_SNAPSHOT.name
|
||||
self._dst_vdiname = self.test_data.TEST_CLONED_VOLUME.name
|
||||
self._dst_vdisize = self.test_data.TEST_CLONED_VOLUME.size
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'check_cluster_status')
|
||||
def test_check_for_setup_error(self, fake_execute):
|
||||
|
@ -673,65 +1272,59 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
self.driver.copy_volume_to_image,
|
||||
*args)
|
||||
|
||||
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',
|
||||
}
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'clone')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
def test_create_cloned_volume(self, fake_delete_snapshot,
|
||||
fake_clone, fake_create_snapshot):
|
||||
src_vol = self.test_data.TEST_VOLUME
|
||||
cloned_vol = self.test_data.TEST_CLONED_VOLUME
|
||||
|
||||
with mock.patch.object(self.driver,
|
||||
'_try_execute') as mock_exe:
|
||||
self.driver.create_cloned_volume(target_vol, src_vol)
|
||||
self.driver.create_cloned_volume(cloned_vol, src_vol)
|
||||
snapshot_name = src_vol.name + '-temp-snapshot'
|
||||
fake_create_snapshot.assert_called_once_with(src_vol.name,
|
||||
snapshot_name)
|
||||
fake_clone.assert_called_once_with(src_vol.name, snapshot_name,
|
||||
cloned_vol.name, cloned_vol.size)
|
||||
fake_delete_snapshot.assert_called_once_with(src_vol.name,
|
||||
snapshot_name)
|
||||
|
||||
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)
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'clone')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_create_cloned_volume_failure(self, fake_logger,
|
||||
fake_delete_snapshot,
|
||||
fake_clone, fake_create_snapshot):
|
||||
src_vol = self.test_data.TEST_VOLUME
|
||||
cloned_vol = self.test_data.TEST_CLONED_VOLUME
|
||||
snapshot_name = src_vol.name + '-temp-snapshot'
|
||||
|
||||
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
|
||||
fake_clone.side_effect = exception.SheepdogCmdError(
|
||||
cmd='dummy', exit_code=1, stdout='dummy', stderr='dummy')
|
||||
self.assertRaises(exception.SheepdogCmdError,
|
||||
self.driver.create_cloned_volume,
|
||||
cloned_vol, src_vol)
|
||||
fake_delete_snapshot.assert_called_once_with(src_vol.name,
|
||||
snapshot_name)
|
||||
self.assertTrue(fake_logger.error.called)
|
||||
|
||||
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)
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
def test_create_snapshot(self, fake_create_snapshot):
|
||||
snapshot = self.test_data.TEST_SNAPSHOT
|
||||
self.driver.create_snapshot(snapshot)
|
||||
fake_create_snapshot.assert_called_once_with(snapshot.volume_name,
|
||||
snapshot.name)
|
||||
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
def test_delete_snapshot(self, fake_delete_snapshot):
|
||||
snapshot = self.test_data.TEST_SNAPSHOT
|
||||
self.driver.delete_snapshot(snapshot)
|
||||
fake_delete_snapshot.assert_called_once_with(snapshot.volume_name,
|
||||
snapshot.name)
|
||||
|
||||
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'}
|
||||
|
@ -740,13 +1333,14 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
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'):
|
||||
with patch(self.client, 'resize'):
|
||||
model_updated, cloned = self.driver.clone_image(
|
||||
context, fake_vol, image_location,
|
||||
image_meta, image_service)
|
||||
context, self.test_data.TEST_CLONED_VOLUME,
|
||||
image_location, image_meta, image_service)
|
||||
|
||||
self.assertTrue(cloned)
|
||||
self.assertEqual("sheepdog:%s" % fake_name,
|
||||
self.assertEqual("sheepdog:%s" %
|
||||
self.test_data.TEST_CLONED_VOLUME.name,
|
||||
model_updated['provider_location'])
|
||||
|
||||
def test_clone_image_failure(self):
|
||||
|
@ -791,57 +1385,35 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
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'}
|
||||
dst_volume = self.test_data.TEST_CLONED_VOLUME
|
||||
snapshot = self.test_data.TEST_SNAPSHOT
|
||||
with mock.patch.object(self.client, 'clone') as fake_execute:
|
||||
self.driver.create_volume_from_snapshot(dst_volume, snapshot)
|
||||
fake_execute.assert_called_once_with(self._src_vdiname,
|
||||
self._snapname,
|
||||
self._dst_vdiname,
|
||||
self._dst_vdisize)
|
||||
|
||||
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)
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'resize')
|
||||
@mock.patch.object(sheepdog, 'LOG')
|
||||
def test_extend_volume(self, fake_logger, fake_execute):
|
||||
self.driver.extend_volume(self.test_data.TEST_VOLUME, 10)
|
||||
fake_execute.assert_called_once_with(self._vdiname, 10)
|
||||
self.assertTrue(fake_logger.debug.called)
|
||||
|
||||
@mock.patch.object(db, 'volume_get')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, '_try_execute')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'create_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
@mock.patch.object(backup_driver, 'BackupDriver')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'delete_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
def test_backup_volume_success(self, fake_delete_snapshot,
|
||||
fake_backup_service, fake_create_snapshot,
|
||||
fake_execute, fake_volume_get):
|
||||
fake_context = {}
|
||||
fake_backup = {'volume_id': '2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = {'id': '2926efe0-24ab-45b7-95e1-ff66e0646a33',
|
||||
'name': 'volume-2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = self.test_data.TEST_VOLUME
|
||||
fake_backup = self.test_data.TEST_BACKUP_VOLUME
|
||||
fake_backup_service = mock.Mock()
|
||||
fake_volume_get.return_value = fake_volume
|
||||
self.driver.backup_volume(fake_context,
|
||||
fake_backup,
|
||||
|
@ -859,22 +1431,21 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
|
||||
@mock.patch.object(db, 'volume_get')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, '_try_execute')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'create_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
@mock.patch.object(backup_driver, 'BackupDriver')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'delete_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
def test_backup_volume_fail_to_create_snap(self, fake_delete_snapshot,
|
||||
fake_backup_service,
|
||||
fake_create_snapshot,
|
||||
fake_execute, fake_volume_get):
|
||||
fake_context = {}
|
||||
fake_backup = {'volume_id': '2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = {'id': '2926efe0-24ab-45b7-95e1-ff66e0646a33',
|
||||
'name': 'volume-2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = self.test_data.TEST_VOLUME
|
||||
fake_backup = self.test_data.TEST_BACKUP_VOLUME
|
||||
fake_volume_get.return_value = fake_volume
|
||||
fake_create_snapshot.side_effect = processutils.ProcessExecutionError(
|
||||
fake_create_snapshot.side_effect = exception.SheepdogCmdError(
|
||||
cmd='dummy', exit_code=1, stdout='dummy', stderr='dummy')
|
||||
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.assertRaises(exception.SheepdogError,
|
||||
self.driver.backup_volume,
|
||||
fake_context,
|
||||
fake_backup,
|
||||
|
@ -886,17 +1457,16 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
|
||||
@mock.patch.object(db, 'volume_get')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, '_try_execute')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'create_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'create_snapshot')
|
||||
@mock.patch.object(backup_driver, 'BackupDriver')
|
||||
@mock.patch.object(sheepdog.SheepdogDriver, 'delete_snapshot')
|
||||
@mock.patch.object(sheepdog.SheepdogClient, 'delete_snapshot')
|
||||
def test_backup_volume_fail_to_backup_vol(self, fake_delete_snapshot,
|
||||
fake_backup_service,
|
||||
fake_create_snapshot,
|
||||
fake_execute, fake_volume_get):
|
||||
fake_context = {}
|
||||
fake_backup = {'volume_id': '2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = {'id': '2926efe0-24ab-45b7-95e1-ff66e0646a33',
|
||||
'name': 'volume-2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_volume = self.test_data.TEST_VOLUME
|
||||
fake_backup = self.test_data.TEST_BACKUP_VOLUME
|
||||
fake_volume_get.return_value = fake_volume
|
||||
|
||||
class BackupError(Exception):
|
||||
|
@ -917,9 +1487,8 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
@mock.patch.object(backup_driver, 'BackupDriver')
|
||||
def test_restore_backup(self, fake_backup_service):
|
||||
fake_context = {}
|
||||
fake_backup = {}
|
||||
fake_volume = {'id': '2926efe0-24ab-45b7-95e1-ff66e0646a33',
|
||||
'name': 'volume-2926efe0-24ab-45b7-95e1-ff66e0646a33'}
|
||||
fake_backup = self.test_data.TEST_BACKUP_VOLUME
|
||||
fake_volume = self.test_data.TEST_VOLUME
|
||||
|
||||
self.driver.restore_backup(
|
||||
fake_context, fake_backup, fake_volume, fake_backup_service)
|
||||
|
@ -927,5 +1496,5 @@ class SheepdogDriverTestCase(test.TestCase):
|
|||
call_args, call_kwargs = fake_backup_service.restore.call_args
|
||||
call_backup, call_volume_id, call_sheepdog_fd = call_args
|
||||
self.assertEqual(fake_backup, call_backup)
|
||||
self.assertEqual(fake_volume['id'], call_volume_id)
|
||||
self.assertEqual(fake_volume.id, call_volume_id)
|
||||
self.assertIsInstance(call_sheepdog_fd, sheepdog.SheepdogIOWrapper)
|
||||
|
|
|
@ -56,6 +56,7 @@ CONF.register_opts(sheepdog_opts)
|
|||
|
||||
class SheepdogClient(object):
|
||||
"""Sheepdog command executor."""
|
||||
QEMU_SHEEPDOG_PREFIX = 'sheepdog:'
|
||||
DOG_RESP_CONNECTION_ERROR = 'failed to connect to'
|
||||
DOG_RESP_CLUSTER_RUNNING = 'Cluster status: running'
|
||||
DOG_RESP_CLUSTER_NOT_FORMATTED = ('Cluster status: '
|
||||
|
@ -64,6 +65,17 @@ class SheepdogClient(object):
|
|||
'Waiting for other nodes to join cluster')
|
||||
DOG_RESP_VDI_ALREADY_EXISTS = ': VDI exists already'
|
||||
DOG_RESP_VDI_NOT_FOUND = ': No VDI found'
|
||||
DOG_RESP_VDI_SHRINK_NOT_SUPPORT = 'Shrinking VDIs is not implemented'
|
||||
DOG_RESP_VDI_SIZE_TOO_LARGE = 'New VDI size is too large'
|
||||
DOG_RESP_SNAPSHOT_VDI_NOT_FOUND = ': No VDI found'
|
||||
DOG_RESP_SNAPSHOT_NOT_FOUND = ': Failed to find requested tag'
|
||||
DOG_RESP_SNAPSHOT_EXISTED = 'tag (%(snapname)s) is existed'
|
||||
QEMU_IMG_RESP_CONNECTION_ERROR = ('Failed to connect socket: '
|
||||
'Connection refused')
|
||||
QEMU_IMG_RESP_ALREADY_EXISTS = ': VDI exists already'
|
||||
QEMU_IMG_RESP_SNAPSHOT_NOT_FOUND = 'Failed to find the requested tag'
|
||||
QEMU_IMG_RESP_VDI_NOT_FOUND = 'No vdi found'
|
||||
QEMU_IMG_RESP_SIZE_TOO_LARGE = 'An image is too large.'
|
||||
|
||||
def __init__(self, addr, port):
|
||||
self.addr = addr
|
||||
|
@ -71,7 +83,7 @@ class SheepdogClient(object):
|
|||
|
||||
def _run_dog(self, command, subcommand, *params):
|
||||
cmd = ('env', 'LC_ALL=C', 'LANG=C', 'dog', command, subcommand,
|
||||
'-a', self.addr, '-p', str(self.port)) + params
|
||||
'-a', self.addr, '-p', self.port) + params
|
||||
try:
|
||||
return utils.execute(*cmd)
|
||||
except OSError as e:
|
||||
|
@ -89,6 +101,36 @@ class SheepdogClient(object):
|
|||
stdout=e.stdout.replace('\n', '\\n'),
|
||||
stderr=e.stderr.replace('\n', '\\n'))
|
||||
|
||||
def _run_qemu_img(self, command, *params):
|
||||
"""Executes qemu-img command wrapper"""
|
||||
cmd = ['env', 'LC_ALL=C', 'LANG=C', 'qemu-img', command]
|
||||
for param in params:
|
||||
if param.startswith(self.QEMU_SHEEPDOG_PREFIX):
|
||||
# replace 'sheepdog:vdiname[:snapshotname]' to
|
||||
# 'sheepdog:addr:port:vdiname[:snapshotname]'
|
||||
param = param.replace(self.QEMU_SHEEPDOG_PREFIX,
|
||||
'%(prefix)s%(addr)s:%(port)s:' %
|
||||
{'prefix': self.QEMU_SHEEPDOG_PREFIX,
|
||||
'addr': self.addr, 'port': self.port},
|
||||
1)
|
||||
cmd.append(param)
|
||||
try:
|
||||
return utils.execute(*cmd)
|
||||
except OSError as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if e.errno == errno.ENOENT:
|
||||
msg = _LE('Qemu-img is not installed. '
|
||||
'OSError: command is %(cmd)s.')
|
||||
else:
|
||||
msg = _LE('OSError: command is %(cmd)s.')
|
||||
LOG.error(msg, {'cmd': tuple(cmd)})
|
||||
except processutils.ProcessExecutionError as e:
|
||||
raise exception.SheepdogCmdError(
|
||||
cmd=e.cmd,
|
||||
exit_code=e.exit_code,
|
||||
stdout=e.stdout.replace('\n', '\\n'),
|
||||
stderr=e.stderr.replace('\n', '\\n'))
|
||||
|
||||
def check_cluster_status(self):
|
||||
try:
|
||||
(_stdout, _stderr) = self._run_dog('cluster', 'info')
|
||||
|
@ -160,6 +202,124 @@ class SheepdogClient(object):
|
|||
else:
|
||||
LOG.error(_LE('Failed to delete volume. %s'), vdiname)
|
||||
|
||||
def create_snapshot(self, vdiname, snapname):
|
||||
try:
|
||||
self._run_dog('vdi', 'snapshot', '-s', snapname, vdiname)
|
||||
except exception.SheepdogCmdError as e:
|
||||
cmd = e.kwargs['cmd']
|
||||
_stderr = e.kwargs['stderr']
|
||||
with excutils.save_and_reraise_exception():
|
||||
if _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR):
|
||||
LOG.error(_LE('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s'),
|
||||
{'addr': self.addr, 'port': self.port})
|
||||
elif _stderr.rstrip('\\n').endswith(
|
||||
self.DOG_RESP_SNAPSHOT_VDI_NOT_FOUND):
|
||||
LOG.error(_LE('Volume "%s" not found. Please check the '
|
||||
'results of "dog vdi list".'),
|
||||
vdiname)
|
||||
elif _stderr.rstrip('\\n').endswith(
|
||||
self.DOG_RESP_SNAPSHOT_EXISTED %
|
||||
{'snapname': snapname}):
|
||||
LOG.error(_LE('Snapshot "%s" already exists.'), snapname)
|
||||
else:
|
||||
LOG.error(_LE('Failed to create snapshot. (command: %s)'),
|
||||
cmd)
|
||||
|
||||
def delete_snapshot(self, vdiname, snapname):
|
||||
try:
|
||||
(_stdout, _stderr) = self._run_dog('vdi', 'delete', '-s',
|
||||
snapname, vdiname)
|
||||
if _stderr.rstrip().endswith(self.DOG_RESP_SNAPSHOT_NOT_FOUND):
|
||||
LOG.warning(_LW('Snapshot "%s" not found.'), snapname)
|
||||
elif _stderr.rstrip().endswith(self.DOG_RESP_VDI_NOT_FOUND):
|
||||
LOG.warning(_LW('Volume "%s" not found.'), vdiname)
|
||||
elif _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR):
|
||||
# NOTE(tishizaki)
|
||||
# Dog command does not return error_code although
|
||||
# dog command cannot connect to sheep process.
|
||||
# That is a Sheepdog's bug.
|
||||
# To avoid a Sheepdog's bug, now we need to check stderr.
|
||||
# If Sheepdog has been fixed, this check logic is needed
|
||||
# by old Sheepdog users.
|
||||
reason = (_('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s'),
|
||||
{'addr': self.addr, 'port': self.port})
|
||||
raise exception.SheepdogError(reason=reason)
|
||||
except exception.SheepdogCmdError as e:
|
||||
cmd = e.kwargs['cmd']
|
||||
_stderr = e.kwargs['stderr']
|
||||
with excutils.save_and_reraise_exception():
|
||||
if _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR):
|
||||
msg = _LE('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s')
|
||||
LOG.error(msg, {'addr': self.addr, 'port': self.port})
|
||||
else:
|
||||
LOG.error(_LE('Failed to delete snapshot. (command: %s)'),
|
||||
cmd)
|
||||
|
||||
def clone(self, src_vdiname, src_snapname, dst_vdiname, size):
|
||||
try:
|
||||
self._run_qemu_img('create', '-b',
|
||||
'sheepdog:%(src_vdiname)s:%(src_snapname)s' %
|
||||
{'src_vdiname': src_vdiname,
|
||||
'src_snapname': src_snapname},
|
||||
'sheepdog:%s' % dst_vdiname, '%sG' % size)
|
||||
except exception.SheepdogCmdError as e:
|
||||
cmd = e.kwargs['cmd']
|
||||
_stderr = e.kwargs['stderr']
|
||||
with excutils.save_and_reraise_exception():
|
||||
if self.QEMU_IMG_RESP_CONNECTION_ERROR in _stderr:
|
||||
LOG.error(_LE('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s'),
|
||||
{'addr': self.addr, 'port': self.port})
|
||||
elif self.QEMU_IMG_RESP_ALREADY_EXISTS in _stderr:
|
||||
LOG.error(_LE('Clone volume "%s" already exists. '
|
||||
'Please check the results of "dog vdi list".'),
|
||||
dst_vdiname)
|
||||
elif self.QEMU_IMG_RESP_VDI_NOT_FOUND in _stderr:
|
||||
LOG.error(_LE('Src Volume "%s" not found. '
|
||||
'Please check the results of "dog vdi list".'),
|
||||
src_vdiname)
|
||||
elif self.QEMU_IMG_RESP_SNAPSHOT_NOT_FOUND in _stderr:
|
||||
LOG.error(_LE('Snapshot "%s" not found. '
|
||||
'Please check the results of "dog vdi list".'),
|
||||
src_snapname)
|
||||
elif self.QEMU_IMG_RESP_SIZE_TOO_LARGE in _stderr:
|
||||
LOG.error(_LE('Volume size "%sG" is too large.'), size)
|
||||
else:
|
||||
LOG.error(_LE('Failed to clone volume.(command: %s)'), cmd)
|
||||
|
||||
def resize(self, vdiname, size):
|
||||
size = int(size) * units.Gi
|
||||
try:
|
||||
(_stdout, _stderr) = self._run_dog('vdi', 'resize', vdiname, size)
|
||||
except exception.SheepdogCmdError as e:
|
||||
_stderr = e.kwargs['stderr']
|
||||
with excutils.save_and_reraise_exception():
|
||||
if _stderr.startswith(self.DOG_RESP_CONNECTION_ERROR):
|
||||
LOG.error(_LE('Failed to connect to sheep daemon. '
|
||||
'addr: %(addr)s, port: %(port)s'),
|
||||
{'addr': self.addr, 'port': self.port})
|
||||
elif _stderr.rstrip('\\n').endswith(
|
||||
self.DOG_RESP_VDI_NOT_FOUND):
|
||||
LOG.error(_LE('Failed to resize vdi. vdi not found. %s'),
|
||||
vdiname)
|
||||
elif _stderr.startswith(self.DOG_RESP_VDI_SHRINK_NOT_SUPPORT):
|
||||
LOG.error(_LE('Failed to resize vdi. '
|
||||
'Shrinking vdi not supported. '
|
||||
'vdi: %(vdiname)s new size: %(size)s'),
|
||||
{'vdiname': vdiname, 'size': size})
|
||||
elif _stderr.startswith(self.DOG_RESP_VDI_SIZE_TOO_LARGE):
|
||||
LOG.error(_LE('Failed to resize vdi. '
|
||||
'Too large volume size. '
|
||||
'vdi: %(vdiname)s new size: %(size)s'),
|
||||
{'vdiname': vdiname, 'size': size})
|
||||
else:
|
||||
LOG.error(_LE('Failed to resize vdi. '
|
||||
'vdi: %(vdiname)s new size: %(size)s'),
|
||||
{'vdiname': vdiname, 'size': size})
|
||||
|
||||
|
||||
class SheepdogIOWrapper(io.RawIOBase):
|
||||
"""File-like object with Sheepdog backend."""
|
||||
|
@ -318,7 +478,7 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
(label, ip, port, name) = image_location.split(":", 3)
|
||||
volume_ref = {'name': name, 'size': image_meta['size']}
|
||||
self.create_cloned_volume(volume, volume_ref)
|
||||
self._resize(volume)
|
||||
self.client.resize(volume.name, volume.size)
|
||||
|
||||
vol_path = self.local_path(volume)
|
||||
return {'provider_location': vol_path}, True
|
||||
|
@ -333,18 +493,18 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
'volume_size': src_vref['size'],
|
||||
}
|
||||
|
||||
self.create_snapshot(snapshot)
|
||||
self.client.create_snapshot(snapshot['volume_name'], snapshot_name)
|
||||
|
||||
try:
|
||||
# Create volume
|
||||
self.create_volume_from_snapshot(volume, snapshot)
|
||||
except processutils.ProcessExecutionError:
|
||||
msg = _('Failed to create cloned volume %s.') % volume['id']
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(msg)
|
||||
self.client.clone(snapshot['volume_name'], snapshot_name,
|
||||
volume.name, volume.size)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to create cloned volume %s.'),
|
||||
volume.name)
|
||||
finally:
|
||||
# Delete temp Snapshot
|
||||
self.delete_snapshot(snapshot)
|
||||
self.client.delete_snapshot(snapshot['volume_name'], snapshot_name)
|
||||
|
||||
def create_volume(self, volume):
|
||||
"""Create a sheepdog volume."""
|
||||
|
@ -352,23 +512,13 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a sheepdog volume from a snapshot."""
|
||||
self._try_execute('qemu-img', 'create', '-b',
|
||||
"sheepdog:%s:%s" % (snapshot['volume_name'],
|
||||
snapshot['name']),
|
||||
"sheepdog:%s" % volume['name'],
|
||||
'%sG' % volume['size'])
|
||||
self.client.clone(snapshot.volume_name, snapshot.name,
|
||||
volume.name, volume.size)
|
||||
|
||||
def delete_volume(self, volume):
|
||||
"""Delete a logical volume."""
|
||||
self.client.delete(volume.name)
|
||||
|
||||
def _resize(self, volume, size=None):
|
||||
if not size:
|
||||
size = int(volume['size']) * units.Gi
|
||||
|
||||
self._try_execute('collie', 'vdi', 'resize',
|
||||
volume['name'], size)
|
||||
|
||||
def copy_image_to_volume(self, context, volume, image_service, image_id):
|
||||
with image_utils.temporary_file() as tmp:
|
||||
# (wenhao): we don't need to convert to raw for sheepdog.
|
||||
|
@ -381,7 +531,7 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
# convert and store into sheepdog
|
||||
image_utils.convert_image(tmp, 'sheepdog:%s' % volume['name'],
|
||||
'raw')
|
||||
self._resize(volume)
|
||||
self.client.resize(volume.name, volume.size)
|
||||
|
||||
def copy_volume_to_image(self, context, volume, image_service, image_meta):
|
||||
"""Copy the volume to the specified image."""
|
||||
|
@ -404,13 +554,11 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
|
||||
def create_snapshot(self, snapshot):
|
||||
"""Create a sheepdog snapshot."""
|
||||
self._try_execute('qemu-img', 'snapshot', '-c', snapshot['name'],
|
||||
"sheepdog:%s" % snapshot['volume_name'])
|
||||
self.client.create_snapshot(snapshot.volume_name, snapshot.name)
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
"""Delete a sheepdog snapshot."""
|
||||
self._try_execute('collie', 'vdi', 'delete', snapshot['volume_name'],
|
||||
'-s', snapshot['name'])
|
||||
self.client.delete_snapshot(snapshot.volume_name, snapshot.name)
|
||||
|
||||
def local_path(self, volume):
|
||||
return "sheepdog:%s" % volume['name']
|
||||
|
@ -472,25 +620,14 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
|
||||
def extend_volume(self, volume, new_size):
|
||||
"""Extend an Existing Volume."""
|
||||
old_size = volume['size']
|
||||
|
||||
try:
|
||||
size = int(new_size) * units.Gi
|
||||
self._resize(volume, size=size)
|
||||
except Exception:
|
||||
msg = _('Failed to Extend Volume '
|
||||
'%(volname)s') % {'volname': volume['name']}
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
LOG.debug("Extend volume from %(old_size)s GB to %(new_size)s GB.",
|
||||
{'old_size': old_size, 'new_size': new_size})
|
||||
self.client.resize(volume.name, new_size)
|
||||
LOG.debug('Extend volume from %(old_size)s GB to %(new_size)s GB.',
|
||||
{'old_size': volume.size, 'new_size': new_size})
|
||||
|
||||
def backup_volume(self, context, backup, backup_service):
|
||||
"""Create a new backup from an existing volume."""
|
||||
volume = self.db.volume_get(context, backup['volume_id'])
|
||||
temp_snapshot = {'volume_name': volume['name'],
|
||||
'name': 'tmp-snap-%s' % volume['name']}
|
||||
src_volume = self.db.volume_get(context, backup.volume_id)
|
||||
temp_snapshot_name = 'tmp-snap-%s' % src_volume.name
|
||||
|
||||
# NOTE(tishizaki): If previous backup_volume operation has failed,
|
||||
# a temporary snapshot for previous operation may exist.
|
||||
|
@ -501,23 +638,23 @@ class SheepdogDriver(driver.VolumeDriver):
|
|||
# is failed, and raise ProcessExecutionError when target snapshot
|
||||
# does not exist.
|
||||
try:
|
||||
self.delete_snapshot(temp_snapshot)
|
||||
except (processutils.ProcessExecutionError):
|
||||
self.client.delete_snapshot(src_volume.name, temp_snapshot_name)
|
||||
except (exception.SheepdogCmdError):
|
||||
pass
|
||||
|
||||
try:
|
||||
self.create_snapshot(temp_snapshot)
|
||||
except (processutils.ProcessExecutionError, OSError):
|
||||
self.client.create_snapshot(src_volume.name, temp_snapshot_name)
|
||||
except (exception.SheepdogCmdError, OSError):
|
||||
msg = (_('Failed to create a temporary snapshot for volume %s.')
|
||||
% volume['id'])
|
||||
% src_volume.id)
|
||||
LOG.exception(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
raise exception.SheepdogError(reason=msg)
|
||||
|
||||
try:
|
||||
sheepdog_fd = SheepdogIOWrapper(volume, temp_snapshot['name'])
|
||||
sheepdog_fd = SheepdogIOWrapper(src_volume, temp_snapshot_name)
|
||||
backup_service.backup(backup, sheepdog_fd)
|
||||
finally:
|
||||
self.delete_snapshot(temp_snapshot)
|
||||
self.client.delete_snapshot(src_volume.name, temp_snapshot_name)
|
||||
|
||||
def restore_backup(self, context, backup, volume, backup_service):
|
||||
"""Restore an existing backup to a new or existing volume."""
|
||||
|
|
Loading…
Reference in New Issue