summaryrefslogtreecommitdiffstats
path: root/src/test/osd/ceph_test_osd_stale_read.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/osd/ceph_test_osd_stale_read.cc')
-rw-r--r--src/test/osd/ceph_test_osd_stale_read.cc180
1 files changed, 180 insertions, 0 deletions
diff --git a/src/test/osd/ceph_test_osd_stale_read.cc b/src/test/osd/ceph_test_osd_stale_read.cc
new file mode 100644
index 000000000..41b5aa6d3
--- /dev/null
+++ b/src/test/osd/ceph_test_osd_stale_read.cc
@@ -0,0 +1,180 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include "gtest/gtest.h"
+
+#include "mds/mdstypes.h"
+#include "include/buffer.h"
+#include "include/rbd_types.h"
+#include "include/rados/librados.h"
+#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "include/types.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/ceph_argparse.h"
+#include "common/common_init.h"
+#include "common/Cond.h"
+#include "json_spirit/json_spirit.h"
+
+#include <errno.h>
+#include <map>
+#include <sstream>
+#include <string>
+
+using namespace librados;
+using std::map;
+using std::ostringstream;
+using std::string;
+
+int get_primary_osd(Rados& rados, const string& pool_name,
+ const string& oid, int *pprimary)
+{
+ bufferlist inbl;
+ string cmd = string("{\"prefix\": \"osd map\",\"pool\":\"")
+ + pool_name
+ + string("\",\"object\": \"")
+ + oid
+ + string("\",\"format\": \"json\"}");
+ bufferlist outbl;
+ if (int r = rados.mon_command(cmd, inbl, &outbl, nullptr);
+ r < 0) {
+ return r;
+ }
+ string outstr(outbl.c_str(), outbl.length());
+ json_spirit::Value v;
+ if (!json_spirit::read(outstr, v)) {
+ cerr <<" unable to parse json " << outstr << std::endl;
+ return -1;
+ }
+
+ json_spirit::Object& o = v.get_obj();
+ for (json_spirit::Object::size_type i=0; i<o.size(); i++) {
+ json_spirit::Pair& p = o[i];
+ if (p.name_ == "acting_primary") {
+ cout << "primary = " << p.value_.get_int() << std::endl;
+ *pprimary = p.value_.get_int();
+ return 0;
+ }
+ }
+ cerr << "didn't find primary in " << outstr << std::endl;
+ return -1;
+}
+
+int fence_osd(Rados& rados, int osd)
+{
+ bufferlist inbl, outbl;
+ string cmd("{\"prefix\": \"injectargs\",\"injected_args\":["
+ "\"--ms-blackhole-osd\", "
+ "\"--ms-blackhole-mon\"]}");
+ return rados.osd_command(osd, cmd, inbl, &outbl, NULL);
+}
+
+int mark_down_osd(Rados& rados, int osd)
+{
+ bufferlist inbl, outbl;
+ string cmd("{\"prefix\": \"osd down\",\"ids\":[\"" +
+ stringify(osd) + "\"]}");
+ return rados.mon_command(cmd, inbl, &outbl, NULL);
+}
+
+TEST(OSD, StaleRead) {
+ // create two rados instances, one pool
+ Rados rados1, rados2;
+ IoCtx ioctx1, ioctx2;
+ int r;
+
+ r = rados1.init_with_context(g_ceph_context);
+ ASSERT_EQ(0, r);
+ r = rados1.connect();
+ ASSERT_EQ(0, r);
+
+ srand(time(0));
+ string pool_name = "read-hole-test-" + stringify(rand());
+ r = rados1.pool_create(pool_name.c_str());
+ ASSERT_EQ(0, r);
+
+ r = rados1.ioctx_create(pool_name.c_str(), ioctx1);
+ ASSERT_EQ(0, r);
+
+ r = rados2.init_with_context(g_ceph_context);
+ ASSERT_EQ(0, r);
+ r = rados2.connect();
+ ASSERT_EQ(0, r);
+ r = rados2.ioctx_create(pool_name.c_str(), ioctx2);
+ ASSERT_EQ(0, r);
+
+ string oid = "foo";
+ bufferlist one;
+ one.append("one");
+ {
+ cout << "client1: writing 'one'" << std::endl;
+ r = ioctx1.write_full(oid, one);
+ ASSERT_EQ(0, r);
+ }
+
+ // make sure 2 can read it
+ {
+ cout << "client2: reading 'one'" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('o', bl[0]);
+ ASSERT_EQ('n', bl[1]);
+ ASSERT_EQ('e', bl[2]);
+ }
+
+ // find the primary
+ int primary;
+ r = get_primary_osd(rados1, pool_name, oid, &primary);
+ ASSERT_EQ(0, r);
+
+ // fence it
+ cout << "client1: fencing primary" << std::endl;
+ fence_osd(rados1, primary);
+ mark_down_osd(rados1, primary);
+ rados1.wait_for_latest_osdmap();
+
+ // should still be able to read the old value on 2
+ {
+ cout << "client2: reading 'one' again from old primary" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('o', bl[0]);
+ ASSERT_EQ('n', bl[1]);
+ ASSERT_EQ('e', bl[2]);
+ }
+
+ // update object on 1
+ bufferlist two;
+ two.append("two");
+ {
+ cout << "client1: writing 'two' to new acting set" << std::endl;
+ r = ioctx1.write_full(oid, two);
+ ASSERT_EQ(0, r);
+ }
+
+ // make sure we can't still read the old value on 2
+ {
+ cout << "client2: reading again from old primary" << std::endl;
+ bufferlist bl;
+ r = ioctx2.read(oid, bl, 3, 0);
+ ASSERT_EQ(3, r);
+ ASSERT_EQ('t', bl[0]);
+ ASSERT_EQ('w', bl[1]);
+ ASSERT_EQ('o', bl[2]);
+ }
+
+ rados1.shutdown();
+ rados2.shutdown();
+}
+
+int main(int argc, char **argv) {
+ vector<const char*> args;
+ argv_to_vec(argc, (const char **)argv, args);
+ auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
+ CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}