else:
self.volname = result[0]['name']
+ def _get_subvolume_path(self, vol_name, subvol_name, group_name=None):
+ args = ["subvolume", "getpath", vol_name, subvol_name]
+ if group_name:
+ args.append(group_name)
+ args = tuple(args)
+ path = self._fs_cmd(*args)
+ # remove the leading '/', and trailing whitespaces
+ return path[1:].rstrip()
+
def _delete_test_volume(self):
self._fs_cmd("volume", "rm", self.volname)
# remove group
self._fs_cmd("subvolumegroup", "rm", self.volname, group)
+ def test_subvolume_group_create_with_desired_data_pool_layout(self):
+ group1 = self._generate_random_group_name()
+ group2 = self._generate_random_group_name()
+
+ # create group
+ self._fs_cmd("subvolumegroup", "create", self.volname, group1)
+ group1_path = os.path.join('volumes', group1)
+
+ default_pool = self.mount_a.getfattr(group1_path, "ceph.dir.layout.pool")
+ new_pool = "new_pool"
+ self.assertNotEqual(default_pool, new_pool)
+
+ # add data pool
+ self.fs.add_data_pool(new_pool)
+
+ # create group specifying the new data pool as its pool layout
+ self._fs_cmd("subvolumegroup", "create", self.volname, group2,
+ "--pool_layout", new_pool)
+ group2_path = os.path.join('volumes', group2)
+
+ desired_pool = self.mount_a.getfattr(group2_path, "ceph.dir.layout.pool")
+ self.assertEqual(desired_pool, new_pool)
+
+ self._fs_cmd("subvolumegroup", "rm", self.volname, group1)
+ self._fs_cmd("subvolumegroup", "rm", self.volname, group2)
+
+ def test_subvolume_create_with_desired_data_pool_layout_in_group(self):
+ subvol1 = self._generate_random_subvolume_name()
+ subvol2 = self._generate_random_subvolume_name()
+ group = self._generate_random_group_name()
+
+ # create group. this also helps set default pool layout for subvolumes
+ # created within the group.
+ self._fs_cmd("subvolumegroup", "create", self.volname, group)
+
+ # create subvolume in group.
+ self._fs_cmd("subvolume", "create", self.volname, subvol1, "--group_name", group)
+ subvol1_path = self._get_subvolume_path(self.volname, subvol1, group_name=group)
+
+ default_pool = self.mount_a.getfattr(subvol1_path, "ceph.dir.layout.pool")
+ new_pool = "new_pool"
+ self.assertNotEqual(default_pool, new_pool)
+
+ # add data pool
+ self.fs.add_data_pool(new_pool)
+
+ # create subvolume specifying the new data pool as its pool layout
+ self._fs_cmd("subvolume", "create", self.volname, subvol2, "--group_name", group,
+ "--pool_layout", new_pool)
+ subvol2_path = self._get_subvolume_path(self.volname, subvol2, group_name=group)
+
+ desired_pool = self.mount_a.getfattr(subvol2_path, "ceph.dir.layout.pool")
+ self.assertEqual(desired_pool, new_pool)
+
+ self._fs_cmd("subvolume", "rm", self.volname, subvol2, group)
+ self._fs_cmd("subvolume", "rm", self.volname, subvol1, group)
+ self._fs_cmd("subvolumegroup", "rm", self.volname, group)
+
def test_nonexistent_subvolme_group_rm(self):
group = "non_existent_group"
### basic subvolume operations
- def create_subvolume(self, spec, size=None, namespace_isolated=True, mode=0o755):
+ def create_subvolume(self, spec, size=None, namespace_isolated=True, mode=0o755, pool=None):
"""
Set up metadata, pools and auth for a subvolume.
:param spec: subvolume path specification
:param size: In bytes, or None for no size limit
:param namespace_isolated: If true, use separate RADOS namespace for this subvolume
+ :param pool: the RADOS pool where the data objects of the subvolumes will be stored
:return: None
"""
subvolpath = spec.subvolume_path
if size is not None:
self.fs.setxattr(subvolpath, 'ceph.quota.max_bytes', str(size).encode('utf-8'), 0)
+ if pool:
+ self.fs.setxattr(subvolpath, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0)
+
xattr_key = xattr_val = None
if namespace_isolated:
# enforce security isolation, use separate namespace for this subvolume
xattr_key = 'ceph.dir.layout.pool_namespace'
xattr_val = spec.fs_namespace
- else:
+ elif not pool:
# If subvolume's namespace layout is not set, then the subvolume's pool
# layout remains unset and will undesirably change with ancestor's
# pool layout changes.
### group operations
- def create_group(self, spec, mode=0o755):
+ def create_group(self, spec, mode=0o755, pool=None):
path = spec.group_path
self._mkdir_p(path, mode)
+ if not pool:
+ pool = self._get_ancestor_xattr(path, "ceph.dir.layout.pool")
+ self.fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0)
def remove_group(self, spec, force):
path = spec.group_path
on the requested path, keep checking parents until we find it.
"""
try:
- result = self.fs.getxattr(path, attr).decode('utf-8')
- if result == "":
- # Annoying! cephfs gives us empty instead of an error when attr not found
- raise cephfs.NoData()
- else:
- return result
+ return self.fs.getxattr(path, attr).decode('utf-8')
except cephfs.NoData:
if path == "/":
raise
### subvolume operations
- def create_subvolume(self, volname, subvolname, groupname, size):
+ def create_subvolume(self, volname, subvolname, groupname, size, pool=None):
ret = 0, "", ""
try:
if not self.volume_exists(volname):
raise VolumeException(
-errno.ENOENT, "Subvolume group '{0}' not found, create it with " \
"`ceph fs subvolumegroup create` before creating subvolumes".format(groupname))
- sv.create_subvolume(spec, size)
+ sv.create_subvolume(spec, size, pool=pool)
except VolumeException as ve:
ret = self.volume_exception_to_retval(ve)
return ret
### group operations
- def create_subvolume_group(self, volname, groupname):
+ def create_subvolume_group(self, volname, groupname, pool=None):
ret = 0, "", ""
try:
if not self.volume_exists(volname):
# TODO: validate that subvol size fits in volume size
with SubVolume(self.mgr, fs_name=volname) as sv:
spec = SubvolumeSpec("", groupname)
- sv.create_group(spec)
+ sv.create_group(spec, pool=pool)
except VolumeException as ve:
ret = self.volume_exception_to_retval(ve)
return ret
{
'cmd': 'fs subvolumegroup create '
'name=vol_name,type=CephString '
- 'name=group_name,type=CephString ',
- 'desc': "Create a CephFS subvolume group in a volume",
+ 'name=group_name,type=CephString '
+ 'name=pool_layout,type=CephString,req=false ',
+ 'desc': "Create a CephFS subvolume group in a volume, and optionally, "
+ "with a specific data pool layout",
'perm': 'rw'
},
{
'name=vol_name,type=CephString '
'name=sub_name,type=CephString '
'name=size,type=CephInt,req=false '
- 'name=group_name,type=CephString,req=false ',
+ 'name=group_name,type=CephString,req=false '
+ 'name=pool_layout,type=CephString,req=false ',
'desc': "Create a CephFS subvolume in a volume, and optionally, "
- "with a specific size (in bytes) and in a specific "
- "subvolume group",
+ "with a specific size (in bytes), a specific data pool layout "
+ "and in a specific subvolume group",
'perm': 'rw'
},
{
"""
vol_name = cmd['vol_name']
group_name = cmd['group_name']
+ pool_layout = cmd.get('pool_layout', None)
- return self.vc.create_subvolume_group(vol_name, group_name)
+ return self.vc.create_subvolume_group(vol_name, group_name, pool=pool_layout)
def _cmd_fs_subvolumegroup_rm(self, inbuf, cmd):
"""
sub_name = cmd['sub_name']
size = cmd.get('size', None)
group_name = cmd.get('group_name', None)
+ pool_layout = cmd.get('pool_layout', None)
- return self.vc.create_subvolume(vol_name, sub_name, group_name, size)
+ return self.vc.create_subvolume(vol_name, sub_name, group_name, size, pool=pool_layout)
def _cmd_fs_subvolume_rm(self, inbuf, cmd):
"""