]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/volumes/fs/operations/group.py
import ceph quincy 17.2.4
[ceph.git] / ceph / src / pybind / mgr / volumes / fs / operations / group.py
index 152383c51d43a3e1ae3ccf4b080d7b4b285deebf..8b40610332dce36c8263e2e339f1af8fd32e9c67 100644 (file)
@@ -8,7 +8,7 @@ import cephfs
 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__)
@@ -20,7 +20,10 @@ class Group(GroupTemplate):
     NO_GROUP_NAME = "_nogroup"
 
     def __init__(self, fs, vol_spec, groupname):
-        assert groupname != Group.NO_GROUP_NAME
+        if groupname == Group.NO_GROUP_NAME:
+            raise VolumeException(-errno.EPERM, "Operation not permitted for group '{0}' as it is an internal group.".format(groupname))
+        if groupname in vol_spec.INTERNAL_DIRS:
+            raise VolumeException(-errno.EINVAL, "'{0}' is an internal directory and not a valid group name.".format(groupname))
         self.fs = fs
         self.user_id = None
         self.group_id = None
@@ -63,6 +66,15 @@ class Group(GroupTemplate):
                 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)
 
@@ -88,14 +100,127 @@ class Group(GroupTemplate):
                 return []
             raise
 
+    def info(self):
+        st = self.fs.statx(self.path, cephfs.CEPH_STATX_BTIME | cephfs.CEPH_STATX_SIZE
+                           | cephfs.CEPH_STATX_UID | cephfs.CEPH_STATX_GID | cephfs.CEPH_STATX_MODE
+                           | cephfs.CEPH_STATX_ATIME | cephfs.CEPH_STATX_MTIME | cephfs.CEPH_STATX_CTIME,
+                           cephfs.AT_SYMLINK_NOFOLLOW)
+        usedbytes = st["size"]
+        try:
+            nsize = int(self.fs.getxattr(self.path, 'ceph.quota.max_bytes').decode('utf-8'))
+        except cephfs.NoData:
+            nsize = 0
+
+        try:
+            data_pool = self.fs.getxattr(self.path, 'ceph.dir.layout.pool').decode('utf-8')
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
+
+        return {'uid': int(st["uid"]),
+                'gid': int(st["gid"]),
+                'atime': str(st["atime"]),
+                'mtime': str(st["mtime"]),
+                'ctime': str(st["ctime"]),
+                'mode': int(st["mode"]),
+                'data_pool': data_pool,
+                'created_at': str(st["btime"]),
+                'bytes_quota': "infinite" if nsize == 0 else nsize,
+                'bytes_used': int(usedbytes),
+                'bytes_pcent': "undefined" if nsize == 0 else '{0:.2f}'.format((float(usedbytes) / nsize) * 100.0)}
+
+    def resize(self, newsize, noshrink):
+        try:
+            newsize = int(newsize)
+            if newsize <= 0:
+                raise VolumeException(-errno.EINVAL, "Invalid subvolume group size")
+        except ValueError:
+            newsize = newsize.lower()
+            if not (newsize == "inf" or newsize == "infinite"):
+                raise (VolumeException(-errno.EINVAL, "invalid size option '{0}'".format(newsize)))
+            newsize = 0
+            noshrink = False
+
+        try:
+            maxbytes = int(self.fs.getxattr(self.path, 'ceph.quota.max_bytes').decode('utf-8'))
+        except cephfs.NoData:
+            maxbytes = 0
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
+
+        group_stat = self.fs.stat(self.path)
+        if newsize > 0 and newsize < group_stat.st_size:
+            if noshrink:
+                raise VolumeException(-errno.EINVAL, "Can't resize the subvolume group. The new size"
+                                      " '{0}' would be lesser than the current used size '{1}'"
+                                      .format(newsize, group_stat.st_size))
+
+        if not newsize == maxbytes:
+            try:
+                self.fs.setxattr(self.path, 'ceph.quota.max_bytes', str(newsize).encode('utf-8'), 0)
+            except cephfs.Error as e:
+                raise (VolumeException(-e.args[0],
+                                       "Cannot set new size for the subvolume group. '{0}'".format(e.args[1])))
+        return newsize, group_stat.st_size
+
+def set_group_attrs(fs, path, attrs):
+    # set subvolume group attrs
+    # set size
+    quota = attrs.get("quota")
+    if quota is not None:
+        try:
+            fs.setxattr(path, 'ceph.quota.max_bytes', str(quota).encode('utf-8'), 0)
+        except cephfs.InvalidValue:
+            raise VolumeException(-errno.EINVAL, "invalid size specified: '{0}'".format(quota))
+        except cephfs.Error as e:
+            raise VolumeException(-e.args[0], e.args[1])
+
+    # set pool layout
+    pool = attrs.get("data_pool")
+    if not pool:
+        pool = get_ancestor_xattr(fs, path, "ceph.dir.layout.pool")
+    try:
+        fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0)
+    except cephfs.InvalidValue:
+        raise VolumeException(-errno.EINVAL,
+                              "Invalid pool layout '{0}'. It must be a valid data pool".format(pool))
 
-def create_group(fs, vol_spec, groupname, pool, mode, uid, gid):
+    # set uid/gid
+    uid = attrs.get("uid")
+    if uid is None:
+        uid = 0
+    else:
+        try:
+            uid = int(uid)
+            if uid < 0:
+                raise ValueError
+        except ValueError:
+            raise VolumeException(-errno.EINVAL, "invalid UID")
+
+    gid = attrs.get("gid")
+    if gid is None:
+        gid = 0
+    else:
+        try:
+            gid = int(gid)
+            if gid < 0:
+                raise ValueError
+        except ValueError:
+            raise VolumeException(-errno.EINVAL, "invalid GID")
+    fs.chown(path, uid, gid)
+
+    # set mode
+    mode = attrs.get("mode", None)
+    if mode is not None:
+        fs.lchmod(path, mode)
+
+def create_group(fs, vol_spec, groupname, size, pool, mode, uid, gid):
     """
     create a subvolume group.
 
     :param fs: ceph filesystem handle
     :param vol_spec: volume specification
     :param groupname: subvolume group name
+    :param size: In bytes, or None for no size limit
     :param pool: the RADOS pool where the data objects of the subvolumes will be stored
     :param mode: the user permissions
     :param uid: the user identifier
@@ -110,32 +235,13 @@ def create_group(fs, vol_spec, groupname, pool, mode, uid, gid):
     create_base_dir(fs, vol_spec_base_dir, vol_spec.DEFAULT_MODE)
     fs.mkdir(path, mode)
     try:
-        if not pool:
-            pool = get_ancestor_xattr(fs, path, "ceph.dir.layout.pool")
-        try:
-            fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0)
-        except cephfs.InvalidValue:
-            raise VolumeException(-errno.EINVAL,
-                                  "Invalid pool layout '{0}'. It must be a valid data pool".format(pool))
-        if uid is None:
-            uid = 0
-        else:
-            try:
-                uid = int(uid)
-                if uid < 0:
-                    raise ValueError
-            except ValueError:
-                raise VolumeException(-errno.EINVAL, "invalid UID")
-        if gid is None:
-            gid = 0
-        else:
-            try:
-                gid = int(gid)
-                if gid < 0:
-                    raise ValueError
-            except ValueError:
-                raise VolumeException(-errno.EINVAL, "invalid GID")
-        fs.chown(path, uid, gid)
+        attrs = {
+            'uid': uid,
+            'gid': gid,
+            'data_pool': pool,
+            'quota': size
+        }
+        set_group_attrs(fs, path, attrs)
     except (cephfs.Error, VolumeException) as e:
         try:
             # cleanup group path on best effort basis