From 80104d6dad6924cd3865c97dfdc70ed0d0f864ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 19 Apr 2017 16:39:08 +0300 Subject: [PATCH] MXS-1220: Add POST handling for servers New servers can now be created by POSTing a new server definition to the /servers/ resource. --- server/core/config_runtime.cc | 162 ++++++++++++++++++++++++++ server/core/maxscale/config_runtime.h | 9 ++ server/core/maxscale/httprequest.hh | 2 +- server/core/resource.cc | 23 +++- 4 files changed, 193 insertions(+), 3 deletions(-) diff --git a/server/core/config_runtime.cc b/server/core/config_runtime.cc index ad683b1ba..4095a4e6e 100644 --- a/server/core/config_runtime.cc +++ b/server/core/config_runtime.cc @@ -13,6 +13,8 @@ #include "maxscale/config_runtime.h" #include +#include +#include #include #include @@ -23,6 +25,9 @@ #include "maxscale/modules.h" #include "maxscale/service.h" +using std::string; +using std::set; + static SPINLOCK crt_lock = SPINLOCK_INIT; bool runtime_link_server(SERVER *server, const char *target) @@ -659,3 +664,160 @@ bool runtime_destroy_monitor(MXS_MONITOR *monitor) spinlock_release(&crt_lock); return rval; } + +static bool server_contains_required_fields(json_t* json) +{ + json_t* value; + + return (value = json_object_get(json, "name")) && json_is_string(value) && + (value = json_object_get(json, "address")) && json_is_string(value) && + (value = json_object_get(json, "port")) && json_is_integer(value); +} + +const char* server_relation_types[] = +{ + "services", + "monitors", + NULL +}; + +static bool server_relation_is_valid(string type, string value) +{ + return (type == "services" && service_find(value.c_str())) || + (type == "monitors" && monitor_find(value.c_str())); +} + +static bool unlink_server_relations(SERVER* server, set& relations) +{ + bool rval = true; + + for (set::iterator it = relations.begin(); it != relations.end(); it++) + { + if (!runtime_unlink_server(server, it->c_str())) + { + rval = false; + } + } + + return rval; +} + +static bool link_server_relations(SERVER* server, set& relations) +{ + bool rval = true; + + for (set::iterator it = relations.begin(); it != relations.end(); it++) + { + if (!runtime_link_server(server, it->c_str())) + { + unlink_server_relations(server, relations); + rval = false; + break; + } + } + + return rval; +} + +static bool extract_relations(json_t* json, set& relations) +{ + bool rval = true; + json_t* rel; + + if ((rel = json_object_get(json, "relationships"))) + { + for (int i = 0; server_relation_types[i]; i++) + { + json_t* arr = json_object_get(rel, server_relation_types[i]); + + if (arr) + { + size_t size = json_array_size(arr); + + for (size_t j = 0; j < size; j++) + { + json_t* t = json_array_get(arr, j); + + if (json_is_string(t)) + { + string value = json_string_value(t); + + // Remove the link part + size_t pos = value.find_last_of("/"); + if (pos != string::npos) + { + value.erase(0, pos + 1); + } + + if (server_relation_is_valid(server_relation_types[i], value)) + { + relations.insert(value); + } + else + { + rval = false; + } + } + else + { + rval = false; + } + } + } + } + } + + return rval; +} + +static inline const char* string_or_null(json_t* json, const char* name) +{ + const char* rval = NULL; + json_t* value = json_object_get(json, name); + + if (value && json_is_string(value)) + { + rval = json_string_value(value); + } + + return rval; +} + +SERVER* runtime_create_server_from_json(json_t* json) +{ + SERVER* rval = NULL; + + if (server_contains_required_fields(json)) + { + const char* name = json_string_value(json_object_get(json, "name")); + const char* address = json_string_value(json_object_get(json, "address")); + + /** The port needs to be in string format */ + char port[200]; // Enough to store any port value + int i = json_integer_value(json_object_get(json, "port")); + snprintf(port, sizeof (port), "%d", i); + + /** Optional parameters */ + const char* protocol = string_or_null(json, "protocol"); + const char* authenticator = string_or_null(json, "authenticator"); + const char* authenticator_options = string_or_null(json, "authenticator_options"); + + + set relations; + + if (extract_relations(json, relations) && + runtime_create_server(name, address, port, protocol, authenticator, authenticator_options)) + { + rval = server_find_by_unique_name(name); + ss_dassert(rval); + + if (!link_server_relations(rval, relations)) + { + runtime_destroy_server(rval); + rval = NULL; + } + } + } + + return rval; +} diff --git a/server/core/maxscale/config_runtime.h b/server/core/maxscale/config_runtime.h index b24b6d3cb..3940668c8 100644 --- a/server/core/maxscale/config_runtime.h +++ b/server/core/maxscale/config_runtime.h @@ -179,4 +179,13 @@ bool runtime_create_monitor(const char *name, const char *module); */ bool runtime_destroy_monitor(MXS_MONITOR *monitor); +/** + * @brief Create a new server from JSON + * + * @param json JSON defining the server + * + * @return Created server or NULL on error + */ +SERVER* runtime_create_server_from_json(json_t* json); + MXS_END_DECLS diff --git a/server/core/maxscale/httprequest.hh b/server/core/maxscale/httprequest.hh index 6fd09a68d..7db40ed68 100644 --- a/server/core/maxscale/httprequest.hh +++ b/server/core/maxscale/httprequest.hh @@ -128,7 +128,7 @@ public: * * @return Raw JSON body or NULL if no body is defined */ - const json_t* get_json() const + json_t* get_json() { return m_json.get(); } diff --git a/server/core/resource.cc b/server/core/resource.cc index b62fb3a88..59a2ef228 100644 --- a/server/core/resource.cc +++ b/server/core/resource.cc @@ -25,6 +25,7 @@ #include "maxscale/filter.h" #include "maxscale/monitor.h" #include "maxscale/service.h" +#include "maxscale/config_runtime.h" using std::list; @@ -103,6 +104,23 @@ bool Resource::matching_variable_path(const string& path, const string& target) return rval; } +HttpResponse cb_create_server(HttpRequest& request) +{ + json_t* json = request.get_json(); + + if (json) + { + SERVER* server = runtime_create_server_from_json(json); + + if (server) + { + return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host())); + } + } + + return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); +} + HttpResponse cb_all_servers(HttpRequest& request) { return HttpResponse(MHD_HTTP_OK, server_list_to_json(request.host())); @@ -262,12 +280,13 @@ public: m_get.push_back(Resource(cb_modules, 2, "maxscale", "modules")); m_post.push_back(Resource(cb_flush, 3, "maxscale", "logs", "flush")); + m_post.push_back(Resource(cb_create_server, 1, "servers")); } HttpResponse process_request_type(ResourceList& list, HttpRequest& request) { - for (ResourceList::iterator it = m_get.begin(); - it != m_get.end(); it++) + for (ResourceList::iterator it = list.begin(); + it != list.end(); it++) { if (it->match(request)) {