[ZFSonLinux] Create share from snapshot in different backends
This patch improves the operation of create share from snapshot to support different backends/hosts for the zfsonlinux driver Partially-implements: bp create-share-from-snapshot-in-another-pool-or-backend Depends-On: Iab13a0961eb4a387a502246e5d4b79bc9046e04b Change-Id: I124803734c81d3630c5147f5f3bb75724489c929
This commit is contained in:
parent
a43f0666c8
commit
8f8ddfd670
contrib/ci
manila
releasenotes/notes
@ -243,6 +243,7 @@ elif [[ "$DRIVER" == "zfsonlinux" ]]; then
|
|||||||
iniset $TEMPEST_CONFIG share image_with_share_tools 'manila-service-image-master'
|
iniset $TEMPEST_CONFIG share image_with_share_tools 'manila-service-image-master'
|
||||||
iniset $TEMPEST_CONFIG auth use_dynamic_credentials True
|
iniset $TEMPEST_CONFIG auth use_dynamic_credentials True
|
||||||
iniset $TEMPEST_CONFIG share capability_snapshot_support True
|
iniset $TEMPEST_CONFIG share capability_snapshot_support True
|
||||||
|
iniset $TEMPEST_CONFIG share run_create_share_from_snapshot_in_another_pool_or_az_tests True
|
||||||
elif [[ "$DRIVER" == "dummy" ]]; then
|
elif [[ "$DRIVER" == "dummy" ]]; then
|
||||||
MANILA_TEMPEST_CONCURRENCY=24
|
MANILA_TEMPEST_CONCURRENCY=24
|
||||||
MANILA_CONFIGURE_DEFAULT_TYPES=False
|
MANILA_CONFIGURE_DEFAULT_TYPES=False
|
||||||
|
@ -158,6 +158,8 @@ elif [[ "$DRIVER" == "zfsonlinux" ]]; then
|
|||||||
MANILA_SERVICE_IMAGE_ENABLED=True
|
MANILA_SERVICE_IMAGE_ENABLED=True
|
||||||
echo "SHARE_DRIVER=manila.share.drivers.zfsonlinux.driver.ZFSonLinuxShareDriver" >> $localconf
|
echo "SHARE_DRIVER=manila.share.drivers.zfsonlinux.driver.ZFSonLinuxShareDriver" >> $localconf
|
||||||
echo "RUN_MANILA_REPLICATION_TESTS=True" >> $localconf
|
echo "RUN_MANILA_REPLICATION_TESTS=True" >> $localconf
|
||||||
|
# Enable using the scheduler when creating a share from snapshot
|
||||||
|
echo "MANILA_USE_SCHEDULER_CREATING_SHARE_FROM_SNAPSHOT=True" >> $localconf
|
||||||
# Set the replica_state_update_interval to 60 seconds to make
|
# Set the replica_state_update_interval to 60 seconds to make
|
||||||
# replication tests run faster. The default is 300, which is greater than
|
# replication tests run faster. The default is 300, which is greater than
|
||||||
# the build timeout for ZFS on the gate.
|
# the build timeout for ZFS on the gate.
|
||||||
|
@ -582,31 +582,48 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
|||||||
def create_share_from_snapshot(self, context, share, snapshot,
|
def create_share_from_snapshot(self, context, share, snapshot,
|
||||||
share_server=None):
|
share_server=None):
|
||||||
"""Is called to create a share from snapshot."""
|
"""Is called to create a share from snapshot."""
|
||||||
|
src_backend_name = share_utils.extract_host(
|
||||||
|
snapshot.share_instance['host'], level='backend_name'
|
||||||
|
)
|
||||||
|
src_snapshot_name = self._get_saved_snapshot_name(snapshot)
|
||||||
dataset_name = self._get_dataset_name(share)
|
dataset_name = self._get_dataset_name(share)
|
||||||
ssh_cmd = '%(username)s@%(host)s' % {
|
|
||||||
|
dst_backend_ssh_cmd = '%(username)s@%(host)s' % {
|
||||||
'username': self.configuration.zfs_ssh_username,
|
'username': self.configuration.zfs_ssh_username,
|
||||||
'host': self.service_ip,
|
'host': self.service_ip,
|
||||||
}
|
}
|
||||||
pool_name = share_utils.extract_host(share['host'], level='pool')
|
|
||||||
|
dst_backend_pool_name = share_utils.extract_host(share['host'],
|
||||||
|
level='pool')
|
||||||
options = self._get_dataset_creation_options(share, is_readonly=False)
|
options = self._get_dataset_creation_options(share, is_readonly=False)
|
||||||
|
|
||||||
self.private_storage.update(
|
self.private_storage.update(
|
||||||
share['id'], {
|
share['id'], {
|
||||||
'entity_type': 'share',
|
'entity_type': 'share',
|
||||||
'dataset_name': dataset_name,
|
'dataset_name': dataset_name,
|
||||||
'ssh_cmd': ssh_cmd, # used in replication
|
'ssh_cmd': dst_backend_ssh_cmd, # used in replication
|
||||||
'pool_name': pool_name, # used in replication
|
'pool_name': dst_backend_pool_name, # used in replication
|
||||||
'used_options': options,
|
'used_options': options,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
snapshot_name = self._get_saved_snapshot_name(snapshot)
|
|
||||||
|
|
||||||
|
# NOTE(andrebeltrami): Implementing the support for create share
|
||||||
|
# from snapshot in different backends in different hosts
|
||||||
|
src_config = get_backend_configuration(src_backend_name)
|
||||||
|
src_backend_ssh_cmd = '%(username)s@%(host)s' % {
|
||||||
|
'username': src_config.zfs_ssh_username,
|
||||||
|
'host': src_config.zfs_service_ip,
|
||||||
|
}
|
||||||
self.execute(
|
self.execute(
|
||||||
# NOTE(vponomaryov): SSH is used as workaround for 'execute'
|
# NOTE(vponomaryov): SSH is used as workaround for 'execute'
|
||||||
# implementation restriction that does not support usage of '|'.
|
# implementation restriction that does not support usage
|
||||||
'ssh', ssh_cmd,
|
# of '|'.
|
||||||
'sudo', 'zfs', 'send', '-vD', snapshot_name, '|',
|
'ssh', src_backend_ssh_cmd,
|
||||||
|
'sudo', 'zfs', 'send', '-vD', src_snapshot_name, '|',
|
||||||
|
'ssh', dst_backend_ssh_cmd,
|
||||||
'sudo', 'zfs', 'receive', '-v', dataset_name,
|
'sudo', 'zfs', 'receive', '-v', dataset_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apply options based on used share type that may differ from
|
# Apply options based on used share type that may differ from
|
||||||
# one used for original share.
|
# one used for original share.
|
||||||
for option in options:
|
for option in options:
|
||||||
@ -615,7 +632,7 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
|||||||
# Delete with retry as right after creation it may be temporary busy.
|
# Delete with retry as right after creation it may be temporary busy.
|
||||||
self.execute_with_retry(
|
self.execute_with_retry(
|
||||||
'sudo', 'zfs', 'destroy',
|
'sudo', 'zfs', 'destroy',
|
||||||
dataset_name + '@' + snapshot_name.split('@')[-1])
|
dataset_name + '@' + src_snapshot_name.split('@')[-1])
|
||||||
|
|
||||||
return self._get_share_helper(
|
return self._get_share_helper(
|
||||||
share['share_proto']).create_exports(dataset_name)
|
share['share_proto']).create_exports(dataset_name)
|
||||||
|
@ -23,6 +23,7 @@ from manila import exception
|
|||||||
from manila.share.drivers.ganesha import utils as ganesha_utils
|
from manila.share.drivers.ganesha import utils as ganesha_utils
|
||||||
from manila.share.drivers.zfsonlinux import driver as zfs_driver
|
from manila.share.drivers.zfsonlinux import driver as zfs_driver
|
||||||
from manila import test
|
from manila import test
|
||||||
|
from manila.tests import db_utils
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
@ -792,7 +793,13 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
|||||||
share_server={'id': 'fake_server'},
|
share_server={'id': 'fake_server'},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_create_share_from_snapshot(self):
|
@ddt.data({'src_backend_name': 'backend_a', 'src_user': 'someuser',
|
||||||
|
'src_ip': '2.2.2.2'},
|
||||||
|
{'src_backend_name': 'backend_b', 'src_user': 'someuser2',
|
||||||
|
'src_ip': '3.3.3.3'})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_create_share_from_snapshot(self, src_backend_name, src_user,
|
||||||
|
src_ip):
|
||||||
mock_get_helper = self.mock_object(self.driver, '_get_share_helper')
|
mock_get_helper = self.mock_object(self.driver, '_get_share_helper')
|
||||||
self.mock_object(self.driver, 'zfs')
|
self.mock_object(self.driver, 'zfs')
|
||||||
self.mock_object(self.driver, 'execute')
|
self.mock_object(self.driver, 'execute')
|
||||||
@ -801,19 +808,31 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
|||||||
'get_extra_specs_from_share',
|
'get_extra_specs_from_share',
|
||||||
mock.Mock(return_value={}))
|
mock.Mock(return_value={}))
|
||||||
context = 'fake_context'
|
context = 'fake_context'
|
||||||
share = {
|
dst_backend_name = 'backend_a'
|
||||||
'id': 'fake_share_id',
|
parent_share = db_utils.create_share_without_instance(
|
||||||
'host': 'hostname@backend_name#bar',
|
id='fake_share_id_1',
|
||||||
'share_proto': 'NFS',
|
size=4
|
||||||
'size': 4,
|
)
|
||||||
}
|
parent_instance = db_utils.create_share_instance(
|
||||||
snapshot = {
|
id='fake_parent_instance',
|
||||||
'id': 'fake_snapshot_instance_id',
|
share_id=parent_share['id'],
|
||||||
'snapshot_id': 'fake_snapshot_id',
|
host='hostname@%s#bar' % src_backend_name
|
||||||
'host': 'hostname@backend_name#bar',
|
)
|
||||||
'size': 4,
|
share = db_utils.create_share(
|
||||||
'share_instance_id': share['id'],
|
id='fake_share_id_2',
|
||||||
}
|
host='hostname@%s#bar' % dst_backend_name,
|
||||||
|
size=4
|
||||||
|
)
|
||||||
|
snapshot = db_utils.create_snapshot(
|
||||||
|
id='fake_snap_id_1',
|
||||||
|
share_id='fake_share_id_1'
|
||||||
|
)
|
||||||
|
snap_instance = db_utils.create_snapshot_instance(
|
||||||
|
id='fake_snap_instance',
|
||||||
|
snapshot_id=snapshot['id'],
|
||||||
|
share_instance_id=parent_instance['id']
|
||||||
|
)
|
||||||
|
|
||||||
dataset_name = 'bar/subbar/some_prefix_%s' % share['id']
|
dataset_name = 'bar/subbar/some_prefix_%s' % share['id']
|
||||||
snap_tag = 'prefix_%s' % snapshot['id']
|
snap_tag = 'prefix_%s' % snapshot['id']
|
||||||
snap_name = '%(dataset)s@%(tag)s' % {
|
snap_name = '%(dataset)s@%(tag)s' % {
|
||||||
@ -823,43 +842,60 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
|||||||
self.driver.share_export_ip = '1.1.1.1'
|
self.driver.share_export_ip = '1.1.1.1'
|
||||||
self.driver.service_ip = '2.2.2.2'
|
self.driver.service_ip = '2.2.2.2'
|
||||||
self.driver.private_storage.update(
|
self.driver.private_storage.update(
|
||||||
snapshot['id'], {'snapshot_name': snap_name})
|
snap_instance['id'], {'snapshot_name': snap_name})
|
||||||
self.driver.private_storage.update(
|
self.driver.private_storage.update(
|
||||||
snapshot['snapshot_id'], {'snapshot_tag': snap_tag})
|
snap_instance['snapshot_id'], {'snapshot_tag': snap_tag})
|
||||||
self.driver.private_storage.update(
|
self.driver.private_storage.update(
|
||||||
snapshot['share_instance_id'], {'dataset_name': dataset_name})
|
snap_instance['share_instance_id'],
|
||||||
|
{'dataset_name': dataset_name})
|
||||||
|
|
||||||
|
self.mock_object(
|
||||||
|
zfs_driver, 'get_backend_configuration',
|
||||||
|
mock.Mock(return_value=type(
|
||||||
|
'FakeConfig', (object,), {
|
||||||
|
'zfs_ssh_username': src_user,
|
||||||
|
'zfs_service_ip': src_ip
|
||||||
|
})))
|
||||||
|
|
||||||
result = self.driver.create_share_from_snapshot(
|
result = self.driver.create_share_from_snapshot(
|
||||||
context, share, snapshot, share_server=None)
|
context, share, snap_instance, share_server=None)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
mock_get_helper.return_value.create_exports.return_value,
|
mock_get_helper.return_value.create_exports.return_value,
|
||||||
result,
|
result,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
dst_ssh_host = (self.configuration.zfs_ssh_username +
|
||||||
|
'@' + self.driver.service_ip)
|
||||||
|
src_ssh_host = src_user + '@' + src_ip
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'share',
|
'share',
|
||||||
self.driver.private_storage.get(share['id'], 'entity_type'))
|
self.driver.private_storage.get(share['id'], 'entity_type'))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
dataset_name,
|
dataset_name,
|
||||||
self.driver.private_storage.get(share['id'], 'dataset_name'))
|
self.driver.private_storage.get(
|
||||||
|
snap_instance['share_instance_id'], 'dataset_name'))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'someuser@2.2.2.2',
|
dst_ssh_host,
|
||||||
self.driver.private_storage.get(share['id'], 'ssh_cmd'))
|
self.driver.private_storage.get(share['id'], 'ssh_cmd'))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'bar',
|
'bar',
|
||||||
self.driver.private_storage.get(share['id'], 'pool_name'))
|
self.driver.private_storage.get(share['id'], 'pool_name'))
|
||||||
|
|
||||||
self.driver.execute.assert_has_calls([
|
self.driver.execute.assert_has_calls([
|
||||||
mock.call(
|
mock.call(
|
||||||
'ssh', 'someuser@2.2.2.2',
|
'ssh', src_ssh_host,
|
||||||
'sudo', 'zfs', 'send', '-vD', snap_name, '|',
|
'sudo', 'zfs', 'send', '-vD', snap_name, '|',
|
||||||
|
'ssh', dst_ssh_host,
|
||||||
'sudo', 'zfs', 'receive', '-v',
|
'sudo', 'zfs', 'receive', '-v',
|
||||||
'bar/subbar/some_prefix_fake_share_id'),
|
'%s' % dataset_name),
|
||||||
mock.call(
|
mock.call(
|
||||||
'sudo', 'zfs', 'destroy',
|
'sudo', 'zfs', 'destroy',
|
||||||
'bar/subbar/some_prefix_fake_share_id@%s' % snap_tag),
|
'%s@%s' % (dataset_name, snap_tag)),
|
||||||
])
|
])
|
||||||
|
|
||||||
self.driver.zfs.assert_has_calls([
|
self.driver.zfs.assert_has_calls([
|
||||||
mock.call('set', opt, 'bar/subbar/some_prefix_fake_share_id')
|
mock.call('set', opt, '%s' % dataset_name)
|
||||||
for opt in ('quota=4G', 'bark=barv', 'readonly=off', 'fook=foov')
|
for opt in ('quota=4G', 'bark=barv', 'readonly=off', 'fook=foov')
|
||||||
], any_order=True)
|
], any_order=True)
|
||||||
mock_get_helper.assert_has_calls([
|
mock_get_helper.assert_has_calls([
|
||||||
|
7
releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml
Normal file
7
releasenotes/notes/zfsonlinux-driver-improvement-create-share-from-snapshot-another-backend-44296f572681be35.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
In this release, the operation create share from snapshot was improved
|
||||||
|
in the ZFSonLinux driver. Now, the operator using the ZFSonLinux driver
|
||||||
|
can create a share from snapshot in different pools or backends by
|
||||||
|
specifying the Manila API configuration option
|
||||||
|
[DEFAULT]/use_scheduler_creating_share_from_snapshot.
|
Loading…
x
Reference in New Issue
Block a user