]>
Commit | Line | Data |
---|---|---|
f67539c2 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/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" | |
15 | #include <sstream> | |
16 | ||
17 | #define dout_subsys ceph_subsys_rbd | |
18 | #undef dout_prefix | |
19 | #define dout_prefix *_dout << "librbd::migration::NativeFormat: " << this \ | |
20 | << " " << __func__ << ": " | |
21 | ||
22 | namespace librbd { | |
23 | namespace migration { | |
24 | ||
25 | namespace { | |
26 | ||
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"}; | |
35 | ||
36 | } // anonymous namespace | |
37 | ||
38 | template <typename I> | |
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; | |
49 | } | |
50 | return json_spirit::write(source_spec); | |
51 | } | |
52 | ||
53 | template <typename I> | |
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) { | |
58 | } | |
59 | ||
60 | template <typename I> | |
61 | void NativeFormat<I>::open(Context* on_finish) { | |
62 | auto cct = m_image_ctx->cct; | |
63 | ldout(cct, 10) << dendl; | |
64 | ||
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); | |
70 | if (r < 0 ) { | |
71 | lderr(cct) << "invalid pool name" << dendl; | |
72 | on_finish->complete(r); | |
73 | return; | |
74 | } | |
75 | ||
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); | |
80 | return; | |
81 | } | |
82 | ||
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); | |
87 | return; | |
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) { | |
91 | try { | |
92 | m_pool_id = boost::lexical_cast<int64_t>(pool_id_val.get_str()); | |
93 | } catch (boost::bad_lexical_cast &) { | |
94 | } | |
95 | } | |
96 | ||
97 | if (m_pool_id == -1) { | |
98 | lderr(cct) << "missing or invalid pool id" << dendl; | |
99 | on_finish->complete(-EINVAL); | |
100 | return; | |
101 | } | |
102 | ||
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); | |
109 | return; | |
110 | } | |
111 | ||
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); | |
116 | return; | |
117 | } | |
118 | m_image_name = image_name_val.get_str(); | |
119 | ||
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); | |
126 | return; | |
127 | } | |
128 | ||
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); | |
135 | return; | |
136 | } | |
137 | ||
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); | |
142 | return; | |
143 | } else if (snap_id_val.type() == json_spirit::str_type) { | |
144 | try { | |
145 | m_snap_id = boost::lexical_cast<uint64_t>(snap_id_val.get_str()); | |
146 | } catch (boost::bad_lexical_cast &) { | |
147 | } | |
148 | } else if (snap_id_val.type() == json_spirit::int_type) { | |
149 | m_snap_id = snap_id_val.get_uint64(); | |
150 | } | |
151 | ||
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); | |
156 | return; | |
157 | } | |
158 | ||
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); | |
163 | return; | |
164 | } | |
165 | ||
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); | |
170 | if (r < 0) { | |
171 | on_finish->complete(r); | |
172 | return; | |
173 | } | |
174 | ||
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; | |
178 | ||
179 | uint64_t flags = 0; | |
180 | if (m_image_id.empty() && !m_import_only) { | |
181 | flags |= OPEN_FLAG_OLD_FORMAT; | |
182 | } else { | |
183 | m_image_ctx->id = m_image_id; | |
184 | } | |
185 | ||
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); | |
192 | } | |
193 | } | |
194 | ||
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); | |
199 | } | |
200 | ||
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; | |
205 | ||
206 | if (r < 0) { | |
207 | lderr(cct) << "failed to open image: " << cpp_strerror(r) << dendl; | |
208 | on_finish->complete(r); | |
209 | return; | |
210 | } | |
211 | ||
212 | if (m_snap_id == CEPH_NOSNAP && m_snap_name.empty()) { | |
213 | on_finish->complete(0); | |
214 | return; | |
215 | } | |
216 | ||
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{}, | |
220 | m_snap_name); | |
221 | } | |
222 | ||
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); | |
228 | return; | |
229 | } | |
230 | ||
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); | |
234 | } | |
235 | ||
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; | |
240 | ||
241 | if (r < 0) { | |
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); | |
247 | return; | |
248 | } | |
249 | ||
250 | on_finish->complete(0); | |
251 | } | |
252 | ||
253 | template <typename I> | |
254 | void NativeFormat<I>::close(Context* on_finish) { | |
255 | auto cct = m_image_ctx->cct; | |
256 | ldout(cct, 10) << dendl; | |
257 | ||
258 | // the native librbd::image::CloseRequest handles all cleanup | |
259 | on_finish->complete(0); | |
260 | } | |
261 | ||
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; | |
266 | ||
267 | m_image_ctx->image_lock.lock_shared(); | |
268 | *snap_infos = m_image_ctx->snap_info; | |
269 | m_image_ctx->image_lock.unlock_shared(); | |
270 | ||
271 | on_finish->complete(0); | |
272 | } | |
273 | ||
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; | |
279 | ||
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(); | |
283 | ||
284 | ||
285 | on_finish->complete(0); | |
286 | } | |
287 | ||
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; | |
296 | ||
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, | |
1e59de90 TL |
301 | std::move(image_extents), io::ImageArea::DATA, std::move(snap_ids), |
302 | list_snaps_flags, snapshot_delta, {}); | |
f67539c2 TL |
303 | req->send(); |
304 | } | |
305 | ||
306 | } // namespace migration | |
307 | } // namespace librbd | |
308 | ||
309 | template class librbd::migration::NativeFormat<librbd::ImageCtx>; |