]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/volumes/fs/volume.py
8cc7ef26fa05883c8bb093097228e6266ddab736
8 from .subvolspec
import SubvolumeSpec
9 from .subvolume
import SubVolume
10 from .exception
import VolumeException
12 log
= logging
.getLogger(__name__
)
14 class VolumeClient(object):
15 def __init__(self
, mgr
):
18 def gen_pool_names(self
, volname
):
20 return metadata and data pool name (from a filesystem/volume name) as a tuple
22 return "cephfs.{}.meta".format(volname
), "cephfs.{}.data".format(volname
)
24 def get_fs(self
, fs_name
):
25 fs_map
= self
.mgr
.get('fs_map')
26 for fs
in fs_map
['filesystems']:
27 if fs
['mdsmap']['fs_name'] == fs_name
:
31 def get_mds_names(self
, fs_name
):
32 fs
= self
.get_fs(fs_name
)
35 return [mds
['name'] for mds
in fs
['mdsmap']['info'].values()]
37 def volume_exists(self
, volname
):
38 return self
.get_fs(volname
) is not None
40 def volume_exception_to_retval(self
, ve
):
42 return a tuple representation from a volume exception
46 def create_pool(self
, pool_name
, pg_num
, pg_num_min
=None, pg_autoscale_factor
=None):
47 # create the given pool
48 command
= {'prefix': 'osd pool create', 'pool': pool_name
, 'pg_num': pg_num
}
50 command
['pg_num_min'] = pg_num_min
51 r
, outb
, outs
= self
.mgr
.mon_command(command
)
55 # set pg autoscale if needed
56 if pg_autoscale_factor
:
57 command
= {'prefix': 'osd pool set', 'pool': pool_name
, 'var': 'pg_autoscale_bias',
58 'val': str(pg_autoscale_factor
)}
59 r
, outb
, outs
= self
.mgr
.mon_command(command
)
62 def remove_pool(self
, pool_name
):
63 command
= {'prefix': 'osd pool rm', 'pool': pool_name
, 'pool2': pool_name
,
64 'yes_i_really_really_mean_it': True}
65 return self
.mgr
.mon_command(command
)
67 def create_filesystem(self
, fs_name
, metadata_pool
, data_pool
):
68 command
= {'prefix': 'fs new', 'fs_name': fs_name
, 'metadata': metadata_pool
,
70 return self
.mgr
.mon_command(command
)
72 def remove_filesystem(self
, fs_name
):
73 command
= {'prefix': 'fs rm', 'fs_name': fs_name
, 'yes_i_really_mean_it': True}
74 return self
.mgr
.mon_command(command
)
76 def create_mds(self
, fs_name
):
77 spec
= orchestrator
.StatelessServiceSpec()
80 completion
= self
.mgr
.add_stateless_service("mds", spec
)
81 self
.mgr
._orchestrator
_wait
([completion
])
82 orchestrator
.raise_if_exception(completion
)
83 except (ImportError, orchestrator
.OrchestratorError
):
84 return 0, "", "Volume created successfully (no MDS daemons created)"
85 except Exception as e
:
86 # Don't let detailed orchestrator exceptions (python backtraces)
87 # bubble out to the user
88 log
.exception("Failed to create MDS daemons")
89 return -errno
.EINVAL
, "", str(e
)
92 def set_mds_down(self
, fs_name
):
93 command
= {'prefix': 'fs set', 'fs_name': fs_name
, 'var': 'cluster_down', 'val': 'true'}
94 r
, outb
, outs
= self
.mgr
.mon_command(command
)
97 for mds
in self
.get_mds_names(fs_name
):
98 command
= {'prefix': 'mds fail', 'role_or_gid': mds
}
99 r
, outb
, outs
= self
.mgr
.mon_command(command
)
104 ### volume operations -- create, rm, ls
106 def create_volume(self
, volname
, size
=None):
108 create volume (pool, filesystem and mds)
110 metadata_pool
, data_pool
= self
.gen_pool_names(volname
)
112 r
, outs
, outb
= self
.create_pool(metadata_pool
, 16, pg_num_min
=16, pg_autoscale_factor
=4.0)
115 r
, outb
, outs
= self
.create_pool(data_pool
, 8)
119 r
, outb
, outs
= self
.create_filesystem(volname
, metadata_pool
, data_pool
)
121 log
.error("Filesystem creation error: {0} {1} {2}".format(r
, outb
, outs
))
124 return self
.create_mds(volname
)
126 def delete_volume(self
, volname
):
128 delete the given module (tear down mds, remove filesystem)
130 # Tear down MDS daemons
132 completion
= self
.mgr
.remove_stateless_service("mds", volname
)
133 self
.mgr
._orchestrator
_wait
([completion
])
134 orchestrator
.raise_if_exception(completion
)
135 except (ImportError, orchestrator
.OrchestratorError
):
136 log
.warning("OrchestratorError, not tearing down MDS daemons")
137 except Exception as e
:
138 # Don't let detailed orchestrator exceptions (python backtraces)
139 # bubble out to the user
140 log
.exception("Failed to tear down MDS daemons")
141 return -errno
.EINVAL
, "", str(e
)
143 # In case orchestrator didn't tear down MDS daemons cleanly, or
144 # there was no orchestrator, we force the daemons down.
145 if self
.volume_exists(volname
):
146 r
, outb
, outs
= self
.set_mds_down(volname
)
149 r
, outb
, outs
= self
.remove_filesystem(volname
)
153 log
.warning("Filesystem already gone for volume '{0}'".format(volname
))
154 metadata_pool
, data_pool
= self
.gen_pool_names(volname
)
155 r
, outb
, outs
= self
.remove_pool(metadata_pool
)
158 return self
.remove_pool(data_pool
)
160 def list_volumes(self
):
162 fs_map
= self
.mgr
.get("fs_map")
163 for f
in fs_map
['filesystems']:
164 result
.append({'name': f
['mdsmap']['fs_name']})
165 return 0, json
.dumps(result
, indent
=2), ""
167 def group_exists(self
, sv
, spec
):
168 # default group need not be explicitly created (as it gets created
169 # at the time of subvolume, snapshot and other create operations).
170 return spec
.is_default_group() or sv
.get_group_path(spec
)
173 def octal_str_to_decimal_int(mode
):
177 raise VolumeException(-errno
.EINVAL
, "Invalid mode '{0}'".format(mode
))
179 ### subvolume operations
181 def create_subvolume(self
, volname
, subvolname
, groupname
, size
, mode
='755', pool
=None):
184 if not self
.volume_exists(volname
):
185 raise VolumeException(
186 -errno
.ENOENT
, "Volume '{0}' not found, create it with `ceph fs " \
187 "volume create` before trying to create subvolumes".format(volname
))
188 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
189 spec
= SubvolumeSpec(subvolname
, groupname
)
190 if not self
.group_exists(sv
, spec
):
191 raise VolumeException(
192 -errno
.ENOENT
, "Subvolume group '{0}' not found, create it with " \
193 "`ceph fs subvolumegroup create` before creating subvolumes".format(groupname
))
194 sv
.create_subvolume(spec
, size
, pool
=pool
, mode
=self
.octal_str_to_decimal_int(mode
))
195 except VolumeException
as ve
:
196 ret
= self
.volume_exception_to_retval(ve
)
199 def remove_subvolume(self
, volname
, subvolname
, groupname
, force
):
202 fs
= self
.get_fs(volname
)
204 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
205 spec
= SubvolumeSpec(subvolname
, groupname
)
206 if self
.group_exists(sv
, spec
):
207 sv
.remove_subvolume(spec
, force
)
208 sv
.purge_subvolume(spec
)
210 raise VolumeException(
211 -errno
.ENOENT
, "Subvolume group '{0}' not found, cannot remove " \
212 "subvolume '{1}'".format(groupname
, subvolname
))
214 raise VolumeException(
215 -errno
.ENOENT
, "Volume '{0}' not found, cannot remove subvolume " \
216 "'{1}'".format(volname
, subvolname
))
217 except VolumeException
as ve
:
218 ret
= self
.volume_exception_to_retval(ve
)
221 def subvolume_getpath(self
, volname
, subvolname
, groupname
):
224 if not self
.volume_exists(volname
):
225 raise VolumeException(
226 -errno
.ENOENT
, "Volume '{0}' not found".format(volname
))
228 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
229 spec
= SubvolumeSpec(subvolname
, groupname
)
230 if not self
.group_exists(sv
, spec
):
231 raise VolumeException(
232 -errno
.ENOENT
, "Subvolume group '{0}' not found".format(groupname
))
233 path
= sv
.get_subvolume_path(spec
)
235 raise VolumeException(
236 -errno
.ENOENT
, "Subvolume '{0}' not found".format(subvolname
))
238 except VolumeException
as ve
:
239 ret
= self
.volume_exception_to_retval(ve
)
242 ### subvolume snapshot
244 def create_subvolume_snapshot(self
, volname
, subvolname
, snapname
, groupname
):
247 if not self
.volume_exists(volname
):
248 raise VolumeException(
249 -errno
.ENOENT
, "Volume '{0}' not found, cannot create snapshot " \
250 "'{1}'".format(volname
, snapname
))
252 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
253 spec
= SubvolumeSpec(subvolname
, groupname
)
254 if not self
.group_exists(sv
, spec
):
255 raise VolumeException(
256 -errno
.ENOENT
, "Subvolume group '{0}' not found, cannot create " \
257 "snapshot '{1}'".format(groupname
, snapname
))
258 if not sv
.get_subvolume_path(spec
):
259 raise VolumeException(
260 -errno
.ENOENT
, "Subvolume '{0}' not found, cannot create snapshot " \
261 "'{1}'".format(subvolname
, snapname
))
262 sv
.create_subvolume_snapshot(spec
, snapname
)
263 except VolumeException
as ve
:
264 ret
= self
.volume_exception_to_retval(ve
)
267 def remove_subvolume_snapshot(self
, volname
, subvolname
, snapname
, groupname
, force
):
270 if self
.volume_exists(volname
):
271 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
272 spec
= SubvolumeSpec(subvolname
, groupname
)
273 if self
.group_exists(sv
, spec
):
274 if sv
.get_subvolume_path(spec
):
275 sv
.remove_subvolume_snapshot(spec
, snapname
, force
)
277 raise VolumeException(
278 -errno
.ENOENT
, "Subvolume '{0}' not found, cannot remove " \
279 "subvolume snapshot '{1}'".format(subvolname
, snapname
))
281 raise VolumeException(
282 -errno
.ENOENT
, "Subvolume group '{0}' already removed, cannot " \
283 "remove subvolume snapshot '{1}'".format(groupname
, snapname
))
285 raise VolumeException(
286 -errno
.ENOENT
, "Volume '{0}' not found, cannot remove subvolumegroup " \
287 "snapshot '{1}'".format(volname
, snapname
))
288 except VolumeException
as ve
:
289 ret
= self
.volume_exception_to_retval(ve
)
294 def create_subvolume_group(self
, volname
, groupname
, mode
='755', pool
=None):
297 if not self
.volume_exists(volname
):
298 raise VolumeException(
299 -errno
.ENOENT
, "Volume '{0}' not found, create it with `ceph fs " \
300 "volume create` before trying to create subvolume groups".format(volname
))
302 # TODO: validate that subvol size fits in volume size
303 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
304 spec
= SubvolumeSpec("", groupname
)
305 sv
.create_group(spec
, pool
=pool
, mode
=self
.octal_str_to_decimal_int(mode
))
306 except VolumeException
as ve
:
307 ret
= self
.volume_exception_to_retval(ve
)
310 def remove_subvolume_group(self
, volname
, groupname
, force
):
313 if self
.volume_exists(volname
):
314 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
315 # TODO: check whether there are no subvolumes in the group
316 spec
= SubvolumeSpec("", groupname
)
317 sv
.remove_group(spec
, force
)
319 raise VolumeException(
320 -errno
.ENOENT
, "Volume '{0}' not found, cannot remove subvolume " \
321 "group '{0}'".format(volname
, groupname
))
322 except VolumeException
as ve
:
323 ret
= self
.volume_exception_to_retval(ve
)
328 def create_subvolume_group_snapshot(self
, volname
, groupname
, snapname
):
331 if not self
.volume_exists(volname
):
332 raise VolumeException(
333 -errno
.ENOENT
, "Volume '{0}' not found, cannot create snapshot " \
334 "'{1}'".format(volname
, snapname
))
336 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
337 spec
= SubvolumeSpec("", groupname
)
338 if not self
.group_exists(sv
, spec
):
339 raise VolumeException(
340 -errno
.ENOENT
, "Subvolume group '{0}' not found, cannot create " \
341 "snapshot '{1}'".format(groupname
, snapname
))
342 sv
.create_group_snapshot(spec
, snapname
)
343 except VolumeException
as ve
:
344 ret
= self
.volume_exception_to_retval(ve
)
347 def remove_subvolume_group_snapshot(self
, volname
, groupname
, snapname
, force
):
350 if self
.volume_exists(volname
):
351 with
SubVolume(self
.mgr
, fs_name
=volname
) as sv
:
352 spec
= SubvolumeSpec("", groupname
)
353 if self
.group_exists(sv
, spec
):
354 sv
.remove_group_snapshot(spec
, snapname
, force
)
356 raise VolumeException(
357 -errno
.ENOENT
, "Subvolume group '{0}' not found, cannot " \
358 "remove it".format(groupname
))
360 raise VolumeException(
361 -errno
.ENOENT
, "Volume '{0}' not found, cannot remove subvolumegroup " \
362 "snapshot '{1}'".format(volname
, snapname
))
363 except VolumeException
as ve
:
364 ret
= self
.volume_exception_to_retval(ve
)