// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "RemotePoolPoller.h" #include "include/ceph_assert.h" #include "common/debug.h" #include "common/errno.h" #include "common/Timer.h" #include "cls/rbd/cls_rbd_client.h" #include "librbd/ImageCtx.h" #include "librbd/Utils.h" #include "librbd/asio/ContextWQ.h" #include "tools/rbd_mirror/Threads.h" #include "tools/rbd_mirror/Types.h" #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rbd_mirror #undef dout_prefix #define dout_prefix *_dout << "rbd::mirror::RemotePoolPoller: " << this << " " \ << __func__ << ": " namespace rbd { namespace mirror { static const double POLL_INTERVAL_SECONDS = 30; using librbd::util::create_rados_callback; template RemotePoolPoller::~RemotePoolPoller() { ceph_assert(m_timer_task == nullptr); } template void RemotePoolPoller::init(Context* on_finish) { dout(10) << dendl; ceph_assert(m_state == STATE_INITIALIZING); ceph_assert(m_on_finish == nullptr); m_on_finish = on_finish; get_mirror_uuid(); } template void RemotePoolPoller::shut_down(Context* on_finish) { dout(10) << dendl; std::unique_lock locker(m_threads->timer_lock); ceph_assert(m_state == STATE_POLLING); m_state = STATE_SHUTTING_DOWN; if (m_timer_task == nullptr) { // currently executing a poll ceph_assert(m_on_finish == nullptr); m_on_finish = on_finish; return; } m_threads->timer->cancel_event(m_timer_task); m_timer_task = nullptr; m_threads->work_queue->queue(on_finish, 0); } template void RemotePoolPoller::get_mirror_uuid() { dout(10) << dendl; librados::ObjectReadOperation op; librbd::cls_client::mirror_uuid_get_start(&op); auto aio_comp = create_rados_callback< RemotePoolPoller, &RemotePoolPoller::handle_get_mirror_uuid>(this); m_out_bl.clear(); int r = m_remote_io_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op, &m_out_bl); ceph_assert(r == 0); aio_comp->release(); } template void RemotePoolPoller::handle_get_mirror_uuid(int r) { dout(10) << "r=" << r << dendl; std::string remote_mirror_uuid; if (r >= 0) { auto it = m_out_bl.cbegin(); r = librbd::cls_client::mirror_uuid_get_finish(&it, &remote_mirror_uuid); if (r >= 0 && remote_mirror_uuid.empty()) { r = -ENOENT; } } if (r < 0) { if (r == -ENOENT) { dout(5) << "remote mirror uuid missing" << dendl; } else { derr << "failed to retrieve remote mirror uuid: " << cpp_strerror(r) << dendl; } m_remote_pool_meta.mirror_uuid = ""; } // if we have the mirror uuid, we will poll until shut down if (m_state == STATE_INITIALIZING) { if (r < 0) { schedule_task(r); return; } m_state = STATE_POLLING; } dout(10) << "remote_mirror_uuid=" << remote_mirror_uuid << dendl; if (m_remote_pool_meta.mirror_uuid != remote_mirror_uuid) { m_remote_pool_meta.mirror_uuid = remote_mirror_uuid; m_updated = true; } mirror_peer_ping(); } template void RemotePoolPoller::mirror_peer_ping() { dout(10) << dendl; librados::ObjectWriteOperation op; librbd::cls_client::mirror_peer_ping(&op, m_site_name, m_local_mirror_uuid); auto aio_comp = create_rados_callback< RemotePoolPoller, &RemotePoolPoller::handle_mirror_peer_ping>(this); int r = m_remote_io_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op); ceph_assert(r == 0); aio_comp->release(); } template void RemotePoolPoller::handle_mirror_peer_ping(int r) { dout(10) << "r=" << r << dendl; if (r == -EOPNOTSUPP) { // older OSD that doesn't support snaphot-based mirroring, so no need // to query remote peers dout(10) << "remote peer does not support snapshot-based mirroring" << dendl; notify_listener(); return; } else if (r < 0) { // we can still see if we can perform a peer list and find outselves derr << "failed to ping remote mirror peer: " << cpp_strerror(r) << dendl; } mirror_peer_list(); } template void RemotePoolPoller::mirror_peer_list() { dout(10) << dendl; librados::ObjectReadOperation op; librbd::cls_client::mirror_peer_list_start(&op); auto aio_comp = create_rados_callback< RemotePoolPoller, &RemotePoolPoller::handle_mirror_peer_list>(this); m_out_bl.clear(); int r = m_remote_io_ctx.aio_operate(RBD_MIRRORING, aio_comp, &op, &m_out_bl); ceph_assert(r == 0); aio_comp->release(); } template void RemotePoolPoller::handle_mirror_peer_list(int r) { dout(10) << "r=" << r << dendl; std::vector peers; if (r == 0) { auto iter = m_out_bl.cbegin(); r = librbd::cls_client::mirror_peer_list_finish(&iter, &peers); } if (r < 0) { derr << "failed to retrieve mirror peers: " << cpp_strerror(r) << dendl; } cls::rbd::MirrorPeer* matched_peer = nullptr; for (auto& peer : peers) { if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) { continue; } if (peer.mirror_uuid == m_local_mirror_uuid) { matched_peer = &peer; break; } else if (peer.site_name == m_site_name) { // keep searching in case we hit an exact match by fsid matched_peer = &peer; } } // older OSDs don't support peer ping so we might fail to find a match, // which will prevent snapshot mirroring from functioning std::string remote_mirror_peer_uuid; if (matched_peer != nullptr) { remote_mirror_peer_uuid = matched_peer->uuid; } dout(10) << "remote_mirror_peer_uuid=" << remote_mirror_peer_uuid << dendl; if (m_remote_pool_meta.mirror_peer_uuid != remote_mirror_peer_uuid) { m_remote_pool_meta.mirror_peer_uuid = remote_mirror_peer_uuid; m_updated = true; } notify_listener(); } template void RemotePoolPoller::notify_listener() { bool updated = false; std::swap(updated, m_updated); if (updated) { dout(10) << dendl; m_listener.handle_updated(m_remote_pool_meta); } schedule_task(0); } template void RemotePoolPoller::schedule_task(int r) { std::unique_lock locker{m_threads->timer_lock}; if (m_state == STATE_POLLING) { dout(10) << dendl; ceph_assert(m_timer_task == nullptr); m_timer_task = new LambdaContext([this](int) { handle_task(); }); m_threads->timer->add_event_after(POLL_INTERVAL_SECONDS, m_timer_task); } // finish init or shut down callback if (m_on_finish != nullptr) { locker.unlock(); Context* on_finish = nullptr; std::swap(on_finish, m_on_finish); on_finish->complete(m_state == STATE_SHUTTING_DOWN ? 0 : r); } } template void RemotePoolPoller::handle_task() { dout(10) << dendl; ceph_assert(ceph_mutex_is_locked_by_me(m_threads->timer_lock)); m_timer_task = nullptr; auto ctx = new LambdaContext([this](int) { get_mirror_uuid(); }); m_threads->work_queue->queue(ctx); } } // namespace mirror } // namespace rbd template class rbd::mirror::RemotePoolPoller;