Skip to content

Commit

Permalink
Merge pull request ceph#46876 from neesingh-rh/feature_55821
Browse files Browse the repository at this point in the history
mgr/volumes: add interface to check the presence of subvolumegroups/subvolumes

Reviewed-by: Venky Shankar <[email protected]>
Reviewed-by: Kotresh HR <[email protected]>
Reviewed-by: Nikhilkumar Shelke <[email protected]>
Reviewed-by: Anthony D'Atri <[email protected]>
  • Loading branch information
vshankar committed Aug 4, 2022
2 parents 4697911 + f182ab4 commit bcfc2e2
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 2 deletions.
18 changes: 18 additions & 0 deletions doc/cephfs/fs-volumes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ The output format is json and contains fields as follows.
* created_at: time of creation of subvolume group in the format "YYYY-MM-DD HH:MM:SS"
* data_pool: data pool the subvolume group belongs to

Check the presence of any subvolume group using::

$ ceph fs subvolumegroup exist <vol_name>

The strings returned by the 'exist' command:
* "subvolumegroup exists": if any subvolumegroup is present
* "no subvolumegroup exists": if no subvolumegroup is present

.. note:: It checks for the presence of custom groups and not the default one. To validate the emptiness of the volume, subvolumegroup existence check alone is not sufficient. The subvolume existence also needs to be checked as there might be subvolumes in the default group.

Resize a subvolume group using::

$ ceph fs subvolumegroup resize <vol_name> <group_name> <new_size> [--no_shrink]
Expand Down Expand Up @@ -292,6 +302,14 @@ List subvolumes using::

.. note:: subvolumes that are removed but have snapshots retained, are also listed.

Check the presence of any subvolume using::

$ ceph fs subvolume exist <vol_name> [--group_name <subvol_group_name>]

The strings returned by the 'exist' command:
* "subvolume exists": if any subvolume of given group_name is present
* "no subvolume exists": if no subvolume of given group_name is present

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>]
Expand Down
111 changes: 111 additions & 0 deletions qa/tasks/cephfs/test_volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,66 @@ def test_subvolume_group_rm_force(self):
except CommandFailedError:
raise RuntimeError("expected the 'fs subvolumegroup rm --force' command to succeed")

def test_subvolume_group_exists_with_subvolumegroup_and_no_subvolume(self):
"""Test the presence of any subvolumegroup when only subvolumegroup is present"""

group = self._generate_random_group_name()
# create subvolumegroup
self._fs_cmd("subvolumegroup", "create", self.volname, group)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "subvolumegroup exists")
# delete subvolumegroup
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolumegroup exists")

def test_subvolume_group_exists_with_no_subvolumegroup_and_subvolume(self):
"""Test the presence of any subvolumegroup when no subvolumegroup is present"""

ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolumegroup exists")

def test_subvolume_group_exists_with_subvolumegroup_and_subvolume(self):
"""Test the presence of any subvolume when subvolumegroup
and subvolume both are present"""

group = self._generate_random_group_name()
subvolume = self._generate_random_subvolume_name(2)
# create subvolumegroup
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group
self._fs_cmd("subvolume", "create", self.volname, subvolume[0], "--group_name", group)
# create subvolume
self._fs_cmd("subvolume", "create", self.volname, subvolume[1])
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "subvolumegroup exists")
# delete subvolume in group
self._fs_cmd("subvolume", "rm", self.volname, subvolume[0], "--group_name", group)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "subvolumegroup exists")
# delete subvolume
self._fs_cmd("subvolume", "rm", self.volname, subvolume[1])
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "subvolumegroup exists")
# delete subvolumegroup
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolumegroup exists")

def test_subvolume_group_exists_without_subvolumegroup_and_with_subvolume(self):
"""Test the presence of any subvolume when subvolume is present
but no subvolumegroup is present"""

subvolume = self._generate_random_subvolume_name()
# create subvolume
self._fs_cmd("subvolume", "create", self.volname, subvolume)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolumegroup exists")
# delete subvolume
self._fs_cmd("subvolume", "rm", self.volname, subvolume)
ret = self._fs_cmd("subvolumegroup", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolumegroup exists")


class TestSubvolumes(TestVolumesHelper):
"""Tests for FS subvolume operations, except snapshot and snapshot clone."""
Expand Down Expand Up @@ -3271,6 +3331,57 @@ def test_subvolume_rm_force(self):
except CommandFailedError:
self.fail("expected the 'fs subvolume rm --force' command to succeed")

def test_subvolume_exists_with_subvolumegroup_and_subvolume(self):
"""Test the presence of any subvolume by specifying the name of subvolumegroup"""

group = self._generate_random_group_name()
subvolume1 = self._generate_random_subvolume_name()
# create subvolumegroup
self._fs_cmd("subvolumegroup", "create", self.volname, group)
# create subvolume in group
self._fs_cmd("subvolume", "create", self.volname, subvolume1, "--group_name", group)
ret = self._fs_cmd("subvolume", "exist", self.volname, "--group_name", group)
self.assertEqual(ret.strip('\n'), "subvolume exists")
# delete subvolume in group
self._fs_cmd("subvolume", "rm", self.volname, subvolume1, "--group_name", group)
ret = self._fs_cmd("subvolume", "exist", self.volname, "--group_name", group)
self.assertEqual(ret.strip('\n'), "no subvolume exists")
# delete subvolumegroup
self._fs_cmd("subvolumegroup", "rm", self.volname, group)

def test_subvolume_exists_with_subvolumegroup_and_no_subvolume(self):
"""Test the presence of any subvolume specifying the name
of subvolumegroup and no subvolumes"""

group = self._generate_random_group_name()
# create subvolumegroup
self._fs_cmd("subvolumegroup", "create", self.volname, group)
ret = self._fs_cmd("subvolume", "exist", self.volname, "--group_name", group)
self.assertEqual(ret.strip('\n'), "no subvolume exists")
# delete subvolumegroup
self._fs_cmd("subvolumegroup", "rm", self.volname, group)

def test_subvolume_exists_without_subvolumegroup_and_with_subvolume(self):
"""Test the presence of any subvolume without specifying the name
of subvolumegroup"""

subvolume1 = self._generate_random_subvolume_name()
# create subvolume
self._fs_cmd("subvolume", "create", self.volname, subvolume1)
ret = self._fs_cmd("subvolume", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "subvolume exists")
# delete subvolume
self._fs_cmd("subvolume", "rm", self.volname, subvolume1)
ret = self._fs_cmd("subvolume", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolume exists")

def test_subvolume_exists_without_subvolumegroup_and_without_subvolume(self):
"""Test the presence of any subvolume without any subvolumegroup
and without any subvolume"""

ret = self._fs_cmd("subvolume", "exist", self.volname)
self.assertEqual(ret.strip('\n'), "no subvolume exists")

def test_subvolume_shrink(self):
"""
That a subvolume can be shrinked in size and its quota matches the expected size.
Expand Down
22 changes: 22 additions & 0 deletions src/pybind/mgr/volumes/fs/fs_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,28 @@ def listdir(fs, dirpath, filter_entries=None):
raise VolumeException(-e.args[0], e.args[1])
return dirs


def has_subdir(fs, dirpath, filter_entries=None):
"""
Check the presence of directory (only dirs) for a given path
"""
res = False
if filter_entries is None:
filter_entries = [b".", b".."]
else:
filter_entries.extend([b".", b".."])
try:
with fs.opendir(dirpath) as dir_handle:
d = fs.readdir(dir_handle)
while d:
if (d.d_name not in filter_entries) and d.is_dir():
res = True
break
d = fs.readdir(dir_handle)
except cephfs.Error as e:
raise VolumeException(-e.args[0], e.args[1])
return res

def is_inherited_snap(snapname):
"""
Returns True if the snapname is inherited else False
Expand Down
11 changes: 10 additions & 1 deletion src/pybind/mgr/volumes/fs/operations/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .snapshot_util import mksnap, rmsnap
from .pin_util import pin
from .template import GroupTemplate
from ..fs_util import listdir, listsnaps, get_ancestor_xattr, create_base_dir
from ..fs_util import listdir, listsnaps, get_ancestor_xattr, create_base_dir, has_subdir
from ..exception import VolumeException

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -64,6 +64,15 @@ def list_subvolumes(self):
return []
raise

def has_subvolumes(self):
try:
return has_subdir(self.fs, self.path)
except VolumeException as ve:
# listing a default group when it's not yet created
if ve.errno == -errno.ENOENT and self.is_default_group():
return False
raise

def pin(self, pin_type, pin_setting):
return pin(self.fs, self.path, pin_type, pin_setting)

Expand Down
8 changes: 8 additions & 0 deletions src/pybind/mgr/volumes/fs/vol_spec.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from .operations.index import Index
from .operations.group import Group
from .operations.trash import Trash
from .operations.versions.subvolume_base import SubvolumeBase


class VolSpec(object):
"""
specification of a "volume" -- base directory and various prefixes.
Expand All @@ -9,6 +15,8 @@ class VolSpec(object):
DEFAULT_NS_PREFIX = "fsvolumens_"
# default mode for subvol prefix and group
DEFAULT_MODE = 0o755
# internal directories
INTERNAL_DIRS = [Group.NO_GROUP_NAME, Index.GROUP_NAME, Trash.GROUP_NAME, SubvolumeBase.LEGACY_CONF_DIR]

def __init__(self, snapshot_prefix, subvolume_prefix=None, pool_ns_prefix=None):
self.snapshot_prefix = snapshot_prefix
Expand Down
46 changes: 45 additions & 1 deletion src/pybind/mgr/volumes/fs/volume.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import json
import errno
import logging
import os
from typing import TYPE_CHECKING

import cephfs

from mgr_util import CephfsClient

from .fs_util import listdir
from .fs_util import listdir, has_subdir

from .operations.volume import create_volume, \
delete_volume, rename_volume, list_volumes, open_volume, get_pool_names
Expand Down Expand Up @@ -466,6 +467,28 @@ def list_subvolumes(self, **kwargs):
ret = self.volume_exception_to_retval(ve)
return ret

def subvolume_exists(self, **kwargs):
volname = kwargs['vol_name']
groupname = kwargs['group_name']
ret = 0, "", ""
volume_exists = False

try:
with open_volume(self, volname) as fs_handle:
volume_exists = True
with open_group(fs_handle, self.volspec, groupname) as group:
res = group.has_subvolumes()
if res:
ret = 0, "subvolume exists", ""
else:
ret = 0, "no subvolume exists", ""
except VolumeException as ve:
if volume_exists and ve.errno == -errno.ENOENT:
ret = 0, "no subvolume exists", ""
else:
ret = self.volume_exception_to_retval(ve)
return ret

### subvolume snapshot

def create_subvolume_snapshot(self, **kwargs):
Expand Down Expand Up @@ -864,6 +887,27 @@ def pin_subvolume_group(self, **kwargs):
ret = self.volume_exception_to_retval(ve)
return ret

def subvolume_group_exists(self, **kwargs):
volname = kwargs['vol_name']
ret = 0, "", ""
volume_exists = False

try:
with open_volume(self, volname) as fs_handle:
volume_exists = True
res = has_subdir(fs_handle, self.volspec.base_dir, filter_entries=[
dir.encode('utf-8') for dir in self.volspec.INTERNAL_DIRS])
if res:
ret = 0, "subvolumegroup exists", ""
else:
ret = 0, "no subvolumegroup exists", ""
except VolumeException as ve:
if volume_exists and ve.errno == -errno.ENOENT:
ret = 0, "no subvolumegroup exists", ""
else:
ret = self.volume_exception_to_retval(ve)
return ret

### group snapshot

def create_subvolume_group_snapshot(self, **kwargs):
Expand Down
23 changes: 23 additions & 0 deletions src/pybind/mgr/volumes/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
'desc': "Resize a CephFS subvolume group",
'perm': 'rw'
},
{
'cmd': 'fs subvolumegroup exist '
'name=vol_name,type=CephString ',
'desc': "Check a volume for the existence of subvolumegroup",
'perm': 'r'
},
{
'cmd': 'fs subvolume ls '
'name=vol_name,type=CephString '
Expand Down Expand Up @@ -210,6 +216,14 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
"and optionally, in a specific subvolume group",
'perm': 'r'
},
{
'cmd': 'fs subvolume exist '
'name=vol_name,type=CephString '
'name=group_name,type=CephString,req=false ',
'desc': "Check a volume for the existence of a subvolume, "
"optionally in a specified subvolume group",
'perm': 'r'
},
{
'cmd': 'fs subvolume metadata set '
'name=vol_name,type=CephString '
Expand Down Expand Up @@ -567,6 +581,10 @@ def _cmd_fs_subvolumegroup_resize(self, inbuf, cmd):
def _cmd_fs_subvolumegroup_ls(self, inbuf, cmd):
return self.vc.list_subvolume_groups(vol_name=cmd['vol_name'])

@mgr_cmd_wrap
def _cmd_fs_subvolumegroup_exist(self, inbuf, cmd):
return self.vc.subvolume_group_exists(vol_name=cmd['vol_name'])

@mgr_cmd_wrap
def _cmd_fs_subvolume_create(self, inbuf, cmd):
"""
Expand Down Expand Up @@ -657,6 +675,11 @@ def _cmd_fs_subvolume_info(self, inbuf, cmd):
sub_name=cmd['sub_name'],
group_name=cmd.get('group_name', None))

@mgr_cmd_wrap
def _cmd_fs_subvolume_exist(self, inbuf, cmd):
return self.vc.subvolume_exists(vol_name=cmd['vol_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'],
Expand Down

0 comments on commit bcfc2e2

Please sign in to comment.