diff --git a/include/maxscale/config.h b/include/maxscale/config.h index b017fc306..7d69f8d85 100644 --- a/include/maxscale/config.h +++ b/include/maxscale/config.h @@ -73,6 +73,7 @@ extern const char CN_MS_TIMESTAMP[]; extern const char CN_NAME[]; extern const char CN_NON_BLOCKING_POLLS[]; extern const char CN_OPTIONS[]; +extern const char CN_PARAMETERS[]; extern const char CN_PASSWORD[]; extern const char CN_POLL_SLEEP[]; extern const char CN_PORT[]; diff --git a/include/maxscale/jansson.hh b/include/maxscale/jansson.hh index 9d40b70bf..73491c6b1 100644 --- a/include/maxscale/jansson.hh +++ b/include/maxscale/jansson.hh @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -71,4 +72,42 @@ static inline std::string json_dump(const Closer& json, int flags = 0) return json_dump(json.get(), flags); } +/** + * @brief Convert JSON to string + * + * @param JSON to convert + * + * @return The JSON value converted to a string + */ +static inline std::string json_to_string(json_t* json) +{ + std::stringstream ss; + + if (json_is_string(json)) + { + ss << json_string_value(json); + } + else if (json_is_boolean(json)) + { + ss << (json_boolean_value(json) ? "true" : "false"); + } + else if (json_is_real(json)) + { + ss << json_real_value(json); + } + else if (json_is_number(json)) + { + ss << json_number_value(json); + } + else if (json_is_integer(json)) + { + ss << json_integer_value(json); + } + else if (json_is_null(json)) + { + ss << ""; + } + + return ss.str(); +} } diff --git a/server/core/config.cc b/server/core/config.cc index 7d80b6b2a..09abeb260 100644 --- a/server/core/config.cc +++ b/server/core/config.cc @@ -84,6 +84,7 @@ const char CN_MS_TIMESTAMP[] = "ms_timestamp"; const char CN_NAME[] = "name"; const char CN_NON_BLOCKING_POLLS[] = "non_blocking_polls"; const char CN_OPTIONS[] = "options"; +const char CN_PARAMETERS[] = "parameters"; const char CN_PASSWORD[] = "password"; const char CN_POLL_SLEEP[] = "poll_sleep"; const char CN_PORT[] = "port"; diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index f57aae731..1cd891d6e 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -10,15 +10,21 @@ * of this software will be governed by version 2 or later of the General * Public License. */ + +#include + #include "maxscale/config_runtime.h" #include #include #include +#include +#include #include #include #include +#include #include "maxscale/config.h" #include "maxscale/monitor.h" @@ -27,6 +33,7 @@ using std::string; using std::set; +using mxs::Closer; static SPINLOCK crt_lock = SPINLOCK_INIT; @@ -271,7 +278,7 @@ bool runtime_enable_server_ssl(SERVER *server, const char *key, const char *cert return rval; } -bool runtime_alter_server(SERVER *server, char *key, char *value) +bool runtime_alter_server(SERVER *server, const char *key, const char *value) { spinlock_acquire(&crt_lock); bool valid = true; @@ -736,8 +743,8 @@ static bool server_contains_required_fields(json_t* json) json_t* value; return (value = json_object_get(json, CN_NAME)) && json_is_string(value) && - (value = json_object_get(json, CN_ADDRESS)) && json_is_string(value) && - (value = json_object_get(json, CN_PORT)) && json_is_integer(value); + (value = json_object_get(json, CN_ADDRESS)) && json_is_string(value) && + (value = json_object_get(json, CN_PORT)) && json_is_integer(value); } const char* server_relation_types[] = @@ -750,7 +757,7 @@ const char* server_relation_types[] = static bool server_relation_is_valid(const string& type, const string& value) { return (type == CN_SERVICES && service_find(value.c_str())) || - (type == CN_MONITORS && monitor_find(value.c_str())); + (type == CN_MONITORS && monitor_find(value.c_str())); } static bool unlink_server_relations(SERVER* server, set& relations) @@ -824,12 +831,71 @@ SERVER* runtime_create_server_from_json(json_t* json) return rval; } +bool handle_alter_server_relations(SERVER* server, json_t* new_json) +{ + bool rval = false; + set old_relations; + set new_relations; + Closer old_json(server_to_json(server, "")); + ss_dassert(old_json.get()); + + if (extract_relations(old_json.get(), old_relations, server_relation_types, server_relation_is_valid) && + extract_relations(new_json, new_relations, server_relation_types, server_relation_is_valid)) + { + set removed_relations; + set added_relations; + + std::set_difference(old_relations.begin(), old_relations.end(), + new_relations.begin(), new_relations.end(), + std::inserter(removed_relations, removed_relations.begin())); + + std::set_difference(new_relations.begin(), new_relations.end(), + old_relations.begin(), old_relations.end(), + std::inserter(added_relations, added_relations.begin())); + + if (link_server_relations(server, added_relations) && + unlink_server_relations(server, removed_relations)) + { + rval = true; + } + } + + return rval; +} + +bool runtime_alter_server_from_json(SERVER* server, json_t* new_json) +{ + bool rval = false; + + if (handle_alter_server_relations(server, new_json)) + { + json_t* parameters = json_object_get(new_json, CN_PARAMETERS); + + if (parameters) + { + rval = true; + const char *key; + json_t *value; + + json_object_foreach(parameters, key, value) + { + if (!runtime_alter_server(server, key, mxs::json_to_string(value).c_str())) + { + rval = false; + } + } + } + } + + return rval; +} + static bool monitor_contains_required_fields(json_t* json) { json_t* value; return (value = json_object_get(json, CN_NAME)) && json_is_string(value) && - (value = json_object_get(json, CN_MODULE)) && json_is_string(value); + (value = json_object_get(json, CN_MODULE)) && json_is_string(value); } const char* monitor_relation_types[] = diff --git a/server/core/maxscale/config_runtime.h b/server/core/maxscale/config_runtime.h index 2c023a9f2..1765a5086 100644 --- a/server/core/maxscale/config_runtime.h +++ b/server/core/maxscale/config_runtime.h @@ -89,7 +89,7 @@ bool runtime_unlink_server(SERVER *server, const char *target); * @param value New value * @return True if @c key was one of the supported parameters */ -bool runtime_alter_server(SERVER *server, char *key, char *value); +bool runtime_alter_server(SERVER *server, const char *key, const char *value); /** * @brief Enable SSL for a server @@ -188,6 +188,16 @@ bool runtime_destroy_monitor(MXS_MONITOR *monitor); */ SERVER* runtime_create_server_from_json(json_t* json); +/** + * @brief Alter a server using JSON + * + * @param server Server to alter + * @param new_json JSON definition of the new server + * + * @return True if the server was successfully modified to represent @c new_json + */ +bool runtime_alter_server_from_json(SERVER* server, json_t* new_json); + /** * @brief Create a new monitor from JSON * diff --git a/server/core/resource.cc b/server/core/resource.cc index 54e38152c..170e0a5dd 100644 --- a/server/core/resource.cc +++ b/server/core/resource.cc @@ -117,7 +117,24 @@ HttpResponse cb_create_server(HttpRequest& request) } } - return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); + return HttpResponse(MHD_HTTP_BAD_REQUEST); +} + +HttpResponse cb_alter_server(HttpRequest& request) +{ + json_t* json = request.get_json(); + + if (json) + { + SERVER* server = server_find_by_unique_name(request.uri_part(1).c_str()); + + if (server && runtime_alter_server_from_json(server, json)) + { + return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host())); + } + } + + return HttpResponse(MHD_HTTP_BAD_REQUEST); } HttpResponse cb_create_monitor(HttpRequest& request) @@ -134,7 +151,7 @@ HttpResponse cb_create_monitor(HttpRequest& request) } } - return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); + return HttpResponse(MHD_HTTP_BAD_REQUEST); } HttpResponse cb_all_servers(HttpRequest& request) @@ -301,6 +318,8 @@ public: m_post.push_back(SResource(new Resource(cb_flush, 3, "maxscale", "logs", "flush"))); m_post.push_back(SResource(new Resource(cb_create_server, 1, "servers"))); m_post.push_back(SResource(new Resource(cb_create_monitor, 1, "monitors"))); + + m_put.push_back(SResource(new Resource(cb_alter_server, 2, "servers", ":server"))); } ~RootResource() diff --git a/server/core/server.cc b/server/core/server.cc index c856c9b12..4cb9daa4b 100644 --- a/server/core/server.cc +++ b/server/core/server.cc @@ -14,23 +14,6 @@ /** * @file server.c - A representation of a backend server within the gateway. * - * @verbatim - * Revision History - * - * Date Who Description - * 18/06/13 Mark Riddoch Initial implementation - * 17/05/14 Mark Riddoch Addition of unique_name - * 20/05/14 Massimiliano Pinto Addition of server_string - * 21/05/14 Massimiliano Pinto Addition of node_id - * 28/05/14 Massimiliano Pinto Addition of rlagd and node_ts fields - * 20/06/14 Massimiliano Pinto Addition of master_id, depth, slaves fields - * 26/06/14 Mark Riddoch Addition of server parameters - * 30/08/14 Massimiliano Pinto Addition of new service status description - * 30/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS description - * 01/06/15 Massimiliano Pinto Addition of server_update_address/port - * 19/06/15 Martin Brampton Extra code for persistent connections - * - * @endverbatim */ #include #include @@ -39,6 +22,7 @@ #include #include +#include #include #include #include @@ -1403,14 +1387,25 @@ json_t* server_list_to_json(const char* host) json_t* server_to_json(const SERVER* server, const char* host) { - // TODO: Add error checks json_t* rval = json_object(); json_object_set_new(rval, CN_NAME, json_string(server->unique_name)); - json_object_set_new(rval, CN_ADDRESS, json_string(server->name)); - json_object_set_new(rval, CN_PORT, json_integer(server->port)); - json_object_set_new(rval, CN_PROTOCOL, json_string(server->protocol)); + /** Store server parameters */ + json_t* params = json_object(); + + json_object_set_new(params, CN_ADDRESS, json_string(server->name)); + json_object_set_new(params, CN_PORT, json_integer(server->port)); + json_object_set_new(params, CN_PROTOCOL, json_string(server->protocol)); + + for (SERVER_PARAM* p = server->parameters; p; p = p->next) + { + json_object_set_new(params, p->name, json_string(p->value)); + } + + json_object_set_new(rval, CN_PARAMETERS, params); + + /** Store general information about the server state */ char* stat = server_status(server); json_object_set_new(rval, "status", json_string(stat)); MXS_FREE(stat); @@ -1452,6 +1447,7 @@ json_t* server_to_json(const SERVER* server, const char* host) json_object_set_new(rval, "last_heartbeat", json_string(timebuf)); } + /** Store statistics */ json_t* stats = json_object(); json_object_set_new(stats, "connections", json_integer(server->stats.n_current)); @@ -1460,13 +1456,14 @@ json_t* server_to_json(const SERVER* server, const char* host) json_object_set_new(rval, "statictics", stats); + /** Store server relations to other objects */ json_t* rel = json_object(); json_t* arr = service_relations_to_server(server, host); if (json_array_size(arr) > 0) { - json_object_set_new(rel, "services", arr); + json_object_set_new(rel, CN_SERVICES, arr); } else { @@ -1477,14 +1474,14 @@ json_t* server_to_json(const SERVER* server, const char* host) if (json_array_size(arr) > 0) { - json_object_set_new(rel, "monitors", arr); + json_object_set_new(rel, CN_MONITORS, arr); } else { json_decref(arr); } - json_object_set_new(rval, "relationships", rel); + json_object_set_new(rval, CN_RELATIONSHIPS, rel); return rval; }