From: Sage Weil Date: Tue, 5 Jan 2016 20:00:12 +0000 (-0500) Subject: [g]hobject_t: normalize operator<< result, and implement parse X-Git-Tag: v10.0.4~154^2~24 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=efa88a4560af7bf233c3d4eb911fac6c4f4cf118;p=ceph.git [g]hobject_t: normalize operator<< result, and implement parse - print hash in *bitwise reverse* order - include all fields, every time - use : delimitor for hobject_t - use @ delimitor for ghobject_t - escape unprintable characters <32, >=127, :, %. - also /, for fuse layer's benefit Signed-off-by: Sage Weil --- diff --git a/src/common/hobject.cc b/src/common/hobject.cc index e651047510cd..9e84219a8fac 100644 --- a/src/common/hobject.cc +++ b/src/common/hobject.cc @@ -194,28 +194,124 @@ void hobject_t::generate_test_instances(list& o) CEPH_SNAPDIR, 910, 1, "n2")); } +static void append_out_escaped(const string &in, string *out) +{ + for (string::const_iterator i = in.begin(); i != in.end(); ++i) { + if (*i == '%' || *i == ':' || *i == '/' || *i < 32 || *i >= 127) { + out->push_back('%'); + char buf[3]; + snprintf(buf, sizeof(buf), "%02x", (int)(unsigned char)*i); + out->append(buf); + } else { + out->push_back(*i); + } + } +} + +static const char *decode_out_escaped(const char *in, string *out) +{ + while (*in && *in != ':') { + if (*in == '%') { + ++in; + char buf[3]; + buf[0] = *in; + ++in; + buf[1] = *in; + buf[2] = 0; + int v = strtol(buf, NULL, 16); + out->push_back(v); + } else { + out->push_back(*in); + } + ++in; + } + return in; +} + ostream& operator<<(ostream& out, const hobject_t& o) { if (o == hobject_t()) return out << "MIN"; if (o.is_max()) return out << "MAX"; - out << o.pool << '/'; + out << o.pool << ':'; out << std::hex; out.width(8); out.fill('0'); - out << o.get_hash(); + out << o.get_bitwise_key_u32(); // << '~' << o.get_hash(); out.width(0); out.fill(' '); out << std::dec; - if (o.nspace.length()) - out << ":" << o.nspace; - if (o.get_key().length()) - out << "." << o.get_key(); - out << "/" << o.oid << "/" << o.snap; + out << ':'; + string v; + append_out_escaped(o.nspace, &v); + v.push_back(':'); + append_out_escaped(o.get_key(), &v); + v.push_back(':'); + append_out_escaped(o.oid.name, &v); + out << v << ':' << o.snap; return out; } +bool hobject_t::parse(const string &s) +{ + if (s == "MIN") { + *this = hobject_t(); + return true; + } + if (s == "MAX") { + *this = hobject_t::get_max(); + return true; + } + + const char *start = s.c_str(); + long long po; + unsigned h; + int r = sscanf(start, "%lld:%x:", &po, &h); + if (r != 2) + return false; + for (; *start && *start != ':'; ++start) ; + for (++start; *start && isxdigit(*start); ++start) ; + if (*start != ':') + return false; + + string ns, k, name; + const char *p = decode_out_escaped(start + 1, &ns); + if (*p != ':') + return false; + p = decode_out_escaped(p + 1, &k); + if (*p != ':') + return false; + p = decode_out_escaped(p + 1, &name); + if (*p != ':') + return false; + start = p + 1; + + unsigned long long sn; + if (strncmp(start, "head", 4) == 0) { + sn = CEPH_NOSNAP; + start += 4; + if (*start != 0) + return false; + } else { + r = sscanf(start, "%llx", &sn); + if (r != 1) + return false; + for (++start; *start && isxdigit(*start); ++start) ; + if (*start) + return false; + } + + max = false; + pool = po; + set_hash(_reverse_bits(h)); + nspace = ns; + oid.name = name; + set_key(k); + snap = sn; + return true; +} + int cmp_nibblewise(const hobject_t& l, const hobject_t& r) { if (l.max < r.max) @@ -402,14 +498,65 @@ ostream& operator<<(ostream& out, const ghobject_t& o) if (o.is_max()) return out << "GHMAX"; if (o.shard_id != shard_id_t::NO_SHARD) - out << std::hex << o.shard_id << std::dec << ":"; - out << o.hobj; - if (o.generation != ghobject_t::NO_GEN) { - out << "/" << std::hex << (unsigned)(o.generation) << std::dec; - } + out << std::hex << o.shard_id << std::dec; + out << '@' << o.hobj << '@'; + if (o.generation != ghobject_t::NO_GEN) + out << std::hex << (unsigned long long)(o.generation) << std::dec; return out; } +bool ghobject_t::parse(const string& s) +{ + if (s == "GHMIN") { + *this = ghobject_t(); + return true; + } + if (s == "GHMAX") { + *this = ghobject_t::get_max(); + return true; + } + + // look for shard@ prefix + const char *start = s.c_str(); + const char *p; + int sh = shard_id_t::NO_SHARD; + for (p = start; *p && isxdigit(*p); ++p) ; + if (!*p && *p != '@') + return false; + if (p > start) { + int r = sscanf(s.c_str(), "%x", &sh); + if (r < 1) + return false; + start = p + 1; + } else { + ++start; + } + + // look for @generation suffix + long long unsigned g = NO_GEN; + const char *last = start + strlen(start) - 1; + p = last; + while (isxdigit(*p)) + p--; + if (*p != '@') + return false; + if (p < last) { + sscanf(p + 1, "%llx", &g); + } + + string inner(start, p - start); + hobject_t h; + if (!h.parse(inner)) { + return false; + } + + shard_id = shard_id_t(sh); + hobj = h; + generation = g; + max = false; + return true; +} + int cmp_nibblewise(const ghobject_t& l, const ghobject_t& r) { if (l.max < r.max) diff --git a/src/common/hobject.h b/src/common/hobject.h index 601af40fa43f..d71554422895 100644 --- a/src/common/hobject.h +++ b/src/common/hobject.h @@ -258,6 +258,8 @@ public: return nspace; } + bool parse(const string& s); + void encode(bufferlist& bl) const; void decode(bufferlist::iterator& bl); void decode(json_spirit::Value& v); @@ -415,6 +417,8 @@ public: shard_id = s; } + bool parse(const string& s); + // maximum sorted value. static ghobject_t get_max() { ghobject_t h; diff --git a/src/test/osd/osd-scrub-snaps.sh b/src/test/osd/osd-scrub-snaps.sh index bf59570b9f51..71eeb19a4ae2 100755 --- a/src/test/osd/osd-scrub-snaps.sh +++ b/src/test/osd/osd-scrub-snaps.sh @@ -175,28 +175,28 @@ function TEST_scrub_snaps() { kill_daemons $dir || return 1 declare -a err_strings - err_strings[0]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/2acecc8b/obj10/1 is missing in clone_overlap" - err_strings[1]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/7 no '_' attr" - err_strings[2]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/7 is an unexpected clone" - err_strings[3]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/4 on disk size [(]4608[)] does not match object info size [(]512[)] adjusted for ondisk to [(]512[)]" - err_strings[4]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/head expected clone [0-9]*/666934a3/obj5/2" - err_strings[5]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/head expected clone [0-9]*/666934a3/obj5/1" - err_strings[6]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 [0-9]*/666934a3/obj5/head 2 missing clone[(]s[)]" - err_strings[7]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/d3a9faf5/obj12/head snapset.head_exists=false, but head exists" - err_strings[8]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/8df7eaa5/obj8/head snaps.seq not set" - err_strings[9]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/5c889059/obj7/head snapset.head_exists=false, but head exists" - err_strings[10]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/5c889059/obj7/1 is an unexpected clone" - err_strings[11]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/61f68bb1/obj3/head on disk size [(]3840[)] does not match object info size [(]768[)] adjusted for ondisk to [(]768[)]" - err_strings[12]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/83425cc4/obj6/1 is an unexpected clone" - err_strings[13]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/3f1ee208/obj2/snapdir no 'snapset' attr" - err_strings[14]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 [0-9]*/3f1ee208/obj2/7 clone ignored due to missing snapset" - err_strings[15]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 [0-9]*/3f1ee208/obj2/4 clone ignored due to missing snapset" - err_strings[16]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/a8759770/obj4/snapdir expected clone [0-9]*/a8759770/obj4/7" - err_strings[17]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 [0-9]*/a8759770/obj4/snapdir 1 missing clone[(]s[)]" - err_strings[18]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/6cf8deff/obj1/1 is an unexpected clone" - err_strings[19]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/e478ac7f/obj9/1 is missing in clone_size" - err_strings[20]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/29547577/obj11/1 is an unexpected clone" - err_strings[21]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 [0-9]*/94122507/obj14/1 size 1032 != clone_size 1033" + err_strings[0]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*::obj10:.* is missing in clone_overlap" + err_strings[1]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*::obj5:7 no '_' attr" + err_strings[2]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*::obj5:7 is an unexpected clone" + err_strings[3]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*::obj5:4 on disk size [(]4608[)] does not match object info size [(]512[)] adjusted for ondisk to [(]512[)]" + err_strings[4]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj5:head expected clone .*:::obj5:2" + err_strings[5]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj5:head expected clone .*:::obj5:1" + err_strings[6]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 .*:::obj5:head 2 missing clone[(]s[)]" + err_strings[7]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj12:head snapset.head_exists=false, but head exists" + err_strings[8]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj8:head snaps.seq not set" + err_strings[9]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj7:head snapset.head_exists=false, but head exists" + err_strings[10]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj7:1 is an unexpected clone" + err_strings[11]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj3:head on disk size [(]3840[)] does not match object info size [(]768[)] adjusted for ondisk to [(]768[)]" + err_strings[12]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj6:1 is an unexpected clone" + err_strings[13]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj2:snapdir no 'snapset' attr" + err_strings[14]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 .*:::obj2:7 clone ignored due to missing snapset" + err_strings[15]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 .*:::obj2:4 clone ignored due to missing snapset" + err_strings[16]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj4:snapdir expected clone .*:::obj4:7" + err_strings[17]="log_channel[(]cluster[)] log [[]INF[]] : scrub [0-9]*[.]0 .*:::obj4:snapdir 1 missing clone[(]s[)]" + err_strings[18]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj1:1 is an unexpected clone" + err_strings[19]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj9:1 is missing in clone_size" + err_strings[20]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj11:1 is an unexpected clone" + err_strings[21]="log_channel[(]cluster[)] log [[]ERR[]] : scrub [0-9]*[.]0 .*:::obj14:1 size 1032 != clone_size 1033" err_strings[22]="log_channel[(]cluster[)] log [[]ERR[]] : [0-9]*[.]0 scrub 19 errors" for i in `seq 0 ${#err_strings[@]}` diff --git a/src/test/osd/types.cc b/src/test/osd/types.cc index 67d6ac8ea15a..276083db560c 100644 --- a/src/test/osd/types.cc +++ b/src/test/osd/types.cc @@ -1391,6 +1391,37 @@ TEST(coll_t, assigment) { ASSERT_NE(left.c_str(), middle.c_str()); } +TEST(hobject_t, parse) { + const char *v[] = { + "MIN", + "MAX", + "-1:60c2fa6d:::inc_osdmap.1:0", + "-1:60c2fa6d:::inc_osdmap.1:333", + "0:00000000::::head", + "1:00000000:nspace:key:obj:head", + "-40:00000000:nspace::obj:head", + "20:00000000::key:obj:head", + "20:00000000:::o%fdj:head", + "20:00000000:::o%02fdj:head", + "20:00000000:::_zero_%00_:head", + NULL + }; + + for (unsigned i=0; v[i]; ++i) { + hobject_t o; + bool b = o.parse(v[i]); + if (!b) { + cout << "failed to parse " << v[i] << std::endl; + ASSERT_TRUE(false); + } + string s = stringify(o); + if (s != v[i]) { + cout << v[i] << " -> " << o << " -> " << s << std::endl; + ASSERT_EQ(s, string(v[i])); + } + } +} + TEST(ghobject_t, cmp) { ghobject_t min; ghobject_t sep; @@ -1407,6 +1438,37 @@ TEST(ghobject_t, cmp) { ASSERT_TRUE(cmp_bitwise(o, sep) > 0); } +TEST(ghobject_t, parse) { + const char *v[] = { + "GHMIN", + "GHMAX", + "13@0:00000000::::head@", + "13@0:00000000::::head@deadbeef", + "@-1:60c2fa6d:::inc_osdmap.1:333@deadbeef", + "@-1:60c2fa6d:::inc%02osdmap.1:333@deadbeef", + "@-1:60c2fa6d:::inc_osdmap.1:333@", + "1@MIN@deadbeefff", + "1@MAX@", + "@MAX@123", + "@-40:00000000:nspace::obj:head@", + NULL + }; + + for (unsigned i=0; v[i]; ++i) { + ghobject_t o; + bool b = o.parse(v[i]); + if (!b) { + cout << "failed to parse " << v[i] << std::endl; + ASSERT_TRUE(false); + } + string s = stringify(o); + if (s != v[i]) { + cout << v[i] << " -> " << o << " -> " << s << std::endl; + ASSERT_EQ(s, string(v[i])); + } + } +} + TEST(pool_opts_t, invalid_opt) { EXPECT_FALSE(pool_opts_t::is_opt_name("INVALID_OPT")); EXPECT_DEATH(pool_opts_t::get_opt_desc("INVALID_OPT"), "");