diff --git a/CMakeLists.txt b/CMakeLists.txt
index eef89a85f..5ba32df44 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/Documentation/Tutorials/Notification-Service.md b/Documentation/Tutorials/Notification-Service.md
new file mode 100644
index 000000000..4cfc89cd2
--- /dev/null
+++ b/Documentation/Tutorials/Notification-Service.md
@@ -0,0 +1,94 @@
+# MaxScale Notification Service and Feedback Support
+
+Massimiliano Pinto
+
+Last Updated: 10th March 2015
+
+## Contents
+
+## Document History
+
+
+
+ | Date |
+ Change |
+ Who |
+
+
+ | 10th March 2015 |
+ Initial version |
+ Massimiliano Pinto |
+
+
+
+
+## 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
diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt
index fbfccbaf0..6a481d370 100644
--- a/server/core/CMakeLists.txt
+++ b/server/core/CMakeLists.txt
@@ -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)
diff --git a/server/core/config.c b/server/core/config.c
index aec9a5b13..26dc70473 100644
--- a/server/core/config.c
+++ b/server/core/config.c
@@ -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
#include
#include
+#include
#include
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
/** 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");
+}
diff --git a/server/core/load_utils.c b/server/core/load_utils.c
index 7f3fc587d..d8a735ecd 100644
--- a/server/core/load_utils.c
+++ b/server/core/load_utils.c
@@ -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
#include
#include
+#include
+#include
+#include
+#include
+#include
/** 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, "ok
");
+ 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;
+}
+
diff --git a/server/core/service.c b/server/core/service.c
index 1d601c44a..129a20f29 100644
--- a/server/core/service.c
+++ b/server/core/service.c
@@ -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
#include
-
/** 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)
{
diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt
index 2a0977088..0cff62772 100644
--- a/server/core/test/CMakeLists.txt
+++ b/server/core/test/CMakeLists.txt
@@ -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}/)
diff --git a/server/core/test/testfeedback.c b/server/core/test/testfeedback.c
new file mode 100644
index 000000000..9e84b5cb2
--- /dev/null
+++ b/server/core/test/testfeedback.c
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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;
+}
diff --git a/server/include/config.h b/server/include/config.h
index 2cf9dccb3..3648c97a0 100644
--- a/server/include/config.h
+++ b/server/include/config.h
@@ -18,7 +18,7 @@
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include
-
+#include
/**
* @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
diff --git a/server/include/modules.h b/server/include/modules.h
index 56215b5d2..51e10b29d 100644
--- a/server/include/modules.h
+++ b/server/include/modules.h
@@ -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
diff --git a/server/include/notification.h b/server/include/notification.h
new file mode 100644
index 000000000..6a7f07bac
--- /dev/null
+++ b/server/include/notification.h
@@ -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
+
+/**
+ * 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
diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c
index 821e38f23..253647786 100644
--- a/server/modules/routing/debugcmd.c
+++ b/server/modules/routing/debugcmd.c
@@ -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)
{
diff --git a/server/test/MaxScale_test.cnf b/server/test/MaxScale_test.cnf
index 1ed59b5b0..06b783ca5 100644
--- a/server/test/MaxScale_test.cnf
+++ b/server/test/MaxScale_test.cnf
@@ -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