diff --git a/connectors/cdc-connector/README.md b/Documentation/Connectors/CDC-Connector.md similarity index 52% rename from connectors/cdc-connector/README.md rename to Documentation/Connectors/CDC-Connector.md index b5bcf94b5..32af1bc68 100644 --- a/connectors/cdc-connector/README.md +++ b/Documentation/Connectors/CDC-Connector.md @@ -1,12 +1,32 @@ -# maxscale-cdc-connector +# Maxscale CDC Connector The C++ connector for the [MariaDB MaxScale](https://mariadb.com/products/technology/maxscale) [CDC system](https://mariadb.com/kb/en/mariadb-enterprise/mariadb-maxscale-22-avrorouter-tutorial/). ## Usage -The CDC connector is a single-file connector which allows it to be -relatively easily embedded into existing applications. +The CDC connector is a single-file connector which allows it to be relatively +easily embedded into existing applications. + +## API Overview + +A CDC connection object is prepared by instantiating the `CDC::Connection` +class. To create the actual connection, call the `CDC::Connection::connect` +method of the class. + +After the connection has been created, call the `CDC::Connection::read` method +to get a row of data. The `CDC::Row::length` method tells how many values a row +has and `CDC::Row::value` is used to access that value. The field name of a +value can be extracted with the `CDC::Row::key` method and the current GTID of a +row of data is retrieved with the `CDC::Row::gtid` method. + +To close the connection, destroy the instantiated object. + +## Examples + +The source code +[contains an example](https://github.com/mariadb-corporation/MaxScale/blob/2.2/connectors/cdc-connector/examples/main.cpp) +that demonstrates basic usage of the MaxScale CDC Connector. ## Dependencies @@ -45,5 +65,5 @@ sudo zypper install -y libjansson-devel openssl-devel cmake make gcc-c++ git ## Building and Packaging To build and package the connector as a library, follow MaxScale build -instructions with the exception of adding `-DTARGET_COMPONENT=devel` to -the CMake call. +instructions with the exception of adding `-DTARGET_COMPONENT=devel` to the +CMake call. diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 75f473557..b52a22427 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -4,7 +4,6 @@ ## About MariaDB MaxScale - [About MariaDB MaxScale](About/About-MaxScale.md) - - [Release Notes](Release-Notes/MaxScale-2.1.5-Release-Notes.md) - [Changelog](Changelog.md) - [Limitations](About/Limitations.md) @@ -53,7 +52,6 @@ These tutorials are for specific use cases and module combinations. Here are tutorials on monitoring and managing MariaDB MaxScale in cluster environments. - - [MariaDB MaxScale HA with Corosync-Pacemaker](Tutorials/MaxScale-HA-with-Corosync-Pacemaker.md) - [MariaDB MaxScale HA with Lsyncd](Tutorials/MaxScale-HA-with-lsyncd.md) - [Nagios Plugins for MariaDB MaxScale Tutorial](Tutorials/Nagios-Plugins.md) @@ -115,6 +113,10 @@ Documentation for MaxScale protocol modules. - [Change Data Capture (CDC) Protocol](Protocols/CDC.md) - [Change Data Capture (CDC) Users](Protocols/CDC_users.md) +The MaxScale CDC Connector provides a C++ API for consuming data from a CDC system. + + - [CDC Connector](Connectors/CDC-Connector.md) + ## Authenticators A short description of the authentication module type can be found in the diff --git a/Documentation/Reference/MaxCtrl.md b/Documentation/Reference/MaxCtrl.md index 53f313e4f..d5effed0d 100644 --- a/Documentation/Reference/MaxCtrl.md +++ b/Documentation/Reference/MaxCtrl.md @@ -43,10 +43,17 @@ All command accept the following global options. -h, --hosts List of MaxScale hosts. The hosts must be in HOST:PORT format and each value must be separated by a comma. [string] [default: "localhost:8989"] - -s, --secure Enable HTTPS requests [boolean] [default: "false"] - -t, --timeout Request timeout in milliseconds [number] [default: "10000"] - -q, --quiet Silence all output [boolean] [default: "false"] - --tsv Print tab separated output [boolean] [default: "false"] + -t, --timeout Request timeout in milliseconds [number] [default: 10000] + -q, --quiet Silence all output [boolean] [default: false] + --tsv Print tab separated output [boolean] [default: false] + +HTTPS/TLS Options: + -s, --secure Enable HTTPS requests [boolean] [default: false] + --tls-key Path to TLS private key [string] + --tls-cert Path to TLS public certificate [string] + --tls-ca-cert Path to TLS CA certificate [string] + --tls-verify-server-cert Whether to verify server TLS certificates + [boolean] [default: true] Options: --version Show version number [boolean] diff --git a/Documentation/Release-Notes/MaxScale-2.2.2-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.2.2-Release-Notes.md index 620ba9012..fe8535a21 100644 --- a/Documentation/Release-Notes/MaxScale-2.2.2-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-2.2.2-Release-Notes.md @@ -10,6 +10,27 @@ report at [Jira](https://jira.mariadb.org). ## Changed Features +### MaxCtrl Moved to `maxscale` Package + +The MaxCtrl client is now a part of the main MaxScale package, `maxscale`. This +means that the `maxctrl` executable is now immediately available upon the +installation of MaxScale. + +In the 2.2.1 beta version MaxCtrl was in its own package. If you have a previous +installation of MaxCtrl, please remove it before upgrading to MaxScale 2.2.2. + +### MaxScale C++ CDC Connector Integration + +The MaxScale C++ CDC Connector is now distributed as a part of MaxScale. The +connector libraries are in a separate package, `maxscale-cdc-connector`. Refer +to the [CDC Connector documentation](../Connectors/CDC-Connector.md) for more details. + +### Output of `show threads` has changed. + +For each thread is shown what state it is in, how many descriptors are currently +in the thread's epoll instance and how many descriptors in total have been in the +thread's epoll instance. + ## Dropped Features ## New Features @@ -26,6 +47,13 @@ It is now possible to specify what local address MaxScale should use when connecting to servers. Please refer to the documentation for [details](../Getting-Started/Configuration-Guide.md#local_address). +### External master support for failover/switchover + +Failover/switchover now tries to preserve replication from an external master +server. Check +[MariaDB Monitor documentation](../Monitors/MariaDB-Monitor.md#external-master-support) +for more information. + ## Bug fixes [Here is a list of bugs fixed in MaxScale 2.2.2.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.2.2) @@ -33,6 +61,9 @@ for [details](../Getting-Started/Configuration-Guide.md#local_address). * [MXS-1661](https://jira.mariadb.org/browse/MXS-1661) Excessive logging by MySQLAuth at authentication error (was: MySQLAuth SQLite database can be permanently locked) * [MXS-1660](https://jira.mariadb.org/browse/MXS-1660) Failure to resolve hostname is considered an error * [MXS-1654](https://jira.mariadb.org/browse/MXS-1654) MaxScale crashes if env-variables are used without substitute_variables=1 having been defined +* [MXS-1653](https://jira.mariadb.org/browse/MXS-1653) sysbench failed to initialize w/ MaxScale read/write splitter +* [MXS-1647](https://jira.mariadb.org/browse/MXS-1647) Module API version is not checked +* [MXS-1643](https://jira.mariadb.org/browse/MXS-1643) Too many monitor events are triggered * [MXS-1641](https://jira.mariadb.org/browse/MXS-1641) Fix overflow in master id * [MXS-1633](https://jira.mariadb.org/browse/MXS-1633) Need remove mutex in sqlite * [MXS-1630](https://jira.mariadb.org/browse/MXS-1630) MaxCtrl binary are not included by default in MaxScale package @@ -46,6 +77,7 @@ for [details](../Getting-Started/Configuration-Guide.md#local_address). * [MXS-1586](https://jira.mariadb.org/browse/MXS-1586) Mysqlmon switchover does not immediately detect bad new master * [MXS-1583](https://jira.mariadb.org/browse/MXS-1583) Database firewall filter failing with multiple users statements in rules file * [MXS-1539](https://jira.mariadb.org/browse/MXS-1539) Authentication data should be thread specific +* [MXS-1508](https://jira.mariadb.org/browse/MXS-1508) Failover is sometimes triggered on non-simple topologies ## Known Issues and Limitations diff --git a/Documentation/Routers/HintRouter.md b/Documentation/Routers/HintRouter.md index dbb7c9ef5..3413e8b98 100644 --- a/Documentation/Routers/HintRouter.md +++ b/Documentation/Routers/HintRouter.md @@ -1,5 +1,7 @@ # HintRouter +HintRouter was introduced in 2.2 and is still beta. + ## Overview The **HintRouter** module is a simple router intended to operate in conjunction diff --git a/Documentation/Tutorials/Failover-with-Keepalived.md b/Documentation/Tutorials/Failover-with-Keepalived.md index c261229c0..bfc272c5d 100644 --- a/Documentation/Tutorials/Failover-with-Keepalived.md +++ b/Documentation/Tutorials/Failover-with-Keepalived.md @@ -188,3 +188,68 @@ Aug 11 10:51:57 maxscale2 Keepalived_vrrp[20257]: VRRP_Instance(VI_1) Entering F Aug 11 10:51:57 maxscale2 Keepalived_vrrp[20257]: VRRP_Instance(VI_1) removing protocol VIPs. Aug 11 10:51:57 maxscale2 Keepalived_vrrp[20257]: VRRP_Instance(VI_1) Now in FAULT state ``` + +## MaxScale active/passive-setting + +When using multiple MaxScales with replication cluster management features +(failover, switchover, rejoin), only one MaxScale instance should be allowed to +modify the cluster at any given time. This instance should be the one with +MASTER Keepalived status. MaxScale itself does not know its state, but MaxCtrl +(a replacement for MaxAdmin) can set a MaxScale instance to passive mode. As of +version 2.2.2, a passive MaxScale behaves similar to an active one with the +distinction that it won't perform failover, switchover or rejoin. Even manual +versions of these commands will end in error. The passive/active mode +differences may be expanded in the future. + +To have Keepalived modify the MaxScale operating mode, a notify script is +needed. This script is ran whenever Keepalived changes its state. The script +file is defined in the Keepalived configuration file as `notify`. + +``` +... +virtual_ipaddress { + 192.168.1.13 +} +track_script { + chk_myscript +} +notify /home/user/notify_script.sh +... +``` +Keepalived calls the script with three parameters. In our case, only the third +parameter, STATE, is relevant. An example script is below. + +``` +#!/bin/bash + +TYPE=$1 +NAME=$2 +STATE=$3 + +OUTFILE=/home/user/state.txt + +case $STATE in + "MASTER") echo "Setting this MaxScale node to active mode" > $OUTFILE + maxctrl alter maxscale passive false + exit 0 + ;; + "BACKUP") echo "Setting this MaxScale node to passive mode" > $OUTFILE + maxctrl alter maxscale passive true + exit 0 + ;; + "FAULT") echo "MaxScale failed the status check." > $OUTFILE + maxctrl alter maxscale passive true + exit 0 + ;; + *) echo "Unknown state" > $OUTFILE + exit 1 + ;; +esac + +``` +The script logs the current state to a text file and sets the operating mode of +MaxScale. The FAULT case also attempts to set MaxScale to passive mode, +although the MaxCtrl command will likely fail. + +If all MaxScale/Keepalived instances have a similar notify script, only one +MaxScale should ever be in active mode. diff --git a/Documentation/Tutorials/MaxScale-HA-with-Corosync-Pacemaker.md b/Documentation/Tutorials/MaxScale-HA-with-Corosync-Pacemaker.md deleted file mode 100644 index c06af5720..000000000 --- a/Documentation/Tutorials/MaxScale-HA-with-Corosync-Pacemaker.md +++ /dev/null @@ -1,528 +0,0 @@ -# How to make MariaDB MaxScale High Available - -The document shows an example of a Pacemaker / Corosync setup with MariaDB MaxScale based on Linux Centos 6.5, using three virtual servers and unicast heartbeat mode with the following minimum requirements: - -- MariaDB MaxScale process is started/stopped and monitored via /etc/init.d/maxscale script that is LSB compatible in order to be managed by Pacemaker resource manager - -- A Virtual IP is set providing the access to the MariaDB MaxScale process that could be set to one of the cluster nodes - -- Pacemaker/Corosync and crmsh command line tool basic knowledge - -Please note the solution is a quick setup example that may not be suited for all production environments. - -## Clustering Software installation - -On each node in the cluster do the following steps. - -### Add clustering repos to yum - -``` -# vi /etc/yum.repos.d/ha-clustering.repo -``` - -Add the following to the file. - -``` -[haclustering] -name=HA Clustering -baseurl=http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/CentOS_CentOS-6/ -enabled=1 -gpgcheck=0 -``` - -### Install the software - -``` -# yum install pacemaker corosync crmsh -``` - -Package versions used - -``` -Package pacemaker-1.1.10-14.el6_5.3.x86_64 -Package corosync-1.4.5-2.4.x86_64 -Package crmsh-2.0+git46-1.1.x86_64 -``` - -### Assign hostname on each node - -In this example the three names used for the nodes are: node1,node,node3 - -``` -[root@server1 ~]# hostname node1 -... -[root@server2 ~]# hostname node2 -... -[root@server3 ~]# hostname node3 -``` - -For each node, add all the server names into `/etc/hosts`. - -``` -[root@node3 ~]# vi /etc/hosts -10.74.14.39 node1 -10.228.103.72 node2 -10.35.15.26 node3 current-node -... -[root@node1 ~]# vi /etc/hosts -10.74.14.39 node1 current-node -10.228.103.72 node2 -10.35.15.26 node3 -``` - -**Note**: add _current-node_ as an alias for the current node in each of the /etc/hosts files. - -### Prepare authkey for optional cryptographic use - -On one of the nodes, say node2 run the corosync-keygen utility and follow - -``` -[root@node2 ~]# corosync-keygen - -Corosync Cluster Engine Authentication key generator. Gathering 1024 bits for key from /dev/random. Press keys on your keyboard to generate entropy. - -After completion the key will be found in /etc/corosync/authkey. -``` - -### Prepare the corosync configuration file - -Using node2 as an example: - -``` -[root@node2 ~]# vi /etc/corosync/corosync.conf -``` - -Add the following to the file: - -``` -# Please read the corosync.conf.5 manual page - -compatibility: whitetank - -totem { - version: 2 - secauth: off - interface { - member { - memberaddr: node1 - } - member { - memberaddr: node2 - } - member { - memberaddr: node3 - } - ringnumber: 0 - bindnetaddr: current-node - mcastport: 5405 - ttl: 1 - } - transport: udpu -} - -logging { - fileline: off - to_logfile: yes - to_syslog: yes - logfile: /var/log/cluster/corosync.log - debug: off - timestamp: on - logger_subsys { - subsys: AMF - debug: off - } -} - -# this will start Pacemaker processes - -service { -ver: 0 -name: pacemaker -} -``` - -**Note**: in this example: - -- unicast UDP is used - -- bindnetaddr for corosync process is current-node, that has the right value on each node due to the alias added in /etc/hosts above - -- Pacemaker processes are started by the corosync daemon, so there is no need to launch it via /etc/init.d/pacemaker start - -### Copy configuration files and auth key on each of the other nodes - -``` -[root@node2 ~]# scp /etc/corosync/* root@node1:/etc/corosync/ -... -[root@node2 ~]# scp /etc/corosync/* root@nodeN:/etc/corosync/ -``` - -Corosync needs port 5405 to be opened. Configure any firewall or iptables accordingly. For a quick start just disable iptables on each nodes: - -``` -[root@node2 ~]# service iptables stop -... -[root@nodeN ~]# service iptables stop -``` - -### Start Corosyn on each node - -``` -[root@node2 ~] #/etc/init.d/corosync start -... -[root@nodeN ~] #/etc/init.d/corosync start -``` - -Check that the corosync daemon is successfully bound to port 5405. - -``` -[root@node2 ~] #netstat -na | grep 5405 -udp 0 0 10.228.103.72:5405 0.0.0.0:* -``` - -Check if other nodes are reachable with nc utility and option UDP (-u). - -``` -[root@node2 ~] #echo "check ..." | nc -u node1 5405 -[root@node2 ~] #echo "check ..." | nc -u node3 5405 -... -[root@node1 ~] #echo "check ..." | nc -u node2 5405 -[root@node1 ~] #echo "check ..." | nc -u node3 5405 -``` - -If the following message is displayed, there is an issue with communication between the nodes. - -``` -nc: Write error: Connection refused -``` - -This is most likely to be an issue with the firewall configuration on your nodes. Check and resolve any issues with your firewall configuration. - -### Check the cluster status from any node - -``` -[root@node3 ~]# crm status -``` - -The command should produce the following. - -``` -[root@node3 ~]# crm status -Last updated: Mon Jun 30 12:47:53 2014 -Last change: Mon Jun 30 12:47:39 2014 via crmd on node2 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -0 Resources configured - -Online: [ node1 node2 node3 ] -``` - -For the basic setup disable the following properties: - -- stonith - -- quorum policy - -``` -[root@node3 ~]# crm configure property 'stonith-enabled'='false' -[root@node3 ~]# crm configure property 'no-quorum-policy'='ignore' -``` - -For additional information see: - -[http://www.clusterlabs.org/doc/crm_fencing.html](http://www.clusterlabs.org/doc/crm_fencing.html) - -[http://clusterlabs.org/doc/](http://clusterlabs.org/doc/) - -The configuration is automatically updated on every node. - -Check it from another node, say node1: - -``` -[root@node1 ~]# crm configure show -node node1 -node node2 -node node3 -property cib-bootstrap-options: \ - dc-version=1.1.10-14.el6_5.3-368c726 \ - cluster-infrastructure="classic openais (with plugin)" \ - expected-quorum-votes=3 \ - stonith-enabled=false \ - no-quorum-policy=ignore \ - placement-strategy=balanced \ - default-resource-stickiness=infinity -``` - -The Corosync / Pacemaker cluster is ready to be configured to manage resources. - -## MariaDB MaxScale init script - -The MariaDB MaxScale init script in `/etc/init.d./maxscale` allows to start, stop, restart and monitor the MariaDB MaxScale process running on the system. - -``` -[root@node1 ~]# /etc/init.d/maxscale -Usage: /etc/init.d/maxscale {start|stop|status|restart|condrestart|reload} -``` - -- Start - -``` -[root@node1 ~]# /etc/init.d/maxscale start -Starting MaxScale: maxscale (pid 25892) is running... [ OK ] -``` - -- Start again - -``` -[root@node1 ~]# /etc/init.d/maxscale start -Starting MaxScale: found maxscale (pid 25892) is running.[ OK ] -``` - -- Stop - -``` -[root@node1 ~]# /etc/init.d/maxscale stop -Stopping MaxScale: [ OK ] -``` - -- Stop again - -``` -[root@node1 ~]# /etc/init.d/maxscale stop -Stopping MaxScale: [FAILED] -``` - -- Status (MaxScale not running) - -``` -[root@node1 ~]# /etc/init.d/maxscale status -MaxScale is stopped [FAILED] -``` - -The script exit code for "status" is 3 - -- Status (MaxScale is running) - -``` -[root@node1 ~]# /etc/init.d/maxscale status -Checking MaxScale status: MaxScale (pid 25953) is running.[ OK ] -``` - -The script exit code for "status" is 0 - -Read the following for additional information about LSB init scripts: - -[http://www.linux-ha.org/wiki/LSB_Resource_Agents](http://www.linux-ha.org/wiki/LSB_Resource_Agents) - -After checking that the init scripts for MariaDB MaxScale work, it is possible to configure MariaDB MaxScale for HA via Pacemaker. - -# Configure MariaDB MaxScale for HA with Pacemaker - -``` -[root@node2 ~]# crm configure primitive MaxScale lsb:maxscale \ -op monitor interval="10s” timeout=”15s” \ -op start interval="0” timeout=”15s” \ -op stop interval="0” timeout=”30s” -``` - -MaxScale resource will be started. - -``` -[root@node2 ~]# crm status -Last updated: Mon Jun 30 13:15:34 2014 -Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node2 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -1 Resources configured - -Online: [ node1 node2 node3 ] - - MaxScale (lsb:maxscale): Started node1 -``` - -## Basic use cases - -### Resource restarted after a failure - -In the example MariaDB MaxScale PID is 26114, kill the process immediately. - -``` -[root@node2 ~]# kill -9 26114 -... -[root@node2 ~]# crm status -Last updated: Mon Jun 30 13:16:11 2014 -Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node2 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -1 Resources configured - -Online: [ node1 node2 node3 ] - -Failed actions: - - MaxScale_monitor_15000 on node1 'not running' (7): call=19, status=complete, last-rc-change='Mon Jun 30 13:16:14 2014', queued=0ms, exec=0ms -``` - -**Note**: the _MaxScale_monitor_ failed action - -After a few seconds it will be started again. - -``` -[root@node2 ~]# crm status -Last updated: Mon Jun 30 13:21:12 2014 -Last change: Mon Jun 30 13:15:28 2014 via cibadmin on node1 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -1 Resources configured - -Online: [ node1 node2 node3 ] - - MaxScale (lsb:maxscale): Started node1 -``` - -### The resource cannot be migrated to node1 for a failure - -First, migrate the the resource to another node, say node3. - -``` -[root@node1 ~]# crm resource migrate MaxScale node3 -... - -Online: [ node1 node2 node3 ] - -Failed actions: - - MaxScale_start_0 on node1 'not running' (7): call=76, status=complete, last-rc-change='Mon Jun 30 13:31:17 2014', queued=2015ms, exec=0ms -``` - -**Note**: the _MaxScale_start_ failed action on node1, and after a few seconds. - -``` -[root@node3 ~]# crm status -Last updated: Mon Jun 30 13:35:00 2014 -Last change: Mon Jun 30 13:31:13 2014 via crm_resource on node3 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -1 Resources configured - -Online: [ node1 node2 node3 ] - - MaxScale (lsb:maxscale): Started node2 - -Failed actions: - - MaxScale_start_0 on node1 'not running' (7): call=76, status=complete, last-rc-change='Mon Jun 30 13:31:17 2014', queued=2015ms, exec=0ms -``` - -Successfully, MaxScale has been started on a new node (node2). - -**Note**: Failed actions remain in the output of crm status. - - With "crm resource cleanup MaxScale" is possible to cleanup the messages: - -``` -[root@node1 ~]# crm resource cleanup MaxScale -Cleaning up MaxScale on node1 -Cleaning up MaxScale on node2 -Cleaning up MaxScale on node3 -``` - -The cleaned status is visible from other nodes as well. - -``` -[root@node2 ~]# crm status -Last updated: Mon Jun 30 13:38:18 2014 -Last change: Mon Jun 30 13:38:17 2014 via crmd on node3 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -1 Resources configured - -Online: [ node1 node2 node3 ] - - MaxScale (lsb:maxscale): Started node2 -``` - -## Add a Virtual IP (VIP) to the cluster - -It’s possible to add a virtual IP to the cluster. MariaDB MaxScale process will be only contacted via this IP. The virtual IP can move across nodes in case one of them fails. - -The Setup is very easy. Assuming an addition IP address is available and can be added to one of the nodes, this is the new configuration to add. - -``` -[root@node2 ~]# crm configure primitive maxscale_vip ocf:heartbeat:IPaddr2 params ip=192.168.122.125 op monitor interval=10s -``` - -MariaDB MaxScale process and the VIP must be run in the same node, so it is mandatory to add to the configuration to the group ‘maxscale_service’. - -``` -[root@node2 ~]# crm configure group maxscale_service maxscale_vip MaxScale -``` - -The following is the final configuration. - -``` -[root@node3 ~]# crm configure show -node node1 -node node2 -node node3 -primitive MaxScale lsb:maxscale \ - op monitor interval=15s timeout=10s \ - op start interval=0 timeout=15s \ - op stop interval=0 timeout=30s -primitive maxscale_vip IPaddr2 \ - params ip=192.168.122.125 \ - op monitor interval=10s -group maxscale_service maxscale_vip MaxScale \ - meta target-role=Started -property cib-bootstrap-options: \ - dc-version=1.1.10-14.el6_5.3-368c726 \ - cluster-infrastructure="classic openais (with plugin)" \ - expected-quorum-votes=3 \ - stonith-enabled=false \ - no-quorum-policy=ignore \ - placement-strategy=balanced \ - last-lrm-refresh=1404125486 -``` - -Check the resource status. - -``` -[root@node1 ~]# crm status -Last updated: Mon Jun 30 13:51:29 2014 -Last change: Mon Jun 30 13:51:27 2014 via crmd on node1 -Stack: classic openais (with plugin) -Current DC: node2 - partition with quorum -Version: 1.1.10-14.el6_5.3-368c726 - -3 Nodes configured, 3 expected votes -2 Resources configured - -Online: [ node1 node2 node3 ] - - Resource Group: maxscale_service - - maxscale_vip (ocf::heartbeat:IPaddr2): Started node2 - - MaxScale (lsb:maxscale): Started node2 -``` - -With both resources on node2, now MariaDB MaxScale service will be reachable via the configured VIP address 192.168.122.125. - diff --git a/Documentation/Tutorials/images/Keepalived.png b/Documentation/Tutorials/images/Keepalived.png index f500e5a17..f9d6d0637 100644 Binary files a/Documentation/Tutorials/images/Keepalived.png and b/Documentation/Tutorials/images/Keepalived.png differ diff --git a/Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md b/Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md index 5fc566a09..1ff71d84c 100644 --- a/Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md +++ b/Documentation/Upgrading/Upgrading-To-MaxScale-2.2.md @@ -25,4 +25,10 @@ string in slashes, e.g. `match=/^select/` defines the pattern `^select`. ### Binlog Server Binlog server automatically accepts GTID connection from MariaDB 10 slave servers -by saving all incoming GTIDs into a SQLite map database. \ No newline at end of file +by saving all incoming GTIDs into a SQLite map database. + +### MaxCtrl Included in Main Package + +In the 2.2.1 beta version MaxCtrl was in its own package whereas in 2.2.2 +it is in the main `maxscale` package. If you have a previous installation +of MaxCtrl, please remove it before upgrading to MaxScale 2.2.2. diff --git a/maxctrl/lib/common.js b/maxctrl/lib/common.js index 9372c28fa..bf786d6dc 100644 --- a/maxctrl/lib/common.js +++ b/maxctrl/lib/common.js @@ -16,6 +16,7 @@ var colors = require('colors/safe'); var Table = require('cli-table'); var consoleLib = require('console') var os = require('os') +var fs = require('fs') module.exports = function() { @@ -57,6 +58,13 @@ module.exports = function() { argv.reject(err) }) }, function(err) { + + if (err.error.cert) { + // TLS errors cause extremely verbose errors, truncate the certifiate details + // from the error output + delete err.error.cert + } + // One of the HTTP request pings to the cluster failed, log the error argv.reject(JSON.stringify(err.error, null, 4)) }) @@ -181,6 +189,31 @@ module.exports = function() { return Promise.resolve(colors.green('OK')) } + + this.setTlsCerts = function(args) { + args.agentOptions = {} + if (this.argv['tls-key']) { + args.agentOptions.key = fs.readFileSync(this.argv['tls-key']) + } + + if (this.argv['tls-cert']) { + args.agentOptions.cert = fs.readFileSync(this.argv['tls-cert']) + } + + if (this.argv['tls-ca-cert']) { + args.agentOptions.ca = fs.readFileSync(this.argv['tls-ca-cert']) + } + + if (this.argv['tls-passphrase']) { + args.agentOptions.passphrase = this.argv['tls-passphrase'] + } + + if (!this.argv['tls-verify-server-cert']) { + args.agentOptions.checkServerIdentity = function() { + } + } + } + // Helper for executing requests and handling their responses, returns a // promise that is fulfilled when all requests successfully complete. The // promise is rejected if any of the requests fails. @@ -189,6 +222,7 @@ module.exports = function() { args.uri = getUri(host, this.argv.secure, resource) args.json = true args.timeout = this.argv.timeout + setTlsCerts(args) return request(args) .then(function(res) { @@ -275,7 +309,11 @@ function pingCluster(hosts) { var promises = [] hosts.forEach(function(i) { - promises.push(request(getUri(i, false, ''))) + args = {} + args.uri = getUri(i, this.argv.secure, '') + args.json = true + setTlsCerts(args) + promises.push(request(args)) }) return Promise.all(promises) diff --git a/maxctrl/lib/core.js b/maxctrl/lib/core.js index 7a3c34ee5..a386c9642 100644 --- a/maxctrl/lib/core.js +++ b/maxctrl/lib/core.js @@ -21,7 +21,7 @@ program .strict() .exitProcess(false) .showHelpOnFail(false) - .group(['u', 'p', 'h', 's', 't', 'q', 'tsv'], 'Global Options:') + .group(['u', 'p', 'h', 't', 'q', 'tsv'], 'Global Options:') .option('u', { alias:'user', global: true, @@ -42,27 +42,45 @@ program default: 'localhost:8989', type: 'string' }) - .option('s', { - alias: 'secure', - describe: 'Enable HTTPS requests', - default: 'false', - type: 'boolean' - }) .option('t', { alias: 'timeout', describe: 'Request timeout in milliseconds', - default: '10000', + default: 10000, type: 'number' }) .option('q', { alias: 'quiet', describe: 'Silence all output', - default: 'false', + default: false, type: 'boolean' }) .option('tsv', { describe: 'Print tab separated output', - default: 'false', + default: false, + type: 'boolean' + }) + .group(['s', 'tls-key', 'tls-cert', 'tls-ca-cert', 'tls-verify-server-cert'], 'HTTPS/TLS Options:') + .option('s', { + alias: 'secure', + describe: 'Enable HTTPS requests', + default: false, + type: 'boolean' + }) + .option('tls-key', { + describe: 'Path to TLS private key', + type: 'string' + }) + .option('tls-cert', { + describe: 'Path to TLS public certificate', + type: 'string' + }) + .option('tls-ca-cert', { + describe: 'Path to TLS CA certificate', + type: 'string' + }) + .option('tls-verify-server-cert', { + describe: 'Whether to verify server TLS certificates', + default: true, type: 'boolean' }) diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index 748ebe651..5c481a7ce 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -822,6 +822,9 @@ add_test_executable(setup_binlog_gtid.cpp setup_binlog_gtid setup_binlog_gtid LA # TODO: make it working with zypper and apt, move part of KDC setup to MDBCI add_test_executable(kerberos_setup.cpp kerberos_setup kerberos LABELS HEAVY gssapi REPL_BACKEND) +# Configures 'keepalived' on two Maxscale machines and tried failover +add_test_executable(keepalived.cpp keepalived keepalived LABELS REPL_BACKEND TWO_MAXSCALES) + # enable after fixing MXS-419 # add_test_executable(mxs419_lots_of_connections.cpp mxs419_lots_of_connections replication LABELS REPL_BACKEND) diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.000 b/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.000 new file mode 100755 index 000000000..7810d3583 --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.000 @@ -0,0 +1,93 @@ +[maxscale] +threads=###threads### + +[MySQL Monitor] +type=monitor +module=mysqlmon +###repl51### +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +monitor_interval=1000 +detect_stale_master=false +detect_standalone_master=false + +[RW Split Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS +max_slave_connections=1 +version_string=10.2-server1 + +[Read Connection Router Slave] +type=service +router=readconnroute +router_options=slave +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +version_string=10.2-server1 + +[Read Connection Router Master] +type=service +router=readconnroute +router_options=master +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +version_string=10.2-server1 + +[RW Split Listener] +type=listener +service=RW Split Router +protocol=MySQLClient +port=4006 + +[Read Connection Listener Slave] +type=listener +service=Read Connection Router Slave +protocol=MySQLClient +port=4009 + +[Read Connection Listener Master] +type=listener +service=Read Connection Router Master +protocol=MySQLClient +port=4008 + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +socket=default + +[server1] +type=server +address=###node_server_IP_1### +port=###node_server_port_1### +protocol=MySQLBackend + +[server2] +type=server +address=###node_server_IP_2### +port=###node_server_port_2### +protocol=MySQLBackend + +[server3] +type=server +address=###node_server_IP_3### +port=###node_server_port_3### +protocol=MySQLBackend + +[server4] +type=server +address=###node_server_IP_4### +port=###node_server_port_4### +protocol=MySQLBackend diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.001 b/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.001 new file mode 100755 index 000000000..c16a40c49 --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.keepalived.001 @@ -0,0 +1,93 @@ +[maxscale] +threads=###threads### + +[MySQL Monitor] +type=monitor +module=mysqlmon +###repl51### +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +monitor_interval=1000 +detect_stale_master=false +detect_standalone_master=false + +[RW Split Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS +max_slave_connections=1 +version_string=10.2-server2 + +[Read Connection Router Slave] +type=service +router=readconnroute +router_options=slave +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +version_string=10.2-server2 + +[Read Connection Router Master] +type=service +router=readconnroute +router_options=master +servers=server1,server2,server3,server4 +user=maxskysql +passwd=skysql +version_string=10.2-server2 + +[RW Split Listener] +type=listener +service=RW Split Router +protocol=MySQLClient +port=4006 + +[Read Connection Listener Slave] +type=listener +service=Read Connection Router Slave +protocol=MySQLClient +port=4009 + +[Read Connection Listener Master] +type=listener +service=Read Connection Router Master +protocol=MySQLClient +port=4008 + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +socket=default + +[server1] +type=server +address=###node_server_IP_1### +port=###node_server_port_1### +protocol=MySQLBackend + +[server2] +type=server +address=###node_server_IP_2### +port=###node_server_port_2### +protocol=MySQLBackend + +[server3] +type=server +address=###node_server_IP_3### +port=###node_server_port_3### +protocol=MySQLBackend + +[server4] +type=server +address=###node_server_IP_4### +port=###node_server_port_4### +protocol=MySQLBackend diff --git a/maxscale-system-test/fail_switch_rejoin_common.cpp b/maxscale-system-test/fail_switch_rejoin_common.cpp index 292020649..765a39381 100644 --- a/maxscale-system-test/fail_switch_rejoin_common.cpp +++ b/maxscale-system-test/fail_switch_rejoin_common.cpp @@ -147,13 +147,16 @@ bool generate_traffic_and_check(TestConnections& test, MYSQL* conn, int insert_c timespec short_sleep; short_sleep.tv_sec = 0; short_sleep.tv_nsec = 100000000; + + mysql_query(conn, "BEGIN"); + for (int i = 0; i < insert_count; i++) { test.try_query(conn, INSERT, inserts++); nanosleep(&short_sleep, NULL); } - sleep(1); bool rval = false; + mysql_query(conn, SELECT); MYSQL_RES *res = mysql_store_result(conn); test.assert(res != NULL, "Query did not return a result set"); @@ -184,6 +187,7 @@ bool generate_traffic_and_check(TestConnections& test, MYSQL* conn, int insert_c } mysql_free_result(res); } + mysql_query(conn, "COMMIT"); return rval; } diff --git a/maxscale-system-test/keepalived.cpp b/maxscale-system-test/keepalived.cpp new file mode 100644 index 000000000..650a28039 --- /dev/null +++ b/maxscale-system-test/keepalived.cpp @@ -0,0 +1,146 @@ +/** + * @file keepalived.cpp keepalived Test of two Maxscale + keepalived failover + * + * - 'version_string' configured to be different for every Maxscale + * - configure keepalived for two nodes (uses xxx.xxx.xxx.253 as a virtual IP + * where xxx.xxx.xxx. - first 3 numbers from client IP) + * - suspend Maxscale 1 + * - wait and check version_string from Maxscale on virtual IP, expect 10.2-server2 + * - resume Maxscale 1, suspend Maxscale 2 + * - wait and check version_string from Maxscale on virtual IP, expect 10.2-server1 + * - resume Maxscale 2 + * TODO: replace 'yum' call with executing Chef recipe + */ + + +#include +#include "testconnections.h" + +#define FAILOVER_WAIT_TIME 5 + +char virtual_ip[16]; +char * print_version_string(TestConnections * Test) +{ + MYSQL * keepalived_conn = open_conn(Test->maxscales->rwsplit_port[0], virtual_ip, Test->maxscales->user_name, Test->maxscales->password, Test->ssl); + const char * version_string; + mariadb_get_info(keepalived_conn, MARIADB_CONNECTION_SERVER_VERSION, (void *)&version_string); + Test->tprintf("%s\n", version_string); + mysql_close(keepalived_conn); + return((char*) version_string); +} + +int main(int argc, char *argv[]) +{ + int i; + char * version; + + TestConnections * Test = new TestConnections(argc, argv); + Test->set_timeout(10); + + Test->tprintf("Maxscale_N %d\n", Test->maxscales->N); + if (Test->maxscales->N < 2) + { + Test->tprintf("At least 2 Maxscales are needed for this test. Exiting\n"); + exit(0); + } + + + Test->check_maxscale_alive(0); + Test->check_maxscale_alive(1); + + // Get test client IP, replace last number in it with 253 and use it as Virtual IP + char client_ip[24]; + char * last_dot; + Test->get_client_ip(0, client_ip); + last_dot = client_ip; + Test->tprintf("My IP is %s\n", client_ip); + for (i = 0; i < 3; i++) + { + last_dot = strstr(last_dot, "."); + last_dot = &last_dot[1]; + } + last_dot[0] = '\0'; + Test->tprintf("First part of IP is %s\n", client_ip); + + sprintf(virtual_ip, "%s253", client_ip); + + + for (i = 0; i < Test->maxscales->N; i++) + { + std::string src = std::string(test_dir) + "/keepalived_cnf/" + std::to_string(i + 1) + ".conf"; + std::string cp_cmd = "cp " + std::string(Test->maxscales->access_homedir[i]) + std::to_string(i + 1) + ".conf " + + " /etc/keepalived/keepalived.conf"; + Test->tprintf("%s\n", src.c_str()); + Test->tprintf("%s\n", cp_cmd.c_str()); + Test->maxscales->ssh_node(i, "yum install -y keepalived", true); + Test->maxscales->copy_to_node(i, src.c_str(), Test->maxscales->access_homedir[i]); + Test->maxscales->ssh_node(i, cp_cmd.c_str(), true); + Test->maxscales->ssh_node_f(i, true, "sed -i \"s/###virtual_ip###/%s/\" /etc/keepalived/keepalived.conf", virtual_ip); + std::string script_src = std::string(test_dir) + "/keepalived_cnf/is_maxscale_running.sh"; + std::string script_cp_cmd = "cp " + std::string(Test->maxscales->access_homedir[i]) + "is_maxscale_running.sh /usr/bin/"; + Test->maxscales->copy_to_node(i, script_src.c_str(), Test->maxscales->access_homedir[i]); + Test->maxscales->ssh_node(i, script_cp_cmd.c_str(), true); + Test->maxscales->ssh_node(i, "sudo service keepalived restart", true); + } + + print_version_string(Test); + + Test->tprintf("Suspend Maxscale 000 machine and waiting\n"); + system(Test->maxscales->stop_vm_command[0]); + sleep(FAILOVER_WAIT_TIME); + + version = print_version_string(Test); + if (strcmp(version, "10.2-server2") != 0) + { + Test->add_result(false, "Failover did not happen"); + } + + + Test->tprintf("Resume Maxscale 000 machine and waiting\n"); + system(Test->maxscales->start_vm_command[0]); + sleep(FAILOVER_WAIT_TIME); + print_version_string(Test); + + Test->tprintf("Suspend Maxscale 001 machine and waiting\n"); + system(Test->maxscales->stop_vm_command[1]); + sleep(FAILOVER_WAIT_TIME); + + version = print_version_string(Test); + if (strcmp(version, "10.2-server1") != 0) + { + Test->add_result(false, "Failover did not happen"); + } + + print_version_string(Test); + Test->tprintf("Resume Maxscale 001 machine and waiting\n"); + system(Test->maxscales->start_vm_command[1]); + sleep(FAILOVER_WAIT_TIME); + print_version_string(Test); + + Test->tprintf("Stop Maxscale on 000 machine\n"); + Test->stop_maxscale(0); + sleep(FAILOVER_WAIT_TIME); + version = print_version_string(Test); + if (strcmp(version, "10.2-server2") != 0) + { + Test->add_result(false, "Failover did not happen"); + } + + Test->tprintf("Start back Maxscale on 000 machine\n"); + Test->start_maxscale(0); + sleep(FAILOVER_WAIT_TIME); + + Test->tprintf("Stop Maxscale on 001 machine\n"); + Test->stop_maxscale(1); + sleep(FAILOVER_WAIT_TIME); + version = print_version_string(Test); + if (strcmp(version, "10.2-server1") != 0) + { + Test->add_result(false, "Failover did not happen"); + } + + int rval = Test->global_result; + delete Test; + return rval; +} + diff --git a/maxscale-system-test/keepalived_cnf/is_maxscale_running.sh b/maxscale-system-test/keepalived_cnf/is_maxscale_running.sh new file mode 100755 index 000000000..e0a894c89 --- /dev/null +++ b/maxscale-system-test/keepalived_cnf/is_maxscale_running.sh @@ -0,0 +1,24 @@ +#!/bin/bash +fileName="maxadmin_output.txt" +rm $fileName +timeout 2s maxadmin list servers > $fileName +to_result=$? +if [ $to_result -ge 1 ] +then + echo Timed out or error, timeout returned $to_result + exit 3 +else + echo MaxAdmin success, rval is $to_result + echo Checking maxadmin output sanity + grep1=$(grep server1 $fileName) + grep2=$(grep server2 $fileName) + + if [ "$grep1" ] && [ "$grep2" ] + then + echo All is fine + exit 0 + else + echo Something is wrong + exit 3 + fi +fi diff --git a/maxscale-system-test/local_address.cpp b/maxscale-system-test/local_address.cpp index d9ddf22c6..cceef3cc6 100644 --- a/maxscale-system-test/local_address.cpp +++ b/maxscale-system-test/local_address.cpp @@ -278,6 +278,7 @@ void run_test(TestConnections& test, const vector& ips) create_user_and_grants(test, zUser1, zPassword1, local_ip); create_user_and_grants(test, zUser2, zPassword2, ip2); create_user_and_grants(test, zUser2, zPassword2, local_ip); + test.repl->sync_slaves(); test.tprintf("\n"); test.tprintf("Testing default; alice should be able to access, bob not."); diff --git a/maxscale-system-test/mdbci/set_env.sh b/maxscale-system-test/mdbci/set_env.sh index 86459e8d1..8461744c3 100644 --- a/maxscale-system-test/mdbci/set_env.sh +++ b/maxscale-system-test/mdbci/set_env.sh @@ -20,6 +20,7 @@ export maxscale_log_dir="/var/log/maxscale/" # Number of nodes export galera_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep galera | grep network | wc -l` export node_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep node | grep network | wc -l` +export maxscale_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep maxscale | grep network | wc -l` sed "s/^/export /g" "$MDBCI_VM_PATH/$config_name"_network_config > "$curr_dir"/"$config_name"_network_config_export source "$curr_dir"/"$config_name"_network_config_export @@ -40,7 +41,7 @@ export maxscale_password="skysql" export maxadmin_password="mariadb" -for prefix in "node" "galera" +for prefix in "node" "galera" "maxscale" do N_var="$prefix"_N Nx=${!N_var} @@ -77,8 +78,8 @@ do eval 'export $stop_cmd_var="$mysql_exe stop "' fi - eval 'export "$prefix"_"$num"_start_vm_command="cd $mdbci_dir/$config_name;vagrant up node_$num --provider=$provider; cd $curr_dir"' - eval 'export "$prefix"_"$num"_kill_vm_command="cd $mdbci_dir/$config_name;vagrant halt node_$num --provider=$provider; cd $curr_dir"' + eval 'export "$prefix"_"$num"_start_vm_command="cd ${MDBCI_VM_PATH}/$config_name;vagrant resume ${prefix}_$num ; cd $curr_dir"' + eval 'export "$prefix"_"$num"_stop_vm_command="cd ${MDBCI_VM_PATH}/$config_name;vagrant suspend ${prefix}_$num ; cd $curr_dir"' done done diff --git a/maxscale-system-test/mysqlmon_failover_stress.cpp b/maxscale-system-test/mysqlmon_failover_stress.cpp index e6452583b..6ccb6d8a2 100755 --- a/maxscale-system-test/mysqlmon_failover_stress.cpp +++ b/maxscale-system-test/mysqlmon_failover_stress.cpp @@ -489,7 +489,15 @@ void check_server_statuses(TestConnections& test) masters += check_server_status(test, 3); masters += check_server_status(test, 4); - test.assert(masters == 1, "Unpexpected number of masters: %d", masters); + if (masters == 0) + { + test.tprintf("No master, checking that autofail has been turned off."); + test.log_includes(0, "disabling automatic failover"); + } + else if (masters != 1) + { + test.assert(!true, "Unexpected number of masters: %d", masters); + } } void run(TestConnections& test) diff --git a/maxscale-system-test/mysqlmon_rejoin_bad2.cpp b/maxscale-system-test/mysqlmon_rejoin_bad2.cpp index 27837696b..7b89d2551 100644 --- a/maxscale-system-test/mysqlmon_rejoin_bad2.cpp +++ b/maxscale-system-test/mysqlmon_rejoin_bad2.cpp @@ -129,9 +129,9 @@ int main(int argc, char** argv) return test.global_result; } - // Manually set current master to replicate from the old master to quickly fix the cluster. - cout << "Setting server " << master_id_new << " to replicate from server 1. Auto-rejoin should redirect " - "servers so that in the end server 1 is master and all others are slaves." << endl; + // Set current master to replicate from the old master. The old master should remain as the current master. + cout << "Setting server " << master_id_new << " to replicate from server 1. Server " << master_id_new + << " should remain as the master because server 1 doesn't have the latest event it has." << endl; const char CHANGE_CMD_FMT[] = "CHANGE MASTER TO MASTER_HOST = '%s', MASTER_PORT = %d, " "MASTER_USE_GTID = current_pos, MASTER_USER='repl', MASTER_PASSWORD = 'repl';"; char cmd[256]; @@ -143,9 +143,10 @@ int main(int argc, char** argv) sleep(5); get_output(test); - expect(test, "server1", "Master", "Running"); - expect(test, "server2", "Slave", "Running"); + expect(test, "server1", "Running"); + expect(test, "server2", "Master", "Running"); expect(test, "server3", "Slave", "Running"); expect(test, "server4", "Slave", "Running"); + test.repl->fix_replication(); return test.global_result; } diff --git a/maxscale-system-test/nodes.cpp b/maxscale-system-test/nodes.cpp index c85b19dab..125b53f16 100644 --- a/maxscale-system-test/nodes.cpp +++ b/maxscale-system-test/nodes.cpp @@ -197,7 +197,6 @@ int Nodes::ssh_node_f(int node, bool sudo, const char* format, ...) va_start(valist, format); vsnprintf(sys, message_len + 1, format, valist); va_end(valist); - int result = ssh_node(node, sys, sudo); free(sys); return (result); @@ -428,6 +427,40 @@ int Nodes::read_basic_env() { sprintf(hostname[i], "%s", IP[i]); } + + sprintf(env_name, "%s_%03d_start_vm_command", prefix, i); + env = getenv(env_name); + if (env == NULL) + { + sprintf(env_name, "%s_start_vm_command", prefix); + env = getenv(env_name); + } + + if (env != NULL) + { + sprintf(start_vm_command[i], "%s", env); + } + else + { + sprintf(start_vm_command[i], "exit 0"); + } + + sprintf(env_name, "%s_%03d_stop_vm_command", prefix, i); + env = getenv(env_name); + if (env == NULL) + { + sprintf(env_name, "%s_stop_vm_command", prefix); + env = getenv(env_name); + } + + if (env != NULL) + { + sprintf(stop_vm_command[i], "%s", env); + } + else + { + sprintf(stop_vm_command[i], "exit 0"); + } } } diff --git a/maxscale-system-test/nodes.h b/maxscale-system-test/nodes.h index b961e226f..376a03ae9 100644 --- a/maxscale-system-test/nodes.h +++ b/maxscale-system-test/nodes.h @@ -64,6 +64,16 @@ public: char hostname[256][1024]; + /** + * @brief stop_vm_command Command to suspend VM + */ + char stop_vm_command[256][1024]; + /** + + * @brief start_vm_command Command to resume VM + */ + char start_vm_command[256][1024]; + /** * @brief User name to access backend nodes */ diff --git a/maxscale-system-test/testconnections.cpp b/maxscale-system-test/testconnections.cpp index 9828afa7b..e41cab5a3 100644 --- a/maxscale-system-test/testconnections.cpp +++ b/maxscale-system-test/testconnections.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "mariadb_func.h" @@ -281,7 +282,7 @@ TestConnections::TestConnections(int argc, char *argv[]): if (maxscale_init) { - init_maxscale(0); + init_maxscales(); } if (backend_ssl) @@ -529,11 +530,26 @@ const char * get_template_name(char * test_name) void TestConnections::process_template(int m, const char *template_name, const char *dest) { + struct stat stb; char str[4096]; char template_file[1024]; + char extended_template_file[1024]; + sprintf(template_file, "%s/cnf/maxscale.cnf.template.%s", test_dir, template_name); + sprintf(extended_template_file, "%s.%03d", template_file, m); + + if (stat((char*)extended_template_file, &stb) == 0) + { + strcpy(template_file, extended_template_file); + } + tprintf("Template file is %s\n", template_file); + sprintf(str, "cp %s maxscale.cnf", template_file); + if (verbose) + { + tprintf("Executing '%s' command\n", str); + } if (system(str) != 0) { tprintf("Error copying maxscale.cnf template\n"); @@ -595,12 +611,19 @@ void TestConnections::process_template(int m, const char *template_name, const c maxscales->copy_to_node_legacy((char *) "maxscale.cnf", (char *) dest, m); } +int TestConnections::init_maxscales() +{ + for (int i = 0; i < maxscales->N; i++) + { + init_maxscale(i); + } +} + int TestConnections::init_maxscale(int m) { const char * template_name = get_template_name(test_name); - tprintf("Template is %s\n", template_name); - process_template(m, template_name, maxscales->access_homedir[m]); + process_template(m, template_name, maxscales->access_homedir[m]); maxscales->ssh_node_f(m, true, "cp maxscale.cnf %s;rm -rf %s/certs;mkdir -m a+wrx %s/certs;", maxscales->maxscale_cnf[m], @@ -614,7 +637,6 @@ int TestConnections::init_maxscale(int m) sprintf(str, "cp %s/ssl-cert/* .", test_dir); system(str); - maxscales->ssh_node_f(m, true, "chown maxscale:maxscale -R %s/certs;" "chmod 664 %s/certs/*.pem;" diff --git a/maxscale-system-test/testconnections.h b/maxscale-system-test/testconnections.h index 18aa92413..824732aa8 100644 --- a/maxscale-system-test/testconnections.h +++ b/maxscale-system-test/testconnections.h @@ -263,11 +263,16 @@ public: /** * @brief InitMaxscale Copies MaxSclae.cnf and start MaxScale + * @param m Number of Maxscale node * @return 0 if case of success */ int init_maxscale(int m = 0); - + /** + * @brief InitMaxscale Copies MaxSclae.cnf and start MaxScale on all Maxscale nodes + * @return 0 if case of success + */ + int init_maxscales(); /** * @brief start_binlog configure first node as Master, Second as slave connected to Master and others as slave connected to MaxScale binlog router diff --git a/server/modules/monitor/mariadbmon/CMakeLists.txt b/server/modules/monitor/mariadbmon/CMakeLists.txt index af152bf4e..ba5aaebb0 100644 --- a/server/modules/monitor/mariadbmon/CMakeLists.txt +++ b/server/modules/monitor/mariadbmon/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(mariadbmon SHARED mysql_mon.cc) +add_library(mariadbmon SHARED mariadbmon.cc) target_link_libraries(mariadbmon maxscale-common) add_dependencies(mariadbmon pcre2) set_target_properties(mariadbmon PROPERTIES VERSION "1.4.0") diff --git a/server/modules/monitor/mariadbmon/mysql_mon.cc b/server/modules/monitor/mariadbmon/mariadbmon.cc similarity index 99% rename from server/modules/monitor/mariadbmon/mysql_mon.cc rename to server/modules/monitor/mariadbmon/mariadbmon.cc index 4de130849..d73bf4603 100644 --- a/server/modules/monitor/mariadbmon/mysql_mon.cc +++ b/server/modules/monitor/mariadbmon/mariadbmon.cc @@ -12,7 +12,7 @@ */ /** - * @file mysql_mon.c - A MySQL replication cluster monitor + * @file A MariaDB replication cluster monitor */ #define MXS_MODULE_NAME "mariadbmon" @@ -811,8 +811,8 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE() { - MXS_NOTICE("Initialise the MySQL Monitor module."); - static const char ARG_MONITOR_DESC[] = "MySQL Monitor name (from configuration file)"; + MXS_NOTICE("Initialise the MariaDB Monitor module."); + static const char ARG_MONITOR_DESC[] = "Monitor name (from configuration file)"; static modulecmd_arg_type_t switchover_argv[] = { { @@ -865,7 +865,7 @@ extern "C" MXS_MODULE_API_MONITOR, MXS_MODULE_GA, MXS_MONITOR_VERSION, - "A MySQL Master/Slave replication monitor", + "A MariaDB Master/Slave replication monitor", "V1.5.0", MXS_NO_MODULE_CAPABILITIES, &MyObject, @@ -939,7 +939,7 @@ void info_free_func(void *val) /** * @brief Helper function that initializes the server info hashtable * - * @param handle MySQL monitor handle + * @param handle MariaDB monitor handle * @param database List of monitored databases * @return True on success, false if initialization failed. At the moment * initialization can only fail if memory allocation fails. @@ -1671,7 +1671,7 @@ static MXS_MONITORED_SERVER *build_mysql51_replication_tree(MXS_MONITOR *mon) /** * Monitor an individual server * - * @param handle The MySQL Monitor object + * @param handle The Monitor object * @param database The database to probe */ static void @@ -2534,7 +2534,7 @@ monitorMain(void *arg) } } - /* Do now the heartbeat replication set/get for MySQL Replication Consistency */ + /* Generate the replication heartbeat event by performing an update */ if (replication_heartbeat && root_master && (SERVER_IS_MASTER(root_master->server) || @@ -2563,7 +2563,8 @@ monitorMain(void *arg) // Do not auto-join servers on this monitor loop if a failover (or any other cluster modification) // has been performed, as server states have not been updated yet. It will happen next iteration. - if (handle->auto_rejoin && !failover_performed && cluster_can_be_joined(handle)) + if (!config_get_global_options()->passive && handle->auto_rejoin && + !failover_performed && cluster_can_be_joined(handle)) { // Check if any servers should be autojoined to the cluster ServerVector joinable_servers; @@ -2598,11 +2599,12 @@ monitorMain(void *arg) } /** - * Fetch a MySQL node by node_id + * Fetch a node by node_id * - * @param ptr The list of servers to monitor - * @param node_id The MySQL server_id to fetch - * @return The server with the required server_id + * @param ptr The list of servers to monitor + * @param node_id The server_id to fetch + * + * @return The server with the required server_id */ static MXS_MONITORED_SERVER * getServerByNodeId(MXS_MONITORED_SERVER *ptr, long node_id) @@ -2621,10 +2623,10 @@ getServerByNodeId(MXS_MONITORED_SERVER *ptr, long node_id) } /** - * Fetch a MySQL slave node from a node_id + * Fetch a slave node from a node_id * * @param ptr The list of servers to monitor - * @param node_id The MySQL server_id to fetch + * @param node_id The server_id to fetch * @param slave_down_setting Whether to accept or reject slaves which are down * @return The slave server of this node_id */ @@ -2899,9 +2901,8 @@ static void set_slave_heartbeat(MXS_MONITOR* mon, MXS_MONITORED_SERVER *database /******* * This function computes the replication tree - * from a set of MySQL Master/Slave monitored servers - * and returns the root server with SERVER_MASTER bit. - * The tree is computed even for servers in 'maintenance' mode. + * from a set of monitored servers and returns the root server with + * SERVER_MASTER bit. The tree is computed even for servers in 'maintenance' mode. * * @param handle The monitor handle * @param num_servers The number of servers monitored @@ -3938,7 +3939,7 @@ static bool query_one_row(MXS_MONITORED_SERVER *database, const char* query, uns if (columns != expected_cols) { mysql_free_result(result); - MXS_ERROR("Unexpected result for '%s'. Expected %d columns, got %d. MySQL Version: %s", + MXS_ERROR("Unexpected result for '%s'. Expected %d columns, got %d. Server version: %s", query, expected_cols, columns, database->server->version_string); } else @@ -4248,7 +4249,7 @@ static bool switchover_start_slave(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER* old } /** - * Get MySQL connection error strings from all the given servers, form one string. + * Get MariaDB connection error strings from all the given servers, form one string. * * @param slaves Servers with errors * @return Concatenated string. diff --git a/server/modules/routing/readwritesplit/readwritesplit.cc b/server/modules/routing/readwritesplit/readwritesplit.cc index 3a12ad460..75d530e27 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.cc +++ b/server/modules/routing/readwritesplit/readwritesplit.cc @@ -485,18 +485,7 @@ static bool route_stored_query(RWSplitSession *rses) if (!routeQuery((MXS_ROUTER*)rses->router, (MXS_ROUTER_SESSION*)rses, query_queue)) { rval = false; - char* sql = modutil_get_SQL(query_queue); - - if (sql) - { - MXS_ERROR("Routing query \"%s\" failed.", sql); - MXS_FREE(sql); - } - else - { - MXS_ERROR("Failed to route query."); - } - gwbuf_free(query_queue); + MXS_ERROR("Failed to route queued query."); } if (rses->query_queue == NULL)