]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mon: add 'ceph fs required_client_feature <fs_name> add/rm' command 33827/head
authorYan, Zheng <zyan@redhat.com>
Mon, 9 Mar 2020 13:14:30 +0000 (21:14 +0800)
committerYan, Zheng <zyan@redhat.com>
Tue, 7 Jul 2020 01:05:56 +0000 (09:05 +0800)
Fixes: https://tracker.ceph.com/issues/43817
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
18 files changed:
PendingReleaseNotes
doc/cephfs/administration.rst
qa/suites/fs/upgrade/featureful_client/old_client/tasks/3-compat_client/pacific.yaml
qa/suites/fs/upgrade/featureful_client/upgraded_client/tasks/4-compat_client.yaml
qa/tasks/cephfs/test_admin.py
qa/tasks/mgr/dashboard/test_health.py
src/CMakeLists.txt
src/crimson/CMakeLists.txt
src/mds/MDSMap.cc
src/mds/MDSMap.h
src/mds/MDSRank.cc
src/mds/Server.cc
src/mds/cephfs_features.cc [new file with mode: 0644]
src/mds/cephfs_features.h
src/mds/mdstypes.h
src/mon/FSCommands.cc
src/mon/MDSMonitor.cc
src/mon/MonCommands.h

index a68499e04842f8a3382c9797fa072c5917cce096..e9b580ba38b7176b7aa28679950313ae0b7bf587 100644 (file)
@@ -86,3 +86,9 @@
 * Monitors now have a config option ``mon_osd_warn_num_repaired``, 10 by default.
   If any OSD has repaired more than this many I/O errors in stored data a
  ``OSD_TOO_MANY_REPAIRS`` health warning is generated.
+
+* Introduce commands that manipulate required client features of a file system::
+
+    ceph fs required_client_features <fs name> add <feature>
+    ceph fs required_client_features <fs name> rm <feature>
+    ceph fs feature ls
index 605864431c270d779afa48eeb0b6bc0d403ad004..4261e9480ee6351f58e8b27caeeeff0f2f3a1d4c 100644 (file)
@@ -237,6 +237,97 @@ For example, to only allow Nautilus clients, use:
 
 Clients running an older version will be automatically evicted.
 
+Enforcing minimum version of CephFS client is achieved by setting required
+client features. Commands to manipulate required client features of a file
+system:
+
+::
+
+    fs required_client_features <fs name> add reply_encoding
+    fs required_client_features <fs name> rm reply_encoding
+
+To list all CephFS features
+
+::
+
+    fs feature ls
+
+
+CephFS features and first release they came out.
+
++------------------+--------------+-----------------+
+| Feature          | Ceph release | Upstream Kernel |
++==================+==============+=================+
+| jewel            | jewel        | 4.5             |
++------------------+--------------+-----------------+
+| kraken           | kraken       | 4.13            |
++------------------+--------------+-----------------+
+| luminous         | luminous     | 4.13            |
++------------------+--------------+-----------------+
+| mimic            | mimic        | 4.19            |
++------------------+--------------+-----------------+
+| reply_encoding   | nautilus     | 5.1             |
++------------------+--------------+-----------------+
+| reclaim_client   | nautilus     | N/A             |
++------------------+--------------+-----------------+
+| lazy_caps_wanted | nautilus     | 5.1             |
++------------------+--------------+-----------------+
+| multi_reconnect  | nautilus     | 5.1             |
++------------------+--------------+-----------------+
+| deleg_ino        | octopus      | 5.6             |
++------------------+--------------+-----------------+
+| metric_collect   | pacific      | N/A             |
++------------------+--------------+-----------------+
+
+CephFS Feature Descriptions
+
+
+::
+
+    reply_encoding
+
+MDS encodes request reply in extensible format if client supports this feature.
+
+
+::
+
+    reclaim_client
+
+MDS allows new client to reclaim another (dead) client's states. This feature
+is used by NFS-Ganesha.
+
+
+::
+
+    lazy_caps_wanted
+
+When a stale client resumes, if the client supports this feature, mds only needs
+to re-issue caps that are explictly wanted.
+
+
+::
+
+    multi_reconnect
+
+When mds failover, client sends reconnect messages to mds, to reestablish cache
+states. If MDS supports this feature, client can split large reconnect message
+into multiple ones.
+
+
+::
+
+    deleg_ino
+
+MDS delegate inode numbers to client if client supports this feature. Having
+delegated inode numbers is a prerequisite for client to do async file creation.
+
+
+::
+
+    metric_collect
+
+Clients can send performance metric to MDS if MDS support this feature.
+
 
 Global settings
 ---------------
index d8761e9bd296ceb4a83a707db09d3cad441d5033..56f8ebde9fd9ee60261874e12db1b47708e35c6e 100644 (file)
@@ -6,5 +6,5 @@ tasks:
 - exec:
     mon.a:
       - ceph fs dump --format=json-pretty
-      - ceph fs set cephfs min_compat_client pacific
+      - ceph fs required_client_features cephfs add metric_collect
 - fs.clients_evicted:
index a553f31d50e23f6b366b966d4bc6c786cc978ef9..be282706818f19a491eab1a40efce0027401c6ad 100644 (file)
@@ -6,7 +6,7 @@ tasks:
 - exec:
     mon.a:
       - ceph fs dump --format=json-pretty
-      - ceph fs set cephfs min_compat_client octopus
+      - ceph fs required_client_features cephfs add metric_collect
 - fs.clients_evicted:
     clients:
       client.0: False
index 393146fa28516d4f64aeca712342c0ae55cef167..661547e733f2adb86d6541485467f34bf099e1f5 100644 (file)
@@ -164,6 +164,40 @@ class TestAdminCommands(CephFSTestCase):
             self._check_pool_application_metadata_key_value(
                 pool_names[i], 'cephfs', keys[i], fs_name)
 
+    def test_required_client_features(self):
+        """
+        That `ceph fs required_client_features` command functions.
+        """
+
+        def is_required(index):
+            out = self.fs.mon_manager.raw_cluster_cmd('fs', 'get', self.fs.name, '--format=json-pretty')
+            features = json.loads(out)['mdsmap']['required_client_features']
+            if "feature_{0}".format(index) in features:
+                return True;
+            return False;
+
+        features = json.loads(self.fs.mon_manager.raw_cluster_cmd('fs', 'feature', 'ls', '--format=json-pretty'))
+        self.assertGreater(len(features), 0);
+
+        for f in features:
+            self.fs.mon_manager.raw_cluster_cmd('fs', 'required_client_features', self.fs.name, 'rm', str(f['index']))
+
+        for f in features:
+            index = f['index']
+            feature = f['name']
+            if feature == 'reserved':
+                feature = str(index)
+
+            if index % 3 == 0:
+                continue;
+            self.fs.mon_manager.raw_cluster_cmd('fs', 'required_client_features', self.fs.name, 'add', feature)
+            self.assertTrue(is_required(index))
+
+            if index % 2 == 0:
+                continue;
+            self.fs.mon_manager.raw_cluster_cmd('fs', 'required_client_features', self.fs.name, 'rm', feature)
+            self.assertFalse(is_required(index))
+
 
 class TestConfigCommands(CephFSTestCase):
     """
index 08551e488ab99164f79afe1e1cd9302c75c30b1d..73028494d855b29a9f29ec48458a92b7ec4ff778 100644 (file)
@@ -40,7 +40,7 @@ class HealthTest(DashboardTestCase):
             'ro_compat': JObj({}, allow_unknown=True),
             'incompat': JObj({}, allow_unknown=True)
         }),
-        'min_compat_client': str,
+        'required_client_features': JObj({}, allow_unknown=True),
         'data_pools': JList(int),
         'info': JObj({}, allow_unknown=True),
         'fs_name': str,
index 054eab5a9be3a5c50fd156e2f2235f2453e267b9..e243f0cd945a388bfbf2d8f22c4095880f23d0f6 100644 (file)
@@ -294,7 +294,8 @@ list(APPEND mds_files
   mds/FSMapUser.cc
   mds/inode_backtrace.cc
   mds/mdstypes.cc
-  mds/flock.cc)
+  mds/flock.cc
+  mds/cephfs_features.cc)
 
 add_subdirectory(json_spirit)
 
index 0d267288f157da129352eed920a213776f836962..f5dc80800c1d8dc1f2e3a6471c7ac36c067b4053 100644 (file)
@@ -100,6 +100,7 @@ add_library(crimson-common STATIC
   ${PROJECT_SOURCE_DIR}/src/mgr/ServiceMap.cc
   ${PROJECT_SOURCE_DIR}/src/mds/inode_backtrace.cc
   ${PROJECT_SOURCE_DIR}/src/mds/mdstypes.cc
+  ${PROJECT_SOURCE_DIR}/src/mds/cephfs_features.cc
   ${PROJECT_SOURCE_DIR}/src/mds/FSMap.cc
   ${PROJECT_SOURCE_DIR}/src/mds/FSMapUser.cc
   ${PROJECT_SOURCE_DIR}/src/mds/MDSMap.cc
index f1ff7c36f7fc4bbfcc659b294c1103515e032b00..b359573b90f21f591c4aa003bf4279ee52fe00d9 100644 (file)
@@ -150,8 +150,9 @@ void MDSMap::dump(Formatter *f) const
   f->dump_int("root", root);
   f->dump_int("session_timeout", session_timeout);
   f->dump_int("session_autoclose", session_autoclose);
-  f->dump_stream("min_compat_client") << to_integer<int>(min_compat_client) << " ("
-                                     << min_compat_client << ")";
+  f->open_object_section("required_client_features");
+  cephfs_dump_features(f, required_client_features);
+  f->close_section();
   f->dump_int("max_file_size", max_file_size);
   f->dump_int("last_failure", last_failure);
   f->dump_int("last_failure_osd_epoch", last_failure_osd_epoch);
@@ -230,8 +231,7 @@ void MDSMap::print(ostream& out) const
   out << "session_timeout\t" << session_timeout << "\n"
       << "session_autoclose\t" << session_autoclose << "\n";
   out << "max_file_size\t" << max_file_size << "\n";
-  out << "min_compat_client\t" << to_integer<int>(min_compat_client) << " ("
-                              << min_compat_client << ")\n";
+  out << "required_client_features\t" << cephfs_stringify_features(required_client_features) << "\n";
   out << "last_failure\t" << last_failure << "\n"
       << "last_failure_osd_epoch\t" << last_failure_osd_epoch << "\n";
   out << "compat\t" << compat << "\n";
@@ -702,7 +702,7 @@ void MDSMap::encode(bufferlist& bl, uint64_t features) const
   encode(data_pools, bl);
   encode(cas_pool, bl);
 
-  __u16 ev = 15;
+  __u16 ev = 16;
   encode(ev, bl);
   encode(compat, bl);
   encode(metadata_pool, bl);
@@ -724,7 +724,11 @@ void MDSMap::encode(bufferlist& bl, uint64_t features) const
   encode(balancer, bl);
   encode(standby_count_wanted, bl);
   encode(old_max_mds, bl);
-  encode(min_compat_client, bl);
+  {
+    ceph_release_t min_compat_client = ceph_release_t::unknown;
+    encode(min_compat_client, bl);
+  }
+  encode(required_client_features, bl);
   ENCODE_FINISH(bl);
 }
 
@@ -852,16 +856,24 @@ void MDSMap::decode(bufferlist::const_iterator& p)
     decode(old_max_mds, p);
   }
 
-  if (ev == 14) {
-    int8_t r;
-    decode(r, p);
-    if (r < 0) {
-      min_compat_client = ceph_release_t::unknown;
+  if (ev >= 14) {
+    ceph_release_t min_compat_client;
+    if (ev == 14) {
+      int8_t r;
+      decode(r, p);
+      if (r < 0) {
+       min_compat_client = ceph_release_t::unknown;
+      } else {
+       min_compat_client = ceph_release_t{static_cast<uint8_t>(r)};
+      }
+    } else if (ev >= 15) {
+      decode(min_compat_client, p);
+    }
+    if (ev >= 16) {
+      decode(required_client_features, p);
     } else {
-      min_compat_client = ceph_release_t{static_cast<uint8_t>(r)};
+      set_min_compat_client(min_compat_client);
     }
-  } else if (ev > 14) {
-    decode(min_compat_client, p);
   }
 
   DECODE_FINISH(p);
@@ -1048,3 +1060,24 @@ bool MDSMap::is_degraded() const {
   }
   return false;
 }
+
+void MDSMap::set_min_compat_client(ceph_release_t version)
+{
+  vector<size_t> bits = CEPHFS_FEATURES_MDS_REQUIRED;
+
+  if (version >= ceph_release_t::octopus)
+    bits.push_back(CEPHFS_FEATURE_OCTOPUS);
+  else if (version >= ceph_release_t::nautilus)
+    bits.push_back(CEPHFS_FEATURE_NAUTILUS);
+  else if (version >= ceph_release_t::mimic)
+    bits.push_back(CEPHFS_FEATURE_MIMIC);
+  else if (version >= ceph_release_t::luminous)
+    bits.push_back(CEPHFS_FEATURE_LUMINOUS);
+  else if (version >= ceph_release_t::kraken)
+    bits.push_back(CEPHFS_FEATURE_KRAKEN);
+  else if (version >= ceph_release_t::jewel)
+    bits.push_back(CEPHFS_FEATURE_JEWEL);
+
+  std::sort(bits.begin(), bits.end());
+  required_client_features = feature_bitset_t(bits);
+}
index 2c3b322114605fd331b5beaeb1e703a3b2f390e4..4efd32f472daeda33b547ceffbe5926aac8f00c6 100644 (file)
@@ -35,6 +35,7 @@
 #include "common/config.h"
 
 #include "mds/mdstypes.h"
+#include "mds/cephfs_features.h"
 
 #define MDS_FEATURE_INCOMPAT_BASE CompatSet::Feature(1, "base v0.20")
 #define MDS_FEATURE_INCOMPAT_CLIENTRANGES CompatSet::Feature(2, "client writeable ranges")
@@ -186,8 +187,17 @@ public:
   uint64_t get_max_filesize() const { return max_file_size; }
   void set_max_filesize(uint64_t m) { max_file_size = m; }
 
-  ceph_release_t get_min_compat_client() const { return min_compat_client; }
-  void set_min_compat_client(ceph_release_t version) { min_compat_client = version; }
+  void set_min_compat_client(ceph_release_t version);
+
+  void add_required_client_feature(size_t bit) {
+    required_client_features.insert(bit);
+  }
+  void remove_required_client_feature(size_t bit) {
+    required_client_features.erase(bit);
+  }
+  const auto& get_required_client_features() const {
+    return required_client_features;
+  }
   
   int get_flags() const { return flags; }
   bool test_flag(int f) const { return flags & f; }
@@ -561,7 +571,7 @@ protected:
   __u32 session_autoclose = 300;
   uint64_t max_file_size = 1ULL<<40; /* 1TB */
 
-  ceph_release_t min_compat_client{ceph_release_t::unknown};
+  feature_bitset_t required_client_features;
 
   std::vector<int64_t> data_pools;  // file data pools available to clients (via an ioctl).  first is the default.
   int64_t cas_pool = -1;            // where CAS objects go
index 1ab7f3a67ced39967735cf427b16f7f1e38d852b..161adeb4d744d0dc9cff9b254ffbbe95e8e59bf0 100644 (file)
@@ -2238,8 +2238,7 @@ void MDSRankDispatcher::handle_mds_map(
   if (objecter->get_client_incarnation() != incarnation)
     objecter->set_client_incarnation(incarnation);
 
-  if (mdsmap->get_min_compat_client() < ceph_release_t::max &&
-      oldmap.get_min_compat_client() != mdsmap->get_min_compat_client())
+  if (mdsmap->get_required_client_features() != oldmap.get_required_client_features())
     server->update_required_client_features();
 
   // for debug
index 1b8a2ac031107e6a3eb61fd8b506a0bb6515c759..41da5d19adee4b0a1a4bbea2af5135f7106dcc3c 100644 (file)
@@ -1516,27 +1516,7 @@ void Server::infer_supported_features(Session *session, client_metadata_t& clien
 
 void Server::update_required_client_features()
 {
-  vector<size_t> bits = CEPHFS_FEATURES_MDS_REQUIRED;
-
-  /* If this blows up on you, you added a release without adding a new release bit to cephfs_features.h */
-  static_assert(CEPHFS_CURRENT_RELEASE == CEPH_RELEASE_MAX-1);
-
-  ceph_release_t min_compat = mds->mdsmap->get_min_compat_client();
-  if (min_compat >= ceph_release_t::octopus)
-    bits.push_back(CEPHFS_FEATURE_OCTOPUS);
-  else if (min_compat >= ceph_release_t::nautilus)
-    bits.push_back(CEPHFS_FEATURE_NAUTILUS);
-  else if (min_compat >= ceph_release_t::mimic)
-    bits.push_back(CEPHFS_FEATURE_MIMIC);
-  else if (min_compat >= ceph_release_t::luminous)
-    bits.push_back(CEPHFS_FEATURE_LUMINOUS);
-  else if (min_compat >= ceph_release_t::kraken)
-    bits.push_back(CEPHFS_FEATURE_KRAKEN);
-  else if (min_compat >= ceph_release_t::jewel)
-    bits.push_back(CEPHFS_FEATURE_JEWEL);
-
-  std::sort(bits.begin(), bits.end());
-  required_client_features = feature_bitset_t(bits);
+  required_client_features = mds->mdsmap->get_required_client_features();
   dout(7) << "required_client_features: " << required_client_features << dendl;
 
   if (mds->get_state() >= MDSMap::STATE_RECONNECT) {
diff --git a/src/mds/cephfs_features.cc b/src/mds/cephfs_features.cc
new file mode 100644 (file)
index 0000000..dcec2c4
--- /dev/null
@@ -0,0 +1,72 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <array>
+#include <sstream>
+#include "cephfs_features.h"
+#include "mdstypes.h"
+
+static const std::array feature_names
+{
+  "reserved",
+  "reserved",
+  "reserved",
+  "reserved",
+  "reserved",
+  "jewel",
+  "kraken",
+  "luminous",
+  "mimic",
+  "reply_encoding",
+  "reclaim_client",
+  "lazy_caps_wanted",
+  "multi_reconnect",
+  "deleg_ino",
+  "metric_collect",
+};
+static_assert(feature_names.size() == CEPHFS_FEATURE_MAX + 1);
+
+std::string_view cephfs_feature_name(size_t id)
+{
+  if (id > feature_names.size())
+    return "unknown";
+  return feature_names[id];
+}
+
+int cephfs_feature_from_name(std::string_view name)
+{
+  for (size_t i = 0; i < feature_names.size(); ++i) {
+    if (name == feature_names[i])
+      return i;
+  }
+  return -1;
+}
+
+std::string cephfs_stringify_features(const feature_bitset_t& features)
+{
+  std::ostringstream ss;
+  bool first = true;
+  ss << "{";
+  for (size_t i = 0; i < feature_names.size(); ++i) {
+    if (!features.test(i))
+      continue;
+    if (!first)
+      ss << ",";
+    ss << i << "=" << cephfs_feature_name(i);
+    first = false;
+  }
+  ss << "}";
+  return ss.str();
+}
+
+void cephfs_dump_features(ceph::Formatter *f, const feature_bitset_t& features)
+{
+  for (size_t i = 0; i < feature_names.size(); ++i) {
+    if (!features.test(i))
+      continue;
+    char s[18];
+    snprintf(s, sizeof(s), "feature_%lu", i);
+    f->dump_string(s, cephfs_feature_name(i));
+  }
+}
+
index 37b0ba6d9ed4df9607328b72c6f6011d4f71e621..75309ef5da89f046758f7659d6c06aba156a9a3b 100644 (file)
 #ifndef CEPHFS_FEATURES_H
 #define CEPHFS_FEATURES_H
 
+class feature_bitset_t;
+namespace ceph {
+  class Formatter;
+}
+
 // When adding a new release, please update the "current" release below, add a
 // feature bit for that release, add that feature bit to CEPHFS_FEATURES_ALL,
 // and update Server::update_required_client_features(). This feature bit
@@ -35,6 +40,7 @@
 #define CEPHFS_FEATURE_DELEG_INO        13
 #define CEPHFS_FEATURE_OCTOPUS          13
 #define CEPHFS_FEATURE_METRIC_COLLECT   14
+#define CEPHFS_FEATURE_MAX             14
 
 #define CEPHFS_FEATURES_ALL {          \
   0, 1, 2, 3, 4,                       \
@@ -58,4 +64,9 @@
 #define CEPHFS_FEATURES_CLIENT_SUPPORTED CEPHFS_FEATURES_ALL
 #define CEPHFS_FEATURES_CLIENT_REQUIRED {}
 
+extern std::string_view cephfs_feature_name(size_t id);
+extern int cephfs_feature_from_name(std::string_view name);
+std::string cephfs_stringify_features(const feature_bitset_t& features);
+void cephfs_dump_features(ceph::Formatter *f, const feature_bitset_t& features);
+
 #endif
index a0b689c37a5056f8e91aed85f126dc7c4a486686..4eeb3311c6d06d9ce0048393b431e1f84eee5016 100644 (file)
@@ -1171,9 +1171,31 @@ public:
       return false;
     return _vec[bit / bits_per_block] & ((block_type)1 << (bit % bits_per_block));
   }
+  void insert(size_t bit) {
+    size_t n = bit / bits_per_block;
+    if (n >= _vec.size())
+      _vec.resize(n + 1);
+    _vec[n] |= ((block_type)1 << (bit % bits_per_block));
+  }
+  void erase(size_t bit) {
+    size_t n = bit / bits_per_block;
+    if (n >= _vec.size())
+      return;
+    _vec[n] &= ~((block_type)1 << (bit % bits_per_block));
+    if (n + 1 == _vec.size()) {
+      while (!_vec.empty() && _vec.back() == 0)
+       _vec.pop_back();
+    }
+  }
   void clear() {
     _vec.clear();
   }
+  bool operator==(const feature_bitset_t& other) const {
+    return _vec == other._vec;
+  }
+  bool operator!=(const feature_bitset_t& other) const {
+    return _vec != other._vec;
+  }
   void encode(ceph::buffer::list& bl) const;
   void decode(ceph::buffer::list::const_iterator &p);
   void dump(ceph::Formatter *f) const;
index fa65213e52bc52451716a9f58b7551c9f23885af..86efed8e64e52b60acf8c91d08be4a9154a4e95e 100644 (file)
@@ -18,6 +18,7 @@
 #include "FSCommands.h"
 #include "MDSMonitor.h"
 #include "MgrStatMonitor.h"
+#include "mds/cephfs_features.h"
 
 using TOPNSPC::common::cmd_getval;
 
@@ -651,6 +652,79 @@ public:
   }
 };
 
+class RequiredClientFeaturesHandler : public FileSystemCommandHandler
+{
+  public:
+    RequiredClientFeaturesHandler()
+      : FileSystemCommandHandler("fs required_client_features")
+    {
+    }
+
+    int handle(
+       Monitor *mon,
+       FSMap &fsmap,
+       MonOpRequestRef op,
+       const cmdmap_t& cmdmap,
+       std::stringstream &ss) override
+    {
+      std::string fs_name;
+      if (!cmd_getval(cmdmap, "fs_name", fs_name) || fs_name.empty()) {
+       ss << "Missing filesystem name";
+       return -EINVAL;
+      }
+      auto fs = fsmap.get_filesystem(fs_name);
+      if (fs == nullptr) {
+       ss << "Not found: '" << fs_name << "'";
+       return -ENOENT;
+      }
+      string subop;
+      if (!cmd_getval(cmdmap, "subop", subop) ||
+         (subop != "add" && subop != "rm")) {
+       ss << "Must either add or rm a feature; " << subop << " is not recognized";
+       return -EINVAL;
+      }
+      string val;
+      if (!cmd_getval(cmdmap, "val", val) || val.empty()) {
+       ss << "Missing feature id/name";
+       return -EINVAL;
+      }
+
+      int feature = cephfs_feature_from_name(val);
+      if (feature < 0) {
+       string err;
+       feature = strict_strtol(val.c_str(), 10, &err);
+       if (err.length()) {
+         ss << "Invalid feature name: " << val;
+         return -EINVAL;
+       }
+       if (feature < 0 || feature > CEPHFS_FEATURE_MAX) {
+         ss << "Invalid feature id: " << feature;
+         return -EINVAL;
+       }
+      }
+
+      if (subop == "add") {
+       fsmap.modify_filesystem(
+           fs->fscid,
+           [feature](std::shared_ptr<Filesystem> fs)
+       {
+         fs->mds_map.add_required_client_feature(feature);
+       });
+       ss << "added feature '" << cephfs_feature_name(feature) << "' to required_client_features";
+      } else {
+       fsmap.modify_filesystem(
+           fs->fscid,
+           [feature](std::shared_ptr<Filesystem> fs)
+       {
+         fs->mds_map.remove_required_client_feature(feature);
+       });
+       ss << "removed feature '" << cephfs_feature_name(feature) << "' to required_client_features";
+      }
+      return 0;
+   }
+};
+
+
 class AddDataPoolHandler : public FileSystemCommandHandler
 {
   public:
@@ -988,6 +1062,7 @@ FileSystemCommandHandler::load(Paxos *paxos)
   handlers.push_back(std::make_shared<SetHandler>());
   handlers.push_back(std::make_shared<FailHandler>());
   handlers.push_back(std::make_shared<FlagSetHandler>());
+  handlers.push_back(std::make_shared<RequiredClientFeaturesHandler>());
   handlers.push_back(std::make_shared<AddDataPoolHandler>(paxos));
   handlers.push_back(std::make_shared<RemoveDataPoolHandler>());
   handlers.push_back(std::make_shared<FsNewHandler>(paxos));
index 738b490d9fc26b9da6f5061e11a4730db8a0cc10..9415ad814c71e6be37135500ecd9aaf3333c57cb 100644 (file)
@@ -1190,6 +1190,23 @@ bool MDSMonitor::preprocess_command(MonOpRequestRef op)
       }
     }
     r = 0;
+  } else if (prefix == "fs feature ls") {
+    if (f) {
+      f->open_array_section("cephfs_features");
+      for (size_t i = 0; i <= CEPHFS_FEATURE_MAX; ++i) {
+       f->open_object_section("feature");
+       f->dump_int("index", i);
+       f->dump_string("name", cephfs_feature_name(i));
+       f->close_section();
+      }
+      f->close_section();
+      f->flush(ds);
+    } else {
+      for (size_t i = 0; i <= CEPHFS_FEATURE_MAX; ++i) {
+        ds << i << " " << cephfs_feature_name(i) << std::endl;
+      }
+    }
+    r = 0;
   }
 
 out:
index 125b16bab287f36026937193ef9b47041ac710fb..f1e429aef23a05b4fad1d7dc0a82b805f2cab143 100644 (file)
@@ -405,6 +405,17 @@ COMMAND("fs flag set name=flag_name,type=CephChoices,strings=enable_multiple "
        "name=yes_i_really_mean_it,type=CephBool,req=false",
        "Set a global CephFS flag",
        "fs", "rw")
+
+COMMAND("fs feature ls",
+        "list available cephfs features to be set/unset",
+       "mds", "r")
+
+COMMAND("fs required_client_features "
+        "name=fs_name,type=CephString "
+        "name=subop,type=CephChoices,strings=add|rm "
+        "name=val,type=CephString ",
+        "add/remove required features of clients", "mds", "rw")
+
 COMMAND("fs add_data_pool name=fs_name,type=CephString "
        "name=pool,type=CephString",
        "add data pool <pool>", "mds", "rw")