hangs when Tee filter uses matching"
+add_test_executable(mxs501_tee_usedb.cpp mxs501_tee_usedb mxs501 LABELS tee REPL_BACKEND)
+
+# Open connection, execute 'change user', close connection in the loop
+add_test_executable(mxs548_short_session_change_user.cpp mxs548_short_session_change_user mxs548 LABELS MySQLProtocol REPL_BACKEND)
+
+# Playing with blocking and unblocking Master under load
+add_test_executable(mxs559_block_master.cpp mxs559_block_master mxs559 LABELS readwritesplit REPL_BACKEND)
+
+# Playing with blocking and unblocking nodes under INSERT load
+add_test_executable(mxs564_big_dump.cpp mxs564_big_dump galera_mxs564 LABELS readwritesplit readconnroute GALERA_BACKEND)
+
+# Executes simple queries from python script in the loop
+add_test_script(mxs585.py mxs585.py replication LABELS readwritesplit readconnroute UNSTABLE HEAVY REPL_BACKEND)
+
+# Simple transactions in the loop from python script with client SSL on
+add_test_script(mxs598.py mxs598.py ssl LABELS MySQLProtocol UNSTABLE HEAVY REPL_BACKEND)
+
+# Regression case for the bug "MaxScale fails to start silently if config file is not readable"
+add_test_executable(mxs621_unreadable_cnf.cpp mxs621_unreadable_cnf replication LABELS maxscale REPL_BACKEND)
+
+# playing with 'restart service' and restart Maxscale under load
+add_test_executable(mxs657_restart.cpp mxs657_restart replication LABELS maxscale HEAVY REPL_BACKEND)
+add_test_executable(mxs657_restart_service.cpp mxs657_restart_service replication LABELS maxscale REPL_BACKEND)
+
+# put cyrillic letters to the table and check from backend
+add_test_executable(mxs682_cyrillic.cpp mxs682_cyrillic replication LABELS maxscale LIGHT REPL_BACKEND)
+
+# put cyrillic letters to the table and check from backend (Galera backend)
+add_test_script(mxs682_cyrillic_galera mxs682_cyrillic galera LABELS maxscale GALERA_BACKEND)
+
+# Connect using different default database using user with database and table level grants
+add_test_executable(mxs716.cpp mxs716 replication LABELS MySQLAuth LIGHT REPL_BACKEND)
+
+# MaxScale configuration check functionality test (maxscale -c)
+add_test_executable(mxs722.cpp mxs722 mxs722 LABELS maxscale LIGHT REPL_BACKEND)
+
+# Test of 'maxadmin' user Unix accounts enable/disable
+add_test_executable(mxs729_maxadmin.cpp mxs729_maxadmin replication LABELS MaxAdminAuth LIGHT REPL_BACKEND)
+
+# Simple connect test in bash, checks that defined in cmd line DB is selected
+add_test_script(mxs791.sh mxs791.sh replication LABELS UNSTABLE HEAVY REPL_BACKEND)
+
+# Simple connect test in bash, checks that defined in cmd line DB is selected (Galera backend)
+add_test_script(mxs791_galera.sh mxs791_galera.sh galera LABELS UNSTABLE HEAVY GALERA_BACKEND)
+
+# Checks "Current no. of conns" maxadmin output after long blob inserting
+add_test_executable(mxs812_1.cpp mxs812_1 longblob LABELS readwritesplit REPL_BACKEND)
+
+# Checks "Current no. of conns" maxadmin output after long blob inserting
+add_test_executable(mxs812_2.cpp mxs812_2 longblob LABELS readwritesplit REPL_BACKEND)
+
+# Execute prepared statements while master is blocked, checks "Current no. of conns" after the test
+add_test_executable(mxs822_maxpasswd.cpp mxs822_maxpasswd maxpasswd LABELS maxscale REPL_BACKEND)
+
+# Do only SELECTS during time > wait_timeout and then do INSERT
+# This test will fail because the functionality hasn't been implemented
+add_test_executable(mxs827_write_timeout.cpp mxs827_write_timeout mxs827_write_timeout LABELS readwritesplit REPL_BACKEND)
+
+# Block and unblock first and second slaves and check that they are recovered
+add_test_executable(mxs874_slave_recovery.cpp mxs874_slave_recovery mxs874 LABELS readwritesplit REPL_BACKEND)
+
+# A set of dynamic configuration tests
+# Server removal test
+add_test_executable(mxs922_bad_server.cpp mxs922_bad_server mxs922 LABELS maxscale REPL_BACKEND)
+
+# Server creation test
+add_test_executable(mxs922_server.cpp mxs922_server mxs922_base LABELS maxscale REPL_BACKEND)
+
+# Monitor creation test
+add_test_executable(mxs922_monitor.cpp mxs922_monitor mxs922_base LABELS maxscale REPL_BACKEND)
+
+# Double creation of listeners, expect no crash
+add_test_executable(mxs922_double_listener.cpp mxs922_double_listener mxs922_base LABELS maxscale REPL_BACKEND)
+
+# Test persisting of configuration changes
+add_test_executable(mxs922_restart.cpp mxs922_restart mxs922 LABELS maxscale REPL_BACKEND)
+
+# Server scaling test
+add_test_executable(mxs922_scaling.cpp mxs922_scaling mxs922_base LABELS maxscale REPL_BACKEND)
+
+# Dynamic listener SSL test
+add_test_executable(mxs922_listener_ssl.cpp mxs922_listener_ssl mxs922_base LABELS maxscale REPL_BACKEND)
+
+# Test of MaxRows filter
+add_test_executable(mxs1071_maxrows.cpp mxs1071_maxrows maxrows LABELS maxrowsfilter REPL_BACKEND)
+
+# Test of Masking filter
+add_test_script(masking_mysqltest masking_mysqltest_driver.sh masking_mysqltest LABELS maskingfilter REPL_BACKEND)
+
+add_test_script(masking_user masking_user.sh masking_mysqltest LABELS maskingfilter REPL_BACKEND)
+
+# Test of Cache filter
+add_test_script(cache_basic cache_basic.sh cache_basic LABELS cachefilter REPL_BACKEND)
+
+# Set utf8mb4 in the backend and restart Maxscale
+add_test_executable(mxs951_utfmb4.cpp mxs951_utfmb4 replication LABELS REPL_BACKEND)
+
+# Execute given SQL through readwritesplit (with temporary tables usage)
+add_test_executable(mxs957.cpp mxs957 replication LABELS readwritesplit REPL_BACKEND)
+
+# Regression case for the bug "Defunct processes after maxscale have executed script during failover"
+add_test_executable(mxs1045.cpp mxs1045 mxs1045 LABELS maxscale REPL_BACKEND)
+
+# MXS-1123: connect_timeout setting causes frequent disconnects
+# https://jira.mariadb.org/browse/MXS-1123
+add_test_executable(mxs1123.cpp mxs1123 mxs1123 LABELS maxscale REPL_BACKEND)
+
+# 'namedserverfilter' test
+add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND)
+
+# Authentication error testing
+add_test_executable(no_password.cpp no_password replication LABELS MySQLAuth LIGHT REPL_BACKEND)
+
+# Open and immediatelly close a big number of connections
+add_test_executable(open_close_connections.cpp open_close_connections replication LABELS maxscale REPL_BACKEND)
+
+# Open and immediatelly close a big number of connections, ssl is in use
+#
+# The test is broken due to some problem in the connector. It crashes with a
+# double free error somewhere deep inside the connector/SSL libraries.
+#
+# add_test_script(open_close_connections_ssl open_close_connections ssl LABELS maxscale REPL_BACKEND)
+
+# Persistant connection test
+add_test_executable(pers_01.cpp pers_01 pers_01 LABELS maxscale REPL_BACKEND GALERA_BACKEND)
+
+# Test with persistant connections configured and big number iof opened connections ,expect no crash
+add_test_executable(pers_02.cpp pers_02 pers_01 LABELS maxscale REPL_BACKEND GALERA_BACKEND)
+
+# Check if prepared statement works via Maxscale (via RWSplit)
+add_test_executable(prepared_statement.cpp prepared_statement replication LABELS readwritesplit LIGHT REPL_BACKEND)
+
+# Connect to ReadConn in master mode and check if there is only one backend connection to master
+add_test_executable(readconnrouter_master.cpp readconnrouter_master replication LABELS readconnroute LIGHT REPL_BACKEND)
+
+# Creates 100 connections to ReadConn in slave mode and check if connections are distributed among all slaves
+add_test_executable(readconnrouter_slave.cpp readconnrouter_slave replication LABELS readconnroute LIGHT REPL_BACKEND)
+
+# Regex filter test
+add_test_executable(regexfilter1.cpp regexfilter1 regexfilter1 LABELS regexfilter LIGHT REPL_BACKEND)
+
+# check that Maxscale is reacting correctly on ctrc+c signal and termination does not take ages
+add_test_script(run_ctrl_c.sh run_ctrl_c.sh replication LABELS maxscale LIGHT REPL_BACKEND)
+
+# run a set of queries in the loop (see setmix.sql) using Perl client
+add_test_script(run_session_hang.sh run_session_hang.sh replication LABELS readwritesplit REPL_BACKEND)
+
+# Checks changes of COM_SELECT and COM_INSERT after queris to check if RWSplit sends queries to master or to slave depending on if it is write or read only query
+add_test_executable(rw_select_insert.cpp rw_select_insert replication LABELS readwritesplit REPL_BACKEND)
+
+# Checks connections are distributed equaly among backends
+add_test_executable(rwsplit_conn_num.cpp rwsplit_conn_num repl_lgc LABELS readwritesplit LIGHT REPL_BACKEND)
+
+# Check that there is one connection to Master and one connection to one of slaves
+add_test_executable(rwsplit_connect.cpp rwsplit_connect replication LABELS readwritesplit LIGHT REPL_BACKEND)
+
+# Test of the read-only mode for readwritesplit when master fails (blocked)
+add_test_executable(rwsplit_readonly.cpp rwsplit_readonly rwsplit_readonly LABELS readwritesplit REPL_BACKEND)
+
+# Test of the read-only mode for readwritesplit when master fails (blocked), under load
+add_test_executable(rwsplit_readonly_stress.cpp rwsplit_readonly_stress rwsplit_readonly LABELS readwritesplit HEAVY REPL_BACKEND)
+
+# Test readwritesplit multi-statement handling
+add_test_executable(rwsplit_multi_stmt.cpp rwsplit_multi_stmt rwsplit_multi_stmt LABELS readwritesplit REPL_BACKEND)
+
+# Test readwritesplit multi-statement handling
+add_test_executable(rwsplit_read_only_trx.cpp rwsplit_read_only_trx rwsplit_read_only_trx LABELS readwritesplit REPL_BACKEND)
+
+# Test replication-manager with MaxScale
+add_test_executable(replication_manager.cpp replication_manager replication_manager LABELS maxscale REPL_BACKEND)
+add_test_executable_notest(replication_manager_2nodes.cpp replication_manager_2nodes replication_manager_2nodes LABELS maxscale REPL_BACKEND)
+add_test_executable_notest(replication_manager_3nodes.cpp replication_manager_3nodes replication_manager_3nodes LABELS maxscale REPL_BACKEND)
+
+# Schemarouter duplicate database detection test: create DB on all nodes and then try query againt schema router
+add_test_executable(schemarouter_duplicate_db.cpp schemarouter_duplicate_db schemarouter_duplicate_db LABELS schemarouter REPL_BACKEND)
+
+# Test of external script execution
+add_test_executable(script.cpp script script LABELS maxscale REPL_BACKEND)
+
+# Check if 'weightby' parameter works
+add_test_executable(server_weight.cpp server_weight galera.weight LABELS readwritesplit readconnroute LIGHT GALERA_BACKEND)
+
+# Executes a lot of session commands with "disable_sescmd_history=true" and check that memory consumption is not increasing
+add_test_executable(ses_bigmem.cpp ses_bigmem no_ses_cmd_store LABELS readwritesplit REPL_BACKEND)
+
+# test for 'max_sescmd_history' and 'connection_timeout' parameters
+add_test_executable(session_limits.cpp session_limits session_limits LABELS readwritesplit REPL_BACKEND)
+
+# Test of schema router
+add_test_executable(sharding.cpp sharding sharding LABELS schemarouter BREAKS_REPL)
+
+# MXS-1160: LOAD DATA LOCAL INFILE with schemarouter
+add_test_executable(sharding_load_data.cpp sharding_load_data sharding LABELS schemarouter BREAKS_REPL)
+
+# Do short sessions (open conn, short query, close conn) in the loop
+add_test_executable(short_sessions.cpp short_sessions replication LABELS readwritesplit readconnroute REPL_BACKEND)
+
+# Do short sessions (open conn, short query, close conn) in the loop, client ssl is ON
+add_test_script(short_sessions_ssl short_sessions ssl LABELS readwritesplit readconnroute REPL_BACKEND)
+
+# Regression case for crash if maxadmin 'show monitors' command is issued, but no monitor is not running
+add_test_executable(show_monitor_crash.cpp show_monitor_crash show_monitor_crash LABELS maxscale)
+
+# Check how Maxscale works in case of one slave failure, only one slave is configured
+add_test_executable(slave_failover.cpp slave_failover replication.one_slave LABELS readwritesplit REPL_BACKEND)
+
+# Execute queries of different size, check data is the same when accessing via Maxscale and directly to backend
+add_test_executable(sql_queries.cpp sql_queries replication LABELS readwritesplit REPL_BACKEND)
+
+# Execute queries of different size, check data is the same when accessing via Maxscale and directly to backend, one persistant connection configured
+add_test_script(sql_queries_pers1 sql_queries sql_queries_pers1 LABELS maxscale readwritesplit HEAVY REPL_BACKEND)
+
+# Execute queries of different size, check data is the same when accessing via Maxscale and directly to backend, 10 persistant connections configured
+add_test_script(sql_queries_pers10 sql_queries sql_queries_pers10 LABELS maxscale readwritesplit HEAVY REPL_BACKEND)
+
+# Execute queries of different size, check data is the same when accessing via Maxscale and directly to backend, client ssl is ON
+add_test_script(ssl sql_queries ssl LABELS maxscale readwritesplit REPL_BACKEND)
+
+# Check load balancing, client ssl is ON
+add_test_script(ssl_load load_balancing ssl_load LABELS maxscale readwritesplit REPL_BACKEND)
+
+# Check load balancing, client ssl is ON, Galera backend
+add_test_script(ssl_load_galera load_balancing_galera ssl_load_galera LABELS maxscale readwritesplit GALERA_BACKEND)
+
+# Testing slaves who have lost their master and how MaxScale works with them
+add_test_executable(stale_slaves.cpp stale_slaves replication LABELS mysqlmon REPL_BACKEND)
+
+# Run sysbech test and block one slave during test execution
+add_test_executable(sysbench_kill_slave.cpp sysbench_kill_slave replication LABELS UNSTABLE HEAVY REPL_BACKEND)
+
+# Check temporal tables commands functionality
+add_test_executable(temporal_tables.cpp temporal_tables replication LABELS readwritesplit REPL_BACKEND)
+
+# Test routing hints
+add_test_executable(test_hints.cpp test_hints hints2 LABELS hintfilter LIGHT REPL_BACKEND)
+
+# Binlogrouter tests, these heavily alter the replication so they are run last
+add_test_executable(avro.cpp avro avro LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
+
+# Test avrorouter file compression
+add_test_script(avro_compression avro avro_compression LABELS avrorouter binlogrouter LIGHT BREAKS_REPL)
+
+# In the binlog router setup stop Master and promote one of the Slaves to be new Master
+add_test_executable(binlog_change_master.cpp binlog_change_master setup_binlog_tx_safe LABELS binlogrouter BREAKS_REPL)
+
+# trying to start binlog setup with incomplete Maxscale.cnf
+add_test_executable(binlog_incompl.cpp binlog_incompl binlog_incompl LABELS binlogrouter BREAKS_REPL)
+
+# configure binlog router setup, execute queries and transactions, check data; install semysync plugin, router options semisync=1,transaction_safety=1
+add_test_executable(binlog_semisync.cpp binlog_semisync setup_binlog_semisync LABELS binlogrouter HEAVY BREAKS_REPL)
+
+# configure binlog router setup, execute queries and transactions, check data; install semysync plugin, router options semisync=0,transaction_safety=0
+add_test_script(binlog_semisync_txs0_ss0 binlog_semisync setup_binlog_semisync_txs0_ss0 LABELS binlogrouter HEAVY BREAKS_REPL)
+
+# configure binlog router setup, execute queries and transactions, check data; install semysync plugin, router options semisync=0,transaction_safety=1
+add_test_script(binlog_semisync_txs0_ss1 binlog_semisync setup_binlog_semisync_txs0_ss1 LABELS binlogrouter HEAVY BREAKS_REPL)
+
+# configure binlog router setup, execute queries and transactions, check data; install semysync plugin, router options semisync=1,transaction_safety=0
+add_test_script(binlog_semisync_txs1_ss0 binlog_semisync setup_binlog_semisync_txs1_ss0 LABELS binlogrouter HEAVY BREAKS_REPL)
+
+set_tests_properties(binlog_semisync PROPERTIES TIMEOUT 3600)
+set_tests_properties(binlog_semisync_txs0_ss0 PROPERTIES TIMEOUT 3600)
+set_tests_properties(binlog_semisync_txs0_ss1 PROPERTIES TIMEOUT 3600)
+set_tests_properties(binlog_semisync_txs1_ss0 PROPERTIES TIMEOUT 3600)
+
+#
+# The encryption tests don't work as they require the file key management plugin
+#
+# Binlog encription test (aes_cbr encryption)
+#add_test_executable(mxs1073_binlog_enc.cpp mxs1073_binlog_enc binlog_enc_aes_cbc LABELS binlogrouter 10.1 BREAKS_REPL)
+# Binlog encription test (aes_ctr encryption)
+#add_test_script(mxs1073_binlog_enc_aes_ctr mxs1073_binlog_enc binlog_enc_aes_ctr LABELS binlogrouter 10.1 BREAKS_REPL)
+
+# Test of CDC protocol (avro listener)
+add_test_executable(cdc_client.cpp cdc_client avro LABELS avrorouter binlogrouter BREAKS_REPL)
+
+# Tries INSERTs with size close to 0x0ffffff * N (with binlog backend)
+add_test_executable(different_size_binlog.cpp different_size_binlog setup_binlog LABELS binlogrouter HEAVY BREAKS_REPL)
+
+# Try to configure binlog router to use wrong password for Master and check 'slave status' on binlog
+add_test_executable(mxs781_binlog_wrong_passwrd.cpp mxs781_binlog_wrong_passwrd setup_binlog LABELS binlogrouter BREAKS_REPL)
+
+# Regression case for crash if long host name is used for binlog router (in 'change master to ...')
+add_test_executable(mxs813_long_hostname.cpp mxs813_long_hostname setup_binlog LABELS binlogrouter BREAKS_REPL)
+
+# configure binlog rouer setup, execute queries and transactions, check data;
+add_test_executable(setup_binlog.cpp setup_binlog setup_binlog LABELS binlogrouter BREAKS_REPL)
+
+# configure binlog rouer setup, execute queries and transactions, check data; install semysync plugin, backends started with --binlog-checksum=CRC32 option
+# disabled because it is included into setup_binlog test, separate test was created for debugging
+# add_test_executable(setup_binlog_crc_32.cpp setup_binlog_crc_32 setup_binlog LABELS binlogrouter BREAKS_REPL)
+
+# configure binlog rouer setup, execute queries and transactions, check data; install semysync plugin, backends started with --binlog-checksum=NONE option
+# disabled because it is included into setup_binlog test, separate test was created for debugging
+# add_test_executable(setup_binlog_crc_none.cpp setup_binlog_crc_none setup_binlog LABELS binlogrouter LIGHT BREAKS_REPL)
+
+
+# Creates KDC and tries authrization via GSSAPI (both client and backend)
+# works only with yum-based distributions
+# 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)
+
+
+# enable after fixing MXS-419
+# add_test_executable(mxs419_lots_of_connections.cpp mxs419_lots_of_connections replication LABELS REPL_BACKEND)
+
+# TODO: Alter the user_cache test
+# add_test_executable(user_cache.cpp user_cache user_cache LABELS REPL_BACKEND)
+
+# https://mariadb.atlassian.net/browse/MXS-576 - it is possible to set negative value for
+# 'persistpoolmax' without any warning
+#add_test_executable(bad_pers.cpp bad_pers bad_pers LABELS REPL_BACKEND)
+
+# Test Aurora RDS monitor
+add_test_executable(auroramon.cpp auroramon auroramon LABELS HEAVY EXTERNAL_BACKEND)
+
+# Disabled for the time being
+# add_test_executable(gatekeeper.cpp gatekeeper gatekeeper LABELS gatekeeper)
+
+# not implemented, just template
+#add_test_executable(rw_galera_select_insert.cpp rw_galera_select_insert galera LABELS readwritesplit GALERA_BACKEND)
+
+# a tool to delete RDS Aurora cluster
+add_test_executable_notest(delete_rds.cpp delete_rds replication LABELS EXTERN_BACKEND)
+
+# a tool to create RDS Aurora cluster
+add_test_executable_notest(create_rds.cpp create_rds replication LABELS EXTERN_BACKEND)
+
+# start sysbench ageints RWSplit for infinite execution
+add_test_executable_notest(long_sysbench.cpp long_sysbench replication LABELS readwritesplit REPL_BACKEND)
+
+configure_file(templates.h.in templates.h @ONLY)
+
+include(CTest)
diff --git a/maxscale-system-test/CTestConfig.cmake b/maxscale-system-test/CTestConfig.cmake
new file mode 100644
index 000000000..1b96cfda2
--- /dev/null
+++ b/maxscale-system-test/CTestConfig.cmake
@@ -0,0 +1,15 @@
+## This file should be placed in the root directory of your project.
+## Then modify the CMakeLists.txt file in the root directory of your
+## project to incorporate the testing dashboard.
+##
+## # The following are required to submit to the CDash dashboard:
+## ENABLE_TESTING()
+## INCLUDE(CTest)
+
+set(CTEST_PROJECT_NAME "MaxScale")
+set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "jenkins.engskysql.com")
+set(CTEST_DROP_LOCATION "/CDash/submit.php?project=MaxScale")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/maxscale-system-test/ENV_SETUP.md b/maxscale-system-test/ENV_SETUP.md
new file mode 100644
index 000000000..700d648e3
--- /dev/null
+++ b/maxscale-system-test/ENV_SETUP.md
@@ -0,0 +1,289 @@
+# Build and test environment setup
+
+### Full build and test environment setup
+
+
+# install ruby
+sudo apt-get install ruby
+
+# install all needed libraries
+sudo apt-get install libxslt-dev libxml2-dev libvirt-dev zlib1g-dev
+
+# install vagrant
+# it is also possible to install Vagrant from distribution repository, but in case of problems please use 1.7.2
+wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.deb
+sudo dpkg -i vagrant_1.7.2_x86_64.deb
+
+# install Vagrant plugins
+vagrant plugin install vagrant-aws vagrant-libvirt vagrant-mutate
+
+# get MDBCI, build scripts, descriptions of MDBCI boxes and keys from GitHub
+git clone https://github.com/OSLL/mdbci.git
+git clone git@github.com:mariadb-corporation/mdbci-repository-config.git
+git clone git@github.com:mariadb-corporation/build-scripts-vagrant.git
+git clone git@github.com:mariadb-corporation/mdbci-boxes
+
+# Copy scripts and boxes to proper places
+mv build-scripts-vagrant build-scripts
+scp -r mdbci-boxes/* mdbci/
+
+# set proper access rights for ssh keys (for ppc64 machines)
+chmod 400 mdbci/KEYS/*
+
+# install all the stuff for test package build
+sudo apt-get install cmake gcc g++ libssl-dev
+sudo apt-get install mariadb-client shellcheck
+
+# install MariaDB development library
+sudo apt-get install libmariadbclient-dev
+# Ubuntu repos can contain the sa,e package with different name 'libmariadb-client-lgpl-dev'
+# but it can not be used to build maxscale-system-test; please use mariadb.org repositories
+# https://downloads.mariadb.org/mariadb/repositories/
+# Do not forget to remove all other MariaDB and MySQL packages!
+
+# install qemu (more info https://en.wikibooks.org/wiki/QEMU/Installing_QEMU)
+sudo apt-get install qemu qemu-kvm libvirt-bin
+
+# install virt-manager (if you prefer UI)
+sudo apt-get install virt-manager
+
+# install docker (if needed) - see https://docs.docker.com/engine/installation/
+
+# if cmake from distribution repository is too old it is recommended to build it from latest sources
+wget https://cmake.org/files/v3.4/cmake-3.4.1.tar.gz # replace 3.4.1 to latest version
+tar xzvf cmake-3.4.1.tar.gz
+cd cmake-3.4.1
+./bootstrap
+make
+sudo make install
+cd
+
+# sysbench 0.5 should be in sysbench_deb7 directory; it can be built from source:
+git clone https://github.com/akopytov/sysbench.git
+cd sysbench
+./autogen.sh
+./configure
+make
+cd ..
+mv sysbench sysbench_deb7
+
+# for OVH servers it is needed to move 'docker' and 'libvirt' working directories to /home
+# (replace 'vagrant' to your home directory name)
+cd /var/lib/
+sudo mv docker /home/vagrant/
+sudo ln -s /home/vagrant/docker docker
+cd libvirt
+sudo mv images /home/vagrant/
+sudo ln -s /home/vagrant/images images
+cd
+
+# (HACK) in case of problem with building sysbench:
+scp -r vagrant@maxscale-jenkins.mariadb.com:/home/vagrant/sysbench_deb7 .
+
+# (HACK) in case of problem with 'dummy' box (problem is caused by MDBCI bug):
+scp -r vagrant@maxscale-jenkins.mariadb.com:/home/vagrant/.vagrant.d/boxes/dummy .vagrant.d/boxes/
+
+# MariaDBManager-GPG* files are needed for Maxscale builds in the home directory
+
+# put AWS keys to aws-config.yml (see https://github.com/OSLL/mdbci/blob/master/aws-config.yml.template)
+
+# add curent user to the group 'libvirtd'
+sudo usermod -a -G user_name libvirtd
+
+# start libvirt default pool
+virsh pool-start default
+
+
+
+### Setup VMs manually
+
+#### Empty virtual machine
+
+Following template can be used to create empty VM (for qemu machines):
+
+{
+ "cookbook_path" : "../recipes/cookbooks/",
+ "build" :
+ {
+ "hostname" : "default",
+ "box" : "###box###",
+ "product" : {
+ "name" : "packages"
+ }
+ }
+}
+
+
+for AWS machines:
+
+{
+ "cookbook_path" : "../recipes/cookbooks/",
+ "aws_config" : "../aws-config.yml",
+ "build" :
+ {
+ "hostname" : "build",
+ "box" : "###box###"
+ }
+}
+
+
+Following boxes are availabe:
+* qemu: debian_7.5_libvirt, ubuntu_trusty_libvirt, centos_7.0_libvirt, centos_6.5_libvirt
+* AWS: rhel5, rhel6, rhel7, sles11, sles12, fedora20, fedora21, fediora22, ubuntu_wily, ubuntu_vivid, centos7, deb_jessie
+
+#### Maxscale and backend machines creation
+
+* Generation of Maxscale repository description
+It is necessary to generate descriptions of MariaDB and Maxscale repositories before bringin up Maxscale machine with Vagrant
+
+export ci_url="http://my_repository_site.com/repostory/"
+~/mdbci-repository-config/generate_all.sh $repo_dir
+~/mdbci-repository-config/maxscale-ci.sh $target $repo_dir
+
+where
+
+$repo_dir - directory where repository descriptions will be created
+$target - directory with MaxScale packages in the repositoy
+
+example:
+
+export ci_url="http://max-tst-01.mariadb.com/ci-repository/"
+~/mdbci-repository-config/generate_all.sh repo.d
+~/mdbci-repository-config/maxscale-ci.sh develop repo.d
+
+More information can be found in the [MDBCI documentation](https://github.com/OSLL/mdbci#repod-files) and in the [mdbci-repository-config documentaion](https://github.com/mariadb-corporation/mdbci-repository-config#mdbci-repository-config)
+
+* Preparing configuration description
+Virtual machines should be described in JSON format. Example template can be found in the [build-scripts package](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/test/template.libvirt.json).
+
+MariaDB machine description example:
+
+"node0" :
+ {
+ "hostname" : "node0",
+ "box" : "centos_7.0_libvirt",
+ "product" : {
+ "name": "mariadb",
+ "version": "10.0",
+ "cnf_template" : "server1.cnf",
+ "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf"
+ }
+
+ }
+
+
+"cnf_template" defines .cnf file which will be places into MariaDB machine. [build-scripts package](https://github.com/mariadb-corporation/build-scripts-vagrant/tree/master/test-setup-scripts/cnf) contains examples of .cnf files.
+
+MariaDB Galera machine description example:
+
+"galera0" :
+ {
+ "hostname" : "galera0",
+ "box" : "centos_7.0_libvirt",
+ "product" : {
+ "name": "galera",
+ "version": "10.0",
+ "cnf_template" : "galera_server1.cnf",
+ "cnf_template_path": "~/build-scripts/test-setup-scripts/cnf"
+ }
+ }
+
+
+For Galera machines MDBCI automatically puts following information into .cnf file:
+
+|field|description|
+|------|----|
+|###NODE-ADDRESS###|IP address of the node (for AWS - private IP)|
+|###NODE-NAME###|Replaces by node name ("node0" in this example)|
+|###GALERA-LIB-PATH###|Path to the Galera library file (.so file)|
+
+Example of Maxscale machine description:
+
+"maxscale" :
+ {
+ "hostname" : "maxscale",
+ "box" : "centos_7.0_libvirt",
+ "product" : {
+ "name": "maxscale"
+ }
+
+ }
+
+
+#### Generation configuration and bringing machines up
+
+After creation machines description JSON two steps are needed.
+
+1. Generate configuration
+
+./mdbci --override --template $template_name.json --repo-dir $repo_dir generate $name
+
+
+where
+
+|variable|description|
+|----|----|
+|$template_name|name of machines descripiton JSON file|
+|$repo_dir|directory with repositories description generated by mdbci-repository-config (repo.d)|
+|$name|name of test configuration; will be used as directory name for Vagrant files|
+
+2. Bringing machines up
+
+./mdbci up $name
+
+
+#### Configuring DB users
+
+Automatic DB users is not implemented yet, so it have to be done manually. See [setup_repl.sh](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/test-setup-scripts/setup_repl.sh) and [setup_galera.sh](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/test-setup-scripts/galera/setup_galera.sh) for details.
+
+Any test from 'maxscale-system-test' checks Master/Slave and Galera configurations and restores them if they are broken, but it works only if DB users are created.
+
+TODO: add it into 'maxscale-system-test'
+
+### Access VMs
+
+MDBCI provides a number of commands to get information about running vrtial machines. See [MDBCI documentation](https://github.com/OSLL/mdbci#mdbci-syntax) for details.
+
+[set_env_vagrant.sh script](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/test/set_env_vagrant.sh) defines environmental variables needed by 'maxscale-system-test'. The same variables can be used to access VMs manually.
+
+Script have to be executed fro 'mbdci' directory. Do not forget '.':
+
+cd ~/mdbci/
+. ../build-scripts/test/set_env_vagrant.sh $name
+
+
+After it virual machines can be accessed via ssh, for example:
+
+ssh -i $maxscale_sshkey $maxscale_access_user@$maxscale_IP
+
+
+Another way is to use 'vagrant ssh':
+
+cd ~/mdbci/$name/
+vagrant ssh <node_name>
+
+
+MDBCI can give IP address, path to ssh key:
+
+./mdbci show network <configuration_name>/<node_name> --silent
+./mdbci show keyfile <configuration_name>/<node_name> --silent
+./mdbci ssh --command 'whoami' <configuration_name>/<node_name> --silent
+
+
+Node name for build machine is 'build'
+
+Nodes names for typical test setup are node0, ..., node3, galera0, ..., galera3, maxscale
+
+Example:
+
+./mdbci show network centos6_vm01/build --silent
+./mdbci show keyfile centos6_vm01/build --silent
+./mdbci ssh --command 'whoami' centos6_vm01/build --silent
+
+
+### Destroying configuration
+
+
+cd ~/mdbci/$name
+vagrant destroy -f
+
diff --git a/maxscale-system-test/HOW_TO_WRITE_TEST.md b/maxscale-system-test/HOW_TO_WRITE_TEST.md
new file mode 100644
index 000000000..74c9d7dc0
--- /dev/null
+++ b/maxscale-system-test/HOW_TO_WRITE_TEST.md
@@ -0,0 +1,204 @@
+# Creating a test case
+
+This document describes basic principles of test case creation and provides list of basic usefull functions and properties.
+For detailed function and properties description and thier full list please see documetation generated by Doxygen.
+
+## Test case basics
+
+For every test case following should be created:
+- test executable
+- record in the 'templates' file
+- Maxscale configuration template (if test requires special Maxscale configuration)
+- [CMakeLists.txt](CMakeLists.txt) record:
+ - add_test_executable( )
+ - 'set_tests_properties' if test should be added to the group or bigger timeout should be defined (> default 1800s)
+
+## 'templates' file
+
+'templates' file contains information about Maxscale configuration template for every test in plain text format:
+
+\ \
+
+Template itself should be:
+
+cnf/maxscale.cnf.template.\
+
+## Maxscale configuration template
+
+All templates are in cnf/ directory:
+
+cnf/maxscale.cnf.template.\
+
+Template can contain following varables:
+
+|Variable|Maeaning|
+|--------|--------|
+|###threads###| Number of Maxscale treads|
+|###node_server_IP_N###|IP of Master/Slave node N|
+|###node_server_port_N###|port of Master/Slave node N|
+|###galera_server_IP_N###|IP of Galera node N|
+|###galera_server_port_N###|port of Galera node N|
+
+## Test creation principles
+
+* start from initializing of an object of TestConnections class
+* set timeout before every operation which can got stuck, do not forget to disable timeout before long sleep()
+* use TestConnections::tprintf function to print test log
+* use TestConnections::add_result() to idicate test failure and print explanation message
+* execute TestConnections::copy_all_logs at the end of test
+* return TestConnections::global_result value
+* do not leave any node blocked by firewall
+
+## Class TestConnections
+
+This class contains all information about Maxscale node and about all backend nodes as well as a set of functions
+to handle Maxscale and backends, interact with Maxscale routers and Maxadmin.
+Here is only list of main functions, for all details see Doxygen comments in [testconnections.h](testconnections.h)
+
+Currently two backend sets are supported (represented by Mariadb_nodes class objects): 'repl' and 'galera'
+- contains all info and operations for Master/Slave and Galera setups
+(see Doxygen comments in [mariadb_nodes.h](mariadb_nodes.h) )
+
+It is assumed that following routers+listers are configured
+
+|Router|Port|
+|------|----|
+|RWSplit|4006|
+|ReadConn master|4008|
+|ReadConn Slave|4009|
+|binlog|5306|
+|test case -specific|4016|
+
+### Most important fuctions and variables
+
+Please check Doxygen comments for details
+
+#### TestConnections(int argc, char *argv[]);
+
+* reads all information from environmental variables
+* checks backends, if broken - does one attempt to restore
+* create maxscale.cnf out of template and copy it to Maxscale node
+* create needed directories, set access righs for them, cleanup logs, coredumps
+* start Maxscale
+* initialize internal structures
+
+#### Timeout functions
+
+int set_timeout(int timeout_seconds)
+stop_timeout()
+
+If after set_timeout() a new call of set_timeout() or stop_timeout() is not done the test execution terminated,
+logs from Maxscale are copied to host.
+
+#### Open connection functions
+|Function|Short description|
+|----|---|
+| int connect_maxscale();
int connect_rwsplit();
int connect_readconn_master();
int connect_maxscale_slave();|store MYSQL handler in TestConnections object (only one cnnection can be created by them, second call leads to MYSQL handler leak)|
+|MYSQL * open_rwsplit_connection()
MYSQL * open_readconn_master_connection()
MYSQL * open_readconn_slave_connection() |returns MYSQL handler (can be used to create a number of connections to each router)|
+| int create_connections(int conn_N) |- open and then close N connections to each router|
+
+A number of useful wrappers for mysql_real_connect() are not included into TestConnections class, but
+they are availve from [mariadb_func.h](mariadb_func.h)
+
+#### Backend check and setup functions
+|Function|Short description|
+|----|---|
+|start_replication()|Configure nodes from 'repl' object as Master/Slave|
+|start_galera()|Configure nodes from 'galera'|
+|start_binlog()|Configure nodes from 'repl' in following way: node0 - Master, node1 - slave of node0, all others - slaves of Maxscale binlog router|
+|start_mm()|Configure nodes from 'repl' in multimuster setup|
+
+#### Result reporting functions
+|Function|Short description|
+|----|---|
+|add_result()|failure printing, increase global_result|
+|tprint()| printing with timestamp|
+|copy_logs()|copy Maxscale log, maxscale.cnf file and core dump from Maxscale machine to current directory|
+
+#### Different checks functions
+|Function|Short description|
+|----|---|
+|try_query()|try SQL query and print error message in case of failure, increase global_result|
+|check_t1_table()|check if t1 present in give DB|
+|test_maxscale_connections|check status of connections to RWSplit, ReadConn master, ReadConn slave routers|
+|check_maxscale_alive()|Check if RWSplit, ReadConn master, ReadConn slave routers are alive|
+|check_log_err()|Check Maxscale log for presence of absence of specific string|
+|find_connected_slave|find first slave that have connections from Maxscale|
+
+#### Maxscale machine control functions
+|Function|Short description|
+|----|---|
+|start_maxscale()||
+|stop_maxscale()||
+|restart_maxscale()||
+|execute_ssh_maxscale()|execute command on Maxscale node via ssh|
+
+#### Properties
+|Name|Short description|Corresponding env variable|
+|----|-----|----|
+|global_result|0 if there is not single failure during the test| - |
+|repl|Mariadb_nodes object for Master/Slave nodes| - |
+|galera|Mariadb_nodes object for Galera nodes| - |
+|smoke|do short tests if TRUE|smoke|
+|maxscale_IP|IP address of Maxscale machine|maxscale_IP|
+|maxscale_user|DB user name to access via Maxscale|maxscale_user|
+|maxscale_password|password for MaxscaleDB user|maxscale_password|
+|maxadmin_password|password for MaxAdmin interface (user name is hard coded 'admin')|maxadmin_password|
+|conn_rwsplit|MYSQL handler of connections to RWSplit router| - |
+|conn_master|MYSQL handler of connections to ReadConn router in master mode| - |
+|conn_slave|MYSQL handler of connections to ReadConn router in master mode| - |
+
+### Mariadb_nodes class
+
+#### Master/Slave and Galera setup and check
+|Function|Short description|
+|----|---|
+|start_replication()|Configure nodes from 'repl' object as Master/Slave|
+|start_galera()|Configure nodes from 'galera'|
+|set_slave()|execute CHANGE MASTER TO agains the node|
+|check_replication()|Check if 'repl' nodes are properly configured as Master/Slave|
+|check_galera()|Check if 'galera' nodes are are properly configured as Galera cluster|
+|change_master|Make another node to be a master|
+
+#### Connections functions
+|Function|Short description|
+|----|---|
+|connect()|open connections to all nodes, store MYSQL handlers in internal variable, second call leads to handlers leak|
+|close_connections()|close connections to all nodes|
+
+#### Nodes control functions
+|Function|Short description|
+|----|---|
+|block_node()|block MariaDB server on the node by firawall|
+|unblock_node()|unblock MariaDB server on the node by firawall|
+|unblock_all_nodes()|unblock MariaDB server on all nodes by firawall|
+|stop_node()|stop MariaDB server on the node|
+|start node()|start MariaDB server on the node|
+|restart_node()|stop and restart MariaDB server on the node|
+|check_node()|check if MariaDB server on the node is alive|
+|check_and_restart_node()|check if MariaDB server on the node is alive and restart it if it is not alive|
+|stop_nodes()|stop MariaDB server on all nodes|
+|ssh_node()|Execute command on the node via ssh, return error code|
+|ssh_node_output()|Same as ssh_nodE(), but return command output|
+|flush_hosts()|Execute 'mysqladmin flush-hosts' on all nodes|
+|execute_query_all_nodes()|Execute same query on all nodes|
+
+#### Properties
+|Name|Short description|Corresponding env variable|
+|----|-----|----|
+|N|Number of nodes|node_N
galera_N|
+|user_name|DB user name|node_user
galera_user|
+|password|password for DB user|node_password
galera_password|
+|IP[ ]|IP address of the node|node_XXX
galera_XXX|
+|IP_private[ ]|private IP of the node (for AWS nodes)|node_private_XXX
galera_private_XXX|
+|port[ ]|MariaDB port for the node|node_port_XXX
galera_port_XXX|
+|nodes[ ]|MYSQL handler| - |
+
+### Maxadmin operations functions
+[maxadmin_operations.h](maxadmin_operations.h) contains fuctions to communicate to Maxscale via MaxAdmin interface
+
+|Function|Short description|
+|----|---|
+|execute_maxadmin_command()|send MaxAdmin command to Maxscale|
+|execute_maxadmin_command_print()|send MaxAdmin command to Maxscale and print reply|
+|get_maxadmin_param()|send MaxAdmin command to Maxscale and try to find the value of given parameter in output|
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/select_for_var_set.result.template b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/select_for_var_set.result.template
new file mode 100644
index 000000000..477535acd
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/select_for_var_set.result.template
@@ -0,0 +1,6 @@
+BEGIN;
+SELECT (@@server_id) INTO @a;
+SELECT @a;
+@a
+####server_id####
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/set_autocommit_disabled.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/set_autocommit_disabled.result
new file mode 100644
index 000000000..ddf4d887b
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/set_autocommit_disabled.result
@@ -0,0 +1,8 @@
+USE test;
+drop table if exists t1;
+create table t1 (id integer);
+set autocommit=0;
+begin;
+insert into t1 values(1);
+commit;
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_after_autocommit_disabled.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_after_autocommit_disabled.result
new file mode 100644
index 000000000..db781d358
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_after_autocommit_disabled.result
@@ -0,0 +1,4 @@
+USE test;
+SELECT IF(@@server_id <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1.result
new file mode 100644
index 000000000..d3bb23c5b
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1.result
@@ -0,0 +1,9 @@
+USE test;
+drop table if exists t1;
+create table t1 (id integer);
+set autocommit=0;
+insert into t1 values(1);
+select count(*) from t1;
+count(*)
+1
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1b.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1b.result
new file mode 100644
index 000000000..3276acad0
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled1b.result
@@ -0,0 +1,9 @@
+USE test;
+drop table if exists t1;
+create table t1 (id integer);
+set autocommit=OFF;
+insert into t1 values(1);
+select count(*) from t1;
+count(*)
+1
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled2.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled2.result
new file mode 100644
index 000000000..05a3e5f0e
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled2.result
@@ -0,0 +1,11 @@
+USE test;
+drop table if exists t1;
+create table t1 (id integer);
+set autocommit=0;
+begin;
+insert into t1 values(1);
+commit;
+select count(*) from t1;
+count(*)
+1
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled3.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled3.result
new file mode 100644
index 000000000..05a3e5f0e
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_autocommit_disabled3.result
@@ -0,0 +1,11 @@
+USE test;
+drop table if exists t1;
+create table t1 (id integer);
+set autocommit=0;
+begin;
+insert into t1 values(1);
+commit;
+select count(*) from t1;
+count(*)
+1
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit1.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit1.result
new file mode 100644
index 000000000..281a2f2ad
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit1.result
@@ -0,0 +1,11 @@
+USE test;
+DROP DATABASE If EXISTS FOO;
+SET autocommit=1;
+BEGIN;
+CREATE DATABASE FOO;
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP DATABASE FOO;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit2.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit2.result
new file mode 100644
index 000000000..0f3187049
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit2.result
@@ -0,0 +1,17 @@
+USE test;
+DROP TABLE IF EXISTS T1;
+DROP EVENT IF EXISTS myevent;
+SET autocommit=1;
+BEGIN;
+CREATE TABLE T1 (id integer);
+CREATE EVENT myevent
+ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
+DO
+UPDATE t1 SET id = id + 1;
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP TABLE T1;
+DROP EVENT myevent;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit3.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit3.result
new file mode 100644
index 000000000..7281c8e54
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit3.result
@@ -0,0 +1,11 @@
+USE test;
+DROP TABLE IF EXISTS T1;
+SET autocommit=1;
+BEGIN;
+CREATE TABLE T1 (id integer);
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP TABLE T1;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit5.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit5.result
new file mode 100644
index 000000000..44985cd1f
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit5.result
@@ -0,0 +1,14 @@
+USE test;
+DROP PROCEDURE IF EXISTS simpleproc;
+SET autocommit=1;
+BEGIN;
+CREATE PROCEDURE simpleproc (OUT param1 INT)
+BEGIN
+SELECT COUNT(*) INTO param1 FROM t;
+END //
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP PROCEDURE simpleproc;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit6.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit6.result
new file mode 100644
index 000000000..32c6173dd
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit6.result
@@ -0,0 +1,13 @@
+USE test;
+DROP FUNCTION IF EXISTS hello;
+SET autocommit=1;
+BEGIN;
+CREATE FUNCTION hello (s CHAR(20))
+RETURNS CHAR(50) DETERMINISTIC
+RETURN CONCAT('Hello, ',s,'!');
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP FUNCTION hello;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit7.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit7.result
new file mode 100644
index 000000000..31ce55ebb
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_implicit_commit7.result
@@ -0,0 +1,12 @@
+USE test;
+DROP TABLE IF EXISTS T1;
+CREATE TABLE T1 (id integer);
+SET autocommit=1;
+BEGIN;
+CREATE INDEX foo_t1 on T1 (id);
+SELECT (@@server_id) INTO @a;
+SELECT IF(@a <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
+result
+OK (slave)
+DROP TABLE T1;
+COMMIT;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_sescmd.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_sescmd.result
new file mode 100644
index 000000000..5ac67caa2
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_sescmd.result
@@ -0,0 +1,6 @@
+use test;
+set autocommit=1;
+use mysql;
+select count(*) from user where user='skysql';
+count(*)
+2
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2.result
new file mode 100644
index 000000000..c347a1ee4
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2.result
@@ -0,0 +1,15 @@
+USE test;
+DROP TABLE IF EXISTS myCity;
+SET autocommit = 0;
+START TRANSACTION;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+START TRANSACTION;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity;
+COUNT(*)
+0
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2b.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2b.result
new file mode 100644
index 000000000..f87f01614
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing2b.result
@@ -0,0 +1,15 @@
+USE test;
+DROP TABLE IF EXISTS myCity;
+SET autocommit = Off;
+START TRANSACTION;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+START TRANSACTION;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity;
+COUNT(*)
+0
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4.result
new file mode 100644
index 000000000..4a7799fcb
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4.result
@@ -0,0 +1,13 @@
+USE test;
+DROP TABLE IF EXISTS myCity;
+SET autocommit = 0;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity;
+COUNT(*)
+0
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4b.result b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4b.result
new file mode 100644
index 000000000..4349c5c63
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/r/test_transaction_routing4b.result
@@ -0,0 +1,13 @@
+USE test;
+DROP TABLE IF EXISTS myCity;
+SET autocommit = oFf;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity;
+COUNT(*)
+0
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/sleep-1.inc b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/sleep-1.inc
new file mode 100644
index 000000000..6d80a161a
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/sleep-1.inc
@@ -0,0 +1,5 @@
+--disable_query_log
+--disable_result_log
+SELECT SLEEP(5);
+--enable_result_log
+--enable_query_log
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/set_autocommit_disabled.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/set_autocommit_disabled.test
new file mode 100644
index 000000000..a27031ac6
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/set_autocommit_disabled.test
@@ -0,0 +1,11 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (id integer);
+set autocommit=0; # open transaction
+begin;
+insert into t1 values(1); # write to master
+commit;
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_after_autocommit_disabled.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_after_autocommit_disabled.test
new file mode 100644
index 000000000..032c7423b
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_after_autocommit_disabled.test
@@ -0,0 +1,3 @@
+--source testconf.inc
+USE test;
+SELECT IF(@@server_id <> @TMASTER_ID,'OK (slave)','FAIL (master)') AS result;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1.test
new file mode 100644
index 000000000..62bcf1238
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1.test
@@ -0,0 +1,11 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (id integer);
+set autocommit=0; # open transaction
+insert into t1 values(1); # write to master
+select count(*) from t1; # read from master
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1b.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1b.test
new file mode 100644
index 000000000..9edeacbf0
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled1b.test
@@ -0,0 +1,11 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (id integer);
+set autocommit=OFF; # open transaction
+insert into t1 values(1); # write to master
+select count(*) from t1; # read from master
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled2.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled2.test
new file mode 100644
index 000000000..c3ab86701
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled2.test
@@ -0,0 +1,13 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (id integer);
+set autocommit=0; # open transaction
+begin;
+insert into t1 values(1); # write to master
+commit;
+select count(*) from t1; # read from master since autocommit is disabled
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled3.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled3.test
new file mode 100644
index 000000000..c3ab86701
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_autocommit_disabled3.test
@@ -0,0 +1,13 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (id integer);
+set autocommit=0; # open transaction
+begin;
+insert into t1 values(1); # write to master
+commit;
+select count(*) from t1; # read from master since autocommit is disabled
+drop table t1;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_sescmd.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_sescmd.test
new file mode 100644
index 000000000..7e75d1bc5
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_sescmd.test
@@ -0,0 +1,4 @@
+use test;
+set autocommit=1;
+use mysql;
+select count(*) from user where user='skysql';
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2.test
new file mode 100644
index 000000000..dc8a0531f
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2.test
@@ -0,0 +1,16 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+DROP TABLE IF EXISTS myCity;
+--enable_warnings
+SET autocommit = 0;
+START TRANSACTION;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+START TRANSACTION;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity; # read transaction's modifications from master
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2b.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2b.test
new file mode 100644
index 000000000..9958d4a16
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing2b.test
@@ -0,0 +1,16 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+DROP TABLE IF EXISTS myCity;
+--enable_warnings
+SET autocommit = Off;
+START TRANSACTION;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+START TRANSACTION;
+DELETE FROM myCity;
+SELECT COUNT(*) FROM myCity; # read transaction's modifications from master
+COMMIT;
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4.test
new file mode 100644
index 000000000..871753cc0
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4.test
@@ -0,0 +1,16 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+DROP TABLE IF EXISTS myCity;
+--enable_warnings
+
+SET autocommit = 0;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+DELETE FROM myCity; # implicit transaction started
+SELECT COUNT(*) FROM myCity; # read transaction's modifications from master
+COMMIT;
+
+DROP TABLE myCity;
diff --git a/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4b.test b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4b.test
new file mode 100644
index 000000000..570bea11e
--- /dev/null
+++ b/maxscale-system-test/Hartmut_tests/maxscale-mysqltest/t/test_transaction_routing4b.test
@@ -0,0 +1,16 @@
+--source testconf.inc
+USE test;
+--disable_warnings
+DROP TABLE IF EXISTS myCity;
+--enable_warnings
+
+SET autocommit = oFf;
+CREATE TABLE myCity (a int, b char(20));
+INSERT INTO myCity VALUES (1, 'Milan');
+INSERT INTO myCity VALUES (2, 'London');
+COMMIT;
+DELETE FROM myCity; # implicit transaction started
+SELECT COUNT(*) FROM myCity; # read transaction's modifications from master
+COMMIT;
+
+DROP TABLE myCity;
diff --git a/maxscale-system-test/JENKINS.md b/maxscale-system-test/JENKINS.md
new file mode 100644
index 000000000..f96bda933
--- /dev/null
+++ b/maxscale-system-test/JENKINS.md
@@ -0,0 +1,127 @@
+# Jenkins
+
+## List of Jenkins installations
+
+| URL | Description |
+|----|----|
+|[max-tst-01.mariadb.com:8089](http://max-tst-01.mariadb.com:8089)|AWS, qemu; Regular testing for different MariaDB versions, different Linux distributions, Developers testing|
+|[maxscale-jenkins.mariadb.com:8089/](http://maxscale-jenkins.mariadb.com:8089/)|AWS, VBox; Regular builds for all distributions, build for Coverity, regular test VBox+CentOS6+MariaDB5.5|
+|[maxscale-jenkins.mariadb.com:8090](http://maxscale-jenkins.mariadb.com:8090/)|MDBCI testing and debugging, Jenkins experiments|
+
+## Basic Jenkins jobs
+
+### [max-tst-01.mariadb.com:8089](http://max-tst-01.mariadb.com:8089)
+
+| Job | Description |
+|----|----|
+|[build_and_test](http://max-tst-01.mariadb.com:8089/view/test/job/build_and_test/)|Build Maxscale and run systems tests|
+|[run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/)|Run system tests, Maxscale package should be in the repository|
+|[build](http://max-tst-01.mariadb.com:8089/job/build/build)|Build Maxscale, create repository and publish it to [http://max-tst-01.mariadb.com/ci-repository/](http://max-tst-01.mariadb.com/ci-repository/)|
+|[run_test_no_env_rebuild](http://max-tst-01.mariadb.com:8089/view/test/job/run_test_no_env_rebuild/)|Run system tests without creating a new set of VMs|
+|[create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/)|Create VMs, install build environment to Maxscale machine, build Maxscale on Maxscale machine|
+|[destroy](http://max-tst-01.mariadb.com:8089/view/axilary/job/destroy/)|Destroy VMs created by [run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/) or [create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/)|
+|[remove_lock](http://max-tst-01.mariadb.com:8089/view/axilary/job/remove_lock/)|Remove Vagrant lock set by [run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/) or [create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/)|
+
+Every test run should have unique name (parameter 'name'). This name is used as a name of MDBCI configuration.
+If parameter 'do_not_destroy' is set to 'yes' virtual machines (VM) are not destroyed after tests execution and can be laters used
+for debugging or new test runs (see [run_test_no_env_rebuild](http://max-tst-01.mariadb.com:8089/view/test/job/run_test_no_env_rebuild/))
+VMs can be accessed from vagrant@max-tst-01.mariadb.com machine using 'mdbci ssh' or 'vagrant ssh' as well as direct ssh
+access using environmental variables provided by
+[set_env_vagrant.sh](https://github.com/mariadb-corporation/maxscale-system-test/blob/master/ENV_SETUP.md#access-vms)
+script.
+
+Parameter 'box' defines type of VM and Linux distribution to be used for tests.
+
+Test results go to [CDash](http://jenkins.engskysql.com/CDash/index.php?project=MaxScale), logs and core dumps are
+stored [here](http://max-tst-01.mariadb.com/LOGS/).
+
+[create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/) job allows to create a set of VMs
+(for backend and Maxscale) and does Maxscale build on the Maxscale VM. After execution this job Maxscale machine
+contains Maxscale source and binaries. *NOTE:* to properly configure Maxscale init scripts it is necessary to
+use rpm/dpkg tool to install Maxscale package (package can be found in the Maxscale build directory).
+
+[run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/) and
+[create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/)
+jobs create Vagrant lock which prevents running two Vagrant instances in parallel (such parallel execution can
+cause Vagrant of VM provider failures). In case of job crash or interruption by user Vagrant lock stays in locked state
+and prevents any new VM creation. To remove lock job
+[remove_lock](http://max-tst-01.mariadb.com:8089/view/axilary/job/remove_lock/)
+should be used.
+
+## Process examples
+
+### Running regression test against a branch
+
+Execute [build_and_test](http://max-tst-01.mariadb.com:8089/view/test/job/build_and_test/)
+
+Recommendations regarding parameters:
+
+* 'name' - unique name: it can be any text string, but as a good practice rule: 'name' should refer to branch,
+Linux distribution, date/time of testing, MariaDB version
+* 'box' - most recommended boxes are 'centos_7.0_libvirt' (QEMU box) and 'centos7' (Amazon Web Services box)
+* 'source' - which type of source to use. BRANCH for git branch, TAG for a git tag and COMMIT for a commit ID.
+* 'value' - name of the branch (if 'source' is BRANCH), name of the GIT tag (if 'source' is TAG) or commint ID (if 'source' is COMMIT)
+
+### Build MaxScale
+
+Execute [build](http://max-tst-01.mariadb.com:8089/job/build/build) job.
+
+Parameter 'target' means a name of repository to put packages:
+e.g. if 'target' is 'develop' packages are going to
+[http://max-tst-01.mariadb.com/ci-repository/develop/](http://max-tst-01.mariadb.com/ci-repository/develop)
+
+NOTE: building is executed only for selected distribution ('box' parameter). Be careful with other distributions: if build is not executed for same distribution old version can be in the repository (from some old builds). Later tests have to be executed against the same distribution otherwise they can be run against old version of MaxScale. It is recommended to use unique name for 'target'.
+
+To debug failed build:
+* set 'do_not_destroy_vm' parameter to 'yes'
+* after the build:
+
+ssh -i vagrant.pem vagrant@max-tst-01.mariadb.com
+cd ~/mdbci/build-<box>-<date><time>
+vagrant ssh
+
+
+For example:
+
+ssh -i vagrant.pem vagrant@max-tst-01.mariadb.com
+cd ~/mdbci/build_centos6-20160119-0935
+vagrant ssh
+
+
+### Create set of Master/Slave and Galera nodes and setup build environment for Maxscale on one more node
+
+Execute [create_env](http://max-tst-01.mariadb.com:8089/view/env/job/create_env/) job.
+
+Login to Maxscale machine (see [environment documentation](ENV_SETUP.md#access-vms)).
+MaxScale source code, binaries and packages can be found in the ~/workspace/ directory.
+All build tools are installed. GIT can be used to go trough source code.
+It is not recommended to commit anything from virtual machine to GitHub.
+
+Please use 'rpm' or 'dpkg' to properly install Maxscale package (/etc/init.d/maxscale script will not be
+installed without execution of 'rpm' or 'dpkg')
+
+### Running test agains exiting version of Maxscale
+
+Execute [run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/) job.
+
+Be sure Maxscale binary repository is present on the
+[http://max-tst-01.mariadb.com/ci-repository/](http://max-tst-01.mariadb.com/ci-repository/)
+server. Please check:
+* there is a directory with the name equal to 'target' parameter
+* there is sub-directory for selected distribution ('box' parameter)
+
+e.g. if 'target' is 'develop' and distribution is CentOS7 (boxes 'centos7' or 'centos_7.0_libvirt') the directory [http://max-tst-01.mariadb.com/ci-repository/develop/mariadb-maxscale/centos/7/x86_64/](http://max-tst-01.mariadb.com/ci-repository/develop/mariadb-maxscale/centos/7/x86_64/) have to contain Maxscale RPM packages.
+
+If parameter 'do_not_destroy' set to 'yes' after the test virtual machine will not be destroyed and
+can be used for debugging. See [environment documentation](ENV_SETUP.md#access-vms) to get know how to access virtual machines.
+
+### Maintenance operations
+
+If test run was executed with parameter 'do_not_destroy' set yo 'yes' please do not forget to execute
+[destroy](http://max-tst-01.mariadb.com:8089/view/axilary/job/destroy/) against your 'target'
+
+This job also have to be executed if test run job crashed or it was interrupted.
+
+In case of build or test job crash, interruption, Jenkins crash during Vagrant operation it is possible that Vagrant lock
+stays in locked state and no other job can progress (job can be started, but it is waiting for Vagrant lock -
+'/home/vagrant/vagrant_lock' can be seen in the job log). In this case lock can be removed by [remove_lock](http://max-tst-01.mariadb.com:8089/view/axilary/job/remove_lock/) job.
diff --git a/maxscale-system-test/LICENSE b/maxscale-system-test/LICENSE
new file mode 100644
index 000000000..d6a93266f
--- /dev/null
+++ b/maxscale-system-test/LICENSE
@@ -0,0 +1,340 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/maxscale-system-test/README b/maxscale-system-test/README
new file mode 100644
index 000000000..e69de29bb
diff --git a/maxscale-system-test/README.md b/maxscale-system-test/README.md
new file mode 100644
index 000000000..1e332b408
--- /dev/null
+++ b/maxscale-system-test/README.md
@@ -0,0 +1,67 @@
+# maxscale-system-test
+System level tests for MaxScale
+
+## Basics
+- every test is separate executable file
+- backend for test:
+ - 1 machine for Maxscale
+ - >= 4 machines for Master/Slave
+ - >= 4 machines for Galera cluster
+- environmental variables contains all information about backend: IPs, user names, passwords, paths to tools, etc
+- backed can be created with help of [MDBCI tool](https://github.com/OSLL/mdbci)
+- configuring of Master/Slave and Galera can be done with help of [build scripts package](https://github.com/mariadb-corporation/build-scripts-vagrant)
+
+## Manuals
+
+[How to run tests](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/RUN_TEST.md)
+
+[Hints: How to write a test](HOW_TO_WRITE_TEST.md)
+
+[Build and test environment setup (if you want to play with MDBCI and Vagrant on your local machine](ENV_SETUP.md)
+
+[Jenkins instructions](JENKINS.md)
+
+## Environmental variables
+|variable|meaning|
+|--------|-------|
+|node_N|Number of machines for Master/Slave|
+|node_XXX_network|IP address of Master/Slave machine number XXX|
+|node_XXX_private_ip|private IP address of Master/Slave machine XXX for AWS machines (for everything else - same as node_XXX|
+|node_XXX_port|MariaDB port of Master/Slave machine XXX|
+|node_XXX_whoami|user name to access Master/Slave machine XXX via ssh|
+|node_XXX_access_sudo|'sudo ' if node_access_user_XXX does not have root rights, empty string if node_access_user_XXX has root rights|
+|node_XXX_keyfile|full name of secret key to access Master/Slave machine XXX via ssh|
+|node_XXX_start_db_command|bash command to start DB server on Master/Slave machine XXX|
+|node_XXX_stop_db_command|bash command to stop DB server on Master/Slave machine XXX|
+|node_user|DB user name to access Master/Slave nodes (have to have all priveligies with GRANT option)|
+|node_password|password for node_user|
+|galera_N|Number of machines for Galera|
+|galera_XXX_network|IP address of Galera machine number XXX|
+|galera_XXX_private|private IP address of Galera machine XXX for AWS machines (for everything else - same as node_XXX|
+|galera_XXX_port|MariaDB port of Galera machine XXX|
+|galera_XXX_whoami|user name to access Galera machine XXX via ssh|
+|galera_XXX_access|'sudo ' if node_access_user_XXX does not have root rights, empty string if node_access_user_XXX has root rights|
+|galera_XXX_keyfile|full name of secret key to access Galera machine XXX via ssh|
+|galera_XXX_start_db_command|bash command to start DB server on Galera machine XXX|
+|galera_XXX_stop_db_command|bash command to stop DB server on Galera machine XXX|
+|galera_user|DB user name to access Galera nodes (have to have all priveligies with GRANT option)|
+|galera_password|password for node_user|
+|maxscale_cnf|full name of Maxscale configuration file (maxscale.cnf)|
+|maxscale_log_dir|directory for Maxscale log files|
+|maxscale_IP|IP address of Maxscale machine|
+|maxscale_sshkey|full name of secret key to access Maxscale machine via ssh|
+|maxscale_access_user|user name to access Maxscale machine via ssh|
+|maxscale_access_sudo|'sudo ' if maxscale_access_user does not have root rights, empty string if maxscale_access_user has root rights|
+|maxscale_user|DB user to access via Maxscale|
+|maxscale_password|password for maxscale_user|
+|maxscale_hostname|hostname of Maxscale machine|
+|sysbench_dir|directory where Sysbanch is installed|
+|ssl|'yes' if tests should try to use ssl to connect to Maxscale and to backends (obsolete, now should be 'yes' in all cases)|
+|smoke|if 'yes' all tests are executed in 'quick' mode (less iterations, skip heavy operations)|
+|backend_ssl|if 'yes' ssl config will be added to all servers definition in maxscale.cnf|
+|use_snapshots|if TRUE every test is trying to revert snapshot before running the test|
+|take_snapshot_command|revert_snapshot_command|
+|revert_snapshot_command|Command line to revert a snapshot of all VMs|
+|no_nodes_check|if yes backend checks are not executed (needed in case of RDS or similar backend)|
+|no_backend_log_copy|if yes logs from backend nodes are not copied (needed in case of RDS or similar backend)|
+|no_maxscale_start|Do not start Maxscale automatically|
diff --git a/maxscale-system-test/TEST_RUSULTS.md b/maxscale-system-test/TEST_RUSULTS.md
new file mode 100644
index 000000000..56f5fac16
--- /dev/null
+++ b/maxscale-system-test/TEST_RUSULTS.md
@@ -0,0 +1,32 @@
+# Results locations
+
+| Location | Description |
+|----------|-------------|
+|[run_test](http://max-tst-01.mariadb.com:8089/view/test/job/run_test/) Jenkins job log|Vagrant and test application outputs|
+|[CDash](jenkins.engskysql.com/CDash/index.php?project=MaxScale)|CTest reports|
+|[http://max-tst-01.mariadb.com/LOGS/](http://max-tst-01.mariadb.com/LOGS/)|MaxScale logs and core dumps|
+|/home/vagrant/LOGS|Same as [http://max-tst-01.mariadb.com/LOGS/](http://max-tst-01.mariadb.com/LOGS/)|
+|Maxscale VM /var/log/maxscale|MaxScale log from latest test case|
+|Maxscale VM /tpm/core*|Core dump from latest test case|
+|Maxscale VM home directory|QLA filter files (if enabled in MaxScale test configuration|
+|nodeN, galeraN VMs|MariaDB/MySQL logs (see MariaDB/MySQL documentation for details)|
+
+For access to VMs see [environment documentation](ENV_SETUP.md#access-vms)
+
+Jenkins job log consists of following parts:
+* Vagrant output: VMs creation priocess, MariaDB Master/Slave and MariaDB Galera stuff installation, MaxScale installation
+* [set_env_vagrant.sh](https://github.com/mariadb-corporation/build-scripts-vagrant/blob/master/test/set_env_vagrant.sh) output: retrieval of all VM parameters
+* setup scripts output: MariaDB initialisation on backend nodes, DB users setup, enabling core dump on MaxScale VM
+* test application output for all tests: eevry line starts from test case number and ':' (can be grepped)
+* CTest final printing: N of M tests passed, CTest warnings, email sending logs
+
+To check presence of core dumps:
+
+find /home/vagrant/LOGS/<last_test_results_dir> | grep core
+
+
+where 'last_test_results_dir' - automatically generated name of logs directory (based on date and time of test run)
+
+To understand test case output please see test case description in Doxygen comments in every test case source file.
+
+VMs are alive after the test run only if test run is done with 'do_not_destroy' parameter.
diff --git a/maxscale-system-test/astylerc b/maxscale-system-test/astylerc
new file mode 100644
index 000000000..90562db93
--- /dev/null
+++ b/maxscale-system-test/astylerc
@@ -0,0 +1,14 @@
+--style=allman
+--indent=spaces=4
+--indent-switches
+--indent-labels
+--min-conditional-indent=0
+--pad-oper
+--pad-header
+--add-brackets
+--convert-tabs
+--max-code-length=110
+--break-after-logical
+--mode=c
+--suffix=none
+--max-instatement-indent=110
diff --git a/maxscale-system-test/auroramon.cpp b/maxscale-system-test/auroramon.cpp
new file mode 100644
index 000000000..39140bbc0
--- /dev/null
+++ b/maxscale-system-test/auroramon.cpp
@@ -0,0 +1,157 @@
+/**
+ * @file auroramon.cpp test of Aurora RDS monitor
+ * - create RDS cluster
+ * - find 'writer' node and uses 'maxadmin' to check that this node is "Master, Running"
+ * - do forced failover
+ * - find 'writer' again and repeat check
+ * - destroy RDS cluster
+ */
+
+
+#include "testconnections.h"
+#include "execute_cmd.h"
+#include "rds_vpc.h"
+
+int set_endspoints(RDS * cluster)
+{
+
+ json_t *endpoint;
+ long long int port;
+ const char * IP;
+ char p[64];
+ size_t i;
+ char cmd[1024];
+
+ json_t * endpoints = cluster->get_endpoints();
+ if (endpoints == NULL)
+ {
+ return -1;
+ }
+
+ json_array_foreach(endpoints, i, endpoint)
+ {
+ port = json_integer_value(json_object_get(endpoint, "Port"));
+ IP = json_string_value(json_object_get(endpoint, "Address"));
+ printf("host: %s \t port: %lld\n", IP, port);
+ sprintf(cmd, "node_%03d_network", (int) i);
+ setenv(cmd, IP, 1);
+ sprintf(cmd, "node_%03d_port", (int) i);
+ sprintf(p, "%lld", port);
+ setenv(cmd, p, 1);
+ }
+
+ setenv("node_password", "skysqlrds", 1);
+ setenv("maxscale_user", "skysql", 1);
+ setenv("maxscale_password", "skysqlrds", 1);
+ setenv("no_nodes_check", "yes", 1);
+ setenv("no_backend_log_copy", "yes", 1);
+ return 0;
+}
+
+
+void compare_masters(TestConnections* Test, RDS * cluster)
+{
+ const char * aurora_master;
+ cluster->get_writer(&aurora_master);
+ Test->tprintf("Aurora writer node: %s\n", aurora_master);
+ char maxadmin_status[1024];
+ int i;
+ char cmd[1024];
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ sprintf(cmd, "show server server%d", i + 1);
+ Test->get_maxadmin_param(cmd, (char *) "Status:", &maxadmin_status[0]);
+ Test->tprintf("Server%d status %s\n", i + 1, maxadmin_status);
+ sprintf(cmd, "node%03d", i);
+ if (strcmp(aurora_master, cmd) == 0)
+ {
+ if (strcmp(maxadmin_status, "Master, Running"))
+ {
+ Test->tprintf("Maxadmin reports node%03d is a Master as expected", i);
+ }
+ else
+ {
+ Test->add_result(1, "Server node%03d status is not 'Master, Running'', it is '%s'", i, maxadmin_status);
+ }
+ }
+ else
+ {
+ if (strcmp(maxadmin_status, "Slave, Running"))
+ {
+ Test->tprintf("Maxadmin reports node%03d is a Slave as expected", i);
+ }
+ else
+ {
+ Test->add_result(1, "Server node%03d status is not 'Slave, Running'', it is '%s'", i, maxadmin_status);
+ }
+ }
+
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ RDS * cluster = new RDS((char *) "auroratest");
+
+ if (cluster->create_rds_db(4) != 0)
+ {
+ printf("Error RDS creation\n");
+ return 1;
+ }
+ cluster->wait_for_nodes(4);
+
+
+ if (set_endspoints(cluster) != 0)
+ {
+ printf("Error getting RDS endpoints\n");
+ return 1;
+ }
+
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+
+ compare_masters(Test, cluster);
+
+ Test->set_timeout(30);
+ Test->tprintf("Executing a query through readwritesplit before failover");
+ Test->connect_rwsplit();
+ Test->try_query(Test->conn_rwsplit, "show processlist");
+ char server_id[1024];
+ Test->tprintf("Get aurora_server_id\n");
+ find_field(Test->conn_rwsplit, "select @@aurora_server_id;", "server_id", &server_id[0]);
+ Test->close_rwsplit();
+ Test->tprintf("server_id before failover: %s\n", server_id);
+
+ Test->stop_timeout();
+ Test->tprintf("Performing cluster failover\n");
+
+ Test->add_result(cluster->do_failover(), "Failover failed\n");
+
+ Test->tprintf("Failover done\n");
+
+ // Do the failover here and wait until it is over
+ //sleep(10);
+
+ Test->set_timeout(30);
+ Test->tprintf("Executing a query through readwritesplit after failover");
+ Test->connect_rwsplit();
+ Test->try_query(Test->conn_rwsplit, "show processlist");
+ Test->tprintf("Get aurora_server_id\n");
+ find_field(Test->conn_rwsplit, "select @@aurora_server_id;", "server_id", &server_id[0]);
+ Test->close_rwsplit();
+ Test->tprintf("server_id after failover: %s\n", server_id);
+
+ compare_masters(Test, cluster);
+
+
+ //Test->check_maxscale_alive();
+
+
+ Test->stop_timeout();
+ cluster->delete_rds_cluster();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/avro.cpp b/maxscale-system-test/avro.cpp
new file mode 100644
index 000000000..7dbd365b2
--- /dev/null
+++ b/maxscale-system-test/avro.cpp
@@ -0,0 +1,107 @@
+/**
+ * @file avro.cpp test of avro
+ * - setup binlog and avro
+ * - put some data to t1
+ * - check avro file with "maxavrocheck -vv /var/lib/maxscale/avro/test.t1.000001.avro"
+ * - check that data in avro file is correct
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+
+#include "test_binlog_fnc.h"
+#include
+#include "maxinfo_func.h"
+
+#include
+#include
+
+using std::cout;
+using std::endl;
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(600);
+ Test->stop_maxscale();
+ Test->ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
+
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], "DROP TABLE IF EXISTS t1");
+ Test->repl->close_connections();
+ sleep(5);
+
+
+ Test->start_binlog();
+
+ Test->set_timeout(120);
+
+ Test->stop_maxscale();
+
+ Test->ssh_maxscale(true, "rm -rf /var/lib/maxscale/avro");
+
+ Test->set_timeout(120);
+
+ Test->start_maxscale();
+
+ Test->set_timeout(60);
+
+ Test->repl->connect();
+ create_t1(Test->repl->nodes[0]);
+ insert_into_t1(Test->repl->nodes[0], 3);
+ execute_query(Test->repl->nodes[0], "FLUSH LOGS");
+
+ Test->repl->close_connections();
+
+ Test->set_timeout(120);
+
+ sleep(10);
+
+ char * avro_check = Test->ssh_maxscale_output(true,
+ "maxavrocheck -vv /var/lib/maxscale/avro/test.t1.000001.avro | grep \"{\"");
+ char * output = Test->ssh_maxscale_output(true, "maxavrocheck -d /var/lib/maxscale/avro/test.t1.000001.avro");
+
+ std::istringstream iss;
+ iss.str(output);
+ int x1_exp = 0;
+ int fl_exp = 0;
+ int x = 16;
+
+ for (std::string line; std::getline(iss, line);)
+ {
+ long long int x1, fl;
+ Test->set_timeout(20);
+ get_x_fl_from_json((char*)line.c_str(), &x1, &fl);
+
+ if (x1 != x1_exp || fl != fl_exp)
+ {
+ Test->add_result(1, "Output:x1 %lld, fl %lld, Expected: x1 %d, fl %d",
+ x1, fl, x1_exp, fl_exp);
+ break;
+ }
+
+ if ((++x1_exp) >= x)
+ {
+ x1_exp = 0;
+ x = x * 16;
+ fl_exp++;
+ Test->tprintf("fl = %d", fl_exp);
+ }
+ }
+
+ if (fl_exp != 3)
+ {
+ Test->add_result(1, "not enough lines in avrocheck output\n");
+ }
+
+ Test->set_timeout(120);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/avro_long.cpp b/maxscale-system-test/avro_long.cpp
new file mode 100644
index 000000000..3221c9ceb
--- /dev/null
+++ b/maxscale-system-test/avro_long.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file avro_long.cpp test of avro
+ * - setup binlog and avro
+ * - put some data to t1 in the loop
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+
+#include "test_binlog_fnc.h"
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(600);
+ Test->stop_maxscale();
+ Test->ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
+
+ //Test->ssh_maxscale(true, (char *) "mkdir /var/lib/maxscale/avro; chown -R maxscale:maxscale /var/lib/maxscale/avro");
+
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], (char *) "DROP TABLE IF EXISTS t1;");
+ Test->repl->close_connections();
+ sleep(5);
+
+
+ Test->start_binlog();
+
+ Test->set_timeout(120);
+
+ Test->stop_maxscale();
+
+ Test->ssh_maxscale(true, (char *) "rm -rf /var/lib/maxscale/avro");
+
+ Test->set_timeout(120);
+
+ Test->start_maxscale();
+
+ Test->set_timeout(60);
+
+ Test->repl->connect();
+ create_t1(Test->repl->nodes[0]);
+
+ for (int i = 0; i < 1000000; i++)
+ {
+ Test->set_timeout(60);
+ insert_into_t1(Test->repl->nodes[0], 3);
+ Test->tprintf("i=%d\n", i);
+ }
+
+ Test->repl->close_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/backend_auth_fail.cpp b/maxscale-system-test/backend_auth_fail.cpp
new file mode 100644
index 000000000..adc0ed3b1
--- /dev/null
+++ b/maxscale-system-test/backend_auth_fail.cpp
@@ -0,0 +1,40 @@
+/**
+ * @backend_auth_fail.cpp Repeatedly connect to maxscale while the backends reject all connections
+ *
+ * MaxScale should not crash
+ */
+
+#include "testconnections.h"
+
+int main(int argc, char** argv)
+{
+ MYSQL *mysql[1000];
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->stop_timeout();
+ Test->repl->execute_query_all_nodes((char *) "set global max_connections = 10;");
+
+ for (int x = 0; x < 3; x++)
+ {
+ Test->tprintf("Creating 100 connections...\n");
+ for (int i = 0; i < 100; i++)
+ {
+ Test->set_timeout(30);
+ mysql[i] = Test->open_readconn_master_connection();
+ execute_query_silent(mysql[i], "select 1");
+ }
+ Test->stop_timeout();
+
+ for (int i = 0; i < 100; i++)
+ {
+ Test->set_timeout(30);
+ mysql_close(mysql[i]);
+ }
+ }
+
+ Test->stop_timeout();
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+
+}
diff --git a/maxscale-system-test/bad_pers.cpp b/maxscale-system-test/bad_pers.cpp
new file mode 100644
index 000000000..993e7bee1
--- /dev/null
+++ b/maxscale-system-test/bad_pers.cpp
@@ -0,0 +1,27 @@
+/**
+ * @file bad_pres.cpp check that Maxscale prints warning if persistpoolmax=-1 for all backends (bug MXS-576)
+ *
+ * - Maxscale.cnf contains persistpoolmax=-1 for all servers
+ * - check log warning about it
+ */
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->connect_maxscale();
+ Test->check_log_err((char *) "warning -1", true);
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/big_load.cpp b/maxscale-system-test/big_load.cpp
new file mode 100644
index 000000000..8c07daff2
--- /dev/null
+++ b/maxscale-system-test/big_load.cpp
@@ -0,0 +1,228 @@
+#include "big_load.h"
+#include
+
+void load(long int *new_inserts, long int *new_selects, long int *selects, long int *inserts, int threads_num,
+ TestConnections * Test, long int *i1, long int *i2, int rwsplit_only, bool galera, bool report_errors)
+{
+ char sql[1000000];
+ thread_data data;
+ Mariadb_nodes * nodes;
+ if (galera)
+ {
+ nodes = Test->galera;
+ }
+ else
+ {
+ nodes = Test->repl;
+ }
+
+ int sql_l = 20000;
+ int run_time = 100;
+ if (Test->smoke)
+ {
+ sql_l = 500;
+ run_time = 10;
+ }
+
+ nodes->connect();
+ Test->connect_rwsplit();
+
+ data.i1 = 0;
+ data.i2 = 0;
+ data.exit_flag = 0;
+ data.Test = Test;
+ data.rwsplit_only = rwsplit_only;
+ // connect to the MaxScale server (rwsplit)
+
+ if (Test->conn_rwsplit == NULL )
+ {
+ if (report_errors)
+ {
+ Test->add_result(1, "Can't connect to MaxScale\n");
+ }
+ //Test->copy_all_logs();
+ exit(1);
+ }
+ else
+ {
+ create_t1(Test->conn_rwsplit);
+ create_insert_string(sql, sql_l, 1);
+
+ if ((execute_query(Test->conn_rwsplit, sql) != 0) && (report_errors))
+ {
+ Test->add_result(1, "Query %s failed\n", sql);
+ }
+ // close connections
+ Test->close_rwsplit();
+
+ Test->tprintf("Waiting for the table to replicate\n");
+ Test->repl->sync_slaves();
+
+ pthread_t thread1[threads_num];
+ pthread_t thread2[threads_num];
+ int iret1[threads_num];
+ int iret2[threads_num];
+
+ Test->tprintf("COM_INSERT and COM_SELECT before executing test\n");
+
+ Test->add_result(get_global_status_allnodes(&selects[0], &inserts[0], nodes, 0),
+ "get_global_status_allnodes failed\n");
+
+ data.exit_flag = 0;
+ /* Create independent threads each of them will execute function */
+ for (int i = 0; i < threads_num; i++)
+ {
+ iret1[i] = pthread_create(&thread1[i], NULL, query_thread1, &data);
+ iret2[i] = pthread_create(&thread2[i], NULL, query_thread2, &data);
+ }
+ Test->tprintf("Threads are running %d seconds \n", run_time);
+ sleep(run_time);
+ data.exit_flag = 1;
+ Test->tprintf("Waiting for all threads to exit\n");
+ Test->set_timeout(100);
+ for (int i = 0; i < threads_num; i++)
+ {
+ pthread_join(thread1[i], NULL);
+ pthread_join(thread2[i], NULL);
+ }
+ sleep(1);
+
+ Test->tprintf("COM_INSERT and COM_SELECT after executing test\n");
+ get_global_status_allnodes(&new_selects[0], &new_inserts[0], nodes, 0);
+ print_delta(&new_selects[0], &new_inserts[0], &selects[0], &inserts[0], nodes->N);
+ Test->tprintf("First group of threads did %d queries, second - %d \n", data.i1, data.i2);
+ }
+ nodes->close_connections();
+ *i1 = data.i1;
+ *i2 = data.i2;
+}
+
+void *query_thread1( void *ptr )
+{
+ MYSQL * conn1;
+ MYSQL * conn2;
+ MYSQL * conn3;
+ int conn_err = 0;
+ thread_data * data = (thread_data *) ptr;
+ conn1 = open_conn_db_timeout(data->Test->rwsplit_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ //conn1 = data->Test->open_rwsplit_connection();
+ if (mysql_errno(conn1) != 0)
+ {
+ conn_err++;
+ }
+ if (data->rwsplit_only == 0)
+ {
+ //conn2 = data->Test->open_readconn_master_connection();
+ conn2 = open_conn_db_timeout(data->Test->readconn_master_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ if (mysql_errno(conn2) != 0)
+ {
+ conn_err++;
+ }
+ //conn3 = data->Test->open_readconn_slave_connection();
+ conn3 = open_conn_db_timeout(data->Test->readconn_slave_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ if (mysql_errno(conn3) != 0)
+ {
+ conn_err++;
+ }
+ }
+ if (conn_err == 0)
+ {
+ while (data->exit_flag == 0)
+ {
+ if (execute_query_silent(conn1, (char *) "SELECT * FROM t1;") == 0)
+ {
+ __sync_fetch_and_add(&data->i1, 1);
+ }
+
+ if (data->rwsplit_only == 0)
+ {
+ execute_query_silent(conn2, (char *) "SELECT * FROM t1;");
+ execute_query_silent(conn3, (char *) "SELECT * FROM t1;");
+ }
+
+ }
+ mysql_close(conn1);
+ if (data->rwsplit_only == 0)
+ {
+ mysql_close(conn2);
+ mysql_close(conn3);
+ }
+ }
+ return NULL;
+}
+
+void *query_thread2(void *ptr )
+{
+ MYSQL * conn1;
+ MYSQL * conn2;
+ MYSQL * conn3;
+ thread_data * data = (thread_data *) ptr;
+ //conn1 = data->Test->open_rwsplit_connection();
+ conn1 = open_conn_db_timeout(data->Test->rwsplit_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ if (data->rwsplit_only == 0)
+ {
+ //conn2 = data->Test->open_readconn_master_connection();
+ //conn3 = data->Test->open_readconn_slave_connection();
+
+ conn2 = open_conn_db_timeout(data->Test->readconn_master_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ //if (mysql_errno(conn2) != 0) { conn_err++; }
+ conn3 = open_conn_db_timeout(data->Test->readconn_slave_port,
+ data->Test->maxscale_IP,
+ (char *) "test",
+ data->Test->maxscale_user,
+ data->Test->maxscale_password,
+ 20,
+ data->Test->ssl);
+ //if (mysql_errno(conn3) != 0) { conn_err++; }
+ }
+ while (data->exit_flag == 0)
+ {
+ sleep(1);
+ if (execute_query_silent(conn1, (char *) "SELECT * FROM t1;") == 0)
+ {
+ __sync_fetch_and_add(&data->i2, 1);
+ }
+ if (data->rwsplit_only == 0)
+ {
+ execute_query_silent(conn2, (char *) "SELECT * FROM t1;");
+ execute_query_silent(conn3, (char *) "SELECT * FROM t1;");
+ }
+ }
+ mysql_close(conn1);
+ if (data->rwsplit_only == 0)
+ {
+ mysql_close(conn2);
+ mysql_close(conn3);
+ }
+ return NULL;
+}
diff --git a/maxscale-system-test/big_load.h b/maxscale-system-test/big_load.h
new file mode 100644
index 000000000..f4e94bdb0
--- /dev/null
+++ b/maxscale-system-test/big_load.h
@@ -0,0 +1,39 @@
+#ifndef BIG_LOAD_H
+#define BIG_LOAD_H
+
+
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "get_com_select_insert.h"
+
+//pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+typedef struct
+{
+ int exit_flag;
+ long i1;
+ long i2;
+ int rwsplit_only;
+ TestConnections * Test;
+} thread_data;
+
+void *query_thread1(void *ptr );
+void *query_thread2(void *ptr );
+
+/**
+ * @brief load Creates load on Maxscale routers
+ * @param new_inserts COM_INSERT variable values array for all nodes after test
+ * @param new_selects COM_SELECT variable values array for all nodes after test
+ * @param selects COM_SELECT variable values array for all nodes before test
+ * @param inserts COM_INSERT variable values array for all nodes before test
+ * @param threads_num Number of load threads
+ * @param Test TestConnections object
+ * @param i1 Number of queries executed by "fast" threads (no wating between queries)
+ * @param i2 Number of queries executed by "slow" threads (sleep 1 second between queries)
+ * @param rwsplit_only if 1 create load only on RWSplit router, do not load ReadConn router
+ * @param galera if true use Galera backend (Test->galera instead of Test->repl)
+ * @param report_errors if true call add_result() in case of query failure
+ */
+void load(long *new_inserts, long *new_selects, long *selects, long *inserts, int threads_num,
+ TestConnections *Test, long *i1, long *i2, int rwsplit_only, bool galera, bool report_errors);
+
+#endif // BIG_LOAD_H
diff --git a/maxscale-system-test/big_transaction.cpp b/maxscale-system-test/big_transaction.cpp
new file mode 100644
index 000000000..2539aedb0
--- /dev/null
+++ b/maxscale-system-test/big_transaction.cpp
@@ -0,0 +1,23 @@
+#include "big_transaction.h"
+
+int big_transaction(MYSQL * conn, int N)
+{
+ int local_result = 0;
+ char sql[1000000];
+ local_result += create_t1(conn);
+ local_result += execute_query(conn, (char *) "START TRANSACTION");
+ local_result += execute_query(conn, (char *) "SET autocommit = 0");
+
+ for (int i = 0; i < N; i++)
+ {
+ create_insert_string(sql, 10000, i);
+ local_result += execute_query(conn, sql);
+ local_result += execute_query(conn, "CREATE TABLE t2(id int);");
+ local_result += execute_query(conn, sql);
+ local_result += execute_query(conn, "DROP TABLE t2;");
+ local_result += execute_query(conn, sql);
+ }
+
+ local_result += execute_query(conn, (char *) "COMMIT");
+ return local_result;
+}
diff --git a/maxscale-system-test/big_transaction.h b/maxscale-system-test/big_transaction.h
new file mode 100644
index 000000000..d5be98e01
--- /dev/null
+++ b/maxscale-system-test/big_transaction.h
@@ -0,0 +1,17 @@
+#ifndef BIG_TRANSACTION_H
+#define BIG_TRANSACTION_H
+
+#include
+#include
+#include
+#include "sql_t1.h"
+
+/**
+ * @brief big_transaction Executes big transaction (includes N INSERTs of 10000 rows)
+ * @param conn MYSQL connection handler
+ * @param N Number of INSERTs
+ * @return 0 if success
+ */
+int big_transaction(MYSQL * conn, int N);
+
+#endif // BIG_TRANSACTION_H
diff --git a/maxscale-system-test/binlog_big_transaction.cpp b/maxscale-system-test/binlog_big_transaction.cpp
new file mode 100644
index 000000000..bb48a390d
--- /dev/null
+++ b/maxscale-system-test/binlog_big_transaction.cpp
@@ -0,0 +1,72 @@
+/**
+ * @file binlog_big_transaction.cpp test of simple binlog router setup and execute a number of big transactions
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+#include "test_binlog_fnc.h"
+#include "big_transaction.h"
+
+void *disconnect_thread( void *ptr );
+TestConnections * Test ;
+int exit_flag;
+int main(int argc, char *argv[])
+{
+
+ Test = new TestConnections(argc, argv);
+ Test->set_timeout(3000);
+ Test->set_log_copy_interval(300);
+
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], (char *) "DROP TABLE IF EXISTS t1;");
+ Test->repl->close_connections();
+ sleep(5);
+
+ Test->start_binlog();
+
+ pthread_t threads;
+ int iret;
+ exit_flag = 0;
+ iret = pthread_create( &threads, NULL, disconnect_thread, NULL);
+
+ Test->repl->connect();
+ for (int i = 0; i < 100000; i++)
+ {
+ Test->set_timeout(3000);
+ Test->tprintf("Trying transactions: %d\n", i);
+ Test->add_result(big_transaction(Test->repl->nodes[0], 7), "Transaction %d failed!\n", i);
+ }
+ Test->repl->close_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+void *disconnect_thread( void *ptr )
+{
+ MYSQL * conn;
+ char cmd[256];
+ int i;
+ conn = open_conn(Test->binlog_port, Test->maxscale_IP, Test->repl->user_name, Test->repl->password,
+ Test->repl->ssl);
+ Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
+ i = 3;
+ while (exit_flag == 0)
+ {
+ sprintf(cmd, "DISCONNECT SERVER %d", i);
+ execute_query(conn, cmd);
+ i++;
+ if (i > Test->repl->N)
+ {
+ i = 3;
+ sleep(30);
+ execute_query(conn, (char *) "DISCONNECT SERVER ALL");
+ }
+ sleep(5);
+ }
+ return NULL;
+}
diff --git a/maxscale-system-test/binlog_change_master.cpp b/maxscale-system-test/binlog_change_master.cpp
new file mode 100644
index 000000000..ef59392a7
--- /dev/null
+++ b/maxscale-system-test/binlog_change_master.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file binlog_change_master.cpp In the binlog router setup stop Master and promote one of the Slaves to be new Master
+ * - setup binlog
+ * - start thread wich executes transactions
+ * - block master
+ * - transaction thread tries to elect a new master a continue with new master
+ * - continue transaction with new master
+ * - stop transactions
+ * - wait
+ * - chack data on all nodes
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+#include "test_binlog_fnc.h"
+#include "big_transaction.h"
+
+void *disconnect_thread( void *ptr );
+void *transaction_thread( void *ptr );
+
+TestConnections * Test ;
+int exit_flag;
+int master = 0;
+int i_trans = 0;
+int failed_transaction_num = 0;
+
+/** The amount of rows each transaction inserts */
+const int N_INSERTS = 100;
+
+int transaction(MYSQL * conn, int N)
+{
+ int local_result = 0;
+ char sql[1000000];
+
+ Test->tprintf("START TRANSACTION\n");
+ local_result += execute_query(conn, (char *) "START TRANSACTION");
+ if (local_result != 0)
+ {
+ Test->tprintf("START TRANSACTION Failed\n");
+ return local_result;
+ }
+ Test->tprintf("SET autocommit = 0\n");
+ local_result += execute_query(conn, (char *) "SET autocommit = 0");
+ if (local_result != 0)
+ {
+ Test->tprintf("SET Failed\n");
+ return local_result;
+ }
+
+ create_insert_string(sql, N_INSERTS, N);
+ Test->tprintf("INSERT\n");
+ local_result += execute_query(conn, sql);
+ if (local_result != 0)
+ {
+ Test->tprintf("Insert Failed\n");
+ return local_result;
+ }
+
+ Test->tprintf("COMMIT\n");
+ local_result += execute_query(conn, (char *) "COMMIT");
+ if (local_result != 0)
+ {
+ Test->tprintf("Commit Failed\n");
+ return local_result;
+ }
+ return local_result;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int j;
+
+ Test = new TestConnections(argc, argv);
+ Test->set_timeout(3000);
+
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], (char *) "DROP TABLE IF EXISTS t1;");
+ Test->repl->close_connections();
+ sleep(5);
+
+ Test->repl->connect();
+ Test->repl->execute_query_all_nodes((char *) "STOP SLAVE");
+ Test->repl->execute_query_all_nodes((char *) "RESET SLAVE ALL");
+ Test->repl->execute_query_all_nodes((char *) "RESET MASTER");
+
+ Test->tprintf("Starting binlog configuration\n");
+ Test->start_binlog();
+
+ pthread_t disconnec_thread_t;
+ int disconnect_iret;
+ pthread_t transaction_thread_t;
+ int transaction_iret;
+
+ exit_flag = 0;
+ Test->tprintf("Starting query thread\n");
+
+ transaction_iret = pthread_create(&transaction_thread_t, NULL, transaction_thread, NULL);
+
+ Test->tprintf("Sleeping\n");
+ Test->stop_timeout();
+
+ Test->repl->connect();
+ int flushes = Test->smoke ? 2 : 5;
+ for (j = 0; j < flushes; j++)
+ {
+ Test->tprintf("Flush logs on master\n");
+ execute_query(Test->repl->nodes[0], (char *) "flush logs");
+ sleep(15);
+ }
+
+ sleep(15);
+
+ Test->tprintf("Blocking master\n");
+ Test->repl->block_node(0);
+ Test->stop_timeout();
+
+ sleep(30);
+
+ Test->tprintf("Done! Waiting for thread\n");
+ exit_flag = 1;
+ pthread_join(transaction_thread_t, NULL );
+ Test->tprintf("Done!\n");
+
+ Test->tprintf("Checking data on the node3 (slave)\n");
+ char sql[256];
+ char rep[256];
+ int rep_d;
+
+ Test->tprintf("Sleeping to let replication happen\n");
+ sleep(30);
+
+ Test->repl->connect();
+
+ for (int i_n = 3; i_n < Test->repl->N; i_n++)
+ {
+ for (j = 0; j < i_trans; j++)
+ {
+ sprintf(sql, "select count(*) from t1 where fl=%d;", j);
+ find_field(Test->repl->nodes[i_n], sql, (char *) "count(*)", rep);
+ Test->tprintf("Transaction %d put %s rows\n", j, rep);
+ sscanf(rep, "%d", &rep_d);
+ if ((rep_d != N_INSERTS) && (j != (failed_transaction_num - 1)))
+ {
+ Test->add_result(1, "Transaction %d did not put data into slave\n", j);
+ }
+ if ((j == (failed_transaction_num - 1)) && (rep_d != 0) && (rep_d != N_INSERTS))
+ {
+ Test->add_result(1, "Incomplete transaction detected - %d\n", j);
+ }
+ if ((j == (failed_transaction_num - 1) && rep_d == 0))
+ {
+ Test->tprintf("Transaction %d was rejected, OK\n", j);
+ }
+ }
+ }
+ Test->repl->close_connections();
+
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
+const char * setup_slave1 =
+ "change master to MASTER_HOST='%s',\
+ MASTER_USER='repl',\
+ MASTER_PASSWORD='repl',\
+ MASTER_LOG_FILE='%s',\
+ MASTER_LOG_POS=%s,\
+ MASTER_PORT=%d";
+
+
+int select_new_master(TestConnections * test)
+{
+ char log_file[256];
+ char log_file_new[256];
+ char log_pos[256];
+
+ char maxscale_log_file[256];
+ char maxscale_log_file_new[256];
+ char maxscale_log_pos[256];
+
+ // Stopping slave
+ test->tprintf("Connection to backend\n");
+ test->repl->connect();
+ test->tprintf("'stop slave' to node2\n");
+ test->try_query(Test->repl->nodes[2], (char *) "stop slave;");
+ test->tprintf("'reset slave all' to node2\n");
+ test->try_query(Test->repl->nodes[2], (char *) "RESET slave all;");
+ //execute_query(Test->repl->nodes[2], (char *) "reset master;");
+
+ // Get master status
+ test->tprintf("show master status\n");
+ find_field(test->repl->nodes[2], (char *) "show master status", (char *) "File", &log_file[0]);
+ find_field(test->repl->nodes[2], (char *) "show master status", (char *) "Position", &log_pos[0]);
+ test->tprintf("Real master file: %s\n", log_file);
+ test->tprintf("Real master pos : %s\n", log_pos);
+
+ test->tprintf("Connecting to MaxScale binlog router (with any DB)\n");
+ MYSQL * binlog = open_conn_no_db(test->binlog_port, test->maxscale_IP, test->repl->user_name,
+ test->repl->password, test->ssl);
+ test->add_result(mysql_errno(binlog), "Error connection to binlog router %s\n", mysql_error(binlog));
+
+ test->tprintf("show master status on maxscale\n");
+ find_field(binlog, (char *) "show master status", (char *) "File", &maxscale_log_file[0]);
+ find_field(binlog, (char *) "show master status", (char *) "Position", &maxscale_log_pos[0]);
+
+ if (!maxscale_log_file[0] || !maxscale_log_pos[0])
+ {
+ test->add_result(1, "Failed to query for master status");
+ return 1;
+ }
+
+ test->tprintf("Real master file: %s\n", maxscale_log_file);
+ test->tprintf("Real master pos : %s\n", maxscale_log_pos);
+
+ char * p = strchr(maxscale_log_file, '.') + 1;
+ test->tprintf("log file num %s\n", p);
+ int pd;
+ sscanf(p, "%d", &pd);
+ test->tprintf("log file num (d) %d\n", pd);
+ p[0] = '\0';
+ test->tprintf("log file name %s\n", maxscale_log_file);
+ sprintf(maxscale_log_file_new, "%s%06d", maxscale_log_file, pd + 1);
+
+ test->try_query(test->repl->nodes[2], (char *) "reset master");
+ test->tprintf("Flush logs %d times\n", pd + 1);
+ for (int k = 0; k < pd + 1; k++)
+ {
+ test->try_query(test->repl->nodes[2], (char *) "flush logs");
+ }
+
+ // Set Maxscale to new master
+ test->try_query(binlog, "stop slave");
+ test->tprintf("configuring Maxscale binlog router\n");
+
+
+ test->tprintf("reconnect to binlog\n");
+ mysql_close(binlog);
+ binlog = open_conn_no_db(test->binlog_port, test->maxscale_IP, test->repl->user_name, test->repl->password,
+ test->ssl);
+ test->add_result(mysql_errno(binlog), "Error connection to binlog router %s\n", mysql_error(binlog));
+
+ char str[1024];
+ //sprintf(str, setup_slave1, test->repl->IP[2], log_file_new, test->repl->port[2]);
+ sprintf(str, setup_slave1, test->repl->IP[2], maxscale_log_file_new, "4", test->repl->port[2]);
+ test->tprintf("change master query: %s\n", str);
+ test->try_query(binlog, str);
+
+ test->try_query(binlog, "start slave");
+
+ test->repl->close_connections();
+
+}
+
+void *disconnect_thread( void *ptr )
+{
+ MYSQL * conn;
+ char cmd[256];
+ int i;
+ conn = open_conn(Test->binlog_port, Test->maxscale_IP, Test->repl->user_name, Test->repl->password,
+ Test->repl->ssl);
+ Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
+ i = 3;
+ while (exit_flag == 0)
+ {
+ sprintf(cmd, "DISCONNECT SERVER %d", i);
+ execute_query(conn, cmd);
+ i++;
+ if (i > Test->repl->N)
+ {
+ i = 3;
+ sleep(30);
+ execute_query(conn, (char *) "DISCONNECT SERVER ALL");
+ }
+ sleep(5);
+ }
+ return NULL;
+}
+
+
+void *transaction_thread( void *ptr )
+{
+ MYSQL * conn;
+ int trans_result = 0;
+
+ conn = open_conn_db_timeout(Test->repl->port[master], Test->repl->IP[master], (char *) "test",
+ Test->repl->user_name, Test->repl->password, 20, Test->repl->ssl);
+ Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
+ create_t1(conn);
+
+ while ((exit_flag == 0))
+ {
+ Test->tprintf("Transaction %d\n", i_trans);
+ trans_result = transaction(conn, i_trans);
+ if (trans_result != 0)
+ {
+ Test->tprintf("Transaction %d failed, doing master failover\n", i_trans);
+ failed_transaction_num = i_trans;
+ Test->tprintf("Closing connection\n");
+ mysql_close(conn);
+ Test->tprintf("Calling select_new_master()\n");
+ select_new_master(Test);
+ master = 2;
+
+ conn = open_conn_db_timeout(Test->repl->port[master], Test->repl->IP[master], (char *) "test",
+ Test->repl->user_name, Test->repl->password, 20, Test->repl->ssl);
+ Test->add_result(mysql_errno(conn), "Error connecting to Binlog router, error: %s\n", mysql_error(conn));
+ Test->tprintf("Retrying transaction %d\n", i_trans);
+ i_trans--;
+ }
+ i_trans++;
+ }
+ i_trans--;
+
+ return NULL;
+}
diff --git a/maxscale-system-test/binlog_enc_aes_cbc.cnf b/maxscale-system-test/binlog_enc_aes_cbc.cnf
new file mode 100644
index 000000000..02fb6e35d
--- /dev/null
+++ b/maxscale-system-test/binlog_enc_aes_cbc.cnf
@@ -0,0 +1,12 @@
+[mysqld]
+plugin-load-add=file_key_management.so
+file_key_management_encryption_algorithm=aes_cbc
+file_key_management_filename = /etc/mariadb_binlog_keys.txt
+encrypt-binlog=1
+
+# Enable checksum
+binlog_checksum=CRC32
+
+#Enable large packets handling
+max_allowed_packet=1042M
+innodb_log_file_size=142M
diff --git a/maxscale-system-test/binlog_enc_aes_ctr.cnf b/maxscale-system-test/binlog_enc_aes_ctr.cnf
new file mode 100644
index 000000000..42f082d8c
--- /dev/null
+++ b/maxscale-system-test/binlog_enc_aes_ctr.cnf
@@ -0,0 +1,12 @@
+[mysqld]
+plugin-load-add=file_key_management.so
+file_key_management_encryption_algorithm=aes_ctr
+file_key_management_filename = /etc/mariadb_binlog_keys.txt
+encrypt-binlog=1
+
+# Enable checksum
+binlog_checksum=CRC32
+
+#Enable large packets handling
+max_allowed_packet=1042M
+innodb_log_file_size=142M
diff --git a/maxscale-system-test/binlog_failover.cpp b/maxscale-system-test/binlog_failover.cpp
new file mode 100644
index 000000000..15b4139bb
--- /dev/null
+++ b/maxscale-system-test/binlog_failover.cpp
@@ -0,0 +1,16 @@
+/**
+ * @file binlog_failover.cpp binlog_failover Test of failover scenarion for binlog router
+ *
+ * - setup following configuration:
+ * - one master
+ * - two maxscale binlog machines
+ * - two slaves connected to each maxscale binlog mashine
+ * - put some date via master
+ * - block master
+ * - stop all Maxscale machines with STOP SLAVE command
+ * - check which Maxscale machine contains most recent data (let's call this machine 'most_recent_maxscale')
+ * - use CHANGE MASETER on the second Maxscale machine to point it to the Maxscale machine from the previous step ('most_recent_maxscale')
+ * - wait until second Maxscale is in sync with 'most_recent_maxscale' (use SHOW MASTER STATUS)
+ * - select new master (HOW??)
+ * - set all Maxscale machines to be a slaves of new master
+ */
diff --git a/maxscale-system-test/binlog_incompl.cpp b/maxscale-system-test/binlog_incompl.cpp
new file mode 100644
index 000000000..d0c9021e2
--- /dev/null
+++ b/maxscale-system-test/binlog_incompl.cpp
@@ -0,0 +1,26 @@
+/**
+ * @file setup_incompl trying to start binlog setup with incomplete Maxscale.cnf
+ * check for crash
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(60);
+ Test->connect_maxscale();
+ Test->close_maxscale_connections();
+ sleep(10);
+ Test->check_log_err("fatal signal 11", false);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/binlog_semisync.cpp b/maxscale-system-test/binlog_semisync.cpp
new file mode 100644
index 000000000..143d9da11
--- /dev/null
+++ b/maxscale-system-test/binlog_semisync.cpp
@@ -0,0 +1,91 @@
+/**
+ * @file binlog_semisync.cpp Same test as setup_binlog, but with semisync enabled
+ */
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+
+#include "test_binlog_fnc.h"
+
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->tprintf("Test object initialized\n");
+ Test->set_timeout(3000);
+ int options_set = 3;
+ if (Test->smoke)
+ {
+ options_set = 1;
+ }
+ Test->tprintf("Trying to connect to backend\n");
+ if (Test->repl->connect() == 0)
+ {
+ Test->tprintf("DROP TABLE t1\n");
+ execute_query(Test->repl->nodes[0], (char *) "DROP TABLE IF EXISTS t1;");
+
+ //Test->tprintf("SET GLOBAL rpl_semi_sync_master_enabled = 1;\n");
+ //execute_query(Test->repl->nodes[0], (char *) "SET GLOBAL rpl_semi_sync_master_enabled = 1;");
+ Test->repl->close_connections();
+ sleep(5);
+
+
+ for (int option = 0; option < options_set; option++)
+ {
+ Test->binlog_cmd_option = option;
+ Test->start_binlog();
+ Test->repl->connect();
+ Test->tprintf("install semisync plugin\n");
+ execute_query(Test->repl->nodes[0],
+ (char *) "INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';");
+ //sleep(10);
+ Test->tprintf("Reconnect\n");
+ Test->repl->close_connections();
+ Test->repl->connect();
+ Test->tprintf("SET GLOBAL rpl_semi_sync_master_enabled = 1;\n");
+ execute_query(Test->repl->nodes[0], (char *) "SET GLOBAL rpl_semi_sync_master_enabled = 1;");
+ //sleep(10);
+ Test->repl->close_connections();
+ test_binlog(Test);
+
+ Test->repl->connect();
+ Test->tprintf("SET GLOBAL rpl_semi_sync_master_enabled = 0;\n");
+ execute_query(Test->repl->nodes[0], (char *) "SET GLOBAL rpl_semi_sync_master_enabled = 0;");
+ //sleep(10);
+ Test->repl->close_connections();
+ test_binlog(Test);
+
+ Test->repl->connect();
+ Test->tprintf("uninstall semisync plugin\n");
+ execute_query(Test->repl->nodes[0], (char *) "UNINSTALL PLUGIN rpl_semi_sync_master;");
+ Test->tprintf("Reconnect\n");
+ Test->repl->close_connections();
+ Test->repl->connect();
+ Test->tprintf("SET GLOBAL rpl_semi_sync_master_enabled = 1;\n");
+ execute_query(Test->repl->nodes[0], (char *) "SET GLOBAL rpl_semi_sync_master_enabled = 1;");
+ //sleep(10);
+ Test->repl->close_connections();
+ test_binlog(Test);
+
+ Test->repl->connect();
+ Test->tprintf("SET GLOBAL rpl_semi_sync_master_enabled = 0;\n");
+ execute_query(Test->repl->nodes[0], (char *) "SET GLOBAL rpl_semi_sync_master_enabled = 0;");
+ sleep(10);
+ Test->repl->close_connections();
+ test_binlog(Test);
+ }
+ }
+ else
+ {
+ Test->add_result(1, "Can't connect to backend\n");
+ }
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/blob_test.cpp b/maxscale-system-test/blob_test.cpp
new file mode 100644
index 000000000..41358bafb
--- /dev/null
+++ b/maxscale-system-test/blob_test.cpp
@@ -0,0 +1,193 @@
+#include "blob_test.h"
+
+int test_longblob(TestConnections* Test, MYSQL * conn, char * blob_name, unsigned long chunk_size, int chunks,
+ int rows)
+{
+ unsigned long size = chunk_size;
+ unsigned long * data;
+ unsigned long i, j;
+ MYSQL_BIND param[1];
+ char sql[256];
+ int global_res = Test->global_result;
+ //Test->tprintf("chunk size %lu chunks %d inserts %d\n", chunk_size, chunks, rows);
+
+ char *insert_stmt = (char *) "INSERT INTO long_blob_table(x, b) VALUES(1, ?)";
+
+ Test->tprintf("Creating table with %s\n", blob_name);
+ Test->try_query(conn, (char *) "DROP TABLE IF EXISTS long_blob_table");
+ sprintf(sql, "CREATE TABLE long_blob_table(id int NOT NULL AUTO_INCREMENT, x INT, b %s, PRIMARY KEY (id))",
+ blob_name);
+ Test->try_query(conn, sql);
+
+ for (int k = 0; k < rows; k++)
+ {
+ Test->tprintf("Preparintg INSERT stmt\n");
+ MYSQL_STMT * stmt = mysql_stmt_init(conn);
+ if (stmt == NULL)
+ {
+ Test->add_result(1, "stmt init error: %s\n", mysql_error(conn));
+ }
+
+ Test->add_result(mysql_stmt_prepare(stmt, insert_stmt, strlen(insert_stmt)), "Error preparing stmt: %s\n",
+ mysql_stmt_error(stmt));
+
+ param[0].buffer_type = MYSQL_TYPE_STRING;
+ param[0].is_null = 0;
+
+ Test->tprintf("Binding parameter\n");
+ Test->add_result(mysql_stmt_bind_param(stmt, param), "Error parameter binding: %s\n", mysql_stmt_error(stmt));
+
+ Test->tprintf("Filling buffer\n");
+ data = (unsigned long *) malloc(size * sizeof(long int));
+
+ if (data == NULL)
+ {
+ Test->add_result(1, "Memory allocation error\n");
+ }
+
+
+
+ Test->tprintf("Sending data in %d bytes chunks, total size is %d\n", size * sizeof(unsigned long),
+ (size * sizeof(unsigned long)) * chunks);
+ for (i = 0; i < chunks; i++)
+ {
+ for (j = 0; j < size; j++)
+ {
+ data[j] = j + i * size;
+ }
+ Test->set_timeout(300);
+ Test->tprintf("Chunk #%d\n", i);
+ if (mysql_stmt_send_long_data(stmt, 0, (char *) data, size * sizeof(unsigned long)) != 0)
+ {
+ Test->add_result(1, "Error inserting data, iteration %d, error %s\n", i, mysql_stmt_error(stmt));
+ return 1;
+ }
+ }
+
+ //for (int k = 0; k < rows; k++)
+ //{
+ Test->tprintf("Executing statement: %02d\n", k);
+ Test->set_timeout(3000);
+ Test->add_result(mysql_stmt_execute(stmt), "INSERT Statement with %s failed, error is %s\n", blob_name,
+ mysql_stmt_error(stmt));
+ //}
+ Test->add_result(mysql_stmt_close(stmt), "Error closing stmt\n");
+ }
+
+ if (global_res == Test->global_result)
+ {
+ Test->tprintf("%s is OK\n", blob_name);
+ }
+ else
+ {
+ Test->tprintf("%s FAILED\n", blob_name);
+ }
+
+ return 0;
+}
+
+int check_longblob_data(TestConnections* Test, MYSQL * conn, unsigned long chunk_size, int chunks,
+ int rows)
+{
+ //char *select_stmt = (char *) "SELECT id, x, b FROM long_blob_table WHERE id = ?";
+ char *select_stmt = (char *) "SELECT id, x, b FROM long_blob_table ";
+ MYSQL_STMT * stmt = mysql_stmt_init(Test->conn_rwsplit);
+ if (stmt == NULL)
+ {
+ Test->add_result(1, "stmt init error: %s\n", mysql_error(Test->conn_rwsplit));
+ }
+
+ Test->add_result(mysql_stmt_prepare(stmt, select_stmt, strlen(select_stmt)), "Error preparing stmt: %s\n",
+ mysql_stmt_error(stmt));
+
+ MYSQL_BIND param[1], result[3];
+ int id = 1;
+
+ memset(param, 0, sizeof(param));
+ memset(result, 0, sizeof(result));
+
+ param[0].buffer_type = MYSQL_TYPE_LONG;
+ param[0].buffer = &id;
+
+ unsigned long * data = (unsigned long *) malloc(chunk_size * chunks * sizeof(long int));
+
+
+ int r_id;
+ int r_x;
+ unsigned long l_id;
+ unsigned long l_x;
+ my_bool b_id;
+ my_bool b_x;
+ my_bool e_id;
+ my_bool e_x;
+
+ result[0].buffer_type = MYSQL_TYPE_LONG;
+ result[0].buffer = &r_id;
+ result[0].buffer_length = 0;
+ result[0].length = &l_id;
+ result[0].is_null = &b_id;
+ result[0].error = &e_id;
+
+ result[1].buffer_type = MYSQL_TYPE_LONG;
+ result[1].buffer = &r_x;
+ result[1].buffer_length = 0;
+ result[1].length = &l_x;
+ result[1].is_null = &b_x;
+ result[1].error = &e_x;
+
+ result[2].buffer_type = MYSQL_TYPE_LONG_BLOB;
+ result[2].buffer = data;
+ result[2].buffer_length = chunk_size * chunks * sizeof(long int);
+
+ /*
+ if (mysql_stmt_bind_param(stmt, param) != 0)
+ {
+ printf("Could not bind parameters\n");
+ return 1;
+ }
+ */
+ if (mysql_stmt_bind_result(stmt, result) != 0)
+ {
+ printf("Could not bind results\n");
+ return 1;
+ }
+
+ if (mysql_stmt_execute(stmt) != 0)
+ {
+ Test->tprintf("Error executing stmt %s\n", mysql_error(Test->conn_rwsplit));
+ }
+
+ if (mysql_stmt_store_result(stmt) != 0)
+ {
+ printf("Could not buffer result set\n");
+ return 1;
+ }
+
+ int row = 0;
+ while (!mysql_stmt_fetch(stmt))
+ {
+ Test->tprintf("id=%d\tx=%d\n", r_id, r_x);
+ if (r_id != row + 1)
+ {
+ Test->add_result(1, "id field is wrong! Expected %d, but it is %d\n", row + 1, r_id);
+ }
+ for (int y = 0; y < chunk_size * chunks; y++)
+ {
+ if (data[y] != y)
+ {
+ Test->add_result(1, "Data is wrong!\n");
+ }
+ //printf("y = %d \t%lu\tid=%d\tx=%d\n", y, data[y], r_id, r_x);
+ }
+ row++;
+ }
+ if (row != rows)
+ {
+ Test->add_result(1, "Wrong number of rows in the table! Expected %d, but it is %d\n", rows, row);
+ }
+ mysql_stmt_free_result(stmt);
+
+ mysql_stmt_close(stmt);
+}
+
+
diff --git a/maxscale-system-test/blob_test.h b/maxscale-system-test/blob_test.h
new file mode 100644
index 000000000..a7f88b888
--- /dev/null
+++ b/maxscale-system-test/blob_test.h
@@ -0,0 +1,32 @@
+#ifndef BLOB_TEST_H
+#define BLOB_TEST_H
+
+#include "testconnections.h"
+
+/**
+ * @brief test_longblob INSERT big amount of data into lobg_blob_table
+ * @param Test TestConnection object
+ * @param conn MYSQL connection handler
+ * @param blob_name blob type (LONGBLOB; MEDIUMBLOB or BLOB)
+ * @param chunk_size size of one data chunk (in sizeof(long usingned))
+ * @param chunks number of chunks to INSERT
+ * @param rows number of rows to INSERT (executes INSERT stetament 'rows' times)
+ * @return 0 in case of success
+ */
+int test_longblob(TestConnections* Test, MYSQL * conn, char * blob_name, unsigned long chunk_size, int chunks,
+ int rows);
+
+
+/**
+ * @brief check_longblob_data Does SELECT against table created by test_longblob() and cheks that data are correct
+ * @param Test TestConnection object
+ * @param conn MYSQL connection handler
+ * @param chunk_size size of one data chunk (in sizeof(long usingned))
+ * @param chunks number of chunks in the table
+ * @param rows number of rows in the table
+ * @return 0 in case of success
+ */
+int check_longblob_data(TestConnections* Test, MYSQL * conn, unsigned long chunk_size, int chunks,
+ int rows);
+
+#endif // BLOB_TEST_H
diff --git a/maxscale-system-test/bug143.cpp b/maxscale-system-test/bug143.cpp
new file mode 100644
index 000000000..16df08866
--- /dev/null
+++ b/maxscale-system-test/bug143.cpp
@@ -0,0 +1,68 @@
+/**
+ * @file bug143.cpp bug143 regression case (MaxScale ignores host in user authentication)
+ *
+ * - create user@'non_existing_host1', user@'%', user@'non_existing_host2' identified by different passwords.
+ * - try to connect using RWSplit. First and third are expected to fail, second should succeed.
+ */
+
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->tprintf("Creating user 'user' with 3 different passwords for different hosts\n");
+ Test->connect_maxscale();
+ execute_query(Test->conn_rwsplit, "CREATE USER 'user'@'non_existing_host1' IDENTIFIED BY 'pass1'");
+ execute_query(Test->conn_rwsplit, "CREATE USER 'user'@'%%' IDENTIFIED BY 'pass2'");
+ execute_query(Test->conn_rwsplit, "CREATE USER 'user'@'non_existing_host2' IDENTIFIED BY 'pass3'");
+ execute_query(Test->conn_rwsplit, "GRANT ALL PRIVILEGES ON *.* TO 'user'@'non_existing_host1'");
+ execute_query(Test->conn_rwsplit, "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%%'");
+ execute_query(Test->conn_rwsplit, "GRANT ALL PRIVILEGES ON *.* TO 'user'@'non_existing_host2'");
+
+ Test->tprintf("Synchronizing slaves");
+ Test->set_timeout(50);
+ Test->repl->sync_slaves();
+
+ Test->tprintf("Trying first hostname, expecting failure");
+ Test->set_timeout(15);
+ MYSQL * conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "user", (char *) "pass1", Test->ssl);
+ if (mysql_errno(conn) == 0)
+ {
+ Test->add_result(1, "MaxScale ignores host in authentication\n");
+ }
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ Test->tprintf("Trying second hostname, expecting success");
+ Test->set_timeout(15);
+ conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "user", (char *) "pass2", Test->ssl);
+ Test->add_result(mysql_errno(conn), "MaxScale can't connect: %s\n", mysql_error(conn));
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ Test->tprintf("Trying third hostname, expecting failure");
+ Test->set_timeout(15);
+ conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "user", (char *) "pass3", Test->ssl);
+ if (mysql_errno(conn) == 0)
+ {
+ Test->add_result(1, "MaxScale ignores host in authentication\n");
+ }
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ execute_query(Test->conn_rwsplit, "DROP USER 'user'@'non_existing_host1'");
+ execute_query(Test->conn_rwsplit, "DROP USER 'user'@'%%'");
+ execute_query(Test->conn_rwsplit, "DROP USER 'user'@'non_existing_host2'");
+ Test->close_maxscale_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug359.cpp b/maxscale-system-test/bug359.cpp
new file mode 100644
index 000000000..28c6a16a2
--- /dev/null
+++ b/maxscale-system-test/bug359.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file bug359.cpp bug359 regression case (router_options in readwritesplit causes errors in error log)
+ *
+ * - Maxscale.cnf contains RWSplit router definition with router_option=slave.
+ * - error is expected in the log. Maxscale should not start.
+ */
+
+
+/*
+Massimiliano 2013-11-22 09:45:13 UTC
+Setting router_options=slave in readwritesplit causes:
+
+in the error log
+
+2013 11/22 10:35:43 Error : Couldn't find suitable Master from 3 candidates.
+2013 11/22 10:35:43 Error : Failed to create router client session. Freeing allocated resources.
+
+
+If no options are allowed here, it could be better to log this and/or unload the module
+
+
+This is something could happen doing copy paste from readconnrouter as an example
+Comment 1 Mark Riddoch 2014-02-05 11:35:57 UTC
+I makes no sense for the read/write splitter to look at the slave and master router options.
+
+Vilho Raatikka 2014-05-22 07:02:50 UTC
+Added check for router option 'synced' which accepts only that, and warns the user of other unsupported options ('master'|'slave' for example). If router option is specified for read write split router, only a node in 'joined' state will be accepted as eligible backend candidate.
+
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->check_log_err((char *) "Unsupported router option \"slave\"", true);
+ Test->check_log_err((char *) "Failed to start all MaxScale services. Exiting", true);
+ Test->check_log_err((char *) "Couldn't find suitable Master", false);
+ //Test->check_maxscale_alive();
+ Test->check_maxscale_processes(0);
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug422.cpp b/maxscale-system-test/bug422.cpp
new file mode 100644
index 000000000..612568a50
--- /dev/null
+++ b/maxscale-system-test/bug422.cpp
@@ -0,0 +1,122 @@
+/**
+ * @file bug422.cpp bug422 regression case ( Executing '\s' doesn't always produce complete result set)
+ *
+ * Test executes "show status" 1000 times against all Maxscale services and checks Maxscale is alive after it.
+ */
+
+/*
+Vilho Raatikka 2014-04-15 07:03:03 UTC
+Read/write split router
+-------------------------
+
+Login to MaxScale & read/write split router, for example
+
+mysql --host=127.0.0.1 -P 4006 -u maxuser -pmaxpwd
+
+Complete result :
+
+MySQL [(none)]> \s
+--------------
+mysql Ver 15.1 Distrib 5.5.33-MariaDB, for Linux (x86_64) using readline 5.1
+
+Connection id: 4051
+Current database:
+Current user: maxuser@localhost
+SSL: Not in use
+Current pager: less
+Using outfile: ''
+Using delimiter: ;
+Server: MySQL
+Server version: MaxScale 0.5.0 Source distribution
+Protocol version: 10
+Connection: 127.0.0.1 via TCP/IP
+Server characterset: latin1
+Db characterset: latin1
+Client characterset: latin1
+Conn. characterset: latin1
+TCP port: 4006
+Uptime: 34 min 23 sec
+
+Threads: 5 Questions: 206 Slow queries: 0 Opens: 0 Flush tables: 2 Open tables: 26 Queries per second avg: 0.099
+--------------
+
+
+By running same a few time in a row, an incomplete result set arrives, like the following:
+
+MySQL [(none)]> \s
+--------------
+mysql Ver 15.1 Distrib 5.5.33-MariaDB, for Linux (x86_64) using readline 5.1
+
+Connection id: 4051
+Current database:
+Current user: maxuser@localhost
+SSL: Not in use
+Current pager: less
+Using outfile: ''
+Using delimiter: ;
+Server: MySQL
+Server version: MaxScale 0.5.0 Source distribution
+Protocol version: 10
+Connection: 127.0.0.1 via TCP/IP
+Server characterset: latin1
+Db characterset: latin1
+Client characterset: latin1
+Conn. characterset: latin1
+TCP port: 4006
+--------------
+
+MySQL [(none)]>
+
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ int i;
+ int iterations = 1000;
+ if (Test->smoke)
+ {
+ iterations = 100;
+ }
+
+ Test->set_timeout(10);
+
+ Test->tprintf("Connecting to all MaxScale services\n");
+ Test->add_result(Test->connect_maxscale(), "Can not connect to Maxscale\n");
+
+ Test->tprintf("executing show status %d times\n", iterations);
+
+
+ for (i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(5);
+ Test->add_result(execute_query(Test->conn_rwsplit, (char *) "show status"),
+ "Query %d agains RWSplit failed\n", i);
+ }
+ for (i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(5);
+ Test->add_result(execute_query(Test->conn_slave, (char *) "show status"),
+ "Query %d agains ReadConn Slave failed\n", i);
+ }
+ for (i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(5);
+ Test->add_result(execute_query(Test->conn_master, (char *) "show status"),
+ "Query %d agains ReadConn Master failed\n", i);
+ }
+ Test->set_timeout(10);
+
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug448.cpp b/maxscale-system-test/bug448.cpp
new file mode 100644
index 000000000..72860e23b
--- /dev/null
+++ b/maxscale-system-test/bug448.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file bug448.cpp bug448 regression case ("Wildcard in host column of mysql.user table don't work properly")
+ *
+ * Test creates user1@xxx.%.%.% and tries to use it to connect
+ */
+
+#include
+#include "testconnections.h"
+#include "get_my_ip.h"
+
+int main(int argc, char *argv[])
+{
+ char my_ip[1024];
+ char my_ip_db[1024];
+ char sql[1024];
+ char * first_dot;
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(20);
+ Test->repl->connect();
+
+ get_my_ip(Test->maxscale_IP, my_ip);
+ Test->tprintf("Test machine IP (got via network request) %s\n", my_ip);
+
+ Test->add_result(Test->get_client_ip(my_ip_db), "Unable to get IP using connection to DB\n");
+
+ Test->tprintf("Test machine IP (got via Show processlist) %s\n", my_ip);
+
+ first_dot = strstr(my_ip, ".");
+ strcpy(first_dot, ".%.%.%");
+
+ Test->tprintf("Test machine IP with %% %s\n", my_ip);
+
+ Test->tprintf("Connecting to Maxscale\n");
+ Test->add_result(Test->connect_maxscale(), "Error connecting to Maxscale\n");
+ Test->tprintf("Creating user 'user1' for %s host\n", my_ip);
+ Test->set_timeout(30);
+
+ Test->add_result(execute_query(Test->conn_rwsplit, "CREATE USER user1@'%s';", my_ip),
+ "Failed to create user");
+ Test->add_result(execute_query(Test->conn_rwsplit,
+ "GRANT ALL PRIVILEGES ON *.* TO user1@'%s' identified by 'pass1'; FLUSH PRIVILEGES;", my_ip),
+ "Failed to grant privileges.");
+
+ Test->tprintf("Trying to open connection using user1\n");
+
+ MYSQL * conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "user1", (char *) "pass1",
+ Test->ssl);
+ if (mysql_errno(conn) != 0)
+ {
+ Test->add_result(1, "TEST_FAILED! Authentification failed! error: %s\n", mysql_error(conn));
+ }
+ else
+ {
+ Test->tprintf("Authentification for user@'%s' is ok", my_ip);
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+ }
+
+ Test->add_result(execute_query(Test->conn_rwsplit, "DROP USER user1@'%s'; FLUSH PRIVILEGES;", my_ip),
+ "Query Failed\n");
+
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug466.cpp b/maxscale-system-test/bug466.cpp
new file mode 100644
index 000000000..fb7d0df78
--- /dev/null
+++ b/maxscale-system-test/bug466.cpp
@@ -0,0 +1,34 @@
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ int global_result = 0;
+
+ Test->ReadEnv();
+ Test->PrintIP();
+
+ Test->ConnectMaxscale();
+
+
+ execute_query(Test->conn_rwsplit, (char *) "select @@server_id; -- max_slave_replication_lag=120");
+
+
+
+
+ Test->CloseMaxscaleConn();
+
+
+ global_result += CheckMaxscaleAlive();
+
+ Test->Copy_all_logs();
+ return global_result;
+}
+
+
diff --git a/maxscale-system-test/bug469.cpp b/maxscale-system-test/bug469.cpp
new file mode 100644
index 000000000..642f4ef95
--- /dev/null
+++ b/maxscale-system-test/bug469.cpp
@@ -0,0 +1,89 @@
+/**
+ * @file bug469 bug469 regression test case ("rwsplit counts every connection twice in master - counnection counts leak")
+ * - use maxadmin command "show server server1" and check "Current no. of conns" and "Number of connections" - both should be 0
+ * - execute simple query against RWSplit
+ * - use maxadmin command "show server server1" and check "Current no. of conns" (should be 0) and "Number of connections" (should be 1)
+ */
+
+
+/*
+Vilho Raatikka 2014-08-05 12:28:21 UTC
+Every connection is counted twice in master and decremented only once. As a result master seems always to have active connections after first connection is established.
+
+Server 0x21706e0 (server1)
+ Server: 127.0.0.1
+ Status: Master, Running
+ Protocol: MySQLBackend
+ Port: 3000
+ Server Version: 5.5.37-MariaDB-debug-log
+ Node Id: 3000
+ Master Id: -1
+ Slave Ids: 3001, 3002 , 3003
+ Repl Depth: 0
+ Number of connections: 6
+ Current no. of conns: 3
+ Current no. of operations: 0
+Server 0x21705e0 (server2)
+ Server: 127.0.0.1
+ Status: Slave, Running
+ Protocol: MySQLBackend
+ Port: 3001
+ Server Version: 5.5.37-MariaDB-debug-log
+ Node Id: 3001
+ Master Id: 3000
+ Slave Ids:
+ Repl Depth: 1
+ Number of connections: 3
+ Current no. of conns: 0
+ Current no. of operations: 0
+
+*/
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ char res[1024];
+ int res_d;
+ Test->set_timeout(10);
+
+ Test->get_maxadmin_param((char *) "show server server1", (char *) "Current no. of conns:", res);
+ sscanf(res, "%d", &res_d);
+ Test->tprintf("Before: Current num of conn %d\n", res_d);
+ Test->add_result(res_d, "curr num of conn is not 0\n");
+ Test->get_maxadmin_param((char *) "show server server1", (char *) "Number of connections:", res);
+ sscanf(res, "%d", &res_d);
+ Test->tprintf("Before: num of conn %d\n", res_d);
+ Test->add_result(res_d, "num of conn is not 0");
+
+ Test->connect_rwsplit();
+ Test->try_query(Test->conn_rwsplit, (char *) "select 1");
+ Test->close_rwsplit();
+
+ Test->stop_timeout();
+ sleep(10);
+
+ Test->set_timeout(10);
+
+ Test->get_maxadmin_param((char *) "show server server1", (char *) "Current no. of conns:", res);
+ sscanf(res, "%d", &res_d);
+ Test->tprintf("After: Current num of conn %d\n", res_d);
+ Test->add_result(res_d, "curr num of conn is not 0\n");
+ Test->get_maxadmin_param((char *) "show server server1", (char *) "Number of connections:", res);
+ sscanf(res, "%d", &res_d);
+ Test->tprintf("After: num of conn %d\n", res_d);
+ if (res_d != 1)
+ {
+ Test->add_result(1, "num of conn is not 1");
+ }
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug471.cpp b/maxscale-system-test/bug471.cpp
new file mode 100644
index 000000000..da0185f00
--- /dev/null
+++ b/maxscale-system-test/bug471.cpp
@@ -0,0 +1,193 @@
+/**
+ * @file bug471.cpp bug471 regression case ( Routing Hints route to server sometimes doesn't work )
+ *
+ * - try "select @@server_id; -- maxscale route to server server%d" (where %d - server number) and compares result
+ * with "select @@server_id;" sent directly to backend node.
+ * - do it 25 times.
+ */
+
+/*
+Massimiliano 2014-08-06 13:27:05 UTC
+I found using basic routing hints such as:
+
+ select @@server_id; -- maxscale route to server server4
+ select @@server_id; -- maxscale route to server server3
+ select @@server_id; -- maxscale route to server server2
+
+server3 is the current master
+
+and server4 server_id is 4
+server3 server_id is 3
+server2 server_id is 2
+
+sometimes I cannot get the expected results that are:
+
+4
+3
+2
+
+
+Sometimes I got:
+
+2
+3
+2
+
+or
+
+4
+3
+4
+
+In maxScale configuration 5 servers defined:
+
+server1 is not monitored but listed in [RW Split Service]
+server5 is always stopped
+
+
+
+MaxScale configuration:
+
+[gateway]
+threads=4
+
+[RW Split Service]
+#Please note server1 is not monitored n MySQL Monitor section
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server5,server4
+max_slave_connections=100%
+max_slave_replication_lag=21
+user=massi
+passwd=massi
+enable_root_user=0
+filters=Hint
+
+# Definition of the servers
+[server1]
+#not monitored
+type=server
+address=127.0.0.1
+port=3306
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=127.0.0.1
+port=3307
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=127.0.0.1
+port=3308
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=127.0.0.1
+port=3309
+protocol=MySQLBackend
+
+[server5]
+#always stopped
+type=server
+address=127.0.0.1
+port=3310
+protocol=MySQLBackend
+
+
+[RW Split Listener]
+type=listener
+service=RW Split Service
+protocol=MySQLClient
+port=4606
+
+[Hint]
+type=filter
+module=hintfilter
+
+[MySQL Monitor]
+# Please note server1 is not monitored
+type=monitor
+module=mysqlmon
+servers=server4,server2,server3,server5
+user=massi
+passwd=massi
+detect_replication_lag=1
+monitor_interval=10001
+
+
+
+Removing server1 from the service section gives right results for server_id selection
+
+
+
+# mysql -c -h 127.0.0.1 -P 4606 -umassi -pmassi
+MariaDB> select @@server_id; -- maxscale route to server server4
+
+
+
+Please not -c option that allows comments to be sent
+
+
+Vilho Raatikka 2014-08-08 08:13:42 UTC
+After further consideration we decided that the behavior is invalid. Routing hints should be followed if they don't violate database consistency nor cluster setup.
+Comment 11 Vilho Raatikka 2014-08-08 17:26:25 UTC
+Pushed fix in commit d4de582e1622908cc95396f57878f8691289c35b to Z2.
+Replication lag is not checked if routing hint is used.
+
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->repl->connect();
+ Test->add_result(Test->connect_maxscale(), "Failed to connect to MaxScale\n");
+
+ char server_id[256];
+ char server_id_d[256];
+
+ char hint_sql[64];
+
+ for (int i = 1; i < 25; i++)
+ {
+ for (int j = 0; j < Test->repl->N; j++)
+ {
+ if (j != 1 )
+ {
+ Test->set_timeout(5);
+ sprintf(hint_sql, "select @@server_id; -- maxscale route to server server%d", j + 1);
+
+ find_field(Test->conn_rwsplit, hint_sql, (char *) "@@server_id", &server_id[0]);
+ find_field(Test->repl->nodes[j], (char *) "select @@server_id;", (char *) "@@server_id", &server_id_d[0]);
+
+ Test->tprintf("server%d ID from Maxscale: \t%s\n", j + 1, server_id);
+ Test->tprintf("server%d ID directly from node: \t%s\n", j + 1, server_id_d);
+
+ Test->add_result(strcmp(server_id, server_id_d), "Hints does not work!\n");
+ }
+ }
+ }
+
+ Test->set_timeout(10);
+
+ Test->close_maxscale_connections();
+ Test->repl->close_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug473.cpp b/maxscale-system-test/bug473.cpp
new file mode 100644
index 000000000..33278f0f9
--- /dev/null
+++ b/maxscale-system-test/bug473.cpp
@@ -0,0 +1,98 @@
+/**
+ * @file bug473.cpp bug470, 472, 473 regression cases ( malformed hints cause crash )
+ *
+ * Test tries different hints with syntax errors (see source for details)
+ */
+
+/*
+Markus Mäkelä 2014-08-07 09:21:44 UTC
+All of the following queries cause a segmentation fault:
+
+select @@server_id; -- maxscale route to server =(
+select @@server_id; -- maxscale route to server =)
+select @@server_id; -- maxscale route to server =:
+select @@server_id; -- maxscale route to server =a
+select @@server_id; -- maxscale route to server = a
+
+Most likely all variatios with the equals sign and a character after it cause the crash.
+
+Call stack:
+
+#0 __strncasecmp_l_sse2 () at ../sysdeps/x86_64/strcmp.S:209
+#1 0x00007fffdd0e9d0d in get_route_target (qtype=QUERY_TYPE_READ, trx_active=false, hint=0x74b830) at readwritesplit.c:1116
+#2 0x00007fffdd0ea494 in routeQuery (instance=0x72f960, router_session=0x73dbf0, querybuf=0x74b7a0) at readwritesplit.c:1346
+#3 0x00007fffd7191ed8 in routeQuery (instance=0x74b670, session=0x74b6b0, queue=0x74b7a0) at hintfilter.c:236
+#4 0x00007fffdc2a0b3d in route_by_statement (session=0x744ae0, readbuf=0x0) at mysql_client.c:1442
+#5 0x00007fffdc29f22c in gw_read_client_event (dcb=0x7446b0) at mysql_client.c:786
+#6 0x00000000004165da in poll_waitevents (arg=0x0) at poll.c:424
+#7 0x000000000040a72c in main (argc=4, argv=0x7fffffffe2e8) at gateway.c:1379
+
+Failing point:
+
+1114 else if (hint->type == HINT_PARAMETER)
+1115 {
+1116 if (strncasecmp(
+1117 (char *)hint->data,
+1118 "max_slave_replication_lag",
+1119 strlen("max_slave_replication_lag")) == 0)
+1120 {
+
+
+Value of hint:
+
+$6 = {type = HINT_PARAMETER, data = 0x0, value = 0x743a60, dsize = 0, next = 0x0}
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->add_result(Test->connect_maxscale(), "Can not connect to Maxscale\n");
+
+
+ Test->tprintf("Trying queries that caused crashes before fix: bug473\n");
+
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale route to server =(");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale route to server =)");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale route to server =:");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale route to server =a");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale route to server = a");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "select @@server_id; -- maxscale route to server = кириллица åäö");
+
+ // bug472
+ Test->tprintf("Trying queries that caused crashes before fix: bug472\n");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "select @@server_id; -- maxscale s1 begin route to server server3");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale end");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale s1 begin");
+
+ // bug470
+ Test->tprintf("Trying queries that caused crashes before fix: bug470\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id; -- maxscale named begin route to master");
+ Test->try_query(Test->conn_rwsplit, (char *) "select @@server_id;");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "select @@server_id; -- maxscale named begin route to master; select @@server_id;");
+
+
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Checking if Maxscale is alive\n");
+ fflush(stdout);
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug475.cpp b/maxscale-system-test/bug475.cpp
new file mode 100644
index 000000000..a1e03f214
--- /dev/null
+++ b/maxscale-system-test/bug475.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file bug475.cpp regression case for bug 475 (The end comment tag in hints isn't properly detected)
+ *
+ * Test tries different hints with comments syntax and then checks log and checks if MaxScale is alive
+ */
+
+//
+//Markus Mäkelä 2014-08-08 10:09:48 UTC
+//The closing tag isn't properly detected when using inline comments.
+//Multiple commands cause this behaviour. The following commands cause these messages in the error log:
+
+//select /* maxscale hintname prepare route to master */ @@server_id;
+//2014 08/08 13:01:09 Error : Syntax error in hint. Expected 'master', 'slave', or 'server' instead of '*/'. Hint ignored.
+
+//select /* maxscale hintname begin */ @@server_id;
+//2014 08/08 13:02:45 Error : Syntax error in hint. Expected '=', 'prepare', or 'start' instead of '@@server_id'. Hint ignored.
+
+//The following only happens when no whitespace is used after 'master' and '*/':
+//select /* maxscale route to master*/ @@server_id;
+//2014 08/08 13:04:38 Error : Syntax error in hint. Expected 'master', 'slave', or 'server' instead of 'master*/'. Hint ignored.
+
+//All other forms of '/* maxscale route to [slave|server ]*/' work even without the whitespace before the closing tag.
+
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->connect_maxscale();
+
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "select /* maxscale hintname prepare route to master */ @@server_id;");
+ Test->try_query(Test->conn_rwsplit, (char *) "select /* maxscale hintname begin */ @@server_id;");
+ Test->try_query(Test->conn_rwsplit, (char *) "select /* maxscale route to master*/ @@server_id;");
+
+ Test->check_log_err((char *) "Syntax error in hint", false);
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug479.cpp b/maxscale-system-test/bug479.cpp
new file mode 100644
index 000000000..bb7c4a311
--- /dev/null
+++ b/maxscale-system-test/bug479.cpp
@@ -0,0 +1,35 @@
+/**
+ * @file bug479.cpp regression case for bug 479 ( Undefined filter reference in MaxScale.cnf causes a crash)
+ *
+ * - Maxscale.cnf with "filters=non existing filter | не существуюший фильтер", cheks error log for warnings and
+ * - check if Maxscale is alive
+ */
+
+
+/*
+Markus Mäkelä 2014-08-15 17:38:06 UTC
+Undefined filters in services cause a crash when the service is accessed.
+
+How to reproduce:
+Define a service with a filter not defined in the MaxScale.cnf file, start MaxScale and access the service.
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->check_log_err((char *) "Unable to find filter 'non existing filter", true);
+ //global_result =Test->check_log_err((char *) "не существуюший фильтер", true);
+ //global_result +=Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug488.cpp b/maxscale-system-test/bug488.cpp
new file mode 100644
index 000000000..4968698b7
--- /dev/null
+++ b/maxscale-system-test/bug488.cpp
@@ -0,0 +1,125 @@
+/**
+ * @file bug488.cpp regression case for bug 488 ("SHOW VARIABLES randomly failing with "Lost connection to MySQL server")
+ *
+ * - try "SHOW VARIABLES;" 100 times against all Maxscale services
+ * First round: 100 iterations for RWSplit, then ReadConn Master, then ReadConn Slave
+ * Second round: 100 iteration and in every iterations all three Maxscale services are in use.
+ * - check if Maxscale is alive.
+ */
+
+/*
+Kolbe Kegel 2014-08-27 18:37:14 UTC
+Created attachment 138 [details]
+good.txt and bad.txt
+
+Sending "SHOW VARIABLES" to MaxScale seems to sometimes result in "ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query". It appears to be random. It seems to be sending the query to the same backend server, so I'm not sure what is happening. I'm including the debug log for both the "good" and "bad" queries.
+Comment 1 Vilho Raatikka 2014-08-27 18:41:25 UTC
+Seems to happen exactly every second time with rwsplit router. Didn't experience it with read connection router.
+Comment 2 Kolbe Kegel 2014-08-27 18:47:13 UTC
+Not exactly every 2nd time.
+
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
+$ mysql -h max1 -P 4006 -u maxuser -pmaxpwd -e 'show variables' >/dev/null
+ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query
+Comment 3 Vilho Raatikka 2014-08-28 10:48:39 UTC
+SHOW VARIABLES is session write command - which is unnecessary because those could be read from any server - and what causes client to return 'lost connection' message to the user is duplicated response packet from MaxScale to the client.
+
+SHOW VARIABLES response should start like this:
+T 127.0.0.1:59776 -> 127.0.0.1:4006 [AP]
+ 0f 00 00 00 03 73 68 6f 77 20 76 61 72 69 61 62 .....show variab
+ 6c 65 73 les
+
+T 127.0.0.1:4006 -> 127.0.0.1:59776 [AP]
+ 01 00 00 01 02 .....
+
+T 127.0.0.1:4006 -> 127.0.0.1:59776 [AP]
+ 54 00 00 02 03 64 65 66 12 69 6e 66 6f 72 6d 61 T....def.informa
+ 74 69 6f 6e 5f 73 63 68 65 6d 61 09 56 41 52 49 tion_schema.VARI
+ 41 42 4c 45 53 09 56 41 52 49 41 42 4c 45 53 0d ABLES.VARIABLES.
+ 56 61 72 69 61 62 6c 65 5f 6e 61 6d 65 0d 56 41 Variable_name.VA
+ 52 49 41 42 4c 45 5f 4e 41 4d 45 0c 08 00 40 00 RIABLE_NAME...@.
+ 00 00 fd 01 00 00 00 00 ........
+
+While in the failing case the initial packet is followed something like this:
+
+T 127.0.0.1:59776 -> 127.0.0.1:4006 [AP]
+ 0f 00 00 00 03 73 68 6f 77 20 76 61 72 69 61 62 .....show variab
+ 6c 65 73 les
+
+T 127.0.0.1:4006 -> 127.0.0.1:59776 [AP]
+ 1d 00 00 d5 18 69 6e 6e 6f 64 62 5f 75 73 65 5f .....innodb_use_
+ 61 74 6f 6d 69 63 5f 77 72 69 74 65 73 03 4f 46 atomic_writes.OF
+ 46 F
+
+T 127.0.0.1:4006 -> 127.0.0.1:59776 [AP]
+ 19 00 00 d6 14 69 6e 6e 6f 64 62 5f 75 73 65 5f .....innodb_use_
+ 66 61 6c 6c 6f 63 61 74 65 03 4f 46 46 fallocate.OFF
+
+- where those innodb related packets are duplicates from the previous response.
+
+*/
+
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ int i;
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ Test->tprintf("Trying SHOW VARIABLES to different Maxscale services\n");
+ fflush(stdout);
+ Test->tprintf("RWSplit\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(5);
+ Test->try_query(Test->conn_rwsplit, (char *) "SHOW VARIABLES;");
+ }
+ Test->tprintf("ReadConn master\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(5);
+ Test->try_query(Test->conn_master, (char *) "SHOW VARIABLES;");
+ }
+ Test->tprintf("ReadConn slave\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(5);
+ Test->try_query(Test->conn_slave, (char *) "SHOW VARIABLES;");
+ }
+
+ Test->tprintf("All in one loop\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(5);
+ Test->try_query(Test->conn_rwsplit, (char *) "SHOW VARIABLES;");
+ Test->try_query(Test->conn_master, (char *) "SHOW VARIABLES;");
+ Test->try_query(Test->conn_slave, (char *) "SHOW VARIABLES;");
+ }
+
+ Test->set_timeout(10);
+ Test->close_maxscale_connections();
+ Test->repl->close_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug493.cpp b/maxscale-system-test/bug493.cpp
new file mode 100644
index 000000000..7ef712f6b
--- /dev/null
+++ b/maxscale-system-test/bug493.cpp
@@ -0,0 +1,90 @@
+/**
+ * @file bug493.cpp regression case for bug 493 ( Can have same section name multiple times without warning)
+ *
+ * - Maxscale.cnf in which 'server2' is defined twice and tests checks error log for proper error message.
+ * - check if Maxscale is alive
+ */
+
+/*
+Hartmut Holzgraefe 2014-08-31 21:01:06 UTC
+Due to a copy/paste error I ended up with two [server2] sections instead of having [server2] and [server3].
+
+There were no error or warning messages about this in skygw_err1.log or skygw_msg1.log but only the second [server2] was actually used.
+
+
+Configuration file:
+
+---8<------------------
+[maxscale]
+threads=1
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+address=localhost
+port=6603
+
+[server1]
+type=server
+address=127.0.0.1
+port=3000
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=127.0.0.1
+port=3001
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=127.0.0.1
+port=3002
+protocol=MySQLBackend
+-------->8---
+
+maxadmin results:
+
+
+---8<--------------------
+MaxScale> list servers
+Servers.
+-------------------+-----------------+-------+----------------------+------------
+Server | Address | Port | Status | Connections
+-------------------+-----------------+-------+----------------------+------------
+server1 | 127.0.0.1 | 3000 | Running | 0
+server2 | 127.0.0.1 | 3002 | Running | 0
+-------------------+-----------------+-------+----------------------+------------
+------------->8---
+
+So no entry for the first (actually correct) [server2] on port 3001,
+but only for the duplicate 2nd [server2] with port set to 3002 ...
+Comment 1 Mark Riddoch 2014-09-01 16:17:51 UTC
+The ini file parser we use allows multiple sections with the same name and will combine the section contains. Within this restriction we now have added code that will detect the same parameter being set twice and will warn the user.
+
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->check_log_err((char *) "Duplicate section found: server2", true);
+ Test->check_log_err((char *)
+ "Failed to open, read or process the MaxScale configuration file /etc/maxscale.cnf. Exiting", true);
+ //Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug495.cpp b/maxscale-system-test/bug495.cpp
new file mode 100644
index 000000000..cde63223b
--- /dev/null
+++ b/maxscale-system-test/bug495.cpp
@@ -0,0 +1,43 @@
+/**
+ * @file bug495.cpp regression case for bug 495 ( Referring to a nonexisting server in servers=... doesn't even raise a warning )
+ *
+ * - Maxscale.cnf with "servers= server1, server2,server3 ,server4,server5"
+ * but 'server5' is not defined. Test checks error log for proper error message.
+ * - check if Maxscale is alive
+ */
+
+/*
+
+Description Hartmut Holzgraefe 2014-08-31 21:32:09 UTC
+Only [server1] and [server2] are defined,
+service [test_service] and monitor [MySQL monitor]
+refer to a third server "server3" in their servers=...
+list though ...
+
+Nothing in the err or msg log hints towards a problem ...
+(which originally was caused by a copy/paste error that
+also lead to the "duplicate section name" error reported
+earlier ... and which would have been easy to track down
+if either of these problems would at least have raised a
+warning - took me almost an hour to track down the actual
+problem ... :(
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->check_log_err((char *) "Unable to find server", true);
+ Test->check_log_err((char *) "errors were encountered while processing the configuration", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug507.cpp b/maxscale-system-test/bug507.cpp
new file mode 100644
index 000000000..64568575b
--- /dev/null
+++ b/maxscale-system-test/bug507.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file bug507.cpp regression case for bug 507 ( "rw-split router does not send last_insert_id() to master" )
+ *
+ * - "CREATE TABLE t2 (id INT(10) NOT NULL AUTO_INCREMENT, x int, PRIMARY KEY (id));",
+ * - do INSERT using RWsplit
+ * - do "select last_insert_id(), @@server_id" using RWSplit and directly to Master, compare @@server_id
+ *
+ */
+
+/*
+Kolbe Kegel 2014-09-01 14:45:56 UTC
+After inserting a row via the rw-split router, a call to last_insert_id() can go to a slave, producing bad results.
+
+mariadb> select * from t1;
++----+
+| id |
++----+
+| 1 |
+| 4 |
++----+
+2 rows in set (0.00 sec)
+
+mariadb> insert into t1 values ();
+Query OK, 1 row affected (0.00 sec)
+
+mariadb> select * from t1;
++----+
+| id |
++----+
+| 1 |
+| 4 |
+| 7 |
++----+
+3 rows in set (0.00 sec)
+
+mariadb> select last_insert_id();
++------------------+
+| last_insert_id() |
++------------------+
+| 0 |
++------------------+
+1 row in set (0.00 sec)
+
+mariadb> select @@wsrep_node_address, last_insert_id();
++----------------------+------------------+
+| @@wsrep_node_address | last_insert_id() |
++----------------------+------------------+
+| 192.168.30.31 | 7 |
++----------------------+------------------+
+1 row in set (0.00 sec)
+Comment 1 Vilho Raatikka 2014-09-01 17:51:45 UTC
+last_inserted_id() belongs to UNKNOWN_FUNC class to which many read-only system functions belong too. Thus last_inserted_id() was routed to any slave.
+
+Unfortunately I can't confirm wrong behavior since running the same sequence generates same output when connected directly to MariaDB backend. Perhaps there is something required for the table t1 which is not included here?
+Comment 2 Vilho Raatikka 2014-09-01 20:01:35 UTC
+An autoincrement attribute was missing.
+*/
+
+
+#include
+#include "testconnections.h"
+
+const char * sel1 = "select last_insert_id(), @@server_id";
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ if (Test->repl->N < 3)
+ {
+ Test->tprintf("There is not enoght nodes for test\n");
+ delete Test;
+ exit(1);
+ }
+
+ Test->tprintf("Creating table\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE IF EXISTS t2");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "CREATE TABLE t2 (id INT(10) NOT NULL AUTO_INCREMENT, x int, PRIMARY KEY (id));");
+ Test->tprintf("Doing INSERTs\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_rwsplit, (char *) "insert into t2 (x) values (1);");
+
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+
+ Test->set_timeout(20);
+ Test->tprintf("Trying \n");
+ char last_insert_id1[1024];
+ char last_insert_id2[1024];
+ if ( (
+ find_field(
+ Test->conn_rwsplit, sel1,
+ "@@server_id", &last_insert_id1[0])
+ != 0 ) || (
+ find_field(
+ Test->repl->nodes[0], sel1,
+ "@@server_id", &last_insert_id2[0])
+ != 0 ))
+ {
+ Test->tprintf("@@server_id fied not found!!\n");
+ delete Test;
+ exit(1);
+ }
+ else
+ {
+ Test->tprintf("'%s' to RWSplit gave @@server_id %s\n", sel1, last_insert_id1);
+ Test->tprintf("'%s' directly to master gave @@server_id %s\n", sel1, last_insert_id2);
+ Test->add_result(strcmp(last_insert_id1, last_insert_id2),
+ "last_insert_id() are different depending in which order terms are in SELECT\n");
+ }
+
+ Test->close_maxscale_connections();
+ Test->repl->close_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug509.cpp b/maxscale-system-test/bug509.cpp
new file mode 100644
index 000000000..56bd204a7
--- /dev/null
+++ b/maxscale-system-test/bug509.cpp
@@ -0,0 +1,144 @@
+/**
+ * @file bug509.cpp regression case for bug 509 and 507 ( "Referring to a nonexisting server in servers=... doesn't even raise a warning"
+ * and "rw-split router does not send last_insert_id() to master" )
+ *
+ * - "CREATE TABLE t2 (id INT(10) NOT NULL AUTO_INCREMENT, x int, PRIMARY KEY (id));",
+ * - do a number of INSERTs first using RWsplit, then directly Galera nodes.
+ * - do "select @@wsrep_node_address, last_insert_id();" and "select last_insert_id(), @@wsrep_node_address;" and compares results.
+ * - do "insert into t2 (x) values (i);" 1000 times and compares results of
+ * "select @@wsrep_node_address, last_insert_id();" and "select last_insert_id(), @@wsrep_node_address;"
+ *
+ * Test fails if results are different (after 5 seconds of waiting after last INSERT)
+ */
+
+/*
+Kolbe Kegel 2014-09-01 14:48:12 UTC
+For some reason, the order of terms in the field list of a SELECT statement influences how the rw-split router decides where to send a statement.
+
+mariadb> select @@wsrep_node_address, last_insert_id();
++----------------------+------------------+
+| @@wsrep_node_address | last_insert_id() |
++----------------------+------------------+
+| 192.168.30.31 | 7 |
++----------------------+------------------+
+1 row in set (0.00 sec)
+
+mariadb> select last_insert_id(), @@wsrep_node_address;
++------------------+----------------------+
+| last_insert_id() | @@wsrep_node_address |
++------------------+----------------------+
+| 0 | 192.168.30.33 |
++------------------+----------------------+
+1 row in set (0.00 sec)
+Comment 1 Vilho Raatikka 2014-09-03 20:44:17 UTC
+Added code to detect last_insert_id() function and now both types of elements are routed to master and their order of appearance doesn't matter.
+*/
+
+
+#include
+#include "testconnections.h"
+
+const char * sel1 = "select @@wsrep_node_address, last_insert_id();";
+const char * sel2 = "select last_insert_id(), @@wsrep_node_address;";
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(60);
+
+ Test->galera->connect();
+ Test->connect_maxscale();
+
+ if (Test->galera->N < 3)
+ {
+ Test->tprintf("There is not enoght nodes for test\n");
+ delete Test;
+ exit(1);
+ }
+
+ Test->tprintf("Creating table\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE IF EXISTS t2;");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "CREATE TABLE t2 (id INT(10) NOT NULL AUTO_INCREMENT, x int, PRIMARY KEY (id));");
+ Test->tprintf("Doing INSERTs\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "insert into t2 (x) values (1);");
+
+ Test->try_query(Test->galera->nodes[0], (char *) "insert into t2 (x) values (2);");
+ Test->try_query(Test->galera->nodes[0], (char *) "insert into t2 (x) values (3);");
+
+ Test->try_query(Test->galera->nodes[1], (char *) "insert into t2 (x) values (4);");
+ Test->try_query(Test->galera->nodes[1], (char *) "insert into t2 (x) values (5);");
+ Test->try_query(Test->galera->nodes[1], (char *) "insert into t2 (x) values (6);");
+
+ Test->try_query(Test->galera->nodes[2], (char *) "insert into t2 (x) values (7);");
+ Test->try_query(Test->galera->nodes[2], (char *) "insert into t2 (x) values (8);");
+ Test->try_query(Test->galera->nodes[2], (char *) "insert into t2 (x) values (9);");
+ Test->try_query(Test->galera->nodes[2], (char *) "insert into t2 (x) values (10);");
+
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+
+ Test->tprintf("Trying \n");
+ char last_insert_id1[1024];
+ char last_insert_id2[1024];
+ if ( (
+ find_field(
+ Test->conn_rwsplit, sel1,
+ "last_insert_id()", &last_insert_id1[0])
+ != 0 ) || (
+ find_field(
+ Test->conn_rwsplit, sel2,
+ "last_insert_id()", &last_insert_id2[0])
+ != 0 ))
+ {
+ Test->tprintf("last_insert_id() fied not found!!\n");
+ delete Test;
+ exit(1);
+ }
+ else
+ {
+ Test->tprintf("'%s' gave last_insert_id() %s\n", sel1, last_insert_id1);
+ Test->tprintf("'%s' gave last_insert_id() %s\n", sel2, last_insert_id2);
+ Test->add_result(strcmp(last_insert_id1, last_insert_id2),
+ "last_insert_id() are different depending in which order terms are in SELECT\n");
+ }
+
+ char id_str[1024];
+ char str1[1024];
+ int iterations = 150;
+
+ for (int i = 100; i < iterations; i++)
+ {
+ Test->set_timeout(50);
+ Test->add_result(execute_query(Test->conn_rwsplit, "insert into t2 (x) values (%d);", i), "Query failed");
+
+ sprintf(str1, "select * from t2 where x=%d;", i);
+
+ find_field(Test->conn_rwsplit, sel1, "last_insert_id()", &last_insert_id1[0]);
+ find_field(Test->conn_rwsplit, str1, "id", &id_str[0]);
+
+ int n = 0;
+
+ while (strcmp(last_insert_id1, id_str) != 0 && n < 5)
+ {
+ Test->tprintf("Replication is lagging");
+ sleep(1);
+ find_field(Test->conn_rwsplit, str1, "id", &id_str[0]);
+ n++;
+ }
+
+ Test->add_result(strcmp(last_insert_id1, id_str),
+ "last_insert_id is not equal to id even after waiting 5 seconds");
+
+ if (i % 10 == 0)
+ {
+ Test->tprintf("last_insert_id is %s, id is %s", last_insert_id1, id_str);
+ }
+ }
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug519.cpp b/maxscale-system-test/bug519.cpp
new file mode 100644
index 000000000..9cfb474bb
--- /dev/null
+++ b/maxscale-system-test/bug519.cpp
@@ -0,0 +1,152 @@
+/**
+ * @file bug519.cpp - Jira task is MAX-345
+ * - fill t1 with data
+ * - execute SELECT * INTO OUTFILE '/tmp/t1.csv' FROM t1; against all routers
+ * - DROP TABLE t1
+ * - LOAD DATA LOCAL INFILE 't1.csv' INTO TABLE t1; using RWSplit
+ * - check if t1 contains right data
+ * - DROP t1 again and repeat LOAD DATA LOCAL INFILE 't1.csv' INTO TABLE t1; using ReadConn master
+ */
+
+/*
+
+It seems that LOAD DATA LOCAL INFILE is not handled by readwritesplit? Maybe it's a bigger problem elsewhere in MaxScale?
+
+I can execute the command, it looks like it is getting sent to the master, but ... no data is actually loaded. Does/can MaxScale handle LOAD DATA LOCAL INFILE?
+Comment 1 Kolbe Kegel 2014-09-03 02:39:47 UTC
+The LOAD DATA LOCAL INFILE statement is stuck in "Reading from net" until some timeout is hit:
+
+| 22 | maxuser | 192.168.30.38:59996 | test | Query | 10 | Reading from net | load data local infile '/Users/kolbe/Devel/seattleparking/Street_Parking_Signs.csv' into table parki | 0.000 |
+
+The client never sees the statement end, though, even after the server has long ago killed its connection...
+
+When I start a *new* connection to MaxScale and I try to execute the LOAD DATA LOCAL INFILE statement again, I have some problems:
+
+mysql 5.5.38-MariaDB (maxuser) [test]> source /Users/kolbe/Devel/seattleparking/loaddata.sql
+ERROR 2013 (HY000) at line 1 in file: '/Users/kolbe/Devel/seattleparking/loaddata.sql': Lost connection to MySQL server during query
+mysql 5.5.38-MariaDB (maxuser) [test]> select @@wsrep_node_address;
+ERROR 2006 (HY000): MySQL server has gone away
+No connection. Trying to reconnect...
+Connection id: 1709
+Current database: test
+
++----------------------+
+| @@wsrep_node_address |
++----------------------+
+| 192.168.30.32 |
++----------------------+
+1 row in set (0.01 sec)
+
+mysql 5.5.38-MariaDB (maxuser) [test]> source /Users/kolbe/Devel/seattleparking/loaddata.sql
+ERROR 2013 (HY000) at line 1 in file: '/Users/kolbe/Devel/seattleparking/loaddata.sql': Lost connection to MySQL server during query
+mysql 5.5.38-MariaDB (maxuser) [test]> select @@wsrep_node_address;
+ERROR 2006 (HY000): MySQL server has gone away
+No connection. Trying to reconnect...
+Connection id: 1709
+Current database: test
+
++----------------------+
+| @@wsrep_node_address |
++----------------------+
+| 192.168.30.32 |
++----------------------+
+1 row in set (0.01 sec)
+
+mysql 5.5.38-MariaDB (maxuser) [test]> source /Users/kolbe/Devel/seattleparking/loaddata.sql
+ERROR 2013 (HY000) at line 1 in file: '/Users/kolbe/Devel/seattleparking/loaddata.sql': Lost connection to MySQL server during query
+mysql 5.5.38-MariaDB (maxuser) [test]> Bye
+*/
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ int N = 4;
+ int iterations = 2;
+ if (Test->smoke)
+ {
+ iterations = 1;
+ }
+ char str[1024];
+ Test->set_timeout(10);
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+
+ Test->tprintf("Create t1\n");
+ create_t1(Test->conn_rwsplit);
+ Test->tprintf("Insert data into t1\n");
+ Test->set_timeout(60);
+ insert_into_t1(Test->conn_rwsplit, N);
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+ Test->set_timeout(200);
+
+ sprintf(str, "%s rm -f /tmp/t*.csv; %s chmod 777 /tmp", Test->repl->access_sudo[0],
+ Test->repl->access_sudo[0]);
+ Test->tprintf("%s\n", str);
+ for (int k = 0; k < Test->repl->N; k++)
+ {
+ Test->repl->ssh_node(k, str, false);
+ }
+ //system(str);
+
+ Test->tprintf("Copying data from t1 to file...\n");
+ Test->tprintf("using RWSplit: SELECT * INTO OUTFILE '/tmp/t1.csv' FROM t1;\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "SELECT * INTO OUTFILE '/tmp/t1.csv' FROM t1;");
+ Test->tprintf("using ReadConn master: SELECT * INTO OUTFILE '/tmp/t2.csv' FROM t1;\n");
+ Test->try_query(Test->conn_master, (char *) "SELECT * INTO OUTFILE '/tmp/t2.csv' FROM t1;");
+ Test->tprintf("using ReadConn slave: SELECT * INTO OUTFILE '/tmp/t3.csv' FROM t1;\n");
+ Test->try_query(Test->conn_slave, (char *) "SELECT * INTO OUTFILE '/tmp/t3.csv' FROM t1;");
+
+ Test->tprintf("Copying t1.cvs from Maxscale machine:\n");
+ Test->copy_from_maxscale((char *) "/tmp/t1.csv", (char *) "./");
+ /*sprintf(str,
+ "scp -i %s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s:/tmp/t1.csv ./",
+ Test->repl->sshkey[0], Test->repl->access_user[0], Test->repl->IP[0]);
+ Test->tprintf("%s\n", str);
+ system(str);*/
+
+ MYSQL *srv[2];
+
+ srv[0] = Test->conn_rwsplit;
+ srv[1] = Test->conn_master;
+ for (int i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(100);
+ Test->tprintf("Dropping t1 \n");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE t1;");
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+
+ Test->set_timeout(100);
+ Test->tprintf("Create t1\n");
+ create_t1(Test->conn_rwsplit);
+ Test->tprintf("Loading data to t1 from file\n");
+ Test->try_query(srv[i], (char *) "LOAD DATA LOCAL INFILE 't1.csv' INTO TABLE t1;");
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+
+ Test->set_timeout(100);
+ Test->tprintf("SELECT: rwsplitter\n");
+ Test->add_result(select_from_t1(Test->conn_rwsplit, N), "Wrong data in 't1'");
+ Test->tprintf("SELECT: master\n");
+ Test->add_result(select_from_t1(Test->conn_master, N), "Wrong data in 't1'");
+ Test->tprintf("SELECT: slave\n");
+ Test->add_result(select_from_t1(Test->conn_slave, N), "Wrong data in 't1'");
+ }
+
+ Test->repl->close_connections();
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug526.cpp b/maxscale-system-test/bug526.cpp
new file mode 100644
index 000000000..fa8042c4b
--- /dev/null
+++ b/maxscale-system-test/bug526.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file bug526.cpp regression case for bug 526 ( " Wrong module name crashes maxscale on connect" )
+ *
+ * - Maxscale.cnf with "filters=QLA|testfilter" for RWSplit router service, 'testfilter' is not defined.
+ * - check error log for proper error messages and checks if ReadConn services are alive
+ */
+
+/*
+Hartmut Holzgraefe 2014-09-08 13:08:46 UTC
+I mistyped a module name (for a filter in this case)
+
+ [testfilter]
+ type=filter
+ module=foobar
+
+There were no warnings about this on startup at all, but at the first time trying to connect to a service this filter was used in maxscale crashed with a segmentation fault after writing the following error log entries:
+
+ 2014 09/08 15:00:53 Error : Unable to find library for module: foobar.
+ 2014 09/08 15:00:53 Failed to create filter 'testfilter' for service 'testrouter'.
+ 2014 09/08 15:00:53 Error : Failed to create Read Connection Router session.
+ 2014 09/08 15:00:53 Error : Invalid authentication message from backend. Error : 28000, Msg : Access denied for user 'maxuser'@'localhost' (using password: YES)
+ 2014 09/08 15:00:53 Error : Backend server didn't accept authentication for user denied for user 'maxuser'@'localhost' (using password: YES).
+
+Two problems here:
+
+1) can't check up front that my configuration is valid / without errors without connecting to each defined service
+
+2) maxscale crashes instead of handling this situation gracefully (e.g. ignoring the misconfigured filter, or disabling the service that refers to it alltogether)
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ if (Test->connect_rwsplit() == 0)
+ {
+ Test->add_result(1, "Filter config is broken, but service is started\n");
+ }
+ if (Test->connect_readconn_master() == 0)
+ {
+ Test->add_result(1, "Filter config is broken, but Maxscale is started\n");
+ }
+ if (Test->connect_readconn_slave() == 0)
+ {
+ Test->add_result(1, "Filter config is broken, but Maxscale is started\n");
+ }
+
+ //sleep(5);
+ Test->execute_maxadmin_command((char*) "sync logs");
+ Test->check_log_err((char *) "Unable to find library for module: foobar", true);
+ Test->check_log_err((char *) "Failed to load filter module 'foobar'", true);
+ Test->check_log_err((char *) "Failed to load filter 'testfilter' for service 'RW Split Router'", true);
+ Test->check_log_err((char *)
+ "Failed to open, read or process the MaxScale configuration file /etc/maxscale.cnf. Exiting", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug529.cpp b/maxscale-system-test/bug529.cpp
new file mode 100644
index 000000000..5f5de6d36
--- /dev/null
+++ b/maxscale-system-test/bug529.cpp
@@ -0,0 +1,315 @@
+/**
+ * @file bug529.cpp regression case for bug 529 ( "'Current no. of conns' not going down" )
+ *
+ * - create table, opens 50 connections for every router, fill table with data using these connections.
+ * - check number of connections to Master - failure if there are more then 100 connections to master.
+ * - close RWSptit and ReadConn master connections and check connections to master again.
+ * - create 50 ReadConn slave connection in parallel threads, execute "SELECT * FROM t1" ones for every connections, then
+ * start using one of connections to create "SELECT" load.
+ * - check number of connections to Master again, wait 5 seconds and check number of connections to
+ * master ones more time: now expecting 0 connections to master (fail if there is a least one connection to master).
+ * - close and reopens all ReadConn slave connections in the main thread and check connections to master again
+ * - close all connection in all threads, close parallel thread
+ * - do final 'connections to master' check
+ */
+
+/*
+lisu87 2014-09-08 16:50:29 UTC
+After starting maxscale and putting some traffic to it, the number of current connections to master server are still going up:
+
+Server 0x29e6330 (carlsberg)
+ Server: xxx.xxx.xxx.xxx
+ Status: Master, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Node Id: -1
+ Master Id: -1
+ Repl Depth: -1
+ Number of connections: 58
+ Current no. of conns: 29
+ Current no. of operations: 0
+Server 0x2948f60 (psy-carslave-1)
+ Server: xxx.xxx.xxx.xxx
+ Status: Slave, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Node Id: -1
+ Master Id: -1
+ Repl Depth: -1
+ Number of connections: 0
+ Current no. of conns: 0
+ Current no. of operations: 0
+Server 0x2948e60 (psy-carslave-2)
+ Server: xxx.xxx.xxx.xxx
+ Status: Slave, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Node Id: -1
+ Master Id: -1
+ Repl Depth: -1
+ Number of connections: 29
+ Current no. of conns: 0
+ Current no. of operations: 0
+Comment 1 Vilho Raatikka 2014-09-09 06:53:56 UTC
+Is the version release-1.0beta?
+Does any load cause this or does it require multiple parallel clients, for example?
+Comment 2 lisu87 2014-09-09 07:53:18 UTC
+The version is release-1.0beta.
+
+Even when just one short connection is made the counter of "Current no. of conns" goes up.
+
+Interesting thing is that the amount of current connections for my slave is always exactly two times smaller than "Number of connections":
+
+Server 0x1f51330 (carlsberg)
+ Server: 172.16.76.8
+ Status: Master, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Node Id: -1
+ Master Id: -1
+ Repl Depth: -1
+ Number of connections: 3278
+ Current no. of conns: 1639
+ Current no. of operations: 0
+Comment 3 lisu87 2014-09-11 09:54:34 UTC
+Any update on this one?
+Comment 4 Vilho Raatikka 2014-09-11 10:34:20 UTC
+The problem can't be reproduced with the code I'm working currently, and which will be the one where beta release will be refresed from. Thus, I'd wait till beta refresh is done and see if the problem still exists.
+Comment 5 lisu87 2014-09-11 10:47:32 UTC
+Thank you.
+
+And one more question: is it normal that even if SELECT query has been performed on skave the "Number of connections" counter for master increases too?
+Comment 6 Vilho Raatikka 2014-09-11 11:02:08 UTC
+(In reply to comment #5)
+> Thank you.
+>
+> And one more question: is it normal that even if SELECT query has been
+> performed on skave the "Number of connections" counter for master increases
+> too?
+
+When rwsplit listens port 3333 and when a command like :
+
+mysql -h 127.0.0.1 -P 3333 -u maxscaleuser -ppwd -e "select count(user) from mysql.user"
+
+is executed client connects to MaxScale:3333, and MaxScale connects to master and slave(s). So connection count increases in each of those backends despite of query type.
+
+If you already have a rwsplit session, no new connections should be created when new queries are executed.
+Comment 7 Vilho Raatikka 2014-09-11 12:34:26 UTC
+I built MaxScale from releaes-1.0beta-refresh branch and tested by running 5000 prepared statements in one session to MaxScale/RWSplit and executing 'show servers' in another window. During the run the number of current connections was 1 in each server and after the run all 'current' counters show 0.
+
+If you want me to try with some other use case, describe it and I'll give it a try.
+Comment 8 lisu87 2014-09-11 12:45:37 UTC
+Thanks, Vilho.
+
+I'm building maxscale from that branch now and will retest shortly.
+Comment 9 lisu87 2014-09-11 14:45:26 UTC
+Confirmed. It works fine with 1.0beta-refresh.
+
+Thank you!
+Comment 10 Vilho Raatikka 2014-09-22 10:11:06 UTC
+The problem reappeared later and was eventually fixed in release-1.0beta-refresh commit a41a8d6060c7b60e09686bea8124803f047d85ad
+
+*/
+
+// counting connection to all services
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+using namespace std;
+
+
+pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+int exit_flag = 0;
+int conn_N = 50;
+
+TestConnections * Test ;
+
+void *parall_traffic( void *ptr );
+
+
+int main(int argc, char *argv[])
+{
+
+ Test = new TestConnections(argc, argv);
+ Test->set_timeout(120);
+ int i;
+ int num_conn = 0;
+ char sql[100];
+
+ pthread_t parall_traffic1;
+ int check_iret;
+
+ MYSQL * conn;
+ MYSQL * rwsplit_conn[conn_N];
+ MYSQL * master_conn[conn_N];
+ MYSQL * slave_conn[conn_N];
+
+ Test->repl->connect();
+
+ conn = Test->open_rwsplit_connection();
+ execute_query(conn, (char *) "DROP DATABASE IF EXISTS test");
+ execute_query(conn, (char *) "CREATE DATABASE test");
+ execute_query(conn, (char *) "USE test;");
+
+ create_t1(conn);
+ mysql_close(conn);
+ Test->tprintf("Table t1 is created\n");
+
+ for (i = 0; i < conn_N; i++)
+ {
+ Test->set_timeout(60);
+ rwsplit_conn[i] = Test->open_rwsplit_connection();
+ master_conn[i] = Test->open_readconn_master_connection();
+ slave_conn[i] = Test->open_readconn_slave_connection();
+ sprintf(sql, "INSERT INTO t1 (x1, fl) VALUES(%d, 1);", i);
+ execute_query(rwsplit_conn[i], sql);
+ sprintf(sql, "INSERT INTO t1 (x1, fl) VALUES(%d, 2);", i);
+ execute_query(master_conn[i], sql);
+ fflush(stdout);
+ }
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ Test->tprintf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn > 2 * conn_N))
+ {
+ Test->add_result(1, "too many connections to master\n");
+ }
+ }
+
+ Test->tprintf("Closing RWSptit and ReadConn master connections\n");
+ for (i = 0; i < conn_N; i++)
+ {
+ mysql_close(rwsplit_conn[i]);
+ mysql_close(master_conn[i]);
+ }
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ printf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn > 2 * conn_N))
+ {
+ Test->add_result(1, "too many connections to master\n");
+ }
+ }
+
+ Test->tprintf("Opening more connection to ReadConn slave in parallel thread\n");
+
+ check_iret = pthread_create( ¶ll_traffic1, NULL, parall_traffic, NULL);
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ printf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn > 2 * conn_N))
+ {
+ Test->add_result(1, "too many connections to master\n");
+ }
+ }
+
+ Test->stop_timeout();
+ Test->tprintf("Sleeping 15 seconds\n");
+ sleep(15);
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ printf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn != 0))
+ {
+ Test->add_result(1, "there are still connections to master\n");
+ }
+ }
+
+ printf("Closing ReadConn slave connections\n");
+ for (i = 0; i < conn_N; i++)
+ {
+ mysql_close(slave_conn[i]);
+ }
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ Test->tprintf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn != 0))
+ {
+ Test->add_result(1, "there are still connections to master\n");
+ }
+ }
+
+ Test->tprintf("Opening ReadConn slave connections again\n");
+ for (i = 0; i < conn_N; i++)
+ {
+ Test->set_timeout(60);
+ slave_conn[i] = Test->open_readconn_slave_connection();
+ sprintf(sql, "SELECT * FROM t1");
+ execute_query(slave_conn[i], sql);
+ fflush(stdout);
+ }
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ Test->tprintf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn != 0))
+ {
+ Test->add_result(1, "there are still connections to master\n");
+ }
+ }
+
+ Test->tprintf("Closing ReadConn slave connections\n");
+ for (i = 0; i < conn_N; i++)
+ {
+ Test->set_timeout(20);
+ mysql_close(slave_conn[i]);
+ }
+ exit_flag = 1;
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(60);
+ num_conn = get_conn_num(Test->repl->nodes[i], Test->maxscale_IP, Test->maxscale_hostname, (char *) "test");
+ Test->tprintf("Connections to node %d (%s): %d\n", i, Test->repl->IP[i], num_conn);
+ if ((i == 0) && (num_conn != 0))
+ {
+ Test->add_result(1, "there are still connections to master\n");
+ }
+ }
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
+void *parall_traffic( void *ptr )
+{
+ MYSQL * slave_conn1[conn_N];
+ int i;
+ for (i = 0; i < conn_N; i++)
+ {
+ slave_conn1[i] = Test->open_readconn_slave_connection();
+ execute_query(slave_conn1[i], "SELECT * FROM t1");
+ }
+
+ while (exit_flag == 0)
+ {
+ execute_query(slave_conn1[0], "SELECT * FROM t1");
+ }
+ for (i = 0; i < conn_N; i++)
+ {
+ mysql_close(slave_conn1[i]);
+ }
+
+ return NULL;
+}
diff --git a/maxscale-system-test/bug539.cpp b/maxscale-system-test/bug539.cpp
new file mode 100644
index 000000000..25ce79914
--- /dev/null
+++ b/maxscale-system-test/bug539.cpp
@@ -0,0 +1,56 @@
+/**
+ * @file bug539.cpp regression case for bug539 ("MaxScale crashes in session_setup_filters")
+ * using maxadmin execute "fail backendfd"
+ * try quries against all services
+ * using maxadmin execute "fail clientfd"
+ * try quries against all services
+ * check if MaxScale alive
+ */
+
+
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+ int i, j;
+ MYSQL * conn;
+
+ int N_cmd = 2;
+ char * fail_cmd[N_cmd - 1];
+
+ int N_ports = 3;
+ int ports[N_ports];
+
+ fail_cmd[0] = (char *) "fail backendfd";
+ fail_cmd[1] = (char *) "fail clientfd";
+
+ ports[0] = Test->rwsplit_port;
+ ports[1] = Test->readconn_master_port;
+ ports[2] = Test->readconn_slave_port;
+
+ for (i = 0; i < N_cmd; i++)
+ {
+ for (j = 0; j < N_ports; j++)
+ {
+ Test->tprintf("Executing MaxAdmin command '%s'\n", fail_cmd[i]);
+ if (execute_maxadmin_command(Test->maxscale_IP, (char *) "admin", Test->maxadmin_password, fail_cmd[i]) != 0)
+ {
+ Test->add_result(1, "MaxAdmin command failed\n");
+ }
+ else
+ {
+ printf("Trying query against %d\n", ports[j]);
+ conn = open_conn(ports[j], Test->maxscale_IP, Test->maxscale_user, Test->maxscale_user, Test->ssl);
+ Test->try_query(conn, (char *) "show processlist;");
+ }
+ }
+ }
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug547.cpp b/maxscale-system-test/bug547.cpp
new file mode 100644
index 000000000..8649f14a2
--- /dev/null
+++ b/maxscale-system-test/bug547.cpp
@@ -0,0 +1,72 @@
+/**
+ * @file bug547.cpp regression case for bug 547 and bug 594 ( "get_dcb fails if slaves are not available" and "Maxscale fails to start without anything in the logs if there is no slave available" )
+ * Behaviour has been changed and this test check only for crash
+ * - block all slaves
+ * - try some queries (create table, do INSERT using RWSplit router)
+ * - check there is no crash
+ */
+
+/*
+Vilho Raatikka 2014-09-16 07:43:54 UTC
+get_dcb function returns the backend descriptor for router. Some merge has broken the logic and in case of non-existent slave the router simply fails to find a backend server although master would be available.
+Comment 1 Vilho Raatikka 2014-09-16 09:40:14 UTC
+get_dcb now searches master if slaves are not available.
+*/
+
+// also relates to bug594
+// all slaves in MaxScale config have wrong IP
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ int i;
+
+ for (i = 1; i < Test->repl->N; i++)
+ {
+ Test->set_timeout(20);
+ Test->repl->block_node(i);
+ }
+
+ Test->set_timeout(30);
+ sleep(15);
+
+ Test->set_timeout(30);
+ Test->tprintf("Connecting to all MaxScale services, expecting error\n");
+ Test->connect_maxscale();
+
+ Test->set_timeout(30);
+ Test->tprintf("Trying some queries, expecting failure, but not a crash\n");
+ execute_query(Test->conn_rwsplit, (char *) "DROP TABLE IF EXISTS t1");
+ execute_query(Test->conn_rwsplit, (char *) "CREATE TABLE t1 (x INT)");
+ execute_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 (x) VALUES (1)");
+ execute_query(Test->conn_rwsplit, (char *) "select * from t1");
+ execute_query(Test->conn_master, (char *) "select * from t1");
+ execute_query(Test->conn_slave, (char *) "select * from t1");
+
+ Test->set_timeout(10);
+ Test->close_maxscale_connections();
+
+ Test->set_timeout(30);
+ Test->repl->unblock_all_nodes();
+
+ Test->stop_timeout();
+ sleep(15);
+ Test->check_log_err((char *) "fatal signal 11", false);
+ Test->check_log_err((char *) "Failed to create new router session for service 'RW-Split-Router'", true);
+ Test->check_log_err((char *)
+ "Failed to create new router session for service 'Read-Connection-Router-Master'", true);
+ Test->check_log_err((char *) "Failed to create new router session for service 'Read-Connection-Router-Slave'",
+ true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug561.sh b/maxscale-system-test/bug561.sh
new file mode 100755
index 000000000..981401005
--- /dev/null
+++ b/maxscale-system-test/bug561.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+###
+## @file bug561.sh Regression case for the bug "Different error messages from MariaDB and Maxscale"
+## - try to connect to non existing DB directly to MariaDB server and via Maxscale
+## - compare error messages
+## - repeat for RWSplit, ReadConn
+
+
+rp=`realpath $0`
+export test_dir=`dirname $rp`
+export test_name=`basename $rp`
+
+$test_dir/non_native_setup $test_name
+
+if [ $? -ne 0 ] ; then
+ echo "configuring maxscale failed"
+ exit 1
+fi
+export ssl_options="--ssl-cert=$test_dir/ssl-cert/client-cert.pem --ssl-key=$test_dir/ssl-cert/client-key.pem"
+
+#echo "Waiting for 15 seconds"
+#sleep 15
+
+mariadb_err=`mysql -u$node_user -p$node_password -h $node_000_network $ssl_options --socket=$node_000_socket -P $node_000_port non_existing_db 2>&1`
+maxscale_err=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options non_existing_db 2>&1`
+
+maxscale_err1=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4008 $ssl_options non_existing_db 2>&1`
+maxscale_err2=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4009 $ssl_options non_existing_db 2>&1`
+
+echo "MariaDB message"
+echo "$mariadb_err"
+echo " "
+echo "Maxscale message from RWSplit"
+echo "$maxscale_err"
+echo "Maxscale message from ReadConn master"
+echo "$maxscale_err1"
+echo "Maxscale message from ReadConn slave"
+echo "$maxscale_err2"
+
+res=0
+
+#echo "$maxscale_err" | grep "$mariadb_err"
+if [ "$maxscale_err" != "$mariadb_err" ] ; then
+ echo "Messages are different!"
+ echo "MaxScale: $maxscale_err"
+ echo "Server: $mariadb_err"
+ res=1
+else
+ echo "Messages are same"
+fi
+
+if [ "$maxscale_err1" != "$mariadb_err" ] ; then
+ echo "Messages are different!"
+ echo "MaxScale: $maxscale_err1"
+ echo "Server: $mariadb_err"
+ res=1
+else
+ echo "Messages are same"
+fi
+
+if [ "$maxscale_err2" != "$mariadb_err" ] ; then
+ echo "Messages are different!"
+ echo "MaxScale: $maxscale_err2"
+ echo "Server: $mariadb_err"
+
+ res=1
+else
+ echo "Messages are same"
+fi
+
+$test_dir/copy_logs.sh bug561
+exit $res
diff --git a/maxscale-system-test/bug562.sh b/maxscale-system-test/bug562.sh
new file mode 100755
index 000000000..336547717
--- /dev/null
+++ b/maxscale-system-test/bug562.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+###
+## @file bug562.sh Regression case for the bug "Wrong error message for Access denied error"
+## - try to connect with bad credestials directly to MariaDB server and via Maxscale
+## - compare error messages
+
+rp=`realpath $0`
+export test_dir=`dirname $rp`
+export test_name=`basename $rp`
+
+$test_dir/non_native_setup $test_name
+
+if [ $? -ne 0 ] ; then
+ echo "configuring maxscale failed"
+ exit 1
+fi
+export ssl_options="--ssl-cert=$test_dir/ssl-cert/client-cert.pem --ssl-key=$test_dir/ssl-cert/client-key.pem"
+
+mariadb_err=`mysql -u no_such_user -psome_pwd -h $node_001_network $ssl_option --socket=$node_000_socket test 2>&1`
+maxscale_err=`mysql -u no_such_user -psome_pwd -h $maxscale_IP -P 4006 $ssl_options test 2>&1`
+
+echo "MariaDB message"
+echo "$mariadb_err"
+echo " "
+echo "Maxscale message"
+echo "$maxscale_err"
+
+res=0
+#echo "$maxscale_err" | grep "$mariadb_err"
+echo "$maxscale_err" |grep "ERROR 1045 (28000): Access denied for user 'no_such_user'@'"
+if [ "$?" != 0 ]; then
+ echo "Maxscale message is not ok!"
+ echo "Message: $maxscale_err"
+ res=1
+else
+ echo "Messages are same"
+ res=0
+fi
+
+$test_dir/copy_logs.sh bug562
+exit $res
diff --git a/maxscale-system-test/bug564.sh b/maxscale-system-test/bug564.sh
new file mode 100755
index 000000000..53dc50e08
--- /dev/null
+++ b/maxscale-system-test/bug564.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+###
+## @file bug564.sh Regression case for the bug "Wrong charset settings"
+## - call MariaDB client with different --default-character-set= settings
+## - check output of SHOW VARIABLES LIKE 'char%'
+
+rp=`realpath $0`
+export test_dir=`dirname $rp`
+export test_name=`basename $rp`
+$test_dir/non_native_setup $test_name
+
+if [ $? -ne 0 ] ; then
+ echo "configuring maxscale failed"
+ exit 1
+fi
+export ssl_options="--ssl-cert=$test_dir/ssl-cert/client-cert.pem --ssl-key=$test_dir/ssl-cert/client-key.pem"
+
+for char_set in "latin1" "latin2"
+do
+
+ line1=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options --default-character-set="$char_set" -e "SHOW VARIABLES LIKE 'char%'" | grep "character_set_client"`
+ line2=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options --default-character-set="$char_set" -e "SHOW VARIABLES LIKE 'char%'" | grep "character_set_connection"`
+ line3=`mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options --default-character-set="$char_set" -e "SHOW VARIABLES LIKE 'char%'" | grep "character_set_results"`
+
+ echo $line1 | grep "$char_set"
+ res1=$?
+ echo $line2 | grep "$char_set"
+ res2=$?
+ echo $line3 | grep "$char_set"
+ res3=$?
+
+
+ if [[ $res1 != 0 ]] || [[ $res2 != 0 ]] || [[ $res3 != 0 ]] ; then
+ echo "charset is ignored"
+ mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options --default-character-set="latin2" -e "SHOW VARIABLES LIKE 'char%'"
+ $test_dir/copy_logs.sh bug564
+ exit 1
+ fi
+done
+$test_dir/copy_logs.sh bug564
+exit 0
+
+
diff --git a/maxscale-system-test/bug565.cpp b/maxscale-system-test/bug565.cpp
new file mode 100644
index 000000000..deaa749ff
--- /dev/null
+++ b/maxscale-system-test/bug565.cpp
@@ -0,0 +1,106 @@
+/**
+ * @file bug565.cpp regression case for bug 565 ( "Clients CLIENT_FOUND_ROWS setting is ignored by maxscale" ) MAX-311
+ *
+ * - open connection with CLIENT_FOUND_ROWS flag
+ * - CREATE TABLE t1(id INT PRIMARY KEY, val INT, msg VARCHAR(100))
+ * - INSERT INTO t1 VALUES (1, 1, 'foo'), (2, 1, 'bar'), (3, 2, 'baz'), (4, 2, 'abc')"
+ * - check 'affected_rows' for folloing UPDATES:
+ * + UPDATE t1 SET msg='xyz' WHERE val=2" (expect 2)
+ * + UPDATE t1 SET msg='xyz' WHERE val=2 (expect 0)
+ * + UPDATE t1 SET msg='xyz' WHERE val=2 (expect 2)
+ */
+
+/*
+Hartmut Holzgraefe 2014-10-02 14:27:18 UTC
+Created attachment 155 [details]
+test for mysql_affected_rows() with/without CLIENT_FOUND_ROWS connection flag
+
+Even worse: connections via maxscale always behave as if CLIENT_FOUND_ROWS is set even though the default is NOT having it set.
+
+When doing the same update two times in a row without CLIENT_FOUND_ROWS
+mysql_affected_rows() should return the number of rows actually changed
+by the last query, while with CLIENT_FOUND_ROWS connection flag set the
+number of matching rows is returned, even if the UPDATE didn't change
+any column values.
+
+With a direct connection to mysqld this works as expected,
+through readconnroute(master) I'm always getting the number of matching
+rows (as if CLIENT_FOUND_ROWS was set), and not the number of actually
+changed rows when CLIENT_FOUND_ROWS is not set (which is the default
+behaviour when not setting connection options)
+
+Attaching PHP mysqli test script, result with direct mysqld connection is
+
+ update #1: 2
+ update #2: 0
+ update #3: 2
+
+while through maxscale it is
+
+ update #1: 2
+ update #2: 2
+ update #3: 2
+
+I also verified this using the C API directly to rule out that this is
+a PHP specific problem
+Comment 1 Vilho Raatikka 2014-10-08 14:11:38 UTC
+Client flags are not passed to backend server properly.
+Comment 2 Vilho Raatikka 2014-10-08 19:35:58 UTC
+Pushed initial fix to MAX-311. Waiting for validation for the fix.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+ MYSQL * conn_found_rows;
+ my_ulonglong rows;
+
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ conn_found_rows = open_conn_db_flags(Test->rwsplit_port, Test->maxscale_IP, (char *) "test",
+ Test->maxscale_user, Test->maxscale_password, CLIENT_FOUND_ROWS, Test->ssl);
+
+ Test->set_timeout(30);
+ execute_query(Test->conn_rwsplit, "DROP TABLE IF EXISTS t1");
+ execute_query(Test->conn_rwsplit, "CREATE TABLE t1(id INT PRIMARY KEY, val INT, msg VARCHAR(100))");
+ execute_query(Test->conn_rwsplit,
+ "INSERT INTO t1 VALUES (1, 1, 'foo'), (2, 1, 'bar'), (3, 2, 'baz'), (4, 2, 'abc')");
+
+ Test->set_timeout(30);
+ execute_query_affected_rows(Test->conn_rwsplit, "UPDATE t1 SET msg='xyz' WHERE val=2", &rows);
+ Test->tprintf("update #1: %ld (expeced value is 2)\n", (long) rows);
+ if (rows != 2)
+ {
+ Test->add_result(1, "Affected rows is not 2\n");
+ }
+
+ Test->set_timeout(30);
+ execute_query_affected_rows(Test->conn_rwsplit, "UPDATE t1 SET msg='xyz' WHERE val=2", &rows);
+ Test->tprintf("update #2: %ld (expeced value is 0)\n", (long) rows);
+ if (rows != 0)
+ {
+ Test->add_result(1, "Affected rows is not 0\n");
+ }
+
+ Test->set_timeout(30);
+ execute_query_affected_rows(conn_found_rows, "UPDATE t1 SET msg='xyz' WHERE val=2", &rows);
+ Test->tprintf("update #3: %ld (expeced value is 2)\n", (long) rows);
+ if (rows != 2)
+ {
+ Test->add_result(1, "Affected rows is not 2\n");
+ }
+
+ Test->close_maxscale_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+
+}
diff --git a/maxscale-system-test/bug567.sh b/maxscale-system-test/bug567.sh
new file mode 100755
index 000000000..3799e4317
--- /dev/null
+++ b/maxscale-system-test/bug567.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+###
+## @file bug567.sh Regression case for the bug "Crash if files from /dev/shm/ removed"
+## - try to remove everythign from /dev/shm/$maxscale_pid
+## check if Maxscale is alive
+
+rp=`realpath $0`
+export test_dir=`dirname $rp`
+export test_name=`basename $rp`
+$test_dir/non_native_setup $test_name
+
+if [ $? -ne 0 ] ; then
+ echo "configuring maxscale failed"
+ exit 1
+fi
+export ssl_options="--ssl-cert=$test_dir/ssl-cert/client-cert.pem --ssl-key=$test_dir/ssl-cert/client-key.pem"
+
+#pid=`ssh -i $maxscale_sshkey -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $maxscale_access_user@$maxscale_IP "pgrep maxscale"`
+#echo "Maxscale pid is $pid"
+echo "removing log directory from /dev/shm/"
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ ssh -i $maxscale_sshkey -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $maxscale_access_user@$maxscale_IP "sudo rm -rf /dev/shm/maxscale/*"
+else
+ sudo rm -rf /dev/shm/maxscale/*
+fi
+sleep 1
+echo "checking if Maxscale is alive"
+echo "show databases;" | mysql -u$node_user -p$node_password -h $maxscale_IP -P 4006 $ssl_options
+res=$?
+
+$test_dir/copy_logs.sh bug567
+exit $res
+
diff --git a/maxscale-system-test/bug571.cpp b/maxscale-system-test/bug571.cpp
new file mode 100644
index 000000000..d9e7ab492
--- /dev/null
+++ b/maxscale-system-test/bug571.cpp
@@ -0,0 +1,141 @@
+/**
+ * @file bug571.cpp regression case for bug 571 and bug 585 ( "Using regex filter hangs MaxScale" and "modutil_extract_SQL doesn't work with multiple GWBUF buffers" )
+ *
+ * - Maxscale.cnf
+ * @verbatim
+ [regex]
+ type=filter
+ module=regexfilter
+ match=[Ff][Oo0][rR][mM]
+ replace=FROM
+
+ [r2]
+ type=filter
+ module=regexfilter
+ match=fetch
+ replace=select
+
+ [hints]
+ type=filter
+ module=hintfilter
+
+ [RW Split Router]
+ type=service
+ router= readwritesplit
+ servers=server1, server2, server3,server4
+ user=skysql
+ passwd=skysql
+ max_slave_connections=100%
+ use_sql_variables_in=all
+ router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+ filters=hints|regex|r2
+ @endverbatim
+ * for bug585:
+ * @verbatim
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=skysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=regex|typo
+ @endverbatim
+ * - fetch * from mysql.user;
+ * - fetch count(*) form mysql.user;
+ * - check if Maxscale is alive
+ */
+
+/*
+Vilho Raatikka 2014-10-10 11:09:19 UTC
+Branch:release-1.0.1beta
+Executing :
+
+fetch * from mysql.user
+
+with this config hangs MaxScale
+
+[regex]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][rR][mM]
+replace=FROM
+
+[r2]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+
+[hints]
+type=filter
+module=hintfilter
+
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+user=maxuser
+passwd=maxpwd
+filters=hints|regex|r2
+Comment 1 Vilho Raatikka 2014-10-11 18:55:23 UTC
+If we look at the rewrite function we see that query lacks one character.
+
+T 127.0.0.1:37858 -> 127.0.0.1:4006 [AP]
+ 16 00 00 00 03 66 65 74 63 68 20 69 64 20 66 72 .....fetch id fr
+ 6f 6d 20 74 65 73 74 2e 74 31 om test.t1
+
+T 127.0.0.1:44591 -> 127.0.0.1:3000 [AP]
+ 17 00 00 00 03 73 65 6c 65 63 74 20 69 64 20 66 .....select id f
+ 72 6f 6d 20 74 65 73 74 2e 74 rom test.t
+*/
+
+// the same code is used for bug585
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->tprintf("Connecting to all MaxScale services\n");
+ Test->set_timeout(10);
+ Test->add_result(Test->connect_maxscale(), "Error connectiong to Maxscale\n");
+
+ Test->tprintf("executing fetch * from mysql.user \n");
+ Test->set_timeout(10);
+ Test->try_query(Test->conn_rwsplit, (char *) "fetch * from mysql.user;");
+ Test->set_timeout(10);
+ Test->try_query(Test->conn_rwsplit, (char *) "fetch count(*) form mysql.user;");
+
+ Test->set_timeout(10);
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug572.cpp b/maxscale-system-test/bug572.cpp
new file mode 100644
index 000000000..e6e91c97b
--- /dev/null
+++ b/maxscale-system-test/bug572.cpp
@@ -0,0 +1,46 @@
+/**
+ * @file bug572.cpp regression case for bug 572 ( " If reading a user from users table fails, MaxScale fails" )
+ *
+ * - try GRANT with wrong IP using all Maxscale services:
+ * + GRANT ALL PRIVILEGES ON *.* TO 'foo'@'*.foo.notexists' IDENTIFIED BY 'foo';
+ * + GRANT ALL PRIVILEGES ON *.* TO 'bar'@'127.0.0.*' IDENTIFIED BY 'bar'
+ * + DROP USER 'foo'@'*.foo.notexists'
+ * + DROP USER 'bar'@'127.0.0.*'
+ * - do "select * from mysql.user" using RWSplit to check if Maxsclae crashed
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+void create_drop_bad_user(MYSQL * conn, TestConnections * Test)
+{
+
+ Test->try_query(conn, (char *)
+ "GRANT ALL PRIVILEGES ON *.* TO 'foo'@'*.foo.notexists' IDENTIFIED BY 'foo';");
+ Test->try_query(conn, (char *) "GRANT ALL PRIVILEGES ON *.* TO 'bar'@'127.0.0.*' IDENTIFIED BY 'bar'");
+ Test->try_query(conn, (char *) "DROP USER 'foo'@'*.foo.notexists'");
+ Test->try_query(conn, (char *) "DROP USER 'bar'@'127.0.0.*'");
+}
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ Test->tprintf("Trying GRANT for with bad IP: RWSplit\n");
+ create_drop_bad_user(Test->conn_rwsplit, Test);
+
+ Test->tprintf("Trying SELECT to check if Maxscale hangs\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "select * from mysql.user");
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug587.cpp b/maxscale-system-test/bug587.cpp
new file mode 100644
index 000000000..1a9a96670
--- /dev/null
+++ b/maxscale-system-test/bug587.cpp
@@ -0,0 +1,110 @@
+/**
+ * @file bug587.cpp regression case for the bug 587 ( "Hint filter don't work if listed before regex filter in configuration file" )
+ *
+ * - Maxscale.cnf
+ * @verbatim
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=skysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints|regex
+@endverbatim
+ * - second test (bug587_1) is executed with "filters=regex|hints" (dffeent order of filters)
+ * - check if hints filter working by executing and comparing results:
+ * + via RWSPLIT: "select @@server_id; -- maxscale route to server server%d" (%d - node number)
+ * + directly to backend node "select @@server_id;"
+ * - do the same test with "filters=regex|hints" "filters=hints|regex"
+ */
+
+/*
+Vilho Raatikka 2014-10-21 19:12:33 UTC
+If filters and rwsplit are configured as follows, hints don't work.
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+user=maxuser
+passwd=maxpwd
+filters=hints|regex
+
+Changing filters=regex|hints makes it work. This is due to processing order. Regex filter drops hint off.
+Comment 1 Vilho Raatikka 2014-10-23 18:08:07 UTC
+buffer.c:gwbuf_make_contiguous: hint wasn't duplicated to new GWBUF struct. As a result hints were lost if query rewriting resulted in longer query than the original.
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ char server_id[256];
+ char server_id_d[256];
+
+ char hint_sql[64];
+
+ for (int i = 1; i < 25; i++)
+ {
+ for (int j = 0; j < Test->repl->N; j++)
+ {
+ Test->set_timeout(10);
+ sprintf(hint_sql, "select @@server_id; -- maxscale route to server server%d", j + 1);
+ Test->tprintf("%s\n", hint_sql);
+
+ find_field(Test->conn_rwsplit, hint_sql, (char *) "@@server_id", &server_id[0]);
+ find_field(Test->repl->nodes[j], (char *) "select @@server_id;", (char *) "@@server_id", &server_id_d[0]);
+
+ Test->tprintf("server%d ID from Maxscale: \t%s\n", j + 1, server_id);
+ Test->tprintf("server%d ID directly from node: \t%s\n", j + 1, server_id_d);
+
+ Test->add_result(strcmp(server_id, server_id_d), "Hints does not work!\n");
+ }
+ }
+
+ Test->close_maxscale_connections();
+ Test->repl->close_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
diff --git a/maxscale-system-test/bug592.cpp b/maxscale-system-test/bug592.cpp
new file mode 100644
index 000000000..5c0488b3c
--- /dev/null
+++ b/maxscale-system-test/bug592.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file bug592.cpp regression case for bug 592 ( "slave in "Running" state breaks authorization" ) MXS-326
+ *
+ * - stop all slaves: "stop slave;" directly to every node (now they are in "Running" state, not in "Russning, Slave")
+ * - via RWSplit "CREATE USER 'test_user'@'%' IDENTIFIED BY 'pass'"
+ * - try to connect using 'test_user' (expecting success)
+ * - start all slaves: "start slave;" directly to every node
+ * - via RWSplit: "DROP USER 'test_user'@'%'"
+ */
+
+/*
+Timofey Turenko 2014-10-24 09:35:35 UTC
+1. setup: Master/Slave replication
+2. reboot slaves
+3. create user usinf connection to RWSplit
+4. try to use this user to connect to Maxscale
+
+expected result:
+Authentication is ok
+
+actual result:
+ Access denied for user 'user'@'192.168.122.1' (using password: YES)
+
+Th issue was discovered with following setup state:
+
+MaxScale> show servers
+Server 0x3428260 (server1)
+ Server: 192.168.122.106
+ Status: Master, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Server Version: 5.5.40-MariaDB-log
+ Node Id: 106
+ Master Id: -1
+ Slave Ids: 107, 108 , 109
+ Repl Depth: 0
+ Number of connections: 0
+ Current no. of conns: 0
+ Current no. of operations: 0
+Server 0x3428160 (server2)
+ Server: 192.168.122.107
+ Status: Slave, Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Server Version: 5.5.40-MariaDB-log
+ Node Id: 107
+ Master Id: 106
+ Slave Ids:
+ Repl Depth: 1
+ Number of connections: 0
+ Current no. of conns: 0
+ Current no. of operations: 0
+Server 0x3428060 (server3)
+ Server: 192.168.122.108
+ Status: Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Server Version: 5.5.40-MariaDB-log
+ Node Id: 108
+ Master Id: 106
+ Slave Ids:
+ Repl Depth: 1
+ Number of connections: 0
+ Current no. of conns: 0
+ Current no. of operations: 0
+Server 0x338c3f0 (server4)
+ Server: 192.168.122.109
+ Status: Running
+ Protocol: MySQLBackend
+ Port: 3306
+ Server Version: 5.5.40-MariaDB-log
+ Node Id: 109
+ Master Id: 106
+ Slave Ids:
+ Repl Depth: 1
+ Number of connections: 0
+ Current no. of conns: 0
+ Current no. of operations: 0
+
+
+Maxscale read mysql.user table from server4 which was not properly replicated
+Comment 1 Mark Riddoch 2014-11-05 09:55:07 UTC
+In the reload users routine, if there is a master available then use that rather than the first.
+*/
+
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ int i;
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ for (i = 1; i < Test->repl->N; i++)
+ {
+ execute_query(Test->repl->nodes[i], (char *) "stop slave;");
+ }
+
+ execute_query(Test->conn_rwsplit, (char *) "CREATE USER 'test_user'@'%%' IDENTIFIED BY 'pass'");
+
+ MYSQL * conn = open_conn_no_db(Test->rwsplit_port, Test->maxscale_IP, (char *) "test_user", (char *) "pass",
+ Test->ssl);
+
+ if (conn == NULL)
+ {
+ Test->add_result(1, "Connections error\n");
+ }
+
+ for (i = 1; i < Test->repl->N; i++)
+ {
+ execute_query(Test->repl->nodes[i], (char *) "start slave;");
+ }
+
+ execute_query(Test->conn_rwsplit, (char *) "DROP USER 'test_user'@'%%'");
+
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+
+}
diff --git a/maxscale-system-test/bug601.cpp b/maxscale-system-test/bug601.cpp
new file mode 100644
index 000000000..4b1205fe7
--- /dev/null
+++ b/maxscale-system-test/bug601.cpp
@@ -0,0 +1,128 @@
+/**
+ * @file bug601.cpp regression case for bug 601 ("COM_CHANGE_USER fails with correct user/pwd if executed during authentication")
+ * - configure Maxscale.cnf to use only one thread
+ * - in 100 parallel threads start to open/close session
+ * - do change_user 2000 times
+ * - check all change_user are ok
+ * - check Mascale is alive
+ */
+
+/*
+Vilho Raatikka 2014-10-30 14:30:57 UTC
+If COM_CHANGE_USER is executed while backend protocol's state is not yet MYSQL_AUTH_RECV it will fail in the backend.
+
+If MaxScale uses multiple worked threads this occurs rarely and it would be possible to easily suspend execution of COM_CHANGE_USER.
+
+If MaxScale uses one worker thread then there's currently no way to suspend execution. It would require thread to put current task on hold, complete authentication task and return to COM_CHANGE_USER execution.
+
+In theory it is possible to add an event to client's DCB and let it become notified in the same way than events that occur in sockets. It would have to be added first (not last) and ensure that no other command is executed before it.
+
+Since this is the only case known where this would be necessary, it could be enough to add a "pending auth change" pointer in client's protocol object which would be checked before thread returns to epoll_wait after completing the authentication.
+Comment 1 Massimiliano 2014-11-07 17:01:29 UTC
+Current code in develop branch let COM_CHANGE_USER work fine.
+
+I noticed sometime a failed authentication issue using only.
+This because backend protocol's state is not yet MYSQL_AUTH_RECV and necessary data for succesfull backend change user (such as scramble data from handshake) may be not available.
+
+
+I put a query before change_user and the issue doesn't appear: that's another proof.
+Comment 2 Vilho Raatikka 2014-11-13 15:54:15 UTC
+In gw_change_user->gw_send_change_user_to_backend authentication message was sent to backend server before backend had its scramble data. That caused authentication to fail.
+Comment 3 Vilho Raatikka 2014-11-13 15:58:34 UTC
+if (func.auth ==)gw_change_user->gw_send_change_user_to_backend is called before backend has its scramble, auth packet is set to backend's delauqueue instead of writing it to backend. When backend_write_delayqueue is called COM_CHANGE_USER packets are rewritten with backend's current data.
+*/
+
+
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+int exit_flag = 0;
+
+TestConnections * Test ;
+
+void *parall_traffic( void *ptr );
+
+
+int main(int argc, char *argv[])
+{
+ int iterations = 1000;
+ Test = new TestConnections(argc, argv);
+ if (Test->smoke)
+ {
+ iterations = 100;
+ }
+
+
+ pthread_t parall_traffic1[100];
+ int check_iret[100];
+
+ Test->set_timeout(60);
+ Test->repl->connect();
+ Test->repl->execute_query_all_nodes((char *) "set global max_connect_errors=1000;");
+ Test->repl->execute_query_all_nodes((char *) "set global max_connections=1000;");
+
+ Test->connect_maxscale();
+ Test->tprintf("Creating one user 'user@%%'");
+ execute_query_silent(Test->conn_rwsplit, (char *) "DROP USER user@'%'");
+ Test->try_query(Test->conn_rwsplit, (char *) "CREATE USER user@'%%' identified by 'pass2'");
+ Test->try_query(Test->conn_rwsplit, (char *) "GRANT SELECT ON test.* TO user@'%%';");
+ Test->try_query(Test->conn_rwsplit, (char *) "FLUSH PRIVILEGES;");
+
+ Test->tprintf("Starting parallel thread which opens/closes session in the loop");
+
+ for (int j = 0; j < 25; j++)
+ {
+ check_iret[j] = pthread_create(¶ll_traffic1[j], NULL, parall_traffic, NULL);
+ }
+
+ Test->tprintf("Doing change_user in the loop");
+ for (int i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(15);
+ Test->add_result(mysql_change_user(Test->conn_rwsplit, "user", "pass2", (char *) "test"),
+ "change_user failed! %", mysql_error(Test->conn_rwsplit));
+ Test->add_result(mysql_change_user(Test->conn_rwsplit, Test->maxscale_user, Test->maxscale_password,
+ (char *) "test"), "change_user failed! %s", mysql_error(Test->conn_rwsplit));
+ }
+
+ Test->tprintf("Waiting for all threads to finish");
+ exit_flag = 1;
+ for (int j = 0; j < 25; j++)
+ {
+ Test->set_timeout(30);
+ pthread_join(parall_traffic1[j], NULL);
+ }
+ Test->tprintf("All threads are finished");
+ Test->repl->flush_hosts();
+
+ Test->tprintf("Change user to '%s' in order to be able to DROP user", Test->maxscale_user);
+ Test->set_timeout(30);
+ mysql_change_user(Test->conn_rwsplit, Test->maxscale_user, Test->maxscale_password, NULL);
+
+ Test->tprintf("Dropping user", Test->maxscale_user);
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP USER user@'%%';");
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+void *parall_traffic( void *ptr )
+{
+ MYSQL * conn;
+ while (exit_flag == 0)
+ {
+ conn = Test->open_rwsplit_connection();
+ mysql_close(conn);
+ if (Test->backend_ssl)
+ {
+ sleep(1);
+ }
+ }
+ return NULL;
+}
+
diff --git a/maxscale-system-test/bug620.cpp b/maxscale-system-test/bug620.cpp
new file mode 100644
index 000000000..e321504f5
--- /dev/null
+++ b/maxscale-system-test/bug620.cpp
@@ -0,0 +1,268 @@
+/**
+ * @file bug620.cpp bug620 regression case ("enable_root_user=true generates errors to error log")
+ *
+ * - Maxscale.cnf contains RWSplit router definition with enable_root_user=true
+ * - GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'skysqlroot';
+ * - try to connect using 'root' user and execute some query
+ * - errors are not expected in the log. All Maxscale services should be alive.
+ */
+
+/*
+Vilho Raatikka 2014-11-14 09:03:59 UTC
+Enabling use of root user in MaxScale causes the following being printed to error log. Disabling the setting enable_root_user prevents these errors.
+
+2014-11-14 11:02:47 Error : getaddrinfo failed for [linux-yxkl.site] due [Name or service not known]
+2014-11-14 11:02:47 140635119954176 [mysql_users_add()] Failed adding user root@linux-yxkl.site for service [RW Split Router]
+2014-11-14 11:02:47 Error : getaddrinfo failed for [::1] due [Address family for hostname not supported]
+2014-11-14 11:02:47 140635119954176 [mysql_users_add()] Failed adding user root@::1 for service [RW Split Router]
+2014-11-14 11:02:47 140635119954176 [mysql_users_add()] Failed adding user root@127.0.0.1 for service [RW Split Router]
+Comment 1 Vilho Raatikka 2014-11-14 09:04:40 UTC
+This appears with binary built from develop branch 14.11.14
+Comment 2 Massimiliano 2014-11-14 09:15:27 UTC
+The messages appear in the log because root user has an invalid hostname: linux-yxkl.site
+
+
+The second message root@127.0.0.1 may be related to a previous root@localhost entry.
+
+
+Names are resolved to IPs and added into maxscale hashtable: localhost and 127.0.0.1 result in a duplicated entry
+
+
+
+A standard root@localhost only entry doesn't cause any logged message
+Comment 3 Vilho Raatikka 2014-11-14 09:24:56 UTC
+Problem is that they seem critical errors but MaxScale still works like nothing had happened. If the default hostname of the server host is not good, what does it mean for MaxScale? Doest it still accept root user or not? Why it only causes trouble for root user but not for others?
+
+If the error has no effect in practice, then log entries could be better in debug log.
+
+Thread ids are suitable in debug log but not anywhere else.
+Comment 4 Massimiliano 2014-11-14 09:32:27 UTC
+The 'enable_root_user' option only allows selecting 'root' user from backend databases.
+
+
+The error messages are printed for all users and report that
+
+ specific_user@specific_host is not loaded but
+
+
+Example:
+
+2014-11-14 11:02:47 Error : getaddrinfo failed for [linux-yxkl.site] due [Name or service not known]
+2014-11-14 11:02:47 140635119954176 [mysql_users_add()] Failed adding user root@linux-yxkl.site for service [RW Split Router]
+
+2014-11-14 04:19:23 Error : getaddrinfo failed for [ftp.*.fi] due [Name or service not known]
+2014-11-14 04:19:23 67322400 [mysql_users_add()] Failed adding user foo@ftp.*.fi for service [Master Service]
+
+
+
+In the examples foo@%.funet.fi and root@linux-yxkl.site are not loaded.
+
+
+foo@localhost and root@localhost are loaded
+Comment 5 Vilho Raatikka 2014-11-14 10:55:35 UTC
+(In reply to comment #4)
+> The 'enable_root_user' option only allows selecting 'root' user from backend
+> databases.
+
+I think that enable_root_user means : MaxScale user can use her 'root' account also with MaxScale.
+
+Technically your explanation may be correct and I'm not against that. What I mean is that the user may not want to worry about what is 'loaded' or 'selected' under the cover.
+She simply wants to use root user. If it is not possible then that should be written to error log clearly. For example, "Use of 'root' disabled due to unreachable hostname" or something equally clear.
+
+Reporting several lines of errors may confuse the user especially if the root account still works perfectly.
+
+>
+>
+> The error messages are printed for all users and report that
+>
+> specific_user@specific_host is not loaded but
+>
+>
+> Example:
+>
+> 2014-11-14 11:02:47 Error : getaddrinfo failed for [linux-yxkl.site] due
+> [Name or service not known]
+> 2014-11-14 11:02:47 140635119954176 [mysql_users_add()] Failed adding user
+> root@linux-yxkl.site for service [RW Split Router]
+>
+> 2014-11-14 04:19:23 Error : getaddrinfo failed for [ftp.*.fi] due [Name or
+> service not known]
+> 2014-11-14 04:19:23 67322400 [mysql_users_add()] Failed adding user
+> foo@ftp.*.fi for service [Master Service]
+>
+>
+>
+> In the examples foo@%.funet.fi and root@linux-yxkl.site are not loaded.
+>
+>
+> foo@localhost and root@localhost are loaded
+Comment 6 Massimiliano 2014-11-14 11:00:04 UTC
+MaxScale MySQL authentication is based on user@host
+
+
+You may have such situation:
+
+foo@localhost [OK]
+foo@x-y-z.notexists [KO]
+
+user 'foo@localhost' is loaded the latter isn't
+
+
+For root user is the same.
+
+Allowing selection of root user means selecting all the rows from mysql.user table where user='root'
+
+
+if there is any row (root@xxxx) that cannot be loaded the message appears.
+
+In a standard setup we don't expect any log messages
+Comment 7 Timofey Turenko 2014-12-10 16:02:36 UTC
+I tried following:
+
+via RWSplit:
+
+GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'skysqlroot';
+
+and try to connect to RWSplit using 'root' and 'skysqlroot' and try some simple query:
+
+2014-12-10 17:35:43 Error : getaddrinfo failed for [::1] due [Address family for hostname not supported]
+2014-12-10 17:35:43 Warning: Failed to add user root@::1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+2014-12-10 17:35:43 Warning: Failed to add user root@127.0.0.1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+2014-12-10 17:35:43 Error : Failed to start router for service 'HTTPD Router'.
+2014-12-10 17:35:43 Error : Failed to start service 'HTTPD Router'.
+2014-12-10 17:36:08 Error : getaddrinfo failed for [::1] due [Address family for hostname not supported]
+2014-12-10 17:36:08 Warning: Failed to add user root@::1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+2014-12-10 17:36:08 Warning: Failed to add user root@127.0.0.1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+
+
+Is it expected?
+Comment 8 Massimiliano 2014-12-10 16:09:23 UTC
+root@::1 could not be loaded because it's for IPv6
+
+root@127.0.0.1 may be not loaded if root@localhost was found before
+
+As names are translated to IPv4 addresses localhost->127.0.0.1 and that'a duplicate
+
+
+2014-12-10 17:35:43 Error : Failed to start router for service 'HTTPD Router'.
+2014-12-10 17:35:43 Error : Failed to start service 'HTTPD Router'.
+
+Those messages are not part of mysql users load phase.
+
+when you have auth errors users are reload (in the allowed time window) and you see the messages again
+
+
+With admin interface you can check:
+
+
+show dbusers RW Split Router
+
+and you should see root@% you added with the grant
+Comment 9 Timofey Turenko 2014-12-12 21:59:30 UTC
+Following is present in the error log just after MaxScale start:
+
+
+2014-12-12 23:49:07 Error : getaddrinfo failed for [::1] due [Address family for hostname not supported]
+2014-12-12 23:49:07 Warning: Failed to add user root@::1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+2014-12-12 23:49:07 Warning: Failed to add user root@127.0.0.1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+
+
+first two line are clear: no support for IPv6, but would it be better to print 'warning' instead of 'error'?
+
+"Failed to add user root@127.0.0.1" - is it correct?
+
+direct connection to backend gives:
+MariaDB [(none)]> select User, host from mysql.user;
++---------+-----------+
+| User | host |
++---------+-----------+
+| maxuser | % |
+| repl | % |
+| skysql | % |
+| root | 127.0.0.1 |
+| root | ::1 |
+| | localhost |
+| maxuser | localhost |
+| root | localhost |
+| skysql | localhost |
+| | node1 |
+| root | node1 |
++---------+-----------+
+
+admin interface gives:
+
+MaxScale> show dbusers "RW Split Router"
+Users table data
+Hashtable: 0x7f6b64000c30, size 52
+ No. of entries: 7
+ Average chain length: 0.1
+ Longest chain length: 1
+User names: root@192.168.122.106, repl@%, skysql@%, maxuser@127.0.0.1, skysql@127.0.0.1, root@127.0.0.1, maxuser@%
+
+
+So, root@127.0.0.1 is present in the list.
+Comment 10 Mark Riddoch 2015-01-05 13:03:34 UTC
+The message "Failed to add user root@127.0.0.1" is because the two entries root@localhsot and root@127.0.0.1 are seen as duplicates in MaxScale. This is a result of MaxScale resolving hostnames at the time it reads the database rather than at connect time. So a duplicate is detected and the second one causes the error to be displayed.
+Comment 11 Timofey Turenko 2015-01-09 19:26:35 UTC
+works as expected, closing.
+Check for lack of "Error : getaddrinfo failed" added (just in case) and for warning about 'skysql'
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+
+ Test->connect_maxscale();
+
+ Test->tprintf("Creating 'root'@'%%'\n");
+ //global_result += execute_query(Test->conn_rwsplit, (char *) "CREATE USER 'root'@'%'; SET PASSWORD FOR 'root'@'%' = PASSWORD('skysqlroot');");
+
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%%' IDENTIFIED BY 'skysqlroot';");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'skysqlroot';");
+ sleep(10);
+
+ MYSQL * conn;
+
+ Test->tprintf("Connecting using 'root'@'%%'\n");
+ conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "root", (char *) "skysqlroot", Test->ssl);
+ if (mysql_errno(conn) != 0)
+ {
+ Test->add_result(1, "Connection using 'root' user failed, error: %s\n", mysql_error(conn));
+ }
+ else
+ {
+ Test->tprintf("Simple query...\n");
+ Test->try_query(conn, (char *) "SELECT * from mysql.user");
+ Test->try_query(conn,
+ (char *) "set password for 'root'@'localhost' = PASSWORD('');");
+ }
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ Test->tprintf("Dropping 'root'@'%%'\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP USER 'root'@'%%';");
+
+ Test->close_maxscale_connections();
+
+ Test->check_log_err((char *) "Failed to add user skysql", false);
+ Test->check_log_err((char *) "getaddrinfo failed", false);
+ Test->check_log_err((char *) "Couldn't find suitable Master", false);
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug626.cpp b/maxscale-system-test/bug626.cpp
new file mode 100644
index 000000000..a7bd23fa7
--- /dev/null
+++ b/maxscale-system-test/bug626.cpp
@@ -0,0 +1,181 @@
+/**
+ * @file bug626.cpp regression case for bug 626 ("Crash when user define with old password style (before 4.1 protocol)"), also checks error message in the log for bug428 ("Pre MySQL 4.1 encrypted passwords cause authorization failure")
+ *
+ * - CREATE USER 'old'@'%' IDENTIFIED BY 'old';
+ * - SET PASSWORD FOR 'old'@'%' = OLD_PASSWORD('old');
+ * - try to connect using user 'old'
+ * - check log for "MaxScale does not support these old passwords" warning
+ * - DROP USER 'old'@'%'
+ * - check MaxScale is alive
+ */
+
+/*
+Stephane VAROQUI 2014-11-25 17:37:58 UTC
+2014-11-21 16:24:03 Error : Invalid authentication message from backend. Error code: 1129, Msg : Host '192.168.42.172' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
+2014-11-21 16:24:03 Error : access for secrets file [/usr/local/maxscale/maxscale-1.0.1-beta/etc/.secrets] failed. Error 2, No such file or directory.
+2014-11-21 16:24:03 Error : Unable to get user data from backend database for service RW Split Router. Missing server information.
+2014-11-21 16:24:03 Error : Unable to write to backend due to authentication failure.
+2014-11-21 16:24:03 Error : Invalid authentication message from backend. Error code: 1129, Msg : Host '192.168.42.172' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
+2014-11-21 16:24:03 Error : access for secrets file [/usr/local/maxscale/maxscale-1.0.1-beta/etc/.secrets] failed. Error 2, No such file or directory.
+2014-11-21 16:24:03 Error : Unable to get user data from backend database for service RW Split Router. Missing server information.
+2014-11-21 16:24:03 Error : Unable to write to backend due to authentication failure.
+2014-11-21 16:24:03 Fatal: MaxScale received fatal signal 11. Attempting backtrace.
+2014-11-21 16:24:03 ./maxscale() [0x53ad1c]
+
+2014-11-21 16:24:03 /usr/lib64/libpthread.so.0(+0xf6d0) [0x7fd8039756d0]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libreadwritesplit.so(is_read_tmp_table+0x64) [0x7fd7ec0f9d25]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libreadwritesplit.so(+0x5577) [0x7fd7ec0fa577]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libMySQLClient.so(+0x5821) [0x7fd7ea1a1821]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libMySQLClient.so(+0x49df) [0x7fd7ea1a09df]
+
+2014-11-21 16:24:03 ./maxscale() [0x547093]
+
+2014-11-21 16:24:03 ./maxscale(poll_waitevents+0xbd) [0x546858]
+
+2014-11-21 16:24:03 ./maxscale(main+0x12b2) [0x53cfd2]
+
+2014-11-21 16:24:03 /usr/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fd8035c9d65]
+
+2014-11-21 16:24:03 ./maxscale() [0x539fdd]
+Comment 1 Mark Riddoch 2014-11-25 17:57:20 UTC
+Vilho,
+
+looks like multiple issues here, the missing authentication data is one problem, but the SEGFAULT appears to occur in the Read/Write Splitter
+
+2014-11-21 16:24:03 Fatal: MaxScale received fatal signal 11. Attempting backtrace.
+2014-11-21 16:24:03 ./maxscale() [0x53ad1c]
+
+2014-11-21 16:24:03 /usr/lib64/libpthread.so.0(+0xf6d0) [0x7fd8039756d0]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libreadwritesplit.so(is_read_tmp_table+0x64) [0x7fd7ec0f9d25]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libreadwritesplit.so(+0x5577) [0x7fd7ec0fa577]
+
+2014-11-21 16:24:03 /usr/local/maxscale/maxscale-1.0.1-beta/modules/libMySQLClient.so(+0x5821) [0x7fd7ea1a1821]
+
+Massimiliano 2014-12-01 17:29:26 UTC
+I have found an easy way to produce the "Host xxxx is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'" error
+
+
+Connect directly to mysql backend(s):
+
+# mysql -h 127.0.0.1 -P 3310
+MariaDB [(none)]> set global max_connect_errors=1;
+Query OK, 0 rows affected (0.00 sec)
+
+...
+
+# nc 127.0.0.1 3310
+]
+5.5.5-10.0.11-MariaDB-log??A[(SHQ>$???6$PEI"ilc+L{mysql_native_password
+
+Ctrl-C
+
+Next attempt results in:
+
+ # nc 127.0.0.1 3310
+j?iHost '151.20.6.153' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'[root@mcentos62 ~]
+
+
+Then I restored error count:
+
+# mysqladmin -h 127.0.0.1 -P 3310 flush-hosts
+
+
+
+I launched a mysqlslap test against MaxScale, and after a few seconds I caused the error as described above and ...
+
+[root@mcentos62 ~]# mysqlslap -h 127.0.0.1 -P 4008 -umassi -pmassi --query="select 1" --concurrency=16 --iterations=200
+mysqlslap: Cannot run query select 1 ERROR : Authentication with backend failed. Session will be closed.
+
+But no crash at all, this with GA branch and maxscale-1.0.1-beta RPMs.
+
+
+Some details about the error itself.
+
+http://dev.mysql.com/doc/refman/5.0/en/blocked-host.html
+
+B.5.2.6 Host 'host_name' is blocked
+If the following error occurs, it means that mysqld has received many connection requests from the given host that were interrupted in the middle:
+
+...
+
+By default, mysqld blocks a host after 10 connection errors.
+
+
+------------------------------------
+
+I also tried with with gdb and MaxScale binary, with a breakpoint set to the gw_create_backend_connection routine.
+
+Once the breakpoint is reached I did Ctrl-C int the mysql client (connected to MaxScale) and this caused the error too in the next connection.
+
+As the client stopped running, MaxScale cannot continue with async backend connection and this may increase the error counter: this may be a good example looking for any possible incomplete backend authentication due to a potential bug.
+
+
+Note, using a value as high as 10000 for max_connect_errors doesn't result in any issue of course.
+
+
+BTW, in my today setup even having backends with max_connect_errors=1 doesn't result in any issue at all.
+
+I run a test with MaxScale on a Virtual CentOS 6.2 on my laptop and backends in a Digital Ocean server, so with the Internet in the middle.
+Comment 9 Massimiliano 2014-12-03 10:04:49 UTC
+Reported segfault is related to is_read_tmp_table() routine.
+
+
+"many connection errors" not spotted yet during MaxScale tests
+
+
+A new setup is highly desiderable, it should happen in a few days.
+Comment 10 Massimiliano 2014-12-03 15:48:55 UTC
+No issues/crea found with user and old_password style.
+
+Message is logged into the error log where there is such case.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ printf("Creating user with old style password\n");
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], "CREATE USER 'old'@'%%' IDENTIFIED BY 'old';");
+ execute_query(Test->repl->nodes[0], "SET PASSWORD FOR 'old'@'%%' = OLD_PASSWORD('old');");
+ Test->stop_timeout();
+ Test->repl->sync_slaves();
+
+ Test->set_timeout(20);
+ printf("Trying to connect using user with old style password\n");
+ MYSQL * conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "old", (char *) "old", Test->ssl);
+
+ if ( mysql_errno(conn) != 0)
+ {
+ Test->tprintf("Connections is not open as expected\n");
+ }
+ else
+ {
+ Test->add_result(1, "Connections is open for the user with old style password.\n");
+ }
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ execute_query(Test->repl->nodes[0], "DROP USER 'old'@'%%'");
+
+ Test->check_log_err((char *) "MaxScale does not support these old passwords", true);
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug634.cpp b/maxscale-system-test/bug634.cpp
new file mode 100644
index 000000000..877f6f8ef
--- /dev/null
+++ b/maxscale-system-test/bug634.cpp
@@ -0,0 +1,42 @@
+/**
+ * @file bug634.cpp regression case for bug 634 ("SHOW SLAVE STATUS in RW SPLITTER is send to master")
+ *
+ * - execute SHOW SLAVE STATUS and check resut
+ */
+
+/*
+
+Description Stephane VAROQUI 2014-12-03 10:41:30 UTC
+SHOW SLAVE STATUS in RW SPLITTER is send to master ? That could break some monitoring scripts for generic proxy abstraction .
+Comment 1 Vilho Raatikka 2014-12-03 11:10:12 UTC
+COM_SHOW_SLAVE_STAT was unknown to query classifier. Being fixed.
+Comment 2 Vilho Raatikka 2014-12-03 11:26:17 UTC
+COM_SHOW_SLAVE_STAT wasn't classified but it was treated as 'unknown' and thus routed to master.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(5);
+
+ char master_ip[100];
+
+ Test->connect_maxscale();
+
+ for (int i = 0; i < 100; i++)
+ {
+ Test->set_timeout(5);
+ Test->add_result(find_field(Test->conn_rwsplit, (char *) "SHOW SLAVE STATUS", (char *) "Master_Host",
+ master_ip), "Master_host files is not found in the SHOW SLAVE STATUS reply, probably query went to master\n");
+ Test->add_result(strcmp(master_ip, Test->repl->IP_private[0]), "Master IP is wrong\n");
+ }
+
+ Test->close_maxscale_connections();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug643.cpp b/maxscale-system-test/bug643.cpp
new file mode 100644
index 000000000..6a3974882
--- /dev/null
+++ b/maxscale-system-test/bug643.cpp
@@ -0,0 +1,64 @@
+/**
+ * @file bug643.cpp regression case for bugs 643 ("Hints, RWSplit: MaxScale goes into infinite loop and crashes") and bug645
+ * - setup RWSplit in the following way for bug643
+ * @verbatim
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+user=skysql
+passwd=skysql
+filters=duplicate
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split Router
+ @endverbatim
+ * - try to connect
+ * - try simple query using ReadConn router (both, master and slave)
+ * - check warnig in the log "RW Split Router: Recursive use of tee filter in service"
+ */
+
+/*
+Mark Riddoch 2014-12-11 11:59:19 UTC
+There is a recursive use of the tee filter in the configuration.
+
+The "RW Split Router" uses the"duplicate" filter that will then duplicate all traffic to the original destination and another copy of the "RW Split Router", which again will duplicate all traffic to the original destination and another copy of the "RW Split Router"...
+
+Really this needs to be trapped as a configuration error.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->tprintf("Trying to connect to all Maxscale services\n");
+ fflush(stdout);
+ Test->connect_maxscale();
+ Test->tprintf("Trying to send query to ReadConn master\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_master, (char *) "show processlist");
+ Test->tprintf("Trying to send query to ReadConn slave\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_slave, (char *) "show processlist");
+ Test->tprintf("Trying to send query to RWSplit, expecting failure\n");
+ fflush(stdout);
+ if (execute_query(Test->conn_rwsplit, (char *) "show processlist") == 0)
+ {
+ Test->add_result(1, "FAIL: Query to broken service succeeded!\n");
+ }
+ Test->close_maxscale_connections();
+ Test->check_log_err((char *) "RW-Split-Router: Recursive use of tee filter in service", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug643_1.cpp b/maxscale-system-test/bug643_1.cpp
new file mode 100644
index 000000000..0cc2aa7d5
--- /dev/null
+++ b/maxscale-system-test/bug643_1.cpp
@@ -0,0 +1,96 @@
+/**
+ * @file bug643.cpp regression case for bugs 643 ("Hints, RWSplit: MaxScale goes into infinite loop and crashes") and bug645
+ * - setup RWSplit in the following way for bug643
+ * @verbatim
+ [hints]
+type=filter
+module=hintfilter
+
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[qla]
+type=filter
+module=qlafilter
+options=/tmp/QueryLog
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split2
+
+[testfilter]
+type=filter
+module=foobar
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+#servers=server1,server2
+max_slave_connections=100%
+use_sql_variables_in=all
+#use_sql_variables_in=master
+user=skysql
+passwd=skysql
+#filters=typo|qla|regex|hints|regex|hints
+#enable_root_user=1
+filters=duplicate
+
+[RW Split2]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+user=skysql
+passwd=skysql
+filters=qla|tests|hints
+
+ @endverbatim
+ * - try to connect
+ * - try simple query using all services
+ * - check warnig in the log "Failed to start service 'RW Split2"
+ * - check if Maxscale still alive
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->tprintf("Trying to connect to all Maxscale services\n");
+ fflush(stdout);
+ Test->connect_maxscale();
+ Test->tprintf("Trying to send query to RWSplit\n");
+ fflush(stdout);
+ execute_query(Test->conn_rwsplit, (char *) "show processlist");
+ Test->tprintf("Trying to send query to ReadConn master\n");
+ fflush(stdout);
+ execute_query(Test->conn_master, (char *) "show processlist");
+ Test->tprintf("Trying to send query to ReadConn slave\n");
+ fflush(stdout);
+ execute_query(Test->conn_slave, (char *) "show processlist");
+ Test->close_maxscale_connections();
+
+ Test->check_log_err((char *) "Unable to find filter 'tests' for service 'RW Split2'", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug645.cpp b/maxscale-system-test/bug645.cpp
new file mode 100644
index 000000000..9d5062c10
--- /dev/null
+++ b/maxscale-system-test/bug645.cpp
@@ -0,0 +1,213 @@
+/**
+ * @file bug643.cpp regression case for bugs 645 ("Tee filter with readwritesplit service hangs MaxScale")
+ * - setup RWSplit in the following way
+ * @verbatim
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=skysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server1, server3,server2
+user=skysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+ @endverbatim
+ * - try to connect
+ * - try simple query
+ * - check MaxScale is alive
+ */
+
+/*
+Massimiliano 2014-12-11 14:19:51 UTC
+When tee filter is used with a readwritesplit service MaxScale hangs (each service including admin interface)or there is a failed assetion in Debug mode:
+
+debug assert /source/GA/server/modules/routing/readwritesplit/readwritesplit.c:1825
+maxscale: /source/GA/server/modules/routing/readwritesplit/readwritesplit.c:1825: routeQuery: Assertion `!(querybuf->gwbuf_type == 0)' failed.
+
+
+Configuration:
+
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=massi
+passwd=massi
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=massi
+passwd=massi
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4606
+
+
+Accessing the RW_listener:
+
+mysql -h 127.0.0.1 -P 4606 -umassi -pmassi
+
+
+
+Debug version:
+
+2014-12-11 08:48:48 Fatal: MaxScale received fatal signal 6. Attempting backtrace.
+2014-12-11 08:48:48 ./maxscale() [0x53c80e]
+2014-12-11 08:48:48 /lib64/libpthread.so.0(+0xf710) [0x7fd418a62710]
+2014-12-11 08:48:48 /lib64/libc.so.6(gsignal+0x35) [0x7fd417318925]
+2014-12-11 08:48:48 /lib64/libc.so.6(abort+0x175) [0x7fd41731a105]
+2014-12-11 08:48:48 /lib64/libc.so.6(+0x2ba4e) [0x7fd417311a4e]
+2014-12-11 08:48:48 /lib64/libc.so.6(__assert_perror_fail+0) [0x7fd417311b10]
+2014-12-11 08:48:48 /usr/local/skysql/maxscale/modules/libreadwritesplit.so(+0x69ca) [0x7fd4142789ca]
+2014-12-11 08:48:48 /usr/local/skysql/maxscale/modules/libtee.so(+0x3707) [0x7fd3fc2db707]
+2014-12-11 08:48:48 /usr/local/skysql/maxscale/modules/libMySQLClient.so(+0x595d) [0x7fd3fe34b95d]
+2014-12-11 08:48:48 ./maxscale() [0x54d3ec]
+2014-12-11 08:48:48 ./maxscale(poll_waitevents+0x63d) [0x54ca8a]
+2014-12-11 08:48:48 ./maxscale(main+0x1acc) [0x53f616]
+2014-12-11 08:48:48 /lib64/libc.so.6(__libc_start_main+0xfd) [0x7fd417304d1d]
+2014-12-11 08:48:48 ./maxscale() [0x53a92d]
+
+
+Without debug:
+
+we got mysql prompt but then maxscale is stucked
+or
+when don't have the prompt, it hangs after few welcome messages
+Comment 1 Vilho Raatikka 2014-12-11 15:14:50 UTC
+The assertion occurs because query is is not statement - but packet type. That is, it was sent to read connection router which doesn't examine MySQL packets except the header. Thus, the type of query is not set in mysql_client.c:gw_read_client_event:
+>>>
+ if (cap == 0 || (cap == RCAP_TYPE_PACKET_INPUT))
+ {
+ stmt_input = false;
+ }
+ else if (cap == RCAP_TYPE_STMT_INPUT)
+ {
+ stmt_input = true;
+
+ gwbuf_set_type(read_buffer, GWBUF_TYPE_MYSQL);
+ }
+>>>
+Comment 2 Massimiliano 2014-12-11 16:00:52 UTC
+Using readconnroute (with router_options=master) instead seems fine.
+
+I found that "USE dbname" is not passed via tee filter:
+
+
+4606 is the listener to a service with tee filter
+
+root@maxscale-02 build]# mysql -h 127.0.0.1 -P 4606 -u massi -pmassi
+
+
+
+USE test; SELECT DATABASE()
+client to maxscale:
+
+T 127.0.0.1:40440 -> 127.0.0.1:4606 [AP]
+ 05 00 00 00 02 74 65 73 74 .....test
+
+T 127.0.0.1:4606 -> 127.0.0.1:40440 [AP]
+ 07 00 00 01 00 00 00 02 00 00 00 ...........
+
+T 127.0.0.1:40440 -> 127.0.0.1:4606 [AP]
+ 12 00 00 00 03 53 45 4c 45 43 54 20 44 41 54 41 .....SELECT DATA
+ 42 41 53 45 28 29 BASE()
+
+T 127.0.0.1:4606 -> 127.0.0.1:40440 [AP]
+ 01 00 00 01 01 20 00 00 02 03 64 65 66 00 00 00 ..... ....def...
+ 0a 44 41 54 41 42 41 53 45 28 29 00 0c 08 00 22 .DATABASE()...."
+ 00 00 00 fd 00 00 1f 00 00 05 00 00 03 fe 00 00 ................
+ 02 00 05 00 00 04 04 74 65 73 74 05 00 00 05 fe .......test.....
+ 00 00 02 00 ....
+
+maxscale to backend:
+
+
+T 127.0.0.1:56578 -> 127.0.0.1:3308 [AP]
+ 12 00 00 00 03 53 45 4c 45 43 54 20 44 41 54 41 .....SELECT DATA
+ 42 41 53 45 28 29 BASE()
+
+T 127.0.0.1:3308 -> 127.0.0.1:56578 [AP]
+ 01 00 00 01 01 20 00 00 02 03 64 65 66 00 00 00 ..... ....def...
+ 0a 44 41 54 41 42 41 53 45 28 29 00 0c 08 00 22 .DATABASE()...."
+ 00 00 00 fd 00 00 1f 00 00 05 00 00 03 fe 00 00 ................
+ 02 00 01 00 00 04 fb 05 00 00 05 fe 00 00 02 00 ................
+
+
+USE test was not sent
+
+
+
+May be a similar issue is present with readwritesplit but I cannot test it
+Comment 3 Vilho Raatikka 2014-12-11 16:35:46 UTC
+(In reply to comment #2)
+> Using readconnroute (with router_options=master) instead seems fine.
+
+Using readconnroute _where_? in tee?
+Comment 4 Vilho Raatikka 2014-12-12 08:27:41 UTC
+gwbuf_type is not set and that is the immediate cause for assertion with Debug version.
+Reason why the type is not set is in the way the packets are first processed in mysql_client.c client protocol module and then passed optionally to filters and router. There is a bug because it is assumed that when client protocol module reads incoming packet it can resolve which router will handle the packet processing. The code doesn't take into account that same packet can be processed by many different routers, like in the case of readconnrouter->tee->readwritesplit.
+Another problem is in readwritesplit where it is assumed that it is the first and the only router that will process tha data. So it includes checks that the buffer has correct type.
+
+Required changes are:
+- readwritesplit should check if buffer has no type and in that case, insted of asserting, merge incoming MySQL packet fragments into a single contiguous buffer.
+- remove checks which enforce rules which are based on false assumption.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->connect_maxscale();
+ Test->try_query(Test->conn_master, (char *) "show processlist");
+ Test->try_query(Test->conn_slave, (char *) "show processlist");
+ Test->try_query(Test->conn_rwsplit, (char *) "show processlist");
+ Test->close_maxscale_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug645_1.cpp b/maxscale-system-test/bug645_1.cpp
new file mode 100644
index 000000000..26d1a56bc
--- /dev/null
+++ b/maxscale-system-test/bug645_1.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file bug643.cpp regression case for bugs 645 ("Tee filter with readwritesplit service hangs MaxScale")
+ * - setup RWSplit in the following way
+ * @verbatim
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=skysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=skysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysql
+filters=QLA
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysql
+filters=QLA
+
+[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
+
+
+ @endverbatim
+ * - try to connect to all services except 4016
+ * - try simple query
+ * - check ReadConn is ok
+ * - check log for presens of "Couldn't find suitable Master from 2 candidates" errors
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->connect_maxscale();
+ Test->tprintf("trying query to RWSplit, expecting failure\n");
+ if (execute_query(Test->conn_rwsplit, (char *) "show processlist") == 0)
+ {
+ Test->add_result(1, "Query is ok, but failue is expected\n");
+ }
+ Test->tprintf("Trying query to ReadConn router master\n");
+ Test->try_query(Test->conn_master, (char *) "show processlist");
+ Test->tprintf("Trying query to ReadConn router slave\n");
+ Test->try_query(Test->conn_slave, (char *) "show processlist");
+
+ Test->close_maxscale_connections();
+
+ Test->check_log_err((char *) "Couldn't find suitable Master from 2 candidates", true);
+ Test->check_log_err((char *) "Creating client session for Tee filter failed. Terminating session.", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug649.cpp b/maxscale-system-test/bug649.cpp
new file mode 100644
index 000000000..bfd9bca3f
--- /dev/null
+++ b/maxscale-system-test/bug649.cpp
@@ -0,0 +1,156 @@
+/**
+ * @file bug649.cpp regression case for bug 649 ("Segfault using RW Splitter")
+ * @verbatim
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=skysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server1,server3,server2
+user=skysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+ @endverbatim
+ * - Connect to RWSplit
+ * - create load on RWSplit (25 threads doing long INSERTs in the loop)
+ * - block Mariadb server on Master node by Firewall
+ * - unblock Mariadb server
+ * - check if Maxscale is alive
+ * - reconnect and check if query execution is ok
+ */
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+int exit_flag = 0;
+
+TestConnections * Test ;
+
+char sql[1000000];
+
+void *parall_traffic( void *ptr );
+
+int main(int argc, char *argv[])
+{
+ int threads_num = 20;
+ pthread_t parall_traffic1[threads_num];
+ int check_iret[threads_num];
+
+ Test = new TestConnections(argc, argv);
+ int time_to_run = (Test->smoke) ? 10 : 30;
+ Test->set_timeout(10);
+
+ Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
+ Test->connect_rwsplit();
+
+ Test->repl->connect();
+ Test->tprintf("Drop t1 if exists\n");
+ execute_query(Test->repl->nodes[0], "DROP TABLE IF EXISTS t1;");
+ Test->tprintf("Create t1\n");
+ Test->add_result(create_t1(Test->repl->nodes[0]), "t1 creation Failed\n");
+ Test->repl->close_connections();
+
+ Test->stop_timeout();
+ sleep(5);
+
+ create_insert_string(sql, 65000, 1);
+ Test->tprintf("Creating query threads\n", time_to_run);
+ for (int j = 0; j < threads_num; j++)
+ {
+ Test->set_timeout(20);
+ check_iret[j] = pthread_create(¶ll_traffic1[j], NULL, parall_traffic, NULL);
+ }
+
+ Test->stop_timeout();
+ Test->tprintf("Waiting %d seconds\n", time_to_run);
+ sleep(time_to_run);
+
+ Test->tprintf("Setup firewall to block mysql on master\n");
+ Test->repl->block_node(0);
+ fflush(stdout);
+
+ Test->tprintf("Waiting %d seconds\n", time_to_run);
+ sleep(time_to_run);
+
+ Test->set_timeout(30);
+ Test->tprintf("Trying query to RWSplit, expecting failure, but not a crash\n");
+ if (execute_query_silent(Test->conn_rwsplit, (char *) "show processlist;") == 0)
+ {
+ Test->add_result(1, "Failure is expected, but query is ok\n");
+ }
+
+ Test->stop_timeout();
+ sleep(time_to_run);
+
+ Test->tprintf("Setup firewall back to allow mysql\n");
+ Test->repl->unblock_node(0);
+ fflush(stdout);
+ Test->stop_timeout();
+ sleep(time_to_run);
+ exit_flag = 1;
+ for (int i = 0; i < threads_num; i++)
+ {
+ Test->set_timeout(30);
+ pthread_join(parall_traffic1[i], NULL);
+ Test->tprintf("exit %d\n", i);
+ }
+ Test->stop_timeout();
+ sleep(5);
+
+ Test->set_timeout(20);
+ Test->tprintf("Checking Maxscale is alive\n");
+ Test->check_maxscale_alive();
+
+ Test->set_timeout(20);
+ Test->tprintf("Reconnecting to RWSplit ...\n");
+ Test->connect_rwsplit();
+ Test->tprintf(" ... and trying query\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "show processlist;");
+ Test->close_rwsplit();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
+void *parall_traffic( void *ptr )
+{
+ MYSQL * conn;
+ mysql_thread_init();
+ conn = Test->open_rwsplit_connection();
+ if ((conn != NULL) && (mysql_errno(conn) == 0))
+ {
+ while (exit_flag == 0)
+ {
+ execute_query_silent(conn, sql);
+ fflush(stdout);
+ }
+ }
+ else
+ {
+ Test->tprintf("Error opening connection");
+ }
+
+ if (conn != NULL )
+ {
+ mysql_close(conn);
+ }
+ return NULL;
+}
diff --git a/maxscale-system-test/bug650.cpp b/maxscale-system-test/bug650.cpp
new file mode 100644
index 000000000..94262277b
--- /dev/null
+++ b/maxscale-system-test/bug650.cpp
@@ -0,0 +1,84 @@
+/**
+ * @file bug650.cpp regression case for bug 650 ("Hints, RWSplit: MaxScale goes into infinite loop and crashes") and bug645
+ * - setup RWSplit in the following way
+ * @verbatim
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=skysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=skysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+ @endverbatim
+ * - try to connect
+ * - try simple query using ReadConn router (both, master and slave)
+ * - check errors in the log
+ @verbatim
+ Couldn't find suitable Master from 2 candidates
+ Failed to create RW_Split session.
+ Creating client session for Tee filter failed. Terminating session.
+ Failed to create filter 'DuplicaFilter' for service 'RW_Router'
+ Setting up filters failed. Terminating session RW_Router
+ @endverbatim
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ Test->connect_maxscale();
+ Test->tprintf("Trying query to ReadConn master\n");
+ Test->try_query(Test->conn_master, (char *) "show processlist");
+ Test->tprintf("Trying query to ReadConn slave\n");
+ Test->try_query(Test->conn_slave, (char *) "show processlist");
+ Test->tprintf("Trying query to RWSplit, expecting failure\n");
+ if (execute_query(Test->conn_rwsplit, (char *) "show processlist") == 0)
+ {
+ Test->add_result(1, "Query is ok, but failure is expected\n");
+ }
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Checking logs\n");
+
+ Test->check_log_err((char *) "Couldn't find suitable Master from 2 candidates", true);
+ Test->check_log_err((char *) "Failed to create new router session for service 'RW_Split'", true);
+ Test->check_log_err((char *) "Creating client session for Tee filter failed. Terminating session.", true);
+ Test->check_log_err((char *) "Failed to create filter 'DuplicaFilter' for service 'RW_Router'", true);
+ Test->check_log_err((char *) "Setting up filters failed. Terminating session RW_Router", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug653.cpp b/maxscale-system-test/bug653.cpp
new file mode 100644
index 000000000..261eb579a
--- /dev/null
+++ b/maxscale-system-test/bug653.cpp
@@ -0,0 +1,80 @@
+/**
+ * @file bug653.cpp regression case for bug 653 ("Memory corruption when users with long hostnames that can no the resolved are loaded into MaxScale")
+ *
+ * - CREATE USER 'user_with_very_long_hostname'@'very_long_hostname_that_can_not_be_resolved_and_it_probably_caused_crash.com.net.org' IDENTIFIED BY 'old';
+ * - try to connect using user 'user_with_very_long_hostname'
+ * - DROP USER 'user_with_very_long_hostname'@'very_long_hostname_that_can_not_be_resolved_and_it_probably_caused_crash.com.net.org'
+ * - check MaxScale is alive
+ */
+
+/*
+Mark Riddoch 2014-12-16 13:17:25 UTC
+Program received signal SIGSEGV, Segmentation fault.
+0x00007ffff49385ac in free () from /lib64/libc.so.6
+Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.47.el6_2.12.x86_64 keyutils-libs-1.4-4.el6.x86_64 krb5-libs-1.10.3-10.el6_4.2.x86_64 libaio-0.3.107-10.el6.x86_64 libcom_err-1.41.12-14.el6.x86_64 libgcc-4.4.7-4.el6.x86_64 libselinux-2.0.94-5.3.el6_4.1.x86_64 libstdc++-4.4.7-4.el6.x86_64 nss-pam-ldapd-0.7.5-14.el6_2.1.x86_64 nss-softokn-freebl-3.14.3-10.el6_5.x86_64 openssl-1.0.1e-16.el6_5.15.x86_64 zlib-1.2.3-29.el6.x86_64
+(gdb) where
+#0 0x00007ffff49385ac in free () from /lib64/libc.so.6
+#1 0x000000000041d421 in add_mysql_users_with_host_ipv4 (users=0x72c4c0,
+ user=0x739030 "u3", host=0x739033 "aver.log.hostname.to.overflow.the.buffer",
+ passwd=0x73905c "", anydb=0x739089 "Y", db=0x0) at dbusers.c:291
+#2 0x000000000041e302 in getUsers (service=0x728ef0, users=0x72c4c0)
+ at dbusers.c:742
+#3 0x000000000041cf97 in load_mysql_users (service=0x728ef0) at dbusers.c:99
+#4 0x00000000004128c7 in serviceStartPort (service=0x728ef0, port=0x729b70)
+ at service.c:227
+#5 0x0000000000412e27 in serviceStart (service=0x728ef0) at service.c:365
+#6 0x0000000000412f00 in serviceStartAll () at service.c:413
+#7 0x000000000040b592 in main (argc=2, argv=0x7fffffffe108) at gateway.c:1750
+Comment 1 Mark Riddoch 2014-12-16 13:18:09 UTC
+The problem is a buffer overrun in normalise_hostname. Fix underway.
+Comment 2 Mark Riddoch 2014-12-16 15:45:59 UTC
+Increased buffer size to prevent overrun issue
+Comment 3 Timofey Turenko 2014-12-22 15:39:32 UTC
+I'm not sure I understand the bug correctly.
+But 60-chars long host name does not cause problem (longer is not possible "String 'very_long_hostname_that_can_not_be_resolved_and_it_probably_caused_cra' is too long for host name (should be no longer than 60)"
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(50);
+ Test->connect_maxscale();
+
+ Test->tprintf("Creating user with old style password\n");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "CREATE USER 'user_long_host11'@'very_long_hostname_that_probably_caused_crashhh.com.net.org' IDENTIFIED BY 'old'");
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "GRANT ALL PRIVILEGES ON *.* TO 'user_long_host11'@'very_long_hostname_that_probably_caused_crashhh.com.net.org' WITH GRANT OPTION");
+ sleep(10);
+
+ Test->tprintf("Trying to connect using user with old style password\n");
+ MYSQL * conn = open_conn(Test->rwsplit_port, Test->maxscale_IP, (char *) "user_long_host11", (char *) "old",
+ Test->ssl);
+
+ if ( mysql_errno(conn) != 0 )
+ {
+ Test->tprintf("Connections is not open as expected\n");
+ }
+ else
+ {
+ Test->add_result(1, "Connections is open for the user with bad host\n");
+ }
+ if (conn != NULL)
+ {
+ mysql_close(conn);
+ }
+
+ Test->try_query(Test->conn_rwsplit,
+ (char *) "DROP USER 'user_long_host11'@'very_long_hostname_that_probably_caused_crashhh.com.net.org'");
+ Test->close_maxscale_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug654.cpp b/maxscale-system-test/bug654.cpp
new file mode 100644
index 000000000..4fd192446
--- /dev/null
+++ b/maxscale-system-test/bug654.cpp
@@ -0,0 +1,202 @@
+/**
+ * @file bug654.cpp regression case for bug654 abd 698 ("maxadm: show dbusers causes SEGFAULT", "Using invalid parameter in many maxadmin commands causes MaxScale to fail")
+ *
+ * - execute maxadmin command show dbusers RW Split Router and show dbusers "RW Split Router"
+ * . execute different maxadmin commands with wrong parameters
+ * - check MaxScale is alive
+ */
+
+/*
+Vilho Raatikka 2014-12-16 13:54:36 UTC
+MaxScale> show services
+Service 0x1af7eb0
+ Service: RW Split Router
+ Router: readwritesplit (0x7fffdf501440)
+ Number of router sessions: 0
+ Current no. of router sessions: 0
+ Number of queries forwarded: 0
+ Number of queries forwarded to master: 0
+ Number of queries forwarded to slave: 0
+ Number of queries forwarded to all: 0
+ Started: Tue Dec 16 15:51:54 2014
+ Root user access: Disabled
+ Filter chain: duplicate
+ Backend databases
+ 127.0.0.1:3003 Protocol: MySQLBackend
+ 127.0.0.1:3002 Protocol: MySQLBackend
+ 127.0.0.1:3001 Protocol: MySQLBackend
+ 127.0.0.1:3000 Protocol: MySQLBackend
+ Users data: 0x1aea000
+ Total connections: 1
+ Currently connected: 1
+
+...
+
+MaxScale> show dbusers RW Split Router
+
+(gdb) bt
+#0 0x00007fffdfb4950a in execute_cmd (cli=0x7fffc0000c70) at /home/raatikka/src/git/MaxScale/server/modules/routing/debugcmd.c:805
+#1 0x00007fffdfb48ef8 in execute (instance=0x1b0f7b0, router_session=0x7fffc0000c70, queue=0x0) at /home/raatikka/src/git/MaxScale/server/modules/routing/cli.c:279
+#2 0x00007ffff46ae934 in maxscaled_read_event (dcb=0x7fffc00009c0) at /home/raatikka/src/git/MaxScale/server/modules/protocol/maxscaled.c:177
+#3 0x000000000058b145 in process_pollq (thread_id=2) at /home/raatikka/src/git/MaxScale/server/core/poll.c:858
+#4 0x000000000058a7df in poll_waitevents (arg=0x2) at /home/raatikka/src/git/MaxScale/server/core/poll.c:608
+#5 0x00007ffff7527e0f in start_thread () from /lib64/libpthread.so.0
+#6 0x00007ffff5e0e0dd in clone () from /lib64/libc.so.6
+(gdb)
+Comment 1 Vilho Raatikka 2014-12-16 13:58:37 UTC
+805 for (i = 0; args[i] && *args[i]; i++)
+
+Off-by-one if there are more arguments than expected.
+Comment 2 Vilho Raatikka 2014-12-23 16:11:12 UTC
+NULL-terminated argument list in case where there are given more arguments than expected.
+*/
+
+
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+ char result[1024];
+
+ Test->get_maxadmin_param((char *) "show dbusers RW Split Router", (char *) "Incorrect number of arguments:",
+ result);
+ Test->tprintf("result %s\n", result);
+
+ if (strstr(result, "show dbusers expects 1 argument") == NULL)
+ {
+ Test->add_result(1, "there is NO \"show dbusers expects 1 argument\" message");
+ }
+ Test->set_timeout(30);
+ Test->get_maxadmin_param((char *) "show dbusers \"RW Split Router\"", (char *) "User names:", result);
+ Test->tprintf("result %s\n", result);
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "reload dbusers 0x232fed0");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "reload dbusers Хрен");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "reload dbusers Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show Хрен");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show dcb Хрен");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show dcb Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show dcb khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show server Хрен");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show server Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show server khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show service Хрен");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show service Хрен моржовый");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show service khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "show service khren morzhovyj");
+
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "list listeners");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "restart monitor");
+ Test->set_timeout(30);
+ Test->execute_maxadmin_command((char *) "restart service");
+
+ if (!Test->smoke)
+ {
+ int N = 28;
+ const char * cmd[N];
+
+ int Ng = 6;
+ const char * garbage[Ng];
+
+ garbage[0] = "qwerty";
+ garbage[1] = "khren morzhovyj";
+ garbage[2] = "Хрен";
+ garbage[3] = "Хрен моржовый";
+ garbage[4] =
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ garbage[5] =
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Хрен моржовый Хрен моржовый ";
+
+ cmd[0] = "enable disable heartbeat ";
+ cmd[1] = "disable heartbeat ";
+ cmd[2] = "reload dbusers ";
+
+ cmd[3] = "set server server1 master ";
+
+ cmd[4] = "set pollsleep ";
+ cmd[5] = "set nbpolls ";
+
+ cmd[6] = "show dcb ";
+ cmd[7] = "show eventq ";
+ cmd[8] = "show eventstats ";
+ cmd[9] = "show filter ";
+ cmd[10] = "show monitor ";
+ cmd[11] = "show server ";
+ cmd[12] = "show service ";
+ cmd[13] = "show session ";
+
+ cmd[14] = "show filters ";
+ cmd[15] = "show modules ";
+ cmd[16] = "show monitors ";
+ cmd[17] = "show servers ";
+ cmd[18] = "show services ";
+ cmd[19] = "show sessions ";
+ cmd[20] = "show tasks ";
+ cmd[21] = "show threads ";
+ cmd[22] = "show users ";
+
+ cmd[23] = "shutdown monitor ";
+ cmd[24] = "shutdown service ";
+
+ cmd[25] = "shutdown maxscale ";
+
+ cmd[26] = "enable root ";
+ cmd[27] = "disable root ";
+
+ char str1[4096];
+ int i1, i2;
+
+ for (i1 = 0; i1 < N; i1++)
+ {
+ for (i2 = 0; i2 < Ng; i2++)
+ {
+ Test->set_timeout(30);
+ sprintf(str1, "%s %s", cmd[i1], garbage[i2]);
+ Test->tprintf("Trying '%s'\n", str1);
+ Test->execute_maxadmin_command(str1);
+
+ sprintf(str1, "%s %s%s%s%s %s ", cmd[i1], garbage[i2], garbage[i2], garbage[i2], garbage[i2], garbage[i2]);
+ Test->tprintf("Trying '%s'\n", str1);
+ Test->execute_maxadmin_command(str1);
+ }
+ }
+ }
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug656.cpp b/maxscale-system-test/bug656.cpp
new file mode 100644
index 000000000..d27a45aa7
--- /dev/null
+++ b/maxscale-system-test/bug656.cpp
@@ -0,0 +1,42 @@
+/**
+ * @file bug656.cpp Checks Maxscale behaviour in case if Master node is blocked - NOT NEEDED BECAUSE IT IS ALREADY CHECKED BY OTHER TESTS!!!!
+ *
+ * - ConnecT to RWSplit
+ * - block Mariadb server on Master node by Firewall
+ * - try simple query *show servers" via Maxadmin
+ */
+
+
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+
+ Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
+ Test->connect_rwsplit();
+
+ Test->tprintf("Setup firewall to block mysql on master\n");
+ Test->repl->block_node(0);
+
+ //printf("Trying query to RWSplit, expecting failure, but not a crash\n"); fflush(stdout);
+ //execute_query(Test->conn_rwsplit, (char *) "show processlist;");
+ execute_maxadmin_command_print(Test->maxscale_IP, (char *) "admin", Test->maxadmin_password,
+ (char *) "show servers");
+
+ Test->tprintf("Setup firewall back to allow mysql and wait\n");
+ Test->repl->unblock_node(0);
+ sleep(10);
+
+ Test->close_rwsplit();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
diff --git a/maxscale-system-test/bug657.cpp b/maxscale-system-test/bug657.cpp
new file mode 100644
index 000000000..e6c9ec72f
--- /dev/null
+++ b/maxscale-system-test/bug657.cpp
@@ -0,0 +1,119 @@
+/**
+ * @file bug657.cpp regression case for bug 657 ("Tee filter: closing child session causes MaxScale to fail")
+ *
+ * - Configure readconnrouter with tee filter and tee filter with a readwritesplit as a child service.
+ * @verbatim
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=skysql
+passwd=skysql
+#filters=QLA
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysql
+filters=TEE
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysql
+filters=TEE
+
+[TEE]
+type=filter
+module=tee
+service=RW Split Router
+@endverbatim
+ * - Start MaxScale
+ * - Connect readconnrouter
+ * - Fail the master node
+ * - Reconnect readconnrouter
+ */
+
+/*
+Vilho Raatikka 2014-12-22 08:35:52 UTC
+How to reproduce:
+1. Configure readconnrouter with tee filter and tee filter with a readwritesplit as a child service.
+2. Start MaxScale
+3. Connect readconnrouter
+4. Fail the master node
+5. Reconnect readconnrouter
+
+As a consequence, next routeQuery will be duplicated to closed readwritesplit router and eventually fred memory will be accessed which causes SEGFAULT.
+
+Reason for this is that situation where child (=branch -) session is closed as a consequence of node failure, is not handled in tee filter. Tee filter handles the case where client closes the session.
+Comment 1 Vilho Raatikka 2014-12-22 09:14:13 UTC
+Background: client session may be closed for different reasons. If client actively closes it by sending COM_QUIT packet, it happens from top to bottom: packet is identified and client DCB is closed. Client's DCB close routine also closes the client router session.
+
+If backend fails and monitor detects it, then every DCB that isn't running or isn't master, slave, joined (Galera) nor ndb calls its hangup function. If the failed node was master then client session gets state SESSION_STATE_STOPPING which triggers first closing the client DCB and as a part of it, the whole session.
+
+In tee filter, the first issue is the client DCB's close routine which doesn't trigger closing the session. The other issue is that if child session gets closed there's no mechanism that would prevent future queries being routed to tee's child service. As a consequence, future calls to routeQuery will access closed child session including freed memory etc.
+Comment 2 Vilho Raatikka 2014-12-22 22:32:25 UTC
+session.c:session_free:if session is child of another service (tee in this case), it is the parent which releases child's allocated memory back to the system. This now also includes the child router session.
+ dcb.h: Added DCB_IS_CLONE macro
+ tee.c:freeSession:if parent session triggered closing of tee, then child session may not be closed yet. In that case free the child session first and only then free child router session and release child session's memory back to system.
+ tee.c:routeQuery: only route if child session is ready for routing. Log if session is not ready for routing and set tee session inactive
+ mysql_client.c:gw_client_close:if DCB is cloned one don't close the protocol because they it is shared with the original DCB.
+Comment 3 Vilho Raatikka 2014-12-23 10:04:11 UTC
+If monitor haven't yet changed the status for failed backend, even the fixed won't notice the failure, and the client is left waiting for reply until some lower level timeout exceeds and closes the socket.
+
+The solution is to register a callback function to readconnrouter's backend DCB in the same way that it is done in readwritesplit. Callback needs to be implemented and tests added.
+By using this mechanism the client must wait at most one monitor interval before the session is closed.
+
+Vilho Raatikka 2014-12-31 23:19:41 UTC
+filter.c:filter_free:if filter parameter is NULL, return.
+ tee.c:freeSession: if my_session->dummy_filterdef is NULL, don't try to release the memory
+*/
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections *Test = new TestConnections(argc, argv);
+ Test->set_timeout(200);
+
+ Test->tprintf("Connecting to ReadConn Master %s\n", Test->maxscale_IP);
+ Test->connect_readconn_master();
+
+ sleep(1);
+
+ Test->tprintf("Setup firewall to block mysql on master\n");
+ Test->repl->block_node(0);
+
+ sleep(10);
+
+ Test->tprintf("Reconnecting to ReadConnMaster\n");
+ Test->close_readconn_master();
+ Test->connect_readconn_master();
+
+ sleep(5);
+
+ Test->repl->unblock_node(0);
+ sleep(10);
+
+ Test->tprintf("Closing connection\n");
+
+ Test->close_readconn_master();
+ fflush(stdout);
+
+ Test->tprintf("Checking Maxscale is alive\n");
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug658.cpp b/maxscale-system-test/bug658.cpp
new file mode 100644
index 000000000..b8c6cced4
--- /dev/null
+++ b/maxscale-system-test/bug658.cpp
@@ -0,0 +1,111 @@
+/**
+ * @file bug658.cpp regression case for bug 658 ("readconnroute: client is not closed if backend fails")
+ *
+ * - Connect all MaxScale
+ * - block Mariadb server on Master node by Firewall
+ * - execute query
+ * - unblock Mariadb server
+ * - do same test, but block all backend nodes
+ * - check if Maxscale is alive
+ */
+
+/*
+ilho Raatikka 2014-12-22 22:38:42 UTC
+Reproduce:
+1. connect readconnroute with mysql client
+2. fail the backend server
+3. execute query by using mysql client
+
+>> client hangs if write to backend socket doesn't return error (which doesn't happen in many cases)
+Comment 1 Markus Mäkelä 2014-12-23 09:19:17 UTC
+Added a check for server status before routing the query. Now if the server is down it returns an error.
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(40);
+ int i;
+
+ Test->tprintf("Connecting to Maxscale %s\n", Test->maxscale_IP);
+ Test->connect_maxscale();
+
+ printf("Setup firewall to block mysql on master\n");
+ fflush(stdout);
+ Test->repl->block_node(0);
+
+ sleep(1);
+
+ Test->tprintf("Trying query to RWSplit, expecting failure, but not a crash\n");
+ execute_query(Test->conn_rwsplit, (char *) "show processlist;");
+ fflush(stdout);
+ Test->tprintf("Trying query to ReadConn master, expecting failure, but not a crash\n");
+ execute_query(Test->conn_master, (char *) "show processlist;");
+ fflush(stdout);
+ Test->tprintf("Trying query to ReadConn slave, expecting failure, but not a crash\n");
+ execute_query(Test->conn_slave, (char *) "show processlist;");
+ fflush(stdout);
+
+ sleep(1);
+
+ Test->repl->unblock_node(0);
+ sleep(10);
+
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Checking Maxscale is alive\n");
+ Test->check_maxscale_alive();
+
+ Test->set_timeout(20);
+
+ Test->tprintf("Connecting to Maxscale %s to check its behaviour in case of blocking all bacxkends\n",
+ Test->maxscale_IP);
+ Test->connect_maxscale();
+
+ if (!Test->smoke)
+ {
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->tprintf("Setup firewall to block mysql on node %d\n", i);
+ Test->repl->block_node(i);
+ fflush(stdout);
+ }
+ sleep(1);
+
+ Test->tprintf("Trying query to RWSplit, expecting failure, but not a crash\n");
+ execute_query(Test->conn_rwsplit, (char *) "show processlist;");
+ fflush(stdout);
+ Test->tprintf("Trying query to ReadConn master, expecting failure, but not a crash\n");
+ execute_query(Test->conn_master, (char *) "show processlist;");
+ fflush(stdout);
+ Test->tprintf("Trying query to ReadConn slave, expecting failure, but not a crash\n");
+ execute_query(Test->conn_slave, (char *) "show processlist;");
+ fflush(stdout);
+
+ sleep(1);
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->tprintf("Setup firewall back to allow mysql on node %d\n", i);
+ Test->repl->unblock_node(i);
+ fflush(stdout);
+ }
+ }
+ Test->stop_timeout();
+ Test->tprintf("Sleeping 20 seconds\n");
+ sleep(20);
+
+ Test->set_timeout(20);
+
+ Test->close_maxscale_connections();
+ Test->tprintf("Checking Maxscale is alive\n");
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug662.cpp b/maxscale-system-test/bug662.cpp
new file mode 100644
index 000000000..af7e514f3
--- /dev/null
+++ b/maxscale-system-test/bug662.cpp
@@ -0,0 +1,79 @@
+/**
+ * @file bug662.cpp regression case for bug 662 ("MaxScale hangs in startup if backend server is not responsive"), covers also bug680 ("RWSplit can't load DB user if backend is not available at MaxScale start")
+ *
+ * - block all Mariadb servers Firewall
+ * - restart MaxScale
+ * - check it took no more then 20 seconds
+ * - unblock Mariadb servers
+ * - sleep one minute
+ * - check if Maxscale is alive
+ */
+
+/*
+Vilho Raatikka 2014-12-29 08:38:28 UTC
+During startup, load_mysql_users tries to read the contents of the mysql.user table. If the chosen backend is not responsive, connection hangs for a long time.
+Comment 1 Vilho Raatikka 2014-12-29 11:41:32 UTC
+The issue causes long stalls for the executing thread whenever getUsers function is called and one or more backends are not responsive.
+Comment 2 Vilho Raatikka 2014-12-29 11:50:10 UTC
+dbusers.c: Added function for setting read, write and connection timeout values. Set default timeouts for getUsers. Defaults are listed in service.c
+ gateway.c:shutdown_server is called whenever MaxScale is to be shut down. Added call for service_shutdown to shutdown_server.
+ service.c:service_alloc: replaced malloc with calloc and removed unnecessary zero/NULL initialization statements as a consequence.
+ serviceStart: Exit serviceStartPort loop if shutdown flag is set for the service.
+ serviceStartAll: Exit serviceStart loop if shutdown flag is set for the service.
+ service.c: Added service_shutdown which sets shutdown flag for each service found in allServices list.
+ service.h: Added prototype for service_shutdown
+*/
+
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+ int i;
+
+ Test->tprintf("Connecting to Maxscale %s\n", Test->maxscale_IP);
+
+ Test->tprintf("Connecting to Maxscale %s to check its behaviour in case of blocking all backends\n",
+ Test->maxscale_IP);
+ Test->connect_maxscale();
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->tprintf("Setup firewall to block mysql on node %d\n", i);
+ Test->repl->block_node(i);
+ fflush(stdout);
+ }
+
+ Test->set_timeout(100);
+ Test->restart_maxscale();
+
+ Test->set_timeout(20);
+ Test->tprintf("Checking if MaxScale is alive by connecting to MaxAdmin\n");
+ Test->add_result(Test->execute_maxadmin_command((char* ) "show servers"), "Maxadmin execution failed.\n");
+
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->tprintf("Setup firewall back to allow mysql on node %d\n", i);
+ Test->repl->unblock_node(i);
+ fflush(stdout);
+ }
+
+ Test->stop_timeout();
+ Test->tprintf("Sleeping 30 seconds\n");
+ sleep(30);
+
+ Test->set_timeout(20);
+
+ Test->tprintf("Checking Maxscale is alive\n");
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+ //}
+}
diff --git a/maxscale-system-test/bug664.cpp b/maxscale-system-test/bug664.cpp
new file mode 100644
index 000000000..ef7c22801
--- /dev/null
+++ b/maxscale-system-test/bug664.cpp
@@ -0,0 +1,316 @@
+/**
+ * @file bug664.cpp bug664 regression case ("Core: Access of freed memory in gw_send_authentication_to_backend")
+ *
+ * - Maxscale.cnf contains:
+ * @verbatim
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxuser
+passwd=maxpwd
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=maxuser
+passwd=maxpwd
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxuser
+passwd=maxpwd
+filters=QLA
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxuser
+passwd=maxpwd
+filters=QLA
+
+[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
+
+ @endverbatim
+ * - warning is expected in the log, but not an error. All Maxscale services should be alive.
+ * - Check MaxScale is alive
+ */
+
+/*
+Vilho Raatikka 2014-12-29 18:12:23 UTC
+All these cases are due to accessing freed dcb->data (MYSQL_session *):
+
+==12419== Invalid read of size 1
+==12419== at 0x1B1434BA: gw_send_authentication_to_backend (mysql_common.c:544)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690285 is 149 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x1B1434D6: gw_send_authentication_to_backend (mysql_common.c:547)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x186901f0 is 0 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 8
+==12419== at 0x1B1435FC: gw_send_authentication_to_backend (mysql_common.c:572)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x186901f0 is 0 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 8
+==12419== at 0x1B143606: gw_send_authentication_to_backend (mysql_common.c:572)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x186901f8 is 8 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 4
+==12419== at 0x1B143611: gw_send_authentication_to_backend (mysql_common.c:572)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690200 is 16 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCA2: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B143719: gw_send_authentication_to_backend (mysql_common.c:604)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690204 is 20 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCB4: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B143719: gw_send_authentication_to_backend (mysql_common.c:604)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690205 is 21 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCA2: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B143893: gw_send_authentication_to_backend (mysql_common.c:660)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690204 is 20 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCB4: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B143893: gw_send_authentication_to_backend (mysql_common.c:660)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690205 is 21 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2DE21: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B1438AF: gw_send_authentication_to_backend (mysql_common.c:660)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x1869020a is 26 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 2
+==12419== at 0x4C2DEA0: memcpy@@GLIBC_2.14 (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B1438AF: gw_send_authentication_to_backend (mysql_common.c:660)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690206 is 22 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCA2: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B1438BE: gw_send_authentication_to_backend (mysql_common.c:661)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690204 is 20 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419==
+==12419== Invalid read of size 1
+==12419== at 0x4C2CCB4: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x1B1438BE: gw_send_authentication_to_backend (mysql_common.c:661)
+==12419== by 0x1B13F90E: gw_read_backend_event (mysql_backend.c:228)
+==12419== by 0x588CA2: process_pollq (poll.c:858)
+==12419== by 0x58854B: poll_waitevents (poll.c:608)
+==12419== by 0x57C11B: main (gateway.c:1792)
+==12419== Address 0x18690205 is 21 bytes inside a block of size 278 free'd
+==12419== at 0x4C2AF6C: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
+==12419== by 0x57D806: dcb_final_free (dcb.c:406)
+==12419== by 0x57DDE6: dcb_process_zombies (dcb.c:603)
+==12419== by 0x588598: poll_waitevents (poll.c:613)
+==12419== by 0x57C11B: main (gateway.c:1792)
+Comment 1 Vilho Raatikka 2014-12-29 18:29:11 UTC
+ dcb_final_free:don't free dcb->data, it is either freed in session_alloc if session creation fails or in session_free only.
+ mysql_client.c:gw_mysql_do_authentication:if anything fails, and session_alloc won't be called, free dcb->data.
+ mysql_common.c:gw_send_authentication_to_backend:if session is already closing then return with error.
+Comment 2 Markus Mäkelä 2014-12-30 08:58:59 UTC
+Created attachment 170 [details]
+failing configuration
+
+The attached configuration currently crashes into a debug assert in handleError in readconnroute.c when connecting to port 4006. If this is removed, the next point of failure is in dcb_final_free when the session->data object is freed.
+Comment 3 Vilho Raatikka 2014-12-30 10:14:43 UTC
+(In reply to comment #2)
+> Created attachment 170 [details]
+> failing configuration
+>
+> The attached configuration currently crashes into a debug assert in
+> handleError in readconnroute.c when connecting to port 4006. If this is
+> removed, the next point of failure is in dcb_final_free when the
+> session->data object is freed.
+
+Should this be open or closed based on the information provided?
+Comment 4 Markus Mäkelä 2014-12-30 10:17:55 UTC
+My apologies, I thought I did reopen it.
+Comment 5 Vilho Raatikka 2014-12-30 10:27:32 UTC
+Fixed double freeing dcb->data if authentication phase fails.
+Comment 6 Vilho Raatikka 2014-12-30 10:30:12 UTC
+Reopen due to crash. Another double free somewhere.
+Comment 7 Vilho Raatikka 2014-12-30 11:36:38 UTC
+Cloned session was freeing the shared 'data' dcb->data/session->data. Now only session_free for the non-clone session is allowed to free the data.
+
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ Test->connect_maxscale();
+
+ Test->tprintf("Trying query to ReadConn master\n");
+ fflush(stdout);
+ Test->try_query(Test->conn_master, "show processlist;");
+ Test->tprintf("Trying query to ReadConn slave\n");
+ Test->try_query(Test->conn_slave, "show processlist;");
+
+ Test->close_maxscale_connections();
+
+ Test->check_log_err((char *) "Creating client session for Tee filter failed. Terminating session.", true);
+ Test->check_log_err((char *) "Failed to create filter 'DuplicaFilter' for service 'RW_Router'", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bug670.cpp b/maxscale-system-test/bug670.cpp
new file mode 100644
index 000000000..dcd68d8a0
--- /dev/null
+++ b/maxscale-system-test/bug670.cpp
@@ -0,0 +1,267 @@
+/**
+ * @file bug670.cpp bug670 regression case
+ * configuration
+ * @verbatim
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+monitor_interval=10000
+servers=server1,server2,server3,server4
+detect_replication_lag=1
+detect_stale_master=1
+user=maxuser
+passwd=maxpwd
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[qla]
+type=filter
+module=qlafilter
+options=/tmp/QueryLog
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split2
+
+[testfilter]
+type=filter
+module=foobar
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+#servers=server1
+max_slave_connections=100%
+use_sql_variables_in=all
+#use_sql_variables_in=master
+user=maxuser
+passwd=maxpwd
+filters=typo|qla|regex|hints|regex|hints
+enable_root_user=1
+
+[RW Split2]
+type=service
+router=readwritesplit
+servers=server1,server2
+max_slave_connections=100%
+use_sql_variables_in=all
+user=maxuser
+passwd=maxpwd
+#filters=qla|tests|hints
+
+[Read Connection Router]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2
+user=maxuser
+passwd=maxpwd
+filters=duplicate
+
+[HTTPD Router]
+type=service
+router=testroute
+servers=server1,server2,server3
+
+[Debug Interface]
+type=service
+router=debugcli
+
+[CLI]
+type=service
+router=cli
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[RW Split Listener2]
+type=listener
+service=RW Split2
+protocol=MySQLClient
+port=4012
+
+[Read Connection Listener]
+type=listener
+service=Read Connection Router
+protocol=MySQLClient
+port=4008
+#socket=/tmp/readconn.sock
+
+ @endverbatim
+ * execute following SQLs against all services in the loop (100 times)
+ * @verbatim
+set autocommit=0;
+use mysql;
+set autocommit=1;
+use test;
+set autocommit=0;
+use mysql;
+set autocommit=1;
+select user,host from user;
+set autocommit=0;
+use fakedb;
+use test;
+use mysql;
+use dontuse;
+use mysql;
+drop table if exists t1;
+commit;
+use test;
+use mysql;
+set autocommit=1;
+create table t1(id integer primary key);
+insert into t1 values(5);
+use test;
+use mysql;
+select user from user;
+set autocommit=0;
+set autocommit=1;
+set autocommit=0;
+insert into mysql.t1 values(7);
+use mysql;
+rollback work;
+commit;
+delete from mysql.t1 where id=7;
+insert into mysql.t1 values(7);
+select host,user from mysql.user;
+set autocommit=1;
+delete from mysql.t1 where id = 7;
+select 1 as "endof cycle" from dual;
+ @endverbatim
+ *
+ * check that Maxscale is alive, no crash
+ */
+
+
+/*
+
+Description Vilho Raatikka 2014-12-30 11:54:52 UTC
+Statement router (readwritesplit) loses pending statement if the other router executes statements faster than it. Statement router assumes that client doesn't send next statement before previous has replied. The only supported exception is session command which doesn't need to reply before client may send the next statement.
+
+What happens in practice, is, that when 'next' statement arrives router's routeQuery it finds previous, still pending statement. In Debug build process traps. In Release build the pending command is overwritten.
+
+(gdb) bt
+#0 0x00007f050fa56065 in raise () from /lib64/libc.so.6
+#1 0x00007f050fa574e8 in abort () from /lib64/libc.so.6
+#2 0x00007f050fa4ef72 in __assert_fail_base () from /lib64/libc.so.6
+#3 0x00007f050fa4f022 in __assert_fail () from /lib64/libc.so.6
+#4 0x00007f050c06ddbb in route_single_stmt (inst=0x25ea750, rses=0x7f04d4002350, querybuf=0x7f04d400f130)
+ at /home/raatikka/src/git/MaxScale/server/modules/routing/readwritesplit/readwritesplit.c:2372
+#5 0x00007f050c06c2ef in routeQuery (instance=0x25ea750, router_session=0x7f04d4002350, querybuf=0x7f04d400f130)
+ at /home/raatikka/src/git/MaxScale/server/modules/routing/readwritesplit/readwritesplit.c:1895
+#6 0x00007f04f03ca573 in routeQuery (instance=0x7f04d4001f20, session=0x7f04d4002050, queue=0x7f04d400f130)
+ at /home/raatikka/src/git/MaxScale/server/modules/filter/tee.c:597
+#7 0x00007f04f8df700a in gw_read_client_event (dcb=0x7f04cc0009c0)
+ at /home/raatikka/src/git/MaxScale/server/modules/protocol/mysql_client.c:867
+#8 0x000000000058b351 in process_pollq (thread_id=4) at /home/raatikka/src/git/MaxScale/server/core/poll.c:858
+#9 0x000000000058a9eb in poll_waitevents (arg=0x4) at /home/raatikka/src/git/MaxScale/server/core/poll.c:608
+#10 0x00007f0511223e0f in start_thread () from /lib64/libpthread.so.0
+#11 0x00007f050fb0a0dd in clone () from /lib64/libc.so.6
+(gdb)
+Comment 1 Vilho Raatikka 2014-12-30 12:04:06 UTC
+Created attachment 171 [details]
+Configuration.
+
+1. Start MaxScale
+2. feed session command/statement load to port 4008 which belongs to readconnrouter. Statements are then duplicated to rwsplit which starts to lag behind immediately.
+Comment 2 Vilho Raatikka 2014-12-30 12:05:26 UTC
+Created attachment 172 [details]
+Simple script to run session command/statement load
+
+Requires setmix.sql
+Comment 3 Vilho Raatikka 2014-12-30 12:05:49 UTC
+Created attachment 173 [details]
+List of statements used by run_setmix.sh
+Comment 4 Vilho Raatikka 2014-12-31 19:13:21 UTC
+tee filter doesn't send reply to client before both routers have replied. This required adding upstream processing to tee filter. First reply is routed to client. By this tee ensures that new query is never sent to either router before they have replied to previous one.
+Comment 5 Timofey Turenko 2015-01-08 12:40:34 UTC
+test added, closing
+Comment 6 Timofey Turenko 2015-02-28 18:11:16 UTC
+Reopen: starting from 1.0.5 test for bug670 start to fail with debug assert:
+
+(gdb) bt
+#0 0x00007f542de05625 in raise () from /lib64/libc.so.6
+#1 0x00007f542de06e05 in abort () from /lib64/libc.so.6
+#2 0x00007f542ddfe74e in __assert_fail_base () from /lib64/libc.so.6
+#3 0x00007f542ddfe810 in __assert_fail () from /lib64/libc.so.6
+#4 0x00007f53fe7a2daf in clientReply (instance=0x7f53ec001970, session=0x7f53ec001aa0, reply=0x7f5404000b70)
+ at /usr/local/skysql/maxscale/server/modules/filter/tee.c:973
+#5 0x00007f54292c1b55 in clientReply (instance=0x335ae20, router_session=0x7f53ec000f90, queue=0x7f5404000b70,
+ backend_dcb=0x7f53ec000fd0) at /usr/local/skysql/maxscale/server/modules/routing/readconnroute.c:814
+#6 0x00007f54143f7fb7 in gw_read_backend_event (dcb=0x7f53ec000fd0)
+ at /usr/local/skysql/maxscale/server/modules/protocol/mysql_backend.c:577
+#7 0x000000000056962d in process_pollq (thread_id=3) at /usr/local/skysql/maxscale/server/core/poll.c:867
+#8 0x0000000000568508 in poll_waitevents (arg=0x3) at /usr/local/skysql/maxscale/server/core/poll.c:608
+#9 0x00007f542f54a9d1 in start_thread () from /lib64/libpthread.so.0
+#10 0x00007f542debb8fd in clone () from /lib64/libc.so.6
+
+*/
+
+
+
+#include
+#include
+#include "testconnections.h"
+#include "bug670_sql.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+ int i;
+
+ Test->tprintf("Connecting to all MaxScale services\n");
+ Test->add_result(Test->connect_maxscale(), "Error connecting to Maxscale\n");
+
+ Test->tprintf("executing sql 100 times (ReadConn Slave)\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(15);
+ execute_query_silent(Test->conn_slave, bug670_sql);
+ }
+
+ Test->tprintf("executing sql 100 times (ReadConn Master)\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(15);
+ execute_query_silent(Test->conn_master, bug670_sql);
+ }
+
+ Test->tprintf("executing sql 100 times (RWSplit)\n");
+ for (i = 0; i < 100; i++)
+ {
+ Test->set_timeout(15);
+ execute_query_silent(Test->conn_rwsplit, bug670_sql);
+ }
+
+ Test->set_timeout(10);
+ Test->close_maxscale_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug670_sql.h b/maxscale-system-test/bug670_sql.h
new file mode 100644
index 000000000..88040a769
--- /dev/null
+++ b/maxscale-system-test/bug670_sql.h
@@ -0,0 +1,43 @@
+#ifndef BUG670_SQL_H
+#define BUG670_SQL_H
+
+const char * bug670_sql =
+ "set autocommit=0;\
+ use mysql;\
+ set autocommit=1;\
+ use test;\
+ set autocommit=0;\
+ use mysql;\
+ set autocommit=1;\
+ select user,host from user;\
+ set autocommit=0;\
+ use fakedb;\
+ use test;\
+ use mysql;\
+ use dontuse;\
+ use mysql;\
+ drop table if exists t1;\
+ commit;\
+ use test;\
+ use mysql;\
+ set autocommit=1;\
+ create table t1(id integer primary key);\
+ insert into t1 values(5);\
+ use test;\
+ use mysql;\
+ select user from user;\
+ set autocommit=0;\
+ set autocommit=1;\
+ set autocommit=0;\
+ insert into mysql.t1 values(7);\
+ use mysql;\
+ rollback work;\
+ commit;\
+ delete from mysql.t1 where id=7;\
+ insert into mysql.t1 values(7);\
+ select host,user from mysql.user;\
+ set autocommit=1;\
+ delete from mysql.t1 where id = 7; \
+ select 1 as \"endof cycle\" from dual;\n";
+
+#endif // BUG670_SQL_H
diff --git a/maxscale-system-test/bug673.cpp b/maxscale-system-test/bug673.cpp
new file mode 100644
index 000000000..2977bb2aa
--- /dev/null
+++ b/maxscale-system-test/bug673.cpp
@@ -0,0 +1,30 @@
+/**
+ * @file bug673.cpp regression case for bug673 ("MaxScale crashes if "Users table data" is empty and "show dbusers" is executed in maxadmin")
+ *
+ * - configure wrong IP for all backends
+ * - execute maxadmin command show dbusers "RW Split Router"
+ * - check MaxScale is alive by executing maxadmin again
+ */
+
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ char result[1024];
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(20);
+
+ for (int i = 0; i < 2; i++)
+ {
+ Test->tprintf("Trying show dbusers \"RW Split Router\"\n");
+ Test->add_result(Test->get_maxadmin_param((char *) "show dbusers \"RW Split Router\"", (char *) "User names:",
+ result), "Maxadmin failed\n");
+ Test->tprintf("result %s\n", result);
+ }
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug676.cpp b/maxscale-system-test/bug676.cpp
new file mode 100644
index 000000000..0f43bb1ca
--- /dev/null
+++ b/maxscale-system-test/bug676.cpp
@@ -0,0 +1,44 @@
+/**
+ * @file bug676.cpp reproducing attempt for bug676
+ * - connect to RWSplit
+ * - stop node0
+ * - sleep 20 seconds
+ * - reconnect
+ * - check if 'USE test' is ok
+ * - check MaxScale is alive
+ */
+
+#include
+#include "testconnections.h"
+#include "mariadb_func.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections test(argc, argv);
+
+ test.set_timeout(30);
+
+ test.connect_maxscale();
+ test.tprintf("Stopping node 0");
+ test.galera->block_node(0);
+ test.close_maxscale_connections();
+
+ test.stop_timeout();
+
+ test.tprintf("Waiting until the monitor picks a new master");
+ sleep(20);
+
+ test.set_timeout(30);
+
+ test.connect_maxscale();
+ test.try_query(test.conn_rwsplit, "USE test");
+ test.try_query(test.conn_rwsplit, "show processlist;");
+ test.close_maxscale_connections();
+
+ test.stop_timeout();
+
+ test.galera->unblock_node(0);
+
+ return test.global_result;
+}
+
diff --git a/maxscale-system-test/bug681.cpp b/maxscale-system-test/bug681.cpp
new file mode 100644
index 000000000..53350a3de
--- /dev/null
+++ b/maxscale-system-test/bug681.cpp
@@ -0,0 +1,53 @@
+/**
+ * @file bug681.cpp - regression test for bug681 ("crash if max_slave_connections=10% and 4 or less backends are configured")
+ *
+ * - Configure RWSplit with max_slave_connections=10%
+ * - check ReadConn master and ReadConn slave are alive and RWSplit is not started
+ */
+
+/*
+Timofey Turenko 2015-01-05 11:33:29 UTC
+try to start MaxScale with max_slave_connections=10%
+
+
+Result:
+Program terminated with signal 8, Arithmetic exception.
+#0 0x00007ff0517fee3f in have_enough_servers (p_rses=0x7fff9ed17ed0, min_nsrv=1, router_nsrv=3, router=0x397c2b0)
+ at /usr/local/skysql/maxscale/server/modules/routing/readwritesplit/readwritesplit.c:4668
+4668 LOGIF(LE, (skygw_log_write_flush(
+Comment 1 Markus Mäkelä 2015-01-05 11:59:38 UTC
+Added casts to floating point values when doing divisions.
+
+*/
+
+
+#include
+#include "testconnections.h"
+#include "mariadb_func.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ Test->connect_maxscale();
+
+ if (mysql_errno(Test->conn_rwsplit) == 0)
+ {
+ Test->add_result(1, "RWSplit services should fail, but it is started\n");
+ }
+
+ Test->tprintf("Trying query to ReadConn master\n");
+ Test->try_query(Test->conn_master, "show processlist;");
+ Test->tprintf("Trying query to ReadConn slave\n");
+ Test->try_query(Test->conn_slave, "show processlist;");
+
+ Test->close_maxscale_connections();
+
+ Test->check_log_err((char *)
+ "Unable to start RW-Split-Router service. There are too few backend servers configured in", true);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug694.cpp b/maxscale-system-test/bug694.cpp
new file mode 100644
index 000000000..9949bf98d
--- /dev/null
+++ b/maxscale-system-test/bug694.cpp
@@ -0,0 +1,88 @@
+/**
+ * @file bug694.cpp - regression test for bug694 ("RWSplit: SELECT @a:=@a+1 as a, test.b FROM test breaks client session")
+ *
+ * - set use_sql_variables_in=all in MaxScale.cnf
+ * - connect to readwritesplit router and execute:
+ * @verbatim
+CREATE TABLE test (b integer);
+SELECT @a:=@a+1 as a, test.b FROM test;
+USE test
+@endverbatim
+ * - check if MaxScale alive
+ */
+
+/*
+
+Description Vilho Raatikka 2015-01-14 08:09:45 UTC
+Reproduce:
+- set use_sql_variables_in=all in MaxScale.cnf
+- connect to readwritesplit router and execute:
+CREATE TABLE test (b integer);
+SELECT @a:=@a+1 as a, test.b FROM test;
+USE test
+
+You'll get:
+ERROR 2006 (HY000): MySQL server has gone away
+
+It is a known limitation that SELÈCTs with SQL variable modifications are not supported. The issue is that they aren't detected and as a consequence hte client session is disconnected.
+
+It is possible to detect this kind of query in query classifier, but set_query_type loses part of the information. If both SELECT and SQL variable update are detected they can be stored in query type and rwsplit could, for example, prevent from executing the query, execute it in master only (and log), force all SQL variable modifications from that point to master (and log), etc.
+Comment 1 Vilho Raatikka 2015-01-15 13:19:18 UTC
+query_classifier.cc: set_query_type lost previous query type if the new was more restrictive. Problem was that if query is both READ and SESSION_WRITE and configuration parameter use_sql_variables_in=all was set, routing target became ambiguous. Replaced call to set_query_type with simply adding new type to type (=bit field) and checking unsupported combinations in readwritesplit.c:get_route_target. If such a case is met, a detailed error is written to error log in readwritesplit.c. mysql_client.c sees the error code and sends an error to client. Then mysql_client.c calls router's handleError which ensures that there are enough backend servers so that the session can continue.
+*/
+
+
+#include
+#include "testconnections.h"
+#include "mariadb_func.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(120);
+ Test->connect_maxscale();
+
+ Test->try_query(Test->conn_rwsplit, "USE test");
+ Test->try_query(Test->conn_rwsplit, "DROP TABLE IF EXISTS test");
+ Test->try_query(Test->conn_rwsplit, "CREATE TABLE test (b integer)");
+
+ const int iter = Test->smoke ? 10 : 100;
+ Test->tprintf("Creating and inserting %d rows into a table\n", iter);
+
+ for (int i = 0; i < iter; i++)
+ {
+ Test->set_timeout(30);
+ execute_query(Test->conn_rwsplit, "insert into test value(2);");
+ Test->stop_timeout();
+ }
+
+ Test->set_timeout(200);
+
+ Test->tprintf("Trying SELECT @a:=@a+1 as a, test.b FROM test\n");
+ if (execute_query(Test->conn_rwsplit, "SELECT @a:=@a+1 as a, test.b FROM test;") == 0)
+ {
+ Test->add_result(1, "Query succeded, but expected to fail.\n");
+ }
+ Test->tprintf("Trying USE test\n");
+ Test->try_query(Test->conn_rwsplit, "USE test");
+
+ Test->try_query(Test->conn_rwsplit, "DROP TABLE IF EXISTS test");
+
+ Test->tprintf("Checking if MaxScale alive\n");
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Checking logs\n");
+ Test->check_log_err((char *)
+ "The query can't be routed to all backend servers because it includes SELECT and SQL variable modifications which is not supported",
+ true);
+ Test->check_log_err((char *)
+ "SELECT with session data modification is not supported if configuration parameter use_sql_variables_in=all",
+ true);
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug699.cpp b/maxscale-system-test/bug699.cpp
new file mode 100644
index 000000000..6f209ffa2
--- /dev/null
+++ b/maxscale-system-test/bug699.cpp
@@ -0,0 +1,247 @@
+/**
+ * @file bug699.cpp regression case for bug 699 ( "rw-split sensitive to order of terms in field list of SELECT (round 2)" )
+ *
+ * - compare @@hostname from "select @@wsrep_node_name, @@hostname" and "select @@hostname, @@wsrep_node_name"
+ * - comapre @@server_id from "select @@wsrep_node_name, @@server_id" and "select @@server_id, @@wsrep_node_name"
+ */
+
+/*
+Kolbe Kegel 2015-01-16 18:38:15 UTC
+I opened bug #509 some time ago, but that bug was handled specifically for the case of last_insert_id(). The crux of that bug still exists in 1.0.4 GA:
+
+[root@max1 ~]# mysql -h 127.0.0.1 -P 4006 -u maxuser -pmaxpwd -e 'select @@hostname, @@wsrep_node_name; select @@wsrep_node_name, @@hostname;'
++------------+-------------------+
+| @@hostname | @@wsrep_node_name |
++------------+-------------------+
+| db2 | db2 |
++------------+-------------------+
++-------------------+------------+
+| @@wsrep_node_name | @@hostname |
++-------------------+------------+
+| db3 | db3 |
++-------------------+------------+
+
+In a single connection, fetching the values of the same two system variables results in the query being routed differently depending on the order of the variables.
+
+Is there some set of variables that should always be routed to the master for some reason? If so, that should be documented.
+
+Regardless, the order of terms in the SELECT list should not have any effect on query routing.
+Comment 1 Vilho Raatikka 2015-01-16 22:33:38 UTC
+@@wsrep_node_name can be resolved only in backend, MaxScale doesn't know about it. As a consequence, Since MaxScale doesn't know what it is it takes the safe bet and routes it to master.
+Comment 2 Kolbe Kegel 2015-01-16 22:35:13 UTC
+What does "(In reply to comment #1)
+> @@wsrep_node_name can be resolved only in backend, MaxScale doesn't know
+> about it. As a consequence, Since MaxScale doesn't know what it is it takes
+> the safe bet and routes it to master.
+
+What do you mean by "resolved only in backend" and "MaxScale doesn't know about it"?
+
+How is @@wsrep_node_name different in that respect from any other server variable that could be different on any given backend?
+Comment 3 Vilho Raatikka 2015-01-16 23:01:31 UTC
+(In reply to comment #2)
+> What does "(In reply to comment #1)
+> > @@wsrep_node_name can be resolved only in backend, MaxScale doesn't know
+> > about it. As a consequence, Since MaxScale doesn't know what it is it takes
+> > the safe bet and routes it to master.
+>
+> What do you mean by "resolved only in backend" and "MaxScale doesn't know
+> about it"?
+>
+> How is @@wsrep_node_name different in that respect from any other server
+> variable that could be different on any given backend?
+
+MaxScale doesn't know that there is such system variable as @@wsrep_node_name. In my understanding the reason is that the embedded MariaDB server doesn't have Galera's patch. Thus the variable is unknown.
+Comment 4 Kolbe Kegel 2015-01-16 23:03:13 UTC
+> >
+> > How is @@wsrep_node_name different in that respect from any other server
+> > variable that could be different on any given backend?
+>
+> MaxScale doesn't know that there is such system variable as
+> @@wsrep_node_name. In my understanding the reason is that the embedded
+> MariaDB server doesn't have Galera's patch. Thus the variable is unknown.
+
+Ahh, right. That sounds familiar. But it's still quite strange that the order of variables in the SELECT statement is meaningful, isn't it?
+Comment 5 Vilho Raatikka 2015-01-16 23:09:47 UTC
+(In reply to comment #4)
+> > >
+> > > How is @@wsrep_node_name different in that respect from any other server
+> > > variable that could be different on any given backend?
+> >
+> > MaxScale doesn't know that there is such system variable as
+> > @@wsrep_node_name. In my understanding the reason is that the embedded
+> > MariaDB server doesn't have Galera's patch. Thus the variable is unknown.
+>
+> Ahh, right. That sounds familiar. But it's still quite strange that the
+> order of variables in the SELECT statement is meaningful, isn't it?
+
+The effectiveness of atrribute order in SELECT clause is a bug which is fixed in http://bugs.skysql.com/show_bug.cgi?id=694 .
+
+MaxScale's inability to detect different system variables is slightly problematic as well but haven't really concentrated on finding a decent solution to it yet. It might, however, be necessary.
+Comment 6 Vilho Raatikka 2015-01-16 23:55:56 UTC
+Attribute order effectiveness is fixed in http://bugs.skysql.com/show_bug.cgi?id=694
+
+Inability to detect Galera's system variables is hard to overcome since Galera patch doesn't work with embedded library and embedded library doesn't know Galera's system variables.
+Comment 7 Vilho Raatikka 2015-01-17 09:38:09 UTC
+Appeared that MariaDB parsing end result depends on the order of [known,unknown] system variable pair in the query.
+
+There was similar-looking bug in query_classifier before which hide this one. However, debugging and examining the resulting thd and lex for the following queries shows that thd->free_list is non-empty if @@hostname (known variable) is before @@wsrep_node_name. If @@wsrep_node_name is first on the attribute list the resulting thd->free_list==NULL.
+In the former case resulting query type is QUERY_TYPE_SYSVAR_READ (routed to slaves) and in the latter case it is unknown (routed to master).
+
+1. select @@wsrep_node_name, @@hostname;
+2. select @@hostname, @@wsrep_node_name;
+
+Both queries produce similar response but routing them to master only limits scalability.
+Comment 8 Mark Riddoch 2015-01-28 08:39:25 UTC
+Raised this with the server team as the issue is related to the behaviour of the parser in the embedded server. System variables are resolved at parse time, unknown variables result in a parse error normally, however this order dependency is slightly puzzling.
+Comment 9 Mark Riddoch 2015-02-13 10:06:08 UTC
+Hi Sergei,
+
+we have an interesting bug in MaxScale related to parsing. If we try to parse the query
+
+select @@wsrep_node_name;
+
+Using the embedded server we link with we do not get a parse tree. A select of other system variables works. I guess the parser is resolving the name of the variable rather than leaving it to the execution phase. Since we do not have Galera this variable is unknown. What is even more strange is that if we have a query of the form
+
+select @@hostname, @@wsrep_node_name;
+
+We do get a parse tree, but reversing the order of the select we again fail to get a parse tree with the query
+
+select @@wsrep_node_name, @@hostname;
+
+For our purposes we would ideally like to disable the resolving of the variable name at parse time, since that would give us flexibility with regard to new variables being introduced in the servers. Do you know if this is possible or if there is some easy fix we can do to the MariaDB parser that will help us here?
+
+For your reference the MaxScale bug report can be found here http://bugs.skysql.com/show_bug.cgi?id=699
+
+
+Thanks
+Mark
+Comment 10 Mark Riddoch 2015-02-13 10:08:34 UTC
+Hi, Mark!
+
+On Jan 28, Mark Riddoch wrote:
+> Hi Sergei,
+>
+> we have an interesting bug in MaxScale related to parsing. If we try
+> to parse the query
+>
+> select @@wsrep_node_name;
+>
+> Using the embedded server we link with we do not get a parse tree. A
+> select of other system variables works. I guess the parser is
+> resolving the name of the variable rather than leaving it to the
+> execution phase. Since we do not have Galera this variable is unknown.
+
+Right... That would be *very* difficult to fix, it'd require a pretty
+serious refactoring to get this out of the parser.
+
+> What is even more strange is that if we have a query of the form
+>
+> select @@hostname, @@wsrep_node_name;
+>
+> We do get a parse tree, but reversing the order of the select we again
+> fail to get a parse tree with the query
+>
+> select @@wsrep_node_name, @@hostname;
+
+That depends on what you call a "parse tree". Items of the select clause
+are stored in the thd->lex->current_select->item_list.
+
+For the first query, the list have 1 element, Item_func_get_system_var
+for @@hostname.
+
+For the second query the list has 0 elements. In both cases, I've
+examined the list in the debugger after MYSQLparse() returned.
+
+So apparently the parsing as aborted as soon as unknown variable is
+encountered.
+
+> For our purposes we would ideally like to disable the resolving of the
+> variable name at parse time, since that would give us flexibility with
+> regard to new variables being introduced in the servers. Do you know
+> if this is possible or if there is some easy fix we can do to the
+> MariaDB parser that will help us here?
+
+I don't see how you can do that from MaxScale.
+This looks like something that has to be fixed in the server.
+Perhaps - not sure - it'd be possible to introduce some "mode" for the
+parser where it doesn't check for valid variable names in certain
+contextes.
+
+Regards,
+Sergei
+
+*/
+
+
+
+#include
+#include "testconnections.h"
+
+const char * sel1 = "select @@wsrep_node_name, @@hostname";
+const char * sel2 = "select @@hostname, @@wsrep_node_name";
+
+const char * sel3 = "select @@wsrep_node_name, @@server_id";
+const char * sel4 = "select @@server_id, @@wsrep_node_name";
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ Test->connect_maxscale();
+
+ Test->tprintf("Trying \n");
+
+ char serverid1[1024];
+ char serverid2[1024];
+
+ if ( (
+ find_field(
+ Test->conn_rwsplit, sel3,
+ "@@server_id", &serverid1[0])
+ != 0 ) || (
+ find_field(
+ Test->conn_rwsplit, sel4,
+ "@@server_id", &serverid2[0])
+ != 0 ))
+ {
+ Test->add_result(1, "@@server_id field not found!!\n");
+ delete Test;
+ exit(1);
+ }
+ else
+ {
+ Test->tprintf("'%s' to RWSplit gave @@server_id %s\n", sel3, serverid1);
+ Test->tprintf("'%s' directly to master gave @@server_id %s\n", sel4, serverid2);
+ Test->add_result(strcmp(serverid1, serverid2),
+ "server_id are different depending in which order terms are in SELECT\n");
+ }
+
+ if ( (
+ find_field(
+ Test->conn_rwsplit, sel1,
+ "@@hostname", &serverid1[0])
+ != 0 ) || (
+ find_field(
+ Test->conn_rwsplit, sel2,
+ "@@hostname", &serverid2[0])
+ != 0 ))
+ {
+ Test->add_result(1, "@@hostname field not found!!\n");
+ delete Test;
+ exit(1);
+ }
+ else
+ {
+ Test->tprintf("'%s' to RWSplit gave @@hostname %s\n", sel1, serverid1);
+ Test->tprintf("'%s' to RWSplit gave @@hostname %s\n", sel2, serverid2);
+ Test->add_result(strcmp(serverid1, serverid2),
+ "hostname are different depending in which order terms are in SELECT\n");
+ }
+
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug705.cpp b/maxscale-system-test/bug705.cpp
new file mode 100644
index 000000000..ec11bd5b5
--- /dev/null
+++ b/maxscale-system-test/bug705.cpp
@@ -0,0 +1,261 @@
+/**
+ * @file bug705.cpp regression case for bug 705 ("Authentication fails when the user connects to a database when the SQL mode includes ANSI_QUOTES")
+ *
+ * - use only one backend
+ * - derectly to backend SET GLOBAL sql_mode="ANSI"
+ * - restart MaxScale
+ * - check log for "Error : Loading database names for service RW_Split encountered error: Unknown column"
+ */
+
+/*
+ivan.stoykov@skysql.com 2015-01-26 14:01:11 UTC
+When the sql_mode includes ANSI_QUOTES, maxscale fails to execute the SQL at LOAD_MYSQL_DATABASE_NAMES string
+
+https://github.com/mariadb-corporation/MaxScale/blob/master/server/core/dbusers.c
+line 90:
+#define LOAD_MYSQL_DATABASE_NAMES "SELECT * FROM ( (SELECT COUNT(1) AS ndbs FROM INFORMATION_SCHEMA.SCHEMATA) AS tbl1, (SELECT GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, \"\'\",\"\")=CURRENT_USER()) AS tbl2)"
+
+the error log outputs that string:
+"Error : Loading database names for service galera_bs_router encountered error: Unknown column ''' in 'where clause'"
+
+I think the quotes in LOAD_MYSQL_DATABASE_NAMES and all the SQL used by MaxScale should be adjusted according to the recent sql_mode at the backend server.
+
+How to repeat:
+mysql root@centos-7-minimal:[Mon Jan 26 15:00:48 2015][(none)]> SET SESSION sql_mode = "ORACLE"; select @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++----------------------------------------------------------------------------------------------------------------------+
+| @@sql_mode |
++----------------------------------------------------------------------------------------------------------------------+
+| PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ORACLE,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS,NO_AUTO_CREATE_USER |
++----------------------------------------------------------------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql root@centos-7-minimal:[Mon Jan 26 15:00:55 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
+ERROR 1054 (42S22): Unknown column '\'' in 'where clause'
+mysql root@centos-7-minimal:[Mon Jan 26 15:00:57 2015][(none)]>
+Comment 1 ivan.stoykov@skysql.com 2015-01-26 14:02:42 UTC
+Work around: set the sql_mode without ANSI_QUOTES:
+
+mysql root@centos-7-minimal:[Mon Jan 26 15:00:57 2015][(none)]> SET SESSION sql_mode = "MYSQL323"; select @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++------------------------------+
+| @@sql_mode |
++------------------------------+
+| MYSQL323,HIGH_NOT_PRECEDENCE |
++------------------------------+
+1 row in set (0.00 sec)
+
+mysql root@centos-7-minimal:[Mon Jan 26 15:01:52 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
++------------------+-----------------------+-----------------------------------+--------------------+----------------+
+| @@innodb_version | @@version | @@version_comment | GRANTEE | PRIVILEGE_TYPE |
++------------------+-----------------------+-----------------------------------+--------------------+----------------+
+| 5.6.21-70.0 | 10.0.15-MariaDB-wsrep | MariaDB Server, wsrep_25.10.r4144 | 'root'@'localhost' | SHOW DATABASES |
++------------------+-----------------------+-----------------------------------+--------------------+----------------+
+1 row in set (0.00 sec)
+Comment 2 Massimiliano 2015-01-26 14:19:45 UTC
+More informations needed for "the recent sql_mode at the backend server"
+
+Is that an issue with a particular mysql/mariadb backend version?
+Comment 3 ivan.stoykov@skysql.com 2015-01-26 14:30:08 UTC
+No, it is not related to any particular version.
+
+I tested on Percona, MySQL , MariaDB 5.5, MariaDB 10.0.15 with the same result:
+
+[Mon Jan 26 16:24:34 2015][mysql]> SET SESSION sql_mode = ""; SELECT @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++------------+
+| @@sql_mode |
++------------+
+| |
++------------+
+1 row in set (0.00 sec)
+[Mon Jan 26 16:24:53 2015][mysql]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
++------------------+-----------------+--------------------------------------------------+--------------------+----------------+
+| @@innodb_version | @@version | @@version_comment | GRANTEE | PRIVILEGE_TYPE |
++------------------+-----------------+--------------------------------------------------+--------------------+----------------+
+| 5.5.41-37.0 | 5.5.41-37.0-log | Percona Server (GPL), Release 37.0, Revision 727 | 'seik'@'localhost' | SHOW DATABASES |
++------------------+-----------------+--------------------------------------------------+--------------------+----------------+
+1 row in set (0.00 sec)
+
+[Mon Jan 26 16:24:57 2015][mysql]> SET SESSION sql_mode = "DB2";SELECT @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++-----------------------------------------------------------------------------------------------+
+| @@sql_mode |
++-----------------------------------------------------------------------------------------------+
+| PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,DB2,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS |
++-----------------------------------------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+:[Mon Jan 26 16:26:19 2015][mysql]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
+ERROR 1054 (42S22): Unknown column '\'' in 'where clause'
+
+mysql root@centos-7-minimal:[Mon Jan 26 14:27:33 2015][(none)]> SET SESSION sql_mode = "POSTGRESQL"; select @@sql_mode; Query OK, 0 rows affected (0.00 sec)
+
++------------------------------------------------------------------------------------------------------+
+| @@sql_mode |
++------------------------------------------------------------------------------------------------------+
+| PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,POSTGRESQL,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS |
++------------------------------------------------------------------------------------------------------+
+1 row in set (0.01 sec)
+
+mysql root@centos-7-minimal:[Mon Jan 26 14:42:23 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
+ERROR 1054 (42S22): Unknown column '\'' in 'where clause'
+mysql root@centos-7-minimal:[Mon Jan 26 14:58:57 2015][(none)]> SET SESSION sql_mode = ""; select @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++------------+
+| @@sql_mode |
++------------+
+| |
++------------+
+1 row in set (0.00 sec)
+
+mysql root@centos-7-minimal:[Mon Jan 26 14:59:03 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
++------------------+-----------+------------------------------+--------------------+----------------+
+| @@innodb_version | @@version | @@version_comment | GRANTEE | PRIVILEGE_TYPE |
++------------------+-----------+------------------------------+--------------------+----------------+
+| 5.6.22 | 5.6.22 | MySQL Community Server (GPL) | 'root'@'localhost' | SHOW DATABASES |
++------------------+-----------+------------------------------+--------------------+----------------+
+1 row in set (0.00 sec)
+
+mysql root@istoykov.skysql.com:[Mon Jan 26 15:28:12 2015][(none)]> SET SESSION sql_mode = "DB2"; SELECT @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++-----------------------------------------------------------------------------------------------+
+| @@sql_mode |
++-----------------------------------------------------------------------------------------------+
+| PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,DB2,NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS |
++-----------------------------------------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+mysql root@istoykov.skysql.com:[Mon Jan 26 15:28:19 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
+ERROR 1054 (42S22): Unknown column '\'' in 'where clause'
+mysql root@istoykov.skysql.com:[Mon Jan 26 15:28:32 2015][(none)]> SET SESSION sql_mode = "MYSQL40"; SELECT @@sql_mode;
+Query OK, 0 rows affected (0.00 sec)
+
++-----------------------------+
+| @@sql_mode |
++-----------------------------+
+| MYSQL40,HIGH_NOT_PRECEDENCE |
++-----------------------------+
+1 row in set (0.00 sec)
+
+mysql root@istoykov.skysql.com:[Mon Jan 26 15:29:09 2015][(none)]> SELECT @@innodb_version,@@version,@@version_comment, GRANTEE,PRIVILEGE_TYPE from INFORMATION_SCHEMA.USER_PRIVILEGES WHERE privilege_type='SHOW DATABASES' AND REPLACE(GRANTEE, "\'","")=CURRENT_USER();
++---------------------+--------------------+-------------------+--------------------+----------------+
+| @@innodb_version | @@version | @@version_comment | GRANTEE | PRIVILEGE_TYPE |
++---------------------+--------------------+-------------------+--------------------+----------------+
+| 5.5.38-MariaDB-35.2 | 5.5.39-MariaDB-log | MariaDB Server | 'root'@'localhost' | SHOW DATABASES |
++---------------------+--------------------+-------------------+--------------------+----------------+
+1 row in set (0.00 sec)
+Comment 4 Massimiliano 2015-01-26 14:48:04 UTC
+It's still not clear if the issue is related to MaxScale or it's spotted only when yoy send the statements via mysql client
+Comment 5 ivan.stoykov@skysql.com 2015-01-26 16:37:32 UTC
+There is at least one case that after setting the sql_mode to string :
+"REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" at 10.0.15-MariaDB-wsrep-log , using maxscale in this way returned an error.
+
+$ mysql --host max-scale-host --user=test --password=xxx --port 4449 mysqlslap
+ERROR 1045 (28000): Access denied for user 'test'@'IP (using password: YES) to database 'mysqlslap'
+
+error at the maxscale log:
+Error : Loading database names for service galera_bs_router encountered error: Unknown column ''' in 'where clause'.
+
+the following test was OK:
+$ mysql --host max-scale-host --user=test --password=xxx --port 4449
+
+After switch sql_mode to '' as "mysql> set global sql_mode='';",
+the connection of the user to a database seems to work OK:
+$ mysql --host max-scale-host --user=test --password=xxx -D mysqlslap
+Reading table information for completion of table and column names
+You can turn off this feature to get a quicker startup with -A
+
+Welcome to the MySQL monitor. Commands end with ; or \g.
+Your MySQL connection id is 2532
+Server version: 5.5.41-MariaDB MariaDB Server, wsrep_25.10.r4144
+
+Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+
+Oracle is a registered trademark of Oracle Corporation and/or its
+affiliates. Other names may be trademarks of their respective
+owners.
+
+Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
+
+mysql> Bye
+
+
+If needed , I will prepare other test case ?
+Comment 6 Massimiliano 2015-01-26 16:40:45 UTC
+Yes, please provide us other test cases and we will try to reproduce it
+Comment 7 Markus Mäkelä 2015-01-26 18:23:25 UTC
+Changed the double quotation marks to single quotation marks because the MySQL client manual says that ANSI_QUOTES still accepts single quotes.
+
+This can be verified by first setting sql_mode to ANSI:
+
+set global sql_mode="ANSI";
+
+after that, start MaxScale and the error log contains:
+
+MariaDB Corporation MaxScale /home/markus/build/log/skygw_err1.log Mon Jan 26 20:16:17 2015
+-----------------------------------------------------------------------
+--- Logging is enabled.
+2015-01-26 20:16:17 Error : Loading database names for service RW Split Router encountered error: Unknown column ''' in 'where clause'.
+2015-01-26 20:16:17 Error : Loading database names for service RW Split Hint Router encountered error: Unknown column ''' in 'where clause'.
+2015-01-26 20:16:17 Error : Loading database names for service Read Connection Router encountered error: Unknown column ''' in 'where clause'.
+
+After the change the error is gone.
+Comment 8 Massimiliano 2015-01-26 21:16:03 UTC
+I managed to reproduce it in my environment:
+
+- created a setup with 1 server in a service named "RW_Split"
+
+- issued SET GLOBAL sql_mode="ANSI" via mysql client to that server
+
+- started MaxScale and found an error in the log:
+
+
+2015-01-26 16:10:52 Error : Loading database names for service RW_Split encountered error: Unknown column ''' in 'where clause'.
+
+*/
+
+
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ printf("Connecting to backend %s\n", Test->repl->IP[0]);
+ fflush(stdout);
+ Test->repl->connect();
+
+ Test->tprintf("Sending SET GLOBAL sql_mode=\"ANSI\" to backend %s\n", Test->repl->IP[0]);
+ execute_query(Test->repl->nodes[0], "SET GLOBAL sql_mode=\"ANSI\"");
+
+ Test->repl->close_connections();
+
+ Test->tprintf("Restarting MaxScale\n");
+
+ Test->set_timeout(100);
+ Test->restart_maxscale();
+
+ Test->stop_maxscale();
+ Test->stop_timeout();
+ Test->tprintf("Waiting 20 seconds\n");
+ sleep(20);
+
+ Test->set_timeout(60);
+ Test->check_log_err((char *) "Loading database names", false);
+ Test->check_log_err((char *) "Unknown column", false);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+ // }
+}
diff --git a/maxscale-system-test/bug711.cpp b/maxscale-system-test/bug711.cpp
new file mode 100644
index 000000000..23e74d0a7
--- /dev/null
+++ b/maxscale-system-test/bug711.cpp
@@ -0,0 +1,58 @@
+/**
+ * @file bug711.cpp bug711 regression case (Some MySQL Workbench Management actions hang with R/W split router)
+ * - configure rwsplit with use_sql_variables_in=all
+ * - try SHOW GLOBAL STATUS with all routers
+ * - check if Maxscale is still alive
+ */
+
+/*
+Massimiliano 2015-01-29 15:35:52 UTC
+Some MySQL Workbench Management actions hang with R/W split router
+
+MySQL Workbench 6.2 on OSX
+
+
+When selecting "Users and Privileges" the client gets hanged.
+
+
+
+The quick solution is setting "use_sql_variables_in" in MaxScale config file for R/W split router section
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=massi
+passwd=xxxx
+
+use_sql_variables_in=master
+
+
+
+This way everything seems ok
+
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->connect_maxscale();
+ Test->set_timeout(10);
+ Test->tprintf("Trying SHOW GLOBAL STATUS against RWSplit\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "SHOW GLOBAL STATUS;");
+ Test->tprintf("Trying SHOW GLOBAL STATUS against ReadConn master\n");
+ Test->try_query(Test->conn_master, (char *) "SHOW GLOBAL STATUS;");
+ Test->tprintf("Trying SHOW GLOBAL STATUS against ReadConn slave\n");
+ Test->try_query(Test->conn_slave, (char *) "SHOW GLOBAL STATUS;");
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug718.cpp b/maxscale-system-test/bug718.cpp
new file mode 100644
index 000000000..fbb512e61
--- /dev/null
+++ b/maxscale-system-test/bug718.cpp
@@ -0,0 +1,103 @@
+/**
+ * @file bug718.cpp bug718 (MXS-19) regression case REMOVED FROM TEST SUITE!! (because manuall Master setting breaks backend)
+ * trying to execute INSERTS from several paralell threads when monitors are disabled
+ */
+
+
+
+#include
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "maxadmin_operations.h"
+
+using namespace std;
+
+TestConnections * Test;
+void *thread1( void *ptr );
+//void *thread2( void *ptr );
+
+int iterations;
+
+int db1_num = 0;
+int main(int argc, char *argv[])
+{
+ Test = new TestConnections(argc, argv);
+ iterations = (Test->smoke) ? 20 : 100;
+ Test->set_timeout(20);
+ int i;
+
+ Test->execute_maxadmin_command((char *) "set server server1 master");
+ Test->execute_maxadmin_command((char *) "set server server2 slave");
+ Test->execute_maxadmin_command((char *) "set server server3 slave");
+ Test->execute_maxadmin_command((char *) "set server server4 slave");
+
+ Test->execute_maxadmin_command((char *) "set server g_server1 master");
+ Test->execute_maxadmin_command((char *) "set server g_server2 slave");
+ Test->execute_maxadmin_command((char *) "set server g_server3 slave");
+ Test->execute_maxadmin_command((char *) "set server g_server4 slave");
+
+ Test->tprintf("Connecting to all MaxScale services\n");
+ Test->add_result(Test->connect_maxscale(), "Error connection to Maxscale\n");
+
+ //MYSQL * galera_rwsplit = open_conn(4016, Test->Maxscale_IP, Test->Maxscale_User, Test->Maxscale_Password);
+
+ Test->tprintf("executing show status 1000 times\n");
+
+ int ThreadsNum = 25;
+ pthread_t thread_v1[ThreadsNum];
+
+ int iret1[ThreadsNum];
+ for (i = 0; i < ThreadsNum; i ++)
+ {
+ iret1[i] = pthread_create(&thread_v1[i], NULL, thread1, NULL);
+ }
+
+ create_t1(Test->conn_rwsplit);
+ for (i = 0; i < iterations; i++)
+ {
+ Test->set_timeout(200);
+ insert_into_t1(Test->conn_rwsplit, 4);
+ printf("i=%d\n", i);
+ }
+ Test->set_timeout(300);
+ for (i = 0; i < ThreadsNum; i ++)
+ {
+ pthread_join(thread_v1[i], NULL);
+ }
+
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+void *thread1( void *ptr )
+{
+ MYSQL * conn = open_conn(Test->rwsplit_port , Test->maxscale_IP, Test->maxscale_user, Test->maxscale_password,
+ Test->ssl);
+ MYSQL * g_conn = open_conn(4016 , Test->maxscale_IP, Test->maxscale_user, Test->maxscale_password, Test->ssl);
+ char sql[1034];
+
+ sprintf(sql, "CREATE DATABASE IF NOT EXISTS test%d;", db1_num);
+ execute_query(conn, sql);
+ sprintf(sql, "USE test%d", db1_num);
+ execute_query(conn, sql);
+
+ create_t1(conn);
+ create_t1(g_conn);
+ for (int i = 0; i < iterations; i++)
+ {
+ insert_into_t1(conn, 4);
+ insert_into_t1(g_conn, 4);
+ if ((i / 100) * 100 == i)
+ {
+ printf("Iteration %d\n", i);
+ fflush(stdout);
+ }
+ }
+ return NULL;
+}
+
diff --git a/maxscale-system-test/bug729.cpp b/maxscale-system-test/bug729.cpp
new file mode 100644
index 000000000..f68c8c8a4
--- /dev/null
+++ b/maxscale-system-test/bug729.cpp
@@ -0,0 +1,72 @@
+/**
+ * @file bug729.cpp regression case for bug 729 ("PDO prepared statements bug introduced")
+ *
+ * - execute following PHP script
+ * @verbatim
+ PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_EMULATE_PREPARES => false,
+];
+
+$host=$argv[1];
+$port=$argv[2];
+$user=$argv[3];
+$pass=$argv[4];
+
+$dsn = "mysql:host=".$host.";port=".$port.";dbname=information_schema";
+$dbh = new PDO( $dsn, $user, $pass, $options );
+$res = $dbh
+ ->query( "SELECT COLLATION_NAME FROM COLLATIONS" )
+ ->fetch( PDO::FETCH_COLUMN );
+
+var_dump( $res );
+
+ @endverbatim
+ * - check log for "Can't route MYSQL_COM_STMT_PREPARE"
+ */
+
+/*
+
+Description Andreas K-Hansen 2015-02-12 19:32:13 UTC
+
+The error occurred when upgrading from Maxscale 1.0.4 to 1.0.5.
+The following exception occurs when trying to execute a query with prepared statements enabled:
+
+PHP Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 Routing query to backend failed. See the error log for further details.' in /root/test.php:10
+Stack trace:
+#0 /root/test.php(10): PDO->query('SELECT COLLATIO...')
+#1 {main}
+ thrown in /root/test.php on line 10
+
+- Error log
+Feb 12 19:14:01 363d6aec0f8c MaxScale[263]: Error: Failed to obtain address for host ::1, Address family for hostname not supported
+Feb 12 19:14:01 363d6aec0f8c MaxScale[263]: Warning: Failed to add user root@::1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+Feb 12 19:14:01 363d6aec0f8c MaxScale[263]: Warning: Failed to add user root@127.0.0.1 for service [RW Split Router]. This user will be unavailable via MaxScale.
+Feb 12 19:14:10 363d6aec0f8c MaxScale[263]: Warning : The query can't be routed to all backend servers because it includes SELECT and SQL variable modifications which is not supported. Set use_sql_variables_in=master or split the query to two, where SQL variable modifications are done in the first and the SELECT in the second one.
+Feb 12 19:14:10 363d6aec0f8c MaxScale[263]: Error : Can't route MYSQL_COM_STMT_PREPARE:QUERY_TYPE_READ|QUERY_TYPE_PREPARE_STMT:"MYSQL_COM_STMT_PREPARE". SELECT with session data modification is not supported if configuration parameter use_sql_variables_in=all .
+
+*/
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+ char str[1024];
+
+ sprintf(str, "php %s/bug729.php %s %d %s %s", test_dir, Test->maxscale_IP, Test->rwsplit_port,
+ Test->maxscale_user, Test->maxscale_password);
+
+ Test->tprintf("Executing PHP script: %s\n", str);
+ Test->add_result(system(str), "PHP script FAILED!\n");
+
+ Test->check_log_err((char *) "Can't route MYSQL_COM_STMT_PREPARE", false);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/bug729.php b/maxscale-system-test/bug729.php
new file mode 100644
index 000000000..e3da14f10
--- /dev/null
+++ b/maxscale-system-test/bug729.php
@@ -0,0 +1,19 @@
+ PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_EMULATE_PREPARES => false,
+];
+
+$host=$argv[1];
+$port=$argv[2];
+$user=$argv[3];
+$pass=$argv[4];
+
+$dsn = "mysql:host=".$host.";port=".$port.";dbname=information_schema";
+$dbh = new PDO( $dsn, $user, $pass, $options );
+$res = $dbh
+ ->query( "SELECT COLLATION_NAME FROM COLLATIONS" )
+ ->fetch( PDO::FETCH_COLUMN );
+
+var_dump( $res );
+
diff --git a/maxscale-system-test/bug730.cpp b/maxscale-system-test/bug730.cpp
new file mode 100644
index 000000000..152e84edb
--- /dev/null
+++ b/maxscale-system-test/bug730.cpp
@@ -0,0 +1,68 @@
+/**
+ * @file bug730.cpp regression case for bug 730 ("Regex filter and shorter than original replacement queries MaxScale")
+ *
+ * - setup regex filter, add it to all routers
+ * @verbatim
+[MySetOptionFilter]
+type=filter
+module=regexfilter
+options=ignorecase
+match=SET OPTION SQL_QUOTE_SHOW_CREATE
+replace=SET SQL_QUOTE_SHOW_CREATE
+
+ @endverbatim
+ * - try SET OPTION SQL_QUOTE_SHOW_CREATE = 1; against all routers
+ * - check if Maxscale alive
+ */
+
+/*
+Markus Mäkelä 2015-02-16 10:25:50 UTC
+Using the following regex filter:
+
+[MySetOptionFilter]
+type=filter
+module=regexfilter
+options=ignorecase
+match=SET OPTION SQL_QUOTE_SHOW_CREATE
+replace=SET SQL_QUOTE_SHOW_CREATE
+
+Sending the following query hangs MaxScale:
+
+SET OPTION SQL_QUOTE_SHOW_CREATE = 1;
+
+This happens because modutil_replace_SQL doesn't modify the SQL packet length if the resulting replacement is shorter.
+Comment 1 Markus Mäkelä 2015-02-16 10:27:20 UTC
+Added SQL packet length modifications to modutil_replace_SQL when the original length is different from the replacement length.
+*/
+
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(10);
+
+ Test->connect_maxscale();
+
+ Test->tprintf("RWSplit: \n");
+ fflush(stdout);
+ Test->try_query(Test->conn_rwsplit, (char *) "SET OPTION SQL_QUOTE_SHOW_CREATE = 1;");
+ Test->tprintf("ReadConn master: \n");
+ fflush(stdout);
+ Test->try_query(Test->conn_master, (char *) "SET OPTION SQL_QUOTE_SHOW_CREATE = 1;");
+ Test->tprintf("readConn slave: \n");
+ fflush(stdout);
+ Test->try_query(Test->conn_slave, (char *) "SET OPTION SQL_QUOTE_SHOW_CREATE = 1;");
+
+ Test->close_maxscale_connections();
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/bulk_insert.cpp b/maxscale-system-test/bulk_insert.cpp
new file mode 100644
index 000000000..9e3595e54
--- /dev/null
+++ b/maxscale-system-test/bulk_insert.cpp
@@ -0,0 +1,219 @@
+/**
+ * MXS-1121: MariaDB 10.2 Bulk Insert test
+ *
+ * This test is a copy of one of the examples for bulk inserts:
+ * https://mariadb.com/kb/en/mariadb/bulk-insert-column-wise-binding/
+ */
+
+#include "testconnections.h"
+
+static int show_mysql_error(MYSQL *mysql)
+{
+ printf("Error(%d) [%s] \"%s\"\n", mysql_errno(mysql),
+ mysql_sqlstate(mysql),
+ mysql_error(mysql));
+ return 1;
+}
+
+static int show_stmt_error(MYSQL_STMT *stmt)
+{
+ printf("Error(%d) [%s] \"%s\"\n", mysql_stmt_errno(stmt),
+ mysql_stmt_sqlstate(stmt),
+ mysql_stmt_error(stmt));
+ return 1;
+}
+
+int bind_by_column(MYSQL *mysql)
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[3];
+
+ /* Data for insert */
+ const char *surnames[] = {"Widenius", "Axmark", "N.N."};
+ unsigned long surnames_length[] = {8, 6, 4};
+ const char *forenames[] = {"Monty", "David", "will be replaced by default value"};
+ char forename_ind[] = {STMT_INDICATOR_NTS, STMT_INDICATOR_NTS, STMT_INDICATOR_DEFAULT};
+ char id_ind[] = {STMT_INDICATOR_NULL, STMT_INDICATOR_NULL, STMT_INDICATOR_NULL};
+ unsigned int array_size = 3;
+
+ if (mysql_query(mysql, "DROP TABLE IF EXISTS test.bulk_example1"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+ if (mysql_query(mysql, "CREATE TABLE test.bulk_example1 (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," \
+ "forename CHAR(30) NOT NULL DEFAULT 'unknown', surname CHAR(30))"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+ stmt = mysql_stmt_init(mysql);
+ if (mysql_stmt_prepare(stmt, "INSERT INTO test.bulk_example1 VALUES (?,?,?)", -1))
+ {
+ return show_stmt_error(stmt);
+ }
+
+ memset(bind, 0, sizeof(MYSQL_BIND) * 3);
+
+ /* We autogenerate id's, so all indicators are STMT_INDICATOR_NULL */
+ bind[0].u.indicator = id_ind;
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+
+ bind[1].buffer = forenames;
+ bind[1].buffer_type = MYSQL_TYPE_STRING;
+ bind[1].u.indicator = forename_ind;
+
+ bind[2].buffer_type = MYSQL_TYPE_STRING;
+ bind[2].buffer = surnames;
+ bind[2].length = surnames_length;
+
+ /* set array size */
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size);
+
+ /* bind parameter */
+ mysql_stmt_bind_param(stmt, bind);
+
+ /* execute */
+ if (mysql_stmt_execute(stmt))
+ {
+ return show_stmt_error(stmt);
+ }
+
+ mysql_stmt_close(stmt);
+
+ /* Check that the rows were inserted */
+ if (mysql_query(mysql, "SELECT * FROM test.bulk_example1"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+ MYSQL_RES *res = mysql_store_result(mysql);
+
+ if (res == NULL || mysql_num_rows(res) != 3)
+ {
+ printf("Expected 3 rows but got %d (%s)\n", res ? (int)mysql_num_rows(res) : 0, mysql_error(mysql));
+ return 1;
+ }
+
+ if (mysql_query(mysql, "DROP TABLE test.bulk_example1"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+ return 0;
+}
+
+int bind_by_row(MYSQL *mysql)
+{
+ MYSQL_STMT *stmt;
+ MYSQL_BIND bind[3];
+
+ struct st_data
+ {
+ unsigned long id;
+ char id_ind;
+ char forename[30];
+ char forename_ind;
+ char surname[30];
+ char surname_ind;
+ };
+
+ struct st_data data[] =
+ {
+ {0, STMT_INDICATOR_NULL, "Monty", STMT_INDICATOR_NTS, "Widenius", STMT_INDICATOR_NTS},
+ {0, STMT_INDICATOR_NULL, "David", STMT_INDICATOR_NTS, "Axmark", STMT_INDICATOR_NTS},
+ {0, STMT_INDICATOR_NULL, "default", STMT_INDICATOR_DEFAULT, "N.N.", STMT_INDICATOR_NTS},
+ };
+
+ unsigned int array_size = 3;
+ size_t row_size = sizeof(struct st_data);
+
+ if (mysql_query(mysql, "DROP TABLE IF EXISTS bulk_example2"))
+ {
+ show_mysql_error(mysql);
+ }
+
+ if (mysql_query(mysql, "CREATE TABLE bulk_example2 (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,"\
+ "forename CHAR(30) NOT NULL DEFAULT 'unknown', surname CHAR(30))"))
+ {
+ show_mysql_error(mysql);
+ }
+
+ stmt = mysql_stmt_init(mysql);
+ if (mysql_stmt_prepare(stmt, "INSERT INTO bulk_example2 VALUES (?,?,?)", -1))
+ {
+ show_stmt_error(stmt);
+ }
+
+ memset(bind, 0, sizeof(MYSQL_BIND) * 3);
+
+ /* We autogenerate id's, so all indicators are STMT_INDICATOR_NULL */
+ bind[0].u.indicator = &data[0].id_ind;
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+
+ bind[1].buffer = &data[0].forename;
+ bind[1].buffer_type = MYSQL_TYPE_STRING;
+ bind[1].u.indicator = &data[0].forename_ind;
+
+ bind[2].buffer_type = MYSQL_TYPE_STRING;
+ bind[2].buffer = &data[0].surname;
+ bind[2].u.indicator = &data[0].surname_ind;
+
+ /* set array size */
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ARRAY_SIZE, &array_size);
+
+ /* set row size */
+ mysql_stmt_attr_set(stmt, STMT_ATTR_ROW_SIZE, &row_size);
+
+ /* bind parameter */
+ mysql_stmt_bind_param(stmt, bind);
+
+ /* execute */
+ if (mysql_stmt_execute(stmt))
+ {
+ show_stmt_error(stmt);
+ }
+
+ mysql_stmt_close(stmt);
+
+
+ /* Check that the rows were inserted */
+ if (mysql_query(mysql, "SELECT * FROM test.bulk_example2"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+ MYSQL_RES *res = mysql_store_result(mysql);
+
+ if (res == NULL || mysql_num_rows(res) != 3)
+ {
+ printf("Expected 3 rows but got %d (%s)\n", res ? (int)mysql_num_rows(res) : 0, mysql_error(mysql));
+ return 1;
+ }
+
+ if (mysql_query(mysql, "DROP TABLE test.bulk_example2"))
+ {
+ return show_mysql_error(mysql);
+ }
+
+}
+
+int main(int argc, char** argv)
+{
+ TestConnections::require_repl_version("10.2");
+ TestConnections test(argc, argv);
+ test.connect_maxscale();
+
+ test.tprintf("Testing column-wise binding with readwritesplit");
+ test.add_result(bind_by_column(test.conn_rwsplit), "Bulk inserts with readwritesplit should work");
+ test.tprintf("Testing column-wise binding with readconnroute");
+ test.add_result(bind_by_column(test.conn_master), "Bulk inserts with readconnroute should work");
+
+ test.tprintf("Testing row-wise binding with readwritesplit");
+ test.add_result(bind_by_row(test.conn_rwsplit), "Bulk inserts with readwritesplit should work");
+ test.tprintf("Testing row-wise binding with readconnroute");
+ test.add_result(bind_by_row(test.conn_master), "Bulk inserts with readconnroute should work");
+
+ test.close_maxscale_connections();
+ return test.global_result;
+}
diff --git a/maxscale-system-test/cache/cache_basic/cache_rules.json b/maxscale-system-test/cache/cache_basic/cache_rules.json
new file mode 100644
index 000000000..51a5fed30
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/cache_rules.json
@@ -0,0 +1,9 @@
+{
+ "store": [
+ {
+ "attribute": "table",
+ "op": "=",
+ "value": "caching"
+ }
+ ]
+}
diff --git a/maxscale-system-test/cache/cache_basic/r/create.result b/maxscale-system-test/cache/cache_basic/r/create.result
new file mode 100644
index 000000000..9da6dc673
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/create.result
@@ -0,0 +1,4 @@
+drop database if exists cachingdb;
+create database cachingdb;
+use cachingdb;
+create table caching (a INT, b TEXT, c FLOAT);
diff --git a/maxscale-system-test/cache/cache_basic/r/delete.result b/maxscale-system-test/cache/cache_basic/r/delete.result
new file mode 100644
index 000000000..1b2dae330
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/delete.result
@@ -0,0 +1,2 @@
+USE cachingdb;
+DELETE FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/r/drop.result b/maxscale-system-test/cache/cache_basic/r/drop.result
new file mode 100644
index 000000000..bf61cc3c5
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/drop.result
@@ -0,0 +1 @@
+drop database if exists cachingdb;
diff --git a/maxscale-system-test/cache/cache_basic/r/insert1.result b/maxscale-system-test/cache/cache_basic/r/insert1.result
new file mode 100644
index 000000000..76426ae91
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/insert1.result
@@ -0,0 +1,2 @@
+USE cachingdb;
+INSERT INTO caching VALUES (42, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.', 3.14);
diff --git a/maxscale-system-test/cache/cache_basic/r/select0.result b/maxscale-system-test/cache/cache_basic/r/select0.result
new file mode 100644
index 000000000..f5d68b631
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/select0.result
@@ -0,0 +1,3 @@
+USE cachingdb;
+SELECT * FROM caching;
+a b c
diff --git a/maxscale-system-test/cache/cache_basic/r/select1.result b/maxscale-system-test/cache/cache_basic/r/select1.result
new file mode 100644
index 000000000..3806f9135
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/select1.result
@@ -0,0 +1,4 @@
+USE cachingdb;
+SELECT * FROM caching;
+a b c
+42 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis. 3.14
diff --git a/maxscale-system-test/cache/cache_basic/r/select2.result b/maxscale-system-test/cache/cache_basic/r/select2.result
new file mode 100644
index 000000000..e906f4e4c
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/select2.result
@@ -0,0 +1,6 @@
+USE cachingdb;
+SELECT * FROM caching;
+a b c
+84 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula. 6.28
diff --git a/maxscale-system-test/cache/cache_basic/r/select3.result b/maxscale-system-test/cache/cache_basic/r/select3.result
new file mode 100644
index 000000000..08a41efba
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/select3.result
@@ -0,0 +1,8 @@
+USE cachingdb;
+SELECT * FROM caching;
+a b c
+126 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula. 9.42
diff --git a/maxscale-system-test/cache/cache_basic/r/update1.result b/maxscale-system-test/cache/cache_basic/r/update1.result
new file mode 100644
index 000000000..f66186eb1
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/update1.result
@@ -0,0 +1,4 @@
+USE cachingdb;
+UPDATE caching SET a=84, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=6.28;
diff --git a/maxscale-system-test/cache/cache_basic/r/update2.result b/maxscale-system-test/cache/cache_basic/r/update2.result
new file mode 100644
index 000000000..f66186eb1
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/update2.result
@@ -0,0 +1,4 @@
+USE cachingdb;
+UPDATE caching SET a=84, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=6.28;
diff --git a/maxscale-system-test/cache/cache_basic/r/update3.result b/maxscale-system-test/cache/cache_basic/r/update3.result
new file mode 100644
index 000000000..e0dc15564
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/r/update3.result
@@ -0,0 +1,6 @@
+USE cachingdb;
+UPDATE caching SET a=126, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=9.42;
diff --git a/maxscale-system-test/cache/cache_basic/t/create.test b/maxscale-system-test/cache/cache_basic/t/create.test
new file mode 100644
index 000000000..babd49f67
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/create.test
@@ -0,0 +1,14 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+
+--disable_warnings
+drop database if exists cachingdb;
+--enable_warnings
+
+create database cachingdb;
+use cachingdb;
+
+create table caching (a INT, b TEXT, c FLOAT);
+
diff --git a/maxscale-system-test/cache/cache_basic/t/delete.test b/maxscale-system-test/cache/cache_basic/t/delete.test
new file mode 100644
index 000000000..2c8af71ca
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/delete.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/create.test has been successfully executed.
+#
+
+USE cachingdb;
+
+DELETE FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/t/drop.test b/maxscale-system-test/cache/cache_basic/t/drop.test
new file mode 100644
index 000000000..ead172343
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/drop.test
@@ -0,0 +1,6 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+
+drop database if exists cachingdb;
diff --git a/maxscale-system-test/cache/cache_basic/t/insert1.test b/maxscale-system-test/cache/cache_basic/t/insert1.test
new file mode 100644
index 000000000..9bf0be92a
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/insert1.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/create.test has been successfully executed.
+#
+
+USE cachingdb;
+
+INSERT INTO caching VALUES (42, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.', 3.14);
diff --git a/maxscale-system-test/cache/cache_basic/t/select0.test b/maxscale-system-test/cache/cache_basic/t/select0.test
new file mode 100644
index 000000000..dbde83e58
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/select0.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/create.test has been successfully executed.
+#
+
+USE cachingdb;
+
+SELECT * FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/t/select1.test b/maxscale-system-test/cache/cache_basic/t/select1.test
new file mode 100644
index 000000000..d8ad3aab5
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/select1.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/insert1.test has been successfully executed.
+#
+
+USE cachingdb;
+
+SELECT * FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/t/select2.test b/maxscale-system-test/cache/cache_basic/t/select2.test
new file mode 100644
index 000000000..074390786
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/select2.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/update2.test has been successfully executed.
+#
+
+USE cachingdb;
+
+SELECT * FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/t/select3.test b/maxscale-system-test/cache/cache_basic/t/select3.test
new file mode 100644
index 000000000..8f7df3a24
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/select3.test
@@ -0,0 +1,11 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/update3.test has been successfully executed.
+#
+
+USE cachingdb;
+
+SELECT * FROM caching;
diff --git a/maxscale-system-test/cache/cache_basic/t/update1.test b/maxscale-system-test/cache/cache_basic/t/update1.test
new file mode 100644
index 000000000..a9f70de61
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/update1.test
@@ -0,0 +1,13 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/insert.test has been successfully executed.
+#
+
+USE cachingdb;
+
+UPDATE caching SET a=84, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=6.28;
diff --git a/maxscale-system-test/cache/cache_basic/t/update2.test b/maxscale-system-test/cache/cache_basic/t/update2.test
new file mode 100644
index 000000000..a9f70de61
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/update2.test
@@ -0,0 +1,13 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/insert.test has been successfully executed.
+#
+
+USE cachingdb;
+
+UPDATE caching SET a=84, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=6.28;
diff --git a/maxscale-system-test/cache/cache_basic/t/update3.test b/maxscale-system-test/cache/cache_basic/t/update3.test
new file mode 100644
index 000000000..ef143bf48
--- /dev/null
+++ b/maxscale-system-test/cache/cache_basic/t/update3.test
@@ -0,0 +1,15 @@
+#
+# Cache basic
+#
+# See ../cache_rules.
+#
+# This script assumes t/insert.test has been successfully executed.
+#
+
+USE cachingdb;
+
+UPDATE caching SET a=126, b='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi eget turpis massa. Duis sit amet commodo ante. Aenean eleifend ipsum sed enim fermentum, eget efficitur risus pulvinar. Maecenas tellus augue, laoreet eget risus porta, porta volutpat tellus. Mauris aliquam vitae velit id faucibus. Aenean euismod, mi nec luctus lacinia, ligula eros commodo velit, ac sagittis ipsum magna scelerisque mi. Aliquam sed sapien sit amet mi convallis pharetra. Sed facilisis, felis ac eleifend fringilla, mauris augue egestas dui, aliquet mollis tellus enim eget erat. Vivamus rhoncus neque nec feugiat mollis.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.
+
+Ut feugiat facilisis urna, ac mollis purus iaculis eget. Fusce egestas est quis mauris euismod, et laoreet nunc commodo. Nullam vehicula tellus in sapien viverra vulputate. Etiam eu libero ultrices mi auctor laoreet. Curabitur lacus nisi, ullamcorper eu quam a, auctor malesuada est. Sed feugiat sagittis augue, non semper ante commodo at. Donec lobortis dapibus nunc sit amet interdum. Quisque egestas elementum enim, nec malesuada nisl tincidunt eget. Suspendisse nulla purus, ullamcorper ut ultricies et, pharetra sed metus. Donec eleifend neque vitae lorem dignissim mattis. Donec gravida dui et ultricies feugiat. Aliquam est lectus, consectetur eu est at, finibus ullamcorper ex. Etiam sit amet erat quis dolor commodo facilisis sed finibus enim. Etiam iaculis ultrices vehicula.', c=9.42;
diff --git a/maxscale-system-test/cache_basic.sh b/maxscale-system-test/cache_basic.sh
new file mode 100755
index 000000000..a65b40f85
--- /dev/null
+++ b/maxscale-system-test/cache_basic.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+
+user=skysql
+password=skysql
+
+# See cnf/maxscale.cnf.template.cache_basic
+port=4008
+# Ensure that these are EXACTLY like the corresponding values
+# in cnf/maxscale.cnf.template.cache_basic
+soft_ttl=5
+hard_ttl=10
+
+function run_test
+{
+ local test_name=$1
+
+ echo $test_name
+
+ mysqltest --host=$maxscale_IP --port=$port \
+ --user=$user --password=$password \
+ --logdir=log \
+ --test-file=t/$test_name.test \
+ --result-file=r/$test_name.result \
+ --silent
+
+ if [ $? -eq 0 ]
+ then
+ echo " OK"
+ rc=0
+ else
+ echo " FAILED"
+ rc=1
+ fi
+
+ return $rc
+}
+
+if [ $# -lt 1 ]
+then
+ echo "usage: $script name"
+ echo ""
+ echo "name : The name of the test (from CMakeLists.txt) That selects the"
+ echo " configuration template to be used."
+ exit 1
+fi
+
+if [ "$maxscale_IP" == "" ]
+then
+ echo "Error: The environment variable maxscale_IP must be set."
+ exit 1
+fi
+
+expected_name="cache_basic"
+
+if [ "$1" != "$expected_name" ]
+then
+ echo "warning: Expected test name to be $expected_name_basic, was $1."
+fi
+
+source=cache/$1/cache_rules.json
+target=vagrant@$maxscale_IP:/home/$maxscale_access_user/cache_rules.json
+
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ scp -i $maxscale_keyfile -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $source $target
+else
+ cp $source /home/$maxscale_access_user/cache_rules.json
+fi
+
+if [ $? -ne 0 ]
+then
+ echo "error: Could not copy rules file to maxscale host."
+ exit 1
+fi
+
+echo $source copied to $target
+
+test_dir=`pwd`
+
+$test_dir/non_native_setup $1
+
+cd cache/$expected_name
+
+[ -d log ] && rm -r log
+mkdir log || exit 1
+
+echo
+
+# We sleep slightly longer than the TTL to ensure that the TTL mechanism
+# kicks in.
+let seconds=$soft_ttl+2
+
+run_test create || exit 1
+run_test insert1 || exit 1
+# We should now get result 1, as this is the first select.
+run_test select1 || exit 1
+
+run_test update2 || exit 1
+# We should now get result 1, as ttl has NOT passed.
+run_test select1 || exit 1
+
+echo "Sleeping $seconds"
+sleep $seconds
+# We should now get result 2, as soft ttl has PASSED.
+run_test select2 || exit 1
+
+run_test update3 || exit 1
+# We should now get result 2, as ttl has NOT passed.
+run_test select2 || exit 1
+
+echo "Sleeping $seconds"
+sleep $seconds
+# We should now get result 3, as soft ttl has PASSED.
+run_test select3 || exit 1
+
+run_test delete || exit 1
+# We should now get result 3, as soft ttl has NOT passed.
+run_test select3 || exit 1
+
+echo "Sleeping $seconds"
+sleep $seconds
+# We should now get result 0, as soft ttl has PASSED.
+run_test select0 || exit 1
+
+# Cleanup
+run_test drop || exit 1
diff --git a/maxscale-system-test/ccrfilter.cpp b/maxscale-system-test/ccrfilter.cpp
new file mode 100644
index 000000000..fd9469780
--- /dev/null
+++ b/maxscale-system-test/ccrfilter.cpp
@@ -0,0 +1,144 @@
+/**
+ * @file ccrfilter.cpp Tests for the CCRFilter module
+ * - configure Maxscale to use Consistent Critical Read Filter
+ * - configure CCR filter with parameter 'time=10'
+ * - Execute INSERT
+ * - check that SELECT goes to Master
+ * - wait 11 seconds
+ * - chat that SELECT goes to slave
+ * - configure CCR filter with parameter 'count=3'
+ * - execute INSERT
+ * - execute 5 SELECTs, check that first 3 go to Master, 2 last - to slave
+ * - configure CCR filter with parameter 'match=t2'
+ * - execute INSERT INTO t1
+ * - check SELECTs go to SLAVE
+ * - execute INSERT INTO t2
+ * - check SELECTs go to Master
+ * - configure CCR filter with parameter 'ignore=t1' and remove parameter 'match=t2'
+ * - execute INSERT INTO t1
+ * - check SELECTs go to SLAVE
+ * - execute INSERT INTO t2
+ * - check SELECTs go to Master
+ */
+
+#include
+#include
+#include "testconnections.h"
+
+static int master_id;
+
+bool is_master(MYSQL *conn)
+{
+ char str[1024];
+
+ if (find_field(conn, "SELECT @@server_id", "@@server_id", str) == 0)
+ {
+ int server_id = atoi(str);
+ return server_id == master_id;
+ }
+
+ return false;
+}
+
+int main(int argc, char *argv[])
+{
+ TestConnections * test = new TestConnections(argc, argv);
+
+ test->repl->connect();
+
+ /**
+ * Get the master's @@server_id
+ */
+ master_id = test->repl->get_server_id(0);
+ test->tprintf("Master server_id: %d", master_id);
+
+
+ execute_query(test->repl->nodes[0], "CREATE OR REPLACE TABLE test.t1 (id INT);");
+ execute_query(test->repl->nodes[0], "CREATE OR REPLACE TABLE test.t2 (id INT);");
+
+ test->connect_maxscale();
+
+ test->tprintf("Test `time`. The first SELECT within 10 seconds should go the "
+ "master and all SELECTs after it should go to the slaves.");
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ sleep(1);
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the first SELECT");
+ sleep(11);
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the second SELECT");
+
+
+ test->tprintf("Change test setup for `count`, the first three selects after an "
+ "insert should go to the master.");
+
+ test->close_maxscale_connections();
+ test->ssh_maxscale(true, "sed -i -e 's/time.*/time=0/' /etc/maxscale.cnf");
+ test->ssh_maxscale(true, "sed -i -e 's/###count/count/' /etc/maxscale.cnf");
+ test->restart_maxscale();
+ test->connect_maxscale();
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the first SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the second SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the third SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fourth SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fifth SELECT");
+
+
+ test->tprintf("Change test setup for `count` and `match`, selects after an insert "
+ "to t1 should go to the slaves and selects after an insert to t2 "
+ "should go to the master.");
+
+ test->close_maxscale_connections();
+ test->ssh_maxscale(true, "sed -i -e 's/###match/match/' /etc/maxscale.cnf");
+ test->restart_maxscale();
+ test->connect_maxscale();
+
+
+ test->tprintf("t1 first, should be ignored");
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the first SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the second SELECT");
+
+ test->tprintf("t2 should match and trigger the critical reads");
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t2 VALUES (1)");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the first SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the second SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the third SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fourth SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fifth SELECT");
+
+
+ test->tprintf("Change test setup for `count` and `ignore`, expects the same "
+ "results as previous test.");
+
+ test->close_maxscale_connections();
+ test->ssh_maxscale(true, "sed -i -e 's/match/###match/' /etc/maxscale.cnf");
+ test->ssh_maxscale(true, "sed -i -e 's/###ignore/ignore/' /etc/maxscale.cnf");
+ test->restart_maxscale();
+ test->connect_maxscale();
+
+ test->tprintf("t1 first, should be ignored");
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the first SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the second SELECT");
+
+ test->tprintf("t2 should match and trigger the critical reads");
+
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t2 VALUES (1)");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the first SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the second SELECT");
+ test->add_result(!is_master(test->conn_rwsplit), "Master should reply to the third SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fourth SELECT");
+ test->add_result(is_master(test->conn_rwsplit), "Master should NOT reply to the fifth SELECT");
+
+ execute_query(test->repl->nodes[0], "DROP TABLE test.t1");
+ execute_query(test->repl->nodes[0], "DROP TABLE test.t2");
+
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/cdc_client.cpp b/maxscale-system-test/cdc_client.cpp
new file mode 100644
index 000000000..2c3b7fe1f
--- /dev/null
+++ b/maxscale-system-test/cdc_client.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file cdc_client.cpp Test of CDC protocol (avro listener)
+ * - configure binlog router setup, avro router, avro listener
+ * - connect to avro listener
+ * - start INSERT load thread
+ * - read data from avro listener, comapre it with inserted data
+ */
+
+#include
+#include
+#include "testconnections.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "maxinfo_func.h"
+#include "sql_t1.h"
+#include
+
+using namespace std;
+char reg_str[] = "REGISTER UUID=XXX-YYY_YYY, TYPE=JSON";
+char req_str[] = "REQUEST-DATA test.t1";
+int insert_val = 0;
+bool exit_flag = false;
+
+void *query_thread(void *ptr);
+
+/**
+ * @brief cdc_com Connects to avro listenet by CDC protocal, read data, compare data with inserted data
+ * @param Test TestConnections object
+ * @return true if test PASSED
+ */
+bool cdc_com(TestConnections *Test)
+{
+ int max_inserted_val = Test->smoke ? 25 : 100;
+ int sock = create_tcp_socket();
+ char *ip = get_ip(Test->maxscale_IP);
+
+ if (ip == NULL)
+ {
+ Test->tprintf("Can't get IP");
+ return false;
+ }
+
+ struct sockaddr_in *remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
+ remote->sin_family = AF_INET;
+ int tmpres = inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr)));
+
+ if ( tmpres < 0)
+ {
+ Test->tprintf("Can't set remote->sin_addr.s_addr");
+ return false;
+ }
+ else if (tmpres == 0)
+ {
+ Test->tprintf("%s is not a valid IP address", ip);
+ return false;
+ }
+
+ remote->sin_port = htons(4001);
+
+ if (connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0)
+ {
+ Test->tprintf("Could not connect");
+ return false;
+ }
+
+ char *get = cdc_auth_srt((char *) "skysql", (char *) "skysql");
+ Test->tprintf("Auth string: %s", get);
+
+ //Send the query to the server
+ if (send_so(sock, get) != 0)
+ {
+ Test->tprintf("Cat't send data to scoket");
+ return false;
+ }
+
+ char buf1[1024];
+ recv(sock, buf1, 1024, 0);
+
+ //Send the query to the server
+ if (send_so(sock, reg_str) != 0)
+ {
+ Test->tprintf("Cat't send data to scoket");
+ return false;
+ }
+
+ recv(sock, buf1, 1024, 0);
+
+ //Send the query to the server
+ if (send_so(sock, req_str) != 0)
+ {
+ Test->tprintf("Cat't send data to scoket");
+ return false;
+ }
+
+ Test->stop_timeout();
+ int epfd = epoll_create(1);
+ static struct epoll_event ev;
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ ev.data.fd = sock;
+
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
+ {
+ Test->tprintf("Error in epoll_ctl! errno = %d, %s", errno, strerror(errno));
+ return false;
+ }
+
+ epoll_event events[2];
+ setnonblocking(sock);
+
+ int inserted_val = 0;
+ int ignore_first = 2;
+
+ while (inserted_val < max_inserted_val)
+ {
+ Test->set_timeout(60);
+ // wait for something to do...
+ Test->tprintf("epoll_wait");
+ int nfds = epoll_wait(epfd, &events[0], 1, -1);
+ if (nfds < 0)
+ {
+ Test->tprintf("Error in epoll_wait! errno = %d, %s", errno, strerror(errno));
+ return false;
+ }
+
+ if (nfds > 0)
+ {
+ // for each ready socket
+ //for(int i = 0; i < nfds; i++)
+ //{
+ int fd = events[0].data.fd;
+ char *json = read_sc(sock);
+ Test->tprintf("%s", json);
+ //}
+ if (ignore_first > 0)
+ {
+ ignore_first--; // ignoring first reads
+ if (ignore_first == 0)
+ {
+ // first reads done, starting inserting
+ insert_val = 10;
+ inserted_val = insert_val;
+ }
+ }
+ else
+ {
+ // trying to check JSON
+ long long int x1;
+ long long int fl;
+ get_x_fl_from_json(json, &x1, &fl);
+ Test->tprintf("data received, x1=%lld fl=%lld", x1, fl);
+
+ if (x1 != inserted_val || fl != inserted_val + 100)
+ {
+ Test->tprintf("wrong values in JSON");
+ }
+
+ inserted_val++;
+ insert_val = inserted_val;
+ }
+
+ free(json);
+ }
+ else
+ {
+ Test->tprintf("waiting");
+ }
+ }
+
+ free(remote);
+ free(ip);
+ close(sock);
+
+ return true;
+}
+
+static TestConnections *Test;
+
+int main(int argc, char *argv[])
+{
+
+ Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(600);
+ Test->stop_maxscale();
+
+ // Remove old data files and make sure that port 4001 is open
+ Test->ssh_maxscale(true, "rm -rf /var/lib/maxscale/avro;"
+ "iptables -n -L INPUT|grep 4001 || iptables -I INPUT -p tcp --dport 4001 -j ACCEPT;");
+
+ Test->repl->connect();
+ execute_query(Test->repl->nodes[0], "DROP TABLE IF EXISTS t1;");
+ Test->repl->close_connections();
+ sleep(5);
+
+ Test->start_binlog();
+
+ Test->set_timeout(120);
+ Test->stop_maxscale();
+
+ Test->ssh_maxscale(true, "rm -rf /var/lib/maxscale/avro");
+
+ Test->set_timeout(120);
+ Test->start_maxscale();
+
+ Test->set_timeout(60);
+ Test->repl->connect();
+ create_t1(Test->repl->nodes[0]);
+ execute_query(Test->repl->nodes[0], (char *) "INSERT INTO t1 VALUES (111, 222)");
+ Test->repl->close_connections();
+
+ Test->tprintf("Waiting for binlogs to be processed...");
+ Test->stop_timeout();
+ sleep(15);
+
+ Test->set_timeout(120);
+
+ pthread_t thread;
+ pthread_create(&thread, NULL, query_thread, NULL);
+
+ Test->add_result(!cdc_com(Test), "Failed to execute test");
+
+ exit_flag = true;
+
+ pthread_join(thread, NULL);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+void *query_thread(void *ptr)
+{
+
+ Test->repl->connect();
+
+ while (!exit_flag)
+ {
+ if (insert_val != 0)
+ {
+ char str[256];
+ sprintf(str, "INSERT INTO t1 VALUES (%d, %d)", insert_val, insert_val + 100);
+ insert_val = 0;
+ execute_query(Test->repl->nodes[0], str);
+ }
+ }
+
+ Test->repl->close_connections();
+}
diff --git a/maxscale-system-test/cdc_connector.cpp b/maxscale-system-test/cdc_connector.cpp
new file mode 100644
index 000000000..0800d698e
--- /dev/null
+++ b/maxscale-system-test/cdc_connector.cpp
@@ -0,0 +1,308 @@
+#include "cdc_connector.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define CDC_CONNECTOR_VERSION "1.0.0"
+
+#define ERRBUF_SIZE 512
+#define READBUF_SIZE 1024
+
+static const char OK_RESPONSE[] = "OK\n";
+
+static const char CLOSE_MSG[] = "CLOSE";
+static const char REGISTER_MSG[] = "REGISTER UUID=CDC_CONNECTOR-" CDC_CONNECTOR_VERSION ", TYPE=";
+static const char REQUEST_MSG[] = "REQUEST-DATA ";
+
+namespace
+{
+
+static inline int nointr_read(int fd, void *dest, size_t size)
+{
+ int rc = read(fd, dest, size);
+
+ while (rc == -1 && errno == EINTR)
+ {
+ rc = read(fd, dest, size);
+ }
+
+ return rc;
+}
+
+static inline int nointr_write(int fd, const void *src, size_t size)
+{
+ int rc = write(fd, src, size);
+
+ while (rc == -1 && errno == EINTR)
+ {
+ rc = write(fd, src, size);
+ }
+
+ return rc;
+}
+
+static std::string bin2hex(const uint8_t *data, size_t len)
+{
+ std::string result;
+ static const char hexconvtab[] = "0123456789abcdef";
+
+ for (int i = 0; i < len; i++)
+ {
+ result += hexconvtab[data[i] >> 4];
+ result += hexconvtab[data[i] & 0x0f];
+ }
+
+ return result;
+}
+
+std::string generateAuthString(const std::string& user, const std::string& password)
+{
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast (password.c_str()), password.length(), digest);
+
+ std::string auth_str = user;
+ auth_str += ":";
+
+ std::string part1 = bin2hex((uint8_t*)auth_str.c_str(), auth_str.length());
+ std::string part2 = bin2hex(digest, sizeof(digest));
+
+ return part1 + part2;
+}
+}
+
+namespace CDC
+{
+
+/**
+ * Public functions
+ */
+
+Connection::Connection(const std::string& address,
+ uint16_t port,
+ const std::string& user,
+ const std::string& password,
+ uint32_t flags) :
+ m_fd(-1),
+ m_address(address),
+ m_port(port),
+ m_user(user),
+ m_password(password),
+ m_flags(flags) { }
+
+Connection::~Connection()
+{
+ closeConnection();
+}
+
+bool Connection::createConnection()
+{
+ bool rval = false;
+ struct sockaddr_in remote = {};
+
+ remote.sin_port = htons(m_port);
+ remote.sin_family = AF_INET;
+
+ if (inet_aton(m_address.c_str(), (struct in_addr*)&remote.sin_addr.s_addr) == 0)
+ {
+ m_error = "Invalid address: ";
+ m_error += m_address;
+ }
+ else
+ {
+ int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to create socket: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+
+ m_fd = fd;
+
+ if (connect(fd, (struct sockaddr*) &remote, sizeof (remote)) == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to connect: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+ else if (doAuth())
+ {
+ rval = doRegistration();
+ }
+ }
+
+ return rval;
+}
+
+void Connection::closeConnection()
+{
+ if (m_fd != -1)
+ {
+ nointr_write(m_fd, CLOSE_MSG, sizeof (CLOSE_MSG) - 1);
+ close(m_fd);
+ m_fd = -1;
+ }
+}
+
+bool Connection::requestData(const std::string& table, const std::string& gtid)
+{
+ bool rval = true;
+
+ std::string req_msg(REQUEST_MSG);
+ req_msg += table;
+
+ if (gtid.length())
+ {
+ req_msg += " ";
+ req_msg += gtid;
+ }
+
+ if (nointr_write(m_fd, req_msg.c_str(), req_msg.length()) == -1)
+ {
+ rval = false;
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to write request: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+
+ if (rval)
+ {
+ /** Read the Avro schema */
+ rval = readRow(m_schema);
+ }
+
+ return rval;
+}
+
+bool Connection::readRow(std::string& dest)
+{
+ bool rval = true;
+
+ while (true)
+ {
+ char buf;
+ int rc = nointr_read(m_fd, &buf, 1);
+
+ if (rc == -1)
+ {
+ rval = false;
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to read row: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ break;
+ }
+
+ if (buf == '\n')
+ {
+ break;
+ }
+ else
+ {
+ dest += buf;
+ }
+ }
+
+ return rval;
+}
+
+/**
+ * Private functions
+ */
+
+bool Connection::doAuth()
+{
+ bool rval = false;
+ std::string auth_str = generateAuthString(m_user, m_password);
+
+ /** Send the auth string */
+ if (nointr_write(m_fd, auth_str.c_str(), auth_str.length()) == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to write authentication data: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+ else
+ {
+ /** Read the response */
+ char buf[READBUF_SIZE];
+ int bytes;
+
+ if ((bytes = nointr_read(m_fd, buf, sizeof (buf))) == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to read authentication response: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+ else if (memcmp(buf, OK_RESPONSE, sizeof (OK_RESPONSE) - 1) != 0)
+ {
+ buf[bytes] = '\0';
+ m_error = "Authentication failed: ";
+ m_error += buf;
+ }
+ else
+ {
+ rval = true;
+ }
+ }
+
+ return rval;
+}
+
+bool Connection::doRegistration()
+{
+ bool rval = false;
+ std::string reg_msg(REGISTER_MSG);
+
+ const char *type = "";
+
+ if (m_flags & CDC_REQUEST_TYPE_JSON)
+ {
+ type = "JSON";
+ }
+ else if (m_flags & CDC_REQUEST_TYPE_AVRO)
+ {
+ type = "AVRO";
+ }
+
+ reg_msg += type;
+
+ /** Send the registration message */
+ if (nointr_write(m_fd, reg_msg.c_str(), reg_msg.length()) == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to write registration message: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+ else
+ {
+ /** Read the response */
+ char buf[READBUF_SIZE];
+ int bytes;
+
+ if ((bytes = nointr_read(m_fd, buf, sizeof (buf))) == -1)
+ {
+ char err[ERRBUF_SIZE];
+ m_error = "Failed to read registration response: ";
+ m_error += strerror_r(errno, err, sizeof (err));
+ }
+ else if (memcmp(buf, OK_RESPONSE, sizeof (OK_RESPONSE) - 1) != 0)
+ {
+ buf[bytes] = '\0';
+ m_error = "Registration failed: ";
+ m_error += buf;
+ }
+ else
+ {
+ rval = true;
+ }
+ }
+
+ return rval;
+}
+
+}
diff --git a/maxscale-system-test/cdc_connector.h b/maxscale-system-test/cdc_connector.h
new file mode 100644
index 000000000..74969bfb9
--- /dev/null
+++ b/maxscale-system-test/cdc_connector.h
@@ -0,0 +1,47 @@
+#include
+#include
+
+/** Request format flags */
+#define CDC_REQUEST_TYPE_JSON (1 << 0)
+#define CDC_REQUEST_TYPE_AVRO (1 << 1)
+
+namespace CDC
+{
+
+class Connection
+{
+public:
+ Connection(const std::string& address,
+ uint16_t port,
+ const std::string& user,
+ const std::string& password,
+ uint32_t flags = CDC_REQUEST_TYPE_JSON);
+ virtual ~Connection();
+ bool createConnection();
+ bool requestData(const std::string& table, const std::string& gtid = "");
+ bool readRow(std::string& dest);
+ void closeConnection();
+ const std::string& getSchema() const
+ {
+ return m_schema;
+ }
+ const std::string& getError() const
+ {
+ return m_error;
+ }
+
+private:
+ int m_fd;
+ uint32_t m_flags;
+ uint16_t m_port;
+ std::string m_address;
+ std::string m_user;
+ std::string m_password;
+ std::string m_error;
+ std::string m_schema;
+
+ bool doAuth();
+ bool doRegistration();
+};
+
+}
diff --git a/maxscale-system-test/cdc_datatypes/CMakeLists.txt b/maxscale-system-test/cdc_datatypes/CMakeLists.txt
new file mode 100644
index 000000000..b10375204
--- /dev/null
+++ b/maxscale-system-test/cdc_datatypes/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_test_executable(cdc_datatypes.cpp cdc_datatypes avro LABELS avrorouter binlogrouter BREAKS_REPL)
+add_library(cdc_result cdc_result.cpp)
+target_link_libraries(cdc_datatypes cdc_result)
diff --git a/maxscale-system-test/cdc_datatypes/cdc_datatypes.cpp b/maxscale-system-test/cdc_datatypes/cdc_datatypes.cpp
new file mode 100644
index 000000000..97b5191a2
--- /dev/null
+++ b/maxscale-system-test/cdc_datatypes/cdc_datatypes.cpp
@@ -0,0 +1,222 @@
+/**
+ * @file cdc_connect.cpp Test the CDC protocol
+ */
+
+#include "../testconnections.h"
+#include "../cdc_connector.h"
+#include "cdc_result.h"
+#include
+#include
+
+static const char* table_name = "test.type";
+static const char* field_name = "a";
+
+static const char* integer_types[] =
+{
+ "TINYINT",
+ "SMALLINT",
+ "MEDIUMINT",
+ "INT",
+ "BIGINT",
+ NULL
+};
+
+static const char* integer_values[] =
+{
+ "0",
+ "1",
+ "-1",
+ "20",
+ "-20",
+ NULL
+};
+
+static const char* decimal_types[] =
+{
+ "FLOAT",
+ "DOUBLE",
+ "DECIMAL(10, 2)",
+ NULL
+};
+
+static const char* decimal_values[] =
+{
+ "0",
+ "1.5",
+ "-1.5",
+ "20.5",
+ "-20.5",
+ NULL
+};
+
+static const char* string_types[] =
+{
+ "CHAR(50)",
+ "VARCHAR(50)",
+ "TINYTEXT",
+ "TEXT",
+ "MEDIUMTEXT",
+ "LONGTEXT",
+ NULL
+};
+
+static const char* string_values[] =
+{
+ "\"Hello world!\"",
+ "\"The quick brown fox jumps over the lazy dog\"",
+// "\"The Unicode should work: äöåǢ\"",
+ NULL
+};
+
+static const char* binary_types[] =
+{
+ "BINARY(50)",
+ "VARBINARY(50)",
+ "TINYBLOB",
+ "BLOB",
+ "MEDIUMBLOB",
+ "LONGBLOB",
+ NULL
+};
+
+static const char* binary_values[] =
+{
+ "\"Hello world!\"",
+ "\"The quick brown fox jumps over the lazy dog\"",
+ "NULL",
+// "\"The Unicode should work: äöåǢ\"",
+// "\"These should work for binary types: ⦿☏☃☢😤😂\"",
+ NULL
+};
+
+struct
+{
+ const char** types;
+ const char** values;
+} test_set[]
+{
+ { integer_types, integer_values },
+ { decimal_types, decimal_values },
+ { string_types, string_values },
+ { binary_types, binary_values },
+ { 0 }
+};
+
+void insert_data(TestConnections& test, const char *table, const char* type, const char** values)
+{
+ test.repl->connect();
+ execute_query(test.repl->nodes[0], "CREATE TABLE %s(%s %s)", table, field_name, type);
+
+ for (int i = 0; values[i]; i++)
+ {
+ execute_query(test.repl->nodes[0], "INSERT INTO %s VALUES (%s)", table, values[i]);
+ }
+
+ execute_query(test.repl->nodes[0], "DROP TABLE %s", table);
+ test.repl->close_connections();
+}
+
+std::string type_to_table_name(const char* type)
+{
+ std::string name = table_name;
+ name += "_";
+ name += type;
+
+ size_t offset = name.find('(');
+
+ if (offset != std::string::npos)
+ {
+ name = name.substr(0, offset);
+ }
+
+ offset = name.find(' ');
+
+ if (offset != std::string::npos)
+ {
+ name = name.substr(0, offset);
+ }
+
+ return name;
+}
+
+bool run_test(TestConnections& test)
+{
+ bool rval = true;
+
+ for (int x = 0; test_set[x].types; x++)
+ {
+ for (int i = 0; test_set[x].types[i]; i++)
+ {
+ std::string name = type_to_table_name(test_set[x].types[i]);
+ insert_data(test, name.c_str(), test_set[x].types[i], test_set[x].values);
+ }
+ }
+
+ test.repl->connect();
+ execute_query(test.repl->nodes[0], "FLUSH LOGS");
+ test.repl->close_connections();
+ sleep(10);
+
+ for (int x = 0; test_set[x].types; x++)
+ {
+ for (int i = 0; test_set[x].types[i]; i++)
+ {
+ test.set_timeout(60);
+ test.tprintf("Testing type: %s", test_set[x].types[i]);
+ std::string name = type_to_table_name(test_set[x].types[i]);
+ CDC::Connection conn(test.maxscale_IP, 4001, "skysql", "skysql");
+
+ if (conn.createConnection() && conn.requestData(name))
+ {
+ for (int j = 0; test_set[x].values[j]; j++)
+ {
+ std::string row;
+
+ if (conn.readRow(row))
+ {
+ TestInput input(test_set[x].values[j], test_set[x].types[i]);
+ TestOutput output(row, field_name);
+
+ if (input != output)
+ {
+ test.tprintf("Result mismatch: %s(%s) => %s",
+ test_set[x].types[i], test_set[x].values[j], output.getValue().c_str());
+ rval = false;
+ }
+ }
+ else
+ {
+ std::string err = conn.getError();
+ test.tprintf("Failed to read data: %s", err.c_str());
+ }
+ }
+ }
+ else
+ {
+ std::string err = conn.getError();
+ test.tprintf("Failed to request data: %s", err.c_str());
+ rval = false;
+ }
+ test.stop_timeout();
+ }
+ }
+ return rval;
+}
+
+int main(int argc, char *argv[])
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections::check_nodes(false);
+ TestConnections test(argc, argv);
+
+ test.start_binlog();
+ test.restart_maxscale();
+
+ if (!run_test(test))
+ {
+ test.add_result(1, "Test failed");
+ }
+
+ test.check_maxscale_processes(1);
+ return test.global_result;
+}
diff --git a/maxscale-system-test/cdc_datatypes/cdc_result.cpp b/maxscale-system-test/cdc_datatypes/cdc_result.cpp
new file mode 100644
index 000000000..159fe5c7f
--- /dev/null
+++ b/maxscale-system-test/cdc_datatypes/cdc_result.cpp
@@ -0,0 +1,80 @@
+#include "cdc_result.h"
+#include
+#include
+#include
+#include
+#include
+
+using std::cout;
+using std::endl;
+
+TestInput::TestInput(const std::string& value, const std::string& type, const std::string& name) :
+ m_value(value), m_type(type), m_name(name)
+{
+ if (m_value[0] == '"' || m_value[0] == '\'')
+ {
+ /** Remove quotes from the value */
+ m_value = m_value.substr(1, m_value.length() - 2);
+ }
+}
+
+TestOutput::TestOutput(const std::string& input, const std::string& name)
+{
+ json_error_t err;
+ json_t *js = json_loads(input.c_str(), 0, &err);
+
+ if (js)
+ {
+ json_t *value = json_object_get(js, name.c_str());
+
+ if (value)
+ {
+ std::stringstream ss;
+
+ if (json_is_string(value))
+ {
+ if (strlen(json_string_value(value)) == 0)
+ {
+ ss << "NULL";
+ }
+ else
+ {
+ ss << json_string_value(value);
+ }
+ }
+ else if (json_is_integer(value))
+ {
+ ss << json_integer_value(value);
+ }
+ else if (json_is_null(value))
+ {
+ ss << "NULL";
+ }
+ else if (json_is_real(value))
+ {
+ ss << json_real_value(value);
+ }
+ else
+ {
+ cout << "Value '" << name << "' is not a primitive type: " << input << endl;
+ }
+
+ m_value = ss.str();
+ }
+ else
+ {
+ cout << "Value '" << name << "' not found" << endl;
+ }
+
+ json_decref(js);
+ }
+ else
+ {
+ cout << "Failed to parse JSON: " << err.text << endl;
+ }
+}
+
+const std::string& TestOutput::getValue() const
+{
+ return m_value;
+}
diff --git a/maxscale-system-test/cdc_datatypes/cdc_result.h b/maxscale-system-test/cdc_datatypes/cdc_result.h
new file mode 100644
index 000000000..3ef96982a
--- /dev/null
+++ b/maxscale-system-test/cdc_datatypes/cdc_result.h
@@ -0,0 +1,60 @@
+#ifndef CDC_RESULT_H
+#define CDC_RESULT_H
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+class TestOutput
+{
+public:
+ TestOutput(const std::string& input, const std::string& name);
+ const std::string& getValue() const;
+
+private:
+ std::string m_value;
+};
+
+class TestInput
+{
+public:
+ TestInput(const std::string& value, const std::string& type, const std::string& name = "a");
+ const std::string& getName() const
+ {
+ return m_name;
+ }
+ const std::string& getValue() const
+ {
+ return m_value;
+ }
+ const std::string& getType() const
+ {
+ return m_type;
+ }
+
+ bool operator ==(const TestOutput& output) const
+ {
+ return m_value == output.getValue();
+ }
+
+ bool operator !=(const TestOutput& output) const
+ {
+ return !(*this == output);
+ }
+
+private:
+ std::string m_value;
+ std::string m_type;
+ std::string m_name;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CDC_RESULT_H */
+
diff --git a/maxscale-system-test/change_master.cpp b/maxscale-system-test/change_master.cpp
new file mode 100644
index 000000000..f1b02b057
--- /dev/null
+++ b/maxscale-system-test/change_master.cpp
@@ -0,0 +1,32 @@
+
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ int OldMaster;
+ int NewMaster;
+
+ if (argc != 3)
+ {
+ printf("Usage: change_master NewMasterNode OldMasterNode\n");
+ exit(1);
+ }
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ sscanf(argv[1], "%d", &NewMaster);
+ sscanf(argv[2], "%d", &OldMaster);
+
+ Test->tprintf("Changing master from node %d (%s) to node %d (%s)\n", OldMaster, Test->repl->IP[OldMaster],
+ NewMaster, Test->repl->IP[NewMaster]);
+
+ Test->repl->connect();
+ Test->repl->change_master(NewMaster, OldMaster);
+ Test->repl->close_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/change_master_during_session.cpp b/maxscale-system-test/change_master_during_session.cpp
new file mode 100644
index 000000000..382cdf647
--- /dev/null
+++ b/maxscale-system-test/change_master_during_session.cpp
@@ -0,0 +1,66 @@
+/**
+ * @file change_master_during_seesion.cpp Tries to reconfigure replication setup to use another node as a Master
+ * - connect to RWSplit
+ * - reconfugure backend
+ * - checks that after time > monitor_interval everything is ok
+ */
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+using namespace std;
+
+
+int main(int argc, char *argv[])
+{
+ char sql[1024];
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+
+ Test->repl->connect();
+
+ printf("Connecting to RWsplit\n");
+ Test->connect_rwsplit();
+ Test->set_timeout(30);
+ Test->add_result(create_t1(Test->conn_rwsplit), "Error creating 't1'\n");
+
+ Test->try_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 (x1, fl) VALUES(0, 1);");
+ Test->tprintf("Changing master to node 1\n");
+ Test->set_timeout(60);
+ Test->repl->change_master(1, 0);
+ Test->tprintf("executing 3 INSERTs\n");
+ for (int i = 0; i++; i < 3)
+ {
+ Test->set_timeout(60);
+ sprintf(sql, "INSERT INTO t1 (x1, fl) VALUES(%d, 2);", i);
+ Test->tprintf("Trying: %d\n", i);
+ execute_query(Test->conn_rwsplit, sql);
+ }
+ Test->set_timeout(60);
+ Test->tprintf("executing SELECT\n");
+ execute_query(Test->conn_rwsplit, (char *) "SELECT * FROM t1;");
+
+ Test->close_rwsplit();
+
+ /** Sleep for at least one monitor interval */
+ Test->tprintf("Waiting for monitor to detect changes\n");
+ Test->stop_timeout();
+ sleep(3);
+
+ Test->set_timeout(60);
+ Test->connect_rwsplit();
+ Test->tprintf("Reconnecting and executing SELECT again\n");
+ Test->set_timeout(60);
+ Test->try_query(Test->conn_rwsplit, (char *) "SELECT * FROM t1;");
+
+ Test->tprintf("Changing master back to node 0\n");
+ Test->set_timeout(60);
+ Test->repl->change_master(0, 1);
+ Test->set_timeout(60);
+ Test->repl->close_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/change_user.cpp b/maxscale-system-test/change_user.cpp
new file mode 100644
index 000000000..b4c6128a0
--- /dev/null
+++ b/maxscale-system-test/change_user.cpp
@@ -0,0 +1,90 @@
+/**
+ * @file change_user.cpp mysql_change_user test
+ *
+ * - using RWSplit and user 'skysql': GRANT SELECT ON test.* TO user@'%' identified by 'pass2'; FLUSH PRIVILEGES;
+ * - create a new connection to RSplit as 'user'
+ * - try INSERT expecting 'access denied'
+ * - call mysql_change_user() to change user to 'skysql'
+ * - try INSERT again expecting success
+ * - try to execute mysql_change_user() to switch to user 'user' but use rong password (expecting access denied)
+ * - try INSERT again expecting success (user should not be changed)
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(60);
+
+ Test->repl->connect();
+ Test->connect_maxscale();
+
+ Test->tprintf("Creating user 'user' \n");
+
+ execute_query(Test->conn_rwsplit, "DROP USER 'user'@'%%'");
+ Test->try_query(Test->conn_rwsplit, (char *) "CREATE USER user@'%%' identified by 'pass2'");
+ Test->try_query(Test->conn_rwsplit, (char *) "GRANT SELECT ON test.* TO user@'%%'");
+ Test->try_query(Test->conn_rwsplit, (char *) "FLUSH PRIVILEGES;");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE IF EXISTS t1");
+ Test->try_query(Test->conn_rwsplit, (char *) "CREATE TABLE t1 (x1 int, fl int)");
+
+ Test->tprintf("Changing user... \n");
+ Test->add_result(mysql_change_user(Test->conn_rwsplit, (char *) "user", (char *) "pass2", (char *) "test") ,
+ "changing user failed \n");
+ Test->tprintf("mysql_error is %s\n", mysql_error(Test->conn_rwsplit));
+
+ Test->tprintf("Trying INSERT (expecting access denied)... \n");
+ if ( execute_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 VALUES (77, 11);") == 0)
+ {
+ Test->add_result(1, "INSERT query succedded to user which does not have INSERT PRIVILEGES\n");
+ }
+
+ Test->tprintf("Changing user back... \n");
+ Test->add_result(mysql_change_user(Test->conn_rwsplit, Test->repl->user_name, Test->repl->password,
+ (char *) "test"), "changing user failed \n");
+
+ Test->tprintf("Trying INSERT (expecting success)... \n");
+ Test->try_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 VALUES (77, 12);");
+
+ Test->tprintf("Changing user with wrong password... \n");
+ if (mysql_change_user(Test->conn_rwsplit, (char *) "user", (char *) "wrong_pass2", (char *) "test") == 0)
+ {
+ Test->add_result(1, "changing user with wrong password successed! \n");
+ }
+ Test->tprintf("%s\n", mysql_error(Test->conn_rwsplit));
+ if ((strstr(mysql_error(Test->conn_rwsplit), "Access denied for user")) == NULL)
+ {
+ Test->add_result(1, "There is no proper error message\n");
+ }
+
+ Test->tprintf("Trying INSERT again (expecting success - use change should fail)... \n");
+ Test->try_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 VALUES (77, 13);");
+
+
+ Test->tprintf("Changing user with wrong password using ReadConn \n");
+ if (mysql_change_user(Test->conn_slave, (char *) "user", (char *) "wrong_pass2", (char *) "test") == 0)
+ {
+ Test->add_result(1, "FAILED: changing user with wrong password successed! \n");
+ }
+ Test->tprintf("%s\n", mysql_error(Test->conn_slave));
+ if ((strstr(mysql_error(Test->conn_slave), "Access denied for user")) == NULL)
+ {
+ Test->add_result(1, "There is no proper error message\n");
+ }
+
+ Test->tprintf("Changing user for ReadConn \n");
+ Test->add_result(mysql_change_user(Test->conn_slave, (char *) "user", (char *) "pass2", (char *) "test") ,
+ "changing user failed \n");
+
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP USER user@'%%';");
+ execute_query_silent(Test->conn_rwsplit, "DROP TABLE test.t1");
+
+ Test->close_maxscale_connections();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/check_backend.cpp b/maxscale-system-test/check_backend.cpp
new file mode 100644
index 000000000..2c4ae6852
--- /dev/null
+++ b/maxscale-system-test/check_backend.cpp
@@ -0,0 +1,50 @@
+/**
+ * @file check_backend.cpp simply checks if backend is alive
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ /*Test->restart_maxscale();
+ sleep(5);*/
+ Test->set_timeout(10);
+
+ Test->tprintf("Connecting to Maxscale routers with Master/Slave backend\n");
+ Test->connect_maxscale();
+ Test->tprintf("Testing connections\n");
+ Test->add_result(Test->test_maxscale_connections(true, true, true), "Can't connect to backend\n");
+ Test->tprintf("Connecting to Maxscale router with Galera backend\n");
+ MYSQL * g_conn = open_conn(4016 , Test->maxscale_IP, Test->maxscale_user, Test->maxscale_password, Test->ssl);
+ if (g_conn != NULL )
+ {
+ Test->tprintf("Testing connection\n");
+ Test->add_result(Test->try_query(g_conn, (char *) "SELECT 1"),
+ (char *) "Error executing query against RWSplit Galera\n");
+ }
+ Test->tprintf("Closing connections\n");
+ Test->close_maxscale_connections();
+ Test->check_maxscale_alive();
+
+ char * ver = Test->ssh_maxscale_output(false, "maxscale --version-full");
+ Test->tprintf("Maxscale_full_version_start:\n%s\nMaxscale_full_version_end\n", ver);
+
+ if ((Test->global_result == 0) && (Test->use_snapshots))
+ {
+ Test->tprintf("Taking snapshot\n");
+ Test->take_snapshot((char *) "clean");
+ }
+ else
+ {
+ Test->tprintf("Snapshots are not in use\n");
+ }
+
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.add_server b/maxscale-system-test/cnf/maxscale.cnf.template.add_server
new file mode 100644
index 000000000..a21990d06
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.add_server
@@ -0,0 +1,65 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[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
+
+[server5]
+type=server
+address=127.0.0.1
+port=3005
+protocol=MySQLBackend
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.add_service b/maxscale-system-test/cnf/maxscale.cnf.template.add_service
new file mode 100644
index 000000000..a629dc819
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.add_service
@@ -0,0 +1,73 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+
+[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.auroramon b/maxscale-system-test/cnf/maxscale.cnf.template.auroramon
new file mode 100644
index 000000000..e31f369f1
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.auroramon
@@ -0,0 +1,66 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+# Monitors
+
+[Aurora Monitor]
+type=monitor
+module=auroramon
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysqlrds
+monitor_interval=1000
+
+# Services
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysqlrds
+
+[CLI]
+type=service
+router=cli
+
+# Listeners
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+# Servers
+
+[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.avro b/maxscale-system-test/cnf/maxscale.cnf.template.avro
new file mode 100755
index 000000000..a8593069f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.avro
@@ -0,0 +1,59 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+log_notice=1
+log_info=1
+#log_debug=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+[avro-converter]
+type=service
+router=avrorouter
+router_options=binlogdir=/var/lib/maxscale/Binlog_Service,
+ filestem=mar-bin,start_index=1,
+ group_trx=1,group_rows=1,
+ avrodir=/var/lib/maxscale/avro/
+user=skysql
+passwd=skysql
+
+[avro-listener]
+type=listener
+service=avro-converter
+protocol=CDC
+port=4001
+authenticator=CDCPlainAuth
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.avro_compression b/maxscale-system-test/cnf/maxscale.cnf.template.avro_compression
new file mode 100644
index 000000000..1ea65a5ac
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.avro_compression
@@ -0,0 +1,46 @@
+[maxscale]
+threads=###threads###
+log_notice=1
+log_info=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1
+
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[avro-converter]
+type=service
+router=avrorouter
+codec=deflate
+router_options=binlogdir=/var/lib/maxscale/Binlog_Service,
+ filestem=mar-bin,start_index=1,
+ group_trx=1,group_rows=1,
+ avrodir=/var/lib/maxscale/avro/
+user=skysql
+passwd=skysql
+
+[avro-listener]
+type=listener
+service=avro-converter
+protocol=CDC
+port=4001
+authenticator=CDCPlainAuth
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bad_pers b/maxscale-system-test/cnf/maxscale.cnf.template.bad_pers
new file mode 100755
index 000000000..e67181178
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bad_pers
@@ -0,0 +1,100 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=-1
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=-1
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=-1
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=-1
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bad_ssl b/maxscale-system-test/cnf/maxscale.cnf.template.bad_ssl
new file mode 100755
index 000000000..8f1d24c19
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bad_ssl
@@ -0,0 +1,107 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.batchinsert b/maxscale-system-test/cnf/maxscale.cnf.template.batchinsert
new file mode 100644
index 000000000..e37d64c8c
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.batchinsert
@@ -0,0 +1,88 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.binlog_enc_aes_cbc b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_enc_aes_cbc
new file mode 100755
index 000000000..e4259fad6
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_enc_aes_cbc
@@ -0,0 +1,38 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1,encrypt_binlog=1,encryption_key_file=/etc/mariadb_binlog_keys.txt,encryption_algorithm=aes_cbc
+
+
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.binlog_enc_aes_ctr b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_enc_aes_ctr
new file mode 100755
index 000000000..265a5e751
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_enc_aes_ctr
@@ -0,0 +1,38 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1,encrypt_binlog=1,encryption_key_file=/etc/mariadb_binlog_keys.txt,encryption_algorithm=aes_ctr
+
+
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.binlog_incompl b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_incompl
new file mode 100755
index 000000000..40f9d0dbd
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.binlog_incompl
@@ -0,0 +1,38 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=repl
+passwd=repl
+version_string=5.6.15-log
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bug359 b/maxscale-system-test/cnf/maxscale.cnf.template.bug359
new file mode 100755
index 000000000..203272f92
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug359
@@ -0,0 +1,79 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+router_options=slave
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[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.bug471 b/maxscale-system-test/cnf/maxscale.cnf.template.bug471
new file mode 100755
index 000000000..0a146643d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug471
@@ -0,0 +1,104 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints|regex
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug479 b/maxscale-system-test/cnf/maxscale.cnf.template.bug479
new file mode 100755
index 000000000..51c3862df
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug479
@@ -0,0 +1,79 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+filters=non existing filter | не существуюший фильтер
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[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.bug493 b/maxscale-system-test/cnf/maxscale.cnf.template.bug493
new file mode 100755
index 000000000..8df071bfa
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug493
@@ -0,0 +1,78 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[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
+
+[server2]
+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.bug495 b/maxscale-system-test/cnf/maxscale.cnf.template.bug495
new file mode 100755
index 000000000..10c4ff938
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug495
@@ -0,0 +1,78 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4,server5
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[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.bug526 b/maxscale-system-test/cnf/maxscale.cnf.template.bug526
new file mode 100755
index 000000000..393d62adb
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug526
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+filters=testfilter
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[testfilter]
+type=filter
+module=foobar
+
+
+[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.bug539 b/maxscale-system-test/cnf/maxscale.cnf.template.bug539
new file mode 100755
index 000000000..f79d1b450
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug539
@@ -0,0 +1,99 @@
+[maxscale]
+threads=8
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+detect_replication_lag=1
+servers=server1,server2,server3,server4
+user=maxuser
+passwd=maxpwd
+
+[hints]
+type=filter
+module=hintfilter
+
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=master
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+user=maxskysql
+passwd=skysql
+filters=hints
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bug547 b/maxscale-system-test/cnf/maxscale.cnf.template.bug547
new file mode 100755
index 000000000..f08237e97
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug547
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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=1.1.1.1
+port=3306
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=1.1.1.2
+port=3306
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=1.1.1.3
+port=3306
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bug567 b/maxscale-system-test/cnf/maxscale.cnf.template.bug567
new file mode 100755
index 000000000..2f5f687d6
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug567
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+log_to_shm=1
+log_debug=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bug571 b/maxscale-system-test/cnf/maxscale.cnf.template.bug571
new file mode 100755
index 000000000..98d03c96f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug571
@@ -0,0 +1,110 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[regex]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][rR][mM]
+replace=FROM
+
+[r2]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[hints]
+type=filter
+module=hintfilter
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints|regex|r2
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug585 b/maxscale-system-test/cnf/maxscale.cnf.template.bug585
new file mode 100755
index 000000000..169ecdab5
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug585
@@ -0,0 +1,109 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=regex|typo|hints
+
+[hints]
+type=filter
+module=hintfilter
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug587 b/maxscale-system-test/cnf/maxscale.cnf.template.bug587
new file mode 100755
index 000000000..b1ddcaaad
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug587
@@ -0,0 +1,103 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints|regex
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug587_1 b/maxscale-system-test/cnf/maxscale.cnf.template.bug587_1
new file mode 100755
index 000000000..47a7942b8
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug587_1
@@ -0,0 +1,103 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=regex|hints
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug601 b/maxscale-system-test/cnf/maxscale.cnf.template.bug601
new file mode 100755
index 000000000..39327ceb0
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug601
@@ -0,0 +1,89 @@
+[maxscale]
+threads=1
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug620 b/maxscale-system-test/cnf/maxscale.cnf.template.bug620
new file mode 100755
index 000000000..806f6d3e7
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug620
@@ -0,0 +1,90 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+enable_root_user=true
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug643 b/maxscale-system-test/cnf/maxscale.cnf.template.bug643
new file mode 100755
index 000000000..5b0a7fdf4
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug643
@@ -0,0 +1,98 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+user=maxskysql
+passwd=skysql
+filters=duplicate
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split Router
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug643_1 b/maxscale-system-test/cnf/maxscale.cnf.template.bug643_1
new file mode 100755
index 000000000..04952b30e
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug643_1
@@ -0,0 +1,142 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+monitor_interval=10000
+servers=server1,server2,server3,server4
+detect_replication_lag=1
+detect_stale_master=1
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[qla]
+type=filter
+module=qlafilter
+filebase=/tmp/QueryLog
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split2
+
+[testfilter]
+type=filter
+module=foobar
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+#servers=server1,server2
+max_slave_connections=100%
+use_sql_variables_in=all
+#use_sql_variables_in=master
+user=maxskysql
+passwd=skysql
+#filters=typo|qla|regex|hints|regex|hints
+#enable_root_user=1
+filters=duplicate
+
+[RW Split2]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=all
+user=maxskysql
+passwd=skysql
+filters=qla|tests|hints
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug645 b/maxscale-system-test/cnf/maxscale.cnf.template.bug645
new file mode 100755
index 000000000..7133ba292
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug645
@@ -0,0 +1,108 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server1,server3,server2
+user=maxskysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.bug645_1 b/maxscale-system-test/cnf/maxscale.cnf.template.bug645_1
new file mode 100755
index 000000000..5d6d17161
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug645_1
@@ -0,0 +1,108 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=maxskysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.bug648 b/maxscale-system-test/cnf/maxscale.cnf.template.bug648
new file mode 100755
index 000000000..a2e35736e
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug648
@@ -0,0 +1,97 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+#filters=TEE
+
+[TEE]
+type=filter
+module=tee
+service=RW Split Router
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=TEE
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=TEE
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.bug650 b/maxscale-system-test/cnf/maxscale.cnf.template.bug650
new file mode 100755
index 000000000..623e21494
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug650
@@ -0,0 +1,109 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=maxskysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.bug664 b/maxscale-system-test/cnf/maxscale.cnf.template.bug664
new file mode 100755
index 000000000..e51e143fb
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug664
@@ -0,0 +1,108 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW_Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+version_string=5.1-OLD-Bored-Mysql
+filters=DuplicaFilter
+
+[RW_Split]
+type=service
+router=readwritesplit
+servers=server3,server2
+user=maxskysql
+passwd=skysql
+
+[DuplicaFilter]
+type=filter
+module=tee
+service=RW_Split
+
+[RW_Listener]
+type=listener
+service=RW_Router
+protocol=MySQLClient
+port=4006
+
+[RW_Split_list]
+type=listener
+service=RW_Split
+protocol=MySQLClient
+port=4016
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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
+#address=localhost
+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.bug670 b/maxscale-system-test/cnf/maxscale.cnf.template.bug670
new file mode 100755
index 000000000..08bd193dd
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug670
@@ -0,0 +1,134 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+monitor_interval=10000
+servers=server1,server2,server3,server4
+user=skysql
+passwd=skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[typo]
+type=filter
+module=regexfilter
+match=[Ff][Oo0][Rr][Mm]
+replace=from
+
+[qla]
+type=filter
+module=qlafilter
+filebase=/tmp/QueryLog
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split2
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+
+max_slave_connections=100%
+use_sql_variables_in=all
+
+user=skysql
+passwd=skysql
+filters=typo|qla|regex|hints|regex|hints
+enable_root_user=1
+
+[RW Split2]
+type=service
+router=readwritesplit
+servers=server1,server2
+max_slave_connections=100%
+use_sql_variables_in=all
+user=skysql
+passwd=skysql
+
+[Read Connection Router]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=skysql
+passwd=skysql
+filters=duplicate
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[RW Split Listener2]
+type=listener
+service=RW Split2
+protocol=MySQLClient
+port=4012
+
+[Read Connection Listener]
+type=listener
+service=Read Connection Router
+protocol=MySQLClient
+port=4008
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+
+[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.bug673 b/maxscale-system-test/cnf/maxscale.cnf.template.bug673
new file mode 100755
index 000000000..dc7c608fa
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug673
@@ -0,0 +1,42 @@
+[maxscale]
+threads=###threads###
+auth_connect_timeout=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+backend_connect_timeout=1
+
+[RW Split Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[server1]
+type=server
+address=1.1.1.1
+port=1234
+protocol=MySQLBackend
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bug694 b/maxscale-system-test/cnf/maxscale.cnf.template.bug694
new file mode 100755
index 000000000..b145e7d8b
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug694
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+use_sql_variables_in=all
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bug705 b/maxscale-system-test/cnf/maxscale.cnf.template.bug705
new file mode 100755
index 000000000..c21b4f90e
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug705
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bug711 b/maxscale-system-test/cnf/maxscale.cnf.template.bug711
new file mode 100755
index 000000000..bf3ffe11e
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug711
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+use_sql_variables_in=all
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bug718 b/maxscale-system-test/cnf/maxscale.cnf.template.bug718
new file mode 100755
index 000000000..0037bb149
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug718
@@ -0,0 +1,119 @@
+[maxscale]
+threads=1
+log_warning=1
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=g_server1, g_server2, g_server3, g_server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+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
+
+[g_server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[g_server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[g_server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[g_server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.bug730 b/maxscale-system-test/cnf/maxscale.cnf.template.bug730
new file mode 100755
index 000000000..cbfcdb021
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bug730
@@ -0,0 +1,100 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+filters=MySetOptionFilter
+
+[MySetOptionFilter]
+type=filter
+module=regexfilter
+options=ignorecase
+match=SET OPTION SQL_QUOTE_SHOW_CREATE
+replace=SET SQL_QUOTE_SHOW_CREATE
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=MySetOptionFilter
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=MySetOptionFilter
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.bulk_insert b/maxscale-system-test/cnf/maxscale.cnf.template.bulk_insert
new file mode 100755
index 000000000..d7bfc8a17
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.bulk_insert
@@ -0,0 +1,68 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[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
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.cache_basic b/maxscale-system-test/cnf/maxscale.cnf.template.cache_basic
new file mode 100644
index 000000000..04c3308e2
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.cache_basic
@@ -0,0 +1,76 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_master=false
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+filters=Cache
+
+[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
+#address=localhost
+socket=default
+
+[Cache]
+type=filter
+module=cache
+storage=storage_inmemory
+# NOTE: If you adjust the TTL values, ensure that test programs dependent
+# NOTE: on the TTL are ajusted as well.
+hard_ttl=10
+soft_ttl=5
+max_size=10M
+rules=/###access_homedir###/cache_rules.json
+
+[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
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.ccrfilter b/maxscale-system-test/cnf/maxscale.cnf.template.ccrfilter
new file mode 100644
index 000000000..1883ddeb6
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.ccrfilter
@@ -0,0 +1,110 @@
+[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
+
+# CCRFilter
+
+[ccrfilter]
+type=filter
+module=ccrfilter
+time=10
+###count=3
+###match=t2
+###ignore=t1
+
+# RWSplit
+
+[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
+filters=ccrfilter
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+# RConn - Slave
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+
+# RConn - Master
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+
+# MaxAdmin
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+# Servers
+
+[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.check_backend b/maxscale-system-test/cnf/maxscale.cnf.template.check_backend
new file mode 100755
index 000000000..3dd76b1a7
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.check_backend
@@ -0,0 +1,140 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+#socket=/tmp/rwsplit.sock
+
+
+[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
+#address=localhost
+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
+
+[gserver1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[gserver2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[gserver3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[gserver4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.config_reload b/maxscale-system-test/cnf/maxscale.cnf.template.config_reload
new file mode 100644
index 000000000..e61ab0492
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.config_reload
@@ -0,0 +1,59 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[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.connection_limit b/maxscale-system-test/cnf/maxscale.cnf.template.connection_limit
new file mode 100755
index 000000000..118fdd05d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.connection_limit
@@ -0,0 +1,97 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+max_connections=10
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_connections=25
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_connections=20
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.failover_mysqlmon b/maxscale-system-test/cnf/maxscale.cnf.template.failover_mysqlmon
new file mode 100644
index 000000000..09cc8a5ba
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.failover_mysqlmon
@@ -0,0 +1,88 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers= server1, server2, server3, server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_standalone_master=true
+failcount=2
+allow_cluster_recovery=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+
+[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.fwf b/maxscale-system-test/cnf/maxscale.cnf.template.fwf
new file mode 100755
index 000000000..8c9e1213b
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.fwf
@@ -0,0 +1,77 @@
+[maxscale]
+threads=###threads###
+query_classifier_args=log_unrecognized_statements=3
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=100
+
+[Database Firewall]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+log_match=true
+log_no_match=true
+
+[RW Split Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=Database Firewall
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[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
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.fwf_action b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_action
new file mode 100644
index 000000000..652f7e77b
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_action
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+# Service with blacklist action
+
+[readconn-blacklist]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=blacklist
+
+[readconn-blacklist-listener]
+type=listener
+service=readconn-blacklist
+protocol=MySQLClient
+port=4006
+
+[blacklist]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+action=block
+log_match=true
+log_no_match=true
+
+# Service with whitelist action
+
+[readconn-whitelist]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=whitelist
+
+[readconn-whitelist-listener]
+type=listener
+service=readconn-whitelist
+protocol=MySQLClient
+port=4009
+
+[whitelist]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+action=allow
+log_match=true
+log_no_match=true
+
+# Service with ignore action
+
+[readconn-ignore]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=ignore
+
+[readconn-ignore-listener]
+type=listener
+service=readconn-ignore
+protocol=MySQLClient
+port=4008
+
+[ignore]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+action=ignore
+log_match=true
+log_no_match=true
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.fwf_com_ping b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_com_ping
new file mode 100644
index 000000000..511a5a2ae
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_com_ping
@@ -0,0 +1,41 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+# Service with whitelist action
+
+[readconn-whitelist]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=whitelist
+
+[readconn-whitelist-listener]
+type=listener
+service=readconn-whitelist
+protocol=MySQLClient
+port=4006
+
+[whitelist]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+action=allow
+log_match=true
+log_no_match=true
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.fwf_logging b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_logging
new file mode 100644
index 000000000..de855516b
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_logging
@@ -0,0 +1,39 @@
+[maxscale]
+threads=###threads###
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[readconn]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=ignore
+
+[readconn-listener]
+type=listener
+service=readconn
+protocol=MySQLClient
+port=4009
+
+[ignore]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+action=ignore
+log_match=true
+log_no_match=true
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.fwf_syntax b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_syntax
new file mode 100644
index 000000000..893b4d6f1
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.fwf_syntax
@@ -0,0 +1,28 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=Database Firewall
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+[Database Firewall]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.galera b/maxscale-system-test/cnf/maxscale.cnf.template.galera
new file mode 100755
index 000000000..cddd792c5
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.galera
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.galera.bug681 b/maxscale-system-test/cnf/maxscale.cnf.template.galera.bug681
new file mode 100755
index 000000000..938111b73
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.galera.bug681
@@ -0,0 +1,107 @@
+[maxscale]
+threads=1
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3
+#user=maxmon
+#passwd=maxpwd
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3
+#user=maxpriv
+#passwd=maxpwd
+user=maxskysql
+passwd=skysql
+filters=MyLogFilter
+version_string=MariaDBEC-10.0.14
+localhost_match_wildcard_host=1
+max_slave_connections=10%
+#max_slave_replication_lag=30
+#router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+
+[Read Connection Router]
+type=service
+router=readconnroute
+router_options=synced
+servers=server1,server2,server3
+#user=maxpriv
+#passwd=maxpwd
+user=maxskysql
+passwd=skysql
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[Read Connection Listener]
+type=listener
+service=Read Connection Router
+protocol=MySQLClient
+port=4008
+#socket=/tmp/readconn.sock
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[MyLogFilter]
+type=filter
+module=qlafilter
+filebase=/tmp/QueryLog
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+#[server4]
+#type=server
+#address=###galera_server_IP_4###
+#port=###galera_server_port_4###
+#protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.galera.weight b/maxscale-system-test/cnf/maxscale.cnf.template.galera.weight
new file mode 100755
index 000000000..6e782421d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.galera.weight
@@ -0,0 +1,78 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+use_priority=true
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+weightby=serversize_rws
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router]
+type=service
+router=readconnroute
+router_options=synced
+servers=server1,server2,server3,server4
+weightby=serversize
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[Read Connection Listener]
+type=listener
+service=Read Connection Router
+protocol=MySQLClient
+port=4008
+#socket=/tmp/readconn.sock
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+serversize=1
+serversize_rws=1
+priority=1
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+serversize=2
+serversize_rws=30000
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+serversize=3
+serversize_rws=20000
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+serversize=0
+serversize_rws=10000
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.galera_mxs564 b/maxscale-system-test/cnf/maxscale.cnf.template.galera_mxs564
new file mode 100644
index 000000000..292d6b7fe
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.galera_mxs564
@@ -0,0 +1,88 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.galera_priority b/maxscale-system-test/cnf/maxscale.cnf.template.galera_priority
new file mode 100644
index 000000000..88d5bfe31
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.galera_priority
@@ -0,0 +1,66 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+log_info=1
+log_notice=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+use_priority=true
+monitor_interval=1000
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+priority=2
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+priority=4
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+priority=1
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+priority=3
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.gatekeeper b/maxscale-system-test/cnf/maxscale.cnf.template.gatekeeper
new file mode 100644
index 000000000..7bce3ef9c
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.gatekeeper
@@ -0,0 +1,81 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+# Monitors
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_master=false
+
+# Services
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=master
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+user=maxskysql
+passwd=skysql
+filters=gatekeeper
+
+[CLI]
+type=service
+router=cli
+
+# Filters
+
+[gatekeeper]
+type=filter
+module=gatekeeper
+mode=learn
+
+# Listeners
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+socket=/tmp/rwsplit.sock
+
+# Servers
+
+[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.hartmut b/maxscale-system-test/cnf/maxscale.cnf.template.hartmut
new file mode 100755
index 000000000..2f651e5e1
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.hartmut
@@ -0,0 +1,93 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=500
+detect_replication_lag=1
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_replication_lag=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.hints b/maxscale-system-test/cnf/maxscale.cnf.template.hints
new file mode 100755
index 000000000..8e45ac28b
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.hints
@@ -0,0 +1,98 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.hints2 b/maxscale-system-test/cnf/maxscale.cnf.template.hints2
new file mode 100644
index 000000000..575c19b22
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.hints2
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+
+[hints]
+type=filter
+module=hintfilter
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+filters=hints
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.insertstream b/maxscale-system-test/cnf/maxscale.cnf.template.insertstream
new file mode 100755
index 000000000..d0b63d4e0
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.insertstream
@@ -0,0 +1,47 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[insertstream]
+type=filter
+module=insertstream
+
+[RW Split Router]
+type=service
+router=readconnroute
+servers=server1
+user=maxskysql
+passwd=skysql
+filters=insertstream
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.java_prep_stmt b/maxscale-system-test/cnf/maxscale.cnf.template.java_prep_stmt
new file mode 100644
index 000000000..5002f8c5f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.java_prep_stmt
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[galera Monitor]
+type=monitor
+module=galeramon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+version_string=5.5.5-10.0.0-mxs
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+version_string=5.5.5-10.0.0-mxs
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+version_string=5.5.5-10.0.0-mxs
+
+[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=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.kerberos b/maxscale-system-test/cnf/maxscale.cnf.template.kerberos
new file mode 100755
index 000000000..32b8b8243
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.kerberos
@@ -0,0 +1,106 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+authenticator=GSSAPIAuth
+authenticator_options=principal_name=mariadb/maxscale.test@MAXSCALE.TEST
+
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+authenticator=GSSAPIAuth
+authenticator_options=principal_name=mariadb/maxscale.test@MAXSCALE.TEST
+
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+authenticator=GSSAPIAuth
+authenticator_options=principal_name=mariadb/maxscale.test@MAXSCALE.TEST
+
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+authenticator=GSSAPIBackendAuth
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+authenticator=GSSAPIBackendAuth
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+authenticator=GSSAPIBackendAuth
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+authenticator=GSSAPIBackendAuth
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.lag b/maxscale-system-test/cnf/maxscale.cnf.template.lag
new file mode 100755
index 000000000..4a9fec583
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.lag
@@ -0,0 +1,104 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+detect_replication_lag=1
+user=maxskysql
+passwd= skysql
+backend_connect_timeout=10
+backend_read_timeout=10
+backend_write_timeout=10
+
+
+[hints]
+type=filter
+module=hintfilter
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+use_sql_variables_in=all
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+max_slave_replication_lag=20
+filters=hints
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.load b/maxscale-system-test/cnf/maxscale.cnf.template.load
new file mode 100755
index 000000000..ed8851054
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.load_galera b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera
new file mode 100755
index 000000000..c9bf14fda
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers1 b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers1
new file mode 100755
index 000000000..5d51a9781
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers1
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers10 b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers10
new file mode 100755
index 000000000..ee3cd6bc8
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load_galera_pers10
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.load_pers1 b/maxscale-system-test/cnf/maxscale.cnf.template.load_pers1
new file mode 100755
index 000000000..df99fcaae
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load_pers1
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+persistpoolmax=1
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.load_pers10 b/maxscale-system-test/cnf/maxscale.cnf.template.load_pers10
new file mode 100755
index 000000000..204ef156a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.load_pers10
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+persistpoolmax=10
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.longblob b/maxscale-system-test/cnf/maxscale.cnf.template.longblob
new file mode 100755
index 000000000..732891a5a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.longblob
@@ -0,0 +1,94 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+backend_connect_timeout=10
+backend_read_timeout=10
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.longblob_filters b/maxscale-system-test/cnf/maxscale.cnf.template.longblob_filters
new file mode 100755
index 000000000..2d508dd3f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.longblob_filters
@@ -0,0 +1,205 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+backend_connect_timeout=10
+backend_read_timeout=10
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+#filters=duplicate|hints|regex|ccrfilter|MaxRows|Masking|Cache|namedserverfilter|Database Firewall
+filters=duplicate
+#filters=hints|regex|ccrfilter|MaxRows|Masking|Cache|namedserverfilter|Database Firewall
+
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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
+
+[duplicate]
+type=filter
+module=tee
+service=RW Split Router Galera
+
+[hints]
+type=filter
+module=hintfilter
+
+[regex]
+type=filter
+module=regexfilter
+match=fetch
+replace=select
+
+[ccrfilter]
+type=filter
+module=ccrfilter
+time=1000
+###count=3
+###match=t2
+###ignore=t1
+
+[MaxRows]
+type=filter
+module=maxrows
+max_resultset_rows=20
+max_resultset_size=900000000
+debug=3
+
+[Masking]
+type=filter
+module=masking
+rules=/###access_homedir###/masking_rules.json
+large_payload=ignore
+
+[Cache]
+type=filter
+module=cache
+storage=storage_inmemory
+# NOTE: If you adjust the TTL values, ensure that test programs dependent
+# NOTE: on the TTL are ajusted as well.
+hard_ttl=10
+soft_ttl=5
+max_size=10M
+rules=/###access_homedir###/cache_rules.json
+
+[namedserverfilter]
+type=filter
+module=namedserverfilter
+match=SELECT
+server=server2
+
+[Database Firewall]
+type=filter
+module=dbfwfilter
+rules=/###access_homedir###/rules/rules.txt
+log_match=true
+log_no_match=true
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+monitor_interval=100
+root_node_as_master=false
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+
+[gserver1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[gserver2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[gserver3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[gserver4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.masking_mysqltest b/maxscale-system-test/cnf/maxscale.cnf.template.masking_mysqltest
new file mode 100644
index 000000000..37f1d68db
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.masking_mysqltest
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=Masking
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[Masking]
+type=filter
+module=masking
+rules=/###access_homedir###/masking_rules.json
+
+[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.master_only b/maxscale-system-test/cnf/maxscale.cnf.template.master_only
new file mode 100755
index 000000000..de5df66d2
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.master_only
@@ -0,0 +1,74 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.maxinfo b/maxscale-system-test/cnf/maxscale.cnf.template.maxinfo
new file mode 100644
index 000000000..ca277fde2
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.maxinfo
@@ -0,0 +1,105 @@
+[maxscale]
+threads=4
+
+[maxinfo]
+type=service
+router=maxinfo
+user=maxuser
+passwd=maxpwd
+
+[Maxinfo SQL Listener]
+type=listener
+service=maxinfo
+protocol=MySQLClient
+port=4000
+
+[Maxinfo HTTP Listener]
+type=listener
+service=maxinfo
+protocol=HTTPD
+port=8080
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2,server3,server4
+user=maxuser
+passwd=maxpwd
+monitor_interval=10000
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.maxpasswd b/maxscale-system-test/cnf/maxscale.cnf.template.maxpasswd
new file mode 100755
index 000000000..0d4c1122f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.maxpasswd
@@ -0,0 +1,94 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.maxrows b/maxscale-system-test/cnf/maxscale.cnf.template.maxrows
new file mode 100755
index 000000000..261315c2c
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.maxrows
@@ -0,0 +1,101 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+filters=MaxRows
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[MaxRows]
+type=filter
+module=maxrows
+max_resultset_rows=20
+max_resultset_size=900000000
+debug=3
+
+[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.mm b/maxscale-system-test/cnf/maxscale.cnf.template.mm
new file mode 100755
index 000000000..d7ad51c4d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mm
@@ -0,0 +1,79 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mmmon
+servers= server1, server2
+user=maxskysql
+passwd= skysql
+detect_stale_master=0
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mm_mysqlmon b/maxscale-system-test/cnf/maxscale.cnf.template.mm_mysqlmon
new file mode 100644
index 000000000..f8a49d36f
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mm_mysqlmon
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers= server1, server2, server3, server4
+user=maxskysql
+passwd= skysql
+detect_stale_master=0
+monitor_interval=1000
+multimaster=true
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[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.mxs1045 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1045
new file mode 100644
index 000000000..b69ca8ed1
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1045
@@ -0,0 +1,93 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_master=false
+script=/bin/sh -c "echo hello world!"
+events=master_down,server_down
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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.mxs1123 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1123
new file mode 100644
index 000000000..a10232875
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1123
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_stale_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+connection_timeout=60
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.mxs118 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs118
new file mode 100755
index 000000000..482e34a75
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs118
@@ -0,0 +1,98 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[MySQL Monitor 1]
+type=monitor
+module=galeramon
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs127 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs127
new file mode 100755
index 000000000..0287088f9
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs127
@@ -0,0 +1,90 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS,disable_sescmd_history=YES
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs361 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs361
new file mode 100755
index 000000000..f1f4a3a90
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs361
@@ -0,0 +1,154 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=100
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+monitor_interval=100
+root_node_as_master=false
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+#persistpoolmax=1
+#persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+#persistpoolmax=5
+#persistmaxtime=60
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+#persistpoolmax=10
+#persistmaxtime=60
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+#persistpoolmax=30
+#persistmaxtime=30
+
+[gserver1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+#persistpoolmax=10
+#persistmaxtime=3660
+
+[gserver2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+#persistpoolmax=15
+#persistmaxtime=30
+
+[gserver3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+#persistpoolmax=19
+#persistmaxtime=0
+
+[gserver4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+#persistpoolmax=0
+#persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs501 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs501
new file mode 100755
index 000000000..f02cb93c4
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs501
@@ -0,0 +1,99 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+filters=duplicate
+
+[duplicate]
+type=filter
+module=tee
+match=insert
+service=Read Connection Router Master
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs548 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs548
new file mode 100755
index 000000000..f43430f78
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs548
@@ -0,0 +1,92 @@
+[maxscale]
+threads=1
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs559 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs559
new file mode 100755
index 000000000..1cd8f26eb
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs559
@@ -0,0 +1,94 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+backend_connect_timeout=1
+backend_read_timeout=1
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs710_bad_socket b/maxscale-system-test/cnf/maxscale.cnf.template.mxs710_bad_socket
new file mode 100755
index 000000000..36294a748
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs710_bad_socket
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+socket=/var/lib/mysqld/mysql.sock
+
+[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
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+socket=/var/lib/mysqld/mysql.sock
+
+[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.mxs711_two_ports b/maxscale-system-test/cnf/maxscale.cnf.template.mxs711_two_ports
new file mode 100755
index 000000000..fba1e0127
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs711_two_ports
@@ -0,0 +1,98 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[RW Split Listener1]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=22
+
+[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
+#address=localhost
+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.mxs720_line_with_no_equal b/maxscale-system-test/cnf/maxscale.cnf.template.mxs720_line_with_no_equal
new file mode 100755
index 000000000..337209132
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs720_line_with_no_equal
@@ -0,0 +1,93 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[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
+line_no_equal
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs720_wierd_line b/maxscale-system-test/cnf/maxscale.cnf.template.mxs720_wierd_line
new file mode 100755
index 000000000..31a3bd49a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs720_wierd_line
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+hren=hren'
+укпоукц=так_и_крешнуться_можно
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.mxs722 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs722
new file mode 100644
index 000000000..760e939ea
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs722
@@ -0,0 +1,109 @@
+[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
+
+[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
+filters=QLA
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=QLA
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=QLA
+
+[QLA]
+type=filter
+module=qlafilter
+filebase=/tmp/QueryLog
+
+[Debug Interface]
+type=service
+router=debugcli
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[Debug Listener]
+type=listener
+service=Debug Interface
+protocol=telnetd
+port=4442
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+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.mxs799 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs799
new file mode 100755
index 000000000..b7f6e60ba
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs799
@@ -0,0 +1,90 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+socket=/var/run/mysqld/mysqld.sock
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs827_write_timeout b/maxscale-system-test/cnf/maxscale.cnf.template.mxs827_write_timeout
new file mode 100755
index 000000000..8486e5244
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs827_write_timeout
@@ -0,0 +1,91 @@
+[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
+
+[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
+connection_keepalive=5
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.mxs874 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs874
new file mode 100644
index 000000000..83346052e
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs874
@@ -0,0 +1,82 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_stale_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3
+router_options=disable_sescmd_history=false
+user=maxskysql
+passwd=skysql
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+
+[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
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs922 b/maxscale-system-test/cnf/maxscale.cnf.template.mxs922
new file mode 100644
index 000000000..af71b9a81
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs922
@@ -0,0 +1,57 @@
+[maxscale]
+threads=###threads###
+
+[mysql-monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[rwsplit-service]
+type=service
+router= readwritesplit
+user=maxskysql
+passwd=skysql
+
+[read-connection-router-slave]
+type=service
+router=readconnroute
+user=maxskysql
+passwd=skysql
+
+[read-connection-router-master]
+type=service
+router=readconnroute
+router_options=master
+user=maxskysql
+passwd=skysql
+
+[rwsplit-service-listener]
+type=listener
+service=rwsplit-service
+protocol=MySQLClient
+port=4006
+
+[read-connection-router-slave-listener]
+type=listener
+service=read-connection-router-slave
+protocol=MySQLClient
+port=4009
+
+[read-connection-router-master-listener]
+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
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs922_base b/maxscale-system-test/cnf/maxscale.cnf.template.mxs922_base
new file mode 100644
index 000000000..d8a68b4b4
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs922_base
@@ -0,0 +1,32 @@
+[maxscale]
+threads=###threads###
+
+[rwsplit-service]
+type=service
+router=readwritesplit
+weightby=weight
+user=maxskysql
+passwd=skysql
+
+[read-connection-router-slave]
+type=service
+router=readconnroute
+user=maxskysql
+passwd=skysql
+
+[read-connection-router-master]
+type=service
+router=readconnroute
+router_options=master
+user=maxskysql
+passwd=skysql
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_backup b/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_backup
new file mode 100644
index 000000000..b8c6a8276
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.mysqlmon_backup
@@ -0,0 +1,86 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+master_failure_mode=fail_on_write
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[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.namedserverfilter b/maxscale-system-test/cnf/maxscale.cnf.template.namedserverfilter
new file mode 100644
index 000000000..dba6a0419
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.namedserverfilter
@@ -0,0 +1,96 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[namedserverfilter]
+type=filter
+module=namedserverfilter
+match=SELECT
+server=server2
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+
+# Mixing the order of slaves will make server3 the first slave server
+servers=server1,server3,server2,server4
+user=maxskysql
+passwd=skysql
+filters=namedserverfilter
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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
+
+[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.no_ses_cmd_store b/maxscale-system-test/cnf/maxscale.cnf.template.no_ses_cmd_store
new file mode 100755
index 000000000..ac07e5cfc
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.no_ses_cmd_store
@@ -0,0 +1,92 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_GLOBAL_CONNECTIONS,disable_sescmd_history=true
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.nsfilter b/maxscale-system-test/cnf/maxscale.cnf.template.nsfilter
new file mode 100644
index 000000000..002a26668
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.nsfilter
@@ -0,0 +1,93 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+filters=nsfilter
+
+[nsfilter]
+type=filter
+module=namedserverfilter
+match=test
+server=server1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1, server2, server3, server4
+user=maxskysql
+passwd=skysql
+
+[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.open_close b/maxscale-system-test/cnf/maxscale.cnf.template.open_close
new file mode 100755
index 000000000..fb4dae219
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.open_close
@@ -0,0 +1,101 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=100
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=100
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=100
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=100
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.pers_01 b/maxscale-system-test/cnf/maxscale.cnf.template.pers_01
new file mode 100755
index 000000000..7519176c9
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.pers_01
@@ -0,0 +1,154 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=100
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+monitor_interval=100
+root_node_as_master=false
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=5
+persistmaxtime=60
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=60
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=30
+persistmaxtime=30
+
+[gserver1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[gserver2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=15
+persistmaxtime=30
+
+[gserver3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=19
+persistmaxtime=0
+
+[gserver4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=0
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.regexfilter1 b/maxscale-system-test/cnf/maxscale.cnf.template.regexfilter1
new file mode 100644
index 000000000..d04e388c3
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.regexfilter1
@@ -0,0 +1,103 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+# Normal usage, should replace SELECT 123 with SELECT 0
+[regex1]
+type=filter
+module=regexfilter
+match=SELECT [1-5]*
+replace=SELECT 0
+
+[Read Connection Router 1]
+type=service
+router=readconnroute
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=regex1
+
+[Read Connection Listener 1]
+type=listener
+service=Read Connection Router 1
+protocol=MySQLClient
+port=4006
+
+# Test with a username, should not match
+[regex2]
+type=filter
+module=regexfilter
+match=SELECT [1-5]*
+replace=SELECT 0
+user=bad_username
+
+[Read Connection Router 2]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=regex2
+
+[Read Connection Listener 2]
+type=listener
+service=Read Connection Router 2
+protocol=MySQLClient
+port=4008
+
+# Test with a hostname, should not match
+[regex3]
+type=filter
+module=regexfilter
+match=SELECT [1-5]*
+replace=SELECT 0
+source=127.0.0.1
+
+[Read Connection Router 3]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+filters=regex3
+
+[Read Connection Listener 3]
+type=listener
+service=Read Connection Router 3
+protocol=MySQLClient
+port=4009
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[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.repl_lgc b/maxscale-system-test/cnf/maxscale.cnf.template.repl_lgc
new file mode 100755
index 000000000..3e7477211
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.repl_lgc
@@ -0,0 +1,92 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.replication b/maxscale-system-test/cnf/maxscale.cnf.template.replication
new file mode 100755
index 000000000..bd3d9c0a2
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication
@@ -0,0 +1,90 @@
+[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
+
+[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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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.replication.bug539 b/maxscale-system-test/cnf/maxscale.cnf.template.replication.bug539
new file mode 100755
index 000000000..938e1fc65
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication.bug539
@@ -0,0 +1,55 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[hints]
+type=filter
+module=hintfilter
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+max_slave_connections=100%
+use_sql_variables_in=master
+router_options=slave_selection_criteria=LEAST_BEHIND_MASTER
+user=maxskysql
+passwd=skysql
+filters=hints
+
+[Client Interface]
+type=service
+router=cli
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+socket=/tmp/rwsplit.sock
+
+[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.replication.one_slave b/maxscale-system-test/cnf/maxscale.cnf.template.replication.one_slave
new file mode 100755
index 000000000..e84463849
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication.one_slave
@@ -0,0 +1,79 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Router]
+type=service
+router=readwritesplit
+max_slave_connections=1
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+
+[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.replication_manager b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager
new file mode 100644
index 000000000..d685f2424
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager
@@ -0,0 +1,69 @@
+[maxscale]
+threads=###threads###
+log_info=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_standalone_master=true
+failcount=2
+allow_cluster_recovery=true
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+master_failure_mode=fail_on_write
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[CLI Network Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+address=127.0.0.1
+port=6603
+
+[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.replication_manager_2nodes b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager_2nodes
new file mode 100644
index 000000000..2cb70f249
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager_2nodes
@@ -0,0 +1,56 @@
+[maxscale]
+threads=4
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_standalone_master=true
+failcount=2
+allow_cluster_recovery=true
+
+[Traffic Router]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Traffic Listener]
+type=listener
+service=Traffic Router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[CLI Network Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+address=127.0.0.1
+port=6603
+
+[server1]
+type=server
+address=node-000
+port=3306
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=node-001
+port=3306
+protocol=MySQLBackend
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager_3nodes b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager_3nodes
new file mode 100644
index 000000000..6c65d805d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.replication_manager_3nodes
@@ -0,0 +1,61 @@
+[maxscale]
+threads=4
+
+[MySQL-Monitor]
+type=monitor
+module=mysqlmon
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_standalone_master=true
+failcount=2
+allow_cluster_recovery=true
+
+[Traffic-Router]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3
+user=maxskysql
+passwd=skysql
+
+[Traffic-Listener]
+type=listener
+service=Traffic-Router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI-Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[CLI-Network-Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+port=6603
+
+[server1]
+type=server
+address=node-000
+port=3306
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=node-001
+port=3306
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=node-002
+port=3306
+protocol=MySQLBackend
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_multi_stmt b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_multi_stmt
new file mode 100644
index 000000000..52b3f29ba
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_multi_stmt
@@ -0,0 +1,92 @@
+[maxscale]
+threads=###threads###
+log_info=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+detect_stale_master=false
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+router_options=strict_multi_stmt=true
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.rwsplit_read_only_trx b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_read_only_trx
new file mode 100755
index 000000000..500764e3c
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_read_only_trx
@@ -0,0 +1,87 @@
+[maxscale]
+threads=###threads###
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options=slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[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
+#address=localhost
+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.rwsplit_readonly b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_readonly
new file mode 100644
index 000000000..ab510030a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.rwsplit_readonly
@@ -0,0 +1,88 @@
+[maxscale]
+threads=###threads###
+log_info=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+router_options=master_failure_mode=fail_instantly
+
+[Read Connection Router Slave]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+router_options=master_failure_mode=error_on_write
+
+[Read Connection Router Master]
+type=service
+router=readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+router_options=master_failure_mode=fail_on_write
+
+[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
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+socket=default
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+
+[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.schemarouter_duplicate_db b/maxscale-system-test/cnf/maxscale.cnf.template.schemarouter_duplicate_db
new file mode 100755
index 000000000..03610cf67
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.schemarouter_duplicate_db
@@ -0,0 +1,59 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Sharding router]
+type=service
+router=schemarouter
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+auth_all_servers=1
+
+[Sharding Listener]
+type=listener
+service=Sharding router
+protocol=MySQLClient
+port=4006
+
+[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.script b/maxscale-system-test/cnf/maxscale.cnf.template.script
new file mode 100755
index 000000000..026748347
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.script
@@ -0,0 +1,144 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+script=/###access_homedir###/script/script.sh --event=$EVENT --initiator=$INITIATOR --nodelist=$NODELIST
+events=master_down,master_up, slave_up, server_down ,server_up,lost_master,lost_slave,new_master,new_slave
+monitor_interval=1000
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=gserver1,gserver2,gserver3,gserver4
+script=/###access_homedir###/script/script.sh --event=$EVENT --initiator=$INITIATOR --nodelist=$NODELIST
+events=master_down,master_up, slave_up, server_down ,server_up,lost_master,lost_slave,new_master,new_slave,server_down,server_up,synced_down,synced_up
+disable_master_role_setting=true
+root_node_as_master=false
+user=maxskysql
+passwd=skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[RW Split Router Galera]
+type=service
+router= readwritesplit
+servers=gserver1,gserver2,gserver3,gserver4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[RW Split Listener Galera]
+type=listener
+service=RW Split Router Galera
+protocol=MySQLClient
+port=4016
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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
+
+[gserver1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[gserver2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[gserver3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[gserver4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.session_limits b/maxscale-system-test/cnf/maxscale.cnf.template.session_limits
new file mode 100755
index 000000000..68f9fac21
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.session_limits
@@ -0,0 +1,91 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+connection_timeout=30
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS,max_sescmd_history=10,disable_sescmd_history=false
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.setup_binlog b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog
new file mode 100755
index 000000000..c85f33b95
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog
@@ -0,0 +1,38 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1
+
+
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in
new file mode 100755
index 000000000..c85f33b95
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog.in
@@ -0,0 +1,38 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1
+
+
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1
new file mode 100755
index 000000000..473832a71
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog1
@@ -0,0 +1,40 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2
new file mode 100755
index 000000000..2d41e8396
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog2
@@ -0,0 +1,41 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+log_trace=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync
new file mode 100755
index 000000000..4fb918415
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync
@@ -0,0 +1,41 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=1,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0
new file mode 100755
index 000000000..13a459c20
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss0
@@ -0,0 +1,41 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=0,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1
new file mode 100755
index 000000000..93098385d
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs0_ss1
@@ -0,0 +1,40 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=1,transaction_safety=0,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0 b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0
new file mode 100755
index 000000000..8e78d9126
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_semisync_txs1_ss0
@@ -0,0 +1,40 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,semisync=0,transaction_safety=1,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe
new file mode 100755
index 000000000..33957920a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.setup_binlog_tx_safe
@@ -0,0 +1,41 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Binlog_Service]
+type=service
+router=binlogrouter
+#servers=master
+user=skysql
+passwd=skysql
+version_string=5.6.15-log
+#router_options=server-id=3,user=repl,password=repl,master-id=1
+router_options=server-id=3,user=repl,password=repl,longburst=500,heartbeat=30,binlogdir=/var/lib/maxscale/Binlog_Service,transaction_safety=1,mariadb10-compatibility=1
+
+
+
+#
+[Binlog Listener]
+type=listener
+service=Binlog_Service
+protocol=MySQLClient
+port=5306
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+socket=default
+
+[master]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.sharding b/maxscale-system-test/cnf/maxscale.cnf.template.sharding
new file mode 100755
index 000000000..3e775f747
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.sharding
@@ -0,0 +1,62 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Sharding router]
+type=service
+router=schemarouter
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+auth_all_servers=1
+ignore_databases_regex=.*
+
+[Sharding Listener]
+type=listener
+service=Sharding router
+protocol=MySQLClient
+port=4006
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+#address=localhost
+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.show_monitor_crash b/maxscale-system-test/cnf/maxscale.cnf.template.show_monitor_crash
new file mode 100755
index 000000000..bf4956aa7
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.show_monitor_crash
@@ -0,0 +1,93 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql_bad
+passwd= skysql
+monitor_interval=1000
+detect_stale_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
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+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.simplejavatest b/maxscale-system-test/cnf/maxscale.cnf.template.simplejavatest
new file mode 100644
index 000000000..1ca3a6713
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.simplejavatest
@@ -0,0 +1,92 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[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=5.5.5-10.0.0-mxs
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+version_string=5.5.5-10.0.0-mxs
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+version_string=5.5.5-10.0.0-mxs
+
+[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.sql_queries_pers1 b/maxscale-system-test/cnf/maxscale.cnf.template.sql_queries_pers1
new file mode 100755
index 000000000..8b1ef3abd
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.sql_queries_pers1
@@ -0,0 +1,100 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=1
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.sql_queries_pers10 b/maxscale-system-test/cnf/maxscale.cnf.template.sql_queries_pers10
new file mode 100755
index 000000000..d4f8ce3e8
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.sql_queries_pers10
@@ -0,0 +1,100 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+max_slave_connections=1
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+
+[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
+#address=localhost
+socket=default
+
+[server1]
+type=server
+address=###node_server_IP_1###
+port=###node_server_port_1###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server2]
+type=server
+address=###node_server_IP_2###
+port=###node_server_port_2###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server3]
+type=server
+address=###node_server_IP_3###
+port=###node_server_port_3###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
+[server4]
+type=server
+address=###node_server_IP_4###
+port=###node_server_port_4###
+protocol=MySQLBackend
+persistpoolmax=10
+persistmaxtime=3660
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.ssl b/maxscale-system-test/cnf/maxscale.cnf.template.ssl
new file mode 100755
index 000000000..41a0bb71a
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.ssl
@@ -0,0 +1,95 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1, server2, server3,server4
+user=maxskysql
+passwd=skysql
+router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[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
+#address=localhost
+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.ssl_load b/maxscale-system-test/cnf/maxscale.cnf.template.ssl_load
new file mode 100755
index 000000000..65021601c
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.ssl_load
@@ -0,0 +1,106 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1, server2,server3 ,server4
+user=maxskysql
+passwd= skysql
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[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.ssl_load_galera b/maxscale-system-test/cnf/maxscale.cnf.template.ssl_load_galera
new file mode 100755
index 000000000..194eef2e8
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.ssl_load_galera
@@ -0,0 +1,106 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[Galera Monitor]
+type=monitor
+module=galeramon
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+root_node_as_master=false
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+max_slave_connections=100%
+router_options=slave_selection_criteria=LEAST_CURRENT_OPERATIONS
+
+[Read Connection Router Slave]
+type=service
+router=readconnroute
+router_options= slave
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=maxskysql
+passwd=skysql
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+#socket=/tmp/rwsplit.sock
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[Read Connection Listener Slave]
+type=listener
+service=Read Connection Router Slave
+protocol=MySQLClient
+port=4009
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[Read Connection Listener Master]
+type=listener
+service=Read Connection Router Master
+protocol=MySQLClient
+port=4008
+ssl=required
+ssl_cert=/###access_homedir###/certs/server-cert.pem
+ssl_key=/###access_homedir###/certs/server-key.pem
+ssl_ca_cert=/###access_homedir###/certs/ca.pem
+ssl_version=TLSv12
+
+[CLI]
+type=service
+router=cli
+
+[CLI Listener]
+type=listener
+service=CLI
+protocol=maxscaled
+
+socket=default
+
+[server1]
+type=server
+address=###galera_server_IP_1###
+port=###galera_server_port_1###
+protocol=MySQLBackend
+
+[server2]
+type=server
+address=###galera_server_IP_2###
+port=###galera_server_port_2###
+protocol=MySQLBackend
+
+[server3]
+type=server
+address=###galera_server_IP_3###
+port=###galera_server_port_3###
+protocol=MySQLBackend
+
+[server4]
+type=server
+address=###galera_server_IP_4###
+port=###galera_server_port_4###
+protocol=MySQLBackend
+
diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.user_cache b/maxscale-system-test/cnf/maxscale.cnf.template.user_cache
new file mode 100644
index 000000000..52f474f30
--- /dev/null
+++ b/maxscale-system-test/cnf/maxscale.cnf.template.user_cache
@@ -0,0 +1,89 @@
+[maxscale]
+threads=###threads###
+log_warning=1
+
+[MySQL Monitor]
+type=monitor
+module=mysqlmon
+###repl51###
+servers= server1,server2,server3,server4
+user=maxskysql
+passwd= skysql
+monitor_interval=1000
+detect_stale_master=false
+
+#
+# The 'testuser'@'%' user will be created as a part of the test
+#
+
+[RW Split Router]
+type=service
+router= readwritesplit
+servers=server1,server2,server3,server4
+user=testuser
+passwd=testpasswd
+
+[Read Connection Router Master]
+type=service
+router=readconnroute
+router_options=master
+servers=server1,server2,server3,server4
+user=testuser
+passwd=testpasswd
+
+[RW Split Listener]
+type=listener
+service=RW Split Router
+protocol=MySQLClient
+port=4006
+
+#
+# This listener will have its own user cache
+#
+
+[Read Connection Listener Slave]
+type=listener
+service=RW Split Router
+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
+#address=localhost
+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/config_operations.cpp b/maxscale-system-test/config_operations.cpp
new file mode 100644
index 000000000..dedd2c8b1
--- /dev/null
+++ b/maxscale-system-test/config_operations.cpp
@@ -0,0 +1,222 @@
+#include "config_operations.h"
+
+// The configuration should use these names for the services, listeners and monitors
+#define SERVICE_NAME1 "rwsplit-service"
+#define SERVICE_NAME2 "read-connection-router-master"
+#define SERVICE_NAME3 "read-connection-router-slave"
+#define LISTENER_NAME1 "rwsplit-service-listener"
+#define LISTENER_NAME2 "read-connection-router-master-listener"
+#define LISTENER_NAME3 "read-connection-router-slave-listener"
+
+struct
+{
+ const char *service;
+ const char *listener;
+ int port;
+} services[]
+{
+ {SERVICE_NAME1, LISTENER_NAME1, 4006},
+ {SERVICE_NAME2, LISTENER_NAME2, 4008},
+ {SERVICE_NAME3, LISTENER_NAME3, 4009}
+};
+
+Config::Config(TestConnections* parent):
+ test_(parent)
+{
+}
+
+Config::~Config()
+{
+}
+
+void Config::add_server(int num)
+{
+ test_->tprintf("Adding the servers");
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin add server server%d " SERVICE_NAME1, num);
+ test_->ssh_maxscale(true, "maxadmin add server server%d " SERVICE_NAME2, num);
+ test_->ssh_maxscale(true, "maxadmin add server server%d " SERVICE_NAME3, num);
+
+ for (auto& a : created_monitors_)
+ {
+ test_->ssh_maxscale(true, "maxadmin add server server%d %s", num, a.c_str());
+ }
+
+ test_->stop_timeout();
+}
+
+void Config::remove_server(int num)
+{
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin remove server server%d " SERVICE_NAME1, num);
+ test_->ssh_maxscale(true, "maxadmin remove server server%d " SERVICE_NAME2, num);
+ test_->ssh_maxscale(true, "maxadmin remove server server%d " SERVICE_NAME3, num);
+
+ for (auto& a : created_monitors_)
+ {
+ test_->ssh_maxscale(true, "maxadmin remove server server%d %s", num, a.c_str());
+ }
+
+ test_->stop_timeout();
+}
+
+void Config::add_created_servers(const char *object)
+{
+ for (auto a : created_servers_)
+ {
+ test_->ssh_maxscale(true, "maxadmin add server server%d %s", a, object);
+ }
+}
+
+void Config::destroy_server(int num)
+{
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin destroy server server%d", num);
+ created_servers_.erase(num);
+ test_->stop_timeout();
+}
+
+void Config::create_server(int num)
+{
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin create server server%d %s %d",
+ num, test_->repl->IP[num], test_->repl->port[num]);
+ created_servers_.insert(num);
+ test_->stop_timeout();
+}
+
+void Config::alter_server(int num, const char *key, const char *value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter server server%d %s=%s", num, key, value);
+}
+
+void Config::alter_server(int num, const char *key, int value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter server server%d %s=%d", num, key, value);
+}
+
+void Config::alter_server(int num, const char *key, float value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter server server%d %s=%f", num, key, value);
+}
+
+void Config::create_monitor(const char *name, const char *module, int interval)
+{
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin create monitor %s %s", name, module);
+ alter_monitor(name, "monitor_interval", interval);
+ alter_monitor(name, "user", test_->maxscale_user);
+ alter_monitor(name, "password", test_->maxscale_password);
+ test_->ssh_maxscale(true, "maxadmin restart monitor %s", name);
+ test_->stop_timeout();
+
+ created_monitors_.insert(std::string(name));
+}
+
+void Config::alter_monitor(const char* name, const char *key, const char *value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter monitor %s %s=%s", name, key, value);
+}
+
+void Config::alter_monitor(const char* name, const char *key, int value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter monitor %s %s=%d", name, key, value);
+}
+
+void Config::alter_monitor(const char* name, const char *key, float value)
+{
+ test_->ssh_maxscale(true, "maxadmin alter monitor %s %s=%f", name, key, value);
+}
+
+void Config::start_monitor(const char *name)
+{
+ test_->ssh_maxscale(true, "maxadmin restart monitor %s", name);
+}
+
+void Config::destroy_monitor(const char *name)
+{
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin destroy monitor %s", name);
+ test_->stop_timeout();
+ created_monitors_.erase(std::string(name));
+}
+
+void Config::restart_monitors()
+{
+ for (auto& a : created_monitors_)
+ {
+ test_->ssh_maxscale(true, "maxadmin shutdown monitor \"%s\"", a.c_str());
+ test_->ssh_maxscale(true, "maxadmin restart monitor \"%s\"", a.c_str());
+ }
+}
+
+void Config::create_listener(Config::Service service)
+{
+ int i = static_cast(service);
+
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin create listener %s %s default %d",
+ services[i].service,
+ services[i].listener,
+ services[i].port);
+ test_->stop_timeout();
+}
+
+void Config::create_ssl_listener(Config::Service service)
+{
+ int i = static_cast(service);
+
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin create listener %s %s default %d default default default "
+ "/home/vagrant/certs/server-key.pem "
+ "/home/vagrant/certs/server-cert.pem "
+ "/home/vagrant/certs/ca.pem ",
+ services[i].service,
+ services[i].listener,
+ services[i].port);
+ test_->stop_timeout();
+}
+
+void Config::destroy_listener(Config::Service service)
+{
+ int i = static_cast(service);
+
+ test_->set_timeout(120);
+ test_->ssh_maxscale(true, "maxadmin destroy listener %s %s",
+ services[i].service, services[i].listener);
+ test_->stop_timeout();
+}
+
+void Config::create_all_listeners()
+{
+ create_listener(SERVICE_RWSPLIT);
+ create_listener(SERVICE_RCONN_SLAVE);
+ create_listener(SERVICE_RCONN_MASTER);
+}
+
+void Config::reset()
+{
+ /** Make sure the servers exist before checking that connectivity is OK */
+ for (int i = 0; i < test_->repl->N; i++)
+ {
+ if (created_servers_.find(i) == created_servers_.end())
+ {
+ create_server(i);
+ add_server(i);
+ }
+ }
+}
+
+bool Config::check_server_count(int expected)
+{
+ bool rval = true;
+
+ if (test_->ssh_maxscale(true, "test \"`maxadmin list servers|grep 'server[0-9]'|wc -l`\" == \"%d\"",
+ expected))
+ {
+ test_->add_result(1, "Number of servers is not %d.", expected);
+ rval = false;
+ }
+
+ return rval;
+}
diff --git a/maxscale-system-test/config_operations.h b/maxscale-system-test/config_operations.h
new file mode 100644
index 000000000..ced7db799
--- /dev/null
+++ b/maxscale-system-test/config_operations.h
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "testconnections.h"
+#include
+#include
+
+class Config
+{
+public:
+ Config(TestConnections* parent);
+ ~Config();
+
+ /**
+ * Service identifiers for listener creation
+ */
+ enum Service
+ {
+ SERVICE_RWSPLIT = 0,
+ SERVICE_RCONN_SLAVE = 1,
+ SERVICE_RCONN_MASTER = 2
+ };
+
+ /**
+ * Add a server to all services and monitors
+ *
+ * @param num Backend number
+ */
+ void add_server(int num);
+
+ /**
+ * Add all created servers to an object
+ *
+ * @param object Object to add servers to
+ */
+ void add_created_servers(const char *object);
+
+ /**
+ * Remove a server
+ *
+ * @param num Backend number
+ */
+ void remove_server(int num);
+
+ /**
+ * Create a new server
+ *
+ * @param num Backend number
+ */
+ void create_server(int num);
+
+ /**
+ * Alter a server
+ *
+ * @param num Backend number
+ * @param key Key to alter
+ * @oaram value Value for @c key, empty string for no value
+ */
+ void alter_server(int num, const char *key, const char *value);
+ void alter_server(int num, const char *key, int value);
+ void alter_server(int num, const char *key, float value);
+
+ /**
+ * Destroy a server
+ * @param num Backend number
+ */
+ void destroy_server(int num);
+
+ /**
+ * Test that server count is at the expected amount
+ * @param expected How many servers are expected to exist
+ * @return True if the number of servers is @c expected
+ */
+ bool check_server_count(int expected);
+
+ /**
+ * Create the monitor
+ * @param type The name of the monitor module to use
+ * @param interval Monitoring interval
+ */
+ void create_monitor(const char *name, const char *module, int interval = 1000);
+
+ /**
+ * Start the created monitor
+ */
+ void start_monitor(const char *name);
+
+ /**
+ * Alter a monitor
+ * @param key Key to alter
+ * @oaram value Value for @c key, empty string for no value
+ */
+ void alter_monitor(const char *name, const char *key, const char *value);
+ void alter_monitor(const char *name, const char *key, int value);
+ void alter_monitor(const char *name, const char *key, float value);
+
+ /**
+ * Destroy the monitor
+ */
+ void destroy_monitor(const char *name);
+
+ /**
+ * Restart all created monitors
+ */
+ void restart_monitors();
+
+ /**
+ * Create a listener
+ *
+ * @param service Service where listener is created
+ */
+ void create_listener(Service service);
+
+
+ /**
+ * Create a listener with SSL enabled
+ *
+ * @param service Service where SSL listener is created
+ */
+ void create_ssl_listener(Service service);
+
+ /**
+ * Destroy a listener
+ *
+ * @param service Service whose listener is destroyed
+ */
+ void destroy_listener(Service service);
+
+ /**
+ * Create all basic listeners
+ */
+ void create_all_listeners();
+
+ /**
+ * Reset the configuration to a standard state
+ */
+ void reset();
+
+private:
+ TestConnections *test_;
+ std::set created_servers_;
+ std::set created_monitors_;
+};
diff --git a/maxscale-system-test/config_test.cpp b/maxscale-system-test/config_test.cpp
new file mode 100644
index 000000000..fca2f88f9
--- /dev/null
+++ b/maxscale-system-test/config_test.cpp
@@ -0,0 +1,48 @@
+/**
+* Bad configuration test
+*/
+
+
+#include
+#include
+#include "testconnections.h"
+
+const char *bad_configs[] =
+{
+ "bug359",
+ "bug495",
+ "bug526",
+ "bug479",
+ "bug493",
+ "bad_ssl",
+ "mxs710_bad_socket",
+ "mxs711_two_ports",
+ "mxs720_line_with_no_equal",
+ "mxs720_wierd_line",
+ "mxs799",
+ NULL
+};
+
+int main(int argc, char **argv)
+{
+ TestConnections *test = new TestConnections(argc, argv);
+ int rval = 0;
+
+ test->stop_maxscale();
+
+ for (int i = 0; bad_configs[i]; i++)
+ {
+ printf("Testing %s...\n", bad_configs[i]);
+ if (test->test_bad_config(bad_configs[i]))
+ {
+ printf("FAILED\n");
+ rval++;
+ }
+ else
+ {
+ printf("SUCCESS\n");
+ }
+ }
+
+ return rval;
+}
diff --git a/maxscale-system-test/connect_to_nonexisting_db.cpp b/maxscale-system-test/connect_to_nonexisting_db.cpp
new file mode 100644
index 000000000..bf7199db8
--- /dev/null
+++ b/maxscale-system-test/connect_to_nonexisting_db.cpp
@@ -0,0 +1,66 @@
+/**
+ * @file connect_to_nonexisting_db.cpp Tries to connect to non existing DB, expects no crash
+ */
+
+// some relations to bug#425
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(30);
+
+ Test->tprintf("Connecting to RWSplit\n");
+ Test->conn_rwsplit = open_conn_no_db(Test->rwsplit_port, Test->maxscale_IP, Test->maxscale_user,
+ Test->maxscale_password, Test->ssl);
+ if (Test->conn_rwsplit == NULL)
+ {
+ Test->add_result(1, "Error connecting to MaxScale\n");
+ delete Test;
+ return 1;
+ }
+ Test->tprintf("Removing 'test' DB\n");
+ execute_query(Test->conn_rwsplit, (char *) "DROP DATABASE IF EXISTS test;");
+ Test->tprintf("Closing connections and waiting 5 seconds\n");
+ Test->close_rwsplit();
+ sleep(5);
+
+ Test->tprintf("Connection to non-existing DB (all routers)\n");
+ Test->connect_maxscale();
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Connecting to RWSplit again to recreate 'test' db\n");
+ Test->conn_rwsplit = open_conn_no_db(Test->rwsplit_port, Test->maxscale_IP, Test->maxscale_user,
+ Test->maxscale_password, Test->ssl);
+ if (Test->conn_rwsplit == NULL)
+ {
+ printf("Error connecting to MaxScale\n");
+ delete Test;
+ return 1;
+ }
+
+ Test->tprintf("Creating and selecting 'test' DB\n");
+ Test->try_query(Test->conn_rwsplit, (char *) "CREATE DATABASE test; USE test");
+ Test->tprintf("Creating 't1' table\n");
+ Test->add_result(create_t1(Test->conn_rwsplit), "Error creation 't1'\n");
+ Test->close_rwsplit();
+
+ Test->tprintf("Reconnectiong\n");
+ Test->add_result(Test->connect_maxscale(), "error connection to Maxscale\n");
+ Test->tprintf("Trying simple operations with t1 \n");
+ Test->try_query(Test->conn_rwsplit, (char *) "INSERT INTO t1 (x1, fl) VALUES(0, 1);");
+ Test->set_timeout(240);
+ Test->add_result(execute_select_query_and_check(Test->conn_rwsplit, (char *) "SELECT * FROM t1;", 1),
+ "Error execution SELECT * FROM t1;\n");
+
+ Test->close_maxscale_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/connection_limit.cpp b/maxscale-system-test/connection_limit.cpp
new file mode 100644
index 000000000..46294ba52
--- /dev/null
+++ b/maxscale-system-test/connection_limit.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file connection_limit.cpp connection_limit check if max_connections parameter works
+ *
+ * - Maxscale.cnf contains max_connections=10 for RWSplit, max_connections=20 for ReadConn master and max_connections=25 for ReadConn slave
+ * - create max num of connections and check tha N+1 connection fails
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int check_max_conn(int router, int max_conn, TestConnections * Test)
+{
+ MYSQL * conn[max_conn + 1];
+
+ int i;
+ for (i = 0; i < max_conn; i++)
+ {
+ conn[i] = open_conn(Test->ports[router], Test->maxscale_IP, Test->maxscale_user, Test->maxscale_password,
+ Test->ssl);
+ if (mysql_errno(conn[i]) != 0)
+ {
+ Test->add_result(1, "Connection %d failed, error is %s\n", i, mysql_error(conn[i]));
+ }
+ }
+ conn[max_conn] = open_conn(Test->ports[router], Test->maxscale_IP, Test->maxscale_user,
+ Test->maxscale_password, Test->ssl);
+ if (mysql_errno(conn[i]) != 1040)
+ {
+ Test->add_result(1, "Max_xonnections reached, but error is not 1040, it is %d %s\n", mysql_errno(conn[i]),
+ mysql_error(conn[i]));
+ }
+ for (i = 0; i < max_conn; i++)
+ {
+ mysql_close(conn[i]);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->tprintf("Trying 11 connections with RWSplit\n");
+ check_max_conn(0, 10, Test);
+ Test->tprintf("Trying 21 connections with Readconn master\n");
+ check_max_conn(1, 20, Test);
+ Test->tprintf("Trying 26 connections with Readconnn slave\n");
+ check_max_conn(2, 25, Test);
+
+ sleep(10);
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/copy_logs.sh b/maxscale-system-test/copy_logs.sh
new file mode 100755
index 000000000..198157bb4
--- /dev/null
+++ b/maxscale-system-test/copy_logs.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# $1 - test name
+# $2 - time mark (in case of periodic logs copying)
+set -x
+
+if [ -z $1 ]; then
+ echo "Test name missing"
+ logs_dir="LOGS/nomane"
+else
+ if [ -z $2 ]; then
+ logs_dir="LOGS/$1"
+ else
+ logs_dir="LOGS/$1/$2"
+ fi
+# rm -rf $logs_dir
+fi
+
+
+echo "Creating log dir in workspace $logs_dir"
+mkdir -p $logs_dir
+if [ $? -ne 0 ]; then
+ echo "Error creating log dir"
+fi
+
+export maxscale_sshkey=$maxscale_keyfile
+echo "log_dir: $logs_dir"
+echo "maxscale_sshkey: $maxscale_sshkey"
+echo "maxscale_IP: $maxscale_IP"
+
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP "rm -rf logs; mkdir logs; $maxscale_access_sudo cp $maxscale_log_dir/*.log logs/; $maxscale_access_sudo cp /tmp/core* logs; $maxscale_access_sudo chmod 777 -R logs"
+ scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:logs/* $logs_dir
+ if [ $? -ne 0 ]; then
+ echo "Error copying Maxscale logs"
+ fi
+ #scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:/tmp/core* $logs_dir
+ #scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:/root/core* $logs_dir
+ scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:$maxscale_cnf $logs_dir
+ chmod a+r $logs_dir/*
+else
+ sudo cp $maxscale_log_dir/*.log $logs_dir
+ sudo cp /tmp/core* $logs_dir
+ sudo cp $maxscale_cnf $logs_dir
+ sudo chmod a+r $logs_dir/*
+fi
+
+if [ -z $logs_publish_dir ] ; then
+ echo "logs are in workspace only"
+else
+ echo "Logs publish dir is $logs_publish_dir"
+ rsync -a --no-o --no-g LOGS $logs_publish_dir
+fi
+
diff --git a/maxscale-system-test/crash_out_of_files.cpp b/maxscale-system-test/crash_out_of_files.cpp
new file mode 100644
index 000000000..62bf8d8ec
--- /dev/null
+++ b/maxscale-system-test/crash_out_of_files.cpp
@@ -0,0 +1,52 @@
+/**
+ * @file crash_ot_of_files.cpp Tries to open to many connections, expect no crash
+ * - set global max_connections = 20
+ * - create load on RWSplit using big number of threads (e.g. 100)
+ * - check that no backends are disconnected with error ""refresh rate limit exceeded"
+ */
+
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "get_com_select_insert.h"
+
+#include "big_load.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(20);
+ Test->repl->execute_query_all_nodes((char *) "set global max_connections = 20;");
+
+ long int i1, i2;
+ long int selects[256];
+ long int inserts[256];
+ long int new_selects[256];
+ long int new_inserts[256];
+
+ Test->tprintf("Start load\n");
+ Test->set_timeout(1200);
+ load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], 100, Test, &i1, &i2, 0, false, false);
+
+ Test->tprintf("restoring nodes\n");
+ Test->set_timeout(60);
+ Test->repl->connect();
+
+ for (int i = 0; i < Test->repl->N; i++)
+ {
+ Test->tprintf("Trying to flush node %d\n", i);
+ Test->add_result(execute_query(Test->repl->nodes[i], (char *) "flush hosts;"), "node %i flush failed\n", i);
+ Test->tprintf("Trying to set max_connections for node %d\n", i);
+ Test->add_result(execute_query(Test->repl->nodes[i], (char *) "set global max_connections = 151;"),
+ "set max_connections failed for node %d\n", i);
+ }
+
+ Test->repl->close_connections();
+ Test->stop_timeout();
+
+ Test->check_log_err((char *) "refresh rate limit exceeded", false);
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/crash_out_of_files_galera.cpp b/maxscale-system-test/crash_out_of_files_galera.cpp
new file mode 100644
index 000000000..ec733270b
--- /dev/null
+++ b/maxscale-system-test/crash_out_of_files_galera.cpp
@@ -0,0 +1,54 @@
+/**
+ * @file crash_ot_of_files_galera.cpp Tries to open to many connections, expect no crash, Galera backend
+ * - set global max_connections = 20
+ * - create load on RWSplit using big number of threads (e.g. 100)
+ * - check that no backends are disconnected with error ""refresh rate limit exceeded"
+ */
+
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "get_com_select_insert.h"
+
+#include "big_load.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+ int q;
+ long int i1, i2;
+
+ long int selects[256];
+ long int inserts[256];
+ long int new_selects[256];
+ long int new_inserts[256];
+
+ Test->galera->execute_query_all_nodes((char *) "set global max_connections = 20;");
+
+ Test->set_timeout(1200);
+ load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], 100, Test, &i1, &i2, 0, true, false);
+ sleep(10);
+ //load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], 1000, Test, &i1, &i2, 0, true, false);
+ //sleep(10);
+ Test->set_timeout(1200);
+ load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], 100, Test, &i1, &i2, 0, true, false);
+
+ Test->set_timeout(20);
+ Test->galera->connect();
+ for (int i = 0; i < Test->galera->N; i++)
+ {
+ execute_query(Test->galera->nodes[i], (char *) "flush hosts;");
+ execute_query(Test->galera->nodes[i], (char *) "set global max_connections = 151;");
+ }
+ Test->galera->close_connections();
+
+ Test->check_log_err((char *) "refresh rate limit exceeded", false);
+
+ Test->galera->execute_query_all_nodes((char *) "set global max_connections = 100;");
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/create.sql b/maxscale-system-test/create.sql
new file mode 100644
index 000000000..11f243118
--- /dev/null
+++ b/maxscale-system-test/create.sql
@@ -0,0 +1,2 @@
+DROP TABLE IF EXISTS test.large_event;
+CREATE TABLE test.large_event(id INT, data LONGBLOB);
diff --git a/maxscale-system-test/create_rds.cpp b/maxscale-system-test/create_rds.cpp
new file mode 100644
index 000000000..2c7b3da63
--- /dev/null
+++ b/maxscale-system-test/create_rds.cpp
@@ -0,0 +1,17 @@
+/**
+ * @file create_rds.cpp Creates RDS Aurora cluster with 4 instances
+ * Creates VPC, subnets, subnets group, internet gateway, routing table, routes, configure security group
+ * In case of any error tries to remove all created stuff
+ */
+
+#include
+#include
+#include "testconnections.h"
+#include
+#include "rds_vpc.h"
+
+int main(int argc, char *argv[])
+{
+ RDS * cluster = new RDS((char *) "auroratest");
+ return cluster->create_rds_db(4);
+}
diff --git a/maxscale-system-test/create_rds.sh b/maxscale-system-test/create_rds.sh
new file mode 100755
index 000000000..a50de508d
--- /dev/null
+++ b/maxscale-system-test/create_rds.sh
@@ -0,0 +1,31 @@
+set -x
+vpc_id=`aws ec2 create-vpc --cidr-block 172.30.0.0/16 | grep "VpcId" | sed 's/\"VpcId\": \"//' | sed 's/\"//' | sed 's/ //g' | sed 's/,//'`
+echo $vpc_id > vpc_id
+subnet_id1=`aws ec2 create-subnet --cidr-block 172.30.0.0/24 --availability-zone eu-west-1b --vpc-id $vpc_id | grep "SubnetId" | sed 's/\"SubnetId\": \"//' | sed 's/\"//' | sed 's/ //g' | sed 's/,//'`
+subnet_id2=`aws ec2 create-subnet --cidr-block 172.30.1.0/24 --availability-zone eu-west-1a --vpc-id $vpc_id | grep "SubnetId" | sed 's/\"SubnetId\": \"//' | sed 's/\"//' | sed 's/ //g' | sed 's/,//'`
+aws rds create-db-subnet-group --db-subnet-group-name maxscaleaurora --db-subnet-group-description maxscale --subnet-ids $subnet_id1 $subnet_id2
+
+gw_id=`aws ec2 create-internet-gateway | grep "InternetGatewayId" | sed 's/\"InternetGatewayId\": \"//' | sed 's/\"//' | sed 's/ //g' | sed 's/,//'`
+aws ec2 attach-internet-gateway --internet-gateway-id $gw_id --vpc-id $vpc_id
+
+aws ec2 modify-vpc-attribute --enable-dns-support --vpc-id $vpc_id
+aws ec2 modify-vpc-attribute --enable-dns-hostnames --vpc-id $vpc_id
+
+aws ec2 modify-subnet-attribute --map-public-ip-on-launch --subnet-id $subnet_id1
+aws ec2 modify-subnet-attribute --map-public-ip-on-launch --subnet-id $subnet_id2
+
+# works only if there is only one route table
+routetab_id=`aws ec2 describe-route-tables | grep "RouteTableId" | sed 's/\"RouteTableId\": \"//' | sed 's/\"//' | sed 's/ //g' | grep "," | sed 's/,//'`
+
+aws ec2 create-route --route-table-id $routetab_id --gateway-id $gw_id --destination-cidr-block 0.0.0.0/0
+
+
+aws rds create-db-cluster --database-name=test --engine=aurora --master-username=skysql --master-user-password=skysqlrds --db-cluster-identifier=auroratest --db-subnet-group-name=maxscaleaurora
+aws rds create-db-instance --db-cluster-identifier=auroratest --engine=aurora --db-instance-class=db.t2.medium --publicly-accessible --db-instance-identifier=node000
+aws rds create-db-instance --db-cluster-identifier=auroratest --engine=aurora --db-instance-class=db.t2.medium --publicly-accessible --db-instance-identifier=node001
+aws rds create-db-instance --db-cluster-identifier=auroratest --engine=aurora --db-instance-class=db.t2.medium --publicly-accessible --db-instance-identifier=node002
+aws rds create-db-instance --db-cluster-identifier=auroratest --engine=aurora --db-instance-class=db.t2.medium --publicly-accessible --db-instance-identifier=node003
+
+
+secgr_id=`aws rds describe-db-instances --db-instance-identifier node000 | grep "VpcSecurityGroupId" | sed 's/\"VpcSecurityGroupId\": \"//' | sed 's/\"//' | sed 's/ //g' | sed 's/,//'`
+aws ec2 authorize-security-group-ingress --group-id $secgr_id --protocol tcp --port 3306 --cidr 0.0.0.0/0
diff --git a/maxscale-system-test/create_user.sh b/maxscale-system-test/create_user.sh
new file mode 100755
index 000000000..d10f9a55e
--- /dev/null
+++ b/maxscale-system-test/create_user.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+mysql -u root --force $1 <& /dev/null
+
+DROP USER '$node_user'@'%';
+CREATE USER '$node_user'@'%' IDENTIFIED BY '$node_password';
+GRANT ALL PRIVILEGES ON *.* TO '$node_user'@'%' WITH GRANT OPTION;
+
+DROP USER 'repl'@'%';
+CREATE USER 'repl'@'%' IDENTIFIED BY 'repl';
+GRANT ALL ON *.* TO 'repl'@'%' WITH GRANT OPTION;
+
+DROP USER 'repl'@'localhost';
+CREATE USER 'repl'@'localhost' IDENTIFIED BY 'repl';
+GRANT ALL ON *.* TO 'repl'@'localhost' WITH GRANT OPTION;
+
+DROP USER 'skysql'@'%';
+CREATE USER 'skysql'@'%' IDENTIFIED BY 'skysql';
+GRANT ALL ON *.* TO 'skysql'@'%' WITH GRANT OPTION;
+
+DROP USER 'skysql'@'localhost';
+CREATE USER 'skysql'@'localhost' IDENTIFIED BY 'skysql';
+GRANT ALL ON *.* TO 'skysql'@'localhost' WITH GRANT OPTION;
+
+DROP USER 'maxskysql'@'%';
+CREATE USER 'maxskysql'@'%' IDENTIFIED BY 'skysql';
+GRANT ALL ON *.* TO 'maxskysql'@'%' WITH GRANT OPTION;
+
+DROP USER 'maxskysql'@'localhost';
+CREATE USER 'maxskysql'@'localhost' IDENTIFIED BY 'skysql';
+GRANT ALL ON *.* TO 'maxskysql'@'localhost' WITH GRANT OPTION;
+
+EOF
diff --git a/maxscale-system-test/create_user_galera.sh b/maxscale-system-test/create_user_galera.sh
new file mode 100755
index 000000000..d82876cff
--- /dev/null
+++ b/maxscale-system-test/create_user_galera.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Wait until the node is ready
+
+for ((i=0;i<100;i++))
+do
+ mysql -ss -u root $1 -e 'show status like "wsrep_ready"' | grep 'ON' && break || sleep 1
+done
+
+# The removal of the anonymous ''@'localhost' user is done in a somewhat crude
+# way. The proper way would be to do a secure installation or drop the users
+# with DROP USER statements.
+
+mysql -u root --force <
+#include
+#include "testconnections.h"
+#include
+#include "rds_vpc.h"
+
+int main(int argc, char *argv[])
+{
+ RDS * cluster = new RDS((char *) "auroratest");
+ cluster->delete_rds_cluster();
+}
diff --git a/maxscale-system-test/demo_prep1.sh b/maxscale-system-test/demo_prep1.sh
new file mode 100755
index 000000000..ae930400f
--- /dev/null
+++ b/maxscale-system-test/demo_prep1.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# The following environment variables must be set:
+#
+# maxscale_sshkey The ssh key to the MaxScale VM
+# maxscale_access_user The username on the VM
+# maxscale_IP The IP address of the MaxScale VM
+# node_000_network IP address of the
+#
+# The Vagrant setup is located in ~/mdbci/my-test-build/. Vagrant is used
+# for SSH access to the machines.
+#
+# The backend server also must have log-slave-updates enabled.
+
+# This is the VM name where the replication-manager is installed
+mrm=galera_000
+
+# Helper functions for ssh and scp
+function do_ssh() {
+ cd ~/mdbci/my-test-build/
+ vagrant ssh $1 -c "$2"
+ cd - > /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+cat < /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+cat < /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+function stop_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql stop"
+}
+
+function start_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql start"
+}
+
+function do_test() {
+ mysql -v -u skysql -pskysql -h $maxscale_IP -P $maxscale_port < /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+function stop_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql stop"
+}
+
+function start_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql start"
+}
+
+function do_test() {
+ mysql -v -u skysql -pskysql -h $maxscale_IP -P $maxscale_port < /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+function stop_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql stop"
+}
+
+function start_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql start"
+}
+
+function do_test() {
+ mysql -v -u skysql -pskysql -h $maxscale_IP -P $maxscale_port < /dev/null
+}
+
+# Helper functions for ssh and scp
+function do_scp() {
+ cd ~/mdbci/my-test-build/
+ local dest=$(vagrant ssh-config $1|grep HostName|sed 's/.*HostName //')
+ scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $2 vagrant@$dest:~
+ cd - > /dev/null
+}
+
+function stop_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql stop"
+}
+
+function start_node() {
+ do_ssh $1 "sudo /etc/init.d/mysql start"
+}
+
+function do_test() {
+ mysql -v -u skysql -pskysql -h $maxscale_IP -P $maxscale_port <
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+char * create_event_size(unsigned long size)
+{
+ char * prefix = (char *) "insert into test.large_event values (1, '";
+ unsigned long prefix_size = strlen(prefix);
+ char * postfix = (char *) "');" ;
+ char * event = (char*)malloc(size + 1);
+ strcpy(event, prefix);
+
+ unsigned long max = size - 55 - 45;
+
+
+ //printf("BLOB data size %lu\n", max);
+
+ for (unsigned long i = 0; i < max; i++)
+ {
+ event[i + prefix_size] = 'a';
+ }
+
+ strcpy((char *) event + max + prefix_size, postfix);
+ return event;
+}
+
+MYSQL * connect_to_serv(TestConnections* Test, bool binlog)
+{
+ MYSQL * conn;
+ if (binlog)
+ {
+ conn = open_conn(Test->repl->port[0], Test->repl->IP[0], Test->repl->user_name, Test->repl->password,
+ Test->ssl);
+ }
+ else
+ {
+ conn = Test->open_rwsplit_connection();
+ }
+ return conn;
+}
+
+void set_max_packet(TestConnections* Test, bool binlog, char * cmd)
+{
+ Test->tprintf("Setting maximum packet size ...");
+ if (binlog)
+ {
+ Test->repl->connect();
+ Test->try_query(Test->repl->nodes[0], cmd);
+ Test->repl->close_connections();
+ }
+ else
+ {
+ Test->connect_maxscale();
+ Test->try_query(Test->conn_rwsplit, cmd);
+ Test->close_maxscale_connections();
+ }
+ Test->tprintf(".. done\n");
+}
+
+void different_packet_size(TestConnections* Test, bool binlog)
+{
+ MYSQL * conn;
+ Test->set_timeout(60);
+ Test->tprintf("Set big max_allowed_packet\n");
+ set_max_packet(Test, binlog, (char *) "set global max_allowed_packet = 200000000;");
+
+ Test->set_timeout(40);
+ Test->tprintf("Create table\n");
+ conn = connect_to_serv(Test, binlog);
+ Test->try_query(conn, (char *)
+ "DROP TABLE IF EXISTS test.large_event;CREATE TABLE test.large_event(id INT, data LONGBLOB);");
+ mysql_close(conn);
+
+ int ranges_num = 3;
+ unsigned int range_min[ranges_num];
+ unsigned int range_max[ranges_num];
+ unsigned int range[ranges_num];
+
+ range[0] = 50;
+ if (Test->smoke)
+ {
+ range[0] = 20;
+ }
+ range_min[0] = 0x0ffffff - range[0];
+ range_max[0] = 0x0ffffff + range[0];
+
+ range[1] = 50;
+ if (Test->smoke)
+ {
+ range[1] = 20;
+ }
+ range_min[1] = 0x0ffffff * 2 - range[1];
+ range_max[1] = 0x0ffffff * 2 + range[1];
+
+ range[2] = 10;
+ if (Test->smoke)
+ {
+ range[2] = 10;
+ }
+ range_min[2] = 0x0ffffff * 3 - range[2];
+ range_max[2] = 0x0ffffff * 3 + range[2];
+
+ char * event;
+ int i;
+ unsigned long j;
+
+ for (i = 0; i < ranges_num; i++)
+ {
+ for (j = range_min[i]; j < range_max[i]; j++)
+ {
+ Test->set_timeout(120);
+ event = create_event_size(j);
+ Test->tprintf("Trying event app. %d bytes\t", j);
+ fflush(stdout);
+ conn = connect_to_serv(Test, binlog);
+ if (execute_query_silent(conn, event) == 0)
+ {
+ Test->tprintf("OK\n");
+ }
+ else
+ {
+ Test->tprintf("FAIL\n");
+ }
+
+ free(event);
+ execute_query_silent(conn, (char *) "DELETE FROM test.large_event WHERE id=1");
+ mysql_close(conn);
+ }
+ }
+
+ Test->set_timeout(40);
+ Test->tprintf("Restoring max_allowed_packet\n");
+ set_max_packet(Test, binlog, (char *) "set global max_allowed_packet = 1048576;");
+}
diff --git a/maxscale-system-test/different_size.h b/maxscale-system-test/different_size.h
new file mode 100644
index 000000000..b28117dd5
--- /dev/null
+++ b/maxscale-system-test/different_size.h
@@ -0,0 +1,38 @@
+#ifndef DIFFERENT_SIZE_H
+#define DIFFERENT_SIZE_H
+
+#include
+#include
+#include "testconnections.h"
+
+/**
+ * @brief create_event_size Creates SQL query to generate even of given size
+ * @param size desired size of event
+ * @return SQL query string
+ */
+char * create_event_size(unsigned long size);
+
+/**
+ * @brief connect_to_serv Open connection
+ * @param Test TestConnections object
+ * @param binlog if true - connects to Master, otherwise - to RWSplit router
+ * @return MYSQL handler
+ */
+MYSQL * connect_to_serv(TestConnections* Test, bool binlog);
+
+/**
+ * @brief set_max_packet Executes 'cmd' on Master of RWSplit ('cmd' should be 'set global max_paxket_size=...')
+ * @param Test TestConnections object
+ * @param binlog if true - connects to Master, otherwise - to RWSplit router
+ * @param cmd command to execute
+ */
+void set_max_packet(TestConnections* Test, bool binlog, char * cmd);
+
+/**
+ * @brief different_packet_size Tries INSERTs with size close to 0x0ffffff * N (N is 1, 2 and 3)
+ * @param Test TestConnections object
+ * @param binlog if true - connects to Master, otherwise - to RWSplit router
+ */
+void different_packet_size(TestConnections* Test, bool binlog);
+
+#endif // DIFFERENT_SIZE_H
diff --git a/maxscale-system-test/different_size_binlog.cpp b/maxscale-system-test/different_size_binlog.cpp
new file mode 100644
index 000000000..b98c1c296
--- /dev/null
+++ b/maxscale-system-test/different_size_binlog.cpp
@@ -0,0 +1,29 @@
+/**
+ * @file different_size_binlog.cpp Tries INSERTs with size close to 0x0ffffff * N
+ * - configure binlog
+ * - executes inserts with size: from 0x0ffffff * N - X up to 0x0ffffff * N - X
+ * (N = 3, X = 50 or 20 for 'soke' test)
+ * - check if Maxscale is still alive
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "different_size.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ Test->set_timeout(300);
+ Test->start_binlog();
+ different_packet_size(Test, true);
+
+ Test->check_maxscale_processes(1);
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/different_size_rwsplit.cpp b/maxscale-system-test/different_size_rwsplit.cpp
new file mode 100644
index 000000000..2079ef793
--- /dev/null
+++ b/maxscale-system-test/different_size_rwsplit.cpp
@@ -0,0 +1,27 @@
+/**
+ * @file different_size_rwsplit.cpp Tries INSERTs with size close to 0x0ffffff * N
+ * - executes inserts with size: from 0x0ffffff * N - X up to 0x0ffffff * N - X
+ * (N = 3, X = 50 or 20 for 'soke' test)
+ * - check if Maxscale is still alive
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "different_size.h"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ different_packet_size(Test, false);
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/encrypted_passwords.cpp b/maxscale-system-test/encrypted_passwords.cpp
new file mode 100644
index 000000000..9accc94d0
--- /dev/null
+++ b/maxscale-system-test/encrypted_passwords.cpp
@@ -0,0 +1,74 @@
+/**
+ * @file encrypted_passwords.cpp - Test maxkeys and maxpasswd interaction with MaxScale
+ * - put encrypted password into maxscale.cnf and try to use Maxscale
+ */
+
+#include
+#include "testconnections.h"
+
+/** Remove old keys and create a new one */
+int create_key(TestConnections *test)
+{
+ int res = 0;
+ test->set_timeout(120);
+ test->tprintf("Creating new encryption keys\n");
+ test->ssh_maxscale(true, "test -f /var/lib/maxscale/.secrets && sudo rm /var/lib/maxscale/.secrets");
+ test->ssh_maxscale(true, "maxkeys");
+ char *result = test->ssh_maxscale_output(false, "sudo test -f /var/lib/maxscale/.secrets && echo SUCCESS");
+
+ if (strncmp(result, "SUCCESS", 7) != 0)
+ {
+ test->tprintf("FAILURE: /var/lib/maxscale/.secrets was not created\n");
+ res = 1;
+ }
+ else
+ {
+ test->ssh_maxscale(true, "sudo chown maxscale:maxscale /var/lib/maxscale/.secrets");
+ }
+
+ free(result);
+ return res;
+}
+
+
+/** Hash a new password and start MaxScale */
+int hash_password(TestConnections *test)
+{
+ test->stop_maxscale();
+ test->stop_timeout();
+
+ int res = 0;
+ test->tprintf("Creating a new encrypted password\n");
+ char *enc_pw = test->ssh_maxscale_output(true, "maxpasswd /var/lib/maxscale/ skysql");
+
+ char *ptr = strchr(enc_pw, '\n');
+ if (ptr)
+ {
+ *ptr = '\0';
+ }
+
+ test->tprintf("Encrypted password is: %s\n", enc_pw);
+ test->ssh_maxscale(true, "sed -i -e 's/passwd[[:space:]]*=[[:space:]]*skysql/passwd=%s/' /etc/maxscale.cnf",
+ enc_pw);
+ free(enc_pw);
+
+ test->tprintf("Starting MaxScale\n");
+ test->start_maxscale();
+
+ test->tprintf("Checking if MaxScale is alive\n");
+ return test->check_maxscale_alive();
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ TestConnections * test = new TestConnections(argc, argv);
+
+ test->global_result += create_key(test);
+ test->global_result += hash_password(test);
+
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/execute_cmd.cpp b/maxscale-system-test/execute_cmd.cpp
new file mode 100644
index 000000000..f465d1d5b
--- /dev/null
+++ b/maxscale-system-test/execute_cmd.cpp
@@ -0,0 +1,44 @@
+#include
+#include
+#include
+#include
+#include
+#include "execute_cmd.h"
+
+using namespace std;
+
+
+int execute_cmd(char * cmd, char ** res)
+{
+ char * result;
+ FILE *output = popen(cmd, "r");
+ if (output == NULL)
+ {
+ printf("Error opening ssh %s\n", strerror(errno));
+ return -1;
+ }
+ char buffer[10240];
+ size_t rsize = sizeof(buffer);
+ result = (char*)calloc(rsize, sizeof(char));
+
+ while (fgets(buffer, sizeof(buffer), output))
+ {
+ result = (char*)realloc(result, sizeof(buffer) + rsize);
+ rsize += sizeof(buffer);
+ strcat(result, buffer);
+ }
+
+ * res = result;
+
+ int return_code = pclose(output);
+ if (WIFEXITED(return_code))
+ {
+ return WEXITSTATUS(return_code);
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+
diff --git a/maxscale-system-test/execute_cmd.h b/maxscale-system-test/execute_cmd.h
new file mode 100644
index 000000000..4562c8017
--- /dev/null
+++ b/maxscale-system-test/execute_cmd.h
@@ -0,0 +1,17 @@
+#ifndef EXECUTE_CMD_H
+#define EXECUTE_CMD_H
+
+#include
+#include
+
+using namespace std;
+
+/**
+ * @brief execute_cmd Execute shell command
+ * @param cmd Command line
+ * @param res Pointer to variable that will contain command console output (stdout)
+ * @return Process exit code
+ */
+int execute_cmd(char * cmd, char ** res);
+
+#endif // EXECUTE_CMD_H
diff --git a/maxscale-system-test/failover_mysqlmon.cpp b/maxscale-system-test/failover_mysqlmon.cpp
new file mode 100644
index 000000000..52f2a9ed1
--- /dev/null
+++ b/maxscale-system-test/failover_mysqlmon.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file failover_mysqlmon.cpp MySQL Monitor Failover Test
+ * - block all nodes, but one
+ * - wait for minitor (monitor_interval)
+ * - check maxadmin output
+ * - check that queries work
+ * - unblock backend nodes
+ * - wait for monitor
+ * - check that we are still using the last node to which we failed over to and that the old nodes are in maintenance mode
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * test = new TestConnections(argc, argv);
+
+ test->tprintf(" Create the test table and insert some data ");
+ test->connect_maxscale();
+ test->try_query(test->conn_rwsplit, "CREATE OR REPLACE TABLE test.t1 (id int)");
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ test->close_maxscale_connections();
+
+ test->tprintf(" Block all but one node ");
+ test->repl->block_node(0);
+ test->repl->block_node(1);
+ test->repl->block_node(2);
+ test->repl->connect();
+ execute_query(test->repl->nodes[3], "STOP SLAVE;RESET SLAVE ALL;");
+
+ test->tprintf(" Wait for the monitor to detect it ");
+ sleep(15);
+
+ test->tprintf(" Connect and insert should work ");
+ char *output = test->ssh_maxscale_output(true, "maxadmin list servers");
+ test->tprintf("%s", output);
+ free(output);
+ test->connect_maxscale();
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ test->close_maxscale_connections();
+
+ test->tprintf(" Unblock nodes ");
+ test->repl->unblock_node(0);
+ test->repl->unblock_node(1);
+ test->repl->unblock_node(2);
+
+ test->tprintf(" Wait for the monitor to detect it ");
+ sleep(15);
+
+ test->tprintf("Check that we are still using the last node to which we failed over "
+ "to and that the old nodes are in maintenance mode");
+
+ test->connect_maxscale();
+ test->try_query(test->conn_rwsplit, "INSERT INTO test.t1 VALUES (1)");
+ char maxscale_id[256], real_id[256];
+ find_field(test->conn_rwsplit, "SELECT @@server_id", "@@server_id", maxscale_id);
+ test->repl->connect();
+ find_field(test->repl->nodes[3], "SELECT @@server_id", "@@server_id", real_id);
+ test->add_result(strcmp(maxscale_id, real_id) != 0,
+ "@@server_id is different: %s != %s", maxscale_id, real_id);
+ test->close_maxscale_connections();
+
+ test->tprintf(" Check that MaxScale is running ");
+ test->check_maxscale_alive();
+
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/false_monitor_state_change.cpp b/maxscale-system-test/false_monitor_state_change.cpp
new file mode 100644
index 000000000..268d90797
--- /dev/null
+++ b/maxscale-system-test/false_monitor_state_change.cpp
@@ -0,0 +1,28 @@
+/**
+ * Test false server state changes when manually clearing master bit
+ */
+
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections test(argc, argv);
+
+ test.tprintf("Block master");
+ test.repl->block_node(0);
+
+ test.tprintf("Wait for monitor to see it");
+ sleep(10);
+
+ test.tprintf("Clear master status");
+ test.ssh_maxscale(true, "maxadmin clear server server1 master");
+ sleep(5);
+
+ test.repl->unblock_node(0);
+ sleep(5);
+
+ test.check_maxscale_alive();
+ test.check_log_err("debug assert", false);
+
+ return test.global_result;
+}
diff --git a/maxscale-system-test/fw/deny1 b/maxscale-system-test/fw/deny1
new file mode 100644
index 000000000..9ef40c5a7
--- /dev/null
+++ b/maxscale-system-test/fw/deny1
@@ -0,0 +1 @@
+select * from mysql.user;
diff --git a/maxscale-system-test/fw/deny10 b/maxscale-system-test/fw/deny10
new file mode 100644
index 000000000..61315519b
--- /dev/null
+++ b/maxscale-system-test/fw/deny10
@@ -0,0 +1,4 @@
+load data infile;
+LOAD data infile;
+LOAD DATA infile;
+LOAD DATA INFILE;
\ No newline at end of file
diff --git a/maxscale-system-test/fw/deny11 b/maxscale-system-test/fw/deny11
new file mode 100644
index 000000000..634099d7d
--- /dev/null
+++ b/maxscale-system-test/fw/deny11
@@ -0,0 +1,3 @@
+SELECT sum(x1) FROM t1;
+SELECT concat(x1,sum(x1)) FROM t1;
+SELECT concat(sum(x1),avg(x1)) FROM t1;
diff --git a/maxscale-system-test/fw/deny2 b/maxscale-system-test/fw/deny2
new file mode 100644
index 000000000..9ef40c5a7
--- /dev/null
+++ b/maxscale-system-test/fw/deny2
@@ -0,0 +1 @@
+select * from mysql.user;
diff --git a/maxscale-system-test/fw/deny3 b/maxscale-system-test/fw/deny3
new file mode 100644
index 000000000..0c5f68f1a
--- /dev/null
+++ b/maxscale-system-test/fw/deny3
@@ -0,0 +1,3 @@
+update t1 set x1=1 where fl=0;
+SELECT x1 FROM t1;
+select t1.x1 as 'something' from t1 as t1 limit 1;
\ No newline at end of file
diff --git a/maxscale-system-test/fw/deny4 b/maxscale-system-test/fw/deny4
new file mode 100644
index 000000000..f3ed2374e
--- /dev/null
+++ b/maxscale-system-test/fw/deny4
@@ -0,0 +1 @@
+SELECT x1 FROM t1;
diff --git a/maxscale-system-test/fw/deny5 b/maxscale-system-test/fw/deny5
new file mode 100644
index 000000000..cd9b04032
--- /dev/null
+++ b/maxscale-system-test/fw/deny5
@@ -0,0 +1,3 @@
+DELETE FROM t1;
+SELECT fl FROM t1;
+UPDATE t1 SET f1=1;
diff --git a/maxscale-system-test/fw/deny6 b/maxscale-system-test/fw/deny6
new file mode 100644
index 000000000..fc543202d
--- /dev/null
+++ b/maxscale-system-test/fw/deny6
@@ -0,0 +1 @@
+SELECT fl FROM t1;
diff --git a/maxscale-system-test/fw/deny7 b/maxscale-system-test/fw/deny7
new file mode 100644
index 000000000..dde2f4ba5
--- /dev/null
+++ b/maxscale-system-test/fw/deny7
@@ -0,0 +1 @@
+UPDATE t1 SET fl=1;
diff --git a/maxscale-system-test/fw/deny8 b/maxscale-system-test/fw/deny8
new file mode 100644
index 000000000..06602f22d
--- /dev/null
+++ b/maxscale-system-test/fw/deny8
@@ -0,0 +1 @@
+SELECT fl FROM t1 WHERE fl = 0 AND x1 = 1 OR x1=2
diff --git a/maxscale-system-test/fw/deny9 b/maxscale-system-test/fw/deny9
new file mode 100644
index 000000000..b3c24053d
--- /dev/null
+++ b/maxscale-system-test/fw/deny9
@@ -0,0 +1,3 @@
+UPDATE t1 SET x1=1 where fl=0;
+SELECT x1 FROM t1;
+SELECT * FROM t1;
diff --git a/maxscale-system-test/fw/pass1 b/maxscale-system-test/fw/pass1
new file mode 100644
index 000000000..9d6c23e48
--- /dev/null
+++ b/maxscale-system-test/fw/pass1
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+select sleep(5);
+delete from t1 where x1 like '_';
+select user from mysql.user;
+SELECT COUNT( * )
\ No newline at end of file
diff --git a/maxscale-system-test/fw/pass10 b/maxscale-system-test/fw/pass10
new file mode 100644
index 000000000..e0ac49d1e
--- /dev/null
+++ b/maxscale-system-test/fw/pass10
@@ -0,0 +1 @@
+SELECT 1;
diff --git a/maxscale-system-test/fw/pass11 b/maxscale-system-test/fw/pass11
new file mode 100644
index 000000000..4b0b05d42
--- /dev/null
+++ b/maxscale-system-test/fw/pass11
@@ -0,0 +1,3 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+SELECT fl FROM t1;
diff --git a/maxscale-system-test/fw/pass2 b/maxscale-system-test/fw/pass2
new file mode 100644
index 000000000..90c732fdf
--- /dev/null
+++ b/maxscale-system-test/fw/pass2
@@ -0,0 +1,2 @@
+select user from mysql.user;
+delete from t1 where x1 like '_';
diff --git a/maxscale-system-test/fw/pass3 b/maxscale-system-test/fw/pass3
new file mode 100644
index 000000000..5c08ac68c
--- /dev/null
+++ b/maxscale-system-test/fw/pass3
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+select sleep(5);
+SELECT fl FROM t1;
+SELECT * FROM t1;
+select t1.fl as 'something' from t1 as t1 limit 1;
diff --git a/maxscale-system-test/fw/pass4 b/maxscale-system-test/fw/pass4
new file mode 100644
index 000000000..8a2562b84
--- /dev/null
+++ b/maxscale-system-test/fw/pass4
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+select sleep(5);
+delete from t1 where x1=0;
+SELECT fl FROM t1;
+SELECT * FROM t1;
diff --git a/maxscale-system-test/fw/pass5 b/maxscale-system-test/fw/pass5
new file mode 100644
index 000000000..c3c9032f5
--- /dev/null
+++ b/maxscale-system-test/fw/pass5
@@ -0,0 +1,5 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 INT, fl INT);
+DELETE FROM t1 WHERE x1=0;
+SELECT fl FROM t1 WHERE x1=0;
+UPDATE t1 SET fl=1 WHERE x1=0;
diff --git a/maxscale-system-test/fw/pass6 b/maxscale-system-test/fw/pass6
new file mode 100644
index 000000000..8bf19d04b
--- /dev/null
+++ b/maxscale-system-test/fw/pass6
@@ -0,0 +1,5 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+delete from t1;
+UPDATE t1 SET fl=1;
+SELECT fl FROM t1 where x1=0;
diff --git a/maxscale-system-test/fw/pass7 b/maxscale-system-test/fw/pass7
new file mode 100644
index 000000000..d0b03895d
--- /dev/null
+++ b/maxscale-system-test/fw/pass7
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+select sleep(5);
+delete from t1;
+SELECT fl FROM t1;
+UPDATE t1 SET fl=1 WHERE x1=0;
diff --git a/maxscale-system-test/fw/pass8 b/maxscale-system-test/fw/pass8
new file mode 100644
index 000000000..8d4eac501
--- /dev/null
+++ b/maxscale-system-test/fw/pass8
@@ -0,0 +1 @@
+SELECT fl FROM t1 WHERE fl = 0 AND x1 = 1 ;
diff --git a/maxscale-system-test/fw/pass9 b/maxscale-system-test/fw/pass9
new file mode 100644
index 000000000..f513744b4
--- /dev/null
+++ b/maxscale-system-test/fw/pass9
@@ -0,0 +1,4 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (x1 int, fl int);
+select sleep(5);
+SELECT fl FROM t1;
diff --git a/maxscale-system-test/fw/rules1 b/maxscale-system-test/fw/rules1
new file mode 100644
index 000000000..ffa8feb0c
--- /dev/null
+++ b/maxscale-system-test/fw/rules1
@@ -0,0 +1,2 @@
+rule test1 deny wildcard
+users %@% match all rules test1
diff --git a/maxscale-system-test/fw/rules10 b/maxscale-system-test/fw/rules10
new file mode 100644
index 000000000..0c3a7c501
--- /dev/null
+++ b/maxscale-system-test/fw/rules10
@@ -0,0 +1,3 @@
+rule regex_rule deny regex '^(?i)load.*data.*infile*'
+rule files2 deny regex '^(?i)load.data.*local.*infile'
+users %@% match any rules regex_rule
diff --git a/maxscale-system-test/fw/rules11 b/maxscale-system-test/fw/rules11
new file mode 100644
index 000000000..52a6c6850
--- /dev/null
+++ b/maxscale-system-test/fw/rules11
@@ -0,0 +1,2 @@
+rule no_func deny functions sum avg
+users %@% match any rules no_func
diff --git a/maxscale-system-test/fw/rules2 b/maxscale-system-test/fw/rules2
new file mode 100644
index 000000000..89609cc60
--- /dev/null
+++ b/maxscale-system-test/fw/rules2
@@ -0,0 +1,2 @@
+rule test2 deny wildcard on_queries select
+users %@% match all rules test2
diff --git a/maxscale-system-test/fw/rules3 b/maxscale-system-test/fw/rules3
new file mode 100644
index 000000000..14cf6e181
--- /dev/null
+++ b/maxscale-system-test/fw/rules3
@@ -0,0 +1,2 @@
+rule test3 deny columns x1
+users %@% match all rules test3
diff --git a/maxscale-system-test/fw/rules4 b/maxscale-system-test/fw/rules4
new file mode 100644
index 000000000..2110ab8f5
--- /dev/null
+++ b/maxscale-system-test/fw/rules4
@@ -0,0 +1,2 @@
+rule test4 deny columns x1 on_queries select
+users %@% match all rules test4
diff --git a/maxscale-system-test/fw/rules5 b/maxscale-system-test/fw/rules5
new file mode 100644
index 000000000..62c322890
--- /dev/null
+++ b/maxscale-system-test/fw/rules5
@@ -0,0 +1,2 @@
+rule test7 deny no_where_clause on_queries select|delete|update
+users %@% match all rules test7
diff --git a/maxscale-system-test/fw/rules6 b/maxscale-system-test/fw/rules6
new file mode 100644
index 000000000..2e18050fe
--- /dev/null
+++ b/maxscale-system-test/fw/rules6
@@ -0,0 +1,2 @@
+rule test9 deny no_where_clause on_queries select
+users %@% match all rules test9
diff --git a/maxscale-system-test/fw/rules7 b/maxscale-system-test/fw/rules7
new file mode 100644
index 000000000..67e3d3f75
--- /dev/null
+++ b/maxscale-system-test/fw/rules7
@@ -0,0 +1,2 @@
+rule test11 deny no_where_clause on_queries update
+users %@% match all rules test11
diff --git a/maxscale-system-test/fw/rules8 b/maxscale-system-test/fw/rules8
new file mode 100644
index 000000000..27445bd78
--- /dev/null
+++ b/maxscale-system-test/fw/rules8
@@ -0,0 +1,2 @@
+rule test12 deny regex ".*FROM.*t1.*WHERE.*OR.*" on_queries select
+users %@% match all rules test12
diff --git a/maxscale-system-test/fw/rules9 b/maxscale-system-test/fw/rules9
new file mode 100644
index 000000000..0fc692b4b
--- /dev/null
+++ b/maxscale-system-test/fw/rules9
@@ -0,0 +1,3 @@
+rule no_wildcard deny wildcard
+rule no_x1 deny columns x1
+users %@% match any rules no_wildcard no_x1
diff --git a/maxscale-system-test/fw/rules_actions b/maxscale-system-test/fw/rules_actions
new file mode 100644
index 000000000..d8c059914
--- /dev/null
+++ b/maxscale-system-test/fw/rules_actions
@@ -0,0 +1,2 @@
+rule r1 deny regex 'select'
+users %@% match any rules r1
diff --git a/maxscale-system-test/fw/rules_at_time b/maxscale-system-test/fw/rules_at_time
new file mode 100644
index 000000000..8a3364ab7
--- /dev/null
+++ b/maxscale-system-test/fw/rules_at_time
@@ -0,0 +1,2 @@
+rule testrule deny no_where_clause at_times ###time### on_queries delete
+users skysql@% match strict_all rules testrule
diff --git a/maxscale-system-test/fw/rules_limit_queries b/maxscale-system-test/fw/rules_limit_queries
new file mode 100644
index 000000000..120cf04d3
--- /dev/null
+++ b/maxscale-system-test/fw/rules_limit_queries
@@ -0,0 +1,2 @@
+rule testrule deny limit_queries 10 7 5
+users skysql@% match strict_all rules testrule
diff --git a/maxscale-system-test/fw/rules_logging b/maxscale-system-test/fw/rules_logging
new file mode 100644
index 000000000..50cb687a0
--- /dev/null
+++ b/maxscale-system-test/fw/rules_logging
@@ -0,0 +1,2 @@
+rule r1 deny regex 'select.*1'
+users %@% match any rules r1
diff --git a/maxscale-system-test/fw/rules_syntax_error b/maxscale-system-test/fw/rules_syntax_error
new file mode 100644
index 000000000..4da14bf38
--- /dev/null
+++ b/maxscale-system-test/fw/rules_syntax_error
@@ -0,0 +1,2 @@
+rule test1 deny wildcard1
+users %@% match all rules test1
diff --git a/maxscale-system-test/fw2/deny1 b/maxscale-system-test/fw2/deny1
new file mode 100644
index 000000000..08df3dd10
--- /dev/null
+++ b/maxscale-system-test/fw2/deny1
@@ -0,0 +1,3 @@
+drop table if exists t1;
+create table t1(id int);
+alter table t1 add column b int;
diff --git a/maxscale-system-test/fw2/deny2 b/maxscale-system-test/fw2/deny2
new file mode 100644
index 000000000..afd589610
--- /dev/null
+++ b/maxscale-system-test/fw2/deny2
@@ -0,0 +1,2 @@
+grant select on *.* to 'skysql'@'localhost';
+revoke select on *.* from 'skysql'@'localhost';
diff --git a/maxscale-system-test/fw2/deny3 b/maxscale-system-test/fw2/deny3
new file mode 100644
index 000000000..dee339a0e
--- /dev/null
+++ b/maxscale-system-test/fw2/deny3
@@ -0,0 +1,2 @@
+use test;
+load data local infile 'test.csv' into table test.t1;
diff --git a/maxscale-system-test/fw2/deny4 b/maxscale-system-test/fw2/deny4
new file mode 100644
index 000000000..f000fa062
--- /dev/null
+++ b/maxscale-system-test/fw2/deny4
@@ -0,0 +1,10 @@
+select sum(1) from test.t1;
+select avg(1) from test.t1;
+select sum(avg(1)) from test.t1;
+select my_function(1) from test.t1;
+select my_function("1") from test.t1;
+select * from test.t1 where 1 = 1;
+select * from test.t1 where 1 >= 1;
+select * from test.t1 where 1 <= 1;
+select * from test.t1 where 1 != 1;
+select * from test.t1 where 1 <> 1;
diff --git a/maxscale-system-test/fw2/pass1 b/maxscale-system-test/fw2/pass1
new file mode 100644
index 000000000..8487c645f
--- /dev/null
+++ b/maxscale-system-test/fw2/pass1
@@ -0,0 +1,3 @@
+select * from mysql.user;
+use mysql;
+set @var = 1;
diff --git a/maxscale-system-test/fw2/pass2 b/maxscale-system-test/fw2/pass2
new file mode 100644
index 000000000..8487c645f
--- /dev/null
+++ b/maxscale-system-test/fw2/pass2
@@ -0,0 +1,3 @@
+select * from mysql.user;
+use mysql;
+set @var = 1;
diff --git a/maxscale-system-test/fw2/pass3 b/maxscale-system-test/fw2/pass3
new file mode 100644
index 000000000..2b68250d3
--- /dev/null
+++ b/maxscale-system-test/fw2/pass3
@@ -0,0 +1,2 @@
+select * from mysql.user;
+set @var = 1;
diff --git a/maxscale-system-test/fw2/pass4 b/maxscale-system-test/fw2/pass4
new file mode 100644
index 000000000..5f597334b
--- /dev/null
+++ b/maxscale-system-test/fw2/pass4
@@ -0,0 +1,5 @@
+create or replace table test.t1(id int);
+create function my_function (arg int) returns int deterministic return arg * arg;
+select "sum(1)";
+select (1);
+select * from(select 1) as a;
diff --git a/maxscale-system-test/fw2/rules1 b/maxscale-system-test/fw2/rules1
new file mode 100644
index 000000000..bb40bd532
--- /dev/null
+++ b/maxscale-system-test/fw2/rules1
@@ -0,0 +1,4 @@
+rule test1 deny regex '.*' on_queries drop
+rule test2 deny regex '.*' on_queries create
+rule test3 deny regex '.*' on_queries alter
+users %@% match any rules test1 test2 test3
diff --git a/maxscale-system-test/fw2/rules2 b/maxscale-system-test/fw2/rules2
new file mode 100644
index 000000000..2d6dc47db
--- /dev/null
+++ b/maxscale-system-test/fw2/rules2
@@ -0,0 +1,3 @@
+rule test1 deny regex '.*' on_queries grant
+rule test2 deny regex '.*' on_queries revoke
+users %@% match any rules test1 test2
diff --git a/maxscale-system-test/fw2/rules3 b/maxscale-system-test/fw2/rules3
new file mode 100644
index 000000000..6672bca49
--- /dev/null
+++ b/maxscale-system-test/fw2/rules3
@@ -0,0 +1,3 @@
+rule test1 deny regex '.*' on_queries use
+rule test2 deny regex '.*' on_queries load
+users %@% match any rules test1 test2
diff --git a/maxscale-system-test/fw2/rules4 b/maxscale-system-test/fw2/rules4
new file mode 100644
index 000000000..f428c7c9a
--- /dev/null
+++ b/maxscale-system-test/fw2/rules4
@@ -0,0 +1,4 @@
+rule test1 deny function sum avg on_queries select
+rule test2 deny function my_function on_queries select
+rule test3 deny function = >= <= != <> on_queries select
+users %@% match any rules test1 test2 test3
diff --git a/maxscale-system-test/fw_copy_rules.cpp b/maxscale-system-test/fw_copy_rules.cpp
new file mode 100644
index 000000000..df5ba3141
--- /dev/null
+++ b/maxscale-system-test/fw_copy_rules.cpp
@@ -0,0 +1,30 @@
+#include "fw_copy_rules.h"
+#include
+
+void copy_rules(TestConnections* Test, const char* rules_name, const char* rules_dir)
+{
+ Test->set_timeout(30);
+ Test->ssh_maxscale(true, "cd %s;"
+ "rm -rf rules;"
+ "mkdir rules;"
+ "chown vagrant:vagrant rules -R",
+ Test->maxscale_access_homedir);
+
+ Test->set_timeout(30);
+
+ std::string src;
+ std::string dest;
+
+ src += rules_dir;
+ src += "/";
+ src += rules_name;
+
+ dest += Test->maxscale_access_homedir;
+ dest += "/rules/rules.txt";
+
+ Test->copy_to_maxscale(src.c_str(), dest.c_str());
+
+ Test->set_timeout(30);
+ Test->ssh_maxscale(true, "chown maxscale:maxscale %s/rules -R", Test->maxscale_access_homedir);
+ Test->stop_timeout();
+}
diff --git a/maxscale-system-test/fw_copy_rules.h b/maxscale-system-test/fw_copy_rules.h
new file mode 100644
index 000000000..fb3437270
--- /dev/null
+++ b/maxscale-system-test/fw_copy_rules.h
@@ -0,0 +1,14 @@
+#ifndef FW_COPY_RULES_H
+#define FW_COPY_RULES_H
+
+#include "testconnections.h"
+
+/**
+ * @brief copy_rules Copy rules file for firewall filter to Maxscale machine
+ * @param Test TestConnections object
+ * @param rules_name Name of file to be copied
+ * @param rules_dir Directory where file is located
+ */
+void copy_rules(TestConnections* Test, const char* rules_name, const char* rules_dir);
+
+#endif // FW_COPY_RULES_H
diff --git a/maxscale-system-test/fwf.cpp b/maxscale-system-test/fwf.cpp
new file mode 100644
index 000000000..2765ee86b
--- /dev/null
+++ b/maxscale-system-test/fwf.cpp
@@ -0,0 +1,213 @@
+/**
+ * @file fwf - Firewall filter test (also regression test for MXS-683 "qc_mysqlembedded reports as-name instead of original-name")
+ * - setup Firewall filter to use rules from rule file fw/ruleXX, where XX - number of sub-test
+ * - execute queries for fw/passXX file, expect OK
+ * - execute queries from fw/denyXX, expect Access Denied error (mysql_error 1141)
+ * - repeat for all XX
+ * - setup Firewall filter to block queries next 2 minutes using 'at_time' statement (see template fw/rules_at_time)
+ * - start sending queries, expect Access Denied now and OK after two mintes
+ * - setup Firewall filter to limit a number of queries during certain time
+ * - start sending queries as fast as possible, expect OK for N first quries and Access Denied for next queries
+ * - wait, start sending queries again, but only one query per second, expect OK
+ * - try to load rules with syntax error, expect failure for all sessions and queries
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+#include "fw_copy_rules.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections * Test = new TestConnections(argc, argv);
+ int local_result;
+ char str[4096];
+ char sql[4096];
+ char pass_file[4096];
+ char deny_file[4096];
+ char rules_dir[4096];
+ FILE* file;
+
+ sprintf(rules_dir, "%s/fw/", test_dir);
+ int N = 10;
+ int i;
+
+ for (i = 1; i < N + 1; i++)
+ {
+ Test->set_timeout(180);
+ local_result = 0;
+
+ Test->stop_maxscale();
+
+ sprintf(str, "rules%d", i);
+ copy_rules(Test, str, rules_dir);
+
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+ sprintf(pass_file, "%s/fw/pass%d", test_dir, i);
+ sprintf(deny_file, "%s/fw/deny%d", test_dir, i);
+ Test->tprintf("Pass file: %s\n", pass_file);
+ Test->tprintf("Deny file: %s\n", deny_file);
+
+ file = fopen(pass_file, "r");
+ if (file != NULL)
+ {
+ Test->tprintf("********** Trying queries that should be OK ********** \n");
+ while (fgets(sql, sizeof(sql), file))
+ {
+ if (strlen(sql) > 1)
+ {
+ Test->tprintf("%s", sql);
+ local_result += execute_query(Test->conn_rwsplit, sql);
+ }
+ }
+ fclose(file);
+ }
+ else
+ {
+ Test->add_result(1, "Error opening query file\n");
+ }
+
+ file = fopen(deny_file, "r");
+ if (file != NULL)
+ {
+ Test->tprintf("********** Trying queries that should FAIL ********** \n");
+ while (fgets(sql, sizeof(sql), file))
+ {
+ Test->set_timeout(180);
+ if (strlen(sql) > 1)
+ {
+ Test->tprintf("%s", sql);
+ execute_query(Test->conn_rwsplit, sql);
+ if (mysql_errno(Test->conn_rwsplit) != 1141)
+ {
+ Test->tprintf("Query succeded, but fail expected, errono is %d\n", mysql_errno(Test->conn_rwsplit));
+ local_result++;
+ }
+ }
+ }
+ fclose(file);
+ }
+ else
+ {
+ Test->add_result(1, "Error opening query file\n");
+ }
+ if (local_result != 0)
+ {
+ Test->add_result(1, "********** rules%d test FAILED\n", i);
+ }
+ else
+ {
+ Test->tprintf("********** rules%d test PASSED\n", i);
+ }
+
+ mysql_close(Test->conn_rwsplit);
+ }
+
+ Test->set_timeout(180);
+ Test->stop_maxscale();
+
+ // Test for at_times clause
+ Test->tprintf("Trying at_times clause\n");
+ copy_rules(Test, (char *) "rules_at_time", rules_dir);
+
+ Test->tprintf("DELETE quries without WHERE clause will be blocked during next 2 minutes\n");
+ Test->tprintf("Put time to rules.txt: %s\n", str);
+ Test->ssh_maxscale(false, "start_time=`date +%%T`; stop_time=` date --date "
+ "\"now +2 mins\" +%%T`; %s sed -i \"s/###time###/$start_time-$stop_time/\" %s/rules/rules.txt",
+ Test->maxscale_access_sudo, Test->maxscale_access_homedir);
+
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+ Test->tprintf("Trying 'DELETE FROM t1' and expecting FAILURE\n");
+ execute_query(Test->conn_rwsplit, "DELETE FROM t1");
+ if (mysql_errno(Test->conn_rwsplit) != 1141)
+ {
+ Test->add_result(1, "Query succeded, but fail expected, errono is %d\n", mysql_errno(Test->conn_rwsplit));
+ }
+ Test->tprintf("Waiting 3 minutes and trying 'DELETE FROM t1', expecting OK\n");
+ Test->stop_timeout();
+ sleep(180);
+ Test->set_timeout(180);
+ Test->try_query(Test->conn_rwsplit, "DELETE FROM t1");
+
+ mysql_close(Test->conn_rwsplit);
+ Test->stop_maxscale();
+
+ Test->tprintf("Trying limit_queries clause\n");
+ Test->tprintf("Copying rules to Maxscale machine: %s\n", str);
+ copy_rules(Test, (char *) "rules_limit_queries", rules_dir);
+
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+ printf("Trying 10 quries as fast as possible\n");
+ for (i = 0; i < 10; i++)
+ {
+ Test->add_result(execute_query(Test->conn_rwsplit, "SELECT * FROM t1"), "%d -query failed\n", i);
+ }
+
+ Test->tprintf("Expecting failures during next 5 seconds\n");
+
+ time_t start_time_clock = time(NULL);
+ timeval t1, t2;
+ double elapsedTime;
+ gettimeofday(&t1, NULL);
+
+
+ do
+ {
+ gettimeofday(&t2, NULL);
+ elapsedTime = (t2.tv_sec - t1.tv_sec);
+ elapsedTime += (double) (t2.tv_usec - t1.tv_usec) / 1000000.0;
+ }
+ while ((execute_query_silent(Test->conn_rwsplit, "SELECT * FROM t1") != 0) && (elapsedTime < 10));
+
+ Test->tprintf("Quries were blocked during %f (using clock_gettime())\n", elapsedTime);
+ Test->tprintf("Quries were blocked during %lu (using time())\n", time(NULL) - start_time_clock);
+ if ((elapsedTime > 6) or (elapsedTime < 4))
+ {
+ Test->add_result(1, "Queries were blocked during wrong time\n");
+ }
+
+ Test->set_timeout(180);
+ printf("Trying 20 quries, 1 query / second\n");
+ for (i = 0; i < 20; i++)
+ {
+ sleep(1);
+ Test->add_result(execute_query(Test->conn_rwsplit, "SELECT * FROM t1"), "query failed\n");
+ Test->tprintf("%d ", i);
+ }
+ Test->tprintf("\n");
+ Test->set_timeout(180);
+ Test->tprintf("Stopping Maxscale\n");
+ Test->stop_maxscale();
+
+ Test->tprintf("Trying rules with syntax error\n");
+ Test->tprintf("Copying rules to Maxscale machine: %s\n", str);
+ copy_rules(Test, (char *) "rules_syntax_error", rules_dir);
+
+ Test->tprintf("Starting Maxscale\n");
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+ Test->tprintf("Trying to connectt to Maxscale when 'rules' has syntax error, expecting failures\n");
+ if (execute_query(Test->conn_rwsplit, "SELECT * FROM t1") == 0)
+ {
+ Test->add_result(1, "Rule has syntax error, but query OK\n");
+ }
+
+ Test->check_maxscale_processes(0);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
diff --git a/maxscale-system-test/fwf2.cpp b/maxscale-system-test/fwf2.cpp
new file mode 100644
index 000000000..fdda24663
--- /dev/null
+++ b/maxscale-system-test/fwf2.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file fwf - Firewall filtyer test
+ * - setup Firewall filter to use rules from rule file fw/ruleXX, where XX - number of sub-test
+ * - execute queries for fw/passXX file, expect OK
+ * - execute queries from fw/denyXX, expect Access Denied error (mysql_error 1141)
+ * - repeat for all XX
+ * - setup Firewall filter to block queries next 2 minutes using 'at_time' statement (see template fw/rules_at_time)
+ * - start sending queries, expect Access Denied now and OK after two mintes
+ * - setup Firewall filter to limit a number of queries during certain time
+ * - start sending queries as fast as possible, expect OK for N first quries and Access Denied for next queries
+ * - wait, start sending queries again, but only one query per second, expect OK
+ * - try to load rules with syntax error, expect failure for all sessions and queries
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+#include "fw_copy_rules.h"
+
+int read_and_execute_queries(TestConnections *Test, const char* filename, int expected)
+{
+ FILE *file = fopen(filename, "r");
+ int local_result = 0;
+ if (file != NULL)
+ {
+ char sql[4096];
+ while (fgets(sql, sizeof(sql), file))
+ {
+ Test->set_timeout(60);
+ if (strlen(sql) > 1)
+ {
+ Test->tprintf("%s", sql);
+ if (execute_query(Test->conn_rwsplit, sql) != expected &&
+ (expected == 1 || mysql_errno(Test->conn_rwsplit) == 1141))
+ {
+ Test->tprintf("Query %s, but %s expected, MySQL error: %d, %s\n",
+ expected ? "succeeded" : "failed",
+ expected ? "failure" : "success",
+ mysql_errno(Test->conn_rwsplit), mysql_error(Test->conn_rwsplit));
+ local_result++;
+ }
+ }
+ }
+ fclose(file);
+ }
+ else
+ {
+ Test->add_result(1, "Error opening file '%s'\n", filename);
+ }
+ return local_result;
+}
+
+int main(int argc, char *argv[])
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections * Test = new TestConnections(argc, argv);
+ int local_result;
+ char str[4096];
+ char pass_file[4096];
+ char deny_file[4096];
+ char rules_dir[4096];
+ FILE* file;
+
+ sprintf(rules_dir, "%s/fw2/", test_dir);
+ int N = 4;
+ int i;
+
+ for (i = 1; i < N + 1; i++)
+ {
+ Test->set_timeout(60);
+ local_result = 0;
+
+ Test->stop_maxscale();
+
+ sprintf(str, "rules%d", i);
+ copy_rules(Test, str, rules_dir);
+
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+ sprintf(pass_file, "%s/fw2/pass%d", test_dir, i);
+ sprintf(deny_file, "%s/fw2/deny%d", test_dir, i);
+
+ Test->tprintf("********** Trying queries that should be OK ********** \n");
+ local_result += read_and_execute_queries(Test, pass_file, 0);
+
+ Test->tprintf("********** Trying queries that should FAIL ********** \n");
+ local_result += read_and_execute_queries(Test, deny_file, 1);
+
+ Test->add_result(local_result, "********** rules%d test FAILED\n", i);
+ mysql_close(Test->conn_rwsplit);
+ }
+
+ Test->check_maxscale_processes(1);
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
diff --git a/maxscale-system-test/fwf_actions.cpp b/maxscale-system-test/fwf_actions.cpp
new file mode 100644
index 000000000..78652ae1c
--- /dev/null
+++ b/maxscale-system-test/fwf_actions.cpp
@@ -0,0 +1,75 @@
+/**
+ * Firewall filter match action test
+ *
+ * Check if the blacklisting, whitelisting and ignoring funcionality of
+ * the dbfwfilter works. This test executes a matching and a non-matching query
+ * to three services configured in block, allow and ignore modes.
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "fw_copy_rules.h"
+
+int main(int argc, char** argv)
+{
+ TestConnections::skip_maxscale_start(true);
+ char rules_dir[4096];
+
+ TestConnections *test = new TestConnections(argc, argv);
+ test->stop_timeout();
+
+ test->tprintf("Creating rules\n");
+ test->stop_maxscale();
+
+ sprintf(rules_dir, "%s/fw/", test_dir);
+ copy_rules(test, (char*) "rules_actions", rules_dir);
+
+ test->set_timeout(60);
+ test->start_maxscale();
+
+ test->set_timeout(30);
+ test->connect_maxscale();
+
+ /** Test blacklisting functionality */
+ test->tprintf("Trying matching query to blacklisted RWSplit, expecting failure\n");
+ test->set_timeout(30);
+ test->add_result(!execute_query_silent(test->conn_rwsplit, "select 1"),
+ "Matching query to blacklist service should fail.\n");
+ test->tprintf("Trying non-matching query to blacklisted RWSplit, expecting success\n");
+ test->set_timeout(30);
+ test->add_result(execute_query_silent(test->conn_rwsplit, "show status"),
+ "Non-matching query to blacklist service should succeed.\n");
+
+ /** Test whitelisting functionality */
+ test->tprintf("Trying matching query to whitelisted Conn slave, expecting success\n");
+ test->set_timeout(30);
+ test->add_result(execute_query_silent(test->conn_slave, "select 1"),
+ "Query to whitelist service should succeed.\n");
+ test->tprintf("Trying non-matching query to whitelisted Conn slave, expecting failure\n");
+ test->set_timeout(30);
+ test->add_result(!execute_query_silent(test->conn_slave, "show status"),
+ "Non-matching query to blacklist service should fail.\n");
+
+ /** Testing NO OP mode */
+ test->tprintf("Trying matching query to ignoring Conn master, expecting success\n");
+ test->set_timeout(30);
+ test->add_result(execute_query_silent(test->conn_master, "select 1"),
+ "Query to ignoring service should succeed.\n");
+ test->tprintf("Trying non-matching query to ignoring Conn master, expecting success\n");
+ test->set_timeout(30);
+ test->add_result(execute_query_silent(test->conn_master, "show status"),
+ "Non-matching query to ignoring service should succeed.\n");
+
+ test->stop_timeout();
+ test->tprintf("Checking if MaxScale is alive\n");
+ test->check_maxscale_processes(1);
+ test->stop_maxscale();
+ sleep(10);
+ test->tprintf("Checking if MaxScale was succesfully terminated\n");
+ test->check_maxscale_processes(0);
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/fwf_com_ping.cpp b/maxscale-system-test/fwf_com_ping.cpp
new file mode 100644
index 000000000..5bbaa1d26
--- /dev/null
+++ b/maxscale-system-test/fwf_com_ping.cpp
@@ -0,0 +1,33 @@
+/**
+ * MXS-1111: Dbfwfilter COM_PING test
+ *
+ * Check that COM_PING is allowed with `action=allow`
+ */
+
+#include "testconnections.h"
+
+const char *rules = "rule test1 deny regex '.*'\n"
+ "users %@% match any rules test1\n";
+
+int main(int argc, char** argv)
+{
+ /** Create the rule file */
+ FILE *file = fopen("rules.txt", "w");
+ fwrite(rules, 1, strlen(rules), file);
+ fclose(file);
+
+ TestConnections::skip_maxscale_start(true);
+ TestConnections test(argc, argv);
+
+ test.ssh_maxscale(true, "mkdir -p /home/vagrant/rules/; chown -R vagrant:vagrant /home/vagrant/rules/");
+ test.copy_to_maxscale((char*)"rules.txt", (char*)"~/rules/rules.txt");
+ test.ssh_maxscale(true, "chmod a+r /home/vagrant/rules/rules.txt;");
+
+ test.restart_maxscale();
+ test.connect_maxscale();
+ test.tprintf("Pinging MaxScale, expecting success");
+ test.add_result(mysql_ping(test.conn_rwsplit), "Ping should not fail: %s", mysql_error(test.conn_rwsplit));
+ test.close_maxscale_connections();
+
+ return test.global_result;
+}
diff --git a/maxscale-system-test/fwf_duplicate_rules.cpp b/maxscale-system-test/fwf_duplicate_rules.cpp
new file mode 100644
index 000000000..0b9d556f7
--- /dev/null
+++ b/maxscale-system-test/fwf_duplicate_rules.cpp
@@ -0,0 +1,36 @@
+/**
+ * Dbfwfilter duplicate rule test
+ *
+ * Check if duplicate rules are detected.
+ */
+
+#include "testconnections.h"
+
+const char *rules = "rule test1 deny no_where_clause\n"
+ "rule test1 deny columns a b c\n"
+ "users %@% match any rules test1\n";
+
+int main(int argc, char** argv)
+{
+ /** Create the rule file */
+ FILE *file = fopen("rules.txt", "w");
+ fwrite(rules, 1, strlen(rules), file);
+ fclose(file);
+
+ TestConnections::skip_maxscale_start(true);
+ TestConnections test(argc, argv);
+
+ test.ssh_maxscale(true, "mkdir -p /home/vagrant/rules/; chown -R vagrant:vagrant /home/vagrant/rules/");
+ test.copy_to_maxscale((char*)"rules.txt", (char*)"~/rules/rules.txt");
+ test.ssh_maxscale(true, "chmod a+r /home/vagrant/rules/rules.txt;");
+
+ int rc = 0;
+
+ if (test.restart_maxscale() == 0)
+ {
+ test.tprintf("Restarting MaxScale succeeded when it should've failed!");
+ rc = 1;
+ }
+
+ return rc;
+}
diff --git a/maxscale-system-test/fwf_logging.cpp b/maxscale-system-test/fwf_logging.cpp
new file mode 100644
index 000000000..2095a2180
--- /dev/null
+++ b/maxscale-system-test/fwf_logging.cpp
@@ -0,0 +1,56 @@
+/**
+ * Firewall filter logging test
+ *
+ * Check if the log_match and log_no_match parameters work
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "fw_copy_rules.h"
+
+int main(int argc, char** argv)
+{
+ TestConnections::skip_maxscale_start(true);
+ char rules_dir[4096];
+
+ TestConnections *test = new TestConnections(argc, argv);
+ test->stop_timeout();
+
+ sprintf(rules_dir, "%s/fw/", test_dir);
+
+ test->tprintf("Creating rules\n");
+ test->stop_maxscale();
+ copy_rules(test, (char*) "rules_logging", rules_dir);
+
+ test->start_maxscale();
+ test->set_timeout(20);
+ test->connect_maxscale();
+
+ test->tprintf("trying first: 'select 1'\n");
+ test->set_timeout(20);
+ test->add_result(execute_query_silent(test->conn_slave, "select 1"), "First query should succeed\n");
+
+ test->tprintf("trying second: 'select 2'\n");
+ test->set_timeout(20);
+ test->add_result(execute_query_silent(test->conn_slave, "select 2"), "Second query should succeed\n");
+
+ /** Check that MaxScale is alive */
+ test->stop_timeout();
+ test->check_maxscale_processes(1);
+
+ /** Check that MaxScale was terminated successfully */
+ test->stop_maxscale();
+ sleep(10);
+ test->check_maxscale_processes(0);
+
+ /** Check that the logs contains entries for both matching and
+ * non-matching queries */
+ test->check_log_err("matched by", true);
+ test->check_log_err("was not matched", true);
+
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/fwf_prepared_stmt.cpp b/maxscale-system-test/fwf_prepared_stmt.cpp
new file mode 100644
index 000000000..11b884a38
--- /dev/null
+++ b/maxscale-system-test/fwf_prepared_stmt.cpp
@@ -0,0 +1,53 @@
+/**
+ * Dbfwfilter prepared statement test
+ *
+ * Checks that both text protocol and binary protocol prepared statements are
+ * properly handled.
+ */
+
+#include "testconnections.h"
+
+int main(int argc, char** argv)
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections test(argc, argv);
+ test.ssh_maxscale(true, "mkdir -p /home/vagrant/rules/;"
+ "echo 'rule test1 deny columns c on_queries select' > /home/vagrant/rules/rules.txt;"
+ "echo 'users %%@%% match any rules test1' >> /home/vagrant/rules/rules.txt;"
+ "chmod a+r /home/vagrant/rules/rules.txt;");
+
+ test.add_result(test.restart_maxscale(), "Restarting MaxScale failed");
+
+ test.connect_maxscale();
+ execute_query_silent(test.conn_rwsplit, "DROP TABLE test.t1");
+
+ test.try_query(test.conn_rwsplit, "CREATE TABLE test.t1(a INT, b INT, c INT)");
+ test.try_query(test.conn_rwsplit, "INSERT INTO test.t1 VALUES (1, 1, 1)");
+
+ test.add_result(execute_query(test.conn_rwsplit, "PREPARE my_ps FROM 'SELECT a, b FROM test.t1'"),
+ "Text protocol preparation should succeed");
+ test.add_result(execute_query(test.conn_rwsplit, "EXECUTE my_ps"),
+ "Text protocol execution should succeed");
+
+ test.add_result(execute_query(test.conn_rwsplit, "PREPARE my_ps2 FROM 'SELECT c FROM test.t1'") == 0,
+ "Text protocol preparation should fail");
+ test.add_result(execute_query(test.conn_rwsplit, "EXECUTE my_ps2") == 0,
+ "Text protocol execution should fail");
+
+ MYSQL_STMT* stmt = mysql_stmt_init(test.conn_rwsplit);
+ const char *query = "SELECT a, b FROM test.t1";
+
+ test.add_result(mysql_stmt_prepare(stmt, query, strlen(query)), "Binary protocol preparation should succeed");
+ test.add_result(mysql_stmt_execute(stmt), "Binary protocol execution should succeed");
+ mysql_stmt_close(stmt);
+
+ stmt = mysql_stmt_init(test.conn_rwsplit);
+ query = "SELECT c FROM test.t1";
+
+ test.add_result(!mysql_stmt_prepare(stmt, query, strlen(query)), "Binary protocol preparation should fail");
+ mysql_stmt_close(stmt);
+
+ test.try_query(test.conn_rwsplit, "DROP TABLE test.t1");
+
+ return test.global_result;
+}
diff --git a/maxscale-system-test/fwf_reload.cpp b/maxscale-system-test/fwf_reload.cpp
new file mode 100644
index 000000000..a820caf59
--- /dev/null
+++ b/maxscale-system-test/fwf_reload.cpp
@@ -0,0 +1,112 @@
+/**
+ * @file fwf_reload - Same as fwf but with reloading of rules
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "maxadmin_operations.h"
+#include "sql_t1.h"
+#include "fw_copy_rules.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections *Test = new TestConnections(argc, argv);
+ char sql[4096];
+ char pass_file[4096];
+ char deny_file[4096];
+ char rules_dir[4096];
+
+ sprintf(rules_dir, "%s/fw/", test_dir);
+ int N = 10;
+ int i;
+
+ Test->stop_maxscale();
+ char first_rule[] = "rules1";
+ copy_rules(Test, first_rule, rules_dir);
+ Test->start_maxscale();
+ Test->connect_rwsplit();
+
+
+ for (i = 1; i <= N; i++)
+ {
+ char str[1024];
+ sprintf(str, "rules%d", i);
+ Test->set_timeout(180);
+ copy_rules(Test, str, rules_dir);
+ Test->ssh_maxscale(true, "maxadmin call command dbfwfilter rules/reload Database-Firewall");
+
+ int local_result = 0;
+ sprintf(pass_file, "%s/fw/pass%d", test_dir, i);
+ FILE *file = fopen(pass_file, "r");
+
+ if (file)
+ {
+ Test->tprintf("********** Trying queries that should be OK ********** \n");
+
+ while (!feof(file))
+ {
+ Test->set_timeout(180);
+
+ if (execute_query_from_file(Test->conn_rwsplit, file) == 1)
+ {
+ Test->tprintf("Query should succeed: %s\n", sql);
+ local_result++;
+ }
+ }
+ fclose(file);
+ }
+ else
+ {
+ Test->add_result(1, "Error opening file '%s': %d, %s\n", pass_file, errno, strerror(errno));
+ break;
+ }
+
+ sprintf(deny_file, "%s/fw/deny%d", test_dir, i);
+ file = fopen(deny_file, "r");
+
+ if (file)
+ {
+ Test->tprintf("********** Trying queries that should FAIL ********** \n");
+
+ while (!feof(file))
+ {
+ Test->set_timeout(180);
+
+ int rc = execute_query_from_file(Test->conn_rwsplit, file);
+
+ if (rc != -1 && (rc == 0 ||
+ mysql_errno(Test->conn_rwsplit) != 1141))
+ {
+ Test->tprintf("Query should fail: %s\n", sql);
+ local_result++;
+ }
+ }
+
+ fclose(file);
+ }
+ else
+ {
+ Test->add_result(1, "Error opening file '%s': %d, %s\n", deny_file, errno, strerror(errno));
+ break;
+ }
+
+ Test->add_result(local_result, "********** rules%d test FAILED\n", i);
+ }
+
+ Test->tprintf("Trying rules with syntax error\n");
+ copy_rules(Test, (char *) "rules_syntax_error", rules_dir);
+
+ char *output = Test->ssh_maxscale_output(true,
+ "maxadmin call command dbfwfilter rules/reload \"Database Firewall\"");
+ Test->add_result(strcasestr(output, "Failed") == NULL, "Reloading rules should fail with syntax errors");
+
+ Test->check_maxscale_processes(1);
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+
diff --git a/maxscale-system-test/fwf_syntax.cpp b/maxscale-system-test/fwf_syntax.cpp
new file mode 100644
index 000000000..be1aa19c0
--- /dev/null
+++ b/maxscale-system-test/fwf_syntax.cpp
@@ -0,0 +1,85 @@
+/**
+ * Firewall filter syntax error test
+ *
+ * Generate various syntax errors and check if they are detected.
+ * With every rule file in this test, MaxScale should not start and the error
+ * log should contain a message about a syntax error.
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+#include "fw_copy_rules.h"
+
+const char *temp_rules = "rules_tmp.txt";
+
+const char *users_ok[] =
+{
+ "users %@% match any rules testrule",
+ NULL
+};
+
+const char *rules_failure[] =
+{
+ "rule testrule deny nothing",
+ "rule testrule deny regex",
+ "rule testrule deny columns",
+ "rule testrule deny limit_queries",
+ "rule testrule deny no-where-clause",
+ "rule testrule deny wildcard wildcard",
+ "rule testrule deny wildcard rule testrule deny no_where_clause",
+ "rule testrule allow anything",
+ "rule testrule block",
+ "rule deny wildcard",
+ "testrule deny wildcard",
+ "rule testrule deny wildcard on_queries select | not_select",
+ "rule testrule deny wildcard on_queries select|not_select",
+ "rule testrule deny wildcard on_queries select |",
+ "rule testrule deny wildcard on_queries select|",
+ "rule ᐫᐬᐭᐮᐯᐰᐱ deny wildcard on_queries select|",
+ NULL
+};
+
+void truncate_maxscale_logs(TestConnections *test)
+{
+ test->ssh_maxscale(true, "truncate -s 0 /var/log/maxscale/*");
+}
+
+void add_rule(const char *rule)
+{
+ FILE *file = fopen(temp_rules, "a");
+ fprintf(file, "%s\n", rule);
+ fclose(file);
+}
+
+int main(int argc, char** argv)
+{
+ TestConnections::skip_maxscale_start(true);
+ TestConnections *test = new TestConnections(argc, argv);
+ test->stop_timeout();
+ test->stop_maxscale();
+
+ for (int i = 0; rules_failure[i]; i++)
+ {
+ /** Create rule file with syntax error */
+ truncate(temp_rules, 0);
+ add_rule(rules_failure[i]);
+ add_rule(users_ok[0]);
+ copy_rules(test, (char*)temp_rules, (char*)test_dir);
+
+ test->tprintf("Testing rule: %s\n", rules_failure[i]);
+ test->start_maxscale();
+ sleep(3);
+
+ /** Check that MaxScale did not start and that the log contains
+ * a message about the syntax error. */
+ test->check_maxscale_processes(0);
+ test->check_log_err("syntax error", true);
+ truncate_maxscale_logs(test);
+ }
+
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/galera_priority.cpp b/maxscale-system-test/galera_priority.cpp
new file mode 100644
index 000000000..538bf61e2
--- /dev/null
+++ b/maxscale-system-test/galera_priority.cpp
@@ -0,0 +1,159 @@
+/**
+ * @file galera_priority.cpp Galera node priority test
+ *
+ * Node priorities are configured in the following order:
+ * node3 > node1 > node4 > node2
+ *
+ * The test executes a SELECT @@server_id to get the server id of each
+ * node. The same query is executed in a transaction through MaxScale
+ * and the server id should match the expected output depending on which
+ * of the nodes are available. The simple test blocks nodes from highest priority
+ * to lowest priority.
+ */
+
+
+#include
+#include "testconnections.h"
+
+using namespace std;
+
+int check_server_id(TestConnections* test, char *node_id)
+{
+ char str[1024];
+ int rval = 0;
+ if (execute_query(test->conn_rwsplit, "BEGIN") ||
+ find_field(test->conn_rwsplit, "SELECT @@server_id", "@@server_id", str) ||
+ execute_query(test->conn_rwsplit, "COMMIT"))
+ {
+ test->tprintf("Failed to compare @@server_id.\n");
+ rval = 1;
+ }
+ else if (strcmp(node_id, str))
+ {
+ test->tprintf("@@server_id is %s instead of %s\n", str, node_id);
+ rval = 1;
+ }
+ return rval;
+}
+
+int simple_failover(TestConnections* test)
+{
+ test->galera->connect();
+ int rval = 0;
+ bool blocked = false;
+ char server_id[test->galera->N][1024];
+
+ /** Get server_id for each node */
+ for (int i = 0; i < test->galera->N; i++)
+ {
+ sprintf(server_id[i], "%d", test->galera->get_server_id(i));
+ }
+
+ do
+ {
+ /** Node 3 should be master */
+ test->tprintf("Executing SELECT @@server_id, expecting '%s'...\n", server_id[2]);
+ if (test->connect_rwsplit() || check_server_id(test, server_id[2]))
+ {
+ test->tprintf("Test failed without any blocked nodes.\n");
+ rval = 1;
+ break;
+ }
+ test->close_rwsplit();
+ test->galera->block_node(2);
+ blocked = true;
+ test->tprintf("OK\n");
+ sleep(15);
+
+ /** Block node 3 and node 1 should be master */
+ test->tprintf("Expecting '%s'...\n", server_id[0]);
+ if (test->connect_rwsplit() || check_server_id(test, server_id[0]))
+ {
+ test->tprintf("Test failed with first blocked node.\n");
+ rval = 1;
+ break;
+ }
+ test->close_rwsplit();
+ test->galera->block_node(0);
+ test->tprintf("OK\n");
+ sleep(15);
+
+ /** Block node 1 and node 4 should be master */
+ test->tprintf("Expecting '%s'...\n", server_id[3]);
+ if (test->connect_rwsplit() || check_server_id(test, server_id[3]))
+ {
+ test->tprintf("Test failed with second blocked node.\n");
+ rval = 1;
+ break;
+ }
+ test->close_rwsplit();
+ test->galera->block_node(3);
+ test->tprintf("OK\n");
+ sleep(15);
+
+ /** Block node 4 and node 2 should be master */
+ test->tprintf("Expecting '%s'...\n", server_id[1]);
+ if (test->connect_rwsplit() || check_server_id(test, server_id[1]))
+ {
+ test->tprintf("Test failed with third blocked node.\n");
+ rval = 1;
+ break;
+ }
+ test->close_rwsplit();
+ test->galera->block_node(1);
+ test->tprintf("OK\n");
+ sleep(15);
+
+ /** All nodes blocked, expect failure */
+ test->tprintf("Expecting failure...\n");
+ int myerrno = 0;
+ if ((myerrno = test->connect_rwsplit()) == 0 && test->conn_rwsplit)
+ {
+ test->tprintf("Connecting to rwsplit was expected to fail but it was"
+ " successful. Returned error was %d.\n", myerrno);
+ if (execute_query(test->conn_rwsplit, "SELECT @@server_id") == 0)
+ {
+ test->tprintf("SELECT @@server_id was expected to fail but the query was successful.\n");
+ }
+ else
+ {
+ test->tprintf("Connection succeeded but query failed.\n");
+ }
+ test->tprintf("Test failed with all nodes blocked.\n");
+ rval = 1;
+ }
+ test->tprintf("OK\n");
+
+ /** Unblock all nodes, node 3 should be master again */
+ test->galera->unblock_all_nodes();
+ blocked = false;
+ sleep(15);
+ test->tprintf("Expecting '%s'...\n", server_id[2]);
+ if (test->connect_rwsplit() || check_server_id(test, server_id[2]))
+ {
+ test->tprintf("Test failed after unblocking all nodes.\n");
+ rval = 1;
+ break;
+ }
+ test->close_rwsplit();
+ test->tprintf("OK\n");
+ }
+ while (false);
+
+ if (blocked)
+ {
+ test->galera->unblock_all_nodes();
+ }
+ return rval;
+}
+
+int main(int argc, char **argv)
+{
+ TestConnections *test = new TestConnections(argc, argv);
+ test->galera->verbose = false;
+ int rval1 = 0;
+ rval1 += simple_failover(test);
+ int rval = test->global_result;
+ delete test;
+ return rval;
+}
diff --git a/maxscale-system-test/gatekeeper.cpp b/maxscale-system-test/gatekeeper.cpp
new file mode 100644
index 000000000..a01a15344
--- /dev/null
+++ b/maxscale-system-test/gatekeeper.cpp
@@ -0,0 +1,92 @@
+
+#include
+#include
+#include "testconnections.h"
+
+/**
+ * Test for the gatekeeper module
+ */
+
+const char* training_queries[] =
+{
+ "SELECT * FROM test.t1 WHERE id = 1",
+ "INSERT INTO test.t1 VALUES (1)",
+ "UPDATE test.t1 SET id = 2 WHERE id = 1",
+ NULL
+};
+
+const char* allowed_queries[] =
+{
+ "SELECT * FROM test.t1 WHERE id = 1",
+ "SELECT * FROM test.t1 WHERE id = 2",
+ "SELECT * FROM test.t1 WHERE id = 102",
+ "INSERT INTO test.t1 VALUES (1)",
+ "INSERT INTO test.t1 VALUES (124)",
+ "INSERT INTO test.t1 VALUES (127419823)",
+ "UPDATE test.t1 SET id = 4 WHERE id = 1",
+ "UPDATE test.t1 SET id = 3 WHERE id = 2",
+ "UPDATE test.t1 SET id = 2 WHERE id = 3",
+ "UPDATE test.t1 SET id = 1 WHERE id = 4",
+ " UPDATE test.t1 SET id = 1 WHERE id = 4 ",
+ NULL
+};
+
+const char* denied_queries[] =
+{
+ "SELECT * FROM test.t1 WHERE id = 1 OR 1=1",
+ "INSERT INTO test.t1 VALUES (1), ('This is not a number')",
+ "UPDATE test.t1 SET id = 2 WHERE id = 1 OR id > 0",
+ NULL
+};
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->ssh_maxscale(true, "rm -f /var/lib/maxscale/gatekeeper.data");
+ Test->set_timeout(30);
+
+ Test->connect_rwsplit();
+
+ Test->try_query(Test->conn_rwsplit, "CREATE OR REPLACE TABLE test.t1 (id INT)");
+
+ for (int i = 0; training_queries[i]; i++)
+ {
+ Test->try_query(Test->conn_rwsplit, training_queries[i]);
+ }
+
+ Test->close_rwsplit();
+
+ Test->ssh_maxscale(true, "sed -i -e 's/mode=learn/mode=enforce/' /etc/maxscale.cnf");
+
+ Test->restart_maxscale();
+
+ sleep(5);
+
+ Test->connect_rwsplit();
+
+ for (int i = 0; training_queries[i]; i++)
+ {
+ Test->set_timeout(30);
+ Test->add_result(execute_query(Test->conn_rwsplit, training_queries[i]), "Query should not fail: %s",
+ training_queries[i]);
+ }
+
+ for (int i = 0; allowed_queries[i]; i++)
+ {
+ Test->set_timeout(30);
+ Test->add_result(execute_query(Test->conn_rwsplit, allowed_queries[i]), "Query should not fail: %s",
+ allowed_queries[i]);
+ }
+
+ for (int i = 0; denied_queries[i]; i++)
+ {
+ Test->set_timeout(30);
+ Test->add_result(execute_query(Test->conn_rwsplit, denied_queries[i]) == 0, "Query should fail: %s",
+ denied_queries[i]);
+ }
+
+ Test->ssh_maxscale(true, "rm -f /var/lib/maxscale/gatekeeper.data");
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/generate_log_sql.cpp b/maxscale-system-test/generate_log_sql.cpp
new file mode 100644
index 000000000..7aea3cc0a
--- /dev/null
+++ b/maxscale-system-test/generate_log_sql.cpp
@@ -0,0 +1,30 @@
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+using namespace std;
+
+
+int main(int argc, char *argv[])
+{
+ char sql[1000000];
+ create_insert_string(sql, 16, 0);
+
+ printf("%s\n", sql);
+
+ create_insert_string(sql, 256, 1);
+
+ printf("%s\n", sql);
+
+ create_insert_string(sql, 4096, 2);
+
+ printf("%s\n", sql);
+
+ create_insert_string(sql, 65536, 3);
+
+ printf("%s\n", sql);
+
+ exit(0);
+}
diff --git a/maxscale-system-test/get_com_select_insert.cpp b/maxscale-system-test/get_com_select_insert.cpp
new file mode 100644
index 000000000..a821990d9
--- /dev/null
+++ b/maxscale-system-test/get_com_select_insert.cpp
@@ -0,0 +1,105 @@
+#include "testconnections.h"
+
+/**
+Reads COM_SELECT and COM_INSERT variables from all nodes and stores into 'selects' and 'inserts'
+*/
+int get_global_status_allnodes(long int *selects, long int *inserts, Mariadb_nodes * nodes, int silent)
+{
+ int i;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ for (i = 0; i < nodes->N; i++)
+ {
+ if (nodes->nodes[i] != NULL)
+ {
+
+ if (mysql_query(nodes->nodes[i], "show global status like 'COM_SELECT';") != 0)
+ {
+ printf("Error: can't execute SQL-query\n");
+ printf("%s\n", mysql_error(nodes->nodes[i]));
+ return 1;
+ }
+
+ res = mysql_store_result(nodes->nodes[i]);
+ if (res == NULL)
+ {
+ printf("Error: can't get the result description\n");
+ return 1;
+ }
+
+ if (mysql_num_rows(res) > 0)
+ {
+ while ((row = mysql_fetch_row(res)) != NULL)
+ {
+ if (silent == 0)
+ {
+ printf("Node %d COM_SELECT=%s\n", i, row[1]);
+ }
+ sscanf(row[1], "%ld", &selects[i]);
+ }
+ }
+
+ mysql_free_result(res);
+ while ( mysql_next_result(nodes->nodes[i]) == 0 )
+ {
+ res = mysql_store_result(nodes->nodes[i]);
+ mysql_free_result(res);
+ }
+
+ if (mysql_query(nodes->nodes[i], "show global status like 'COM_INSERT';") != 0)
+ {
+ printf("Error: can't execute SQL-query\n");
+ }
+
+ res = mysql_store_result(nodes->nodes[i]);
+ if (res == NULL)
+ {
+ printf("Error: can't get the result description\n");
+ }
+
+ if (mysql_num_rows(res) > 0)
+ {
+ while ((row = mysql_fetch_row(res)) != NULL)
+ {
+ if (silent == 0)
+ {
+ printf("Node %d COM_INSERT=%s\n", i, row[1]);
+ }
+ sscanf(row[1], "%ld", &inserts[i]);
+ }
+ }
+
+ mysql_free_result(res);
+ while ( mysql_next_result(nodes->nodes[i]) == 0 )
+ {
+ res = mysql_store_result(nodes->nodes[i]);
+ mysql_free_result(res);
+ }
+ }
+ else
+ {
+ selects[i] = 0;
+ inserts[i] = 0;
+ }
+ }
+ return 0;
+
+}
+
+/**
+Prints difference in COM_SELECT and COM_INSERT
+*/
+int print_delta(long int *new_selects, long int *new_inserts, long int *selects, long int *inserts,
+ int nodes_num)
+{
+ int i;
+ for (i = 0; i < nodes_num; i++)
+ {
+ printf("COM_SELECT increase on node %d is %ld\n", i, new_selects[i] - selects[i]);
+ printf("COM_INSERT increase on node %d is %ld\n", i, new_inserts[i] - inserts[i]);
+ }
+ return 0;
+}
+
+
diff --git a/maxscale-system-test/get_com_select_insert.h b/maxscale-system-test/get_com_select_insert.h
new file mode 100644
index 000000000..2beb46376
--- /dev/null
+++ b/maxscale-system-test/get_com_select_insert.h
@@ -0,0 +1,29 @@
+#ifndef GET_COM_SELECT_INSERT_H
+#define GET_COM_SELECT_INSERT_H
+
+#include "testconnections.h"
+
+/**
+ * @brief get_global_status_allnodes Reads COM_SELECT and COM_INSERT variables from all nodes and stores into 'selects' and 'inserts'
+ * @param selects pointer to array to store COM_SELECT for all nodes
+ * @param inserts pointer to array to store COM_INSERT for all nodes
+ * @param nodes Mariadb_nodes object that contains information about nodes
+ * @param silent if 1 do not print anything
+ * @return 0 in case of success
+ */
+int get_global_status_allnodes(long int *selects, long int *inserts, Mariadb_nodes * nodes, int silent);
+
+/**
+ * @brief print_delta Prints difference in COM_SELECT and COM_INSERT
+ * @param new_selects pointer to array to store COM_SELECT for all nodes after test
+ * @param new_inserts pointer to array to store COM_INSERT for all nodes after test
+ * @param selects pointer to array to store COM_SELECT for all nodes before test
+ * @param inserts pointer to array to store COM_INSERT for all nodes before test
+ * @param NodesNum Number of nodes
+ * @return
+ */
+int print_delta(long int *new_selects, long int *new_inserts, long int *selects, long int *inserts,
+ int nodes_num);
+
+
+#endif // GET_COM_SELECT_INSERT_H
diff --git a/maxscale-system-test/get_logs.sh b/maxscale-system-test/get_logs.sh
new file mode 100755
index 000000000..4d174b112
--- /dev/null
+++ b/maxscale-system-test/get_logs.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+#set -x
+
+export maxscale_sshkey=$maxscale_keyfile
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP "mkdir -p logs; $maxscale_access_sudo cp $maxscale_log_dir/* logs/; $maxscale_access_sudo chmod a+r logs/*"
+ scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:logs/* .
+ scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP:$maxscale_cnf .
+else
+ mkdir -p logs;
+ sudo cp $maxscale_log_dir/* logs/
+ cp $maxscale_cnf logs/
+ sudo chmod a+r logs/*
+ cp logs/* .
+fi
diff --git a/maxscale-system-test/get_my_ip.cpp b/maxscale-system-test/get_my_ip.cpp
new file mode 100644
index 000000000..2b4574d2d
--- /dev/null
+++ b/maxscale-system-test/get_my_ip.cpp
@@ -0,0 +1,59 @@
+/*
+ * Find local ip used as source ip in ip packets.
+ * Use getsockname and a udp connection
+ */
+
+#include //printf
+#include //memset
+#include //errno
+#include //socket
+#include //sockaddr_in
+#include //getsockname
+#include //close
+
+#include "get_my_ip.h"
+
+int get_my_ip(char * remote_ip, char * my_ip )
+{
+
+ int dns_port = 53;
+
+ struct sockaddr_in serv;
+
+ int sock = socket ( AF_INET, SOCK_DGRAM, 0);
+
+ //Socket could not be created
+ if (sock < 0)
+ {
+ return 1;
+ }
+
+ memset( &serv, 0, sizeof(serv) );
+ serv.sin_family = AF_INET;
+ serv.sin_addr.s_addr = inet_addr( remote_ip );
+ serv.sin_port = htons( dns_port );
+
+ int err = connect( sock , (const struct sockaddr*) &serv , sizeof(serv) );
+
+ struct sockaddr_in name;
+ socklen_t namelen = sizeof(name);
+ err = getsockname(sock, (struct sockaddr*) &name, &namelen);
+
+ char buffer[100];
+ const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100);
+
+ if (p != NULL)
+ {
+ //printf("Local ip is : %s \n" , buffer);
+ strcpy(my_ip, buffer);
+ close(sock);
+ return 0;
+ }
+ else
+ {
+ //Some error
+ printf ("Error number : %d . Error message : %s \n" , errno , strerror(errno));
+ close(sock);
+ return 2;
+ }
+}
diff --git a/maxscale-system-test/get_my_ip.h b/maxscale-system-test/get_my_ip.h
new file mode 100644
index 000000000..255d935b0
--- /dev/null
+++ b/maxscale-system-test/get_my_ip.h
@@ -0,0 +1,13 @@
+#ifndef GET_MY_IP_H
+#define GET_MY_IP_H
+
+/**
+ * @brief get_my_ip Get IP address of machine where this code is executed as it is visible from remote machine
+ * Connects to DNS port 53 of remote machine and gets own IP from socket info
+ * @param remote_ip IP of remote machine
+ * @param my_ip Pointer to result (own IP string)
+ * @return 0 in case of success
+ */
+int get_my_ip(char * remote_ip, char *my_ip );
+
+#endif // GET_MY_IP_H
diff --git a/maxscale-system-test/insertstream.sh b/maxscale-system-test/insertstream.sh
new file mode 100755
index 000000000..6e7a59c61
--- /dev/null
+++ b/maxscale-system-test/insertstream.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./mysqltest_driver.sh insertstream insertstream 4006
diff --git a/maxscale-system-test/insertstream/r/insert.result b/maxscale-system-test/insertstream/r/insert.result
new file mode 100644
index 000000000..fed1b5fd7
--- /dev/null
+++ b/maxscale-system-test/insertstream/r/insert.result
@@ -0,0 +1,118 @@
+DROP TABLE IF EXISTS test.t1;
+CREATE TABLE test.t1(id INT);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+1
+DELETE FROM test.t1;
+FLUSH STATUS;
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (2);
+INSERT INTO test.t1 VALUES (3);
+COMMIT;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+3
+DELETE FROM test.t1;
+FLUSH STATUS;
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1), (2), (3);
+COMMIT;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+3
+DELETE FROM test.t1;
+FLUSH STATUS;
+INSERT INTO test.t1 VALUES (1);
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 1
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 0
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+1
+DELETE FROM test.t1;
+FLUSH STATUS;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (2);
+INSERT INTO test.t1 VALUES (3);
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 3
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 0
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+3
+DELETE FROM test.t1;
+FLUSH STATUS;
+DROP TABLE IF EXISTS test.t2;
+CREATE TABLE test.t2(id int);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t2 VALUES (1);
+ERROR HY000: Invalid insert target
+COMMIT;
+SELECT COUNT(*) FROM test.t2;
+COUNT(*)
+0
+DROP TABLE test.t2;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+1
+DELETE FROM test.t1;
+FLUSH STATUS;
+CREATE TABLE test.t2(id int);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT 1;
+1
+1
+INSERT INTO test.t2 VALUES (1);
+COMMIT;
+SELECT COUNT(*) FROM test.t2;
+COUNT(*)
+1
+DROP TABLE test.t2;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 2
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+1
+DELETE FROM test.t1;
+FLUSH STATUS;
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/insertstream/r/mixed.result b/maxscale-system-test/insertstream/r/mixed.result
new file mode 100644
index 000000000..3ee9f3e34
--- /dev/null
+++ b/maxscale-system-test/insertstream/r/mixed.result
@@ -0,0 +1,68 @@
+DROP TABLE IF EXISTS test.t1;
+CREATE TABLE test.t1(id INT);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT 1;
+1
+1
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 0
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 2
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+2
+DELETE FROM test.t1;
+FLUSH STATUS;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 4
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+5
+DELETE FROM test.t1;
+FLUSH STATUS;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+2
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+3
+UPDATE test.t1 SET id = 0;
+DELETE FROM test.t1;
+COMMIT;
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+0
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 4
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 1
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+2
+DELETE FROM test.t1;
+FLUSH STATUS;
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/insertstream/r/non-trx.result b/maxscale-system-test/insertstream/r/non-trx.result
new file mode 100644
index 000000000..b2afa6c26
--- /dev/null
+++ b/maxscale-system-test/insertstream/r/non-trx.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS test.t1;
+CREATE TABLE test.t1(id INT);
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+0
+DELETE FROM test.t1;
+INSERT INTO test.t1 VALUES (1), (2), (3);
+SELECT COUNT(*) FROM test.t1;
+COUNT(*)
+3
+UPDATE test.t1 SET id = 0;
+CREATE TABLE test.new_table(id int) ENGINE=MyISAM;
+INSERT INTO test.new_table VALUES (1), (2);
+DROP TABLE test.new_table;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+SHOW STATUS LIKE 'COM_INSERT';
+Variable_name Value
+Com_insert 10
+SHOW STATUS LIKE 'COM_LOAD';
+Variable_name Value
+Com_load 0
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/insertstream/t/insert.test b/maxscale-system-test/insertstream/t/insert.test
new file mode 100644
index 000000000..76d76cdb3
--- /dev/null
+++ b/maxscale-system-test/insertstream/t/insert.test
@@ -0,0 +1,108 @@
+# Setup
+--disable_warnings
+DROP TABLE IF EXISTS test.t1;
+--enable_warnings
+CREATE TABLE test.t1(id INT);
+
+# Test one insert inside transaction
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test multiple inserts inside transaction
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (2);
+INSERT INTO test.t1 VALUES (3);
+COMMIT;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test multi-value insert inside transaction
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1), (2), (3);
+COMMIT;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test non-transaction inserts
+
+INSERT INTO test.t1 VALUES (1);
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (2);
+INSERT INTO test.t1 VALUES (3);
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test different tables in inserts inside transactions
+
+--disable_warnings
+DROP TABLE IF EXISTS test.t2;
+--enable_warnings
+CREATE TABLE test.t2(id int);
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+--disable_abort_on_error
+INSERT INTO test.t2 VALUES (1);
+--enable_abort_on_error
+COMMIT;
+
+SELECT COUNT(*) FROM test.t2;
+DROP TABLE test.t2;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test inserts to different tables with selects between them inside a transaction
+
+CREATE TABLE test.t2(id int);
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT 1;
+INSERT INTO test.t2 VALUES (1);
+COMMIT;
+
+SELECT COUNT(*) FROM test.t2;
+DROP TABLE test.t2;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Cleanup
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/insertstream/t/mixed.test b/maxscale-system-test/insertstream/t/mixed.test
new file mode 100644
index 000000000..46065c91e
--- /dev/null
+++ b/maxscale-system-test/insertstream/t/mixed.test
@@ -0,0 +1,59 @@
+# Setup
+--disable_warnings
+DROP TABLE IF EXISTS test.t1;
+--enable_warnings
+CREATE TABLE test.t1(id INT);
+
+# Test mixed queries in transactions
+
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT 1;
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Test transaction and non-transaction inserts
+
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+COMMIT;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Mix inserts and selects in and out of transactions
+
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+SELECT COUNT(*) FROM test.t1;
+START TRANSACTION;
+INSERT INTO test.t1 VALUES (1);
+SELECT COUNT(*) FROM test.t1;
+UPDATE test.t1 SET id = 0;
+DELETE FROM test.t1;
+COMMIT;
+SELECT COUNT(*) FROM test.t1;
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+FLUSH STATUS;
+
+# Cleanup
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/insertstream/t/non-trx.test b/maxscale-system-test/insertstream/t/non-trx.test
new file mode 100644
index 000000000..872bef222
--- /dev/null
+++ b/maxscale-system-test/insertstream/t/non-trx.test
@@ -0,0 +1,31 @@
+# Setup
+--disable_warnings
+DROP TABLE IF EXISTS test.t1;
+--enable_warnings
+CREATE TABLE test.t1(id INT);
+
+# Test that nothing increases COM_LOAD outside of a transaction
+
+SELECT COUNT(*) FROM test.t1;
+DELETE FROM test.t1;
+INSERT INTO test.t1 VALUES (1), (2), (3);
+SELECT COUNT(*) FROM test.t1;
+UPDATE test.t1 SET id = 0;
+CREATE TABLE test.new_table(id int) ENGINE=MyISAM;
+INSERT INTO test.new_table VALUES (1), (2);
+DROP TABLE test.new_table;
+
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+INSERT INTO test.t1 VALUES (1);
+
+SHOW STATUS LIKE 'COM_INSERT';
+SHOW STATUS LIKE 'COM_LOAD';
+
+# Cleanup
+DROP TABLE test.t1;
diff --git a/maxscale-system-test/install_aws_tool.sh b/maxscale-system-test/install_aws_tool.sh
new file mode 100755
index 000000000..4be9e729e
--- /dev/null
+++ b/maxscale-system-test/install_aws_tool.sh
@@ -0,0 +1,4 @@
+sudo yum install -y curl unzip
+curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
+unzip awscli-bundle.zip
+sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
diff --git a/maxscale-system-test/kerb.cnf b/maxscale-system-test/kerb.cnf
new file mode 100644
index 000000000..74fd1671c
--- /dev/null
+++ b/maxscale-system-test/kerb.cnf
@@ -0,0 +1,3 @@
+[mysqld]
+gssapi-keytab-path=/etc/krb5.keytab
+gssapi-principal-name=mariadb/maxscale.test@MAXSCALE.TEST
diff --git a/maxscale-system-test/kerberos_setup.cpp b/maxscale-system-test/kerberos_setup.cpp
new file mode 100644
index 000000000..a6a2fe7c7
--- /dev/null
+++ b/maxscale-system-test/kerberos_setup.cpp
@@ -0,0 +1,139 @@
+/**
+ * @file kerberos_setup.cpp Attempt to configure KDC and try to use passwordless authentification
+ * - configure KDC on Maxscale machine and Kerberos workstation on all other nodes
+ * - create MariaDB user which is authentificated via GSSAPI
+ * - try to login to Maxscale as this GSSAPI user and execute simple query
+ */
+
+
+#include
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(1000);
+ char str[1024];
+
+ int i;
+
+ // To be moved to MDBCI
+ Test->tprintf("Creating 'hosts' file\n");
+ FILE * f;
+ f = fopen("hosts", "wt");
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ fprintf(f, "%s node_%03d.maxscale.test\n", Test->repl->IP[i], i);
+ }
+ fprintf(f, "%s maxscale.maxscale.test\n", Test->maxscale_IP);
+ fclose(f);
+
+ Test->tprintf("Copying 'hosts' and krb5.conf files to all nodes, installing kerberos client and MariaDB plugins\n");
+ sprintf(str, "%s/krb5.conf", test_dir);
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ Test->repl->ssh_node(i, (char *)
+ "yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-workstation pam_krb5", true);
+ Test->repl->copy_to_node(str, (char *) "~/", i);
+ Test->repl->ssh_node(i, (char *) "cp ~/krb5.conf /etc/", true);
+
+ Test->repl->copy_to_node((char *) "hosts", (char *) "~/", i);
+ Test->repl->ssh_node(i, (char *) "cp ~/hosts /etc/", true);
+ }
+
+ Test->tprintf("Copying 'hosts' and krb5.conf files to Maxscale node\n");
+
+ Test->copy_to_maxscale((char *) "hosts", (char *) "~/");
+ Test->ssh_maxscale(true, (char *) "cp ~/hosts /etc/");
+
+ Test->copy_to_maxscale(str, (char *) "~/");
+ Test->ssh_maxscale(true, (char *) "cp ~/krb5.conf /etc/");
+
+ Test->tprintf("Instaling Kerberos server packages to Maxscale node\n");
+ Test->ssh_maxscale(true, (char *) "yum install rng-tools -y");
+ Test->ssh_maxscale(true, (char *) "rngd -r /dev/urandom -o /dev/random");
+
+ Test->ssh_maxscale(true, (char *)
+ "yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5");
+
+
+ Test->tprintf("Configuring Kerberos server\n");
+ Test->ssh_maxscale(true, (char *) "sed -i \"s/EXAMPLE.COM/MAXSCALE.TEST/\" /var/kerberos/krb5kdc/kdc.conf");
+ Test->ssh_maxscale(true, (char *) "sed -i \"s/EXAMPLE.COM/MAXSCALE.TEST/\" /var/kerberos/krb5kdc/kadm5.acl");
+
+ Test->tprintf("Creating Kerberos DB and admin principal\n");
+ Test->ssh_maxscale(true, (char *) "kdb5_util create -P skysql -r MAXSCALE.TEST -s");
+ Test->ssh_maxscale(true, (char *) "kadmin.local -q \"addprinc -pw skysql admin/admin@MAXSCALE.TEST\"");
+
+ Test->tprintf("Opening ports 749 and 88\n");
+ Test->ssh_maxscale(true, (char *) "iptables -I INPUT -p tcp --dport 749 -j ACCEPT");
+ Test->ssh_maxscale(true, (char *) "iptables -I INPUT -p tcp --dport 88 -j ACCEPT");
+
+ Test->tprintf("Starting Kerberos\n");
+ Test->ssh_maxscale(true, (char *) "service krb5kdc start");
+ Test->ssh_maxscale(true, (char *) "service kadmin start");
+
+ Test->tprintf("Creating principal\n");
+ Test->ssh_maxscale(true, (char *)
+ "echo \"skysql\" | sudo kadmin -p admin/admin -q \"addprinc -randkey mariadb/maxscale.test\"");
+
+ Test->tprintf("Creating keytab file\n");
+ Test->ssh_maxscale(true, (char *)
+ "echo \"skysql\" | sudo kadmin -p admin/admin -q \"ktadd mariadb/maxscale.test\"");
+
+ Test->tprintf("Making keytab file readable for all\n");
+ Test->ssh_maxscale(true, (char *) "chmod a+r /etc/krb5.keytab;");
+
+ Test->ssh_maxscale(false, (char *) "kinit mariadb/maxscale.test@MAXSCALE.TEST -k -t /etc/krb5.keytab");
+ Test->ssh_maxscale(true, (char *)
+ "su maxscale --login -s /bin/sh -c \"kinit mariadb/maxscale.test@MAXSCALE.TEST -k -t /etc/krb5.keytab\"");
+
+ Test->tprintf("Coping keytab file from Maxscale node\n");
+ Test->copy_from_maxscale((char *) "/etc/krb5.keytab", (char *) ".");
+
+ Test->tprintf("Coping keytab and .cnf files to all nodes and executing knit for all nodes\n");
+ for (i = 0; i < Test->repl->N; i++)
+ {
+ sprintf(str, "%s/kerb.cnf", test_dir);
+ Test->repl->copy_to_node(str, (char *) "~/", i);
+ Test->repl->ssh_node(i, (char *) "cp ~/kerb.cnf /etc/my.cnf.d/", true);
+
+ Test->repl->copy_to_node((char *) "krb5.keytab", (char *) "~/", i);
+ Test->repl->ssh_node(i, (char *) "cp ~/krb5.keytab /etc/", true);
+
+ Test->repl->ssh_node(i, (char *) "kinit mariadb/maxscale.test@MAXSCALE.TEST -k -t /etc/krb5.keytab", false);
+ }
+
+ Test->tprintf("Installing gssapi plugin to all nodes\n");
+ Test->repl->connect();
+ Test->repl->execute_query_all_nodes((char *) "INSTALL SONAME 'auth_gssapi'");
+ Test->repl->close_connections();
+
+ Test->tprintf("Creating usr1 user\n");
+ Test->repl->connect();
+ Test->try_query(Test->repl->nodes[0],
+ (char *) "CREATE USER usr1 IDENTIFIED VIA gssapi AS 'mariadb/maxscale.test@MAXSCALE.TEST'");
+ Test->try_query(Test->repl->nodes[0], (char *) "grant all privileges on *.* to 'usr1'");
+ Test->repl->close_connections();
+
+ Test->tprintf("Trying use usr1 to execute query: RW Split\n");
+ Test->add_result(
+ Test->repl->ssh_node(1,
+ "echo select User,Host from mysql.user | mysql -uusr1 -h maxscale.maxscale.test -P 4006", false),
+ "Error executing query against RW Split\n");
+ Test->tprintf("Trying use usr1 to execute query: Read Connection Master\n");
+ Test->add_result(
+ Test->repl->ssh_node(1,
+ "echo select User,Host from mysql.user | mysql -uusr1 -h maxscale.maxscale.test -P 4008", false),
+ "Error executing query against Read Connection Master\n");
+ Test->tprintf("Trying use usr1 to execute query: Read Connection Slave\n");
+ Test->add_result(
+ Test->repl->ssh_node(1,
+ "echo select User,Host from mysql.user | mysql -uusr1 -h maxscale.maxscale.test -P 4009", false),
+ "Error executing query against Read Connection Slave\n");
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/kill_master.cpp b/maxscale-system-test/kill_master.cpp
new file mode 100644
index 000000000..2fb8fb243
--- /dev/null
+++ b/maxscale-system-test/kill_master.cpp
@@ -0,0 +1,48 @@
+/**
+ * @file kill_master.cpp Checks Maxscale behaviour in case if Master node is blocked
+ *
+ * - Connect to RWSplit
+ * - block Mariadb server on Master node by Firewall
+ * - try simple query *show processlist" expecting failure, but not a crash
+ * - check if Maxscale is alive
+ * - reconnect and check if query execution is ok
+ */
+
+
+#include "testconnections.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+
+ Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
+ Test->connect_rwsplit();
+
+ Test->set_timeout(30);
+ Test->tprintf("Setup firewall to block mysql on master\n");
+ Test->repl->block_node(0);
+
+ Test->tprintf("Trying query to RWSplit, expecting failure, but not a crash\n");
+ Test->set_timeout(30);
+ execute_query(Test->conn_rwsplit, (char *) "show processlist;");
+
+ Test->set_timeout(30);
+ Test->tprintf("Setup firewall back to allow mysql\n");
+ Test->repl->unblock_node(0);
+
+ Test->stop_timeout();
+ sleep(10);
+
+ Test->set_timeout(30);
+ Test->tprintf("Reconnecting and trying query to RWSplit\n");
+ Test->connect_rwsplit();
+ Test->try_query(Test->conn_rwsplit, (char *) "show processlist;");
+ Test->close_rwsplit();
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/krb5.conf b/maxscale-system-test/krb5.conf
new file mode 100644
index 000000000..0fc9a3c91
--- /dev/null
+++ b/maxscale-system-test/krb5.conf
@@ -0,0 +1,24 @@
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+[libdefaults]
+ dns_lookup_realm = false
+ ticket_lifetime = 24h
+ renew_lifetime = 7d
+ forwardable = true
+ rdns = false
+ default_realm = MAXSCALE.TEST
+ default_ccache_name = KEYRING:persistent:%{uid}
+
+[realms]
+ MAXSCALE.TEST = {
+ kdc = maxscale.maxscale.test
+ admin_server = maxscale.maxscale.test
+ }
+
+[domain_realm]
+ .maxscale.test = MAXSCALE.TEST
+ maxscale.test = MAXSCALE.TEST
+
diff --git a/maxscale-system-test/labels_list.sh b/maxscale-system-test/labels_list.sh
new file mode 100755
index 000000000..08c8cb777
--- /dev/null
+++ b/maxscale-system-test/labels_list.sh
@@ -0,0 +1 @@
+ctest --print-labels | grep " " | sed "s/ //g" | tr '\n' ','
diff --git a/maxscale-system-test/load_balancing.cpp b/maxscale-system-test/load_balancing.cpp
new file mode 100644
index 000000000..a7c23df87
--- /dev/null
+++ b/maxscale-system-test/load_balancing.cpp
@@ -0,0 +1,90 @@
+/**
+ * @file load_balancing.cpp Checks how Maxscale balances load
+ *
+ * - also used for 'load_balancing_pers1' and 'load_balancing_pers10' tests (with 'persistpoolmax=1' and 'persistpoolmax=10' for all servers)
+ *
+ * - start two groups of threads: each group consists of 25 threads, each thread creates connections to RWSplit,
+ * threads from first group try to execute as many SELECTs as possible, from second group - one query per second
+ * - after 100 seconds all threads are stopped
+ * - check number of connections to every slave: test PASSED if COM_SELECT difference between slaves is not greater then 3 times and no
+ * more then 10% of quesries went to Master
+ */
+
+
+
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "get_com_select_insert.h"
+
+#include "big_load.h"
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(20);
+ long int q;
+ int threads_num = 25;
+
+ long int selects[256];
+ long int inserts[256];
+ long int new_selects[256];
+ long int new_inserts[256];
+ long int i1, i2;
+
+ if (Test->smoke)
+ {
+ threads_num = 15;
+ }
+ Test->tprintf("Increasing connection and error limits on backend nodes.\n");
+ Test->repl->connect();
+ for (int i = 0; i < Test->repl->N; i++)
+ {
+ execute_query(Test->repl->nodes[i], (char *) "set global max_connections = 300;");
+ execute_query(Test->repl->nodes[i], (char *) "set global max_connect_errors = 100000;");
+ }
+ Test->repl->close_connections();
+
+ Test->tprintf("Creating query load with %d threads...\n", threads_num);
+ Test->set_timeout(1200);
+ load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], threads_num, Test, &i1, &i2, 1, false, true);
+
+ long int avr = (i1 + i2 ) / (Test->repl->N);
+ Test->tprintf("average number of quries per node %ld\n", avr);
+ long int min_q = avr / 3;
+ long int max_q = avr * 3;
+ Test->tprintf("Acceplable value for every node from %ld until %ld\n", min_q, max_q);
+
+ for (int i = 1; i < Test->repl->N; i++)
+ {
+ q = new_selects[i] - selects[i];
+ if ((q > max_q) || (q < min_q))
+ {
+ Test->add_result(1, "number of queries for node %d is %ld\n", i + 1, q);
+ }
+ }
+
+ if ((new_selects[0] - selects[0]) > avr / 3 )
+ {
+ Test->add_result(1,
+ "number of queries for master greater then 30%% of averange number of queries per node\n");
+ }
+
+ Test->tprintf("Restoring nodes\n");
+ Test->repl->connect();
+ for (int i = 0; i < Test->repl->N; i++)
+ {
+ execute_query(Test->repl->nodes[i], (char *) "flush hosts;");
+ execute_query(Test->repl->nodes[i], (char *) "set global max_connections = 151;");
+ }
+ Test->repl->close_connections();
+
+
+ Test->check_maxscale_alive();
+
+ Test->repl->start_replication();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/load_balancing_galera.cpp b/maxscale-system-test/load_balancing_galera.cpp
new file mode 100644
index 000000000..8c36881b1
--- /dev/null
+++ b/maxscale-system-test/load_balancing_galera.cpp
@@ -0,0 +1,101 @@
+/**
+ * @file load_balancing_galera.cpp Checks how Maxscale balances load
+ *
+ * - also used for 'load_balancing_galera_pers1' and 'load_balancing_galera_pers10' tests (with 'persistpoolmax=1' and 'persistpoolmax=10' for all servers)
+ *
+ * - start two groups of threads: each group consists of 25 threads, each thread creates connections to RWSplit,
+ * threads from first group try to execute as many SELECTs as possible, from second group - one query per second
+ * - after 100 seconds all threads are stopped
+ * - check number of connections to every slave: test PASSED if COM_SELECT difference between slaves is not greater then 3 times and no
+ * more then 10% of quesries went to Master
+ */
+
+
+
+#include "testconnections.h"
+#include "sql_t1.h"
+#include "get_com_select_insert.h"
+
+#include "big_load.h"
+
+int main(int argc, char *argv[])
+{
+
+ TestConnections * Test = new TestConnections(argc, argv);
+ int master;
+ long int q;
+ int threads_num = 25;
+
+ long int selects[256];
+ long int inserts[256];
+ long int new_selects[256];
+ long int new_inserts[256];
+ long int i1, i2;
+
+ Test->set_timeout(20);
+ master = Test->find_master_maxadmin(Test->galera);
+
+ if (master >= 0)
+ {
+ Test->tprintf("Master node is %d (server%d)\n", master, master + 1);
+ Test->set_timeout(20);
+
+ if (Test->smoke)
+ {
+ threads_num = 15;
+ }
+ Test->galera->connect();
+ for (int i = 0; i < Test->galera->N; i++)
+ {
+ execute_query(Test->galera->nodes[i], (char *) "set global max_connections = 300;");
+ execute_query(Test->galera->nodes[i], (char *) "set global max_connect_errors = 100000;");
+ }
+ Test->galera->close_connections();
+
+ Test->set_timeout(1200);
+ load(&new_inserts[0], &new_selects[0], &selects[0], &inserts[0], threads_num, Test, &i1, &i2, 1, true, true);
+
+ long int avr = (i1 + i2 ) / (Test->galera->N);
+ Test->tprintf("average number of quries per node %ld\n", avr);
+ long int min_q = avr / 3;
+ long int max_q = avr * 3;
+ Test->tprintf("Acceplable value for every node from %ld until %ld\n", min_q, max_q);
+
+ for (int i = 0; i < Test->galera->N; i++)
+ {
+ if ( i != master)
+ {
+ q = new_selects[i] - selects[i];
+ if ((q > max_q) || (q < min_q))
+ {
+ Test->add_result(1, "number of queries for node %d is %ld\n", i + 1, q);
+ }
+ }
+ }
+
+ if ((new_selects[master] - selects[master]) > avr / 3 )
+ {
+ Test->add_result(1,
+ "number of queries for master greater then 30%% of averange number of queries per node\n");
+ }
+
+ Test->tprintf("Restoring nodes\n");
+ Test->galera->connect();
+ for (int i = 0; i < Test->galera->N; i++)
+ {
+ execute_query(Test->galera->nodes[i], (char *) "flush hosts;");
+ execute_query(Test->galera->nodes[i], (char *) "set global max_connections = 151;");
+ }
+ Test->galera->close_connections();
+
+ Test->check_maxscale_alive();
+ }
+ else
+ {
+ Test->add_result(1, "Master is not found\n");
+ }
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/local_tests/cleanup_db.sh b/maxscale-system-test/local_tests/cleanup_db.sh
new file mode 100755
index 000000000..37f0270db
--- /dev/null
+++ b/maxscale-system-test/local_tests/cleanup_db.sh
@@ -0,0 +1,16 @@
+set -x
+
+dir=`pwd`
+
+
+#cp ~/build-scripts/test/multiple_servers.cnf $dir
+sudo killall mysqld
+sudo killall mysql_install_db
+sleep 10
+rm -rf /data/mysql/mysql$1
+rm -rf /var/log/mysql/*
+mkdir -p /data/mysql/mysql$1
+chown mysql:mysql -R /data
+chown mysql:mysql -R /var/run/mysqld
+
+mysql_install_db --defaults-file=$dir/local_tests/multiple_servers.cnf --user=mysql --datadir=/data/mysql/mysql$1
diff --git a/maxscale-system-test/local_tests/create_repl_user.sql b/maxscale-system-test/local_tests/create_repl_user.sql
new file mode 100644
index 000000000..98431e94e
--- /dev/null
+++ b/maxscale-system-test/local_tests/create_repl_user.sql
@@ -0,0 +1,4 @@
+#create user repl@'%' identified by 'repl';
+grant replication slave on *.* to repl@'%' identified by 'repl';
+
+FLUSH PRIVILEGES;
diff --git a/maxscale-system-test/local_tests/create_skysql_user.sql b/maxscale-system-test/local_tests/create_skysql_user.sql
new file mode 100644
index 000000000..668190d4f
--- /dev/null
+++ b/maxscale-system-test/local_tests/create_skysql_user.sql
@@ -0,0 +1,18 @@
+create user skysql@'%' identified by 'skysql';
+create user skysql@'localhost' identified by 'skysql';
+GRANT ALL PRIVILEGES ON *.* TO skysql@'%' WITH GRANT OPTION;
+GRANT ALL PRIVILEGES ON *.* TO skysql@'localhost' WITH GRANT OPTION;
+
+create user maxuser@'%' identified by 'maxpwd';
+create user maxuser@'localhost' identified by 'maxpwd';
+GRANT ALL PRIVILEGES ON *.* TO maxuser@'%' WITH GRANT OPTION;
+GRANT ALL PRIVILEGES ON *.* TO maxuser@'localhost' WITH GRANT OPTION;
+
+create user maxskysql@'%' identified by 'skysql';
+create user maxskysql@'localhost' identified by 'skysql';
+GRANT ALL PRIVILEGES ON *.* TO maxskysql@'%' WITH GRANT OPTION;
+GRANT ALL PRIVILEGES ON *.* TO maxskysql@'localhost' WITH GRANT OPTION;
+
+
+FLUSH PRIVILEGES;
+CREATE DATABASE IF NOT EXISTS test;
diff --git a/maxscale-system-test/local_tests/multiple_servers.cnf b/maxscale-system-test/local_tests/multiple_servers.cnf
new file mode 100644
index 000000000..b49106b8e
--- /dev/null
+++ b/maxscale-system-test/local_tests/multiple_servers.cnf
@@ -0,0 +1,79 @@
+
+#
+# These groups are read by MariaDB server.
+# Use it for options that only the server (but not clients) should see
+#
+# See the examples of server my.cnf files in /usr/share/mysql/
+#
+
+# this is read by the standalone daemon and embedded servers
+[server]
+
+# this is only for the mysqld standalone daemon
+[mysqld1]
+log-bin=mar-bin
+binlog-format=row
+#max_long_data_size=1000000000
+#innodb_log_file_size=2000000000
+slave-skip-errors=all
+server_id=1
+user = mysql
+pid-file = /var/run/mysqld/mysqld1.pid
+socket = /var/run/mysqld/mysqld1.sock
+port = 3301
+datadir = /data/mysql/mysql1
+
+[mysqld2]
+log-bin=mar-bin
+binlog-format=row
+#max_long_data_size=1000000000
+#innodb_log_file_size=2000000000
+slave-skip-errors=all
+server_id=2
+user = mysql
+pid-file = /var/run/mysqld/mysqld2.pid
+socket = /var/run/mysqld/mysqld2.sock
+port = 3302
+datadir = /data/mysql/mysql2
+
+[mysqld3]
+log-bin=mar-bin
+binlog-format=row
+#max_long_data_size=1000000000
+#innodb_log_file_size=2000000000
+slave-skip-errors=all
+server_id=3
+user = mysql
+pid-file = /var/run/mysqld/mysqld3.pid
+socket = /var/run/mysqld/mysqld3.sock
+port = 3303
+datadir = /data/mysql/mysql3
+
+[mysqld4]
+log-bin=mar-bin
+binlog-format=row
+#max_long_data_size=1000000000
+#innodb_log_file_size=2000000000
+slave-skip-errors=all
+server_id=4
+user = mysql
+pid-file = /var/run/mysqld/mysqld4.pid
+socket = /var/run/mysqld/mysqld4.sock
+port = 3304
+datadir = /data/mysql/mysql4
+
+# this is only for embedded server
+[embedded]
+
+# This group is only read by MariaDB-5.5 servers.
+# If you use the same .cnf file for MariaDB of different versions,
+# use this group for options that older servers don't understand
+[mysqld-5.5]
+
+# These two groups are only read by MariaDB servers, not by MySQL.
+# If you use the same .cnf file for MySQL and MariaDB,
+# you can put MariaDB-only options here
+[mariadb]
+
+[mariadb-5.5]
+
diff --git a/maxscale-system-test/local_tests/set_env_local.sh b/maxscale-system-test/local_tests/set_env_local.sh
new file mode 100644
index 000000000..16e4afb48
--- /dev/null
+++ b/maxscale-system-test/local_tests/set_env_local.sh
@@ -0,0 +1,93 @@
+set -x
+echo $*
+export config_name="$1"
+if [ -z $1 ] ; then
+ config_name="local1"
+fi
+
+export curr_dir=`pwd`
+
+export new_dirs="yes"
+
+export maxscale_binlog_dir="/var/lib/maxscale/Binlog_Service"
+export maxdir="/usr/bin/"
+export maxdir_bin="/usr/bin/"
+export maxscale_cnf="/etc/maxscale.cnf"
+export maxscale_log_dir="/var/log/maxscale/"
+export maxscale_sshkey=$maxscale_keyfile
+
+cd $mdbci_dir
+
+# Number of nodes
+export node_N=4
+
+export maxscale_IP=127.0.0.1
+export maxscale_network=127.0.0.1
+export maxscale_keyfile=$HOME/.ssh/id_rsa
+
+# User name and Password for Master/Slave replication setup (should have all PRIVILEGES)
+export node_user="skysql"
+export node_password="skysql"
+
+# User name and Password for Galera setup (should have all PRIVILEGES)
+#export galera_user="skysql"
+#export galera_password="skysql"
+
+export maxscale_user="skysql"
+export maxscale_password="skysql"
+
+export maxadmin_password="mariadb"
+
+#for prefix in "node" "galera"
+for prefix in "node"
+do
+ N_var="$prefix"_N
+ Nx=${!N_var}
+ N=`expr $Nx - 1`
+ for i in $(seq 0 $N)
+ do
+ num=`printf "%03d" $i`
+ username=`whoami`
+ eval 'export "$prefix"_"$num"_network=127.0.0.1'
+ eval 'export "$prefix"_"$num"_private_ip=127.0.0.1'
+ eval 'export "$prefix"_"$num"_hostname="$prefix""$num"'
+ eval 'export "$prefix"_"$num"_whoami="$username"'
+ eval 'export "$prefix"_"$num"_keyfile="$HOME"/.ssh/id_rsa'
+ j=`expr $i + 1`
+ eval 'export "$prefix"_"$num"_socket=/var/run/mysqld/mysqld"$j".sock'
+
+ mariadbport=`expr $i + 3301`
+ eval 'export "$prefix"_"$num"_port="$mariadbport"'
+ eval 'export "$prefix"_"$num"_access_sudo=sudo'
+
+ start_cmd_var="$prefix"_"$num"_start_db_command
+ stop_cmd_var="$prefix"_"$num"_stop_db_command
+ GRN=`expr $i + 1`
+ eval 'export $start_cmd_var="mysqld_multi --defaults-file=$HOME/maxscale-system-test/local_tests/multiple_servers.cnf start $GRN"'
+ eval 'export $stop_cmd_var="mysqld_multi --defaults-file=$HOME/maxscale-system-test/local_tests/multiple_servers.cnf stop $GRN"'
+
+ start_cmd_var="$prefix"_"$num"_cleanup_db_command
+ GRN=`expr $i + 1`
+ eval 'export $start_cmd_var="$HOME/maxscale-system-test/local_tests/cleanup_db.sh $GRN"'
+
+# cd ..
+ done
+done
+
+cd $mdbci_dir
+export maxscale_access_user=`whoami`
+export maxscale_whoami=`whoami`
+export maxscale_access_sudo="sudo "
+
+# Sysbench directory (should be sysbench >= 0.5)
+export sysbench_dir="$HOME/sysbench_deb7/sysbench/"
+
+export ssl=true
+
+#export use_snapshots=yes
+export take_snapshot_command="echo Snapshots are not supported in the local config"
+export revert_snapshot_command="echo Snapshots are not supported in the local config"
+
+export smoke=yes
+cd $curr_dir
+set +x
diff --git a/maxscale-system-test/local_tests/start_multiple_mariadb.sh b/maxscale-system-test/local_tests/start_multiple_mariadb.sh
new file mode 100755
index 000000000..926514509
--- /dev/null
+++ b/maxscale-system-test/local_tests/start_multiple_mariadb.sh
@@ -0,0 +1,34 @@
+set -x
+
+servers=4;
+dir=`pwd`
+
+#cp ~/build-scripts/test/multiple_servers.cnf $dir
+sudo rm -rf /data/mysql/*
+sudo rm -rf /var/log/mysql/*
+sudo mkdir -p /data/mysql
+sudo chown mysql:mysql -R /data
+sudo mkdir -p /var/run/mysqld
+sudo chown mysql:mysql -R /var/run/mysqld
+sudo killall mysqld
+sudo killall mysql_install_db
+sleep 20
+
+for i in `seq 1 $servers`;
+do
+ sudo mysql_install_db --defaults-file=$dir/multiple_servers.cnf --user=mysql --datadir=/data/mysql/mysql$i
+done
+
+sudo mysqld_multi --defaults-file=$dir/multiple_servers.cnf start &
+
+running_servers=0
+while [ $running_servers != $servers ] ; do
+ running_servers=`mysqld_multi --defaults-file=$dir/multiple_servers.cnf report | grep "is running" | wc -l`
+done
+
+
+for i in `seq 1 $servers`;
+do
+ sudo mysql --socket=/var/run/mysqld/mysqld$i.sock < $dir/create_repl_user.sql
+ sudo mysql --socket=/var/run/mysqld/mysqld$i.sock < $dir/create_skysql_user.sql
+done
diff --git a/maxscale-system-test/long_insert.sh b/maxscale-system-test/long_insert.sh
new file mode 100755
index 000000000..4b67df2e9
--- /dev/null
+++ b/maxscale-system-test/long_insert.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+rp=`realpath $0`
+export test_dir=`dirname $rp`
+export test_name=`basename $rp`
+$test_dir/non_native_setup $test_name
+export ssl_options="--ssl-cert=$test_dir/ssl-cert/client-cert.pem --ssl-key=$test_dir/ssl-cert/client-key.pem"
+
+IP=$Maxscale_IP
+
+mysql -h $IP -P 4006 -u $node_user -p$node_password $ssl_options < $test_dir/long_insert_sql/test_init.sql
+
+echo "RWSplit router:"
+for ((i=0 ; i<1000 ; i++)) ; do
+ echo "iteration: $i"
+ mysql -h $IP -P 4006 -u $node_user -p$node_password $ssl_options < $test_dir/long_insert_sql/test_query.sql
+done
+
+echo "ReadConn router (master):"
+for ((i=0 ; i<1000 ; i++)) ; do
+ echo "iteration: $i"
+ mysql -h $IP -P 4008 -u $node_user -p$node_uassword $ssl_options < $test_dir/long_insert_sql/test_query.sql
+done
+
+
+res=$?
+
+$test_dir/copy_logs.sh long_insert
+exit $res
diff --git a/maxscale-system-test/long_insert_sql/test.sql b/maxscale-system-test/long_insert_sql/test.sql
new file mode 100644
index 000000000..a87df8faa
--- /dev/null
+++ b/maxscale-system-test/long_insert_sql/test.sql
@@ -0,0 +1,4 @@
+USE test;
+DROP TABLE IF EXISTS test_bind_fetch;
+CREATE TABLE test_bind_fetch(c1 char(10), c2 text);
+INSERT INTO test_bind_fetch VALUES ('1234567890', 'A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1');
diff --git a/maxscale-system-test/long_insert_sql/test_init.sql b/maxscale-system-test/long_insert_sql/test_init.sql
new file mode 100644
index 000000000..f73e0d4d3
--- /dev/null
+++ b/maxscale-system-test/long_insert_sql/test_init.sql
@@ -0,0 +1,4 @@
+CREATE DATABASE IF NOT EXISTS test;
+USE test;
+DROP TABLE IF EXISTS test_bind_fetch;
+CREATE TABLE test_bind_fetch(c1 char(10), c2 text);
diff --git a/maxscale-system-test/long_insert_sql/test_query.sql b/maxscale-system-test/long_insert_sql/test_query.sql
new file mode 100644
index 000000000..14e072497
--- /dev/null
+++ b/maxscale-system-test/long_insert_sql/test_query.sql
@@ -0,0 +1,2 @@
+USE test;
+INSERT INTO test_bind_fetch VALUES ('1234567890', 'A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1');
diff --git a/maxscale-system-test/long_sysbench.cpp b/maxscale-system-test/long_sysbench.cpp
new file mode 100644
index 000000000..b63559db3
--- /dev/null
+++ b/maxscale-system-test/long_sysbench.cpp
@@ -0,0 +1,92 @@
+/**
+ * @file long_sysbanch.cpp Run 'sysbench' for long long execution (long load test)
+ *
+ * - start sysbanch test
+ * - repeat for all services
+ * - DROP sysbanch tables
+ * - check if Maxscale is alive
+ */
+
+
+#include "testconnections.h"
+#include "sysbench_commands.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+
+ char sys1[4096];
+ int port[3];
+ int current_port;
+
+ port[0] = Test->rwsplit_port;
+ port[1] = Test->readconn_master_port;
+ port[2] = Test->readconn_slave_port;
+
+ Test->tprintf("Connecting to RWSplit %s\n", Test->maxscale_IP);
+
+ sprintf(&sys1[0], sysbench_prepare, Test->sysbench_dir, Test->sysbench_dir, Test->maxscale_IP);
+
+ Test->tprintf("Preparing sysbench tables\n%s\n", sys1);
+ Test->set_timeout(10000);
+ Test->add_result(system(sys1), "Error executing sysbench prepare\n");
+
+ char *readonly;
+ char *ro_on = (char *) "on";
+ char *ro_off = (char *) "off";
+
+ Test->stop_timeout();
+
+ current_port = port[0];
+
+ Test->tprintf("Trying test with port %d\n", current_port);
+
+ if (current_port == Test->readconn_slave_port )
+ {
+ readonly = ro_on;
+ }
+ else
+ {
+ readonly = ro_off;
+ }
+
+ sprintf(&sys1[0], sysbench_command_long, Test->sysbench_dir, Test->sysbench_dir, Test->maxscale_IP,
+ current_port, readonly);
+ Test->set_log_copy_interval(300);
+ Test->tprintf("Executing sysbench \n%s\n", sys1);
+ if (system(sys1) != 0)
+ {
+ Test->tprintf("Error executing sysbench test\n");
+ }
+
+ Test->connect_maxscale();
+
+ printf("Dropping sysbanch tables!\n");
+ fflush(stdout);
+
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE sbtest1");
+ if (!Test->smoke)
+ {
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE sbtest2");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE sbtest3");
+ Test->try_query(Test->conn_rwsplit, (char *) "DROP TABLE sbtest4");
+ }
+
+ //global_result += execute_query(Test->conn_rwsplit, (char *) "DROP TABLE sbtest");
+
+ printf("closing connections to MaxScale!\n");
+ fflush(stdout);
+
+ Test->close_maxscale_connections();
+
+ Test->tprintf("Checking if MaxScale is still alive!\n");
+ fflush(stdout);
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ fflush(stdout);
+ Test->tprintf("Logs copied!\n");
+ fflush(stdout);
+ return rval;
+}
diff --git a/maxscale-system-test/longblob.cpp b/maxscale-system-test/longblob.cpp
new file mode 100644
index 000000000..8809e4e32
--- /dev/null
+++ b/maxscale-system-test/longblob.cpp
@@ -0,0 +1,86 @@
+/**
+ * @file longblob.cpp - trying to use LONGBLOB
+ * - try to insert large BLOB, MEDIUMBLOB and LONGBLOB via RWSplit, ReadConn Master and directly to backend
+ */
+
+
+#include "testconnections.h"
+#include "blob_test.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->set_timeout(60);
+
+ Test->repl->execute_query_all_nodes( (char *) "set global max_allowed_packet=10000000");
+
+ /*Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("LONGBLOB: Trying send data directly to Master\n");
+ test_longblob(Test, Test->repl->nodes[0], (char *) "LONGBLOB", 1000000, 20, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();*/
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("LONGBLOB: Trying send data via RWSplit\n");
+ test_longblob(Test, Test->conn_rwsplit, (char *) "LONGBLOB", 1000000, 20, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("LONGBLOB: Trying send data via ReadConn master\n");
+ test_longblob(Test, Test->conn_master, (char *) "LONGBLOB", 1000000, 20, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+
+
+ /*Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("BLOB: Trying send data directly to Master\n");
+ test_longblob(Test, Test->repl->nodes[0], (char *) "BLOB", 1000, 8, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();*/
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("BLOB: Trying send data via RWSplit\n");
+ test_longblob(Test, Test->conn_rwsplit, (char *) "BLOB", 1000, 8, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("BLOB: Trying send data via ReadConn master\n");
+ test_longblob(Test, Test->conn_master, (char *) "BLOB", 1000, 8, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+
+ /*Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("MEDIUMBLOB: Trying send data directly to Master\n");
+ test_longblob(Test, Test->repl->nodes[0], (char *) "MEDIUMBLOB", 1000000, 2, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();*/
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("MEDIUMBLOB: Trying send data via RWSplit\n");
+ test_longblob(Test, Test->conn_rwsplit, (char *) "MEDIUMBLOB", 1000000, 2, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+ Test->connect_maxscale();
+ Test->repl->connect();
+ Test->tprintf("MEDIUMBLOB: Trying send data via ReadConn master\n");
+ test_longblob(Test, Test->conn_master, (char *) "MEDIUMBLOB", 1000000, 2, 1);
+ Test->repl->close_connections();
+ Test->close_maxscale_connections();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
diff --git a/maxscale-system-test/lots_of_rows.cpp b/maxscale-system-test/lots_of_rows.cpp
new file mode 100644
index 000000000..67de39828
--- /dev/null
+++ b/maxscale-system-test/lots_of_rows.cpp
@@ -0,0 +1,39 @@
+/**
+ * @file lots_of_row.cpp INSERT extremelly big number of rows
+ * - do INSERT of 100 rows in the loop 2000 times
+ * - do SELECT *
+ */
+
+
+#include
+#include "testconnections.h"
+#include "sql_t1.h"
+
+int main(int argc, char *argv[])
+{
+ TestConnections * Test = new TestConnections(argc, argv);
+ char sql[10240];
+
+ Test->connect_maxscale();
+ create_t1(Test->conn_rwsplit);
+
+ Test->tprintf("INSERTing data\n");
+ for (int i = 0; i < 2000; i++)
+ {
+ Test->set_timeout(20);
+ create_insert_string(sql, 100, i);
+ Test->try_query(Test->conn_rwsplit, sql);
+ }
+ Test->tprintf("done, sleeping\n");
+ Test->stop_timeout();
+ sleep(20);
+ Test->tprintf("Trying SELECT\n");
+ Test->set_timeout(30);
+ Test->try_query(Test->conn_rwsplit, (char *) "SELECT * FROM t1");
+
+ Test->check_maxscale_alive();
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
diff --git a/maxscale-system-test/macros.cmake b/maxscale-system-test/macros.cmake
new file mode 100644
index 000000000..9aaa77673
--- /dev/null
+++ b/maxscale-system-test/macros.cmake
@@ -0,0 +1,70 @@
+macro(set_maxscale_version)
+
+ #MaxScale-test version number
+ set(MAXSCALE_VERSION_MAJOR "1")
+ set(MAXSCALE_VERSION_MINOR "3")
+ set(MAXSCALE_VERSION_PATCH "0")
+ set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
+ set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}-beta")
+
+endmacro()
+
+macro(check_deps)
+
+ find_library(MYSQL_CLIENT mariadbclient mysqlclient PATH_SUFFIXES mysql mariadb)
+
+ # Check for libraries MaxScale depends on
+ set(MAXSCALE_DEPS z crypt nsl m pthread ssl crypto dl rt jansson)
+ foreach(lib ${MAXSCALE_DEPS})
+ find_library(lib${lib} ${lib})
+ if((DEFINED lib${lib}) AND (${lib${lib}} MATCHES "NOTFOUND"))
+ set(DEPS_ERROR TRUE)
+ set(FAILED_DEPS "${FAILED_DEPS} lib${lib}")
+ elseif(DEBUG_OUTPUT)
+ message(STATUS "Library was found at: ${lib${lib}}")
+ endif()
+ endforeach()
+
+ if(DEPS_ERROR)
+ set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
+ message(FATAL_ERROR "Cannot find dependencies: ${FAILED_DEPS}")
+ endif()
+ if(DEFINED MYSQL_CLIENT MATCHES "NOTFOUND")
+ set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
+ message(FATAL_ERROR "Cannot find dependencies: mariadbclient or mysqlclient")
+ endif()
+
+endmacro()
+
+macro(check_dirs)
+
+ # This variable is used to prevent redundant checking of dependencies
+ set(DEPS_OK TRUE CACHE BOOL "If all the dependencies were found.")
+
+ # Find the MySQL headers if they were not defined
+
+ if(DEFINED MYSQL_DIR)
+ if(DEBUG_OUTPUT)
+ message(STATUS "Searching for MySQL headers at: ${MYSQL_DIR}")
+ endif()
+ find_path(MYSQL_DIR_LOC mysql.h PATHS ${MYSQL_DIR} PATH_SUFFIXES mysql mariadb NO_DEFAULT_PATH)
+ else()
+ find_path(MYSQL_DIR_LOC mysql.h PATH_SUFFIXES mysql mariadb)
+ endif()
+
+ if(DEBUG_OUTPUT)
+ message(STATUS "Search returned: ${MYSQL_DIR_LOC}")
+ endif()
+
+ if(${MYSQL_DIR_LOC} MATCHES "NOTFOUND")
+ set(DEPS_OK FALSE CACHE BOOL "If all the dependencies were found.")
+ message(FATAL_ERROR "Fatal Error: MySQL headers were not found.")
+ else()
+ set(MYSQL_DIR ${MYSQL_DIR_LOC} CACHE PATH "Path to MySQL headers" FORCE)
+ message(STATUS "Using MySQL headers found at: ${MYSQL_DIR}")
+ endif()
+
+ unset(MYSQL_DIR_LOC)
+
+endmacro()
+
diff --git a/maxscale-system-test/manage_mrm.sh b/maxscale-system-test/manage_mrm.sh
new file mode 100755
index 000000000..72f1bd3db
--- /dev/null
+++ b/maxscale-system-test/manage_mrm.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+function do_ssh() {
+ ssh -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $maxscale_access_user@$maxscale_IP
+}
+
+function do_scp() {
+ scp -i $maxscale_sshkey -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet $1 $maxscale_access_user@$maxscale_IP:$2
+}
+
+function create_config() {
+
+ if [ "$1" == "3" ]
+ then
+ nodelist=$node_000_network:3306,$node_001_network:3306,$node_002_network:3306
+ elif [ "$1" == "2" ]
+ then
+ nodelist=$node_000_network:3306,$node_001_network:3306
+ else
+ nodelist=$node_000_network:3306,$node_001_network:3306,$node_002_network:3306,$node_003_network:3306
+ fi
+
+ if [ -n "$new_replication_manager" ]
+ then
+ default_section="[Default]"
+ fi
+
+ cat < config.toml
+# config.toml
+# Example replication-manager configuration file
+
+$default_section
+hosts = "$nodelist"
+user = "skysql:skysql"
+rpluser = "skysql:skysql"
+title = "Cluster01"
+connect-timeout = 1
+prefmaster = "$node_000_network:3306"
+interactive = false
+log-level=1
+# LOG
+# ---
+
+logfile = "/var/log/replication-manager.log"
+verbose = true
+
+# TOPOLOGY
+# --------
+
+
+# Automatically rejoin a failed server to the current master
+# Slaves will re enter with read-only
+
+readonly = true
+failover-event-scheduler = false
+failover-event-status = false
+
+# FAILOVER
+# --------
+
+# Timeout in seconds between consecutive monitoring
+# check type can be tcp or agent
+monitoring-ticker = 1
+check-type = "tcp"
+check-replication-filters = true
+check-binlog-filters = true
+check-replication-state = true
+
+# Failover after N failures detection
+# Reset number of failure if server auto recover after N seconds
+failcount = 1
+failcount-reset-time = 300
+
+# Cancel failover if already N failover
+# Cancel failover if last failover was N seconds before
+# Cancel failover in semi-sync when one slave is not in sync
+# Cancel failover when replication delay is more than N seconds
+failover-limit = 100
+failover-time-limit = 1
+failover-at-sync = false
+switchover-at-sync = false
+maxdelay = 30
+
+# SWITCHOVER
+# ----------
+
+# In switchover Wait N milliseconds before killing long running transactions
+# Cancel switchover if transaction running more than N seconds
+# Cancel switchover if write query running more than N seconds
+# Cancel switchover if one of the slaves is not synced based on GTID equality
+wait-kill = 5000
+wait-trx = 10
+wait-write-query = 10
+gtidcheck = true
+
+EOF
+
+}
+
+function install_mrm() {
+
+ # new_replication_manager means that we're using a custom build and it's already installed on the system
+ if [ -z "$new_replication_manager" ]
+ then
+ do_ssh < /dev/null || sudo yum -y install wget
+wget -q https://github.com/tanji/replication-manager/releases/download/1.0.2/replication-manager-1.0.2_1_g8faf64d-8faf64d.x86_64.rpm
+sudo yum -y install ./replication-manager-1.0.2_1_g8faf64d-8faf64d.x86_64.rpm
+sudo systemctl daemon-reload
+rm ./replication-manager-1.0.2_1_g8faf64d-8faf64d.x86_64.rpm
+EOF
+ fi
+
+ create_config $1
+ do_scp './config.toml' '~/config.toml'
+
+ do_ssh < /dev/null || sudo yum -y install wget
+test -f go1.8.linux-amd64.tar.gz || wget -q https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz
+sudo su -
+cd /home/vagrant/
+sudo tar -axf go1.8.linux-amd64.tar.gz -C /usr
+sudo echo 'export GOROOT=/usr/go/' > /etc/profile.d/go.sh
+sudo echo 'export GOPATH=/usr/' >> /etc/profile.d/go.sh
+sudo echo 'export PATH=/$PATH:/usr/go/bin/' >> /etc/profile.d/go.sh
+source /etc/profile
+go get github.com/tanji/replication-manager
+go install github.com/tanji/replication-manager
+cp /usr/src/github.com/tanji/replication-manager/service/replication-manager.service /etc/systemd/system/
+exit
+EOF
+}
+
+function remove_mrm() {
+ do_ssh <
+#include
+
+int set_ssl(MYSQL * conn)
+{
+ char client_key[1024];
+ char client_cert[1024];
+ char ca[1024];
+ char * test_dir;
+ test_dir = getenv("test_dir");
+ if (test_dir == NULL)
+ {
+ sprintf(client_key, "./ssl-cert/client-key.pem");
+ sprintf(client_cert, "./ssl-cert/client-cert.pem");
+ sprintf(ca, "./ssl-cert/ca.pem");
+ }
+ else
+ {
+ sprintf(client_key, "%s/ssl-cert/client-key.pem", test_dir);
+ sprintf(client_cert, "%s/ssl-cert/client-cert.pem", test_dir);
+ sprintf(ca, "%s/ssl-cert/ca.pem", test_dir);
+ }
+ return mysql_ssl_set(conn, client_key, client_cert, ca, NULL, NULL);
+}
+
+MYSQL * open_conn_db_flags(int port, const char* ip, const char* db, const char* User, const char* Password,
+ unsigned long flag, bool ssl)
+{
+ MYSQL * conn = mysql_init(NULL);
+
+ if (conn == NULL)
+ {
+ fprintf(stdout, "Error: can't create MySQL-descriptor\n");
+ return NULL;
+ }
+
+ if (ssl)
+ {
+ set_ssl(conn);
+ }
+
+ if (!mysql_real_connect(conn,
+ ip,
+ User,
+ Password,
+ db,
+ port,
+ NULL,
+ flag
+ ))
+ {
+ //printf("Error: can't connect to database, error is %s:\n", mysql_error(conn));
+ return conn;
+ }
+
+ return conn;
+}
+
+MYSQL * open_conn_db_timeout(int port, const char* ip, const char* db, const char* User, const char* Password,
+ unsigned long timeout, bool ssl)
+{
+ MYSQL * conn = mysql_init(NULL);
+
+ if (conn == NULL)
+ {
+ fprintf(stdout, "Error: can't create MySQL-descriptor\n");
+ return NULL;
+ }
+
+ unsigned int conn_timeout = timeout;
+ unsigned int read_timeout = timeout;
+ unsigned int write_timeout = timeout;
+ mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &conn_timeout);
+ mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, &read_timeout);
+ mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
+
+ if (ssl)
+ {
+ if (ssl)
+ {
+ set_ssl(conn);
+ }
+ }
+
+ if (!mysql_real_connect(conn,
+ ip,
+ User,
+ Password,
+ db,
+ port,
+ NULL,
+ CLIENT_MULTI_STATEMENTS
+ ))
+ {
+ //printf("Error: can't connect to database, error is %s:\n", mysql_error(conn));
+ return conn;
+ }
+
+ return conn;
+}
+MYSQL* open_conn_db_timeout(int port, const std::string& ip, const std::string& db,
+ const std::string& user, const std::string& password,
+ unsigned long timeout, bool ssl)
+{
+ return open_conn_db_timeout(port, ip.c_str(), db.c_str(), user.c_str(), password.c_str(), timeout, ssl);
+}
+
+MYSQL * open_conn_db(int port, const char* ip, const char* db, const char* User, const char* Password,
+ bool ssl)
+{
+ return open_conn_db_flags(port, ip, db, User, Password, CLIENT_MULTI_STATEMENTS, ssl);
+}
+
+MYSQL * open_conn(int port, const char* ip, const char* User, const char* Password, bool ssl)
+{
+ return open_conn_db(port, ip, "test", User, Password, ssl);
+}
+
+MYSQL * open_conn_no_db(int port, const char* ip, const char*User, const char*Password, bool ssl)
+{
+ return open_conn_db_flags(port, ip, NULL, User, Password, CLIENT_MULTI_STATEMENTS, ssl);
+}
+
+int execute_query(MYSQL *conn, const char *format, ...)
+{
+ va_list valist;
+
+ va_start(valist, format);
+ int message_len = vsnprintf(NULL, 0, format, valist);
+ va_end(valist);
+
+ char sql[message_len + 1];
+
+ va_start(valist, format);
+ vsnprintf(sql, sizeof(sql), format, valist);
+ va_end(valist);
+
+ return execute_query1(conn, sql, false);
+}
+
+int execute_query_from_file(MYSQL *conn, FILE *file)
+{
+ int rc = -1;
+ char buf[4096];
+
+ if (fgets(buf, sizeof(buf), file))
+ {
+ char *nul = strchr(buf, '\0') - 1;
+
+ while (isspace(*nul))
+ {
+ *nul-- = '\0';
+ }
+
+ char *ptr = buf;
+
+ while (isspace(*ptr))
+ {
+ ptr++;
+ }
+
+ if (*ptr)
+ {
+ rc = execute_query1(conn, buf, false);
+ }
+
+ }
+ else if (!feof(file))
+ {
+ printf("Failed to read file: %d, %s", errno, strerror(errno));
+ rc = 1;
+ }
+
+ return rc;
+}
+
+int execute_query_silent(MYSQL *conn, const char *sql)
+{
+ return execute_query1(conn, sql, true);
+}
+
+int execute_query1(MYSQL *conn, const char *sql, bool silent)
+{
+ MYSQL_RES *res;
+ if (conn != NULL)
+ {
+ if (mysql_query(conn, sql) != 0)
+ {
+ if (!silent)
+ {
+ int len = strlen(sql);
+ printf("Error: can't execute SQL-query: %.*s\n", len < 60 ? len : 60, sql);
+ printf("%s\n\n", mysql_error(conn));
+ }
+ return 1;
+ }
+ else
+ {
+ do
+ {
+ res = mysql_store_result(conn);
+ mysql_free_result(res);
+ }
+ while ( mysql_next_result(conn) == 0 );
+ return 0;
+ }
+ }
+ else
+ {
+ if (!silent)
+ {
+ printf("Connection is broken\n");
+ }
+ return 1;
+ }
+}
+
+int execute_query_check_one(MYSQL *conn, const char *sql, const char *expected)
+{
+ int r = 1;
+
+ if (conn != NULL)
+ {
+ const int n_attempts = 3;
+
+ for (int i = 0; i < n_attempts && r != 0; i++)
+ {
+ if (i > 0)
+ {
+ sleep(1);
+ }
+
+ if (mysql_query(conn, sql) != 0)
+ {
+ printf("Error: can't execute SQL-query: %s\n", sql);
+ printf("%s\n\n", mysql_error(conn));
+ break;
+ }
+ else
+ {
+ do
+ {
+ MYSQL_RES *res = mysql_store_result(conn);
+
+ if (res)
+ {
+ if (mysql_num_rows(res) == 1)
+ {
+ MYSQL_ROW row = mysql_fetch_row(res);
+
+ if (row[0] != NULL)
+ {
+ if (strcmp(row[0], expected) == 0)
+ {
+ r = 0;
+ printf("First field is '%s' as expected\n", row[0]);
+ }
+ else
+ {
+ printf("First field is '%s, but expected %s'\n", row[0], expected);
+ }
+ }
+ else
+ {
+ printf("First field is NULL\n");
+ }
+ }
+ else
+ {
+ printf("Number of rows is not 1, it is %llu\n", mysql_num_rows(res));
+ }
+
+ mysql_free_result(res);
+ }
+ }
+ while (mysql_next_result(conn) == 0);
+ }
+ }
+ }
+ else
+ {
+ printf("Connection is broken\n");
+ }
+
+ return r;
+}
+
+int execute_query_affected_rows(MYSQL *conn, const char *sql, my_ulonglong * affected_rows)
+{
+ MYSQL_RES *res;
+ if (conn != NULL)
+ {
+ if (mysql_query(conn, sql) != 0)
+ {
+ printf("Error: can't execute SQL-query: %s\n", sql);
+ printf("%s\n\n", mysql_error(conn));
+ return 1;
+ }
+ else
+ {
+ do
+ {
+ *affected_rows = mysql_affected_rows(conn);
+ res = mysql_store_result(conn);
+ mysql_free_result(res);
+ }
+ while ( mysql_next_result(conn) == 0 );
+ return 0;
+ }
+ }
+ else
+ {
+ printf("Connection is broken\n");
+ return 1;
+ }
+}
+
+int execute_query_num_of_rows(MYSQL *conn, const char *sql, my_ulonglong num_of_rows[],
+ unsigned long long * i)
+{
+ MYSQL_RES *res;
+ my_ulonglong N;
+
+
+ printf("%s\n", sql);
+ if (conn != NULL)
+ {
+ if (mysql_query(conn, sql) != 0)
+ {
+ printf("Error: can't execute SQL-query: %s\n", sql);
+ printf("%s\n\n", mysql_error(conn));
+ * i = 0;
+ return 1;
+ }
+ else
+ {
+ *i = 0;
+ do
+ {
+ res = mysql_store_result(conn);
+ if (res != NULL)
+ {
+ N = mysql_num_rows(res);
+ mysql_free_result(res);
+ }
+ else
+ {
+ N = 0;
+ }
+ num_of_rows[*i] = N;
+ *i = *i + 1;
+ }
+ while ( mysql_next_result(conn) == 0 );
+ return 0;
+ }
+ }
+ else
+ {
+ printf("Connection is broken\n");
+ * i = 0;
+ return 1;
+ }
+}
+
+int execute_stmt_num_of_rows(MYSQL_STMT * stmt, my_ulonglong num_of_rows[], unsigned long long * i)
+{
+ my_ulonglong N;
+
+ /* This is debug hack; compatible only with t1 from t1_sql.h
+ my_ulonglong k;
+ MYSQL_BIND bind[2];
+ my_ulonglong x1;
+ my_ulonglong fl;
+
+ unsigned long length[2];
+ my_bool is_null[2];
+ my_bool error[2];
+
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer = &x1;
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].length = &length[0];
+ bind[0].is_null = &is_null[0];
+ bind[0].error = &error[0];
+
+ bind[1].buffer = &fl;
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].length = &length[0];
+ bind[1].is_null = &is_null[0];
+ bind[1].error = &error[0];
+ */
+
+ if (mysql_stmt_execute(stmt) != 0)
+ {
+ printf("Error: can't execute prepared statement\n");
+ printf("%s\n\n", mysql_stmt_error(stmt));
+ * i = 0;
+ return 1;
+ }
+ else
+ {
+ *i = 0;
+ do
+ {
+ mysql_stmt_store_result(stmt);
+ N = mysql_stmt_num_rows(stmt);
+ /* This is debug hack; compatible only with t1 from t1_sql.h
+ mysql_stmt_bind_result(stmt, bind);
+ for (k = 0; k < N; k++)
+ {
+ mysql_stmt_fetch(stmt);
+ printf("%04llu: x1 %llu, fl %llu\n", k, x1, fl);
+ }
+ */
+ num_of_rows[*i] = N;
+ *i = *i + 1;
+
+ }
+ while ( mysql_stmt_next_result(stmt) == 0 );
+ return 0;
+ }
+ return 1;
+}
+
+int execute_query_count_rows(MYSQL *conn, const char *sql)
+{
+ int rval = -1;
+
+ unsigned long long num_of_rows[1024];
+ unsigned long long total;
+
+ if (execute_query_num_of_rows(conn, sql, num_of_rows, &total) == 0)
+ {
+ rval = 0;
+
+ for (int i = 0; i < total && i < 1024; i++)
+ {
+ rval += num_of_rows[i];
+ }
+ }
+
+ return rval;
+}
+
+int get_conn_num(MYSQL *conn, char * ip, char *hostname, char * db)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ unsigned long long int num_fields;
+ //unsigned long long int row_i=0;
+ unsigned long long int rows;
+ unsigned long long int i;
+ unsigned int conn_num = 0;
+ char * hostname_internal;
+ if (strcmp(ip, "127.0.0.1") == 0)
+ {
+ hostname_internal = (char*) "localhost";
+ }
+ else
+ {
+ hostname_internal = hostname;
+ }
+
+ if (conn != NULL)
+ {
+ if (mysql_query(conn, "show processlist;") != 0)
+ {
+ printf("Error: can't execute SQL-query: show processlist\n");
+ printf("%s\n\n", mysql_error(conn));
+ conn_num = 0;
+ }
+ else
+ {
+ res = mysql_store_result(conn);
+ if (res == NULL)
+ {
+ printf("Error: can't get the result description\n");
+ conn_num = -1;
+ }
+ else
+ {
+ num_fields = mysql_num_fields(res);
+ rows = mysql_num_rows(res);
+ for (i = 0; i < rows; i++)
+ {
+ row = mysql_fetch_row(res);
+ if ( (row[2] != NULL ) && (row[3] != NULL) )
+ {
+ if ((strstr(row[2], ip) != NULL) && (strstr(row[3], db) != NULL))
+ {
+ conn_num++;
+ }
+ if ((strstr(row[2], hostname_internal) != NULL) && (strstr(row[3], db) != NULL))
+ {
+ conn_num++;
+ }
+ }
+ }
+ }
+ mysql_free_result(res);
+ }
+ }
+ if (strcmp(ip, "127.0.0.1") == 0)
+ {
+ // one extra connection i svisible in the processlist
+ // output in case of local test
+ // (when maxscale is on the same machine as backends)
+ conn_num--;
+ }
+ return conn_num;
+}
+
+int find_field(MYSQL *conn, const char *sql, const char *field_name, char * value)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ MYSQL_FIELD *field;
+ unsigned long long int num_fields;
+ unsigned int ret = 1;
+ unsigned long long int filed_i = 0;
+ unsigned long long int i = 0;
+
+ if (conn != NULL )
+ {
+ if (mysql_query(conn, sql) != 0)
+ {
+ printf("Error: can't execute SQL-query: %s\n", sql);
+ printf("%s\n\n", mysql_error(conn));
+ }
+ else
+ {
+ res = mysql_store_result(conn);
+ if (res == NULL)
+ {
+ printf("Error: can't get the result description\n");
+ }
+ else
+ {
+ num_fields = mysql_num_fields(res);
+
+ while ((field = mysql_fetch_field(res)) && ret != 0)
+ {
+ if (strstr(field->name, field_name) != NULL)
+ {
+ filed_i = i;
+ ret = 0;
+ }
+ i++;
+ }
+ if (mysql_num_rows(res) > 0)
+ {
+ row = mysql_fetch_row(res);
+ sprintf(value, "%s", row[filed_i]);
+ }
+ }
+ mysql_free_result(res);
+ do
+ {
+ res = mysql_store_result(conn);
+ mysql_free_result(res);
+ }
+ while ( mysql_next_result(conn) == 0 );
+ }
+ }
+ return ret;
+}
+
+unsigned int get_seconds_behind_master(MYSQL *conn)
+{
+ char SBM_str[16];
+ unsigned int SBM = 0;
+ if (find_field(
+ conn, (char *) "show slave status;",
+ (char *) "Seconds_Behind_Master", &SBM_str[0]
+ ) != 1)
+ {
+ sscanf(SBM_str, "%u", &SBM);
+ }
+ return SBM;
+}
+
+int read_log(char * name, char ** err_log_content_p)
+{
+ FILE *f;
+ *err_log_content_p = NULL;
+ char * err_log_content;
+ f = fopen(name, "rb");
+ if (f != NULL)
+ {
+
+ int prev = ftell(f);
+ fseek(f, 0L, SEEK_END);
+ long int size = ftell(f);
+ fseek(f, prev, SEEK_SET);
+ err_log_content = (char *)malloc(size + 2);
+ if (err_log_content != NULL)
+ {
+ fread(err_log_content, 1, size, f);
+ for (int i = 0; i < size; i++)
+ {
+ if (err_log_content[i] == 0)
+ {
+ //printf("null detected at position %d\n", i);
+ err_log_content[i] = '\n';
+ }
+ }
+ //printf("s=%ld\n", strlen(err_log_content));
+ err_log_content[size] = '\0';
+ //printf("s=%ld\n", strlen(err_log_content));
+ * err_log_content_p = err_log_content;
+ return 0;
+ }
+ else
+ {
+ printf("Error allocationg memory for the log\n");
+ return 1;
+ }
+ }
+ else
+ {
+ printf ("Error reading log %s \n", name);
+ return 1;
+ }
+
+}
+
+int get_int_version(const std::string& version)
+{
+ std::istringstream str(version);
+ int major = 0;
+ int minor = 0;
+ int patch = 0;
+ char dot;
+
+ str >> major >> dot >> minor >> dot >> patch;
+ return major * 10000 + minor * 100 + patch;
+}
+
+int get_int_version(const char* version)
+{
+ return get_int_version(std::string(version));
+}
diff --git a/maxscale-system-test/mariadb_func.h b/maxscale-system-test/mariadb_func.h
new file mode 100644
index 000000000..3090dda9d
--- /dev/null
+++ b/maxscale-system-test/mariadb_func.h
@@ -0,0 +1,235 @@
+#ifndef MARIADB_FUNC_H
+#define MARIADB_FUNC_H
+
+
+/**
+ * @file mariadb_func.h - basic DB interaction routines
+ *
+ * @verbatim
+ * Revision History
+ *
+ * Date Who Description
+ * 17/11/14 Timofey Turenko Initial implementation
+ *
+ * @endverbatim
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Opens connection to DB: wropper over mysql_real_connect
+ *
+ * @param port DB server port
+ * @param ip DB server IP address
+ * @param db name of DB to connect
+ * @param User User name
+ * @param Password Password
+ * @param flag Connections flags
+ * @param ssl true if ssl should be used
+ * @return MYSQL struct or NULL in case of error
+ */
+MYSQL * open_conn_db_flags(int port, const char* ip, const char* db, const char* User, const char* Password,
+ unsigned long flag, bool ssl);
+
+
+/**
+ * Opens connection to DB: wropper over mysql_real_connect
+ *
+ * @param port DB server port
+ * @param ip DB server IP address
+ * @param db name of DB to connect
+ * @param User User name
+ * @param Password Password
+ * @param timeout timeout on seconds
+ * @param ssl true if ssl should be used
+ * @return MYSQL struct or NULL in case of error
+ */
+MYSQL * open_conn_db_timeout(int port, const char* ip, const char* db, const char* User, const char* Password,
+ unsigned long timeout, bool ssl);
+
+MYSQL* open_conn_db_timeout(int port, const std::string& ip, const std::string& db,
+ const std::string& user, const std::string& password,
+ unsigned long timeout, bool ssl);
+
+/**
+ * Opens connection to DB with default flags
+ *
+ * @param port DB server port
+ * @param ip DB server IP address
+ * @param db name of DB to connect
+ * @param User User name
+ * @param Password Password
+ * @param ssl true if ssl should be used
+ * @return MYSQL struct or NULL in case of error
+ */
+MYSQL * open_conn_db(int port, const char* ip, const char* db, const char* User, const char* Password,
+ bool ssl);
+
+
+/**
+ * Opens connection to 'test' with default flags
+ *
+ * @param port DB server port
+ * @param ip DB server IP address
+ * @param User User name
+ * @param Password Password
+ * @param ssl true if ssl should be used
+ * @return MYSQL struct or NULL in case of error
+ */
+MYSQL * open_conn(int port, const char* ip, const char* User, const char* Password, bool ssl);
+
+/**
+ * Opens connection to with default flags without defning DB name (just conecto server)
+ *
+ * @param port DB server port
+ * @param ip DB server IP address
+ * @param User User name
+ * @param Password Password
+ * @param ssl true if ssl should be used
+ * @return MYSQL struct or NULL in case of error
+ */
+MYSQL * open_conn_no_db(int port, const char* ip, const char* User, const char* Password, bool ssl);
+
+/**
+ * @brief set_ssl Configure SSL for given connection
+ * Function assumes that certificates are in test_dir/ssl-cert/ directory
+ * @param conn MYSQL handler
+ * @return return of mysql_ssl_set() (always 0, see mysql_ssl_set() documentation)
+ */
+int set_ssl(MYSQL * conn);
+
+/**
+ * @brief Executes SQL query. Function also executes mysql_store_result() and mysql_free_result() to clean up returns
+ * @param conn MYSQL connection
+ * @param format SQL string with printf style formatting
+ * @param ... Parameters for @c format
+ * @return 0 in case of success
+ */
+int execute_query(MYSQL *conn, const char *format, ...);
+
+/**
+ * @brief execute_query_from_file Read a line from a file, trim leading and trailing whitespace and execute it.
+ * @param conn MYSQL handler
+ * @param file file handler
+ * @return 0 in case of success
+ */
+int execute_query_from_file(MYSQL *conn, FILE *file);
+
+/**
+ * @brief Executes SQL query. Function also executes mysql_store_result() and mysql_free_result() to clean up returns
+ * @param conn MYSQL connection struct
+ * @param sql SQL string
+ * @return 0 in case of success
+ */
+int execute_query_silent(MYSQL *conn, const char *sql);
+
+/**
+ * @brief Executes SQL query. Function also executes mysql_store_result() and mysql_free_result() to clean up returns
+ * This function do not support 'printf' format for sql (in compare with execute_query()
+ * @param conn MYSQL connection struct
+ * @param sql SQL string
+ * @param silent if true function do not produce any printing
+ * @return 0 in case of success
+ */
+int execute_query1(MYSQL *conn, const char *sql, bool silent);
+
+/**
+ * @brief Executes SQL query and store 'affected rows' number in affectet_rows parameter
+ * @param conn MYSQL connection struct
+ * @param sql SQL string
+ * @param affected_rows pointer to variabe to store number of affected rows
+ * @return 0 in case of success
+ */
+int execute_query_affected_rows(MYSQL *conn, const char *sql, my_ulonglong * affected_rows);
+
+/**
+* @brief A more convenient form of execute_query_affected_rows()
+*
+* @param conn Connection to use for the query
+* @param sql The SQL statement to execute
+* @return Number of rows or -1 on error
+*/
+int execute_query_count_rows(MYSQL *conn, const char *sql);
+
+/**
+ * @brief Executes SQL query and get number of rows in the result
+ * This function does not check boudaries of 'num_of_rows' array. This
+ * array have to be big enough to store all results
+ * @param conn MYSQL connection struct
+ * @param sql SQL string
+ * @param num_of_rows pointer to array to store number of result rows
+ * @param i pointer to variable to store number of result sets
+ * @return 0 in case of success
+ */
+int execute_query_num_of_rows(MYSQL *conn, const char *sql, my_ulonglong num_of_rows[],
+ unsigned long long *i);
+
+/**
+ * @brief Executes perared statement and get number of rows in the result
+ * This function does not check boudaries of 'num_of_rows' array. This
+ * array have to be big enough to store all results
+ * @param stmt MYSQL_STMT statetement struct (from mysql_stmt_init())
+ * @param num_of_rows pointer to array to store number of result rows
+ * @param i pointer to variable to store number of result sets
+ * @return 0 in case of success
+ */
+int execute_stmt_num_of_rows(MYSQL_STMT *stmt, my_ulonglong num_of_rows[], unsigned long long * i);
+
+/**
+ * @brief execute_query_check_one Executes query and check if first field of first row is equal to 'expected'
+ * @param conn MYSQL handler
+ * @param sql query SQL query to execute
+ * @param expected Expected result
+ * @return 0 in case of success
+ */
+int execute_query_check_one(MYSQL *conn, const char *sql, const char *expected);
+
+/**
+ * @brief Executes 'show processlist' and calculates number of connections from defined host to defined DB
+ * @param conn MYSQL connection struct
+ * @param ip connections from this IP address are counted
+ * @param db name of DB to which connections are counted
+ * @return number of connections
+ */
+int get_conn_num(MYSQL *conn, char * ip, char * hostname, char * db);
+
+/**
+ * @brief Find given filed in the SQL query reply
+ * Function checks only firs row from the table
+ * @param conn MYSQL connection struct
+ * @param sql SQL query to execute
+ * @param filed_name name of field to find
+ * @param value pointer to variable to store value of found field
+ * @return 0 in case of success
+ */
+int find_field(MYSQL *conn, const char * sql, const char * field_name, char * value);
+
+/**
+ * @brief Return the value of SECONDS_BEHIND_MASTER
+ * @param conn MYSQL connection struct
+ * @return value of SECONDS_BEHIND_MASTER
+ */
+unsigned int get_seconds_behind_master(MYSQL *conn);
+
+
+/**
+ * @brief Read MaxScale log file
+ * @param name Name of log file (full path)
+ * @param err_log_content pointer to the buffer to store log file content
+ * @return 0 in case of success, 1 in case of error
+ */
+int read_log(char * name, char **err_log_content_p);
+
+int get_int_version(const std::string& version);
+int get_int_version(const char* version);
+
+#endif // MARIADB_FUNC_H
diff --git a/maxscale-system-test/mariadb_nodes.cpp b/maxscale-system-test/mariadb_nodes.cpp
new file mode 100644
index 000000000..43fe57237
--- /dev/null
+++ b/maxscale-system-test/mariadb_nodes.cpp
@@ -0,0 +1,1369 @@
+/**
+ * @file mariadb_nodes.cpp - backend nodes routines
+ *
+ * @verbatim
+ * Revision History
+ *
+ * Date Who Description
+ * 17/11/14 Timofey Turenko Initial implementation
+ *
+ * @endverbatim
+ */
+
+#include "mariadb_nodes.h"
+#include "sql_const.h"
+#include
+#include
+#include
+
+Mariadb_nodes::Mariadb_nodes(const char *pref, const char *test_cwd, bool verbose):
+ v51(false), use_ipv6(false)
+{
+ strcpy(prefix, pref);
+ memset(this->nodes, 0, sizeof(this->nodes));
+ memset(blocked, 0, sizeof(blocked));
+ no_set_pos = false;
+ this->verbose = verbose;
+ strcpy(test_dir, test_cwd);
+ read_env();
+ truncate_mariadb_logs();
+ flush_hosts();
+ close_active_connections();
+}
+
+Mariadb_nodes::~Mariadb_nodes()
+{
+ for (int i = 0; i < N; i++)
+ {
+ if (blocked[i])
+ {
+ unblock_node(i);
+ }
+ }
+}
+
+int Mariadb_nodes::connect()
+{
+ int res = 0;
+
+ for (int i = 0; i < N; i++)
+ {
+ if (nodes[i] == NULL || mysql_ping(nodes[i]) != 0)
+ {
+ if (nodes[i])
+ {
+ mysql_close(nodes[i]);
+ }
+ nodes[i] = open_conn_db_timeout(port[i], IP[i], "test", user_name, password, 50, ssl);
+ }
+
+ if ((nodes[i] != NULL) && (mysql_errno(nodes[i]) != 0))
+ {
+ res++;
+ }
+ }
+
+ return res;
+}
+
+void Mariadb_nodes::close_connections()
+{
+ for (int i = 0; i < N; i++)
+ {
+ if (nodes[i] != NULL)
+ {
+ mysql_close(nodes[i]);
+ nodes[i] = NULL;
+ }
+ }
+}
+
+int Mariadb_nodes::read_env()
+{
+ char * env;
+ char env_name[64];
+
+ sprintf(env_name, "%s_N", prefix);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sscanf(env, "%d", &N);
+ }
+ else
+ {
+ N = 0;
+ }
+
+ sprintf(env_name, "%s_user", prefix);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sscanf(env, "%s", user_name);
+ }
+ else
+ {
+ sprintf(user_name, "skysql");
+ }
+ sprintf(env_name, "%s_password", prefix);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sscanf(env, "%s", password);
+ }
+ else
+ {
+ sprintf(password, "skysql");
+ }
+
+ ssl = false;
+ sprintf(env_name, "%s_ssl", prefix);
+ env = getenv(env_name);
+ if ((env != NULL) && ((strcasecmp(env, "yes") == 0) || (strcasecmp(env, "true") == 0) ))
+ {
+ ssl = true;
+ }
+
+
+ if ((N > 0) && (N < 255))
+ {
+ for (int i = 0; i < N; i++)
+ {
+ //reading IPs
+ sprintf(env_name, "%s_%03d_network", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(IP[i], "%s", env);
+ }
+
+ //reading private IPs
+ sprintf(env_name, "%s_%03d_private_ip", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(IP_private[i], "%s", env);
+ }
+ else
+ {
+ sprintf(IP_private[i], "%s", IP[i]);
+ }
+
+ //reading IPv6
+ sprintf(env_name, "%s_%03d_network6", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(IP6[i], "%s", env);
+ }
+ else
+ {
+ sprintf(IP6[i], "%s", IP[i]);
+ }
+
+ //reading ports
+ sprintf(env_name, "%s_%03d_port", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sscanf(env, "%d", &port[i]);
+ }
+ else
+ {
+ port[i] = 3306;
+ }
+ //reading sockets
+ sprintf(env_name, "%s_%03d_socket", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(socket[i], "%s", env);
+ sprintf(socket_cmd[i], "--socket=%s", env);
+ }
+ else
+ {
+ sprintf(socket[i], " ");
+ sprintf(socket_cmd[i], " ");
+ }
+ //reading sshkey
+ sprintf(env_name, "%s_%03d_keyfile", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(sshkey[i], "%s", env);
+ }
+ else
+ {
+ sprintf(sshkey[i], "vagrant.pem");
+ }
+
+ //reading start_db_command
+ sprintf(env_name, "%s_%03d_start_db_command", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(start_db_command[i], "%s", env);
+ }
+ else
+ {
+ sprintf(start_db_command[i], "%s", "service mysql start");
+ }
+
+ //reading stop_db_command
+ sprintf(env_name, "%s_%03d_stop_db_command", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(stop_db_command[i], "%s", env);
+ }
+ else
+ {
+ sprintf(start_db_command[i], "%s", "service mysql stop");
+ }
+
+ //reading cleanup_db_command
+ sprintf(env_name, "%s_%03d_cleanup_db_command", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(cleanup_db_command[i], "%s", env);
+ }
+ else
+ {
+ sprintf(cleanup_db_command[i], " ");
+ }
+
+ sprintf(env_name, "%s_%03d_whoami", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(access_user[i], "%s", env);
+ }
+ else
+ {
+ sprintf(access_user[i], "root");
+ }
+
+ sprintf(env_name, "%s_%03d_access_sudo", prefix, i);
+ env = getenv(env_name);
+ if (env != NULL)
+ {
+ sprintf(access_sudo[i], "%s", env);
+ }
+ else
+ {
+ sprintf(access_sudo[i], " ");
+ }
+
+ if (strcmp(access_user[i], "root") == 0)
+ {
+ sprintf(access_homedir[i], "/%s/", access_user[i]);
+ }
+ else
+ {
+ sprintf(access_homedir[i], "/home/%s/", access_user[i]);
+ }
+
+ }
+ }
+}
+
+int Mariadb_nodes::print_env()
+{
+ for (int i = 0; i < N; i++)
+ {
+ printf("%s node %d \t%s\tPort=%d\n", prefix, i, IP[i], port[i]);
+ printf("%s Access user %s\n", prefix, access_user[i]);
+ }
+ printf("%s User name %s\n", prefix, user_name);
+ printf("%s Password %s\n", prefix, password);
+
+}
+
+int Mariadb_nodes::find_master()
+{
+ char str[255];
+ char master_IP[256];
+ int i = 0;
+ int found = 0;
+ int master_node = 255;
+ while ((found == 0) && (i < N))
+ {
+ if (find_field(
+ nodes[i], (char *) "show slave status;",
+ (char *) "Master_Host", &str[0]
+ ) == 0 )
+ {
+ found = 1;
+ strcpy(master_IP, str);
+ }
+ i++;
+ }
+ if (found == 1)
+ {
+ found = 0;
+ i = 0;
+ while ((found == 0) && (i < N))
+ {
+ if (strcmp(IP[i], master_IP) == 0)
+ {
+ found = 1;
+ master_node = i;
+ }
+ i++;
+ }
+ }
+ return master_node;
+}
+
+int Mariadb_nodes::change_master(int NewMaster, int OldMaster)
+{
+ int i;
+ //int OldMaster = FindMaster();
+ char log_file[256];
+ char log_pos[256];
+ char str[1024];
+
+ for (i = 0; i < N; i++)
+ {
+ if (i != OldMaster)
+ {
+ execute_query(nodes[i], (char *) "stop slave;");
+ }
+ }
+ execute_query(nodes[NewMaster], create_repl_user);
+ execute_query(nodes[OldMaster], (char *) "reset master;");
+ find_field(nodes[NewMaster], (char *) "show master status", (char *) "File", &log_file[0]);
+ find_field(nodes[NewMaster], (char *) "show master status", (char *) "Position", &log_pos[0]);
+ for (i = 0; i < N; i++)
+ {
+ if (i != NewMaster)
+ {
+ sprintf(str, setup_slave, IP[NewMaster], log_file, log_pos, port[NewMaster]);
+ execute_query(nodes[i], str);
+ }
+ }
+ //for (i = 0; i < N; i++) {if (i != NewMaster) {execute_query(nodes[i], (char *) "start slave;"); }}
+}
+
+int Mariadb_nodes::stop_node(int node)
+{
+ return ssh_node(node, stop_db_command[node], true);
+}
+
+int Mariadb_nodes::start_node(int node, char * param)
+{
+ char cmd[1024];
+ if (v51)
+ {
+ sprintf(cmd, "%s %s --report-host", start_db_command[node], param);
+ }
+ else
+ {
+ sprintf(cmd, "%s %s", start_db_command[node], param);
+ }
+ return ssh_node(node, cmd, true);
+}
+
+int Mariadb_nodes::stop_nodes()
+{
+ int i;
+ int local_result = 0;
+ connect();
+ for (i = 0; i < N; i++)
+ {
+ printf("Stopping node %d\n", i);
+ fflush(stdout);
+ local_result += execute_query(nodes[i], (char *) "stop slave;");
+ fflush(stdout);
+ local_result += stop_node(i);
+ fflush(stdout);
+ }
+ return local_result;
+}
+
+int Mariadb_nodes::stop_slaves()
+{
+ int i;
+ int global_result = 0;
+ connect();
+ for (i = 0; i < N; i++)
+ {
+ printf("Stopping slave %d\n", i);
+ fflush(stdout);
+ global_result += execute_query(nodes[i], (char *) "stop slave;");
+ }
+ close_connections();
+ return global_result;
+}
+
+int Mariadb_nodes::cleanup_db_node(int node)
+{
+ return ssh_node(node, cleanup_db_command[node], true);
+}
+
+int Mariadb_nodes::cleanup_db_nodes()
+{
+ int i;
+ int local_result = 0;
+
+ for (i = 0; i < N; i++)
+ {
+ printf("Cleaning node %d\n", i);
+ fflush(stdout);
+ local_result += cleanup_db_node(i);
+ fflush(stdout);
+ }
+ return local_result;
+}
+
+
+
+int Mariadb_nodes::start_replication()
+{
+ char str[1024];
+ char dtr[1024];
+ int local_result = 0;
+
+ // Start all nodes
+ for (int i = 0; i < N; i++)
+ {
+ local_result += start_node(i, (char *) "");
+ sprintf(str,
+ "mysql -u root %s -e \"STOP SLAVE; RESET SLAVE; RESET SLAVE ALL; RESET MASTER; SET GLOBAL read_only=OFF;\"",
+ socket_cmd[i]);
+ ssh_node(i, str, true);
+ }
+
+ sprintf(str, "%s/create_user.sh", test_dir);
+ sprintf(dtr, "%s", access_homedir[0]);
+ copy_to_node(str, dtr , 0);
+ sprintf(str, "export node_user=\"%s\"; export node_password=\"%s\"; %s/create_user.sh %s",
+ user_name, password, access_homedir[0], socket_cmd[0]);
+ printf("cmd: %s\n", str);
+ ssh_node(0, str, false);
+
+ // Create a database dump from the master and distribute it to the slaves
+ sprintf(str,
+ "mysqldump --all-databases --add-drop-database --flush-privileges --master-data=1 --gtid %s > /tmp/master_backup.sql",
+ socket_cmd[0]);
+ ssh_node(0, str, true);
+ sprintf(str, "%s/master_backup.sql", test_dir);
+ copy_from_node("/tmp/master_backup.sql", str, 0);
+
+ for (int i = 1; i < N; i++)
+ {
+ // Reset all nodes by first loading the dump and then starting the replication
+ printf("Starting node %d\n", i);
+ fflush(stdout);
+ copy_to_node(str, "/tmp/master_backup.sql", i);
+ sprintf(dtr,
+ "mysql -u root %s < /tmp/master_backup.sql",
+ socket_cmd[i]);
+ ssh_node(i, dtr, true);
+ char query[512];
+
+ sprintf(query, "mysql -u root %s -e \"CHANGE MASTER TO MASTER_HOST=\\\"%s\\\", MASTER_PORT=%d, "
+ "MASTER_USER=\\\"repl\\\", MASTER_PASSWORD=\\\"repl\\\";"
+ "START SLAVE;\"", socket_cmd[i], IP_private[0], port[0]);
+ ssh_node(i, query, true);
+ }
+
+ return local_result;
+}
+
+int Galera_nodes::start_galera()
+{
+ char sys1[4096];
+ char str[1024];
+ int i;
+ int local_result = 0;
+ local_result += stop_nodes();
+
+ // Remove the grastate.dat file
+ ssh_node(0, "rm -f /var/lib/mysql/grastate.dat", true);
+
+ printf("Starting new Galera cluster\n");
+ fflush(stdout);
+ ssh_node(0, "echo [mysqld] > cluster_address.cnf", false);
+ ssh_node(0, "echo wsrep_cluster_address=gcomm:// >> cluster_address.cnf", false);
+ ssh_node(0, "cp cluster_address.cnf /etc/my.cnf.d/", true);
+ local_result += start_node(0, (char *) " --wsrep-cluster-address=gcomm://");
+
+ sprintf(str, "%s/create_user_galera.sh", test_dir);
+ copy_to_node(str, "~/", 0);
+
+ sprintf(str, "export galera_user=\"%s\"; export galera_password=\"%s\"; ./create_user_galera.sh %s", user_name,
+ password, socket_cmd[0]);
+ ssh_node(0, str, false);
+
+ for (i = 1; i < N; i++)
+ {
+ printf("Starting node %d\n", i);
+ fflush(stdout);
+ ssh_node(i, "echo [mysqld] > cluster_address.cnf", true);
+ sprintf(str, "echo wsrep_cluster_address=gcomm://%s >> cluster_address.cnf", IP_private[0]);
+ ssh_node(i, str, true);
+ ssh_node(i, "cp cluster_address.cnf /etc/my.cnf.d/", true);
+
+ sprintf(&sys1[0], " --wsrep-cluster-address=gcomm://%s", IP_private[0]);
+ if (this->verbose)
+ {
+ printf("%s\n", sys1);
+ fflush(stdout);
+ }
+ local_result += start_node(i, sys1);
+ fflush(stdout);
+ }
+ sleep(5);
+
+ local_result += connect();
+ local_result += execute_query(nodes[0], create_repl_user);
+
+ close_connections();
+ return local_result;
+}
+
+int Mariadb_nodes::clean_iptables(int node)
+{
+ char sys1[1024];
+ int local_result = 0;
+
+ local_result += ssh_node(node, (char *) "echo \"#!/bin/bash\" > clean_iptables.sh", false);
+ sprintf(sys1,
+ "echo \"while [ \\\"\\$(iptables -n -L INPUT 1|grep '%d')\\\" != \\\"\\\" ]; do iptables -D INPUT 1; done\" >> clean_iptables.sh",
+ port[node]);
+ local_result += ssh_node(node, (char *) sys1, false);
+ sprintf(sys1,
+ "echo \"while [ \\\"\\$(ip6tables -n -L INPUT 1|grep '%d')\\\" != \\\"\\\" ]; do ip6tables -D INPUT 1; done\" >> clean_iptables.sh",
+ port[node]);
+ local_result += ssh_node(node, (char *) sys1, false);
+
+ local_result += ssh_node(node, (char *) "chmod a+x clean_iptables.sh", false);
+ local_result += ssh_node(node, (char *) "./clean_iptables.sh", true);
+ return local_result;
+}
+
+int Mariadb_nodes::block_node(int node)
+{
+ char sys1[1024];
+ int local_result = 0;
+ local_result += clean_iptables(node);
+ sprintf(&sys1[0], "iptables -I INPUT -p tcp --dport %d -j REJECT", port[node]);
+ if (this->verbose)
+ {
+ printf("%s\n", sys1);
+ fflush(stdout);
+ }
+ local_result += ssh_node(node, sys1, true);
+
+ sprintf(&sys1[0], "ip6tables -I INPUT -p tcp --dport %d -j REJECT", port[node]);
+ if (this->verbose)
+ {
+ printf("%s\n", sys1);
+ fflush(stdout);
+ }
+ local_result += ssh_node(node, sys1, true);
+
+ blocked[node] = true;
+ return local_result;
+}
+
+int Mariadb_nodes::unblock_node(int node)
+{
+ char sys1[1024];
+ int local_result = 0;
+ local_result += clean_iptables(node);
+ sprintf(&sys1[0], "iptables -I INPUT -p tcp --dport %d -j ACCEPT", port[node]);
+ if (this->verbose)
+ {
+ printf("%s\n", sys1);
+ fflush(stdout);
+ }
+ local_result += ssh_node(node, sys1, true);
+ sprintf(&sys1[0], "ip6tables -I INPUT -p tcp --dport %d -j ACCEPT", port[node]);
+ if (this->verbose)
+ {
+ printf("%s\n", sys1);
+ fflush(stdout);
+ }
+ local_result += ssh_node(node, sys1, true);
+
+ blocked[node] = false;
+ return local_result;
+}
+
+
+int Mariadb_nodes::unblock_all_nodes()
+{
+ int rval = 0;
+ for (int i = 0; i < this->N; i++)
+ {
+ rval += this->unblock_node(i);
+ }
+ return rval;
+}
+
+int Mariadb_nodes::check_node_ssh(int node)
+{
+ int res = 0;
+ printf("Checking node %d\n", node);
+ fflush(stdout);
+
+ if (ssh_node(0, (char *) "ls > /dev/null", false) != 0)
+ {
+ printf("Node %d is not available\n", node);
+ fflush(stdout);
+ res = 1;
+ }
+ else
+ {
+ printf("Node %d is OK\n", node);
+ fflush(stdout);
+ }
+ return res;
+}
+
+int Mariadb_nodes::check_nodes()
+{
+ int res = 0;
+ for (int i = 0; i < N; i++)
+ {
+ res += check_node_ssh(i);
+ }
+ return res;
+}
+
+bool Mariadb_nodes::check_master_node(MYSQL *conn)
+{
+ bool rval = true;
+
+ if (mysql_query(conn, "SHOW SLAVE HOSTS"))
+ {
+ printf("%s\n", mysql_error(conn));
+ rval = false;
+ }
+ else
+ {
+ MYSQL_RES *res = mysql_store_result(conn);
+
+ if (res)
+ {
+ int rows = mysql_num_rows(res);
+
+ if (rows != N - 1)
+ {
+ if (!v51)
+ {
+ printf("Number of slave hosts is %d when it should be %d\n", rows, N - 1);
+ rval = false;
+ }
+ }
+ }
+ mysql_free_result(res);
+ }
+
+ if (mysql_query(conn, "SHOW SLAVE STATUS"))
+ {
+ printf("%s\n", mysql_error(conn));
+ rval = false;
+ }
+ else
+ {
+ MYSQL_RES *res = mysql_store_result(conn);
+
+ if (res)
+ {
+ if (mysql_num_rows(res) > 0)
+ {
+ printf("The master is configured as a slave\n");
+ rval = false;
+ }
+ mysql_free_result(res);
+ }
+ }
+
+ char output[512];
+ find_field(conn, "SHOW VARIABLES LIKE 'read_only'", "Value", output);
+
+ if (strcmp(output, "OFF"))
+ {
+ printf("The master is in read-only mode\n");
+ rval = false;
+ }
+
+ return rval;
+}
+
+static bool bad_slave_thread_status(MYSQL *conn, const char *field, int node)
+{
+ char str[1024] = "";
+ bool rval = false;
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (find_field(conn, "SHOW SLAVE STATUS;", field, str) != 0)
+ {
+ printf("Node %d: %s not found in SHOW SLAVE STATUS\n", node, field);
+ fflush(stdout);
+ break;
+ }
+ else if (strcmp(str, "Yes") == 0 || strcmp(str, "No") == 0)
+ {
+ break;
+ }
+
+ /** Any other state is transient and we should try again */
+ sleep(1);
+ }
+
+ if (strcmp(str, "Yes") != 0)
+ {
+ printf("Node %d: %s is '%s'\n", node, field, str);
+ fflush(stdout);
+ rval = true;
+ }
+ return rval;
+}
+
+int Mariadb_nodes::check_replication()
+{
+ int master = 0;
+ int res = 0;
+ char str[1024];
+
+ if (verbose)
+ {
+ printf("Checking Master/Slave setup\n");
+ fflush(stdout);
+ }
+
+ if (this->connect())
+ {
+ return 1;
+ }
+
+ res = get_versions();
+
+ for (int i = 0; i < N && res == 0; i++)
+ {
+ if (i == master)
+ {
+ if (!check_master_node(nodes[i]))
+ {
+ res = 1;
+ }
+ }
+ else if (bad_slave_thread_status(nodes[i], "Slave_IO_Running", i) ||
+ bad_slave_thread_status(nodes[i], "Slave_SQL_Running", i))
+ {
+ res = 1;
+ }
+ }
+
+ return res;
+}
+
+bool Mariadb_nodes::fix_replication()
+{
+ if (check_replication())
+ {
+ unblock_all_nodes();
+
+ if (check_nodes())
+ {
+ printf("****** VMS ARE BROKEN! Exiting *****\n");
+ return false;
+ }
+
+ int attempts = 2;
+ int attempts_with_cleanup = 2;
+
+ while (check_replication() && attempts > 0)
+ {
+ if (attempts != 2)
+ {
+ stop_nodes();
+ }
+
+ start_replication();
+ close_connections();
+
+ attempts--;
+
+ if (attempts == 0 && check_replication())
+ {
+ if (attempts_with_cleanup > 0)
+ {
+ printf("****** BACKEND IS STILL BROKEN! Trying to cleanup all nodes *****\n");
+ stop_nodes();
+ cleanup_db_nodes();
+ attempts_with_cleanup--;
+ attempts = 2;
+ sleep(30);
+ start_replication();
+ sleep(30);
+ }
+ else
+ {
+ printf("****** BACKEND IS STILL BROKEN! Exiting *****\n");
+ return false;
+ }
+ }
+ }
+ flush_hosts();
+ }
+
+ return true;
+}
+
+int Galera_nodes::check_galera()
+{
+ int res1 = 0;
+
+ if (verbose)
+ {
+ printf("Checking Galera\n");
+ fflush(stdout);
+ }
+
+ if (this->nodes[0] == NULL)
+ {
+ this->connect();
+ }
+
+ res1 = get_versions();
+
+ for (int i = 0; i < N; i++)
+ {
+ MYSQL *conn = open_conn(port[i], IP[i], user_name, password, ssl);
+ if (conn == NULL || mysql_errno(conn) != 0)
+ {
+ printf("Error connectiong node %d: %s\n", i, mysql_error(conn));
+ res1 = 1;
+ }
+ else
+ {
+ char str[1024] = "";
+
+ if (find_field(conn, (char *) "SHOW STATUS WHERE Variable_name='wsrep_cluster_size';", (char *) "Value",
+ str) != 0)
+ {
+ printf("wsrep_cluster_size is not found in SHOW STATUS LIKE 'wsrep%%' results\n");
+ fflush(stdout);
+ res1 = 1;
+ }
+ else
+ {
+ int cluster_size;
+ sscanf(str, "%d", &cluster_size);
+ if (cluster_size != N)
+ {
+ printf("wsrep_cluster_size is not %d, it is %d\n", N, cluster_size);
+ fflush(stdout);
+ res1 = 1;
+ }
+ }
+ }
+ mysql_close(conn);
+ }
+
+ return res1;
+}
+
+int Mariadb_nodes::set_slave(MYSQL * conn, char master_host[], int master_port, char log_file[],
+ char log_pos[])
+{
+ char str[1024];
+
+ sprintf(str, setup_slave, master_host, log_file, log_pos, master_port);
+ if (no_set_pos)
+ {
+ sprintf(str, setup_slave_no_pos, master_host, master_port);
+ }
+
+ if (this->verbose)
+ {
+ printf("Setup slave SQL: %s\n", str);
+ }
+ return execute_query(conn, str);
+}
+
+int Mariadb_nodes::set_repl_user()
+{
+ int global_result = 0;
+ global_result += connect();
+ for (int i = 0; i < N; i++)
+ {
+ global_result += execute_query(nodes[i], create_repl_user);
+ }
+ close_connections();
+ return global_result;
+}
+
+int Mariadb_nodes::get_server_id(int index)
+{
+ int id = -1;
+ char str[1024];
+
+ if (find_field(this->nodes[index], "SELECT @@server_id", "@@server_id", (char*) str) == 0)
+ {
+ id = atoi(str);
+ }
+ else
+ {
+ printf("find_field failed for %s:%d\n", this->IP[index], this->port[index]);
+ }
+
+ return id;
+}
+
+void Mariadb_nodes::generate_ssh_cmd(char *cmd, int node, const char *ssh, bool sudo)
+{
+ if (strcmp(IP[node], "127.0.0.1") == 0)
+ {
+ if (sudo)
+ {
+ sprintf(cmd, "%s %s",
+ access_sudo[node], ssh);
+ }
+ else
+ {
+ sprintf(cmd, "%s",
+ ssh);
+
+ }
+ }
+ else
+ {
+
+ if (sudo)
+ {
+ sprintf(cmd,
+ "ssh -i %s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s '%s %s\'",
+ sshkey[node], access_user[node], IP[node], access_sudo[node], ssh);
+ }
+ else
+ {
+ sprintf(cmd,
+ "ssh -i %s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s '%s'",
+ sshkey[node], access_user[node], IP[node], ssh);
+ }
+ }
+}
+
+char * Mariadb_nodes::ssh_node_output(int node, const char *ssh, bool sudo, int *exit_code)
+{
+ char sys[strlen(ssh) + 1024];
+ generate_ssh_cmd(sys, node, ssh, sudo);
+ FILE *output = popen(sys, "r");
+ if (output == NULL)
+ {
+ printf("Error opening ssh %s\n", strerror(errno));
+ return NULL;
+ }
+ char buffer[1024];
+ size_t rsize = sizeof(buffer);
+ char* result = (char*)calloc(rsize, sizeof(char));
+
+ while (fgets(buffer, sizeof(buffer), output))
+ {
+ result = (char*)realloc(result, sizeof(buffer) + rsize);
+ rsize += sizeof(buffer);
+ strcat(result, buffer);
+ }
+ int code = pclose(output);
+ if (WIFEXITED(code))
+ {
+ * exit_code = WEXITSTATUS(code);
+ }
+ else
+ {
+ * exit_code = 256;
+ }
+ return result;
+}
+
+int Mariadb_nodes::ssh_node(int node, const char *ssh, bool sudo)
+{
+ char sys[strlen(ssh) + 1024];
+ generate_ssh_cmd(sys, node, ssh, sudo);
+ int return_code = system(sys);
+ if (WIFEXITED(return_code))
+ {
+ return WEXITSTATUS(return_code);
+ }
+ else
+ {
+ return 256;
+ }
+}
+
+int Mariadb_nodes::flush_hosts()
+{
+
+ if (this->nodes[0] == NULL && this->connect())
+ {
+ return 1;
+ }
+
+ int local_result = 0;
+
+ for (int i = 0; i < N; i++)
+ {
+ if (mysql_query(nodes[i], "FLUSH HOSTS"))
+ {
+ local_result++;
+ }
+
+ if (mysql_query(nodes[i], "SET GLOBAL max_connections=10000"))
+ {
+ local_result++;
+ }
+
+ if (mysql_query(nodes[i], "SELECT CONCAT('\\'', user, '\\'@\\'', host, '\\'') FROM mysql.user WHERE user = ''") == 0)
+ {
+ MYSQL_RES *res = mysql_store_result(nodes[i]);
+
+ if (res)
+ {
+ std::vector users;
+ MYSQL_ROW row;
+
+ while ((row = mysql_fetch_row(res)))
+ {
+ users.push_back(row[0]);
+ }
+
+ mysql_free_result(res);
+
+ if (users.size() > 0)
+ {
+ printf("Detected anonymous users, dropping them.\n");
+
+ for (auto& s: users)
+ {
+ std::string query = "DROP USER ";
+ query += s;
+ printf("%s\n", query.c_str());
+ mysql_query(nodes[i], query.c_str());
+ }
+ }
+ }
+ }
+ else
+ {
+ printf("Failed to query for anonymous users: %s\n", mysql_error(nodes[i]));
+ local_result++;
+ }
+ }
+
+ return local_result;
+}
+
+int Mariadb_nodes::execute_query_all_nodes(const char* sql)
+{
+ int local_result = 0;
+ connect();
+ for (int i = 0; i < N; i++)
+ {
+ local_result += execute_query(nodes[i], sql);
+ }
+ close_connections();
+ return local_result;
+}
+
+int Mariadb_nodes::get_versions()
+{
+ int local_result = 0;
+ char * str;
+ v51 = false;
+
+ for (int i = 0; i < N; i++)
+ {
+ if ((local_result += find_field(nodes[i], (char *) "SELECT @@version", (char *) "@@version", version[i])))
+ {
+ printf("Failed to get version: %s\n", mysql_error(nodes[i]));
+ }
+ strcpy(version_number[i], version[i]);
+ str = strchr(version_number[i], '-');
+ if (str != NULL)
+ {
+ str[0] = 0;
+ }
+ strcpy(version_major[i], version_number[i]);
+ if (strstr(version_major[i], "5.") == version_major[i])
+ {
+ version_major[i][3] = 0;
+ }
+ if (strstr(version_major[i], "10.") == version_major[i])
+ {
+ version_major[i][4] = 0;
+ }
+
+ if (verbose)
+ {
+ printf("Node %s%d: %s\t %s \t %s\n", prefix, i, version[i], version_number[i], version_major[i]);
+ }
+ }
+
+ for (int i = 0; i < N; i++)
+ {
+ if (strcmp(version_major[i], "5.1") == 0)
+ {
+ v51 = true;
+ }
+ }
+
+ return local_result;
+}
+
+std::string Mariadb_nodes::get_lowest_version()
+{
+ std::string rval;
+ get_versions();
+
+ int lowest = INT_MAX;
+
+ for (int i = 0; i < N; i++)
+ {
+ int int_version = get_int_version(version[i]);
+
+ if (lowest > int_version)
+ {
+ rval = version[i];
+ lowest = int_version;
+ }
+ }
+
+ return rval;
+}
+
+int Mariadb_nodes::truncate_mariadb_logs()
+{
+ int local_result = 0;
+ for (int node = 0; node < N; node++)
+ {
+ char sys[1024];
+ if (strcmp(IP[node], "127.0.0.1") !=0)
+ {
+ sprintf(sys,
+ "ssh -i %s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s 'sudo truncate /var/lib/mysql/*.err --size 0;sudo rm -f /etc/my.cnf.d/binlog_enc*\' &",
+ sshkey[node], access_user[node], IP[node]);
+ local_result += system(sys);
+ }
+ }
+ return local_result;
+}
+
+int Mariadb_nodes::configure_ssl(bool require)
+{
+ int local_result = 0;
+ char str[1024];
+
+ this->ssl = 1;
+
+ for (int i = 0; i < N; i++)
+ {
+ printf("Node %d\n", i);
+ stop_node(i);
+ sprintf(str, "%s/ssl-cert", test_dir);
+ local_result += copy_to_node(str, (char *) "~/", i);
+ sprintf(str, "%s/ssl.cnf", test_dir);
+ local_result += copy_to_node(str, (char *) "~/", i);
+ local_result += ssh_node(i, (char *) "cp ~/ssl.cnf /etc/my.cnf.d/", true);
+ local_result += ssh_node(i, (char *) "cp -r ~/ssl-cert /etc/", true);
+ local_result += ssh_node(i, (char *) "chown mysql:mysql -R /etc/ssl-cert", true);
+ start_node(i, (char *) "");
+ }
+
+ if (require)
+ {
+ // Create DB user on first node
+ printf("Set user to require ssl: %s\n", str);
+ sprintf(str, "%s/create_user_ssl.sh", test_dir);
+ copy_to_node(str, (char *) "~/", 0);
+
+ sprintf(str, "export node_user=\"%s\"; export node_password=\"%s\"; ./create_user_ssl.sh %s",
+ user_name,
+ password,
+ socket_cmd[0]);
+ printf("cmd: %s\n", str);
+ ssh_node(0, str, false);
+ }
+
+ return local_result;
+}
+
+int Mariadb_nodes::disable_ssl()
+{
+ int local_result = 0;
+ char str[1024];
+
+ local_result += connect();
+ sprintf(str, "DROP USER %s; grant all privileges on *.* to '%s'@'%%' identified by '%s';", user_name,
+ user_name, password);
+ local_result += execute_query(nodes[0], (char *) "");
+ close_connections();
+
+ for (int i = 0; i < N; i++)
+ {
+ stop_node(i);
+ local_result += ssh_node(i, (char *) "rm -f /etc/my.cnf.d/ssl.cnf", true);
+ start_node(i, (char *) "");
+ }
+
+ return local_result;
+}
+
+int Mariadb_nodes::copy_to_node(const char* src, const char* dest, int i)
+{
+ if (i >= N)
+ {
+ return 1;
+ }
+ char sys[strlen(src) + strlen(dest) + 1024];
+
+ if (strcmp(IP[i], "127.0.0.1") == 0)
+ {
+ sprintf(sys, "cp %s %s",
+ src, dest);
+ }
+ else
+ {
+ sprintf(sys, "scp -q -r -i %s -o UserKnownHostsFile=/dev/null "
+ "-o StrictHostKeyChecking=no -o LogLevel=quiet %s %s@%s:%s",
+ sshkey[i], src, access_user[i], IP[i], dest);
+ }
+ if (verbose)
+ {
+ printf("%s\n", sys);
+ }
+
+ return system(sys);
+}
+
+
+int Mariadb_nodes::copy_from_node(const char* src, const char* dest, int i)
+{
+ if (i >= N)
+ {
+ return 1;
+ }
+ char sys[strlen(src) + strlen(dest) + 1024];
+ if (strcmp(IP[i], "127.0.0.1") == 0)
+ {
+ sprintf(sys, "cp %s %s",
+ src, dest);
+ }
+ else
+ {
+ sprintf(sys, "scp -q -r -i %s -o UserKnownHostsFile=/dev/null "
+ "-o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s:%s %s",
+ sshkey[i], access_user[i], IP[i], src, dest);
+ }
+ if (verbose)
+ {
+ printf("%s\n", sys);
+ }
+
+ return system(sys);
+}
+
+static void wait_until_pos(MYSQL *mysql, int filenum, int pos)
+{
+ int slave_filenum = 0;
+ int slave_pos = 0;
+
+ do
+ {
+ if (mysql_query(mysql, "SHOW SLAVE STATUS"))
+ {
+ printf("Failed to execute SHOW SLAVE STATUS: %s", mysql_error(mysql));
+ break;
+ }
+
+ MYSQL_RES *res = mysql_store_result(mysql);
+
+ if (res)
+ {
+ MYSQL_ROW row = mysql_fetch_row(res);
+
+ if (row && row[6] && row[21])
+ {
+ char *file_suffix = strchr(row[5], '.') + 1;
+ slave_filenum = atoi(file_suffix);
+ slave_pos = atoi(row[21]);
+ }
+ mysql_free_result(res);
+ }
+ }
+ while (slave_filenum < filenum || slave_pos < pos);
+}
+
+void Mariadb_nodes::sync_slaves(int node)
+{
+ if (this->nodes[node] == NULL)
+ {
+ this->connect();
+ }
+
+ if (mysql_query(this->nodes[node], "SHOW MASTER STATUS"))
+ {
+ printf("Failed to execute SHOW MASTER STATUS: %s", mysql_error(this->nodes[node]));
+ }
+ else
+ {
+ MYSQL_RES *res = mysql_store_result(this->nodes[node]);
+
+ if (res)
+ {
+ MYSQL_ROW row = mysql_fetch_row(res);
+ if (row && row[node] && row[1])
+ {
+ const char* file_suffix = strchr(row[node], '.') + 1;
+ int filenum = atoi(file_suffix);
+ int pos = atoi(row[1]);
+
+ for (int i = 0; i < this->N; i++)
+ {
+ if (i != node)
+ {
+ wait_until_pos(this->nodes[i], filenum, pos);
+ }
+ }
+ }
+ mysql_free_result(res);
+ }
+ }
+}
+
+void Mariadb_nodes::close_active_connections()
+{
+ if (this->nodes[0] == NULL)
+ {
+ this->connect();
+ }
+
+ const char *sql =
+ "select id from information_schema.processlist where id != @@pseudo_thread_id and user not in ('system user', 'repl')";
+
+ for (int i = 0; i < N; i++)
+ {
+ if (!mysql_query(nodes[i], sql))
+ {
+ MYSQL_RES *res = mysql_store_result(nodes[i]);
+ if (res)
+ {
+ MYSQL_ROW row;
+
+ while ((row = mysql_fetch_row(res)))
+ {
+ std::string q("KILL ");
+ q += row[0];
+ execute_query_silent(nodes[i], q.c_str());
+ }
+ mysql_free_result(res);
+ }
+ }
+ }
+}
diff --git a/maxscale-system-test/mariadb_nodes.h b/maxscale-system-test/mariadb_nodes.h
new file mode 100644
index 000000000..bc01f34dc
--- /dev/null
+++ b/maxscale-system-test/mariadb_nodes.h
@@ -0,0 +1,471 @@
+#ifndef MARIADB_NODES_H
+#define MARIADB_NODES_H
+
+/**
+ * @file mariadb_nodes.h - backend nodes routines
+ *
+ * @verbatim
+ * Revision History
+ *
+ * Date Who Description
+ * 17/11/14 Timofey Turenko Initial implementation
+ *
+ * @endverbatim
+ */
+
+
+#include "mariadb_func.h"
+#include
+
+/**
+ * @brief A class to handle backend nodes
+ * Contains references up to 256 nodes, info about IP, port, ssh key, use name and password for each node
+ * Node parameters should be defined in the enviromental variables in the follwing way:
+ * prefix_N - N number of nodes in the setup
+ * prefix_NNN - IP adress of the node (NNN 3 digits node index)
+ * prefix_port_NNN - MariaDB port number of the node
+ * prefix_User - User name to access backend setup (should have full access to 'test' DB with GRANT OPTION)
+ * prefix_Password - Password to access backend setup
+ */
+class Mariadb_nodes
+{
+public:
+ /**
+ * @brief Constructor
+ * @param pref name of backend setup (like 'repl' or 'galera')
+ */
+ Mariadb_nodes(const char *pref, const char *test_cwd, bool verbose);
+
+ ~Mariadb_nodes();
+
+ /**
+ * @brief MYSQL structs for every backend node
+ */
+ MYSQL *nodes[256];
+ /**
+ * @brief IP address strings for every backend node
+ */
+ char IP[256][1024];
+ /**
+ * @brief private IP address strings for every backend node (for AWS)
+ */
+ char IP_private[256][1024];
+ /**
+ * @brief IP address strings for every backend node (IPv6)
+ */
+ char IP6[256][1024];
+ /**
+ * @brief use_ipv6 If true IPv6 addresses will be used to connect Maxscale and backed
+ * Also IPv6 addresses go to maxscale.cnf
+ */
+ bool use_ipv6;
+ /**
+ * @brief MariaDB port for every backend node
+ */
+ int port[256];
+ /**
+ * @brief Unix socket to connecto to MariaDB
+ */
+ char socket[256][1024];
+ /**
+ * @brief 'socket=$socket' line
+ */
+ char socket_cmd[256][1024];
+ /**
+ * @brief Path to ssh key for every backend node
+ */
+ char sshkey[256][4096];
+ /**
+ * @brief Number of backend nodes
+ */
+ int N;
+ /**
+ * @brief User name to access backend nodes
+ */
+ char user_name[256];
+ /**
+ * @brief Password to access backend nodes
+ */
+ char password[256];
+ /**
+ * @brief master index of node which was last configured to be Master
+ */
+ int master;
+ /**
+ * @brief name of backend setup (like 'repl' or 'galera')
+ */
+ char prefix[16];
+ /**
+ * @brief start_db_command Command to start DB server
+ */
+ char start_db_command[256][4096];
+
+ /**
+ * @brief stop_db_command Command to start DB server
+ */
+ char stop_db_command[256][4096];
+
+ /**
+ * @brief cleanup_db_command Command to remove all
+ * data files and re-install DB with mysql_install_db
+ */
+ char cleanup_db_command[256][4096];
+
+ /**
+ * @brief ssl if true ssl will be used
+ */
+ int ssl;
+
+ /**
+ * @brief access_user Unix users name to access nodes via ssh
+ */
+ char access_user[256][256];
+
+ /**
+ * @brief access_sudo empty if sudo is not needed or "sudo " if sudo is needed.
+ */
+ char access_sudo[256][64];
+
+
+ /**
+ * @brief access_homedir home directory of access_user
+ */
+ char access_homedir[256][256];
+
+ /**
+ * @brief no_set_pos if set to true setup_binlog function do not set log position
+ */
+ bool no_set_pos;
+
+ /**
+ * @brief Verbose command output
+ */
+ bool verbose;
+
+ /**
+ * @brief version Value of @@version
+ */
+ char version[256][256];
+
+ /**
+ * @brief version major part of number value of @@version
+ */
+ char version_major[256][256];
+
+ /**
+ * @brief version Number part of @@version
+ */
+ char version_number[256][256];
+
+ /**
+ * @brief connect open connections to all nodes
+ * @return 0 in case of success
+ */
+
+ /**
+ * @brief v51 true indicates that one backed is 5.1
+ */
+ bool v51;
+
+ /**
+ * @brief test_dir path to test application
+ */
+ char test_dir[4096];
+
+
+ /**
+ * @brief List of blocked nodes
+ */
+ bool blocked[256];
+
+ /**
+ * @brief Open connctions to all backend nodes (to 'test' DB)
+ * @return 0 in case of success
+ */
+ int connect();
+
+ /**
+ * @brief Close connections opened by connect()
+ *
+ * This sets the values of used @c nodes to NULL.
+ */
+ void close_connections();
+
+ /**
+ * @brief reads IP, Ports, sshkeys for every node from enviromental variables as well as number of nodes (N) and User/Password
+ * @return 0
+ */
+ int read_env();
+ /**
+ * @brief prints all nodes information
+ * @return 0
+ */
+ int print_env();
+
+ /**
+ * @brief find_master Tries to find Master node
+ * @return Index of Master node
+ */
+ int find_master();
+ /**
+ * @brief change_master set a new master node for Master/Slave setup
+ * @param NewMaster index of new Master node
+ * @param OldMaster index of current Master node
+ * @return 0 in case of success
+ */
+ int change_master(int NewMaster, int OldMaster);
+
+ /**
+ * @brief stop_nodes stops mysqld on all nodes
+ * @return 0 in case of success
+ */
+ int stop_nodes();
+
+ /**
+ * @brief stop_slaves isues 'stop slave;' to all nodes
+ * @return 0 in case of success
+ */
+ int stop_slaves();
+
+ /**
+ * @brief cleanup_db_node Removes all data files and reinstall DB
+ * with mysql_install_db
+ * @param node
+ * @return 0 in case of success
+ */
+ int cleanup_db_node(int node);
+
+ /**
+ * @brief cleanup_db_node Removes all data files and reinstall DB
+ * with mysql_install_db for all nodes
+ * @param node
+ * @return 0 in case of success
+ */
+ int cleanup_db_nodes();
+
+ /**
+ * @brief configures nodes and starts Master/Slave replication
+ * @return 0 in case of success
+ */
+ virtual int start_replication();
+
+ /**
+ * @brif BlockNode setup firewall on a backend node to block MariaDB port
+ * @param node Index of node to block
+ * @return 0 in case of success
+ */
+ int block_node(int node);
+
+ /**
+ * @brief UnblockNode setup firewall on a backend node to unblock MariaDB port
+ * @param node Index of node to unblock
+ * @return 0 in case of success
+ */
+ int unblock_node(int node);
+
+
+ /**
+ * @brief Unblock all nodes for this cluster
+ * @return 0 in case of success
+ */
+ int unblock_all_nodes();
+
+ /**
+ * @brief clean_iptables removes all itables rules connected to MariaDB port to avoid duplicates
+ * @param node Index of node to clean
+ * @return 0 in case of success
+ */
+ int clean_iptables(int node);
+
+ /**
+ * @brief Stop DB server on the node
+ * @param node Node index
+ * @return 0 if success
+ */
+ int stop_node(int node);
+
+ /**
+ * @brief Start DB server on the node
+ * @param node Node index
+ * @param param command line parameters for DB server start command
+ * @return 0 if success
+ */
+ int start_node(int node, char * param);
+
+ /**
+ * @brief Check node via ssh and restart it if it is not resposible
+ * @param node Node index
+ * @return 0 if node is ok, 1 if start failed
+ */
+ int check_nodes();
+
+ /**
+ * @brief Check if all slaves have "Slave_IO_Running" set to "Yes" and master has N-1 slaves
+ * @param master Index of master node
+ * @return 0 if everything is ok
+ */
+ virtual int check_replication();
+
+ /**
+ * @brief executes 'CHANGE MASTER TO ..' and 'START SLAVE'
+ * @param MYSQL conn struct of slave node
+ * @param master_host IP address of master node
+ * @param master_port port of master node
+ * @param log_file name of log file
+ * @param log_pos initial position
+ * @return 0 if everything is ok
+ */
+ int set_slave(MYSQL * conn, char master_host[], int master_port, char log_file[], char log_pos[]);
+
+ /**
+ * @brief Creates 'repl' user on all nodes
+ * @return 0 if everything is ok
+ */
+ int set_repl_user();
+
+ /**
+ * @brief Get the server_id of the node
+ * @param index The index of the node whose server_id to retrieve
+ * @return Node id of the server or -1 on error
+ */
+ int get_server_id(int index);
+
+ /**
+ * @brief Generate command line to execute command on the node via ssh
+ * @param cmd result
+ * @param index index number of the node (index)
+ * @param ssh command to execute
+ * @param sudo if true the command is executed with root privelegues
+ */
+ void generate_ssh_cmd(char * cmd, int node, const char *ssh, bool sudo);
+
+ /**
+ * @brief executes shell command on the node using ssh
+ * @param index number of the node (index)
+ * @param ssh command to execute
+ * @param sudo if true the command is executed with root privelegues
+ * @param pointer to variable to store process exit code
+ * @return output of the command
+ */
+ char *ssh_node_output(int node, const char *ssh, bool sudo, int *exit_code);
+
+ /**
+ * @brief executes shell command on the node using ssh
+ * @param index number of the node (index)
+ * @param ssh command to execute
+ * @param sudo if true the command is executed with root privelegues
+ * @return exit code of the coomand
+ */
+ int ssh_node(int node, const char *ssh, bool sudo);
+
+ /**
+ * @brief Execute 'mysqladmin flush-hosts' on all nodes
+ * @return 0 in case of success
+ */
+ int flush_hosts();
+
+ /**
+ * @brief Execute query on all nodes
+ * @param sql query to execute
+ * @return 0 in case of success
+ */
+ int execute_query_all_nodes(const char* sql);
+
+ /**
+ * @brief execute 'SELECT @@version' against all nodes and store result in 'version' fied
+ * @return 0 in case of success
+ */
+ int get_versions();
+
+
+ /**
+ * @brief Return lowest server version in the cluster
+ * @return The version string of the server with the lowest version number
+ */
+ std::string get_lowest_version();
+
+ /**
+ * @brief truncate_mariadb_logs clean ups MariaDB logs on backend nodes
+ * @return 0 if success
+ */
+ int truncate_mariadb_logs();
+
+ /**
+ * @brief configure_ssl Modifies my.cnf in order to enable ssl, redefine access user to require ssl
+ * @return 0 if success
+ */
+ int configure_ssl(bool require);
+
+ /**
+ * @brief disable_ssl Modifies my.cnf in order to get rid of ssl, redefine access user to allow connections without ssl
+ * @return 0 if success
+ */
+ int disable_ssl();
+
+ /**
+ * @brief Copy a local file to the Node i machine
+ * @param src Source file on the local filesystem
+ * @param dest Destination file on the remote file system
+ * @param i Node index
+ * @return exit code of the system command or 1 in case of i > N
+ */
+ int copy_to_node(const char* src, const char* dest, int i);
+
+ /**
+ * @brief Copy a local file to the Node i machine
+ * @param src Source file on the remote filesystem
+ * @param dest Destination file on the local file system
+ * @param i Node index
+ * @return exit code of the system command or 1 in case of i > N
+ */
+ int copy_from_node(const char* src, const char* dest, int i);
+
+ /**
+ * @brief Synchronize slaves with the master
+ *
+ * Only works with master-slave replication and should not be used with Galera clusters.
+ * The function expects that the first node, @c nodes[0], is the master.
+ */
+ void sync_slaves(int node = 0);
+
+ /**
+ * @brief Close all connections to this node
+ *
+ * This will kill all connections that have been created to this node.
+ */
+ void close_active_connections();
+
+ /**
+ * @brief Check and fix replication
+ */
+ bool fix_replication();
+
+private:
+
+ int check_node_ssh(int node);
+ bool check_master_node(MYSQL *conn);
+};
+
+class Galera_nodes : public Mariadb_nodes
+{
+public:
+
+ Galera_nodes(const char *pref, const char *test_cwd, bool verbose) :
+ Mariadb_nodes(pref, test_cwd, verbose) { }
+
+ int start_galera();
+
+ virtual int start_replication()
+ {
+ return start_galera();
+ }
+
+ int check_galera();
+
+ virtual int check_replication()
+ {
+ return check_galera();
+ }
+};
+
+#endif // MARIADB_NODES_H
diff --git a/maxscale-system-test/mariadb_tests_hartmut.sh b/maxscale-system-test/mariadb_tests_hartmut.sh
new file mode 100755
index 000000000..ef8b6e64d
--- /dev/null
+++ b/maxscale-system-test/mariadb_tests_hartmut.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# This is required by one of the tests
+#
+# TODO: Don't test correctness of routing with mysqltest
+#
+./non_native_setup $1
+master_id=`echo "SELECT @@server_id" | mysql -u$node_user -p$node_password -h $node_000_network $ssl_options -P $node_000_port | tail -n1`
+echo "--disable_query_log" > Hartmut_tests/maxscale-mysqltest/testconf.inc
+echo "SET @TMASTER_ID=$master_id;" >> Hartmut_tests/maxscale-mysqltest/testconf.inc
+echo "--enable_query_log" >> Hartmut_tests/maxscale-mysqltest/testconf.inc
+
+./mysqltest_driver.sh $1 Hartmut_tests/maxscale-mysqltest 4006
+
+ret=$?
+./copy_logs.sh $1
+
+exit $ret
diff --git a/maxscale-system-test/masking/README.TXT b/maxscale-system-test/masking/README.TXT
new file mode 100644
index 000000000..5b9b59f7a
--- /dev/null
+++ b/maxscale-system-test/masking/README.TXT
@@ -0,0 +1,4 @@
+Note that if modifications are made to any of the t/*-files and the r/*-files
+are regenerated, the output MUST BE CHECKED MANUALLY to ensure that the produced
+result indeed is the expected. Otherwise the mysqltest-based tests effectively
+tests nothing.
diff --git a/maxscale-system-test/masking/masking_mysqltest/masking_rules.json b/maxscale-system-test/masking/masking_mysqltest/masking_rules.json
new file mode 100644
index 000000000..76ee2f24a
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/masking_rules.json
@@ -0,0 +1,29 @@
+{
+ "rules": [
+ {
+ "replace": {
+ "column": "a"
+ },
+ "with": {
+ "fill": "X"
+ }
+ },
+ {
+ "replace": {
+ "column": "b"
+ },
+ "with": {
+ "value": "012345-ABCD"
+ }
+ },
+ {
+ "replace": {
+ "column": "c"
+ },
+ "with": {
+ "value": "012345-ABCD",
+ "fill": "X"
+ }
+ }
+ ]
+}
diff --git a/maxscale-system-test/masking/masking_mysqltest/r/masking_column.result b/maxscale-system-test/masking/masking_mysqltest/r/masking_column.result
new file mode 100644
index 000000000..fb9cfd59f
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/r/masking_column.result
@@ -0,0 +1,57 @@
+drop database if exists maskingdb;
+create database maskingdb;
+use maskingdb;
+create table masking (a TEXT, x TEXT, y TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+a x y
+XXXXX hello hello
+NULL hello hello
+XXXXX NULL hello
+XXXXX hello NULL
+NULL NULL hello
+XXXXX NULL NULL
+NULL NULL NULL
+drop table masking;
+create table masking (x TEXT, a TEXT, y TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+x a y
+hello XXXXX hello
+NULL XXXXX hello
+hello NULL hello
+hello XXXXX NULL
+NULL NULL hello
+hello NULL NULL
+NULL NULL NULL
+drop table masking;
+create table masking (x TEXT, y TEXT, a TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+x y a
+hello hello XXXXX
+NULL hello XXXXX
+hello NULL XXXXX
+hello hello NULL
+NULL NULL XXXXX
+hello NULL NULL
+NULL NULL NULL
+drop table masking;
diff --git a/maxscale-system-test/masking/masking_mysqltest/r/masking_replace.result b/maxscale-system-test/masking/masking_mysqltest/r/masking_replace.result
new file mode 100644
index 000000000..afbd3a9dd
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/r/masking_replace.result
@@ -0,0 +1,19 @@
+drop database if exists maskingdb;
+create database maskingdb;
+use maskingdb;
+create table masking (a TEXT, b TEXT, c TEXT);
+insert into masking values ("blah", "012345-ABC", "012345-ABC");
+select * from masking;
+a b c
+XXXX 012345-ABC XXXXXXXXXX
+delete from masking;
+insert into masking values ("blahblah", "221073-01AB", "012345-ABC");
+select * from masking;
+a b c
+XXXXXXXX 012345-ABCD XXXXXXXXXX
+delete from masking;
+insert into masking values ("221073-01AB", "221073-01AB", "221073-01AB");
+select * from masking;
+a b c
+XXXXXXXXXXX 012345-ABCD 012345-ABCD
+delete from masking;
diff --git a/maxscale-system-test/masking/masking_mysqltest/r/masking_smoke.result b/maxscale-system-test/masking/masking_mysqltest/r/masking_smoke.result
new file mode 100644
index 000000000..ae84e1d18
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/r/masking_smoke.result
@@ -0,0 +1,123 @@
+drop database if exists maskingdb;
+create database maskingdb;
+use maskingdb;
+create table masking_BINARY (a BINARY(3));
+create table masking_VARBINARY (a VARBINARY(8));
+create table masking_CHAR (a CHAR(3));
+create table masking_VARCHAR (a VARCHAR(13));
+create table masking_BLOB (a BLOB);
+create table masking_TINYBLOB (a TINYBLOB);
+create table masking_MEDIUMBLOB (a MEDIUMBLOB);
+create table masking_LONGBLOB (a LONGBLOB);
+create table masking_TEXT (a TEXT);
+create table masking_TINYTEXT (a TINYTEXT);
+create table masking_MEDIUMTEXT (a MEDIUMTEXT);
+create table masking_LONGTEXT (a LONGTEXT);
+create table masking_ENUM (a ENUM('aaa', 'bbb', 'ccc'));
+create table masking_SET (a SET('aaa', 'bbb', 'ccc'));
+insert into masking_BINARY values ("aaa");
+insert into masking_VARBINARY values ("aaa");
+insert into masking_CHAR values ("aaa");
+insert into masking_VARCHAR values ("aaa");
+insert into masking_BLOB values ("aaa");
+insert into masking_TINYBLOB values ("aaa");
+insert into masking_MEDIUMBLOB values ("aaa");
+insert into masking_LONGBLOB values ("aaa");
+insert into masking_TEXT values ("aaa");
+insert into masking_TINYTEXT values ("aaa");
+insert into masking_MEDIUMTEXT values ("aaa");
+insert into masking_LONGTEXT values ("aaa");
+insert into masking_ENUM values ("aaa");
+insert into masking_SET values ("aaa");
+select * from masking_BINARY;
+a
+XXX
+select * from masking_VARBINARY;
+a
+XXX
+select * from masking_CHAR;
+a
+XXX
+select * from masking_VARCHAR;
+a
+XXX
+select * from masking_BLOB;
+a
+XXX
+select * from masking_TINYBLOB;
+a
+XXX
+select * from masking_MEDIUMBLOB;
+a
+XXX
+select * from masking_LONGBLOB;
+a
+XXX
+select * from masking_TEXT;
+a
+XXX
+select * from masking_TINYTEXT;
+a
+XXX
+select * from masking_MEDIUMTEXT;
+a
+XXX
+select * from masking_LONGTEXT;
+a
+XXX
+select * from masking_ENUM;
+a
+XXX
+select * from masking_SET;
+a
+XXX
+create table masking_INT (a INT);
+create table masking_REAL (a REAL(3, 2));
+create table masking_DECIMAL (a DECIMAL(3, 2));
+create table masking_FLOAT (a FLOAT(3, 2));
+create table masking_DOUBLE (a DOUBLE(3, 2));
+create table masking_DATE (a DATE);
+create table masking_TIME (a TIME);
+create table masking_DATETIME (a DATETIME);
+create table masking_TIMESTAMP (a TIMESTAMP);
+create table masking_YEAR (a YEAR);
+insert into masking_INT values (4711);
+insert into masking_REAL values (3.14);
+insert into masking_DECIMAL values (3.14);
+insert into masking_FLOAT values (3.14);
+insert into masking_DOUBLE values (3.14);
+insert into masking_DATE values ('2017-01-24');
+insert into masking_TIME values ('13:52:21');
+insert into masking_DATETIME values ('2017-01-24 13:52:21');
+insert into masking_TIMESTAMP values ('2017-01-24 13:52:21');
+insert into masking_YEAR values ('2001');
+select * from masking_INT;
+a
+4711
+select * from masking_REAL;
+a
+3.14
+select * from masking_DECIMAL;
+a
+3.14
+select * from masking_FLOAT;
+a
+3.14
+select * from masking_DOUBLE;
+a
+3.14
+select * from masking_DATE;
+a
+2017-01-24
+select * from masking_TIME;
+a
+13:52:21
+select * from masking_DATETIME;
+a
+2017-01-24 13:52:21
+select * from masking_TIMESTAMP;
+a
+2017-01-24 13:52:21
+select * from masking_YEAR;
+a
+2001
diff --git a/maxscale-system-test/masking/masking_mysqltest/t/masking_column.test b/maxscale-system-test/masking/masking_mysqltest/t/masking_column.test
new file mode 100644
index 000000000..2c1fe27d3
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/t/masking_column.test
@@ -0,0 +1,75 @@
+#
+# Masking column handling
+#
+# Whether the column to be masked is first, in the middle or
+# last should not matter.
+#
+# See ../masking_rules.json
+
+--disable_warnings
+drop database if exists maskingdb;
+--enable_warnings
+
+create database maskingdb;
+use maskingdb;
+
+create table masking (a TEXT, x TEXT, y TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+#a x y
+#XXXXX hello hello
+#NULL hello hello
+#XXXXX NULL hello
+#XXXXX hello NULL
+#NULL NULL hello
+#XXXXX NULL NULL
+#NULL NULL NULL
+
+drop table masking;
+
+create table masking (x TEXT, a TEXT, y TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+#x a y
+#hello XXXXX hello
+#hello XXXXX hello
+#NULL XXXXX hello
+#hello NULL hello
+#hello XXXXX NULL
+#NULL NULL hello
+#hello NULL NULL
+#NULL NULL NULL
+
+drop table masking;
+
+create table masking (x TEXT, y TEXT, a TEXT);
+insert into masking values ("hello", "hello", "hello");
+insert into masking values (NULL, "hello", "hello");
+insert into masking values ("hello", NULL, "hello");
+insert into masking values ("hello", "hello", NULL);
+insert into masking values (NULL, NULL, "hello");
+insert into masking values ("hello", NULL, NULL);
+insert into masking values (NULL, NULL, NULL);
+select * from masking;
+#x y a
+#hello hello XXXXX
+#NULL hello XXXXX
+#hello NULL XXXXX
+#hello hello NULL
+#NULL NULL XXXXX
+#hello NULL NULL
+#NULL NULL NULL
+
+drop table masking;
diff --git a/maxscale-system-test/masking/masking_mysqltest/t/masking_replace.test b/maxscale-system-test/masking/masking_mysqltest/t/masking_replace.test
new file mode 100644
index 000000000..e95534f53
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/t/masking_replace.test
@@ -0,0 +1,83 @@
+#
+# Masking Smoke
+#
+# We expect the masking rules to be as follows:
+#
+# {
+# "rules": [
+# {
+# "replace": {
+# "column": "a"
+# },
+# "with": {
+# "fill": "X"
+# }
+# },
+# {
+# "replace": {
+# "column": "b"
+# },
+# "with": {
+# "value": "012345-ABCD"
+# }
+# },
+# {
+# "replace": {
+# "column": "c"
+# },
+# "with": {
+# "value": "012345-ABCD",
+# "fill": "X"
+# }
+# }
+# ]
+# }
+
+--disable_warnings
+drop database if exists maskingdb;
+--enable_warnings
+
+create database maskingdb;
+use maskingdb;
+
+#
+# Each table contains a single column 'a' of a type subject
+# to masking.
+#
+create table masking (a TEXT, b TEXT, c TEXT);
+
+# - a should be just "X...",
+# - b should be unchanged as the length does not match the string of "value", and
+# there is no catch all "fill".
+# - c should be just "X..." as the length does not match, so "value" is not applied
+# and has "fill", which is applied.
+#
+#a b c
+#XXXX 012345-ABC XXXXXXXXXX
+insert into masking values ("blah", "012345-ABC", "012345-ABC");
+select * from masking;
+delete from masking;
+
+# - a should be just "X...",
+# - b should be changed as the length matches the length of the string of "value"
+# - c should be just "X..." as the length does not match, so "value" is not applied
+# and has "fill", which is applied.
+#
+#a b c
+#XXXXXXXX 012345-ABCD XXXXXXXXXX
+insert into masking values ("blahblah", "221073-01AB", "012345-ABC");
+select * from masking;
+delete from masking;
+
+# - a should be just "X...",
+# - b should be changed as the length matches the length of the string of "value"
+# - c should be chanched into a specific string as the length matches the string of
+# "value"
+#
+#a b c
+#a b c
+#XXXXXXXXXXX 012345-ABCD 012345-ABCD
+# a should still be just "X", b should be "012345-ABCD" and c should be "012345-ABCD"
+insert into masking values ("221073-01AB", "221073-01AB", "221073-01AB");
+select * from masking;
+delete from masking;
diff --git a/maxscale-system-test/masking/masking_mysqltest/t/masking_smoke.test b/maxscale-system-test/masking/masking_mysqltest/t/masking_smoke.test
new file mode 100644
index 000000000..f0db2040e
--- /dev/null
+++ b/maxscale-system-test/masking/masking_mysqltest/t/masking_smoke.test
@@ -0,0 +1,126 @@
+#
+# Masking Smoke
+#
+# We expect the masking rules to be as follows:
+#
+# {
+# "rules": [
+# {
+# "replace": {
+# "column": "a"
+# },
+# "with": {
+# "fill": "X"
+# }
+# }
+# ]
+# }
+
+--disable_warnings
+drop database if exists maskingdb;
+--enable_warnings
+
+create database maskingdb;
+use maskingdb;
+
+#
+# Each table contains a single column 'a' of a type subject
+# to masking.
+#
+create table masking_BINARY (a BINARY(3));
+create table masking_VARBINARY (a VARBINARY(8));
+create table masking_CHAR (a CHAR(3));
+create table masking_VARCHAR (a VARCHAR(13));
+create table masking_BLOB (a BLOB);
+create table masking_TINYBLOB (a TINYBLOB);
+create table masking_MEDIUMBLOB (a MEDIUMBLOB);
+create table masking_LONGBLOB (a LONGBLOB);
+create table masking_TEXT (a TEXT);
+create table masking_TINYTEXT (a TINYTEXT);
+create table masking_MEDIUMTEXT (a MEDIUMTEXT);
+create table masking_LONGTEXT (a LONGTEXT);
+create table masking_ENUM (a ENUM('aaa', 'bbb', 'ccc'));
+create table masking_SET (a SET('aaa', 'bbb', 'ccc'));
+
+insert into masking_BINARY values ("aaa");
+insert into masking_VARBINARY values ("aaa");
+insert into masking_CHAR values ("aaa");
+insert into masking_VARCHAR values ("aaa");
+insert into masking_BLOB values ("aaa");
+insert into masking_TINYBLOB values ("aaa");
+insert into masking_MEDIUMBLOB values ("aaa");
+insert into masking_LONGBLOB values ("aaa");
+insert into masking_TEXT values ("aaa");
+insert into masking_TINYTEXT values ("aaa");
+insert into masking_MEDIUMTEXT values ("aaa");
+insert into masking_LONGTEXT values ("aaa");
+insert into masking_ENUM values ("aaa");
+insert into masking_SET values ("aaa");
+
+#
+# In masking_smoke.result, we should have:
+#
+# a
+# XXX
+#
+# for each following select.
+#
+select * from masking_BINARY;
+select * from masking_VARBINARY;
+select * from masking_CHAR;
+select * from masking_VARCHAR;
+select * from masking_BLOB;
+select * from masking_TINYBLOB;
+select * from masking_MEDIUMBLOB;
+select * from masking_LONGBLOB;
+select * from masking_TEXT;
+select * from masking_TINYTEXT;
+select * from masking_MEDIUMTEXT;
+select * from masking_LONGTEXT;
+select * from masking_ENUM;
+select * from masking_SET;
+
+#
+# Each table contains a single column 'a' of a type NOT subject
+# to masking.
+#
+create table masking_INT (a INT);
+create table masking_REAL (a REAL(3, 2));
+create table masking_DECIMAL (a DECIMAL(3, 2));
+create table masking_FLOAT (a FLOAT(3, 2));
+create table masking_DOUBLE (a DOUBLE(3, 2));
+create table masking_DATE (a DATE);
+create table masking_TIME (a TIME);
+create table masking_DATETIME (a DATETIME);
+create table masking_TIMESTAMP (a TIMESTAMP);
+create table masking_YEAR (a YEAR);
+
+insert into masking_INT values (4711);
+insert into masking_REAL values (3.14);
+insert into masking_DECIMAL values (3.14);
+insert into masking_FLOAT values (3.14);
+insert into masking_DOUBLE values (3.14);
+insert into masking_DATE values ('2017-01-24');
+insert into masking_TIME values ('13:52:21');
+insert into masking_DATETIME values ('2017-01-24 13:52:21');
+insert into masking_TIMESTAMP values ('2017-01-24 13:52:21');
+insert into masking_YEAR values ('2001');
+
+#
+# In masking_smoke.result, we should have:
+#
+# a
+#
+#
+# for each following select.
+#
+select * from masking_INT;
+select * from masking_REAL;
+select * from masking_DECIMAL;
+select * from masking_FLOAT;
+select * from masking_DOUBLE;
+select * from masking_DATE;
+select * from masking_TIME;
+select * from masking_DATETIME;
+select * from masking_TIMESTAMP;
+select * from masking_YEAR;
diff --git a/maxscale-system-test/masking/masking_user/masking_rules.json b/maxscale-system-test/masking/masking_user/masking_rules.json
new file mode 100644
index 000000000..110fc8f6f
--- /dev/null
+++ b/maxscale-system-test/masking/masking_user/masking_rules.json
@@ -0,0 +1,76 @@
+{
+ "rules": [
+ {
+ "replace": {
+ "column": "a"
+ },
+ "with": {
+ "fill": "X"
+ }
+ },
+ {
+ "replace": {
+ "column": "b"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "applies_to" : [ "'skysql'" ]
+ },
+ {
+ "replace": {
+ "column": "c"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "applies_to" : [ "'maxskysql'" ]
+ },
+ {
+ "replace": {
+ "column": "d"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "exempted" : [ "'skysql'" ]
+ },
+ {
+ "replace": {
+ "column": "e"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "exempted" : [ "'maxskysql'" ]
+ },
+ {
+ "replace": {
+ "column": "f"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "applies_to" : [ "'skysql'", "'maxskysql'" ]
+ },
+ {
+ "replace": {
+ "column": "g"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "exempted" : [ "'skysql'", "'maxskysql'" ]
+ },
+ {
+ "replace": {
+ "column": "h"
+ },
+ "with": {
+ "fill": "X"
+ },
+ "applies_to" : [ "'skysql'" ],
+ "exempted" : [ "'maxskysql'" ]
+ }
+ ]
+}
diff --git a/maxscale-system-test/masking/masking_user/r/masking_user_maxskysql.result b/maxscale-system-test/masking/masking_user/r/masking_user_maxskysql.result
new file mode 100644
index 000000000..cdfe6da23
--- /dev/null
+++ b/maxscale-system-test/masking/masking_user/r/masking_user_maxskysql.result
@@ -0,0 +1,8 @@
+drop database if exists maskingdb;
+create database maskingdb;
+use maskingdb;
+create table masking (a TEXT, b TEXT, c TEXT, d TEXT, e TEXT, f TEXT, g TEXT, h TEXT);
+insert into masking values ("hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello");
+select * from masking;
+a b c d e f g h
+XXXXX hello XXXXX XXXXX hello XXXXX hello hello
diff --git a/maxscale-system-test/masking/masking_user/r/masking_user_skysql.result b/maxscale-system-test/masking/masking_user/r/masking_user_skysql.result
new file mode 100644
index 000000000..4aff09b95
--- /dev/null
+++ b/maxscale-system-test/masking/masking_user/r/masking_user_skysql.result
@@ -0,0 +1,8 @@
+drop database if exists maskingdb;
+create database maskingdb;
+use maskingdb;
+create table masking (a TEXT, b TEXT, c TEXT, d TEXT, e TEXT, f TEXT, g TEXT, h TEXT);
+insert into masking values ("hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello");
+select * from masking;
+a b c d e f g h
+XXXXX XXXXX hello hello XXXXX XXXXX hello XXXXX
diff --git a/maxscale-system-test/masking/masking_user/t/masking_user.test b/maxscale-system-test/masking/masking_user/t/masking_user.test
new file mode 100644
index 000000000..7b5a74594
--- /dev/null
+++ b/maxscale-system-test/masking/masking_user/t/masking_user.test
@@ -0,0 +1,46 @@
+#
+# Masking User
+#
+# See ../masking_rules.json
+
+drop database if exists maskingdb;
+
+create database maskingdb;
+use maskingdb;
+
+#
+# Each table contains a single column 'a' of a type subject
+# to masking.
+#
+create table masking (a TEXT, b TEXT, c TEXT, d TEXT, e TEXT, f TEXT, g TEXT, h TEXT);
+
+insert into masking values ("hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello");
+select * from masking;
+
+# With the user skysql
+#
+# a: XXXXX, because the rule applies to everyone.
+# b: XXXXX, because the rule specifically applies to the user 'skysql'
+# c: hello, because the rule specifically applies to the user 'maxskysql'
+# d: hello, because the rule specifically excludes the user 'skysql'
+# e: XXXXX, because the rule applies to everyone except to the user 'maxskysql'
+# f: XXXXX, because the rule applies to the user 'skysql' and 'maxskysql'
+# g: hello, because the rule specifically does not apply to the users 'skysql' and 'maxskysql'
+# h: XXXXX, because the rule specifically applies to the user 'skysql'
+#
+#a b c d e f g h
+#XXXXX XXXXX hello hello XXXXX XXXXX hello XXXXX
+
+# With the user maxskysql
+#
+# a: XXXXX, because the rule applies to everyone.
+# b: hello, because the rule specifically applies to the user 'skysql'
+# c: XXXXX, because the rule specifically applies to the user 'maxskysql'
+# d: XXXXX, because the rule specifically excludes the user 'skysql'
+# e: hello, because the rule applies to everyone except to the user 'maxskysql'
+# f: XXXXX, because the rule applies to the user 'skysql' and 'maxskysql'
+# g: hello, because the rule specifically does not apply to the users 'skysql' and 'maxskysql'
+# h: XXXXX, because the rule specifically applies to the user 'skysql'
+#
+#a b c d e f g h
+#XXXXX hello XXXXX XXXXX hello XXXXX hello hello
diff --git a/maxscale-system-test/masking_mysqltest_driver.sh b/maxscale-system-test/masking_mysqltest_driver.sh
new file mode 100755
index 000000000..676a3a24f
--- /dev/null
+++ b/maxscale-system-test/masking_mysqltest_driver.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+script=`basename "$0"`
+
+if [ $# -lt 1 ]
+then
+ echo "usage: $script name [user] [password]"
+ echo ""
+ echo "name : The name of the test (from CMakeLists.txt) That selects the"
+ echo " configuration template to be used."
+ echo "user : The user using which the test should be run."
+ echo "password: The password of the user."
+ exit 1
+fi
+
+if [ "$maxscale_IP" == "" ]
+then
+ echo "Error: The environment variable maxscale_IP must be set."
+ exit 1
+fi
+
+source=masking/$1/masking_rules.json
+target=$maxscale_access_user@$maxscale_IP:/home/$maxscale_access_user/masking_rules.json
+
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ scp -i $maxscale_keyfile -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $source $target
+else
+ cp $source /home/$maxscale_access_user/masking_rules.json
+fi
+
+if [ $? -ne 0 ]
+then
+ echo "error: Could not copy rules file to maxscale host."
+ exit 1
+fi
+
+echo $source copied to $target
+
+password=
+if [ $# -ge 3 ]
+then
+ password=$3
+fi
+
+user=
+if [ $# -ge 2 ]
+then
+ user=$2
+fi
+
+# [Read Connection Listener Master] in cnf/maxscale.maxscale.cnf.template.$1
+port=4008
+
+./mysqltest_driver.sh $1 ./masking/$1 $port $user $password
diff --git a/maxscale-system-test/masking_user.sh b/maxscale-system-test/masking_user.sh
new file mode 100755
index 000000000..9f16ccea8
--- /dev/null
+++ b/maxscale-system-test/masking_user.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+script=`basename "$0"`
+
+if [ $# -lt 1 ]
+then
+ echo "usage: $script name"
+ echo ""
+ echo "name : The name of the test (from CMakeLists.txt) That selects the"
+ echo " configuration template to be used."
+ exit 1
+fi
+
+if [ "$maxscale_IP" == "" ]
+then
+ echo "Error: The environment variable maxscale_IP must be set."
+ exit 1
+fi
+
+source=masking/$1/masking_rules.json
+target=vagrant@$maxscale_IP:/home/$maxscale_access_user/masking_rules.json
+
+if [ $maxscale_IP != "127.0.0.1" ] ; then
+ scp -i $maxscale_keyfile -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $source $target
+else
+ cp $source /home/$maxscale_access_user/masking_rules.json
+fi
+
+if [ $? -ne 0 ]
+then
+ echo "error: Could not copy rules file to maxscale host."
+ exit 1
+fi
+
+echo $source copied to $target
+
+test_dir=`pwd`
+
+$test_dir/non_native_setup $1
+
+cd masking/$1
+[ -d log ] && rm -r log
+mkdir log || exit 1
+
+# [Read Connection Listener Master] in cnf/maxscale.maxscale.cnf.template.$1
+port=4008
+password=skysql
+
+user=skysql
+test_name=masking_user
+mysqltest --host=$maxscale_IP --port=$port \
+ --user=$user --password=$password \
+ --logdir=log \
+ --test-file=t/$test_name.test \
+ --result-file=r/"$test_name"_"$user".result \
+ --silent
+if [ $? -eq 0 ]
+then
+ echo " OK"
+else
+ echo " FAILED"
+ res=1
+fi
+
+user=maxskysql
+test_name=masking_user
+mysqltest --host=$maxscale_IP --port=$port \
+ --user=$user --password=$password \
+ --logdir=log \
+ --test-file=t/$test_name.test \
+ --result-file=r/"$test_name"_"$user".result \
+ --silent
+if [ $? -eq 0 ]
+then
+ echo " OK"
+else
+ echo " FAILED"
+ res=1
+fi
+
+echo
+
+# Copy logs from the VM
+$test_dir/copy_logs.sh $1
+
+exit $res
diff --git a/maxscale-system-test/max_connections.cpp b/maxscale-system-test/max_connections.cpp
new file mode 100644
index 000000000..0f93dcbca
--- /dev/null
+++ b/maxscale-system-test/max_connections.cpp
@@ -0,0 +1,73 @@
+/**
+ * @file max_connections.cpp Creates a number of connections > max_connections setting
+ * - set global max_connections = 20
+ * - create 20 connections, find on which iteration query start to fail
+ * - when limit is found close last 2 connections
+ * - in the loop: open two connections, expect first to succeed, second to fail, close them both and repeat
+ * - close all connections
+ */
+
+#include "testconnections.h"
+
+#define CONNECTIONS 21
+#define ITER 25
+
+int main(int argc, char** argv)
+{
+ MYSQL *mysql[CONNECTIONS];
+ TestConnections * Test = new TestConnections(argc, argv);
+ Test->stop_timeout();
+ Test->repl->execute_query_all_nodes((char *) "set global max_connections = 20;");
+ sleep(5);
+ int limit = 0;
+
+ for (int i = 0; i < CONNECTIONS - 1; i++)
+ {
+ Test->tprintf("Opening connection %d\n", i + 1);
+ Test->set_timeout(30);
+ mysql[i] = Test->open_rwsplit_connection();
+ if (execute_query_silent(mysql[i], "select 1"))
+ {
+ /** Monitors and such take up some connections so we'll set the
+ * limit to the point where we know it'll start failing.*/
+ Test->stop_timeout();
+ limit = i;
+ mysql_close(mysql[limit]);
+ mysql_close(mysql[limit - 1]);
+ Test->tprintf("Found limit, %d connections\n", limit);
+ break;
+ }
+ Test->stop_timeout();
+ sleep(1);
+ }
+
+ sleep(5);
+ Test->tprintf("Opening two connections for %d times. One should succeed while the other should fail.\n",
+ ITER);
+ for (int i = 0; i < ITER; i++)
+ {
+ Test->set_timeout(30);
+ mysql[limit - 1] = Test->open_rwsplit_connection();
+ mysql[limit] = Test->open_rwsplit_connection();
+ Test->add_result(execute_query_silent(mysql[limit - 1], "select 1"), "Query should succeed\n");
+ Test->add_result(!execute_query_silent(mysql[limit], "select 1"), "Query should fail\n");
+ mysql_close(mysql[limit - 1]);
+ mysql_close(mysql[limit]);
+ sleep(2);
+ }
+
+ Test->set_timeout(30);
+ for (int i = 0; i < limit - 1; i++)
+ {
+ mysql_close(mysql[i]);
+ }
+
+ sleep(5);
+ Test->stop_timeout();
+ Test->check_maxscale_alive();
+ Test->repl->execute_query_all_nodes((char *) "set global max_connections = 100;");
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+
+}
diff --git a/maxscale-system-test/maxadmin_operations.cpp b/maxscale-system-test/maxadmin_operations.cpp
new file mode 100644
index 000000000..83f9bee93
--- /dev/null
+++ b/maxscale-system-test/maxadmin_operations.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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
+ */
+
+#include "maxadmin_operations.h"
+
+int
+connectMaxScale(char *hostname, char *port)
+{
+ struct sockaddr_in addr;
+ int so;
+ int keepalive = 1;
+
+ if ((so = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ fprintf(stderr, "Unable to create socket: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ memset(&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ setipaddress(&addr.sin_addr, hostname);
+ addr.sin_port = htons(atoi(port));
+ if (connect(so, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ {
+ fprintf(stderr, "Unable to connect to MaxScale at %s, %s: %s\n",
+ hostname, port, strerror(errno));
+ close(so);
+ return -1;
+ }
+ if (setsockopt(so, SOL_SOCKET,
+ SO_KEEPALIVE, &keepalive , sizeof(keepalive )))
+ {
+ perror("setsockopt");
+ }
+
+ return so;
+}
+
+
+int
+setipaddress(struct in_addr *a, char *p)
+{
+#ifdef __USE_POSIX
+ struct addrinfo *ai = NULL, hint;
+ int rc;
+ struct sockaddr_in * res_addr;
+ memset(&hint, 0, sizeof (hint));
+
+ hint.ai_socktype = SOCK_STREAM;
+ hint.ai_flags = AI_CANONNAME;
+ hint.ai_family = AF_INET;
+
+ if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0)
+ {
+ return 0;
+ }
+
+ /* take the first one */
+ if (ai != NULL)
+ {
+ res_addr = (struct sockaddr_in *)(ai->ai_addr);
+ memcpy(a, &res_addr->sin_addr, sizeof(struct in_addr));
+
+ freeaddrinfo(ai);
+
+ return 1;
+ }
+#else
+ struct hostent *h;
+
+ spinlock_acquire(&tmplock);
+ h = gethostbyname(p);
+ spinlock_release(&tmplock);
+
+ if (h == NULL)
+ {
+ if ((a->s_addr = inet_addr(p)) == -1)
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ /* take the first one */
+ memcpy(a, h->h_addr, h->h_length);
+
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+int
+authMaxScale(int so, char *user, char *password)
+{
+ char buf[20];
+
+ if (read(so, buf, 4) != 4)
+ {
+ return 0;
+ }
+ write(so, user, strlen(user));
+ if (read(so, buf, 8) != 8)
+ {
+ return 0;
+ }
+ write(so, password, strlen(password));
+ if (read(so, buf, 6) != 6)
+ {
+ return 0;
+ }
+
+ return strncmp(buf, "FAILED", 6);
+}
+
+int
+sendCommand(int so, char *cmd, char *buf)
+{
+ char buf1[80];
+ int i, j, newline = 1;
+ int k = 0;
+
+ if (write(so, cmd, strlen(cmd)) == -1)
+ {
+ return 0;
+ }
+ while (1)
+ {
+ if ((i = read(so, buf1, 80)) <= 0)
+ {
+ return 0;
+ }
+ for (j = 0; j < i; j++)
+ {
+ if (newline == 1 && buf1[j] == 'O')
+ {
+ newline = 2;
+ }
+ else if (newline == 2 && buf1[j] == 'K' && j == i - 1)
+ {
+ return 1;
+ }
+ else if (newline == 2)
+ {
+ buf[k] = 'O';
+ k++;
+ buf[k] = buf1[j];
+ k++;
+ newline = 0;
+ }
+ else if (buf1[j] == '\n' || buf1[j] == '\r')
+ {
+ buf[k] = buf1[j];
+ k++;
+ newline = 1;
+ }
+ else
+ {
+ buf[k] = buf1[j];
+ k++;
+ newline = 0;
+ }
+ }
+ }
+ return 1;
+}
+
+int
+get_maxadmin_param_tcp(char * hostname, char *user, char *password, char * cmd, char *param, char *result)
+{
+
+ char buf[10240];
+ char *port = (char *) "6603";
+ int so;
+
+ if ((so = connectMaxScale(hostname, port)) == -1)
+ {
+ return 1;
+ }
+ if (!authMaxScale(so, user, password))
+ {
+ fprintf(stderr, "Failed to connect to MaxScale. "
+ "Incorrect username or password.\n");
+ close(so);
+ return 1;
+ }
+
+ sendCommand(so, cmd, buf);
+
+ //printf("%s\n", buf);
+
+ char * x = strstr(buf, param);
+ if (x == NULL )
+ {
+ return 1;
+ }
+ //char f_field[100];
+ int param_len = strlen(param);
+ int cnt = 0;
+ while (x[cnt + param_len] != '\n')
+ {
+ result[cnt] = x[cnt + param_len];
+ cnt++;
+ }
+ result[cnt] = '\0';
+ //sprintf(f_field, "%s %%s", param);
+ //sscanf(x, f_field, result);
+ close(so);
+ return 0;
+}
+
+int
+execute_maxadmin_command_tcp(char * hostname, char *user, char *password, char * cmd)
+{
+
+ char buf[10240];
+ char *port = (char *) "6603";
+ int so;
+
+ if ((so = connectMaxScale(hostname, port)) == -1)
+ {
+ return 1;
+ }
+ if (!authMaxScale(so, user, password))
+ {
+ fprintf(stderr, "Failed to connect to MaxScale. "
+ "Incorrect username or password.\n");
+ close(so);
+ return 1;
+ }
+
+ sendCommand(so, cmd, buf);
+
+ close(so);
+ return 0;
+}
+
+int
+execute_maxadmin_command_print_tcp(char * hostname, char *user, char *password, char * cmd)
+{
+
+ char buf[10240];
+ char *port = (char *) "6603";
+ int so;
+
+ if ((so = connectMaxScale(hostname, port)) == -1)
+ {
+ return 1;
+ }
+ if (!authMaxScale(so, user, password))
+ {
+ fprintf(stderr, "Failed to connect to MaxScale. "
+ "Incorrect username or password.\n");
+ close(so);
+ return 1;
+ }
+
+ sendCommand(so, cmd, buf);
+ printf("%s\n", buf);
+ close(so);
+ return 0;
+}
diff --git a/maxscale-system-test/maxadmin_operations.h b/maxscale-system-test/maxadmin_operations.h
new file mode 100644
index 000000000..a426d95fe
--- /dev/null
+++ b/maxscale-system-test/maxadmin_operations.h
@@ -0,0 +1,106 @@
+#ifndef MAXADMIN_OPERATIONS_H
+#define MAXADMIN_OPERATIONS_H
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//#include
+
+
+/**
+ * @brief Connect to the MaxScale server
+ *
+ * @param hostname The hostname to connect to
+ * @param port The port to use for the connection
+ * @return The connected socket or -1 on error
+ */
+int connectMaxScale(char *hostname, char *port);
+
+/**
+ * @brief Set IP address in socket structure in_addr
+ *
+ * @param a Pointer to a struct in_addr into which the address is written
+ * @param p The hostname to lookup
+ * @return 1 on success, 0 on failure
+ */
+int setipaddress(struct in_addr *a, char *p);
+
+
+/**
+ * @brief Perform authentication using the maxscaled protocol conventions
+ *
+ * @param so The socket connected to MaxScale
+ * @param user The username to authenticate
+ * @param password The password to authenticate with
+ * @return Non-zero of succesful authentication
+ */
+int authMaxScale(int so, char *user, char *password);
+
+/**
+ * @brief Send a comamnd using the MaxScaled protocol, display the return data on standard output.
+ *
+ * Input terminates with a lien containing just the text OK
+ *
+ * @param so The socket connect to MaxScale
+ * @param cmd The command to send
+ * @return 0 if the connection was closed
+ */
+int sendCommand(int so, char *cmd, char *buf);
+
+
+/**
+ * @brief Send a comamnd using the MaxScaled protocol, search for certain numeric parameter in MaxScaled output.
+ *
+ * Input terminates with a lien containing just the text OK
+ *
+ * @param user The username to authenticate
+ * @param password The password to authenticate with
+ * @param cmd The command to send
+ * @param param Parameter to find
+ * @param result Value of found parameter
+ * @return 0 if parameter is found
+ */
+int get_maxadmin_param_tcp(char *hostname, char *user, char *password, char *command, char *param,
+ char *result);
+
+/**
+ * @brief Send a comamnd using the MaxScaled protocol
+ *
+ * Input terminates with a line containing just the text OK
+ *
+ * @param user The username to authenticate
+ * @param password The password to authenticate with
+ * @param cmd The command to send
+ * @return 0 if parameter is found
+ */
+int execute_maxadmin_command_tcp(char * hostname, char *user, char *password, char * cmd);
+
+/**
+ * @brief Send a comamnd using the MaxScaled protocol, print results of stdout
+ *
+ * Input terminates with a line containing just the text OK
+ *
+ * @param user The username to authenticate
+ * @param password The password to authenticate with
+ * @param cmd The command to send
+ * @return 0 if parameter is found
+ */
+int execute_maxadmin_command_print_pcp(char * hostname, char *user, char *password, char * cmd);
+
+#endif // MAXADMIN_OPERATIONS_H
diff --git a/maxscale-system-test/maxinfo.cpp b/maxscale-system-test/maxinfo.cpp
new file mode 100644
index 000000000..fdf648cb5
--- /dev/null
+++ b/maxscale-system-test/maxinfo.cpp
@@ -0,0 +1,80 @@
+/**
+ * @file maxinfo.cpp maxinfo JSON listener test
+ * - sends 1000 'status' request to the listener
+ */
+
+
+#include
+#include
+#include "testconnections.h"
+
+#include
+#include
+#include "maxinfo_func.h"
+
+using namespace std;
+
+//const int N = 9;
+//const char * resources[N] = {"variables", "status", "services", "listeners", "modules", "sessions", "clients", "servers", "eventTimes"};
+const int N = 8;
+const char * resources[N] = {"variables", "status", "services", "listeners", "modules", "sessions", "clients", "servers"};
+
+bool exit_flag = false;
+
+void *maxinfo_thread( void *ptr );
+int threads_num = 25;
+TestConnections * Test;
+
+int main(int argc, char *argv[])
+{
+ Test = new TestConnections(argc, argv);
+ int sleep_time = Test->smoke ? 30 : 1000;
+
+ Test->set_timeout(sleep_time + 100);
+
+ pthread_t thread1[threads_num];
+ int iret1[threads_num];
+
+ int i;
+ for (i = 0; i < threads_num; i++)
+ {
+ iret1[i] = pthread_create(&thread1[i], NULL, maxinfo_thread, NULL);
+ }
+
+ sleep(sleep_time);
+
+ exit_flag = true;
+ Test->set_timeout(120);
+ for (i = 0; i < threads_num; i++)
+ {
+ pthread_join(thread1[i], NULL);
+ }
+
+ Test->check_maxscale_alive();
+
+ int rval = Test->global_result;
+ delete Test;
+ return rval;
+}
+
+void *maxinfo_thread( void *ptr )
+{
+ char * result;
+ int ind;
+
+ while (! exit_flag)
+ {
+ ind = rand() % N;
+ result = get_maxinfo(resources[ind], Test);
+ if (result != NULL)
+ {
+ Test->tprintf("Query %s, result: \n%s\n", resources[ind], result);
+ free(result);
+ }
+ else
+ {
+ Test->add_result(1, "Can't get result from maxinfo, query %s\n", resources[ind]);
+ }
+ }
+
+}
diff --git a/maxscale-system-test/maxinfo.py b/maxscale-system-test/maxinfo.py
new file mode 100755
index 000000000..fb4f18c51
--- /dev/null
+++ b/maxscale-system-test/maxinfo.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python3
+
+import http.client
+import os
+import json
+import subprocess
+import threading
+
+# Needs to be declared here to allow Python 3 modules to be used
+def prepare_test(testname = "replication"):
+ subprocess.call(os.getcwd() + "/non_native_setup " + str(testname), shell=True)
+
+prepare_test("maxinfo.py")
+
+# Test all Maxinfo HTTP entry points
+entry_points = ["/services",
+ "/listeners",
+ "/modules",
+ "/monitors",
+ "/sessions",
+ "/clients",
+ "/servers",
+ "/variables",
+ "/status",
+ "/event/times"]
+
+decoder = json.JSONDecoder()
+
+def test_thr(thrnum):
+ for r in range(0,10):
+ for i in entry_points:
+ data = ""
+ try:
+ conn = http.client.HTTPConnection(os.getenv("maxscale_network"), 8080)
+ conn.request("GET", i)
+ req = conn.getresponse()
+ data = req.read().decode('ascii')
+ json.loads(data)
+ except Exception as ex:
+ print("Thread", thrnum, "Exception (", ex, "):", data)
+ exit(1)
+
+thr = []
+
+for i in range(0, 10):
+ thr.append(threading.Thread(target=test_thr, args=(i,)))
+
+print("Created", len(thr), "threads")
+
+for i in thr:
+ i.start()
+
+print("Started threads")
+
+for i in thr:
+ i.join()
diff --git a/maxscale-system-test/maxinfo_func.cpp b/maxscale-system-test/maxinfo_func.cpp
new file mode 100644
index 000000000..a41066132
--- /dev/null
+++ b/maxscale-system-test/maxinfo_func.cpp
@@ -0,0 +1,301 @@
+
+#include
+#include
+#include "testconnections.h"
+
+#include
+#include
+#include
+#include