]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/volumes/fs/operations/group.py
aac81f299346afcb706577adca2a4d8ab5375908
[ceph.git] / ceph / src / pybind / mgr / volumes / fs / operations / group.py
1 import os
2 import errno
3 import logging
4 from contextlib import contextmanager
5
6 import cephfs
7
8 from .snapshot_util import mksnap, rmsnap
9 from .pin_util import pin
10 from .template import GroupTemplate
11 from ..fs_util import listdir, get_ancestor_xattr
12 from ..exception import VolumeException
13
14 log = logging.getLogger(__name__)
15
16 class Group(GroupTemplate):
17 # Reserved subvolume group name which we use in paths for subvolumes
18 # that are not assigned to a group (i.e. created with group=None)
19 NO_GROUP_NAME = "_nogroup"
20
21 def __init__(self, fs, vol_spec, groupname):
22 assert groupname != Group.NO_GROUP_NAME
23 self.fs = fs
24 self.user_id = None
25 self.group_id = None
26 self.vol_spec = vol_spec
27 self.groupname = groupname if groupname else Group.NO_GROUP_NAME
28
29 @property
30 def path(self):
31 return os.path.join(self.vol_spec.base_dir.encode('utf-8'), self.groupname.encode('utf-8'))
32
33 @property
34 def group_name(self):
35 return self.groupname
36
37 @property
38 def uid(self):
39 return self.user_id
40
41 @uid.setter
42 def uid(self, val):
43 self.user_id = val
44
45 @property
46 def gid(self):
47 return self.group_id
48
49 @gid.setter
50 def gid(self, val):
51 self.group_id = val
52
53 def is_default_group(self):
54 return self.groupname == Group.NO_GROUP_NAME
55
56 def list_subvolumes(self):
57 try:
58 return listdir(self.fs, self.path)
59 except VolumeException as ve:
60 # listing a default group when it's not yet created
61 if ve.errno == -errno.ENOENT and self.is_default_group():
62 return []
63 raise
64
65 def pin(self, pin_type, pin_setting):
66 return pin(self.fs, self.path, pin_type, pin_setting)
67
68 def create_snapshot(self, snapname):
69 snappath = os.path.join(self.path,
70 self.vol_spec.snapshot_dir_prefix.encode('utf-8'),
71 snapname.encode('utf-8'))
72 mksnap(self.fs, snappath)
73
74 def remove_snapshot(self, snapname):
75 snappath = os.path.join(self.path,
76 self.vol_spec.snapshot_dir_prefix.encode('utf-8'),
77 snapname.encode('utf-8'))
78 rmsnap(self.fs, snappath)
79
80 def list_snapshots(self):
81 try:
82 dirpath = os.path.join(self.path,
83 self.vol_spec.snapshot_dir_prefix.encode('utf-8'))
84 return listdir(self.fs, dirpath)
85 except VolumeException as ve:
86 if ve.errno == -errno.ENOENT:
87 return []
88 raise
89
90 def create_group(fs, vol_spec, groupname, pool, mode, uid, gid):
91 """
92 create a subvolume group.
93
94 :param fs: ceph filesystem handle
95 :param vol_spec: volume specification
96 :param groupname: subvolume group name
97 :param pool: the RADOS pool where the data objects of the subvolumes will be stored
98 :param mode: the user permissions
99 :param uid: the user identifier
100 :param gid: the group identifier
101 :return: None
102 """
103 group = Group(fs, vol_spec, groupname)
104 path = group.path
105 fs.mkdirs(path, mode)
106 try:
107 if not pool:
108 pool = get_ancestor_xattr(fs, path, "ceph.dir.layout.pool")
109 try:
110 fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0)
111 except cephfs.InvalidValue:
112 raise VolumeException(-errno.EINVAL,
113 "Invalid pool layout '{0}'. It must be a valid data pool".format(pool))
114 if uid is None:
115 uid = 0
116 else:
117 try:
118 uid = int(uid)
119 if uid < 0:
120 raise ValueError
121 except ValueError:
122 raise VolumeException(-errno.EINVAL, "invalid UID")
123 if gid is None:
124 gid = 0
125 else:
126 try:
127 gid = int(gid)
128 if gid < 0:
129 raise ValueError
130 except ValueError:
131 raise VolumeException(-errno.EINVAL, "invalid GID")
132 fs.chown(path, uid, gid)
133 except (cephfs.Error, VolumeException) as e:
134 try:
135 # cleanup group path on best effort basis
136 log.debug("cleaning up subvolume group path: {0}".format(path))
137 fs.rmdir(path)
138 except cephfs.Error as ce:
139 log.debug("failed to clean up subvolume group {0} with path: {1} ({2})".format(groupname, path, ce))
140 if isinstance(e, cephfs.Error):
141 e = VolumeException(-e.args[0], e.args[1])
142 raise e
143
144 def remove_group(fs, vol_spec, groupname):
145 """
146 remove a subvolume group.
147
148 :param fs: ceph filesystem handle
149 :param vol_spec: volume specification
150 :param groupname: subvolume group name
151 :return: None
152 """
153 group = Group(fs, vol_spec, groupname)
154 try:
155 fs.rmdir(group.path)
156 except cephfs.Error as e:
157 if e.args[0] == errno.ENOENT:
158 raise VolumeException(-errno.ENOENT, "subvolume group '{0}' does not exist".format(groupname))
159 raise VolumeException(-e.args[0], e.args[1])
160
161 @contextmanager
162 def open_group(fs, vol_spec, groupname):
163 """
164 open a subvolume group. This API is to be used as a context manager.
165
166 :param fs: ceph filesystem handle
167 :param vol_spec: volume specification
168 :param groupname: subvolume group name
169 :return: yields a group object (subclass of GroupTemplate)
170 """
171 group = Group(fs, vol_spec, groupname)
172 try:
173 st = fs.stat(group.path)
174 group.uid = int(st.st_uid)
175 group.gid = int(st.st_gid)
176 except cephfs.Error as e:
177 if e.args[0] == errno.ENOENT:
178 if not group.is_default_group():
179 raise VolumeException(-errno.ENOENT, "subvolume group '{0}' does not exist".format(groupname))
180 else:
181 raise VolumeException(-e.args[0], e.args[1])
182 yield group
183
184 @contextmanager
185 def open_group_unique(fs, vol_spec, groupname, c_group, c_groupname):
186 if groupname == c_groupname:
187 yield c_group
188 else:
189 with open_group(fs, vol_spec, groupname) as group:
190 yield group