Merge branch '2.1' into 2.2
This commit is contained in:
@ -688,6 +688,17 @@ Note that MariaDB MaxScale is **not** explicitly aware of the sql mode of
|
|||||||
the server, so the value of `sql_mode` should reflect the sql mode used
|
the server, so the value of `sql_mode` should reflect the sql mode used
|
||||||
when the server is started.
|
when the server is started.
|
||||||
|
|
||||||
|
#### `local_address`
|
||||||
|
|
||||||
|
What specific local address/interface to use when connecting to servers.
|
||||||
|
|
||||||
|
This can be used for ensuring that MaxScale uses a particular interface
|
||||||
|
when connecting to servers, in case the computer MaxScale is running on
|
||||||
|
has multiple interfaces.
|
||||||
|
```
|
||||||
|
local_address=192.168.1.254
|
||||||
|
```
|
||||||
|
|
||||||
### Service
|
### Service
|
||||||
|
|
||||||
A service represents the database service that MariaDB MaxScale offers to the
|
A service represents the database service that MariaDB MaxScale offers to the
|
||||||
|
|||||||
63
Documentation/Release-Notes/MaxScale-2.1.14-Release-Notes.md
Normal file
63
Documentation/Release-Notes/MaxScale-2.1.14-Release-Notes.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# MariaDB MaxScale 2.1.14 Release Notes
|
||||||
|
|
||||||
|
Release 2.1.14 is a GA release.
|
||||||
|
|
||||||
|
This document describes the changes in release 2.1.14, when compared
|
||||||
|
to release [2.1.13](MaxScale-2.1.13-Release-Notes.md).
|
||||||
|
|
||||||
|
If you are upgrading from release 2.0, please also read the following
|
||||||
|
release notes:
|
||||||
|
|
||||||
|
* [2.1.13](./MaxScale-2.1.13-Release-Notes.md)
|
||||||
|
* [2.1.12](./MaxScale-2.1.12-Release-Notes.md)
|
||||||
|
* [2.1.11](./MaxScale-2.1.11-Release-Notes.md)
|
||||||
|
* [2.1.10](./MaxScale-2.1.10-Release-Notes.md)
|
||||||
|
* [2.1.9](./MaxScale-2.1.9-Release-Notes.md)
|
||||||
|
* [2.1.8](./MaxScale-2.1.8-Release-Notes.md)
|
||||||
|
* [2.1.7](./MaxScale-2.1.7-Release-Notes.md)
|
||||||
|
* [2.1.6](./MaxScale-2.1.6-Release-Notes.md)
|
||||||
|
* [2.1.5](./MaxScale-2.1.5-Release-Notes.md)
|
||||||
|
* [2.1.4](./MaxScale-2.1.4-Release-Notes.md)
|
||||||
|
* [2.1.3](./MaxScale-2.1.3-Release-Notes.md)
|
||||||
|
* [2.1.2](./MaxScale-2.1.2-Release-Notes.md)
|
||||||
|
* [2.1.1](./MaxScale-2.1.1-Release-Notes.md)
|
||||||
|
* [2.1.0](./MaxScale-2.1.0-Release-Notes.md)
|
||||||
|
|
||||||
|
For any problems you encounter, please consider submitting a bug report at
|
||||||
|
[Jira](https://jira.mariadb.org).
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
### Local Address
|
||||||
|
|
||||||
|
It is now possible to specify what local address MaxScale should
|
||||||
|
use when connecting to servers. Please refer to the documentation
|
||||||
|
for [details](../Getting-Started/Configuration-Guide.md#local_address).
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
[Here is a list of bugs fixed in MaxScale 2.1.14.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.1.14)
|
||||||
|
|
||||||
|
* [MXS-1627](https://jira.mariadb.org/browse/MXS-1627) MySQLAuth loads users that use authentication plugins
|
||||||
|
* [MXS-1620](https://jira.mariadb.org/browse/MXS-1620) CentOS package symbols are stripped
|
||||||
|
* [MXS-1602](https://jira.mariadb.org/browse/MXS-1602) cannot connect to maxinfo with python client
|
||||||
|
* [MXS-1601](https://jira.mariadb.org/browse/MXS-1601) maxinfo crash at execute query 'flush;'
|
||||||
|
* [MXS-1600](https://jira.mariadb.org/browse/MXS-1600) maxscale it seen to not coop well with lower-case-table-names=1 on cnf
|
||||||
|
* [MXS-1576](https://jira.mariadb.org/browse/MXS-1576) Maxscale crashes when starting if .avro and .avsc files are present
|
||||||
|
* [MXS-1543](https://jira.mariadb.org/browse/MXS-1543) Avrorouter doesn't detect MIXED or STATEMENT format replication
|
||||||
|
* [MXS-1416](https://jira.mariadb.org/browse/MXS-1416) maxscale should not try to do anything when started with --config-check
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
|
||||||
|
RPM and Debian packages are provided for the Linux distributions supported by
|
||||||
|
MariaDB Enterprise.
|
||||||
|
|
||||||
|
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||||
|
|
||||||
|
## Source Code
|
||||||
|
|
||||||
|
The source code of MaxScale is tagged at GitHub with a tag, which is identical
|
||||||
|
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
|
||||||
|
is maxscale-X.Y.Z.
|
||||||
|
|
||||||
|
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
||||||
@ -231,6 +231,7 @@ typedef struct
|
|||||||
int query_retries; /**< Number of times a interrupted query is retried */
|
int query_retries; /**< Number of times a interrupted query is retried */
|
||||||
time_t query_retry_timeout; /**< Timeout for query retries */
|
time_t query_retry_timeout; /**< Timeout for query retries */
|
||||||
bool substitute_variables; /**< Should environment variables be substituted */
|
bool substitute_variables; /**< Should environment variables be substituted */
|
||||||
|
char* local_address; /**< Local address to use when connecting */
|
||||||
} MXS_CONFIG;
|
} MXS_CONFIG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
1
maxscale-system-test/.gitignore
vendored
1
maxscale-system-test/.gitignore
vendored
@ -106,6 +106,7 @@ kill_master
|
|||||||
large_insert_hang
|
large_insert_hang
|
||||||
load_balancing
|
load_balancing
|
||||||
load_balancing_galera
|
load_balancing_galera
|
||||||
|
local_address
|
||||||
long_sysbench
|
long_sysbench
|
||||||
longblob
|
longblob
|
||||||
lots_of_rows
|
lots_of_rows
|
||||||
|
|||||||
@ -840,6 +840,9 @@ add_test_executable_notest(create_rds.cpp create_rds replication LABELS EXTERN_B
|
|||||||
# start sysbench ageints RWSplit for infinite execution
|
# start sysbench ageints RWSplit for infinite execution
|
||||||
add_test_executable_notest(long_sysbench.cpp long_sysbench replication LABELS readwritesplit REPL_BACKEND)
|
add_test_executable_notest(long_sysbench.cpp long_sysbench replication LABELS readwritesplit REPL_BACKEND)
|
||||||
|
|
||||||
|
# test effect of local_address in configuration file
|
||||||
|
add_test_executable(local_address.cpp local_address local_address LABELS REPL_BACKEND)
|
||||||
|
|
||||||
configure_file(templates.h.in templates.h @ONLY)
|
configure_file(templates.h.in templates.h @ONLY)
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|||||||
@ -28,6 +28,7 @@ static const char* integer_values[] =
|
|||||||
"-1",
|
"-1",
|
||||||
"20",
|
"20",
|
||||||
"-20",
|
"-20",
|
||||||
|
"NULL",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ static const char* decimal_values[] =
|
|||||||
"-1.5",
|
"-1.5",
|
||||||
"20.5",
|
"20.5",
|
||||||
"-20.5",
|
"-20.5",
|
||||||
|
"NULL",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ static const char* string_values[] =
|
|||||||
{
|
{
|
||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
"\"The quick brown fox jumps over the lazy dog\"",
|
"\"The quick brown fox jumps over the lazy dog\"",
|
||||||
// "\"The Unicode should work: äöåǢ\"",
|
"NULL",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,26 +87,27 @@ static const char* binary_values[] =
|
|||||||
"\"Hello world!\"",
|
"\"Hello world!\"",
|
||||||
"\"The quick brown fox jumps over the lazy dog\"",
|
"\"The quick brown fox jumps over the lazy dog\"",
|
||||||
"NULL",
|
"NULL",
|
||||||
// "\"The Unicode should work: äöåǢ\"",
|
|
||||||
// "\"These should work for binary types: ⦿☏☃☢😤😂\"",
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* datetime_types[] =
|
static const char* datetime_types[] =
|
||||||
{
|
{
|
||||||
|
"DATETIME",
|
||||||
"DATETIME(1)",
|
"DATETIME(1)",
|
||||||
"DATETIME(2)",
|
"DATETIME(2)",
|
||||||
"DATETIME(3)",
|
"DATETIME(3)",
|
||||||
"DATETIME(4)",
|
"DATETIME(4)",
|
||||||
"DATETIME(5)",
|
"DATETIME(5)",
|
||||||
"DATETIME(6)",
|
"DATETIME(6)",
|
||||||
"TIMESTAMP",
|
// TODO: Fix test setup to use same timezone
|
||||||
|
// "TIMESTAMP",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* datetime_values[] =
|
static const char* datetime_values[] =
|
||||||
{
|
{
|
||||||
"'2018-01-01 11:11:11'",
|
"'2018-01-01 11:11:11'",
|
||||||
|
"NULL",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,6 +120,26 @@ static const char* date_types[] =
|
|||||||
static const char* date_values[] =
|
static const char* date_values[] =
|
||||||
{
|
{
|
||||||
"'2018-01-01'",
|
"'2018-01-01'",
|
||||||
|
"NULL",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* time_types[] =
|
||||||
|
{
|
||||||
|
"TIME",
|
||||||
|
"TIME(1)",
|
||||||
|
"TIME(2)",
|
||||||
|
"TIME(3)",
|
||||||
|
"TIME(4)",
|
||||||
|
"TIME(5)",
|
||||||
|
"TIME(6)",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* time_values[] =
|
||||||
|
{
|
||||||
|
"'12:00:00'",
|
||||||
|
"NULL",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -132,6 +155,7 @@ struct
|
|||||||
{ binary_types, binary_values },
|
{ binary_types, binary_values },
|
||||||
{ datetime_types, datetime_values },
|
{ datetime_types, datetime_values },
|
||||||
{ date_types, date_values },
|
{ date_types, date_values },
|
||||||
|
{ time_types, time_values },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -39,8 +39,10 @@ public:
|
|||||||
bool operator ==(const TestOutput& output) const
|
bool operator ==(const TestOutput& output) const
|
||||||
{
|
{
|
||||||
return m_value == output.getValue() ||
|
return m_value == output.getValue() ||
|
||||||
(m_type.find("BLOB") != std::string::npos &&
|
(m_type.find("BLOB") != std::string::npos && output.getValue().length() == 0) ||
|
||||||
output.getValue().length() == 0);
|
// A NULL timestamp appears to be inserted as NOW() by default in 10.2, a NULL INT is
|
||||||
|
// inserted as 0 and a NULL string gets converted into an empty string by the CDC system
|
||||||
|
(m_value == "NULL" && (output.getValue().empty() || m_type == "TIMESTAMP" || output.getValue() == "0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator !=(const TestOutput& output) const
|
bool operator !=(const TestOutput& output) const
|
||||||
|
|||||||
90
maxscale-system-test/cnf/maxscale.cnf.template.local_address
Normal file
90
maxscale-system-test/cnf/maxscale.cnf.template.local_address
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
[maxscale]
|
||||||
|
threads=###threads###
|
||||||
|
###local_address###
|
||||||
|
|
||||||
|
[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
|
||||||
@ -60,6 +60,8 @@ int main(int argc, char *argv[])
|
|||||||
Test->maxscales->ssh_node(0, (char *) "yum clean all", true);
|
Test->maxscales->ssh_node(0, (char *) "yum clean all", true);
|
||||||
Test->maxscales->ssh_node(0, (char *) "yum install rng-tools -y", true);
|
Test->maxscales->ssh_node(0, (char *) "yum install rng-tools -y", true);
|
||||||
Test->maxscales->ssh_node(0, (char *) "rngd -r /dev/urandom -o /dev/random", true);
|
Test->maxscales->ssh_node(0, (char *) "rngd -r /dev/urandom -o /dev/random", true);
|
||||||
|
Test->maxscales->ssh_node(0, (char *)
|
||||||
|
"yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5", true);
|
||||||
|
|
||||||
Test->maxscales->ssh_node_f(0, true, (char *)
|
Test->maxscales->ssh_node_f(0, true, (char *)
|
||||||
"yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5", true);
|
"yum install -y MariaDB-gssapi-server MariaDB-gssapi-client krb5-server krb5-workstation pam_krb5", true);
|
||||||
|
|||||||
333
maxscale-system-test/local_address.cpp
Normal file
333
maxscale-system-test/local_address.cpp
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 MariaDB Corporation Ab
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License included
|
||||||
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||||
|
*
|
||||||
|
* Change Date: 2019-07-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source License, use
|
||||||
|
* of this software will be governed by version 2 or later of the General
|
||||||
|
* Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "testconnections.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void to_collection(string s, const string& delimiter, T* pT)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
while ((pos = s.find(delimiter)) != std::string::npos)
|
||||||
|
{
|
||||||
|
pT->push_back(s.substr(0, pos));
|
||||||
|
s.erase(0, pos + delimiter.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.length() != 0)
|
||||||
|
{
|
||||||
|
pT->push_back(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string& ltrim(std::string& s)
|
||||||
|
{
|
||||||
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
|
||||||
|
std::not1(std::ptr_fun<int, int>(std::isspace))));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string& rtrim(std::string& s)
|
||||||
|
{
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||||
|
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string& trim(std::string& s)
|
||||||
|
{
|
||||||
|
return ltrim(rtrim(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
string extract_ip(string s)
|
||||||
|
{
|
||||||
|
// 's' looks something like: " inet 127.0.0.1/...";
|
||||||
|
s = s.substr(9); // => "127.0.0.1/...";
|
||||||
|
s = s.substr(0, s.find_first_of('/')); // => "127.0.0.1"
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_maxscale_ips(TestConnections& test, vector<string>* pIps)
|
||||||
|
{
|
||||||
|
int exit_code;
|
||||||
|
string output(test.maxscales->ssh_node_output(0, "ip addr|fgrep inet|fgrep -v ::", false, &exit_code));
|
||||||
|
|
||||||
|
to_collection(output, "\n", pIps);
|
||||||
|
transform(pIps->begin(), pIps->end(), pIps->begin(), extract_ip);
|
||||||
|
|
||||||
|
pIps->erase(find(pIps->begin(), pIps->end(), "127.0.0.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void drop_user(TestConnections& test, const string& user, const string& host)
|
||||||
|
{
|
||||||
|
string stmt("DROP USER IF EXISTS ");
|
||||||
|
|
||||||
|
stmt += "'";
|
||||||
|
stmt += user;
|
||||||
|
stmt += "'@'";
|
||||||
|
stmt += host;
|
||||||
|
stmt += "'";
|
||||||
|
test.try_query(test.maxscales->conn_rwsplit[0], stmt.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_user(TestConnections& test, const string& user, const string& password, const string& host)
|
||||||
|
{
|
||||||
|
string stmt("CREATE USER ");
|
||||||
|
|
||||||
|
stmt += "'";
|
||||||
|
stmt += user;
|
||||||
|
stmt += "'@'";
|
||||||
|
stmt += host;
|
||||||
|
stmt += "'";
|
||||||
|
stmt += " IDENTIFIED BY ";
|
||||||
|
stmt += "'";
|
||||||
|
stmt += password;
|
||||||
|
stmt += "'";
|
||||||
|
test.try_query(test.maxscales->conn_rwsplit[0], stmt.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void grant_access(TestConnections& test, const string& user, const string& host)
|
||||||
|
{
|
||||||
|
string stmt("GRANT SELECT, INSERT, UPDATE ON *.* TO ");
|
||||||
|
|
||||||
|
stmt += "'";
|
||||||
|
stmt += user;
|
||||||
|
stmt += "'@'";
|
||||||
|
stmt += host;
|
||||||
|
stmt += "'";
|
||||||
|
test.try_query(test.maxscales->conn_rwsplit[0], stmt.c_str());
|
||||||
|
|
||||||
|
test.try_query(test.maxscales->conn_rwsplit[0], "FLUSH PRIVILEGES");
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_user_and_grants(TestConnections& test,
|
||||||
|
const string& user, const string& password, const string& host)
|
||||||
|
{
|
||||||
|
test.tprintf("Creating user: %s@%s", user.c_str(), host.c_str());
|
||||||
|
|
||||||
|
drop_user(test, user, host);
|
||||||
|
create_user(test, user, password, host);
|
||||||
|
grant_access(test, user, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_user(MYSQL* pMysql, string* pUser)
|
||||||
|
{
|
||||||
|
bool rv = false;
|
||||||
|
|
||||||
|
if (mysql_query(pMysql, "SELECT USER()") == 0)
|
||||||
|
{
|
||||||
|
MYSQL_RES* pRes = mysql_store_result(pMysql);
|
||||||
|
|
||||||
|
if (mysql_num_rows(pRes) == 1)
|
||||||
|
{
|
||||||
|
MYSQL_ROW row = mysql_fetch_row(pRes);
|
||||||
|
*pUser = row[0];
|
||||||
|
rv = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result(pRes);
|
||||||
|
|
||||||
|
while (mysql_next_result(pMysql) == 0)
|
||||||
|
{
|
||||||
|
MYSQL_RES* pRes = mysql_store_result(pMysql);
|
||||||
|
mysql_free_result(pRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_connect_to_maxscale(const char* zHost, int port, const char* zUser, const char* zPassword)
|
||||||
|
{
|
||||||
|
bool could_connect = false;
|
||||||
|
|
||||||
|
MYSQL* pMysql = mysql_init(NULL);
|
||||||
|
|
||||||
|
if (pMysql)
|
||||||
|
{
|
||||||
|
unsigned int timeout = 5;
|
||||||
|
mysql_options(pMysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
|
||||||
|
mysql_options(pMysql, MYSQL_OPT_READ_TIMEOUT, &timeout);
|
||||||
|
mysql_options(pMysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout);
|
||||||
|
|
||||||
|
if (mysql_real_connect(pMysql, zHost, zUser, zPassword, NULL, port, NULL, 0))
|
||||||
|
{
|
||||||
|
string user;
|
||||||
|
if (select_user(pMysql, &user))
|
||||||
|
{
|
||||||
|
could_connect = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "Could not 'SELECT USER()' as '" << zUser << "': " << mysql_error(pMysql) << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "Could not connect as '" << zUser << "': " << mysql_error(pMysql) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_close(pMysql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return could_connect;
|
||||||
|
}
|
||||||
|
|
||||||
|
string get_local_ip(TestConnections& test)
|
||||||
|
{
|
||||||
|
int exit_code;
|
||||||
|
string output(test.maxscales->ssh_node_output(0, "nslookup maxscale|fgrep Server:|sed s/Server://", false, &exit_code));
|
||||||
|
return trim(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_maxscale_with_local_address(TestConnections& test,
|
||||||
|
const string& replace,
|
||||||
|
const string& with)
|
||||||
|
{
|
||||||
|
string command("sed -i s/");
|
||||||
|
command += replace;
|
||||||
|
command += "/";
|
||||||
|
command += with;
|
||||||
|
command += "/ ";
|
||||||
|
command += "/etc/maxscale.cnf";
|
||||||
|
|
||||||
|
test.maxscales->ssh_node(0, command.c_str(), true);
|
||||||
|
|
||||||
|
test.start_maxscale();
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_connecting(TestConnections& test,
|
||||||
|
const char* zUser, const char* zPassword, const char* zHost,
|
||||||
|
bool should_be_able_to)
|
||||||
|
{
|
||||||
|
bool could_connect = can_connect_to_maxscale(test.maxscales->IP[0], test.maxscales->rwsplit_port[0], zUser, zPassword);
|
||||||
|
|
||||||
|
if (!could_connect && should_be_able_to)
|
||||||
|
{
|
||||||
|
test.assert(false, "%s@%s should have been able to connect, but wasn't.", zUser, zHost);
|
||||||
|
}
|
||||||
|
else if (could_connect && !should_be_able_to)
|
||||||
|
{
|
||||||
|
test.assert(false, "%s@%s should NOT have been able to connect, but was.", zUser, zHost);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (could_connect)
|
||||||
|
{
|
||||||
|
test.tprintf("%s@%s could connect, as expected.", zUser, zHost);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
test.tprintf("%s@%s could NOT connect, as expected.", zUser, zHost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_test(TestConnections& test, const string& ip1, const string& ip2)
|
||||||
|
{
|
||||||
|
test.maxscales->connect();
|
||||||
|
|
||||||
|
string local_ip = get_local_ip(test);
|
||||||
|
|
||||||
|
const char* zUser1 = "alice";
|
||||||
|
const char* zUser2 = "bob";
|
||||||
|
const char* zPassword1 = "alicepwd";
|
||||||
|
const char* zPassword2 = "bobpwd";
|
||||||
|
|
||||||
|
create_user_and_grants(test, zUser1, zPassword1, ip1);
|
||||||
|
create_user_and_grants(test, zUser1, zPassword1, local_ip);
|
||||||
|
create_user_and_grants(test, zUser2, zPassword2, ip2);
|
||||||
|
create_user_and_grants(test, zUser2, zPassword2, local_ip);
|
||||||
|
|
||||||
|
test.tprintf("\n");
|
||||||
|
test.tprintf("Testing default; alice should be able to access, bob not.");
|
||||||
|
|
||||||
|
test_connecting(test, zUser1, zPassword1, ip1.c_str(), true);
|
||||||
|
test_connecting(test, zUser2, zPassword2, ip2.c_str(), false);
|
||||||
|
|
||||||
|
test.maxscales->disconnect();
|
||||||
|
test.stop_maxscale();
|
||||||
|
|
||||||
|
test.tprintf("\n");
|
||||||
|
test.tprintf("Testing with local_address=%s; alice should be able to access, bob not.",
|
||||||
|
ip1.c_str());
|
||||||
|
|
||||||
|
string local_address_ip1 = "local_address=" + ip1;
|
||||||
|
start_maxscale_with_local_address(test, "###local_address###", local_address_ip1);
|
||||||
|
test.maxscales->connect();
|
||||||
|
|
||||||
|
test_connecting(test, zUser1, zPassword1, ip1.c_str(), true);
|
||||||
|
test_connecting(test, zUser2, zPassword2, ip2.c_str(), false);
|
||||||
|
|
||||||
|
test.maxscales->disconnect();
|
||||||
|
test.stop_maxscale();
|
||||||
|
|
||||||
|
test.tprintf("\n");
|
||||||
|
test.tprintf("WARNING: Other IP-address not tested, as usable IP-address not available.");
|
||||||
|
|
||||||
|
#ifdef USABLE_SECOND_IP_ADDRESS_ON_MAXSCALE_NODE_IS_AVAILABLE
|
||||||
|
test.tprintf("\n");
|
||||||
|
test.tprintf("\nTesting with local_address=%s, bob should be able to access, alice not.",
|
||||||
|
ip2.c_str());
|
||||||
|
|
||||||
|
string local_address_ip2 = "local_address=" + ip2;
|
||||||
|
start_maxscale_with_local_address(test, local_address_ip1, local_address_ip2);
|
||||||
|
test.connect_maxscale();
|
||||||
|
|
||||||
|
test_connecting(test, zUser1, zPassword1, ip1.c_str(), false);
|
||||||
|
test_connecting(test, zUser2, zPassword2, ip2.c_str(), true);
|
||||||
|
|
||||||
|
test.maxscales->disconnect();
|
||||||
|
test.stop_maxscale();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
TestConnections test(argc, argv);
|
||||||
|
|
||||||
|
vector<string> ips;
|
||||||
|
get_maxscale_ips(test, &ips);
|
||||||
|
|
||||||
|
if (ips.size() >= 2)
|
||||||
|
{
|
||||||
|
run_test(test, ips[0], ips[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
test.assert(false, "MaxScale node does not have at least two IP-addresses.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return test.global_result;
|
||||||
|
}
|
||||||
@ -20,7 +20,8 @@ int main(int argc, char** argv)
|
|||||||
// Give the monitor some time to detect it
|
// Give the monitor some time to detect it
|
||||||
sleep(5);
|
sleep(5);
|
||||||
|
|
||||||
test.add_result(execute_query_silent(test.maxscales->conn_master[0], "SELECT 1") == 0, "Query should fail");
|
test.add_result(execute_query_silent(test.maxscales->conn_master[0], "SELECT 1") == 0,
|
||||||
|
"Query should fail");
|
||||||
|
|
||||||
// Change the master back to the original one
|
// Change the master back to the original one
|
||||||
test.repl->change_master(0, 1);
|
test.repl->change_master(0, 1);
|
||||||
|
|||||||
@ -116,6 +116,7 @@ const char CN_QUERY_RETRIES[] = "query_retries";
|
|||||||
const char CN_QUERY_RETRY_TIMEOUT[] = "query_retry_timeout";
|
const char CN_QUERY_RETRY_TIMEOUT[] = "query_retry_timeout";
|
||||||
const char CN_RELATIONSHIPS[] = "relationships";
|
const char CN_RELATIONSHIPS[] = "relationships";
|
||||||
const char CN_LINKS[] = "links";
|
const char CN_LINKS[] = "links";
|
||||||
|
const char CN_LOCAL_ADDRESS[] = "local_address";
|
||||||
const char CN_REQUIRED[] = "required";
|
const char CN_REQUIRED[] = "required";
|
||||||
const char CN_RETRY_ON_FAILURE[] = "retry_on_failure";
|
const char CN_RETRY_ON_FAILURE[] = "retry_on_failure";
|
||||||
const char CN_ROUTER[] = "router";
|
const char CN_ROUTER[] = "router";
|
||||||
@ -1622,6 +1623,10 @@ handle_global_item(const char *name, const char *value)
|
|||||||
{
|
{
|
||||||
gateway.passive = config_truth_value((char*)value);
|
gateway.passive = config_truth_value((char*)value);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(name, CN_LOCAL_ADDRESS) == 0)
|
||||||
|
{
|
||||||
|
gateway.local_address = MXS_STRDUP_A(value);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; lognames[i].name; i++)
|
for (i = 0; lognames[i].name; i++)
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
#include <maxscale/alloc.h>
|
#include <maxscale/alloc.h>
|
||||||
|
#include <maxscale/config.h>
|
||||||
#include <maxscale/dcb.h>
|
#include <maxscale/dcb.h>
|
||||||
#include <maxscale/log_manager.h>
|
#include <maxscale/log_manager.h>
|
||||||
#include <maxscale/limits.h>
|
#include <maxscale/limits.h>
|
||||||
@ -1019,6 +1020,8 @@ int open_network_socket(enum mxs_socket_type type, struct sockaddr_storage *addr
|
|||||||
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
|
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
|
||||||
set_port(addr, port);
|
set_port(addr, port);
|
||||||
|
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
|
||||||
if ((type == MXS_SOCKET_NETWORK && !configure_network_socket(so)) ||
|
if ((type == MXS_SOCKET_NETWORK && !configure_network_socket(so)) ||
|
||||||
(type == MXS_SOCKET_LISTENER && !configure_listener_socket(so)))
|
(type == MXS_SOCKET_LISTENER && !configure_listener_socket(so)))
|
||||||
{
|
{
|
||||||
@ -1032,9 +1035,39 @@ int open_network_socket(enum mxs_socket_type type, struct sockaddr_storage *addr
|
|||||||
close(so);
|
close(so);
|
||||||
so = -1;
|
so = -1;
|
||||||
}
|
}
|
||||||
}
|
else if (type == MXS_SOCKET_NETWORK)
|
||||||
|
{
|
||||||
|
MXS_CONFIG* config = config_get_global_options();
|
||||||
|
|
||||||
freeaddrinfo(ai);
|
if (config->local_address)
|
||||||
|
{
|
||||||
|
if ((rc = getaddrinfo(config->local_address, NULL, &hint, &ai)) == 0)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage local_address = {};
|
||||||
|
|
||||||
|
memcpy(&local_address, ai->ai_addr, ai->ai_addrlen);
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
|
||||||
|
if (bind(so, (struct sockaddr*)&local_address, sizeof(local_address)) == 0)
|
||||||
|
{
|
||||||
|
MXS_INFO("Bound connecting socket to \"%s\".", config->local_address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MXS_ERROR("Could not bind connecting socket to local address \"%s\", "
|
||||||
|
"connecting to server using default local address: %s",
|
||||||
|
config->local_address, mxs_strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MXS_ERROR("Could not get address information for local address \"%s\", "
|
||||||
|
"connecting to server using default local address: %s",
|
||||||
|
config->local_address, mxs_strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return so;
|
return so;
|
||||||
|
|||||||
@ -517,6 +517,23 @@ int get_metadata_len(uint8_t type)
|
|||||||
} \
|
} \
|
||||||
}while(false)
|
}while(false)
|
||||||
|
|
||||||
|
// Debug function for checking whether a row event consists of only NULL values
|
||||||
|
static bool all_fields_null(uint8_t* null_bitmap, int ncolumns)
|
||||||
|
{
|
||||||
|
bool rval = true;
|
||||||
|
|
||||||
|
for (long i = 0; i < ncolumns; i++)
|
||||||
|
{
|
||||||
|
if (!bit_is_set(null_bitmap, ncolumns, i))
|
||||||
|
{
|
||||||
|
rval = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Extract the values from a single row in a row event
|
* @brief Extract the values from a single row in a row event
|
||||||
*
|
*
|
||||||
@ -544,7 +561,7 @@ uint8_t* process_row_event_data(TABLE_MAP *map, TABLE_CREATE *create, avro_value
|
|||||||
/** Store the null value bitmap */
|
/** Store the null value bitmap */
|
||||||
uint8_t *null_bitmap = ptr;
|
uint8_t *null_bitmap = ptr;
|
||||||
ptr += (ncolumns + 7) / 8;
|
ptr += (ncolumns + 7) / 8;
|
||||||
ss_dassert(ptr < end);
|
ss_dassert(ptr < end || (bit_is_set(null_bitmap, ncolumns, 0)));
|
||||||
|
|
||||||
char trace[ncolumns][768];
|
char trace[ncolumns][768];
|
||||||
memset(trace, 0, sizeof(trace));
|
memset(trace, 0, sizeof(trace));
|
||||||
|
|||||||
@ -1251,7 +1251,7 @@ static void skip_token(const char** saved)
|
|||||||
{
|
{
|
||||||
const char* ptr = *saved;
|
const char* ptr = *saved;
|
||||||
|
|
||||||
while (*ptr && !isspace(*ptr))
|
while (*ptr && !isspace(*ptr) && *ptr != '(' && *ptr != '.')
|
||||||
{
|
{
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user