Merge pull request #45603 from nmshelke/feature-54472

mgr/volumes: set, get, list and remove metadata of subvolume

Reviewed-by: Venky Shankar <vshankar@redhat.com>
This commit is contained in:
Venky Shankar 2022-04-19 16:52:13 +05:30 committed by GitHub
commit 3f13df8388
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 717 additions and 26 deletions

View File

@ -212,7 +212,7 @@ Fetch the absolute path of a subvolume using::
$ ceph fs subvolume getpath <vol_name> <subvol_name> [--group_name <subvol_group_name>]
Fetch the metadata of a subvolume using::
Fetch the information of a subvolume using::
$ ceph fs subvolume info <vol_name> <subvol_name> [--group_name <subvol_group_name>]
@ -260,6 +260,31 @@ List subvolumes using::
.. note:: subvolumes that are removed but have snapshots retained, are also listed.
Set custom metadata on the subvolume as a key-value pair using::
$ ceph fs subvolume metadata set <vol_name> <subvol_name> <key_name> <value> [--group_name <subvol_group_name>]
.. note:: If the key_name already exists then the old value will get replaced by the new value.
.. note:: key_name and value should be a string of ASCII characters (as specified in python's string.printable). key_name is case-insensitive and always stored in lower case.
.. note:: Custom metadata on a subvolume is not preserved when snapshotting the subvolume, and hence, is also not preserved when cloning the subvolume snapshot.
Get custom metadata set on the subvolume using the metadata key::
$ ceph fs subvolume metadata get <vol_name> <subvol_name> <key_name> [--group_name <subvol_group_name>]
List custom metadata (key-value pairs) set on the subvolume using::
$ ceph fs subvolume metadata ls <vol_name> <subvol_name> [--group_name <subvol_group_name>]
Remove custom metadata set on the subvolume using the metadata key::
$ ceph fs subvolume metadata rm <vol_name> <subvol_name> <key_name> [--group_name <subvol_group_name>] [--force]
Using the '--force' flag allows the command to succeed that would otherwise
fail if the metadata key did not exist.
Create a snapshot of a subvolume using::
$ ceph fs subvolume snapshot create <vol_name> <subvol_name> <snap_name> [--group_name <subvol_group_name>]

View File

@ -2484,6 +2484,492 @@ class TestSubvolumes(TestVolumesHelper):
self._wait_for_trash_empty()
def test_subvolume_user_metadata_set(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
try:
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata set' command to succeed")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_set_idempotence(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
try:
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata set' command to succeed")
# set same metadata again for subvolume.
try:
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata set' command to succeed because it is idempotent operation")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_get(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# get value for specified key.
try:
ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata get' command to succeed")
# remove '\n' from returned value.
ret = ret.strip('\n')
# match received value with expected value.
self.assertEqual(value, ret)
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_get_for_nonexisting_key(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# try to get value for nonexisting key
# Expecting ENOENT exit status because key does not exist
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key_nonexist", "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because 'key_nonexist' does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_get_for_nonexisting_section(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# try to get value for nonexisting key (as section does not exist)
# Expecting ENOENT exit status because key does not exist
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key", "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because section does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_update(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# update metadata against key.
new_value = "new_value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, new_value, "--group_name", group)
# get metadata for specified key of subvolume.
try:
ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata get' command to succeed")
# remove '\n' from returned value.
ret = ret.strip('\n')
# match received value with expected value.
self.assertEqual(new_value, ret)
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_list(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
input_metadata_dict = {f'key_{i}' : f'value_{i}' for i in range(3)}
for k, v in input_metadata_dict.items():
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, k, v, "--group_name", group)
# list metadata
try:
ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata ls' command to succeed")
ret_dict = json.loads(ret)
# compare output with expected output
self.assertDictEqual(input_metadata_dict, ret_dict)
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_list_if_no_metadata_set(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# list metadata
try:
ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata ls' command to succeed")
# remove '\n' from returned value.
ret = ret.strip('\n')
# compare output with expected output
# expecting empty json/dictionary
self.assertEqual(ret, "{}")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_remove(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# remove metadata against specified key.
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata rm' command to succeed")
# confirm key is removed by again fetching metadata
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because key does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_remove_for_nonexisting_key(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# try to remove value for nonexisting key
# Expecting ENOENT exit status because key does not exist
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key_nonexist", "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because 'key_nonexist' does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_remove_for_nonexisting_section(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# try to remove value for nonexisting key (as section does not exist)
# Expecting ENOENT exit status because key does not exist
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key", "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because section does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_remove_force(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# remove metadata against specified key with --force option.
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group, "--force")
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata rm' command to succeed")
# confirm key is removed by again fetching metadata
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because key does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_remove_force_for_nonexisting_key(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# create group.
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group.
self._fs_cmd("subvolume", "create", self.volname, subvolname, "--group_name", group)
# set metadata for subvolume.
key = "key"
value = "value"
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
# remove metadata against specified key.
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata rm' command to succeed")
# confirm key is removed by again fetching metadata
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because key does not exist")
# again remove metadata against already removed key with --force option.
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, key, "--group_name", group, "--force")
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata rm' (with --force) command to succeed")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_set_and_get_for_legacy_subvolume(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# emulate a old-fashioned subvolume in a custom group
createpath = os.path.join(".", "volumes", group, subvolname)
self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True)
# set metadata for subvolume.
key = "key"
value = "value"
try:
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, key, value, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata set' command to succeed")
# get value for specified key.
try:
ret = self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, key, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata get' command to succeed")
# remove '\n' from returned value.
ret = ret.strip('\n')
# match received value with expected value.
self.assertEqual(value, ret)
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
def test_subvolume_user_metadata_list_and_remove_for_legacy_subvolume(self):
subvolname = self._generate_random_subvolume_name()
group = self._generate_random_group_name()
# emulate a old-fashioned subvolume in a custom group
createpath = os.path.join(".", "volumes", group, subvolname)
self.mount_a.run_shell(['mkdir', '-p', createpath], sudo=True)
# set metadata for subvolume.
input_metadata_dict = {f'key_{i}' : f'value_{i}' for i in range(3)}
for k, v in input_metadata_dict.items():
self._fs_cmd("subvolume", "metadata", "set", self.volname, subvolname, k, v, "--group_name", group)
# list metadata
try:
ret = self._fs_cmd("subvolume", "metadata", "ls", self.volname, subvolname, "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata ls' command to succeed")
ret_dict = json.loads(ret)
# compare output with expected output
self.assertDictEqual(input_metadata_dict, ret_dict)
# remove metadata against specified key.
try:
self._fs_cmd("subvolume", "metadata", "rm", self.volname, subvolname, "key_1", "--group_name", group)
except CommandFailedError:
self.fail("expected the 'fs subvolume metadata rm' command to succeed")
# confirm key is removed by again fetching metadata
try:
self._fs_cmd("subvolume", "metadata", "get", self.volname, subvolname, "key_1", "--group_name", group)
except CommandFailedError as e:
self.assertEqual(e.exitstatus, errno.ENOENT)
else:
self.fail("Expected ENOENT because key_1 does not exist")
self._fs_cmd("subvolume", "rm", self.volname, subvolname, group)
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
# verify trash dir is clean.
self._wait_for_trash_empty()
class TestSubvolumeGroupSnapshots(TestVolumesHelper):
"""Tests for FS subvolume group snapshot operations."""
@unittest.skip("skipping subvolumegroup snapshot tests")

View File

@ -37,29 +37,33 @@ class GroupTemplate(object):
@unique
class SubvolumeOpType(Enum):
CREATE = 'create'
REMOVE = 'rm'
REMOVE_FORCE = 'rm-force'
PIN = 'pin'
LIST = 'ls'
GETPATH = 'getpath'
INFO = 'info'
RESIZE = 'resize'
SNAP_CREATE = 'snap-create'
SNAP_REMOVE = 'snap-rm'
SNAP_LIST = 'snap-ls'
SNAP_INFO = 'snap-info'
SNAP_PROTECT = 'snap-protect'
SNAP_UNPROTECT = 'snap-unprotect'
CLONE_SOURCE = 'clone-source'
CLONE_CREATE = 'clone-create'
CLONE_STATUS = 'clone-status'
CLONE_CANCEL = 'clone-cancel'
CLONE_INTERNAL = 'clone_internal'
ALLOW_ACCESS = 'allow-access'
DENY_ACCESS = 'deny-access'
AUTH_LIST = 'auth-list'
EVICT = 'evict'
CREATE = 'create'
REMOVE = 'rm'
REMOVE_FORCE = 'rm-force'
PIN = 'pin'
LIST = 'ls'
GETPATH = 'getpath'
INFO = 'info'
RESIZE = 'resize'
SNAP_CREATE = 'snap-create'
SNAP_REMOVE = 'snap-rm'
SNAP_LIST = 'snap-ls'
SNAP_INFO = 'snap-info'
SNAP_PROTECT = 'snap-protect'
SNAP_UNPROTECT = 'snap-unprotect'
CLONE_SOURCE = 'clone-source'
CLONE_CREATE = 'clone-create'
CLONE_STATUS = 'clone-status'
CLONE_CANCEL = 'clone-cancel'
CLONE_INTERNAL = 'clone_internal'
ALLOW_ACCESS = 'allow-access'
DENY_ACCESS = 'deny-access'
AUTH_LIST = 'auth-list'
EVICT = 'evict'
USER_METADATA_SET = 'metadata-set'
USER_METADATA_GET = 'metadata-get'
USER_METADATA_LIST = 'metadata-ls'
USER_METADATA_REMOVE = 'metadata-rm'
class SubvolumeTemplate(object):
VERSION = None # type: int

View File

@ -21,6 +21,7 @@ log = logging.getLogger(__name__)
class MetadataManager(object):
GLOBAL_SECTION = "GLOBAL"
USER_METADATA_SECTION = "USER_METADATA"
GLOBAL_META_KEY_VERSION = "version"
GLOBAL_META_KEY_TYPE = "type"
GLOBAL_META_KEY_PATH = "path"
@ -113,7 +114,7 @@ class MetadataManager(object):
def remove_option(self, section, key):
if not self.config.has_section(section):
raise MetadataMgrException(-errno.ENOENT, "section '{0}' does not exist".format(section))
self.config.remove_option(section, key)
return self.config.remove_option(section, key)
def remove_section(self, section):
self.config.remove_section(section)
@ -142,6 +143,14 @@ class MetadataManager(object):
def get_global_option(self, key):
return self.get_option(MetadataManager.GLOBAL_SECTION, key)
def list_all_options_from_section(self, section):
metadata_dict = {}
if self.config.has_section(section):
options = self.config.options(section)
for option in options:
metadata_dict[option] = self.config.get(section,option)
return metadata_dict
def section_has_item(self, section, item):
if not self.config.has_section(section):
raise MetadataMgrException(-errno.ENOENT, "section '{0}' does not exist".format(section))

View File

@ -405,3 +405,31 @@ class SubvolumeBase(object):
else '{0:.2f}'.format((float(usedbytes) / nsize) * 100.0),
'pool_namespace': pool_namespace,
'features': self.features, 'state': self.state.value}
def set_user_metadata(self, keyname, value):
self.metadata_mgr.add_section(MetadataManager.USER_METADATA_SECTION)
self.metadata_mgr.update_section(MetadataManager.USER_METADATA_SECTION, keyname, str(value))
self.metadata_mgr.flush()
def get_user_metadata(self, keyname):
try:
value = self.metadata_mgr.get_option(MetadataManager.USER_METADATA_SECTION, keyname)
except MetadataMgrException as me:
if me.errno == -errno.ENOENT:
raise VolumeException(-errno.ENOENT, "key '{0}' does not exist.".format(keyname))
raise VolumeException(-me.args[0], me.args[1])
return value
def list_user_metadata(self):
return self.metadata_mgr.list_all_options_from_section(MetadataManager.USER_METADATA_SECTION)
def remove_user_metadata(self, keyname):
try:
ret = self.metadata_mgr.remove_option(MetadataManager.USER_METADATA_SECTION, keyname)
if not ret:
raise VolumeException(-errno.ENOENT, "key '{0}' does not exist.".format(keyname))
self.metadata_mgr.flush()
except MetadataMgrException as me:
if me.errno == -errno.ENOENT:
raise VolumeException(-errno.ENOENT, "subvolume metadata not does not exist")
raise VolumeException(-me.args[0], me.args[1])

View File

@ -372,6 +372,7 @@ class SubvolumeV2(SubvolumeV1):
return
if self.state != SubvolumeStates.STATE_RETAINED:
self.trash_incarnation_dir()
self.metadata_mgr.remove_section(MetadataManager.USER_METADATA_SECTION)
self.metadata_mgr.update_global_section(MetadataManager.GLOBAL_META_KEY_PATH, "")
self.metadata_mgr.update_global_section(MetadataManager.GLOBAL_META_KEY_STATE, SubvolumeStates.STATE_RETAINED.value)
self.metadata_mgr.flush()

View File

@ -383,6 +383,74 @@ class VolumeClient(CephfsClient["Module"]):
ret = self.volume_exception_to_retval(ve)
return ret
def set_user_metadata(self, **kwargs):
ret = 0, "", ""
volname = kwargs['vol_name']
subvolname = kwargs['sub_name']
groupname = kwargs['group_name']
keyname = kwargs['key_name']
value = kwargs['value']
try:
with open_volume(self, volname) as fs_handle:
with open_group(fs_handle, self.volspec, groupname) as group:
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_SET) as subvolume:
subvolume.set_user_metadata(keyname, value)
except VolumeException as ve:
ret = self.volume_exception_to_retval(ve)
return ret
def get_user_metadata(self, **kwargs):
ret = 0, "", ""
volname = kwargs['vol_name']
subvolname = kwargs['sub_name']
groupname = kwargs['group_name']
keyname = kwargs['key_name']
try:
with open_volume(self, volname) as fs_handle:
with open_group(fs_handle, self.volspec, groupname) as group:
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_GET) as subvolume:
value = subvolume.get_user_metadata(keyname)
ret = 0, value, ""
except VolumeException as ve:
ret = self.volume_exception_to_retval(ve)
return ret
def list_user_metadata(self, **kwargs):
ret = 0, "", ""
volname = kwargs['vol_name']
subvolname = kwargs['sub_name']
groupname = kwargs['group_name']
try:
with open_volume(self, volname) as fs_handle:
with open_group(fs_handle, self.volspec, groupname) as group:
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_LIST) as subvolume:
subvol_metadata_dict = subvolume.list_user_metadata()
ret = 0, json.dumps(subvol_metadata_dict, indent=4, sort_keys=True), ""
except VolumeException as ve:
ret = self.volume_exception_to_retval(ve)
return ret
def remove_user_metadata(self, **kwargs):
ret = 0, "", ""
volname = kwargs['vol_name']
subvolname = kwargs['sub_name']
groupname = kwargs['group_name']
keyname = kwargs['key_name']
force = kwargs['force']
try:
with open_volume(self, volname) as fs_handle:
with open_group(fs_handle, self.volspec, groupname) as group:
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_REMOVE) as subvolume:
subvolume.remove_user_metadata(keyname)
except VolumeException as ve:
if not (ve.errno == -errno.ENOENT and force):
ret = self.volume_exception_to_retval(ve)
return ret
def list_subvolumes(self, **kwargs):
ret = 0, "", ""
volname = kwargs['vol_name']

View File

@ -189,10 +189,51 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=group_name,type=CephString,req=false ',
'desc': "Get the metadata of a CephFS subvolume in a volume, "
'desc': "Get the information of a CephFS subvolume in a volume, "
"and optionally, in a specific subvolume group",
'perm': 'r'
},
{
'cmd': 'fs subvolume metadata set '
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=key_name,type=CephString '
'name=value,type=CephString '
'name=group_name,type=CephString,req=false ',
'desc': "Set custom metadata (key-value) for a CephFS subvolume in a volume, "
"and optionally, in a specific subvolume group",
'perm': 'rw'
},
{
'cmd': 'fs subvolume metadata get '
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=key_name,type=CephString '
'name=group_name,type=CephString,req=false ',
'desc': "Get custom metadata associated with the key of a CephFS subvolume in a volume, "
"and optionally, in a specific subvolume group",
'perm': 'r'
},
{
'cmd': 'fs subvolume metadata ls '
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=group_name,type=CephString,req=false ',
'desc': "List custom metadata (key-value pairs) of a CephFS subvolume in a volume, "
"and optionally, in a specific subvolume group",
'perm': 'r'
},
{
'cmd': 'fs subvolume metadata rm '
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=key_name,type=CephString '
'name=group_name,type=CephString,req=false '
'name=force,type=CephBool,req=false ',
'desc': "Remove custom metadata (key-value) associated with the key of a CephFS subvolume in a volume, "
"and optionally, in a specific subvolume group",
'perm': 'rw'
},
{
'cmd': 'fs subvolumegroup pin'
' name=vol_name,type=CephString'
@ -543,6 +584,35 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
sub_name=cmd['sub_name'],
group_name=cmd.get('group_name', None))
@mgr_cmd_wrap
def _cmd_fs_subvolume_metadata_set(self, inbuf, cmd):
return self.vc.set_user_metadata(vol_name=cmd['vol_name'],
sub_name=cmd['sub_name'],
key_name=cmd['key_name'],
value=cmd['value'],
group_name=cmd.get('group_name', None))
@mgr_cmd_wrap
def _cmd_fs_subvolume_metadata_get(self, inbuf, cmd):
return self.vc.get_user_metadata(vol_name=cmd['vol_name'],
sub_name=cmd['sub_name'],
key_name=cmd['key_name'],
group_name=cmd.get('group_name', None))
@mgr_cmd_wrap
def _cmd_fs_subvolume_metadata_ls(self, inbuf, cmd):
return self.vc.list_user_metadata(vol_name=cmd['vol_name'],
sub_name=cmd['sub_name'],
group_name=cmd.get('group_name', None))
@mgr_cmd_wrap
def _cmd_fs_subvolume_metadata_rm(self, inbuf, cmd):
return self.vc.remove_user_metadata(vol_name=cmd['vol_name'],
sub_name=cmd['sub_name'],
key_name=cmd['key_name'],
group_name=cmd.get('group_name', None),
force=cmd.get('force', False))
@mgr_cmd_wrap
def _cmd_fs_subvolumegroup_pin(self, inbuf, cmd):
return self.vc.pin_subvolume_group(vol_name=cmd['vol_name'],