});
cmdctx->reply(r, "");
return true;
+ } else if (prefix == "osd safe-to-destroy") {
+ vector<string> ids;
+ cmd_getval(g_ceph_context, cmdctx->cmdmap, "ids", ids);
+ set<int> osds;
+ int r;
+ cluster_state.with_osdmap([&](const OSDMap& osdmap) {
+ r = osdmap.parse_osd_id_list(ids, &osds, &ss);
+ });
+ if (!r && osds.empty()) {
+ ss << "must specify one or more OSDs";
+ r = -EINVAL;
+ }
+ if (r < 0) {
+ cmdctx->reply(r, ss);
+ return true;
+ }
+ set<int> active_osds, missing_stats, stored_pgs;
+ int affected_pgs = 0;
+ cluster_state.with_pgmap([&](const PGMap& pg_map) {
+ if (pg_map.num_pg_unknown > 0) {
+ ss << pg_map.num_pg_unknown << " pgs have unknown state; cannot draw"
+ << " any conclusions";
+ r = -EAGAIN;
+ return;
+ }
+ int num_active_clean = 0;
+ for (auto& p : pg_map.num_pg_by_state) {
+ unsigned want = PG_STATE_ACTIVE|PG_STATE_CLEAN;
+ if ((p.first & want) == want) {
+ num_active_clean += p.second;
+ }
+ }
+ cluster_state.with_osdmap([&](const OSDMap& osdmap) {
+ for (auto osd : osds) {
+ if (!osdmap.exists(osd)) {
+ continue; // clearly safe to destroy
+ }
+ auto q = pg_map.num_pg_by_osd.find(osd);
+ if (q != pg_map.num_pg_by_osd.end()) {
+ if (q->second.acting > 0 || q->second.up > 0) {
+ active_osds.insert(osd);
+ affected_pgs += q->second.acting + q->second.up;
+ continue;
+ }
+ }
+ if (num_active_clean < pg_map.num_pg) {
+ // all pgs aren't active+clean; we need to be careful.
+ auto p = pg_map.osd_stat.find(osd);
+ if (p == pg_map.osd_stat.end()) {
+ missing_stats.insert(osd);
+ }
+ if (p->second.num_pgs > 0) {
+ stored_pgs.insert(osd);
+ }
+ }
+ }
+ });
+ });
+ if (!r && !active_osds.empty()) {
+ ss << "OSD(s) " << active_osds << " have " << affected_pgs
+ << " pgs currently mapped to them";
+ r = -EBUSY;
+ } else if (!missing_stats.empty()) {
+ ss << "OSD(s) " << missing_stats << " have no reported stats, and not all"
+ << " PGs are active+clean; we cannot draw any conclusions";
+ r = -EAGAIN;
+ } else if (!stored_pgs.empty()) {
+ ss << "OSD(s) " << stored_pgs << " last reported they still store some PG"
+ << " data, and not all PGs are active+clean; we cannot be sure they"
+ << " aren't still needed.";
+ r = -EBUSY;
+ }
+ if (r) {
+ cmdctx->reply(r, ss);
+ return true;
+ }
+ ss << "OSD(s) " << osds << " are safe to destroy without reducing data"
+ << " durability.";
+ cmdctx->reply(0, ss);
+ return true;
+ } else if (prefix == "osd ok-to-stop") {
+ vector<string> ids;
+ cmd_getval(g_ceph_context, cmdctx->cmdmap, "ids", ids);
+ set<int> osds;
+ int r;
+ cluster_state.with_osdmap([&](const OSDMap& osdmap) {
+ r = osdmap.parse_osd_id_list(ids, &osds, &ss);
+ });
+ if (!r && osds.empty()) {
+ ss << "must specify one or more OSDs";
+ r = -EINVAL;
+ }
+ if (r < 0) {
+ cmdctx->reply(r, ss);
+ return true;
+ }
+ map<pg_t,int> pg_delta; // pgid -> net acting set size change
+ int dangerous_pgs = 0;
+ cluster_state.with_pgmap([&](const PGMap& pg_map) {
+ return cluster_state.with_osdmap([&](const OSDMap& osdmap) {
+ if (pg_map.num_pg_unknown > 0) {
+ ss << pg_map.num_pg_unknown << " pgs have unknown state; "
+ << "cannot draw any conclusions";
+ r = -EAGAIN;
+ return;
+ }
+ for (auto osd : osds) {
+ auto p = pg_map.pg_by_osd.find(osd);
+ if (p != pg_map.pg_by_osd.end()) {
+ for (auto& pgid : p->second) {
+ --pg_delta[pgid];
+ }
+ }
+ }
+ for (auto& p : pg_delta) {
+ auto q = pg_map.pg_stat.find(p.first);
+ if (q == pg_map.pg_stat.end()) {
+ ss << "missing information about " << p.first << "; cannot draw"
+ << " any conclusions";
+ r = -EAGAIN;
+ return;
+ }
+ if (!(q->second.state & PG_STATE_ACTIVE) ||
+ (q->second.state & PG_STATE_DEGRADED)) {
+ // we don't currently have a good way to tell *how* degraded
+ // a degraded PG is, so we have to assume we cannot remove
+ // any more replicas/shards.
+ ++dangerous_pgs;
+ continue;
+ }
+ const pg_pool_t *pi = osdmap.get_pg_pool(p.first.pool());
+ if (!pi) {
+ ++dangerous_pgs; // pool is creating or deleting
+ } else {
+ if (q->second.acting.size() + p.second < pi->min_size) {
+ ++dangerous_pgs;
+ }
+ }
+ }
+ });
+ });
+ if (r) {
+ cmdctx->reply(r, ss);
+ return true;
+ }
+ if (dangerous_pgs) {
+ ss << dangerous_pgs << " PGs are already degraded or might become "
+ << "unavailable";
+ cmdctx->reply(-EBUSY, ss);
+ return true;
+ }
+ ss << "OSD(s) " << osds << " are ok to stop without reducing"
+ << " availability, provided there are no other concurrent failures"
+ << " or interventions. " << pg_delta.size() << " PGs are likely to be"
+ << " degraded (but remain available) as a result.";
+ cmdctx->reply(0, ss);
+ return true;
} else if (prefix == "pg force-recovery" ||
prefix == "pg force-backfill" ||
prefix == "pg cancel-force-recovery" ||