Skip to content

Commit

Permalink
MDS: lock out snapshots until after a flag has been set in the MDSMap
Browse files Browse the repository at this point in the history
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: ceph#6332

Signed-off-by: Greg Farnum <[email protected]>
Signed-off-by: Loic Dachary <[email protected]>
  • Loading branch information
gregsfortytwo authored and Loic Dachary committed Sep 26, 2013
1 parent 3cffff2 commit 9771b1d
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 3 deletions.
3 changes: 2 additions & 1 deletion PendingReleaseNotes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
v0.69
v0.70
~~~~~
mds: disable adding snapshots by default. (re-enable them with "ceph mds allow_snaps")
1 change: 1 addition & 0 deletions src/include/ceph_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion src/mds/MDSMap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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);
}
15 changes: 14 additions & 1 deletion src/mds/MDSMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ class MDSMap {
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;

Expand All @@ -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() {
Expand All @@ -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++; }

Expand Down
6 changes: 6 additions & 0 deletions src/mds/Server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
30 changes: 30 additions & 0 deletions src/mon/MDSMonitor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions src/mon/MonCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
24 changes: 24 additions & 0 deletions src/test/pybind/test_ceph_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down

0 comments on commit 9771b1d

Please sign in to comment.