diff --git a/PendingReleaseNotes b/PendingReleaseNotes index e7fcd7201bb8c..7d667f3ff594a 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -1,2 +1,3 @@ -v0.69 +v0.70 ~~~~~ +mds: disable adding snapshots by default. (re-enable them with "ceph mds allow_snaps") \ No newline at end of file diff --git a/src/include/ceph_fs.h b/src/include/ceph_fs.h index 6c41d14f5da9c..ba0b5eb0f19fd 100644 --- a/src/include/ceph_fs.h +++ b/src/include/ceph_fs.h @@ -224,6 +224,7 @@ struct ceph_mon_subscribe_ack { * mdsmap flags */ #define CEPH_MDSMAP_DOWN (1<<0) /* cluster deliberately down */ +#define CEPH_MDSMAP_ALLOW_SNAPS (1<<1) /* cluster allowed to create snapshots */ /* * mds states diff --git a/src/mds/MDSMap.cc b/src/mds/MDSMap.cc index 1646a134ad58e..f1ab9b112d887 100644 --- a/src/mds/MDSMap.cc +++ b/src/mds/MDSMap.cc @@ -470,7 +470,7 @@ void MDSMap::encode(bufferlist& bl, uint64_t features) const ::encode(cas_pool, bl); // kclient ignores everything from here - __u16 ev = 5; + __u16 ev = 6; ::encode(ev, bl); ::encode(compat, bl); ::encode(metadata_pool, bl); @@ -483,6 +483,8 @@ void MDSMap::encode(bufferlist& bl, uint64_t features) const ::encode(failed, bl); ::encode(stopped, bl); ::encode(last_failure_osd_epoch, bl); + ::encode(ever_allowed_snaps, bl); + ::encode(explicitly_allowed_snaps, bl); ENCODE_FINISH(bl); } } @@ -540,5 +542,12 @@ void MDSMap::decode(bufferlist::iterator& p) ::decode(stopped, p); if (ev >= 4) ::decode(last_failure_osd_epoch, p); + if (ev >= 6) { + ::decode(ever_allowed_snaps, p); + ::decode(explicitly_allowed_snaps, p); + } else { + ever_allowed_snaps = true; + explicitly_allowed_snaps = false; + } DECODE_FINISH(p); } diff --git a/src/mds/MDSMap.h b/src/mds/MDSMap.h index 5bfc7cc20d5a8..5eadf156a956d 100644 --- a/src/mds/MDSMap.h +++ b/src/mds/MDSMap.h @@ -175,6 +175,9 @@ class MDSMap { map up; // who is in those roles map mds_info; + bool ever_allowed_snaps; //< the cluster has ever allowed snap creation + bool explicitly_allowed_snaps; //< the user has explicitly enabled snap creation + public: CompatSet compat; @@ -188,7 +191,9 @@ class MDSMap { max_file_size(0), cas_pool(-1), metadata_pool(0), - max_mds(0) + max_mds(0), + ever_allowed_snaps(false), + explicitly_allowed_snaps(false) { } utime_t get_session_timeout() { @@ -201,6 +206,14 @@ class MDSMap { void set_flag(int f) { flags |= f; } void clear_flag(int f) { flags &= ~f; } + void set_snaps_allowed() { + set_flag(CEPH_MDSMAP_ALLOW_SNAPS); + ever_allowed_snaps = true; + explicitly_allowed_snaps = true; + } + bool allows_snaps() { return test_flag(CEPH_MDSMAP_ALLOW_SNAPS); } + void clear_snaps_allowed() { clear_flag(CEPH_MDSMAP_ALLOW_SNAPS); } + epoch_t get_epoch() const { return epoch; } void inc_epoch() { epoch++; } diff --git a/src/mds/Server.cc b/src/mds/Server.cc index 3140194abdc57..869f37734419f 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -7162,6 +7162,12 @@ struct C_MDS_mksnap_finish : public Context { /* This function takes responsibility for the passed mdr*/ void Server::handle_client_mksnap(MDRequest *mdr) { + if (!mds->mdsmap->allows_snaps()) { + // you can't make snapshots until you set an option right now + reply_request(mdr, -EPERM); + return; + } + MClientRequest *req = mdr->client_request; CInode *diri = mdcache->get_inode(req->get_filepath().get_ino()); if (!diri || diri->state_test(CInode::STATE_PURGING)) { diff --git a/src/mon/MDSMonitor.cc b/src/mon/MDSMonitor.cc index b2273274521d2..48c1c99d5847e 100644 --- a/src/mon/MDSMonitor.cc +++ b/src/mon/MDSMonitor.cc @@ -920,6 +920,36 @@ bool MDSMonitor::prepare_command(MMonCommand *m) r = 0; } + } else if (prefix == "mds set") { + string key; + cmd_getval(g_ceph_context, cmdmap, "key", key); + string sure; + cmd_getval(g_ceph_context, cmdmap, "sure", sure); + if (key == "allow_new_snaps") { + if (sure != "--yes-i-really-mean-it") { + ss << "Snapshots are unstable and will probably break your FS! Add --yes-i-really-mean-it if you are sure"; + r = -EPERM; + } else { + pending_mdsmap.set_snaps_allowed(); + ss << "turned on snaps"; + r = 0; + } + } + } else if (prefix == "mds unset") { + string key; + cmd_getval(g_ceph_context, cmdmap, "key", key); + string sure; + cmd_getval(g_ceph_context, cmdmap, "sure", sure); + if (key == "allow_new_snaps") { + if (sure != "--yes-i-really-mean-it") { + ss << "this won't get rid of snapshots or restore the cluster if it's broken. Add --yes-i-really-mean-it if you are sure"; + r = -EPERM; + } else { + pending_mdsmap.clear_snaps_allowed(); + ss << "disabled new snapshots"; + r = 0; + } + } } else if (prefix == "mds add_data_pool") { int64_t poolid; cmd_getval(g_ceph_context, cmdmap, "poolid", poolid); diff --git a/src/mon/MonCommands.h b/src/mon/MonCommands.h index 482ea91ea022d..f13bd8d2685bf 100644 --- a/src/mon/MonCommands.h +++ b/src/mon/MonCommands.h @@ -274,6 +274,15 @@ COMMAND("mds compat rm_compat " \ COMMAND("mds compat rm_incompat " \ "name=feature,type=CephInt,range=0", \ "remove incompatible feature", "mds", "rw", "cli,rest") +COMMAND("mds set " \ + "name=key,type=CephChoices,strings=allow_new_snaps " \ + "name=sure,type=CephString,req=false", \ + "set ", \ + "mds", "w", "cli,rest") +COMMAND("mds unset " \ + "name=key,type=CephChoices,strings=allow_new_snaps " \ + "name=sure,type=CephString,req=false", \ + "unset ", "mds", "w", "cli,rest") COMMAND("mds add_data_pool " \ "name=poolid,type=CephInt,range=0", \ "add data pool ", "mds", "rw", "cli,rest") diff --git a/src/test/pybind/test_ceph_argparse.py b/src/test/pybind/test_ceph_argparse.py index 85af54d6f75c0..14cbaf50c2407 100755 --- a/src/test/pybind/test_ceph_argparse.py +++ b/src/test/pybind/test_ceph_argparse.py @@ -435,6 +435,30 @@ def test_incompat_rm_incompat(self): 'compat', 'rm_incompat', '1', '1'])) + def test_mds_set(self): + self.assert_valid_command(['mds', 'set', 'allow_new_snaps']) + self.assert_valid_command(['mds', 'set', 'allow_new_snaps', 'sure']) + assert_equal({}, validate_command(sigdict, ['mds', + 'set', + 'invalid'])) + assert_equal({}, validate_command(sigdict, ['mds', + 'set', + 'allow_new_snaps', + 'sure', + 'toomany'])) + + def test_mds_unset(self): + self.assert_valid_command(['mds', 'unset', 'allow_new_snaps']) + self.assert_valid_command(['mds', 'unset', 'allow_new_snaps', 'sure']) + assert_equal({}, validate_command(sigdict, ['mds', + 'unset', + 'invalid'])) + assert_equal({}, validate_command(sigdict, ['mds', + 'unset', + 'allow_new_snaps', + 'sure', + 'toomany'])) + def test_add_data_pool(self): self.check_1_natural_arg('mds', 'add_data_pool')