mirror of
https://github.com/ceph/ceph
synced 2025-02-23 11:07:35 +00:00
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:
commit
3f13df8388
@ -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>]
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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])
|
||||
|
@ -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()
|
||||
|
@ -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']
|
||||
|
@ -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'],
|
||||
|
Loading…
Reference in New Issue
Block a user