From: Jesse F. Williamson <1643380+chardan@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:27:11 +0000 (-0800) Subject: convert spirit_json to Boost.JSON (ceph_json, Mgr) X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=cc93db84c17861acfcc35d45fdcd38894acf2c62;p=ceph.git convert spirit_json to Boost.JSON (ceph_json, Mgr) Signed-off-by: Jesse F. Williamson --- diff --git a/src/common/ceph_json.cc b/src/common/ceph_json.cc index b63d73e5a8c..1b54d6f71ce 100644 --- a/src/common/ceph_json.cc +++ b/src/common/ceph_json.cc @@ -1,17 +1,16 @@ #include "common/ceph_json.h" #include "include/utime.h" -// for testing DELETE ME -#include -#include - #include #include #include +#include -#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 -using namespace json_spirit; +#include +#include 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(el, obj)); + auto p = std::unique_ptr(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(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 JSONObj::get_array_elements() { - vector elements; - Array temp_array; + vector 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(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; } diff --git a/src/common/ceph_json.h b/src/common/ceph_json.h index a90b7ec79d0..dcdc9b995fb 100644 --- a/src/common/ceph_json.h +++ b/src/common/ceph_json.h @@ -1,38 +1,42 @@ #ifndef CEPH_JSON_H #define CEPH_JSON_H -#include #include #include -#include +#include #include +#include +#include #include -#include + +#include +#include #include #include -#include + +#include #include -#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::iterator map_iter_t; + + using map_iter_t = std::map>::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>; + 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 children; - std::map attr_map; - void handle_value(json_spirit::Value v); -public: + std::multimap> children; + + std::map 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 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"); } } diff --git a/src/mgr/ActivePyModules.cc b/src/mgr/ActivePyModules.cc index 709fcfae227..94ae44d9675 100644 --- a/src/mgr/ActivePyModules.cc +++ b/src/mgr/ActivePyModules.cc @@ -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 diff --git a/src/mgr/Mgr.cc b/src/mgr/Mgr.cc index 418e8ae984a..7e0b4c26f1c 100644 --- a/src/mgr/Mgr.cc +++ b/src/mgr/Mgr.cc @@ -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 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(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 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 Mgr::load_store() std::map 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(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(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 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(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 m; - for (const auto &i : osd_metadata) { - m[i.first] = i.second.get_str(); + map 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() { diff --git a/src/mgr/MgrContext.h b/src/mgr/MgrContext.h index a5490bef3d6..5b4f8727fde 100644 --- a/src/mgr/MgrContext.h +++ b/src/mgr/MgrContext.h @@ -20,6 +20,8 @@ #include "common/Cond.h" #include "mon/MonClient.h" +#include + 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; } +*/ } } };