Notification Service integration added

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

View File

@ -28,6 +28,13 @@ find_package(MySQLClient)
find_package(MySQL)
find_package(Pandoc)
# You can find the variables set by this in the FindCURL.cmake file
# which is a default module in CMake.
find_package(CURL)
if(NOT CURL_FOUND)
message(FATAL_ERROR "Failed to locate dependency: libcurl")
endif()
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/modules)
# Make sure the release notes for this release are present if it is a stable one
@ -114,6 +121,7 @@ include_directories(server/include)
include_directories(server/inih)
include_directories(server/modules/include)
include_directories(${CMAKE_BINARY_DIR}/server/include)
include_directories(${CURL_INCLUDE_DIRS})
add_subdirectory(utils)
add_subdirectory(log_manager)

View File

@ -0,0 +1,94 @@
# MaxScale Notification Service and Feedback Support
Massimiliano Pinto
Last Updated: 10th March 2015
## Contents
## Document History
<table>
<tr>
<td>Date</td>
<td>Change</td>
<td>Who</td>
</tr>
<tr>
<td>10th March 2015</td>
<td>Initial version</td>
<td>Massimiliano Pinto</td>
</tr>
</table>
## Overview
The purpose of Notification Service in MaxScale is for a customer registered for the service to receive update notices, security bulletins, fixes and workarounds that are tailored to the database server configuration.
## MaxScale Setup
MaxScale may collect the installed plugins and send the informations nightly, between 2:00 AM and 4:59 AM.
It tries to send data and if there is any failure (timeout, server is down, etc), the next retry is in 1800 seconds (30 minutes)
This feature is not enabled by default: MaxScale must be configured in [feedback] section:
[feedback]
feedback_enable=1
feedback_url=https://mariadb.org/feedback_plugin/post
feedback_user_info=x-y-z-w
The activation code that will be provided by MariaDB corp upon request by the customer and it shlud be put in feedback_user_info.
Example:
feedback_user_info=0467009f-b04d-45b1-a77b-b6b2ec9c6cf4
MaxScale generates the feedback report containing following information:
-The activation code used to enable feedback
- MaxScale Version
- An identifier of the MaxScale installation, i.e. the HEX encoding of SHA1 digest of the first network interface MAC address
- Operating System (i.e Linux)
- Operating Suystem Distribution (i.e. CentOS release 6.5 (Final))
- All the modules in use in MaxScale and their API and version
- MaxScale server UNIX_TIME at generation time
MaxScale shall send the generated feedback report to a feedback server specified in feedback_url
## Manual Operation
If it’s not possible to send data due to firewall or security settings the report could be generated manually (feedback_user_info is required) via MaxAdmin
MaxScale>show feedback report
Report could be saved to report.txt file:
maxadmin -uxxx -pyyy show feedbackreport > ./report.txt
curl -F data=@./report.txt https://mariadb.org/feedback_plugin/post
Report Example:
FEEDBACK_SERVER_UID 6B5C44AEA73137D049B02E6D1C7629EF431A350F
FEEDBACK_USER_INFO 0467009f-b04d-45b1-a77b-b6b2ec9c6cf4
VERSION 1.0.6-unstable
NOW 1425914890
PRODUCT maxscale
Uname_sysname Linux
Uname_distribution CentOS release 6.5 (Final)
module_maxscaled_type Protocol
module_maxscaled_version V1.0.0
module_maxscaled_api 1.0.0
module_maxscaled_releasestatus GA
module_telnetd_type Protocol
module_telnetd_version V1.0.1
module_telnetd_api 1.0.0
module_telnetd_releasestatus GA

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;
}

View File

@ -18,7 +18,7 @@
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <skygw_utils.h>
#include <openssl/sha.h>
/**
* @file config.h The configuration handling elements
*
@ -30,12 +30,15 @@
* 07/05/14 Massimiliano Pinto Added version_string to global configuration
* 23/05/14 Massimiliano Pinto Added id to global configuration
* 17/10/14 Mark Riddoch Added poll tuning configuration parameters
* 05/03/15 Massimiliano Pinto Added sysname, release, sha1_mac to gateway struct
*
* @endverbatim
*/
#define DEFAULT_NBPOLLS 3 /**< Default number of non block polls before we block */
#define DEFAULT_POLLSLEEP 1000 /**< Default poll wait time (milliseconds) */
#define _SYSNAME_STR_LENGTH 256 /**< sysname len */
#define _RELEASE_STR_LENGTH 256 /**< release len */
/**
* Maximum length for configuration parameter value.
*/
@ -92,22 +95,25 @@ typedef struct config_context {
* The gateway global configuration data
*/
typedef struct {
int n_threads; /**< Number of polling threads */
char *version_string; /**< The version string of embedded database library */
unsigned long id; /**< MaxScale ID */
int n_threads; /**< Number of polling threads */
char *version_string; /**< The version string of embedded database library */
char release_string[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */
char sysname[_SYSNAME_STR_LENGTH]; /**< The release name string of the system */
uint8_t mac_sha1[SHA_DIGEST_LENGTH]; /*< The SHA1 digest of an interface MAC address */
unsigned long id; /**< MaxScale ID */
unsigned int n_nbpoll; /**< Tune number of non-blocking polls */
unsigned int pollsleep; /**< Wait time in blocking polls */
} GATEWAY_CONF;
extern int config_load(char *);
extern int config_reload();
extern int config_threadcount();
extern unsigned int config_nbpolls();
extern unsigned int config_pollsleep();
CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name);
config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param);
CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param);
int config_truth_value(char *str);
extern int config_load(char *);
extern int config_reload();
extern int config_threadcount();
extern unsigned int config_nbpolls();
extern unsigned int config_pollsleep();
CONFIG_PARAMETER* config_get_param(CONFIG_PARAMETER* params, const char* name);
config_param_type_t config_get_paramtype(CONFIG_PARAMETER* param);
CONFIG_PARAMETER* config_clone_param(CONFIG_PARAMETER* param);
extern int config_truth_value(char *);
bool config_set_qualified_param(
CONFIG_PARAMETER* param,
void* val,
@ -131,4 +137,7 @@ bool config_get_valtarget(
CONFIG_PARAMETER* param,
const char* name, /*< if NULL examine current param only */
config_param_type_t ptype);
void config_enable_feedback_task(void);
void config_disable_feedback_task(void);
#endif

View File

@ -29,13 +29,15 @@
* @verbatim
* Revision History
*
* Date Who Description
* 13/06/13 Mark Riddoch Initial implementation
* 08/07/13 Mark Riddoch Addition of monitor modules
* 29/05/14 Mark Riddoch Addition of filter modules
* 01/10/14 Mark Riddoch Addition of call to unload all modules on
* shutdown
* 19/02/15 Mark Riddoch Addition of moduleGetList
* Date Who Description
* 13/06/13 Mark Riddoch Initial implementation
* 08/07/13 Mark Riddoch Addition of monitor modules
* 29/05/14 Mark Riddoch Addition of filter modules
* 01/10/14 Mark Riddoch Addition of call to unload all modules on
* shutdown
* 19/02/15 Mark Riddoch Addition of moduleGetList
* 26/02/15 Massimiliano Pinto Addition of module_feedback_send
*
* @endverbatim
*/
@ -65,7 +67,9 @@ extern void unload_module(const char *module);
extern void unload_all_modules();
extern void printModules();
extern void dprintAllModules(DCB *);
extern char *get_maxscale_home(void);
extern RESULTSET *moduleGetList();
extern char *get_maxscale_home(void);
extern void module_feedback_send(void*);
extern void moduleShowFeedbackReport(DCB *dcb);
#endif

View File

@ -0,0 +1,63 @@
#ifndef _NOTIFICATION_SERVICE_H
#define _NOTIFICATION_SERVICE_H
/*
* This file is distributed as part of the MariaDB Corporation 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 2013-2014
*/
/**
* @file notification.h
*
* The configuration stuct for notification/feedback service
*
* @verbatim
* Revision History
*
* Date Who Description
* 02/03/15 Massimiliano Pinto Initial implementation
*
* @endverbatim
*/
#define _NOTIFICATION_CONNECT_TIMEOUT 30
#define _NOTIFICATION_OPERATION_TIMEOUT 30
#define _NOTIFICATION_SEND_PENDING 0
#define _NOTIFICATION_SEND_OK 1
#define _NOTIFICATION_SEND_ERROR 2
#define _NOTIFICATION_REPORT_ROW_LEN 255
#include <stdint.h>
/**
* The configuration and usage information data for feeback service
*/
typedef struct {
int feedback_enable; /**< Enable/Disable Notification feedback */
char *feedback_url; /**< URL to which the data is sent */
char *feedback_user_info; /**< User info included in the feedback data sent */
int feedback_timeout; /**< An attempt to write/read the data times out and fails after this many seconds */
int feedback_connect_timeout; /**< An attempt to send the data times out and fails after this many seconds */
int feedback_last_action; /**< Holds the feedback last send action status */
char *release_info; /**< Operating system Release name */
char *sysname; /**< Operating system name */
uint8_t *mac_sha1; /**< First available MAC address*/
} FEEDBACK_CONF;
extern char *gw_bin2hex(char *out, const uint8_t *in, unsigned int len);
extern void gw_sha1_str(const uint8_t *in, int in_len, uint8_t *out);
extern FEEDBACK_CONF * config_get_feedback_data();
#endif

View File

@ -36,12 +36,13 @@
* Date Who Description
* 20/06/13 Mark Riddoch Initial implementation
* 17/07/13 Mark Riddoch Additional commands
* 09/08/2013 Massimiliano Pinto Added enable/disable commands (now only for log)
* 09/08/13 Massimiliano Pinto Added enable/disable commands (now only for log)
* 20/05/14 Mark Riddoch Added ability to give server and service names rather
* than simply addresses
* 23/05/14 Mark Riddoch Added support for developer and user modes
* 29/05/14 Mark Riddoch Add Filter support
* 16/10/14 Mark Riddoch Add show eventq
* 05/03/15 Massimiliano Pinto Added enable/disable feedback
*
* @endverbatim
*/
@ -129,6 +130,10 @@ struct subcommand showoptions[] = {
"Show the event statistics",
"Show the event statistics",
{0, 0, 0} },
{ "feedbackreport", 0, moduleShowFeedbackReport,
"Show the report of MaxScale loaded modules, suitable for Notification Service",
"Show the report of MaxScale loaded modules, suitable for Notification Service",
{0, 0, 0} },
{ "filter", 1, dprintFilter,
"Show details of a filter, called with a filter name",
"Show details of a filter, called with the address of a filter",
@ -365,6 +370,8 @@ static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor);
static void disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor);
static void enable_service_root(DCB *dcb, SERVICE *service);
static void disable_service_root(DCB *dcb, SERVICE *service);
static void enable_feedback_action();
static void disable_feedback_action();
/**
* * The subcommands of the enable command
@ -406,6 +413,14 @@ struct subcommand enableoptions[] = {
"Enable root access to a service, pass a service name to enable root access",
{ARG_TYPE_SERVICE, 0, 0}
},
{
"feedback",
0,
enable_feedback_action,
"Enable MaxScale modules list sending via http to notification service",
"Enable MaxScale modules list sending via http to notification service",
{0, 0, 0}
},
{
NULL,
0,
@ -458,6 +473,14 @@ struct subcommand disableoptions[] = {
"Disable root access to a service",
{ARG_TYPE_SERVICE, 0, 0}
},
{
"feedback",
0,
disable_feedback_action,
"Disable MaxScale modules list sending via http to notification service",
"Disable MaxScale modules list sending via http to notification service",
{0, 0, 0}
},
{
NULL,
0,
@ -1381,6 +1404,29 @@ set_nbpoll(DCB *dcb, int nb)
poll_set_nonblocking_polls(nb);
}
/**
* Re-enable sendig MaxScale module list via http
* Proper [feedback] section in MaxSclale.cnf
* is required.
*/
static void
enable_feedback_action(void)
{
config_enable_feedback_task();
return;
}
/**
* Disable sendig MaxScale module list via http
*/
static void
disable_feedback_action(void)
{
config_disable_feedback_task();
return;
}
#if defined(FAKE_CODE)
static void fail_backendfd(void)
{

View File

@ -1,6 +1,13 @@
[maxscale]
threads=4
[feedback]
feedback_enable=true
feedback_user_info=user_info
feedback_url=http://127.0.0.1:8080/load.php
feedback_timeout=60
feedback_connect_timeout=60
[MySQL Monitor]
type=monitor
module=mysqlmon