diff --git a/src/mds/MDSMap.cc b/src/mds/MDSMap.cc index 0c15674a0e5ab..eec9d46876290 100644 --- a/src/mds/MDSMap.cc +++ b/src/mds/MDSMap.cc @@ -727,3 +727,29 @@ MDSMap::availability_t MDSMap::is_cluster_available() const return STUCK_UNAVAILABLE; } } + +bool MDSMap::state_transition_valid(DaemonState prev, DaemonState next) +{ + bool state_valid = true; + if (next != prev) { + if (prev == MDSMap::STATE_REPLAY) { + if (next != MDSMap::STATE_RESOLVE && next != MDSMap::STATE_RECONNECT) { + state_valid = false; + } + } else if (prev == MDSMap::STATE_REJOIN) { + if (next != MDSMap::STATE_ACTIVE + && next != MDSMap::STATE_CLIENTREPLAY + && next != MDSMap::STATE_STOPPED) { + state_valid = false; + } + } else if (prev >= MDSMap::STATE_RECONNECT && prev < MDSMap::STATE_ACTIVE) { + // Once I have entered replay, the only allowable transitions are to + // the next next along in the sequence. + if (next != prev + 1) { + state_valid = false; + } + } + } + + return state_valid; +} diff --git a/src/mds/MDSMap.h b/src/mds/MDSMap.h index a34bf9da0b713..992e2de1458c6 100644 --- a/src/mds/MDSMap.h +++ b/src/mds/MDSMap.h @@ -628,6 +628,8 @@ class MDSMap { void dump(Formatter *f) const; static void generate_test_instances(list& ls); + + static bool state_transition_valid(DaemonState prev, DaemonState next); }; WRITE_CLASS_ENCODER_FEATURES(MDSMap::mds_info_t) WRITE_CLASS_ENCODER_FEATURES(MDSMap) diff --git a/src/mds/MDSRank.cc b/src/mds/MDSRank.cc index e662f1b1a7b7e..3c7c919cd11a5 100644 --- a/src/mds/MDSRank.cc +++ b/src/mds/MDSRank.cc @@ -1409,28 +1409,7 @@ void MDSRankDispatcher::handle_mds_map( } // Validate state transitions while I hold a rank - bool state_valid = true; - if (state != oldstate) { - if (oldstate == MDSMap::STATE_REPLAY) { - if (state != MDSMap::STATE_RESOLVE && state != MDSMap::STATE_RECONNECT) { - state_valid = false; - } - } else if (oldstate == MDSMap::STATE_REJOIN) { - if (state != MDSMap::STATE_ACTIVE - && state != MDSMap::STATE_CLIENTREPLAY - && state != MDSMap::STATE_STOPPED) { - state_valid = false; - } - } else if (oldstate >= MDSMap::STATE_RECONNECT && oldstate < MDSMap::STATE_ACTIVE) { - // Once I have entered replay, the only allowable transitions are to - // the next state along in the sequence. - if (state != oldstate + 1) { - state_valid = false; - } - } - } - - if (!state_valid) { + if (!MDSMap::state_transition_valid(oldstate, state)) { derr << "Invalid state transition " << ceph_mds_state_name(oldstate) << "->" << ceph_mds_state_name(state) << dendl; respawn(); diff --git a/src/mon/MDSMonitor.cc b/src/mon/MDSMonitor.cc index 10d6bc3e13c62..c72fd5a05a827 100644 --- a/src/mon/MDSMonitor.cc +++ b/src/mon/MDSMonitor.cc @@ -645,7 +645,23 @@ bool MDSMonitor::prepare_beacon(MonOpRequestRef op) mon->monmap->fsid, m->get_global_id(), m->get_name(), fsmap.get_epoch(), state, seq, CEPH_FEATURES_SUPPORTED_DEFAULT)); + } else if (info.state == MDSMap::STATE_STANDBY && state != info.state) { + // Standby daemons should never modify their own + // state. Reject any attempts to do so. + derr << "standby " << gid << " attempted to change state to " + << ceph_mds_state_name(state) << ", rejecting" << dendl; + return true; + } else if (info.state != MDSMap::STATE_STANDBY && state != info.state && + !MDSMap::state_transition_valid(info.state, state)) { + // Validate state transitions for daemons that hold a rank + derr << "daemon " << gid << " (rank " << info.rank << ") " + << "reported invalid state transition " + << ceph_mds_state_name(info.state) << " -> " + << ceph_mds_state_name(state) << dendl; + return true; } else { + // Made it through special cases and validations, record the + // daemon's reported state to the FSMap. pending_fsmap.modify_daemon(gid, [state, seq](MDSMap::mds_info_t *info) { info->state = state; info->state_seq = seq;