]> git.proxmox.com Git - ceph.git/blob - ceph/src/mon/FSCommands.cc
abdea4333820558960e68b267e98b91a048d3fd5
[ceph.git] / ceph / src / mon / FSCommands.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) 2017 Red Hat Ltd
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15
16 #include "OSDMonitor.h"
17 #include "PGMonitor.h"
18
19 #include "FSCommands.h"
20 #include "MDSMonitor.h"
21
22
23
24 static const string EXPERIMENTAL_WARNING("Warning! This feature is experimental."
25 "It may cause problems up to and including data loss."
26 "Consult the documentation at ceph.com, and if unsure, do not proceed."
27 "Add --yes-i-really-mean-it if you are certain.");
28
29
30
31 class FlagSetHandler : public FileSystemCommandHandler
32 {
33 public:
34 FlagSetHandler()
35 : FileSystemCommandHandler("fs flag set")
36 {
37 }
38
39 int handle(
40 Monitor *mon,
41 FSMap &fsmap,
42 MonOpRequestRef op,
43 map<string, cmd_vartype> &cmdmap,
44 std::stringstream &ss) override
45 {
46 string flag_name;
47 cmd_getval(g_ceph_context, cmdmap, "flag_name", flag_name);
48
49 string flag_val;
50 cmd_getval(g_ceph_context, cmdmap, "val", flag_val);
51
52 string confirm;
53 cmd_getval(g_ceph_context, cmdmap, "confirm", confirm);
54
55 if (flag_name == "enable_multiple") {
56 bool flag_bool = false;
57 int r = parse_bool(flag_val, &flag_bool, ss);
58 if (r != 0) {
59 ss << "Invalid boolean value '" << flag_val << "'";
60 return r;
61 }
62
63 bool jewel = mon->get_quorum_con_features() & CEPH_FEATURE_SERVER_JEWEL;
64 if (flag_bool && !jewel) {
65 ss << "Multiple-filesystems are forbidden until all mons are updated";
66 return -EINVAL;
67 }
68 if (confirm != "--yes-i-really-mean-it") {
69 ss << EXPERIMENTAL_WARNING;
70 }
71 fsmap.set_enable_multiple(flag_bool);
72 return 0;
73 } else {
74 ss << "Unknown flag '" << flag_name << "'";
75 return -EINVAL;
76 }
77 }
78 };
79
80 class FsNewHandler : public FileSystemCommandHandler
81 {
82 public:
83 FsNewHandler()
84 : FileSystemCommandHandler("fs new")
85 {
86 }
87
88 int handle(
89 Monitor *mon,
90 FSMap &fsmap,
91 MonOpRequestRef op,
92 map<string, cmd_vartype> &cmdmap,
93 std::stringstream &ss) override
94 {
95 string metadata_name;
96 cmd_getval(g_ceph_context, cmdmap, "metadata", metadata_name);
97 int64_t metadata = mon->osdmon()->osdmap.lookup_pg_pool_name(metadata_name);
98 if (metadata < 0) {
99 ss << "pool '" << metadata_name << "' does not exist";
100 return -ENOENT;
101 }
102
103 string force;
104 cmd_getval(g_ceph_context,cmdmap, "force", force);
105 const pool_stat_t *stat = mon->pgservice->get_pool_stat(metadata);
106 if (stat) {
107 int64_t metadata_num_objects = stat->stats.sum.num_objects;
108 if (force != "--force" && metadata_num_objects > 0) {
109 ss << "pool '" << metadata_name
110 << "' already contains some objects. Use an empty pool instead.";
111 return -EINVAL;
112 }
113 }
114
115 string data_name;
116 cmd_getval(g_ceph_context, cmdmap, "data", data_name);
117 int64_t data = mon->osdmon()->osdmap.lookup_pg_pool_name(data_name);
118 if (data < 0) {
119 ss << "pool '" << data_name << "' does not exist";
120 return -ENOENT;
121 }
122 if (data == 0) {
123 ss << "pool '" << data_name << "' has id 0, which CephFS does not allow. Use another pool or recreate it to get a non-zero pool id.";
124 return -EINVAL;
125 }
126
127 string fs_name;
128 cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name);
129 if (fs_name.empty()) {
130 // Ensure fs name is not empty so that we can implement
131 // commmands that refer to FS by name in future.
132 ss << "Filesystem name may not be empty";
133 return -EINVAL;
134 }
135
136 if (fsmap.get_filesystem(fs_name)) {
137 auto fs = fsmap.get_filesystem(fs_name);
138 if (*(fs->mds_map.get_data_pools().begin()) == data
139 && fs->mds_map.get_metadata_pool() == metadata) {
140 // Identical FS created already, this is a no-op
141 ss << "filesystem '" << fs_name << "' already exists";
142 return 0;
143 } else {
144 ss << "filesystem already exists with name '" << fs_name << "'";
145 return -EINVAL;
146 }
147 }
148
149 if (fsmap.filesystem_count() > 0
150 && !fsmap.get_enable_multiple()) {
151 ss << "Creation of multiple filesystems is disabled. To enable "
152 "this experimental feature, use 'ceph fs flag set enable_multiple "
153 "true'";
154 return -EINVAL;
155 }
156
157 for (auto fs : fsmap.get_filesystems()) {
158 const std::vector<int64_t> &data_pools = fs->mds_map.get_data_pools();
159 string sure;
160 if ((std::find(data_pools.begin(), data_pools.end(), data) != data_pools.end()
161 || fs->mds_map.get_metadata_pool() == metadata)
162 && ((!cmd_getval(g_ceph_context, cmdmap, "sure", sure)
163 || sure != "--allow-dangerous-metadata-overlay"))) {
164 ss << "Filesystem '" << fs_name
165 << "' is already using one of the specified RADOS pools. This should ONLY be done in emergencies and after careful reading of the documentation. Pass --allow-dangerous-metadata-overlay to permit this.";
166 return -EEXIST;
167 }
168 }
169
170 pg_pool_t const *data_pool = mon->osdmon()->osdmap.get_pg_pool(data);
171 assert(data_pool != NULL); // Checked it existed above
172 pg_pool_t const *metadata_pool = mon->osdmon()->osdmap.get_pg_pool(metadata);
173 assert(metadata_pool != NULL); // Checked it existed above
174
175 // we must make these checks before we even allow ourselves to *think*
176 // about requesting a proposal to the osdmonitor and bail out now if
177 // we believe we must. bailing out *after* we request the proposal is
178 // bad business as we could have changed the osdmon's state and ending up
179 // returning an error to the user.
180 int r = _check_pool(mon->osdmon()->osdmap, data, false, &ss);
181 if (r < 0) {
182 return r;
183 }
184
185 r = _check_pool(mon->osdmon()->osdmap, metadata, true, &ss);
186 if (r < 0) {
187 return r;
188 }
189
190 // All checks passed, go ahead and create.
191 fsmap.create_filesystem(fs_name, metadata, data,
192 mon->get_quorum_con_features());
193 ss << "new fs with metadata pool " << metadata << " and data pool " << data;
194 return 0;
195 }
196 };
197
198 class SetHandler : public FileSystemCommandHandler
199 {
200 public:
201 SetHandler()
202 : FileSystemCommandHandler("fs set")
203 {}
204
205 int handle(
206 Monitor *mon,
207 FSMap &fsmap,
208 MonOpRequestRef op,
209 map<string, cmd_vartype> &cmdmap,
210 std::stringstream &ss) override
211 {
212 std::string fs_name;
213 if (!cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name) || fs_name.empty()) {
214 ss << "Missing filesystem name";
215 return -EINVAL;
216 }
217
218 auto fs = fsmap.get_filesystem(fs_name);
219 if (fs == nullptr) {
220 ss << "Not found: '" << fs_name << "'";
221 return -ENOENT;
222 }
223
224 string var;
225 if (!cmd_getval(g_ceph_context, cmdmap, "var", var) || var.empty()) {
226 ss << "Invalid variable";
227 return -EINVAL;
228 }
229 string val;
230 string interr;
231 int64_t n = 0;
232 if (!cmd_getval(g_ceph_context, cmdmap, "val", val)) {
233 return -EINVAL;
234 }
235 // we got a string. see if it contains an int.
236 n = strict_strtoll(val.c_str(), 10, &interr);
237 if (var == "max_mds") {
238 // NOTE: see also "mds set_max_mds", which can modify the same field.
239 if (interr.length()) {
240 ss << interr;
241 return -EINVAL;
242 }
243
244 if (n <= 0) {
245 ss << "You must specify at least one MDS";
246 return -EINVAL;
247 }
248
249 if (!fs->mds_map.allows_multimds() && n > fs->mds_map.get_max_mds() &&
250 n > 1) {
251 ss << "multi-MDS clusters are not enabled; set 'allow_multimds' to enable";
252 return -EINVAL;
253 }
254 if (n > MAX_MDS) {
255 ss << "may not have more than " << MAX_MDS << " MDS ranks";
256 return -EINVAL;
257 }
258 fsmap.modify_filesystem(
259 fs->fscid,
260 [n](std::shared_ptr<Filesystem> fs)
261 {
262 fs->mds_map.set_max_mds(n);
263 });
264 } else if (var == "inline_data") {
265 bool enable_inline = false;
266 int r = parse_bool(val, &enable_inline, ss);
267 if (r != 0) {
268 return r;
269 }
270
271 if (enable_inline) {
272 string confirm;
273 if (!cmd_getval(g_ceph_context, cmdmap, "confirm", confirm) ||
274 confirm != "--yes-i-really-mean-it") {
275 ss << EXPERIMENTAL_WARNING;
276 return -EPERM;
277 }
278 ss << "inline data enabled";
279
280 fsmap.modify_filesystem(
281 fs->fscid,
282 [](std::shared_ptr<Filesystem> fs)
283 {
284 fs->mds_map.set_inline_data_enabled(true);
285 });
286
287 // Update `compat`
288 CompatSet c = fsmap.get_compat();
289 c.incompat.insert(MDS_FEATURE_INCOMPAT_INLINE);
290 fsmap.update_compat(c);
291 } else {
292 ss << "inline data disabled";
293 fsmap.modify_filesystem(
294 fs->fscid,
295 [](std::shared_ptr<Filesystem> fs)
296 {
297 fs->mds_map.set_inline_data_enabled(false);
298 });
299 }
300 } else if (var == "balancer") {
301 if (val.empty()) {
302 ss << "unsetting the metadata load balancer";
303 } else {
304 ss << "setting the metadata load balancer to " << val;
305 }
306 fsmap.modify_filesystem(
307 fs->fscid,
308 [val](std::shared_ptr<Filesystem> fs)
309 {
310 fs->mds_map.set_balancer(val);
311 });
312 return true;
313 } else if (var == "max_file_size") {
314 if (interr.length()) {
315 ss << var << " requires an integer value";
316 return -EINVAL;
317 }
318 if (n < CEPH_MIN_STRIPE_UNIT) {
319 ss << var << " must at least " << CEPH_MIN_STRIPE_UNIT;
320 return -ERANGE;
321 }
322 fsmap.modify_filesystem(
323 fs->fscid,
324 [n](std::shared_ptr<Filesystem> fs)
325 {
326 fs->mds_map.set_max_filesize(n);
327 });
328 } else if (var == "allow_new_snaps") {
329 bool enable_snaps = false;
330 int r = parse_bool(val, &enable_snaps, ss);
331 if (r != 0) {
332 return r;
333 }
334
335 if (!enable_snaps) {
336 fsmap.modify_filesystem(
337 fs->fscid,
338 [](std::shared_ptr<Filesystem> fs)
339 {
340 fs->mds_map.clear_snaps_allowed();
341 });
342 ss << "disabled new snapshots";
343 } else {
344 string confirm;
345 if (!cmd_getval(g_ceph_context, cmdmap, "confirm", confirm) ||
346 confirm != "--yes-i-really-mean-it") {
347 ss << EXPERIMENTAL_WARNING;
348 return -EPERM;
349 }
350 fsmap.modify_filesystem(
351 fs->fscid,
352 [](std::shared_ptr<Filesystem> fs)
353 {
354 fs->mds_map.set_snaps_allowed();
355 });
356 ss << "enabled new snapshots";
357 }
358 } else if (var == "allow_multimds") {
359 bool enable_multimds = false;
360 int r = parse_bool(val, &enable_multimds, ss);
361 if (r != 0) {
362 return r;
363 }
364
365 if (!enable_multimds) {
366 fsmap.modify_filesystem(fs->fscid,
367 [](std::shared_ptr<Filesystem> fs)
368 {
369 fs->mds_map.clear_multimds_allowed();
370 });
371 ss << "disallowed increasing the cluster size past 1";
372 } else {
373 string confirm;
374 if (!cmd_getval(g_ceph_context, cmdmap, "confirm", confirm) ||
375 confirm != "--yes-i-really-mean-it") {
376 ss << EXPERIMENTAL_WARNING;
377 return -EPERM;
378 }
379 fsmap.modify_filesystem(
380 fs->fscid,
381 [](std::shared_ptr<Filesystem> fs)
382 {
383 fs->mds_map.set_multimds_allowed();
384 });
385 ss << "enabled creation of more than 1 active MDS";
386 }
387 } else if (var == "allow_dirfrags") {
388 bool enable_dirfrags = false;
389 int r = parse_bool(val, &enable_dirfrags, ss);
390 if (r != 0) {
391 return r;
392 }
393
394 if (!enable_dirfrags) {
395 fsmap.modify_filesystem(fs->fscid,
396 [](std::shared_ptr<Filesystem> fs)
397 {
398 fs->mds_map.clear_dirfrags_allowed();
399 });
400 ss << "disallowed new directory fragmentation";
401 } else {
402 fsmap.modify_filesystem(
403 fs->fscid,
404 [](std::shared_ptr<Filesystem> fs)
405 {
406 fs->mds_map.set_dirfrags_allowed();
407 });
408 ss << "enabled directory fragmentation";
409 }
410 } else if (var == "cluster_down") {
411 bool is_down = false;
412 int r = parse_bool(val, &is_down, ss);
413 if (r != 0) {
414 return r;
415 }
416
417 fsmap.modify_filesystem(
418 fs->fscid,
419 [is_down](std::shared_ptr<Filesystem> fs)
420 {
421 if (is_down) {
422 fs->mds_map.set_flag(CEPH_MDSMAP_DOWN);
423 } else {
424 fs->mds_map.clear_flag(CEPH_MDSMAP_DOWN);
425 }
426 });
427
428 ss << "marked " << (is_down ? "down" : "up");
429 } else if (var == "standby_count_wanted") {
430 if (interr.length()) {
431 ss << var << " requires an integer value";
432 return -EINVAL;
433 }
434 if (n < 0) {
435 ss << var << " must be non-negative";
436 return -ERANGE;
437 }
438 fsmap.modify_filesystem(
439 fs->fscid,
440 [n](std::shared_ptr<Filesystem> fs)
441 {
442 fs->mds_map.set_standby_count_wanted(n);
443 });
444 } else {
445 ss << "unknown variable " << var;
446 return -EINVAL;
447 }
448
449 return 0;
450 }
451 };
452
453 class AddDataPoolHandler : public FileSystemCommandHandler
454 {
455 public:
456 AddDataPoolHandler()
457 : FileSystemCommandHandler("fs add_data_pool")
458 {}
459
460 int handle(
461 Monitor *mon,
462 FSMap &fsmap,
463 MonOpRequestRef op,
464 map<string, cmd_vartype> &cmdmap,
465 std::stringstream &ss) override
466 {
467 string poolname;
468 cmd_getval(g_ceph_context, cmdmap, "pool", poolname);
469
470 std::string fs_name;
471 if (!cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name)
472 || fs_name.empty()) {
473 ss << "Missing filesystem name";
474 return -EINVAL;
475 }
476
477 auto fs = fsmap.get_filesystem(fs_name);
478 if (fs == nullptr) {
479 ss << "Not found: '" << fs_name << "'";
480 return -ENOENT;
481 }
482
483 int64_t poolid = mon->osdmon()->osdmap.lookup_pg_pool_name(poolname);
484 if (poolid < 0) {
485 string err;
486 poolid = strict_strtol(poolname.c_str(), 10, &err);
487 if (err.length()) {
488 ss << "pool '" << poolname << "' does not exist";
489 return -ENOENT;
490 }
491 }
492
493 int r = _check_pool(mon->osdmon()->osdmap, poolid, false, &ss);
494 if (r != 0) {
495 return r;
496 }
497
498 // no-op when the data_pool already on fs
499 if (fs->mds_map.is_data_pool(poolid)) {
500 ss << "data pool " << poolid << " is already on fs " << fs_name;
501 return 0;
502 }
503
504 fsmap.modify_filesystem(
505 fs->fscid,
506 [poolid](std::shared_ptr<Filesystem> fs)
507 {
508 fs->mds_map.add_data_pool(poolid);
509 });
510
511 ss << "added data pool " << poolid << " to fsmap";
512
513 return 0;
514 }
515 };
516
517 class SetDefaultHandler : public FileSystemCommandHandler
518 {
519 public:
520 SetDefaultHandler()
521 : FileSystemCommandHandler("fs set-default")
522 {}
523
524 int handle(
525 Monitor *mon,
526 FSMap &fsmap,
527 MonOpRequestRef op,
528 map<string, cmd_vartype> &cmdmap,
529 std::stringstream &ss) override
530 {
531 std::string fs_name;
532 cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name);
533 auto fs = fsmap.get_filesystem(fs_name);
534 if (fs == nullptr) {
535 ss << "filesystem '" << fs_name << "' does not exist";
536 return -ENOENT;
537 }
538
539 fsmap.set_legacy_client_fscid(fs->fscid);
540 return 0;
541 }
542 };
543
544 class RemoveFilesystemHandler : public FileSystemCommandHandler
545 {
546 public:
547 RemoveFilesystemHandler()
548 : FileSystemCommandHandler("fs rm")
549 {}
550
551 int handle(
552 Monitor *mon,
553 FSMap &fsmap,
554 MonOpRequestRef op,
555 map<string, cmd_vartype> &cmdmap,
556 std::stringstream &ss) override
557 {
558 // Check caller has correctly named the FS to delete
559 // (redundant while there is only one FS, but command
560 // syntax should apply to multi-FS future)
561 string fs_name;
562 cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name);
563 auto fs = fsmap.get_filesystem(fs_name);
564 if (fs == nullptr) {
565 // Consider absence success to make deletes idempotent
566 ss << "filesystem '" << fs_name << "' does not exist";
567 return 0;
568 }
569
570 // Check that no MDS daemons are active
571 if (fs->mds_map.get_num_up_mds() > 0) {
572 ss << "all MDS daemons must be inactive before removing filesystem";
573 return -EINVAL;
574 }
575
576 // Check for confirmation flag
577 string sure;
578 cmd_getval(g_ceph_context, cmdmap, "sure", sure);
579 if (sure != "--yes-i-really-mean-it") {
580 ss << "this is a DESTRUCTIVE operation and will make data in your filesystem permanently" \
581 " inaccessible. Add --yes-i-really-mean-it if you are sure you wish to continue.";
582 return -EPERM;
583 }
584
585 if (fsmap.get_legacy_client_fscid() == fs->fscid) {
586 fsmap.set_legacy_client_fscid(FS_CLUSTER_ID_NONE);
587 }
588
589 std::vector<mds_gid_t> to_fail;
590 // There may be standby_replay daemons left here
591 for (const auto &i : fs->mds_map.get_mds_info()) {
592 assert(i.second.state == MDSMap::STATE_STANDBY_REPLAY);
593 to_fail.push_back(i.first);
594 }
595
596 for (const auto &gid : to_fail) {
597 // Standby replays don't write, so it isn't important to
598 // wait for an osdmap propose here: ignore return value.
599 mon->mdsmon()->fail_mds_gid(gid);
600 }
601
602 fsmap.erase_filesystem(fs->fscid);
603
604 return 0;
605 }
606 };
607
608 class ResetFilesystemHandler : public FileSystemCommandHandler
609 {
610 public:
611 ResetFilesystemHandler()
612 : FileSystemCommandHandler("fs reset")
613 {}
614
615 int handle(
616 Monitor *mon,
617 FSMap &fsmap,
618 MonOpRequestRef op,
619 map<string, cmd_vartype> &cmdmap,
620 std::stringstream &ss) override
621 {
622 string fs_name;
623 cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name);
624 auto fs = fsmap.get_filesystem(fs_name);
625 if (fs == nullptr) {
626 ss << "filesystem '" << fs_name << "' does not exist";
627 // Unlike fs rm, we consider this case an error
628 return -ENOENT;
629 }
630
631 // Check that no MDS daemons are active
632 if (fs->mds_map.get_num_up_mds() > 0) {
633 ss << "all MDS daemons must be inactive before resetting filesystem: set the cluster_down flag"
634 " and use `ceph mds fail` to make this so";
635 return -EINVAL;
636 }
637
638 // Check for confirmation flag
639 string sure;
640 cmd_getval(g_ceph_context, cmdmap, "sure", sure);
641 if (sure != "--yes-i-really-mean-it") {
642 ss << "this is a potentially destructive operation, only for use by experts in disaster recovery. "
643 "Add --yes-i-really-mean-it if you are sure you wish to continue.";
644 return -EPERM;
645 }
646
647 fsmap.reset_filesystem(fs->fscid);
648
649 return 0;
650 }
651 };
652
653 class RemoveDataPoolHandler : public FileSystemCommandHandler
654 {
655 public:
656 RemoveDataPoolHandler()
657 : FileSystemCommandHandler("fs rm_data_pool")
658 {}
659
660 int handle(
661 Monitor *mon,
662 FSMap &fsmap,
663 MonOpRequestRef op,
664 map<string, cmd_vartype> &cmdmap,
665 std::stringstream &ss) override
666 {
667 string poolname;
668 cmd_getval(g_ceph_context, cmdmap, "pool", poolname);
669
670 std::string fs_name;
671 if (!cmd_getval(g_ceph_context, cmdmap, "fs_name", fs_name)
672 || fs_name.empty()) {
673 ss << "Missing filesystem name";
674 return -EINVAL;
675 }
676
677 auto fs = fsmap.get_filesystem(fs_name);
678 if (fs == nullptr) {
679 ss << "Not found: '" << fs_name << "'";
680 return -ENOENT;
681 }
682
683 int64_t poolid = mon->osdmon()->osdmap.lookup_pg_pool_name(poolname);
684 if (poolid < 0) {
685 string err;
686 poolid = strict_strtol(poolname.c_str(), 10, &err);
687 if (err.length()) {
688 ss << "pool '" << poolname << "' does not exist";
689 return -ENOENT;
690 } else if (poolid < 0) {
691 ss << "invalid pool id '" << poolid << "'";
692 return -EINVAL;
693 }
694 }
695
696 assert(poolid >= 0); // Checked by parsing code above
697
698 if (fs->mds_map.get_first_data_pool() == poolid) {
699 ss << "cannot remove default data pool";
700 return -EINVAL;
701 }
702
703
704 int r = 0;
705 fsmap.modify_filesystem(fs->fscid,
706 [&r, poolid](std::shared_ptr<Filesystem> fs)
707 {
708 r = fs->mds_map.remove_data_pool(poolid);
709 });
710 if (r == -ENOENT) {
711 // It was already removed, succeed in silence
712 return 0;
713 } else if (r == 0) {
714 // We removed it, succeed
715 ss << "removed data pool " << poolid << " from fsmap";
716 return 0;
717 } else {
718 // Unexpected error, bubble up
719 return r;
720 }
721 }
722 };
723
724
725 /**
726 * For commands that refer to a particular filesystem,
727 * enable wrapping to implement the legacy version of
728 * the command (like "mds add_data_pool" vs "fs add_data_pool")
729 *
730 * The wrapped handler must expect a fs_name argument in
731 * its command map.
732 */
733 template<typename T>
734 class LegacyHandler : public T
735 {
736 std::string legacy_prefix;
737
738 public:
739 LegacyHandler(const std::string &new_prefix)
740 : T()
741 {
742 legacy_prefix = new_prefix;
743 }
744
745 std::string const &get_prefix() override {return legacy_prefix;}
746
747 int handle(
748 Monitor *mon,
749 FSMap &fsmap,
750 MonOpRequestRef op,
751 map<string, cmd_vartype> &cmdmap,
752 std::stringstream &ss) override
753 {
754 auto fs = fsmap.get_legacy_filesystem();
755 if (fs == nullptr) {
756 ss << "No filesystem configured";
757 return -ENOENT;
758 }
759 std::map<string, cmd_vartype> modified = cmdmap;
760 modified["fs_name"] = fs->mds_map.get_fs_name();
761 return T::handle(mon, fsmap, op, modified, ss);
762 }
763 };
764
765 /**
766 * For commands with an alternative prefix
767 */
768 template<typename T>
769 class AliasHandler : public T
770 {
771 std::string alias_prefix;
772
773 public:
774 AliasHandler(const std::string &new_prefix)
775 : T()
776 {
777 alias_prefix = new_prefix;
778 }
779
780 std::string const &get_prefix() override {return alias_prefix;}
781
782 int handle(
783 Monitor *mon,
784 FSMap &fsmap,
785 MonOpRequestRef op,
786 map<string, cmd_vartype> &cmdmap,
787 std::stringstream &ss) override
788 {
789 return T::handle(mon, fsmap, op, cmdmap, ss);
790 }
791 };
792
793
794 std::list<std::shared_ptr<FileSystemCommandHandler> > FileSystemCommandHandler::load()
795 {
796 std::list<std::shared_ptr<FileSystemCommandHandler> > handlers;
797
798 handlers.push_back(std::make_shared<SetHandler>());
799 handlers.push_back(std::make_shared<LegacyHandler<SetHandler> >("mds set"));
800 handlers.push_back(std::make_shared<FlagSetHandler>());
801 handlers.push_back(std::make_shared<AddDataPoolHandler>());
802 handlers.push_back(std::make_shared<LegacyHandler<AddDataPoolHandler> >(
803 "mds add_data_pool"));
804 handlers.push_back(std::make_shared<RemoveDataPoolHandler>());
805 handlers.push_back(std::make_shared<LegacyHandler<RemoveDataPoolHandler> >(
806 "mds remove_data_pool"));
807 handlers.push_back(std::make_shared<LegacyHandler<RemoveDataPoolHandler> >(
808 "mds rm_data_pool"));
809 handlers.push_back(std::make_shared<FsNewHandler>());
810 handlers.push_back(std::make_shared<RemoveFilesystemHandler>());
811 handlers.push_back(std::make_shared<ResetFilesystemHandler>());
812
813 handlers.push_back(std::make_shared<SetDefaultHandler>());
814 handlers.push_back(std::make_shared<AliasHandler<SetDefaultHandler> >(
815 "fs set_default"));
816
817 return handlers;
818 }
819
820 int FileSystemCommandHandler::parse_bool(
821 const std::string &bool_str,
822 bool *result,
823 std::ostream &ss)
824 {
825 assert(result != nullptr);
826
827 string interr;
828 int64_t n = strict_strtoll(bool_str.c_str(), 10, &interr);
829
830 if (bool_str == "false" || bool_str == "no"
831 || (interr.length() == 0 && n == 0)) {
832 *result = false;
833 return 0;
834 } else if (bool_str == "true" || bool_str == "yes"
835 || (interr.length() == 0 && n == 1)) {
836 *result = true;
837 return 0;
838 } else {
839 ss << "value must be false|no|0 or true|yes|1";
840 return -EINVAL;
841 }
842 }
843
844 int FileSystemCommandHandler::_check_pool(
845 OSDMap &osd_map,
846 const int64_t pool_id,
847 bool metadata,
848 std::stringstream *ss) const
849 {
850 assert(ss != NULL);
851
852 const pg_pool_t *pool = osd_map.get_pg_pool(pool_id);
853 if (!pool) {
854 *ss << "pool id '" << pool_id << "' does not exist";
855 return -ENOENT;
856 }
857
858 const string& pool_name = osd_map.get_pool_name(pool_id);
859
860 if (pool->is_erasure() && metadata) {
861 *ss << "pool '" << pool_name << "' (id '" << pool_id << "')"
862 << " is an erasure-coded pool. Use of erasure-coded pools"
863 << " for CephFS metadata is not permitted";
864 return -EINVAL;
865 } else if (pool->is_erasure() && !pool->allows_ecoverwrites()) {
866 // non-overwriteable EC pools are only acceptable with a cache tier overlay
867 if (!pool->has_tiers() || !pool->has_read_tier() || !pool->has_write_tier()) {
868 *ss << "pool '" << pool_name << "' (id '" << pool_id << "')"
869 << " is an erasure-coded pool, with no overwrite support";
870 return -EINVAL;
871 }
872
873 // That cache tier overlay must be writeback, not readonly (it's the
874 // write operations like modify+truncate we care about support for)
875 const pg_pool_t *write_tier = osd_map.get_pg_pool(
876 pool->write_tier);
877 assert(write_tier != NULL); // OSDMonitor shouldn't allow DNE tier
878 if (write_tier->cache_mode == pg_pool_t::CACHEMODE_FORWARD
879 || write_tier->cache_mode == pg_pool_t::CACHEMODE_READONLY) {
880 *ss << "EC pool '" << pool_name << "' has a write tier ("
881 << osd_map.get_pool_name(pool->write_tier)
882 << ") that is configured "
883 "to forward writes. Use a cache mode such as 'writeback' for "
884 "CephFS";
885 return -EINVAL;
886 }
887 }
888
889 if (pool->is_tier()) {
890 *ss << " pool '" << pool_name << "' (id '" << pool_id
891 << "') is already in use as a cache tier.";
892 return -EINVAL;
893 }
894
895 // Nothing special about this pool, so it is permissible
896 return 0;
897 }
898