]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "include/compat.h" | |
5 | #include "BootstrapRequest.h" | |
6 | #include "CloseImageRequest.h" | |
7 | #include "CreateImageRequest.h" | |
8 | #include "IsPrimaryRequest.h" | |
9 | #include "OpenImageRequest.h" | |
10 | #include "OpenLocalImageRequest.h" | |
11 | #include "common/debug.h" | |
12 | #include "common/dout.h" | |
13 | #include "common/errno.h" | |
14 | #include "common/WorkQueue.h" | |
15 | #include "cls/rbd/cls_rbd_client.h" | |
16 | #include "journal/Journaler.h" | |
17 | #include "librbd/ImageCtx.h" | |
18 | #include "librbd/ImageState.h" | |
19 | #include "librbd/internal.h" | |
20 | #include "librbd/Journal.h" | |
21 | #include "librbd/Utils.h" | |
22 | #include "librbd/journal/Types.h" | |
23 | #include "tools/rbd_mirror/ProgressContext.h" | |
31f18b77 | 24 | #include "tools/rbd_mirror/ImageSync.h" |
7c673cae FG |
25 | |
26 | #define dout_context g_ceph_context | |
27 | #define dout_subsys ceph_subsys_rbd_mirror | |
28 | #undef dout_prefix | |
29 | #define dout_prefix *_dout << "rbd::mirror::image_replayer::BootstrapRequest: " \ | |
30 | << this << " " << __func__ | |
31 | ||
32 | namespace rbd { | |
33 | namespace mirror { | |
34 | namespace image_replayer { | |
35 | ||
36 | using librbd::util::create_context_callback; | |
37 | using librbd::util::create_rados_callback; | |
38 | using librbd::util::unique_lock_name; | |
39 | ||
40 | template <typename I> | |
41 | BootstrapRequest<I>::BootstrapRequest( | |
42 | librados::IoCtx &local_io_ctx, | |
43 | librados::IoCtx &remote_io_ctx, | |
31f18b77 | 44 | InstanceWatcher<I> *instance_watcher, |
7c673cae FG |
45 | I **local_image_ctx, |
46 | const std::string &local_image_id, | |
47 | const std::string &remote_image_id, | |
48 | const std::string &global_image_id, | |
49 | ContextWQ *work_queue, SafeTimer *timer, | |
50 | Mutex *timer_lock, | |
51 | const std::string &local_mirror_uuid, | |
52 | const std::string &remote_mirror_uuid, | |
53 | Journaler *journaler, | |
b32b8144 | 54 | cls::journal::ClientState *client_state, |
7c673cae FG |
55 | MirrorPeerClientMeta *client_meta, |
56 | Context *on_finish, | |
57 | bool *do_resync, | |
58 | rbd::mirror::ProgressContext *progress_ctx) | |
59 | : BaseRequest("rbd::mirror::image_replayer::BootstrapRequest", | |
60 | reinterpret_cast<CephContext*>(local_io_ctx.cct()), on_finish), | |
61 | m_local_io_ctx(local_io_ctx), m_remote_io_ctx(remote_io_ctx), | |
31f18b77 FG |
62 | m_instance_watcher(instance_watcher), m_local_image_ctx(local_image_ctx), |
63 | m_local_image_id(local_image_id), m_remote_image_id(remote_image_id), | |
64 | m_global_image_id(global_image_id), m_work_queue(work_queue), | |
65 | m_timer(timer), m_timer_lock(timer_lock), | |
7c673cae FG |
66 | m_local_mirror_uuid(local_mirror_uuid), |
67 | m_remote_mirror_uuid(remote_mirror_uuid), m_journaler(journaler), | |
b32b8144 FG |
68 | m_client_state(client_state), m_client_meta(client_meta), |
69 | m_progress_ctx(progress_ctx), m_do_resync(do_resync), | |
7c673cae FG |
70 | m_lock(unique_lock_name("BootstrapRequest::m_lock", this)) { |
71 | } | |
72 | ||
73 | template <typename I> | |
74 | BootstrapRequest<I>::~BootstrapRequest() { | |
75 | assert(m_remote_image_ctx == nullptr); | |
76 | } | |
77 | ||
c07f9fc5 FG |
78 | template <typename I> |
79 | bool BootstrapRequest<I>::is_syncing() const { | |
80 | Mutex::Locker locker(m_lock); | |
81 | return (m_image_sync != nullptr); | |
82 | } | |
83 | ||
7c673cae FG |
84 | template <typename I> |
85 | void BootstrapRequest<I>::send() { | |
86 | *m_do_resync = false; | |
87 | ||
88 | get_remote_tag_class(); | |
89 | } | |
90 | ||
91 | template <typename I> | |
92 | void BootstrapRequest<I>::cancel() { | |
93 | dout(20) << dendl; | |
94 | ||
95 | Mutex::Locker locker(m_lock); | |
96 | m_canceled = true; | |
97 | ||
31f18b77 FG |
98 | if (m_image_sync != nullptr) { |
99 | m_image_sync->cancel(); | |
100 | } | |
7c673cae FG |
101 | } |
102 | ||
103 | template <typename I> | |
104 | void BootstrapRequest<I>::get_remote_tag_class() { | |
105 | dout(20) << dendl; | |
106 | ||
107 | update_progress("GET_REMOTE_TAG_CLASS"); | |
108 | ||
109 | Context *ctx = create_context_callback< | |
110 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_tag_class>( | |
111 | this); | |
112 | m_journaler->get_client(librbd::Journal<>::IMAGE_CLIENT_ID, &m_client, ctx); | |
113 | } | |
114 | ||
115 | template <typename I> | |
116 | void BootstrapRequest<I>::handle_get_remote_tag_class(int r) { | |
117 | dout(20) << ": r=" << r << dendl; | |
118 | ||
119 | if (r < 0) { | |
120 | derr << ": failed to retrieve remote client: " << cpp_strerror(r) << dendl; | |
121 | finish(r); | |
122 | return; | |
123 | } | |
124 | ||
125 | librbd::journal::ClientData client_data; | |
126 | bufferlist::iterator it = m_client.data.begin(); | |
127 | try { | |
128 | ::decode(client_data, it); | |
129 | } catch (const buffer::error &err) { | |
130 | derr << ": failed to decode remote client meta data: " << err.what() | |
131 | << dendl; | |
132 | finish(-EBADMSG); | |
133 | return; | |
134 | } | |
135 | ||
136 | librbd::journal::ImageClientMeta *client_meta = | |
137 | boost::get<librbd::journal::ImageClientMeta>(&client_data.client_meta); | |
138 | if (client_meta == nullptr) { | |
139 | derr << ": unknown remote client registration" << dendl; | |
140 | finish(-EINVAL); | |
141 | return; | |
142 | } | |
143 | ||
144 | m_remote_tag_class = client_meta->tag_class; | |
145 | dout(10) << ": remote tag class=" << m_remote_tag_class << dendl; | |
146 | ||
d2e6a577 FG |
147 | open_remote_image(); |
148 | } | |
149 | ||
150 | template <typename I> | |
151 | void BootstrapRequest<I>::open_remote_image() { | |
152 | dout(20) << dendl; | |
153 | ||
154 | update_progress("OPEN_REMOTE_IMAGE"); | |
155 | ||
156 | Context *ctx = create_context_callback< | |
157 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_open_remote_image>( | |
158 | this); | |
159 | OpenImageRequest<I> *request = OpenImageRequest<I>::create( | |
160 | m_remote_io_ctx, &m_remote_image_ctx, m_remote_image_id, false, | |
161 | ctx); | |
162 | request->send(); | |
163 | } | |
164 | ||
165 | template <typename I> | |
166 | void BootstrapRequest<I>::handle_open_remote_image(int r) { | |
167 | dout(20) << ": r=" << r << dendl; | |
168 | ||
169 | if (r < 0) { | |
170 | derr << ": failed to open remote image: " << cpp_strerror(r) << dendl; | |
171 | assert(m_remote_image_ctx == nullptr); | |
172 | finish(r); | |
173 | return; | |
174 | } | |
175 | ||
7c673cae FG |
176 | is_primary(); |
177 | } | |
178 | ||
179 | template <typename I> | |
180 | void BootstrapRequest<I>::is_primary() { | |
181 | dout(20) << dendl; | |
182 | ||
183 | update_progress("OPEN_REMOTE_IMAGE"); | |
184 | ||
185 | Context *ctx = create_context_callback< | |
186 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_is_primary>( | |
187 | this); | |
188 | IsPrimaryRequest<I> *request = IsPrimaryRequest<I>::create(m_remote_image_ctx, | |
189 | &m_primary, ctx); | |
190 | request->send(); | |
191 | } | |
192 | ||
193 | template <typename I> | |
194 | void BootstrapRequest<I>::handle_is_primary(int r) { | |
195 | dout(20) << ": r=" << r << dendl; | |
196 | ||
28e407b8 AA |
197 | if (r == -ENOENT) { |
198 | dout(5) << ": remote image is not mirrored" << dendl; | |
199 | m_ret_val = -EREMOTEIO; | |
200 | close_remote_image(); | |
201 | return; | |
202 | } else if (r < 0) { | |
7c673cae FG |
203 | derr << ": error querying remote image primary status: " << cpp_strerror(r) |
204 | << dendl; | |
205 | m_ret_val = r; | |
206 | close_remote_image(); | |
207 | return; | |
208 | } | |
209 | ||
210 | if (!m_primary) { | |
28e407b8 AA |
211 | if (m_local_image_id.empty()) { |
212 | // no local image and remote isn't primary -- don't sync it | |
213 | dout(5) << ": remote image is not primary -- not syncing" | |
214 | << dendl; | |
215 | m_ret_val = -EREMOTEIO; | |
216 | close_remote_image(); | |
217 | return; | |
218 | } else if (m_client_meta->state != | |
219 | librbd::journal::MIRROR_PEER_STATE_REPLAYING) { | |
220 | // ensure we attempt to re-sync to remote if it's re-promoted | |
221 | dout(5) << ": remote image is not primary -- sync interrupted" | |
222 | << dendl; | |
223 | m_ret_val = -EREMOTEIO; | |
224 | update_client_state(); | |
225 | return; | |
226 | } | |
7c673cae FG |
227 | } |
228 | ||
b32b8144 FG |
229 | if (!m_client_meta->image_id.empty()) { |
230 | // have an image id -- use that to open the image since a deletion (resync) | |
231 | // will leave the old image id registered in the peer | |
232 | m_local_image_id = m_client_meta->image_id; | |
233 | } | |
234 | ||
7c673cae | 235 | if (m_local_image_id.empty()) { |
28e407b8 | 236 | // prepare to create local image |
d2e6a577 | 237 | update_client_image(); |
7c673cae FG |
238 | return; |
239 | } | |
240 | ||
241 | open_local_image(); | |
242 | } | |
243 | ||
244 | template <typename I> | |
245 | void BootstrapRequest<I>::update_client_state() { | |
7c673cae FG |
246 | dout(20) << dendl; |
247 | update_progress("UPDATE_CLIENT_STATE"); | |
248 | ||
249 | librbd::journal::MirrorPeerClientMeta client_meta(*m_client_meta); | |
250 | client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; | |
251 | ||
252 | librbd::journal::ClientData client_data(client_meta); | |
253 | bufferlist data_bl; | |
254 | ::encode(client_data, data_bl); | |
255 | ||
256 | Context *ctx = create_context_callback< | |
257 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_update_client_state>( | |
258 | this); | |
259 | m_journaler->update_client(data_bl, ctx); | |
260 | } | |
261 | ||
262 | template <typename I> | |
263 | void BootstrapRequest<I>::handle_update_client_state(int r) { | |
264 | dout(20) << ": r=" << r << dendl; | |
265 | if (r < 0) { | |
266 | derr << ": failed to update client: " << cpp_strerror(r) << dendl; | |
267 | } else { | |
d2e6a577 | 268 | m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; |
7c673cae FG |
269 | } |
270 | ||
271 | close_remote_image(); | |
272 | } | |
273 | ||
274 | template <typename I> | |
275 | void BootstrapRequest<I>::open_local_image() { | |
276 | dout(20) << dendl; | |
277 | ||
278 | update_progress("OPEN_LOCAL_IMAGE"); | |
279 | ||
280 | Context *ctx = create_context_callback< | |
281 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_open_local_image>( | |
282 | this); | |
283 | OpenLocalImageRequest<I> *request = OpenLocalImageRequest<I>::create( | |
284 | m_local_io_ctx, m_local_image_ctx, m_local_image_id, m_work_queue, | |
285 | ctx); | |
286 | request->send(); | |
287 | } | |
288 | ||
289 | template <typename I> | |
290 | void BootstrapRequest<I>::handle_open_local_image(int r) { | |
291 | dout(20) << ": r=" << r << dendl; | |
292 | ||
293 | if (r == -ENOENT) { | |
294 | assert(*m_local_image_ctx == nullptr); | |
295 | dout(10) << ": local image missing" << dendl; | |
d2e6a577 | 296 | unregister_client(); |
7c673cae FG |
297 | return; |
298 | } else if (r == -EREMOTEIO) { | |
299 | assert(*m_local_image_ctx == nullptr); | |
300 | dout(10) << "local image is primary -- skipping image replay" << dendl; | |
301 | m_ret_val = r; | |
302 | close_remote_image(); | |
303 | return; | |
304 | } else if (r < 0) { | |
305 | assert(*m_local_image_ctx == nullptr); | |
306 | derr << ": failed to open local image: " << cpp_strerror(r) << dendl; | |
307 | m_ret_val = r; | |
308 | close_remote_image(); | |
309 | return; | |
310 | } | |
311 | ||
312 | I *local_image_ctx = (*m_local_image_ctx); | |
313 | { | |
28e407b8 | 314 | local_image_ctx->snap_lock.get_read(); |
7c673cae | 315 | if (local_image_ctx->journal == nullptr) { |
28e407b8 AA |
316 | local_image_ctx->snap_lock.put_read(); |
317 | ||
7c673cae FG |
318 | derr << ": local image does not support journaling" << dendl; |
319 | m_ret_val = -EINVAL; | |
320 | close_local_image(); | |
321 | return; | |
322 | } | |
323 | ||
324 | r = (*m_local_image_ctx)->journal->is_resync_requested(m_do_resync); | |
325 | if (r < 0) { | |
28e407b8 AA |
326 | local_image_ctx->snap_lock.put_read(); |
327 | ||
7c673cae FG |
328 | derr << ": failed to check if a resync was requested" << dendl; |
329 | m_ret_val = r; | |
330 | close_local_image(); | |
331 | return; | |
332 | } | |
28e407b8 AA |
333 | |
334 | m_local_tag_tid = local_image_ctx->journal->get_tag_tid(); | |
335 | m_local_tag_data = local_image_ctx->journal->get_tag_data(); | |
336 | dout(10) << ": local tag=" << m_local_tag_tid << ", " | |
337 | << "local tag data=" << m_local_tag_data << dendl; | |
338 | local_image_ctx->snap_lock.put_read(); | |
339 | } | |
340 | ||
341 | if (m_local_tag_data.mirror_uuid != m_remote_mirror_uuid && !m_primary) { | |
342 | // if the local mirror is not linked to the (now) non-primary image, | |
343 | // stop the replay. Otherwise, we ignore that the remote is non-primary | |
344 | // so that we can replay the demotion | |
345 | dout(5) << ": remote image is not primary -- skipping image replay" | |
346 | << dendl; | |
347 | m_ret_val = -EREMOTEIO; | |
348 | close_local_image(); | |
349 | return; | |
7c673cae FG |
350 | } |
351 | ||
352 | if (*m_do_resync) { | |
353 | close_remote_image(); | |
354 | return; | |
355 | } | |
356 | ||
b32b8144 | 357 | if (*m_client_state == cls::journal::CLIENT_STATE_DISCONNECTED) { |
7c673cae FG |
358 | dout(10) << ": client flagged disconnected -- skipping bootstrap" << dendl; |
359 | // The caller is expected to detect disconnect initializing remote journal. | |
360 | m_ret_val = 0; | |
361 | close_remote_image(); | |
362 | return; | |
363 | } | |
364 | ||
d2e6a577 | 365 | get_remote_tags(); |
7c673cae FG |
366 | } |
367 | ||
368 | template <typename I> | |
d2e6a577 | 369 | void BootstrapRequest<I>::unregister_client() { |
7c673cae | 370 | dout(20) << dendl; |
d2e6a577 | 371 | update_progress("UNREGISTER_CLIENT"); |
7c673cae FG |
372 | |
373 | m_local_image_id = ""; | |
7c673cae | 374 | Context *ctx = create_context_callback< |
d2e6a577 | 375 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_unregister_client>( |
7c673cae | 376 | this); |
d2e6a577 | 377 | m_journaler->unregister_client(ctx); |
7c673cae FG |
378 | } |
379 | ||
380 | template <typename I> | |
d2e6a577 | 381 | void BootstrapRequest<I>::handle_unregister_client(int r) { |
7c673cae | 382 | dout(20) << ": r=" << r << dendl; |
7c673cae | 383 | if (r < 0) { |
d2e6a577 FG |
384 | derr << ": failed to unregister with remote journal: " << cpp_strerror(r) |
385 | << dendl; | |
7c673cae FG |
386 | m_ret_val = r; |
387 | close_remote_image(); | |
388 | return; | |
389 | } | |
390 | ||
d2e6a577 FG |
391 | *m_client_meta = librbd::journal::MirrorPeerClientMeta(""); |
392 | register_client(); | |
7c673cae FG |
393 | } |
394 | ||
b32b8144 FG |
395 | template <typename I> |
396 | void BootstrapRequest<I>::register_client() { | |
397 | dout(20) << dendl; | |
398 | ||
399 | update_progress("REGISTER_CLIENT"); | |
400 | ||
28e407b8 AA |
401 | assert(m_local_image_id.empty()); |
402 | librbd::journal::MirrorPeerClientMeta mirror_peer_client_meta; | |
b32b8144 FG |
403 | mirror_peer_client_meta.state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; |
404 | ||
405 | librbd::journal::ClientData client_data{mirror_peer_client_meta}; | |
406 | bufferlist client_data_bl; | |
407 | ::encode(client_data, client_data_bl); | |
408 | ||
409 | Context *ctx = create_context_callback< | |
410 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_register_client>( | |
411 | this); | |
412 | m_journaler->register_client(client_data_bl, ctx); | |
413 | } | |
414 | ||
415 | template <typename I> | |
416 | void BootstrapRequest<I>::handle_register_client(int r) { | |
417 | dout(20) << ": r=" << r << dendl; | |
418 | ||
419 | if (r < 0) { | |
420 | derr << ": failed to register with remote journal: " << cpp_strerror(r) | |
421 | << dendl; | |
422 | m_ret_val = r; | |
423 | close_remote_image(); | |
424 | return; | |
425 | } | |
426 | ||
427 | *m_client_state = cls::journal::CLIENT_STATE_CONNECTED; | |
28e407b8 | 428 | *m_client_meta = librbd::journal::MirrorPeerClientMeta(); |
b32b8144 FG |
429 | m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_REPLAYING; |
430 | ||
431 | is_primary(); | |
432 | } | |
433 | ||
7c673cae FG |
434 | template <typename I> |
435 | void BootstrapRequest<I>::update_client_image() { | |
436 | dout(20) << dendl; | |
7c673cae FG |
437 | update_progress("UPDATE_CLIENT_IMAGE"); |
438 | ||
d2e6a577 FG |
439 | assert(m_local_image_id.empty()); |
440 | m_local_image_id = librbd::util::generate_image_id<I>(m_local_io_ctx); | |
7c673cae FG |
441 | |
442 | librbd::journal::MirrorPeerClientMeta client_meta{m_local_image_id}; | |
443 | client_meta.state = librbd::journal::MIRROR_PEER_STATE_SYNCING; | |
444 | ||
445 | librbd::journal::ClientData client_data(client_meta); | |
446 | bufferlist data_bl; | |
447 | ::encode(client_data, data_bl); | |
448 | ||
449 | Context *ctx = create_context_callback< | |
450 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_update_client_image>( | |
451 | this); | |
452 | m_journaler->update_client(data_bl, ctx); | |
453 | } | |
454 | ||
455 | template <typename I> | |
456 | void BootstrapRequest<I>::handle_update_client_image(int r) { | |
457 | dout(20) << ": r=" << r << dendl; | |
458 | ||
459 | if (r < 0) { | |
460 | derr << ": failed to update client: " << cpp_strerror(r) << dendl; | |
461 | m_ret_val = r; | |
d2e6a577 | 462 | close_remote_image(); |
7c673cae FG |
463 | return; |
464 | } | |
465 | ||
466 | if (m_canceled) { | |
467 | dout(10) << ": request canceled" << dendl; | |
468 | m_ret_val = -ECANCELED; | |
d2e6a577 | 469 | close_remote_image(); |
7c673cae FG |
470 | return; |
471 | } | |
472 | ||
473 | *m_client_meta = {m_local_image_id}; | |
474 | m_client_meta->state = librbd::journal::MIRROR_PEER_STATE_SYNCING; | |
d2e6a577 FG |
475 | create_local_image(); |
476 | } | |
477 | ||
478 | template <typename I> | |
479 | void BootstrapRequest<I>::create_local_image() { | |
480 | dout(20) << dendl; | |
481 | update_progress("CREATE_LOCAL_IMAGE"); | |
482 | ||
483 | m_remote_image_ctx->snap_lock.get_read(); | |
484 | std::string image_name = m_remote_image_ctx->name; | |
485 | m_remote_image_ctx->snap_lock.put_read(); | |
486 | ||
487 | Context *ctx = create_context_callback< | |
488 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_create_local_image>( | |
489 | this); | |
490 | CreateImageRequest<I> *request = CreateImageRequest<I>::create( | |
491 | m_local_io_ctx, m_work_queue, m_global_image_id, m_remote_mirror_uuid, | |
492 | image_name, m_local_image_id, m_remote_image_ctx, ctx); | |
493 | request->send(); | |
494 | } | |
495 | ||
496 | template <typename I> | |
497 | void BootstrapRequest<I>::handle_create_local_image(int r) { | |
498 | dout(20) << ": r=" << r << dendl; | |
499 | ||
500 | if (r < 0) { | |
501 | derr << ": failed to create local image: " << cpp_strerror(r) << dendl; | |
502 | m_ret_val = r; | |
503 | close_remote_image(); | |
504 | return; | |
505 | } | |
506 | ||
507 | open_local_image(); | |
7c673cae FG |
508 | } |
509 | ||
510 | template <typename I> | |
511 | void BootstrapRequest<I>::get_remote_tags() { | |
7c673cae FG |
512 | if (m_client_meta->state == librbd::journal::MIRROR_PEER_STATE_SYNCING) { |
513 | // optimization -- no need to compare remote tags if we just created | |
514 | // the image locally or sync was interrupted | |
515 | image_sync(); | |
516 | return; | |
517 | } | |
518 | ||
519 | dout(20) << dendl; | |
3efd9988 | 520 | update_progress("GET_REMOTE_TAGS"); |
7c673cae FG |
521 | |
522 | Context *ctx = create_context_callback< | |
523 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_get_remote_tags>(this); | |
524 | m_journaler->get_tags(m_remote_tag_class, &m_remote_tags, ctx); | |
525 | } | |
526 | ||
527 | template <typename I> | |
528 | void BootstrapRequest<I>::handle_get_remote_tags(int r) { | |
529 | dout(20) << ": r=" << r << dendl; | |
530 | ||
531 | if (r < 0) { | |
532 | derr << ": failed to retrieve remote tags: " << cpp_strerror(r) << dendl; | |
533 | m_ret_val = r; | |
534 | close_local_image(); | |
535 | return; | |
536 | } | |
537 | ||
538 | if (m_canceled) { | |
539 | dout(10) << ": request canceled" << dendl; | |
540 | m_ret_val = -ECANCELED; | |
541 | close_local_image(); | |
542 | return; | |
543 | } | |
544 | ||
545 | // At this point, the local image was existing, non-primary, and replaying; | |
546 | // and the remote image is primary. Attempt to link the local image's most | |
547 | // recent tag to the remote image's tag chain. | |
7c673cae FG |
548 | bool remote_tag_data_valid = false; |
549 | librbd::journal::TagData remote_tag_data; | |
550 | boost::optional<uint64_t> remote_orphan_tag_tid = | |
551 | boost::make_optional<uint64_t>(false, 0U); | |
552 | bool reconnect_orphan = false; | |
553 | ||
554 | // decode the remote tags | |
555 | for (auto &remote_tag : m_remote_tags) { | |
28e407b8 AA |
556 | if (m_local_tag_data.predecessor.commit_valid && |
557 | m_local_tag_data.predecessor.mirror_uuid == m_remote_mirror_uuid && | |
558 | m_local_tag_data.predecessor.tag_tid > remote_tag.tid) { | |
559 | dout(15) << ": skipping processed predecessor remote tag " | |
7c673cae FG |
560 | << remote_tag.tid << dendl; |
561 | continue; | |
562 | } | |
563 | ||
564 | try { | |
565 | bufferlist::iterator it = remote_tag.data.begin(); | |
566 | ::decode(remote_tag_data, it); | |
567 | remote_tag_data_valid = true; | |
568 | } catch (const buffer::error &err) { | |
569 | derr << ": failed to decode remote tag " << remote_tag.tid << ": " | |
570 | << err.what() << dendl; | |
571 | m_ret_val = -EBADMSG; | |
572 | close_local_image(); | |
573 | return; | |
574 | } | |
575 | ||
576 | dout(10) << ": decoded remote tag " << remote_tag.tid << ": " | |
577 | << remote_tag_data << dendl; | |
578 | ||
28e407b8 | 579 | if (!m_local_tag_data.predecessor.commit_valid) { |
7c673cae FG |
580 | // newly synced local image (no predecessor) replays from the first tag |
581 | if (remote_tag_data.mirror_uuid != librbd::Journal<>::LOCAL_MIRROR_UUID) { | |
582 | dout(20) << ": skipping non-primary remote tag" << dendl; | |
583 | continue; | |
584 | } | |
585 | ||
586 | dout(20) << ": using initial primary remote tag" << dendl; | |
587 | break; | |
588 | } | |
589 | ||
28e407b8 | 590 | if (m_local_tag_data.mirror_uuid == librbd::Journal<>::ORPHAN_MIRROR_UUID) { |
7c673cae FG |
591 | // demotion last available local epoch |
592 | ||
28e407b8 | 593 | if (remote_tag_data.mirror_uuid == m_local_tag_data.mirror_uuid && |
7c673cae FG |
594 | remote_tag_data.predecessor.commit_valid && |
595 | remote_tag_data.predecessor.tag_tid == | |
28e407b8 | 596 | m_local_tag_data.predecessor.tag_tid) { |
7c673cae FG |
597 | // demotion matches remote epoch |
598 | ||
599 | if (remote_tag_data.predecessor.mirror_uuid == m_local_mirror_uuid && | |
28e407b8 | 600 | m_local_tag_data.predecessor.mirror_uuid == |
7c673cae FG |
601 | librbd::Journal<>::LOCAL_MIRROR_UUID) { |
602 | // local demoted and remote has matching event | |
603 | dout(20) << ": found matching local demotion tag" << dendl; | |
604 | remote_orphan_tag_tid = remote_tag.tid; | |
605 | continue; | |
606 | } | |
607 | ||
28e407b8 | 608 | if (m_local_tag_data.predecessor.mirror_uuid == m_remote_mirror_uuid && |
7c673cae FG |
609 | remote_tag_data.predecessor.mirror_uuid == |
610 | librbd::Journal<>::LOCAL_MIRROR_UUID) { | |
611 | // remote demoted and local has matching event | |
612 | dout(20) << ": found matching remote demotion tag" << dendl; | |
613 | remote_orphan_tag_tid = remote_tag.tid; | |
614 | continue; | |
615 | } | |
616 | } | |
617 | ||
618 | if (remote_tag_data.mirror_uuid == librbd::Journal<>::LOCAL_MIRROR_UUID && | |
619 | remote_tag_data.predecessor.mirror_uuid == librbd::Journal<>::ORPHAN_MIRROR_UUID && | |
620 | remote_tag_data.predecessor.commit_valid && remote_orphan_tag_tid && | |
621 | remote_tag_data.predecessor.tag_tid == *remote_orphan_tag_tid) { | |
622 | // remote promotion tag chained to remote/local demotion tag | |
623 | dout(20) << ": found chained remote promotion tag" << dendl; | |
624 | reconnect_orphan = true; | |
625 | break; | |
626 | } | |
627 | ||
628 | // promotion must follow demotion | |
629 | remote_orphan_tag_tid = boost::none; | |
630 | } | |
631 | } | |
632 | ||
633 | if (remote_tag_data_valid && | |
28e407b8 AA |
634 | m_local_tag_data.mirror_uuid == m_remote_mirror_uuid) { |
635 | dout(10) << ": local image is in clean replay state" << dendl; | |
7c673cae FG |
636 | } else if (reconnect_orphan) { |
637 | dout(20) << ": remote image was demoted/promoted" << dendl; | |
638 | } else { | |
639 | derr << ": split-brain detected -- skipping image replay" << dendl; | |
640 | m_ret_val = -EEXIST; | |
641 | close_local_image(); | |
642 | return; | |
643 | } | |
644 | ||
645 | image_sync(); | |
646 | } | |
647 | ||
648 | template <typename I> | |
649 | void BootstrapRequest<I>::image_sync() { | |
650 | if (m_client_meta->state == librbd::journal::MIRROR_PEER_STATE_REPLAYING) { | |
651 | // clean replay state -- no image sync required | |
652 | close_remote_image(); | |
653 | return; | |
654 | } | |
655 | ||
656 | dout(20) << dendl; | |
7c673cae FG |
657 | { |
658 | Mutex::Locker locker(m_lock); | |
31f18b77 FG |
659 | if (m_canceled) { |
660 | m_ret_val = -ECANCELED; | |
661 | } else { | |
662 | assert(m_image_sync == nullptr); | |
c07f9fc5 FG |
663 | |
664 | Context *ctx = create_context_callback< | |
665 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_image_sync>(this); | |
31f18b77 FG |
666 | m_image_sync = ImageSync<I>::create( |
667 | *m_local_image_ctx, m_remote_image_ctx, m_timer, m_timer_lock, | |
668 | m_local_mirror_uuid, m_journaler, m_client_meta, m_work_queue, | |
669 | m_instance_watcher, ctx, m_progress_ctx); | |
670 | ||
671 | m_image_sync->get(); | |
c07f9fc5 FG |
672 | |
673 | m_lock.Unlock(); | |
674 | update_progress("IMAGE_SYNC"); | |
675 | m_lock.Lock(); | |
676 | ||
31f18b77 | 677 | m_image_sync->send(); |
7c673cae FG |
678 | return; |
679 | } | |
680 | } | |
681 | ||
682 | dout(10) << ": request canceled" << dendl; | |
7c673cae FG |
683 | close_remote_image(); |
684 | } | |
685 | ||
686 | template <typename I> | |
687 | void BootstrapRequest<I>::handle_image_sync(int r) { | |
688 | dout(20) << ": r=" << r << dendl; | |
689 | ||
31f18b77 FG |
690 | { |
691 | Mutex::Locker locker(m_lock); | |
31f18b77 FG |
692 | m_image_sync->put(); |
693 | m_image_sync = nullptr; | |
694 | ||
695 | if (m_canceled) { | |
696 | dout(10) << ": request canceled" << dendl; | |
697 | m_ret_val = -ECANCELED; | |
698 | } | |
699 | ||
700 | if (r < 0) { | |
701 | derr << ": failed to sync remote image: " << cpp_strerror(r) << dendl; | |
702 | m_ret_val = r; | |
703 | } | |
7c673cae FG |
704 | } |
705 | ||
706 | close_remote_image(); | |
707 | } | |
708 | ||
709 | template <typename I> | |
710 | void BootstrapRequest<I>::close_local_image() { | |
711 | dout(20) << dendl; | |
712 | ||
713 | update_progress("CLOSE_LOCAL_IMAGE"); | |
714 | ||
715 | Context *ctx = create_context_callback< | |
716 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_close_local_image>( | |
717 | this); | |
718 | CloseImageRequest<I> *request = CloseImageRequest<I>::create( | |
719 | m_local_image_ctx, ctx); | |
720 | request->send(); | |
721 | } | |
722 | ||
723 | template <typename I> | |
724 | void BootstrapRequest<I>::handle_close_local_image(int r) { | |
725 | dout(20) << ": r=" << r << dendl; | |
726 | ||
727 | if (r < 0) { | |
728 | derr << ": error encountered closing local image: " << cpp_strerror(r) | |
729 | << dendl; | |
730 | } | |
731 | ||
732 | close_remote_image(); | |
733 | } | |
734 | ||
735 | template <typename I> | |
736 | void BootstrapRequest<I>::close_remote_image() { | |
737 | dout(20) << dendl; | |
738 | ||
739 | update_progress("CLOSE_REMOTE_IMAGE"); | |
740 | ||
741 | Context *ctx = create_context_callback< | |
742 | BootstrapRequest<I>, &BootstrapRequest<I>::handle_close_remote_image>( | |
743 | this); | |
744 | CloseImageRequest<I> *request = CloseImageRequest<I>::create( | |
745 | &m_remote_image_ctx, ctx); | |
746 | request->send(); | |
747 | } | |
748 | ||
749 | template <typename I> | |
750 | void BootstrapRequest<I>::handle_close_remote_image(int r) { | |
751 | dout(20) << ": r=" << r << dendl; | |
752 | ||
753 | if (r < 0) { | |
754 | derr << ": error encountered closing remote image: " << cpp_strerror(r) | |
755 | << dendl; | |
756 | } | |
757 | ||
758 | finish(m_ret_val); | |
759 | } | |
760 | ||
7c673cae FG |
761 | template <typename I> |
762 | void BootstrapRequest<I>::update_progress(const std::string &description) { | |
763 | dout(20) << ": " << description << dendl; | |
764 | ||
765 | if (m_progress_ctx) { | |
766 | m_progress_ctx->update_progress(description); | |
767 | } | |
768 | } | |
769 | ||
770 | } // namespace image_replayer | |
771 | } // namespace mirror | |
772 | } // namespace rbd | |
773 | ||
774 | template class rbd::mirror::image_replayer::BootstrapRequest<librbd::ImageCtx>; |