]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
convert spirit_json to Boost.JSON (ceph_json, Mgr)
authorJesse F. Williamson <1643380+chardan@users.noreply.github.com>
Mon, 16 Dec 2024 16:27:11 +0000 (08:27 -0800)
committerJesse F. Williamson <jfw@ibm.com>
Mon, 17 Mar 2025 17:41:27 +0000 (10:41 -0700)
Signed-off-by: Jesse F. Williamson <jfw@ibm.com>
src/common/ceph_json.cc
src/common/ceph_json.h
src/mgr/ActivePyModules.cc
src/mgr/Mgr.cc
src/mgr/MgrContext.h

index b63d73e5a8c3c9c50ad86d3a5a75226cbc52e502..1b54d6f71cecaca79e5b6eeca61ecfcd1ad7de11 100644 (file)
@@ -1,17 +1,16 @@
 #include "common/ceph_json.h"
 #include "include/utime.h"
 
-// for testing DELETE ME
-#include <fstream>
-#include <include/types.h>
-
 #include <boost/algorithm/string.hpp>
 #include <boost/tokenizer.hpp>
 #include <boost/lexical_cast.hpp>
+#include <include/types.h>
 
-#include "json_spirit/json_spirit_writer_template.h"
+// Enable boost.json's header-only mode ("https://github.com/boostorg/json?tab=readme-ov-file#header-only"):
+#include <boost/json/src.hpp>
 
-using namespace json_spirit;
+#include <memory>
+#include <fstream>
 
 using std::ifstream;
 using std::pair;
@@ -35,14 +34,6 @@ void encode_json(const char *name, const JSONObj::data_val& v, Formatter *f)
   }
 }
 
-JSONObjIter::JSONObjIter()
-{
-}
-
-JSONObjIter::~JSONObjIter()
-{
-}
-
 void JSONObjIter::set(const JSONObjIter::map_iter_t &_cur, const JSONObjIter::map_iter_t &_last)
 {
   cur = _cur;
@@ -55,9 +46,10 @@ void JSONObjIter::operator++()
     ++cur;
 }
 
+// IMPORTANT: The returned pointer is intended as NON-OWNING (i.e. the JSONObjIter is responsible for it):
 JSONObj *JSONObjIter::operator*()
 {
-  return cur->second;
+  return cur->second.get();
 }
 
 // does not work, FIXME
@@ -66,18 +58,11 @@ ostream& operator<<(ostream &out, const JSONObj &obj) {
    return out;
 }
 
-JSONObj::~JSONObj()
-{
-  for (auto iter = children.begin(); iter != children.end(); ++iter) {
-    JSONObj *obj = iter->second;
-    delete obj;
-  }
-}
-
-
 void JSONObj::add_child(string el, JSONObj *obj)
 {
-  children.insert(pair<string, JSONObj *>(el, obj));
+  auto p = std::unique_ptr<JSONObj>(obj);
+
+  children.insert({el, std::move(p)});
 }
 
 bool JSONObj::get_attr(string name, data_val& attr)
@@ -136,49 +121,53 @@ bool JSONObj::get_data(const string& key, data_val *dest)
 }
 
 /* accepts a JSON Array or JSON Object contained in
- * a JSON Spirit Value, v,  and creates a JSONObj for each
+ * a Boost.JSON value, v, and creates a Ceph JSONObj for each
  * child contained in v
  */
-void JSONObj::handle_value(Value v)
-{
-  if (v.type() == obj_type) {
-    Object temp_obj = v.get_obj();
-    for (Object::size_type i = 0; i < temp_obj.size(); i++) {
-      Pair temp_pair = temp_obj[i];
-      string temp_name = temp_pair.name_;
-      Value temp_value = temp_pair.value_;
-      JSONObj *child = new JSONObj;
-      child->init(this, temp_value, temp_name);
-      add_child(temp_name, child);
-    }
-  } else if (v.type() == array_type) {
-    Array temp_array = v.get_array();
-    Value value;
+void JSONObj::handle_value(boost::json::value v)
+{
+  if (auto op = v.if_object()) {
 
-    for (unsigned j = 0; j < temp_array.size(); j++) {
-      Value cur = temp_array[j];
-      string temp_name;
+       for (const auto& kvp : *op)
+        {
+               JSONObj *child = new JSONObj;
 
-      JSONObj *child = new JSONObj;
-      child->init(this, cur, temp_name);
-      add_child(child->get_name(), child);
-    }
+               child->init(this, kvp.value(), kvp.key());
+
+               add_child(kvp.key(), child);
+        }
+
+       return;
   }
+
+ if (auto ap = v.if_array()) {
+       for (const auto& kvp : *ap)
+        {
+               JSONObj *child = new JSONObj;
+               child->init(this, kvp, ""); 
+
+               add_child(child->get_name(), child);
+        }
+ }
+
+ // unknown type is not-an-error
 }
 
-void JSONObj::init(JSONObj *p, Value v, string n)
+void JSONObj::init(JSONObj *parent_node, boost::json::value data_in, std::string name_in)
 {
-  name = n;
-  parent = p;
-  data = v;
+  parent = parent_node;
+  data = data_in;
+  name = name_in;
 
-  handle_value(v);
-  if (v.type() == str_type) {
-    val.set(v.get_str(), true);
-  } else {
-    val.set(json_spirit::write_string(v), false);
+  handle_value(data_in);
+
+  if (auto vp = data_in.if_string())
+   val.set(*vp, true); 
+  else {
+   val.set(boost::json::serialize(data_in), false);
   }
-  attr_map.insert(pair<string,data_val>(name, val));
+
+  attr_map.insert({name, val});
 }
 
 JSONObj *JSONObj::get_parent()
@@ -188,44 +177,37 @@ JSONObj *JSONObj::get_parent()
 
 bool JSONObj::is_object()
 {
-  return (data.type() == obj_type);
+  return data.is_object();
 }
 
 bool JSONObj::is_array()
 {
-  return (data.type() == array_type);
+  return data.is_array();
 }
 
 vector<string> JSONObj::get_array_elements()
 {
-  vector<string> elements;
-  Array temp_array;
+ vector<string> elements;
 
-  if (data.type() == array_type)
-    temp_array = data.get_array();
+ boost::json::array temp_array;
 
-  int array_size = temp_array.size();
-  if (array_size > 0)
-    for (int i = 0; i < array_size; i++) {
-      Value temp_value = temp_array[i];
-      string temp_string;
-      temp_string = write(temp_value, raw_utf8);
-      elements.push_back(temp_string);
-    }
+ if (auto ap = data.if_array())
+  temp_array = *ap;
 
-  return elements;
-}
+ auto array_size = temp_array.size();
 
-JSONParser::JSONParser() : buf_len(0), success(true)
-{
-}
+ if (array_size > 0) {
+       for (boost::json::array::size_type i = 0; i < array_size; i++) {
+             boost::json::value temp_value = temp_array[i];
+             string temp_string;
+             temp_string = boost::json::serialize(temp_value);
+             elements.push_back(temp_string);
+       }
+  }
 
-JSONParser::~JSONParser()
-{
+  return elements;
 }
 
-
-
 void JSONParser::handle_data(const char *s, int len)
 {
   json_buffer.append(s, len); // check for problems with null termination FIXME
@@ -235,71 +217,84 @@ void JSONParser::handle_data(const char *s, int len)
 // parse a supplied JSON fragment
 bool JSONParser::parse(const char *buf_, int len)
 {
-  if (!buf_) {
-    set_failure();
+  if (!buf_ || 0 >= len) {
     return false;
   }
 
-  string json_string(buf_, len);
-  success = read(json_string, data);
-  if (success) {
-    handle_value(data);
-    if (data.type() != obj_type &&
-        data.type() != array_type) {
-      if (data.type() == str_type) {
-        val.set(data.get_str(), true);
-      } else {
-        const std::string& s = json_spirit::write_string(data);
-        if (s.size() == (uint64_t)len) { /* Check if entire string is read */
-          val.set(s, false);
-        } else {
-          set_failure();
-        }
-      }
-    }
-  } else {
-    set_failure();
-  }
+  std::string_view json_string_view(buf_, len);
+
+  std::error_code ec; 
+  data = boost::json::parse(json_string_view, ec);
+  
+  if(ec)
+   return false;
+
+  // recursively evaluate the result:
+  handle_value(data);
 
-  return success;
+  if (data.is_object() or data.is_array()) 
+   return true;
+
+  if (data.is_string()) {
+   val.set(data.as_string(), true);
+   return true;
+  } 
+
+  // For any other kind of value:
+  std::string s = boost::json::serialize(data);
+
+  if (s.size() == static_cast<uint64_t>(len)) { // entire string was read
+    val.set(s, false);
+    return true;
+   }
+
+  // Could not parse and convert:
+  return false; 
 }
 
 // parse the internal json_buffer up to len
 bool JSONParser::parse(int len)
 {
-  string json_string = json_buffer.substr(0, len);
-  success = read(json_string, data);
-  if (success)
-    handle_value(data);
-  else
-    set_failure();
+  std::string_view json_string(std::begin(json_buffer), len + std::begin(json_buffer));
+
+  std::error_code ec;
+  if(data = boost::json::parse(json_string, ec); ec)
+   return false;
 
-  return success;
+  handle_value(data);
+
+  return true;
 }
 
 // parse the complete internal json_buffer
 bool JSONParser::parse()
 {
-  success = read(json_buffer, data);
-  if (success)
-    handle_value(data);
-  else
-    set_failure();
+  std::error_code ec;
+  data = boost::json::parse(json_buffer, ec);
 
-  return success;
+  if(ec)
+   return false;
+
+  handle_value(data);
+
+  return true;
 }
 
-// parse a supplied ifstream, for testing. DELETE ME
+// parse a supplied ifstream:
 bool JSONParser::parse(const char *file_name)
 {
-  ifstream is(file_name);
-  success = read(is, data);
-  if (success)
-    handle_value(data);
-  else
-    set_failure();
+ ifstream is(file_name);
+
+ std::error_code ec;
+ data = boost::json::parse(is, ec);
+
+ if(ec)
+  return false;
+
+ handle_value(data);
 
 return success;
return true;
 }
 
 
index a90b7ec79d014f9cbf9fb1ba2d7f82b59d82d078..dcdc9b995fb4f37e4923e457c7d5f5d43c35ed77 100644 (file)
@@ -1,38 +1,42 @@
 #ifndef CEPH_JSON_H
 #define CEPH_JSON_H
 
-#include <deque>
 #include <map>
 #include <set>
-#include <stdexcept>
+#include <deque>
 #include <string>
+#include <iostream>
+#include <stdexcept>
 #include <typeindex>
-#include <include/types.h>
+
+#include <boost/json.hpp>
+#include <boost/optional.hpp>
 #include <boost/container/flat_map.hpp>
 #include <boost/container/flat_set.hpp>
-#include <boost/optional.hpp>
+
+#include <include/types.h>
 #include <include/ceph_fs.h>
-#include "common/ceph_time.h"
 
-#include "json_spirit/json_spirit.h"
+#include "common/ceph_time.h"
 
 #include "Formatter.h"
 
-
-
 class JSONObj;
 
 class JSONObjIter {
-  typedef std::map<std::string, JSONObj *>::iterator map_iter_t;
+
+  using map_iter_t = std::map<std::string, std::unique_ptr<JSONObj>>::iterator;
+
   map_iter_t cur;
   map_iter_t last;
 
 public:
-  JSONObjIter();
-  ~JSONObjIter();
   void set(const JSONObjIter::map_iter_t &_cur, const JSONObjIter::map_iter_t &_end);
 
   void operator++();
+
+  // IMPORTANT: The returned pointer is intended as NON-OWNING (i.e. JSONObjIter 
+  // is responsible for it):
   JSONObj *operator*();
 
   bool end() const {
@@ -40,9 +44,13 @@ public:
   }
 };
 
-class JSONObj
+class JSONObj 
 {
-  JSONObj *parent;
+  using children_multimap_t = std::multimap<std::string, std::unique_ptr<JSONObj>>;
+  using children_multimap_value_type = typename children_multimap_t::value_type;
+
+  JSONObj *parent = nullptr;
+
 public:
   struct data_val {
     std::string str;
@@ -53,30 +61,41 @@ public:
       quoted = q;
     }
   };
+
 protected:
   std::string name; // corresponds to obj_type in XMLObj
-  json_spirit::Value data;
-  struct data_val val;
+
+  boost::json::value data;
+
+  data_val val;
+
   bool data_quoted{false};
-  std::multimap<std::string, JSONObj *> children;
-  std::map<std::string, data_val> attr_map;
-  void handle_value(json_spirit::Value v);
 
-public:
+  std::multimap<std::string, std::unique_ptr<JSONObj>> children;
+
+  std::map<std::string, data_val> attr_map;
 
-  JSONObj() : parent(NULL){}
+  void handle_value(boost::json::value v);
 
-  virtual ~JSONObj();
+public:
+  virtual ~JSONObj() = default;
 
-  void init(JSONObj *p, json_spirit::Value v, std::string n);
+public:
+  void init(JSONObj *parent_node, boost::json::value data_in, std::string name_in);
 
   std::string& get_name() { return name; }
   data_val& get_data_val() { return val; }
+
   const std::string& get_data() { return val.str; }
   bool get_data(const std::string& key, data_val *dest);
+
   JSONObj *get_parent();
+
+  // Note: takes ownership of child:
   void add_child(std::string el, JSONObj *child);
+
   bool get_attr(std::string name, data_val& attr);
+
   JSONObjIter find(const std::string& name);
   JSONObjIter find_first();
   JSONObjIter find_first(const std::string& name);
@@ -87,6 +106,7 @@ public:
 
   bool is_array();
   bool is_object();
+
   std::vector<std::string> get_array_elements();
 };
 
@@ -98,12 +118,10 @@ inline std::ostream& operator<<(std::ostream &out, const JSONObj::data_val& dv)
 
 class JSONParser : public JSONObj
 {
-  int buf_len;
+  int buf_len = 0;
   std::string json_buffer;
-  bool success;
+
 public:
-  JSONParser();
-  ~JSONParser() override;
   void handle_data(const char *s, int len);
 
   bool parse(const char *buf_, int len);
@@ -112,7 +130,6 @@ public:
   bool parse(const char *file_name);
 
   const char *get_json() { return json_buffer.c_str(); }
-  void set_failure() { success = false; }
 };
 
 void encode_json(const char *name, const JSONObj::data_val& v, ceph::Formatter *f);
@@ -127,7 +144,7 @@ public:
 
   JSONDecoder(ceph::buffer::list& bl) {
     if (!parser.parse(bl.c_str(), bl.length())) {
-      std::cout << "JSONDecoder::err()" << std::endl;
+      std::cout << "JSONDecoder::err()" << std::endl;  // JFW: do we still want this here?
       throw JSONDecoder::err("failed to parse JSON input");
     }
   }
index 709fcfae227c751f423a6f1cdebb4ee747eeaad1..94ae44d9675e60e29e16537aaca63f8db754f323 100644 (file)
@@ -38,6 +38,8 @@
 #include "PyModuleRegistry.h"
 #include "PyUtil.h"
 
+#include "json_spirit/json_spirit.h"
+
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_mgr
 #undef dout_prefix
index 418e8ae984aee0c5fe3d402d7dee0645adb7e760..7e0b4c26f1c39e9c6bbf3907668d3aa0ff64e9d0 100644 (file)
@@ -79,26 +79,29 @@ Mgr::~Mgr()
 }
 
 void MetadataUpdate::finish(int r)
+try
 {
   daemon_state.clear_updating(key);
   if (r == 0) {
     if (key.type == "mds" || key.type == "osd" ||
         key.type == "mgr" || key.type == "mon") {
-      json_spirit::mValue json_result;
-      bool read_ok = json_spirit::read(
-          outbl.to_str(), json_result);
-      if (!read_ok) {
+
+      std::error_code ec;
+
+      boost::json::value json_result = boost::json::parse(outbl.to_str(), ec);
+     
+      if (ec) {
         dout(1) << "mon returned invalid JSON for " << key << dendl;
         return;
       }
-      if (json_result.type() != json_spirit::obj_type) {
+      if (!json_result.is_object()) {
         dout(1) << "mon returned valid JSON " << key
                << " but not an object: '" << outbl.to_str() << "'" << dendl;
         return;
       }
       dout(4) << "mon returned valid metadata JSON for " << key << dendl;
 
-      json_spirit::mObject daemon_meta = json_result.get_obj();
+      boost::json::object daemon_meta = json_result.get_object();
 
       // Skip daemon who doesn't have hostname yet
       if (daemon_meta.count("hostname") == 0) {
@@ -118,7 +121,7 @@ void MetadataUpdate::finish(int r)
        std::map<string,string> m;
        {
          std::lock_guard l(state->lock);
-         state->hostname = daemon_meta.at("hostname").get_str();
+         state->hostname = daemon_meta.at("hostname").get_string();
 
          if (key.type == "mds" || key.type == "mgr" || key.type == "mon") {
            daemon_meta.erase("name");
@@ -127,14 +130,14 @@ void MetadataUpdate::finish(int r)
          }
          daemon_meta.erase("hostname");
          for (const auto &[key, val] : daemon_meta) {
-           m.emplace(key, val.get_str());
+           m.emplace(key, val.get_string());
          }
        }
        daemon_state.update_metadata(state, m);
       } else {
         auto state = std::make_shared<DaemonState>(daemon_state.types);
         state->key = key;
-        state->hostname = daemon_meta.at("hostname").get_str();
+        state->hostname = daemon_meta.at("hostname").get_string();
 
         if (key.type == "mds" || key.type == "mgr" || key.type == "mon") {
           daemon_meta.erase("name");
@@ -145,7 +148,7 @@ void MetadataUpdate::finish(int r)
 
        std::map<string,string> m;
         for (const auto &[key, val] : daemon_meta) {
-          m.emplace(key, val.get_str());
+          m.emplace(key, val.get_string());
         }
        state->set_metadata(m);
 
@@ -159,6 +162,10 @@ void MetadataUpdate::finish(int r)
            << ": " << cpp_strerror(r) << dendl;
   }
 }
+catch(const std::exception& e)
+{
+    dout(1) << "!!JFW: mon caught: " << e.what() << dendl;
+}
 
 void Mgr::background_init(Context *completion)
 {
@@ -190,7 +197,7 @@ std::map<std::string, std::string> Mgr::load_store()
   std::map<std::string, std::string> loaded;
   
   for (auto &key_str : cmd.json_result.get_array()) {
-    std::string const key = key_str.get_str();
+    std::string const key = key_str.get_string().c_str();
     
     dout(20) << "saw key '" << key << "'" << dendl;
 
@@ -394,6 +401,7 @@ void Mgr::init()
 }
 
 void Mgr::load_all_metadata()
+try
 {
   ceph_assert(ceph_mutex_is_locked_by_me(lock));
 
@@ -415,7 +423,7 @@ void Mgr::load_all_metadata()
   ceph_assert(osd_cmd.r == 0);
 
   for (auto &metadata_val : mds_cmd.json_result.get_array()) {
-    json_spirit::mObject daemon_meta = metadata_val.get_obj();
+    boost::json::object daemon_meta = metadata_val.get_object();
     if (daemon_meta.count("hostname") == 0) {
       dout(1) << "Skipping incomplete metadata entry" << dendl;
       continue;
@@ -423,21 +431,21 @@ void Mgr::load_all_metadata()
 
     DaemonStatePtr dm = std::make_shared<DaemonState>(daemon_state.types);
     dm->key = DaemonKey{"mds",
-                        daemon_meta.at("name").get_str()};
-    dm->hostname = daemon_meta.at("hostname").get_str();
+                        daemon_meta.at("name").get_string().c_str()};
+    dm->hostname = daemon_meta.at("hostname").get_string().c_str();
 
     daemon_meta.erase("name");
     daemon_meta.erase("hostname");
 
     for (const auto &[key, val] : daemon_meta) {
-      dm->metadata.emplace(key, val.get_str());
+      dm->metadata.emplace(key, val.get_string());
     }
 
     daemon_state.insert(dm);
   }
 
   for (auto &metadata_val : mon_cmd.json_result.get_array()) {
-    json_spirit::mObject daemon_meta = metadata_val.get_obj();
+    boost::json::object daemon_meta = metadata_val.get_object();
     if (daemon_meta.count("hostname") == 0) {
       dout(1) << "Skipping incomplete metadata entry" << dendl;
       continue;
@@ -445,15 +453,15 @@ void Mgr::load_all_metadata()
 
     DaemonStatePtr dm = std::make_shared<DaemonState>(daemon_state.types);
     dm->key = DaemonKey{"mon",
-                        daemon_meta.at("name").get_str()};
-    dm->hostname = daemon_meta.at("hostname").get_str();
+                        daemon_meta.at("name").get_string().c_str()};
+    dm->hostname = daemon_meta.at("hostname").get_string().c_str();
 
     daemon_meta.erase("name");
     daemon_meta.erase("hostname");
 
     std::map<string,string> m;
     for (const auto &[key, val] : daemon_meta) {
-      m.emplace(key, val.get_str());
+      m.emplace(key, val.get_string());
     }
     dm->set_metadata(m);
 
@@ -461,30 +469,34 @@ void Mgr::load_all_metadata()
   }
 
   for (auto &osd_metadata_val : osd_cmd.json_result.get_array()) {
-    json_spirit::mObject osd_metadata = osd_metadata_val.get_obj();
+    boost::json::object osd_metadata = osd_metadata_val.get_object();
     if (osd_metadata.count("hostname") == 0) {
       dout(1) << "Skipping incomplete metadata entry" << dendl;
       continue;
     }
-    dout(4) << osd_metadata.at("hostname").get_str() << dendl;
+    dout(4) << osd_metadata.at("hostname").get_string() << dendl;
 
     DaemonStatePtr dm = std::make_shared<DaemonState>(daemon_state.types);
     dm->key = DaemonKey{"osd",
-                        stringify(osd_metadata.at("id").get_int())};
-    dm->hostname = osd_metadata.at("hostname").get_str();
+                        stringify(osd_metadata.at("id").get_int64())};
+    dm->hostname = osd_metadata.at("hostname").get_string();
 
     osd_metadata.erase("id");
     osd_metadata.erase("hostname");
 
-    std::map<string,string> m;
-    for (const auto &i : osd_metadata) {
-      m[i.first] = i.second.get_str();
+    map<string,string> m;
+    for (const auto &[k, v] : osd_metadata) {
+      m[k] = v.get_string();
     }
     dm->set_metadata(m);
 
     daemon_state.insert(dm);
   }
 }
+catch(const std::exception& e)
+{
+      dout(1) << "JFW: load_all_metadata(): caught " << e.what() << dendl;
+}
 
 void Mgr::handle_osd_map()
 {
index a5490bef3d6768e7a4b251a728f66522e1d96add..5b4f8727fde5e254930f4fe9eff1f2e5d0f53f17 100644 (file)
@@ -20,6 +20,8 @@
 #include "common/Cond.h"
 #include "mon/MonClient.h"
 
+#include <boost/json.hpp>
+
 class Command
 {
 protected:
@@ -49,22 +51,30 @@ public:
   virtual ~Command() {}
 };
 
-
 class JSONCommand : public Command
 {
 public:
-  json_spirit::mValue json_result;
+  boost::json::value json_result;
 
   void wait() override
   {
     Command::wait();
 
     if (r == 0) {
+      boost::system::error_code ec;
+
+      json_result = boost::json::parse(outbl.to_str(), ec);
+
+      if(ec)
+       r = -EINVAL;
+
+/*JFW:
       bool read_ok = json_spirit::read(
           outbl.to_str(), json_result);
       if (!read_ok) {
         r = -EINVAL;
       }
+*/
     }
   }
 };