MXS-1220: Add PUT support for servers
The server can now be modified with a PUT request of a modified server resource. The server resource was reorganized to have the parameters as a separate entity from the other more general entities of the resource. The PUT/POST functions return a more appropriate error message when no request body is provided. Moved some of the constant names used in server.cc into the config.h header.
This commit is contained in:

committed by
Markus Mäkelä

parent
1e55ee5b2f
commit
043ed95bdf
@ -73,6 +73,7 @@ extern const char CN_MS_TIMESTAMP[];
|
|||||||
extern const char CN_NAME[];
|
extern const char CN_NAME[];
|
||||||
extern const char CN_NON_BLOCKING_POLLS[];
|
extern const char CN_NON_BLOCKING_POLLS[];
|
||||||
extern const char CN_OPTIONS[];
|
extern const char CN_OPTIONS[];
|
||||||
|
extern const char CN_PARAMETERS[];
|
||||||
extern const char CN_PASSWORD[];
|
extern const char CN_PASSWORD[];
|
||||||
extern const char CN_POLL_SLEEP[];
|
extern const char CN_POLL_SLEEP[];
|
||||||
extern const char CN_PORT[];
|
extern const char CN_PORT[];
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <maxscale/cppdefs.hh>
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <maxscale/jansson.h>
|
#include <maxscale/jansson.h>
|
||||||
#include <maxscale/utils.hh>
|
#include <maxscale/utils.hh>
|
||||||
@ -71,4 +72,42 @@ static inline std::string json_dump(const Closer<json_t*>& json, int flags = 0)
|
|||||||
return json_dump(json.get(), flags);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ const char CN_MS_TIMESTAMP[] = "ms_timestamp";
|
|||||||
const char CN_NAME[] = "name";
|
const char CN_NAME[] = "name";
|
||||||
const char CN_NON_BLOCKING_POLLS[] = "non_blocking_polls";
|
const char CN_NON_BLOCKING_POLLS[] = "non_blocking_polls";
|
||||||
const char CN_OPTIONS[] = "options";
|
const char CN_OPTIONS[] = "options";
|
||||||
|
const char CN_PARAMETERS[] = "parameters";
|
||||||
const char CN_PASSWORD[] = "password";
|
const char CN_PASSWORD[] = "password";
|
||||||
const char CN_POLL_SLEEP[] = "poll_sleep";
|
const char CN_POLL_SLEEP[] = "poll_sleep";
|
||||||
const char CN_PORT[] = "port";
|
const char CN_PORT[] = "port";
|
||||||
|
@ -10,15 +10,21 @@
|
|||||||
* of this software will be governed by version 2 or later of the General
|
* of this software will be governed by version 2 or later of the General
|
||||||
* Public License.
|
* Public License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <maxscale/cppdefs.hh>
|
||||||
|
|
||||||
#include "maxscale/config_runtime.h"
|
#include "maxscale/config_runtime.h"
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <maxscale/atomic.h>
|
#include <maxscale/atomic.h>
|
||||||
#include <maxscale/paths.h>
|
#include <maxscale/paths.h>
|
||||||
#include <maxscale/spinlock.h>
|
#include <maxscale/spinlock.h>
|
||||||
|
#include <maxscale/jansson.hh>
|
||||||
|
|
||||||
#include "maxscale/config.h"
|
#include "maxscale/config.h"
|
||||||
#include "maxscale/monitor.h"
|
#include "maxscale/monitor.h"
|
||||||
@ -27,6 +33,7 @@
|
|||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::set;
|
using std::set;
|
||||||
|
using mxs::Closer;
|
||||||
|
|
||||||
static SPINLOCK crt_lock = SPINLOCK_INIT;
|
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;
|
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);
|
spinlock_acquire(&crt_lock);
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
@ -824,6 +831,65 @@ SERVER* runtime_create_server_from_json(json_t* json)
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool handle_alter_server_relations(SERVER* server, json_t* new_json)
|
||||||
|
{
|
||||||
|
bool rval = false;
|
||||||
|
set<string> old_relations;
|
||||||
|
set<string> new_relations;
|
||||||
|
Closer<json_t*> 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<string> removed_relations;
|
||||||
|
set<string> 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)
|
static bool monitor_contains_required_fields(json_t* json)
|
||||||
{
|
{
|
||||||
json_t* value;
|
json_t* value;
|
||||||
|
@ -89,7 +89,7 @@ bool runtime_unlink_server(SERVER *server, const char *target);
|
|||||||
* @param value New value
|
* @param value New value
|
||||||
* @return True if @c key was one of the supported parameters
|
* @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
|
* @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);
|
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
|
* @brief Create a new monitor from JSON
|
||||||
*
|
*
|
||||||
|
@ -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)
|
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)
|
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_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_server, 1, "servers")));
|
||||||
m_post.push_back(SResource(new Resource(cb_create_monitor, 1, "monitors")));
|
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()
|
~RootResource()
|
||||||
|
@ -14,23 +14,6 @@
|
|||||||
/**
|
/**
|
||||||
* @file server.c - A representation of a backend server within the gateway.
|
* @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 <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -39,6 +22,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <maxscale/config.h>
|
||||||
#include <maxscale/service.h>
|
#include <maxscale/service.h>
|
||||||
#include <maxscale/session.h>
|
#include <maxscale/session.h>
|
||||||
#include <maxscale/server.h>
|
#include <maxscale/server.h>
|
||||||
@ -1403,14 +1387,25 @@ json_t* server_list_to_json(const char* host)
|
|||||||
|
|
||||||
json_t* server_to_json(const SERVER* server, const char* host)
|
json_t* server_to_json(const SERVER* server, const char* host)
|
||||||
{
|
{
|
||||||
// TODO: Add error checks
|
|
||||||
json_t* rval = json_object();
|
json_t* rval = json_object();
|
||||||
|
|
||||||
json_object_set_new(rval, CN_NAME, json_string(server->unique_name));
|
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);
|
char* stat = server_status(server);
|
||||||
json_object_set_new(rval, "status", json_string(stat));
|
json_object_set_new(rval, "status", json_string(stat));
|
||||||
MXS_FREE(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));
|
json_object_set_new(rval, "last_heartbeat", json_string(timebuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Store statistics */
|
||||||
json_t* stats = json_object();
|
json_t* stats = json_object();
|
||||||
|
|
||||||
json_object_set_new(stats, "connections", json_integer(server->stats.n_current));
|
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);
|
json_object_set_new(rval, "statictics", stats);
|
||||||
|
|
||||||
|
/** Store server relations to other objects */
|
||||||
json_t* rel = json_object();
|
json_t* rel = json_object();
|
||||||
|
|
||||||
json_t* arr = service_relations_to_server(server, host);
|
json_t* arr = service_relations_to_server(server, host);
|
||||||
|
|
||||||
if (json_array_size(arr) > 0)
|
if (json_array_size(arr) > 0)
|
||||||
{
|
{
|
||||||
json_object_set_new(rel, "services", arr);
|
json_object_set_new(rel, CN_SERVICES, arr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1477,14 +1474,14 @@ json_t* server_to_json(const SERVER* server, const char* host)
|
|||||||
|
|
||||||
if (json_array_size(arr) > 0)
|
if (json_array_size(arr) > 0)
|
||||||
{
|
{
|
||||||
json_object_set_new(rel, "monitors", arr);
|
json_object_set_new(rel, CN_MONITORS, arr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
json_decref(arr);
|
json_decref(arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_set_new(rval, "relationships", rel);
|
json_object_set_new(rval, CN_RELATIONSHIPS, rel);
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user