]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py
e4255d25b330c8a11912f8b2bcf0b4afa683827e
6 from datetime
import datetime
10 from .metadata_manager
import MetadataManager
11 from .subvolume_base
import SubvolumeBase
12 from ..op_sm
import OpSm
13 from ..template
import SubvolumeTemplate
14 from ..snapshot_util
import mksnap
, rmsnap
15 from ...exception
import IndexException
, OpSmException
, VolumeException
, MetadataMgrException
16 from ...fs_util
import listdir
18 from ..clone_index
import open_clone_index
, create_clone_index
20 log
= logging
.getLogger(__name__
)
22 class SubvolumeV1(SubvolumeBase
, SubvolumeTemplate
):
27 return SubvolumeV1
.VERSION
32 # no need to stat the path -- open() does that
33 return self
.metadata_mgr
.get_global_option('path').encode('utf-8')
34 except MetadataMgrException
as me
:
35 raise VolumeException(-errno
.EINVAL
, "error fetching subvolume metadata")
37 def create(self
, size
, isolate_nspace
, pool
, mode
, uid
, gid
):
38 subvolume_type
= SubvolumeBase
.SUBVOLUME_TYPE_NORMAL
40 initial_state
= OpSm
.get_init_state(subvolume_type
)
41 except OpSmException
as oe
:
42 raise VolumeException(-errno
.EINVAL
, "subvolume creation failed: internal error")
44 subvol_path
= os
.path
.join(self
.base_path
, str(uuid
.uuid4()).encode('utf-8'))
46 # create directory and set attributes
47 self
.fs
.mkdirs(subvol_path
, mode
)
48 self
.set_attrs(subvol_path
, size
, isolate_nspace
, pool
, uid
, gid
)
50 # persist subvolume metadata
51 qpath
= subvol_path
.decode('utf-8')
52 self
.init_config(SubvolumeV1
.VERSION
, subvolume_type
, qpath
, initial_state
)
53 except (VolumeException
, MetadataMgrException
, cephfs
.Error
) as e
:
55 log
.info("cleaning up subvolume with path: {0}".format(self
.subvolname
))
57 except VolumeException
as ve
:
58 log
.info("failed to cleanup subvolume '{0}' ({1})".format(self
.subvolname
, ve
))
60 if isinstance(e
, MetadataMgrException
):
61 log
.error("metadata manager exception: {0}".format(e
))
62 e
= VolumeException(-errno
.EINVAL
, "exception in subvolume metadata")
63 elif isinstance(e
, cephfs
.Error
):
64 e
= VolumeException(-e
.args
[0], e
.args
[1])
67 def add_clone_source(self
, volname
, subvolume
, snapname
, flush
=False):
68 self
.metadata_mgr
.add_section("source")
69 self
.metadata_mgr
.update_section("source", "volume", volname
)
70 if not subvolume
.group
.is_default_group():
71 self
.metadata_mgr
.update_section("source", "group", subvolume
.group_name
)
72 self
.metadata_mgr
.update_section("source", "subvolume", subvolume
.subvol_name
)
73 self
.metadata_mgr
.update_section("source", "snapshot", snapname
)
75 self
.metadata_mgr
.flush()
77 def remove_clone_source(self
, flush
=False):
78 self
.metadata_mgr
.remove_section("source")
80 self
.metadata_mgr
.flush()
82 def create_clone(self
, pool
, source_volname
, source_subvolume
, snapname
):
83 subvolume_type
= SubvolumeBase
.SUBVOLUME_TYPE_CLONE
85 initial_state
= OpSm
.get_init_state(subvolume_type
)
86 except OpSmException
as oe
:
87 raise VolumeException(-errno
.EINVAL
, "clone failed: internal error")
89 subvol_path
= os
.path
.join(self
.base_path
, str(uuid
.uuid4()).encode('utf-8'))
91 # create directory and set attributes
92 self
.fs
.mkdirs(subvol_path
, source_subvolume
.mode
)
93 self
.set_attrs(subvol_path
, None, None, pool
, source_subvolume
.uid
, source_subvolume
.gid
)
95 # persist subvolume metadata and clone source
96 qpath
= subvol_path
.decode('utf-8')
97 self
.metadata_mgr
.init(SubvolumeV1
.VERSION
, subvolume_type
, qpath
, initial_state
)
98 self
.add_clone_source(source_volname
, source_subvolume
, snapname
)
99 self
.metadata_mgr
.flush()
100 except (VolumeException
, MetadataMgrException
, cephfs
.Error
) as e
:
102 log
.info("cleaning up subvolume with path: {0}".format(self
.subvolname
))
104 except VolumeException
as ve
:
105 log
.info("failed to cleanup subvolume '{0}' ({1})".format(self
.subvolname
, ve
))
107 if isinstance(e
, MetadataMgrException
):
108 log
.error("metadata manager exception: {0}".format(e
))
109 e
= VolumeException(-errno
.EINVAL
, "exception in subvolume metadata")
110 elif isinstance(e
, cephfs
.Error
):
111 e
= VolumeException(-e
.args
[0], e
.args
[1])
114 def open(self
, need_complete
=True, expected_types
=[]):
116 self
.metadata_mgr
.refresh()
117 subvol_path
= self
.path
118 log
.debug("refreshed metadata, checking subvolume path '{0}'".format(subvol_path
))
119 st
= self
.fs
.stat(subvol_path
)
120 etype
= self
.metadata_mgr
.get_global_option(MetadataManager
.GLOBAL_META_KEY_TYPE
)
121 if len(expected_types
) and not etype
in expected_types
:
122 raise VolumeException(-errno
.ENOTSUP
, "subvolume '{0}' is not {1}".format(
123 self
.subvolname
, "a {0}".format(expected_types
[0]) if len(expected_types
) == 1 else \
124 "one of types ({0})".format(",".join(expected_types
))))
126 estate
= self
.metadata_mgr
.get_global_option(MetadataManager
.GLOBAL_META_KEY_STATE
)
127 if not OpSm
.is_final_state(estate
):
128 raise VolumeException(-errno
.EAGAIN
, "subvolume '{0}' is not ready for use".format(self
.subvolname
))
129 self
.uid
= int(st
.st_uid
)
130 self
.gid
= int(st
.st_gid
)
131 self
.mode
= int(st
.st_mode
& ~stat
.S_IFMT(st
.st_mode
))
132 except MetadataMgrException
as me
:
133 if me
.errno
== -errno
.ENOENT
:
134 raise VolumeException(-errno
.ENOENT
, "subvolume '{0}' does not exist".format(self
.subvolname
))
135 raise VolumeException(me
.args
[0], me
.args
[1])
136 except cephfs
.ObjectNotFound
:
137 log
.debug("missing subvolume path '{0}' for subvolume '{1}'".format(subvol_path
, self
.subvolname
))
138 raise VolumeException(-errno
.ENOENT
, "mount path missing for subvolume '{0}'".format(self
.subvolname
))
139 except cephfs
.Error
as e
:
140 raise VolumeException(-e
.args
[0], e
.args
[1])
142 def _get_clone_source(self
):
145 'volume' : self
.metadata_mgr
.get_option("source", "volume"),
146 'subvolume': self
.metadata_mgr
.get_option("source", "subvolume"),
147 'snapshot' : self
.metadata_mgr
.get_option("source", "snapshot"),
151 clone_source
["group"] = self
.metadata_mgr
.get_option("source", "group")
152 except MetadataMgrException
as me
:
153 if me
.errno
== -errno
.ENOENT
:
157 except MetadataMgrException
as me
:
158 raise VolumeException(-errno
.EINVAL
, "error fetching subvolume metadata")
163 state
= self
.metadata_mgr
.get_global_option(MetadataManager
.GLOBAL_META_KEY_STATE
)
164 subvolume_type
= self
.metadata_mgr
.get_global_option(MetadataManager
.GLOBAL_META_KEY_TYPE
)
168 if not OpSm
.is_final_state(state
) and subvolume_type
== SubvolumeBase
.SUBVOLUME_TYPE_CLONE
:
169 subvolume_status
["source"] = self
._get
_clone
_source
()
170 return subvolume_status
174 return self
.metadata_mgr
.get_global_option(MetadataManager
.GLOBAL_META_KEY_STATE
)
177 def state(self
, val
):
180 self
.metadata_mgr
.update_global_section(MetadataManager
.GLOBAL_META_KEY_STATE
, state
)
182 self
.metadata_mgr
.flush()
185 self
.trash_base_dir()
187 def resize(self
, newsize
, noshrink
):
188 subvol_path
= self
.path
189 return self
._resize
(subvol_path
, newsize
, noshrink
)
191 def snapshot_path(self
, snapname
):
192 return os
.path
.join(self
.path
,
193 self
.vol_spec
.snapshot_dir_prefix
.encode('utf-8'),
194 snapname
.encode('utf-8'))
196 def create_snapshot(self
, snapname
):
197 snappath
= self
.snapshot_path(snapname
)
198 mksnap(self
.fs
, snappath
)
200 def is_snapshot_protected(self
, snapname
):
202 self
.metadata_mgr
.get_option('protected snaps', snapname
)
203 except MetadataMgrException
as me
:
204 if me
.errno
== -errno
.ENOENT
:
207 log
.warning("error checking protected snap {0} ({1})".format(snapname
, me
))
208 raise VolumeException(-errno
.EINVAL
, "snapshot protection check failed")
212 def has_pending_clones(self
, snapname
):
214 return self
.metadata_mgr
.section_has_item('clone snaps', snapname
)
215 except MetadataMgrException
as me
:
216 if me
.errno
== -errno
.ENOENT
:
220 def remove_snapshot(self
, snapname
):
221 if self
.is_snapshot_protected(snapname
):
222 raise VolumeException(-errno
.EINVAL
, "snapshot '{0}' is protected".format(snapname
))
223 snappath
= self
.snapshot_path(snapname
)
224 rmsnap(self
.fs
, snappath
)
226 def snapshot_info(self
, snapname
):
227 snappath
= self
.snapshot_path(snapname
)
230 snap_attrs
= {'created_at':'ceph.snap.btime', 'size':'ceph.dir.rbytes',
231 'data_pool':'ceph.dir.layout.pool'}
232 for key
, val
in snap_attrs
.items():
233 snap_info
[key
] = self
.fs
.getxattr(snappath
, val
)
234 return {'size': int(snap_info
['size']),
235 'created_at': str(datetime
.fromtimestamp(float(snap_info
['created_at']))),
236 'data_pool': snap_info
['data_pool'].decode('utf-8'),
237 'protected': "yes" if self
.is_snapshot_protected(snapname
) else "no",
238 'has_pending_clones': "yes" if self
.has_pending_clones(snapname
) else "no"}
239 except cephfs
.Error
as e
:
240 if e
.errno
== errno
.ENOENT
:
241 raise VolumeException(-errno
.ENOENT
,
242 "snapshot '{0}' doesnot exist".format(snapname
))
243 raise VolumeException(-e
.args
[0], e
.args
[1])
245 def list_snapshots(self
):
247 dirpath
= os
.path
.join(self
.path
,
248 self
.vol_spec
.snapshot_dir_prefix
.encode('utf-8'))
249 return listdir(self
.fs
, dirpath
)
250 except VolumeException
as ve
:
251 if ve
.errno
== -errno
.ENOENT
:
255 def _protect_snapshot(self
, snapname
):
257 self
.metadata_mgr
.add_section("protected snaps")
258 self
.metadata_mgr
.update_section("protected snaps", snapname
, "1")
259 self
.metadata_mgr
.flush()
260 except MetadataMgrException
as me
:
261 log
.warning("error updating protected snap list ({0})".format(me
))
262 raise VolumeException(-errno
.EINVAL
, "error protecting snapshot")
264 def _unprotect_snapshot(self
, snapname
):
266 self
.metadata_mgr
.remove_option("protected snaps", snapname
)
267 self
.metadata_mgr
.flush()
268 except MetadataMgrException
as me
:
269 log
.warning("error updating protected snap list ({0})".format(me
))
270 raise VolumeException(-errno
.EINVAL
, "error unprotecting snapshot")
272 def protect_snapshot(self
, snapname
):
273 if not snapname
.encode('utf-8') in self
.list_snapshots():
274 raise VolumeException(-errno
.ENOENT
, "snapshot '{0}' does not exist".format(snapname
))
275 if self
.is_snapshot_protected(snapname
):
276 raise VolumeException(-errno
.EEXIST
, "snapshot '{0}' is already protected".format(snapname
))
277 self
._protect
_snapshot
(snapname
)
279 def unprotect_snapshot(self
, snapname
):
280 if not snapname
.encode('utf-8') in self
.list_snapshots():
281 raise VolumeException(-errno
.ENOENT
, "snapshot '{0}' does not exist".format(snapname
))
282 if not self
.is_snapshot_protected(snapname
):
283 raise VolumeException(-errno
.EEXIST
, "snapshot '{0}' is not protected".format(snapname
))
284 if self
.has_pending_clones(snapname
):
285 raise VolumeException(-errno
.EEXIST
, "snapshot '{0}' has pending clones".format(snapname
))
286 self
._unprotect
_snapshot
(snapname
)
288 def _add_snap_clone(self
, track_id
, snapname
):
289 self
.metadata_mgr
.add_section("clone snaps")
290 self
.metadata_mgr
.update_section("clone snaps", track_id
, snapname
)
291 self
.metadata_mgr
.flush()
293 def _remove_snap_clone(self
, track_id
):
294 self
.metadata_mgr
.remove_option("clone snaps", track_id
)
295 self
.metadata_mgr
.flush()
297 def attach_snapshot(self
, snapname
, tgt_subvolume
):
298 if not snapname
.encode('utf-8') in self
.list_snapshots():
299 raise VolumeException(-errno
.ENOENT
, "snapshot '{0}' does not exist".format(snapname
))
300 if not self
.is_snapshot_protected(snapname
):
301 raise VolumeException(-errno
.EINVAL
, "snapshot '{0}' is not protected".format(snapname
))
303 create_clone_index(self
.fs
, self
.vol_spec
)
304 with
open_clone_index(self
.fs
, self
.vol_spec
) as index
:
305 track_idx
= index
.track(tgt_subvolume
.base_path
)
306 self
._add
_snap
_clone
(track_idx
, snapname
)
307 except (IndexException
, MetadataMgrException
) as e
:
308 log
.warning("error creating clone index: {0}".format(e
))
309 raise VolumeException(-errno
.EINVAL
, "error cloning subvolume")
311 def detach_snapshot(self
, snapname
, track_id
):
312 if not snapname
.encode('utf-8') in self
.list_snapshots():
313 raise VolumeException(-errno
.ENOENT
, "snapshot '{0}' does not exist".format(snapname
))
315 with
open_clone_index(self
.fs
, self
.vol_spec
) as index
:
316 index
.untrack(track_id
)
317 self
._remove
_snap
_clone
(track_id
)
318 except (IndexException
, MetadataMgrException
) as e
:
319 log
.warning("error delining snapshot from clone: {0}".format(e
))
320 raise VolumeException(-errno
.EINVAL
, "error delinking snapshot from clone")