]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
MDS: lock out snapshots until after a flag has been set in the MDSMap
authorGreg Farnum <greg@inktank.com>
Wed, 25 Sep 2013 22:52:50 +0000 (15:52 -0700)
committerLoic Dachary <loic@dachary.org>
Thu, 26 Sep 2013 16:26:36 +0000 (18:26 +0200)
This way users can't put snapshots on their clusters unless they explicitly
ask for them and have seen a warning message. We take a bit of the MDSMap
flags in order to do so. The only thing a little weird here is that anybody
who upgrades to this patch who already has snapshots will hit the EPERM
and have to go through the warning, but it doesn't impact existing snapshots
at all so they should be good.

To go along with this, we add "ever_allowed_snaps" and "explicitly_allowed_snaps"
members to the MDSMap, which are default to false and are set to true
when allow_new_snaps is set. Old maps decoded with new code default to true
and false, respectively, so we can tell.

Fixes: #6332
Signed-off-by: Greg Farnum <greg@inktank.com>
Signed-off-by: Loic Dachary <loic@dachary.org>
PendingReleaseNotes
src/include/ceph_fs.h
src/mds/MDSMap.cc
src/mds/MDSMap.h
src/mds/Server.cc
src/mon/MDSMonitor.cc
src/mon/MonCommands.h
src/test/pybind/test_ceph_argparse.py

index e7fcd7201bb8caf5fb5027e2e6d7e3980a27e814..7d667f3ff594a8ffde0f416f0c6b67e41e7a4781 100644 (file)
@@ -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
index 6c41d14f5da9cd37488e0c95dcf92281a3754199..ba0b5eb0f19fd25891620bd6a1e52767c75946ef 100644 (file)
@@ -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
index 1646a134ad58e740b15c0a49fec6e1b9c0e027d8..f1ab9b112d887c028e0b66867e1d20e4c8b7e198 100644 (file)
@@ -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);
 }
index 5bfc7cc20d5a895b5a28b25fb915a960f5f13e55..5eadf156a956d51f0e2f8fca9dec2425c73ea217 100644 (file)
@@ -175,6 +175,9 @@ protected:
   map<int32_t,uint64_t> up;        // who is in those roles
   map<uint64_t,mds_info_t> 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 @@ public:
       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 @@ public:
   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++; }
 
index 3140194abdc5742a75adb2006476a7ce56b7ffb5..869f37734419f252c43408ca6b6d331528bb4d62 100644 (file)
@@ -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)) {
index b2273274521d2f4634a01e8bff0e3d16ab291983..48c1c99d5847ec6352a94bdc4fc6bf266beb52bd 100644 (file)
@@ -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);
index 482ea91ea022dac29a59002574aefb1eb9d2b90e..f13bd8d2685bf7fd13a1764facdf7ae54254f432 100644 (file)
@@ -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 <key>", \
+        "mds", "w", "cli,rest")
+COMMAND("mds unset " \
+        "name=key,type=CephChoices,strings=allow_new_snaps " \
+        "name=sure,type=CephString,req=false", \
+        "unset <key>", "mds", "w", "cli,rest")
 COMMAND("mds add_data_pool " \
        "name=poolid,type=CephInt,range=0", \
        "add data pool <poolid>", "mds", "rw", "cli,rest")
index 85af54d6f75c0a6f5ec2caa8bb861a1672f81ed1..14cbaf50c240796466c9238427c385ede9e9818f 100755 (executable)
@@ -435,6 +435,30 @@ class TestMDS(TestArgparse):
                                                     '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')