#include "messages/MAuthReply.h"
#include "messages/MMonCommand.h"
#include "messages/MMonCommandAck.h"
+#include "messages/MPing.h"
#include "messages/MMonSubscribe.h"
#include "messages/MMonSubscribeAck.h"
}
+/**
+ * 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())
class MCommandReply;
struct MAuthReply;
class MAuthRotating;
+class MPing;
class LogClient;
class AuthSupported;
class AuthAuthorizeHandlerRegistry;
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;
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);