1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/migration/NativeFormat.h"
5 #include "include/neorados/RADOS.hpp"
6 #include "common/dout.h"
7 #include "common/errno.h"
8 #include "librbd/ImageCtx.h"
9 #include "librbd/ImageState.h"
10 #include "librbd/Utils.h"
11 #include "librbd/asio/ContextWQ.h"
12 #include "librbd/io/ImageDispatchSpec.h"
13 #include "json_spirit/json_spirit.h"
14 #include "boost/lexical_cast.hpp"
17 #define dout_subsys ceph_subsys_rbd
19 #define dout_prefix *_dout << "librbd::migration::NativeFormat: " << this \
20 << " " << __func__ << ": "
27 const std::string TYPE_KEY
{"type"};
28 const std::string POOL_ID_KEY
{"pool_id"};
29 const std::string POOL_NAME_KEY
{"pool_name"};
30 const std::string POOL_NAMESPACE_KEY
{"pool_namespace"};
31 const std::string IMAGE_NAME_KEY
{"image_name"};
32 const std::string IMAGE_ID_KEY
{"image_id"};
33 const std::string SNAP_NAME_KEY
{"snap_name"};
34 const std::string SNAP_ID_KEY
{"snap_id"};
36 } // anonymous namespace
39 std::string NativeFormat
<I
>::build_source_spec(
40 int64_t pool_id
, const std::string
& pool_namespace
,
41 const std::string
& image_name
, const std::string
& image_id
) {
42 json_spirit::mObject source_spec
;
43 source_spec
[TYPE_KEY
] = "native";
44 source_spec
[POOL_ID_KEY
] = pool_id
;
45 source_spec
[POOL_NAMESPACE_KEY
] = pool_namespace
;
46 source_spec
[IMAGE_NAME_KEY
] = image_name
;
47 if (!image_id
.empty()) {
48 source_spec
[IMAGE_ID_KEY
] = image_id
;
50 return json_spirit::write(source_spec
);
54 NativeFormat
<I
>::NativeFormat(
55 I
* image_ctx
, const json_spirit::mObject
& json_object
, bool import_only
)
56 : m_image_ctx(image_ctx
), m_json_object(json_object
),
57 m_import_only(import_only
) {
61 void NativeFormat
<I
>::open(Context
* on_finish
) {
62 auto cct
= m_image_ctx
->cct
;
63 ldout(cct
, 10) << dendl
;
65 auto& pool_name_val
= m_json_object
[POOL_NAME_KEY
];
66 if (pool_name_val
.type() == json_spirit::str_type
) {
67 librados::Rados
rados(m_image_ctx
->md_ctx
);
68 librados::IoCtx io_ctx
;
69 int r
= rados
.ioctx_create(pool_name_val
.get_str().c_str(), io_ctx
);
71 lderr(cct
) << "invalid pool name" << dendl
;
72 on_finish
->complete(r
);
76 m_pool_id
= io_ctx
.get_id();
77 } else if (pool_name_val
.type() != json_spirit::null_type
) {
78 lderr(cct
) << "invalid pool name" << dendl
;
79 on_finish
->complete(-EINVAL
);
83 auto& pool_id_val
= m_json_object
[POOL_ID_KEY
];
84 if (m_pool_id
!= -1 && pool_id_val
.type() != json_spirit::null_type
) {
85 lderr(cct
) << "cannot specify both pool name and pool id" << dendl
;
86 on_finish
->complete(-EINVAL
);
88 } else if (pool_id_val
.type() == json_spirit::int_type
) {
89 m_pool_id
= pool_id_val
.get_int64();
90 } else if (pool_id_val
.type() == json_spirit::str_type
) {
92 m_pool_id
= boost::lexical_cast
<int64_t>(pool_id_val
.get_str());
93 } catch (boost::bad_lexical_cast
&) {
97 if (m_pool_id
== -1) {
98 lderr(cct
) << "missing or invalid pool id" << dendl
;
99 on_finish
->complete(-EINVAL
);
103 auto& pool_namespace_val
= m_json_object
[POOL_NAMESPACE_KEY
];
104 if (pool_namespace_val
.type() == json_spirit::str_type
) {
105 m_pool_namespace
= pool_namespace_val
.get_str();
106 } else if (pool_namespace_val
.type() != json_spirit::null_type
) {
107 lderr(cct
) << "invalid pool namespace" << dendl
;
108 on_finish
->complete(-EINVAL
);
112 auto& image_name_val
= m_json_object
[IMAGE_NAME_KEY
];
113 if (image_name_val
.type() != json_spirit::str_type
) {
114 lderr(cct
) << "missing or invalid image name" << dendl
;
115 on_finish
->complete(-EINVAL
);
118 m_image_name
= image_name_val
.get_str();
120 auto& image_id_val
= m_json_object
[IMAGE_ID_KEY
];
121 if (image_id_val
.type() == json_spirit::str_type
) {
122 m_image_id
= image_id_val
.get_str();
123 } else if (image_id_val
.type() != json_spirit::null_type
) {
124 lderr(cct
) << "invalid image id" << dendl
;
125 on_finish
->complete(-EINVAL
);
129 auto& snap_name_val
= m_json_object
[SNAP_NAME_KEY
];
130 if (snap_name_val
.type() == json_spirit::str_type
) {
131 m_snap_name
= snap_name_val
.get_str();
132 } else if (snap_name_val
.type() != json_spirit::null_type
) {
133 lderr(cct
) << "invalid snap name" << dendl
;
134 on_finish
->complete(-EINVAL
);
138 auto& snap_id_val
= m_json_object
[SNAP_ID_KEY
];
139 if (!m_snap_name
.empty() && snap_id_val
.type() != json_spirit::null_type
) {
140 lderr(cct
) << "cannot specify both snap name and snap id" << dendl
;
141 on_finish
->complete(-EINVAL
);
143 } else if (snap_id_val
.type() == json_spirit::str_type
) {
145 m_snap_id
= boost::lexical_cast
<uint64_t>(snap_id_val
.get_str());
146 } catch (boost::bad_lexical_cast
&) {
148 } else if (snap_id_val
.type() == json_spirit::int_type
) {
149 m_snap_id
= snap_id_val
.get_uint64();
152 if (snap_id_val
.type() != json_spirit::null_type
&&
153 m_snap_id
== CEPH_NOSNAP
) {
154 lderr(cct
) << "invalid snap id" << dendl
;
155 on_finish
->complete(-EINVAL
);
159 // snapshot is required for import to keep source read-only
160 if (m_import_only
&& m_snap_name
.empty() && m_snap_id
== CEPH_NOSNAP
) {
161 lderr(cct
) << "snapshot required for import" << dendl
;
162 on_finish
->complete(-EINVAL
);
166 // TODO add support for external clusters
167 librados::IoCtx io_ctx
;
168 int r
= util::create_ioctx(m_image_ctx
->md_ctx
, "source image",
169 m_pool_id
, m_pool_namespace
, &io_ctx
);
171 on_finish
->complete(r
);
175 m_image_ctx
->md_ctx
.dup(io_ctx
);
176 m_image_ctx
->data_ctx
.dup(io_ctx
);
177 m_image_ctx
->name
= m_image_name
;
180 if (m_image_id
.empty() && !m_import_only
) {
181 flags
|= OPEN_FLAG_OLD_FORMAT
;
183 m_image_ctx
->id
= m_image_id
;
186 if (m_image_ctx
->child
!= nullptr) {
187 // set rados flags for reading the parent image
188 if (m_image_ctx
->child
->config
.template get_val
<bool>("rbd_balance_parent_reads")) {
189 m_image_ctx
->set_read_flag(librados::OPERATION_BALANCE_READS
);
190 } else if (m_image_ctx
->child
->config
.template get_val
<bool>("rbd_localize_parent_reads")) {
191 m_image_ctx
->set_read_flag(librados::OPERATION_LOCALIZE_READS
);
195 // open the source RBD image
196 on_finish
= new LambdaContext([this, on_finish
](int r
) {
197 handle_open(r
, on_finish
); });
198 m_image_ctx
->state
->open(flags
, on_finish
);
201 template <typename I
>
202 void NativeFormat
<I
>::handle_open(int r
, Context
* on_finish
) {
203 auto cct
= m_image_ctx
->cct
;
204 ldout(cct
, 10) << "r=" << r
<< dendl
;
207 lderr(cct
) << "failed to open image: " << cpp_strerror(r
) << dendl
;
208 on_finish
->complete(r
);
212 if (m_snap_id
== CEPH_NOSNAP
&& m_snap_name
.empty()) {
213 on_finish
->complete(0);
217 if (!m_snap_name
.empty()) {
218 std::shared_lock image_locker
{m_image_ctx
->image_lock
};
219 m_snap_id
= m_image_ctx
->get_snap_id(cls::rbd::UserSnapshotNamespace
{},
223 if (m_snap_id
== CEPH_NOSNAP
) {
224 lderr(cct
) << "failed to locate snapshot " << m_snap_name
<< dendl
;
225 on_finish
= new LambdaContext([on_finish
](int) {
226 on_finish
->complete(-ENOENT
); });
227 m_image_ctx
->state
->close(on_finish
);
231 on_finish
= new LambdaContext([this, on_finish
](int r
) {
232 handle_snap_set(r
, on_finish
); });
233 m_image_ctx
->state
->snap_set(m_snap_id
, on_finish
);
236 template <typename I
>
237 void NativeFormat
<I
>::handle_snap_set(int r
, Context
* on_finish
) {
238 auto cct
= m_image_ctx
->cct
;
239 ldout(cct
, 10) << "r=" << r
<< dendl
;
242 lderr(cct
) << "failed to set snapshot " << m_snap_id
<< ": "
243 << cpp_strerror(r
) << dendl
;
244 on_finish
= new LambdaContext([r
, on_finish
](int) {
245 on_finish
->complete(r
); });
246 m_image_ctx
->state
->close(on_finish
);
250 on_finish
->complete(0);
253 template <typename I
>
254 void NativeFormat
<I
>::close(Context
* on_finish
) {
255 auto cct
= m_image_ctx
->cct
;
256 ldout(cct
, 10) << dendl
;
258 // the native librbd::image::CloseRequest handles all cleanup
259 on_finish
->complete(0);
262 template <typename I
>
263 void NativeFormat
<I
>::get_snapshots(SnapInfos
* snap_infos
, Context
* on_finish
) {
264 auto cct
= m_image_ctx
->cct
;
265 ldout(cct
, 10) << dendl
;
267 m_image_ctx
->image_lock
.lock_shared();
268 *snap_infos
= m_image_ctx
->snap_info
;
269 m_image_ctx
->image_lock
.unlock_shared();
271 on_finish
->complete(0);
274 template <typename I
>
275 void NativeFormat
<I
>::get_image_size(uint64_t snap_id
, uint64_t* size
,
276 Context
* on_finish
) {
277 auto cct
= m_image_ctx
->cct
;
278 ldout(cct
, 10) << dendl
;
280 m_image_ctx
->image_lock
.lock_shared();
281 *size
= m_image_ctx
->get_image_size(snap_id
);
282 m_image_ctx
->image_lock
.unlock_shared();
285 on_finish
->complete(0);
288 template <typename I
>
289 void NativeFormat
<I
>::list_snaps(io::Extents
&& image_extents
,
290 io::SnapIds
&& snap_ids
, int list_snaps_flags
,
291 io::SnapshotDelta
* snapshot_delta
,
292 const ZTracer::Trace
&parent_trace
,
293 Context
* on_finish
) {
294 auto cct
= m_image_ctx
->cct
;
295 ldout(cct
, 20) << "image_extents=" << image_extents
<< dendl
;
297 auto aio_comp
= io::AioCompletion::create_and_start(
298 on_finish
, util::get_image_ctx(m_image_ctx
), io::AIO_TYPE_GENERIC
);
299 auto req
= io::ImageDispatchSpec::create_list_snaps(
300 *m_image_ctx
, io::IMAGE_DISPATCH_LAYER_MIGRATION
, aio_comp
,
301 std::move(image_extents
), io::ImageArea::DATA
, std::move(snap_ids
),
302 list_snaps_flags
, snapshot_delta
, {});
306 } // namespace migration
307 } // namespace librbd
309 template class librbd::migration::NativeFormat
<librbd::ImageCtx
>;