Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / mon / OSDMonitor.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7  * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
8  * Copyright (C) 2014 Red Hat <contact@redhat.com>
9  *
10  * Author: Loic Dachary <loic@dachary.org>
11  *
12  * This is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License version 2.1, as published by the Free Software
15  * Foundation.  See file COPYING.
16  *
17  */
18
19 #include <algorithm>
20 #include <boost/algorithm/string.hpp>
21 #include <locale>
22 #include <sstream>
23
24 #include "mon/OSDMonitor.h"
25 #include "mon/Monitor.h"
26 #include "mon/MDSMonitor.h"
27 #include "mon/PGMonitor.h"
28 #include "mon/MgrStatMonitor.h"
29 #include "mon/AuthMonitor.h"
30 #include "mon/ConfigKeyService.h"
31
32 #include "mon/MonitorDBStore.h"
33 #include "mon/Session.h"
34
35 #include "crush/CrushWrapper.h"
36 #include "crush/CrushTester.h"
37 #include "crush/CrushTreeDumper.h"
38
39 #include "messages/MOSDBeacon.h"
40 #include "messages/MOSDFailure.h"
41 #include "messages/MOSDMarkMeDown.h"
42 #include "messages/MOSDFull.h"
43 #include "messages/MOSDMap.h"
44 #include "messages/MMonGetOSDMap.h"
45 #include "messages/MOSDBoot.h"
46 #include "messages/MOSDAlive.h"
47 #include "messages/MPoolOp.h"
48 #include "messages/MPoolOpReply.h"
49 #include "messages/MOSDPGCreate.h"
50 #include "messages/MOSDPGCreated.h"
51 #include "messages/MOSDPGTemp.h"
52 #include "messages/MMonCommand.h"
53 #include "messages/MRemoveSnaps.h"
54 #include "messages/MOSDScrub.h"
55 #include "messages/MRoute.h"
56
57 #include "common/TextTable.h"
58 #include "common/Timer.h"
59 #include "common/ceph_argparse.h"
60 #include "common/perf_counters.h"
61 #include "common/strtol.h"
62
63 #include "common/config.h"
64 #include "common/errno.h"
65
66 #include "erasure-code/ErasureCodePlugin.h"
67 #include "compressor/Compressor.h"
68 #include "common/Checksummer.h"
69
70 #include "include/compat.h"
71 #include "include/assert.h"
72 #include "include/stringify.h"
73 #include "include/util.h"
74 #include "common/cmdparse.h"
75 #include "include/str_list.h"
76 #include "include/str_map.h"
77 #include "include/scope_guard.h"
78
79 #include "json_spirit/json_spirit_reader.h"
80
81 #include <boost/algorithm/string/predicate.hpp>
82
83 #define dout_subsys ceph_subsys_mon
84 static const string OSD_PG_CREATING_PREFIX("osd_pg_creating");
85 static const string OSD_METADATA_PREFIX("osd_metadata");
86
87 namespace {
88
89 const uint32_t MAX_POOL_APPLICATIONS = 4;
90 const uint32_t MAX_POOL_APPLICATION_KEYS = 64;
91 const uint32_t MAX_POOL_APPLICATION_LENGTH = 128;
92
93 } // anonymous namespace
94
95 void LastEpochClean::Lec::report(ps_t ps, epoch_t last_epoch_clean)
96 {
97   if (epoch_by_pg.size() <= ps) {
98     epoch_by_pg.resize(ps + 1, 0);
99   }
100   const auto old_lec = epoch_by_pg[ps];
101   if (old_lec >= last_epoch_clean) {
102     // stale lec
103     return;
104   }
105   epoch_by_pg[ps] = last_epoch_clean;
106   if (last_epoch_clean < floor) {
107     floor = last_epoch_clean;
108   } else if (last_epoch_clean > floor) {
109     if (old_lec == floor) {
110       // probably should increase floor?
111       auto new_floor = std::min_element(std::begin(epoch_by_pg),
112                                         std::end(epoch_by_pg));
113       floor = *new_floor;
114     }
115   }
116   if (ps != next_missing) {
117     return;
118   }
119   for (; next_missing < epoch_by_pg.size(); next_missing++) {
120     if (epoch_by_pg[next_missing] == 0) {
121       break;
122     }
123   }
124 }
125
126 void LastEpochClean::remove_pool(uint64_t pool)
127 {
128   report_by_pool.erase(pool);
129 }
130
131 void LastEpochClean::report(const pg_t& pg, epoch_t last_epoch_clean)
132 {
133   auto& lec = report_by_pool[pg.pool()];
134   return lec.report(pg.ps(), last_epoch_clean);
135 }
136
137 epoch_t LastEpochClean::get_lower_bound(const OSDMap& latest) const
138 {
139   auto floor = latest.get_epoch();
140   for (auto& pool : latest.get_pools()) {
141     auto reported = report_by_pool.find(pool.first);
142     if (reported == report_by_pool.end()) {
143       return 0;
144     }
145     if (reported->second.next_missing < pool.second.get_pg_num()) {
146       return 0;
147     }
148     if (reported->second.floor < floor) {
149       floor = reported->second.floor;
150     }
151   }
152   return floor;
153 }
154
155
156 struct C_UpdateCreatingPGs : public Context {
157   OSDMonitor *osdmon;
158   utime_t start;
159   epoch_t epoch;
160   C_UpdateCreatingPGs(OSDMonitor *osdmon, epoch_t e) :
161     osdmon(osdmon), start(ceph_clock_now()), epoch(e) {}
162   void finish(int r) override {
163     if (r >= 0) {
164       utime_t end = ceph_clock_now();
165       dout(10) << "osdmap epoch " << epoch << " mapping took "
166                << (end - start) << " seconds" << dendl;
167       osdmon->update_creating_pgs();
168       osdmon->check_pg_creates_subs();
169     }
170   }
171 };
172
173 #undef dout_prefix
174 #define dout_prefix _prefix(_dout, mon, osdmap)
175 static ostream& _prefix(std::ostream *_dout, Monitor *mon, const OSDMap& osdmap) {
176   return *_dout << "mon." << mon->name << "@" << mon->rank
177                 << "(" << mon->get_state_name()
178                 << ").osd e" << osdmap.get_epoch() << " ";
179 }
180
181 OSDMonitor::OSDMonitor(
182   CephContext *cct,
183   Monitor *mn,
184   Paxos *p,
185   const string& service_name)
186  : PaxosService(mn, p, service_name),
187    cct(cct),
188    inc_osd_cache(g_conf->mon_osd_cache_size),
189    full_osd_cache(g_conf->mon_osd_cache_size),
190    last_attempted_minwait_time(utime_t()),
191    mapper(mn->cct, &mn->cpu_tp),
192    op_tracker(cct, true, 1)
193 {}
194
195 bool OSDMonitor::_have_pending_crush()
196 {
197   return pending_inc.crush.length() > 0;
198 }
199
200 CrushWrapper &OSDMonitor::_get_stable_crush()
201 {
202   return *osdmap.crush;
203 }
204
205 void OSDMonitor::_get_pending_crush(CrushWrapper& newcrush)
206 {
207   bufferlist bl;
208   if (pending_inc.crush.length())
209     bl = pending_inc.crush;
210   else
211     osdmap.crush->encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
212
213   bufferlist::iterator p = bl.begin();
214   newcrush.decode(p);
215 }
216
217 void OSDMonitor::create_initial()
218 {
219   dout(10) << "create_initial for " << mon->monmap->fsid << dendl;
220
221   OSDMap newmap;
222
223   bufferlist bl;
224   mon->store->get("mkfs", "osdmap", bl);
225
226   if (bl.length()) {
227     newmap.decode(bl);
228     newmap.set_fsid(mon->monmap->fsid);
229   } else {
230     newmap.build_simple(g_ceph_context, 0, mon->monmap->fsid, 0);
231   }
232   newmap.set_epoch(1);
233   newmap.created = newmap.modified = ceph_clock_now();
234
235   // new clusters should sort bitwise by default.
236   newmap.set_flag(CEPH_OSDMAP_SORTBITWISE);
237
238   // new cluster should require latest by default
239   if (g_conf->mon_debug_no_require_luminous) {
240     newmap.require_osd_release = CEPH_RELEASE_KRAKEN;
241     derr << __func__ << " mon_debug_no_require_luminous=true" << dendl;
242   } else {
243     newmap.require_osd_release = CEPH_RELEASE_LUMINOUS;
244     newmap.flags |=
245       CEPH_OSDMAP_RECOVERY_DELETES |
246       CEPH_OSDMAP_PURGED_SNAPDIRS;
247     newmap.full_ratio = g_conf->mon_osd_full_ratio;
248     if (newmap.full_ratio > 1.0) newmap.full_ratio /= 100;
249     newmap.backfillfull_ratio = g_conf->mon_osd_backfillfull_ratio;
250     if (newmap.backfillfull_ratio > 1.0) newmap.backfillfull_ratio /= 100;
251     newmap.nearfull_ratio = g_conf->mon_osd_nearfull_ratio;
252     if (newmap.nearfull_ratio > 1.0) newmap.nearfull_ratio /= 100;
253     int r = ceph_release_from_name(
254       g_conf->mon_osd_initial_require_min_compat_client.c_str());
255     if (r <= 0) {
256       assert(0 == "mon_osd_initial_require_min_compat_client is not valid");
257     }
258     newmap.require_min_compat_client = r;
259   }
260
261   // encode into pending incremental
262   newmap.encode(pending_inc.fullmap,
263                 mon->get_quorum_con_features() | CEPH_FEATURE_RESERVED);
264   pending_inc.full_crc = newmap.get_crc();
265   dout(20) << " full crc " << pending_inc.full_crc << dendl;
266 }
267
268 void OSDMonitor::get_store_prefixes(std::set<string>& s)
269 {
270   s.insert(service_name);
271   s.insert(OSD_PG_CREATING_PREFIX);
272   s.insert(OSD_METADATA_PREFIX);
273 }
274
275 void OSDMonitor::update_from_paxos(bool *need_bootstrap)
276 {
277   version_t version = get_last_committed();
278   if (version == osdmap.epoch)
279     return;
280   assert(version > osdmap.epoch);
281
282   dout(15) << "update_from_paxos paxos e " << version
283            << ", my e " << osdmap.epoch << dendl;
284
285   if (mapping_job) {
286     if (!mapping_job->is_done()) {
287       dout(1) << __func__ << " mapping job "
288               << mapping_job.get() << " did not complete, "
289               << mapping_job->shards << " left, canceling" << dendl;
290       mapping_job->abort();
291     }
292     mapping_job.reset();
293   }
294
295   load_health();
296
297   /*
298    * We will possibly have a stashed latest that *we* wrote, and we will
299    * always be sure to have the oldest full map in the first..last range
300    * due to encode_trim_extra(), which includes the oldest full map in the trim
301    * transaction.
302    *
303    * encode_trim_extra() does not however write the full map's
304    * version to 'full_latest'.  This is only done when we are building the
305    * full maps from the incremental versions.  But don't panic!  We make sure
306    * that the following conditions find whichever full map version is newer.
307    */
308   version_t latest_full = get_version_latest_full();
309   if (latest_full == 0 && get_first_committed() > 1)
310     latest_full = get_first_committed();
311
312   if (get_first_committed() > 1 &&
313       latest_full < get_first_committed()) {
314     // the monitor could be just sync'ed with its peer, and the latest_full key
315     // is not encoded in the paxos commits in encode_pending(), so we need to
316     // make sure we get it pointing to a proper version.
317     version_t lc = get_last_committed();
318     version_t fc = get_first_committed();
319
320     dout(10) << __func__ << " looking for valid full map in interval"
321              << " [" << fc << ", " << lc << "]" << dendl;
322
323     latest_full = 0;
324     for (version_t v = lc; v >= fc; v--) {
325       string full_key = "full_" + stringify(v);
326       if (mon->store->exists(get_service_name(), full_key)) {
327         dout(10) << __func__ << " found latest full map v " << v << dendl;
328         latest_full = v;
329         break;
330       }
331     }
332
333     assert(latest_full > 0);
334     auto t(std::make_shared<MonitorDBStore::Transaction>());
335     put_version_latest_full(t, latest_full);
336     mon->store->apply_transaction(t);
337     dout(10) << __func__ << " updated the on-disk full map version to "
338              << latest_full << dendl;
339   }
340
341   if ((latest_full > 0) && (latest_full > osdmap.epoch)) {
342     bufferlist latest_bl;
343     get_version_full(latest_full, latest_bl);
344     assert(latest_bl.length() != 0);
345     dout(7) << __func__ << " loading latest full map e" << latest_full << dendl;
346     osdmap.decode(latest_bl);
347   }
348
349   if (mon->monmap->get_required_features().contains_all(
350         ceph::features::mon::FEATURE_LUMINOUS)) {
351     bufferlist bl;
352     if (!mon->store->get(OSD_PG_CREATING_PREFIX, "creating", bl)) {
353       auto p = bl.begin();
354       std::lock_guard<std::mutex> l(creating_pgs_lock);
355       creating_pgs.decode(p);
356       dout(7) << __func__ << " loading creating_pgs last_scan_epoch "
357               << creating_pgs.last_scan_epoch
358               << " with " << creating_pgs.pgs.size() << " pgs" << dendl;
359     } else {
360       dout(1) << __func__ << " missing creating pgs; upgrade from post-kraken?"
361               << dendl;
362     }
363   }
364
365   // make sure we're using the right pg service.. remove me post-luminous!
366   if (osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS) {
367     dout(10) << __func__ << " pgservice is mgrstat" << dendl;
368     mon->pgservice = mon->mgrstatmon()->get_pg_stat_service();
369   } else {
370     dout(10) << __func__ << " pgservice is pg" << dendl;
371     mon->pgservice = mon->pgmon()->get_pg_stat_service();
372   }
373
374   // walk through incrementals
375   MonitorDBStore::TransactionRef t;
376   size_t tx_size = 0;
377   while (version > osdmap.epoch) {
378     bufferlist inc_bl;
379     int err = get_version(osdmap.epoch+1, inc_bl);
380     assert(err == 0);
381     assert(inc_bl.length());
382
383     dout(7) << "update_from_paxos  applying incremental " << osdmap.epoch+1
384             << dendl;
385     OSDMap::Incremental inc(inc_bl);
386     err = osdmap.apply_incremental(inc);
387     assert(err == 0);
388
389     if (!t)
390       t.reset(new MonitorDBStore::Transaction);
391
392     // Write out the full map for all past epochs.  Encode the full
393     // map with the same features as the incremental.  If we don't
394     // know, use the quorum features.  If we don't know those either,
395     // encode with all features.
396     uint64_t f = inc.encode_features;
397     if (!f)
398       f = mon->get_quorum_con_features();
399     if (!f)
400       f = -1;
401     bufferlist full_bl;
402     osdmap.encode(full_bl, f | CEPH_FEATURE_RESERVED);
403     tx_size += full_bl.length();
404
405     bufferlist orig_full_bl;
406     get_version_full(osdmap.epoch, orig_full_bl);
407     if (orig_full_bl.length()) {
408       // the primary provided the full map
409       assert(inc.have_crc);
410       if (inc.full_crc != osdmap.crc) {
411         // This will happen if the mons were running mixed versions in
412         // the past or some other circumstance made the full encoded
413         // maps divergent.  Reloading here will bring us back into
414         // sync with the primary for this and all future maps.  OSDs
415         // will also be brought back into sync when they discover the
416         // crc mismatch and request a full map from a mon.
417         derr << __func__ << " full map CRC mismatch, resetting to canonical"
418              << dendl;
419         osdmap = OSDMap();
420         osdmap.decode(orig_full_bl);
421       }
422     } else {
423       assert(!inc.have_crc);
424       put_version_full(t, osdmap.epoch, full_bl);
425     }
426     put_version_latest_full(t, osdmap.epoch);
427
428     // share
429     dout(1) << osdmap << dendl;
430
431     if (osdmap.epoch == 1) {
432       t->erase("mkfs", "osdmap");
433     }
434
435     // make sure we're using the right pg service.. remove me post-luminous!
436     if (osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS) {
437       dout(10) << __func__ << " pgservice is mgrstat" << dendl;
438       mon->pgservice = mon->mgrstatmon()->get_pg_stat_service();
439     } else {
440       dout(10) << __func__ << " pgservice is pg" << dendl;
441       mon->pgservice = mon->pgmon()->get_pg_stat_service();
442     }
443
444     if (tx_size > g_conf->mon_sync_max_payload_size*2) {
445       mon->store->apply_transaction(t);
446       t = MonitorDBStore::TransactionRef();
447       tx_size = 0;
448     }
449     if (mon->monmap->get_required_features().contains_all(
450           ceph::features::mon::FEATURE_LUMINOUS)) {
451       for (const auto &osd_state : inc.new_state) {
452         if (osd_state.second & CEPH_OSD_UP) {
453           // could be marked up *or* down, but we're too lazy to check which
454           last_osd_report.erase(osd_state.first);
455         }
456         if (osd_state.second & CEPH_OSD_EXISTS) {
457           // could be created *or* destroyed, but we can safely drop it
458           osd_epochs.erase(osd_state.first);
459         }
460       }
461     }
462   }
463
464   if (t) {
465     mon->store->apply_transaction(t);
466   }
467
468   for (int o = 0; o < osdmap.get_max_osd(); o++) {
469     if (osdmap.is_out(o))
470       continue;
471     auto found = down_pending_out.find(o);
472     if (osdmap.is_down(o)) {
473       // populate down -> out map
474       if (found == down_pending_out.end()) {
475         dout(10) << " adding osd." << o << " to down_pending_out map" << dendl;
476         down_pending_out[o] = ceph_clock_now();
477       }
478     } else {
479       if (found != down_pending_out.end()) {
480         dout(10) << " removing osd." << o << " from down_pending_out map" << dendl;
481         down_pending_out.erase(found);
482       }
483     }
484   }
485   // XXX: need to trim MonSession connected with a osd whose id > max_osd?
486
487   if (mon->is_leader()) {
488     // kick pgmon, make sure it's seen the latest map
489     mon->pgmon()->check_osd_map(osdmap.epoch);
490   }
491
492   check_osdmap_subs();
493   check_pg_creates_subs();
494
495   share_map_with_random_osd();
496   update_logger();
497
498   process_failures();
499
500   // make sure our feature bits reflect the latest map
501   update_msgr_features();
502
503   if (!mon->is_leader()) {
504     // will be called by on_active() on the leader, avoid doing so twice
505     start_mapping();
506   }
507 }
508
509 void OSDMonitor::start_mapping()
510 {
511   // initiate mapping job
512   if (mapping_job) {
513     dout(10) << __func__ << " canceling previous mapping_job " << mapping_job.get()
514              << dendl;
515     mapping_job->abort();
516   }
517   if (!osdmap.get_pools().empty()) {
518     auto fin = new C_UpdateCreatingPGs(this, osdmap.get_epoch());
519     mapping_job = mapping.start_update(osdmap, mapper,
520                                        g_conf->mon_osd_mapping_pgs_per_chunk);
521     dout(10) << __func__ << " started mapping job " << mapping_job.get()
522              << " at " << fin->start << dendl;
523     mapping_job->set_finish_event(fin);
524   } else {
525     dout(10) << __func__ << " no pools, no mapping job" << dendl;
526     mapping_job = nullptr;
527   }
528 }
529
530 void OSDMonitor::update_msgr_features()
531 {
532   set<int> types;
533   types.insert((int)entity_name_t::TYPE_OSD);
534   types.insert((int)entity_name_t::TYPE_CLIENT);
535   types.insert((int)entity_name_t::TYPE_MDS);
536   types.insert((int)entity_name_t::TYPE_MON);
537   for (set<int>::iterator q = types.begin(); q != types.end(); ++q) {
538     uint64_t mask;
539     uint64_t features = osdmap.get_features(*q, &mask);
540     if ((mon->messenger->get_policy(*q).features_required & mask) != features) {
541       dout(0) << "crush map has features " << features << ", adjusting msgr requires" << dendl;
542       Messenger::Policy p = mon->messenger->get_policy(*q);
543       p.features_required = (p.features_required & ~mask) | features;
544       mon->messenger->set_policy(*q, p);
545     }
546   }
547 }
548
549 void OSDMonitor::on_active()
550 {
551   update_logger();
552
553   if (mon->is_leader()) {
554     mon->clog->debug() << "osdmap " << osdmap;
555   } else {
556     list<MonOpRequestRef> ls;
557     take_all_failures(ls);
558     while (!ls.empty()) {
559       MonOpRequestRef op = ls.front();
560       op->mark_osdmon_event(__func__);
561       dispatch(op);
562       ls.pop_front();
563     }
564   }
565   start_mapping();
566 }
567
568 void OSDMonitor::on_restart()
569 {
570   last_osd_report.clear();
571 }
572
573 void OSDMonitor::on_shutdown()
574 {
575   dout(10) << __func__ << dendl;
576   if (mapping_job) {
577     dout(10) << __func__ << " canceling previous mapping_job " << mapping_job.get()
578              << dendl;
579     mapping_job->abort();
580   }
581
582   // discard failure info, waiters
583   list<MonOpRequestRef> ls;
584   take_all_failures(ls);
585   ls.clear();
586 }
587
588 void OSDMonitor::update_logger()
589 {
590   dout(10) << "update_logger" << dendl;
591
592   mon->cluster_logger->set(l_cluster_num_osd, osdmap.get_num_osds());
593   mon->cluster_logger->set(l_cluster_num_osd_up, osdmap.get_num_up_osds());
594   mon->cluster_logger->set(l_cluster_num_osd_in, osdmap.get_num_in_osds());
595   mon->cluster_logger->set(l_cluster_osd_epoch, osdmap.get_epoch());
596 }
597
598 void OSDMonitor::create_pending()
599 {
600   pending_inc = OSDMap::Incremental(osdmap.epoch+1);
601   pending_inc.fsid = mon->monmap->fsid;
602
603   dout(10) << "create_pending e " << pending_inc.epoch << dendl;
604
605   // clean up pg_temp, primary_temp
606   OSDMap::clean_temps(g_ceph_context, osdmap, &pending_inc);
607   dout(10) << "create_pending  did clean_temps" << dendl;
608
609   // On upgrade OSDMap has new field set by mon_osd_backfillfull_ratio config
610   // instead of osd_backfill_full_ratio config
611   if (osdmap.backfillfull_ratio <= 0) {
612     pending_inc.new_backfillfull_ratio = g_conf->mon_osd_backfillfull_ratio;
613     if (pending_inc.new_backfillfull_ratio > 1.0)
614       pending_inc.new_backfillfull_ratio /= 100;
615     dout(1) << __func__ << " setting backfillfull_ratio = "
616             << pending_inc.new_backfillfull_ratio << dendl;
617   }
618   if (osdmap.get_epoch() > 0 &&
619       osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
620     // transition full ratios from PGMap to OSDMap (on upgrade)
621     float full_ratio = mon->pgservice->get_full_ratio();
622     float nearfull_ratio = mon->pgservice->get_nearfull_ratio();
623     if (osdmap.full_ratio != full_ratio) {
624       dout(10) << __func__ << " full_ratio " << osdmap.full_ratio
625                << " -> " << full_ratio << " (from pgmap)" << dendl;
626       pending_inc.new_full_ratio = full_ratio;
627     }
628     if (osdmap.nearfull_ratio != nearfull_ratio) {
629       dout(10) << __func__ << " nearfull_ratio " << osdmap.nearfull_ratio
630                << " -> " << nearfull_ratio << " (from pgmap)" << dendl;
631       pending_inc.new_nearfull_ratio = nearfull_ratio;
632     }
633   } else {
634     // safety check (this shouldn't really happen)
635     if (osdmap.full_ratio <= 0) {
636       pending_inc.new_full_ratio = g_conf->mon_osd_full_ratio;
637       if (pending_inc.new_full_ratio > 1.0)
638         pending_inc.new_full_ratio /= 100;
639       dout(1) << __func__ << " setting full_ratio = "
640               << pending_inc.new_full_ratio << dendl;
641     }
642     if (osdmap.nearfull_ratio <= 0) {
643       pending_inc.new_nearfull_ratio = g_conf->mon_osd_nearfull_ratio;
644       if (pending_inc.new_nearfull_ratio > 1.0)
645         pending_inc.new_nearfull_ratio /= 100;
646       dout(1) << __func__ << " setting nearfull_ratio = "
647               << pending_inc.new_nearfull_ratio << dendl;
648     }
649   }
650
651   // Rewrite CRUSH rule IDs if they are using legacy "ruleset"
652   // structure.
653   if (osdmap.crush->has_legacy_rule_ids()) {
654     CrushWrapper newcrush;
655     _get_pending_crush(newcrush);
656
657     // First, for all pools, work out which rule they really used
658     // by resolving ruleset to rule.
659     for (const auto &i : osdmap.get_pools()) {
660       const auto pool_id = i.first;
661       const auto &pool = i.second;
662       int new_rule_id = newcrush.find_rule(pool.crush_rule,
663                                            pool.type, pool.size);
664
665       dout(1) << __func__ << " rewriting pool "
666               << osdmap.get_pool_name(pool_id) << " crush ruleset "
667               << pool.crush_rule << " -> rule id " << new_rule_id << dendl;
668       if (pending_inc.new_pools.count(pool_id) == 0) {
669         pending_inc.new_pools[pool_id] = pool;
670       }
671       pending_inc.new_pools[pool_id].crush_rule = new_rule_id;
672     }
673
674     // Now, go ahead and renumber all the rules so that their
675     // rule_id field corresponds to their position in the array
676     auto old_to_new = newcrush.renumber_rules();
677     dout(1) << __func__ << " Rewrote " << old_to_new << " crush IDs:" << dendl;
678     for (const auto &i : old_to_new) {
679       dout(1) << __func__ << " " << i.first << " -> " << i.second << dendl;
680     }
681     pending_inc.crush.clear();
682     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
683   }
684 }
685
686 creating_pgs_t
687 OSDMonitor::update_pending_pgs(const OSDMap::Incremental& inc)
688 {
689   dout(10) << __func__ << dendl;
690   creating_pgs_t pending_creatings;
691   {
692     std::lock_guard<std::mutex> l(creating_pgs_lock);
693     pending_creatings = creating_pgs;
694   }
695   // check for new or old pools
696   if (pending_creatings.last_scan_epoch < inc.epoch) {
697     if (osdmap.get_epoch() &&
698         osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
699       auto added =
700         mon->pgservice->maybe_add_creating_pgs(creating_pgs.last_scan_epoch,
701                                                osdmap.get_pools(),
702                                                &pending_creatings);
703       dout(7) << __func__ << " " << added << " pgs added from pgmap" << dendl;
704     }
705     unsigned queued = 0;
706     queued += scan_for_creating_pgs(osdmap.get_pools(),
707                                     inc.old_pools,
708                                     inc.modified,
709                                     &pending_creatings);
710     queued += scan_for_creating_pgs(inc.new_pools,
711                                     inc.old_pools,
712                                     inc.modified,
713                                     &pending_creatings);
714     dout(10) << __func__ << " " << queued << " pools queued" << dendl;
715     for (auto deleted_pool : inc.old_pools) {
716       auto removed = pending_creatings.remove_pool(deleted_pool);
717       dout(10) << __func__ << " " << removed
718                << " pg removed because containing pool deleted: "
719                << deleted_pool << dendl;
720       last_epoch_clean.remove_pool(deleted_pool);
721     }
722     // pgmon updates its creating_pgs in check_osd_map() which is called by
723     // on_active() and check_osd_map() could be delayed if lease expires, so its
724     // creating_pgs could be stale in comparison with the one of osdmon. let's
725     // trim them here. otherwise, they will be added back after being erased.
726     unsigned removed = 0;
727     for (auto& pg : pending_created_pgs) {
728       dout(20) << __func__ << " noting created pg " << pg << dendl;
729       pending_creatings.created_pools.insert(pg.pool());
730       removed += pending_creatings.pgs.erase(pg);
731     }
732     pending_created_pgs.clear();
733     dout(10) << __func__ << " " << removed
734              << " pgs removed because they're created" << dendl;
735     pending_creatings.last_scan_epoch = osdmap.get_epoch();
736   }
737
738   // process queue
739   unsigned max = MAX(1, g_conf->mon_osd_max_creating_pgs);
740   const auto total = pending_creatings.pgs.size();
741   while (pending_creatings.pgs.size() < max &&
742          !pending_creatings.queue.empty()) {
743     auto p = pending_creatings.queue.begin();
744     int64_t poolid = p->first;
745     dout(10) << __func__ << " pool " << poolid
746              << " created " << p->second.created
747              << " modified " << p->second.modified
748              << " [" << p->second.start << "-" << p->second.end << ")"
749              << dendl;
750     int n = MIN(max - pending_creatings.pgs.size(),
751                 p->second.end - p->second.start);
752     ps_t first = p->second.start;
753     ps_t end = first + n;
754     for (ps_t ps = first; ps < end; ++ps) {
755       const pg_t pgid{ps, static_cast<uint64_t>(poolid)};
756       // NOTE: use the *current* epoch as the PG creation epoch so that the
757       // OSD does not have to generate a long set of PastIntervals.
758       pending_creatings.pgs.emplace(pgid, make_pair(inc.epoch,
759                                                     p->second.modified));
760       dout(10) << __func__ << " adding " << pgid << dendl;
761     }
762     p->second.start = end;
763     if (p->second.done()) {
764       dout(10) << __func__ << " done with queue for " << poolid << dendl;
765       pending_creatings.queue.erase(p);
766     } else {
767       dout(10) << __func__ << " pool " << poolid
768                << " now [" << p->second.start << "-" << p->second.end << ")"
769                << dendl;
770     }
771   }
772   dout(10) << __func__ << " queue remaining: " << pending_creatings.queue.size()
773            << " pools" << dendl;
774   dout(10) << __func__
775            << " " << (pending_creatings.pgs.size() - total)
776            << "/" << pending_creatings.pgs.size()
777            << " pgs added from queued pools" << dendl;
778   return pending_creatings;
779 }
780
781 void OSDMonitor::maybe_prime_pg_temp()
782 {
783   bool all = false;
784   if (pending_inc.crush.length()) {
785     dout(10) << __func__ << " new crush map, all" << dendl;
786     all = true;
787   }
788
789   if (!pending_inc.new_up_client.empty()) {
790     dout(10) << __func__ << " new up osds, all" << dendl;
791     all = true;
792   }
793
794   // check for interesting OSDs
795   set<int> osds;
796   for (auto p = pending_inc.new_state.begin();
797        !all && p != pending_inc.new_state.end();
798        ++p) {
799     if ((p->second & CEPH_OSD_UP) &&
800         osdmap.is_up(p->first)) {
801       osds.insert(p->first);
802     }
803   }
804   for (map<int32_t,uint32_t>::iterator p = pending_inc.new_weight.begin();
805        !all && p != pending_inc.new_weight.end();
806        ++p) {
807     if (p->second < osdmap.get_weight(p->first)) {
808       // weight reduction
809       osds.insert(p->first);
810     } else {
811       dout(10) << __func__ << " osd." << p->first << " weight increase, all"
812                << dendl;
813       all = true;
814     }
815   }
816
817   if (!all && osds.empty())
818     return;
819
820   if (!all) {
821     unsigned estimate =
822       mapping.get_osd_acting_pgs(*osds.begin()).size() * osds.size();
823     if (estimate > mapping.get_num_pgs() *
824         g_conf->mon_osd_prime_pg_temp_max_estimate) {
825       dout(10) << __func__ << " estimate " << estimate << " pgs on "
826                << osds.size() << " osds >= "
827                << g_conf->mon_osd_prime_pg_temp_max_estimate << " of total "
828                << mapping.get_num_pgs() << " pgs, all"
829                << dendl;
830       all = true;
831     } else {
832       dout(10) << __func__ << " estimate " << estimate << " pgs on "
833                << osds.size() << " osds" << dendl;
834     }
835   }
836
837   OSDMap next;
838   next.deepish_copy_from(osdmap);
839   next.apply_incremental(pending_inc);
840
841   if (next.get_pools().empty()) {
842     dout(10) << __func__ << " no pools, no pg_temp priming" << dendl;
843   } else if (all) {
844     PrimeTempJob job(next, this);
845     mapper.queue(&job, g_conf->mon_osd_mapping_pgs_per_chunk);
846     if (job.wait_for(g_conf->mon_osd_prime_pg_temp_max_time)) {
847       dout(10) << __func__ << " done in " << job.get_duration() << dendl;
848     } else {
849       dout(10) << __func__ << " did not finish in "
850                << g_conf->mon_osd_prime_pg_temp_max_time
851                << ", stopping" << dendl;
852       job.abort();
853     }
854   } else {
855     dout(10) << __func__ << " " << osds.size() << " interesting osds" << dendl;
856     utime_t stop = ceph_clock_now();
857     stop += g_conf->mon_osd_prime_pg_temp_max_time;
858     const int chunk = 1000;
859     int n = chunk;
860     std::unordered_set<pg_t> did_pgs;
861     for (auto osd : osds) {
862       auto& pgs = mapping.get_osd_acting_pgs(osd);
863       dout(20) << __func__ << " osd." << osd << " " << pgs << dendl;
864       for (auto pgid : pgs) {
865         if (!did_pgs.insert(pgid).second) {
866           continue;
867         }
868         prime_pg_temp(next, pgid);
869         if (--n <= 0) {
870           n = chunk;
871           if (ceph_clock_now() > stop) {
872             dout(10) << __func__ << " consumed more than "
873                      << g_conf->mon_osd_prime_pg_temp_max_time
874                      << " seconds, stopping"
875                      << dendl;
876             return;
877           }
878         }
879       }
880     }
881   }
882 }
883
884 void OSDMonitor::prime_pg_temp(
885   const OSDMap& next,
886   pg_t pgid)
887 {
888   if (mon->monmap->get_required_features().contains_all(
889         ceph::features::mon::FEATURE_LUMINOUS)) {
890     // TODO: remove this creating_pgs direct access?
891     if (creating_pgs.pgs.count(pgid)) {
892       return;
893     }
894   } else {
895     if (mon->pgservice->is_creating_pg(pgid)) {
896       return;
897     }
898   }
899   if (!osdmap.pg_exists(pgid)) {
900     return;
901   }
902
903   vector<int> up, acting;
904   mapping.get(pgid, &up, nullptr, &acting, nullptr);
905
906   vector<int> next_up, next_acting;
907   int next_up_primary, next_acting_primary;
908   next.pg_to_up_acting_osds(pgid, &next_up, &next_up_primary,
909                             &next_acting, &next_acting_primary);
910   if (acting == next_acting && next_up != next_acting)
911     return;  // no change since last epoch
912
913   if (acting.empty())
914     return;  // if previously empty now we can be no worse off
915   const pg_pool_t *pool = next.get_pg_pool(pgid.pool());
916   if (pool && acting.size() < pool->min_size)
917     return;  // can be no worse off than before
918
919   if (next_up == next_acting) {
920     acting.clear();
921     dout(20) << __func__ << "next_up === next_acting now, clear pg_temp"
922              << dendl;
923   }
924
925   dout(20) << __func__ << " " << pgid << " " << up << "/" << acting
926            << " -> " << next_up << "/" << next_acting
927            << ", priming " << acting
928            << dendl;
929   {
930     Mutex::Locker l(prime_pg_temp_lock);
931     // do not touch a mapping if a change is pending
932     pending_inc.new_pg_temp.emplace(
933       pgid,
934       mempool::osdmap::vector<int>(acting.begin(), acting.end()));
935   }
936 }
937
938 /**
939  * @note receiving a transaction in this function gives a fair amount of
940  * freedom to the service implementation if it does need it. It shouldn't.
941  */
942 void OSDMonitor::encode_pending(MonitorDBStore::TransactionRef t)
943 {
944   dout(10) << "encode_pending e " << pending_inc.epoch
945            << dendl;
946
947   // finalize up pending_inc
948   pending_inc.modified = ceph_clock_now();
949
950   int r = pending_inc.propagate_snaps_to_tiers(g_ceph_context, osdmap);
951   assert(r == 0);
952
953   if (mapping_job) {
954     if (!mapping_job->is_done()) {
955       dout(1) << __func__ << " skipping prime_pg_temp; mapping job "
956               << mapping_job.get() << " did not complete, "
957               << mapping_job->shards << " left" << dendl;
958       mapping_job->abort();
959     } else if (mapping.get_epoch() < osdmap.get_epoch()) {
960       dout(1) << __func__ << " skipping prime_pg_temp; mapping job "
961               << mapping_job.get() << " is prior epoch "
962               << mapping.get_epoch() << dendl;
963     } else {
964       if (g_conf->mon_osd_prime_pg_temp) {
965         maybe_prime_pg_temp();
966       }
967     } 
968   } else if (g_conf->mon_osd_prime_pg_temp) {
969     dout(1) << __func__ << " skipping prime_pg_temp; mapping job did not start"
970             << dendl;
971   }
972   mapping_job.reset();
973
974   // ensure we don't have blank new_state updates.  these are interrpeted as
975   // CEPH_OSD_UP (and almost certainly not what we want!).
976   auto p = pending_inc.new_state.begin();
977   while (p != pending_inc.new_state.end()) {
978     if (p->second == 0) {
979       dout(10) << "new_state for osd." << p->first << " is 0, removing" << dendl;
980       p = pending_inc.new_state.erase(p);
981     } else {
982       ++p;
983     }
984   }
985
986   bufferlist bl;
987
988   {
989     OSDMap tmp;
990     tmp.deepish_copy_from(osdmap);
991     tmp.apply_incremental(pending_inc);
992
993     if (tmp.require_osd_release >= CEPH_RELEASE_LUMINOUS) {
994       // remove any legacy osdmap nearfull/full flags
995       {
996         if (tmp.test_flag(CEPH_OSDMAP_FULL | CEPH_OSDMAP_NEARFULL)) {
997           dout(10) << __func__ << " clearing legacy osdmap nearfull/full flag"
998                    << dendl;
999           remove_flag(CEPH_OSDMAP_NEARFULL);
1000           remove_flag(CEPH_OSDMAP_FULL);
1001         }
1002       }
1003       // collect which pools are currently affected by
1004       // the near/backfill/full osd(s),
1005       // and set per-pool near/backfill/full flag instead
1006       set<int64_t> full_pool_ids;
1007       set<int64_t> backfillfull_pool_ids;
1008       set<int64_t> nearfull_pool_ids;
1009       tmp.get_full_pools(g_ceph_context,
1010                          &full_pool_ids,
1011                          &backfillfull_pool_ids,
1012                          &nearfull_pool_ids);
1013       if (full_pool_ids.empty() ||
1014           backfillfull_pool_ids.empty() ||
1015           nearfull_pool_ids.empty()) {
1016         // normal case - no nearfull, backfillfull or full osds
1017         // try cancel any improper nearfull/backfillfull/full pool
1018         // flags first
1019         for (auto &pool: tmp.get_pools()) {
1020           auto p = pool.first;
1021           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_NEARFULL) &&
1022               nearfull_pool_ids.empty()) {
1023             dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1024                      << "'s nearfull flag" << dendl;
1025             if (pending_inc.new_pools.count(p) == 0) {
1026               // load original pool info first!
1027               pending_inc.new_pools[p] = pool.second;
1028             }
1029             pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_NEARFULL;
1030           }
1031           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_BACKFILLFULL) &&
1032               backfillfull_pool_ids.empty()) {
1033             dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1034                      << "'s backfillfull flag" << dendl;
1035             if (pending_inc.new_pools.count(p) == 0) {
1036               pending_inc.new_pools[p] = pool.second;
1037             }
1038             pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_BACKFILLFULL;
1039           }
1040           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL) &&
1041               full_pool_ids.empty()) {
1042             if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL_NO_QUOTA)) {
1043               // set by EQUOTA, skipping
1044               continue;
1045             }
1046             dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1047                      << "'s full flag" << dendl;
1048             if (pending_inc.new_pools.count(p) == 0) {
1049               pending_inc.new_pools[p] = pool.second;
1050             }
1051             pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_FULL;
1052           }
1053         }
1054       }
1055       if (!full_pool_ids.empty()) {
1056         dout(10) << __func__ << " marking pool(s) " << full_pool_ids
1057                  << " as full" << dendl;
1058         for (auto &p: full_pool_ids) {
1059           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL)) {
1060             continue;
1061           }
1062           if (pending_inc.new_pools.count(p) == 0) {
1063             pending_inc.new_pools[p] = tmp.pools[p];
1064           }
1065           pending_inc.new_pools[p].flags |= pg_pool_t::FLAG_FULL;
1066           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_BACKFILLFULL;
1067           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_NEARFULL;
1068         }
1069         // cancel FLAG_FULL for pools which are no longer full too
1070         for (auto &pool: tmp.get_pools()) {
1071           auto p = pool.first;
1072           if (full_pool_ids.count(p)) {
1073             // skip pools we have just marked as full above
1074             continue;
1075           }
1076           if (!tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL) ||
1077                tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL_NO_QUOTA)) {
1078             // don't touch if currently is not full
1079             // or is running out of quota (and hence considered as full)
1080             continue;
1081           }
1082           dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1083                    << "'s full flag" << dendl;
1084           if (pending_inc.new_pools.count(p) == 0) {
1085             pending_inc.new_pools[p] = pool.second;
1086           }
1087           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_FULL;
1088         }
1089       }
1090       if (!backfillfull_pool_ids.empty()) {
1091         for (auto &p: backfillfull_pool_ids) {
1092           if (full_pool_ids.count(p)) {
1093             // skip pools we have already considered as full above
1094             continue;
1095           }
1096           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL_NO_QUOTA)) {
1097             // make sure FLAG_FULL is truly set, so we are safe not
1098             // to set a extra (redundant) FLAG_BACKFILLFULL flag
1099             assert(tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL));
1100             continue;
1101           }
1102           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_BACKFILLFULL)) {
1103             // don't bother if pool is already marked as backfillfull
1104             continue;
1105           }
1106           dout(10) << __func__ << " marking pool '" << tmp.pool_name[p]
1107                    << "'s as backfillfull" << dendl;
1108           if (pending_inc.new_pools.count(p) == 0) {
1109             pending_inc.new_pools[p] = tmp.pools[p];
1110           }
1111           pending_inc.new_pools[p].flags |= pg_pool_t::FLAG_BACKFILLFULL;
1112           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_NEARFULL;
1113         }
1114         // cancel FLAG_BACKFILLFULL for pools
1115         // which are no longer backfillfull too
1116         for (auto &pool: tmp.get_pools()) {
1117           auto p = pool.first;
1118           if (full_pool_ids.count(p) || backfillfull_pool_ids.count(p)) {
1119             // skip pools we have just marked as backfillfull/full above
1120             continue;
1121           }
1122           if (!tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_BACKFILLFULL)) {
1123             // and don't touch if currently is not backfillfull
1124             continue;
1125           }
1126           dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1127                    << "'s backfillfull flag" << dendl;
1128           if (pending_inc.new_pools.count(p) == 0) {
1129             pending_inc.new_pools[p] = pool.second;
1130           }
1131           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_BACKFILLFULL;
1132         }
1133       }
1134       if (!nearfull_pool_ids.empty()) {
1135         for (auto &p: nearfull_pool_ids) {
1136           if (full_pool_ids.count(p) || backfillfull_pool_ids.count(p)) {
1137             continue;
1138           }
1139           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL_NO_QUOTA)) {
1140             // make sure FLAG_FULL is truly set, so we are safe not
1141             // to set a extra (redundant) FLAG_NEARFULL flag
1142             assert(tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_FULL));
1143             continue;
1144           }
1145           if (tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_NEARFULL)) {
1146             // don't bother if pool is already marked as nearfull
1147             continue;
1148           }
1149           dout(10) << __func__ << " marking pool '" << tmp.pool_name[p]
1150                    << "'s as nearfull" << dendl;
1151           if (pending_inc.new_pools.count(p) == 0) {
1152             pending_inc.new_pools[p] = tmp.pools[p];
1153           }
1154           pending_inc.new_pools[p].flags |= pg_pool_t::FLAG_NEARFULL;
1155         }
1156         // cancel FLAG_NEARFULL for pools
1157         // which are no longer nearfull too
1158         for (auto &pool: tmp.get_pools()) {
1159           auto p = pool.first;
1160           if (full_pool_ids.count(p) ||
1161               backfillfull_pool_ids.count(p) ||
1162               nearfull_pool_ids.count(p)) {
1163             // skip pools we have just marked as
1164             // nearfull/backfillfull/full above
1165             continue;
1166           }
1167           if (!tmp.get_pg_pool(p)->has_flag(pg_pool_t::FLAG_NEARFULL)) {
1168             // and don't touch if currently is not nearfull
1169             continue;
1170           }
1171           dout(10) << __func__ << " clearing pool '" << tmp.pool_name[p]
1172                    << "'s nearfull flag" << dendl;
1173           if (pending_inc.new_pools.count(p) == 0) {
1174             pending_inc.new_pools[p] = pool.second;
1175           }
1176           pending_inc.new_pools[p].flags &= ~pg_pool_t::FLAG_NEARFULL;
1177         }
1178       }
1179
1180       // min_compat_client?
1181       if (tmp.require_min_compat_client == 0) {
1182         auto mv = tmp.get_min_compat_client();
1183         dout(1) << __func__ << " setting require_min_compat_client to currently "
1184                 << "required " << ceph_release_name(mv) << dendl;
1185         mon->clog->info() << "setting require_min_compat_client to currently "
1186                           << "required " << ceph_release_name(mv);
1187         pending_inc.new_require_min_compat_client = mv;
1188       }
1189
1190       if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
1191         // convert ec profile ruleset-* -> crush-*
1192         for (auto& p : tmp.erasure_code_profiles) {
1193           bool changed = false;
1194           map<string,string> newprofile;
1195           for (auto& q : p.second) {
1196             if (q.first.find("ruleset-") == 0) {
1197               string key = "crush-";
1198               key += q.first.substr(8);
1199               newprofile[key] = q.second;
1200               changed = true;
1201               dout(20) << " updating ec profile " << p.first
1202                        << " key " << q.first << " -> " << key << dendl;
1203             } else {
1204               newprofile[q.first] = q.second;
1205             }
1206           }
1207           if (changed) {
1208             dout(10) << " updated ec profile " << p.first << ": "
1209                      << newprofile << dendl;
1210             pending_inc.new_erasure_code_profiles[p.first] = newprofile;
1211           }
1212         }
1213
1214         // auto-enable pool applications upon upgrade
1215         // NOTE: this can be removed post-Luminous assuming upgrades need to
1216         // proceed through Luminous
1217         for (auto &pool_pair : tmp.pools) {
1218           int64_t pool_id = pool_pair.first;
1219           pg_pool_t pg_pool = pool_pair.second;
1220           if (pg_pool.is_tier()) {
1221             continue;
1222           }
1223
1224           std::string pool_name = tmp.get_pool_name(pool_id);
1225           uint32_t match_count = 0;
1226
1227           // CephFS
1228           FSMap const &pending_fsmap = mon->mdsmon()->get_pending();
1229           if (pending_fsmap.pool_in_use(pool_id)) {
1230             dout(10) << __func__ << " auto-enabling CephFS on pool '"
1231                      << pool_name << "'" << dendl;
1232             pg_pool.application_metadata.insert(
1233               {pg_pool_t::APPLICATION_NAME_CEPHFS, {}});
1234             ++match_count;
1235           }
1236
1237           // RBD heuristics (default OpenStack pool names from docs and
1238           // ceph-ansible)
1239           if (boost::algorithm::contains(pool_name, "rbd") ||
1240               pool_name == "images" || pool_name == "volumes" ||
1241               pool_name == "backups" || pool_name == "vms") {
1242             dout(10) << __func__ << " auto-enabling RBD on pool '"
1243                      << pool_name << "'" << dendl;
1244             pg_pool.application_metadata.insert(
1245               {pg_pool_t::APPLICATION_NAME_RBD, {}});
1246             ++match_count;
1247           }
1248
1249           // RGW heuristics
1250           if (boost::algorithm::contains(pool_name, ".rgw") ||
1251               boost::algorithm::contains(pool_name, ".log") ||
1252               boost::algorithm::contains(pool_name, ".intent-log") ||
1253               boost::algorithm::contains(pool_name, ".usage") ||
1254               boost::algorithm::contains(pool_name, ".users")) {
1255             dout(10) << __func__ << " auto-enabling RGW on pool '"
1256                      << pool_name << "'" << dendl;
1257             pg_pool.application_metadata.insert(
1258               {pg_pool_t::APPLICATION_NAME_RGW, {}});
1259             ++match_count;
1260           }
1261
1262           // OpenStack gnocchi (from ceph-ansible)
1263           if (pool_name == "metrics" && match_count == 0) {
1264             dout(10) << __func__ << " auto-enabling OpenStack Gnocchi on pool '"
1265                      << pool_name << "'" << dendl;
1266             pg_pool.application_metadata.insert({"openstack_gnocchi", {}});
1267             ++match_count;
1268           }
1269
1270           if (match_count == 1) {
1271             pg_pool.last_change = pending_inc.epoch;
1272             pending_inc.new_pools[pool_id] = pg_pool;
1273           } else if (match_count > 1) {
1274             auto pstat = mon->pgservice->get_pool_stat(pool_id);
1275             if (pstat != nullptr && pstat->stats.sum.num_objects > 0) {
1276               mon->clog->info() << "unable to auto-enable application for pool "
1277                                 << "'" << pool_name << "'";
1278             }
1279           }
1280         }
1281       }
1282     }
1283   }
1284
1285   // tell me about it
1286   for (auto i = pending_inc.new_state.begin();
1287        i != pending_inc.new_state.end();
1288        ++i) {
1289     int s = i->second ? i->second : CEPH_OSD_UP;
1290     if (s & CEPH_OSD_UP)
1291       dout(2) << " osd." << i->first << " DOWN" << dendl;
1292     if (s & CEPH_OSD_EXISTS)
1293       dout(2) << " osd." << i->first << " DNE" << dendl;
1294   }
1295   for (map<int32_t,entity_addr_t>::iterator i = pending_inc.new_up_client.begin();
1296        i != pending_inc.new_up_client.end();
1297        ++i) {
1298     //FIXME: insert cluster addresses too
1299     dout(2) << " osd." << i->first << " UP " << i->second << dendl;
1300   }
1301   for (map<int32_t,uint32_t>::iterator i = pending_inc.new_weight.begin();
1302        i != pending_inc.new_weight.end();
1303        ++i) {
1304     if (i->second == CEPH_OSD_OUT) {
1305       dout(2) << " osd." << i->first << " OUT" << dendl;
1306     } else if (i->second == CEPH_OSD_IN) {
1307       dout(2) << " osd." << i->first << " IN" << dendl;
1308     } else {
1309       dout(2) << " osd." << i->first << " WEIGHT " << hex << i->second << dec << dendl;
1310     }
1311   }
1312
1313   // features for osdmap and its incremental
1314   uint64_t features = mon->get_quorum_con_features();
1315
1316   // encode full map and determine its crc
1317   OSDMap tmp;
1318   {
1319     tmp.deepish_copy_from(osdmap);
1320     tmp.apply_incremental(pending_inc);
1321
1322     // determine appropriate features
1323     if (tmp.require_osd_release < CEPH_RELEASE_LUMINOUS) {
1324       dout(10) << __func__ << " encoding without feature SERVER_LUMINOUS"
1325                << dendl;
1326       features &= ~CEPH_FEATURE_SERVER_LUMINOUS;
1327     }
1328     if (tmp.require_osd_release < CEPH_RELEASE_KRAKEN) {
1329       dout(10) << __func__ << " encoding without feature SERVER_KRAKEN | "
1330                << "MSG_ADDR2" << dendl;
1331       features &= ~(CEPH_FEATURE_SERVER_KRAKEN |
1332                     CEPH_FEATURE_MSG_ADDR2);
1333     }
1334     if (tmp.require_osd_release < CEPH_RELEASE_JEWEL) {
1335       dout(10) << __func__ << " encoding without feature SERVER_JEWEL" << dendl;
1336       features &= ~CEPH_FEATURE_SERVER_JEWEL;
1337     }
1338     dout(10) << __func__ << " encoding full map with " << features << dendl;
1339
1340     bufferlist fullbl;
1341     ::encode(tmp, fullbl, features | CEPH_FEATURE_RESERVED);
1342     pending_inc.full_crc = tmp.get_crc();
1343
1344     // include full map in the txn.  note that old monitors will
1345     // overwrite this.  new ones will now skip the local full map
1346     // encode and reload from this.
1347     put_version_full(t, pending_inc.epoch, fullbl);
1348   }
1349
1350   // encode
1351   assert(get_last_committed() + 1 == pending_inc.epoch);
1352   ::encode(pending_inc, bl, features | CEPH_FEATURE_RESERVED);
1353
1354   dout(20) << " full_crc " << tmp.get_crc()
1355            << " inc_crc " << pending_inc.inc_crc << dendl;
1356
1357   /* put everything in the transaction */
1358   put_version(t, pending_inc.epoch, bl);
1359   put_last_committed(t, pending_inc.epoch);
1360
1361   // metadata, too!
1362   for (map<int,bufferlist>::iterator p = pending_metadata.begin();
1363        p != pending_metadata.end();
1364        ++p)
1365     t->put(OSD_METADATA_PREFIX, stringify(p->first), p->second);
1366   for (set<int>::iterator p = pending_metadata_rm.begin();
1367        p != pending_metadata_rm.end();
1368        ++p)
1369     t->erase(OSD_METADATA_PREFIX, stringify(*p));
1370   pending_metadata.clear();
1371   pending_metadata_rm.clear();
1372
1373   // and pg creating, also!
1374   if (mon->monmap->get_required_features().contains_all(
1375         ceph::features::mon::FEATURE_LUMINOUS)) {
1376     auto pending_creatings = update_pending_pgs(pending_inc);
1377     if (osdmap.get_epoch() &&
1378         osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
1379       dout(7) << __func__ << " in the middle of upgrading, "
1380               << " trimming pending creating_pgs using pgmap" << dendl;
1381       mon->pgservice->maybe_trim_creating_pgs(&pending_creatings);
1382     }
1383     bufferlist creatings_bl;
1384     ::encode(pending_creatings, creatings_bl);
1385     t->put(OSD_PG_CREATING_PREFIX, "creating", creatings_bl);
1386   }
1387
1388   // health
1389   health_check_map_t next;
1390   tmp.check_health(&next);
1391   encode_health(next, t);
1392 }
1393
1394 void OSDMonitor::trim_creating_pgs(creating_pgs_t* creating_pgs,
1395                                    const ceph::unordered_map<pg_t,pg_stat_t>& pg_stat)
1396 {
1397   auto p = creating_pgs->pgs.begin();
1398   while (p != creating_pgs->pgs.end()) {
1399     auto q = pg_stat.find(p->first);
1400     if (q != pg_stat.end() &&
1401         !(q->second.state & PG_STATE_CREATING)) {
1402       dout(20) << __func__ << " pgmap shows " << p->first << " is created"
1403                << dendl;
1404       p = creating_pgs->pgs.erase(p);
1405     } else {
1406       ++p;
1407     }
1408   }
1409 }
1410
1411 int OSDMonitor::load_metadata(int osd, map<string, string>& m, ostream *err)
1412 {
1413   bufferlist bl;
1414   int r = mon->store->get(OSD_METADATA_PREFIX, stringify(osd), bl);
1415   if (r < 0)
1416     return r;
1417   try {
1418     bufferlist::iterator p = bl.begin();
1419     ::decode(m, p);
1420   }
1421   catch (buffer::error& e) {
1422     if (err)
1423       *err << "osd." << osd << " metadata is corrupt";
1424     return -EIO;
1425   }
1426   return 0;
1427 }
1428
1429 void OSDMonitor::count_metadata(const string& field, map<string,int> *out)
1430 {
1431   for (int osd = 0; osd < osdmap.get_max_osd(); ++osd) {
1432     if (osdmap.is_up(osd)) {
1433       map<string,string> meta;
1434       load_metadata(osd, meta, nullptr);
1435       auto p = meta.find(field);
1436       if (p == meta.end()) {
1437         (*out)["unknown"]++;
1438       } else {
1439         (*out)[p->second]++;
1440       }
1441     }
1442   }
1443 }
1444
1445 void OSDMonitor::count_metadata(const string& field, Formatter *f)
1446 {
1447   map<string,int> by_val;
1448   count_metadata(field, &by_val);
1449   f->open_object_section(field.c_str());
1450   for (auto& p : by_val) {
1451     f->dump_int(p.first.c_str(), p.second);
1452   }
1453   f->close_section();
1454 }
1455
1456 int OSDMonitor::get_osd_objectstore_type(int osd, string *type)
1457 {
1458   map<string, string> metadata;
1459   int r = load_metadata(osd, metadata, nullptr);
1460   if (r < 0)
1461     return r;
1462
1463   auto it = metadata.find("osd_objectstore");
1464   if (it == metadata.end())
1465     return -ENOENT;
1466   *type = it->second;
1467   return 0;
1468 }
1469
1470 bool OSDMonitor::is_pool_currently_all_bluestore(int64_t pool_id,
1471                                                  const pg_pool_t &pool,
1472                                                  ostream *err)
1473 {
1474   // just check a few pgs for efficiency - this can't give a guarantee anyway,
1475   // since filestore osds could always join the pool later
1476   set<int> checked_osds;
1477   for (unsigned ps = 0; ps < MIN(8, pool.get_pg_num()); ++ps) {
1478     vector<int> up, acting;
1479     pg_t pgid(ps, pool_id, -1);
1480     osdmap.pg_to_up_acting_osds(pgid, up, acting);
1481     for (int osd : up) {
1482       if (checked_osds.find(osd) != checked_osds.end())
1483         continue;
1484       string objectstore_type;
1485       int r = get_osd_objectstore_type(osd, &objectstore_type);
1486       // allow with missing metadata, e.g. due to an osd never booting yet
1487       if (r < 0 || objectstore_type == "bluestore") {
1488         checked_osds.insert(osd);
1489         continue;
1490       }
1491       *err << "osd." << osd << " uses " << objectstore_type;
1492       return false;
1493     }
1494   }
1495   return true;
1496 }
1497
1498 int OSDMonitor::dump_osd_metadata(int osd, Formatter *f, ostream *err)
1499 {
1500   map<string,string> m;
1501   if (int r = load_metadata(osd, m, err))
1502     return r;
1503   for (map<string,string>::iterator p = m.begin(); p != m.end(); ++p)
1504     f->dump_string(p->first.c_str(), p->second);
1505   return 0;
1506 }
1507
1508 void OSDMonitor::print_nodes(Formatter *f)
1509 {
1510   // group OSDs by their hosts
1511   map<string, list<int> > osds; // hostname => osd
1512   for (int osd = 0; osd < osdmap.get_max_osd(); osd++) {
1513     map<string, string> m;
1514     if (load_metadata(osd, m, NULL)) {
1515       continue;
1516     }
1517     map<string, string>::iterator hostname = m.find("hostname");
1518     if (hostname == m.end()) {
1519       // not likely though
1520       continue;
1521     }
1522     osds[hostname->second].push_back(osd);
1523   }
1524
1525   dump_services(f, osds, "osd");
1526 }
1527
1528 void OSDMonitor::share_map_with_random_osd()
1529 {
1530   if (osdmap.get_num_up_osds() == 0) {
1531     dout(10) << __func__ << " no up osds, don't share with anyone" << dendl;
1532     return;
1533   }
1534
1535   MonSession *s = mon->session_map.get_random_osd_session(&osdmap);
1536   if (!s) {
1537     dout(10) << __func__ << " no up osd on our session map" << dendl;
1538     return;
1539   }
1540
1541   dout(10) << "committed, telling random " << s->inst << " all about it" << dendl;
1542   // whatev, they'll request more if they need it
1543   MOSDMap *m = build_incremental(osdmap.get_epoch() - 1, osdmap.get_epoch());
1544   s->con->send_message(m);
1545   // NOTE: do *not* record osd has up to this epoch (as we do
1546   // elsewhere) as they may still need to request older values.
1547 }
1548
1549 version_t OSDMonitor::get_trim_to()
1550 {
1551   if (mon->get_quorum().empty()) {
1552     dout(10) << __func__ << ": quorum not formed" << dendl;
1553     return 0;
1554   }
1555
1556   epoch_t floor;
1557   if (mon->monmap->get_required_features().contains_all(
1558         ceph::features::mon::FEATURE_LUMINOUS)) {
1559     {
1560       // TODO: Get this hidden in PGStatService
1561       std::lock_guard<std::mutex> l(creating_pgs_lock);
1562       if (!creating_pgs.pgs.empty()) {
1563         return 0;
1564       }
1565     }
1566     floor = get_min_last_epoch_clean();
1567   } else {
1568     if (!mon->pgservice->is_readable())
1569       return 0;
1570     if (mon->pgservice->have_creating_pgs()) {
1571       return 0;
1572     }
1573     floor = mon->pgservice->get_min_last_epoch_clean();
1574   }
1575   {
1576     dout(10) << " min_last_epoch_clean " << floor << dendl;
1577     if (g_conf->mon_osd_force_trim_to > 0 &&
1578         g_conf->mon_osd_force_trim_to < (int)get_last_committed()) {
1579       floor = g_conf->mon_osd_force_trim_to;
1580       dout(10) << " explicit mon_osd_force_trim_to = " << floor << dendl;
1581     }
1582     unsigned min = g_conf->mon_min_osdmap_epochs;
1583     if (floor + min > get_last_committed()) {
1584       if (min < get_last_committed())
1585         floor = get_last_committed() - min;
1586       else
1587         floor = 0;
1588     }
1589     if (floor > get_first_committed())
1590       return floor;
1591   }
1592   return 0;
1593 }
1594
1595 epoch_t OSDMonitor::get_min_last_epoch_clean() const
1596 {
1597   auto floor = last_epoch_clean.get_lower_bound(osdmap);
1598   // also scan osd epochs
1599   // don't trim past the oldest reported osd epoch
1600   for (auto& osd_epoch : osd_epochs) {
1601     if (osd_epoch.second < floor) {
1602       floor = osd_epoch.second;
1603     }
1604   }
1605   return floor;
1606 }
1607
1608 void OSDMonitor::encode_trim_extra(MonitorDBStore::TransactionRef tx,
1609                                    version_t first)
1610 {
1611   dout(10) << __func__ << " including full map for e " << first << dendl;
1612   bufferlist bl;
1613   get_version_full(first, bl);
1614   put_version_full(tx, first, bl);
1615 }
1616
1617 // -------------
1618
1619 bool OSDMonitor::preprocess_query(MonOpRequestRef op)
1620 {
1621   op->mark_osdmon_event(__func__);
1622   Message *m = op->get_req();
1623   dout(10) << "preprocess_query " << *m << " from " << m->get_orig_source_inst() << dendl;
1624
1625   switch (m->get_type()) {
1626     // READs
1627   case MSG_MON_COMMAND:
1628     return preprocess_command(op);
1629   case CEPH_MSG_MON_GET_OSDMAP:
1630     return preprocess_get_osdmap(op);
1631
1632     // damp updates
1633   case MSG_OSD_MARK_ME_DOWN:
1634     return preprocess_mark_me_down(op);
1635   case MSG_OSD_FULL:
1636     return preprocess_full(op);
1637   case MSG_OSD_FAILURE:
1638     return preprocess_failure(op);
1639   case MSG_OSD_BOOT:
1640     return preprocess_boot(op);
1641   case MSG_OSD_ALIVE:
1642     return preprocess_alive(op);
1643   case MSG_OSD_PG_CREATED:
1644     return preprocess_pg_created(op);
1645   case MSG_OSD_PGTEMP:
1646     return preprocess_pgtemp(op);
1647   case MSG_OSD_BEACON:
1648     return preprocess_beacon(op);
1649
1650   case CEPH_MSG_POOLOP:
1651     return preprocess_pool_op(op);
1652
1653   case MSG_REMOVE_SNAPS:
1654     return preprocess_remove_snaps(op);
1655
1656   default:
1657     ceph_abort();
1658     return true;
1659   }
1660 }
1661
1662 bool OSDMonitor::prepare_update(MonOpRequestRef op)
1663 {
1664   op->mark_osdmon_event(__func__);
1665   Message *m = op->get_req();
1666   dout(7) << "prepare_update " << *m << " from " << m->get_orig_source_inst() << dendl;
1667
1668   switch (m->get_type()) {
1669     // damp updates
1670   case MSG_OSD_MARK_ME_DOWN:
1671     return prepare_mark_me_down(op);
1672   case MSG_OSD_FULL:
1673     return prepare_full(op);
1674   case MSG_OSD_FAILURE:
1675     return prepare_failure(op);
1676   case MSG_OSD_BOOT:
1677     return prepare_boot(op);
1678   case MSG_OSD_ALIVE:
1679     return prepare_alive(op);
1680   case MSG_OSD_PG_CREATED:
1681     return prepare_pg_created(op);
1682   case MSG_OSD_PGTEMP:
1683     return prepare_pgtemp(op);
1684   case MSG_OSD_BEACON:
1685     return prepare_beacon(op);
1686
1687   case MSG_MON_COMMAND:
1688     return prepare_command(op);
1689
1690   case CEPH_MSG_POOLOP:
1691     return prepare_pool_op(op);
1692
1693   case MSG_REMOVE_SNAPS:
1694     return prepare_remove_snaps(op);
1695
1696
1697   default:
1698     ceph_abort();
1699   }
1700
1701   return false;
1702 }
1703
1704 bool OSDMonitor::should_propose(double& delay)
1705 {
1706   dout(10) << "should_propose" << dendl;
1707
1708   // if full map, propose immediately!  any subsequent changes will be clobbered.
1709   if (pending_inc.fullmap.length())
1710     return true;
1711
1712   // adjust osd weights?
1713   if (!osd_weight.empty() &&
1714       osd_weight.size() == (unsigned)osdmap.get_max_osd()) {
1715     dout(0) << " adjusting osd weights based on " << osd_weight << dendl;
1716     osdmap.adjust_osd_weights(osd_weight, pending_inc);
1717     delay = 0.0;
1718     osd_weight.clear();
1719     return true;
1720   }
1721
1722   // propose as fast as possible if updating up_thru or pg_temp
1723   // want to merge OSDMap changes as much as possible
1724   if ((pending_inc.new_primary_temp.size() == 1
1725       || pending_inc.new_up_thru.size() == 1)
1726       && pending_inc.new_state.size() < 2) {
1727     dout(15) << " propose as fast as possible for up_thru/pg_temp" << dendl;
1728
1729     utime_t now = ceph_clock_now();
1730     if (now - last_attempted_minwait_time > g_conf->paxos_propose_interval
1731         && now - paxos->get_last_commit_time() > g_conf->paxos_min_wait) {
1732       delay = g_conf->paxos_min_wait;
1733       last_attempted_minwait_time = now;
1734       return true;
1735     }
1736   }
1737
1738   return PaxosService::should_propose(delay);
1739 }
1740
1741
1742
1743 // ---------------------------
1744 // READs
1745
1746 bool OSDMonitor::preprocess_get_osdmap(MonOpRequestRef op)
1747 {
1748   op->mark_osdmon_event(__func__);
1749   MMonGetOSDMap *m = static_cast<MMonGetOSDMap*>(op->get_req());
1750   dout(10) << __func__ << " " << *m << dendl;
1751   MOSDMap *reply = new MOSDMap(mon->monmap->fsid);
1752   epoch_t first = get_first_committed();
1753   epoch_t last = osdmap.get_epoch();
1754   int max = g_conf->osd_map_message_max;
1755   for (epoch_t e = MAX(first, m->get_full_first());
1756        e <= MIN(last, m->get_full_last()) && max > 0;
1757        ++e, --max) {
1758     int r = get_version_full(e, reply->maps[e]);
1759     assert(r >= 0);
1760   }
1761   for (epoch_t e = MAX(first, m->get_inc_first());
1762        e <= MIN(last, m->get_inc_last()) && max > 0;
1763        ++e, --max) {
1764     int r = get_version(e, reply->incremental_maps[e]);
1765     assert(r >= 0);
1766   }
1767   reply->oldest_map = first;
1768   reply->newest_map = last;
1769   mon->send_reply(op, reply);
1770   return true;
1771 }
1772
1773
1774 // ---------------------------
1775 // UPDATEs
1776
1777 // failure --
1778
1779 bool OSDMonitor::check_source(PaxosServiceMessage *m, uuid_d fsid) {
1780   // check permissions
1781   MonSession *session = m->get_session();
1782   if (!session)
1783     return true;
1784   if (!session->is_capable("osd", MON_CAP_X)) {
1785     dout(0) << "got MOSDFailure from entity with insufficient caps "
1786             << session->caps << dendl;
1787     return true;
1788   }
1789   if (fsid != mon->monmap->fsid) {
1790     dout(0) << "check_source: on fsid " << fsid
1791             << " != " << mon->monmap->fsid << dendl;
1792     return true;
1793   }
1794   return false;
1795 }
1796
1797
1798 bool OSDMonitor::preprocess_failure(MonOpRequestRef op)
1799 {
1800   op->mark_osdmon_event(__func__);
1801   MOSDFailure *m = static_cast<MOSDFailure*>(op->get_req());
1802   // who is target_osd
1803   int badboy = m->get_target().name.num();
1804
1805   // check permissions
1806   if (check_source(m, m->fsid))
1807     goto didit;
1808
1809   // first, verify the reporting host is valid
1810   if (m->get_orig_source().is_osd()) {
1811     int from = m->get_orig_source().num();
1812     if (!osdmap.exists(from) ||
1813         osdmap.get_addr(from) != m->get_orig_source_inst().addr ||
1814         (osdmap.is_down(from) && m->if_osd_failed())) {
1815       dout(5) << "preprocess_failure from dead osd." << from << ", ignoring" << dendl;
1816       send_incremental(op, m->get_epoch()+1);
1817       goto didit;
1818     }
1819   }
1820
1821
1822   // weird?
1823   if (osdmap.is_down(badboy)) {
1824     dout(5) << "preprocess_failure dne(/dup?): " << m->get_target() << ", from " << m->get_orig_source_inst() << dendl;
1825     if (m->get_epoch() < osdmap.get_epoch())
1826       send_incremental(op, m->get_epoch()+1);
1827     goto didit;
1828   }
1829   if (osdmap.get_inst(badboy) != m->get_target()) {
1830     dout(5) << "preprocess_failure wrong osd: report " << m->get_target() << " != map's " << osdmap.get_inst(badboy)
1831             << ", from " << m->get_orig_source_inst() << dendl;
1832     if (m->get_epoch() < osdmap.get_epoch())
1833       send_incremental(op, m->get_epoch()+1);
1834     goto didit;
1835   }
1836
1837   // already reported?
1838   if (osdmap.is_down(badboy) ||
1839       osdmap.get_up_from(badboy) > m->get_epoch()) {
1840     dout(5) << "preprocess_failure dup/old: " << m->get_target() << ", from " << m->get_orig_source_inst() << dendl;
1841     if (m->get_epoch() < osdmap.get_epoch())
1842       send_incremental(op, m->get_epoch()+1);
1843     goto didit;
1844   }
1845
1846   if (!can_mark_down(badboy)) {
1847     dout(5) << "preprocess_failure ignoring report of " << m->get_target() << " from " << m->get_orig_source_inst() << dendl;
1848     goto didit;
1849   }
1850
1851   dout(10) << "preprocess_failure new: " << m->get_target() << ", from " << m->get_orig_source_inst() << dendl;
1852   return false;
1853
1854  didit:
1855   return true;
1856 }
1857
1858 class C_AckMarkedDown : public C_MonOp {
1859   OSDMonitor *osdmon;
1860 public:
1861   C_AckMarkedDown(
1862     OSDMonitor *osdmon,
1863     MonOpRequestRef op)
1864     : C_MonOp(op), osdmon(osdmon) {}
1865
1866   void _finish(int) override {
1867     MOSDMarkMeDown *m = static_cast<MOSDMarkMeDown*>(op->get_req());
1868     osdmon->mon->send_reply(
1869       op,
1870       new MOSDMarkMeDown(
1871         m->fsid,
1872         m->get_target(),
1873         m->get_epoch(),
1874         false));   // ACK itself does not request an ack
1875   }
1876   ~C_AckMarkedDown() override {
1877   }
1878 };
1879
1880 bool OSDMonitor::preprocess_mark_me_down(MonOpRequestRef op)
1881 {
1882   op->mark_osdmon_event(__func__);
1883   MOSDMarkMeDown *m = static_cast<MOSDMarkMeDown*>(op->get_req());
1884   int requesting_down = m->get_target().name.num();
1885   int from = m->get_orig_source().num();
1886
1887   // check permissions
1888   if (check_source(m, m->fsid))
1889     goto reply;
1890
1891   // first, verify the reporting host is valid
1892   if (!m->get_orig_source().is_osd())
1893     goto reply;
1894
1895   if (!osdmap.exists(from) ||
1896       osdmap.is_down(from) ||
1897       osdmap.get_addr(from) != m->get_target().addr) {
1898     dout(5) << "preprocess_mark_me_down from dead osd."
1899             << from << ", ignoring" << dendl;
1900     send_incremental(op, m->get_epoch()+1);
1901     goto reply;
1902   }
1903
1904   // no down might be set
1905   if (!can_mark_down(requesting_down))
1906     goto reply;
1907
1908   dout(10) << "MOSDMarkMeDown for: " << m->get_target() << dendl;
1909   return false;
1910
1911  reply:
1912   if (m->request_ack) {
1913     Context *c(new C_AckMarkedDown(this, op));
1914     c->complete(0);
1915   }
1916   return true;
1917 }
1918
1919 bool OSDMonitor::prepare_mark_me_down(MonOpRequestRef op)
1920 {
1921   op->mark_osdmon_event(__func__);
1922   MOSDMarkMeDown *m = static_cast<MOSDMarkMeDown*>(op->get_req());
1923   int target_osd = m->get_target().name.num();
1924
1925   assert(osdmap.is_up(target_osd));
1926   assert(osdmap.get_addr(target_osd) == m->get_target().addr);
1927
1928   mon->clog->info() << "osd." << target_osd << " marked itself down";
1929   pending_inc.new_state[target_osd] = CEPH_OSD_UP;
1930   if (m->request_ack)
1931     wait_for_finished_proposal(op, new C_AckMarkedDown(this, op));
1932   return true;
1933 }
1934
1935 bool OSDMonitor::can_mark_down(int i)
1936 {
1937   if (osdmap.test_flag(CEPH_OSDMAP_NODOWN)) {
1938     dout(5) << __func__ << " NODOWN flag set, will not mark osd." << i
1939             << " down" << dendl;
1940     return false;
1941   }
1942
1943   if (osdmap.is_nodown(i)) {
1944     dout(5) << __func__ << " osd." << i << " is marked as nodown, "
1945             << "will not mark it down" << dendl;
1946     return false;
1947   }
1948
1949   int num_osds = osdmap.get_num_osds();
1950   if (num_osds == 0) {
1951     dout(5) << __func__ << " no osds" << dendl;
1952     return false;
1953   }
1954   int up = osdmap.get_num_up_osds() - pending_inc.get_net_marked_down(&osdmap);
1955   float up_ratio = (float)up / (float)num_osds;
1956   if (up_ratio < g_conf->mon_osd_min_up_ratio) {
1957     dout(2) << __func__ << " current up_ratio " << up_ratio << " < min "
1958             << g_conf->mon_osd_min_up_ratio
1959             << ", will not mark osd." << i << " down" << dendl;
1960     return false;
1961   }
1962   return true;
1963 }
1964
1965 bool OSDMonitor::can_mark_up(int i)
1966 {
1967   if (osdmap.test_flag(CEPH_OSDMAP_NOUP)) {
1968     dout(5) << __func__ << " NOUP flag set, will not mark osd." << i
1969             << " up" << dendl;
1970     return false;
1971   }
1972
1973   if (osdmap.is_noup(i)) {
1974     dout(5) << __func__ << " osd." << i << " is marked as noup, "
1975             << "will not mark it up" << dendl;
1976     return false;
1977   }
1978
1979   return true;
1980 }
1981
1982 /**
1983  * @note the parameter @p i apparently only exists here so we can output the
1984  *       osd's id on messages.
1985  */
1986 bool OSDMonitor::can_mark_out(int i)
1987 {
1988   if (osdmap.test_flag(CEPH_OSDMAP_NOOUT)) {
1989     dout(5) << __func__ << " NOOUT flag set, will not mark osds out" << dendl;
1990     return false;
1991   }
1992
1993   if (osdmap.is_noout(i)) {
1994     dout(5) << __func__ << " osd." << i << " is marked as noout, "
1995             << "will not mark it out" << dendl;
1996     return false;
1997   }
1998
1999   int num_osds = osdmap.get_num_osds();
2000   if (num_osds == 0) {
2001     dout(5) << __func__ << " no osds" << dendl;
2002     return false;
2003   }
2004   int in = osdmap.get_num_in_osds() - pending_inc.get_net_marked_out(&osdmap);
2005   float in_ratio = (float)in / (float)num_osds;
2006   if (in_ratio < g_conf->mon_osd_min_in_ratio) {
2007     if (i >= 0)
2008       dout(5) << __func__ << " current in_ratio " << in_ratio << " < min "
2009               << g_conf->mon_osd_min_in_ratio
2010               << ", will not mark osd." << i << " out" << dendl;
2011     else
2012       dout(5) << __func__ << " current in_ratio " << in_ratio << " < min "
2013               << g_conf->mon_osd_min_in_ratio
2014               << ", will not mark osds out" << dendl;
2015     return false;
2016   }
2017
2018   return true;
2019 }
2020
2021 bool OSDMonitor::can_mark_in(int i)
2022 {
2023   if (osdmap.test_flag(CEPH_OSDMAP_NOIN)) {
2024     dout(5) << __func__ << " NOIN flag set, will not mark osd." << i
2025             << " in" << dendl;
2026     return false;
2027   }
2028
2029   if (osdmap.is_noin(i)) {
2030     dout(5) << __func__ << " osd." << i << " is marked as noin, "
2031             << "will not mark it in" << dendl;
2032     return false;
2033   }
2034
2035   return true;
2036 }
2037
2038 bool OSDMonitor::check_failures(utime_t now)
2039 {
2040   bool found_failure = false;
2041   for (map<int,failure_info_t>::iterator p = failure_info.begin();
2042        p != failure_info.end();
2043        ++p) {
2044     if (can_mark_down(p->first)) {
2045       found_failure |= check_failure(now, p->first, p->second);
2046     }
2047   }
2048   return found_failure;
2049 }
2050
2051 bool OSDMonitor::check_failure(utime_t now, int target_osd, failure_info_t& fi)
2052 {
2053   // already pending failure?
2054   if (pending_inc.new_state.count(target_osd) &&
2055       pending_inc.new_state[target_osd] & CEPH_OSD_UP) {
2056     dout(10) << " already pending failure" << dendl;
2057     return true;
2058   }
2059
2060   set<string> reporters_by_subtree;
2061   string reporter_subtree_level = g_conf->mon_osd_reporter_subtree_level;
2062   utime_t orig_grace(g_conf->osd_heartbeat_grace, 0);
2063   utime_t max_failed_since = fi.get_failed_since();
2064   utime_t failed_for = now - max_failed_since;
2065
2066   utime_t grace = orig_grace;
2067   double my_grace = 0, peer_grace = 0;
2068   double decay_k = 0;
2069   if (g_conf->mon_osd_adjust_heartbeat_grace) {
2070     double halflife = (double)g_conf->mon_osd_laggy_halflife;
2071     decay_k = ::log(.5) / halflife;
2072
2073     // scale grace period based on historical probability of 'lagginess'
2074     // (false positive failures due to slowness).
2075     const osd_xinfo_t& xi = osdmap.get_xinfo(target_osd);
2076     double decay = exp((double)failed_for * decay_k);
2077     dout(20) << " halflife " << halflife << " decay_k " << decay_k
2078              << " failed_for " << failed_for << " decay " << decay << dendl;
2079     my_grace = decay * (double)xi.laggy_interval * xi.laggy_probability;
2080     grace += my_grace;
2081   }
2082
2083   // consider the peers reporting a failure a proxy for a potential
2084   // 'subcluster' over the overall cluster that is similarly
2085   // laggy.  this is clearly not true in all cases, but will sometimes
2086   // help us localize the grace correction to a subset of the system
2087   // (say, a rack with a bad switch) that is unhappy.
2088   assert(fi.reporters.size());
2089   for (map<int,failure_reporter_t>::iterator p = fi.reporters.begin();
2090         p != fi.reporters.end();
2091         ++p) {
2092     // get the parent bucket whose type matches with "reporter_subtree_level".
2093     // fall back to OSD if the level doesn't exist.
2094     map<string, string> reporter_loc = osdmap.crush->get_full_location(p->first);
2095     map<string, string>::iterator iter = reporter_loc.find(reporter_subtree_level);
2096     if (iter == reporter_loc.end()) {
2097       reporters_by_subtree.insert("osd." + to_string(p->first));
2098     } else {
2099       reporters_by_subtree.insert(iter->second);
2100     }
2101     if (g_conf->mon_osd_adjust_heartbeat_grace) {
2102       const osd_xinfo_t& xi = osdmap.get_xinfo(p->first);
2103       utime_t elapsed = now - xi.down_stamp;
2104       double decay = exp((double)elapsed * decay_k);
2105       peer_grace += decay * (double)xi.laggy_interval * xi.laggy_probability;
2106     }
2107   }
2108   
2109   if (g_conf->mon_osd_adjust_heartbeat_grace) {
2110     peer_grace /= (double)fi.reporters.size();
2111     grace += peer_grace;
2112   }
2113
2114   dout(10) << " osd." << target_osd << " has "
2115            << fi.reporters.size() << " reporters, "
2116            << grace << " grace (" << orig_grace << " + " << my_grace
2117            << " + " << peer_grace << "), max_failed_since " << max_failed_since
2118            << dendl;
2119
2120   if (failed_for >= grace &&
2121       (int)reporters_by_subtree.size() >= g_conf->mon_osd_min_down_reporters) {
2122     dout(1) << " we have enough reporters to mark osd." << target_osd
2123             << " down" << dendl;
2124     pending_inc.new_state[target_osd] = CEPH_OSD_UP;
2125
2126     mon->clog->info() << "osd." << target_osd << " failed ("
2127                       << osdmap.crush->get_full_location_ordered_string(
2128                         target_osd)
2129                       << ") ("
2130                       << (int)reporters_by_subtree.size()
2131                       << " reporters from different "
2132                       << reporter_subtree_level << " after "
2133                       << failed_for << " >= grace " << grace << ")";
2134     return true;
2135   }
2136   return false;
2137 }
2138
2139 void OSDMonitor::force_failure(int target_osd, int by)
2140 {
2141   // already pending failure?
2142   if (pending_inc.new_state.count(target_osd) &&
2143       pending_inc.new_state[target_osd] & CEPH_OSD_UP) {
2144     dout(10) << " already pending failure" << dendl;
2145     return;
2146   }
2147
2148   dout(1) << " we're forcing failure of osd." << target_osd << dendl;
2149   pending_inc.new_state[target_osd] = CEPH_OSD_UP;
2150
2151   mon->clog->info() << "osd." << target_osd << " failed ("
2152                     << osdmap.crush->get_full_location_ordered_string(target_osd)
2153                     << ") (connection refused reported by osd." << by << ")";
2154   return;
2155 }
2156
2157 bool OSDMonitor::prepare_failure(MonOpRequestRef op)
2158 {
2159   op->mark_osdmon_event(__func__);
2160   MOSDFailure *m = static_cast<MOSDFailure*>(op->get_req());
2161   dout(1) << "prepare_failure " << m->get_target()
2162           << " from " << m->get_orig_source_inst()
2163           << " is reporting failure:" << m->if_osd_failed() << dendl;
2164
2165   int target_osd = m->get_target().name.num();
2166   int reporter = m->get_orig_source().num();
2167   assert(osdmap.is_up(target_osd));
2168   assert(osdmap.get_addr(target_osd) == m->get_target().addr);
2169
2170   if (m->if_osd_failed()) {
2171     // calculate failure time
2172     utime_t now = ceph_clock_now();
2173     utime_t failed_since =
2174       m->get_recv_stamp() - utime_t(m->failed_for, 0);
2175
2176     // add a report
2177     if (m->is_immediate()) {
2178       mon->clog->debug() << m->get_target() << " reported immediately failed by "
2179             << m->get_orig_source_inst();
2180       force_failure(target_osd, reporter);
2181       return true;
2182     }
2183     mon->clog->debug() << m->get_target() << " reported failed by "
2184                       << m->get_orig_source_inst();
2185
2186     failure_info_t& fi = failure_info[target_osd];
2187     MonOpRequestRef old_op = fi.add_report(reporter, failed_since, op);
2188     if (old_op) {
2189       mon->no_reply(old_op);
2190     }
2191
2192     return check_failure(now, target_osd, fi);
2193   } else {
2194     // remove the report
2195     mon->clog->debug() << m->get_target() << " failure report canceled by "
2196                        << m->get_orig_source_inst();
2197     if (failure_info.count(target_osd)) {
2198       failure_info_t& fi = failure_info[target_osd];
2199       MonOpRequestRef report_op = fi.cancel_report(reporter);
2200       if (report_op) {
2201         mon->no_reply(report_op);
2202       }
2203       if (fi.reporters.empty()) {
2204         dout(10) << " removing last failure_info for osd." << target_osd
2205                  << dendl;
2206         failure_info.erase(target_osd);
2207       } else {
2208         dout(10) << " failure_info for osd." << target_osd << " now "
2209                  << fi.reporters.size() << " reporters" << dendl;
2210       }
2211     } else {
2212       dout(10) << " no failure_info for osd." << target_osd << dendl;
2213     }
2214     mon->no_reply(op);
2215   }
2216
2217   return false;
2218 }
2219
2220 void OSDMonitor::process_failures()
2221 {
2222   map<int,failure_info_t>::iterator p = failure_info.begin();
2223   while (p != failure_info.end()) {
2224     if (osdmap.is_up(p->first)) {
2225       ++p;
2226     } else {
2227       dout(10) << "process_failures osd." << p->first << dendl;
2228       list<MonOpRequestRef> ls;
2229       p->second.take_report_messages(ls);
2230       failure_info.erase(p++);
2231
2232       while (!ls.empty()) {
2233         MonOpRequestRef o = ls.front();
2234         if (o) {
2235           o->mark_event(__func__);
2236           MOSDFailure *m = o->get_req<MOSDFailure>();
2237           send_latest(o, m->get_epoch());
2238         }
2239         ls.pop_front();
2240       }
2241     }
2242   }
2243 }
2244
2245 void OSDMonitor::take_all_failures(list<MonOpRequestRef>& ls)
2246 {
2247   dout(10) << __func__ << " on " << failure_info.size() << " osds" << dendl;
2248
2249   for (map<int,failure_info_t>::iterator p = failure_info.begin();
2250        p != failure_info.end();
2251        ++p) {
2252     p->second.take_report_messages(ls);
2253   }
2254   failure_info.clear();
2255 }
2256
2257
2258 // boot --
2259
2260 bool OSDMonitor::preprocess_boot(MonOpRequestRef op)
2261 {
2262   op->mark_osdmon_event(__func__);
2263   MOSDBoot *m = static_cast<MOSDBoot*>(op->get_req());
2264   int from = m->get_orig_source_inst().name.num();
2265
2266   // check permissions, ignore if failed (no response expected)
2267   MonSession *session = m->get_session();
2268   if (!session)
2269     goto ignore;
2270   if (!session->is_capable("osd", MON_CAP_X)) {
2271     dout(0) << "got preprocess_boot message from entity with insufficient caps"
2272             << session->caps << dendl;
2273     goto ignore;
2274   }
2275
2276   if (m->sb.cluster_fsid != mon->monmap->fsid) {
2277     dout(0) << "preprocess_boot on fsid " << m->sb.cluster_fsid
2278             << " != " << mon->monmap->fsid << dendl;
2279     goto ignore;
2280   }
2281
2282   if (m->get_orig_source_inst().addr.is_blank_ip()) {
2283     dout(0) << "preprocess_boot got blank addr for " << m->get_orig_source_inst() << dendl;
2284     goto ignore;
2285   }
2286
2287   assert(m->get_orig_source_inst().name.is_osd());
2288
2289   // check if osd has required features to boot
2290   if ((osdmap.get_features(CEPH_ENTITY_TYPE_OSD, NULL) &
2291        CEPH_FEATURE_OSD_ERASURE_CODES) &&
2292       !(m->get_connection()->get_features() & CEPH_FEATURE_OSD_ERASURE_CODES)) {
2293     dout(0) << __func__ << " osdmap requires erasure code but osd at "
2294             << m->get_orig_source_inst()
2295             << " doesn't announce support -- ignore" << dendl;
2296     goto ignore;
2297   }
2298
2299   if ((osdmap.get_features(CEPH_ENTITY_TYPE_OSD, NULL) &
2300        CEPH_FEATURE_ERASURE_CODE_PLUGINS_V2) &&
2301       !(m->get_connection()->get_features() & CEPH_FEATURE_ERASURE_CODE_PLUGINS_V2)) {
2302     dout(0) << __func__ << " osdmap requires erasure code plugins v2 but osd at "
2303             << m->get_orig_source_inst()
2304             << " doesn't announce support -- ignore" << dendl;
2305     goto ignore;
2306   }
2307
2308   if ((osdmap.get_features(CEPH_ENTITY_TYPE_OSD, NULL) &
2309        CEPH_FEATURE_ERASURE_CODE_PLUGINS_V3) &&
2310       !(m->get_connection()->get_features() & CEPH_FEATURE_ERASURE_CODE_PLUGINS_V3)) {
2311     dout(0) << __func__ << " osdmap requires erasure code plugins v3 but osd at "
2312             << m->get_orig_source_inst()
2313             << " doesn't announce support -- ignore" << dendl;
2314     goto ignore;
2315   }
2316
2317   if (osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS &&
2318       !HAVE_FEATURE(m->osd_features, SERVER_LUMINOUS)) {
2319     mon->clog->info() << "disallowing boot of OSD "
2320                       << m->get_orig_source_inst()
2321                       << " because the osdmap requires"
2322                       << " CEPH_FEATURE_SERVER_LUMINOUS"
2323                       << " but the osd lacks CEPH_FEATURE_SERVER_LUMINOUS";
2324     goto ignore;
2325   }
2326
2327   if (osdmap.require_osd_release >= CEPH_RELEASE_JEWEL &&
2328       !(m->osd_features & CEPH_FEATURE_SERVER_JEWEL)) {
2329     mon->clog->info() << "disallowing boot of OSD "
2330                       << m->get_orig_source_inst()
2331                       << " because the osdmap requires"
2332                       << " CEPH_FEATURE_SERVER_JEWEL"
2333                       << " but the osd lacks CEPH_FEATURE_SERVER_JEWEL";
2334     goto ignore;
2335   }
2336
2337   if (osdmap.require_osd_release >= CEPH_RELEASE_KRAKEN &&
2338       !HAVE_FEATURE(m->osd_features, SERVER_KRAKEN)) {
2339     mon->clog->info() << "disallowing boot of OSD "
2340                       << m->get_orig_source_inst()
2341                       << " because the osdmap requires"
2342                       << " CEPH_FEATURE_SERVER_KRAKEN"
2343                       << " but the osd lacks CEPH_FEATURE_SERVER_KRAKEN";
2344     goto ignore;
2345   }
2346
2347   if (osdmap.test_flag(CEPH_OSDMAP_SORTBITWISE) &&
2348       !(m->osd_features & CEPH_FEATURE_OSD_BITWISE_HOBJ_SORT)) {
2349     mon->clog->info() << "disallowing boot of OSD "
2350                       << m->get_orig_source_inst()
2351                       << " because 'sortbitwise' osdmap flag is set and OSD lacks the OSD_BITWISE_HOBJ_SORT feature";
2352     goto ignore;
2353   }
2354
2355   if (osdmap.test_flag(CEPH_OSDMAP_RECOVERY_DELETES) &&
2356       !(m->osd_features & CEPH_FEATURE_OSD_RECOVERY_DELETES)) {
2357     mon->clog->info() << "disallowing boot of OSD "
2358                       << m->get_orig_source_inst()
2359                       << " because 'recovery_deletes' osdmap flag is set and OSD lacks the OSD_RECOVERY_DELETES feature";
2360     goto ignore;
2361   }
2362
2363   if (any_of(osdmap.get_pools().begin(),
2364              osdmap.get_pools().end(),
2365              [](const std::pair<int64_t,pg_pool_t>& pool)
2366              { return pool.second.use_gmt_hitset; })) {
2367     assert(osdmap.get_num_up_osds() == 0 ||
2368            osdmap.get_up_osd_features() & CEPH_FEATURE_OSD_HITSET_GMT);
2369     if (!(m->osd_features & CEPH_FEATURE_OSD_HITSET_GMT)) {
2370       dout(0) << __func__ << " one or more pools uses GMT hitsets but osd at "
2371               << m->get_orig_source_inst()
2372               << " doesn't announce support -- ignore" << dendl;
2373       goto ignore;
2374     }
2375   }
2376
2377   // make sure upgrades stop at luminous
2378   if (HAVE_FEATURE(m->osd_features, SERVER_M) &&
2379       osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
2380     mon->clog->info() << "disallowing boot of post-luminous OSD "
2381                       << m->get_orig_source_inst()
2382                       << " because require_osd_release < luminous";
2383     goto ignore;
2384   }
2385
2386   // make sure upgrades stop at jewel
2387   if (HAVE_FEATURE(m->osd_features, SERVER_KRAKEN) &&
2388       osdmap.require_osd_release < CEPH_RELEASE_JEWEL) {
2389     mon->clog->info() << "disallowing boot of post-jewel OSD "
2390                       << m->get_orig_source_inst()
2391                       << " because require_osd_release < jewel";
2392     goto ignore;
2393   }
2394
2395   // make sure upgrades stop at hammer
2396   //  * HAMMER_0_94_4 is the required hammer feature
2397   //  * MON_METADATA is the first post-hammer feature
2398   if (osdmap.get_num_up_osds() > 0) {
2399     if ((m->osd_features & CEPH_FEATURE_MON_METADATA) &&
2400         !(osdmap.get_up_osd_features() & CEPH_FEATURE_HAMMER_0_94_4)) {
2401       mon->clog->info() << "disallowing boot of post-hammer OSD "
2402                         << m->get_orig_source_inst()
2403                         << " because one or more up OSDs is pre-hammer v0.94.4";
2404       goto ignore;
2405     }
2406     if (!(m->osd_features & CEPH_FEATURE_HAMMER_0_94_4) &&
2407         (osdmap.get_up_osd_features() & CEPH_FEATURE_MON_METADATA)) {
2408       mon->clog->info() << "disallowing boot of pre-hammer v0.94.4 OSD "
2409                         << m->get_orig_source_inst()
2410                         << " because all up OSDs are post-hammer";
2411       goto ignore;
2412     }
2413   }
2414
2415   // already booted?
2416   if (osdmap.is_up(from) &&
2417       osdmap.get_inst(from) == m->get_orig_source_inst() &&
2418       osdmap.get_cluster_addr(from) == m->cluster_addr) {
2419     // yup.
2420     dout(7) << "preprocess_boot dup from " << m->get_orig_source_inst()
2421             << " == " << osdmap.get_inst(from) << dendl;
2422     _booted(op, false);
2423     return true;
2424   }
2425
2426   if (osdmap.exists(from) &&
2427       !osdmap.get_uuid(from).is_zero() &&
2428       osdmap.get_uuid(from) != m->sb.osd_fsid) {
2429     dout(7) << __func__ << " from " << m->get_orig_source_inst()
2430             << " clashes with existing osd: different fsid"
2431             << " (ours: " << osdmap.get_uuid(from)
2432             << " ; theirs: " << m->sb.osd_fsid << ")" << dendl;
2433     goto ignore;
2434   }
2435
2436   if (osdmap.exists(from) &&
2437       osdmap.get_info(from).up_from > m->version &&
2438       osdmap.get_most_recent_inst(from) == m->get_orig_source_inst()) {
2439     dout(7) << "prepare_boot msg from before last up_from, ignoring" << dendl;
2440     send_latest(op, m->sb.current_epoch+1);
2441     return true;
2442   }
2443
2444   // noup?
2445   if (!can_mark_up(from)) {
2446     dout(7) << "preprocess_boot ignoring boot from " << m->get_orig_source_inst() << dendl;
2447     send_latest(op, m->sb.current_epoch+1);
2448     return true;
2449   }
2450
2451   dout(10) << "preprocess_boot from " << m->get_orig_source_inst() << dendl;
2452   return false;
2453
2454  ignore:
2455   return true;
2456 }
2457
2458 bool OSDMonitor::prepare_boot(MonOpRequestRef op)
2459 {
2460   op->mark_osdmon_event(__func__);
2461   MOSDBoot *m = static_cast<MOSDBoot*>(op->get_req());
2462   dout(7) << __func__ << " from " << m->get_orig_source_inst() << " sb " << m->sb
2463           << " cluster_addr " << m->cluster_addr
2464           << " hb_back_addr " << m->hb_back_addr
2465           << " hb_front_addr " << m->hb_front_addr
2466           << dendl;
2467
2468   assert(m->get_orig_source().is_osd());
2469   int from = m->get_orig_source().num();
2470
2471   // does this osd exist?
2472   if (from >= osdmap.get_max_osd()) {
2473     dout(1) << "boot from osd." << from << " >= max_osd "
2474             << osdmap.get_max_osd() << dendl;
2475     return false;
2476   }
2477
2478   int oldstate = osdmap.exists(from) ? osdmap.get_state(from) : CEPH_OSD_NEW;
2479   if (pending_inc.new_state.count(from))
2480     oldstate ^= pending_inc.new_state[from];
2481
2482   // already up?  mark down first?
2483   if (osdmap.is_up(from)) {
2484     dout(7) << __func__ << " was up, first marking down "
2485             << osdmap.get_inst(from) << dendl;
2486     // preprocess should have caught these;  if not, assert.
2487     assert(osdmap.get_inst(from) != m->get_orig_source_inst() ||
2488            osdmap.get_cluster_addr(from) != m->cluster_addr);
2489     assert(osdmap.get_uuid(from) == m->sb.osd_fsid);
2490
2491     if (pending_inc.new_state.count(from) == 0 ||
2492         (pending_inc.new_state[from] & CEPH_OSD_UP) == 0) {
2493       // mark previous guy down
2494       pending_inc.new_state[from] = CEPH_OSD_UP;
2495     }
2496     wait_for_finished_proposal(op, new C_RetryMessage(this, op));
2497   } else if (pending_inc.new_up_client.count(from)) {
2498     // already prepared, just wait
2499     dout(7) << __func__ << " already prepared, waiting on "
2500             << m->get_orig_source_addr() << dendl;
2501     wait_for_finished_proposal(op, new C_RetryMessage(this, op));
2502   } else {
2503     // mark new guy up.
2504     pending_inc.new_up_client[from] = m->get_orig_source_addr();
2505     if (!m->cluster_addr.is_blank_ip())
2506       pending_inc.new_up_cluster[from] = m->cluster_addr;
2507     pending_inc.new_hb_back_up[from] = m->hb_back_addr;
2508     if (!m->hb_front_addr.is_blank_ip())
2509       pending_inc.new_hb_front_up[from] = m->hb_front_addr;
2510
2511     down_pending_out.erase(from);  // if any
2512
2513     if (m->sb.weight)
2514       osd_weight[from] = m->sb.weight;
2515
2516     // set uuid?
2517     dout(10) << " setting osd." << from << " uuid to " << m->sb.osd_fsid
2518              << dendl;
2519     if (!osdmap.exists(from) || osdmap.get_uuid(from) != m->sb.osd_fsid) {
2520       // preprocess should have caught this;  if not, assert.
2521       assert(!osdmap.exists(from) || osdmap.get_uuid(from).is_zero());
2522       pending_inc.new_uuid[from] = m->sb.osd_fsid;
2523     }
2524
2525     // fresh osd?
2526     if (m->sb.newest_map == 0 && osdmap.exists(from)) {
2527       const osd_info_t& i = osdmap.get_info(from);
2528       if (i.up_from > i.lost_at) {
2529         dout(10) << " fresh osd; marking lost_at too" << dendl;
2530         pending_inc.new_lost[from] = osdmap.get_epoch();
2531       }
2532     }
2533
2534     // metadata
2535     bufferlist osd_metadata;
2536     ::encode(m->metadata, osd_metadata);
2537     pending_metadata[from] = osd_metadata;
2538     pending_metadata_rm.erase(from);
2539
2540     // adjust last clean unmount epoch?
2541     const osd_info_t& info = osdmap.get_info(from);
2542     dout(10) << " old osd_info: " << info << dendl;
2543     if (m->sb.mounted > info.last_clean_begin ||
2544         (m->sb.mounted == info.last_clean_begin &&
2545          m->sb.clean_thru > info.last_clean_end)) {
2546       epoch_t begin = m->sb.mounted;
2547       epoch_t end = m->sb.clean_thru;
2548
2549       dout(10) << __func__ << " osd." << from << " last_clean_interval "
2550                << "[" << info.last_clean_begin << "," << info.last_clean_end
2551                << ") -> [" << begin << "-" << end << ")"
2552                << dendl;
2553       pending_inc.new_last_clean_interval[from] =
2554         pair<epoch_t,epoch_t>(begin, end);
2555     }
2556
2557     osd_xinfo_t xi = osdmap.get_xinfo(from);
2558     if (m->boot_epoch == 0) {
2559       xi.laggy_probability *= (1.0 - g_conf->mon_osd_laggy_weight);
2560       xi.laggy_interval *= (1.0 - g_conf->mon_osd_laggy_weight);
2561       dout(10) << " not laggy, new xi " << xi << dendl;
2562     } else {
2563       if (xi.down_stamp.sec()) {
2564         int interval = ceph_clock_now().sec() -
2565           xi.down_stamp.sec();
2566         if (g_conf->mon_osd_laggy_max_interval &&
2567             (interval > g_conf->mon_osd_laggy_max_interval)) {
2568           interval =  g_conf->mon_osd_laggy_max_interval;
2569         }
2570         xi.laggy_interval =
2571           interval * g_conf->mon_osd_laggy_weight +
2572           xi.laggy_interval * (1.0 - g_conf->mon_osd_laggy_weight);
2573       }
2574       xi.laggy_probability =
2575         g_conf->mon_osd_laggy_weight +
2576         xi.laggy_probability * (1.0 - g_conf->mon_osd_laggy_weight);
2577       dout(10) << " laggy, now xi " << xi << dendl;
2578     }
2579
2580     // set features shared by the osd
2581     if (m->osd_features)
2582       xi.features = m->osd_features;
2583     else
2584       xi.features = m->get_connection()->get_features();
2585
2586     // mark in?
2587     if ((g_conf->mon_osd_auto_mark_auto_out_in &&
2588          (oldstate & CEPH_OSD_AUTOOUT)) ||
2589         (g_conf->mon_osd_auto_mark_new_in && (oldstate & CEPH_OSD_NEW)) ||
2590         (g_conf->mon_osd_auto_mark_in)) {
2591       if (can_mark_in(from)) {
2592         if (osdmap.osd_xinfo[from].old_weight > 0) {
2593           pending_inc.new_weight[from] = osdmap.osd_xinfo[from].old_weight;
2594           xi.old_weight = 0;
2595         } else {
2596           pending_inc.new_weight[from] = CEPH_OSD_IN;
2597         }
2598       } else {
2599         dout(7) << __func__ << " NOIN set, will not mark in "
2600                 << m->get_orig_source_addr() << dendl;
2601       }
2602     }
2603
2604     pending_inc.new_xinfo[from] = xi;
2605
2606     // wait
2607     wait_for_finished_proposal(op, new C_Booted(this, op));
2608   }
2609   return true;
2610 }
2611
2612 void OSDMonitor::_booted(MonOpRequestRef op, bool logit)
2613 {
2614   op->mark_osdmon_event(__func__);
2615   MOSDBoot *m = static_cast<MOSDBoot*>(op->get_req());
2616   dout(7) << "_booted " << m->get_orig_source_inst() 
2617           << " w " << m->sb.weight << " from " << m->sb.current_epoch << dendl;
2618
2619   if (logit) {
2620     mon->clog->info() << m->get_orig_source_inst() << " boot";
2621   }
2622
2623   send_latest(op, m->sb.current_epoch+1);
2624 }
2625
2626
2627 // -------------
2628 // full
2629
2630 bool OSDMonitor::preprocess_full(MonOpRequestRef op)
2631 {
2632   op->mark_osdmon_event(__func__);
2633   MOSDFull *m = static_cast<MOSDFull*>(op->get_req());
2634   int from = m->get_orig_source().num();
2635   set<string> state;
2636   unsigned mask = CEPH_OSD_NEARFULL | CEPH_OSD_BACKFILLFULL | CEPH_OSD_FULL;
2637
2638   // check permissions, ignore if failed
2639   MonSession *session = m->get_session();
2640   if (!session)
2641     goto ignore;
2642   if (!session->is_capable("osd", MON_CAP_X)) {
2643     dout(0) << "MOSDFull from entity with insufficient privileges:"
2644             << session->caps << dendl;
2645     goto ignore;
2646   }
2647
2648   // ignore a full message from the osd instance that already went down
2649   if (!osdmap.exists(from)) {
2650     dout(7) << __func__ << " ignoring full message from nonexistent "
2651             << m->get_orig_source_inst() << dendl;
2652     goto ignore;
2653   }
2654   if ((!osdmap.is_up(from) &&
2655        osdmap.get_most_recent_inst(from) == m->get_orig_source_inst()) ||
2656       (osdmap.is_up(from) &&
2657        osdmap.get_inst(from) != m->get_orig_source_inst())) {
2658     dout(7) << __func__ << " ignoring full message from down "
2659             << m->get_orig_source_inst() << dendl;
2660     goto ignore;
2661   }
2662
2663   OSDMap::calc_state_set(osdmap.get_state(from), state);
2664
2665   if ((osdmap.get_state(from) & mask) == m->state) {
2666     dout(7) << __func__ << " state already " << state << " for osd." << from
2667             << " " << m->get_orig_source_inst() << dendl;
2668     _reply_map(op, m->version);
2669     goto ignore;
2670   }
2671
2672   dout(10) << __func__ << " want state " << state << " for osd." << from
2673            << " " << m->get_orig_source_inst() << dendl;
2674   return false;
2675
2676  ignore:
2677   return true;
2678 }
2679
2680 bool OSDMonitor::prepare_full(MonOpRequestRef op)
2681 {
2682   op->mark_osdmon_event(__func__);
2683   const MOSDFull *m = static_cast<MOSDFull*>(op->get_req());
2684   const int from = m->get_orig_source().num();
2685
2686   const unsigned mask = CEPH_OSD_NEARFULL | CEPH_OSD_BACKFILLFULL | CEPH_OSD_FULL;
2687   const unsigned want_state = m->state & mask;  // safety first
2688
2689   unsigned cur_state = osdmap.get_state(from);
2690   auto p = pending_inc.new_state.find(from);
2691   if (p != pending_inc.new_state.end()) {
2692     cur_state ^= p->second;
2693   }
2694   cur_state &= mask;
2695
2696   set<string> want_state_set, cur_state_set;
2697   OSDMap::calc_state_set(want_state, want_state_set);
2698   OSDMap::calc_state_set(cur_state, cur_state_set);
2699
2700   if (cur_state != want_state) {
2701     if (p != pending_inc.new_state.end()) {
2702       p->second &= ~mask;
2703     } else {
2704       pending_inc.new_state[from] = 0;
2705     }
2706     pending_inc.new_state[from] |= (osdmap.get_state(from) & mask) ^ want_state;
2707     dout(7) << __func__ << " osd." << from << " " << cur_state_set
2708             << " -> " << want_state_set << dendl;
2709   } else {
2710     dout(7) << __func__ << " osd." << from << " " << cur_state_set
2711             << " = wanted " << want_state_set << ", just waiting" << dendl;
2712   }
2713
2714   wait_for_finished_proposal(op, new C_ReplyMap(this, op, m->version));
2715   return true;
2716 }
2717
2718 // -------------
2719 // alive
2720
2721 bool OSDMonitor::preprocess_alive(MonOpRequestRef op)
2722 {
2723   op->mark_osdmon_event(__func__);
2724   MOSDAlive *m = static_cast<MOSDAlive*>(op->get_req());
2725   int from = m->get_orig_source().num();
2726
2727   // check permissions, ignore if failed
2728   MonSession *session = m->get_session();
2729   if (!session)
2730     goto ignore;
2731   if (!session->is_capable("osd", MON_CAP_X)) {
2732     dout(0) << "attempt to send MOSDAlive from entity with insufficient privileges:"
2733             << session->caps << dendl;
2734     goto ignore;
2735   }
2736
2737   if (!osdmap.is_up(from) ||
2738       osdmap.get_inst(from) != m->get_orig_source_inst()) {
2739     dout(7) << "preprocess_alive ignoring alive message from down " << m->get_orig_source_inst() << dendl;
2740     goto ignore;
2741   }
2742
2743   if (osdmap.get_up_thru(from) >= m->want) {
2744     // yup.
2745     dout(7) << "preprocess_alive want up_thru " << m->want << " dup from " << m->get_orig_source_inst() << dendl;
2746     _reply_map(op, m->version);
2747     return true;
2748   }
2749
2750   dout(10) << "preprocess_alive want up_thru " << m->want
2751            << " from " << m->get_orig_source_inst() << dendl;
2752   return false;
2753
2754  ignore:
2755   return true;
2756 }
2757
2758 bool OSDMonitor::prepare_alive(MonOpRequestRef op)
2759 {
2760   op->mark_osdmon_event(__func__);
2761   MOSDAlive *m = static_cast<MOSDAlive*>(op->get_req());
2762   int from = m->get_orig_source().num();
2763
2764   if (0) {  // we probably don't care much about these
2765     mon->clog->debug() << m->get_orig_source_inst() << " alive";
2766   }
2767
2768   dout(7) << "prepare_alive want up_thru " << m->want << " have " << m->version
2769           << " from " << m->get_orig_source_inst() << dendl;
2770
2771   update_up_thru(from, m->version); // set to the latest map the OSD has
2772   wait_for_finished_proposal(op, new C_ReplyMap(this, op, m->version));
2773   return true;
2774 }
2775
2776 void OSDMonitor::_reply_map(MonOpRequestRef op, epoch_t e)
2777 {
2778   op->mark_osdmon_event(__func__);
2779   dout(7) << "_reply_map " << e
2780           << " from " << op->get_req()->get_orig_source_inst()
2781           << dendl;
2782   send_latest(op, e);
2783 }
2784
2785 // pg_created
2786 bool OSDMonitor::preprocess_pg_created(MonOpRequestRef op)
2787 {
2788   op->mark_osdmon_event(__func__);
2789   auto m = static_cast<MOSDPGCreated*>(op->get_req());
2790   dout(10) << __func__ << " " << *m << dendl;
2791   auto session = m->get_session();
2792   if (!session) {
2793     dout(10) << __func__ << ": no monitor session!" << dendl;
2794     return true;
2795   }
2796   if (!session->is_capable("osd", MON_CAP_X)) {
2797     derr << __func__ << " received from entity "
2798          << "with insufficient privileges " << session->caps << dendl;
2799     return true;
2800   }
2801   // always forward the "created!" to the leader
2802   return false;
2803 }
2804
2805 bool OSDMonitor::prepare_pg_created(MonOpRequestRef op)
2806 {
2807   op->mark_osdmon_event(__func__);
2808   auto m = static_cast<MOSDPGCreated*>(op->get_req());
2809   dout(10) << __func__ << " " << *m << dendl;
2810   auto src = m->get_orig_source();
2811   auto from = src.num();
2812   if (!src.is_osd() ||
2813       !mon->osdmon()->osdmap.is_up(from) ||
2814       m->get_orig_source_inst() != mon->osdmon()->osdmap.get_inst(from)) {
2815     dout(1) << __func__ << " ignoring stats from non-active osd." << dendl;
2816     return false;
2817   }
2818   pending_created_pgs.push_back(m->pgid);
2819   return true;
2820 }
2821
2822 // -------------
2823 // pg_temp changes
2824
2825 bool OSDMonitor::preprocess_pgtemp(MonOpRequestRef op)
2826 {
2827   MOSDPGTemp *m = static_cast<MOSDPGTemp*>(op->get_req());
2828   dout(10) << "preprocess_pgtemp " << *m << dendl;
2829   mempool::osdmap::vector<int> empty;
2830   int from = m->get_orig_source().num();
2831   size_t ignore_cnt = 0;
2832
2833   // check caps
2834   MonSession *session = m->get_session();
2835   if (!session)
2836     goto ignore;
2837   if (!session->is_capable("osd", MON_CAP_X)) {
2838     dout(0) << "attempt to send MOSDPGTemp from entity with insufficient caps "
2839             << session->caps << dendl;
2840     goto ignore;
2841   }
2842
2843   if (!osdmap.is_up(from) ||
2844       osdmap.get_inst(from) != m->get_orig_source_inst()) {
2845     dout(7) << "ignoring pgtemp message from down " << m->get_orig_source_inst() << dendl;
2846     goto ignore;
2847   }
2848
2849   if (m->forced) {
2850     return false;
2851   }
2852
2853   for (auto p = m->pg_temp.begin(); p != m->pg_temp.end(); ++p) {
2854     dout(20) << " " << p->first
2855              << (osdmap.pg_temp->count(p->first) ? osdmap.pg_temp->get(p->first) : empty)
2856              << " -> " << p->second << dendl;
2857
2858     // does the pool exist?
2859     if (!osdmap.have_pg_pool(p->first.pool())) {
2860       /*
2861        * 1. If the osdmap does not have the pool, it means the pool has been
2862        *    removed in-between the osd sending this message and us handling it.
2863        * 2. If osdmap doesn't have the pool, it is safe to assume the pool does
2864        *    not exist in the pending either, as the osds would not send a
2865        *    message about a pool they know nothing about (yet).
2866        * 3. However, if the pool does exist in the pending, then it must be a
2867        *    new pool, and not relevant to this message (see 1).
2868        */
2869       dout(10) << __func__ << " ignore " << p->first << " -> " << p->second
2870                << ": pool has been removed" << dendl;
2871       ignore_cnt++;
2872       continue;
2873     }
2874
2875     int acting_primary = -1;
2876     osdmap.pg_to_up_acting_osds(
2877       p->first, nullptr, nullptr, nullptr, &acting_primary);
2878     if (acting_primary != from) {
2879       /* If the source isn't the primary based on the current osdmap, we know
2880        * that the interval changed and that we can discard this message.
2881        * Indeed, we must do so to avoid 16127 since we can't otherwise determine
2882        * which of two pg temp mappings on the same pg is more recent.
2883        */
2884       dout(10) << __func__ << " ignore " << p->first << " -> " << p->second
2885                << ": primary has changed" << dendl;
2886       ignore_cnt++;
2887       continue;
2888     }
2889
2890     // removal?
2891     if (p->second.empty() && (osdmap.pg_temp->count(p->first) ||
2892                               osdmap.primary_temp->count(p->first)))
2893       return false;
2894     // change?
2895     //  NOTE: we assume that this will clear pg_primary, so consider
2896     //        an existing pg_primary field to imply a change
2897     if (p->second.size() &&
2898         (osdmap.pg_temp->count(p->first) == 0 ||
2899          !vectors_equal(osdmap.pg_temp->get(p->first), p->second) ||
2900          osdmap.primary_temp->count(p->first)))
2901       return false;
2902   }
2903
2904   // should we ignore all the pgs?
2905   if (ignore_cnt == m->pg_temp.size())
2906     goto ignore;
2907
2908   dout(7) << "preprocess_pgtemp e" << m->map_epoch << " no changes from " << m->get_orig_source_inst() << dendl;
2909   _reply_map(op, m->map_epoch);
2910   return true;
2911
2912  ignore:
2913   return true;
2914 }
2915
2916 void OSDMonitor::update_up_thru(int from, epoch_t up_thru)
2917 {
2918   epoch_t old_up_thru = osdmap.get_up_thru(from);
2919   auto ut = pending_inc.new_up_thru.find(from);
2920   if (ut != pending_inc.new_up_thru.end()) {
2921     old_up_thru = ut->second;
2922   }
2923   if (up_thru > old_up_thru) {
2924     // set up_thru too, so the osd doesn't have to ask again
2925     pending_inc.new_up_thru[from] = up_thru;
2926   }
2927 }
2928
2929 bool OSDMonitor::prepare_pgtemp(MonOpRequestRef op)
2930 {
2931   op->mark_osdmon_event(__func__);
2932   MOSDPGTemp *m = static_cast<MOSDPGTemp*>(op->get_req());
2933   int from = m->get_orig_source().num();
2934   dout(7) << "prepare_pgtemp e" << m->map_epoch << " from " << m->get_orig_source_inst() << dendl;
2935   for (map<pg_t,vector<int32_t> >::iterator p = m->pg_temp.begin(); p != m->pg_temp.end(); ++p) {
2936     uint64_t pool = p->first.pool();
2937     if (pending_inc.old_pools.count(pool)) {
2938       dout(10) << __func__ << " ignore " << p->first << " -> " << p->second
2939                << ": pool pending removal" << dendl;
2940       continue;
2941     }
2942     if (!osdmap.have_pg_pool(pool)) {
2943       dout(10) << __func__ << " ignore " << p->first << " -> " << p->second
2944                << ": pool has been removed" << dendl;
2945       continue;
2946     }
2947     pending_inc.new_pg_temp[p->first] =
2948       mempool::osdmap::vector<int>(p->second.begin(), p->second.end());
2949
2950     // unconditionally clear pg_primary (until this message can encode
2951     // a change for that, too.. at which point we need to also fix
2952     // preprocess_pg_temp)
2953     if (osdmap.primary_temp->count(p->first) ||
2954         pending_inc.new_primary_temp.count(p->first))
2955       pending_inc.new_primary_temp[p->first] = -1;
2956   }
2957
2958   // set up_thru too, so the osd doesn't have to ask again
2959   update_up_thru(from, m->map_epoch);
2960
2961   wait_for_finished_proposal(op, new C_ReplyMap(this, op, m->map_epoch));
2962   return true;
2963 }
2964
2965
2966 // ---
2967
2968 bool OSDMonitor::preprocess_remove_snaps(MonOpRequestRef op)
2969 {
2970   op->mark_osdmon_event(__func__);
2971   MRemoveSnaps *m = static_cast<MRemoveSnaps*>(op->get_req());
2972   dout(7) << "preprocess_remove_snaps " << *m << dendl;
2973
2974   // check privilege, ignore if failed
2975   MonSession *session = m->get_session();
2976   if (!session)
2977     goto ignore;
2978   if (!session->caps.is_capable(
2979         g_ceph_context,
2980         CEPH_ENTITY_TYPE_MON,
2981         session->entity_name,
2982         "osd", "osd pool rmsnap", {}, true, true, false)) {
2983     dout(0) << "got preprocess_remove_snaps from entity with insufficient caps "
2984             << session->caps << dendl;
2985     goto ignore;
2986   }
2987
2988   for (map<int, vector<snapid_t> >::iterator q = m->snaps.begin();
2989        q != m->snaps.end();
2990        ++q) {
2991     if (!osdmap.have_pg_pool(q->first)) {
2992       dout(10) << " ignoring removed_snaps " << q->second << " on non-existent pool " << q->first << dendl;
2993       continue;
2994     }
2995     const pg_pool_t *pi = osdmap.get_pg_pool(q->first);
2996     for (vector<snapid_t>::iterator p = q->second.begin();
2997          p != q->second.end();
2998          ++p) {
2999       if (*p > pi->get_snap_seq() ||
3000           !pi->removed_snaps.contains(*p))
3001         return false;
3002     }
3003   }
3004
3005  ignore:
3006   return true;
3007 }
3008
3009 bool OSDMonitor::prepare_remove_snaps(MonOpRequestRef op)
3010 {
3011   op->mark_osdmon_event(__func__);
3012   MRemoveSnaps *m = static_cast<MRemoveSnaps*>(op->get_req());
3013   dout(7) << "prepare_remove_snaps " << *m << dendl;
3014
3015   for (map<int, vector<snapid_t> >::iterator p = m->snaps.begin();
3016        p != m->snaps.end();
3017        ++p) {
3018
3019     if (!osdmap.have_pg_pool(p->first)) {
3020       dout(10) << " ignoring removed_snaps " << p->second << " on non-existent pool " << p->first << dendl;
3021       continue;
3022     }
3023
3024     pg_pool_t& pi = osdmap.pools[p->first];
3025     for (vector<snapid_t>::iterator q = p->second.begin();
3026          q != p->second.end();
3027          ++q) {
3028       if (!pi.removed_snaps.contains(*q) &&
3029           (!pending_inc.new_pools.count(p->first) ||
3030            !pending_inc.new_pools[p->first].removed_snaps.contains(*q))) {
3031         pg_pool_t *newpi = pending_inc.get_new_pool(p->first, &pi);
3032         newpi->removed_snaps.insert(*q);
3033         dout(10) << " pool " << p->first << " removed_snaps added " << *q
3034                  << " (now " << newpi->removed_snaps << ")" << dendl;
3035         if (*q > newpi->get_snap_seq()) {
3036           dout(10) << " pool " << p->first << " snap_seq " << newpi->get_snap_seq() << " -> " << *q << dendl;
3037           newpi->set_snap_seq(*q);
3038         }
3039         newpi->set_snap_epoch(pending_inc.epoch);
3040       }
3041     }
3042   }
3043   return true;
3044 }
3045
3046 // osd beacon
3047 bool OSDMonitor::preprocess_beacon(MonOpRequestRef op)
3048 {
3049   op->mark_osdmon_event(__func__);
3050   auto beacon = static_cast<MOSDBeacon*>(op->get_req());
3051   // check caps
3052   auto session = beacon->get_session();
3053   if (!session) {
3054     dout(10) << __func__ << " no monitor session!" << dendl;
3055     return true;
3056   }
3057   if (!session->is_capable("osd", MON_CAP_X)) {
3058     derr << __func__ << " received from entity "
3059          << "with insufficient privileges " << session->caps << dendl;
3060     return true;
3061   }
3062   // Always forward the beacon to the leader, even if they are the same as
3063   // the old one. The leader will mark as down osds that haven't sent
3064   // beacon for a few minutes.
3065   return false;
3066 }
3067
3068 bool OSDMonitor::prepare_beacon(MonOpRequestRef op)
3069 {
3070   op->mark_osdmon_event(__func__);
3071   const auto beacon = static_cast<MOSDBeacon*>(op->get_req());
3072   const auto src = beacon->get_orig_source();
3073   dout(10) << __func__ << " " << *beacon
3074            << " from " << src << dendl;
3075   int from = src.num();
3076
3077   if (!src.is_osd() ||
3078       !osdmap.is_up(from) ||
3079       beacon->get_orig_source_inst() != osdmap.get_inst(from)) {
3080     dout(1) << " ignoring beacon from non-active osd." << dendl;
3081     return false;
3082   }
3083
3084   last_osd_report[from] = ceph_clock_now();
3085   osd_epochs[from] = beacon->version;
3086
3087   for (const auto& pg : beacon->pgs) {
3088     last_epoch_clean.report(pg, beacon->min_last_epoch_clean);
3089   }
3090   return false;
3091 }
3092
3093 // ---------------
3094 // map helpers
3095
3096 void OSDMonitor::send_latest(MonOpRequestRef op, epoch_t start)
3097 {
3098   op->mark_osdmon_event(__func__);
3099   dout(5) << "send_latest to " << op->get_req()->get_orig_source_inst()
3100           << " start " << start << dendl;
3101   if (start == 0)
3102     send_full(op);
3103   else
3104     send_incremental(op, start);
3105 }
3106
3107
3108 MOSDMap *OSDMonitor::build_latest_full()
3109 {
3110   MOSDMap *r = new MOSDMap(mon->monmap->fsid);
3111   get_version_full(osdmap.get_epoch(), r->maps[osdmap.get_epoch()]);
3112   r->oldest_map = get_first_committed();
3113   r->newest_map = osdmap.get_epoch();
3114   return r;
3115 }
3116
3117 MOSDMap *OSDMonitor::build_incremental(epoch_t from, epoch_t to)
3118 {
3119   dout(10) << "build_incremental [" << from << ".." << to << "]" << dendl;
3120   MOSDMap *m = new MOSDMap(mon->monmap->fsid);
3121   m->oldest_map = get_first_committed();
3122   m->newest_map = osdmap.get_epoch();
3123
3124   for (epoch_t e = to; e >= from && e > 0; e--) {
3125     bufferlist bl;
3126     int err = get_version(e, bl);
3127     if (err == 0) {
3128       assert(bl.length());
3129       // if (get_version(e, bl) > 0) {
3130       dout(20) << "build_incremental    inc " << e << " "
3131                << bl.length() << " bytes" << dendl;
3132       m->incremental_maps[e] = bl;
3133     } else {
3134       assert(err == -ENOENT);
3135       assert(!bl.length());
3136       get_version_full(e, bl);
3137       if (bl.length() > 0) {
3138       //else if (get_version("full", e, bl) > 0) {
3139       dout(20) << "build_incremental   full " << e << " "
3140                << bl.length() << " bytes" << dendl;
3141       m->maps[e] = bl;
3142       } else {
3143         ceph_abort();  // we should have all maps.
3144       }
3145     }
3146   }
3147   return m;
3148 }
3149
3150 void OSDMonitor::send_full(MonOpRequestRef op)
3151 {
3152   op->mark_osdmon_event(__func__);
3153   dout(5) << "send_full to " << op->get_req()->get_orig_source_inst() << dendl;
3154   mon->send_reply(op, build_latest_full());
3155 }
3156
3157 void OSDMonitor::send_incremental(MonOpRequestRef op, epoch_t first)
3158 {
3159   op->mark_osdmon_event(__func__);
3160
3161   MonSession *s = op->get_session();
3162   assert(s);
3163
3164   if (s->proxy_con &&
3165       s->proxy_con->has_feature(CEPH_FEATURE_MON_ROUTE_OSDMAP)) {
3166     // oh, we can tell the other mon to do it
3167     dout(10) << __func__ << " asking proxying mon to send_incremental from "
3168              << first << dendl;
3169     MRoute *r = new MRoute(s->proxy_tid, NULL);
3170     r->send_osdmap_first = first;
3171     s->proxy_con->send_message(r);
3172     op->mark_event("reply: send routed send_osdmap_first reply");
3173   } else {
3174     // do it ourselves
3175     send_incremental(first, s, false, op);
3176   }
3177 }
3178
3179 void OSDMonitor::send_incremental(epoch_t first,
3180                                   MonSession *session,
3181                                   bool onetime,
3182                                   MonOpRequestRef req)
3183 {
3184   dout(5) << "send_incremental [" << first << ".." << osdmap.get_epoch() << "]"
3185           << " to " << session->inst << dendl;
3186
3187   if (first <= session->osd_epoch) {
3188     dout(10) << __func__ << " " << session->inst << " should already have epoch "
3189              << session->osd_epoch << dendl;
3190     first = session->osd_epoch + 1;
3191   }
3192
3193   if (first < get_first_committed()) {
3194     first = get_first_committed();
3195     bufferlist bl;
3196     int err = get_version_full(first, bl);
3197     assert(err == 0);
3198     assert(bl.length());
3199
3200     dout(20) << "send_incremental starting with base full "
3201              << first << " " << bl.length() << " bytes" << dendl;
3202
3203     MOSDMap *m = new MOSDMap(osdmap.get_fsid());
3204     m->oldest_map = get_first_committed();
3205     m->newest_map = osdmap.get_epoch();
3206     m->maps[first] = bl;
3207
3208     if (req) {
3209       mon->send_reply(req, m);
3210       session->osd_epoch = first;
3211       return;
3212     } else {
3213       session->con->send_message(m);
3214       session->osd_epoch = first;
3215     }
3216     first++;
3217   }
3218
3219   while (first <= osdmap.get_epoch()) {
3220     epoch_t last = MIN(first + g_conf->osd_map_message_max - 1,
3221                        osdmap.get_epoch());
3222     MOSDMap *m = build_incremental(first, last);
3223
3224     if (req) {
3225       // send some maps.  it may not be all of them, but it will get them
3226       // started.
3227       mon->send_reply(req, m);
3228     } else {
3229       session->con->send_message(m);
3230       first = last + 1;
3231     }
3232     session->osd_epoch = last;
3233     if (onetime || req)
3234       break;
3235   }
3236 }
3237
3238 int OSDMonitor::get_version(version_t ver, bufferlist& bl)
3239 {
3240     if (inc_osd_cache.lookup(ver, &bl)) {
3241       return 0;
3242     }
3243     int ret = PaxosService::get_version(ver, bl);
3244     if (!ret) {
3245       inc_osd_cache.add(ver, bl);
3246     }
3247     return ret;
3248 }
3249
3250 int OSDMonitor::get_version_full(version_t ver, bufferlist& bl)
3251 {
3252     if (full_osd_cache.lookup(ver, &bl)) {
3253       return 0;
3254     }
3255     int ret = PaxosService::get_version_full(ver, bl);
3256     if (!ret) {
3257       full_osd_cache.add(ver, bl);
3258     }
3259     return ret;
3260 }
3261
3262 epoch_t OSDMonitor::blacklist(const entity_addr_t& a, utime_t until)
3263 {
3264   dout(10) << "blacklist " << a << " until " << until << dendl;
3265   pending_inc.new_blacklist[a] = until;
3266   return pending_inc.epoch;
3267 }
3268
3269
3270 void OSDMonitor::check_osdmap_subs()
3271 {
3272   dout(10) << __func__ << dendl;
3273   if (!osdmap.get_epoch()) {
3274     return;
3275   }
3276   auto osdmap_subs = mon->session_map.subs.find("osdmap");
3277   if (osdmap_subs == mon->session_map.subs.end()) {
3278     return;
3279   }
3280   auto p = osdmap_subs->second->begin();
3281   while (!p.end()) {
3282     auto sub = *p;
3283     ++p;
3284     check_osdmap_sub(sub);
3285   }
3286 }
3287
3288 void OSDMonitor::check_osdmap_sub(Subscription *sub)
3289 {
3290   dout(10) << __func__ << " " << sub << " next " << sub->next
3291            << (sub->onetime ? " (onetime)":" (ongoing)") << dendl;
3292   if (sub->next <= osdmap.get_epoch()) {
3293     if (sub->next >= 1)
3294       send_incremental(sub->next, sub->session, sub->incremental_onetime);
3295     else
3296       sub->session->con->send_message(build_latest_full());
3297     if (sub->onetime)
3298       mon->session_map.remove_sub(sub);
3299     else
3300       sub->next = osdmap.get_epoch() + 1;
3301   }
3302 }
3303
3304 void OSDMonitor::check_pg_creates_subs()
3305 {
3306   if (!mon->monmap->get_required_features().contains_all(
3307         ceph::features::mon::FEATURE_LUMINOUS)) {
3308     // PGMonitor takes care of this in pre-luminous era.
3309     return;
3310   }
3311   if (!osdmap.get_num_up_osds()) {
3312     return;
3313   }
3314   assert(osdmap.get_up_osd_features() & CEPH_FEATURE_MON_STATEFUL_SUB);
3315   mon->with_session_map([this](const MonSessionMap& session_map) {
3316       auto pg_creates_subs = session_map.subs.find("osd_pg_creates");
3317       if (pg_creates_subs == session_map.subs.end()) {
3318         return;
3319       }
3320       for (auto sub : *pg_creates_subs->second) {
3321         check_pg_creates_sub(sub);
3322       }
3323     });
3324 }
3325
3326 void OSDMonitor::check_pg_creates_sub(Subscription *sub)
3327 {
3328   dout(20) << __func__ << " .. " << sub->session->inst << dendl;
3329   assert(sub->type == "osd_pg_creates");
3330   // only send these if the OSD is up.  we will check_subs() when they do
3331   // come up so they will get the creates then.
3332   if (sub->session->inst.name.is_osd() &&
3333       mon->osdmon()->osdmap.is_up(sub->session->inst.name.num())) {
3334     sub->next = send_pg_creates(sub->session->inst.name.num(),
3335                                 sub->session->con.get(),
3336                                 sub->next);
3337   }
3338 }
3339
3340 void OSDMonitor::do_application_enable(int64_t pool_id,
3341                                        const std::string &app_name)
3342 {
3343   assert(paxos->is_plugged() && is_writeable());
3344
3345   dout(20) << __func__ << ": pool_id=" << pool_id << ", app_name=" << app_name
3346            << dendl;
3347
3348   assert(osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS ||
3349          pending_inc.new_require_osd_release >= CEPH_RELEASE_LUMINOUS);
3350
3351   auto pp = osdmap.get_pg_pool(pool_id);
3352   assert(pp != nullptr);
3353
3354   pg_pool_t p = *pp;
3355   if (pending_inc.new_pools.count(pool_id)) {
3356     p = pending_inc.new_pools[pool_id];
3357   }
3358
3359   p.application_metadata.insert({app_name, {}});
3360   p.last_change = pending_inc.epoch;
3361   pending_inc.new_pools[pool_id] = p;
3362 }
3363
3364 unsigned OSDMonitor::scan_for_creating_pgs(
3365   const mempool::osdmap::map<int64_t,pg_pool_t>& pools,
3366   const mempool::osdmap::set<int64_t>& removed_pools,
3367   utime_t modified,
3368   creating_pgs_t* creating_pgs) const
3369 {
3370   unsigned queued = 0;
3371   for (auto& p : pools) {
3372     int64_t poolid = p.first;
3373     const pg_pool_t& pool = p.second;
3374     int ruleno = osdmap.crush->find_rule(pool.get_crush_rule(),
3375                                          pool.get_type(), pool.get_size());
3376     if (ruleno < 0 || !osdmap.crush->rule_exists(ruleno))
3377       continue;
3378
3379     const auto last_scan_epoch = creating_pgs->last_scan_epoch;
3380     const auto created = pool.get_last_change();
3381     if (last_scan_epoch && created <= last_scan_epoch) {
3382       dout(10) << __func__ << " no change in pool " << poolid
3383                << " " << pool << dendl;
3384       continue;
3385     }
3386     if (removed_pools.count(poolid)) {
3387       dout(10) << __func__ << " pool is being removed: " << poolid
3388                << " " << pool << dendl;
3389       continue;
3390     }
3391     dout(10) << __func__ << " queueing pool create for " << poolid
3392              << " " << pool << dendl;
3393     if (creating_pgs->create_pool(poolid, pool.get_pg_num(),
3394                                   created, modified)) {
3395       queued++;
3396     }
3397   }
3398   return queued;
3399 }
3400
3401 void OSDMonitor::update_creating_pgs()
3402 {
3403   dout(10) << __func__ << " " << creating_pgs.pgs.size() << " pgs creating, "
3404            << creating_pgs.queue.size() << " pools in queue" << dendl;
3405   decltype(creating_pgs_by_osd_epoch) new_pgs_by_osd_epoch;
3406   std::lock_guard<std::mutex> l(creating_pgs_lock);
3407   for (const auto& pg : creating_pgs.pgs) {
3408     int acting_primary = -1;
3409     auto pgid = pg.first;
3410     auto mapped = pg.second.first;
3411     dout(20) << __func__ << " looking up " << pgid << "@" << mapped << dendl;
3412     mapping.get(pgid, nullptr, nullptr, nullptr, &acting_primary);
3413     // check the previous creating_pgs, look for the target to whom the pg was
3414     // previously mapped
3415     for (const auto& pgs_by_epoch : creating_pgs_by_osd_epoch) {
3416       const auto last_acting_primary = pgs_by_epoch.first;
3417       for (auto& pgs: pgs_by_epoch.second) {
3418         if (pgs.second.count(pgid)) {
3419           if (last_acting_primary == acting_primary) {
3420             mapped = pgs.first;
3421           } else {
3422             dout(20) << __func__ << " " << pgid << " "
3423                      << " acting_primary:" << last_acting_primary
3424                      << " -> " << acting_primary << dendl;
3425             // note epoch if the target of the create message changed.
3426             mapped = mapping.get_epoch();
3427           }
3428           break;
3429         } else {
3430           // newly creating
3431           mapped = mapping.get_epoch();
3432         }
3433       }
3434     }
3435     dout(10) << __func__ << " will instruct osd." << acting_primary
3436              << " to create " << pgid << "@" << mapped << dendl;
3437     new_pgs_by_osd_epoch[acting_primary][mapped].insert(pgid);
3438   }
3439   creating_pgs_by_osd_epoch = std::move(new_pgs_by_osd_epoch);
3440   creating_pgs_epoch = mapping.get_epoch();
3441 }
3442
3443 epoch_t OSDMonitor::send_pg_creates(int osd, Connection *con, epoch_t next) const
3444 {
3445   dout(30) << __func__ << " osd." << osd << " next=" << next
3446            << " " << creating_pgs_by_osd_epoch << dendl;
3447   std::lock_guard<std::mutex> l(creating_pgs_lock);
3448   if (creating_pgs_epoch <= creating_pgs.last_scan_epoch) {
3449     dout(20) << __func__
3450              << " not using stale creating_pgs@" << creating_pgs_epoch << dendl;
3451     // the subscribers will be updated when the mapping is completed anyway
3452     return next;
3453   }
3454   auto creating_pgs_by_epoch = creating_pgs_by_osd_epoch.find(osd);
3455   if (creating_pgs_by_epoch == creating_pgs_by_osd_epoch.end())
3456     return next;
3457   assert(!creating_pgs_by_epoch->second.empty());
3458
3459   MOSDPGCreate *m = nullptr;
3460   epoch_t last = 0;
3461   for (auto epoch_pgs = creating_pgs_by_epoch->second.lower_bound(next);
3462        epoch_pgs != creating_pgs_by_epoch->second.end(); ++epoch_pgs) {
3463     auto epoch = epoch_pgs->first;
3464     auto& pgs = epoch_pgs->second;
3465     dout(20) << __func__ << " osd." << osd << " from " << next
3466              << " : epoch " << epoch << " " << pgs.size() << " pgs" << dendl;
3467     last = epoch;
3468     for (auto& pg : pgs) {
3469       if (!m)
3470         m = new MOSDPGCreate(creating_pgs_epoch);
3471       // Need the create time from the monitor using its clock to set
3472       // last_scrub_stamp upon pg creation.
3473       auto create = creating_pgs.pgs.find(pg);
3474       assert(create != creating_pgs.pgs.end());
3475       m->mkpg.emplace(pg, pg_create_t{create->second.first, pg, 0});
3476       m->ctimes.emplace(pg, create->second.second);
3477       dout(20) << __func__ << " will create " << pg
3478                << " at " << create->second.first << dendl;
3479     }
3480   }
3481   if (!m) {
3482     dout(20) << __func__ << " osd." << osd << " from " << next
3483              << " has nothing to send" << dendl;
3484     return next;
3485   }
3486   con->send_message(m);
3487   // sub is current through last + 1
3488   return last + 1;
3489 }
3490
3491 // TICK
3492
3493
3494 void OSDMonitor::tick()
3495 {
3496   if (!is_active()) return;
3497
3498   dout(10) << osdmap << dendl;
3499
3500   if (!mon->is_leader()) return;
3501
3502   bool do_propose = false;
3503   utime_t now = ceph_clock_now();
3504
3505   if (osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS &&
3506       mon->monmap->get_required_features().contains_all(
3507         ceph::features::mon::FEATURE_LUMINOUS)) {
3508     if (handle_osd_timeouts(now, last_osd_report)) {
3509       do_propose = true;
3510     }
3511   }
3512   if (!osdmap.test_flag(CEPH_OSDMAP_PURGED_SNAPDIRS) &&
3513       osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS &&
3514       mon->mgrstatmon()->is_readable() &&
3515       mon->mgrstatmon()->definitely_converted_snapsets()) {
3516     dout(1) << __func__ << " all snapsets converted, setting purged_snapdirs"
3517             << dendl;
3518     add_flag(CEPH_OSDMAP_PURGED_SNAPDIRS);
3519     do_propose = true;
3520   }
3521
3522   // mark osds down?
3523   if (check_failures(now))
3524     do_propose = true;
3525
3526   // mark down osds out?
3527
3528   /* can_mark_out() checks if we can mark osds as being out. The -1 has no
3529    * influence at all. The decision is made based on the ratio of "in" osds,
3530    * and the function returns false if this ratio is lower that the minimum
3531    * ratio set by g_conf->mon_osd_min_in_ratio. So it's not really up to us.
3532    */
3533   if (can_mark_out(-1)) {
3534     set<int> down_cache;  // quick cache of down subtrees
3535
3536     map<int,utime_t>::iterator i = down_pending_out.begin();
3537     while (i != down_pending_out.end()) {
3538       int o = i->first;
3539       utime_t down = now;
3540       down -= i->second;
3541       ++i;
3542
3543       if (osdmap.is_down(o) &&
3544           osdmap.is_in(o) &&
3545           can_mark_out(o)) {
3546         utime_t orig_grace(g_conf->mon_osd_down_out_interval, 0);
3547         utime_t grace = orig_grace;
3548         double my_grace = 0.0;
3549
3550         if (g_conf->mon_osd_adjust_down_out_interval) {
3551           // scale grace period the same way we do the heartbeat grace.
3552           const osd_xinfo_t& xi = osdmap.get_xinfo(o);
3553           double halflife = (double)g_conf->mon_osd_laggy_halflife;
3554           double decay_k = ::log(.5) / halflife;
3555           double decay = exp((double)down * decay_k);
3556           dout(20) << "osd." << o << " laggy halflife " << halflife << " decay_k " << decay_k
3557                    << " down for " << down << " decay " << decay << dendl;
3558           my_grace = decay * (double)xi.laggy_interval * xi.laggy_probability;
3559           grace += my_grace;
3560         }
3561
3562         // is this an entire large subtree down?
3563         if (g_conf->mon_osd_down_out_subtree_limit.length()) {
3564           int type = osdmap.crush->get_type_id(g_conf->mon_osd_down_out_subtree_limit);
3565           if (type > 0) {
3566             if (osdmap.containing_subtree_is_down(g_ceph_context, o, type, &down_cache)) {
3567               dout(10) << "tick entire containing " << g_conf->mon_osd_down_out_subtree_limit
3568                        << " subtree for osd." << o << " is down; resetting timer" << dendl;
3569               // reset timer, too.
3570               down_pending_out[o] = now;
3571               continue;
3572             }
3573           }
3574         }
3575
3576         bool down_out = !osdmap.is_destroyed(o) &&
3577           g_conf->mon_osd_down_out_interval > 0 && down.sec() >= grace;
3578         bool destroyed_out = osdmap.is_destroyed(o) &&
3579           g_conf->mon_osd_destroyed_out_interval > 0 &&
3580         // this is not precise enough as we did not make a note when this osd
3581         // was marked as destroyed, but let's not bother with that
3582         // complexity for now.
3583           down.sec() >= g_conf->mon_osd_destroyed_out_interval;
3584         if (down_out || destroyed_out) {
3585           dout(10) << "tick marking osd." << o << " OUT after " << down
3586                    << " sec (target " << grace << " = " << orig_grace << " + " << my_grace << ")" << dendl;
3587           pending_inc.new_weight[o] = CEPH_OSD_OUT;
3588
3589           // set the AUTOOUT bit.
3590           if (pending_inc.new_state.count(o) == 0)
3591             pending_inc.new_state[o] = 0;
3592           pending_inc.new_state[o] |= CEPH_OSD_AUTOOUT;
3593
3594           // remember previous weight
3595           if (pending_inc.new_xinfo.count(o) == 0)
3596             pending_inc.new_xinfo[o] = osdmap.osd_xinfo[o];
3597           pending_inc.new_xinfo[o].old_weight = osdmap.osd_weight[o];
3598
3599           do_propose = true;
3600
3601           mon->clog->info() << "Marking osd." << o << " out (has been down for "
3602                             << int(down.sec()) << " seconds)";
3603         } else
3604           continue;
3605       }
3606
3607       down_pending_out.erase(o);
3608     }
3609   } else {
3610     dout(10) << "tick NOOUT flag set, not checking down osds" << dendl;
3611   }
3612
3613   // expire blacklisted items?
3614   for (ceph::unordered_map<entity_addr_t,utime_t>::iterator p = osdmap.blacklist.begin();
3615        p != osdmap.blacklist.end();
3616        ++p) {
3617     if (p->second < now) {
3618       dout(10) << "expiring blacklist item " << p->first << " expired " << p->second << " < now " << now << dendl;
3619       pending_inc.old_blacklist.push_back(p->first);
3620       do_propose = true;
3621     }
3622   }
3623
3624   // if map full setting has changed, get that info out there!
3625   if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS &&
3626       mon->pgservice->is_readable()) {
3627     // for pre-luminous compat only!
3628     if (mon->pgservice->have_full_osds()) {
3629       dout(5) << "There are full osds, setting full flag" << dendl;
3630       add_flag(CEPH_OSDMAP_FULL);
3631     } else if (osdmap.test_flag(CEPH_OSDMAP_FULL)){
3632       dout(10) << "No full osds, removing full flag" << dendl;
3633       remove_flag(CEPH_OSDMAP_FULL);
3634     }
3635
3636     if (mon->pgservice->have_nearfull_osds()) {
3637       dout(5) << "There are near full osds, setting nearfull flag" << dendl;
3638       add_flag(CEPH_OSDMAP_NEARFULL);
3639     } else if (osdmap.test_flag(CEPH_OSDMAP_NEARFULL)){
3640       dout(10) << "No near full osds, removing nearfull flag" << dendl;
3641       remove_flag(CEPH_OSDMAP_NEARFULL);
3642     }
3643     if (pending_inc.new_flags != -1 &&
3644        (pending_inc.new_flags ^ osdmap.flags) & (CEPH_OSDMAP_FULL | CEPH_OSDMAP_NEARFULL)) {
3645       dout(1) << "New setting for" <<
3646               (pending_inc.new_flags & CEPH_OSDMAP_FULL ? " CEPH_OSDMAP_FULL" : "") <<
3647               (pending_inc.new_flags & CEPH_OSDMAP_NEARFULL ? " CEPH_OSDMAP_NEARFULL" : "")
3648               << " -- doing propose" << dendl;
3649       do_propose = true;
3650     }
3651   }
3652
3653   if (update_pools_status())
3654     do_propose = true;
3655
3656   if (do_propose ||
3657       !pending_inc.new_pg_temp.empty())  // also propose if we adjusted pg_temp
3658     propose_pending();
3659 }
3660
3661 bool OSDMonitor::handle_osd_timeouts(const utime_t &now,
3662                                      std::map<int,utime_t> &last_osd_report)
3663 {
3664   utime_t timeo(g_conf->mon_osd_report_timeout, 0);
3665   if (now - mon->get_leader_since() < timeo) {
3666     // We haven't been the leader for long enough to consider OSD timeouts
3667     return false;
3668   }
3669
3670   int max_osd = osdmap.get_max_osd();
3671   bool new_down = false;
3672
3673   for (int i=0; i < max_osd; ++i) {
3674     dout(30) << __func__ << ": checking up on osd " << i << dendl;
3675     if (!osdmap.exists(i)) {
3676       last_osd_report.erase(i); // if any
3677       continue;
3678     }
3679     if (!osdmap.is_up(i))
3680       continue;
3681     const std::map<int,utime_t>::const_iterator t = last_osd_report.find(i);
3682     if (t == last_osd_report.end()) {
3683       // it wasn't in the map; start the timer.
3684       last_osd_report[i] = now;
3685     } else if (can_mark_down(i)) {
3686       utime_t diff = now - t->second;
3687       if (diff > timeo) {
3688         mon->clog->info() << "osd." << i << " marked down after no beacon for "
3689                           << diff << " seconds";
3690         derr << "no beacon from osd." << i << " since " << t->second
3691              << ", " << diff << " seconds ago.  marking down" << dendl;
3692         pending_inc.new_state[i] = CEPH_OSD_UP;
3693         new_down = true;
3694       }
3695     }
3696   }
3697   return new_down;
3698 }
3699
3700 void OSDMonitor::get_health(list<pair<health_status_t,string> >& summary,
3701                             list<pair<health_status_t,string> > *detail,
3702                             CephContext *cct) const
3703 {
3704   int num_osds = osdmap.get_num_osds();
3705
3706   if (num_osds == 0) {
3707     summary.push_back(make_pair(HEALTH_ERR, "no osds"));
3708   } else {
3709     int num_in_osds = 0;
3710     int num_down_in_osds = 0;
3711     set<int> osds;
3712     set<int> down_in_osds;
3713     set<int> up_in_osds;
3714     set<int> subtree_up;
3715     unordered_map<int, set<int> > subtree_type_down;
3716     unordered_map<int, int> num_osds_subtree;
3717     int max_type = osdmap.crush->get_max_type_id();
3718
3719     for (int i = 0; i < osdmap.get_max_osd(); i++) {
3720       if (!osdmap.exists(i)) {
3721         if (osdmap.crush->item_exists(i)) {
3722           osds.insert(i);
3723         }
3724         continue;
3725       }
3726       if (osdmap.is_out(i))
3727         continue;
3728       ++num_in_osds;
3729       if (down_in_osds.count(i) || up_in_osds.count(i))
3730         continue;
3731       if (!osdmap.is_up(i)) {
3732         down_in_osds.insert(i);
3733         int parent_id = 0;
3734         int current = i;
3735         for (int type = 0; type <= max_type; type++) {
3736           if (!osdmap.crush->get_type_name(type))
3737             continue;
3738           int r = osdmap.crush->get_immediate_parent_id(current, &parent_id);
3739           if (r == -ENOENT)
3740             break;
3741           // break early if this parent is already marked as up
3742           if (subtree_up.count(parent_id))
3743             break;
3744           type = osdmap.crush->get_bucket_type(parent_id);
3745           if (!osdmap.subtree_type_is_down(
3746                 g_ceph_context, parent_id, type,
3747                 &down_in_osds, &up_in_osds, &subtree_up, &subtree_type_down))
3748             break;
3749           current = parent_id;
3750         }
3751       }
3752     }
3753
3754     // calculate the number of down osds in each down subtree and
3755     // store it in num_osds_subtree
3756     for (int type = 1; type <= max_type; type++) {
3757       if (!osdmap.crush->get_type_name(type))
3758         continue;
3759       for (auto j = subtree_type_down[type].begin();
3760            j != subtree_type_down[type].end();
3761            ++j) {
3762         if (type == 1) {
3763           list<int> children;
3764           int num = osdmap.crush->get_children(*j, &children);
3765           num_osds_subtree[*j] = num;
3766         } else {
3767           list<int> children;
3768           int num = 0;
3769           int num_children = osdmap.crush->get_children(*j, &children);
3770           if (num_children == 0)
3771             continue;
3772           for (auto l = children.begin(); l != children.end(); ++l) {
3773             if (num_osds_subtree[*l] > 0) {
3774               num = num + num_osds_subtree[*l];
3775             }
3776           }
3777           num_osds_subtree[*j] = num;
3778         }
3779       }
3780     }
3781     num_down_in_osds = down_in_osds.size();
3782     assert(num_down_in_osds <= num_in_osds);
3783     if (num_down_in_osds > 0) {
3784       // summary of down subtree types and osds
3785       for (int type = max_type; type > 0; type--) {
3786         if (!osdmap.crush->get_type_name(type))
3787           continue;
3788         if (subtree_type_down[type].size() > 0) {
3789           ostringstream ss;
3790           ss << subtree_type_down[type].size() << " "
3791              << osdmap.crush->get_type_name(type);
3792           if (subtree_type_down[type].size() > 1) {
3793             ss << "s";
3794           }
3795           int sum_down_osds = 0;
3796           for (auto j = subtree_type_down[type].begin();
3797                j != subtree_type_down[type].end();
3798                ++j) {
3799             sum_down_osds = sum_down_osds + num_osds_subtree[*j];
3800           }
3801           ss << " (" << sum_down_osds << " osds) down";
3802           summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3803         }
3804       }
3805       ostringstream ss;
3806       ss << down_in_osds.size() << " osds down";
3807       summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3808
3809       if (detail) {
3810         // details of down subtree types
3811         for (int type = max_type; type > 0; type--) {
3812           if (!osdmap.crush->get_type_name(type))
3813             continue;
3814           for (auto j = subtree_type_down[type].rbegin();
3815                j != subtree_type_down[type].rend();
3816                ++j) {
3817             ostringstream ss;
3818             ss << osdmap.crush->get_type_name(type);
3819             ss << " ";
3820             ss << osdmap.crush->get_item_name(*j);
3821             // at the top level, do not print location
3822             if (type != max_type) {
3823               ss << " (";
3824               ss << osdmap.crush->get_full_location_ordered_string(*j);
3825               ss << ")";
3826             }
3827             int num = num_osds_subtree[*j];
3828             ss << " (" << num << " osds)";
3829             ss << " is down";
3830             detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3831           }
3832         }
3833         // details of down osds
3834         for (auto it = down_in_osds.begin(); it != down_in_osds.end(); ++it) {
3835           ostringstream ss;
3836           ss << "osd." << *it << " (";
3837           ss << osdmap.crush->get_full_location_ordered_string(*it);
3838           ss << ") is down";
3839           detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3840         }
3841       }
3842     }
3843
3844     if (!osds.empty()) {
3845       ostringstream ss;
3846       ss << osds.size() << " osds exist in the crush map but not in the osdmap";
3847       summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3848       if (detail) {
3849         ss << " (osds: " << osds << ")";
3850         detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3851       }
3852     }
3853
3854     // note: we leave it to ceph-mgr to generate details health warnings
3855     // with actual osd utilizations
3856
3857     // warn about flags
3858     uint64_t warn_flags =
3859       CEPH_OSDMAP_FULL |
3860       CEPH_OSDMAP_PAUSERD |
3861       CEPH_OSDMAP_PAUSEWR |
3862       CEPH_OSDMAP_PAUSEREC |
3863       CEPH_OSDMAP_NOUP |
3864       CEPH_OSDMAP_NODOWN |
3865       CEPH_OSDMAP_NOIN |
3866       CEPH_OSDMAP_NOOUT |
3867       CEPH_OSDMAP_NOBACKFILL |
3868       CEPH_OSDMAP_NORECOVER |
3869       CEPH_OSDMAP_NOSCRUB |
3870       CEPH_OSDMAP_NODEEP_SCRUB |
3871       CEPH_OSDMAP_NOTIERAGENT |
3872       CEPH_OSDMAP_NOREBALANCE;
3873     if (osdmap.test_flag(warn_flags)) {
3874       ostringstream ss;
3875       ss << osdmap.get_flag_string(osdmap.get_flags() & warn_flags)
3876          << " flag(s) set";
3877       summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3878       if (detail)
3879         detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3880     }
3881
3882     // old crush tunables?
3883     if (g_conf->mon_warn_on_legacy_crush_tunables) {
3884       string min = osdmap.crush->get_min_required_version();
3885       if (min < g_conf->mon_crush_min_required_version) {
3886         ostringstream ss;
3887         ss << "crush map has legacy tunables (require " << min
3888            << ", min is " << g_conf->mon_crush_min_required_version << ")";
3889         summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3890         if (detail) {
3891           ss << "; see http://docs.ceph.com/docs/master/rados/operations/crush-map/#tunables";
3892           detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3893         }
3894       }
3895     }
3896     if (g_conf->mon_warn_on_crush_straw_calc_version_zero) {
3897       if (osdmap.crush->get_straw_calc_version() == 0) {
3898         ostringstream ss;
3899         ss << "crush map has straw_calc_version=0";
3900         summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3901         if (detail) {
3902           ss << "; see http://docs.ceph.com/docs/master/rados/operations/crush-map/#tunables";
3903           detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3904         }
3905       }
3906     }
3907
3908     // hit_set-less cache_mode?
3909     if (g_conf->mon_warn_on_cache_pools_without_hit_sets) {
3910       int problem_cache_pools = 0;
3911       for (map<int64_t, pg_pool_t>::const_iterator p = osdmap.pools.begin();
3912            p != osdmap.pools.end();
3913            ++p) {
3914         const pg_pool_t& info = p->second;
3915         if (info.cache_mode_requires_hit_set() &&
3916             info.hit_set_params.get_type() == HitSet::TYPE_NONE) {
3917           ++problem_cache_pools;
3918           if (detail) {
3919             ostringstream ss;
3920             ss << "pool '" << osdmap.get_pool_name(p->first)
3921                << "' with cache_mode " << info.get_cache_mode_name()
3922                << " needs hit_set_type to be set but it is not";
3923             detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3924           }
3925         }
3926       }
3927       if (problem_cache_pools) {
3928         ostringstream ss;
3929         ss << problem_cache_pools << " cache pools are missing hit_sets";
3930         summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3931       }
3932     }
3933
3934     // Not using 'sortbitwise' and should be?
3935     if (!osdmap.test_flag(CEPH_OSDMAP_SORTBITWISE) &&
3936         (osdmap.get_up_osd_features() &
3937          CEPH_FEATURE_OSD_BITWISE_HOBJ_SORT)) {
3938       ostringstream ss;
3939       ss << "no legacy OSD present but 'sortbitwise' flag is not set";
3940       summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3941     }
3942
3943     // Warn if 'mon_osd_down_out_interval' is set to zero.
3944     // Having this option set to zero on the leader acts much like the
3945     // 'noout' flag.  It's hard to figure out what's going wrong with clusters
3946     // without the 'noout' flag set but acting like that just the same, so
3947     // we report a HEALTH_WARN in case this option is set to zero.
3948     // This is an ugly hack to get the warning out, but until we find a way
3949     // to spread global options throughout the mon cluster and have all mons
3950     // using a base set of the same options, we need to work around this sort
3951     // of things.
3952     // There's also the obvious drawback that if this is set on a single
3953     // monitor on a 3-monitor cluster, this warning will only be shown every
3954     // third monitor connection.
3955     if (g_conf->mon_warn_on_osd_down_out_interval_zero &&
3956         g_conf->mon_osd_down_out_interval == 0) {
3957       ostringstream ss;
3958       ss << "mon." << mon->name << " has mon_osd_down_out_interval set to 0";
3959       summary.push_back(make_pair(HEALTH_WARN, ss.str()));
3960       if (detail) {
3961         ss << "; this has the same effect as the 'noout' flag";
3962         detail->push_back(make_pair(HEALTH_WARN, ss.str()));
3963       }
3964     }
3965
3966     // warn about upgrade flags that can be set but are not.
3967     if (g_conf->mon_debug_no_require_luminous) {
3968       // ignore these checks
3969     } else if (HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_LUMINOUS) &&
3970                osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
3971       string msg = "all OSDs are running luminous or later but"
3972         " require_osd_release < luminous";
3973       summary.push_back(make_pair(HEALTH_WARN, msg));
3974       if (detail) {
3975         detail->push_back(make_pair(HEALTH_WARN, msg));
3976       }
3977     } else if (HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_KRAKEN) &&
3978                osdmap.require_osd_release < CEPH_RELEASE_KRAKEN) {
3979       string msg = "all OSDs are running kraken or later but"
3980         " require_osd_release < kraken";
3981       summary.push_back(make_pair(HEALTH_WARN, msg));
3982       if (detail) {
3983         detail->push_back(make_pair(HEALTH_WARN, msg));
3984       }
3985     } else if (HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_JEWEL) &&
3986                osdmap.require_osd_release < CEPH_RELEASE_JEWEL) {
3987       string msg = "all OSDs are running jewel or later but"
3988         " require_osd_release < jewel";
3989       summary.push_back(make_pair(HEALTH_WARN, msg));
3990       if (detail) {
3991         detail->push_back(make_pair(HEALTH_WARN, msg));
3992       }
3993     }
3994
3995     for (auto it : osdmap.get_pools()) {
3996       const pg_pool_t &pool = it.second;
3997       if (pool.has_flag(pg_pool_t::FLAG_FULL)) {
3998         const string& pool_name = osdmap.get_pool_name(it.first);
3999         stringstream ss;
4000         ss << "pool '" << pool_name << "' is full";
4001         summary.push_back(make_pair(HEALTH_WARN, ss.str()));
4002         if (detail)
4003           detail->push_back(make_pair(HEALTH_WARN, ss.str()));
4004       }
4005     }
4006   }
4007 }
4008
4009 void OSDMonitor::dump_info(Formatter *f)
4010 {
4011   f->open_object_section("osdmap");
4012   osdmap.dump(f);
4013   f->close_section();
4014
4015   f->open_array_section("osd_metadata");
4016   for (int i=0; i<osdmap.get_max_osd(); ++i) {
4017     if (osdmap.exists(i)) {
4018       f->open_object_section("osd");
4019       f->dump_unsigned("id", i);
4020       dump_osd_metadata(i, f, NULL);
4021       f->close_section();
4022     }
4023   }
4024   f->close_section();
4025
4026   f->dump_unsigned("osdmap_first_committed", get_first_committed());
4027   f->dump_unsigned("osdmap_last_committed", get_last_committed());
4028
4029   f->open_object_section("crushmap");
4030   osdmap.crush->dump(f);
4031   f->close_section();
4032 }
4033
4034 namespace {
4035   enum osd_pool_get_choices {
4036     SIZE, MIN_SIZE, CRASH_REPLAY_INTERVAL,
4037     PG_NUM, PGP_NUM, CRUSH_RULE, HASHPSPOOL,
4038     NODELETE, NOPGCHANGE, NOSIZECHANGE,
4039     WRITE_FADVISE_DONTNEED, NOSCRUB, NODEEP_SCRUB,
4040     HIT_SET_TYPE, HIT_SET_PERIOD, HIT_SET_COUNT, HIT_SET_FPP,
4041     USE_GMT_HITSET, AUID, TARGET_MAX_OBJECTS, TARGET_MAX_BYTES,
4042     CACHE_TARGET_DIRTY_RATIO, CACHE_TARGET_DIRTY_HIGH_RATIO,
4043     CACHE_TARGET_FULL_RATIO,
4044     CACHE_MIN_FLUSH_AGE, CACHE_MIN_EVICT_AGE,
4045     ERASURE_CODE_PROFILE, MIN_READ_RECENCY_FOR_PROMOTE,
4046     MIN_WRITE_RECENCY_FOR_PROMOTE, FAST_READ,
4047     HIT_SET_GRADE_DECAY_RATE, HIT_SET_SEARCH_LAST_N,
4048     SCRUB_MIN_INTERVAL, SCRUB_MAX_INTERVAL, DEEP_SCRUB_INTERVAL,
4049     RECOVERY_PRIORITY, RECOVERY_OP_PRIORITY, SCRUB_PRIORITY,
4050     COMPRESSION_MODE, COMPRESSION_ALGORITHM, COMPRESSION_REQUIRED_RATIO,
4051     COMPRESSION_MAX_BLOB_SIZE, COMPRESSION_MIN_BLOB_SIZE,
4052     CSUM_TYPE, CSUM_MAX_BLOCK, CSUM_MIN_BLOCK };
4053
4054   std::set<osd_pool_get_choices>
4055     subtract_second_from_first(const std::set<osd_pool_get_choices>& first,
4056                                 const std::set<osd_pool_get_choices>& second)
4057     {
4058       std::set<osd_pool_get_choices> result;
4059       std::set_difference(first.begin(), first.end(),
4060                           second.begin(), second.end(),
4061                           std::inserter(result, result.end()));
4062       return result;
4063     }
4064 }
4065
4066
4067 bool OSDMonitor::preprocess_command(MonOpRequestRef op)
4068 {
4069   op->mark_osdmon_event(__func__);
4070   MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
4071   int r = 0;
4072   bufferlist rdata;
4073   stringstream ss, ds;
4074
4075   map<string, cmd_vartype> cmdmap;
4076   if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
4077     string rs = ss.str();
4078     mon->reply_command(op, -EINVAL, rs, get_last_committed());
4079     return true;
4080   }
4081
4082   MonSession *session = m->get_session();
4083   if (!session) {
4084     mon->reply_command(op, -EACCES, "access denied", get_last_committed());
4085     return true;
4086   }
4087
4088   string prefix;
4089   cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
4090
4091   string format;
4092   cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
4093   boost::scoped_ptr<Formatter> f(Formatter::create(format));
4094
4095   if (prefix == "osd stat") {
4096     osdmap.print_summary(f.get(), ds, "");
4097     if (f)
4098       f->flush(rdata);
4099     else
4100       rdata.append(ds);
4101   }
4102   else if (prefix == "osd perf" ||
4103            prefix == "osd blocked-by") {
4104     r = mon->pgservice->process_pg_command(prefix, cmdmap,
4105                                            osdmap, f.get(), &ss, &rdata);
4106   }
4107   else if (prefix == "osd dump" ||
4108            prefix == "osd tree" ||
4109            prefix == "osd ls" ||
4110            prefix == "osd getmap" ||
4111            prefix == "osd getcrushmap" ||
4112            prefix == "osd ls-tree") {
4113     string val;
4114
4115     epoch_t epoch = 0;
4116     int64_t epochnum;
4117     cmd_getval(g_ceph_context, cmdmap, "epoch", epochnum, (int64_t)osdmap.get_epoch());
4118     epoch = epochnum;
4119     
4120     bufferlist osdmap_bl;
4121     int err = get_version_full(epoch, osdmap_bl);
4122     if (err == -ENOENT) {
4123       r = -ENOENT;
4124       ss << "there is no map for epoch " << epoch;
4125       goto reply;
4126     }
4127     assert(err == 0);
4128     assert(osdmap_bl.length());
4129
4130     OSDMap *p;
4131     if (epoch == osdmap.get_epoch()) {
4132       p = &osdmap;
4133     } else {
4134       p = new OSDMap;
4135       p->decode(osdmap_bl);
4136     }
4137
4138     auto sg = make_scope_guard([&] {
4139       if (p != &osdmap) {
4140         delete p;
4141       }
4142     });
4143
4144     if (prefix == "osd dump") {
4145       stringstream ds;
4146       if (f) {
4147         f->open_object_section("osdmap");
4148         p->dump(f.get());
4149         f->close_section();
4150         f->flush(ds);
4151       } else {
4152         p->print(ds);
4153       }
4154       rdata.append(ds);
4155       if (!f)
4156         ds << " ";
4157     } else if (prefix == "osd ls") {
4158       if (f) {
4159         f->open_array_section("osds");
4160         for (int i = 0; i < osdmap.get_max_osd(); i++) {
4161           if (osdmap.exists(i)) {
4162             f->dump_int("osd", i);
4163           }
4164         }
4165         f->close_section();
4166         f->flush(ds);
4167       } else {
4168         bool first = true;
4169         for (int i = 0; i < osdmap.get_max_osd(); i++) {
4170           if (osdmap.exists(i)) {
4171             if (!first)
4172               ds << "\n";
4173             first = false;
4174             ds << i;
4175           }
4176         }
4177       }
4178       rdata.append(ds);
4179     } else if (prefix == "osd tree") {
4180       vector<string> states;
4181       cmd_getval(g_ceph_context, cmdmap, "states", states);
4182       unsigned filter = 0;
4183       for (auto& s : states) {
4184         if (s == "up") {
4185           filter |= OSDMap::DUMP_UP;
4186         } else if (s == "down") {
4187           filter |= OSDMap::DUMP_DOWN;
4188         } else if (s == "in") {
4189           filter |= OSDMap::DUMP_IN;
4190         } else if (s == "out") {
4191           filter |= OSDMap::DUMP_OUT;
4192         } else if (s == "destroyed") {
4193           filter |= OSDMap::DUMP_DESTROYED;
4194         } else {
4195           ss << "unrecognized state '" << s << "'";
4196           r = -EINVAL;
4197           goto reply;
4198         }
4199       }
4200       if ((filter & (OSDMap::DUMP_IN|OSDMap::DUMP_OUT)) ==
4201           (OSDMap::DUMP_IN|OSDMap::DUMP_OUT)) {
4202         ss << "cannot specify both 'in' and 'out'";
4203         r = -EINVAL;
4204         goto reply;
4205       }
4206       if (((filter & (OSDMap::DUMP_UP|OSDMap::DUMP_DOWN)) ==
4207            (OSDMap::DUMP_UP|OSDMap::DUMP_DOWN)) ||
4208            ((filter & (OSDMap::DUMP_UP|OSDMap::DUMP_DESTROYED)) ==
4209            (OSDMap::DUMP_UP|OSDMap::DUMP_DESTROYED)) ||
4210            ((filter & (OSDMap::DUMP_DOWN|OSDMap::DUMP_DESTROYED)) ==
4211            (OSDMap::DUMP_DOWN|OSDMap::DUMP_DESTROYED))) {
4212         ss << "can specify only one of 'up', 'down' and 'destroyed'";
4213         r = -EINVAL;
4214         goto reply;
4215       }
4216       if (f) {
4217         f->open_object_section("tree");
4218         p->print_tree(f.get(), NULL, filter);
4219         f->close_section();
4220         f->flush(ds);
4221       } else {
4222         p->print_tree(NULL, &ds, filter);
4223       }
4224       rdata.append(ds);
4225     } else if (prefix == "osd getmap") {
4226       rdata.append(osdmap_bl);
4227       ss << "got osdmap epoch " << p->get_epoch();
4228     } else if (prefix == "osd getcrushmap") {
4229       p->crush->encode(rdata, mon->get_quorum_con_features());
4230       ss << p->get_crush_version();
4231     } else if (prefix == "osd ls-tree") {
4232       string bucket_name;
4233       cmd_getval(g_ceph_context, cmdmap, "name", bucket_name);
4234       set<int> osds;
4235       r = p->get_osds_by_bucket_name(bucket_name, &osds);
4236       if (r == -ENOENT) {
4237         ss << "\"" << bucket_name << "\" does not exist";
4238         goto reply;
4239       } else if (r < 0) {
4240         ss << "can not parse bucket name:\"" << bucket_name << "\"";
4241         goto reply;
4242       }
4243
4244       if (f) {
4245         f->open_array_section("osds");
4246         for (auto &i : osds) {
4247           if (osdmap.exists(i)) {
4248             f->dump_int("osd", i);
4249           }
4250         }
4251         f->close_section();
4252         f->flush(ds);
4253       } else {
4254         bool first = true;
4255         for (auto &i : osds) {
4256           if (osdmap.exists(i)) {
4257             if (!first)
4258               ds << "\n";
4259             first = false;
4260             ds << i;
4261           }
4262         }
4263       }
4264
4265       rdata.append(ds);
4266     }
4267   } else if (prefix == "osd df") {
4268     string method;
4269     cmd_getval(g_ceph_context, cmdmap, "output_method", method);
4270     print_osd_utilization(osdmap, mon->pgservice, ds,
4271                           f.get(), method == "tree");
4272     rdata.append(ds);
4273   } else if (prefix == "osd getmaxosd") {
4274     if (f) {
4275       f->open_object_section("getmaxosd");
4276       f->dump_unsigned("epoch", osdmap.get_epoch());
4277       f->dump_int("max_osd", osdmap.get_max_osd());
4278       f->close_section();
4279       f->flush(rdata);
4280     } else {
4281       ds << "max_osd = " << osdmap.get_max_osd() << " in epoch " << osdmap.get_epoch();
4282       rdata.append(ds);
4283     }
4284   } else if (prefix == "osd utilization") {
4285     string out;
4286     osdmap.summarize_mapping_stats(NULL, NULL, &out, f.get());
4287     if (f)
4288       f->flush(rdata);
4289     else
4290       rdata.append(out);
4291     r = 0;
4292     goto reply;
4293   } else if (prefix  == "osd find") {
4294     int64_t osd;
4295     if (!cmd_getval(g_ceph_context, cmdmap, "id", osd)) {
4296       ss << "unable to parse osd id value '"
4297          << cmd_vartype_stringify(cmdmap["id"]) << "'";
4298       r = -EINVAL;
4299       goto reply;
4300     }
4301     if (!osdmap.exists(osd)) {
4302       ss << "osd." << osd << " does not exist";
4303       r = -ENOENT;
4304       goto reply;
4305     }
4306     string format;
4307     cmd_getval(g_ceph_context, cmdmap, "format", format);
4308     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
4309     f->open_object_section("osd_location");
4310     f->dump_int("osd", osd);
4311     f->dump_stream("ip") << osdmap.get_addr(osd);
4312     f->open_object_section("crush_location");
4313     map<string,string> loc = osdmap.crush->get_full_location(osd);
4314     for (map<string,string>::iterator p = loc.begin(); p != loc.end(); ++p)
4315       f->dump_string(p->first.c_str(), p->second);
4316     f->close_section();
4317     f->close_section();
4318     f->flush(rdata);
4319   } else if (prefix == "osd metadata") {
4320     int64_t osd = -1;
4321     if (cmd_vartype_stringify(cmdmap["id"]).size() &&
4322         !cmd_getval(g_ceph_context, cmdmap, "id", osd)) {
4323       ss << "unable to parse osd id value '"
4324          << cmd_vartype_stringify(cmdmap["id"]) << "'";
4325       r = -EINVAL;
4326       goto reply;
4327     }
4328     if (osd >= 0 && !osdmap.exists(osd)) {
4329       ss << "osd." << osd << " does not exist";
4330       r = -ENOENT;
4331       goto reply;
4332     }
4333     string format;
4334     cmd_getval(g_ceph_context, cmdmap, "format", format);
4335     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
4336     if (osd >= 0) {
4337       f->open_object_section("osd_metadata");
4338       f->dump_unsigned("id", osd);
4339       r = dump_osd_metadata(osd, f.get(), &ss);
4340       if (r < 0)
4341         goto reply;
4342       f->close_section();
4343     } else {
4344       r = 0;
4345       f->open_array_section("osd_metadata");
4346       for (int i=0; i<osdmap.get_max_osd(); ++i) {
4347         if (osdmap.exists(i)) {
4348           f->open_object_section("osd");
4349           f->dump_unsigned("id", i);
4350           r = dump_osd_metadata(i, f.get(), NULL);
4351           if (r == -EINVAL || r == -ENOENT) {
4352             // Drop error, continue to get other daemons' metadata
4353             dout(4) << "No metadata for osd." << i << dendl;
4354             r = 0;
4355           } else if (r < 0) {
4356             // Unexpected error
4357             goto reply;
4358           }
4359           f->close_section();
4360         }
4361       }
4362       f->close_section();
4363     }
4364     f->flush(rdata);
4365   } else if (prefix == "osd versions") {
4366     if (!f)
4367       f.reset(Formatter::create("json-pretty"));
4368     count_metadata("ceph_version", f.get());
4369     f->flush(rdata);
4370     r = 0;
4371   } else if (prefix == "osd count-metadata") {
4372     if (!f)
4373       f.reset(Formatter::create("json-pretty"));
4374     string field;
4375     cmd_getval(g_ceph_context, cmdmap, "property", field);
4376     count_metadata(field, f.get());
4377     f->flush(rdata);
4378     r = 0;
4379   } else if (prefix == "osd map") {
4380     string poolstr, objstr, namespacestr;
4381     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
4382     cmd_getval(g_ceph_context, cmdmap, "object", objstr);
4383     cmd_getval(g_ceph_context, cmdmap, "nspace", namespacestr);
4384
4385     int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
4386     if (pool < 0) {
4387       ss << "pool " << poolstr << " does not exist";
4388       r = -ENOENT;
4389       goto reply;
4390     }
4391     object_locator_t oloc(pool, namespacestr);
4392     object_t oid(objstr);
4393     pg_t pgid = osdmap.object_locator_to_pg(oid, oloc);
4394     pg_t mpgid = osdmap.raw_pg_to_pg(pgid);
4395     vector<int> up, acting;
4396     int up_p, acting_p;
4397     osdmap.pg_to_up_acting_osds(mpgid, &up, &up_p, &acting, &acting_p);
4398
4399     string fullobjname;
4400     if (!namespacestr.empty())
4401       fullobjname = namespacestr + string("/") + oid.name;
4402     else
4403       fullobjname = oid.name;
4404     if (f) {
4405       f->open_object_section("osd_map");
4406       f->dump_unsigned("epoch", osdmap.get_epoch());
4407       f->dump_string("pool", poolstr);
4408       f->dump_int("pool_id", pool);
4409       f->dump_stream("objname") << fullobjname;
4410       f->dump_stream("raw_pgid") << pgid;
4411       f->dump_stream("pgid") << mpgid;
4412       f->open_array_section("up");
4413       for (vector<int>::iterator p = up.begin(); p != up.end(); ++p)
4414         f->dump_int("osd", *p);
4415       f->close_section();
4416       f->dump_int("up_primary", up_p);
4417       f->open_array_section("acting");
4418       for (vector<int>::iterator p = acting.begin(); p != acting.end(); ++p)
4419         f->dump_int("osd", *p);
4420       f->close_section();
4421       f->dump_int("acting_primary", acting_p);
4422       f->close_section(); // osd_map
4423       f->flush(rdata);
4424     } else {
4425       ds << "osdmap e" << osdmap.get_epoch()
4426         << " pool '" << poolstr << "' (" << pool << ")"
4427         << " object '" << fullobjname << "' ->"
4428         << " pg " << pgid << " (" << mpgid << ")"
4429         << " -> up (" << pg_vector_string(up) << ", p" << up_p << ") acting ("
4430         << pg_vector_string(acting) << ", p" << acting_p << ")";
4431       rdata.append(ds);
4432     }
4433
4434   } else if (prefix == "pg map") {
4435     pg_t pgid;
4436     string pgidstr;
4437     cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr);
4438     if (!pgid.parse(pgidstr.c_str())) {
4439       ss << "invalid pgid '" << pgidstr << "'";
4440       r = -EINVAL;
4441       goto reply;
4442     }
4443     vector<int> up, acting;
4444     if (!osdmap.have_pg_pool(pgid.pool())) {
4445       ss << "pg '" << pgidstr << "' does not exist";
4446       r = -ENOENT;
4447       goto reply;
4448     }
4449     pg_t mpgid = osdmap.raw_pg_to_pg(pgid);
4450     osdmap.pg_to_up_acting_osds(pgid, up, acting);
4451     if (f) {
4452       f->open_object_section("pg_map");
4453       f->dump_unsigned("epoch", osdmap.get_epoch());
4454       f->dump_stream("raw_pgid") << pgid;
4455       f->dump_stream("pgid") << mpgid;
4456       f->open_array_section("up");
4457       for (auto osd : up) {
4458         f->dump_int("up_osd", osd);
4459       }
4460       f->close_section();
4461       f->open_array_section("acting");
4462       for (auto osd : acting) {
4463         f->dump_int("acting_osd", osd);
4464       }
4465       f->close_section();
4466       f->close_section();
4467       f->flush(rdata);
4468     } else {
4469       ds << "osdmap e" << osdmap.get_epoch()
4470          << " pg " << pgid << " (" << mpgid << ")"
4471          << " -> up " << up << " acting " << acting;
4472       rdata.append(ds);
4473     }
4474     goto reply;
4475
4476   } else if (prefix == "osd scrub" ||
4477              prefix == "osd deep-scrub" ||
4478              prefix == "osd repair") {
4479     string whostr;
4480     cmd_getval(g_ceph_context, cmdmap, "who", whostr);
4481     vector<string> pvec;
4482     get_str_vec(prefix, pvec);
4483
4484     if (whostr == "*" || whostr == "all" || whostr == "any") {
4485       ss << "osds ";
4486       int c = 0;
4487       for (int i = 0; i < osdmap.get_max_osd(); i++)
4488         if (osdmap.is_up(i)) {
4489           ss << (c++ ? "," : "") << i;
4490           mon->try_send_message(new MOSDScrub(osdmap.get_fsid(),
4491                                               pvec.back() == "repair",
4492                                               pvec.back() == "deep-scrub"),
4493                                 osdmap.get_inst(i));
4494         }
4495       r = 0;
4496       ss << " instructed to " << pvec.back();
4497     } else {
4498       long osd = parse_osd_id(whostr.c_str(), &ss);
4499       if (osd < 0) {
4500         r = -EINVAL;
4501       } else if (osdmap.is_up(osd)) {
4502         mon->try_send_message(new MOSDScrub(osdmap.get_fsid(),
4503                                             pvec.back() == "repair",
4504                                             pvec.back() == "deep-scrub"),
4505                               osdmap.get_inst(osd));
4506         ss << "osd." << osd << " instructed to " << pvec.back();
4507       } else {
4508         ss << "osd." << osd << " is not up";
4509         r = -EAGAIN;
4510       }
4511     }
4512   } else if (prefix == "osd lspools") {
4513     int64_t auid;
4514     cmd_getval(g_ceph_context, cmdmap, "auid", auid, int64_t(0));
4515     if (f)
4516       f->open_array_section("pools");
4517     for (map<int64_t, pg_pool_t>::iterator p = osdmap.pools.begin();
4518          p != osdmap.pools.end();
4519          ++p) {
4520       if (!auid || p->second.auid == (uint64_t)auid) {
4521         if (f) {
4522           f->open_object_section("pool");
4523           f->dump_int("poolnum", p->first);
4524           f->dump_string("poolname", osdmap.pool_name[p->first]);
4525           f->close_section();
4526         } else {
4527           ds << p->first << ' ' << osdmap.pool_name[p->first] << ',';
4528         }
4529       }
4530     }
4531     if (f) {
4532       f->close_section();
4533       f->flush(ds);
4534     }
4535     rdata.append(ds);
4536   } else if (prefix == "osd blacklist ls") {
4537     if (f)
4538       f->open_array_section("blacklist");
4539
4540     for (ceph::unordered_map<entity_addr_t,utime_t>::iterator p = osdmap.blacklist.begin();
4541          p != osdmap.blacklist.end();
4542          ++p) {
4543       if (f) {
4544         f->open_object_section("entry");
4545         f->dump_stream("addr") << p->first;
4546         f->dump_stream("until") << p->second;
4547         f->close_section();
4548       } else {
4549         stringstream ss;
4550         string s;
4551         ss << p->first << " " << p->second;
4552         getline(ss, s);
4553         s += "\n";
4554         rdata.append(s);
4555       }
4556     }
4557     if (f) {
4558       f->close_section();
4559       f->flush(rdata);
4560     }
4561     ss << "listed " << osdmap.blacklist.size() << " entries";
4562
4563   } else if (prefix == "osd pool ls") {
4564     string detail;
4565     cmd_getval(g_ceph_context, cmdmap, "detail", detail);
4566     if (!f && detail == "detail") {
4567       ostringstream ss;
4568       osdmap.print_pools(ss);
4569       rdata.append(ss.str());
4570     } else {
4571       if (f)
4572         f->open_array_section("pools");
4573       for (map<int64_t,pg_pool_t>::const_iterator it = osdmap.get_pools().begin();
4574            it != osdmap.get_pools().end();
4575            ++it) {
4576         if (f) {
4577           if (detail == "detail") {
4578             f->open_object_section("pool");
4579             f->dump_string("pool_name", osdmap.get_pool_name(it->first));
4580             it->second.dump(f.get());
4581             f->close_section();
4582           } else {
4583             f->dump_string("pool_name", osdmap.get_pool_name(it->first));
4584           }
4585         } else {
4586           rdata.append(osdmap.get_pool_name(it->first) + "\n");
4587         }
4588       }
4589       if (f) {
4590         f->close_section();
4591         f->flush(rdata);
4592       }
4593     }
4594
4595   } else if (prefix == "osd crush get-tunable") {
4596     string tunable;
4597     cmd_getval(g_ceph_context, cmdmap, "tunable", tunable);
4598     ostringstream rss;
4599     if (f)
4600       f->open_object_section("tunable");
4601     if (tunable == "straw_calc_version") {
4602       if (f)
4603         f->dump_int(tunable.c_str(), osdmap.crush->get_straw_calc_version());
4604       else
4605         rss << osdmap.crush->get_straw_calc_version() << "\n";
4606     } else {
4607       r = -EINVAL;
4608       goto reply;
4609     }
4610     if (f) {
4611       f->close_section();
4612       f->flush(rdata);
4613     } else {
4614       rdata.append(rss.str());
4615     }
4616     r = 0;
4617
4618   } else if (prefix == "osd pool get") {
4619     string poolstr;
4620     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
4621     int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
4622     if (pool < 0) {
4623       ss << "unrecognized pool '" << poolstr << "'";
4624       r = -ENOENT;
4625       goto reply;
4626     }
4627
4628     const pg_pool_t *p = osdmap.get_pg_pool(pool);
4629     string var;
4630     cmd_getval(g_ceph_context, cmdmap, "var", var);
4631
4632     typedef std::map<std::string, osd_pool_get_choices> choices_map_t;
4633     const choices_map_t ALL_CHOICES = {
4634       {"size", SIZE},
4635       {"min_size", MIN_SIZE},
4636       {"crash_replay_interval", CRASH_REPLAY_INTERVAL},
4637       {"pg_num", PG_NUM}, {"pgp_num", PGP_NUM},
4638       {"crush_rule", CRUSH_RULE},
4639       {"hashpspool", HASHPSPOOL}, {"nodelete", NODELETE},
4640       {"nopgchange", NOPGCHANGE}, {"nosizechange", NOSIZECHANGE},
4641       {"noscrub", NOSCRUB}, {"nodeep-scrub", NODEEP_SCRUB},
4642       {"write_fadvise_dontneed", WRITE_FADVISE_DONTNEED},
4643       {"hit_set_type", HIT_SET_TYPE}, {"hit_set_period", HIT_SET_PERIOD},
4644       {"hit_set_count", HIT_SET_COUNT}, {"hit_set_fpp", HIT_SET_FPP},
4645       {"use_gmt_hitset", USE_GMT_HITSET},
4646       {"auid", AUID}, {"target_max_objects", TARGET_MAX_OBJECTS},
4647       {"target_max_bytes", TARGET_MAX_BYTES},
4648       {"cache_target_dirty_ratio", CACHE_TARGET_DIRTY_RATIO},
4649       {"cache_target_dirty_high_ratio", CACHE_TARGET_DIRTY_HIGH_RATIO},
4650       {"cache_target_full_ratio", CACHE_TARGET_FULL_RATIO},
4651       {"cache_min_flush_age", CACHE_MIN_FLUSH_AGE},
4652       {"cache_min_evict_age", CACHE_MIN_EVICT_AGE},
4653       {"erasure_code_profile", ERASURE_CODE_PROFILE},
4654       {"min_read_recency_for_promote", MIN_READ_RECENCY_FOR_PROMOTE},
4655       {"min_write_recency_for_promote", MIN_WRITE_RECENCY_FOR_PROMOTE},
4656       {"fast_read", FAST_READ},
4657       {"hit_set_grade_decay_rate", HIT_SET_GRADE_DECAY_RATE},
4658       {"hit_set_search_last_n", HIT_SET_SEARCH_LAST_N},
4659       {"scrub_min_interval", SCRUB_MIN_INTERVAL},
4660       {"scrub_max_interval", SCRUB_MAX_INTERVAL},
4661       {"deep_scrub_interval", DEEP_SCRUB_INTERVAL},
4662       {"recovery_priority", RECOVERY_PRIORITY},
4663       {"recovery_op_priority", RECOVERY_OP_PRIORITY},
4664       {"scrub_priority", SCRUB_PRIORITY},
4665       {"compression_mode", COMPRESSION_MODE},
4666       {"compression_algorithm", COMPRESSION_ALGORITHM},
4667       {"compression_required_ratio", COMPRESSION_REQUIRED_RATIO},
4668       {"compression_max_blob_size", COMPRESSION_MAX_BLOB_SIZE},
4669       {"compression_min_blob_size", COMPRESSION_MIN_BLOB_SIZE},
4670       {"csum_type", CSUM_TYPE},
4671       {"csum_max_block", CSUM_MAX_BLOCK},
4672       {"csum_min_block", CSUM_MIN_BLOCK},
4673     };
4674
4675     typedef std::set<osd_pool_get_choices> choices_set_t;
4676
4677     const choices_set_t ONLY_TIER_CHOICES = {
4678       HIT_SET_TYPE, HIT_SET_PERIOD, HIT_SET_COUNT, HIT_SET_FPP,
4679       TARGET_MAX_OBJECTS, TARGET_MAX_BYTES, CACHE_TARGET_FULL_RATIO,
4680       CACHE_TARGET_DIRTY_RATIO, CACHE_TARGET_DIRTY_HIGH_RATIO,
4681       CACHE_MIN_FLUSH_AGE, CACHE_MIN_EVICT_AGE,
4682       MIN_READ_RECENCY_FOR_PROMOTE,
4683       MIN_WRITE_RECENCY_FOR_PROMOTE,
4684       HIT_SET_GRADE_DECAY_RATE, HIT_SET_SEARCH_LAST_N
4685     };
4686     const choices_set_t ONLY_ERASURE_CHOICES = {
4687       ERASURE_CODE_PROFILE
4688     };
4689
4690     choices_set_t selected_choices;
4691     if (var == "all") {
4692       for(choices_map_t::const_iterator it = ALL_CHOICES.begin();
4693           it != ALL_CHOICES.end(); ++it) {
4694         selected_choices.insert(it->second);
4695       }
4696
4697       if(!p->is_tier()) {
4698         selected_choices = subtract_second_from_first(selected_choices,
4699                                                       ONLY_TIER_CHOICES);
4700       }
4701
4702       if(!p->is_erasure()) {
4703         selected_choices = subtract_second_from_first(selected_choices,
4704                                                       ONLY_ERASURE_CHOICES);
4705       }
4706     } else /* var != "all" */  {
4707       choices_map_t::const_iterator found = ALL_CHOICES.find(var);
4708       osd_pool_get_choices selected = found->second;
4709
4710       if (!p->is_tier() &&
4711           ONLY_TIER_CHOICES.find(selected) != ONLY_TIER_CHOICES.end()) {
4712         ss << "pool '" << poolstr
4713            << "' is not a tier pool: variable not applicable";
4714         r = -EACCES;
4715         goto reply;
4716       }
4717
4718       if (!p->is_erasure() &&
4719           ONLY_ERASURE_CHOICES.find(selected)
4720           != ONLY_ERASURE_CHOICES.end()) {
4721         ss << "pool '" << poolstr
4722            << "' is not a erasure pool: variable not applicable";
4723         r = -EACCES;
4724         goto reply;
4725       }
4726
4727       selected_choices.insert(selected);
4728     }
4729
4730     if (f) {
4731       for(choices_set_t::const_iterator it = selected_choices.begin();
4732           it != selected_choices.end(); ++it) {
4733         choices_map_t::const_iterator i;
4734         for (i = ALL_CHOICES.begin(); i != ALL_CHOICES.end(); ++i) {
4735           if (i->second == *it) {
4736             break;
4737           }
4738         }
4739         assert(i != ALL_CHOICES.end());
4740         bool pool_opt = pool_opts_t::is_opt_name(i->first);
4741         if (!pool_opt) {
4742           f->open_object_section("pool");
4743           f->dump_string("pool", poolstr);
4744           f->dump_int("pool_id", pool);
4745         }
4746         switch(*it) {
4747           case PG_NUM:
4748             f->dump_int("pg_num", p->get_pg_num());
4749             break;
4750           case PGP_NUM:
4751             f->dump_int("pgp_num", p->get_pgp_num());
4752             break;
4753           case AUID:
4754             f->dump_int("auid", p->get_auid());
4755             break;
4756           case SIZE:
4757             f->dump_int("size", p->get_size());
4758             break;
4759           case MIN_SIZE:
4760             f->dump_int("min_size", p->get_min_size());
4761             break;
4762           case CRASH_REPLAY_INTERVAL:
4763             f->dump_int("crash_replay_interval",
4764                         p->get_crash_replay_interval());
4765             break;
4766           case CRUSH_RULE:
4767             if (osdmap.crush->rule_exists(p->get_crush_rule())) {
4768               f->dump_string("crush_rule", osdmap.crush->get_rule_name(
4769                                p->get_crush_rule()));
4770             } else {
4771               f->dump_string("crush_rule", stringify(p->get_crush_rule()));
4772             }
4773             break;
4774           case HASHPSPOOL:
4775           case NODELETE:
4776           case NOPGCHANGE:
4777           case NOSIZECHANGE:
4778           case WRITE_FADVISE_DONTNEED:
4779           case NOSCRUB:
4780           case NODEEP_SCRUB:
4781             f->dump_string(i->first.c_str(),
4782                            p->has_flag(pg_pool_t::get_flag_by_name(i->first)) ?
4783                            "true" : "false");
4784             break;
4785           case HIT_SET_PERIOD:
4786             f->dump_int("hit_set_period", p->hit_set_period);
4787             break;
4788           case HIT_SET_COUNT:
4789             f->dump_int("hit_set_count", p->hit_set_count);
4790             break;
4791           case HIT_SET_TYPE:
4792             f->dump_string("hit_set_type",
4793                            HitSet::get_type_name(p->hit_set_params.get_type()));
4794             break;
4795           case HIT_SET_FPP:
4796             {
4797               if (p->hit_set_params.get_type() == HitSet::TYPE_BLOOM) {
4798                 BloomHitSet::Params *bloomp =
4799                   static_cast<BloomHitSet::Params*>(p->hit_set_params.impl.get());
4800                 f->dump_float("hit_set_fpp", bloomp->get_fpp());
4801               } else if(var != "all") {
4802                 f->close_section();
4803                 ss << "hit set is not of type Bloom; " <<
4804                   "invalid to get a false positive rate!";
4805                 r = -EINVAL;
4806                 goto reply;
4807               }
4808             }
4809             break;
4810           case USE_GMT_HITSET:
4811             f->dump_bool("use_gmt_hitset", p->use_gmt_hitset);
4812             break;
4813           case TARGET_MAX_OBJECTS:
4814             f->dump_unsigned("target_max_objects", p->target_max_objects);
4815             break;
4816           case TARGET_MAX_BYTES:
4817             f->dump_unsigned("target_max_bytes", p->target_max_bytes);
4818             break;
4819           case CACHE_TARGET_DIRTY_RATIO:
4820             f->dump_unsigned("cache_target_dirty_ratio_micro",
4821                              p->cache_target_dirty_ratio_micro);
4822             f->dump_float("cache_target_dirty_ratio",
4823                           ((float)p->cache_target_dirty_ratio_micro/1000000));
4824             break;
4825           case CACHE_TARGET_DIRTY_HIGH_RATIO:
4826             f->dump_unsigned("cache_target_dirty_high_ratio_micro",
4827                              p->cache_target_dirty_high_ratio_micro);
4828             f->dump_float("cache_target_dirty_high_ratio",
4829                           ((float)p->cache_target_dirty_high_ratio_micro/1000000));
4830             break;
4831           case CACHE_TARGET_FULL_RATIO:
4832             f->dump_unsigned("cache_target_full_ratio_micro",
4833                              p->cache_target_full_ratio_micro);
4834             f->dump_float("cache_target_full_ratio",
4835                           ((float)p->cache_target_full_ratio_micro/1000000));
4836             break;
4837           case CACHE_MIN_FLUSH_AGE:
4838             f->dump_unsigned("cache_min_flush_age", p->cache_min_flush_age);
4839             break;
4840           case CACHE_MIN_EVICT_AGE:
4841             f->dump_unsigned("cache_min_evict_age", p->cache_min_evict_age);
4842             break;
4843           case ERASURE_CODE_PROFILE:
4844             f->dump_string("erasure_code_profile", p->erasure_code_profile);
4845             break;
4846           case MIN_READ_RECENCY_FOR_PROMOTE:
4847             f->dump_int("min_read_recency_for_promote",
4848                         p->min_read_recency_for_promote);
4849             break;
4850           case MIN_WRITE_RECENCY_FOR_PROMOTE:
4851             f->dump_int("min_write_recency_for_promote",
4852                         p->min_write_recency_for_promote);
4853             break;
4854           case FAST_READ:
4855             f->dump_int("fast_read", p->fast_read);
4856             break;
4857           case HIT_SET_GRADE_DECAY_RATE:
4858             f->dump_int("hit_set_grade_decay_rate",
4859                         p->hit_set_grade_decay_rate);
4860             break;
4861           case HIT_SET_SEARCH_LAST_N:
4862             f->dump_int("hit_set_search_last_n",
4863                         p->hit_set_search_last_n);
4864             break;
4865           case SCRUB_MIN_INTERVAL:
4866           case SCRUB_MAX_INTERVAL:
4867           case DEEP_SCRUB_INTERVAL:
4868           case RECOVERY_PRIORITY:
4869           case RECOVERY_OP_PRIORITY:
4870           case SCRUB_PRIORITY:
4871           case COMPRESSION_MODE:
4872           case COMPRESSION_ALGORITHM:
4873           case COMPRESSION_REQUIRED_RATIO:
4874           case COMPRESSION_MAX_BLOB_SIZE:
4875           case COMPRESSION_MIN_BLOB_SIZE:
4876           case CSUM_TYPE:
4877           case CSUM_MAX_BLOCK:
4878           case CSUM_MIN_BLOCK:
4879             pool_opts_t::key_t key = pool_opts_t::get_opt_desc(i->first).key;
4880             if (p->opts.is_set(key)) {
4881               f->open_object_section("pool");
4882               f->dump_string("pool", poolstr);
4883               f->dump_int("pool_id", pool);
4884               if(*it == CSUM_TYPE) {
4885                 int val;
4886                 p->opts.get(pool_opts_t::CSUM_TYPE, &val);
4887                 f->dump_string(i->first.c_str(), Checksummer::get_csum_type_string(val));
4888               } else {
4889                 p->opts.dump(i->first, f.get());
4890               }
4891               f->close_section();
4892               f->flush(rdata);
4893             }
4894             break;
4895         }
4896         if (!pool_opt) {
4897           f->close_section();
4898           f->flush(rdata);
4899         }
4900       }
4901
4902     } else /* !f */ {
4903       for(choices_set_t::const_iterator it = selected_choices.begin();
4904           it != selected_choices.end(); ++it) {
4905         choices_map_t::const_iterator i;
4906         switch(*it) {
4907           case PG_NUM:
4908             ss << "pg_num: " << p->get_pg_num() << "\n";
4909             break;
4910           case PGP_NUM:
4911             ss << "pgp_num: " << p->get_pgp_num() << "\n";
4912             break;
4913           case AUID:
4914             ss << "auid: " << p->get_auid() << "\n";
4915             break;
4916           case SIZE:
4917             ss << "size: " << p->get_size() << "\n";
4918             break;
4919           case MIN_SIZE:
4920             ss << "min_size: " << p->get_min_size() << "\n";
4921             break;
4922           case CRASH_REPLAY_INTERVAL:
4923             ss << "crash_replay_interval: " <<
4924               p->get_crash_replay_interval() << "\n";
4925             break;
4926           case CRUSH_RULE:
4927             if (osdmap.crush->rule_exists(p->get_crush_rule())) {
4928               ss << "crush_rule: " << osdmap.crush->get_rule_name(
4929                 p->get_crush_rule()) << "\n";
4930             } else {
4931               ss << "crush_rule: " << p->get_crush_rule() << "\n";
4932             }
4933             break;
4934           case HIT_SET_PERIOD:
4935             ss << "hit_set_period: " << p->hit_set_period << "\n";
4936             break;
4937           case HIT_SET_COUNT:
4938             ss << "hit_set_count: " << p->hit_set_count << "\n";
4939             break;
4940           case HIT_SET_TYPE:
4941             ss << "hit_set_type: " <<
4942               HitSet::get_type_name(p->hit_set_params.get_type()) << "\n";
4943             break;
4944           case HIT_SET_FPP:
4945             {
4946               if (p->hit_set_params.get_type() == HitSet::TYPE_BLOOM) {
4947                 BloomHitSet::Params *bloomp =
4948                   static_cast<BloomHitSet::Params*>(p->hit_set_params.impl.get());
4949                 ss << "hit_set_fpp: " << bloomp->get_fpp() << "\n";
4950               } else if(var != "all") {
4951                 ss << "hit set is not of type Bloom; " <<
4952                   "invalid to get a false positive rate!";
4953                 r = -EINVAL;
4954                 goto reply;
4955               }
4956             }
4957             break;
4958           case USE_GMT_HITSET:
4959             ss << "use_gmt_hitset: " << p->use_gmt_hitset << "\n";
4960             break;
4961           case TARGET_MAX_OBJECTS:
4962             ss << "target_max_objects: " << p->target_max_objects << "\n";
4963             break;
4964           case TARGET_MAX_BYTES:
4965             ss << "target_max_bytes: " << p->target_max_bytes << "\n";
4966             break;
4967           case CACHE_TARGET_DIRTY_RATIO:
4968             ss << "cache_target_dirty_ratio: "
4969                << ((float)p->cache_target_dirty_ratio_micro/1000000) << "\n";
4970             break;
4971           case CACHE_TARGET_DIRTY_HIGH_RATIO:
4972             ss << "cache_target_dirty_high_ratio: "
4973                << ((float)p->cache_target_dirty_high_ratio_micro/1000000) << "\n";
4974             break;
4975           case CACHE_TARGET_FULL_RATIO:
4976             ss << "cache_target_full_ratio: "
4977                << ((float)p->cache_target_full_ratio_micro/1000000) << "\n";
4978             break;
4979           case CACHE_MIN_FLUSH_AGE:
4980             ss << "cache_min_flush_age: " << p->cache_min_flush_age << "\n";
4981             break;
4982           case CACHE_MIN_EVICT_AGE:
4983             ss << "cache_min_evict_age: " << p->cache_min_evict_age << "\n";
4984             break;
4985           case ERASURE_CODE_PROFILE:
4986             ss << "erasure_code_profile: " << p->erasure_code_profile << "\n";
4987             break;
4988           case MIN_READ_RECENCY_FOR_PROMOTE:
4989             ss << "min_read_recency_for_promote: " <<
4990               p->min_read_recency_for_promote << "\n";
4991             break;
4992           case HIT_SET_GRADE_DECAY_RATE:
4993             ss << "hit_set_grade_decay_rate: " <<
4994               p->hit_set_grade_decay_rate << "\n";
4995             break;
4996           case HIT_SET_SEARCH_LAST_N:
4997             ss << "hit_set_search_last_n: " <<
4998               p->hit_set_search_last_n << "\n";
4999             break;
5000           case HASHPSPOOL:
5001           case NODELETE:
5002           case NOPGCHANGE:
5003           case NOSIZECHANGE:
5004           case WRITE_FADVISE_DONTNEED:
5005           case NOSCRUB:
5006           case NODEEP_SCRUB:
5007             for (i = ALL_CHOICES.begin(); i != ALL_CHOICES.end(); ++i) {
5008               if (i->second == *it)
5009                 break;
5010             }
5011             assert(i != ALL_CHOICES.end());
5012             ss << i->first << ": " <<
5013               (p->has_flag(pg_pool_t::get_flag_by_name(i->first)) ?
5014                "true" : "false") << "\n";
5015             break;
5016           case MIN_WRITE_RECENCY_FOR_PROMOTE:
5017             ss << "min_write_recency_for_promote: " <<
5018               p->min_write_recency_for_promote << "\n";
5019             break;
5020           case FAST_READ:
5021             ss << "fast_read: " << p->fast_read << "\n";
5022             break;
5023           case SCRUB_MIN_INTERVAL:
5024           case SCRUB_MAX_INTERVAL:
5025           case DEEP_SCRUB_INTERVAL:
5026           case RECOVERY_PRIORITY:
5027           case RECOVERY_OP_PRIORITY:
5028           case SCRUB_PRIORITY:
5029           case COMPRESSION_MODE:
5030           case COMPRESSION_ALGORITHM:
5031           case COMPRESSION_REQUIRED_RATIO:
5032           case COMPRESSION_MAX_BLOB_SIZE:
5033           case COMPRESSION_MIN_BLOB_SIZE:
5034           case CSUM_TYPE:
5035           case CSUM_MAX_BLOCK:
5036           case CSUM_MIN_BLOCK:
5037             for (i = ALL_CHOICES.begin(); i != ALL_CHOICES.end(); ++i) {
5038               if (i->second == *it)
5039                 break;
5040             }
5041             assert(i != ALL_CHOICES.end());
5042             {
5043               pool_opts_t::key_t key = pool_opts_t::get_opt_desc(i->first).key;
5044               if (p->opts.is_set(key)) {
5045                 if(key == pool_opts_t::CSUM_TYPE) {
5046                   int val;
5047                   p->opts.get(key, &val);
5048                   ss << i->first << ": " << Checksummer::get_csum_type_string(val) << "\n";
5049                 } else {
5050                   ss << i->first << ": " << p->opts.get(key) << "\n";
5051                 }
5052               }
5053             }
5054             break;
5055         }
5056         rdata.append(ss.str());
5057         ss.str("");
5058       }
5059     }
5060     r = 0;
5061   } else if (prefix == "osd pool stats") {
5062     r = mon->pgservice->process_pg_command(prefix, cmdmap,
5063                                            osdmap, f.get(), &ss, &rdata);
5064   } else if (prefix == "osd pool get-quota") {
5065     string pool_name;
5066     cmd_getval(g_ceph_context, cmdmap, "pool", pool_name);
5067
5068     int64_t poolid = osdmap.lookup_pg_pool_name(pool_name);
5069     if (poolid < 0) {
5070       assert(poolid == -ENOENT);
5071       ss << "unrecognized pool '" << pool_name << "'";
5072       r = -ENOENT;
5073       goto reply;
5074     }
5075     const pg_pool_t *p = osdmap.get_pg_pool(poolid);
5076
5077     if (f) {
5078       f->open_object_section("pool_quotas");
5079       f->dump_string("pool_name", pool_name);
5080       f->dump_unsigned("pool_id", poolid);
5081       f->dump_unsigned("quota_max_objects", p->quota_max_objects);
5082       f->dump_unsigned("quota_max_bytes", p->quota_max_bytes);
5083       f->close_section();
5084       f->flush(rdata);
5085     } else {
5086       stringstream rs;
5087       rs << "quotas for pool '" << pool_name << "':\n"
5088          << "  max objects: ";
5089       if (p->quota_max_objects == 0)
5090         rs << "N/A";
5091       else
5092         rs << si_t(p->quota_max_objects) << " objects";
5093       rs << "\n"
5094          << "  max bytes  : ";
5095       if (p->quota_max_bytes == 0)
5096         rs << "N/A";
5097       else
5098         rs << si_t(p->quota_max_bytes) << "B";
5099       rdata.append(rs.str());
5100     }
5101     rdata.append("\n");
5102     r = 0;
5103   } else if (prefix == "osd crush rule list" ||
5104              prefix == "osd crush rule ls") {
5105     if (f) {
5106       f->open_array_section("rules");
5107       osdmap.crush->list_rules(f.get());
5108       f->close_section();
5109       f->flush(rdata);
5110     } else {
5111       ostringstream ss;
5112       osdmap.crush->list_rules(&ss);
5113       rdata.append(ss.str());
5114     }
5115   } else if (prefix == "osd crush rule ls-by-class") {
5116     string class_name;
5117     cmd_getval(g_ceph_context, cmdmap, "class", class_name);
5118     if (class_name.empty()) {
5119       ss << "no class specified";
5120       r = -EINVAL;
5121       goto reply;
5122     }
5123     set<int> rules;
5124     r = osdmap.crush->get_rules_by_class(class_name, &rules);
5125     if (r < 0) {
5126       ss << "failed to get rules by class '" << class_name << "'";
5127       goto reply;
5128     }
5129     if (f) {
5130       f->open_array_section("rules");
5131       for (auto &rule: rules) {
5132         f->dump_string("name", osdmap.crush->get_rule_name(rule));
5133       }
5134       f->close_section();
5135       f->flush(rdata);
5136     } else {
5137       ostringstream rs;
5138       for (auto &rule: rules) {
5139         rs << osdmap.crush->get_rule_name(rule) << "\n";
5140       }
5141       rdata.append(rs.str());
5142     }
5143   } else if (prefix == "osd crush rule dump") {
5144     string name;
5145     cmd_getval(g_ceph_context, cmdmap, "name", name);
5146     string format;
5147     cmd_getval(g_ceph_context, cmdmap, "format", format);
5148     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
5149     if (name == "") {
5150       f->open_array_section("rules");
5151       osdmap.crush->dump_rules(f.get());
5152       f->close_section();
5153     } else {
5154       int ruleno = osdmap.crush->get_rule_id(name);
5155       if (ruleno < 0) {
5156         ss << "unknown crush rule '" << name << "'";
5157         r = ruleno;
5158         goto reply;
5159       }
5160       osdmap.crush->dump_rule(ruleno, f.get());
5161     }
5162     ostringstream rs;
5163     f->flush(rs);
5164     rs << "\n";
5165     rdata.append(rs.str());
5166   } else if (prefix == "osd crush dump") {
5167     string format;
5168     cmd_getval(g_ceph_context, cmdmap, "format", format);
5169     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
5170     f->open_object_section("crush_map");
5171     osdmap.crush->dump(f.get());
5172     f->close_section();
5173     ostringstream rs;
5174     f->flush(rs);
5175     rs << "\n";
5176     rdata.append(rs.str());
5177   } else if (prefix == "osd crush show-tunables") {
5178     string format;
5179     cmd_getval(g_ceph_context, cmdmap, "format", format);
5180     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
5181     f->open_object_section("crush_map_tunables");
5182     osdmap.crush->dump_tunables(f.get());
5183     f->close_section();
5184     ostringstream rs;
5185     f->flush(rs);
5186     rs << "\n";
5187     rdata.append(rs.str());
5188   } else if (prefix == "osd crush tree") {
5189     string shadow;
5190     cmd_getval(g_ceph_context, cmdmap, "shadow", shadow);
5191     bool show_shadow = shadow == "--show-shadow";
5192     boost::scoped_ptr<Formatter> f(Formatter::create(format));
5193     if (f) {
5194       osdmap.crush->dump_tree(nullptr,
5195                               f.get(),
5196                               osdmap.get_pool_names(),
5197                               show_shadow);
5198       f->flush(rdata);
5199     } else {
5200       ostringstream ss;
5201       osdmap.crush->dump_tree(&ss,
5202                               nullptr,
5203                               osdmap.get_pool_names(),
5204                               show_shadow);
5205       rdata.append(ss.str());
5206     }
5207   } else if (prefix == "osd crush ls") {
5208     string name;
5209     if (!cmd_getval(g_ceph_context, cmdmap, "node", name)) {
5210       ss << "no node specified";
5211       r = -EINVAL;
5212       goto reply;
5213     }
5214     if (!osdmap.crush->name_exists(name)) {
5215       ss << "node '" << name << "' does not exist";
5216       r = -ENOENT;
5217       goto reply;
5218     }
5219     int id = osdmap.crush->get_item_id(name);
5220     list<int> result;
5221     if (id >= 0) {
5222       result.push_back(id);
5223     } else {
5224       int num = osdmap.crush->get_bucket_size(id);
5225       for (int i = 0; i < num; ++i) {
5226         result.push_back(osdmap.crush->get_bucket_item(id, i));
5227       }
5228     }
5229     if (f) {
5230       f->open_array_section("items");
5231       for (auto i : result) {
5232         f->dump_string("item", osdmap.crush->get_item_name(i));
5233       }
5234       f->close_section();
5235       f->flush(rdata);
5236     } else {
5237       ostringstream ss;
5238       for (auto i : result) {
5239         ss << osdmap.crush->get_item_name(i) << "\n";
5240       }
5241       rdata.append(ss.str());
5242     }
5243     r = 0;
5244   } else if (prefix == "osd crush class ls") {
5245     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty", "json-pretty"));
5246     f->open_array_section("crush_classes");
5247     for (auto i : osdmap.crush->class_name)
5248       f->dump_string("class", i.second);
5249     f->close_section();
5250     f->flush(rdata);
5251   } else if (prefix == "osd crush class ls-osd") {
5252     string name;
5253     cmd_getval(g_ceph_context, cmdmap, "class", name);
5254     set<int> osds;
5255     osdmap.crush->get_devices_by_class(name, &osds);
5256     if (f) {
5257       f->open_array_section("osds");
5258       for (auto &osd: osds)
5259         f->dump_int("osd", osd);
5260       f->close_section();
5261       f->flush(rdata);
5262     } else {
5263       bool first = true;
5264       for (auto &osd : osds) {
5265         if (!first)
5266           ds << "\n";
5267         first = false;
5268         ds << osd;
5269       }
5270       rdata.append(ds);
5271     }
5272   } else if (prefix == "osd erasure-code-profile ls") {
5273     const auto &profiles = osdmap.get_erasure_code_profiles();
5274     if (f)
5275       f->open_array_section("erasure-code-profiles");
5276     for (auto i = profiles.begin(); i != profiles.end(); ++i) {
5277       if (f)
5278         f->dump_string("profile", i->first.c_str());
5279       else
5280         rdata.append(i->first + "\n");
5281     }
5282     if (f) {
5283       f->close_section();
5284       ostringstream rs;
5285       f->flush(rs);
5286       rs << "\n";
5287       rdata.append(rs.str());
5288     }
5289   } else if (prefix == "osd crush weight-set ls") {
5290     boost::scoped_ptr<Formatter> f(Formatter::create(format));
5291     if (f) {
5292       f->open_array_section("weight_sets");
5293       if (osdmap.crush->have_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS)) {
5294         f->dump_string("pool", "(compat)");
5295       }
5296       for (auto& i : osdmap.crush->choose_args) {
5297         if (i.first >= 0) {
5298           f->dump_string("pool", osdmap.get_pool_name(i.first));
5299         }
5300       }
5301       f->close_section();
5302       f->flush(rdata);
5303     } else {
5304       ostringstream rs;
5305       if (osdmap.crush->have_choose_args(CrushWrapper::DEFAULT_CHOOSE_ARGS)) {
5306         rs << "(compat)\n";
5307       }
5308       for (auto& i : osdmap.crush->choose_args) {
5309         if (i.first >= 0) {
5310           rs << osdmap.get_pool_name(i.first) << "\n";
5311         }
5312       }
5313       rdata.append(rs.str());
5314     }
5315   } else if (prefix == "osd crush weight-set dump") {
5316     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty",
5317                                                      "json-pretty"));
5318     osdmap.crush->dump_choose_args(f.get());
5319     f->flush(rdata);
5320   } else if (prefix == "osd erasure-code-profile get") {
5321     string name;
5322     cmd_getval(g_ceph_context, cmdmap, "name", name);
5323     if (!osdmap.has_erasure_code_profile(name)) {
5324       ss << "unknown erasure code profile '" << name << "'";
5325       r = -ENOENT;
5326       goto reply;
5327     }
5328     const map<string,string> &profile = osdmap.get_erasure_code_profile(name);
5329     if (f)
5330       f->open_object_section("profile");
5331     for (map<string,string>::const_iterator i = profile.begin();
5332          i != profile.end();
5333          ++i) {
5334       if (f)
5335         f->dump_string(i->first.c_str(), i->second.c_str());
5336       else
5337         rdata.append(i->first + "=" + i->second + "\n");
5338     }
5339     if (f) {
5340       f->close_section();
5341       ostringstream rs;
5342       f->flush(rs);
5343       rs << "\n";
5344       rdata.append(rs.str());
5345     }
5346   } else if (prefix == "osd pool application get") {
5347     boost::scoped_ptr<Formatter> f(Formatter::create(format, "json-pretty",
5348                                                      "json-pretty"));
5349     string pool_name;
5350     cmd_getval(g_ceph_context, cmdmap, "pool", pool_name);
5351     string app;
5352     cmd_getval(g_ceph_context, cmdmap, "app", app);
5353     string key;
5354     cmd_getval(g_ceph_context, cmdmap, "key", key);
5355
5356     if (pool_name.empty()) {
5357       // all
5358       f->open_object_section("pools");
5359       for (const auto &pool : osdmap.pools) {
5360         std::string name("<unknown>");
5361         const auto &pni = osdmap.pool_name.find(pool.first);
5362         if (pni != osdmap.pool_name.end())
5363           name = pni->second;
5364         f->open_object_section(name.c_str());
5365         for (auto &app_pair : pool.second.application_metadata) {
5366           f->open_object_section(app_pair.first.c_str());
5367           for (auto &kv_pair : app_pair.second) {
5368             f->dump_string(kv_pair.first.c_str(), kv_pair.second);
5369           }
5370           f->close_section();
5371         }
5372         f->close_section(); // name
5373       }
5374       f->close_section(); // pools
5375       f->flush(rdata);
5376     } else {
5377       int64_t pool = osdmap.lookup_pg_pool_name(pool_name.c_str());
5378       if (pool < 0) {
5379         ss << "unrecognized pool '" << pool_name << "'";
5380         r = -ENOENT;
5381         goto reply;
5382       }
5383       auto p = osdmap.get_pg_pool(pool);
5384       // filter by pool
5385       if (app.empty()) {
5386         f->open_object_section(pool_name.c_str());
5387         for (auto &app_pair : p->application_metadata) {
5388           f->open_object_section(app_pair.first.c_str());
5389           for (auto &kv_pair : app_pair.second) {
5390             f->dump_string(kv_pair.first.c_str(), kv_pair.second);
5391           }
5392           f->close_section(); // application
5393         }
5394         f->close_section(); // pool_name
5395         f->flush(rdata);
5396         goto reply;
5397       }
5398
5399       auto app_it = p->application_metadata.find(app);
5400       if (app_it == p->application_metadata.end()) {
5401         ss << "pool '" << pool_name << "' has no application '" << app << "'";
5402         r = -ENOENT;
5403         goto reply;
5404       }
5405       // filter by pool + app
5406       if (key.empty()) {
5407         f->open_object_section(app_it->first.c_str());
5408         for (auto &kv_pair : app_it->second) {
5409           f->dump_string(kv_pair.first.c_str(), kv_pair.second);
5410         }
5411         f->close_section(); // application
5412         f->flush(rdata);
5413         goto reply;
5414       }
5415       // filter by pool + app + key
5416       auto key_it = app_it->second.find(key);
5417       if (key_it == app_it->second.end()) {
5418         ss << "application '" << app << "' on pool '" << pool_name
5419            << "' does not have key '" << key << "'";
5420         r = -ENOENT;
5421         goto reply;
5422       }
5423       ss << key_it->second << "\n";
5424       rdata.append(ss.str());
5425       ss.str("");
5426     }
5427   } else {
5428     // try prepare update
5429     return false;
5430   }
5431
5432  reply:
5433   string rs;
5434   getline(ss, rs);
5435   mon->reply_command(op, r, rs, rdata, get_last_committed());
5436   return true;
5437 }
5438
5439 void OSDMonitor::set_pool_flags(int64_t pool_id, uint64_t flags)
5440 {
5441   pg_pool_t *pool = pending_inc.get_new_pool(pool_id,
5442     osdmap.get_pg_pool(pool_id));
5443   assert(pool);
5444   pool->set_flag(flags);
5445 }
5446
5447 void OSDMonitor::clear_pool_flags(int64_t pool_id, uint64_t flags)
5448 {
5449   pg_pool_t *pool = pending_inc.get_new_pool(pool_id,
5450     osdmap.get_pg_pool(pool_id));
5451   assert(pool);
5452   pool->unset_flag(flags);
5453 }
5454
5455 bool OSDMonitor::update_pools_status()
5456 {
5457   if (!mon->pgservice->is_readable())
5458     return false;
5459
5460   bool ret = false;
5461
5462   auto& pools = osdmap.get_pools();
5463   for (auto it = pools.begin(); it != pools.end(); ++it) {
5464     const pool_stat_t *pstat = mon->pgservice->get_pool_stat(it->first);
5465     if (!pstat)
5466       continue;
5467     const object_stat_sum_t& sum = pstat->stats.sum;
5468     const pg_pool_t &pool = it->second;
5469     const string& pool_name = osdmap.get_pool_name(it->first);
5470
5471     bool pool_is_full =
5472       (pool.quota_max_bytes > 0 && (uint64_t)sum.num_bytes >= pool.quota_max_bytes) ||
5473       (pool.quota_max_objects > 0 && (uint64_t)sum.num_objects >= pool.quota_max_objects);
5474
5475     if (pool.has_flag(pg_pool_t::FLAG_FULL_NO_QUOTA)) {
5476       if (pool_is_full)
5477         continue;
5478
5479       mon->clog->info() << "pool '" << pool_name
5480                        << "' no longer out of quota; removing NO_QUOTA flag";
5481       // below we cancel FLAG_FULL too, we'll set it again in
5482       // OSDMonitor::encode_pending if it still fails the osd-full checking.
5483       clear_pool_flags(it->first,
5484                        pg_pool_t::FLAG_FULL_NO_QUOTA | pg_pool_t::FLAG_FULL);
5485       ret = true;
5486     } else {
5487       if (!pool_is_full)
5488         continue;
5489
5490       if (pool.quota_max_bytes > 0 &&
5491           (uint64_t)sum.num_bytes >= pool.quota_max_bytes) {
5492         mon->clog->warn() << "pool '" << pool_name << "' is full"
5493                          << " (reached quota's max_bytes: "
5494                          << si_t(pool.quota_max_bytes) << ")";
5495       }
5496       if (pool.quota_max_objects > 0 &&
5497                  (uint64_t)sum.num_objects >= pool.quota_max_objects) {
5498         mon->clog->warn() << "pool '" << pool_name << "' is full"
5499                          << " (reached quota's max_objects: "
5500                          << pool.quota_max_objects << ")";
5501       }
5502       // set both FLAG_FULL_NO_QUOTA and FLAG_FULL
5503       // note that below we try to cancel FLAG_BACKFILLFULL/NEARFULL too
5504       // since FLAG_FULL should always take precedence
5505       set_pool_flags(it->first,
5506                      pg_pool_t::FLAG_FULL_NO_QUOTA | pg_pool_t::FLAG_FULL);
5507       clear_pool_flags(it->first,
5508                        pg_pool_t::FLAG_NEARFULL |
5509                        pg_pool_t::FLAG_BACKFILLFULL);
5510       ret = true;
5511     }
5512   }
5513   return ret;
5514 }
5515
5516 int OSDMonitor::prepare_new_pool(MonOpRequestRef op)
5517 {
5518   op->mark_osdmon_event(__func__);
5519   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
5520   dout(10) << "prepare_new_pool from " << m->get_connection() << dendl;
5521   MonSession *session = m->get_session();
5522   if (!session)
5523     return -EPERM;
5524   string erasure_code_profile;
5525   stringstream ss;
5526   string rule_name;
5527   if (m->auid)
5528     return prepare_new_pool(m->name, m->auid, m->crush_rule, rule_name,
5529                             0, 0,
5530                             erasure_code_profile,
5531                             pg_pool_t::TYPE_REPLICATED, 0, FAST_READ_OFF, &ss);
5532   else
5533     return prepare_new_pool(m->name, session->auid, m->crush_rule, rule_name,
5534                             0, 0,
5535                             erasure_code_profile,
5536                             pg_pool_t::TYPE_REPLICATED, 0, FAST_READ_OFF, &ss);
5537 }
5538
5539 int OSDMonitor::crush_rename_bucket(const string& srcname,
5540                                     const string& dstname,
5541                                     ostream *ss)
5542 {
5543   int ret;
5544   //
5545   // Avoid creating a pending crush if it does not already exists and
5546   // the rename would fail.
5547   //
5548   if (!_have_pending_crush()) {
5549     ret = _get_stable_crush().can_rename_bucket(srcname,
5550                                                 dstname,
5551                                                 ss);
5552     if (ret)
5553       return ret;
5554   }
5555
5556   CrushWrapper newcrush;
5557   _get_pending_crush(newcrush);
5558
5559   ret = newcrush.rename_bucket(srcname,
5560                                dstname,
5561                                ss);
5562   if (ret)
5563     return ret;
5564
5565   pending_inc.crush.clear();
5566   newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
5567   *ss << "renamed bucket " << srcname << " into " << dstname;   
5568   return 0;
5569 }
5570
5571 void OSDMonitor::check_legacy_ec_plugin(const string& plugin, const string& profile) const
5572 {
5573   string replacement = "";
5574
5575   if (plugin == "jerasure_generic" || 
5576       plugin == "jerasure_sse3" ||
5577       plugin == "jerasure_sse4" ||
5578       plugin == "jerasure_neon") {
5579     replacement = "jerasure";
5580   } else if (plugin == "shec_generic" ||
5581              plugin == "shec_sse3" ||
5582              plugin == "shec_sse4" ||
5583              plugin == "shec_neon") {
5584     replacement = "shec";
5585   }
5586
5587   if (replacement != "") {
5588     dout(0) << "WARNING: erasure coding profile " << profile << " uses plugin "
5589             << plugin << " that has been deprecated. Please use " 
5590             << replacement << " instead." << dendl;
5591   }
5592 }
5593
5594 int OSDMonitor::normalize_profile(const string& profilename,
5595                                   ErasureCodeProfile &profile,
5596                                   bool force,
5597                                   ostream *ss)
5598 {
5599   ErasureCodeInterfaceRef erasure_code;
5600   ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
5601   ErasureCodeProfile::const_iterator plugin = profile.find("plugin");
5602   check_legacy_ec_plugin(plugin->second, profilename);
5603   int err = instance.factory(plugin->second,
5604                              g_conf->get_val<std::string>("erasure_code_dir"),
5605                              profile, &erasure_code, ss);
5606   if (err) {
5607     return err;
5608   }
5609
5610   err = erasure_code->init(profile, ss);
5611   if (err) {
5612     return err;
5613   }
5614
5615   auto it = profile.find("stripe_unit");
5616   if (it != profile.end()) {
5617     string err_str;
5618     uint32_t stripe_unit = strict_si_cast<uint32_t>(it->second.c_str(), &err_str);
5619     if (!err_str.empty()) {
5620       *ss << "could not parse stripe_unit '" << it->second
5621           << "': " << err_str << std::endl;
5622       return -EINVAL;
5623     }
5624     uint32_t data_chunks = erasure_code->get_data_chunk_count();
5625     uint32_t chunk_size = erasure_code->get_chunk_size(stripe_unit * data_chunks);
5626     if (chunk_size != stripe_unit) {
5627       *ss << "stripe_unit " << stripe_unit << " does not match ec profile "
5628           << "alignment. Would be padded to " << chunk_size
5629           << std::endl;
5630       return -EINVAL;
5631     }
5632     if ((stripe_unit % 4096) != 0 && !force) {
5633       *ss << "stripe_unit should be a multiple of 4096 bytes for best performance."
5634           << "use --force to override this check" << std::endl;
5635       return -EINVAL;
5636     }
5637   }
5638   return 0;
5639 }
5640
5641 int OSDMonitor::crush_rule_create_erasure(const string &name,
5642                                              const string &profile,
5643                                              int *rule,
5644                                              ostream *ss)
5645 {
5646   int ruleid = osdmap.crush->get_rule_id(name);
5647   if (ruleid != -ENOENT) {
5648     *rule = osdmap.crush->get_rule_mask_ruleset(ruleid);
5649     return -EEXIST;
5650   }
5651
5652   CrushWrapper newcrush;
5653   _get_pending_crush(newcrush);
5654
5655   ruleid = newcrush.get_rule_id(name);
5656   if (ruleid != -ENOENT) {
5657     *rule = newcrush.get_rule_mask_ruleset(ruleid);
5658     return -EALREADY;
5659   } else {
5660     ErasureCodeInterfaceRef erasure_code;
5661     int err = get_erasure_code(profile, &erasure_code, ss);
5662     if (err) {
5663       *ss << "failed to load plugin using profile " << profile << std::endl;
5664       return err;
5665     }
5666
5667     err = erasure_code->create_rule(name, newcrush, ss);
5668     erasure_code.reset();
5669     if (err < 0)
5670       return err;
5671     *rule = err;
5672     pending_inc.crush.clear();
5673     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
5674     return 0;
5675   }
5676 }
5677
5678 int OSDMonitor::get_erasure_code(const string &erasure_code_profile,
5679                                  ErasureCodeInterfaceRef *erasure_code,
5680                                  ostream *ss) const
5681 {
5682   if (pending_inc.has_erasure_code_profile(erasure_code_profile))
5683     return -EAGAIN;
5684   ErasureCodeProfile profile =
5685     osdmap.get_erasure_code_profile(erasure_code_profile);
5686   ErasureCodeProfile::const_iterator plugin =
5687     profile.find("plugin");
5688   if (plugin == profile.end()) {
5689     *ss << "cannot determine the erasure code plugin"
5690         << " because there is no 'plugin' entry in the erasure_code_profile "
5691         << profile << std::endl;
5692     return -EINVAL;
5693   }
5694   check_legacy_ec_plugin(plugin->second, erasure_code_profile);
5695   ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
5696   return instance.factory(plugin->second,
5697                           g_conf->get_val<std::string>("erasure_code_dir"),
5698                           profile, erasure_code, ss);
5699 }
5700
5701 int OSDMonitor::check_cluster_features(uint64_t features,
5702                                        stringstream &ss)
5703 {
5704   stringstream unsupported_ss;
5705   int unsupported_count = 0;
5706   if ((mon->get_quorum_con_features() & features) != features) {
5707     unsupported_ss << "the monitor cluster";
5708     ++unsupported_count;
5709   }
5710
5711   set<int32_t> up_osds;
5712   osdmap.get_up_osds(up_osds);
5713   for (set<int32_t>::iterator it = up_osds.begin();
5714        it != up_osds.end(); ++it) {
5715     const osd_xinfo_t &xi = osdmap.get_xinfo(*it);
5716     if ((xi.features & features) != features) {
5717       if (unsupported_count > 0)
5718         unsupported_ss << ", ";
5719       unsupported_ss << "osd." << *it;
5720       unsupported_count ++;
5721     }
5722   }
5723
5724   if (unsupported_count > 0) {
5725     ss << "features " << features << " unsupported by: "
5726        << unsupported_ss.str();
5727     return -ENOTSUP;
5728   }
5729
5730   // check pending osd state, too!
5731   for (map<int32_t,osd_xinfo_t>::const_iterator p =
5732          pending_inc.new_xinfo.begin();
5733        p != pending_inc.new_xinfo.end(); ++p) {
5734     const osd_xinfo_t &xi = p->second;
5735     if ((xi.features & features) != features) {
5736       dout(10) << __func__ << " pending osd." << p->first
5737                << " features are insufficient; retry" << dendl;
5738       return -EAGAIN;
5739     }
5740   }
5741
5742   return 0;
5743 }
5744
5745 bool OSDMonitor::validate_crush_against_features(const CrushWrapper *newcrush,
5746                                                  stringstream& ss)
5747 {
5748   OSDMap::Incremental new_pending = pending_inc;
5749   ::encode(*newcrush, new_pending.crush, mon->get_quorum_con_features());
5750   OSDMap newmap;
5751   newmap.deepish_copy_from(osdmap);
5752   newmap.apply_incremental(new_pending);
5753
5754   // client compat
5755   if (newmap.require_min_compat_client > 0) {
5756     auto mv = newmap.get_min_compat_client();
5757     if (mv > newmap.require_min_compat_client) {
5758       ss << "new crush map requires client version " << ceph_release_name(mv)
5759          << " but require_min_compat_client is "
5760          << ceph_release_name(newmap.require_min_compat_client);
5761       return false;
5762     }
5763   }
5764
5765   // osd compat
5766   uint64_t features =
5767     newmap.get_features(CEPH_ENTITY_TYPE_MON, NULL) |
5768     newmap.get_features(CEPH_ENTITY_TYPE_OSD, NULL);
5769   stringstream features_ss;
5770   int r = check_cluster_features(features, features_ss);
5771   if (r) {
5772     ss << "Could not change CRUSH: " << features_ss.str();
5773     return false;
5774   }
5775
5776   return true;
5777 }
5778
5779 bool OSDMonitor::erasure_code_profile_in_use(
5780   const mempool::osdmap::map<int64_t, pg_pool_t> &pools,
5781   const string &profile,
5782   ostream *ss)
5783 {
5784   bool found = false;
5785   for (map<int64_t, pg_pool_t>::const_iterator p = pools.begin();
5786        p != pools.end();
5787        ++p) {
5788     if (p->second.erasure_code_profile == profile) {
5789       *ss << osdmap.pool_name[p->first] << " ";
5790       found = true;
5791     }
5792   }
5793   if (found) {
5794     *ss << "pool(s) are using the erasure code profile '" << profile << "'";
5795   }
5796   return found;
5797 }
5798
5799 int OSDMonitor::parse_erasure_code_profile(const vector<string> &erasure_code_profile,
5800                                            map<string,string> *erasure_code_profile_map,
5801                                            ostream *ss)
5802 {
5803   int r = get_json_str_map(g_conf->osd_pool_default_erasure_code_profile,
5804                            *ss,
5805                            erasure_code_profile_map);
5806   if (r)
5807     return r;
5808   assert((*erasure_code_profile_map).count("plugin"));
5809   string default_plugin = (*erasure_code_profile_map)["plugin"];
5810   map<string,string> user_map;
5811   for (vector<string>::const_iterator i = erasure_code_profile.begin();
5812        i != erasure_code_profile.end();
5813        ++i) {
5814     size_t equal = i->find('=');
5815     if (equal == string::npos) {
5816       user_map[*i] = string();
5817       (*erasure_code_profile_map)[*i] = string();
5818     } else {
5819       string key = i->substr(0, equal);
5820       equal++;
5821       const string value = i->substr(equal);
5822       if (osdmap.require_osd_release >= CEPH_RELEASE_LUMINOUS &&
5823           key.find("ruleset-") == 0) {
5824         if (g_conf->get_val<bool>("mon_fixup_legacy_erasure_code_profiles")) {
5825           mon->clog->warn() << "erasure code profile property '" << key
5826                             << "' is no longer supported; try "
5827                             << "'crush-" << key.substr(8) << "' instead";
5828           key = string("crush-") + key.substr(8);
5829         } else {
5830           *ss << "property '" << key << "' is no longer supported; try "
5831               << "'crush-" << key.substr(8) << "' instead";
5832           return -EINVAL;
5833         }
5834       }
5835       user_map[key] = value;
5836       (*erasure_code_profile_map)[key] = value;
5837     }
5838   }
5839
5840   if (user_map.count("plugin") && user_map["plugin"] != default_plugin)
5841     (*erasure_code_profile_map) = user_map;
5842
5843   return 0;
5844 }
5845
5846 int OSDMonitor::prepare_pool_size(const unsigned pool_type,
5847                                   const string &erasure_code_profile,
5848                                   unsigned *size, unsigned *min_size,
5849                                   ostream *ss)
5850 {
5851   int err = 0;
5852   switch (pool_type) {
5853   case pg_pool_t::TYPE_REPLICATED:
5854     *size = g_conf->osd_pool_default_size;
5855     *min_size = g_conf->get_osd_pool_default_min_size();
5856     break;
5857   case pg_pool_t::TYPE_ERASURE:
5858     {
5859       ErasureCodeInterfaceRef erasure_code;
5860       err = get_erasure_code(erasure_code_profile, &erasure_code, ss);
5861       if (err == 0) {
5862         *size = erasure_code->get_chunk_count();
5863         *min_size = MIN(erasure_code->get_data_chunk_count() + 1, *size);
5864       }
5865     }
5866     break;
5867   default:
5868     *ss << "prepare_pool_size: " << pool_type << " is not a known pool type";
5869     err = -EINVAL;
5870     break;
5871   }
5872   return err;
5873 }
5874
5875 int OSDMonitor::prepare_pool_stripe_width(const unsigned pool_type,
5876                                           const string &erasure_code_profile,
5877                                           uint32_t *stripe_width,
5878                                           ostream *ss)
5879 {
5880   int err = 0;
5881   switch (pool_type) {
5882   case pg_pool_t::TYPE_REPLICATED:
5883     // ignored
5884     break;
5885   case pg_pool_t::TYPE_ERASURE:
5886     {
5887       ErasureCodeProfile profile =
5888         osdmap.get_erasure_code_profile(erasure_code_profile);
5889       ErasureCodeInterfaceRef erasure_code;
5890       err = get_erasure_code(erasure_code_profile, &erasure_code, ss);
5891       if (err)
5892         break;
5893       uint32_t data_chunks = erasure_code->get_data_chunk_count();
5894       uint32_t stripe_unit = g_conf->osd_pool_erasure_code_stripe_unit;
5895       auto it = profile.find("stripe_unit");
5896       if (it != profile.end()) {
5897         string err_str;
5898         stripe_unit = strict_si_cast<uint32_t>(it->second.c_str(), &err_str);
5899         assert(err_str.empty());
5900       }
5901       *stripe_width = data_chunks *
5902         erasure_code->get_chunk_size(stripe_unit * data_chunks);
5903     }
5904     break;
5905   default:
5906     *ss << "prepare_pool_stripe_width: "
5907        << pool_type << " is not a known pool type";
5908     err = -EINVAL;
5909     break;
5910   }
5911   return err;
5912 }
5913
5914 int OSDMonitor::prepare_pool_crush_rule(const unsigned pool_type,
5915                                         const string &erasure_code_profile,
5916                                         const string &rule_name,
5917                                         int *crush_rule,
5918                                         ostream *ss)
5919 {
5920
5921   if (*crush_rule < 0) {
5922     switch (pool_type) {
5923     case pg_pool_t::TYPE_REPLICATED:
5924       {
5925         if (rule_name == "") {
5926           // Use default rule
5927           *crush_rule = osdmap.crush->get_osd_pool_default_crush_replicated_ruleset(g_ceph_context);
5928           if (*crush_rule < 0) {
5929             // Errors may happen e.g. if no valid rule is available
5930             *ss << "No suitable CRUSH rule exists, check "
5931                 << "'osd pool default crush *' config options";
5932             return -ENOENT;
5933           }
5934         } else {
5935           return get_crush_rule(rule_name, crush_rule, ss);
5936         }
5937       }
5938       break;
5939     case pg_pool_t::TYPE_ERASURE:
5940       {
5941         int err = crush_rule_create_erasure(rule_name,
5942                                                erasure_code_profile,
5943                                                crush_rule, ss);
5944         switch (err) {
5945         case -EALREADY:
5946           dout(20) << "prepare_pool_crush_rule: rule "
5947                    << rule_name << " try again" << dendl;
5948           // fall through
5949         case 0:
5950           // need to wait for the crush rule to be proposed before proceeding
5951           err = -EAGAIN;
5952           break;
5953         case -EEXIST:
5954           err = 0;
5955           break;
5956         }
5957         return err;
5958       }
5959       break;
5960     default:
5961       *ss << "prepare_pool_crush_rule: " << pool_type
5962          << " is not a known pool type";
5963       return -EINVAL;
5964       break;
5965     }
5966   } else {
5967     if (!osdmap.crush->ruleset_exists(*crush_rule)) {
5968       *ss << "CRUSH rule " << *crush_rule << " not found";
5969       return -ENOENT;
5970     }
5971   }
5972
5973   return 0;
5974 }
5975
5976 int OSDMonitor::get_crush_rule(const string &rule_name,
5977                                int *crush_rule,
5978                                ostream *ss)
5979 {
5980   int ret;
5981   ret = osdmap.crush->get_rule_id(rule_name);
5982   if (ret != -ENOENT) {
5983     // found it, use it
5984     *crush_rule = ret;
5985   } else {
5986     CrushWrapper newcrush;
5987     _get_pending_crush(newcrush);
5988
5989     ret = newcrush.get_rule_id(rule_name);
5990     if (ret != -ENOENT) {
5991       // found it, wait for it to be proposed
5992       dout(20) << __func__ << ": rule " << rule_name
5993                << " try again" << dendl;
5994       return -EAGAIN;
5995     } else {
5996       // Cannot find it , return error
5997       *ss << "specified rule " << rule_name << " doesn't exist";
5998       return ret;
5999     }
6000   }
6001   return 0;
6002 }
6003
6004 int OSDMonitor::check_pg_num(int64_t pool, int pg_num, int size, ostream *ss)
6005 {
6006   auto max_pgs_per_osd = g_conf->get_val<uint64_t>("mon_max_pg_per_osd");
6007   auto num_osds = std::max(osdmap.get_num_in_osds(), 3u);   // assume min cluster size 3
6008   auto max_pgs = max_pgs_per_osd * num_osds;
6009   uint64_t projected = 0;
6010   if (pool < 0) {
6011     projected += pg_num * size;
6012   }
6013   for (const auto& i : osdmap.get_pools()) {
6014     if (i.first == pool) {
6015       projected += pg_num * size;
6016     } else {
6017       projected += i.second.get_pg_num() * i.second.get_size();
6018     }
6019   }
6020   if (projected > max_pgs) {
6021     if (pool >= 0) {
6022       *ss << "pool id " << pool;
6023     }
6024     *ss << " pg_num " << pg_num << " size " << size
6025         << " would mean " << projected
6026         << " total pgs, which exceeds max " << max_pgs
6027         << " (mon_max_pg_per_osd " << max_pgs_per_osd
6028         << " * num_in_osds " << num_osds << ")";
6029     return -ERANGE;
6030   }
6031   return 0;
6032 }
6033
6034 /**
6035  * @param name The name of the new pool
6036  * @param auid The auid of the pool owner. Can be -1
6037  * @param crush_rule The crush rule to use. If <0, will use the system default
6038  * @param crush_rule_name The crush rule to use, if crush_rulset <0
6039  * @param pg_num The pg_num to use. If set to 0, will use the system default
6040  * @param pgp_num The pgp_num to use. If set to 0, will use the system default
6041  * @param erasure_code_profile The profile name in OSDMap to be used for erasure code
6042  * @param pool_type TYPE_ERASURE, or TYPE_REP
6043  * @param expected_num_objects expected number of objects on the pool
6044  * @param fast_read fast read type. 
6045  * @param ss human readable error message, if any.
6046  *
6047  * @return 0 on success, negative errno on failure.
6048  */
6049 int OSDMonitor::prepare_new_pool(string& name, uint64_t auid,
6050                                  int crush_rule,
6051                                  const string &crush_rule_name,
6052                                  unsigned pg_num, unsigned pgp_num,
6053                                  const string &erasure_code_profile,
6054                                  const unsigned pool_type,
6055                                  const uint64_t expected_num_objects,
6056                                  FastReadType fast_read,
6057                                  ostream *ss)
6058 {
6059   if (name.length() == 0)
6060     return -EINVAL;
6061   if (pg_num == 0)
6062     pg_num = g_conf->osd_pool_default_pg_num;
6063   if (pgp_num == 0)
6064     pgp_num = g_conf->osd_pool_default_pgp_num;
6065   if (pg_num > (unsigned)g_conf->mon_max_pool_pg_num) {
6066     *ss << "'pg_num' must be greater than 0 and less than or equal to "
6067         << g_conf->mon_max_pool_pg_num
6068         << " (you may adjust 'mon max pool pg num' for higher values)";
6069     return -ERANGE;
6070   }
6071   if (pgp_num > pg_num) {
6072     *ss << "'pgp_num' must be greater than 0 and lower or equal than 'pg_num'"
6073         << ", which in this case is " << pg_num;
6074     return -ERANGE;
6075   }
6076   if (pool_type == pg_pool_t::TYPE_REPLICATED && fast_read == FAST_READ_ON) {
6077     *ss << "'fast_read' can only apply to erasure coding pool";
6078     return -EINVAL;
6079   }
6080   int r;
6081   r = prepare_pool_crush_rule(pool_type, erasure_code_profile,
6082                                  crush_rule_name, &crush_rule, ss);
6083   if (r) {
6084     dout(10) << " prepare_pool_crush_rule returns " << r << dendl;
6085     return r;
6086   }
6087   if (g_conf->mon_osd_crush_smoke_test) {
6088     CrushWrapper newcrush;
6089     _get_pending_crush(newcrush);
6090     ostringstream err;
6091     CrushTester tester(newcrush, err);
6092     tester.set_min_x(0);
6093     tester.set_max_x(50);
6094     tester.set_rule(crush_rule);
6095     auto start = ceph::coarse_mono_clock::now();
6096     r = tester.test_with_fork(g_conf->mon_lease);
6097     auto duration = ceph::coarse_mono_clock::now() - start;
6098     if (r < 0) {
6099       dout(10) << " tester.test_with_fork returns " << r
6100                << ": " << err.str() << dendl;
6101       *ss << "crush test failed with " << r << ": " << err.str();
6102       return r;
6103     }
6104     dout(10) << __func__ << " crush smoke test duration: "
6105              << duration << dendl;
6106   }
6107   unsigned size, min_size;
6108   r = prepare_pool_size(pool_type, erasure_code_profile, &size, &min_size, ss);
6109   if (r) {
6110     dout(10) << " prepare_pool_size returns " << r << dendl;
6111     return r;
6112   }
6113   r = check_pg_num(-1, pg_num, size, ss);
6114   if (r) {
6115     dout(10) << " prepare_pool_size returns " << r << dendl;
6116     return r;
6117   }
6118
6119   if (!osdmap.crush->check_crush_rule(crush_rule, pool_type, size, *ss)) {
6120     return -EINVAL;
6121   }
6122
6123   uint32_t stripe_width = 0;
6124   r = prepare_pool_stripe_width(pool_type, erasure_code_profile, &stripe_width, ss);
6125   if (r) {
6126     dout(10) << " prepare_pool_stripe_width returns " << r << dendl;
6127     return r;
6128   }
6129   
6130   bool fread = false;
6131   if (pool_type == pg_pool_t::TYPE_ERASURE) {
6132     switch (fast_read) {
6133       case FAST_READ_OFF:
6134         fread = false;
6135         break;
6136       case FAST_READ_ON:
6137         fread = true;
6138         break;
6139       case FAST_READ_DEFAULT:
6140         fread = g_conf->mon_osd_pool_ec_fast_read;
6141         break;
6142       default:
6143         *ss << "invalid fast_read setting: " << fast_read;
6144         return -EINVAL;
6145     }
6146   }
6147
6148   for (map<int64_t,string>::iterator p = pending_inc.new_pool_names.begin();
6149        p != pending_inc.new_pool_names.end();
6150        ++p) {
6151     if (p->second == name)
6152       return 0;
6153   }
6154
6155   if (-1 == pending_inc.new_pool_max)
6156     pending_inc.new_pool_max = osdmap.pool_max;
6157   int64_t pool = ++pending_inc.new_pool_max;
6158   pg_pool_t empty;
6159   pg_pool_t *pi = pending_inc.get_new_pool(pool, &empty);
6160   pi->type = pool_type;
6161   pi->fast_read = fread; 
6162   pi->flags = g_conf->osd_pool_default_flags;
6163   if (g_conf->osd_pool_default_flag_hashpspool)
6164     pi->set_flag(pg_pool_t::FLAG_HASHPSPOOL);
6165   if (g_conf->osd_pool_default_flag_nodelete)
6166     pi->set_flag(pg_pool_t::FLAG_NODELETE);
6167   if (g_conf->osd_pool_default_flag_nopgchange)
6168     pi->set_flag(pg_pool_t::FLAG_NOPGCHANGE);
6169   if (g_conf->osd_pool_default_flag_nosizechange)
6170     pi->set_flag(pg_pool_t::FLAG_NOSIZECHANGE);
6171   if (g_conf->osd_pool_use_gmt_hitset &&
6172       (osdmap.get_up_osd_features() & CEPH_FEATURE_OSD_HITSET_GMT))
6173     pi->use_gmt_hitset = true;
6174   else
6175     pi->use_gmt_hitset = false;
6176
6177   pi->size = size;
6178   pi->min_size = min_size;
6179   pi->crush_rule = crush_rule;
6180   pi->expected_num_objects = expected_num_objects;
6181   pi->object_hash = CEPH_STR_HASH_RJENKINS;
6182   pi->set_pg_num(pg_num);
6183   pi->set_pgp_num(pgp_num);
6184   pi->last_change = pending_inc.epoch;
6185   pi->auid = auid;
6186   pi->erasure_code_profile = erasure_code_profile;
6187   pi->stripe_width = stripe_width;
6188   pi->cache_target_dirty_ratio_micro =
6189     g_conf->osd_pool_default_cache_target_dirty_ratio * 1000000;
6190   pi->cache_target_dirty_high_ratio_micro =
6191     g_conf->osd_pool_default_cache_target_dirty_high_ratio * 1000000;
6192   pi->cache_target_full_ratio_micro =
6193     g_conf->osd_pool_default_cache_target_full_ratio * 1000000;
6194   pi->cache_min_flush_age = g_conf->osd_pool_default_cache_min_flush_age;
6195   pi->cache_min_evict_age = g_conf->osd_pool_default_cache_min_evict_age;
6196   pending_inc.new_pool_names[pool] = name;
6197   return 0;
6198 }
6199
6200 bool OSDMonitor::prepare_set_flag(MonOpRequestRef op, int flag)
6201 {
6202   op->mark_osdmon_event(__func__);
6203   ostringstream ss;
6204   if (pending_inc.new_flags < 0)
6205     pending_inc.new_flags = osdmap.get_flags();
6206   pending_inc.new_flags |= flag;
6207   ss << OSDMap::get_flag_string(flag) << " is set";
6208   wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
6209                                                     get_last_committed() + 1));
6210   return true;
6211 }
6212
6213 bool OSDMonitor::prepare_unset_flag(MonOpRequestRef op, int flag)
6214 {
6215   op->mark_osdmon_event(__func__);
6216   ostringstream ss;
6217   if (pending_inc.new_flags < 0)
6218     pending_inc.new_flags = osdmap.get_flags();
6219   pending_inc.new_flags &= ~flag;
6220   ss << OSDMap::get_flag_string(flag) << " is unset";
6221   wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
6222                                                     get_last_committed() + 1));
6223   return true;
6224 }
6225
6226 int OSDMonitor::prepare_command_pool_set(map<string,cmd_vartype> &cmdmap,
6227                                          stringstream& ss)
6228 {
6229   string poolstr;
6230   cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
6231   int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
6232   if (pool < 0) {
6233     ss << "unrecognized pool '" << poolstr << "'";
6234     return -ENOENT;
6235   }
6236   string var;
6237   cmd_getval(g_ceph_context, cmdmap, "var", var);
6238
6239   pg_pool_t p = *osdmap.get_pg_pool(pool);
6240   if (pending_inc.new_pools.count(pool))
6241     p = pending_inc.new_pools[pool];
6242
6243   // accept val as a json string in the normal case (current
6244   // generation monitor).  parse out int or float values from the
6245   // string as needed.  however, if it is not a string, try to pull
6246   // out an int, in case an older monitor with an older json schema is
6247   // forwarding a request.
6248   string val;
6249   string interr, floaterr;
6250   int64_t n = 0;
6251   double f = 0;
6252   int64_t uf = 0;  // micro-f
6253   if (!cmd_getval(g_ceph_context, cmdmap, "val", val)) {
6254     // wasn't a string; maybe an older mon forwarded json with an int?
6255     if (!cmd_getval(g_ceph_context, cmdmap, "val", n))
6256       return -EINVAL;  // no value!
6257   } else {
6258     // we got a string.  see if it contains an int.
6259     n = strict_strtoll(val.c_str(), 10, &interr);
6260     // or a float
6261     f = strict_strtod(val.c_str(), &floaterr);
6262     uf = llrintl(f * (double)1000000.0);
6263   }
6264
6265   if (!p.is_tier() &&
6266       (var == "hit_set_type" || var == "hit_set_period" ||
6267        var == "hit_set_count" || var == "hit_set_fpp" ||
6268        var == "target_max_objects" || var == "target_max_bytes" ||
6269        var == "cache_target_full_ratio" || var == "cache_target_dirty_ratio" ||
6270        var == "cache_target_dirty_high_ratio" || var == "use_gmt_hitset" ||
6271        var == "cache_min_flush_age" || var == "cache_min_evict_age" ||
6272        var == "hit_set_grade_decay_rate" || var == "hit_set_search_last_n" ||
6273        var == "min_read_recency_for_promote" || var == "min_write_recency_for_promote")) {
6274     return -EACCES;
6275   }
6276
6277   if (var == "size") {
6278     if (p.has_flag(pg_pool_t::FLAG_NOSIZECHANGE)) {
6279       ss << "pool size change is disabled; you must unset nosizechange flag for the pool first";
6280       return -EPERM;
6281     }
6282     if (p.type == pg_pool_t::TYPE_ERASURE) {
6283       ss << "can not change the size of an erasure-coded pool";
6284       return -ENOTSUP;
6285     }
6286     if (interr.length()) {
6287       ss << "error parsing integer value '" << val << "': " << interr;
6288       return -EINVAL;
6289     }
6290     if (n <= 0 || n > 10) {
6291       ss << "pool size must be between 1 and 10";
6292       return -EINVAL;
6293     }
6294     int r = check_pg_num(pool, p.get_pg_num(), n, &ss);
6295     if (r < 0) {
6296       return r;
6297     }
6298     p.size = n;
6299     if (n < p.min_size)
6300       p.min_size = n;
6301   } else if (var == "min_size") {
6302     if (p.has_flag(pg_pool_t::FLAG_NOSIZECHANGE)) {
6303       ss << "pool min size change is disabled; you must unset nosizechange flag for the pool first";
6304       return -EPERM;
6305     }
6306     if (interr.length()) {
6307       ss << "error parsing integer value '" << val << "': " << interr;
6308       return -EINVAL;
6309     }
6310
6311     if (p.type != pg_pool_t::TYPE_ERASURE) {
6312       if (n < 1 || n > p.size) {
6313         ss << "pool min_size must be between 1 and " << (int)p.size;
6314         return -EINVAL;
6315       }
6316     } else {
6317        ErasureCodeInterfaceRef erasure_code;
6318        int k;
6319        stringstream tmp;
6320        int err = get_erasure_code(p.erasure_code_profile, &erasure_code, &tmp);
6321        if (err == 0) {
6322          k = erasure_code->get_data_chunk_count();
6323        } else {
6324          ss << __func__ << " get_erasure_code failed: " << tmp.rdbuf();
6325          return err;
6326        }
6327
6328        if (n < k || n > p.size) {
6329          ss << "pool min_size must be between " << k << " and " << (int)p.size;
6330          return -EINVAL;
6331        }
6332     }
6333     p.min_size = n;
6334   } else if (var == "auid") {
6335     if (interr.length()) {
6336       ss << "error parsing integer value '" << val << "': " << interr;
6337       return -EINVAL;
6338     }
6339     p.auid = n;
6340   } else if (var == "crash_replay_interval") {
6341     if (interr.length()) {
6342       ss << "error parsing integer value '" << val << "': " << interr;
6343       return -EINVAL;
6344     }
6345     p.crash_replay_interval = n;
6346   } else if (var == "pg_num") {
6347     if (p.has_flag(pg_pool_t::FLAG_NOPGCHANGE)) {
6348       ss << "pool pg_num change is disabled; you must unset nopgchange flag for the pool first";
6349       return -EPERM;
6350     }
6351     if (interr.length()) {
6352       ss << "error parsing integer value '" << val << "': " << interr;
6353       return -EINVAL;
6354     }
6355     if (n <= (int)p.get_pg_num()) {
6356       ss << "specified pg_num " << n << " <= current " << p.get_pg_num();
6357       if (n < (int)p.get_pg_num())
6358         return -EEXIST;
6359       return 0;
6360     }
6361     if (n > (unsigned)g_conf->mon_max_pool_pg_num) {
6362       ss << "'pg_num' must be greater than 0 and less than or equal to "
6363          << g_conf->mon_max_pool_pg_num
6364          << " (you may adjust 'mon max pool pg num' for higher values)";
6365       return -ERANGE;
6366     }
6367     int r = check_pg_num(pool, n, p.get_size(), &ss);
6368     if (r) {
6369       return r;
6370     }
6371     string force;
6372     cmd_getval(g_ceph_context,cmdmap, "force", force);
6373     if (p.cache_mode != pg_pool_t::CACHEMODE_NONE &&
6374         force != "--yes-i-really-mean-it") {
6375       ss << "splits in cache pools must be followed by scrubs and leave sufficient free space to avoid overfilling.  use --yes-i-really-mean-it to force.";
6376       return -EPERM;
6377     }
6378     int expected_osds = MIN(p.get_pg_num(), osdmap.get_num_osds());
6379     int64_t new_pgs = n - p.get_pg_num();
6380     if (new_pgs > g_conf->mon_osd_max_split_count * expected_osds) {
6381       ss << "specified pg_num " << n << " is too large (creating "
6382          << new_pgs << " new PGs on ~" << expected_osds
6383          << " OSDs exceeds per-OSD max of " << g_conf->mon_osd_max_split_count
6384          << ')';
6385       return -E2BIG;
6386     }
6387     p.set_pg_num(n);
6388     // force pre-luminous clients to resend their ops, since they
6389     // don't understand that split PGs now form a new interval.
6390     p.last_force_op_resend_preluminous = pending_inc.epoch;
6391   } else if (var == "pgp_num") {
6392     if (p.has_flag(pg_pool_t::FLAG_NOPGCHANGE)) {
6393       ss << "pool pgp_num change is disabled; you must unset nopgchange flag for the pool first";
6394       return -EPERM;
6395     }
6396     if (interr.length()) {
6397       ss << "error parsing integer value '" << val << "': " << interr;
6398       return -EINVAL;
6399     }
6400     if (n <= 0) {
6401       ss << "specified pgp_num must > 0, but you set to " << n;
6402       return -EINVAL;
6403     }
6404     if (n > (int)p.get_pg_num()) {
6405       ss << "specified pgp_num " << n << " > pg_num " << p.get_pg_num();
6406       return -EINVAL;
6407     }
6408     p.set_pgp_num(n);
6409   } else if (var == "crush_rule") {
6410     int id = osdmap.crush->get_rule_id(val);
6411     if (id == -ENOENT) {
6412       ss << "crush rule " << val << " does not exist";
6413       return -ENOENT;
6414     }
6415     if (id < 0) {
6416       ss << cpp_strerror(id);
6417       return -ENOENT;
6418     }
6419     if (!osdmap.crush->check_crush_rule(id, p.get_type(), p.get_size(), ss)) {
6420       return -EINVAL;
6421     }
6422     p.crush_rule = id;
6423   } else if (var == "nodelete" || var == "nopgchange" ||
6424              var == "nosizechange" || var == "write_fadvise_dontneed" ||
6425              var == "noscrub" || var == "nodeep-scrub") {
6426     uint64_t flag = pg_pool_t::get_flag_by_name(var);
6427     // make sure we only compare against 'n' if we didn't receive a string
6428     if (val == "true" || (interr.empty() && n == 1)) {
6429       p.set_flag(flag);
6430     } else if (val == "false" || (interr.empty() && n == 0)) {
6431       p.unset_flag(flag);
6432     } else {
6433       ss << "expecting value 'true', 'false', '0', or '1'";
6434       return -EINVAL;
6435     }
6436   } else if (var == "hashpspool") {
6437     uint64_t flag = pg_pool_t::get_flag_by_name(var);
6438     string force;
6439     cmd_getval(g_ceph_context, cmdmap, "force", force);
6440     if (force != "--yes-i-really-mean-it") {
6441       ss << "are you SURE?  this will remap all placement groups in this pool,"
6442             " this triggers large data movement,"
6443             " pass --yes-i-really-mean-it if you really do.";
6444       return -EPERM;
6445     }
6446     // make sure we only compare against 'n' if we didn't receive a string
6447     if (val == "true" || (interr.empty() && n == 1)) {
6448       p.set_flag(flag);
6449     } else if (val == "false" || (interr.empty() && n == 0)) {
6450       p.unset_flag(flag);
6451     } else {
6452       ss << "expecting value 'true', 'false', '0', or '1'";
6453       return -EINVAL;
6454     }
6455   } else if (var == "hit_set_type") {
6456     if (val == "none")
6457       p.hit_set_params = HitSet::Params();
6458     else {
6459       int err = check_cluster_features(CEPH_FEATURE_OSD_CACHEPOOL, ss);
6460       if (err)
6461         return err;
6462       if (val == "bloom") {
6463         BloomHitSet::Params *bsp = new BloomHitSet::Params;
6464         bsp->set_fpp(g_conf->osd_pool_default_hit_set_bloom_fpp);
6465         p.hit_set_params = HitSet::Params(bsp);
6466       } else if (val == "explicit_hash")
6467         p.hit_set_params = HitSet::Params(new ExplicitHashHitSet::Params);
6468       else if (val == "explicit_object")
6469         p.hit_set_params = HitSet::Params(new ExplicitObjectHitSet::Params);
6470       else {
6471         ss << "unrecognized hit_set type '" << val << "'";
6472         return -EINVAL;
6473       }
6474     }
6475   } else if (var == "hit_set_period") {
6476     if (interr.length()) {
6477       ss << "error parsing integer value '" << val << "': " << interr;
6478       return -EINVAL;
6479     }
6480     p.hit_set_period = n;
6481   } else if (var == "hit_set_count") {
6482     if (interr.length()) {
6483       ss << "error parsing integer value '" << val << "': " << interr;
6484       return -EINVAL;
6485     }
6486     p.hit_set_count = n;
6487   } else if (var == "hit_set_fpp") {
6488     if (floaterr.length()) {
6489       ss << "error parsing floating point value '" << val << "': " << floaterr;
6490       return -EINVAL;
6491     }
6492     if (p.hit_set_params.get_type() != HitSet::TYPE_BLOOM) {
6493       ss << "hit set is not of type Bloom; invalid to set a false positive rate!";
6494       return -EINVAL;
6495     }
6496     BloomHitSet::Params *bloomp = static_cast<BloomHitSet::Params*>(p.hit_set_params.impl.get());
6497     bloomp->set_fpp(f);
6498   } else if (var == "use_gmt_hitset") {
6499     if (val == "true" || (interr.empty() && n == 1)) {
6500       string force;
6501       cmd_getval(g_ceph_context, cmdmap, "force", force);
6502       if (!osdmap.get_num_up_osds() && force != "--yes-i-really-mean-it") {
6503         ss << "Not advisable to continue since no OSDs are up. Pass "
6504            << "--yes-i-really-mean-it if you really wish to continue.";
6505         return -EPERM;
6506       }
6507       if (!(osdmap.get_up_osd_features() & CEPH_FEATURE_OSD_HITSET_GMT)
6508           && force != "--yes-i-really-mean-it") {
6509         ss << "not all OSDs support GMT hit set.";
6510         return -EINVAL;
6511       }
6512       p.use_gmt_hitset = true;
6513     } else {
6514       ss << "expecting value 'true' or '1'";
6515       return -EINVAL;
6516     }
6517   } else if (var == "allow_ec_overwrites") {
6518     if (!p.is_erasure()) {
6519       ss << "ec overwrites can only be enabled for an erasure coded pool";
6520       return -EINVAL;
6521     }
6522     stringstream err;
6523     if (!g_conf->mon_debug_no_require_bluestore_for_ec_overwrites &&
6524         !is_pool_currently_all_bluestore(pool, p, &err)) {
6525       ss << "pool must only be stored on bluestore for scrubbing to work: " << err.str();
6526       return -EINVAL;
6527     }
6528     if (val == "true" || (interr.empty() && n == 1)) {
6529         p.flags |= pg_pool_t::FLAG_EC_OVERWRITES;
6530     } else if (val == "false" || (interr.empty() && n == 0)) {
6531       ss << "ec overwrites cannot be disabled once enabled";
6532       return -EINVAL;
6533     } else {
6534       ss << "expecting value 'true', 'false', '0', or '1'";
6535       return -EINVAL;
6536     }
6537   } else if (var == "target_max_objects") {
6538     if (interr.length()) {
6539       ss << "error parsing int '" << val << "': " << interr;
6540       return -EINVAL;
6541     }
6542     p.target_max_objects = n;
6543   } else if (var == "target_max_bytes") {
6544     if (interr.length()) {
6545       ss << "error parsing int '" << val << "': " << interr;
6546       return -EINVAL;
6547     }
6548     p.target_max_bytes = n;
6549   } else if (var == "cache_target_dirty_ratio") {
6550     if (floaterr.length()) {
6551       ss << "error parsing float '" << val << "': " << floaterr;
6552       return -EINVAL;
6553     }
6554     if (f < 0 || f > 1.0) {
6555       ss << "value must be in the range 0..1";
6556       return -ERANGE;
6557     }
6558     p.cache_target_dirty_ratio_micro = uf;
6559   } else if (var == "cache_target_dirty_high_ratio") {
6560     if (floaterr.length()) {
6561       ss << "error parsing float '" << val << "': " << floaterr;
6562       return -EINVAL;
6563     }
6564     if (f < 0 || f > 1.0) {
6565       ss << "value must be in the range 0..1";
6566       return -ERANGE;
6567     }
6568     p.cache_target_dirty_high_ratio_micro = uf;
6569   } else if (var == "cache_target_full_ratio") {
6570     if (floaterr.length()) {
6571       ss << "error parsing float '" << val << "': " << floaterr;
6572       return -EINVAL;
6573     }
6574     if (f < 0 || f > 1.0) {
6575       ss << "value must be in the range 0..1";
6576       return -ERANGE;
6577     }
6578     p.cache_target_full_ratio_micro = uf;
6579   } else if (var == "cache_min_flush_age") {
6580     if (interr.length()) {
6581       ss << "error parsing int '" << val << "': " << interr;
6582       return -EINVAL;
6583     }
6584     p.cache_min_flush_age = n;
6585   } else if (var == "cache_min_evict_age") {
6586     if (interr.length()) {
6587       ss << "error parsing int '" << val << "': " << interr;
6588       return -EINVAL;
6589     }
6590     p.cache_min_evict_age = n;
6591   } else if (var == "min_read_recency_for_promote") {
6592     if (interr.length()) {
6593       ss << "error parsing integer value '" << val << "': " << interr;
6594       return -EINVAL;
6595     }
6596     p.min_read_recency_for_promote = n;
6597   } else if (var == "hit_set_grade_decay_rate") {
6598     if (interr.length()) {
6599       ss << "error parsing integer value '" << val << "': " << interr;
6600       return -EINVAL;
6601     }
6602     if (n > 100 || n < 0) {
6603       ss << "value out of range,valid range is 0 - 100";
6604       return -EINVAL;
6605     }
6606     p.hit_set_grade_decay_rate = n;
6607   } else if (var == "hit_set_search_last_n") {
6608     if (interr.length()) {
6609       ss << "error parsing integer value '" << val << "': " << interr;
6610       return -EINVAL;
6611     }
6612     if (n > p.hit_set_count || n < 0) {
6613       ss << "value out of range,valid range is 0 - hit_set_count";
6614       return -EINVAL;
6615     }
6616     p.hit_set_search_last_n = n;
6617   } else if (var == "min_write_recency_for_promote") {
6618     if (interr.length()) {
6619       ss << "error parsing integer value '" << val << "': " << interr;
6620       return -EINVAL;
6621     }
6622     p.min_write_recency_for_promote = n;
6623   } else if (var == "fast_read") {
6624     if (p.is_replicated()) {
6625         ss << "fast read is not supported in replication pool";
6626         return -EINVAL;
6627     }
6628     if (val == "true" || (interr.empty() && n == 1)) {
6629       p.fast_read = true;
6630     } else if (val == "false" || (interr.empty() && n == 0)) {
6631       p.fast_read = false;
6632     } else {
6633       ss << "expecting value 'true', 'false', '0', or '1'";
6634       return -EINVAL;
6635     }
6636   } else if (pool_opts_t::is_opt_name(var)) {
6637     bool unset = val == "unset";
6638     if (var == "compression_mode") {
6639       if (!unset) {
6640         auto cmode = Compressor::get_comp_mode_type(val);
6641         if (!cmode) {
6642           ss << "unrecognized compression mode '" << val << "'";
6643           return -EINVAL;
6644         }
6645       }
6646     } else if (var == "compression_algorithm") {
6647       if (!unset) {
6648         auto alg = Compressor::get_comp_alg_type(val);
6649         if (!alg) {
6650           ss << "unrecognized compression_algorithm '" << val << "'";
6651           return -EINVAL;
6652         }
6653       }
6654     } else if (var == "compression_required_ratio") {
6655       if (floaterr.length()) {
6656         ss << "error parsing float value '" << val << "': " << floaterr;
6657         return -EINVAL;
6658       }
6659       if (f < 0 || f > 1) {
6660         ss << "compression_required_ratio is out of range (0-1): '" << val << "'";
6661         return -EINVAL;
6662       }
6663     } else if (var == "csum_type") {
6664       auto t = unset ? 0 : Checksummer::get_csum_string_type(val);
6665       if (t < 0 ) {
6666         ss << "unrecognized csum_type '" << val << "'";
6667         return -EINVAL;
6668       }
6669       //preserve csum_type numeric value
6670       n = t;
6671       interr.clear(); 
6672     } else if (var == "compression_max_blob_size" ||
6673                var == "compression_min_blob_size" ||
6674                var == "csum_max_block" ||
6675                var == "csum_min_block") {
6676       if (interr.length()) {
6677         ss << "error parsing int value '" << val << "': " << interr;
6678         return -EINVAL;
6679       }
6680     }
6681
6682     pool_opts_t::opt_desc_t desc = pool_opts_t::get_opt_desc(var);
6683     switch (desc.type) {
6684     case pool_opts_t::STR:
6685       if (unset) {
6686         p.opts.unset(desc.key);
6687       } else {
6688         p.opts.set(desc.key, static_cast<std::string>(val));
6689       }
6690       break;
6691     case pool_opts_t::INT:
6692       if (interr.length()) {
6693         ss << "error parsing integer value '" << val << "': " << interr;
6694         return -EINVAL;
6695       }
6696       if (n == 0) {
6697         p.opts.unset(desc.key);
6698       } else {
6699         p.opts.set(desc.key, static_cast<int>(n));
6700       }
6701       break;
6702     case pool_opts_t::DOUBLE:
6703       if (floaterr.length()) {
6704         ss << "error parsing floating point value '" << val << "': " << floaterr;
6705         return -EINVAL;
6706       }
6707       if (f == 0) {
6708         p.opts.unset(desc.key);
6709       } else {
6710         p.opts.set(desc.key, static_cast<double>(f));
6711       }
6712       break;
6713     default:
6714       assert(!"unknown type");
6715     }
6716   } else {
6717     ss << "unrecognized variable '" << var << "'";
6718     return -EINVAL;
6719   }
6720   if (val != "unset") {
6721     ss << "set pool " << pool << " " << var << " to " << val;
6722   } else {
6723     ss << "unset pool " << pool << " " << var;
6724   }
6725   p.last_change = pending_inc.epoch;
6726   pending_inc.new_pools[pool] = p;
6727   return 0;
6728 }
6729
6730 int OSDMonitor::prepare_command_pool_application(const string &prefix,
6731                                                  map<string,cmd_vartype> &cmdmap,
6732                                                  stringstream& ss)
6733 {
6734   string pool_name;
6735   cmd_getval(g_ceph_context, cmdmap, "pool", pool_name);
6736   int64_t pool = osdmap.lookup_pg_pool_name(pool_name.c_str());
6737   if (pool < 0) {
6738     ss << "unrecognized pool '" << pool_name << "'";
6739     return -ENOENT;
6740   }
6741
6742   pg_pool_t p = *osdmap.get_pg_pool(pool);
6743   if (pending_inc.new_pools.count(pool)) {
6744     p = pending_inc.new_pools[pool];
6745   }
6746
6747   string app;
6748   cmd_getval(g_ceph_context, cmdmap, "app", app);
6749   bool app_exists = (p.application_metadata.count(app) > 0);
6750
6751   if (boost::algorithm::ends_with(prefix, "enable")) {
6752     if (app.empty()) {
6753       ss << "application name must be provided";
6754       return -EINVAL;
6755     }
6756
6757     if (p.is_tier()) {
6758       ss << "application must be enabled on base tier";
6759       return -EINVAL;
6760     }
6761
6762     string force;
6763     cmd_getval(g_ceph_context, cmdmap, "force", force);
6764
6765     if (!app_exists && !p.application_metadata.empty() &&
6766         force != "--yes-i-really-mean-it") {
6767       ss << "Are you SURE? Pool '" << pool_name << "' already has an enabled "
6768          << "application; pass --yes-i-really-mean-it to proceed anyway";
6769       return -EPERM;
6770     }
6771
6772     if (!app_exists && p.application_metadata.size() >= MAX_POOL_APPLICATIONS) {
6773       ss << "too many enabled applications on pool '" << pool_name << "'; "
6774          << "max " << MAX_POOL_APPLICATIONS;
6775       return -EINVAL;
6776     }
6777
6778     if (app.length() > MAX_POOL_APPLICATION_LENGTH) {
6779       ss << "application name '" << app << "' too long; max length "
6780          << MAX_POOL_APPLICATION_LENGTH;
6781       return -EINVAL;
6782     }
6783
6784     if (!app_exists) {
6785       p.application_metadata[app] = {};
6786     }
6787     ss << "enabled application '" << app << "' on pool '" << pool_name << "'";
6788
6789   } else if (boost::algorithm::ends_with(prefix, "disable")) {
6790     string force;
6791     cmd_getval(g_ceph_context, cmdmap, "force", force);
6792
6793     if (force != "--yes-i-really-mean-it") {
6794       ss << "Are you SURE? Disabling an application within a pool might result "
6795          << "in loss of application functionality; pass "
6796          << "--yes-i-really-mean-it to proceed anyway";
6797       return -EPERM;
6798     }
6799
6800     if (!app_exists) {
6801       ss << "application '" << app << "' is not enabled on pool '" << pool_name
6802          << "'";
6803       return 0; // idempotent
6804     }
6805
6806     p.application_metadata.erase(app);
6807     ss << "disable application '" << app << "' on pool '" << pool_name << "'";
6808
6809   } else if (boost::algorithm::ends_with(prefix, "set")) {
6810     if (p.is_tier()) {
6811       ss << "application metadata must be set on base tier";
6812       return -EINVAL;
6813     }
6814
6815     if (!app_exists) {
6816       ss << "application '" << app << "' is not enabled on pool '" << pool_name
6817          << "'";
6818       return -ENOENT;
6819     }
6820
6821     string key;
6822     cmd_getval(g_ceph_context, cmdmap, "key", key);
6823
6824     if (key.empty()) {
6825       ss << "key must be provided";
6826       return -EINVAL;
6827     }
6828
6829     auto &app_keys = p.application_metadata[app];
6830     if (app_keys.count(key) == 0 &&
6831         app_keys.size() >= MAX_POOL_APPLICATION_KEYS) {
6832       ss << "too many keys set for application '" << app << "' on pool '"
6833          << pool_name << "'; max " << MAX_POOL_APPLICATION_KEYS;
6834       return -EINVAL;
6835     }
6836
6837     if (key.length() > MAX_POOL_APPLICATION_LENGTH) {
6838       ss << "key '" << app << "' too long; max length "
6839          << MAX_POOL_APPLICATION_LENGTH;
6840       return -EINVAL;
6841     }
6842
6843     string value;
6844     cmd_getval(g_ceph_context, cmdmap, "value", value);
6845     if (value.length() > MAX_POOL_APPLICATION_LENGTH) {
6846       ss << "value '" << value << "' too long; max length "
6847          << MAX_POOL_APPLICATION_LENGTH;
6848       return -EINVAL;
6849     }
6850
6851     p.application_metadata[app][key] = value;
6852     ss << "set application '" << app << "' key '" << key << "' to '"
6853        << value << "' on pool '" << pool_name << "'";
6854   } else if (boost::algorithm::ends_with(prefix, "rm")) {
6855     if (!app_exists) {
6856       ss << "application '" << app << "' is not enabled on pool '" << pool_name
6857          << "'";
6858       return -ENOENT;
6859     }
6860
6861     string key;
6862     cmd_getval(g_ceph_context, cmdmap, "key", key);
6863     auto it = p.application_metadata[app].find(key);
6864     if (it == p.application_metadata[app].end()) {
6865       ss << "application '" << app << "' on pool '" << pool_name
6866          << "' does not have key '" << key << "'";
6867       return 0; // idempotent
6868     }
6869
6870     p.application_metadata[app].erase(it);
6871     ss << "removed application '" << app << "' key '" << key << "' on pool '"
6872        << pool_name << "'";
6873   } else {
6874     assert(false);
6875   }
6876
6877   p.last_change = pending_inc.epoch;
6878   pending_inc.new_pools[pool] = p;
6879   return 0;
6880 }
6881
6882 int OSDMonitor::_prepare_command_osd_crush_remove(
6883     CrushWrapper &newcrush,
6884     int32_t id,
6885     int32_t ancestor,
6886     bool has_ancestor,
6887     bool unlink_only)
6888 {
6889   int err = 0;
6890
6891   if (has_ancestor) {
6892     err = newcrush.remove_item_under(g_ceph_context, id, ancestor,
6893         unlink_only);
6894   } else {
6895     err = newcrush.remove_item(g_ceph_context, id, unlink_only);
6896   }
6897   return err;
6898 }
6899
6900 void OSDMonitor::do_osd_crush_remove(CrushWrapper& newcrush)
6901 {
6902   pending_inc.crush.clear();
6903   newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
6904 }
6905
6906 int OSDMonitor::prepare_command_osd_crush_remove(
6907     CrushWrapper &newcrush,
6908     int32_t id,
6909     int32_t ancestor,
6910     bool has_ancestor,
6911     bool unlink_only)
6912 {
6913   int err = _prepare_command_osd_crush_remove(
6914       newcrush, id, ancestor,
6915       has_ancestor, unlink_only);
6916
6917   if (err < 0)
6918     return err;
6919
6920   assert(err == 0);
6921   do_osd_crush_remove(newcrush);
6922
6923   return 0;
6924 }
6925
6926 int OSDMonitor::prepare_command_osd_remove(int32_t id)
6927 {
6928   if (osdmap.is_up(id)) {
6929     return -EBUSY;
6930   }
6931
6932   pending_inc.new_state[id] = osdmap.get_state(id);
6933   pending_inc.new_uuid[id] = uuid_d();
6934   pending_metadata_rm.insert(id);
6935   pending_metadata.erase(id);
6936
6937   return 0;
6938 }
6939
6940 int32_t OSDMonitor::_allocate_osd_id(int32_t* existing_id)
6941 {
6942   assert(existing_id);
6943   *existing_id = -1;
6944
6945   for (int32_t i = 0; i < osdmap.get_max_osd(); ++i) {
6946     if (!osdmap.exists(i) &&
6947         pending_inc.new_up_client.count(i) == 0 &&
6948         (pending_inc.new_state.count(i) == 0 ||
6949          (pending_inc.new_state[i] & CEPH_OSD_EXISTS) == 0)) {
6950       *existing_id = i;
6951       return -1;
6952     }
6953   }
6954
6955   if (pending_inc.new_max_osd < 0) {
6956     return osdmap.get_max_osd();
6957   }
6958   return pending_inc.new_max_osd;
6959 }
6960
6961 void OSDMonitor::do_osd_create(
6962     const int32_t id,
6963     const uuid_d& uuid,
6964     int32_t* new_id)
6965 {
6966   dout(10) << __func__ << " uuid " << uuid << dendl;
6967   assert(new_id);
6968
6969   // We presume validation has been performed prior to calling this
6970   // function. We assert with prejudice.
6971
6972   int32_t allocated_id = -1; // declare here so we can jump
6973   int32_t existing_id = -1;
6974   if (!uuid.is_zero()) {
6975     existing_id = osdmap.identify_osd(uuid);
6976     if (existing_id >= 0) {
6977       assert(id < 0 || id == existing_id);
6978       *new_id = existing_id;
6979       goto out;
6980     } else if (id >= 0) {
6981       // uuid does not exist, and id has been provided, so just create
6982       // the new osd.id
6983       *new_id = id;
6984       goto out;
6985     }
6986   }
6987
6988   // allocate a new id
6989   allocated_id = _allocate_osd_id(&existing_id);
6990   dout(10) << __func__ << " allocated id " << allocated_id
6991            << " existing id " << existing_id << dendl;
6992   if (existing_id >= 0) {
6993     assert(existing_id < osdmap.get_max_osd());
6994     assert(allocated_id < 0);
6995     pending_inc.new_weight[existing_id] = CEPH_OSD_OUT;
6996     *new_id = existing_id;
6997
6998   } else if (allocated_id >= 0) {
6999     assert(existing_id < 0);
7000     // raise max_osd
7001     if (pending_inc.new_max_osd < 0) {
7002       pending_inc.new_max_osd = osdmap.get_max_osd() + 1;
7003     } else {
7004       ++pending_inc.new_max_osd;
7005     }
7006     *new_id = pending_inc.new_max_osd - 1;
7007     assert(*new_id == allocated_id);
7008   } else {
7009     assert(0 == "unexpected condition");
7010   }
7011
7012 out:
7013   dout(10) << __func__ << " using id " << *new_id << dendl;
7014   if (osdmap.get_max_osd() <= *new_id && pending_inc.new_max_osd <= *new_id) {
7015     pending_inc.new_max_osd = *new_id + 1;
7016   }
7017
7018   pending_inc.new_state[*new_id] |= CEPH_OSD_EXISTS | CEPH_OSD_NEW;
7019   if (!uuid.is_zero())
7020     pending_inc.new_uuid[*new_id] = uuid;
7021 }
7022
7023 int OSDMonitor::validate_osd_create(
7024     const int32_t id,
7025     const uuid_d& uuid,
7026     const bool check_osd_exists,
7027     int32_t* existing_id,
7028     stringstream& ss)
7029 {
7030
7031   dout(10) << __func__ << " id " << id << " uuid " << uuid
7032            << " check_osd_exists " << check_osd_exists << dendl;
7033
7034   assert(existing_id);
7035
7036   if (id < 0 && uuid.is_zero()) {
7037     // we have nothing to validate
7038     *existing_id = -1;
7039     return 0;
7040   } else if (uuid.is_zero()) {
7041     // we have an id but we will ignore it - because that's what
7042     // `osd create` does.
7043     return 0;
7044   }
7045
7046   /*
7047    * This function will be used to validate whether we are able to
7048    * create a new osd when the `uuid` is specified.
7049    *
7050    * It will be used by both `osd create` and `osd new`, as the checks
7051    * are basically the same when it pertains to osd id and uuid validation.
7052    * However, `osd create` presumes an `uuid` is optional, for legacy
7053    * reasons, while `osd new` requires the `uuid` to be provided. This
7054    * means that `osd create` will not be idempotent if an `uuid` is not
7055    * provided, but we will always guarantee the idempotency of `osd new`.
7056    */
7057
7058   assert(!uuid.is_zero());
7059   if (pending_inc.identify_osd(uuid) >= 0) {
7060     // osd is about to exist
7061     return -EAGAIN;
7062   }
7063
7064   int32_t i = osdmap.identify_osd(uuid);
7065   if (i >= 0) {
7066     // osd already exists
7067     if (id >= 0 && i != id) {
7068       ss << "uuid " << uuid << " already in use for different id " << i;
7069       return -EEXIST;
7070     }
7071     // return a positive errno to distinguish between a blocking error
7072     // and an error we consider to not be a problem (i.e., this would be
7073     // an idempotent operation).
7074     *existing_id = i;
7075     return EEXIST;
7076   }
7077   // i < 0
7078   if (id >= 0) {
7079     if (pending_inc.new_state.count(id)) {
7080       // osd is about to exist
7081       return -EAGAIN;
7082     }
7083     // we may not care if an osd exists if we are recreating a previously
7084     // destroyed osd.
7085     if (check_osd_exists && osdmap.exists(id)) {
7086       ss << "id " << id << " already in use and does not match uuid "
7087          << uuid;
7088       return -EINVAL;
7089     }
7090   }
7091   return 0;
7092 }
7093
7094 int OSDMonitor::prepare_command_osd_create(
7095     const int32_t id,
7096     const uuid_d& uuid,
7097     int32_t* existing_id,
7098     stringstream& ss)
7099 {
7100   dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
7101   assert(existing_id);
7102   if (osdmap.is_destroyed(id)) {
7103     ss << "ceph osd create has been deprecated. Please use ceph osd new "
7104           "instead.";
7105     return -EINVAL;
7106   }
7107
7108   if (uuid.is_zero()) {
7109     dout(10) << __func__ << " no uuid; assuming legacy `osd create`" << dendl;
7110   }
7111
7112   return validate_osd_create(id, uuid, true, existing_id, ss);
7113 }
7114
7115 int OSDMonitor::prepare_command_osd_new(
7116     MonOpRequestRef op,
7117     const map<string,cmd_vartype>& cmdmap,
7118     const map<string,string>& secrets,
7119     stringstream &ss,
7120     Formatter *f)
7121 {
7122   uuid_d uuid;
7123   string uuidstr;
7124   int64_t id = -1;
7125
7126   assert(paxos->is_plugged());
7127
7128   dout(10) << __func__ << " " << op << dendl;
7129
7130   /* validate command. abort now if something's wrong. */
7131
7132   /* `osd new` will expect a `uuid` to be supplied; `id` is optional.
7133    *
7134    * If `id` is not specified, we will identify any existing osd based
7135    * on `uuid`. Operation will be idempotent iff secrets match.
7136    *
7137    * If `id` is specified, we will identify any existing osd based on
7138    * `uuid` and match against `id`. If they match, operation will be
7139    * idempotent iff secrets match.
7140    *
7141    * `-i secrets.json` will be optional. If supplied, will be used
7142    * to check for idempotency when `id` and `uuid` match.
7143    *
7144    * If `id` is not specified, and `uuid` does not exist, an id will
7145    * be found or allocated for the osd.
7146    *
7147    * If `id` is specified, and the osd has been previously marked
7148    * as destroyed, then the `id` will be reused.
7149    */
7150   if (!cmd_getval(g_ceph_context, cmdmap, "uuid", uuidstr)) {
7151     ss << "requires the OSD's UUID to be specified.";
7152     return -EINVAL;
7153   } else if (!uuid.parse(uuidstr.c_str())) {
7154     ss << "invalid UUID value '" << uuidstr << "'.";
7155     return -EINVAL;
7156   }
7157
7158   if (cmd_getval(g_ceph_context, cmdmap, "id", id) &&
7159       (id < 0)) {
7160     ss << "invalid OSD id; must be greater or equal than zero.";
7161     return -EINVAL;
7162   }
7163
7164   // are we running an `osd create`-like command, or recreating
7165   // a previously destroyed osd?
7166
7167   bool is_recreate_destroyed = (id >= 0 && osdmap.is_destroyed(id));
7168
7169   // we will care about `id` to assess whether osd is `destroyed`, or
7170   // to create a new osd.
7171   // we will need an `id` by the time we reach auth.
7172
7173   int32_t existing_id = -1;
7174   int err = validate_osd_create(id, uuid, !is_recreate_destroyed,
7175                                 &existing_id, ss);
7176
7177   bool may_be_idempotent = false;
7178   if (err == EEXIST) {
7179     // this is idempotent from the osdmon's point-of-view
7180     may_be_idempotent = true;
7181     assert(existing_id >= 0);
7182     id = existing_id;
7183   } else if (err < 0) {
7184     return err;
7185   }
7186
7187   if (!may_be_idempotent) {
7188     // idempotency is out of the window. We are either creating a new
7189     // osd or recreating a destroyed osd.
7190     //
7191     // We now need to figure out if we have an `id` (and if it's valid),
7192     // of find an `id` if we don't have one.
7193
7194     // NOTE: we need to consider the case where the `id` is specified for
7195     // `osd create`, and we must honor it. So this means checking if
7196     // the `id` is destroyed, and if so assume the destroy; otherwise,
7197     // check if it `exists` - in which case we complain about not being
7198     // `destroyed`. In the end, if nothing fails, we must allow the
7199     // creation, so that we are compatible with `create`.
7200     if (id >= 0 && osdmap.exists(id) && !osdmap.is_destroyed(id)) {
7201       dout(10) << __func__ << " osd." << id << " isn't destroyed" << dendl;
7202       ss << "OSD " << id << " has not yet been destroyed";
7203       return -EINVAL;
7204     } else if (id < 0) {
7205       // find an `id`
7206       id = _allocate_osd_id(&existing_id);
7207       if (id < 0) {
7208         assert(existing_id >= 0);
7209         id = existing_id;
7210       }
7211       dout(10) << __func__ << " found id " << id << " to use" << dendl;
7212     } else if (id >= 0 && osdmap.is_destroyed(id)) {
7213       dout(10) << __func__ << " recreating osd." << id << dendl;
7214     } else {
7215       dout(10) << __func__ << " creating new osd." << id << dendl;
7216     }
7217   } else {
7218     assert(id >= 0);
7219     assert(osdmap.exists(id));
7220   }
7221
7222   // we are now able to either create a brand new osd or reuse an existing
7223   // osd that has been previously destroyed.
7224
7225   dout(10) << __func__ << " id " << id << " uuid " << uuid << dendl;
7226
7227   if (may_be_idempotent && secrets.empty()) {
7228     // nothing to do, really.
7229     dout(10) << __func__ << " idempotent and no secrets -- no op." << dendl;
7230     assert(id >= 0);
7231     if (f) {
7232       f->open_object_section("created_osd");
7233       f->dump_int("osdid", id);
7234       f->close_section();
7235     } else {
7236       ss << id;
7237     }
7238     return EEXIST;
7239   }
7240
7241   string cephx_secret, lockbox_secret, dmcrypt_key;
7242   bool has_lockbox = false;
7243   bool has_secrets = (!secrets.empty());
7244
7245   ConfigKeyService *svc = nullptr;
7246   AuthMonitor::auth_entity_t cephx_entity, lockbox_entity;
7247
7248   if (has_secrets) {
7249     if (secrets.count("cephx_secret") == 0) {
7250       ss << "requires a cephx secret.";
7251       return -EINVAL;
7252     }
7253     cephx_secret = secrets.at("cephx_secret");
7254
7255     bool has_lockbox_secret = (secrets.count("cephx_lockbox_secret") > 0);
7256     bool has_dmcrypt_key = (secrets.count("dmcrypt_key") > 0);
7257
7258     dout(10) << __func__ << " has lockbox " << has_lockbox_secret
7259              << " dmcrypt " << has_dmcrypt_key << dendl;
7260
7261     if (has_lockbox_secret && has_dmcrypt_key) {
7262       has_lockbox = true;
7263       lockbox_secret = secrets.at("cephx_lockbox_secret");
7264       dmcrypt_key = secrets.at("dmcrypt_key");
7265     } else if (!has_lockbox_secret != !has_dmcrypt_key) {
7266       ss << "requires both a cephx lockbox secret and a dm-crypt key.";
7267       return -EINVAL;
7268     }
7269
7270     dout(10) << __func__ << " validate secrets using osd id " << id << dendl;
7271
7272     err = mon->authmon()->validate_osd_new(id, uuid,
7273         cephx_secret,
7274         lockbox_secret,
7275         cephx_entity,
7276         lockbox_entity,
7277         ss);
7278     if (err < 0) {
7279       return err;
7280     } else if (may_be_idempotent && err != EEXIST) {
7281       // for this to be idempotent, `id` should already be >= 0; no need
7282       // to use validate_id.
7283       assert(id >= 0);
7284       ss << "osd." << id << " exists but secrets do not match";
7285       return -EEXIST;
7286     }
7287
7288     if (has_lockbox) {
7289       svc = (ConfigKeyService*)mon->config_key_service;
7290       err = svc->validate_osd_new(uuid, dmcrypt_key, ss);
7291       if (err < 0) {
7292         return err;
7293       } else if (may_be_idempotent && err != EEXIST) {
7294         assert(id >= 0);
7295         ss << "osd." << id << " exists but dm-crypt key does not match.";
7296         return -EEXIST;
7297       }
7298     }
7299   }
7300   assert(!has_secrets || !cephx_secret.empty());
7301   assert(!has_lockbox || !lockbox_secret.empty());
7302
7303   if (may_be_idempotent) {
7304     // we have nothing to do for either the osdmon or the authmon,
7305     // and we have no lockbox - so the config key service will not be
7306     // touched. This is therefore an idempotent operation, and we can
7307     // just return right away.
7308     dout(10) << __func__ << " idempotent -- no op." << dendl;
7309     assert(id >= 0);
7310     if (f) {
7311       f->open_object_section("created_osd");
7312       f->dump_int("osdid", id);
7313       f->close_section();
7314     } else {
7315       ss << id;
7316     }
7317     return EEXIST;
7318   }
7319   assert(!may_be_idempotent);
7320
7321   // perform updates.
7322   if (has_secrets) {
7323     assert(!cephx_secret.empty());
7324     assert((lockbox_secret.empty() && dmcrypt_key.empty()) ||
7325            (!lockbox_secret.empty() && !dmcrypt_key.empty()));
7326
7327     err = mon->authmon()->do_osd_new(cephx_entity,
7328         lockbox_entity,
7329         has_lockbox);
7330     assert(0 == err);
7331
7332     if (has_lockbox) {
7333       assert(nullptr != svc);
7334       svc->do_osd_new(uuid, dmcrypt_key);
7335     }
7336   }
7337
7338   if (is_recreate_destroyed) {
7339     assert(id >= 0);
7340     assert(osdmap.is_destroyed(id));
7341     pending_inc.new_weight[id] = CEPH_OSD_OUT;
7342     pending_inc.new_state[id] |= CEPH_OSD_DESTROYED | CEPH_OSD_NEW;
7343     if (osdmap.get_state(id) & CEPH_OSD_UP) {
7344       // due to http://tracker.ceph.com/issues/20751 some clusters may
7345       // have UP set for non-existent OSDs; make sure it is cleared
7346       // for a newly created osd.
7347       pending_inc.new_state[id] |= CEPH_OSD_UP;
7348     }
7349     pending_inc.new_uuid[id] = uuid;
7350   } else {
7351     assert(id >= 0);
7352     int32_t new_id = -1;
7353     do_osd_create(id, uuid, &new_id);
7354     assert(new_id >= 0);
7355     assert(id == new_id);
7356   }
7357
7358   if (f) {
7359     f->open_object_section("created_osd");
7360     f->dump_int("osdid", id);
7361     f->close_section();
7362   } else {
7363     ss << id;
7364   }
7365
7366   return 0;
7367 }
7368
7369 bool OSDMonitor::prepare_command(MonOpRequestRef op)
7370 {
7371   op->mark_osdmon_event(__func__);
7372   MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
7373   stringstream ss;
7374   map<string, cmd_vartype> cmdmap;
7375   if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
7376     string rs = ss.str();
7377     mon->reply_command(op, -EINVAL, rs, get_last_committed());
7378     return true;
7379   }
7380
7381   MonSession *session = m->get_session();
7382   if (!session) {
7383     mon->reply_command(op, -EACCES, "access denied", get_last_committed());
7384     return true;
7385   }
7386
7387   return prepare_command_impl(op, cmdmap);
7388 }
7389
7390 static int parse_reweights(CephContext *cct,
7391                            const map<string,cmd_vartype> &cmdmap,
7392                            const OSDMap& osdmap,
7393                            map<int32_t, uint32_t>* weights)
7394 {
7395   string weights_str;
7396   if (!cmd_getval(g_ceph_context, cmdmap, "weights", weights_str)) {
7397     return -EINVAL;
7398   }
7399   std::replace(begin(weights_str), end(weights_str), '\'', '"');
7400   json_spirit::mValue json_value;
7401   if (!json_spirit::read(weights_str, json_value)) {
7402     return -EINVAL;
7403   }
7404   if (json_value.type() != json_spirit::obj_type) {
7405     return -EINVAL;
7406   }
7407   const auto obj = json_value.get_obj();
7408   try {
7409     for (auto& osd_weight : obj) {
7410       auto osd_id = std::stoi(osd_weight.first);
7411       if (!osdmap.exists(osd_id)) {
7412         return -ENOENT;
7413       }
7414       if (osd_weight.second.type() != json_spirit::str_type) {
7415         return -EINVAL;
7416       }
7417       auto weight = std::stoul(osd_weight.second.get_str());
7418       weights->insert({osd_id, weight});
7419     }
7420   } catch (const std::logic_error& e) {
7421     return -EINVAL;
7422   }
7423   return 0;
7424 }
7425
7426 int OSDMonitor::prepare_command_osd_destroy(
7427     int32_t id,
7428     stringstream& ss)
7429 {
7430   assert(paxos->is_plugged());
7431
7432   // we check if the osd exists for the benefit of `osd purge`, which may
7433   // have previously removed the osd. If the osd does not exist, return
7434   // -ENOENT to convey this, and let the caller deal with it.
7435   //
7436   // we presume that all auth secrets and config keys were removed prior
7437   // to this command being called. if they exist by now, we also assume
7438   // they must have been created by some other command and do not pertain
7439   // to this non-existent osd.
7440   if (!osdmap.exists(id)) {
7441     dout(10) << __func__ << " osd." << id << " does not exist." << dendl;
7442     return -ENOENT;
7443   }
7444
7445   uuid_d uuid = osdmap.get_uuid(id);
7446   dout(10) << __func__ << " destroying osd." << id
7447            << " uuid " << uuid << dendl;
7448
7449   // if it has been destroyed, we assume our work here is done.
7450   if (osdmap.is_destroyed(id)) {
7451     ss << "destroyed osd." << id;
7452     return 0;
7453   }
7454
7455   EntityName cephx_entity, lockbox_entity;
7456   bool idempotent_auth = false, idempotent_cks = false;
7457
7458   int err = mon->authmon()->validate_osd_destroy(id, uuid,
7459                                                  cephx_entity,
7460                                                  lockbox_entity,
7461                                                  ss);
7462   if (err < 0) {
7463     if (err == -ENOENT) {
7464       idempotent_auth = true;
7465     } else {
7466       return err;
7467     }
7468   }
7469
7470   ConfigKeyService *svc = (ConfigKeyService*)mon->config_key_service;
7471   err = svc->validate_osd_destroy(id, uuid);
7472   if (err < 0) {
7473     assert(err == -ENOENT);
7474     err = 0;
7475     idempotent_cks = true;
7476   }
7477
7478   if (!idempotent_auth) {
7479     err = mon->authmon()->do_osd_destroy(cephx_entity, lockbox_entity);
7480     assert(0 == err);
7481   }
7482
7483   if (!idempotent_cks) {
7484     svc->do_osd_destroy(id, uuid);
7485   }
7486
7487   pending_inc.new_state[id] = CEPH_OSD_DESTROYED;
7488   pending_inc.new_uuid[id] = uuid_d();
7489
7490   // we can only propose_pending() once per service, otherwise we'll be
7491   // defying PaxosService and all laws of nature. Therefore, as we may
7492   // be used during 'osd purge', let's keep the caller responsible for
7493   // proposing.
7494   assert(err == 0);
7495   return 0;
7496 }
7497
7498 int OSDMonitor::prepare_command_osd_purge(
7499     int32_t id,
7500     stringstream& ss)
7501 {
7502   assert(paxos->is_plugged());
7503   dout(10) << __func__ << " purging osd." << id << dendl;
7504
7505   assert(!osdmap.is_up(id));
7506
7507   /*
7508    * This may look a bit weird, but this is what's going to happen:
7509    *
7510    *  1. we make sure that removing from crush works
7511    *  2. we call `prepare_command_osd_destroy()`. If it returns an
7512    *     error, then we abort the whole operation, as no updates
7513    *     have been made. However, we this function will have
7514    *     side-effects, thus we need to make sure that all operations
7515    *     performed henceforth will *always* succeed.
7516    *  3. we call `prepare_command_osd_remove()`. Although this
7517    *     function can return an error, it currently only checks if the
7518    *     osd is up - and we have made sure that it is not so, so there
7519    *     is no conflict, and it is effectively an update.
7520    *  4. finally, we call `do_osd_crush_remove()`, which will perform
7521    *     the crush update we delayed from before.
7522    */
7523
7524   CrushWrapper newcrush;
7525   _get_pending_crush(newcrush);
7526
7527   bool may_be_idempotent = false;
7528
7529   int err = _prepare_command_osd_crush_remove(newcrush, id, 0, false, false);
7530   if (err == -ENOENT) {
7531     err = 0;
7532     may_be_idempotent = true;
7533   } else if (err < 0) {
7534     ss << "error removing osd." << id << " from crush";
7535     return err;
7536   }
7537
7538   // no point destroying the osd again if it has already been marked destroyed
7539   if (!osdmap.is_destroyed(id)) {
7540     err = prepare_command_osd_destroy(id, ss);
7541     if (err < 0) {
7542       if (err == -ENOENT) {
7543         err = 0;
7544       } else {
7545         return err;
7546       }
7547     } else {
7548       may_be_idempotent = false;
7549     }
7550   }
7551   assert(0 == err);
7552
7553   if (may_be_idempotent && !osdmap.exists(id)) {
7554     dout(10) << __func__ << " osd." << id << " does not exist and "
7555              << "we are idempotent." << dendl;
7556     return -ENOENT;
7557   }
7558
7559   err = prepare_command_osd_remove(id);
7560   // we should not be busy, as we should have made sure this id is not up.
7561   assert(0 == err);
7562
7563   do_osd_crush_remove(newcrush);
7564   return 0;
7565 }
7566
7567 bool OSDMonitor::prepare_command_impl(MonOpRequestRef op,
7568                                       map<string,cmd_vartype> &cmdmap)
7569 {
7570   op->mark_osdmon_event(__func__);
7571   MMonCommand *m = static_cast<MMonCommand*>(op->get_req());
7572   bool ret = false;
7573   stringstream ss;
7574   string rs;
7575   bufferlist rdata;
7576   int err = 0;
7577
7578   string format;
7579   cmd_getval(g_ceph_context, cmdmap, "format", format, string("plain"));
7580   boost::scoped_ptr<Formatter> f(Formatter::create(format));
7581
7582   string prefix;
7583   cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
7584
7585   int64_t osdid;
7586   string name;
7587   bool osdid_present = cmd_getval(g_ceph_context, cmdmap, "id", osdid);
7588   if (osdid_present) {
7589     ostringstream oss;
7590     oss << "osd." << osdid;
7591     name = oss.str();
7592   }
7593
7594   // Even if there's a pending state with changes that could affect
7595   // a command, considering that said state isn't yet committed, we
7596   // just don't care about those changes if the command currently being
7597   // handled acts as a no-op against the current committed state.
7598   // In a nutshell, we assume this command  happens *before*.
7599   //
7600   // Let me make this clearer:
7601   //
7602   //   - If we have only one client, and that client issues some
7603   //     operation that would conflict with this operation  but is
7604   //     still on the pending state, then we would be sure that said
7605   //     operation wouldn't have returned yet, so the client wouldn't
7606   //     issue this operation (unless the client didn't wait for the
7607   //     operation to finish, and that would be the client's own fault).
7608   //
7609   //   - If we have more than one client, each client will observe
7610   //     whatever is the state at the moment of the commit.  So, if we
7611   //     have two clients, one issuing an unlink and another issuing a
7612   //     link, and if the link happens while the unlink is still on the
7613   //     pending state, from the link's point-of-view this is a no-op.
7614   //     If different clients are issuing conflicting operations and
7615   //     they care about that, then the clients should make sure they
7616   //     enforce some kind of concurrency mechanism -- from our
7617   //     perspective that's what Douglas Adams would call an SEP.
7618   //
7619   // This should be used as a general guideline for most commands handled
7620   // in this function.  Adapt as you see fit, but please bear in mind that
7621   // this is the expected behavior.
7622    
7623  
7624   if (prefix == "osd setcrushmap" ||
7625       (prefix == "osd crush set" && !osdid_present)) {
7626     if (pending_inc.crush.length()) {
7627       dout(10) << __func__ << " waiting for pending crush update " << dendl;
7628       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
7629       return true;
7630     }
7631     dout(10) << "prepare_command setting new crush map" << dendl;
7632     bufferlist data(m->get_data());
7633     CrushWrapper crush;
7634     try {
7635       bufferlist::iterator bl(data.begin());
7636       crush.decode(bl);
7637     }
7638     catch (const std::exception &e) {
7639       err = -EINVAL;
7640       ss << "Failed to parse crushmap: " << e.what();
7641       goto reply;
7642     }
7643   
7644     int64_t prior_version = 0;
7645     if (cmd_getval(g_ceph_context, cmdmap, "prior_version", prior_version)) {
7646       if (prior_version == osdmap.get_crush_version() - 1) {
7647         // see if we are a resend of the last update.  this is imperfect
7648         // (multiple racing updaters may not both get reliable success)
7649         // but we expect crush updaters (via this interface) to be rare-ish.
7650         bufferlist current, proposed;
7651         osdmap.crush->encode(current, mon->get_quorum_con_features());
7652         crush.encode(proposed, mon->get_quorum_con_features());
7653         if (current.contents_equal(proposed)) {
7654           dout(10) << __func__
7655                    << " proposed matches current and version equals previous"
7656                    << dendl;
7657           err = 0;
7658           ss << osdmap.get_crush_version();
7659           goto reply;
7660         }
7661       }
7662       if (prior_version != osdmap.get_crush_version()) {
7663         err = -EPERM;
7664         ss << "prior_version " << prior_version << " != crush version "
7665            << osdmap.get_crush_version();
7666         goto reply;
7667       }
7668     }
7669
7670     if (crush.has_legacy_rule_ids()) {
7671       err = -EINVAL;
7672       ss << "crush maps with ruleset != ruleid are no longer allowed";
7673       goto reply;
7674     }
7675     if (!validate_crush_against_features(&crush, ss)) {
7676       err = -EINVAL;
7677       goto reply;
7678     }
7679
7680     err = osdmap.validate_crush_rules(&crush, &ss);
7681     if (err < 0) {
7682       goto reply;
7683     }
7684
7685     if (g_conf->mon_osd_crush_smoke_test) {
7686       // sanity check: test some inputs to make sure this map isn't
7687       // totally broken
7688       dout(10) << " testing map" << dendl;
7689       stringstream ess;
7690       CrushTester tester(crush, ess);
7691       tester.set_min_x(0);
7692       tester.set_max_x(50);
7693       auto start = ceph::coarse_mono_clock::now();
7694       int r = tester.test_with_fork(g_conf->mon_lease);
7695       auto duration = ceph::coarse_mono_clock::now() - start;
7696       if (r < 0) {
7697         dout(10) << " tester.test_with_fork returns " << r
7698                  << ": " << ess.str() << dendl;
7699         ss << "crush smoke test failed with " << r << ": " << ess.str();
7700         err = r;
7701         goto reply;
7702       }
7703       dout(10) << __func__ << " crush somke test duration: "
7704                << duration << ", result: " << ess.str() << dendl;
7705     }
7706
7707     pending_inc.crush = data;
7708     ss << osdmap.get_crush_version() + 1;
7709     goto update;
7710
7711   } else if (prefix == "osd crush set-all-straw-buckets-to-straw2") {
7712     CrushWrapper newcrush;
7713     _get_pending_crush(newcrush);
7714     for (int b = 0; b < newcrush.get_max_buckets(); ++b) {
7715       int bid = -1 - b;
7716       if (newcrush.bucket_exists(bid) &&
7717           newcrush.get_bucket_alg(bid)) {
7718         dout(20) << " bucket " << bid << " is straw, can convert" << dendl;
7719         newcrush.bucket_set_alg(bid, CRUSH_BUCKET_STRAW2);
7720       }
7721     }
7722     if (!validate_crush_against_features(&newcrush, ss)) {
7723       err = -EINVAL;
7724       goto reply;
7725     }
7726     pending_inc.crush.clear();
7727     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
7728     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
7729                                               get_last_committed() + 1));
7730     return true;
7731   } else if (prefix == "osd crush set-device-class") {
7732     if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
7733       ss << "you must complete the upgrade and 'ceph osd require-osd-release "
7734          << "luminous' before using crush device classes";
7735       err = -EPERM;
7736       goto reply;
7737     }
7738
7739     string device_class;
7740     if (!cmd_getval(g_ceph_context, cmdmap, "class", device_class)) {
7741       err = -EINVAL; // no value!
7742       goto reply;
7743     }
7744
7745     bool stop = false;
7746     vector<string> idvec;
7747     cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
7748     CrushWrapper newcrush;
7749     _get_pending_crush(newcrush);
7750     set<int> updated;
7751     for (unsigned j = 0; j < idvec.size() && !stop; j++) {
7752       set<int> osds;
7753       // wildcard?
7754       if (j == 0 &&
7755           (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
7756         osdmap.get_all_osds(osds);
7757         stop = true;
7758       } else {
7759         // try traditional single osd way
7760         long osd = parse_osd_id(idvec[j].c_str(), &ss);
7761         if (osd < 0) {
7762           // ss has reason for failure
7763           ss << ", unable to parse osd id:\"" << idvec[j] << "\". ";
7764           err = -EINVAL;
7765           continue;
7766         }
7767         osds.insert(osd);
7768       }
7769
7770       for (auto &osd : osds) {
7771         if (!osdmap.exists(osd)) {
7772           ss << "osd." << osd << " does not exist. ";
7773           continue;
7774         }
7775
7776         ostringstream oss;
7777         oss << "osd." << osd;
7778         string name = oss.str();
7779
7780         string action;
7781         if (newcrush.item_exists(osd)) {
7782           action = "updating";
7783         } else {
7784           action = "creating";
7785           newcrush.set_item_name(osd, name);
7786         }
7787
7788         dout(5) << action << " crush item id " << osd << " name '" << name
7789                 << "' device_class '" << device_class << "'"
7790                 << dendl;
7791         err = newcrush.update_device_class(osd, device_class, name, &ss);
7792         if (err < 0) {
7793           goto reply;
7794         }
7795         if (err == 0 && !_have_pending_crush()) {
7796           if (!stop) {
7797             // for single osd only, wildcard makes too much noise
7798             ss << "set-device-class item id " << osd << " name '" << name
7799                << "' device_class '" << device_class << "': no change";
7800           }
7801         } else {
7802           updated.insert(osd);
7803         }
7804       }
7805     }
7806
7807     if (!updated.empty()) {
7808       pending_inc.crush.clear();
7809       newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
7810       ss << "set osd(s) " << updated << " to class '" << device_class << "'";
7811       getline(ss, rs);
7812       wait_for_finished_proposal(op,
7813         new Monitor::C_Command(mon,op, 0, rs, get_last_committed() + 1));
7814       return true;
7815     }
7816
7817  } else if (prefix == "osd crush rm-device-class") {
7818     bool stop = false;
7819     vector<string> idvec;
7820     cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
7821     CrushWrapper newcrush;
7822     _get_pending_crush(newcrush);
7823     set<int> updated;
7824
7825     for (unsigned j = 0; j < idvec.size() && !stop; j++) {
7826       set<int> osds;
7827
7828       // wildcard?
7829       if (j == 0 &&
7830           (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
7831         osdmap.get_all_osds(osds);
7832         stop = true;
7833       } else {
7834         // try traditional single osd way
7835         long osd = parse_osd_id(idvec[j].c_str(), &ss);
7836         if (osd < 0) {
7837           // ss has reason for failure
7838           ss << ", unable to parse osd id:\"" << idvec[j] << "\". ";
7839           err = -EINVAL;
7840           goto reply;
7841         }
7842         osds.insert(osd);
7843       }
7844
7845       for (auto &osd : osds) {
7846         if (!osdmap.exists(osd)) {
7847           ss << "osd." << osd << " does not exist. ";
7848           continue;
7849         }
7850
7851         auto class_name = newcrush.get_item_class(osd);
7852         if (!class_name) {
7853           ss << "osd." << osd << " belongs to no class, ";
7854           continue;
7855         }
7856         // note that we do not verify if class_is_in_use here
7857         // in case the device is misclassified and user wants
7858         // to overridely reset...
7859
7860         err = newcrush.remove_device_class(g_ceph_context, osd, &ss);
7861         if (err < 0) {
7862           // ss has reason for failure
7863           goto reply;
7864         }
7865         updated.insert(osd);
7866       }
7867     }
7868
7869     if (!updated.empty()) {
7870       pending_inc.crush.clear();
7871       newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
7872       ss << "done removing class of osd(s): " << updated;
7873       getline(ss, rs);
7874       wait_for_finished_proposal(op,
7875         new Monitor::C_Command(mon,op, 0, rs, get_last_committed() + 1));
7876       return true;
7877     }
7878   } else if (prefix == "osd crush class rename") {
7879     string srcname, dstname;
7880     if (!cmd_getval(g_ceph_context, cmdmap, "srcname", srcname)) {
7881       err = -EINVAL;
7882       goto reply;
7883     }
7884     if (!cmd_getval(g_ceph_context, cmdmap, "dstname", dstname)) {
7885       err = -EINVAL;
7886       goto reply;
7887     }
7888
7889     CrushWrapper newcrush;
7890     _get_pending_crush(newcrush);
7891     if (!newcrush.class_exists(srcname) && newcrush.class_exists(dstname)) {
7892       // suppose this is a replay and return success
7893       // so command is idempotent
7894       ss << "already renamed to '" << dstname << "'";
7895       err = 0;
7896       goto reply;
7897     }
7898
7899     err = newcrush.rename_class(srcname, dstname);
7900     if (err < 0) {
7901       ss << "fail to rename '" << srcname << "' to '" << dstname << "' : "
7902          << cpp_strerror(err);
7903       goto reply;
7904     }
7905
7906     pending_inc.crush.clear();
7907     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
7908     ss << "rename class '" << srcname << "' to '" << dstname << "'";
7909     goto update;
7910   } else if (prefix == "osd crush add-bucket") {
7911     // os crush add-bucket <name> <type>
7912     string name, typestr;
7913     cmd_getval(g_ceph_context, cmdmap, "name", name);
7914     cmd_getval(g_ceph_context, cmdmap, "type", typestr);
7915
7916     if (!_have_pending_crush() &&
7917         _get_stable_crush().name_exists(name)) {
7918       ss << "bucket '" << name << "' already exists";
7919       goto reply;
7920     }
7921
7922     CrushWrapper newcrush;
7923     _get_pending_crush(newcrush);
7924
7925     if (newcrush.name_exists(name)) {
7926       ss << "bucket '" << name << "' already exists";
7927       goto update;
7928     }
7929     int type = newcrush.get_type_id(typestr);
7930     if (type < 0) {
7931       ss << "type '" << typestr << "' does not exist";
7932       err = -EINVAL;
7933       goto reply;
7934     }
7935     if (type == 0) {
7936       ss << "type '" << typestr << "' is for devices, not buckets";
7937       err = -EINVAL;
7938       goto reply;
7939     }
7940     int bucketno;
7941     err = newcrush.add_bucket(0, 0,
7942                               CRUSH_HASH_DEFAULT, type, 0, NULL,
7943                               NULL, &bucketno);
7944     if (err < 0) {
7945       ss << "add_bucket error: '" << cpp_strerror(err) << "'";
7946       goto reply;
7947     }
7948     err = newcrush.set_item_name(bucketno, name);
7949     if (err < 0) {
7950       ss << "error setting bucket name to '" << name << "'";
7951       goto reply;
7952     }
7953
7954     pending_inc.crush.clear();
7955     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
7956     ss << "added bucket " << name << " type " << typestr
7957        << " to crush map";
7958     goto update;
7959   } else if (prefix == "osd crush rename-bucket") {
7960     string srcname, dstname;
7961     cmd_getval(g_ceph_context, cmdmap, "srcname", srcname);
7962     cmd_getval(g_ceph_context, cmdmap, "dstname", dstname);
7963
7964     err = crush_rename_bucket(srcname, dstname, &ss);
7965     if (err == -EALREADY) // equivalent to success for idempotency
7966       err = 0;
7967     if (err)
7968       goto reply;
7969     else
7970       goto update;
7971   } else if (prefix == "osd crush weight-set create" ||
7972              prefix == "osd crush weight-set create-compat") {
7973     CrushWrapper newcrush;
7974     _get_pending_crush(newcrush);
7975     int64_t pool;
7976     int positions;
7977     if (newcrush.has_non_straw2_buckets()) {
7978       ss << "crush map contains one or more bucket(s) that are not straw2";
7979       err = -EPERM;
7980       goto reply;
7981     }
7982     if (prefix == "osd crush weight-set create") {
7983       if (osdmap.require_min_compat_client > 0 &&
7984           osdmap.require_min_compat_client < CEPH_RELEASE_LUMINOUS) {
7985         ss << "require_min_compat_client "
7986            << ceph_release_name(osdmap.require_min_compat_client)
7987            << " < luminous, which is required for per-pool weight-sets. "
7988            << "Try 'ceph osd set-require-min-compat-client luminous' "
7989            << "before using the new interface";
7990         err = -EPERM;
7991         goto reply;
7992       }
7993       string poolname, mode;
7994       cmd_getval(g_ceph_context, cmdmap, "pool", poolname);
7995       pool = osdmap.lookup_pg_pool_name(poolname.c_str());
7996       if (pool < 0) {
7997         ss << "pool '" << poolname << "' not found";
7998         err = -ENOENT;
7999         goto reply;
8000       }
8001       cmd_getval(g_ceph_context, cmdmap, "mode", mode);
8002       if (mode != "flat" && mode != "positional") {
8003         ss << "unrecognized weight-set mode '" << mode << "'";
8004         err = -EINVAL;
8005         goto reply;
8006       }
8007       positions = mode == "flat" ? 1 : osdmap.get_pg_pool(pool)->get_size();
8008     } else {
8009       pool = CrushWrapper::DEFAULT_CHOOSE_ARGS;
8010       positions = 1;
8011     }
8012     newcrush.create_choose_args(pool, positions);
8013     pending_inc.crush.clear();
8014     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8015     goto update;
8016
8017   } else if (prefix == "osd crush weight-set rm" ||
8018              prefix == "osd crush weight-set rm-compat") {
8019     CrushWrapper newcrush;
8020     _get_pending_crush(newcrush);
8021     int64_t pool;
8022     if (prefix == "osd crush weight-set rm") {
8023       string poolname;
8024       cmd_getval(g_ceph_context, cmdmap, "pool", poolname);
8025       pool = osdmap.lookup_pg_pool_name(poolname.c_str());
8026       if (pool < 0) {
8027         ss << "pool '" << poolname << "' not found";
8028         err = -ENOENT;
8029         goto reply;
8030       }
8031     } else {
8032       pool = CrushWrapper::DEFAULT_CHOOSE_ARGS;
8033     }
8034     newcrush.rm_choose_args(pool);
8035     pending_inc.crush.clear();
8036     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8037     goto update;
8038
8039   } else if (prefix == "osd crush weight-set reweight" ||
8040              prefix == "osd crush weight-set reweight-compat") {
8041     string poolname, item;
8042     vector<double> weight;
8043     cmd_getval(g_ceph_context, cmdmap, "pool", poolname);
8044     cmd_getval(g_ceph_context, cmdmap, "item", item);
8045     cmd_getval(g_ceph_context, cmdmap, "weight", weight);
8046     CrushWrapper newcrush;
8047     _get_pending_crush(newcrush);
8048     int64_t pool;
8049     if (prefix == "osd crush weight-set reweight") {
8050       pool = osdmap.lookup_pg_pool_name(poolname.c_str());
8051       if (pool < 0) {
8052         ss << "pool '" << poolname << "' not found";
8053         err = -ENOENT;
8054         goto reply;
8055       }
8056       if (!newcrush.have_choose_args(pool)) {
8057         ss << "no weight-set for pool '" << poolname << "'";
8058         err = -ENOENT;
8059         goto reply;
8060       }
8061       auto arg_map = newcrush.choose_args_get(pool);
8062       int positions = newcrush.get_choose_args_positions(arg_map);
8063       if (weight.size() != (size_t)positions) {
8064          ss << "must specify exact " << positions << " weight values";
8065          err = -EINVAL;
8066          goto reply;
8067       }
8068     } else {
8069       pool = CrushWrapper::DEFAULT_CHOOSE_ARGS;
8070       if (!newcrush.have_choose_args(pool)) {
8071         ss << "no backward-compatible weight-set";
8072         err = -ENOENT;
8073         goto reply;
8074       }
8075     }
8076     if (!newcrush.name_exists(item)) {
8077       ss << "item '" << item << "' does not exist";
8078       err = -ENOENT;
8079       goto reply;
8080     }
8081     err = newcrush.choose_args_adjust_item_weightf(
8082       g_ceph_context,
8083       newcrush.choose_args_get(pool),
8084       newcrush.get_item_id(item),
8085       weight,
8086       &ss);
8087     if (err < 0) {
8088       goto reply;
8089     }
8090     err = 0;
8091     pending_inc.crush.clear();
8092     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8093     goto update;
8094   } else if (osdid_present &&
8095              (prefix == "osd crush set" || prefix == "osd crush add")) {
8096     // <OsdName> is 'osd.<id>' or '<id>', passed as int64_t id
8097     // osd crush set <OsdName> <weight> <loc1> [<loc2> ...]
8098     // osd crush add <OsdName> <weight> <loc1> [<loc2> ...]
8099
8100     if (!osdmap.exists(osdid)) {
8101       err = -ENOENT;
8102       ss << name << " does not exist. Create it before updating the crush map";
8103       goto reply;
8104     }
8105
8106     double weight;
8107     if (!cmd_getval(g_ceph_context, cmdmap, "weight", weight)) {
8108       ss << "unable to parse weight value '"
8109          << cmd_vartype_stringify(cmdmap["weight"]) << "'";
8110       err = -EINVAL;
8111       goto reply;
8112     }
8113
8114     string args;
8115     vector<string> argvec;
8116     cmd_getval(g_ceph_context, cmdmap, "args", argvec);
8117     map<string,string> loc;
8118     CrushWrapper::parse_loc_map(argvec, &loc);
8119
8120     if (prefix == "osd crush set"
8121         && !_get_stable_crush().item_exists(osdid)) {
8122       err = -ENOENT;
8123       ss << "unable to set item id " << osdid << " name '" << name
8124          << "' weight " << weight << " at location " << loc
8125          << ": does not exist";
8126       goto reply;
8127     }
8128
8129     dout(5) << "adding/updating crush item id " << osdid << " name '"
8130       << name << "' weight " << weight << " at location "
8131       << loc << dendl;
8132     CrushWrapper newcrush;
8133     _get_pending_crush(newcrush);
8134
8135     string action;
8136     if (prefix == "osd crush set" ||
8137         newcrush.check_item_loc(g_ceph_context, osdid, loc, (int *)NULL)) {
8138       action = "set";
8139       err = newcrush.update_item(g_ceph_context, osdid, weight, name, loc);
8140     } else {
8141       action = "add";
8142       err = newcrush.insert_item(g_ceph_context, osdid, weight, name, loc);
8143       if (err == 0)
8144         err = 1;
8145     }
8146
8147     if (err < 0)
8148       goto reply;
8149
8150     if (err == 0 && !_have_pending_crush()) {
8151       ss << action << " item id " << osdid << " name '" << name << "' weight "
8152         << weight << " at location " << loc << ": no change";
8153       goto reply;
8154     }
8155
8156     pending_inc.crush.clear();
8157     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8158     ss << action << " item id " << osdid << " name '" << name << "' weight "
8159       << weight << " at location " << loc << " to crush map";
8160     getline(ss, rs);
8161     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8162                                                       get_last_committed() + 1));
8163     return true;
8164
8165   } else if (prefix == "osd crush create-or-move") {
8166     do {
8167       // osd crush create-or-move <OsdName> <initial_weight> <loc1> [<loc2> ...]
8168       if (!osdmap.exists(osdid)) {
8169         err = -ENOENT;
8170         ss << name << " does not exist.  create it before updating the crush map";
8171         goto reply;
8172       }
8173
8174       double weight;
8175       if (!cmd_getval(g_ceph_context, cmdmap, "weight", weight)) {
8176         ss << "unable to parse weight value '"
8177            << cmd_vartype_stringify(cmdmap["weight"]) << "'";
8178         err = -EINVAL;
8179         goto reply;
8180       }
8181
8182       string args;
8183       vector<string> argvec;
8184       cmd_getval(g_ceph_context, cmdmap, "args", argvec);
8185       map<string,string> loc;
8186       CrushWrapper::parse_loc_map(argvec, &loc);
8187
8188       dout(0) << "create-or-move crush item name '" << name << "' initial_weight " << weight
8189               << " at location " << loc << dendl;
8190
8191       CrushWrapper newcrush;
8192       _get_pending_crush(newcrush);
8193
8194       err = newcrush.create_or_move_item(g_ceph_context, osdid, weight, name, loc);
8195       if (err == 0) {
8196         ss << "create-or-move updated item name '" << name << "' weight " << weight
8197            << " at location " << loc << " to crush map";
8198         break;
8199       }
8200       if (err > 0) {
8201         pending_inc.crush.clear();
8202         newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8203         ss << "create-or-move updating item name '" << name << "' weight " << weight
8204            << " at location " << loc << " to crush map";
8205         getline(ss, rs);
8206         wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8207                                                   get_last_committed() + 1));
8208         return true;
8209       }
8210     } while (false);
8211
8212   } else if (prefix == "osd crush move") {
8213     do {
8214       // osd crush move <name> <loc1> [<loc2> ...]
8215
8216       string args;
8217       vector<string> argvec;
8218       cmd_getval(g_ceph_context, cmdmap, "name", name);
8219       cmd_getval(g_ceph_context, cmdmap, "args", argvec);
8220       map<string,string> loc;
8221       CrushWrapper::parse_loc_map(argvec, &loc);
8222
8223       dout(0) << "moving crush item name '" << name << "' to location " << loc << dendl;
8224       CrushWrapper newcrush;
8225       _get_pending_crush(newcrush);
8226
8227       if (!newcrush.name_exists(name)) {
8228         err = -ENOENT;
8229         ss << "item " << name << " does not exist";
8230         break;
8231       }
8232       int id = newcrush.get_item_id(name);
8233
8234       if (!newcrush.check_item_loc(g_ceph_context, id, loc, (int *)NULL)) {
8235         if (id >= 0) {
8236           err = newcrush.create_or_move_item(g_ceph_context, id, 0, name, loc);
8237         } else {
8238           err = newcrush.move_bucket(g_ceph_context, id, loc);
8239         }
8240         if (err >= 0) {
8241           ss << "moved item id " << id << " name '" << name << "' to location " << loc << " in crush map";
8242           pending_inc.crush.clear();
8243           newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8244           getline(ss, rs);
8245           wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8246                                                    get_last_committed() + 1));
8247           return true;
8248         }
8249       } else {
8250         ss << "no need to move item id " << id << " name '" << name << "' to location " << loc << " in crush map";
8251         err = 0;
8252       }
8253     } while (false);
8254   } else if (prefix == "osd crush swap-bucket") {
8255     string source, dest, force;
8256     cmd_getval(g_ceph_context, cmdmap, "source", source);
8257     cmd_getval(g_ceph_context, cmdmap, "dest", dest);
8258     cmd_getval(g_ceph_context, cmdmap, "force", force);
8259     CrushWrapper newcrush;
8260     _get_pending_crush(newcrush);
8261     if (!newcrush.name_exists(source)) {
8262       ss << "source item " << source << " does not exist";
8263       err = -ENOENT;
8264       goto reply;
8265     }
8266     if (!newcrush.name_exists(dest)) {
8267       ss << "dest item " << dest << " does not exist";
8268       err = -ENOENT;
8269       goto reply;
8270     }
8271     int sid = newcrush.get_item_id(source);
8272     int did = newcrush.get_item_id(dest);
8273     int sparent;
8274     if (newcrush.get_immediate_parent_id(sid, &sparent) == 0 &&
8275         force != "--yes-i-really-mean-it") {
8276       ss << "source item " << source << " is not an orphan bucket; pass --yes-i-really-mean-it to proceed anyway";
8277       err = -EPERM;
8278       goto reply;
8279     }
8280     if (newcrush.get_bucket_alg(sid) != newcrush.get_bucket_alg(did) &&
8281         force != "--yes-i-really-mean-it") {
8282       ss << "source bucket alg " << crush_alg_name(newcrush.get_bucket_alg(sid)) << " != "
8283          << "dest bucket alg " << crush_alg_name(newcrush.get_bucket_alg(did))
8284          << "; pass --yes-i-really-mean-it to proceed anyway";
8285       err = -EPERM;
8286       goto reply;
8287     }
8288     int r = newcrush.swap_bucket(g_ceph_context, sid, did);
8289     if (r < 0) {
8290       ss << "failed to swap bucket contents: " << cpp_strerror(r);
8291       err = r;
8292       goto reply;
8293     }
8294     ss << "swapped bucket of " << source << " to " << dest;
8295     pending_inc.crush.clear();
8296     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8297     wait_for_finished_proposal(op,
8298                                new Monitor::C_Command(mon, op, err, ss.str(),
8299                                                       get_last_committed() + 1));
8300     return true;
8301   } else if (prefix == "osd crush link") {
8302     // osd crush link <name> <loc1> [<loc2> ...]
8303     string name;
8304     cmd_getval(g_ceph_context, cmdmap, "name", name);
8305     vector<string> argvec;
8306     cmd_getval(g_ceph_context, cmdmap, "args", argvec);
8307     map<string,string> loc;
8308     CrushWrapper::parse_loc_map(argvec, &loc);
8309
8310     // Need an explicit check for name_exists because get_item_id returns
8311     // 0 on unfound.
8312     int id = osdmap.crush->get_item_id(name);
8313     if (!osdmap.crush->name_exists(name)) {
8314       err = -ENOENT;
8315       ss << "item " << name << " does not exist";
8316       goto reply;
8317     } else {
8318       dout(5) << "resolved crush name '" << name << "' to id " << id << dendl;
8319     }
8320     if (osdmap.crush->check_item_loc(g_ceph_context, id, loc, (int*) NULL)) {
8321       ss << "no need to move item id " << id << " name '" << name
8322          << "' to location " << loc << " in crush map";
8323       err = 0;
8324       goto reply;
8325     }
8326
8327     dout(5) << "linking crush item name '" << name << "' at location " << loc << dendl;
8328     CrushWrapper newcrush;
8329     _get_pending_crush(newcrush);
8330
8331     if (!newcrush.name_exists(name)) {
8332       err = -ENOENT;
8333       ss << "item " << name << " does not exist";
8334       goto reply;
8335     } else {
8336       int id = newcrush.get_item_id(name);
8337       if (!newcrush.check_item_loc(g_ceph_context, id, loc, (int *)NULL)) {
8338         err = newcrush.link_bucket(g_ceph_context, id, loc);
8339         if (err >= 0) {
8340           ss << "linked item id " << id << " name '" << name
8341              << "' to location " << loc << " in crush map";
8342           pending_inc.crush.clear();
8343           newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8344         } else {
8345           ss << "cannot link item id " << id << " name '" << name
8346              << "' to location " << loc;
8347           goto reply;
8348         }
8349       } else {
8350         ss << "no need to move item id " << id << " name '" << name
8351            << "' to location " << loc << " in crush map";
8352         err = 0;
8353       }
8354     }
8355     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, err, ss.str(),
8356                                               get_last_committed() + 1));
8357     return true;
8358   } else if (prefix == "osd crush rm" ||
8359              prefix == "osd crush remove" ||
8360              prefix == "osd crush unlink") {
8361     do {
8362       // osd crush rm <id> [ancestor]
8363       CrushWrapper newcrush;
8364       _get_pending_crush(newcrush);
8365
8366       string name;
8367       cmd_getval(g_ceph_context, cmdmap, "name", name);
8368
8369       if (!osdmap.crush->name_exists(name)) {
8370         err = 0;
8371         ss << "device '" << name << "' does not appear in the crush map";
8372         break;
8373       }
8374       if (!newcrush.name_exists(name)) {
8375         err = 0;
8376         ss << "device '" << name << "' does not appear in the crush map";
8377         getline(ss, rs);
8378         wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8379                                                   get_last_committed() + 1));
8380         return true;
8381       }
8382       int id = newcrush.get_item_id(name);
8383       int ancestor = 0;
8384
8385       bool unlink_only = prefix == "osd crush unlink";
8386       string ancestor_str;
8387       if (cmd_getval(g_ceph_context, cmdmap, "ancestor", ancestor_str)) {
8388         if (!newcrush.name_exists(ancestor_str)) {
8389           err = -ENOENT;
8390           ss << "ancestor item '" << ancestor_str
8391              << "' does not appear in the crush map";
8392           break;
8393         }
8394         ancestor = newcrush.get_item_id(ancestor_str);
8395       }
8396
8397       err = prepare_command_osd_crush_remove(
8398           newcrush,
8399           id, ancestor,
8400           (ancestor < 0), unlink_only);
8401
8402       if (err == -ENOENT) {
8403         ss << "item " << id << " does not appear in that position";
8404         err = 0;
8405         break;
8406       }
8407       if (err == 0) {
8408         ss << "removed item id " << id << " name '" << name << "' from crush map";
8409         getline(ss, rs);
8410         wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8411                                                   get_last_committed() + 1));
8412         return true;
8413       }
8414     } while (false);
8415
8416   } else if (prefix == "osd crush reweight-all") {
8417     CrushWrapper newcrush;
8418     _get_pending_crush(newcrush);
8419
8420     newcrush.reweight(g_ceph_context);
8421     pending_inc.crush.clear();
8422     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8423     ss << "reweighted crush hierarchy";
8424     getline(ss, rs);
8425     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8426                                                   get_last_committed() + 1));
8427     return true;
8428   } else if (prefix == "osd crush reweight") {
8429     // osd crush reweight <name> <weight>
8430     CrushWrapper newcrush;
8431     _get_pending_crush(newcrush);
8432
8433     string name;
8434     cmd_getval(g_ceph_context, cmdmap, "name", name);
8435     if (!newcrush.name_exists(name)) {
8436       err = -ENOENT;
8437       ss << "device '" << name << "' does not appear in the crush map";
8438       goto reply;
8439     }
8440
8441     int id = newcrush.get_item_id(name);
8442     if (id < 0) {
8443       ss << "device '" << name << "' is not a leaf in the crush map";
8444       err = -EINVAL;
8445       goto reply;
8446     }
8447     double w;
8448     if (!cmd_getval(g_ceph_context, cmdmap, "weight", w)) {
8449       ss << "unable to parse weight value '"
8450          << cmd_vartype_stringify(cmdmap["weight"]) << "'";
8451       err = -EINVAL;
8452       goto reply;
8453     }
8454
8455     err = newcrush.adjust_item_weightf(g_ceph_context, id, w);
8456     if (err < 0)
8457       goto reply;
8458     pending_inc.crush.clear();
8459     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8460     ss << "reweighted item id " << id << " name '" << name << "' to " << w
8461        << " in crush map";
8462     getline(ss, rs);
8463     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8464                                                   get_last_committed() + 1));
8465     return true;
8466   } else if (prefix == "osd crush reweight-subtree") {
8467     // osd crush reweight <name> <weight>
8468     CrushWrapper newcrush;
8469     _get_pending_crush(newcrush);
8470
8471     string name;
8472     cmd_getval(g_ceph_context, cmdmap, "name", name);
8473     if (!newcrush.name_exists(name)) {
8474       err = -ENOENT;
8475       ss << "device '" << name << "' does not appear in the crush map";
8476       goto reply;
8477     }
8478
8479     int id = newcrush.get_item_id(name);
8480     if (id >= 0) {
8481       ss << "device '" << name << "' is not a subtree in the crush map";
8482       err = -EINVAL;
8483       goto reply;
8484     }
8485     double w;
8486     if (!cmd_getval(g_ceph_context, cmdmap, "weight", w)) {
8487       ss << "unable to parse weight value '"
8488          << cmd_vartype_stringify(cmdmap["weight"]) << "'";
8489       err = -EINVAL;
8490       goto reply;
8491     }
8492
8493     err = newcrush.adjust_subtree_weightf(g_ceph_context, id, w);
8494     if (err < 0)
8495       goto reply;
8496     pending_inc.crush.clear();
8497     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8498     ss << "reweighted subtree id " << id << " name '" << name << "' to " << w
8499        << " in crush map";
8500     getline(ss, rs);
8501     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8502                                               get_last_committed() + 1));
8503     return true;
8504   } else if (prefix == "osd crush tunables") {
8505     CrushWrapper newcrush;
8506     _get_pending_crush(newcrush);
8507
8508     err = 0;
8509     string profile;
8510     cmd_getval(g_ceph_context, cmdmap, "profile", profile);
8511     if (profile == "legacy" || profile == "argonaut") {
8512       newcrush.set_tunables_legacy();
8513     } else if (profile == "bobtail") {
8514       newcrush.set_tunables_bobtail();
8515     } else if (profile == "firefly") {
8516       newcrush.set_tunables_firefly();
8517     } else if (profile == "hammer") {
8518       newcrush.set_tunables_hammer();
8519     } else if (profile == "jewel") {
8520       newcrush.set_tunables_jewel();
8521     } else if (profile == "optimal") {
8522       newcrush.set_tunables_optimal();
8523     } else if (profile == "default") {
8524       newcrush.set_tunables_default();
8525     } else {
8526       ss << "unrecognized profile '" << profile << "'";
8527       err = -EINVAL;
8528       goto reply;
8529     }
8530
8531     if (!validate_crush_against_features(&newcrush, ss)) {
8532       err = -EINVAL;
8533       goto reply;
8534     }
8535
8536     pending_inc.crush.clear();
8537     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8538     ss << "adjusted tunables profile to " << profile;
8539     getline(ss, rs);
8540     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8541                                               get_last_committed() + 1));
8542     return true;
8543   } else if (prefix == "osd crush set-tunable") {
8544     CrushWrapper newcrush;
8545     _get_pending_crush(newcrush);
8546
8547     err = 0;
8548     string tunable;
8549     cmd_getval(g_ceph_context, cmdmap, "tunable", tunable);
8550
8551     int64_t value = -1;
8552     if (!cmd_getval(g_ceph_context, cmdmap, "value", value)) {
8553       err = -EINVAL;
8554       ss << "failed to parse integer value " << cmd_vartype_stringify(cmdmap["value"]);
8555       goto reply;
8556     }
8557
8558     if (tunable == "straw_calc_version") {
8559       if (value != 0 && value != 1) {
8560         ss << "value must be 0 or 1; got " << value;
8561         err = -EINVAL;
8562         goto reply;
8563       }
8564       newcrush.set_straw_calc_version(value);
8565     } else {
8566       ss << "unrecognized tunable '" << tunable << "'";
8567       err = -EINVAL;
8568       goto reply;
8569     }
8570
8571     if (!validate_crush_against_features(&newcrush, ss)) {
8572       err = -EINVAL;
8573       goto reply;
8574     }
8575
8576     pending_inc.crush.clear();
8577     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8578     ss << "adjusted tunable " << tunable << " to " << value;
8579     getline(ss, rs);
8580     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8581                                               get_last_committed() + 1));
8582     return true;
8583
8584   } else if (prefix == "osd crush rule create-simple") {
8585     string name, root, type, mode;
8586     cmd_getval(g_ceph_context, cmdmap, "name", name);
8587     cmd_getval(g_ceph_context, cmdmap, "root", root);
8588     cmd_getval(g_ceph_context, cmdmap, "type", type);
8589     cmd_getval(g_ceph_context, cmdmap, "mode", mode);
8590     if (mode == "")
8591       mode = "firstn";
8592
8593     if (osdmap.crush->rule_exists(name)) {
8594       // The name is uniquely associated to a ruleid and the rule it contains
8595       // From the user point of view, the rule is more meaningfull.
8596       ss << "rule " << name << " already exists";
8597       err = 0;
8598       goto reply;
8599     }
8600
8601     CrushWrapper newcrush;
8602     _get_pending_crush(newcrush);
8603
8604     if (newcrush.rule_exists(name)) {
8605       // The name is uniquely associated to a ruleid and the rule it contains
8606       // From the user point of view, the rule is more meaningfull.
8607       ss << "rule " << name << " already exists";
8608       err = 0;
8609     } else {
8610       int ruleno = newcrush.add_simple_rule(name, root, type, "", mode,
8611                                                pg_pool_t::TYPE_REPLICATED, &ss);
8612       if (ruleno < 0) {
8613         err = ruleno;
8614         goto reply;
8615       }
8616
8617       pending_inc.crush.clear();
8618       newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8619     }
8620     getline(ss, rs);
8621     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8622                                               get_last_committed() + 1));
8623     return true;
8624
8625   } else if (prefix == "osd crush rule create-replicated") {
8626     string name, root, type, device_class;
8627     cmd_getval(g_ceph_context, cmdmap, "name", name);
8628     cmd_getval(g_ceph_context, cmdmap, "root", root);
8629     cmd_getval(g_ceph_context, cmdmap, "type", type);
8630     cmd_getval(g_ceph_context, cmdmap, "class", device_class);
8631
8632     if (!device_class.empty()) {
8633       if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
8634         ss << "you must complete the upgrade and 'ceph osd require-osd-release "
8635            << "luminous' before using crush device classes";
8636         err = -EPERM;
8637         goto reply;
8638       }
8639     }
8640
8641     if (osdmap.crush->rule_exists(name)) {
8642       // The name is uniquely associated to a ruleid and the rule it contains
8643       // From the user point of view, the rule is more meaningfull.
8644       ss << "rule " << name << " already exists";
8645       err = 0;
8646       goto reply;
8647     }
8648
8649     CrushWrapper newcrush;
8650     _get_pending_crush(newcrush);
8651
8652     if (newcrush.rule_exists(name)) {
8653       // The name is uniquely associated to a ruleid and the rule it contains
8654       // From the user point of view, the rule is more meaningfull.
8655       ss << "rule " << name << " already exists";
8656       err = 0;
8657     } else {
8658       int ruleno = newcrush.add_simple_rule(
8659         name, root, type, device_class,
8660         "firstn", pg_pool_t::TYPE_REPLICATED, &ss);
8661       if (ruleno < 0) {
8662         err = ruleno;
8663         goto reply;
8664       }
8665
8666       pending_inc.crush.clear();
8667       newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8668     }
8669     getline(ss, rs);
8670     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8671                                               get_last_committed() + 1));
8672     return true;
8673
8674   } else if (prefix == "osd erasure-code-profile rm") {
8675     string name;
8676     cmd_getval(g_ceph_context, cmdmap, "name", name);
8677
8678     if (erasure_code_profile_in_use(pending_inc.new_pools, name, &ss))
8679       goto wait;
8680
8681     if (erasure_code_profile_in_use(osdmap.pools, name, &ss)) {
8682       err = -EBUSY;
8683       goto reply;
8684     }
8685
8686     if (osdmap.has_erasure_code_profile(name) ||
8687         pending_inc.new_erasure_code_profiles.count(name)) {
8688       if (osdmap.has_erasure_code_profile(name)) {
8689         pending_inc.old_erasure_code_profiles.push_back(name);
8690       } else {
8691         dout(20) << "erasure code profile rm " << name << ": creation canceled" << dendl;
8692         pending_inc.new_erasure_code_profiles.erase(name);
8693       }
8694
8695       getline(ss, rs);
8696       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8697                                                         get_last_committed() + 1));
8698       return true;
8699     } else {
8700       ss << "erasure-code-profile " << name << " does not exist";
8701       err = 0;
8702       goto reply;
8703     }
8704
8705   } else if (prefix == "osd erasure-code-profile set") {
8706     string name;
8707     cmd_getval(g_ceph_context, cmdmap, "name", name);
8708     vector<string> profile;
8709     cmd_getval(g_ceph_context, cmdmap, "profile", profile);
8710     bool force;
8711     if (profile.size() > 0 && profile.back() == "--force") {
8712       profile.pop_back();
8713       force = true;
8714     } else {
8715       force = false;
8716     }
8717     map<string,string> profile_map;
8718     err = parse_erasure_code_profile(profile, &profile_map, &ss);
8719     if (err)
8720       goto reply;
8721     if (profile_map.find("plugin") == profile_map.end()) {
8722       ss << "erasure-code-profile " << profile_map
8723          << " must contain a plugin entry" << std::endl;
8724       err = -EINVAL;
8725       goto reply;
8726     }
8727     string plugin = profile_map["plugin"];
8728
8729     if (pending_inc.has_erasure_code_profile(name)) {
8730       dout(20) << "erasure code profile " << name << " try again" << dendl;
8731       goto wait;
8732     } else {
8733       if (plugin == "isa" || plugin == "lrc") {
8734         err = check_cluster_features(CEPH_FEATURE_ERASURE_CODE_PLUGINS_V2, ss);
8735         if (err == -EAGAIN)
8736           goto wait;
8737         if (err)
8738           goto reply;
8739       } else if (plugin == "shec") {
8740         err = check_cluster_features(CEPH_FEATURE_ERASURE_CODE_PLUGINS_V3, ss);
8741         if (err == -EAGAIN)
8742           goto wait;
8743         if (err)
8744           goto reply;
8745       }
8746       err = normalize_profile(name, profile_map, force, &ss);
8747       if (err)
8748         goto reply;
8749
8750       if (osdmap.has_erasure_code_profile(name)) {
8751         ErasureCodeProfile existing_profile_map =
8752           osdmap.get_erasure_code_profile(name);
8753         err = normalize_profile(name, existing_profile_map, force, &ss);
8754         if (err)
8755           goto reply;
8756
8757         if (existing_profile_map == profile_map) {
8758           err = 0;
8759           goto reply;
8760         }
8761         if (!force) {
8762           err = -EPERM;
8763           ss << "will not override erasure code profile " << name
8764              << " because the existing profile "
8765              << existing_profile_map
8766              << " is different from the proposed profile "
8767              << profile_map;
8768           goto reply;
8769         }
8770       }
8771
8772       dout(20) << "erasure code profile set " << name << "="
8773                << profile_map << dendl;
8774       pending_inc.set_erasure_code_profile(name, profile_map);
8775     }
8776
8777     getline(ss, rs);
8778     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8779                                                       get_last_committed() + 1));
8780     return true;
8781
8782   } else if (prefix == "osd crush rule create-erasure") {
8783     err = check_cluster_features(CEPH_FEATURE_CRUSH_V2, ss);
8784     if (err == -EAGAIN)
8785       goto wait;
8786     if (err)
8787       goto reply;
8788     string name, poolstr;
8789     cmd_getval(g_ceph_context, cmdmap, "name", name);
8790     string profile;
8791     cmd_getval(g_ceph_context, cmdmap, "profile", profile);
8792     if (profile == "")
8793       profile = "default";
8794     if (profile == "default") {
8795       if (!osdmap.has_erasure_code_profile(profile)) {
8796         if (pending_inc.has_erasure_code_profile(profile)) {
8797           dout(20) << "erasure code profile " << profile << " already pending" << dendl;
8798           goto wait;
8799         }
8800
8801         map<string,string> profile_map;
8802         err = osdmap.get_erasure_code_profile_default(g_ceph_context,
8803                                                       profile_map,
8804                                                       &ss);
8805         if (err)
8806           goto reply;
8807         err = normalize_profile(name, profile_map, true, &ss);
8808         if (err)
8809           goto reply;
8810         dout(20) << "erasure code profile set " << profile << "="
8811                  << profile_map << dendl;
8812         pending_inc.set_erasure_code_profile(profile, profile_map);
8813         goto wait;
8814       }
8815     }
8816
8817     int rule;
8818     err = crush_rule_create_erasure(name, profile, &rule, &ss);
8819     if (err < 0) {
8820       switch(err) {
8821       case -EEXIST: // return immediately
8822         ss << "rule " << name << " already exists";
8823         err = 0;
8824         goto reply;
8825         break;
8826       case -EALREADY: // wait for pending to be proposed
8827         ss << "rule " << name << " already exists";
8828         err = 0;
8829         break;
8830       default: // non recoverable error
8831         goto reply;
8832         break;
8833       }
8834     } else {
8835       ss << "created rule " << name << " at " << rule;
8836     }
8837
8838     getline(ss, rs);
8839     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8840                                                       get_last_committed() + 1));
8841     return true;
8842
8843   } else if (prefix == "osd crush rule rm") {
8844     string name;
8845     cmd_getval(g_ceph_context, cmdmap, "name", name);
8846
8847     if (!osdmap.crush->rule_exists(name)) {
8848       ss << "rule " << name << " does not exist";
8849       err = 0;
8850       goto reply;
8851     }
8852
8853     CrushWrapper newcrush;
8854     _get_pending_crush(newcrush);
8855
8856     if (!newcrush.rule_exists(name)) {
8857       ss << "rule " << name << " does not exist";
8858       err = 0;
8859     } else {
8860       int ruleno = newcrush.get_rule_id(name);
8861       assert(ruleno >= 0);
8862
8863       // make sure it is not in use.
8864       // FIXME: this is ok in some situations, but let's not bother with that
8865       // complexity now.
8866       int ruleset = newcrush.get_rule_mask_ruleset(ruleno);
8867       if (osdmap.crush_rule_in_use(ruleset)) {
8868         ss << "crush ruleset " << name << " " << ruleset << " is in use";
8869         err = -EBUSY;
8870         goto reply;
8871       }
8872
8873       err = newcrush.remove_rule(ruleno);
8874       if (err < 0) {
8875         goto reply;
8876       }
8877
8878       pending_inc.crush.clear();
8879       newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8880     }
8881     getline(ss, rs);
8882     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8883                                               get_last_committed() + 1));
8884     return true;
8885
8886   } else if (prefix == "osd crush rule rename") {
8887     string srcname;
8888     string dstname;
8889     cmd_getval(g_ceph_context, cmdmap, "srcname", srcname);
8890     cmd_getval(g_ceph_context, cmdmap, "dstname", dstname);
8891     if (srcname.empty() || dstname.empty()) {
8892       ss << "must specify both source rule name and destination rule name";
8893       err = -EINVAL;
8894       goto reply;
8895     }
8896     if (srcname == dstname) {
8897       ss << "destination rule name is equal to source rule name";
8898       err = 0;
8899       goto reply;
8900     }
8901
8902     CrushWrapper newcrush;
8903     _get_pending_crush(newcrush);
8904     if (!newcrush.rule_exists(srcname) && newcrush.rule_exists(dstname)) {
8905       // srcname does not exist and dstname already exists
8906       // suppose this is a replay and return success
8907       // (so this command is idempotent)
8908       ss << "already renamed to '" << dstname << "'";
8909       err = 0;
8910       goto reply;
8911     }
8912
8913     err = newcrush.rename_rule(srcname, dstname, &ss);
8914     if (err < 0) {
8915       // ss has reason for failure
8916       goto reply;
8917     }
8918     pending_inc.crush.clear();
8919     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
8920     getline(ss, rs);
8921     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8922                                get_last_committed() + 1));
8923     return true;
8924
8925   } else if (prefix == "osd setmaxosd") {
8926     int64_t newmax;
8927     if (!cmd_getval(g_ceph_context, cmdmap, "newmax", newmax)) {
8928       ss << "unable to parse 'newmax' value '"
8929          << cmd_vartype_stringify(cmdmap["newmax"]) << "'";
8930       err = -EINVAL;
8931       goto reply;
8932     }
8933
8934     if (newmax > g_conf->mon_max_osd) {
8935       err = -ERANGE;
8936       ss << "cannot set max_osd to " << newmax << " which is > conf.mon_max_osd ("
8937          << g_conf->mon_max_osd << ")";
8938       goto reply;
8939     }
8940
8941     // Don't allow shrinking OSD number as this will cause data loss
8942     // and may cause kernel crashes.
8943     // Note: setmaxosd sets the maximum OSD number and not the number of OSDs
8944     if (newmax < osdmap.get_max_osd()) {
8945       // Check if the OSDs exist between current max and new value.
8946       // If there are any OSDs exist, then don't allow shrinking number
8947       // of OSDs.
8948       for (int i = newmax; i < osdmap.get_max_osd(); i++) {
8949         if (osdmap.exists(i)) {
8950           err = -EBUSY;
8951           ss << "cannot shrink max_osd to " << newmax
8952              << " because osd." << i << " (and possibly others) still in use";
8953           goto reply;
8954         }
8955       }
8956     }
8957
8958     pending_inc.new_max_osd = newmax;
8959     ss << "set new max_osd = " << pending_inc.new_max_osd;
8960     getline(ss, rs);
8961     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8962                                               get_last_committed() + 1));
8963     return true;
8964
8965   } else if (prefix == "osd set-full-ratio" ||
8966              prefix == "osd set-backfillfull-ratio" ||
8967              prefix == "osd set-nearfull-ratio") {
8968     if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
8969       ss << "you must complete the upgrade and 'ceph osd require-osd-release "
8970          << "luminous' before using the new interface";
8971       err = -EPERM;
8972       goto reply;
8973     }
8974     double n;
8975     if (!cmd_getval(g_ceph_context, cmdmap, "ratio", n)) {
8976       ss << "unable to parse 'ratio' value '"
8977          << cmd_vartype_stringify(cmdmap["ratio"]) << "'";
8978       err = -EINVAL;
8979       goto reply;
8980     }
8981     if (prefix == "osd set-full-ratio")
8982       pending_inc.new_full_ratio = n;
8983     else if (prefix == "osd set-backfillfull-ratio")
8984       pending_inc.new_backfillfull_ratio = n;
8985     else if (prefix == "osd set-nearfull-ratio")
8986       pending_inc.new_nearfull_ratio = n;
8987     ss << prefix << " " << n;
8988     getline(ss, rs);
8989     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
8990                                               get_last_committed() + 1));
8991     return true;
8992   } else if (prefix == "osd set-require-min-compat-client") {
8993     if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
8994       ss << "you must complete the upgrade and 'ceph osd require-osd-release "
8995          << "luminous' before using the new interface";
8996       err = -EPERM;
8997       goto reply;
8998     }
8999     string v;
9000     cmd_getval(g_ceph_context, cmdmap, "version", v);
9001     int vno = ceph_release_from_name(v.c_str());
9002     if (vno <= 0) {
9003       ss << "version " << v << " is not recognized";
9004       err = -EINVAL;
9005       goto reply;
9006     }
9007     OSDMap newmap;
9008     newmap.deepish_copy_from(osdmap);
9009     newmap.apply_incremental(pending_inc);
9010     newmap.require_min_compat_client = vno;
9011     auto mvno = newmap.get_min_compat_client();
9012     if (vno < mvno) {
9013       ss << "osdmap current utilizes features that require "
9014          << ceph_release_name(mvno)
9015          << "; cannot set require_min_compat_client below that to "
9016          << ceph_release_name(vno);
9017       err = -EPERM;
9018       goto reply;
9019     }
9020     string sure;
9021     cmd_getval(g_ceph_context, cmdmap, "sure", sure);
9022     if (sure != "--yes-i-really-mean-it") {
9023       FeatureMap m;
9024       mon->get_combined_feature_map(&m);
9025       uint64_t features = ceph_release_features(vno);
9026       bool first = true;
9027       bool ok = true;
9028       for (int type : {
9029             CEPH_ENTITY_TYPE_CLIENT,
9030             CEPH_ENTITY_TYPE_MDS,
9031             CEPH_ENTITY_TYPE_MGR }) {
9032         auto p = m.m.find(type);
9033         if (p == m.m.end()) {
9034           continue;
9035         }
9036         for (auto& q : p->second) {
9037           uint64_t missing = ~q.first & features;
9038           if (missing) {
9039             if (first) {
9040               ss << "cannot set require_min_compat_client to " << v << ": ";
9041             } else {
9042               ss << "; ";
9043             }
9044             first = false;
9045             ss << q.second << " connected " << ceph_entity_type_name(type)
9046                << "(s) look like " << ceph_release_name(
9047                  ceph_release_from_features(q.first))
9048                << " (missing 0x" << std::hex << missing << std::dec << ")";
9049             ok = false;
9050           }
9051         }
9052       }
9053       if (!ok) {
9054         ss << "; add --yes-i-really-mean-it to do it anyway";
9055         err = -EPERM;
9056         goto reply;
9057       }
9058     }
9059     ss << "set require_min_compat_client to " << ceph_release_name(vno);
9060     pending_inc.new_require_min_compat_client = vno;
9061     getline(ss, rs);
9062     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
9063                                                           get_last_committed() + 1));
9064     return true;
9065   } else if (prefix == "osd pause") {
9066     return prepare_set_flag(op, CEPH_OSDMAP_PAUSERD | CEPH_OSDMAP_PAUSEWR);
9067
9068   } else if (prefix == "osd unpause") {
9069     return prepare_unset_flag(op, CEPH_OSDMAP_PAUSERD | CEPH_OSDMAP_PAUSEWR);
9070
9071   } else if (prefix == "osd set") {
9072     string sure;
9073     cmd_getval(g_ceph_context, cmdmap, "sure", sure);
9074     string key;
9075     cmd_getval(g_ceph_context, cmdmap, "key", key);
9076     if (key == "full")
9077       return prepare_set_flag(op, CEPH_OSDMAP_FULL);
9078     else if (key == "pause")
9079       return prepare_set_flag(op, CEPH_OSDMAP_PAUSERD | CEPH_OSDMAP_PAUSEWR);
9080     else if (key == "noup")
9081       return prepare_set_flag(op, CEPH_OSDMAP_NOUP);
9082     else if (key == "nodown")
9083       return prepare_set_flag(op, CEPH_OSDMAP_NODOWN);
9084     else if (key == "noout")
9085       return prepare_set_flag(op, CEPH_OSDMAP_NOOUT);
9086     else if (key == "noin")
9087       return prepare_set_flag(op, CEPH_OSDMAP_NOIN);
9088     else if (key == "nobackfill")
9089       return prepare_set_flag(op, CEPH_OSDMAP_NOBACKFILL);
9090     else if (key == "norebalance")
9091       return prepare_set_flag(op, CEPH_OSDMAP_NOREBALANCE);
9092     else if (key == "norecover")
9093       return prepare_set_flag(op, CEPH_OSDMAP_NORECOVER);
9094     else if (key == "noscrub")
9095       return prepare_set_flag(op, CEPH_OSDMAP_NOSCRUB);
9096     else if (key == "nodeep-scrub")
9097       return prepare_set_flag(op, CEPH_OSDMAP_NODEEP_SCRUB);
9098     else if (key == "notieragent")
9099       return prepare_set_flag(op, CEPH_OSDMAP_NOTIERAGENT);
9100     else if (key == "sortbitwise") {
9101       if (!osdmap.get_num_up_osds() && sure != "--yes-i-really-mean-it") {
9102         ss << "Not advisable to continue since no OSDs are up. Pass "
9103            << "--yes-i-really-mean-it if you really wish to continue.";
9104         err = -EPERM;
9105         goto reply;
9106       }
9107       if ((osdmap.get_up_osd_features() & CEPH_FEATURE_OSD_BITWISE_HOBJ_SORT)
9108           || sure == "--yes-i-really-mean-it") {
9109         return prepare_set_flag(op, CEPH_OSDMAP_SORTBITWISE);
9110       } else {
9111         ss << "not all up OSDs have OSD_BITWISE_HOBJ_SORT feature";
9112         err = -EPERM;
9113         goto reply;
9114       }
9115     } else if (key == "recovery_deletes") {
9116       if (!osdmap.get_num_up_osds() && sure != "--yes-i-really-mean-it") {
9117         ss << "Not advisable to continue since no OSDs are up. Pass "
9118            << "--yes-i-really-mean-it if you really wish to continue.";
9119         err = -EPERM;
9120         goto reply;
9121       }
9122       if (HAVE_FEATURE(osdmap.get_up_osd_features(), OSD_RECOVERY_DELETES)
9123           || sure == "--yes-i-really-mean-it") {
9124         return prepare_set_flag(op, CEPH_OSDMAP_RECOVERY_DELETES);
9125       } else {
9126         ss << "not all up OSDs have OSD_RECOVERY_DELETES feature";
9127         err = -EPERM;
9128         goto reply;
9129       }
9130     } else if (key == "require_jewel_osds") {
9131       if (!osdmap.get_num_up_osds() && sure != "--yes-i-really-mean-it") {
9132         ss << "Not advisable to continue since no OSDs are up. Pass "
9133            << "--yes-i-really-mean-it if you really wish to continue.";
9134         err = -EPERM;
9135         goto reply;
9136       }
9137       if (!osdmap.test_flag(CEPH_OSDMAP_SORTBITWISE)) {
9138         ss << "the sortbitwise flag must be set before require_jewel_osds";
9139         err = -EPERM;
9140         goto reply;
9141       } else if (osdmap.require_osd_release >= CEPH_RELEASE_JEWEL) {
9142         ss << "require_osd_release is already >= jewel";
9143         err = 0;
9144         goto reply;
9145       } else if (HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_JEWEL)
9146                  || sure == "--yes-i-really-mean-it") {
9147         return prepare_set_flag(op, CEPH_OSDMAP_REQUIRE_JEWEL);
9148       } else {
9149         ss << "not all up OSDs have CEPH_FEATURE_SERVER_JEWEL feature";
9150         err = -EPERM;
9151       }
9152     } else if (key == "require_kraken_osds") {
9153       if (!osdmap.get_num_up_osds() && sure != "--yes-i-really-mean-it") {
9154         ss << "Not advisable to continue since no OSDs are up. Pass "
9155            << "--yes-i-really-mean-it if you really wish to continue.";
9156         err = -EPERM;
9157         goto reply;
9158       }
9159       if (!osdmap.test_flag(CEPH_OSDMAP_SORTBITWISE)) {
9160         ss << "the sortbitwise flag must be set before require_kraken_osds";
9161         err = -EPERM;
9162         goto reply;
9163       } else if (osdmap.require_osd_release >= CEPH_RELEASE_KRAKEN) {
9164         ss << "require_osd_release is already >= kraken";
9165         err = 0;
9166         goto reply;
9167       } else if (HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_KRAKEN)
9168                  || sure == "--yes-i-really-mean-it") {
9169         bool r = prepare_set_flag(op, CEPH_OSDMAP_REQUIRE_KRAKEN);
9170         // ensure JEWEL is also set
9171         pending_inc.new_flags |= CEPH_OSDMAP_REQUIRE_JEWEL;
9172         return r;
9173       } else {
9174         ss << "not all up OSDs have CEPH_FEATURE_SERVER_KRAKEN feature";
9175         err = -EPERM;
9176       }
9177     } else {
9178       ss << "unrecognized flag '" << key << "'";
9179       err = -EINVAL;
9180     }
9181
9182   } else if (prefix == "osd unset") {
9183     string key;
9184     cmd_getval(g_ceph_context, cmdmap, "key", key);
9185     if (key == "full")
9186       return prepare_unset_flag(op, CEPH_OSDMAP_FULL);
9187     else if (key == "pause")
9188       return prepare_unset_flag(op, CEPH_OSDMAP_PAUSERD | CEPH_OSDMAP_PAUSEWR);
9189     else if (key == "noup")
9190       return prepare_unset_flag(op, CEPH_OSDMAP_NOUP);
9191     else if (key == "nodown")
9192       return prepare_unset_flag(op, CEPH_OSDMAP_NODOWN);
9193     else if (key == "noout")
9194       return prepare_unset_flag(op, CEPH_OSDMAP_NOOUT);
9195     else if (key == "noin")
9196       return prepare_unset_flag(op, CEPH_OSDMAP_NOIN);
9197     else if (key == "nobackfill")
9198       return prepare_unset_flag(op, CEPH_OSDMAP_NOBACKFILL);
9199     else if (key == "norebalance")
9200       return prepare_unset_flag(op, CEPH_OSDMAP_NOREBALANCE);
9201     else if (key == "norecover")
9202       return prepare_unset_flag(op, CEPH_OSDMAP_NORECOVER);
9203     else if (key == "noscrub")
9204       return prepare_unset_flag(op, CEPH_OSDMAP_NOSCRUB);
9205     else if (key == "nodeep-scrub")
9206       return prepare_unset_flag(op, CEPH_OSDMAP_NODEEP_SCRUB);
9207     else if (key == "notieragent")
9208       return prepare_unset_flag(op, CEPH_OSDMAP_NOTIERAGENT);
9209     else {
9210       ss << "unrecognized flag '" << key << "'";
9211       err = -EINVAL;
9212     }
9213
9214   } else if (prefix == "osd require-osd-release") {
9215     string release;
9216     cmd_getval(g_ceph_context, cmdmap, "release", release);
9217     string sure;
9218     cmd_getval(g_ceph_context, cmdmap, "sure", sure);
9219     if (!osdmap.test_flag(CEPH_OSDMAP_SORTBITWISE)) {
9220       ss << "the sortbitwise flag must be set first";
9221       err = -EPERM;
9222       goto reply;
9223     }
9224     int rel = ceph_release_from_name(release.c_str());
9225     if (rel <= 0) {
9226       ss << "unrecognized release " << release;
9227       err = -EINVAL;
9228       goto reply;
9229     }
9230     if (rel < CEPH_RELEASE_LUMINOUS) {
9231       ss << "use this command only for luminous and later";
9232       err = -EINVAL;
9233       goto reply;
9234     }
9235     if (rel == osdmap.require_osd_release) {
9236       // idempotent
9237       err = 0;
9238       goto reply;
9239     }
9240     if (rel == CEPH_RELEASE_LUMINOUS) {
9241       if (!HAVE_FEATURE(osdmap.get_up_osd_features(), SERVER_LUMINOUS)) {
9242         ss << "not all up OSDs have CEPH_FEATURE_SERVER_LUMINOUS feature";
9243         err = -EPERM;
9244         goto reply;
9245       }
9246     } else {
9247       ss << "not supported for this release yet";
9248       err = -EPERM;
9249       goto reply;
9250     }
9251     if (rel < osdmap.require_osd_release) {
9252       ss << "require_osd_release cannot be lowered once it has been set";
9253       err = -EPERM;
9254       goto reply;
9255     }
9256     pending_inc.new_require_osd_release = rel;
9257     if (rel >= CEPH_RELEASE_LUMINOUS &&
9258         !osdmap.test_flag(CEPH_OSDMAP_RECOVERY_DELETES)) {
9259       return prepare_set_flag(op, CEPH_OSDMAP_RECOVERY_DELETES);
9260     }
9261     goto update;
9262   } else if (prefix == "osd cluster_snap") {
9263     // ** DISABLE THIS FOR NOW **
9264     ss << "cluster snapshot currently disabled (broken implementation)";
9265     // ** DISABLE THIS FOR NOW **
9266
9267   } else if (prefix == "osd down" ||
9268              prefix == "osd out" ||
9269              prefix == "osd in" ||
9270              prefix == "osd rm") {
9271
9272     bool any = false;
9273     bool stop = false;
9274     bool verbose = true;
9275
9276     vector<string> idvec;
9277     cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
9278     for (unsigned j = 0; j < idvec.size() && !stop; j++) {
9279       set<int> osds;
9280
9281       // wildcard?
9282       if (j == 0 &&
9283           (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
9284         if (prefix == "osd in") {
9285           // touch out osds only
9286           osdmap.get_out_osds(osds);
9287         } else {
9288           osdmap.get_all_osds(osds);
9289         }
9290         stop = true;
9291         verbose = false; // so the output is less noisy.
9292       } else {
9293         long osd = parse_osd_id(idvec[j].c_str(), &ss);
9294         if (osd < 0) {
9295           ss << "invalid osd id" << osd;
9296           err = -EINVAL;
9297           continue;
9298         } else if (!osdmap.exists(osd)) {
9299           ss << "osd." << osd << " does not exist. ";
9300           continue;
9301         }
9302
9303         osds.insert(osd);
9304       }
9305
9306       for (auto &osd : osds) {
9307         if (prefix == "osd down") {
9308           if (osdmap.is_down(osd)) {
9309             if (verbose)
9310               ss << "osd." << osd << " is already down. ";
9311           } else {
9312             pending_inc.pending_osd_state_set(osd, CEPH_OSD_UP);
9313             ss << "marked down osd." << osd << ". ";
9314             any = true;
9315           }
9316         } else if (prefix == "osd out") {
9317           if (osdmap.is_out(osd)) {
9318             if (verbose)
9319               ss << "osd." << osd << " is already out. ";
9320           } else {
9321             pending_inc.new_weight[osd] = CEPH_OSD_OUT;
9322             if (osdmap.osd_weight[osd]) {
9323               if (pending_inc.new_xinfo.count(osd) == 0) {
9324                 pending_inc.new_xinfo[osd] = osdmap.osd_xinfo[osd];
9325               }
9326               pending_inc.new_xinfo[osd].old_weight = osdmap.osd_weight[osd];
9327             }
9328             ss << "marked out osd." << osd << ". ";
9329             std::ostringstream msg;
9330             msg << "Client " << op->get_session()->entity_name
9331                 << " marked osd." << osd << " out";
9332             if (osdmap.is_up(osd)) {
9333               msg << ", while it was still marked up";
9334             } else {
9335               auto period = ceph_clock_now() - down_pending_out[osd];
9336               msg << ", after it was down for " << int(period.sec())
9337                   << " seconds";
9338             }
9339
9340             mon->clog->info() << msg.str();
9341             any = true;
9342           }
9343         } else if (prefix == "osd in") {
9344           if (osdmap.is_in(osd)) {
9345             if (verbose)
9346               ss << "osd." << osd << " is already in. ";
9347           } else {
9348             if (osdmap.osd_xinfo[osd].old_weight > 0) {
9349               pending_inc.new_weight[osd] = osdmap.osd_xinfo[osd].old_weight;
9350               if (pending_inc.new_xinfo.count(osd) == 0) {
9351                 pending_inc.new_xinfo[osd] = osdmap.osd_xinfo[osd];
9352               }
9353               pending_inc.new_xinfo[osd].old_weight = 0;
9354             } else {
9355               pending_inc.new_weight[osd] = CEPH_OSD_IN;
9356             }
9357             ss << "marked in osd." << osd << ". ";
9358             any = true;
9359           }
9360         } else if (prefix == "osd rm") {
9361           err = prepare_command_osd_remove(osd);
9362
9363           if (err == -EBUSY) {
9364             if (any)
9365               ss << ", ";
9366             ss << "osd." << osd << " is still up; must be down before removal. ";
9367           } else {
9368             assert(err == 0);
9369             if (any) {
9370               ss << ", osd." << osd;
9371             } else {
9372               ss << "removed osd." << osd;
9373             }
9374             any = true;
9375           }
9376         }
9377       }
9378     }
9379     if (any) {
9380       getline(ss, rs);
9381       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, err, rs,
9382                                                 get_last_committed() + 1));
9383       return true;
9384     }
9385   } else if (prefix == "osd add-noup" ||
9386              prefix == "osd add-nodown" ||
9387              prefix == "osd add-noin" ||
9388              prefix == "osd add-noout") {
9389
9390     enum {
9391       OP_NOUP,
9392       OP_NODOWN,
9393       OP_NOIN,
9394       OP_NOOUT,
9395     } option;
9396
9397     if (prefix == "osd add-noup") {
9398       option = OP_NOUP;
9399     } else if (prefix == "osd add-nodown") {
9400       option = OP_NODOWN;
9401     } else if (prefix == "osd add-noin") {
9402       option = OP_NOIN;
9403     } else {
9404       option = OP_NOOUT;
9405     }
9406
9407     bool any = false;
9408     bool stop = false;
9409
9410     vector<string> idvec;
9411     cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
9412     for (unsigned j = 0; j < idvec.size() && !stop; j++) {
9413
9414       set<int> osds;
9415
9416       // wildcard?
9417       if (j == 0 &&
9418           (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
9419         osdmap.get_all_osds(osds);
9420         stop = true;
9421       } else {
9422         // try traditional single osd way
9423
9424         long osd = parse_osd_id(idvec[j].c_str(), &ss);
9425         if (osd < 0) {
9426           // ss has reason for failure
9427           ss << ", unable to parse osd id:\"" << idvec[j] << "\". ";
9428           err = -EINVAL;
9429           continue;
9430         }
9431
9432         osds.insert(osd);
9433       }
9434
9435       for (auto &osd : osds) {
9436
9437         if (!osdmap.exists(osd)) {
9438           ss << "osd." << osd << " does not exist. ";
9439           continue;
9440         }
9441
9442         switch (option) {
9443         case OP_NOUP:
9444           if (osdmap.is_up(osd)) {
9445             ss << "osd." << osd << " is already up. ";
9446             continue;
9447           }
9448
9449           if (osdmap.is_noup(osd)) {
9450             if (pending_inc.pending_osd_state_clear(osd, CEPH_OSD_NOUP))
9451               any = true;
9452           } else {
9453             pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOUP);
9454             any = true;
9455           }
9456
9457           break;
9458
9459         case OP_NODOWN:
9460           if (osdmap.is_down(osd)) {
9461             ss << "osd." << osd << " is already down. ";
9462             continue;
9463           }
9464
9465           if (osdmap.is_nodown(osd)) {
9466             if (pending_inc.pending_osd_state_clear(osd, CEPH_OSD_NODOWN))
9467               any = true;
9468           } else {
9469             pending_inc.pending_osd_state_set(osd, CEPH_OSD_NODOWN);
9470             any = true;
9471           }
9472
9473           break;
9474
9475         case OP_NOIN:
9476           if (osdmap.is_in(osd)) {
9477             ss << "osd." << osd << " is already in. ";
9478             continue;
9479           }
9480
9481           if (osdmap.is_noin(osd)) {
9482             if (pending_inc.pending_osd_state_clear(osd, CEPH_OSD_NOIN))
9483               any = true;
9484           } else {
9485             pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOIN);
9486             any = true;
9487           }
9488
9489           break;
9490
9491         case OP_NOOUT:
9492           if (osdmap.is_out(osd)) {
9493             ss << "osd." << osd << " is already out. ";
9494             continue;
9495           }
9496
9497           if (osdmap.is_noout(osd)) {
9498             if (pending_inc.pending_osd_state_clear(osd, CEPH_OSD_NOOUT))
9499               any = true;
9500           } else {
9501             pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOOUT);
9502             any = true;
9503           }
9504
9505           break;
9506
9507         default:
9508           assert(0 == "invalid option");
9509         }
9510       }
9511     }
9512
9513     if (any) {
9514       getline(ss, rs);
9515       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, err, rs,
9516                                  get_last_committed() + 1));
9517       return true;
9518     }
9519   } else if (prefix == "osd rm-noup" ||
9520              prefix == "osd rm-nodown" ||
9521              prefix == "osd rm-noin" ||
9522              prefix == "osd rm-noout") {
9523
9524     enum {
9525       OP_NOUP,
9526       OP_NODOWN,
9527       OP_NOIN,
9528       OP_NOOUT,
9529     } option;
9530
9531     if (prefix == "osd rm-noup") {
9532       option = OP_NOUP;
9533     } else if (prefix == "osd rm-nodown") {
9534       option = OP_NODOWN;
9535     } else if (prefix == "osd rm-noin") {
9536       option = OP_NOIN;
9537     } else {
9538       option = OP_NOOUT;
9539     }
9540
9541     bool any = false;
9542     bool stop = false;
9543
9544     vector<string> idvec;
9545     cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
9546
9547     for (unsigned j = 0; j < idvec.size() && !stop; j++) {
9548
9549       vector<int> osds;
9550
9551       // wildcard?
9552       if (j == 0 &&
9553           (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
9554
9555         // touch previous noup/nodown/noin/noout osds only
9556         switch (option) {
9557         case OP_NOUP:
9558           osdmap.get_noup_osds(&osds);
9559           break;
9560         case OP_NODOWN:
9561           osdmap.get_nodown_osds(&osds);
9562           break;
9563         case OP_NOIN:
9564           osdmap.get_noin_osds(&osds);
9565           break;
9566         case OP_NOOUT:
9567           osdmap.get_noout_osds(&osds);
9568           break;
9569         default:
9570           assert(0 == "invalid option");
9571         }
9572
9573         // cancel any pending noup/nodown/noin/noout requests too
9574         vector<int> pending_state_osds;
9575         (void) pending_inc.get_pending_state_osds(&pending_state_osds);
9576         for (auto &p : pending_state_osds) {
9577
9578           switch (option) {
9579           case OP_NOUP:
9580             if (!osdmap.is_noup(p) &&
9581                 pending_inc.pending_osd_state_clear(p, CEPH_OSD_NOUP)) {
9582               any = true;
9583             }
9584             break;
9585
9586           case OP_NODOWN:
9587             if (!osdmap.is_nodown(p) &&
9588                 pending_inc.pending_osd_state_clear(p, CEPH_OSD_NODOWN)) {
9589               any = true;
9590             }
9591             break;
9592
9593           case OP_NOIN:
9594             if (!osdmap.is_noin(p) &&
9595                 pending_inc.pending_osd_state_clear(p, CEPH_OSD_NOIN)) {
9596               any = true;
9597             }
9598             break;
9599
9600           case OP_NOOUT:
9601             if (!osdmap.is_noout(p) &&
9602                 pending_inc.pending_osd_state_clear(p, CEPH_OSD_NOOUT)) {
9603               any = true;
9604             }
9605             break;
9606
9607           default:
9608             assert(0 == "invalid option");
9609           }
9610         }
9611
9612         stop = true;
9613       } else {
9614         // try traditional single osd way
9615
9616         long osd = parse_osd_id(idvec[j].c_str(), &ss);
9617         if (osd < 0) {
9618           // ss has reason for failure
9619           ss << ", unable to parse osd id:\"" << idvec[j] << "\". ";
9620           err = -EINVAL;
9621           continue;
9622         }
9623
9624         osds.push_back(osd);
9625       }
9626
9627       for (auto &osd : osds) {
9628
9629         if (!osdmap.exists(osd)) {
9630           ss << "osd." << osd << " does not exist. ";
9631           continue;
9632         }
9633
9634         switch (option) {
9635           case OP_NOUP:
9636             if (osdmap.is_noup(osd)) {
9637               pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOUP);
9638               any = true;
9639             } else if (pending_inc.pending_osd_state_clear(
9640               osd, CEPH_OSD_NOUP)) {
9641               any = true;
9642             }
9643             break;
9644
9645           case OP_NODOWN:
9646             if (osdmap.is_nodown(osd)) {
9647               pending_inc.pending_osd_state_set(osd, CEPH_OSD_NODOWN);
9648               any = true;
9649             } else if (pending_inc.pending_osd_state_clear(
9650               osd, CEPH_OSD_NODOWN)) {
9651               any = true;
9652             }
9653             break;
9654
9655           case OP_NOIN:
9656             if (osdmap.is_noin(osd)) {
9657               pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOIN);
9658               any = true;
9659             } else if (pending_inc.pending_osd_state_clear(
9660               osd, CEPH_OSD_NOIN)) {
9661               any = true;
9662             }
9663             break;
9664
9665           case OP_NOOUT:
9666             if (osdmap.is_noout(osd)) {
9667               pending_inc.pending_osd_state_set(osd, CEPH_OSD_NOOUT);
9668               any = true;
9669             } else if (pending_inc.pending_osd_state_clear(
9670               osd, CEPH_OSD_NOOUT)) {
9671               any = true;
9672             }
9673             break;
9674
9675           default:
9676             assert(0 == "invalid option");
9677         }
9678       }
9679     }
9680
9681     if (any) {
9682       getline(ss, rs);
9683       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, err, rs,
9684                                  get_last_committed() + 1));
9685       return true;
9686     }
9687   } else if (prefix == "osd pg-temp") {
9688     string pgidstr;
9689     if (!cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr)) {
9690       ss << "unable to parse 'pgid' value '"
9691          << cmd_vartype_stringify(cmdmap["pgid"]) << "'";
9692       err = -EINVAL;
9693       goto reply;
9694     }
9695     pg_t pgid;
9696     if (!pgid.parse(pgidstr.c_str())) {
9697       ss << "invalid pgid '" << pgidstr << "'";
9698       err = -EINVAL;
9699       goto reply;
9700     }
9701     if (!osdmap.pg_exists(pgid)) {
9702       ss << "pg " << pgid << " does not exist";
9703       err = -ENOENT;
9704       goto reply;
9705     }
9706     if (pending_inc.new_pg_temp.count(pgid)) {
9707       dout(10) << __func__ << " waiting for pending update on " << pgid << dendl;
9708       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
9709       return true;
9710     }
9711
9712     vector<int64_t> id_vec;
9713     vector<int32_t> new_pg_temp;
9714     if (!cmd_getval(g_ceph_context, cmdmap, "id", id_vec)) {
9715       ss << "unable to parse 'id' value(s) '"
9716          << cmd_vartype_stringify(cmdmap["id"]) << "'";
9717       err = -EINVAL;
9718       goto reply;
9719     }
9720     for (auto osd : id_vec) {
9721       if (!osdmap.exists(osd)) {
9722         ss << "osd." << osd << " does not exist";
9723         err = -ENOENT;
9724         goto reply;
9725       }
9726       new_pg_temp.push_back(osd);
9727     }
9728
9729     int pool_min_size = osdmap.get_pg_pool_min_size(pgid);
9730     if ((int)new_pg_temp.size() < pool_min_size) {
9731       ss << "num of osds (" << new_pg_temp.size() <<") < pool min size ("
9732          << pool_min_size << ")";
9733       err = -EINVAL;
9734       goto reply;
9735     }
9736
9737     int pool_size = osdmap.get_pg_pool_size(pgid);
9738     if ((int)new_pg_temp.size() > pool_size) {
9739       ss << "num of osds (" << new_pg_temp.size() <<") > pool size ("
9740          << pool_size << ")";
9741       err = -EINVAL;
9742       goto reply;
9743     }
9744
9745     pending_inc.new_pg_temp[pgid] = mempool::osdmap::vector<int>(
9746       new_pg_temp.begin(), new_pg_temp.end());
9747     ss << "set " << pgid << " pg_temp mapping to " << new_pg_temp;
9748     goto update;
9749   } else if (prefix == "osd primary-temp") {
9750     string pgidstr;
9751     if (!cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr)) {
9752       ss << "unable to parse 'pgid' value '"
9753          << cmd_vartype_stringify(cmdmap["pgid"]) << "'";
9754       err = -EINVAL;
9755       goto reply;
9756     }
9757     pg_t pgid;
9758     if (!pgid.parse(pgidstr.c_str())) {
9759       ss << "invalid pgid '" << pgidstr << "'";
9760       err = -EINVAL;
9761       goto reply;
9762     }
9763     if (!osdmap.pg_exists(pgid)) {
9764       ss << "pg " << pgid << " does not exist";
9765       err = -ENOENT;
9766       goto reply;
9767     }
9768
9769     int64_t osd;
9770     if (!cmd_getval(g_ceph_context, cmdmap, "id", osd)) {
9771       ss << "unable to parse 'id' value '"
9772          << cmd_vartype_stringify(cmdmap["id"]) << "'";
9773       err = -EINVAL;
9774       goto reply;
9775     }
9776     if (osd != -1 && !osdmap.exists(osd)) {
9777       ss << "osd." << osd << " does not exist";
9778       err = -ENOENT;
9779       goto reply;
9780     }
9781
9782     if (osdmap.require_min_compat_client > 0 &&
9783         osdmap.require_min_compat_client < CEPH_RELEASE_FIREFLY) {
9784       ss << "require_min_compat_client "
9785          << ceph_release_name(osdmap.require_min_compat_client)
9786          << " < firefly, which is required for primary-temp";
9787       err = -EPERM;
9788       goto reply;
9789     } else if (!g_conf->mon_osd_allow_primary_temp) {
9790       ss << "you must enable 'mon osd allow primary temp = true' on the mons before you can set primary_temp mappings.  note that this is for developers only: older clients/OSDs will break and there is no feature bit infrastructure in place.";
9791       err = -EPERM;
9792       goto reply;
9793     }
9794
9795     pending_inc.new_primary_temp[pgid] = osd;
9796     ss << "set " << pgid << " primary_temp mapping to " << osd;
9797     goto update;
9798   } else if (prefix == "osd pg-upmap" ||
9799              prefix == "osd rm-pg-upmap" ||
9800              prefix == "osd pg-upmap-items" ||
9801              prefix == "osd rm-pg-upmap-items") {
9802     if (osdmap.require_osd_release < CEPH_RELEASE_LUMINOUS) {
9803       ss << "you must complete the upgrade and 'ceph osd require-osd-release "
9804          << "luminous' before using the new interface";
9805       err = -EPERM;
9806       goto reply;
9807     }
9808     if (osdmap.require_min_compat_client < CEPH_RELEASE_LUMINOUS) {
9809       ss << "min_compat_client "
9810          << ceph_release_name(osdmap.require_min_compat_client)
9811          << " < luminous, which is required for pg-upmap. "
9812          << "Try 'ceph osd set-require-min-compat-client luminous' "
9813          << "before using the new interface";
9814       err = -EPERM;
9815       goto reply;
9816     }
9817     err = check_cluster_features(CEPH_FEATUREMASK_OSDMAP_PG_UPMAP, ss);
9818     if (err == -EAGAIN)
9819       goto wait;
9820     if (err < 0)
9821       goto reply;
9822     string pgidstr;
9823     if (!cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr)) {
9824       ss << "unable to parse 'pgid' value '"
9825          << cmd_vartype_stringify(cmdmap["pgid"]) << "'";
9826       err = -EINVAL;
9827       goto reply;
9828     }
9829     pg_t pgid;
9830     if (!pgid.parse(pgidstr.c_str())) {
9831       ss << "invalid pgid '" << pgidstr << "'";
9832       err = -EINVAL;
9833       goto reply;
9834     }
9835     if (!osdmap.pg_exists(pgid)) {
9836       ss << "pg " << pgid << " does not exist";
9837       err = -ENOENT;
9838       goto reply;
9839     }
9840
9841     enum {
9842       OP_PG_UPMAP,
9843       OP_RM_PG_UPMAP,
9844       OP_PG_UPMAP_ITEMS,
9845       OP_RM_PG_UPMAP_ITEMS,
9846     } option;
9847
9848     if (prefix == "osd pg-upmap") {
9849       option = OP_PG_UPMAP;
9850     } else if (prefix == "osd rm-pg-upmap") {
9851       option = OP_RM_PG_UPMAP;
9852     } else if (prefix == "osd pg-upmap-items") {
9853       option = OP_PG_UPMAP_ITEMS;
9854     } else {
9855       option = OP_RM_PG_UPMAP_ITEMS;
9856     }
9857
9858     // check pending upmap changes
9859     switch (option) {
9860     case OP_PG_UPMAP: // fall through
9861     case OP_RM_PG_UPMAP:
9862       if (pending_inc.new_pg_upmap.count(pgid) ||
9863           pending_inc.old_pg_upmap.count(pgid)) {
9864         dout(10) << __func__ << " waiting for pending update on "
9865                  << pgid << dendl;
9866         wait_for_finished_proposal(op, new C_RetryMessage(this, op));
9867         return true;
9868       }
9869       break;
9870
9871     case OP_PG_UPMAP_ITEMS: // fall through
9872     case OP_RM_PG_UPMAP_ITEMS:
9873       if (pending_inc.new_pg_upmap_items.count(pgid) ||
9874           pending_inc.old_pg_upmap_items.count(pgid)) {
9875         dout(10) << __func__ << " waiting for pending update on "
9876                  << pgid << dendl;
9877         wait_for_finished_proposal(op, new C_RetryMessage(this, op));
9878         return true;
9879       }
9880       break;
9881
9882     default:
9883       assert(0 == "invalid option");
9884     }
9885
9886     switch (option) {
9887     case OP_PG_UPMAP:
9888       {
9889         vector<int64_t> id_vec;
9890         if (!cmd_getval(g_ceph_context, cmdmap, "id", id_vec)) {
9891           ss << "unable to parse 'id' value(s) '"
9892              << cmd_vartype_stringify(cmdmap["id"]) << "'";
9893           err = -EINVAL;
9894           goto reply;
9895         }
9896
9897         int pool_min_size = osdmap.get_pg_pool_min_size(pgid);
9898         if ((int)id_vec.size() < pool_min_size) {
9899           ss << "num of osds (" << id_vec.size() <<") < pool min size ("
9900              << pool_min_size << ")";
9901           err = -EINVAL;
9902           goto reply;
9903         }
9904
9905         int pool_size = osdmap.get_pg_pool_size(pgid);
9906         if ((int)id_vec.size() > pool_size) {
9907           ss << "num of osds (" << id_vec.size() <<") > pool size ("
9908              << pool_size << ")";
9909           err = -EINVAL;
9910           goto reply;
9911         }
9912
9913         vector<int32_t> new_pg_upmap;
9914         for (auto osd : id_vec) {
9915           if (osd != CRUSH_ITEM_NONE && !osdmap.exists(osd)) {
9916             ss << "osd." << osd << " does not exist";
9917             err = -ENOENT;
9918             goto reply;
9919           }
9920           auto it = std::find(new_pg_upmap.begin(), new_pg_upmap.end(), osd);
9921           if (it != new_pg_upmap.end()) {
9922             ss << "osd." << osd << " already exists, ";
9923             continue;
9924           }
9925           new_pg_upmap.push_back(osd);
9926         }
9927
9928         if (new_pg_upmap.empty()) {
9929           ss << "no valid upmap items(pairs) is specified";
9930           err = -EINVAL;
9931           goto reply;
9932         }
9933
9934         pending_inc.new_pg_upmap[pgid] = mempool::osdmap::vector<int32_t>(
9935           new_pg_upmap.begin(), new_pg_upmap.end());
9936         ss << "set " << pgid << " pg_upmap mapping to " << new_pg_upmap;
9937       }
9938       break;
9939
9940     case OP_RM_PG_UPMAP:
9941       {
9942         pending_inc.old_pg_upmap.insert(pgid);
9943         ss << "clear " << pgid << " pg_upmap mapping";
9944       }
9945       break;
9946
9947     case OP_PG_UPMAP_ITEMS:
9948       {
9949         vector<int64_t> id_vec;
9950         if (!cmd_getval(g_ceph_context, cmdmap, "id", id_vec)) {
9951           ss << "unable to parse 'id' value(s) '"
9952              << cmd_vartype_stringify(cmdmap["id"]) << "'";
9953           err = -EINVAL;
9954           goto reply;
9955         }
9956
9957         if (id_vec.size() % 2) {
9958           ss << "you must specify pairs of osd ids to be remapped";
9959           err = -EINVAL;
9960           goto reply;
9961         }
9962
9963         int pool_size = osdmap.get_pg_pool_size(pgid);
9964         if ((int)(id_vec.size() / 2) > pool_size) {
9965           ss << "num of osd pairs (" << id_vec.size() / 2 <<") > pool size ("
9966              << pool_size << ")";
9967           err = -EINVAL;
9968           goto reply;
9969         }
9970
9971         vector<pair<int32_t,int32_t>> new_pg_upmap_items;
9972         ostringstream items;
9973         items << "[";
9974         for (auto p = id_vec.begin(); p != id_vec.end(); ++p) {
9975           int from = *p++;
9976           int to = *p;
9977           if (from == to) {
9978             ss << "from osd." << from << " == to osd." << to << ", ";
9979             continue;
9980           }
9981           if (!osdmap.exists(from)) {
9982             ss << "osd." << from << " does not exist";
9983             err = -ENOENT;
9984             goto reply;
9985           }
9986           if (to != CRUSH_ITEM_NONE && !osdmap.exists(to)) {
9987             ss << "osd." << to << " does not exist";
9988             err = -ENOENT;
9989             goto reply;
9990           }
9991           pair<int32_t,int32_t> entry = make_pair(from, to);
9992           auto it = std::find(new_pg_upmap_items.begin(),
9993             new_pg_upmap_items.end(), entry);
9994           if (it != new_pg_upmap_items.end()) {
9995             ss << "osd." << from << " -> osd." << to << " already exists, ";
9996             continue;
9997           }
9998           new_pg_upmap_items.push_back(entry);
9999           items << from << "->" << to << ",";
10000         }
10001         string out(items.str());
10002         out.resize(out.size() - 1); // drop last ','
10003         out += "]";
10004
10005         if (new_pg_upmap_items.empty()) {
10006           ss << "no valid upmap items(pairs) is specified";
10007           err = -EINVAL;
10008           goto reply;
10009         }
10010
10011         pending_inc.new_pg_upmap_items[pgid] =
10012           mempool::osdmap::vector<pair<int32_t,int32_t>>(
10013           new_pg_upmap_items.begin(), new_pg_upmap_items.end());
10014         ss << "set " << pgid << " pg_upmap_items mapping to " << out;
10015       }
10016       break;
10017
10018     case OP_RM_PG_UPMAP_ITEMS:
10019       {
10020         pending_inc.old_pg_upmap_items.insert(pgid);
10021         ss << "clear " << pgid << " pg_upmap_items mapping";
10022       }
10023       break;
10024
10025     default:
10026       assert(0 == "invalid option");
10027     }
10028
10029     goto update;
10030   } else if (prefix == "osd primary-affinity") {
10031     int64_t id;
10032     if (!cmd_getval(g_ceph_context, cmdmap, "id", id)) {
10033       ss << "invalid osd id value '"
10034          << cmd_vartype_stringify(cmdmap["id"]) << "'";
10035       err = -EINVAL;
10036       goto reply;
10037     }
10038     double w;
10039     if (!cmd_getval(g_ceph_context, cmdmap, "weight", w)) {
10040       ss << "unable to parse 'weight' value '"
10041            << cmd_vartype_stringify(cmdmap["weight"]) << "'";
10042       err = -EINVAL;
10043       goto reply;
10044     }
10045     long ww = (int)((double)CEPH_OSD_MAX_PRIMARY_AFFINITY*w);
10046     if (ww < 0L) {
10047       ss << "weight must be >= 0";
10048       err = -EINVAL;
10049       goto reply;
10050     }
10051     if (osdmap.require_min_compat_client > 0 &&
10052         osdmap.require_min_compat_client < CEPH_RELEASE_FIREFLY) {
10053       ss << "require_min_compat_client "
10054          << ceph_release_name(osdmap.require_min_compat_client)
10055          << " < firefly, which is required for primary-affinity";
10056       err = -EPERM;
10057       goto reply;
10058     } else if (!g_conf->mon_osd_allow_primary_affinity) {
10059       ss << "you must enable 'mon osd allow primary affinity = true' on the mons before you can adjust primary-affinity.  note that older clients will no longer be able to communicate with the cluster.";
10060       err = -EPERM;
10061       goto reply;
10062     }
10063     err = check_cluster_features(CEPH_FEATURE_OSD_PRIMARY_AFFINITY, ss);
10064     if (err == -EAGAIN)
10065       goto wait;
10066     if (err < 0)
10067       goto reply;
10068     if (osdmap.exists(id)) {
10069       pending_inc.new_primary_affinity[id] = ww;
10070       ss << "set osd." << id << " primary-affinity to " << w << " (" << ios::hex << ww << ios::dec << ")";
10071       getline(ss, rs);
10072       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10073                                                 get_last_committed() + 1));
10074       return true;
10075     } else {
10076       ss << "osd." << id << " does not exist";
10077       err = -ENOENT;
10078       goto reply;
10079     }
10080   } else if (prefix == "osd reweight") {
10081     int64_t id;
10082     if (!cmd_getval(g_ceph_context, cmdmap, "id", id)) {
10083       ss << "unable to parse osd id value '"
10084          << cmd_vartype_stringify(cmdmap["id"]) << "'";
10085       err = -EINVAL;
10086       goto reply;
10087     }
10088     double w;
10089     if (!cmd_getval(g_ceph_context, cmdmap, "weight", w)) {
10090       ss << "unable to parse weight value '"
10091          << cmd_vartype_stringify(cmdmap["weight"]) << "'";
10092       err = -EINVAL;
10093       goto reply;
10094     }
10095     long ww = (int)((double)CEPH_OSD_IN*w);
10096     if (ww < 0L) {
10097       ss << "weight must be >= 0";
10098       err = -EINVAL;
10099       goto reply;
10100     }
10101     if (osdmap.exists(id)) {
10102       pending_inc.new_weight[id] = ww;
10103       ss << "reweighted osd." << id << " to " << w << " (" << std::hex << ww << std::dec << ")";
10104       getline(ss, rs);
10105       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10106                                                 get_last_committed() + 1));
10107       return true;
10108     } else {
10109       ss << "osd." << id << " does not exist";
10110       err = -ENOENT;
10111       goto reply;
10112     }
10113   } else if (prefix == "osd reweightn") {
10114     map<int32_t, uint32_t> weights;
10115     err = parse_reweights(g_ceph_context, cmdmap, osdmap, &weights);
10116     if (err) {
10117       ss << "unable to parse 'weights' value '"
10118          << cmd_vartype_stringify(cmdmap["weights"]) << "'";
10119       goto reply;
10120     }
10121     pending_inc.new_weight.insert(weights.begin(), weights.end());
10122     wait_for_finished_proposal(
10123         op,
10124         new Monitor::C_Command(mon, op, 0, rs, rdata, get_last_committed() + 1));
10125     return true;
10126   } else if (prefix == "osd lost") {
10127     int64_t id;
10128     if (!cmd_getval(g_ceph_context, cmdmap, "id", id)) {
10129       ss << "unable to parse osd id value '"
10130          << cmd_vartype_stringify(cmdmap["id"]) << "'";
10131       err = -EINVAL;
10132       goto reply;
10133     }
10134     string sure;
10135     if (!cmd_getval(g_ceph_context, cmdmap, "sure", sure) || sure != "--yes-i-really-mean-it") {
10136       ss << "are you SURE?  this might mean real, permanent data loss.  pass "
10137             "--yes-i-really-mean-it if you really do.";
10138       err = -EPERM;
10139       goto reply;
10140     } else if (!osdmap.exists(id)) {
10141       ss << "osd." << id << " does not exist";
10142       err = -ENOENT;
10143       goto reply;
10144     } else if (!osdmap.is_down(id)) {
10145       ss << "osd." << id << " is not down";
10146       err = -EBUSY;
10147       goto reply;
10148     } else {
10149       epoch_t e = osdmap.get_info(id).down_at;
10150       pending_inc.new_lost[id] = e;
10151       ss << "marked osd lost in epoch " << e;
10152       getline(ss, rs);
10153       wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10154                                                 get_last_committed() + 1));
10155       return true;
10156     }
10157
10158   } else if (prefix == "osd destroy" || prefix == "osd purge") {
10159     /* Destroying an OSD means that we don't expect to further make use of
10160      * the OSDs data (which may even become unreadable after this operation),
10161      * and that we are okay with scrubbing all its cephx keys and config-key
10162      * data (which may include lockbox keys, thus rendering the osd's data
10163      * unreadable).
10164      *
10165      * The OSD will not be removed. Instead, we will mark it as destroyed,
10166      * such that a subsequent call to `create` will not reuse the osd id.
10167      * This will play into being able to recreate the OSD, at the same
10168      * crush location, with minimal data movement.
10169      */
10170
10171     // make sure authmon is writeable.
10172     if (!mon->authmon()->is_writeable()) {
10173       dout(10) << __func__ << " waiting for auth mon to be writeable for "
10174                << "osd destroy" << dendl;
10175       mon->authmon()->wait_for_writeable(op, new C_RetryMessage(this, op));
10176       return false;
10177     }
10178
10179     int64_t id;
10180     if (!cmd_getval(g_ceph_context, cmdmap, "id", id)) {
10181       ss << "unable to parse osd id value '"
10182          << cmd_vartype_stringify(cmdmap["id"]) << "";
10183       err = -EINVAL;
10184       goto reply;
10185     }
10186
10187     bool is_destroy = (prefix == "osd destroy");
10188     if (!is_destroy) {
10189       assert("osd purge" == prefix);
10190     }
10191
10192     string sure;
10193     if (!cmd_getval(g_ceph_context, cmdmap, "sure", sure) ||
10194         sure != "--yes-i-really-mean-it") {
10195       ss << "Are you SURE? This will mean real, permanent data loss, as well "
10196          << "as cephx and lockbox keys. Pass --yes-i-really-mean-it if you "
10197          << "really do.";
10198       err = -EPERM;
10199       goto reply;
10200     } else if (!osdmap.exists(id)) {
10201       ss << "osd." << id << " does not exist";
10202       err = 0; // idempotent
10203       goto reply;
10204     } else if (osdmap.is_up(id)) {
10205       ss << "osd." << id << " is not `down`.";
10206       err = -EBUSY;
10207       goto reply;
10208     } else if (is_destroy && osdmap.is_destroyed(id)) {
10209       ss << "destroyed osd." << id;
10210       err = 0;
10211       goto reply;
10212     }
10213
10214     bool goto_reply = false;
10215
10216     paxos->plug();
10217     if (is_destroy) {
10218       err = prepare_command_osd_destroy(id, ss);
10219       // we checked above that it should exist.
10220       assert(err != -ENOENT);
10221     } else {
10222       err = prepare_command_osd_purge(id, ss);
10223       if (err == -ENOENT) {
10224         err = 0;
10225         ss << "osd." << id << " does not exist.";
10226         goto_reply = true;
10227       }
10228     }
10229     paxos->unplug();
10230
10231     if (err < 0 || goto_reply) {
10232       goto reply;
10233     }
10234
10235     if (is_destroy) {
10236       ss << "destroyed osd." << id;
10237     } else {
10238       ss << "purged osd." << id;
10239     }
10240
10241     getline(ss, rs);
10242     wait_for_finished_proposal(op,
10243         new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
10244     force_immediate_propose();
10245     return true;
10246
10247   } else if (prefix == "osd new") {
10248
10249     // make sure authmon is writeable.
10250     if (!mon->authmon()->is_writeable()) {
10251       dout(10) << __func__ << " waiting for auth mon to be writeable for "
10252                << "osd new" << dendl;
10253       mon->authmon()->wait_for_writeable(op, new C_RetryMessage(this, op));
10254       return false;
10255     }
10256
10257     map<string,string> secrets_map;
10258
10259     bufferlist bl = m->get_data();
10260     string secrets_json = bl.to_str();
10261     dout(20) << __func__ << " osd new json = " << secrets_json << dendl;
10262
10263     err = get_json_str_map(secrets_json, ss, &secrets_map);
10264     if (err < 0)
10265       goto reply;
10266
10267     dout(20) << __func__ << " osd new secrets " << secrets_map << dendl;
10268
10269     paxos->plug();
10270     err = prepare_command_osd_new(op, cmdmap, secrets_map, ss, f.get());
10271     paxos->unplug();
10272
10273     if (err < 0) {
10274       goto reply;
10275     }
10276
10277     if (f) {
10278       f->flush(rdata);
10279     } else {
10280       rdata.append(ss);
10281     }
10282
10283     if (err == EEXIST) {
10284       // idempotent operation
10285       err = 0;
10286       goto reply;
10287     }
10288
10289     wait_for_finished_proposal(op,
10290         new Monitor::C_Command(mon, op, 0, rs, rdata,
10291                                get_last_committed() + 1));
10292     force_immediate_propose();
10293     return true;
10294
10295   } else if (prefix == "osd create") {
10296
10297     // optional id provided?
10298     int64_t id = -1, cmd_id = -1;
10299     if (cmd_getval(g_ceph_context, cmdmap, "id", cmd_id)) {
10300       if (cmd_id < 0) {
10301         ss << "invalid osd id value '" << cmd_id << "'";
10302         err = -EINVAL;
10303         goto reply;
10304       }
10305       dout(10) << " osd create got id " << cmd_id << dendl;
10306     }
10307
10308     uuid_d uuid;
10309     string uuidstr;
10310     if (cmd_getval(g_ceph_context, cmdmap, "uuid", uuidstr)) {
10311       if (!uuid.parse(uuidstr.c_str())) {
10312         ss << "invalid uuid value '" << uuidstr << "'";
10313         err = -EINVAL;
10314         goto reply;
10315       }
10316       // we only care about the id if we also have the uuid, to
10317       // ensure the operation's idempotency.
10318       id = cmd_id;
10319     }
10320
10321     int32_t new_id = -1;
10322     err = prepare_command_osd_create(id, uuid, &new_id, ss);
10323     if (err < 0) {
10324       if (err == -EAGAIN) {
10325         wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10326         return true;
10327       }
10328       // a check has failed; reply to the user.
10329       goto reply;
10330
10331     } else if (err == EEXIST) {
10332       // this is an idempotent operation; we can go ahead and reply.
10333       if (f) {
10334         f->open_object_section("created_osd");
10335         f->dump_int("osdid", new_id);
10336         f->close_section();
10337         f->flush(rdata);
10338       } else {
10339         ss << new_id;
10340         rdata.append(ss);
10341       }
10342       err = 0;
10343       goto reply;
10344     }
10345
10346     do_osd_create(id, uuid, &new_id);
10347
10348     if (f) {
10349       f->open_object_section("created_osd");
10350       f->dump_int("osdid", new_id);
10351       f->close_section();
10352       f->flush(rdata);
10353     } else {
10354       ss << new_id;
10355       rdata.append(ss);
10356     }
10357     wait_for_finished_proposal(op,
10358         new Monitor::C_Command(mon, op, 0, rs, rdata,
10359                                get_last_committed() + 1));
10360     return true;
10361
10362   } else if (prefix == "osd blacklist clear") {
10363     pending_inc.new_blacklist.clear();
10364     std::list<std::pair<entity_addr_t,utime_t > > blacklist;
10365     osdmap.get_blacklist(&blacklist);
10366     for (const auto &entry : blacklist) {
10367       pending_inc.old_blacklist.push_back(entry.first);
10368     }
10369     ss << " removed all blacklist entries";
10370     getline(ss, rs);
10371     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10372                                               get_last_committed() + 1));
10373     return true;
10374   } else if (prefix == "osd blacklist") {
10375     string addrstr;
10376     cmd_getval(g_ceph_context, cmdmap, "addr", addrstr);
10377     entity_addr_t addr;
10378     if (!addr.parse(addrstr.c_str(), 0)) {
10379       ss << "unable to parse address " << addrstr;
10380       err = -EINVAL;
10381       goto reply;
10382     }
10383     else {
10384       string blacklistop;
10385       cmd_getval(g_ceph_context, cmdmap, "blacklistop", blacklistop);
10386       if (blacklistop == "add") {
10387         utime_t expires = ceph_clock_now();
10388         double d;
10389         // default one hour
10390         cmd_getval(g_ceph_context, cmdmap, "expire", d,
10391           g_conf->mon_osd_blacklist_default_expire);
10392         expires += d;
10393
10394         pending_inc.new_blacklist[addr] = expires;
10395
10396         {
10397           // cancel any pending un-blacklisting request too
10398           auto it = std::find(pending_inc.old_blacklist.begin(),
10399             pending_inc.old_blacklist.end(), addr);
10400           if (it != pending_inc.old_blacklist.end()) {
10401             pending_inc.old_blacklist.erase(it);
10402           }
10403         }
10404
10405         ss << "blacklisting " << addr << " until " << expires << " (" << d << " sec)";
10406         getline(ss, rs);
10407         wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10408                                                   get_last_committed() + 1));
10409         return true;
10410       } else if (blacklistop == "rm") {
10411         if (osdmap.is_blacklisted(addr) ||
10412             pending_inc.new_blacklist.count(addr)) {
10413           if (osdmap.is_blacklisted(addr))
10414             pending_inc.old_blacklist.push_back(addr);
10415           else
10416             pending_inc.new_blacklist.erase(addr);
10417           ss << "un-blacklisting " << addr;
10418           getline(ss, rs);
10419           wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10420                                                     get_last_committed() + 1));
10421           return true;
10422         }
10423         ss << addr << " isn't blacklisted";
10424         err = 0;
10425         goto reply;
10426       }
10427     }
10428   } else if (prefix == "osd pool mksnap") {
10429     string poolstr;
10430     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10431     int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
10432     if (pool < 0) {
10433       ss << "unrecognized pool '" << poolstr << "'";
10434       err = -ENOENT;
10435       goto reply;
10436     }
10437     string snapname;
10438     cmd_getval(g_ceph_context, cmdmap, "snap", snapname);
10439     const pg_pool_t *p = osdmap.get_pg_pool(pool);
10440     if (p->is_unmanaged_snaps_mode()) {
10441       ss << "pool " << poolstr << " is in unmanaged snaps mode";
10442       err = -EINVAL;
10443       goto reply;
10444     } else if (p->snap_exists(snapname.c_str())) {
10445       ss << "pool " << poolstr << " snap " << snapname << " already exists";
10446       err = 0;
10447       goto reply;
10448     } else if (p->is_tier()) {
10449       ss << "pool " << poolstr << " is a cache tier";
10450       err = -EINVAL;
10451       goto reply;
10452     }
10453     pg_pool_t *pp = 0;
10454     if (pending_inc.new_pools.count(pool))
10455       pp = &pending_inc.new_pools[pool];
10456     if (!pp) {
10457       pp = &pending_inc.new_pools[pool];
10458       *pp = *p;
10459     }
10460     if (pp->snap_exists(snapname.c_str())) {
10461       ss << "pool " << poolstr << " snap " << snapname << " already exists";
10462     } else {
10463       pp->add_snap(snapname.c_str(), ceph_clock_now());
10464       pp->set_snap_epoch(pending_inc.epoch);
10465       ss << "created pool " << poolstr << " snap " << snapname;
10466     }
10467     getline(ss, rs);
10468     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10469                                               get_last_committed() + 1));
10470     return true;
10471   } else if (prefix == "osd pool rmsnap") {
10472     string poolstr;
10473     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10474     int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
10475     if (pool < 0) {
10476       ss << "unrecognized pool '" << poolstr << "'";
10477       err = -ENOENT;
10478       goto reply;
10479     }
10480     string snapname;
10481     cmd_getval(g_ceph_context, cmdmap, "snap", snapname);
10482     const pg_pool_t *p = osdmap.get_pg_pool(pool);
10483     if (p->is_unmanaged_snaps_mode()) {
10484       ss << "pool " << poolstr << " is in unmanaged snaps mode";
10485       err = -EINVAL;
10486       goto reply;
10487     } else if (!p->snap_exists(snapname.c_str())) {
10488       ss << "pool " << poolstr << " snap " << snapname << " does not exist";
10489       err = 0;
10490       goto reply;
10491     }
10492     pg_pool_t *pp = 0;
10493     if (pending_inc.new_pools.count(pool))
10494       pp = &pending_inc.new_pools[pool];
10495     if (!pp) {
10496       pp = &pending_inc.new_pools[pool];
10497       *pp = *p;
10498     }
10499     snapid_t sn = pp->snap_exists(snapname.c_str());
10500     if (sn) {
10501       pp->remove_snap(sn);
10502       pp->set_snap_epoch(pending_inc.epoch);
10503       ss << "removed pool " << poolstr << " snap " << snapname;
10504     } else {
10505       ss << "already removed pool " << poolstr << " snap " << snapname;
10506     }
10507     getline(ss, rs);
10508     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10509                                               get_last_committed() + 1));
10510     return true;
10511   } else if (prefix == "osd pool create") {
10512     int64_t  pg_num;
10513     int64_t pgp_num;
10514     cmd_getval(g_ceph_context, cmdmap, "pg_num", pg_num, int64_t(0));
10515     cmd_getval(g_ceph_context, cmdmap, "pgp_num", pgp_num, pg_num);
10516
10517     string pool_type_str;
10518     cmd_getval(g_ceph_context, cmdmap, "pool_type", pool_type_str);
10519     if (pool_type_str.empty())
10520       pool_type_str = g_conf->osd_pool_default_type;
10521
10522     string poolstr;
10523     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10524     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10525     if (pool_id >= 0) {
10526       const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10527       if (pool_type_str != p->get_type_name()) {
10528         ss << "pool '" << poolstr << "' cannot change to type " << pool_type_str;
10529         err = -EINVAL;
10530       } else {
10531         ss << "pool '" << poolstr << "' already exists";
10532         err = 0;
10533       }
10534       goto reply;
10535     }
10536
10537     int pool_type;
10538     if (pool_type_str == "replicated") {
10539       pool_type = pg_pool_t::TYPE_REPLICATED;
10540     } else if (pool_type_str == "erasure") {
10541       err = check_cluster_features(CEPH_FEATURE_CRUSH_V2 |
10542                                    CEPH_FEATURE_OSD_ERASURE_CODES,
10543                                    ss);
10544       if (err == -EAGAIN)
10545         goto wait;
10546       if (err)
10547         goto reply;
10548       pool_type = pg_pool_t::TYPE_ERASURE;
10549     } else {
10550       ss << "unknown pool type '" << pool_type_str << "'";
10551       err = -EINVAL;
10552       goto reply;
10553     }
10554
10555     bool implicit_rule_creation = false;
10556     string rule_name;
10557     cmd_getval(g_ceph_context, cmdmap, "rule", rule_name);
10558     string erasure_code_profile;
10559     cmd_getval(g_ceph_context, cmdmap, "erasure_code_profile", erasure_code_profile);
10560
10561     if (pool_type == pg_pool_t::TYPE_ERASURE) {
10562       if (erasure_code_profile == "")
10563         erasure_code_profile = "default";
10564       //handle the erasure code profile
10565       if (erasure_code_profile == "default") {
10566         if (!osdmap.has_erasure_code_profile(erasure_code_profile)) {
10567           if (pending_inc.has_erasure_code_profile(erasure_code_profile)) {
10568             dout(20) << "erasure code profile " << erasure_code_profile << " already pending" << dendl;
10569             goto wait;
10570           }
10571
10572           map<string,string> profile_map;
10573           err = osdmap.get_erasure_code_profile_default(g_ceph_context,
10574                                                       profile_map,
10575                                                       &ss);
10576           if (err)
10577             goto reply;
10578           dout(20) << "erasure code profile " << erasure_code_profile << " set" << dendl;
10579           pending_inc.set_erasure_code_profile(erasure_code_profile, profile_map);
10580           goto wait;
10581         }
10582       }
10583       if (rule_name == "") {
10584         implicit_rule_creation = true;
10585         if (erasure_code_profile == "default") {
10586           rule_name = "erasure-code";
10587         } else {
10588           dout(1) << "implicitly use rule named after the pool: "
10589                 << poolstr << dendl;
10590           rule_name = poolstr;
10591         }
10592       }
10593     } else {
10594       //NOTE:for replicated pool,cmd_map will put rule_name to erasure_code_profile field
10595       rule_name = erasure_code_profile;
10596     }
10597
10598     if (!implicit_rule_creation && rule_name != "") {
10599       int rule;
10600       err = get_crush_rule(rule_name, &rule, &ss);
10601       if (err == -EAGAIN) {
10602         wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10603         return true;
10604       }
10605       if (err)
10606         goto reply;
10607     }
10608
10609     int64_t expected_num_objects;
10610     cmd_getval(g_ceph_context, cmdmap, "expected_num_objects", expected_num_objects, int64_t(0));
10611     if (expected_num_objects < 0) {
10612       ss << "'expected_num_objects' must be non-negative";
10613       err = -EINVAL;
10614       goto reply;
10615     }
10616
10617     int64_t fast_read_param;
10618     cmd_getval(g_ceph_context, cmdmap, "fast_read", fast_read_param, int64_t(-1));
10619     FastReadType fast_read = FAST_READ_DEFAULT;
10620     if (fast_read_param == 0)
10621       fast_read = FAST_READ_OFF;
10622     else if (fast_read_param > 0)
10623       fast_read = FAST_READ_ON;
10624     
10625     err = prepare_new_pool(poolstr, 0, // auid=0 for admin created pool
10626                            -1, // default crush rule
10627                            rule_name,
10628                            pg_num, pgp_num,
10629                            erasure_code_profile, pool_type,
10630                            (uint64_t)expected_num_objects,
10631                            fast_read,
10632                            &ss);
10633     if (err < 0) {
10634       switch(err) {
10635       case -EEXIST:
10636         ss << "pool '" << poolstr << "' already exists";
10637         break;
10638       case -EAGAIN:
10639         wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10640         return true;
10641       case -ERANGE:
10642         goto reply;
10643       default:
10644         goto reply;
10645         break;
10646       }
10647     } else {
10648       ss << "pool '" << poolstr << "' created";
10649     }
10650     getline(ss, rs);
10651     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10652                                               get_last_committed() + 1));
10653     return true;
10654
10655   } else if (prefix == "osd pool delete" ||
10656              prefix == "osd pool rm") {
10657     // osd pool delete/rm <poolname> <poolname again> --yes-i-really-really-mean-it
10658     string poolstr, poolstr2, sure;
10659     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10660     cmd_getval(g_ceph_context, cmdmap, "pool2", poolstr2);
10661     cmd_getval(g_ceph_context, cmdmap, "sure", sure);
10662     int64_t pool = osdmap.lookup_pg_pool_name(poolstr.c_str());
10663     if (pool < 0) {
10664       ss << "pool '" << poolstr << "' does not exist";
10665       err = 0;
10666       goto reply;
10667     }
10668
10669     bool force_no_fake = sure == "--yes-i-really-really-mean-it-not-faking";
10670     if (poolstr2 != poolstr ||
10671         (sure != "--yes-i-really-really-mean-it" && !force_no_fake)) {
10672       ss << "WARNING: this will *PERMANENTLY DESTROY* all data stored in pool " << poolstr
10673          << ".  If you are *ABSOLUTELY CERTAIN* that is what you want, pass the pool name *twice*, "
10674          << "followed by --yes-i-really-really-mean-it.";
10675       err = -EPERM;
10676       goto reply;
10677     }
10678     err = _prepare_remove_pool(pool, &ss, force_no_fake);
10679     if (err == -EAGAIN) {
10680       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10681       return true;
10682     }
10683     if (err < 0)
10684       goto reply;
10685     goto update;
10686   } else if (prefix == "osd pool rename") {
10687     string srcpoolstr, destpoolstr;
10688     cmd_getval(g_ceph_context, cmdmap, "srcpool", srcpoolstr);
10689     cmd_getval(g_ceph_context, cmdmap, "destpool", destpoolstr);
10690     int64_t pool_src = osdmap.lookup_pg_pool_name(srcpoolstr.c_str());
10691     int64_t pool_dst = osdmap.lookup_pg_pool_name(destpoolstr.c_str());
10692
10693     if (pool_src < 0) {
10694       if (pool_dst >= 0) {
10695         // src pool doesn't exist, dst pool does exist: to ensure idempotency
10696         // of operations, assume this rename succeeded, as it is not changing
10697         // the current state.  Make sure we output something understandable
10698         // for whoever is issuing the command, if they are paying attention,
10699         // in case it was not intentional; or to avoid a "wtf?" and a bug
10700         // report in case it was intentional, while expecting a failure.
10701         ss << "pool '" << srcpoolstr << "' does not exist; pool '"
10702           << destpoolstr << "' does -- assuming successful rename";
10703         err = 0;
10704       } else {
10705         ss << "unrecognized pool '" << srcpoolstr << "'";
10706         err = -ENOENT;
10707       }
10708       goto reply;
10709     } else if (pool_dst >= 0) {
10710       // source pool exists and so does the destination pool
10711       ss << "pool '" << destpoolstr << "' already exists";
10712       err = -EEXIST;
10713       goto reply;
10714     }
10715
10716     int ret = _prepare_rename_pool(pool_src, destpoolstr);
10717     if (ret == 0) {
10718       ss << "pool '" << srcpoolstr << "' renamed to '" << destpoolstr << "'";
10719     } else {
10720       ss << "failed to rename pool '" << srcpoolstr << "' to '" << destpoolstr << "': "
10721         << cpp_strerror(ret);
10722     }
10723     getline(ss, rs);
10724     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, ret, rs,
10725                                               get_last_committed() + 1));
10726     return true;
10727
10728   } else if (prefix == "osd pool set") {
10729     err = prepare_command_pool_set(cmdmap, ss);
10730     if (err == -EAGAIN)
10731       goto wait;
10732     if (err < 0)
10733       goto reply;
10734
10735     getline(ss, rs);
10736     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
10737                                                    get_last_committed() + 1));
10738     return true;
10739   } else if (prefix == "osd tier add") {
10740     err = check_cluster_features(CEPH_FEATURE_OSD_CACHEPOOL, ss);
10741     if (err == -EAGAIN)
10742       goto wait;
10743     if (err)
10744       goto reply;
10745     string poolstr;
10746     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10747     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10748     if (pool_id < 0) {
10749       ss << "unrecognized pool '" << poolstr << "'";
10750       err = -ENOENT;
10751       goto reply;
10752     }
10753     string tierpoolstr;
10754     cmd_getval(g_ceph_context, cmdmap, "tierpool", tierpoolstr);
10755     int64_t tierpool_id = osdmap.lookup_pg_pool_name(tierpoolstr);
10756     if (tierpool_id < 0) {
10757       ss << "unrecognized pool '" << tierpoolstr << "'";
10758       err = -ENOENT;
10759       goto reply;
10760     }
10761     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10762     assert(p);
10763     const pg_pool_t *tp = osdmap.get_pg_pool(tierpool_id);
10764     assert(tp);
10765
10766     if (!_check_become_tier(tierpool_id, tp, pool_id, p, &err, &ss)) {
10767       goto reply;
10768     }
10769
10770     // make sure new tier is empty
10771     string force_nonempty;
10772     cmd_getval(g_ceph_context, cmdmap, "force_nonempty", force_nonempty);
10773     const pool_stat_t *pstats = mon->pgservice->get_pool_stat(tierpool_id);
10774     if (pstats && pstats->stats.sum.num_objects != 0 &&
10775         force_nonempty != "--force-nonempty") {
10776       ss << "tier pool '" << tierpoolstr << "' is not empty; --force-nonempty to force";
10777       err = -ENOTEMPTY;
10778       goto reply;
10779     }
10780     if (tp->ec_pool()) {
10781       ss << "tier pool '" << tierpoolstr
10782          << "' is an ec pool, which cannot be a tier";
10783       err = -ENOTSUP;
10784       goto reply;
10785     }
10786     if ((!tp->removed_snaps.empty() || !tp->snaps.empty()) &&
10787         ((force_nonempty != "--force-nonempty") ||
10788          (!g_conf->mon_debug_unsafe_allow_tier_with_nonempty_snaps))) {
10789       ss << "tier pool '" << tierpoolstr << "' has snapshot state; it cannot be added as a tier without breaking the pool";
10790       err = -ENOTEMPTY;
10791       goto reply;
10792     }
10793     // go
10794     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
10795     pg_pool_t *ntp = pending_inc.get_new_pool(tierpool_id, tp);
10796     if (np->tiers.count(tierpool_id) || ntp->is_tier()) {
10797       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10798       return true;
10799     }
10800     np->tiers.insert(tierpool_id);
10801     np->set_snap_epoch(pending_inc.epoch); // tier will update to our snap info
10802     ntp->tier_of = pool_id;
10803     ss << "pool '" << tierpoolstr << "' is now (or already was) a tier of '" << poolstr << "'";
10804     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
10805                                               get_last_committed() + 1));
10806     return true;
10807   } else if (prefix == "osd tier remove" ||
10808              prefix == "osd tier rm") {
10809     string poolstr;
10810     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10811     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10812     if (pool_id < 0) {
10813       ss << "unrecognized pool '" << poolstr << "'";
10814       err = -ENOENT;
10815       goto reply;
10816     }
10817     string tierpoolstr;
10818     cmd_getval(g_ceph_context, cmdmap, "tierpool", tierpoolstr);
10819     int64_t tierpool_id = osdmap.lookup_pg_pool_name(tierpoolstr);
10820     if (tierpool_id < 0) {
10821       ss << "unrecognized pool '" << tierpoolstr << "'";
10822       err = -ENOENT;
10823       goto reply;
10824     }
10825     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10826     assert(p);
10827     const pg_pool_t *tp = osdmap.get_pg_pool(tierpool_id);
10828     assert(tp);
10829
10830     if (!_check_remove_tier(pool_id, p, tp, &err, &ss)) {
10831       goto reply;
10832     }
10833
10834     if (p->tiers.count(tierpool_id) == 0) {
10835       ss << "pool '" << tierpoolstr << "' is now (or already was) not a tier of '" << poolstr << "'";
10836       err = 0;
10837       goto reply;
10838     }
10839     if (tp->tier_of != pool_id) {
10840       ss << "tier pool '" << tierpoolstr << "' is a tier of '"
10841          << osdmap.get_pool_name(tp->tier_of) << "': "
10842          // be scary about it; this is an inconsistency and bells must go off
10843          << "THIS SHOULD NOT HAVE HAPPENED AT ALL";
10844       err = -EINVAL;
10845       goto reply;
10846     }
10847     if (p->read_tier == tierpool_id) {
10848       ss << "tier pool '" << tierpoolstr << "' is the overlay for '" << poolstr << "'; please remove-overlay first";
10849       err = -EBUSY;
10850       goto reply;
10851     }
10852     // go
10853     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
10854     pg_pool_t *ntp = pending_inc.get_new_pool(tierpool_id, tp);
10855     if (np->tiers.count(tierpool_id) == 0 ||
10856         ntp->tier_of != pool_id ||
10857         np->read_tier == tierpool_id) {
10858       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
10859       return true;
10860     }
10861     np->tiers.erase(tierpool_id);
10862     ntp->clear_tier();
10863     ss << "pool '" << tierpoolstr << "' is now (or already was) not a tier of '" << poolstr << "'";
10864     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
10865                                               get_last_committed() + 1));
10866     return true;
10867   } else if (prefix == "osd tier set-overlay") {
10868     err = check_cluster_features(CEPH_FEATURE_OSD_CACHEPOOL, ss);
10869     if (err == -EAGAIN)
10870       goto wait;
10871     if (err)
10872       goto reply;
10873     string poolstr;
10874     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10875     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10876     if (pool_id < 0) {
10877       ss << "unrecognized pool '" << poolstr << "'";
10878       err = -ENOENT;
10879       goto reply;
10880     }
10881     string overlaypoolstr;
10882     cmd_getval(g_ceph_context, cmdmap, "overlaypool", overlaypoolstr);
10883     int64_t overlaypool_id = osdmap.lookup_pg_pool_name(overlaypoolstr);
10884     if (overlaypool_id < 0) {
10885       ss << "unrecognized pool '" << overlaypoolstr << "'";
10886       err = -ENOENT;
10887       goto reply;
10888     }
10889     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10890     assert(p);
10891     const pg_pool_t *overlay_p = osdmap.get_pg_pool(overlaypool_id);
10892     assert(overlay_p);
10893     if (p->tiers.count(overlaypool_id) == 0) {
10894       ss << "tier pool '" << overlaypoolstr << "' is not a tier of '" << poolstr << "'";
10895       err = -EINVAL;
10896       goto reply;
10897     }
10898     if (p->read_tier == overlaypool_id) {
10899       err = 0;
10900       ss << "overlay for '" << poolstr << "' is now (or already was) '" << overlaypoolstr << "'";
10901       goto reply;
10902     }
10903     if (p->has_read_tier()) {
10904       ss << "pool '" << poolstr << "' has overlay '"
10905          << osdmap.get_pool_name(p->read_tier)
10906          << "'; please remove-overlay first";
10907       err = -EINVAL;
10908       goto reply;
10909     }
10910
10911     // go
10912     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
10913     np->read_tier = overlaypool_id;
10914     np->write_tier = overlaypool_id;
10915     np->set_last_force_op_resend(pending_inc.epoch);
10916     pg_pool_t *noverlay_p = pending_inc.get_new_pool(overlaypool_id, overlay_p);
10917     noverlay_p->set_last_force_op_resend(pending_inc.epoch);
10918     ss << "overlay for '" << poolstr << "' is now (or already was) '" << overlaypoolstr << "'";
10919     if (overlay_p->cache_mode == pg_pool_t::CACHEMODE_NONE)
10920       ss <<" (WARNING: overlay pool cache_mode is still NONE)";
10921     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
10922                                               get_last_committed() + 1));
10923     return true;
10924   } else if (prefix == "osd tier remove-overlay" ||
10925              prefix == "osd tier rm-overlay") {
10926     string poolstr;
10927     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10928     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10929     if (pool_id < 0) {
10930       ss << "unrecognized pool '" << poolstr << "'";
10931       err = -ENOENT;
10932       goto reply;
10933     }
10934     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10935     assert(p);
10936     if (!p->has_read_tier()) {
10937       err = 0;
10938       ss << "there is now (or already was) no overlay for '" << poolstr << "'";
10939       goto reply;
10940     }
10941
10942     if (!_check_remove_tier(pool_id, p, NULL, &err, &ss)) {
10943       goto reply;
10944     }
10945
10946     // go
10947     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
10948     if (np->has_read_tier()) {
10949       const pg_pool_t *op = osdmap.get_pg_pool(np->read_tier);
10950       pg_pool_t *nop = pending_inc.get_new_pool(np->read_tier,op);
10951       nop->set_last_force_op_resend(pending_inc.epoch);
10952     }
10953     if (np->has_write_tier()) {
10954       const pg_pool_t *op = osdmap.get_pg_pool(np->write_tier);
10955       pg_pool_t *nop = pending_inc.get_new_pool(np->write_tier, op);
10956       nop->set_last_force_op_resend(pending_inc.epoch);
10957     }
10958     np->clear_read_tier();
10959     np->clear_write_tier();
10960     np->set_last_force_op_resend(pending_inc.epoch);
10961     ss << "there is now (or already was) no overlay for '" << poolstr << "'";
10962     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
10963                                               get_last_committed() + 1));
10964     return true;
10965   } else if (prefix == "osd tier cache-mode") {
10966     err = check_cluster_features(CEPH_FEATURE_OSD_CACHEPOOL, ss);
10967     if (err == -EAGAIN)
10968       goto wait;
10969     if (err)
10970       goto reply;
10971     string poolstr;
10972     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
10973     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
10974     if (pool_id < 0) {
10975       ss << "unrecognized pool '" << poolstr << "'";
10976       err = -ENOENT;
10977       goto reply;
10978     }
10979     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
10980     assert(p);
10981     if (!p->is_tier()) {
10982       ss << "pool '" << poolstr << "' is not a tier";
10983       err = -EINVAL;
10984       goto reply;
10985     }
10986     string modestr;
10987     cmd_getval(g_ceph_context, cmdmap, "mode", modestr);
10988     pg_pool_t::cache_mode_t mode = pg_pool_t::get_cache_mode_from_str(modestr);
10989     if (mode < 0) {
10990       ss << "'" << modestr << "' is not a valid cache mode";
10991       err = -EINVAL;
10992       goto reply;
10993     }
10994
10995     string sure;
10996     cmd_getval(g_ceph_context, cmdmap, "sure", sure);
10997     if ((mode != pg_pool_t::CACHEMODE_WRITEBACK &&
10998          mode != pg_pool_t::CACHEMODE_NONE &&
10999          mode != pg_pool_t::CACHEMODE_PROXY &&
11000          mode != pg_pool_t::CACHEMODE_READPROXY) &&
11001         sure != "--yes-i-really-mean-it") {
11002       ss << "'" << modestr << "' is not a well-supported cache mode and may "
11003          << "corrupt your data.  pass --yes-i-really-mean-it to force.";
11004       err = -EPERM;
11005       goto reply;
11006     }
11007
11008     // pool already has this cache-mode set and there are no pending changes
11009     if (p->cache_mode == mode &&
11010         (pending_inc.new_pools.count(pool_id) == 0 ||
11011          pending_inc.new_pools[pool_id].cache_mode == p->cache_mode)) {
11012       ss << "set cache-mode for pool '" << poolstr << "'"
11013          << " to " << pg_pool_t::get_cache_mode_name(mode);
11014       err = 0;
11015       goto reply;
11016     }
11017
11018     /* Mode description:
11019      *
11020      *  none:       No cache-mode defined
11021      *  forward:    Forward all reads and writes to base pool
11022      *  writeback:  Cache writes, promote reads from base pool
11023      *  readonly:   Forward writes to base pool
11024      *  readforward: Writes are in writeback mode, Reads are in forward mode
11025      *  proxy:       Proxy all reads and writes to base pool
11026      *  readproxy:   Writes are in writeback mode, Reads are in proxy mode
11027      *
11028      * Hence, these are the allowed transitions:
11029      *
11030      *  none -> any
11031      *  forward -> proxy || readforward || readproxy || writeback || any IF num_objects_dirty == 0
11032      *  proxy -> forward || readforward || readproxy || writeback || any IF num_objects_dirty == 0
11033      *  readforward -> forward || proxy || readproxy || writeback || any IF num_objects_dirty == 0
11034      *  readproxy -> forward || proxy || readforward || writeback || any IF num_objects_dirty == 0
11035      *  writeback -> readforward || readproxy || forward || proxy
11036      *  readonly -> any
11037      */
11038
11039     // We check if the transition is valid against the current pool mode, as
11040     // it is the only committed state thus far.  We will blantly squash
11041     // whatever mode is on the pending state.
11042
11043     if (p->cache_mode == pg_pool_t::CACHEMODE_WRITEBACK &&
11044         (mode != pg_pool_t::CACHEMODE_FORWARD &&
11045           mode != pg_pool_t::CACHEMODE_PROXY &&
11046           mode != pg_pool_t::CACHEMODE_READFORWARD &&
11047           mode != pg_pool_t::CACHEMODE_READPROXY)) {
11048       ss << "unable to set cache-mode '" << pg_pool_t::get_cache_mode_name(mode)
11049          << "' on a '" << pg_pool_t::get_cache_mode_name(p->cache_mode)
11050          << "' pool; only '"
11051          << pg_pool_t::get_cache_mode_name(pg_pool_t::CACHEMODE_FORWARD)
11052          << "','"
11053          << pg_pool_t::get_cache_mode_name(pg_pool_t::CACHEMODE_PROXY)
11054          << "','"
11055          << pg_pool_t::get_cache_mode_name(pg_pool_t::CACHEMODE_READFORWARD)
11056          << "','"
11057          << pg_pool_t::get_cache_mode_name(pg_pool_t::CACHEMODE_READPROXY)
11058         << "' allowed.";
11059       err = -EINVAL;
11060       goto reply;
11061     }
11062     if ((p->cache_mode == pg_pool_t::CACHEMODE_READFORWARD &&
11063         (mode != pg_pool_t::CACHEMODE_WRITEBACK &&
11064           mode != pg_pool_t::CACHEMODE_FORWARD &&
11065           mode != pg_pool_t::CACHEMODE_PROXY &&
11066           mode != pg_pool_t::CACHEMODE_READPROXY)) ||
11067
11068         (p->cache_mode == pg_pool_t::CACHEMODE_READPROXY &&
11069         (mode != pg_pool_t::CACHEMODE_WRITEBACK &&
11070           mode != pg_pool_t::CACHEMODE_FORWARD &&
11071           mode != pg_pool_t::CACHEMODE_READFORWARD &&
11072           mode != pg_pool_t::CACHEMODE_PROXY)) ||
11073
11074         (p->cache_mode == pg_pool_t::CACHEMODE_PROXY &&
11075         (mode != pg_pool_t::CACHEMODE_WRITEBACK &&
11076           mode != pg_pool_t::CACHEMODE_FORWARD &&
11077           mode != pg_pool_t::CACHEMODE_READFORWARD &&
11078           mode != pg_pool_t::CACHEMODE_READPROXY)) ||
11079
11080         (p->cache_mode == pg_pool_t::CACHEMODE_FORWARD &&
11081         (mode != pg_pool_t::CACHEMODE_WRITEBACK &&
11082           mode != pg_pool_t::CACHEMODE_READFORWARD &&
11083           mode != pg_pool_t::CACHEMODE_PROXY &&
11084           mode != pg_pool_t::CACHEMODE_READPROXY))) {
11085
11086       const pool_stat_t* pstats =
11087         mon->pgservice->get_pool_stat(pool_id);
11088
11089       if (pstats && pstats->stats.sum.num_objects_dirty > 0) {
11090         ss << "unable to set cache-mode '"
11091            << pg_pool_t::get_cache_mode_name(mode) << "' on pool '" << poolstr
11092            << "': dirty objects found";
11093         err = -EBUSY;
11094         goto reply;
11095       }
11096     }
11097     // go
11098     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
11099     np->cache_mode = mode;
11100     // set this both when moving to and from cache_mode NONE.  this is to
11101     // capture legacy pools that were set up before this flag existed.
11102     np->flags |= pg_pool_t::FLAG_INCOMPLETE_CLONES;
11103     ss << "set cache-mode for pool '" << poolstr
11104         << "' to " << pg_pool_t::get_cache_mode_name(mode);
11105     if (mode == pg_pool_t::CACHEMODE_NONE) {
11106       const pg_pool_t *base_pool = osdmap.get_pg_pool(np->tier_of);
11107       assert(base_pool);
11108       if (base_pool->read_tier == pool_id ||
11109           base_pool->write_tier == pool_id)
11110         ss <<" (WARNING: pool is still configured as read or write tier)";
11111     }
11112     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
11113                                               get_last_committed() + 1));
11114     return true;
11115   } else if (prefix == "osd tier add-cache") {
11116     err = check_cluster_features(CEPH_FEATURE_OSD_CACHEPOOL, ss);
11117     if (err == -EAGAIN)
11118       goto wait;
11119     if (err)
11120       goto reply;
11121     string poolstr;
11122     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
11123     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
11124     if (pool_id < 0) {
11125       ss << "unrecognized pool '" << poolstr << "'";
11126       err = -ENOENT;
11127       goto reply;
11128     }
11129     string tierpoolstr;
11130     cmd_getval(g_ceph_context, cmdmap, "tierpool", tierpoolstr);
11131     int64_t tierpool_id = osdmap.lookup_pg_pool_name(tierpoolstr);
11132     if (tierpool_id < 0) {
11133       ss << "unrecognized pool '" << tierpoolstr << "'";
11134       err = -ENOENT;
11135       goto reply;
11136     }
11137     const pg_pool_t *p = osdmap.get_pg_pool(pool_id);
11138     assert(p);
11139     const pg_pool_t *tp = osdmap.get_pg_pool(tierpool_id);
11140     assert(tp);
11141
11142     if (!_check_become_tier(tierpool_id, tp, pool_id, p, &err, &ss)) {
11143       goto reply;
11144     }
11145
11146     int64_t size = 0;
11147     if (!cmd_getval(g_ceph_context, cmdmap, "size", size)) {
11148       ss << "unable to parse 'size' value '"
11149          << cmd_vartype_stringify(cmdmap["size"]) << "'";
11150       err = -EINVAL;
11151       goto reply;
11152     }
11153     // make sure new tier is empty
11154     const pool_stat_t *pstats =
11155       mon->pgservice->get_pool_stat(tierpool_id);
11156     if (pstats && pstats->stats.sum.num_objects != 0) {
11157       ss << "tier pool '" << tierpoolstr << "' is not empty";
11158       err = -ENOTEMPTY;
11159       goto reply;
11160     }
11161     string modestr = g_conf->osd_tier_default_cache_mode;
11162     pg_pool_t::cache_mode_t mode = pg_pool_t::get_cache_mode_from_str(modestr);
11163     if (mode < 0) {
11164       ss << "osd tier cache default mode '" << modestr << "' is not a valid cache mode";
11165       err = -EINVAL;
11166       goto reply;
11167     }
11168     HitSet::Params hsp;
11169     if (g_conf->osd_tier_default_cache_hit_set_type == "bloom") {
11170       BloomHitSet::Params *bsp = new BloomHitSet::Params;
11171       bsp->set_fpp(g_conf->osd_pool_default_hit_set_bloom_fpp);
11172       hsp = HitSet::Params(bsp);
11173     } else if (g_conf->osd_tier_default_cache_hit_set_type == "explicit_hash") {
11174       hsp = HitSet::Params(new ExplicitHashHitSet::Params);
11175     }
11176     else if (g_conf->osd_tier_default_cache_hit_set_type == "explicit_object") {
11177       hsp = HitSet::Params(new ExplicitObjectHitSet::Params);
11178     } else {
11179       ss << "osd tier cache default hit set type '" <<
11180         g_conf->osd_tier_default_cache_hit_set_type << "' is not a known type";
11181       err = -EINVAL;
11182       goto reply;
11183     }
11184     // go
11185     pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
11186     pg_pool_t *ntp = pending_inc.get_new_pool(tierpool_id, tp);
11187     if (np->tiers.count(tierpool_id) || ntp->is_tier()) {
11188       wait_for_finished_proposal(op, new C_RetryMessage(this, op));
11189       return true;
11190     }
11191     np->tiers.insert(tierpool_id);
11192     np->read_tier = np->write_tier = tierpool_id;
11193     np->set_snap_epoch(pending_inc.epoch); // tier will update to our snap info
11194     np->set_last_force_op_resend(pending_inc.epoch);
11195     ntp->set_last_force_op_resend(pending_inc.epoch);
11196     ntp->tier_of = pool_id;
11197     ntp->cache_mode = mode;
11198     ntp->hit_set_count = g_conf->osd_tier_default_cache_hit_set_count;
11199     ntp->hit_set_period = g_conf->osd_tier_default_cache_hit_set_period;
11200     ntp->min_read_recency_for_promote = g_conf->osd_tier_default_cache_min_read_recency_for_promote;
11201     ntp->min_write_recency_for_promote = g_conf->osd_tier_default_cache_min_write_recency_for_promote;
11202     ntp->hit_set_grade_decay_rate = g_conf->osd_tier_default_cache_hit_set_grade_decay_rate;
11203     ntp->hit_set_search_last_n = g_conf->osd_tier_default_cache_hit_set_search_last_n;
11204     ntp->hit_set_params = hsp;
11205     ntp->target_max_bytes = size;
11206     ss << "pool '" << tierpoolstr << "' is now (or already was) a cache tier of '" << poolstr << "'";
11207     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, ss.str(),
11208                                               get_last_committed() + 1));
11209     return true;
11210   } else if (prefix == "osd pool set-quota") {
11211     string poolstr;
11212     cmd_getval(g_ceph_context, cmdmap, "pool", poolstr);
11213     int64_t pool_id = osdmap.lookup_pg_pool_name(poolstr);
11214     if (pool_id < 0) {
11215       ss << "unrecognized pool '" << poolstr << "'";
11216       err = -ENOENT;
11217       goto reply;
11218     }
11219
11220     string field;
11221     cmd_getval(g_ceph_context, cmdmap, "field", field);
11222     if (field != "max_objects" && field != "max_bytes") {
11223       ss << "unrecognized field '" << field << "'; should be 'max_bytes' or 'max_objects'";
11224       err = -EINVAL;
11225       goto reply;
11226     }
11227
11228     // val could contain unit designations, so we treat as a string
11229     string val;
11230     cmd_getval(g_ceph_context, cmdmap, "val", val);
11231     stringstream tss;
11232     int64_t value = unit_to_bytesize(val, &tss);
11233     if (value < 0) {
11234       ss << "error parsing value '" << value << "': " << tss.str();
11235       err = value;
11236       goto reply;
11237     }
11238
11239     pg_pool_t *pi = pending_inc.get_new_pool(pool_id, osdmap.get_pg_pool(pool_id));
11240     if (field == "max_objects") {
11241       pi->quota_max_objects = value;
11242     } else if (field == "max_bytes") {
11243       pi->quota_max_bytes = value;
11244     } else {
11245       assert(0 == "unrecognized option");
11246     }
11247     ss << "set-quota " << field << " = " << value << " for pool " << poolstr;
11248     rs = ss.str();
11249     wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
11250                                               get_last_committed() + 1));
11251     return true;
11252   } else if (prefix == "osd pool application enable" ||
11253              prefix == "osd pool application disable" ||
11254              prefix == "osd pool application set" ||
11255              prefix == "osd pool application rm") {
11256     err = prepare_command_pool_application(prefix, cmdmap, ss);
11257     if (err == -EAGAIN)
11258       goto wait;
11259     if (err < 0)
11260       goto reply;
11261
11262     getline(ss, rs);
11263     wait_for_finished_proposal(
11264       op, new Monitor::C_Command(mon, op, 0, rs, get_last_committed() + 1));
11265     return true;
11266   } else if (prefix == "osd reweight-by-pg" ||
11267              prefix == "osd reweight-by-utilization" ||
11268              prefix == "osd test-reweight-by-pg" ||
11269              prefix == "osd test-reweight-by-utilization") {
11270     bool by_pg =
11271       prefix == "osd reweight-by-pg" || prefix == "osd test-reweight-by-pg";
11272     bool dry_run =
11273       prefix == "osd test-reweight-by-pg" ||
11274       prefix == "osd test-reweight-by-utilization";
11275     int64_t oload;
11276     cmd_getval(g_ceph_context, cmdmap, "oload", oload, int64_t(120));
11277     set<int64_t> pools;
11278     vector<string> poolnamevec;
11279     cmd_getval(g_ceph_context, cmdmap, "pools", poolnamevec);
11280     for (unsigned j = 0; j < poolnamevec.size(); j++) {
11281       int64_t pool = osdmap.lookup_pg_pool_name(poolnamevec[j]);
11282       if (pool < 0) {
11283         ss << "pool '" << poolnamevec[j] << "' does not exist";
11284         err = -ENOENT;
11285         goto reply;
11286       }
11287       pools.insert(pool);
11288     }
11289     double max_change = g_conf->mon_reweight_max_change;
11290     cmd_getval(g_ceph_context, cmdmap, "max_change", max_change);
11291     if (max_change <= 0.0) {
11292       ss << "max_change " << max_change << " must be positive";
11293       err = -EINVAL;
11294       goto reply;
11295     }
11296     int64_t max_osds = g_conf->mon_reweight_max_osds;
11297     cmd_getval(g_ceph_context, cmdmap, "max_osds", max_osds);
11298     if (max_osds <= 0) {
11299       ss << "max_osds " << max_osds << " must be positive";
11300       err = -EINVAL;
11301       goto reply;
11302     }
11303     string no_increasing;
11304     cmd_getval(g_ceph_context, cmdmap, "no_increasing", no_increasing);
11305     string out_str;
11306     mempool::osdmap::map<int32_t, uint32_t> new_weights;
11307     err = mon->pgservice->reweight_by_utilization(osdmap,
11308                                              oload,
11309                                              max_change,
11310                                              max_osds,
11311                                              by_pg,
11312                                              pools.empty() ? NULL : &pools,
11313                                              no_increasing == "--no-increasing",
11314                                              &new_weights,
11315                                              &ss, &out_str, f.get());
11316     if (err >= 0) {
11317       dout(10) << "reweight::by_utilization: finished with " << out_str << dendl;
11318     }
11319     if (f)
11320       f->flush(rdata);
11321     else
11322       rdata.append(out_str);
11323     if (err < 0) {
11324       ss << "FAILED reweight-by-pg";
11325     } else if (err == 0 || dry_run) {
11326       ss << "no change";
11327     } else {
11328       ss << "SUCCESSFUL reweight-by-pg";
11329       pending_inc.new_weight = std::move(new_weights);
11330       wait_for_finished_proposal(
11331         op,
11332         new Monitor::C_Command(mon, op, 0, rs, rdata, get_last_committed() + 1));
11333       return true;
11334     }
11335   } else if (prefix == "osd force-create-pg") {
11336     pg_t pgid;
11337     string pgidstr;
11338     cmd_getval(g_ceph_context, cmdmap, "pgid", pgidstr);
11339     if (!pgid.parse(pgidstr.c_str())) {
11340       ss << "invalid pgid '" << pgidstr << "'";
11341       err = -EINVAL;
11342       goto reply;
11343     }
11344     bool creating_now;
11345     {
11346       std::lock_guard<std::mutex> l(creating_pgs_lock);
11347       auto emplaced = creating_pgs.pgs.emplace(pgid,
11348                                                make_pair(osdmap.get_epoch(),
11349                                                          ceph_clock_now()));
11350       creating_now = emplaced.second;
11351     }
11352     if (creating_now) {
11353       ss << "pg " << pgidstr << " now creating, ok";
11354       err = 0;
11355       goto update;
11356     } else {
11357       ss << "pg " << pgid << " already creating";
11358       err = 0;
11359       goto reply;
11360     }
11361   } else {
11362     err = -EINVAL;
11363   }
11364
11365  reply:
11366   getline(ss, rs);
11367   if (err < 0 && rs.length() == 0)
11368     rs = cpp_strerror(err);
11369   mon->reply_command(op, err, rs, rdata, get_last_committed());
11370   return ret;
11371
11372  update:
11373   getline(ss, rs);
11374   wait_for_finished_proposal(op, new Monitor::C_Command(mon, op, 0, rs,
11375                                             get_last_committed() + 1));
11376   return true;
11377
11378  wait:
11379   wait_for_finished_proposal(op, new C_RetryMessage(this, op));
11380   return true;
11381 }
11382
11383 bool OSDMonitor::preprocess_pool_op(MonOpRequestRef op) 
11384 {
11385   op->mark_osdmon_event(__func__);
11386   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
11387   
11388   if (m->fsid != mon->monmap->fsid) {
11389     dout(0) << __func__ << " drop message on fsid " << m->fsid
11390             << " != " << mon->monmap->fsid << " for " << *m << dendl;
11391     _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11392     return true;
11393   }
11394
11395   if (m->op == POOL_OP_CREATE)
11396     return preprocess_pool_op_create(op);
11397
11398   if (!osdmap.get_pg_pool(m->pool)) {
11399     dout(10) << "attempt to operate on non-existent pool id " << m->pool << dendl;
11400     _pool_op_reply(op, 0, osdmap.get_epoch());
11401     return true;
11402   }
11403
11404   // check if the snap and snapname exist
11405   bool snap_exists = false;
11406   const pg_pool_t *p = osdmap.get_pg_pool(m->pool);
11407   if (p->snap_exists(m->name.c_str()))
11408     snap_exists = true;
11409
11410   switch (m->op) {
11411   case POOL_OP_CREATE_SNAP:
11412     if (p->is_unmanaged_snaps_mode() || p->is_tier()) {
11413       _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11414       return true;
11415     }
11416     if (snap_exists) {
11417       _pool_op_reply(op, 0, osdmap.get_epoch());
11418       return true;
11419     }
11420     return false;
11421   case POOL_OP_CREATE_UNMANAGED_SNAP:
11422     if (p->is_pool_snaps_mode()) {
11423       _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11424       return true;
11425     }
11426     return false;
11427   case POOL_OP_DELETE_SNAP:
11428     if (p->is_unmanaged_snaps_mode()) {
11429       _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11430       return true;
11431     }
11432     if (!snap_exists) {
11433       _pool_op_reply(op, 0, osdmap.get_epoch());
11434       return true;
11435     }
11436     return false;
11437   case POOL_OP_DELETE_UNMANAGED_SNAP:
11438     if (p->is_pool_snaps_mode()) {
11439       _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11440       return true;
11441     }
11442     if (p->is_removed_snap(m->snapid)) {
11443       _pool_op_reply(op, 0, osdmap.get_epoch());
11444       return true;
11445     }
11446     return false;
11447   case POOL_OP_DELETE:
11448     if (osdmap.lookup_pg_pool_name(m->name.c_str()) >= 0) {
11449       _pool_op_reply(op, 0, osdmap.get_epoch());
11450       return true;
11451     }
11452     return false;
11453   case POOL_OP_AUID_CHANGE:
11454     return false;
11455   default:
11456     ceph_abort();
11457     break;
11458   }
11459
11460   return false;
11461 }
11462
11463 bool OSDMonitor::preprocess_pool_op_create(MonOpRequestRef op)
11464 {
11465   op->mark_osdmon_event(__func__);
11466   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
11467   MonSession *session = m->get_session();
11468   if (!session) {
11469     _pool_op_reply(op, -EPERM, osdmap.get_epoch());
11470     return true;
11471   }
11472   if (!session->is_capable("osd", MON_CAP_W)) {
11473     dout(5) << "attempt to create new pool without sufficient auid privileges!"
11474             << "message: " << *m  << std::endl
11475             << "caps: " << session->caps << dendl;
11476     _pool_op_reply(op, -EPERM, osdmap.get_epoch());
11477     return true;
11478   }
11479
11480   int64_t pool = osdmap.lookup_pg_pool_name(m->name.c_str());
11481   if (pool >= 0) {
11482     _pool_op_reply(op, 0, osdmap.get_epoch());
11483     return true;
11484   }
11485
11486   return false;
11487 }
11488
11489 bool OSDMonitor::prepare_pool_op(MonOpRequestRef op)
11490 {
11491   op->mark_osdmon_event(__func__);
11492   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
11493   dout(10) << "prepare_pool_op " << *m << dendl;
11494   if (m->op == POOL_OP_CREATE) {
11495     return prepare_pool_op_create(op);
11496   } else if (m->op == POOL_OP_DELETE) {
11497     return prepare_pool_op_delete(op);
11498   }
11499
11500   int ret = 0;
11501   bool changed = false;
11502
11503   if (!osdmap.have_pg_pool(m->pool)) {
11504     _pool_op_reply(op, -ENOENT, osdmap.get_epoch());
11505     return false;
11506   }
11507
11508   const pg_pool_t *pool = osdmap.get_pg_pool(m->pool);
11509
11510   switch (m->op) {
11511     case POOL_OP_CREATE_SNAP:
11512       if (pool->is_tier()) {
11513         ret = -EINVAL;
11514         _pool_op_reply(op, ret, osdmap.get_epoch());
11515         return false;
11516       }  // else, fall through
11517     case POOL_OP_DELETE_SNAP:
11518       if (!pool->is_unmanaged_snaps_mode()) {
11519         bool snap_exists = pool->snap_exists(m->name.c_str());
11520         if ((m->op == POOL_OP_CREATE_SNAP && snap_exists)
11521           || (m->op == POOL_OP_DELETE_SNAP && !snap_exists)) {
11522           ret = 0;
11523         } else {
11524           break;
11525         }
11526       } else {
11527         ret = -EINVAL;
11528       }
11529       _pool_op_reply(op, ret, osdmap.get_epoch());
11530       return false;
11531
11532     case POOL_OP_DELETE_UNMANAGED_SNAP:
11533       // we won't allow removal of an unmanaged snapshot from a pool
11534       // not in unmanaged snaps mode.
11535       if (!pool->is_unmanaged_snaps_mode()) {
11536         _pool_op_reply(op, -ENOTSUP, osdmap.get_epoch());
11537         return false;
11538       }
11539       /* fall-thru */
11540     case POOL_OP_CREATE_UNMANAGED_SNAP:
11541       // but we will allow creating an unmanaged snapshot on any pool
11542       // as long as it is not in 'pool' snaps mode.
11543       if (pool->is_pool_snaps_mode()) {
11544         _pool_op_reply(op, -EINVAL, osdmap.get_epoch());
11545         return false;
11546       }
11547   }
11548
11549   // projected pool info
11550   pg_pool_t pp;
11551   if (pending_inc.new_pools.count(m->pool))
11552     pp = pending_inc.new_pools[m->pool];
11553   else
11554     pp = *osdmap.get_pg_pool(m->pool);
11555
11556   bufferlist reply_data;
11557
11558   // pool snaps vs unmanaged snaps are mutually exclusive
11559   switch (m->op) {
11560   case POOL_OP_CREATE_SNAP:
11561   case POOL_OP_DELETE_SNAP:
11562     if (pp.is_unmanaged_snaps_mode()) {
11563       ret = -EINVAL;
11564       goto out;
11565     }
11566     break;
11567
11568   case POOL_OP_CREATE_UNMANAGED_SNAP:
11569   case POOL_OP_DELETE_UNMANAGED_SNAP:
11570     if (pp.is_pool_snaps_mode()) {
11571       ret = -EINVAL;
11572       goto out;
11573     }
11574   }
11575
11576   switch (m->op) {
11577   case POOL_OP_CREATE_SNAP:
11578     if (!pp.snap_exists(m->name.c_str())) {
11579       pp.add_snap(m->name.c_str(), ceph_clock_now());
11580       dout(10) << "create snap in pool " << m->pool << " " << m->name << " seq " << pp.get_snap_epoch() << dendl;
11581       changed = true;
11582     }
11583     break;
11584
11585   case POOL_OP_DELETE_SNAP:
11586     {
11587       snapid_t s = pp.snap_exists(m->name.c_str());
11588       if (s) {
11589         pp.remove_snap(s);
11590         changed = true;
11591       }
11592     }
11593     break;
11594
11595   case POOL_OP_CREATE_UNMANAGED_SNAP:
11596     {
11597       uint64_t snapid;
11598       pp.add_unmanaged_snap(snapid);
11599       ::encode(snapid, reply_data);
11600       changed = true;
11601     }
11602     break;
11603
11604   case POOL_OP_DELETE_UNMANAGED_SNAP:
11605     if (!pp.is_removed_snap(m->snapid)) {
11606       pp.remove_unmanaged_snap(m->snapid);
11607       changed = true;
11608     }
11609     break;
11610
11611   case POOL_OP_AUID_CHANGE:
11612     if (pp.auid != m->auid) {
11613       pp.auid = m->auid;
11614       changed = true;
11615     }
11616     break;
11617
11618   default:
11619     ceph_abort();
11620     break;
11621   }
11622
11623   if (changed) {
11624     pp.set_snap_epoch(pending_inc.epoch);
11625     pending_inc.new_pools[m->pool] = pp;
11626   }
11627
11628  out:
11629   wait_for_finished_proposal(op, new OSDMonitor::C_PoolOp(this, op, ret, pending_inc.epoch, &reply_data));
11630   return true;
11631 }
11632
11633 bool OSDMonitor::prepare_pool_op_create(MonOpRequestRef op)
11634 {
11635   op->mark_osdmon_event(__func__);
11636   int err = prepare_new_pool(op);
11637   wait_for_finished_proposal(op, new OSDMonitor::C_PoolOp(this, op, err, pending_inc.epoch));
11638   return true;
11639 }
11640
11641 int OSDMonitor::_check_remove_pool(int64_t pool_id, const pg_pool_t& pool,
11642                                    ostream *ss)
11643 {
11644   const string& poolstr = osdmap.get_pool_name(pool_id);
11645
11646   // If the Pool is in use by CephFS, refuse to delete it
11647   FSMap const &pending_fsmap = mon->mdsmon()->get_pending();
11648   if (pending_fsmap.pool_in_use(pool_id)) {
11649     *ss << "pool '" << poolstr << "' is in use by CephFS";
11650     return -EBUSY;
11651   }
11652
11653   if (pool.tier_of >= 0) {
11654     *ss << "pool '" << poolstr << "' is a tier of '"
11655         << osdmap.get_pool_name(pool.tier_of) << "'";
11656     return -EBUSY;
11657   }
11658   if (!pool.tiers.empty()) {
11659     *ss << "pool '" << poolstr << "' has tiers";
11660     for(auto tier : pool.tiers) {
11661       *ss << " " << osdmap.get_pool_name(tier);
11662     }
11663     return -EBUSY;
11664   }
11665
11666   if (!g_conf->mon_allow_pool_delete) {
11667     *ss << "pool deletion is disabled; you must first set the mon_allow_pool_delete config option to true before you can destroy a pool";
11668     return -EPERM;
11669   }
11670
11671   if (pool.has_flag(pg_pool_t::FLAG_NODELETE)) {
11672     *ss << "pool deletion is disabled; you must unset nodelete flag for the pool first";
11673     return -EPERM;
11674   }
11675
11676   *ss << "pool '" << poolstr << "' removed";
11677   return 0;
11678 }
11679
11680 /**
11681  * Check if it is safe to add a tier to a base pool
11682  *
11683  * @return
11684  * True if the operation should proceed, false if we should abort here
11685  * (abort doesn't necessarily mean error, could be idempotency)
11686  */
11687 bool OSDMonitor::_check_become_tier(
11688     const int64_t tier_pool_id, const pg_pool_t *tier_pool,
11689     const int64_t base_pool_id, const pg_pool_t *base_pool,
11690     int *err,
11691     ostream *ss) const
11692 {
11693   const std::string &tier_pool_name = osdmap.get_pool_name(tier_pool_id);
11694   const std::string &base_pool_name = osdmap.get_pool_name(base_pool_id);
11695
11696   const FSMap &pending_fsmap = mon->mdsmon()->get_pending();
11697   if (pending_fsmap.pool_in_use(tier_pool_id)) {
11698     *ss << "pool '" << tier_pool_name << "' is in use by CephFS";
11699     *err = -EBUSY;
11700     return false;
11701   }
11702
11703   if (base_pool->tiers.count(tier_pool_id)) {
11704     assert(tier_pool->tier_of == base_pool_id);
11705     *err = 0;
11706     *ss << "pool '" << tier_pool_name << "' is now (or already was) a tier of '"
11707       << base_pool_name << "'";
11708     return false;
11709   }
11710
11711   if (base_pool->is_tier()) {
11712     *ss << "pool '" << base_pool_name << "' is already a tier of '"
11713       << osdmap.get_pool_name(base_pool->tier_of) << "', "
11714       << "multiple tiers are not yet supported.";
11715     *err = -EINVAL;
11716     return false;
11717   }
11718
11719   if (tier_pool->has_tiers()) {
11720     *ss << "pool '" << tier_pool_name << "' has following tier(s) already:";
11721     for (set<uint64_t>::iterator it = tier_pool->tiers.begin();
11722          it != tier_pool->tiers.end(); ++it)
11723       *ss << "'" << osdmap.get_pool_name(*it) << "',";
11724     *ss << " multiple tiers are not yet supported.";
11725     *err = -EINVAL;
11726     return false;
11727   }
11728
11729   if (tier_pool->is_tier()) {
11730     *ss << "tier pool '" << tier_pool_name << "' is already a tier of '"
11731        << osdmap.get_pool_name(tier_pool->tier_of) << "'";
11732     *err = -EINVAL;
11733     return false;
11734   }
11735
11736   *err = 0;
11737   return true;
11738 }
11739
11740
11741 /**
11742  * Check if it is safe to remove a tier from this base pool
11743  *
11744  * @return
11745  * True if the operation should proceed, false if we should abort here
11746  * (abort doesn't necessarily mean error, could be idempotency)
11747  */
11748 bool OSDMonitor::_check_remove_tier(
11749     const int64_t base_pool_id, const pg_pool_t *base_pool,
11750     const pg_pool_t *tier_pool,
11751     int *err, ostream *ss) const
11752 {
11753   const std::string &base_pool_name = osdmap.get_pool_name(base_pool_id);
11754
11755   // Apply CephFS-specific checks
11756   const FSMap &pending_fsmap = mon->mdsmon()->get_pending();
11757   if (pending_fsmap.pool_in_use(base_pool_id)) {
11758     if (base_pool->type != pg_pool_t::TYPE_REPLICATED) {
11759       // If the underlying pool is erasure coded, we can't permit the
11760       // removal of the replicated tier that CephFS relies on to access it
11761       *ss << "pool '" << base_pool_name << "' is in use by CephFS via its tier";
11762       *err = -EBUSY;
11763       return false;
11764     }
11765
11766     if (tier_pool && tier_pool->cache_mode == pg_pool_t::CACHEMODE_WRITEBACK) {
11767       *ss << "pool '" << base_pool_name << "' is in use by CephFS, and this "
11768              "tier is still in use as a writeback cache.  Change the cache "
11769              "mode and flush the cache before removing it";
11770       *err = -EBUSY;
11771       return false;
11772     }
11773   }
11774
11775   *err = 0;
11776   return true;
11777 }
11778
11779 int OSDMonitor::_prepare_remove_pool(
11780   int64_t pool, ostream *ss, bool no_fake)
11781 {
11782   dout(10) << __func__ << " " << pool << dendl;
11783   const pg_pool_t *p = osdmap.get_pg_pool(pool);
11784   int r = _check_remove_pool(pool, *p, ss);
11785   if (r < 0)
11786     return r;
11787
11788   auto new_pool = pending_inc.new_pools.find(pool);
11789   if (new_pool != pending_inc.new_pools.end()) {
11790     // if there is a problem with the pending info, wait and retry
11791     // this op.
11792     const auto& p = new_pool->second;
11793     int r = _check_remove_pool(pool, p, ss);
11794     if (r < 0)
11795       return -EAGAIN;
11796   }
11797
11798   if (pending_inc.old_pools.count(pool)) {
11799     dout(10) << __func__ << " " << pool << " already pending removal"
11800              << dendl;
11801     return 0;
11802   }
11803
11804   if (g_conf->mon_fake_pool_delete && !no_fake) {
11805     string old_name = osdmap.get_pool_name(pool);
11806     string new_name = old_name + "." + stringify(pool) + ".DELETED";
11807     dout(1) << __func__ << " faking pool deletion: renaming " << pool << " "
11808             << old_name << " -> " << new_name << dendl;
11809     pending_inc.new_pool_names[pool] = new_name;
11810     return 0;
11811   }
11812
11813   // remove
11814   pending_inc.old_pools.insert(pool);
11815
11816   // remove any pg_temp mappings for this pool
11817   for (auto p = osdmap.pg_temp->begin();
11818        p != osdmap.pg_temp->end();
11819        ++p) {
11820     if (p->first.pool() == (uint64_t)pool) {
11821       dout(10) << __func__ << " " << pool << " removing obsolete pg_temp "
11822                << p->first << dendl;
11823       pending_inc.new_pg_temp[p->first].clear();
11824     }
11825   }
11826   // remove any primary_temp mappings for this pool
11827   for (auto p = osdmap.primary_temp->begin();
11828       p != osdmap.primary_temp->end();
11829       ++p) {
11830     if (p->first.pool() == (uint64_t)pool) {
11831       dout(10) << __func__ << " " << pool
11832                << " removing obsolete primary_temp" << p->first << dendl;
11833       pending_inc.new_primary_temp[p->first] = -1;
11834     }
11835   }
11836   // remove any pg_upmap mappings for this pool
11837   for (auto& p : osdmap.pg_upmap) {
11838     if (p.first.pool() == (uint64_t)pool) {
11839       dout(10) << __func__ << " " << pool
11840                << " removing obsolete pg_upmap "
11841                << p.first << dendl;
11842       pending_inc.old_pg_upmap.insert(p.first);
11843     }
11844   }
11845   // remove any pg_upmap_items mappings for this pool
11846   for (auto& p : osdmap.pg_upmap_items) {
11847     if (p.first.pool() == (uint64_t)pool) {
11848       dout(10) << __func__ << " " << pool
11849                << " removing obsolete pg_upmap_items " << p.first
11850                << dendl;
11851       pending_inc.old_pg_upmap_items.insert(p.first);
11852     }
11853   }
11854
11855   // remove any choose_args for this pool
11856   CrushWrapper newcrush;
11857   _get_pending_crush(newcrush);
11858   if (newcrush.have_choose_args(pool)) {
11859     dout(10) << __func__ << " removing choose_args for pool " << pool << dendl;
11860     newcrush.rm_choose_args(pool);
11861     pending_inc.crush.clear();
11862     newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
11863   }
11864   return 0;
11865 }
11866
11867 int OSDMonitor::_prepare_rename_pool(int64_t pool, string newname)
11868 {
11869   dout(10) << "_prepare_rename_pool " << pool << dendl;
11870   if (pending_inc.old_pools.count(pool)) {
11871     dout(10) << "_prepare_rename_pool " << pool << " pending removal" << dendl;
11872     return -ENOENT;
11873   }
11874   for (map<int64_t,string>::iterator p = pending_inc.new_pool_names.begin();
11875        p != pending_inc.new_pool_names.end();
11876        ++p) {
11877     if (p->second == newname && p->first != pool) {
11878       return -EEXIST;
11879     }
11880   }
11881
11882   pending_inc.new_pool_names[pool] = newname;
11883   return 0;
11884 }
11885
11886 bool OSDMonitor::prepare_pool_op_delete(MonOpRequestRef op)
11887 {
11888   op->mark_osdmon_event(__func__);
11889   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
11890   ostringstream ss;
11891   int ret = _prepare_remove_pool(m->pool, &ss, false);
11892   if (ret == -EAGAIN) {
11893     wait_for_finished_proposal(op, new C_RetryMessage(this, op));
11894     return true;
11895   }
11896   if (ret < 0)
11897     dout(10) << __func__ << " got " << ret << " " << ss.str() << dendl;
11898   wait_for_finished_proposal(op, new OSDMonitor::C_PoolOp(this, op, ret,
11899                                                       pending_inc.epoch));
11900   return true;
11901 }
11902
11903 void OSDMonitor::_pool_op_reply(MonOpRequestRef op,
11904                                 int ret, epoch_t epoch, bufferlist *blp)
11905 {
11906   op->mark_osdmon_event(__func__);
11907   MPoolOp *m = static_cast<MPoolOp*>(op->get_req());
11908   dout(20) << "_pool_op_reply " << ret << dendl;
11909   MPoolOpReply *reply = new MPoolOpReply(m->fsid, m->get_tid(),
11910                                          ret, epoch, get_last_committed(), blp);
11911   mon->send_reply(op, reply);
11912 }