]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "librbd/api/Snapshot.h" | |
5 | #include "cls/rbd/cls_rbd_types.h" | |
6 | #include "common/errno.h" | |
9f95a23c | 7 | #include "librbd/internal.h" |
11fdf7f2 TL |
8 | #include "librbd/ImageCtx.h" |
9 | #include "librbd/ImageState.h" | |
10 | #include "librbd/Operations.h" | |
11 | #include "librbd/Utils.h" | |
9f95a23c | 12 | #include "librbd/api/Image.h" |
11fdf7f2 TL |
13 | #include "include/Context.h" |
14 | #include "common/Cond.h" | |
15 | ||
f67539c2 TL |
16 | #include <boost/variant.hpp> |
17 | ||
11fdf7f2 TL |
18 | #define dout_subsys ceph_subsys_rbd |
19 | #undef dout_prefix | |
20 | #define dout_prefix *_dout << "librbd::api::Snapshot: " << __func__ << ": " | |
21 | ||
9f95a23c TL |
22 | using librados::snap_t; |
23 | ||
11fdf7f2 TL |
24 | namespace librbd { |
25 | namespace api { | |
26 | ||
27 | namespace { | |
28 | ||
1e59de90 | 29 | class GetGroupVisitor { |
11fdf7f2 TL |
30 | public: |
31 | CephContext* cct; | |
32 | librados::IoCtx *image_ioctx; | |
33 | snap_group_namespace_t *group_snap; | |
34 | ||
35 | explicit GetGroupVisitor(CephContext* cct, librados::IoCtx *_image_ioctx, | |
36 | snap_group_namespace_t *group_snap) | |
37 | : cct(cct), image_ioctx(_image_ioctx), group_snap(group_snap) {}; | |
38 | ||
39 | template <typename T> | |
40 | inline int operator()(const T&) const { | |
41 | // ignore other than GroupSnapshotNamespace types. | |
42 | return -EINVAL; | |
43 | } | |
44 | ||
45 | inline int operator()( | |
46 | const cls::rbd::GroupSnapshotNamespace& snap_namespace) { | |
47 | IoCtx group_ioctx; | |
48 | int r = util::create_ioctx(*image_ioctx, "group", snap_namespace.group_pool, | |
49 | {}, &group_ioctx); | |
50 | if (r < 0) { | |
51 | return r; | |
52 | } | |
53 | ||
54 | cls::rbd::GroupSnapshot group_snapshot; | |
55 | ||
56 | std::string group_name; | |
57 | r = cls_client::dir_get_name(&group_ioctx, RBD_GROUP_DIRECTORY, | |
58 | snap_namespace.group_id, &group_name); | |
59 | if (r < 0) { | |
60 | lderr(cct) << "failed to retrieve group name: " << cpp_strerror(r) | |
61 | << dendl; | |
62 | return r; | |
63 | } | |
64 | ||
20effc67 | 65 | std::string group_header_oid = util::group_header_name(snap_namespace.group_id); |
11fdf7f2 TL |
66 | r = cls_client::group_snap_get_by_id(&group_ioctx, |
67 | group_header_oid, | |
68 | snap_namespace.group_snapshot_id, | |
69 | &group_snapshot); | |
70 | if (r < 0) { | |
71 | lderr(cct) << "failed to retrieve group snapshot: " << cpp_strerror(r) | |
72 | << dendl; | |
73 | return r; | |
74 | } | |
75 | ||
76 | group_snap->group_pool = group_ioctx.get_id(); | |
77 | group_snap->group_name = group_name; | |
78 | group_snap->group_snap_name = group_snapshot.name; | |
79 | return 0; | |
80 | } | |
81 | }; | |
82 | ||
1e59de90 | 83 | class GetTrashVisitor { |
11fdf7f2 TL |
84 | public: |
85 | std::string* original_name; | |
86 | ||
87 | explicit GetTrashVisitor(std::string* original_name) | |
88 | : original_name(original_name) { | |
89 | } | |
90 | ||
91 | template <typename T> | |
92 | inline int operator()(const T&) const { | |
93 | return -EINVAL; | |
94 | } | |
95 | ||
96 | inline int operator()( | |
97 | const cls::rbd::TrashSnapshotNamespace& snap_namespace) { | |
98 | *original_name = snap_namespace.original_name; | |
99 | return 0; | |
100 | } | |
101 | }; | |
102 | ||
1e59de90 | 103 | class GetMirrorVisitor { |
9f95a23c TL |
104 | public: |
105 | snap_mirror_namespace_t *mirror_snap; | |
106 | ||
107 | explicit GetMirrorVisitor(snap_mirror_namespace_t *mirror_snap) | |
108 | : mirror_snap(mirror_snap) { | |
109 | } | |
110 | ||
111 | template <typename T> | |
112 | inline int operator()(const T&) const { | |
113 | return -EINVAL; | |
114 | } | |
115 | ||
116 | inline int operator()( | |
117 | const cls::rbd::MirrorSnapshotNamespace& snap_namespace) { | |
118 | mirror_snap->state = static_cast<snap_mirror_state_t>(snap_namespace.state); | |
119 | mirror_snap->complete = snap_namespace.complete; | |
120 | mirror_snap->mirror_peer_uuids = snap_namespace.mirror_peer_uuids; | |
121 | mirror_snap->primary_mirror_uuid = snap_namespace.primary_mirror_uuid; | |
122 | mirror_snap->primary_snap_id = snap_namespace.primary_snap_id; | |
123 | mirror_snap->last_copied_object_number = | |
124 | snap_namespace.last_copied_object_number; | |
125 | return 0; | |
126 | } | |
127 | }; | |
128 | ||
11fdf7f2 TL |
129 | } // anonymous namespace |
130 | ||
131 | template <typename I> | |
132 | int Snapshot<I>::get_group_namespace(I *ictx, uint64_t snap_id, | |
133 | snap_group_namespace_t *group_snap) { | |
134 | int r = ictx->state->refresh_if_required(); | |
135 | if (r < 0) { | |
136 | return r; | |
137 | } | |
138 | ||
9f95a23c | 139 | std::shared_lock image_locker{ictx->image_lock}; |
11fdf7f2 TL |
140 | auto snap_info = ictx->get_snap_info(snap_id); |
141 | if (snap_info == nullptr) { | |
142 | return -ENOENT; | |
143 | } | |
144 | ||
eafe8130 | 145 | GetGroupVisitor ggv = GetGroupVisitor(ictx->cct, &ictx->md_ctx, group_snap); |
1e59de90 | 146 | r = snap_info->snap_namespace.visit(ggv); |
11fdf7f2 TL |
147 | if (r < 0) { |
148 | return r; | |
149 | } | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | template <typename I> | |
155 | int Snapshot<I>::get_trash_namespace(I *ictx, uint64_t snap_id, | |
156 | std::string* original_name) { | |
157 | int r = ictx->state->refresh_if_required(); | |
158 | if (r < 0) { | |
159 | return r; | |
160 | } | |
161 | ||
9f95a23c | 162 | std::shared_lock image_locker{ictx->image_lock}; |
11fdf7f2 TL |
163 | auto snap_info = ictx->get_snap_info(snap_id); |
164 | if (snap_info == nullptr) { | |
165 | return -ENOENT; | |
166 | } | |
167 | ||
168 | auto visitor = GetTrashVisitor(original_name); | |
1e59de90 | 169 | r = snap_info->snap_namespace.visit(visitor); |
11fdf7f2 TL |
170 | if (r < 0) { |
171 | return r; | |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
9f95a23c TL |
177 | template <typename I> |
178 | int Snapshot<I>::get_mirror_namespace( | |
179 | I *ictx, uint64_t snap_id, snap_mirror_namespace_t *mirror_snap) { | |
180 | int r = ictx->state->refresh_if_required(); | |
181 | if (r < 0) { | |
182 | return r; | |
183 | } | |
184 | ||
185 | std::shared_lock image_locker{ictx->image_lock}; | |
186 | auto snap_info = ictx->get_snap_info(snap_id); | |
187 | if (snap_info == nullptr) { | |
188 | return -ENOENT; | |
189 | } | |
190 | ||
191 | auto gmv = GetMirrorVisitor(mirror_snap); | |
1e59de90 | 192 | r = snap_info->snap_namespace.visit(gmv); |
9f95a23c TL |
193 | if (r < 0) { |
194 | return r; | |
195 | } | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
11fdf7f2 TL |
200 | template <typename I> |
201 | int Snapshot<I>::get_namespace_type(I *ictx, uint64_t snap_id, | |
202 | snap_namespace_type_t *namespace_type) { | |
203 | int r = ictx->state->refresh_if_required(); | |
204 | if (r < 0) { | |
205 | return r; | |
206 | } | |
207 | ||
9f95a23c | 208 | std::shared_lock l{ictx->image_lock}; |
11fdf7f2 TL |
209 | auto snap_info = ictx->get_snap_info(snap_id); |
210 | if (snap_info == nullptr) { | |
211 | return -ENOENT; | |
212 | } | |
213 | ||
214 | *namespace_type = static_cast<snap_namespace_type_t>( | |
215 | cls::rbd::get_snap_namespace_type(snap_info->snap_namespace)); | |
216 | return 0; | |
217 | } | |
218 | ||
219 | template <typename I> | |
220 | int Snapshot<I>::remove(I *ictx, uint64_t snap_id) { | |
221 | ldout(ictx->cct, 20) << "snap_remove " << ictx << " " << snap_id << dendl; | |
222 | ||
223 | int r = ictx->state->refresh_if_required(); | |
224 | if (r < 0) { | |
225 | return r; | |
226 | } | |
227 | ||
228 | cls::rbd::SnapshotNamespace snapshot_namespace; | |
229 | std::string snapshot_name; | |
230 | { | |
9f95a23c | 231 | std::shared_lock image_locker{ictx->image_lock}; |
11fdf7f2 TL |
232 | auto it = ictx->snap_info.find(snap_id); |
233 | if (it == ictx->snap_info.end()) { | |
234 | return -ENOENT; | |
235 | } | |
236 | ||
237 | snapshot_namespace = it->second.snap_namespace; | |
238 | snapshot_name = it->second.name; | |
239 | } | |
240 | ||
241 | C_SaferCond ctx; | |
242 | ictx->operations->snap_remove(snapshot_namespace, snapshot_name, &ctx); | |
243 | r = ctx.wait(); | |
244 | return r; | |
245 | } | |
246 | ||
9f95a23c TL |
247 | template <typename I> |
248 | int Snapshot<I>::get_name(I *ictx, uint64_t snap_id, std::string *snap_name) | |
249 | { | |
250 | ldout(ictx->cct, 20) << "snap_get_name " << ictx << " " << snap_id << dendl; | |
251 | ||
252 | int r = ictx->state->refresh_if_required(); | |
253 | if (r < 0) | |
254 | return r; | |
255 | ||
256 | std::shared_lock image_locker{ictx->image_lock}; | |
257 | r = ictx->get_snap_name(snap_id, snap_name); | |
258 | ||
259 | return r; | |
260 | } | |
261 | ||
262 | template <typename I> | |
263 | int Snapshot<I>::get_id(I *ictx, const std::string& snap_name, uint64_t *snap_id) | |
264 | { | |
265 | ldout(ictx->cct, 20) << "snap_get_id " << ictx << " " << snap_name << dendl; | |
266 | ||
267 | int r = ictx->state->refresh_if_required(); | |
268 | if (r < 0) | |
269 | return r; | |
270 | ||
271 | std::shared_lock image_locker{ictx->image_lock}; | |
272 | *snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), snap_name); | |
273 | if (*snap_id == CEPH_NOSNAP) | |
274 | return -ENOENT; | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | template <typename I> | |
20effc67 | 280 | int Snapshot<I>::list(I *ictx, std::vector<snap_info_t>& snaps) { |
9f95a23c TL |
281 | ldout(ictx->cct, 20) << "snap_list " << ictx << dendl; |
282 | ||
283 | int r = ictx->state->refresh_if_required(); | |
284 | if (r < 0) | |
285 | return r; | |
286 | ||
287 | std::shared_lock l{ictx->image_lock}; | |
288 | for (auto &it : ictx->snap_info) { | |
289 | snap_info_t info; | |
290 | info.name = it.second.name; | |
291 | info.id = it.first; | |
292 | info.size = it.second.size; | |
293 | snaps.push_back(info); | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | template <typename I> | |
300 | int Snapshot<I>::exists(I *ictx, const cls::rbd::SnapshotNamespace& snap_namespace, | |
301 | const char *snap_name, bool *exists) { | |
302 | ldout(ictx->cct, 20) << "snap_exists " << ictx << " " << snap_name << dendl; | |
303 | ||
304 | int r = ictx->state->refresh_if_required(); | |
305 | if (r < 0) | |
306 | return r; | |
307 | ||
308 | std::shared_lock l{ictx->image_lock}; | |
309 | *exists = ictx->get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP; | |
310 | return 0; | |
311 | } | |
312 | ||
f67539c2 TL |
313 | template <typename I> |
314 | int Snapshot<I>::create(I *ictx, const char *snap_name, uint32_t flags, | |
315 | ProgressContext& pctx) { | |
316 | ldout(ictx->cct, 20) << "snap_create " << ictx << " " << snap_name | |
317 | << " flags: " << flags << dendl; | |
318 | ||
319 | uint64_t internal_flags = 0; | |
320 | int r = util::snap_create_flags_api_to_internal(ictx->cct, flags, | |
321 | &internal_flags); | |
322 | if (r < 0) { | |
323 | return r; | |
324 | } | |
325 | ||
326 | return ictx->operations->snap_create(cls::rbd::UserSnapshotNamespace(), | |
327 | snap_name, internal_flags, pctx); | |
328 | } | |
329 | ||
9f95a23c TL |
330 | template <typename I> |
331 | int Snapshot<I>::remove(I *ictx, const char *snap_name, uint32_t flags, | |
332 | ProgressContext& pctx) { | |
333 | ldout(ictx->cct, 20) << "snap_remove " << ictx << " " << snap_name << " flags: " << flags << dendl; | |
334 | ||
335 | int r = 0; | |
336 | ||
337 | r = ictx->state->refresh_if_required(); | |
338 | if (r < 0) | |
339 | return r; | |
340 | ||
341 | if (flags & RBD_SNAP_REMOVE_FLATTEN) { | |
342 | r = Image<I>::flatten_children(ictx, snap_name, pctx); | |
343 | if (r < 0) { | |
344 | return r; | |
345 | } | |
346 | } | |
347 | ||
348 | bool protect; | |
349 | r = is_protected(ictx, snap_name, &protect); | |
350 | if (r < 0) { | |
351 | return r; | |
352 | } | |
353 | ||
354 | if (protect && flags & RBD_SNAP_REMOVE_UNPROTECT) { | |
355 | r = ictx->operations->snap_unprotect(cls::rbd::UserSnapshotNamespace(), snap_name); | |
356 | if (r < 0) { | |
357 | lderr(ictx->cct) << "failed to unprotect snapshot: " << snap_name << dendl; | |
358 | return r; | |
359 | } | |
360 | ||
361 | r = is_protected(ictx, snap_name, &protect); | |
362 | if (r < 0) { | |
363 | return r; | |
364 | } | |
365 | if (protect) { | |
366 | lderr(ictx->cct) << "snapshot is still protected after unprotection" << dendl; | |
367 | ceph_abort(); | |
368 | } | |
369 | } | |
370 | ||
371 | C_SaferCond ctx; | |
372 | ictx->operations->snap_remove(cls::rbd::UserSnapshotNamespace(), snap_name, &ctx); | |
373 | ||
374 | r = ctx.wait(); | |
375 | return r; | |
376 | } | |
377 | ||
378 | template <typename I> | |
379 | int Snapshot<I>::get_timestamp(I *ictx, uint64_t snap_id, struct timespec *timestamp) { | |
380 | auto snap_it = ictx->snap_info.find(snap_id); | |
381 | ceph_assert(snap_it != ictx->snap_info.end()); | |
382 | utime_t time = snap_it->second.timestamp; | |
383 | time.to_timespec(timestamp); | |
384 | return 0; | |
385 | } | |
386 | ||
387 | template <typename I> | |
388 | int Snapshot<I>::get_limit(I *ictx, uint64_t *limit) { | |
389 | int r = cls_client::snapshot_get_limit(&ictx->md_ctx, ictx->header_oid, | |
390 | limit); | |
391 | if (r == -EOPNOTSUPP) { | |
392 | *limit = UINT64_MAX; | |
393 | r = 0; | |
394 | } | |
395 | return r; | |
396 | } | |
397 | ||
398 | template <typename I> | |
399 | int Snapshot<I>::set_limit(I *ictx, uint64_t limit) { | |
400 | return ictx->operations->snap_set_limit(limit); | |
401 | } | |
402 | ||
403 | template <typename I> | |
404 | int Snapshot<I>::is_protected(I *ictx, const char *snap_name, bool *protect) { | |
405 | ldout(ictx->cct, 20) << "snap_is_protected " << ictx << " " << snap_name | |
406 | << dendl; | |
407 | ||
408 | int r = ictx->state->refresh_if_required(); | |
409 | if (r < 0) | |
410 | return r; | |
411 | ||
412 | std::shared_lock l{ictx->image_lock}; | |
413 | snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(), snap_name); | |
414 | if (snap_id == CEPH_NOSNAP) | |
415 | return -ENOENT; | |
416 | bool is_unprotected; | |
417 | r = ictx->is_snap_unprotected(snap_id, &is_unprotected); | |
418 | // consider both PROTECTED or UNPROTECTING to be 'protected', | |
419 | // since in either state they can't be deleted | |
420 | *protect = !is_unprotected; | |
421 | return r; | |
422 | } | |
423 | ||
424 | template <typename I> | |
425 | int Snapshot<I>::get_namespace(I *ictx, const char *snap_name, | |
426 | cls::rbd::SnapshotNamespace *snap_namespace) { | |
427 | ldout(ictx->cct, 20) << "get_snap_namespace " << ictx << " " << snap_name | |
428 | << dendl; | |
429 | ||
430 | int r = ictx->state->refresh_if_required(); | |
431 | if (r < 0) | |
432 | return r; | |
433 | std::shared_lock l{ictx->image_lock}; | |
434 | snap_t snap_id = ictx->get_snap_id(*snap_namespace, snap_name); | |
435 | if (snap_id == CEPH_NOSNAP) | |
436 | return -ENOENT; | |
437 | r = ictx->get_snap_namespace(snap_id, snap_namespace); | |
438 | return r; | |
439 | } | |
440 | ||
11fdf7f2 TL |
441 | } // namespace api |
442 | } // namespace librbd | |
443 | ||
444 | template class librbd::api::Snapshot<librbd::ImageCtx>; |