MXS-1220: Add POST handling for servers

New servers can now be created by POSTing a new server definition to the
/servers/ resource.
This commit is contained in:
Markus Mäkelä 2017-04-19 16:39:08 +03:00 committed by Markus Mäkelä
parent 46344b204a
commit 80104d6dad
4 changed files with 193 additions and 3 deletions

View File

@ -13,6 +13,8 @@
#include "maxscale/config_runtime.h"
#include <strings.h>
#include <string>
#include <set>
#include <maxscale/atomic.h>
#include <maxscale/paths.h>
@ -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<string>& relations)
{
bool rval = true;
for (set<string>::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<string>& relations)
{
bool rval = true;
for (set<string>::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<string>& 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<string> 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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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))
{