]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mon: MonClient: allow pinging a monitor without authenticating first
authorJoao Eduardo Luis <joao.luis@inktank.com>
Wed, 16 Oct 2013 15:19:58 +0000 (16:19 +0100)
committerJoao Eduardo Luis <joao.luis@inktank.com>
Wed, 23 Oct 2013 01:52:01 +0000 (02:52 +0100)
Signed-off-by: Joao Eduardo Luis <joao.luis@inktank.com>
src/mon/MonClient.cc
src/mon/MonClient.h

index d034017ce38a0146e9dc9bae10fb1b9c0c586cd9..f35a0dafd446fe768e18eb20bdf01802225d630c 100644 (file)
@@ -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())
index 64a399a197abf90b9ff7dc662f14a4b88902c805..0246050059b9ecb922c101770aa811d0bd77af97 100644 (file)
@@ -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);