From: Joao Eduardo Luis Date: Wed, 16 Oct 2013 15:19:58 +0000 (+0100) Subject: mon: MonClient: allow pinging a monitor without authenticating first X-Git-Tag: v0.72-rc1~22^2~4 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=6a4b196a5b2933224bb50dfbc79a850a903ce115;p=ceph.git mon: MonClient: allow pinging a monitor without authenticating first Signed-off-by: Joao Eduardo Luis --- diff --git a/src/mon/MonClient.cc b/src/mon/MonClient.cc index d034017ce38a..f35a0dafd446 100644 --- a/src/mon/MonClient.cc +++ b/src/mon/MonClient.cc @@ -22,6 +22,7 @@ #include "messages/MAuthReply.h" #include "messages/MMonCommand.h" #include "messages/MMonCommandAck.h" +#include "messages/MPing.h" #include "messages/MMonSubscribe.h" #include "messages/MMonSubscribeAck.h" @@ -164,6 +165,78 @@ int MonClient::get_monmap_privately() } +/** + * Ping the monitor with id @p mon_id and set the resulting reply in + * the provided @p result_reply, if this last parameter is not NULL. + * + * So that we don't rely on the MonClient's default messenger, set up + * during connect(), we create our own messenger to comunicate with the + * specified monitor. This is advantageous in the following ways: + * + * - Isolate the ping procedure from the rest of the MonClient's operations, + * allowing us to not acquire or manage the big monc_lock, thus not + * having to block waiting for some other operation to finish before we + * can proceed. + * * for instance, we can ping mon.FOO even if we are currently hunting + * or blocked waiting for auth to complete with mon.BAR. + * + * - Ping a monitor prior to establishing a connection (using connect()) + * and properly establish the MonClient's messenger. This frees us + * from dealing with the complex foo that happens in connect(). + * + * We also don't rely on MonClient as a dispatcher for this messenger, + * unlike what happens with the MonClient's default messenger. This allows + * us to sandbox the whole ping, having it much as a separate entity in + * the MonClient class, considerably simplifying the handling and dispatching + * of messages without needing to consider monc_lock. + * + * Current drawback is that we will establish a messenger for each ping + * we want to issue, instead of keeping a single messenger instance that + * would be used for all pings. + */ +int MonClient::ping_monitor(const string &mon_id, string *result_reply) +{ + ldout(cct, 10) << __func__ << dendl; + + if (mon_id.empty()) { + ldout(cct, 10) << __func__ << " specified mon id is empty!" << dendl; + return -EINVAL; + } else if (!monmap.contains(mon_id)) { + ldout(cct, 10) << __func__ << " no such monitor 'mon." << mon_id << "'" + << dendl; + return -ENOENT; + } + + MonClientPinger *pinger = new MonClientPinger(cct, result_reply); + + Messenger *smsgr = new SimpleMessenger(cct, + entity_name_t::CLIENT(-1), + "temp_ping_client", getpid()); + smsgr->add_dispatcher_head(pinger); + smsgr->start(); + + ConnectionRef con = smsgr->get_connection(monmap.get_inst(mon_id)); + ldout(cct, 10) << __func__ << " ping mon." << mon_id + << " " << con->get_peer_addr() << dendl; + smsgr->send_message(new MPing, con); + + pinger->lock.Lock(); + int ret = pinger->wait_for_reply(cct->_conf->client_mount_timeout); + if (ret == 0) { + ldout(cct,10) << __func__ << " got ping reply" << dendl; + } else { + ret = -ret; + } + pinger->lock.Unlock(); + + smsgr->mark_down(con); + smsgr->shutdown(); + smsgr->wait(); + delete smsgr; + delete pinger; + return ret; +} + bool MonClient::ms_dispatch(Message *m) { if (my_addr == entity_addr_t()) diff --git a/src/mon/MonClient.h b/src/mon/MonClient.h index 64a399a197ab..0246050059b9 100644 --- a/src/mon/MonClient.h +++ b/src/mon/MonClient.h @@ -42,6 +42,7 @@ class MMonCommandAck; class MCommandReply; struct MAuthReply; class MAuthRotating; +class MPing; class LogClient; class AuthSupported; class AuthAuthorizeHandlerRegistry; @@ -54,6 +55,58 @@ enum MonClientState { MC_STATE_HAVE_SESSION, }; +struct MonClientPinger : public Dispatcher { + + Mutex lock; + Cond ping_recvd_cond; + string *result; + bool done; + + MonClientPinger(CephContext *cct_, string *res_) : + Dispatcher(cct_), + lock("MonClientPinger::lock"), + result(res_), + done(false) + { } + + int wait_for_reply(double timeout = 0.0) { + utime_t until = ceph_clock_now(cct); + until += (timeout > 0 ? timeout : cct->_conf->client_mount_timeout); + done = false; + + int ret = 0; + while (!done) { + ret = ping_recvd_cond.WaitUntil(lock, until); + if (ret == -ETIMEDOUT) + break; + } + return ret; + } + + bool ms_dispatch(Message *m) { + Mutex::Locker l(lock); + if (m->get_type() != CEPH_MSG_PING) + return false; + + bufferlist &payload = m->get_payload(); + if (result && payload.length() > 0) { + bufferlist::iterator p = payload.begin(); + ::decode(*result, p); + } + done = true; + ping_recvd_cond.SignalAll(); + m->put(); + return true; + } + bool ms_handle_reset(Connection *con) { + Mutex::Locker l(lock); + done = true; + ping_recvd_cond.SignalAll(); + return true; + } + void ms_handle_remote_reset(Connection *con) {} +}; + class MonClient : public Dispatcher { public: MonMap monmap; @@ -211,6 +264,17 @@ public: int build_initial_monmap(); int get_monmap(); int get_monmap_privately(); + /** + * Ping monitor with ID @p mon_id and record the resulting + * reply in @p result_reply. + * + * @param[in] mon_id Target monitor's ID + * @param[out] Resulting reply from mon.ID, if param != NULL + * @returns 0 in case of success; < 0 in case of error, + * -ETIMEDOUT if monitor didn't reply before timeout + * expired (default: conf->client_mount_timeout). + */ + int ping_monitor(const string &mon_id, string *result_reply); void send_mon_message(Message *m) { Mutex::Locker l(monc_lock);