]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mon: MonMap: mon addr and all now held by mon_info_t
authorJoao Eduardo Luis <joao@suse.de>
Fri, 27 Nov 2015 18:41:07 +0000 (18:41 +0000)
committerJoao Eduardo Luis <joao@suse.de>
Wed, 2 Nov 2016 23:50:12 +0000 (23:50 +0000)
Instead of keeping several maps mapping back and forth from string to
entity_addr_t for each monitor, have the monitor's address and name in
purpose-specific class, mon_info_t, and everything references it
instead.

Although the benefits may not be obvious right now, it will allow us to
add other ip addresses for the monitors a bit more easily, instead of
having to kludge around the existing structures.

We also keep compatibility with older versions by maintaining the
traditional 'mon_addr' map. This is a logical change inside the monitor,
so we can easily accomodate older monitors without having to rely on
quorum features; just needs a bit of care and foo.

Signed-off-by: Joao Eduardo Luis <joao@suse.de>
src/mon/MonMap.cc
src/mon/MonMap.h

index dc2503c93934a738fdae0dfc30c7adc7d0d6aff0..fe7426632948e75c8600950a8dc5b77cbc1fcb85 100644 (file)
 
 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);
@@ -44,7 +80,7 @@ void MonMap::encode(bufferlist& blist, uint64_t con_features) const
     ::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);
@@ -52,12 +88,15 @@ void MonMap::encode(bufferlist& blist, uint64_t con_features) const
   ::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) {
@@ -79,8 +118,11 @@ void MonMap::decode(bufferlist::iterator &p)
     ::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();
 }
 
@@ -119,8 +161,21 @@ int MonMap::read(const char *fn)
 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
@@ -130,10 +185,11 @@ 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
@@ -148,13 +204,14 @@ 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();
index 3f1150450adf774fb42ab57e9bb4ed93bca0b3f4..51495608089149e7a756771120206ed2d258acf3 100644 (file)
@@ -25,18 +25,50 @@ namespace ceph {
   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
@@ -73,22 +105,86 @@ class MonMap {
     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;
     }
   }
 
@@ -99,112 +195,150 @@ class MonMap {
 
   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;
   }
@@ -273,6 +407,15 @@ class MonMap {
   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)