Notification Service integration added

Notification Service integration added
This commit is contained in:
MassimilianoPinto
2015-03-09 18:06:16 +01:00
13 changed files with 1092 additions and 66 deletions

View File

@ -1,14 +1,15 @@
if(BUILD_TESTS)
file(GLOB FULLCORE_SRC *.c)
add_library(fullcore STATIC ${FULLCORE_SRC})
target_link_libraries(fullcore log_manager utils pthread ${EMBEDDED_LIB} ssl aio rt crypt dl crypto inih z m stdc++)
target_link_libraries(fullcore ${CURL_LIBRARIES} log_manager utils pthread ${EMBEDDED_LIB} ssl aio rt crypt dl crypto inih z m stdc++)
endif()
add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
housekeeper.c memlog.c resultset.c)
target_link_libraries(maxscale ${EMBEDDED_LIB} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++)
target_link_libraries(maxscale ${EMBEDDED_LIB} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++)
install(TARGETS maxscale DESTINATION bin)
add_executable(maxkeys maxkeys.c secrets.c utils.c)

View File

@ -41,6 +41,7 @@
* 30/10/14 Massimiliano Pinto Added disable_master_failback parameter
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
* 20/02/15 Markus Mäkelä Added connection_timeout parameter for services
* 05/03/15 Massimiliano Pinto Added notification_feedback support
*
* @endverbatim
*/
@ -55,9 +56,22 @@
#include <server.h>
#include <users.h>
#include <monitor.h>
#include <modules.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <mysql.h>
#include <sys/utsname.h>
#include <sys/fcntl.h>
#include <glob.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <housekeeper.h>
#include <notification.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/utsname.h>
/** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask;
@ -70,12 +84,19 @@ static int process_config_update(CONFIG_CONTEXT *);
static void free_config_context(CONFIG_CONTEXT *);
static char *config_get_value(CONFIG_PARAMETER *, const char *);
static int handle_global_item(const char *, const char *);
static int handle_feedback_item(const char *, const char *);
static void global_defaults();
static void feedback_defaults();
static void check_config_objects(CONFIG_CONTEXT *context);
static int internalService(char *router);
int config_truth_value(char *str);
static int internalService(char *router);
int config_get_ifaddr(unsigned char *output);
int config_get_release_string(char* release);
FEEDBACK_CONF * config_get_feedback_data();
static char *config_file = NULL;
static GATEWAY_CONF gateway;
static FEEDBACK_CONF feedback;
char *version_string = NULL;
@ -121,6 +142,12 @@ CONFIG_PARAMETER *param, *p1;
{
return handle_global_item(name, value);
}
if (strcasecmp(section, "feedback") == 0)
{
return handle_feedback_item(name, value);
}
/*
* If we already have some parameters for the object
* add the parameters to that object. If not create
@ -192,6 +219,7 @@ int rval;
}
global_defaults();
feedback_defaults();
config.object = "";
config.next = NULL;
@ -280,7 +308,7 @@ int error_count = 0;
char *auth;
char *enable_root_user;
char *connection_timeout;
char *auth_all_servers;
char *auth_all_servers;
char *strip_db_esc;
char *weightby;
char *version_string;
@ -296,19 +324,19 @@ int error_count = 0;
"enable_root_user");
connection_timeout =
config_get_value(
obj->parameters,
"connection_timeout");
config_get_value(
obj->parameters,
"connection_timeout");
auth_all_servers =
config_get_value(
obj->parameters,
"auth_all_servers");
auth_all_servers =
config_get_value(
obj->parameters,
"auth_all_servers");
strip_db_esc =
config_get_value(
obj->parameters,
"strip_db_esc");
config_get_value(
obj->parameters,
"strip_db_esc");
allow_localhost_match_wildcard_host =
config_get_value(obj->parameters,
@ -364,12 +392,14 @@ int error_count = 0;
obj->element,
atoi(connection_timeout));
if(auth_all_servers)
serviceAuthAllServers(obj->element,
config_truth_value(auth_all_servers));
if(auth_all_servers)
serviceAuthAllServers(obj->element,
config_truth_value(auth_all_servers));
if(strip_db_esc)
serviceStripDbEsc(obj->element,
config_truth_value(strip_db_esc));
serviceStripDbEsc(obj->element,
config_truth_value(strip_db_esc));
if (weightby)
serviceWeightBy(obj->element, weightby);
@ -751,7 +781,7 @@ int error_count = 0;
/* if id is not set, do it now */
if (gateway.id == 0) {
setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address);
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port + getpid());
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + atoi(port) + getpid());
}
if (service && socket && protocol) {
@ -1214,6 +1244,16 @@ config_pollsleep()
return gateway.pollsleep;
}
/**
* Return the feedback config data pointer
*
* @return The feedback config data pointer
*/
FEEDBACK_CONF *
config_get_feedback_data()
{
return &feedback;
}
static struct {
char *logname;
@ -1249,8 +1289,8 @@ int i;
}
else if (strcmp(name, "ms_timestamp") == 0)
{
skygw_set_highp(atoi(value));
}
skygw_set_highp(atoi(value));
}
else
{
for (i = 0; lognames[i].logname; i++)
@ -1267,12 +1307,48 @@ int i;
return 1;
}
/**
* Configuration handler for items in the feedback [feedback] section
*
* @param name The item name
* @param value The item value
* @return 0 on error
*/
static int
handle_feedback_item(const char *name, const char *value)
{
int i;
if (strcmp(name, "feedback_enable") == 0)
{
feedback.feedback_enable = config_truth_value((char *)value);
}
else if (strcmp(name, "feedback_user_info") == 0)
{
feedback.feedback_user_info = strdup(value);
}
else if (strcmp(name, "feedback_url") == 0)
{
feedback.feedback_url = strdup(value);
}
if (strcmp(name, "feedback_timeout") == 0)
{
feedback.feedback_timeout = atoi(value);
}
if (strcmp(name, "feedback_connect_timeout") == 0)
{
feedback.feedback_connect_timeout = atoi(value);
}
return 1;
}
/**
* Set the defaults for the global configuration options
*/
static void
global_defaults()
{
uint8_t mac_addr[6]="";
struct utsname uname_data;
gateway.n_threads = 1;
gateway.n_nbpoll = DEFAULT_NBPOLLS;
gateway.pollsleep = DEFAULT_POLLSLEEP;
@ -1281,6 +1357,41 @@ global_defaults()
else
gateway.version_string = NULL;
gateway.id=0;
/* get release string */
if(!config_get_release_string(gateway.release_string))
sprintf(gateway.release_string,"undefined");
/* get first mac_address in SHA1 */
if(config_get_ifaddr(mac_addr)) {
gw_sha1_str(mac_addr, 6, gateway.mac_sha1);
} else {
memset(gateway.mac_sha1, '\0', sizeof(gateway.mac_sha1));
memcpy(gateway.mac_sha1, "MAC-undef", 9);
}
/* get uname info */
if (uname(&uname_data))
strcpy(gateway.sysname, "undefined");
else
strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH);
}
/**
* Set the defaults for the feedback configuration options
*/
static void
feedback_defaults()
{
feedback.feedback_enable = 0;
feedback.feedback_user_info = NULL;
feedback.feedback_last_action = _NOTIFICATION_SEND_PENDING;
feedback.feedback_timeout = _NOTIFICATION_OPERATION_TIMEOUT;
feedback.feedback_connect_timeout = _NOTIFICATION_CONNECT_TIMEOUT;
feedback.feedback_url = NULL;
feedback.release_info = gateway.release_string;
feedback.sysname = gateway.sysname;
feedback.mac_sha1 = gateway.mac_sha1;
}
/**
@ -1326,24 +1437,23 @@ SERVER *server;
char *connection_timeout;
char* auth_all_servers;
char* auth_all_servers;
char* strip_db_esc;
char* max_slave_conn_str;
char* max_slave_rlag_str;
char* max_slave_conn_str;
char* max_slave_rlag_str;
char *version_string;
char *allow_localhost_match_wildcard_host;
enable_root_user = config_get_value(obj->parameters, "enable_root_user");
connection_timeout = config_get_value(obj->parameters, "connection_timeout");
user = config_get_value(obj->parameters,
"user");
user = config_get_value(obj->parameters,
"user");
auth = config_get_value(obj->parameters,
"passwd");
"passwd");
auth_all_servers = config_get_value(obj->parameters, "auth_all_servers");
strip_db_esc = config_get_value(obj->parameters, "strip_db_esc");
auth_all_servers = config_get_value(obj->parameters, "auth_all_servers");
strip_db_esc = config_get_value(obj->parameters, "strip_db_esc");
version_string = config_get_value(obj->parameters, "version_string");
allow_localhost_match_wildcard_host = config_get_value(obj->parameters, "localhost_match_wildcard_host");
@ -1476,13 +1586,12 @@ SERVER *server;
char *enable_root_user;
char *connection_timeout;
char *allow_localhost_match_wildcard_host;
char *auth_all_servers;
char *strip_db_esc;
char *auth_all_servers;
char *strip_db_esc;
enable_root_user =
config_get_value(obj->parameters,
"enable_root_user");
connection_timeout = config_get_value(obj->parameters,
"connection_timeout");
@ -1495,10 +1604,9 @@ SERVER *server;
"strip_db_esc");
allow_localhost_match_wildcard_host =
config_get_value(obj->parameters, "localhost_match_wildcard_host");
user = config_get_value(obj->parameters,
user = config_get_value(obj->parameters,
"user");
auth = config_get_value(obj->parameters,
"passwd");
@ -1928,3 +2036,219 @@ int i;
}
return 0;
}
/**
* Get the MAC address of first network interface
*
* and fill the provided allocated buffer with SHA1 encoding
* @param output Allocated 6 bytes buffer
* @return 1 on success, 0 on failure
*
*/
int
config_get_ifaddr(unsigned char *output)
{
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
struct ifreq* it;
struct ifreq* end;
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) {
return 0;
};
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
return 0;
}
it = ifc.ifc_req;
end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (! (ifr.ifr_flags & IFF_LOOPBACK)) { /* don't count loopback */
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
} else {
return 0;
}
}
if (success)
memcpy(output, ifr.ifr_hwaddr.sa_data, 6);
return success;
}
/**
* Get the linux distribution info
*
* @param release The allocated buffer where
* the found distribution is copied into.
* @return 1 on success, 0 on failure
*
*/
int
config_get_release_string(char* release)
{
const char *masks[]= {
"/etc/*-version", "/etc/*-release",
"/etc/*_version", "/etc/*_release"
};
bool have_distribution;
char distribution[_RELEASE_STR_LENGTH]="";
int fd;
int i;
char *to;
have_distribution= false;
/* get data from lsb-release first */
if ((fd= open("/etc/lsb-release", O_RDONLY)) != -1)
{
/* LSB-compliant distribution! */
size_t len= read(fd, (char*)distribution, sizeof(distribution)-1);
close(fd);
if (len != (size_t)-1)
{
distribution[len]= 0;
char *found= strstr(distribution, "DISTRIB_DESCRIPTION=");
if (found)
{
have_distribution = true;
char *end = strstr(found, "\n");
if (end == NULL)
end = distribution + len;
found += 20;
if (*found == '"' && end[-1] == '"')
{
found++;
end--;
}
*end = 0;
to = strcpy(distribution, "lsb: ");
memmove(to, found, end - found + 1);
strncpy(release, to, _RELEASE_STR_LENGTH);
return 1;
}
}
}
/* if not an LSB-compliant distribution */
for (i= 0; !have_distribution && i < 4; i++)
{
glob_t found;
char *new_to;
if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0)
{
int fd;
int k = 0;
int skipindex = 0;
int startindex = 0;
for (k = 0; k< found.gl_pathc; k++) {
if (strcmp(found.gl_pathv[k], "/etc/lsb-release") == 0) {
skipindex = k;
}
}
if ( skipindex == 0)
startindex++;
if ((fd= open(found.gl_pathv[startindex], O_RDONLY)) != -1)
{
/*
+5 and -8 below cut the file name part out of the
full pathname that corresponds to the mask as above.
*/
new_to = strcpy(distribution, found.gl_pathv[0] + 5);
new_to += 8;
*new_to++ = ':';
*new_to++ = ' ';
size_t to_len= distribution + sizeof(distribution) - 1 - new_to;
size_t len= read(fd, (char*)new_to, to_len);
close(fd);
if (len != (size_t)-1)
{
new_to[len]= 0;
char *end= strstr(new_to, "\n");
if (end)
*end= 0;
have_distribution= true;
strncpy(release, new_to, _RELEASE_STR_LENGTH);
}
}
}
globfree(&found);
}
if (have_distribution)
return 1;
else
return 0;
}
/**
* Add the 'send_feedback' task to the task list
*/
void
config_enable_feedback_task(void) {
FEEDBACK_CONF *cfg = config_get_feedback_data();
int url_set = 0;
int user_info_set = 0;
int enable_set = cfg->feedback_enable;
url_set = cfg->feedback_url != NULL && strlen(cfg->feedback_url);
user_info_set = cfg->feedback_user_info != NULL && strlen(cfg->feedback_user_info);
if (enable_set && url_set && user_info_set) {
/* Add the task to the tasl list */
if (hktask_add("send_feedback", module_feedback_send, cfg, 1800)) {
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Notification service feedback task started: URL=%s, User-Info=%s, Frequency %u seconds",
cfg->feedback_url,
cfg->feedback_user_info,
1800)));
}
} else {
if (enable_set) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: Notification service feedback cannot start: feedback_enable=1 but"
" some required parameters are not set: %s%s%s",
url_set == 0 ? "feedback_url is not set" : "", (user_info_set == 0 && url_set == 0) ? ", " : "", user_info_set == 0 ? "feedback_user_info is not set" : "")));
} else {
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"Notification service feedback is not enabled")));
}
}
}
/**
* Remove the 'send_feedback' task
*/
void
config_disable_feedback_task(void) {
hktask_remove("send_feedback");
}

View File

@ -23,12 +23,13 @@
* @verbatim
* Revision History
*
* Date Who Description
* 13/06/13 Mark Riddoch Initial implementation
* 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is
* defined in the loaded module.
* Also updated to call fixed GetModuleObject
* 02/06/14 Mark Riddoch Addition of module info
* Date Who Description
* 13/06/13 Mark Riddoch Initial implementation
* 14/06/13 Mark Riddoch Updated to add call to ModuleInit if one is
* defined in the loaded module.
* Also updated to call fixed GetModuleObject
* 02/06/14 Mark Riddoch Addition of module info
* 26/02/15 Massimiliano Pinto Addition of module_feedback_send
*
* @endverbatim
*/
@ -42,6 +43,11 @@
#include <modinfo.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <version.h>
#include <notification.h>
#include <curl/curl.h>
#include <sys/utsname.h>
#include <openssl/sha.h>
/** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask;
@ -58,6 +64,44 @@ static void register_module(const char *module,
void *modobj,
MODULE_INFO *info);
static void unregister_module(const char *module);
int module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *cfg);
struct MemoryStruct {
char *data;
size_t size;
};
/**
* Callback write routine for curl library, getting remote server reply
*
* @param contents New data to add
* @param size Data size
* @param nmemb Elements in the buffer
* @param userp Pointer to the buffer
* @return 0 on failure, memory size on success
*
*/
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->data = realloc(mem->data, mem->size + realsize + 1);
if(mem->data == NULL) {
/* out of memory! */
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_feedback_send(), not enough memory for realloc")));
return 0;
}
memcpy(&(mem->data[mem->size]), contents, realsize);
mem->size += realsize;
mem->data[mem->size] = 0;
return realsize;
}
char* get_maxscale_home(void)
{
@ -409,6 +453,29 @@ MODULES *ptr = registered;
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n\n");
}
/**
* Print Modules to a DCB
*
* Diagnostic routine to display all the loaded modules
*/
void
moduleShowFeedbackReport(DCB *dcb)
{
GWBUF *buffer;
MODULES *modules_list = registered;
FEEDBACK_CONF *feedback_config = config_get_feedback_data();
if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory")));
return;
}
dcb_printf(dcb, (char *)GWBUF_DATA(buffer));
gwbuf_free(buffer);
}
/**
* Provide a row to the result set that defines the set of modules
*
@ -485,3 +552,316 @@ int *data;
return set;
}
/**
* Send loaded modules info to notification service
*
* @param data The configuration details of notification service
*/
void
module_feedback_send(void* data) {
MODULES *modules_list = registered;
CURL *curl = NULL;
CURLcode res;
struct curl_httppost *formpost=NULL;
struct curl_httppost *lastptr=NULL;
GWBUF *buffer = NULL;
void *data_ptr=NULL;
long http_code = 0;
struct MemoryStruct chunk;
int last_action = _NOTIFICATION_SEND_PENDING;
time_t now;
struct tm *now_tm;
int hour;
int n_mod=0;
char hex_setup_info[2 * SHA_DIGEST_LENGTH + 1]="";
now = time(NULL);
now_tm = localtime(&now);
hour = now_tm->tm_hour;
FEEDBACK_CONF *feedback_config = (FEEDBACK_CONF *) data;
/* Configuration check */
if (feedback_config->feedback_enable == 0 || feedback_config->feedback_url == NULL || feedback_config->feedback_user_info == NULL) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_feedback_send(): some mandatory parameters are not set"
" feedback_enable=%u, feedback_url=%s, feedback_user_info=%s",
feedback_config->feedback_enable,
feedback_config->feedback_url == NULL ? "NULL" : feedback_config->feedback_url,
feedback_config->feedback_user_info == NULL ? "NULL" : feedback_config->feedback_user_info)));
feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR;
return;
}
/**
* Task runs nightly, from 2 AM to 4 AM
*
* If it's done in that time interval, it will be skipped
*/
if (hour > 4 || hour < 2) {
/* It's not the rigt time, mark it as to be done and return */
feedback_config->feedback_last_action = _NOTIFICATION_SEND_PENDING;
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"module_feedback_send(): execution skipped, current hour [%d]"
" is not within the proper interval (from 2 AM to 4 AM)",
hour)));
return;
}
/* Time to run the task: if a previous run was succesfull skip next runs */
if (feedback_config->feedback_last_action == _NOTIFICATION_SEND_OK) {
/* task was done before, return */
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"module_feedback_send(): execution skipped because of previous succesful run: hour is [%d], last_action [%d]",
hour, feedback_config->feedback_last_action)));
return;
}
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"module_feedback_send(): task now runs: hour is [%d], last_action [%d]",
hour, feedback_config->feedback_last_action)));
/* allocate first memory chunck for httpd servr reply */
chunk.data = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0; /* no data at this point */
if (!module_create_feedback_report(&buffer, modules_list, feedback_config)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_create_feedback_report(): gwbuf_alloc() failed to allocate memory")));
feedback_config->feedback_last_action = _NOTIFICATION_SEND_ERROR;
return;
}
/* Initializing curl library for data send via HTTP */
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
char error_message[CURL_ERROR_SIZE]="";
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_message);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, feedback_config->feedback_connect_timeout);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, feedback_config->feedback_timeout);
/* curl API call for data send via HTTP POST using a "file" type input */
curl_formadd(&formpost,
&lastptr,
CURLFORM_COPYNAME, "data",
CURLFORM_BUFFER, "report.txt",
CURLFORM_BUFFERPTR, (char *)GWBUF_DATA(buffer),
CURLFORM_BUFFERLENGTH, strlen((char *)GWBUF_DATA(buffer)),
CURLFORM_CONTENTTYPE, "text/plain",
CURLFORM_END);
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
/* some servers don't like requests that are made without a user-agent field, so we provide one */
curl_easy_setopt(curl, CURLOPT_USERAGENT, "MaxScale-agent/http-1.0");
/* Force HTTP/1.0 */
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_easy_setopt(curl, CURLOPT_URL, feedback_config->feedback_url);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
#ifdef SKIP_PEER_VERIFICATION
/*
* This makes the connection A LOT LESS SECURE.
*/
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif
#ifdef SKIP_HOSTNAME_VERIFICATION
/*
* this will make the connection less secure.
*/
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif
/* send all data to this function */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK) {
last_action = _NOTIFICATION_SEND_ERROR;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error: module_feedback_send(), curl call for [%s] failed due: %s, %s",
feedback_config->feedback_url,
curl_easy_strerror(res),
error_message)));
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
}
if (http_code == 302) {
char *from = strstr(chunk.data, "<h1>ok</h1>");
if (from) {
last_action = _NOTIFICATION_SEND_OK;
} else {
last_action = _NOTIFICATION_SEND_ERROR;
}
} else {
last_action = _NOTIFICATION_SEND_ERROR;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_feedback_send(), Bad HTTP Code from remote server: %lu",
http_code)));
}
} else {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error in module_feedback_send(), curl object not initialized")));
last_action = _NOTIFICATION_SEND_ERROR;
}
/* update last action in the config struct */
feedback_config->feedback_last_action = last_action;
LOGIF(LT, (skygw_log_write_flush(
LOGFILE_TRACE,
"module_feedback_send(): task run result: hour is [%d], last_action [%d], HTTP code [%d]",
hour, feedback_config->feedback_last_action, http_code)));
if (chunk.data)
free(chunk.data);
if (buffer)
gwbuf_free(buffer);
if (curl) {
curl_easy_cleanup(curl);
curl_formfree(formpost);
}
curl_global_cleanup();
}
/**
* Create the feedback report as string.
* I t could be sent to notification service
* or just printed via maxadmin/telnet
*
* @param buffe The pointr for GWBUF allocation, to be freed by the caller
* @param modules The mouleds list
* @param cfg The feedback configuration
* @return 0 on failure, 1 on success
*
*/
int
module_create_feedback_report(GWBUF **buffer, MODULES *modules, FEEDBACK_CONF *cfg) {
MODULES *ptr = modules;
int n_mod = 0;
char *data_ptr=NULL;
char hex_setup_info[2 * SHA_DIGEST_LENGTH + 1]="";
time_t now;
struct tm *now_tm;
int report_max_bytes=0;
now = time(NULL);
/* count loaded modules */
while (ptr)
{
ptr = ptr->next;
n_mod++;
}
/* module lists pointer is set back to the head */
ptr = modules;
/**
* allocate gwbuf for data to send
*
* each module gives 4 rows
* product and release rows add 7 rows
* row is _NOTIFICATION_REPORT_ROW_LEN bytes long
*/
report_max_bytes = ((n_mod * 4) + 7) * (_NOTIFICATION_REPORT_ROW_LEN + 1);
*buffer = gwbuf_alloc(report_max_bytes);
if (buffer == NULL) {
return 0;
}
/* encode MAC-sha1 to HEX */
gw_bin2hex(hex_setup_info, cfg->mac_sha1, SHA_DIGEST_LENGTH);
data_ptr = (char *)GWBUF_DATA(*buffer);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "FEEDBACK_SERVER_UID\t%s\n", hex_setup_info);
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "FEEDBACK_USER_INFO\t%s\n", cfg->feedback_user_info == NULL ? "not_set" : cfg->feedback_user_info);
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "VERSION\t%s\n", MAXSCALE_VERSION);
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN * 2, "NOW\t%lu\nPRODUCT\t%s\n", now, "maxscale");
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "Uname_sysname\t%s\n", cfg->sysname);
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "Uname_distribution\t%s\n", cfg->release_info);
data_ptr+=strlen(data_ptr);
while (ptr)
{
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN * 2, "module_%s_type\t%s\nmodule_%s_version\t%s\n", ptr->module, ptr->type, ptr->module, ptr->version);
data_ptr+=strlen(data_ptr);
if (ptr->info) {
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "module_%s_api\t%d.%d.%d\n",
ptr->module,
ptr->info->api_version.major,
ptr->info->api_version.minor,
ptr->info->api_version.patch);
data_ptr+=strlen(data_ptr);
snprintf(data_ptr, _NOTIFICATION_REPORT_ROW_LEN, "module_%s_releasestatus\t%s\n",
ptr->module,
ptr->info->status == MODULE_IN_DEVELOPMENT
? "In Development"
: (ptr->info->status == MODULE_ALPHA_RELEASE
? "Alpha"
: (ptr->info->status == MODULE_BETA_RELEASE
? "Beta"
: (ptr->info->status == MODULE_GA
? "GA"
: (ptr->info->status == MODULE_EXPERIMENTAL
? "Experimental" : "Unknown")))));
data_ptr+=strlen(data_ptr);
}
ptr = ptr->next;
}
return 1;
}

View File

@ -35,6 +35,7 @@
* 13/10/14 Massimiliano Pinto Added hashtable for resources (i.e database names for MySQL services)
* 06/02/15 Mark Riddoch Added caching of authentication data
* 18/02/15 Mark Riddoch Added result set management
* 03/03/15 Massimiliano Pinto Added config_enable_feedback_task() call in serviceStartAll
*
* @endverbatim
*/
@ -61,7 +62,6 @@
#include <housekeeper.h>
#include <resultset.h>
/** Defined in log_manager.cc */
extern int lm_enabled_logfiles_bitmask;
extern size_t log_ses_count[];
@ -477,6 +477,8 @@ serviceStartAll()
SERVICE *ptr;
int n = 0,i;
config_enable_feedback_task();
ptr = allServices;
while (ptr && !ptr->svc_do_shutdown)
{

View File

@ -12,6 +12,7 @@ add_executable(test_server testserver.c)
add_executable(test_users testusers.c)
add_executable(test_adminusers testadminusers.c)
add_executable(testmemlog testmemlog.c)
add_executable(testfeedback testfeedback.c)
target_link_libraries(test_mysql_users MySQLClient fullcore)
target_link_libraries(test_hash fullcore)
target_link_libraries(test_hint fullcore)
@ -26,6 +27,7 @@ target_link_libraries(test_server fullcore)
target_link_libraries(test_users fullcore)
target_link_libraries(test_adminusers fullcore)
target_link_libraries(testmemlog fullcore)
target_link_libraries(testfeedback fullcore)
add_test(testMySQLUsers test_mysql_users)
add_test(TestHash test_hash)
add_test(TestHint test_hint)
@ -40,6 +42,7 @@ add_test(TestServer test_server)
add_test(TestUsers test_users)
add_test(TestAdminUsers test_adminusers)
add_test(TestMemlog testmemlog)
add_test(TestFeedback testfeedback)
set_tests_properties(testMySQLUsers
TestHash
TestHint
@ -53,4 +56,5 @@ set_tests_properties(testMySQLUsers
TestServer
TestUsers
TestAdminUsers
TestMemlog PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/)
TestMemlog
TestFeedback PROPERTIES ENVIRONMENT MAXSCALE_HOME=${CMAKE_BINARY_DIR}/)

View File

@ -0,0 +1,84 @@
/*
* This file is distributed as part of MaxScale. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2014
*/
/**
*
* @verbatim
* Revision History
*
* Date Who Description
* 09-03-2015 Markus Mäkelä Initial implementation
*
* @endverbatim
*/
#define FAILTEST(s) printf("TEST FAILED: " s "\n");return 1;
#include <stdio.h>
#include <notification.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <housekeeper.h>
#include <buffer.h>
#include <regex.h>
int main(int argc, char** argv)
{
FEEDBACK_CONF* fc;
char* home;
char* cnf;
GWBUF* buf;
regex_t re;
hkinit();
home = getenv("MAXSCALE_HOME");
if(home == NULL)
{
FAILTEST("MAXSCALE_HOME was not defined.");
}
printf("Home: %s\n",home);
cnf = malloc(strlen(home) + strlen("/etc/MaxScale.cnf") + 1);
strcpy(cnf,home);
strcat(cnf,"/etc/MaxScale.cnf");
printf("Config: %s\n",cnf);
config_load(cnf);
if((fc = config_get_feedback_data()) == NULL ||
fc->feedback_user_info == NULL)
{
FAILTEST("Configuration was NULL.");
}
regcomp(&re,fc->feedback_user_info,0);
module_create_feedback_report(&buf,NULL,fc);
printf("%s",(char*)buf->start);
if(regexec(&re,(char*)buf->start,0,NULL,0))
{
FAILTEST("Regex match of 'user_info' failed.");
}
return 0;
}