]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
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 | |
f6b5b4d7 | 9 | from .pin_util import pin |
92f5a8d4 TL |
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 | ||
92f5a8d4 TL |
41 | @uid.setter |
42 | def uid(self, val): | |
43 | self.user_id = val | |
44 | ||
9f95a23c TL |
45 | @property |
46 | def gid(self): | |
47 | return self.group_id | |
48 | ||
92f5a8d4 TL |
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 | ||
f6b5b4d7 TL |
65 | def pin(self, pin_type, pin_setting): |
66 | return pin(self.fs, self.path, pin_type, pin_setting) | |
67 | ||
92f5a8d4 TL |
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: | |
9f95a23c | 139 | log.debug("failed to clean up subvolume group {0} with path: {1} ({2})".format(groupname, path, ce)) |
92f5a8d4 TL |
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 |