COMMAND("session ls " \
"name=filters,type=CephString,n=N,req=false",
"List client sessions", "mds", "r", "cli,rest")
+COMMAND("session evict " \
+ "name=filters,type=CephString,n=N,req=false",
+ "Evict client session(s)", "mds", "rw", "cli,rest")
COMMAND("heap " \
"name=heapcmd,type=CephChoices,strings=dump|start_profiler|stop_profiler|release|stats", \
"show heap usage info (available only if compiled with tcmalloc)", \
heartbeat_reset();
- // Dump sessions, decorated with recovery/replay status
- f->open_array_section("sessions");
- const ceph::unordered_map<entity_name_t, Session*> session_map = sessionmap.get_sessions();
- for (ceph::unordered_map<entity_name_t,Session*>::const_iterator p = session_map.begin();
- p != session_map.end();
- ++p) {
- if (!p->first.is_client()) {
- continue;
- }
-
- Session *s = p->second;
-
- f->open_object_section("session");
- f->dump_int("id", p->first.num());
-
- f->dump_int("num_leases", s->leases.size());
- f->dump_int("num_caps", s->caps.size());
-
- f->dump_string("state", s->get_state_name());
- f->dump_int("replay_requests", is_clientreplay() ? s->get_request_count() : 0);
- f->dump_unsigned("completed_requests", s->get_num_completed_requests());
- f->dump_bool("reconnecting", server->waiting_for_reconnect(p->first.num()));
- f->dump_stream("inst") << s->info.inst;
- f->open_object_section("client_metadata");
- for (map<string, string>::const_iterator i = s->info.client_metadata.begin();
- i != s->info.client_metadata.end(); ++i) {
- f->dump_string(i->first.c_str(), i->second);
- }
- f->close_section(); // client_metadata
- f->close_section(); //session
- }
- f->close_section(); //sessions
+ dump_sessions(SessionFilter(), f);
mds_lock.Unlock();
} else if (command == "session evict") {
return true;
}
+/**
+ * This function drops the mds_lock, so don't do anything with
+ * MDSRank after calling it (we could have gone into shutdown): just
+ * send your result back to the calling client and finish.
+ */
+std::vector<entity_name_t> MDSRankDispatcher::evict_sessions(
+ const SessionFilter &filter)
+{
+ std::list<Session*> victims;
+
+ const auto sessions = sessionmap.get_sessions();
+ for (const auto p : sessions) {
+ if (!p.first.is_client()) {
+ continue;
+ }
+
+ Session *s = p.second;
+
+ if (filter.match(*s, std::bind(&Server::waiting_for_reconnect, server, std::placeholders::_1))) {
+ victims.push_back(s);
+ }
+ }
+
+ std::vector<entity_name_t> result;
+
+ C_SaferCond on_safe;
+ C_GatherBuilder gather(g_ceph_context, &on_safe);
+ for (const auto s : victims) {
+ server->kill_session(s, gather.new_sub());
+ result.push_back(s->info.inst.name);
+ }
+ gather.activate();
+ mds_lock.Unlock();
+ on_safe.wait();
+ mds_lock.Lock();
+
+ return result;
+}
+
+void MDSRankDispatcher::dump_sessions(const SessionFilter &filter, Formatter *f) const
+{
+ // Dump sessions, decorated with recovery/replay status
+ f->open_array_section("sessions");
+ const ceph::unordered_map<entity_name_t, Session*> session_map = sessionmap.get_sessions();
+ for (ceph::unordered_map<entity_name_t,Session*>::const_iterator p = session_map.begin();
+ p != session_map.end();
+ ++p) {
+ if (!p->first.is_client()) {
+ continue;
+ }
+
+ Session *s = p->second;
+ if (!filter.match(*s, std::bind(&Server::waiting_for_reconnect, server, std::placeholders::_1))) {
+ continue;
+ }
+
+ f->open_object_section("session");
+ f->dump_int("id", p->first.num());
+
+ f->dump_int("num_leases", s->leases.size());
+ f->dump_int("num_caps", s->caps.size());
+
+ f->dump_string("state", s->get_state_name());
+ f->dump_int("replay_requests", is_clientreplay() ? s->get_request_count() : 0);
+ f->dump_unsigned("completed_requests", s->get_num_completed_requests());
+ f->dump_bool("reconnecting", server->waiting_for_reconnect(p->first.num()));
+ f->dump_stream("inst") << s->info.inst;
+ f->open_object_section("client_metadata");
+ for (map<string, string>::const_iterator i = s->info.client_metadata.begin();
+ i != s->info.client_metadata.end(); ++i) {
+ f->dump_string(i->first.c_str(), i->second);
+ }
+ f->close_section(); // client_metadata
+ f->close_section(); //session
+ }
+ f->close_section(); //sessions
+}
void MDSRank::command_scrub_path(Formatter *f, const string& path)
{
msgr, monc_, objecter_, respawn_hook_, suicide_hook_)
{}
+bool MDSRankDispatcher::handle_command(
+ const cmdmap_t &cmdmap,
+ bufferlist const &inbl,
+ int *r,
+ std::stringstream *ds,
+ std::stringstream *ss)
+{
+ assert(r != nullptr);
+ assert(ds != nullptr);
+ assert(ss != nullptr);
+
+ std::string prefix;
+ cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
+
+ if (prefix == "session ls") {
+ std::vector<std::string> filter_args;
+ cmd_getval(g_ceph_context, cmdmap, "filters", filter_args);
+
+ SessionFilter filter;
+ *r = filter.parse(filter_args, ss);
+ if (*r != 0) {
+ return true;
+ }
+
+ Formatter *f = new JSONFormatter();
+ dump_sessions(filter, f);
+ f->flush(*ds);
+ delete f;
+ return true;
+ } else if (prefix == "session evict") {
+ std::vector<std::string> filter_args;
+ cmd_getval(g_ceph_context, cmdmap, "filters", filter_args);
+
+ SessionFilter filter;
+ *r = filter.parse(filter_args, ss);
+ if (*r != 0) {
+ return true;
+ }
+
+ evict_sessions(filter);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
MDSMap::DaemonState get_state() const { return state; }
MDSMap::DaemonState get_want_state() const { return beacon.get_want_state(); }
- bool is_creating() { return state == MDSMap::STATE_CREATING; }
- bool is_starting() { return state == MDSMap::STATE_STARTING; }
- bool is_standby() { return state == MDSMap::STATE_STANDBY; }
- bool is_replay() { return state == MDSMap::STATE_REPLAY; }
- bool is_standby_replay() { return state == MDSMap::STATE_STANDBY_REPLAY; }
- bool is_resolve() { return state == MDSMap::STATE_RESOLVE; }
- bool is_reconnect() { return state == MDSMap::STATE_RECONNECT; }
- bool is_rejoin() { return state == MDSMap::STATE_REJOIN; }
- bool is_clientreplay() { return state == MDSMap::STATE_CLIENTREPLAY; }
- bool is_active() { return state == MDSMap::STATE_ACTIVE; }
- bool is_stopping() { return state == MDSMap::STATE_STOPPING; }
- bool is_oneshot_replay() { return state == MDSMap::STATE_ONESHOT_REPLAY; }
- bool is_any_replay() { return (is_replay() || is_standby_replay() ||
+ bool is_creating() const { return state == MDSMap::STATE_CREATING; }
+ bool is_starting() const { return state == MDSMap::STATE_STARTING; }
+ bool is_standby() const { return state == MDSMap::STATE_STANDBY; }
+ bool is_replay() const { return state == MDSMap::STATE_REPLAY; }
+ bool is_standby_replay() const { return state == MDSMap::STATE_STANDBY_REPLAY; }
+ bool is_resolve() const { return state == MDSMap::STATE_RESOLVE; }
+ bool is_reconnect() const { return state == MDSMap::STATE_RECONNECT; }
+ bool is_rejoin() const { return state == MDSMap::STATE_REJOIN; }
+ bool is_clientreplay() const { return state == MDSMap::STATE_CLIENTREPLAY; }
+ bool is_active() const { return state == MDSMap::STATE_ACTIVE; }
+ bool is_stopping() const { return state == MDSMap::STATE_STOPPING; }
+ bool is_oneshot_replay() const { return state == MDSMap::STATE_ONESHOT_REPLAY; }
+ bool is_any_replay() const { return (is_replay() || is_standby_replay() ||
is_oneshot_replay()); }
- bool is_stopped() { return mdsmap->is_stopped(whoami); }
+ bool is_stopped() const { return mdsmap->is_stopped(whoami); }
void handle_write_error(int err);
void update_log_config();
bool handle_command_legacy(std::vector<std::string> args);
+ bool handle_command(
+ const cmdmap_t &cmdmap,
+ bufferlist const &inbl,
+ int *r,
+ std::stringstream *ds,
+ std::stringstream *ss);
+
+ void dump_sessions(
+ const SessionFilter &filter, Formatter *f) const;
+ std::vector<entity_name_t> evict_sessions(
+ const SessionFilter &filter);
+
// Call into me from MDS::ms_dispatch
bool ms_dispatch(Message *m);
if (info.client_metadata.count("hostname")) {
// Happy path, refer to clients by hostname
human_name = info.client_metadata["hostname"];
- if (info.client_metadata.count("entity_id")) {
- EntityName entity;
- entity.set_id(info.client_metadata["entity_id"]);
- if (!entity.has_default_id()) {
- // When a non-default entity ID is set by the user, assume they
- // would like to see it in references to the client
- human_name += std::string(":") + entity.get_id();
+ if (!info.auth_name.has_default_id()) {
+ // When a non-default entity ID is set by the user, assume they
+ // would like to see it in references to the client, if it's
+ // reasonable short. Limit the length because we don't want
+ // to put e.g. uuid-generated names into a "human readable"
+ // rendering.
+ const int arbitrarily_short = 16;
+ if (info.auth_name.get_id().size() < arbitrarily_short) {
+ human_name += std::string(":") + info.auth_name.get_id();
}
}
} else {
return false;
}
+int SessionFilter::parse(
+ const std::vector<std::string> &args,
+ std::stringstream *ss)
+{
+ assert(ss != NULL);
+
+ for (const auto &s : args) {
+ dout(20) << __func__ << " parsing filter '" << s << "'" << dendl;
+
+ auto eq = s.find("=");
+ if (eq == std::string::npos || eq == s.size()) {
+ *ss << "Invalid filter '" << s << "'";
+ return -EINVAL;
+ }
+
+ // Keys that start with this are to be taken as referring
+ // to freeform client metadata fields.
+ const std::string metadata_prefix("client_metadata.");
+
+ auto k = s.substr(0, eq);
+ auto v = s.substr(eq + 1);
+
+ dout(20) << __func__ << " parsed k='" << k << "', v='" << v << "'" << dendl;
+
+ if (k.compare(0, metadata_prefix.size(), metadata_prefix) == 0
+ && k.size() > metadata_prefix.size()) {
+ // Filter on arbitrary metadata key (no fixed schema for this,
+ // so anything after the dot is a valid field to filter on)
+ auto metadata_key = k.substr(metadata_prefix.size());
+ metadata.insert(std::make_pair(metadata_key, v));
+ } else if (k == "auth_name") {
+ // Filter on client entity name
+ auth_name = v;
+ } else if (k == "state") {
+ state = v;
+ } else if (k == "id") {
+ std::string err;
+ id = strict_strtoll(v.c_str(), 10, &err);
+ if (!err.empty()) {
+ *ss << err;
+ return -EINVAL;
+ }
+ } else if (k == "reconnecting") {
+
+ /**
+ * Strict boolean parser. Allow true/false/0/1.
+ * Anything else is -EINVAL.
+ */
+ auto is_true = [](const std::string &bstr, bool *out) -> bool
+ {
+ assert(out != nullptr);
+
+ if (bstr == "true" || bstr == "1") {
+ *out = true;
+ return 0;
+ } else if (bstr == "false" || bstr == "0") {
+ *out = false;
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+ };
+
+ bool bval;
+ int r = is_true(v, &bval);
+ if (r == 0) {
+ set_reconnecting(bval);
+ } else {
+ *ss << "Invalid boolean value '" << v << "'";
+ return -EINVAL;
+ }
+ } else {
+ *ss << "Invalid filter key '" << k << "'";
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+bool SessionFilter::match(
+ const Session &session,
+ std::function<bool(client_t)> is_reconnecting) const
+{
+ for (auto m : metadata) {
+ auto k = m.first;
+ auto v = m.second;
+ if (session.info.client_metadata.count(k) == 0) {
+ return false;
+ }
+ if (session.info.client_metadata.at(k) != v) {
+ return false;
+ }
+ }
+
+ if (!auth_name.empty() && auth_name != session.info.auth_name.get_id()) {
+ return false;
+ }
+
+ if (!state.empty() && state != session.get_state_name()) {
+ return false;
+ }
+
+ if (id != 0 && id != session.info.inst.name.num()) {
+ return false;
+ }
+
+ if (reconnecting.first) {
+ const bool am_reconnecting = is_reconnecting(session.info.inst.name.num());
+ if (reconnecting.second != am_reconnecting) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
STATE_KILLING = 5
};
- const char *get_state_name(int s) {
+ const char *get_state_name(int s) const {
switch (s) {
case STATE_CLOSED: return "closed";
case STATE_OPENING: return "opening";
// that appropriate mark_dirty calls follow.
std::deque<version_t> projected;
+
+
public:
void push_pv(version_t pv)
}
int get_state() { return state; }
- const char *get_state_name() { return get_state_name(state); }
+ const char *get_state_name() const { return get_state_name(state); }
uint64_t get_state_seq() { return state_seq; }
bool is_closed() const { return state == STATE_CLOSED; }
bool is_opening() const { return state == STATE_OPENING; }
}
};
+class SessionFilter
+{
+protected:
+ // First is whether to filter, second is filter value
+ std::pair<bool, bool> reconnecting;
+
+public:
+ std::map<std::string, std::string> metadata;
+ std::string auth_name;
+ std::string state;
+ int64_t id;
+
+ SessionFilter()
+ : reconnecting(false, false), id(0)
+ {}
+
+ bool match(
+ const Session &session,
+ std::function<bool(client_t)> is_reconnecting) const;
+ int parse(const std::vector<std::string> &args, std::stringstream *ss);
+ void set_reconnecting(bool v)
+ {
+ reconnecting.first = true;
+ reconnecting.second = v;
+ }
+};
+
/*
* session map
*/