1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/operation/SnapshotUnprotectRequest.h"
5 #include "include/rados/librados.hpp"
6 #include "include/stringify.h"
7 #include "common/dout.h"
8 #include "common/errno.h"
9 #include "librbd/AsyncObjectThrottle.h"
10 #include "librbd/ImageCtx.h"
11 #include "librbd/internal.h"
12 #include "librbd/Types.h"
13 #include "librbd/Utils.h"
17 #include <boost/lambda/bind.hpp>
18 #include <boost/lambda/construct.hpp>
20 #define dout_subsys ceph_subsys_rbd
22 #define dout_prefix *_dout << "librbd::SnapshotUnprotectRequest: "
29 typedef std::pair
<int64_t, std::string
> Pool
;
30 typedef std::vector
<Pool
> Pools
;
33 std::ostream
& operator<<(std::ostream
& os
,
34 const typename SnapshotUnprotectRequest
<I
>::State
& state
) {
36 case SnapshotUnprotectRequest
<I
>::STATE_UNPROTECT_SNAP_START
:
37 os
<< "UNPROTECT_SNAP_START";
39 case SnapshotUnprotectRequest
<I
>::STATE_SCAN_POOL_CHILDREN
:
40 os
<< "SCAN_POOL_CHILDREN";
42 case SnapshotUnprotectRequest
<I
>::STATE_UNPROTECT_SNAP_FINISH
:
43 os
<< "UNPROTECT_SNAP_FINISH";
45 case SnapshotUnprotectRequest
<I
>::STATE_UNPROTECT_SNAP_ROLLBACK
:
46 os
<< "UNPROTECT_SNAP_ROLLBACK";
49 os
<< "UNKNOWN (" << static_cast<uint32_t>(state
) << ")";
56 class C_ScanPoolChildren
: public C_AsyncObjectThrottle
<I
> {
58 C_ScanPoolChildren(AsyncObjectThrottle
<I
> &throttle
, I
*image_ctx
,
59 const ParentSpec
&pspec
, const Pools
&pools
,
61 : C_AsyncObjectThrottle
<I
>(throttle
, *image_ctx
), m_pspec(pspec
),
62 m_pool(pools
[pool_idx
]) {
66 I
&image_ctx
= this->m_image_ctx
;
67 assert(image_ctx
.owner_lock
.is_locked());
69 CephContext
*cct
= image_ctx
.cct
;
70 ldout(cct
, 10) << this << " scanning pool '" << m_pool
.second
<< "'"
73 librados::Rados
rados(image_ctx
.md_ctx
);
75 int r
= rados
.pool_get_base_tier(m_pool
.first
, &base_tier
);
77 ldout(cct
, 1) << "pool '" << m_pool
.second
<< "' no longer exists"
81 lderr(cct
) << "error retrieving base tier for pool '"
82 << m_pool
.second
<< "'" << dendl
;
85 if (m_pool
.first
!= base_tier
) {
86 // pool is a cache; skip it
90 r
= rados
.ioctx_create2(m_pool
.first
, m_pool_ioctx
);
92 ldout(cct
, 1) << "pool '" << m_pool
.second
<< "' no longer exists"
96 lderr(cct
) << "can't create ioctx for pool '" << m_pool
.second
101 librados::ObjectReadOperation op
;
102 cls_client::get_children_start(&op
, m_pspec
);
104 librados::AioCompletion
*rados_completion
=
105 util::create_rados_callback(this);
106 r
= m_pool_ioctx
.aio_operate(RBD_CHILDREN
, rados_completion
, &op
,
109 rados_completion
->release();
114 void finish(int r
) override
{
115 I
&image_ctx
= this->m_image_ctx
;
116 CephContext
*cct
= image_ctx
.cct
;
119 bufferlist::iterator it
= m_children_bl
.begin();
120 r
= cls_client::get_children_finish(&it
, &m_children
);
123 ldout(cct
, 10) << this << " retrieved children: r=" << r
<< dendl
;
125 // no children -- proceed with unprotect
128 lderr(cct
) << "cannot get children for pool '" << m_pool
.second
<< "'"
131 lderr(cct
) << "cannot unprotect: at least " << m_children
.size() << " "
132 << "child(ren) [" << joinify(m_children
.begin(),
134 std::string(",")) << "] "
135 << "in pool '" << m_pool
.second
<< "'" << dendl
;
138 C_AsyncObjectThrottle
<I
>::finish(r
);
146 std::set
<std::string
> m_children
;
147 bufferlist m_children_bl
;
150 } // anonymous namespace
152 template <typename I
>
153 SnapshotUnprotectRequest
<I
>::SnapshotUnprotectRequest(I
&image_ctx
,
155 const cls::rbd::SnapshotNamespace
&snap_namespace
,
156 const std::string
&snap_name
)
157 : Request
<I
>(image_ctx
, on_finish
), m_snap_namespace(snap_namespace
),
158 m_snap_name(snap_name
), m_ret_val(0), m_snap_id(CEPH_NOSNAP
) {
161 template <typename I
>
162 void SnapshotUnprotectRequest
<I
>::send_op() {
163 send_unprotect_snap_start();
166 template <typename I
>
167 bool SnapshotUnprotectRequest
<I
>::should_complete(int r
) {
168 I
&image_ctx
= this->m_image_ctx
;
169 CephContext
*cct
= image_ctx
.cct
;
170 ldout(cct
, 5) << this << " " << __func__
<< ": state=" << m_state
<< ", "
171 << "r=" << r
<< dendl
;
174 ldout(cct
, 1) << "snapshot is already unprotected" << dendl
;
176 lderr(cct
) << "encountered error: " << cpp_strerror(r
) << dendl
;
178 if (m_ret_val
== 0) {
183 // use a different state machine once an error is encountered
185 return should_complete_error();
188 RWLock::RLocker
owner_lock(image_ctx
.owner_lock
);
189 bool finished
= false;
191 case STATE_UNPROTECT_SNAP_START
:
192 send_scan_pool_children();
194 case STATE_SCAN_POOL_CHILDREN
:
195 send_unprotect_snap_finish();
197 case STATE_UNPROTECT_SNAP_FINISH
:
207 template <typename I
>
208 bool SnapshotUnprotectRequest
<I
>::should_complete_error() {
209 I
&image_ctx
= this->m_image_ctx
;
210 RWLock::RLocker
owner_locker(image_ctx
.owner_lock
);
211 CephContext
*cct
= image_ctx
.cct
;
212 lderr(cct
) << this << " " << __func__
<< ": "
213 << "ret_val=" << m_ret_val
<< dendl
;
215 bool finished
= true;
216 if (m_state
== STATE_SCAN_POOL_CHILDREN
||
217 m_state
== STATE_UNPROTECT_SNAP_FINISH
) {
218 send_unprotect_snap_rollback();
224 template <typename I
>
225 void SnapshotUnprotectRequest
<I
>::send_unprotect_snap_start() {
226 I
&image_ctx
= this->m_image_ctx
;
227 assert(image_ctx
.owner_lock
.is_locked());
229 CephContext
*cct
= image_ctx
.cct
;
230 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
232 m_state
= STATE_UNPROTECT_SNAP_START
;
234 int r
= verify_and_send_unprotect_snap_start();
236 this->async_complete(r
);
241 template <typename I
>
242 void SnapshotUnprotectRequest
<I
>::send_scan_pool_children() {
243 I
&image_ctx
= this->m_image_ctx
;
244 assert(image_ctx
.owner_lock
.is_locked());
246 CephContext
*cct
= image_ctx
.cct
;
247 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
248 m_state
= STATE_SCAN_POOL_CHILDREN
;
250 // search all pools for children depending on this snapshot
251 // TODO add async version of wait_for_latest_osdmap
252 librados::Rados
rados(image_ctx
.md_ctx
);
253 rados
.wait_for_latest_osdmap();
255 // protect against pools being renamed/deleted
256 std::list
<Pool
> pool_list
;
257 rados
.pool_list2(pool_list
);
259 ParentSpec
pspec(image_ctx
.md_ctx
.get_id(), image_ctx
.id
, m_snap_id
);
260 Pools
pools(pool_list
.begin(), pool_list
.end());
262 Context
*ctx
= this->create_callback_context();
263 typename AsyncObjectThrottle
<I
>::ContextFactory
context_factory(
264 boost::lambda::bind(boost::lambda::new_ptr
<C_ScanPoolChildren
<I
> >(),
265 boost::lambda::_1
, &image_ctx
, pspec
, pools
, boost::lambda::_2
));
266 AsyncObjectThrottle
<I
> *throttle
= new AsyncObjectThrottle
<I
>(
267 nullptr, image_ctx
, context_factory
, ctx
, NULL
, 0, pools
.size());
268 throttle
->start_ops(image_ctx
.concurrent_management_ops
);
271 template <typename I
>
272 void SnapshotUnprotectRequest
<I
>::send_unprotect_snap_finish() {
273 I
&image_ctx
= this->m_image_ctx
;
274 assert(image_ctx
.owner_lock
.is_locked());
276 CephContext
*cct
= image_ctx
.cct
;
277 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
279 m_state
= STATE_UNPROTECT_SNAP_FINISH
;
281 librados::ObjectWriteOperation op
;
282 cls_client::set_protection_status(&op
, m_snap_id
,
283 RBD_PROTECTION_STATUS_UNPROTECTED
);
285 librados::AioCompletion
*comp
= this->create_callback_completion();
286 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, comp
, &op
);
291 template <typename I
>
292 void SnapshotUnprotectRequest
<I
>::send_unprotect_snap_rollback() {
293 I
&image_ctx
= this->m_image_ctx
;
294 assert(image_ctx
.owner_lock
.is_locked());
296 CephContext
*cct
= image_ctx
.cct
;
297 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
299 m_state
= STATE_UNPROTECT_SNAP_ROLLBACK
;
301 librados::ObjectWriteOperation op
;
302 cls_client::set_protection_status(&op
, m_snap_id
,
303 RBD_PROTECTION_STATUS_PROTECTED
);
305 librados::AioCompletion
*comp
= this->create_callback_completion();
306 int r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, comp
, &op
);
311 template <typename I
>
312 int SnapshotUnprotectRequest
<I
>::verify_and_send_unprotect_snap_start() {
313 I
&image_ctx
= this->m_image_ctx
;
314 RWLock::RLocker
md_locker(image_ctx
.md_lock
);
315 RWLock::RLocker
snap_locker(image_ctx
.snap_lock
);
317 CephContext
*cct
= image_ctx
.cct
;
318 if ((image_ctx
.features
& RBD_FEATURE_LAYERING
) == 0) {
319 lderr(cct
) << "image must support layering" << dendl
;
323 m_snap_id
= image_ctx
.get_snap_id(m_snap_namespace
, m_snap_name
);
324 if (m_snap_id
== CEPH_NOSNAP
) {
329 int r
= image_ctx
.is_snap_unprotected(m_snap_id
, &is_unprotected
);
334 if (is_unprotected
) {
335 lderr(cct
) << "snapshot is already unprotected" << dendl
;
339 librados::ObjectWriteOperation op
;
340 cls_client::set_protection_status(&op
, m_snap_id
,
341 RBD_PROTECTION_STATUS_UNPROTECTING
);
343 librados::AioCompletion
*comp
= this->create_callback_completion();
344 r
= image_ctx
.md_ctx
.aio_operate(image_ctx
.header_oid
, comp
, &op
);
348 // TODO legacy code threw a notification post UNPROTECTING update -- required?
352 } // namespace operation
353 } // namespace librbd
355 template class librbd::operation::SnapshotUnprotectRequest
<librbd::ImageCtx
>;