using ceph::Formatter;
+void mon_info_t::encode(bufferlist& bl, uint64_t features) const
+{
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ ::encode(public_addr, bl, features);
+ ENCODE_FINISH(bl);
+}
+
+void mon_info_t::decode(bufferlist::iterator& p)
+{
+ DECODE_START(1, p);
+ ::decode(name, p);
+ ::decode(public_addr, p);
+ DECODE_FINISH(p);
+}
+
+void mon_info_t::print(ostream& out) const
+{
+ out << "mon." << name
+ << " public " << public_addr;
+}
+
void MonMap::encode(bufferlist& blist, uint64_t con_features) const
{
+ /* we keep the mon_addr map when encoding to ensure compatibility
+ * with clients and other monitors that do not yet support the 'mons'
+ * map. This map keeps its original behavior, containing a mapping of
+ * monitor id (i.e., 'foo' in 'mon.foo') to the monitor's public
+ * address -- which is obtained from the public address of each entry
+ * in the 'mons' map.
+ */
+ map<string,entity_addr_t> mon_addr;
+ for (map<string,mon_info_t>::const_iterator p = mon_info.begin();
+ p != mon_info.end();
+ ++p) {
+ mon_addr[p->first] = p->second.public_addr;
+ }
+
if ((con_features & CEPH_FEATURE_MONNAMES) == 0) {
__u16 v = 1;
::encode(v, blist);
::encode(created, blist);
}
- ENCODE_START(4, 3, blist);
+ ENCODE_START(5, 3, blist);
::encode_raw(fsid, blist);
::encode(epoch, blist);
::encode(mon_addr, blist, con_features);
::encode(created, blist);
::encode(persistent_features, blist);
::encode(optional_features, blist);
+ // this superseeds 'mon_addr'
+ ::encode(mon_info, blist, con_features);
ENCODE_FINISH(blist);
}
void MonMap::decode(bufferlist::iterator &p)
{
- DECODE_START_LEGACY_COMPAT_LEN_16(4, 3, 3, p);
+ map<string,entity_addr_t> mon_addr;
+ DECODE_START_LEGACY_COMPAT_LEN_16(5, 3, 3, p);
::decode_raw(fsid, p);
::decode(epoch, p);
if (struct_v == 1) {
::decode(persistent_features, p);
::decode(optional_features, p);
}
-
+ if (struct_v >= 5) {
+ ::decode(mon_info, p);
+ }
DECODE_FINISH(p);
+ sanitize_mons(mon_addr);
calc_ranks();
}
void MonMap::print_summary(ostream& out) const
{
out << "e" << epoch << ": "
- << mon_addr.size() << " mons at "
- << mon_addr;
+ << mon_info.size() << " mons at {";
+ // the map that we used to print, as it was, no longer
+ // maps strings to the monitor's public address, but to
+ // mon_info_t instead. As such, print the map in a way
+ // that keeps the expected format.
+ bool has_printed = false;
+ for (map<string,mon_info_t>::const_iterator p = mon_info.begin();
+ p != mon_info.end();
+ ++p) {
+ if (has_printed)
+ out << ",";
+ out << p->first << "=" << p->second.public_addr;
+ has_printed = true;
+ }
+ out << "}";
}
void MonMap::print(ostream& out) const
out << "last_changed " << last_changed << "\n";
out << "created " << created << "\n";
unsigned i = 0;
- for (map<entity_addr_t,string>::const_iterator p = addr_name.begin();
- p != addr_name.end();
- ++p)
- out << i++ << ": " << p->first << " mon." << p->second << "\n";
+ for (vector<string>::const_iterator p = ranks.begin();
+ p != ranks.end();
+ ++p) {
+ out << i++ << ": " << get_addr(*p) << " mon." << *p << "\n";
+ }
}
void MonMap::dump(Formatter *f) const
f->close_section();
f->open_array_section("mons");
int i = 0;
- for (map<entity_addr_t,string>::const_iterator p = addr_name.begin();
- p != addr_name.end();
+ for (vector<string>::const_iterator p = ranks.begin();
+ p != ranks.end();
++p, ++i) {
f->open_object_section("mon");
f->dump_int("rank", i);
- f->dump_string("name", p->second);
- f->dump_stream("addr") << p->first;
+ f->dump_string("name", *p);
+ f->dump_stream("addr") << get_addr(*p);
+ f->dump_stream("public_addr") << get_addr(*p);
f->close_section();
}
f->close_section();
class Formatter;
}
+struct mon_info_t {
+ /**
+ * monitor name
+ *
+ * i.e., 'foo' in 'mon.foo'
+ */
+ string name;
+ /**
+ * monitor's public address
+ *
+ * public facing address, traditionally used to communicate with all clients
+ * and other monitors.
+ */
+ entity_addr_t public_addr;
+
+ mon_info_t(string &n, entity_addr_t& p_addr)
+ : name(n), public_addr(p_addr)
+ { }
+
+ mon_info_t() { }
+
+
+ void encode(bufferlist& bl, uint64_t features) const;
+ void decode(bufferlist::iterator& p);
+ void print(ostream& out) const;
+};
+WRITE_CLASS_ENCODER_FEATURES(mon_info_t)
+
+inline ostream& operator<<(ostream& out, const mon_info_t& mon) {
+ mon.print(out);
+ return out;
+}
class MonMap {
public:
epoch_t epoch; // what epoch/version of the monmap
uuid_d fsid;
- map<string, entity_addr_t> mon_addr;
utime_t last_changed;
utime_t created;
- map<entity_addr_t,string> addr_name;
- vector<string> rank_name;
- vector<entity_addr_t> rank_addr;
+ map<string, mon_info_t> mon_info;
+ map<entity_addr_t, string> addr_mons;
+
+ vector<string> ranks;
/**
* Persistent Features are all those features that once set on a
return (persistent_features | optional_features);
}
+ void sanitize_mons(map<string,entity_addr_t>& o) {
+
+ // if mon_info is populated, it means we decoded a map encoded
+ // by someone who understands the new format (i.e., is able to
+ // encode 'mon_info'). This means they must also have provided
+ // a properly populated 'mon_addr' (which we have dropped with
+ // this patch), 'o' being the contents of said map. In this
+ // case, 'o' must have the same number of entries as 'mon_info'.
+ //
+ // Also, for each entry in 'o', there has to be a matching
+ // 'mon_info' entry, properly populated with a name and a matching
+ // 'public_addr'.
+ //
+ // OTOH, if 'mon_info' is not populated, it means the one that
+ // originally encoded the map does not know the new format, and
+ // 'o' will be our only source of info about the monitors in the
+ // cluster -- and we will use it to populate our 'mon_info' map.
+
+ bool has_mon_info = false;
+ if (mon_info.size() > 0) {
+ assert(o.size() == mon_info.size());
+ has_mon_info = true;
+ }
+
+ for (map<string, entity_addr_t>::const_iterator p = o.begin();
+ p != o.end();
+ ++p) {
+
+ // make sure the info we have is accurate
+ if (has_mon_info) {
+ assert(mon_info.count(p->first));
+ assert(mon_info[p->first].name == p->first);
+ assert(mon_info[p->first].public_addr == p->second);
+ continue;
+ }
+
+ mon_info_t &m = mon_info[p->first];
+ m.name = p->first;
+ m.public_addr = p->second;
+ }
+ }
+
void calc_ranks() {
- rank_name.resize(mon_addr.size());
- rank_addr.resize(mon_addr.size());
- addr_name.clear();
- for (map<string,entity_addr_t>::iterator p = mon_addr.begin();
- p != mon_addr.end();
- ++p) {
- assert(addr_name.count(p->second) == 0);
- addr_name[p->second] = p->first;
+
+ ranks.resize(mon_info.size());
+ addr_mons.clear();
+
+ // Used to order entries according to public_addr, because that's
+ // how the ranks are expected to be ordered by. We may expand this
+ // later on, according to some other criteria, by specifying a
+ // different comparator.
+ //
+ // Please note that we use a 'set' here instead of resorting to
+ // std::sort() because we need more info than that's available in
+ // the vector. The vector will thus be ordered by, e.g., public_addr
+ // while only containing the names of each individual monitor.
+ // The only way of achieving this with std::sort() would be to first
+ // insert every mon_info_t entry into a vector 'foo', std::sort() 'foo'
+ // with custom comparison functions, and then copy each invidual entry
+ // to a new vector. Unless there's a simpler way, we don't think the
+ // added complexity makes up for the additional memory usage of a 'set'.
+ set<mon_info_t, rank_cmp> tmp;
+
+ for (map<string,mon_info_t>::iterator p = mon_info.begin();
+ p != mon_info.end();
+ ++p) {
+ mon_info_t &m = p->second;
+ tmp.insert(m);
+
+ // populate addr_mons
+ assert(addr_mons.count(m.public_addr) == 0);
+ addr_mons[m.public_addr] = m.name;
}
+
+ // map the set to the actual ranks etc
unsigned i = 0;
- for (map<entity_addr_t,string>::iterator p = addr_name.begin();
- p != addr_name.end();
- ++p, i++) {
- rank_name[i] = p->second;
- rank_addr[i] = p->first;
+ for (set<mon_info_t>::iterator p = tmp.begin();
+ p != tmp.end();
+ ++p, ++i) {
+ ranks[i] = p->name;
}
}
uuid_d& get_fsid() { return fsid; }
- unsigned size() {
- return mon_addr.size();
+ unsigned size() const {
+ return mon_info.size();
}
- epoch_t get_epoch() { return epoch; }
+ epoch_t get_epoch() const { return epoch; }
void set_epoch(epoch_t e) { epoch = e; }
+ /**
+ * Obtain list of public facing addresses
+ *
+ * @param ls list to populate with the monitors' addresses
+ */
void list_addrs(list<entity_addr_t>& ls) const {
- for (map<string,entity_addr_t>::const_iterator p = mon_addr.begin();
- p != mon_addr.end();
- ++p)
- ls.push_back(p->second);
+ for (map<string,mon_info_t>::const_iterator p = mon_info.begin();
+ p != mon_info.end();
+ ++p) {
+ ls.push_back(p->second.public_addr);
+ }
}
+ /**
+ * Add new monitor to the monmap
+ *
+ * @param name Monitor name (i.e., 'foo' in 'mon.foo')
+ * @param addr Monitor's public address
+ */
void add(const string &name, const entity_addr_t &addr) {
- assert(mon_addr.count(name) == 0);
- assert(addr_name.count(addr) == 0);
- mon_addr[name] = addr;
+ assert(mon_info.count(name) == 0);
+ assert(addr_mons.count(addr) == 0);
+ mon_info_t &m = mon_info[name];
+ m.name = name;
+ m.public_addr = addr;
calc_ranks();
}
-
+
+ /**
+ * Remove monitor from the monmap
+ *
+ * @param name Monitor name (i.e., 'foo' in 'mon.foo')
+ */
void remove(const string &name) {
- assert(mon_addr.count(name));
- mon_addr.erase(name);
+ assert(mon_info.count(name));
+ mon_info.erase(name);
+ assert(mon_info.count(name) == 0);
calc_ranks();
}
+ /**
+ * Rename monitor from @p oldname to @p newname
+ *
+ * @param oldname monitor's current name (i.e., 'foo' in 'mon.foo')
+ * @param newname monitor's new name (i.e., 'bar' in 'mon.bar')
+ */
void rename(string oldname, string newname) {
assert(contains(oldname));
assert(!contains(newname));
- mon_addr[newname] = mon_addr[oldname];
- mon_addr.erase(oldname);
+ mon_info[newname] = mon_info[oldname];
+ mon_info.erase(oldname);
+ mon_info[newname].name = newname;
calc_ranks();
}
- bool contains(const string& name) {
- return mon_addr.count(name);
+ bool contains(const string& name) const {
+ return mon_info.count(name);
}
- bool contains(const entity_addr_t &a) {
- for (map<string,entity_addr_t>::iterator p = mon_addr.begin();
- p != mon_addr.end();
- ++p) {
- if (p->second == a)
- return true;
+ /**
+ * Check if monmap contains a monitor with address @p a
+ *
+ * @note checks for all addresses a monitor may have, public or otherwise.
+ *
+ * @param a monitor address
+ * @returns true if monmap contains a monitor with address @p;
+ * false otherwise.
+ */
+ bool contains(const entity_addr_t &a) const {
+ for (map<string,mon_info_t>::const_iterator p = mon_info.begin();
+ p != mon_info.end();
+ ++p) {
+ if (p->second.public_addr == a)
+ return true;
}
return false;
}
string get_name(unsigned n) const {
- assert(n < rank_name.size());
- return rank_name[n];
+ assert(n < ranks.size());
+ return ranks[n];
}
string get_name(const entity_addr_t& a) const {
- map<entity_addr_t,string>::const_iterator p = addr_name.find(a);
- if (p == addr_name.end())
+ map<entity_addr_t,string>::const_iterator p = addr_mons.find(a);
+ if (p == addr_mons.end())
return string();
else
return p->second;
}
int get_rank(const string& n) {
- for (unsigned i=0; i<rank_name.size(); i++)
- if (rank_name[i] == n)
+ for (unsigned i = 0; i < ranks.size(); i++)
+ if (ranks[i] == n)
return i;
return -1;
}
int get_rank(const entity_addr_t& a) {
- for (unsigned i=0; i<rank_addr.size(); i++)
- if (rank_addr[i] == a)
+ string n = get_name(a);
+ if (n.empty())
+ return -1;
+
+ for (unsigned i = 0; i < ranks.size(); i++)
+ if (ranks[i] == n)
return i;
return -1;
}
bool get_addr_name(const entity_addr_t& a, string& name) {
- if (addr_name.count(a) == 0)
+ if (addr_mons.count(a) == 0)
return false;
- name = addr_name[a];
+ name = addr_mons[a];
return true;
}
- const entity_addr_t& get_addr(const string& n) {
- assert(mon_addr.count(n));
- return mon_addr[n];
+ const entity_addr_t& get_addr(const string& n) const {
+ assert(mon_info.count(n));
+ map<string,mon_info_t>::const_iterator p = mon_info.find(n);
+ return p->second.public_addr;
}
- const entity_addr_t& get_addr(unsigned m) {
- assert(m < rank_addr.size());
- return rank_addr[m];
+ const entity_addr_t& get_addr(unsigned m) const {
+ assert(m < ranks.size());
+ return get_addr(ranks[m]);
}
void set_addr(const string& n, const entity_addr_t& a) {
- assert(mon_addr.count(n));
- mon_addr[n] = a;
+ assert(mon_info.count(n));
+ mon_info[n].public_addr = a;
calc_ranks();
}
entity_inst_t get_inst(const string& n) {
- assert(mon_addr.count(n));
+ assert(mon_info.count(n));
int m = get_rank(n);
assert(m >= 0); // vector can't take negative indicies
- entity_inst_t i;
- i.addr = rank_addr[m];
- i.name = entity_name_t::MON(m);
- return i;
+ return get_inst(m);
}
entity_inst_t get_inst(unsigned m) const {
- assert(m < rank_addr.size());
+ assert(m < ranks.size());
entity_inst_t i;
- i.addr = rank_addr[m];
+ i.addr = get_addr(m);
i.name = entity_name_t::MON(m);
return i;
}
void dump(ceph::Formatter *f) const;
static void generate_test_instances(list<MonMap*>& o);
+
+private:
+ struct rank_cmp {
+ bool operator()(const mon_info_t &a, const mon_info_t &b) const {
+ if (a.public_addr == b.public_addr)
+ return a.name < b.name;
+ return a.public_addr < b.public_addr;
+ }
+ };
};
WRITE_CLASS_ENCODER_FEATURES(MonMap)