MXS-922: Serialize created servers
When a server is created via MaxAdmin, it will be serialized to disk. This allows created servers to be retained through a restart of MaxScale. Currently, all serialized objects are stored in one folder and there is no structure in the created files. In the future, servers could be created under a `servers` subdirectory so that it is easier to see what was added. Whether there is a need for this will be seen.
This commit is contained in:
@ -234,13 +234,12 @@ extern void server_update_ssl(SERVER *server, const char *key, const char *value
|
|||||||
* @brief Serialize a server to a file
|
* @brief Serialize a server to a file
|
||||||
*
|
*
|
||||||
* This converts @c server into an INI format file. This allows created servers
|
* This converts @c server into an INI format file. This allows created servers
|
||||||
* to be persisted to disk. A new file is only created if the file pointed by
|
* to be persisted to disk. This will replace any existing files with the same
|
||||||
* @c filename does not exist at the time this function is called.
|
* name.
|
||||||
*
|
*
|
||||||
* @param server Server to serialize
|
* @param server Server to serialize
|
||||||
* @param filename Path to a file where the server is persisted
|
|
||||||
* @return False if the serialization of the server fails, true if it was successful
|
* @return False if the serialization of the server fails, true if it was successful
|
||||||
*/
|
*/
|
||||||
bool server_serialize(SERVER *server, const char *filename);
|
bool server_serialize(SERVER *server);
|
||||||
|
|
||||||
MXS_END_DECLS
|
MXS_END_DECLS
|
||||||
|
|||||||
@ -47,6 +47,7 @@
|
|||||||
#include <maxscale/gw_ssl.h>
|
#include <maxscale/gw_ssl.h>
|
||||||
#include <maxscale/alloc.h>
|
#include <maxscale/alloc.h>
|
||||||
#include <maxscale/modules.h>
|
#include <maxscale/modules.h>
|
||||||
|
#include <maxscale/gwdirs.h>
|
||||||
|
|
||||||
static SPINLOCK server_spin = SPINLOCK_INIT;
|
static SPINLOCK server_spin = SPINLOCK_INIT;
|
||||||
static SERVER *allServers = NULL;
|
static SERVER *allServers = NULL;
|
||||||
@ -1062,7 +1063,14 @@ bool server_set_version_string(SERVER* server, const char* string)
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_serialize(SERVER *server, const char *filename)
|
/**
|
||||||
|
* Creates a server configuration at the location pointed by @c filename
|
||||||
|
*
|
||||||
|
* @param server Server to serialize into a configuration
|
||||||
|
* @param filename Filename where configuration is written
|
||||||
|
* @return True on success, false on error
|
||||||
|
*/
|
||||||
|
static bool create_server_config(SERVER *server, const char *filename)
|
||||||
{
|
{
|
||||||
int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
int file = open(filename, O_EXCL | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||||
|
|
||||||
@ -1162,6 +1170,43 @@ bool server_serialize(SERVER *server, const char *filename)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool server_serialize(SERVER *server)
|
||||||
|
{
|
||||||
|
bool rval = false;
|
||||||
|
char filename[PATH_MAX];
|
||||||
|
snprintf(filename, sizeof(filename), "%s/%s.cnf.tmp", get_config_persistdir(),
|
||||||
|
server->unique_name);
|
||||||
|
|
||||||
|
if (unlink(filename) == -1 && errno != ENOENT)
|
||||||
|
{
|
||||||
|
char err[MXS_STRERROR_BUFLEN];
|
||||||
|
MXS_ERROR("Failed to remove temporary server configuration at '%s': %d, %s",
|
||||||
|
filename, errno, strerror_r(errno, err, sizeof(err)));
|
||||||
|
}
|
||||||
|
else if (create_server_config(server, filename))
|
||||||
|
{
|
||||||
|
char final_filename[PATH_MAX];
|
||||||
|
strcpy(final_filename, filename);
|
||||||
|
|
||||||
|
char *dot = strrchr(final_filename, '.');
|
||||||
|
ss_dassert(dot);
|
||||||
|
*dot = '\0';
|
||||||
|
|
||||||
|
if (rename(filename, final_filename) == 0)
|
||||||
|
{
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char err[MXS_STRERROR_BUFLEN];
|
||||||
|
MXS_ERROR("Failed to rename temporary server configuration at '%s': %d, %s",
|
||||||
|
filename, errno, strerror_r(errno, err, sizeof(err)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
bool server_is_ssl_parameter(const char *key)
|
bool server_is_ssl_parameter(const char *key)
|
||||||
{
|
{
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
|
|||||||
@ -140,31 +140,37 @@ bool test_load_config(const char *input, SERVER *server)
|
|||||||
bool test_serialize()
|
bool test_serialize()
|
||||||
{
|
{
|
||||||
char name[] = "serialized-server";
|
char name[] = "serialized-server";
|
||||||
|
char config_name[] = "serialized-server.cnf";
|
||||||
|
char old_config_name[] = "serialized-server.cnf.old";
|
||||||
|
char *persist_dir = MXS_STRDUP_A("./");
|
||||||
|
set_config_persistdir(persist_dir);
|
||||||
SERVER *server = server_alloc("127.0.0.1", "HTTPD", 9876, "NullAuthAllow", "fake=option");
|
SERVER *server = server_alloc("127.0.0.1", "HTTPD", 9876, "NullAuthAllow", "fake=option");
|
||||||
TEST(server, "Server allocation failed");
|
TEST(server, "Server allocation failed");
|
||||||
server_set_unique_name(server, name);
|
server_set_unique_name(server, name);
|
||||||
|
|
||||||
/** Make sure the file doesn't exist */
|
/** Make sure the files don't exist */
|
||||||
unlink("./server.cnf");
|
unlink(config_name);
|
||||||
|
unlink(old_config_name);
|
||||||
|
|
||||||
/** Serialize server to disk */
|
/** Serialize server to disk */
|
||||||
TEST(server_serialize(server, "./server.cnf"), "Failed to synchronize original server");
|
TEST(server_serialize(server), "Failed to synchronize original server");
|
||||||
|
|
||||||
/** Load it again */
|
/** Load it again */
|
||||||
TEST(test_load_config("./server.cnf", server), "Failed to load the serialized server");
|
TEST(test_load_config(config_name, server), "Failed to load the serialized server");
|
||||||
|
|
||||||
/** We should have two identical servers */
|
/** We should have two identical servers */
|
||||||
SERVER *created = server_find_by_unique_name(name);
|
SERVER *created = server_find_by_unique_name(name);
|
||||||
TEST(created->next == server, "We should end up with two servers");
|
TEST(created->next == server, "We should end up with two servers");
|
||||||
|
|
||||||
/** Make sure the file doesn't exist */
|
rename(config_name, old_config_name);
|
||||||
unlink("./server-created.cnf");
|
|
||||||
|
|
||||||
/** Serialize the loaded server to disk */
|
/** Serialize the loaded server to disk */
|
||||||
TEST(server_serialize(created, "./server-created.cnf"), "Failed to synchronize the copied server");
|
TEST(server_serialize(created), "Failed to synchronize the copied server");
|
||||||
|
|
||||||
/** Check that they serialize to identical files */
|
/** Check that they serialize to identical files */
|
||||||
TEST(system("diff ./server.cnf ./server-created.cnf") == 0, "The files are not identical");
|
char cmd[1024];
|
||||||
|
sprintf(cmd, "diff ./%s ./%s", config_name, old_config_name);
|
||||||
|
TEST(system(cmd) == 0, "The files are not identical");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -987,6 +987,17 @@ struct subcommand flushoptions[] =
|
|||||||
/** This is used to prevent concurrent creation or removal of servers */
|
/** This is used to prevent concurrent creation or removal of servers */
|
||||||
static SPINLOCK server_mod_lock = SPINLOCK_INIT;
|
static SPINLOCK server_mod_lock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new server
|
||||||
|
*
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param name Server name
|
||||||
|
* @param address Server network address
|
||||||
|
* @param port Server port
|
||||||
|
* @param protocol Protocol, NULL for default (MySQLBackend)
|
||||||
|
* @param authenticator Authenticator module, NULL for default (MySQLBackendAuth)
|
||||||
|
* @param authenticator_options Authenticator options, NULL for no options
|
||||||
|
*/
|
||||||
static void createServer(DCB *dcb, char *name, char *address, char *port,
|
static void createServer(DCB *dcb, char *name, char *address, char *port,
|
||||||
char *protocol, char *authenticator, char *authenticator_options)
|
char *protocol, char *authenticator, char *authenticator_options)
|
||||||
{
|
{
|
||||||
@ -1005,8 +1016,18 @@ static void createServer(DCB *dcb, char *name, char *address, char *port,
|
|||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
server_set_unique_name(server, name);
|
server_set_unique_name(server, name);
|
||||||
// TODO: Serialize the server to disk
|
|
||||||
dcb_printf(dcb, "Created server '%s'\n", name);
|
if (server_serialize(server))
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "Created server '%s'\n", name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dcb_printf(dcb, "WARNING: The server was added to the runtime "
|
||||||
|
"configuration but persisting the new server to disk "
|
||||||
|
"failed. This server will NOT be loaded when MaxScale "
|
||||||
|
"is restarted. See log file for more details on why it failed.\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user