[ZFSonLinux] Add 'manage snapshot' feature support
Implement 'manage snapshot' feature in ZFSonLinux share driver. Prerequisites: - snapshot being managed should exist. Details: - snapshot being managed will be renamed by share driver based on taken ID. - size for managed snapshot will be taken either from REST API (--driver_options size=5) or from its "used" attribute. Implements BP zfsonlinux-manage-snapshot DocImpact Change-Id: If240ecde5676c334d39faaccd5703e93544aaa06 Depends-On: Ifc762a882a78159adacfe168a4edbe824178301a
This commit is contained in:
parent
9928ba0190
commit
d953baefcb
@ -171,6 +171,7 @@ elif [[ "$DRIVER" == "zfsonlinux" ]]; then
|
||||
MANILA_TEMPEST_CONCURRENCY=8
|
||||
RUN_MANILA_CG_TESTS=False
|
||||
RUN_MANILA_MANAGE_TESTS=True
|
||||
RUN_MANILA_MANAGE_SNAPSHOT_TESTS=True
|
||||
iniset $TEMPEST_CONFIG share run_migration_tests False
|
||||
iniset $TEMPEST_CONFIG share run_quota_tests True
|
||||
iniset $TEMPEST_CONFIG share run_replication_tests True
|
||||
|
@ -33,7 +33,7 @@ Mapping of share drivers and share features support
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| Driver name | create delete share | manage unmanage share | extend share | shrink share | create delete snapshot | create share from snapshot | manage unmanage snapshot |
|
||||
+========================================+=======================+=======================+==============+==============+========================+============================+==========================+
|
||||
| ZFSonLinux | M | N | M | M | M | M | \- |
|
||||
| ZFSonLinux | M | N | M | M | M | M | N |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
| Generic (Cinder as back-end) | J | K | L | L | J | J | M |
|
||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||
|
@ -48,6 +48,8 @@ The following operations are supported:
|
||||
* Deny NFS Share access
|
||||
* Create snapshot
|
||||
* Delete snapshot
|
||||
* Manage snapshot
|
||||
* Unmanage snapshot
|
||||
* Create share from snapshot
|
||||
* Extend share
|
||||
* Shrink share
|
||||
|
@ -18,6 +18,7 @@ Module with ZFSonLinux share driver that utilizes ZFS filesystem resources
|
||||
and exports them as shares.
|
||||
"""
|
||||
|
||||
import math
|
||||
import time
|
||||
|
||||
from oslo_config import cfg
|
||||
@ -492,6 +493,7 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
||||
}
|
||||
)
|
||||
self.zfs('snapshot', snapshot_name)
|
||||
return {"provider_location": snapshot_name}
|
||||
|
||||
@ensure_share_server_not_provided
|
||||
def delete_snapshot(self, context, snapshot, share_server=None):
|
||||
@ -519,7 +521,7 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
||||
_LW("Snapshot with '%(id)s' ID and '%(name)s' NAME is "
|
||||
"absent on backend. Nothing has been deleted."),
|
||||
{'id': snapshot['id'], 'name': snapshot_name})
|
||||
self.private_storage.delete(snapshot['id'])
|
||||
self.private_storage.delete(snapshot['snapshot_id'])
|
||||
|
||||
@ensure_share_server_not_provided
|
||||
def create_share_from_snapshot(self, context, share, snapshot,
|
||||
@ -723,6 +725,54 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
||||
"""Removes the specified share from Manila management."""
|
||||
self.private_storage.delete(share['id'])
|
||||
|
||||
def manage_existing_snapshot(self, snapshot_instance, driver_options):
|
||||
"""Manage existing share snapshot with manila.
|
||||
|
||||
:param snapshot_instance: SnapshotInstance data
|
||||
:param driver_options: expects only one optional key 'size'.
|
||||
:return: dict with share snapshot instance fields for update, example:
|
||||
{'size': 1,
|
||||
'provider_location': 'path/to/some/dataset@some_snapshot_tag'}
|
||||
"""
|
||||
snapshot_size = int(driver_options.get("size", 0))
|
||||
old_provider_location = snapshot_instance.get("provider_location")
|
||||
old_snapshot_tag = old_provider_location.split("@")[-1]
|
||||
new_snapshot_tag = self._get_snapshot_name(snapshot_instance["id"])
|
||||
|
||||
self.private_storage.update(
|
||||
snapshot_instance["snapshot_id"], {
|
||||
"entity_type": "snapshot",
|
||||
"old_snapshot_tag": old_snapshot_tag,
|
||||
"snapshot_tag": new_snapshot_tag,
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
self.zfs("list", "-r", "-t", "snapshot", old_provider_location)
|
||||
except exception.ProcessExecutionError as e:
|
||||
raise exception.ManageInvalidShareSnapshot(reason=e.stderr)
|
||||
|
||||
if not snapshot_size:
|
||||
consumed_space = self.get_zfs_option(old_provider_location, "used")
|
||||
consumed_space = utils.translate_string_size_to_float(
|
||||
consumed_space)
|
||||
snapshot_size = int(math.ceil(consumed_space))
|
||||
|
||||
dataset_name = self.private_storage.get(
|
||||
snapshot_instance["share_instance_id"], "dataset_name")
|
||||
new_provider_location = dataset_name + "@" + new_snapshot_tag
|
||||
|
||||
self.zfs("rename", old_provider_location, new_provider_location)
|
||||
|
||||
return {
|
||||
"size": snapshot_size,
|
||||
"provider_location": new_provider_location,
|
||||
}
|
||||
|
||||
def unmanage_snapshot(self, snapshot_instance):
|
||||
"""Unmanage dataset snapshot."""
|
||||
self.private_storage.delete(snapshot_instance["snapshot_id"])
|
||||
|
||||
def _get_replication_snapshot_prefix(self, replica):
|
||||
"""Returns replica-based snapshot prefix."""
|
||||
replication_snapshot_prefix = "%s_%s" % (
|
||||
|
@ -632,7 +632,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
||||
snapshot['share_instance_id'],
|
||||
{'dataset_name': 'foo_data_set_name'})
|
||||
|
||||
self.driver.create_snapshot('fake_context', snapshot)
|
||||
result = self.driver.create_snapshot('fake_context', snapshot)
|
||||
|
||||
self.driver.zfs.assert_called_once_with(
|
||||
'snapshot', snapshot_name)
|
||||
@ -640,6 +640,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
||||
snapshot_name.split('@')[-1],
|
||||
self.driver.private_storage.get(
|
||||
snapshot['snapshot_id'], 'snapshot_tag'))
|
||||
self.assertEqual({"provider_location": snapshot_name}, result)
|
||||
|
||||
def test_delete_snapshot(self):
|
||||
snapshot = {
|
||||
@ -1194,6 +1195,89 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
||||
|
||||
self.driver.private_storage.delete.assert_called_once_with(share['id'])
|
||||
|
||||
@ddt.data(
|
||||
{},
|
||||
{"size": 5},
|
||||
{"size": "5"},
|
||||
)
|
||||
def test_manage_existing_snapshot(self, driver_options):
|
||||
dataset_name = "path/to/dataset"
|
||||
old_provider_location = dataset_name + "@original_snapshot_tag"
|
||||
snapshot_instance = {
|
||||
"id": "fake_snapshot_instance_id",
|
||||
"share_instance_id": "fake_share_instance_id",
|
||||
"snapshot_id": "fake_snapshot_id",
|
||||
"provider_location": old_provider_location,
|
||||
}
|
||||
new_snapshot_tag = "fake_new_snapshot_tag"
|
||||
new_provider_location = (
|
||||
old_provider_location.split("@")[0] + "@" + new_snapshot_tag)
|
||||
|
||||
self.mock_object(self.driver, "zfs")
|
||||
self.mock_object(
|
||||
self.driver, "get_zfs_option", mock.Mock(return_value="5G"))
|
||||
self.mock_object(
|
||||
self.driver,
|
||||
'_get_snapshot_name',
|
||||
mock.Mock(return_value=new_snapshot_tag))
|
||||
self.driver.private_storage.update(
|
||||
snapshot_instance["share_instance_id"],
|
||||
{"dataset_name": dataset_name})
|
||||
|
||||
result = self.driver.manage_existing_snapshot(
|
||||
snapshot_instance, driver_options)
|
||||
|
||||
expected_result = {
|
||||
"size": 5,
|
||||
"provider_location": new_provider_location,
|
||||
}
|
||||
self.assertEqual(expected_result, result)
|
||||
self.driver._get_snapshot_name.assert_called_once_with(
|
||||
snapshot_instance["id"])
|
||||
self.driver.zfs.assert_has_calls([
|
||||
mock.call("list", "-r", "-t", "snapshot", old_provider_location),
|
||||
mock.call("rename", old_provider_location, new_provider_location),
|
||||
])
|
||||
|
||||
def test_manage_existing_snapshot_not_found(self):
|
||||
dataset_name = "path/to/dataset"
|
||||
old_provider_location = dataset_name + "@original_snapshot_tag"
|
||||
new_snapshot_tag = "fake_new_snapshot_tag"
|
||||
snapshot_instance = {
|
||||
"id": "fake_snapshot_instance_id",
|
||||
"snapshot_id": "fake_snapshot_id",
|
||||
"provider_location": old_provider_location,
|
||||
}
|
||||
self.mock_object(
|
||||
self.driver, "_get_snapshot_name",
|
||||
mock.Mock(return_value=new_snapshot_tag))
|
||||
self.mock_object(
|
||||
self.driver, "zfs",
|
||||
mock.Mock(side_effect=exception.ProcessExecutionError("FAKE")))
|
||||
|
||||
self.assertRaises(
|
||||
exception.ManageInvalidShareSnapshot,
|
||||
self.driver.manage_existing_snapshot,
|
||||
snapshot_instance, {},
|
||||
)
|
||||
|
||||
self.driver.zfs.assert_called_once_with(
|
||||
"list", "-r", "-t", "snapshot", old_provider_location)
|
||||
self.driver._get_snapshot_name.assert_called_once_with(
|
||||
snapshot_instance["id"])
|
||||
|
||||
def test_unmanage_snapshot(self):
|
||||
snapshot_instance = {
|
||||
"id": "fake_snapshot_instance_id",
|
||||
"snapshot_id": "fake_snapshot_id",
|
||||
}
|
||||
self.mock_object(self.driver.private_storage, "delete")
|
||||
|
||||
self.driver.unmanage_snapshot(snapshot_instance)
|
||||
|
||||
self.driver.private_storage.delete.assert_called_once_with(
|
||||
snapshot_instance["snapshot_id"])
|
||||
|
||||
def test__delete_dataset_or_snapshot_with_retry_snapshot(self):
|
||||
self.mock_object(self.driver, 'get_zfs_option')
|
||||
self.mock_object(self.driver, 'zfs')
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added support of 'manage snapshot' feature to ZFSonLinux driver.
|
Loading…
Reference in New Issue
Block a user