Merge pull request #2560 from ceph/wip-9418

mon: add new profiles & audit cap checks

Reviewed-by: Sage Weil <sage@redhat.com>
This commit is contained in:
Sage Weil 2014-10-07 06:32:53 -07:00
commit 2ac2a96898
10 changed files with 588 additions and 57 deletions

View File

@ -262,7 +262,6 @@ function test_tiering()
ceph osd pool delete basepoolA basepoolA --yes-i-really-really-mean-it
}
function test_auth()
{
ceph auth add client.xx mon allow osd "allow *"
@ -287,6 +286,67 @@ function test_auth()
ceph auth del client.xx
}
function test_auth_profiles()
{
ceph auth add client.xx-profile-ro mon 'allow profile read-only'
ceph auth add client.xx-profile-rw mon 'allow profile read-write'
ceph auth add client.xx-profile-rd mon 'allow profile role-definer'
ceph auth export > client.xx.keyring
# read-only is allowed all read-only commands (auth excluded)
ceph -n client.xx-profile-ro -k client.xx.keyring status
ceph -n client.xx-profile-ro -k client.xx.keyring osd dump
ceph -n client.xx-profile-ro -k client.xx.keyring pg dump
ceph -n client.xx-profile-ro -k client.xx.keyring mon dump
ceph -n client.xx-profile-ro -k client.xx.keyring mds dump
# read-only gets access denied for rw commands or auth commands
ceph -n client.xx-profile-ro -k client.xx.keyring log foo >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-ro -k client.xx.keyring osd set noout >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-ro -k client.xx.keyring auth list >& $TMPFILE || true
check_response "EACCES: access denied"
# read-write is allowed for all read-write commands (except auth)
ceph -n client.xx-profile-rw -k client.xx.keyring status
ceph -n client.xx-profile-rw -k client.xx.keyring osd dump
ceph -n client.xx-profile-rw -k client.xx.keyring pg dump
ceph -n client.xx-profile-rw -k client.xx.keyring mon dump
ceph -n client.xx-profile-rw -k client.xx.keyring mds dump
ceph -n client.xx-profile-rw -k client.xx.keyring log foo
ceph -n client.xx-profile-rw -k client.xx.keyring osd set noout
ceph -n client.xx-profile-rw -k client.xx.keyring osd unset noout
# read-write gets access denied for auth commands
ceph -n client.xx-profile-rw -k client.xx.keyring auth list >& $TMPFILE || true
check_response "EACCES: access denied"
# role-definer is allowed RWX 'auth' commands and read-only 'mon' commands
ceph -n client.xx-profile-rd -k client.xx.keyring auth list
ceph -n client.xx-profile-rd -k client.xx.keyring auth export
ceph -n client.xx-profile-rd -k client.xx.keyring auth add client.xx-profile-foo
ceph -n client.xx-profile-rd -k client.xx.keyring status
ceph -n client.xx-profile-rd -k client.xx.keyring osd dump >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-rd -k client.xx.keyring pg dump >& $TMPFILE || true
check_response "EACCES: access denied"
# read-only 'mon' subsystem commands are allowed
ceph -n client.xx-profile-rd -k client.xx.keyring mon dump
# but read-write 'mon' commands are not
ceph -n client.xx-profile-rd -k client.xx.keyring mon add foo 1.1.1.1 >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-rd -k client.xx.keyring mds dump >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-rd -k client.xx.keyring log foo >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-rd -k client.xx.keyring osd set noout >& $TMPFILE || true
check_response "EACCES: access denied"
ceph -n client.xx-profile-rd -k client.xx.keyring auth del client.xx-profile-ro
ceph -n client.xx-profile-rd -k client.xx.keyring auth del client.xx-profile-rw
ceph -n client.xx-profile-rd -k client.xx.keyring auth del client.xx-profile-rd
rm -f client.xx.keyring
}
function test_mon_misc()
{
@ -1030,6 +1090,7 @@ TESTS=(
mon_injectargs_SI
tiering
auth
auth_profiles
mon_misc
mon_mds
mon_mon

View File

@ -13,7 +13,8 @@ for i in ${combinations}; do
done
# add special caps
keymap["blank"]=`ceph auth get-or-create-key client.blank mon 'allow'` || exit 1
# force blank cap with '--force'
keymap["blank"]=`ceph auth get-or-create-key client.blank mon 'allow' --force` || exit 1
keymap["all"]=`ceph auth get-or-create-key client.all mon 'allow *'` || exit 1
tmp=`mktemp`
@ -24,7 +25,10 @@ trap "rm $tmp" INT ERR EXIT QUIT 0
expect() {
set +e
expected_ret=$1
local expected_ret=$1
local ret
shift
cmd=$@
@ -42,10 +46,11 @@ expect() {
}
read_ops() {
local caps=$1
local has_read=1 has_exec=1
local ret
local args
caps=$1
has_read=1
has_exec=1
( echo $caps | grep 'r' ) || has_read=0
( echo $caps | grep 'x' ) || has_exec=0
@ -72,10 +77,12 @@ read_ops() {
write_ops() {
caps=$1
has_read=1
has_write=1
has_exec=1
local caps=$1
local has_read=1 has_write=1 has_exec=1
local ret
local err
local args
( echo $caps | grep 'r' ) || has_read=0
( echo $caps | grep 'w' ) || has_write=0
( echo $caps | grep 'x' ) || has_exec=0
@ -96,9 +103,16 @@ write_ops() {
expect $ret ceph auth add client.foo $args
expect $ret "ceph auth caps client.foo mon 'allow *' $args"
expect $ret ceph auth get-or-create client.admin $args
expect $ret "ceph auth get-or-create client.bar mon 'allow' $args"
echo "wtf -- before: err=$err ret=$ret"
err=$ret
[[ $ret -eq 0 ]] && err=22 # EINVAL
expect $err "ceph auth get-or-create client.bar mon 'allow' $args"
echo "wtf -- after: err=$err ret=$ret"
expect $ret "ceph auth get-or-create client.bar mon 'allow' --force $args"
expect $ret ceph auth get-or-create-key client.admin $args
expect $ret ceph auth get-or-create-key client.baz $args
expect $ret ceph auth del client.bar $args
expect $ret ceph auth del client.baz $args
expect $ret ceph auth del client.foo $args
expect $ret ceph auth import -i $tmp $args
}
@ -118,4 +132,9 @@ for i in ${!keymap[@]}; do
fi
done
# cleanup
for i in ${combinations} blank all; do
ceph auth del client.$i || exit 1
done
echo "OK"

View File

@ -859,6 +859,11 @@ bool AuthMonitor::prepare_command(MMonCommand *m)
!entity_name.empty()) {
// auth get-or-create <name> [mon osdcapa osd osdcapb ...]
if (!valid_caps(caps_vec, &ss)) {
err = -EINVAL;
goto done;
}
// do we have it?
EntityAuth entity_auth;
if (mon->key_server.get_auth(entity, entity_auth)) {
@ -952,6 +957,11 @@ bool AuthMonitor::prepare_command(MMonCommand *m)
goto done;
}
if (!valid_caps(caps_vec, &ss)) {
err = -EINVAL;
goto done;
}
map<string,bufferlist> newcaps;
for (vector<string>::iterator it = caps_vec.begin();
it != caps_vec.end(); it += 2)

View File

@ -124,6 +124,20 @@ private:
pending_auth.push_back(inc);
}
/* validate mon caps ; don't care about caps for other services as
* we don't know how to validate them */
bool valid_caps(const vector<string>& caps, ostream *out) {
for (vector<string>::const_iterator p = caps.begin();
p != caps.end(); p += 2) {
if (!p->empty() && *p != "mon")
continue;
MonCap tmp;
if (!tmp.parse(*(p+1), out))
return false;
}
return true;
}
void on_active();
bool should_propose(double& delay);
void create_initial();

View File

@ -174,6 +174,25 @@ void MonCapGrant::expand_profile(entity_name_t name) const
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
}
if (profile == "read-only") {
// grants READ-ONLY caps monitor-wide
// 'auth' requires MON_CAP_X even for RO, which we do not grant here.
profile_grants.push_back(mon_rwxa_t(MON_CAP_R));
}
if (profile == "read-write") {
// grants READ-WRITE caps monitor-wide
// 'auth' requires MON_CAP_X for all operations, which we do not grant.
profile_grants.push_back(mon_rwxa_t(MON_CAP_R | MON_CAP_W));
}
if (profile == "role-definer") {
// grants ALL caps to the auth subsystem, read-only on the
// monitor subsystem and nothing else.
profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
profile_grants.push_back(MonCapGrant("auth", MON_CAP_ALL));
}
}
mon_rwxa_t MonCapGrant::get_allowed(CephContext *cct,

View File

@ -167,7 +167,8 @@ COMMAND("auth import", "auth import: read keyring file from -i <file>", \
COMMAND("auth add " \
"name=entity,type=CephString " \
"name=caps,type=CephString,n=N,req=false", \
"add auth info for <entity> from input file, or random key if no input given, and/or any caps specified in the command",
"add auth info for <entity> from input file, or random key if no " \
"input is given, and/or any caps specified in the command",
"auth", "rwx", "cli,rest")
COMMAND("auth get-or-create-key " \
"name=entity,type=CephString " \

View File

@ -2449,6 +2449,13 @@ void Monitor::handle_command(MMonCommand *m)
forward_request_leader(m);
return;
}
/* what we perceive as being the service the command falls under */
string service(mon_cmd->module);
dout(25) << __func__ << " prefix='" << prefix
<< "' module='" << module
<< "' service='" << service << "'" << dendl;
bool cmd_is_rw =
(mon_cmd->requires_perm('w') || mon_cmd->requires_perm('x'));
@ -2456,7 +2463,7 @@ void Monitor::handle_command(MMonCommand *m)
// validate user's permissions for requested command
map<string,string> param_str_map;
_generate_command_map(cmdmap, param_str_map);
if (!_allowed_command(session, module, prefix, cmdmap,
if (!_allowed_command(session, service, prefix, cmdmap,
param_str_map, mon_cmd)) {
dout(1) << __func__ << " access denied" << dendl;
(cmd_is_rw ? audit_clog->info() : audit_clog->debug())
@ -3161,41 +3168,30 @@ void Monitor::dispatch(MonSession *s, Message *m, const bool src_is_mon)
{
assert(m != NULL);
/* deal with all messages that do not necessarily need caps */
bool dealt_with = true;
switch (m->get_type()) {
case MSG_ROUTE:
handle_route(static_cast<MRoute*>(m));
// auth
case MSG_MON_GLOBAL_ID:
case CEPH_MSG_AUTH:
/* no need to check caps here */
paxos_service[PAXOS_AUTH]->dispatch((PaxosServiceMessage*)m);
break;
// misc
case CEPH_MSG_MON_GET_MAP:
handle_mon_get_map(static_cast<MMonGetMap*>(m));
case CEPH_MSG_PING:
handle_ping(static_cast<MPing*>(m));
break;
case CEPH_MSG_MON_GET_VERSION:
handle_get_version(static_cast<MMonGetVersion*>(m));
default:
dealt_with = false;
break;
}
if (dealt_with)
return;
case MSG_MON_COMMAND:
handle_command(static_cast<MMonCommand*>(m));
break;
case CEPH_MSG_MON_SUBSCRIBE:
/* FIXME: check what's being subscribed, filter accordingly */
handle_subscribe(static_cast<MMonSubscribe*>(m));
break;
case MSG_MON_PROBE:
handle_probe(static_cast<MMonProbe*>(m));
break;
// Sync (i.e., the new slurp, but on steroids)
case MSG_MON_SYNC:
handle_sync(static_cast<MMonSync*>(m));
break;
case MSG_MON_SCRUB:
handle_scrub(static_cast<MMonScrub*>(m));
break;
/* deal with all messages which caps should be checked somewhere else */
dealt_with = true;
switch (m->get_type()) {
// OSDs
case MSG_OSD_MARK_ME_DOWN:
@ -3203,9 +3199,6 @@ void Monitor::dispatch(MonSession *s, Message *m, const bool src_is_mon)
case MSG_OSD_BOOT:
case MSG_OSD_ALIVE:
case MSG_OSD_PGTEMP:
paxos_service[PAXOS_OSDMAP]->dispatch((PaxosServiceMessage*)m);
break;
case MSG_REMOVE_SNAPS:
paxos_service[PAXOS_OSDMAP]->dispatch((PaxosServiceMessage*)m);
break;
@ -3216,12 +3209,6 @@ void Monitor::dispatch(MonSession *s, Message *m, const bool src_is_mon)
paxos_service[PAXOS_MDSMAP]->dispatch((PaxosServiceMessage*)m);
break;
// auth
case MSG_MON_GLOBAL_ID:
case CEPH_MSG_AUTH:
/* no need to check caps here */
paxos_service[PAXOS_AUTH]->dispatch((PaxosServiceMessage*)m);
break;
// pg
case CEPH_MSG_STATFS:
@ -3239,6 +3226,81 @@ void Monitor::dispatch(MonSession *s, Message *m, const bool src_is_mon)
paxos_service[PAXOS_LOG]->dispatch((PaxosServiceMessage*)m);
break;
// handle_command() does its own caps checking
case MSG_MON_COMMAND:
handle_command(static_cast<MMonCommand*>(m));
break;
default:
dealt_with = false;
break;
}
if (dealt_with)
return;
/* messages we, the Monitor class, need to deal with
* but may be sent by clients. */
if (!s->is_capable("mon", MON_CAP_R)) {
dout(5) << __func__ << " " << m->get_source_inst()
<< " not enough caps for " << *m << " -- dropping"
<< dendl;
goto drop;
}
dealt_with = true;
switch (m->get_type()) {
// misc
case CEPH_MSG_MON_GET_MAP:
handle_mon_get_map(static_cast<MMonGetMap*>(m));
break;
case CEPH_MSG_MON_GET_VERSION:
handle_get_version(static_cast<MMonGetVersion*>(m));
break;
case CEPH_MSG_MON_SUBSCRIBE:
/* FIXME: check what's being subscribed, filter accordingly */
handle_subscribe(static_cast<MMonSubscribe*>(m));
break;
default:
dealt_with = false;
break;
}
if (dealt_with)
return;
if (!src_is_mon) {
dout(1) << __func__ << " unexpected monitor message from"
<< " non-monitor entity " << m->get_source_inst()
<< " " << *m << " -- dropping" << dendl;
goto drop;
}
/* messages that should only be sent by another monitor */
dealt_with = true;
switch (m->get_type()) {
case MSG_ROUTE:
handle_route(static_cast<MRoute*>(m));
break;
case MSG_MON_PROBE:
handle_probe(static_cast<MMonProbe*>(m));
break;
// Sync (i.e., the new slurp, but on steroids)
case MSG_MON_SYNC:
handle_sync(static_cast<MMonSync*>(m));
break;
case MSG_MON_SCRUB:
handle_scrub(static_cast<MMonScrub*>(m));
break;
/* log acks are sent from a monitor we sent the MLog to, and are
never sent by clients to us. */
case MSG_LOGACK:
log_client.handle_log_ack((MLogAck*)m);
m->put();
@ -3313,15 +3375,18 @@ void Monitor::dispatch(MonSession *s, Message *m, const bool src_is_mon)
health_monitor->dispatch(static_cast<MMonHealth *>(m));
break;
case CEPH_MSG_PING:
handle_ping(static_cast<MPing*>(m));
break;
default:
dout(1) << "dropping unexpected " << *m << dendl;
m->put();
dealt_with = false;
break;
}
if (!dealt_with) {
dout(1) << "dropping unexpected " << *m << dendl;
goto drop;
}
return;
drop:
m->put();
}
void Monitor::handle_ping(MPing *m)

View File

@ -728,6 +728,11 @@ ceph_test_mon_workloadgen_SOURCES = test/mon/test_mon_workloadgen.cc
ceph_test_mon_workloadgen_LDADD = $(LIBOS) $(LIBOSDC) $(CEPH_GLOBAL)
bin_DEBUGPROGRAMS += ceph_test_mon_workloadgen
ceph_test_mon_msg_SOURCES = test/mon/test-mon-msg.cc
ceph_test_mon_msg_LDADD = $(LIBOS) $(LIBOSDC) $(CEPH_GLOBAL) $(UNITTEST_LDADD)
ceph_test_mon_msg_CXXFLAGS = $(UNITTEST_CXXFLAGS)
bin_DEBUGPROGRAMS += ceph_test_mon_msg
ceph_test_rados_api_cmd_SOURCES = test/librados/cmd.cc
ceph_test_rados_api_cmd_LDADD = $(LIBRADOS) $(UNITTEST_LDADD) $(RADOS_TEST_LDADD)
ceph_test_rados_api_cmd_CXXFLAGS = $(UNITTEST_CXXFLAGS)

View File

@ -0,0 +1,337 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2014 Red Hat
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <time.h>
#include <stdlib.h>
#include <map>
#include "global/global_init.h"
#include "global/global_context.h"
#include "common/ceph_argparse.h"
#include "common/dout.h"
#include "common/debug.h"
#include "common/Cond.h"
#include "common/Mutex.h"
#include "common/Timer.h"
#include "common/errno.h"
#include "mon/MonClient.h"
#include "msg/Dispatcher.h"
#include "include/err.h"
#include <boost/scoped_ptr.hpp>
#include "gtest/gtest.h"
#include "common/config.h"
#include "include/assert.h"
#include "messages/MMonProbe.h"
#include "messages/MRoute.h"
#include "messages/MGenericMessage.h"
#include "messages/MMonJoin.h"
#define dout_subsys ceph_subsys_
#undef dout_prefix
#define dout_prefix *_dout << "test-mon-msg "
class MonClientHelper : public Dispatcher
{
protected:
CephContext *cct;
Messenger *msg;
MonClient monc;
Mutex lock;
set<int> wanted;
public:
MonClientHelper(CephContext *cct_)
: Dispatcher(cct_),
cct(cct_),
monc(cct_),
lock("mon-msg-test::lock")
{ }
int post_init() {
dout(1) << __func__ << dendl;
if (!msg)
return -EINVAL;
msg->add_dispatcher_tail(this);
return 0;
}
int init_messenger() {
dout(1) << __func__ << dendl;
msg = Messenger::create(cct, entity_name_t::CLIENT(-1),
"test-mon-msg", 0);
assert(msg != NULL);
msg->set_default_policy(Messenger::Policy::lossy_client(0,0));
dout(0) << __func__ << " starting messenger at "
<< msg->get_myaddr() << dendl;
msg->start();
return 0;
}
int init_monc() {
dout(1) << __func__ << dendl;
assert(msg != NULL);
int err = monc.build_initial_monmap();
if (err < 0) {
derr << __func__ << " error building monmap: "
<< cpp_strerror(err) << dendl;
return err;
}
monc.set_messenger(msg);
msg->add_dispatcher_head(&monc);
monc.set_want_keys(CEPH_ENTITY_TYPE_MON);
err = monc.init();
if (err < 0) {
derr << __func__ << " monc init failed: "
<< cpp_strerror(err) << dendl;
goto fail;
}
err = monc.authenticate();
if (err < 0) {
derr << __func__ << " monc auth failed: "
<< cpp_strerror(err) << dendl;
goto fail_monc;
}
monc.wait_auth_rotating(30.0);
monc.renew_subs();
dout(0) << __func__ << " finished" << dendl;
return 0;
fail_monc:
derr << __func__ << " failing monc" << dendl;
monc.shutdown();
fail:
return err;
}
void shutdown_messenger() {
dout(0) << __func__ << dendl;
msg->shutdown();
msg->wait();
}
void shutdown_monc() {
dout(0) << __func__ << dendl;
monc.shutdown();
}
void shutdown() {
dout(0) << __func__ << dendl;
shutdown_monc();
shutdown_messenger();
}
MonMap *get_monmap() {
return &monc.monmap;
}
int init() {
int err = init_messenger();
if (err < 0)
goto fail;
err = init_monc();
if (err < 0)
goto fail_msgr;
err = post_init();
if (err < 0)
goto fail_monc;
return 0;
fail_monc:
shutdown_monc();
fail_msgr:
shutdown_messenger();
fail:
return err;
}
virtual void handle_wanted(Message *m) { }
bool handle_message(Message *m) {
dout(1) << __func__ << " " << *m << dendl;
if (!is_wanted(m)) {
dout(10) << __func__ << " not wanted" << dendl;
return false;
}
handle_wanted(m);
m->put();
return true;
}
bool ms_dispatch(Message *m) {
return handle_message(m);
}
void ms_handle_connect(Connection *con) { }
void ms_handle_remote_reset(Connection *con) { }
bool ms_handle_reset(Connection *con) { return false; }
bool is_wanted(Message *m) {
dout(20) << __func__ << " " << *m << " type " << m->get_type() << dendl;
return (wanted.find(m->get_type()) != wanted.end());
}
void add_wanted(int t) {
dout(20) << __func__ << " type " << t << dendl;
wanted.insert(t);
}
void rm_wanted(int t) {
dout(20) << __func__ << " type " << t << dendl;
wanted.erase(t);
}
void send_message(Message *m) {
dout(15) << __func__ << " " << *m << dendl;
monc.send_mon_message(m);
}
void wait() { msg->wait(); }
};
class MonMsgTest : public MonClientHelper,
public ::testing::Test
{
protected:
int reply_type;
Message *reply_msg;
Mutex lock;
Cond cond;
MonMsgTest() :
MonClientHelper(g_ceph_context),
lock("lock") { }
public:
virtual void SetUp() {
reply_type = -1;
if (reply_msg) {
reply_msg->put();
reply_msg = NULL;
}
ASSERT_EQ(init(), 0);
}
virtual void TearDown() {
shutdown();
if (reply_msg) {
reply_msg->put();
reply_msg = NULL;
}
}
void handle_wanted(Message *m) {
lock.Lock();
// caller will put() after they call us, so hold on to a ref
m->get();
reply_msg = m;
cond.Signal();
lock.Unlock();
}
Message *send_wait_reply(Message *m, int t, double timeout=30.0) {
lock.Lock();
reply_type = t;
add_wanted(t);
send_message(m);
int err = 0;
if (timeout > 0) {
utime_t cond_timeout;
cond_timeout.set_from_double(timeout);
utime_t s = ceph_clock_now(g_ceph_context);
err = cond.WaitInterval(g_ceph_context, lock, cond_timeout);
utime_t e = ceph_clock_now(g_ceph_context);
dout(20) << __func__ << " took " << (e-s) << " seconds" << dendl;
} else {
err = cond.Wait(lock);
}
rm_wanted(t);
lock.Unlock();
if (err > 0) {
dout(20) << __func__ << " error: " << cpp_strerror(err) << dendl;
return (Message*)((long)-err);
}
if (!reply_msg)
dout(20) << __func__ << " reply_msg is NULL" << dendl;
else
dout(20) << __func__ << " reply_msg " << *reply_msg << dendl;
return reply_msg;
}
};
TEST_F(MonMsgTest, MMonProbeTest)
{
Message *m = new MMonProbe(get_monmap()->fsid,
MMonProbe::OP_PROBE, "b", false);
Message *r = send_wait_reply(m, MSG_MON_PROBE);
ASSERT_NE(IS_ERR(r), 0);
ASSERT_EQ(PTR_ERR(r), -ETIMEDOUT);
}
TEST_F(MonMsgTest, MRouteTest)
{
Message *payload = new MGenericMessage(CEPH_MSG_SHUTDOWN);
MRoute *m = new MRoute;
m->msg = payload;
m->dest = msg->get_myinst();
Message *r = send_wait_reply(m, CEPH_MSG_SHUTDOWN);
// we want an error
ASSERT_NE(IS_ERR(r), 0);
ASSERT_EQ(PTR_ERR(r), -ETIMEDOUT);
}
/* MMonScrub and MMonSync have other safeguards in place that prevent
* us from actually receiving a reply even if the message is handled
* by the monitor due to lack of cap checking.
*/
TEST_F(MonMsgTest, MMonJoin)
{
Message *m = new MMonJoin(get_monmap()->fsid, string("client"),
msg->get_myaddr());
send_wait_reply(m, MSG_MON_PAXOS, 10.0);
int r = monc.get_monmap();
ASSERT_EQ(r, 0);
ASSERT_FALSE(monc.monmap.contains("client"));
}
int main(int argc, char *argv[])
{
vector<const char*> def_args;
vector<const char*> args;
argv_to_vec(argc, (const char **)argv, args);
global_init(&def_args, args,
CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY,
0);
common_init_finish(g_ceph_context);
g_ceph_context->_conf->apply_changes(NULL);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -33,7 +33,7 @@ function vstart_setup()
export LC_ALL=C # some tests are vulnerable to i18n
MON=1 OSD=3 ./vstart.sh \
-o 'paxos propose interval = 0.01' \
-n -X -l mon osd || return 1
-n -l mon osd || return 1
export PATH=.:$PATH
export CEPH_CONF=ceph.conf