Files
DataLogger-Generic/web_db/mysql-connector-python-2.1.4/tests/test_fabric.py
2016-11-01 16:56:22 -05:00

659 lines
23 KiB
Python

# MySQL Connector/Python - MySQL driver written in Python.
# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
# MySQL Connector/Python is licensed under the terms of the GPLv2
# <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
# MySQL Connectors. There are special exceptions to the terms and
# conditions of the GPLv2 as it is applied to this software, see the
# FOSS License Exception
# <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
#
# 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.
#
# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
"""Unittests for mysql.connector.fabric
"""
import datetime
from decimal import Decimal
import time
import unittest
import uuid
try:
from xmlrpclib import Fault, ServerProxy
except ImportError:
# Python v3
from xmlrpc.client import Fault, ServerProxy # pylint: disable=F0401
import tests
import mysql.connector
from mysql.connector import fabric, errorcode
from mysql.connector.fabric import connection, balancing
from mysql.connector.catch23 import UNICODE_TYPES, PY2
from mysql.connector.pooling import PooledMySQLConnection
ERR_NO_FABRIC_CONFIG = "Fabric configuration not available"
def wait_for_gtid(cur, gtid):
cur.execute("SELECT WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(%s, 2)", (gtid,))
cur.fetchall()
class FabricModuleTests(tests.MySQLConnectorTests):
"""Testing mysql.connector.fabric module"""
def test___all___(self):
attrs = [
'MODE_READWRITE',
'MODE_READONLY',
'STATUS_PRIMARY',
'STATUS_SECONDARY',
'SCOPE_GLOBAL',
'SCOPE_LOCAL',
'FabricMySQLServer',
'FabricShard',
'connect',
'Fabric',
'FabricConnection',
'MySQLFabricConnection',
]
for attr in attrs:
try:
getattr(fabric, attr)
except AttributeError:
self.fail("Attribute '{0}' not in fabric.__all__".format(attr))
def test_fabricmyqlserver(self):
attrs = ['uuid', 'group', 'host', 'port', 'mode', 'status', 'weight']
try:
nmdtpl = fabric.FabricMySQLServer(*([''] * len(attrs)))
except TypeError:
self.fail("Fail creating namedtuple FabricMySQLServer")
self.check_namedtuple(nmdtpl, attrs)
def test_fabricshard(self):
attrs = [
'database', 'table', 'column', 'key', 'shard', 'shard_type',
'group', 'global_group'
]
try:
nmdtpl = fabric.FabricShard(*([''] * len(attrs)))
except TypeError:
self.fail("Fail creating namedtuple FabricShard")
self.check_namedtuple(nmdtpl, attrs)
def test_connect(self):
class FakeConnection(object):
def __init__(self, *args, **kwargs):
pass
orig = fabric.MySQLFabricConnection
fabric.MySQLFabricConnection = FakeConnection
self.assertTrue(isinstance(fabric.connect(), FakeConnection))
fabric.MySQLFabricConnection = orig
class ConnectionModuleTests(tests.MySQLConnectorTests):
"""Testing mysql.connector.fabric.connection module"""
def test_module_variables(self):
error_codes = (
errorcode.CR_SERVER_LOST,
errorcode.ER_OPTION_PREVENTS_STATEMENT,
)
self.assertEqual(error_codes, connection.RESET_CACHE_ON_ERROR)
modvars = {
'MYSQL_FABRIC_PORT': {
'xmlrpc': 32274, 'mysql': 32275
},
'DEFAULT_FABRIC_PROTOCOL': 'xmlrpc',
'FABRICS': {},
'_CNX_ATTEMPT_DELAY': 1,
'_CNX_ATTEMPT_MAX': 3,
'_GETCNX_ATTEMPT_DELAY': 1,
'_GETCNX_ATTEMPT_MAX': 3,
'MODE_READONLY': 1,
'MODE_WRITEONLY': 2,
'MODE_READWRITE': 3,
'STATUS_FAULTY': 0,
'STATUS_SPARE': 1,
'STATUS_SECONDARY': 2,
'STATUS_PRIMARY': 3,
'SCOPE_GLOBAL': 'GLOBAL',
'SCOPE_LOCAL': 'LOCAL',
'_SERVER_STATUS_FAULTY': 'FAULTY',
}
for modvar, value in modvars.items():
try:
self.assertEqual(value, getattr(connection, modvar))
except AttributeError:
self.fail("Module variable connection.{0} not found".format(
modvar))
def test_cnx_properties(self):
cnxprops = {
# name: (valid_types, description, default)
'group': ((str,), "Name of group of servers", None),
'key': (tuple([int, str, datetime.datetime,
datetime.date] + list(UNICODE_TYPES)),
"Sharding key", None),
'tables': ((tuple, list), "List of tables in query", None),
'mode': ((int,), "Read-Only, Write-Only or Read-Write",
connection.MODE_READWRITE),
'shard': ((str,), "Identity of the shard for direct connection",
None),
'mapping': ((str,), "", None),
'scope': ((str,), "GLOBAL for accessing Global Group, or LOCAL",
connection.SCOPE_LOCAL),
'attempts': ((int,), "Attempts for getting connection",
connection._CNX_ATTEMPT_MAX),
'attempt_delay': ((int,), "Seconds to wait between each attempt",
connection._CNX_ATTEMPT_DELAY),
}
for prop, desc in cnxprops.items():
try:
self.assertEqual(desc, connection._CNX_PROPERTIES[prop])
except KeyError:
self.fail("Connection property '{0}'' not available".format(
prop))
self.assertEqual(len(cnxprops), len(connection._CNX_PROPERTIES))
def test__fabric_xmlrpc_uri(self):
data = ('example.com', 32274)
exp = 'http://{host}:{port}'.format(host=data[0], port=data[1])
self.assertEqual(exp, connection._fabric_xmlrpc_uri(*data))
def test__fabric_server_uuid(self):
data = ('example.com', 32274)
url = 'http://{host}:{port}'.format(host=data[0], port=data[1])
exp = uuid.uuid3(uuid.NAMESPACE_URL, url)
self.assertEqual(exp, connection._fabric_server_uuid(*data))
def test__validate_ssl_args(self):
func = connection._validate_ssl_args
kwargs = dict(ssl_ca=None, ssl_key=None, ssl_cert=None)
self.assertEqual(None, func(**kwargs))
kwargs = dict(ssl_ca=None, ssl_key='/path/to/key',
ssl_cert=None)
self.assertRaises(AttributeError, func, **kwargs)
kwargs = dict(ssl_ca='/path/to/ca', ssl_key='/path/to/key',
ssl_cert=None)
self.assertRaises(AttributeError, func, **kwargs)
exp = {
'ca': '/path/to/ca',
'key': None,
'cert': None,
}
kwargs = dict(ssl_ca='/path/to/ca', ssl_key=None, ssl_cert=None)
self.assertEqual(exp, func(**kwargs))
exp = {
'ca': '/path/to/ca',
'key': '/path/to/key',
'cert': '/path/to/cert',
}
res = func(ssl_ca=exp['ca'], ssl_cert=exp['cert'], ssl_key=exp['key'])
self.assertEqual(exp, res)
def test_extra_failure_report(self):
func = connection.extra_failure_report
func([])
self.assertEqual([], connection.REPORT_ERRORS_EXTRA)
self.assertRaises(AttributeError, func, 1)
self.assertRaises(AttributeError, func, [1])
exp = [2222]
func(exp)
self.assertEqual(exp, connection.REPORT_ERRORS_EXTRA)
class FabricBalancingBaseScheduling(tests.MySQLConnectorTests):
"""Test fabric.balancing.BaseScheduling"""
def setUp(self):
self.obj = balancing.BaseScheduling()
def test___init__(self):
self.assertEqual([], self.obj._members)
self.assertEqual([], self.obj._ratios)
def test_set_members(self):
self.assertRaises(NotImplementedError, self.obj.set_members, 'spam')
def test_get_next(self):
self.assertRaises(NotImplementedError, self.obj.get_next)
class FabricBalancingWeightedRoundRobin(tests.MySQLConnectorTests):
"""Test fabric.balancing.WeightedRoundRobin"""
def test___init__(self):
balancer = balancing.WeightedRoundRobin()
self.assertEqual([], balancer._members)
self.assertEqual([], balancer._ratios)
self.assertEqual([], balancer._load)
# init with args
class FakeWRR(balancing.WeightedRoundRobin):
def set_members(self, *args):
self.set_members_called = True
balancer = FakeWRR('ham', 'spam')
self.assertTrue(balancer.set_members_called)
def test_members(self):
balancer = balancing.WeightedRoundRobin()
self.assertEqual([], balancer.members)
balancer._members = ['ham']
self.assertEqual(['ham'], balancer.members)
def test_ratios(self):
balancer = balancing.WeightedRoundRobin()
self.assertEqual([], balancer.ratios)
balancer._ratios = ['ham']
self.assertEqual(['ham'], balancer.ratios)
def test_load(self):
balancer = balancing.WeightedRoundRobin()
self.assertEqual([], balancer.load)
balancer._load = ['ham']
self.assertEqual(['ham'], balancer.load)
def test_set_members(self):
balancer = balancing.WeightedRoundRobin()
balancer._members = ['ham']
balancer.set_members()
self.assertEqual([], balancer.members)
servers = [('ham1', 0.2), ('ham2', 0.8)]
balancer.set_members(*servers)
exp = [('ham2', Decimal('0.8')), ('ham1', Decimal('0.2'))]
self.assertEqual(exp, balancer.members)
self.assertEqual([400, 100], balancer.ratios)
self.assertEqual([0, 0], balancer.load)
def test_reset_load(self):
balancer = balancing.WeightedRoundRobin(*[('ham1', 0.2), ('ham2', 0.8)])
balancer._load = [5, 6]
balancer.reset()
self.assertEqual([0, 0], balancer.load)
def test_get_next(self):
servers = [('ham1', 0.2), ('ham2', 0.8)]
balancer = balancing.WeightedRoundRobin(*servers)
self.assertEqual(('ham2', Decimal('0.8')), balancer.get_next())
self.assertEqual([1, 0], balancer.load)
balancer._load = [80, 0]
self.assertEqual(('ham1', Decimal('0.2')), balancer.get_next())
self.assertEqual([80, 1], balancer.load)
balancer._load = [80, 20]
self.assertEqual(('ham2', Decimal('0.8')), balancer.get_next())
self.assertEqual([81, 20], balancer.load)
servers = [('ham1', 0.1), ('ham2', 0.2), ('ham3', 0.7)]
balancer = balancing.WeightedRoundRobin(*servers)
exp_sum = count = 101
while count > 0:
count -= 1
_ = balancer.get_next()
self.assertEqual(exp_sum, sum(balancer.load))
self.assertEqual([34, 34, 33], balancer.load)
servers = [('ham1', 0.2), ('ham2', 0.2), ('ham3', 0.7)]
balancer = balancing.WeightedRoundRobin(*servers)
exp_sum = count = 101
while count > 0:
count -= 1
_ = balancer.get_next()
self.assertEqual(exp_sum, sum(balancer.load))
self.assertEqual([34, 34, 33], balancer.load)
servers = [('ham1', 0.25), ('ham2', 0.25),
('ham3', 0.25), ('ham4', 0.25)]
balancer = balancing.WeightedRoundRobin(*servers)
exp_sum = count = 101
while count > 0:
count -= 1
_ = balancer.get_next()
self.assertEqual(exp_sum, sum(balancer.load))
self.assertEqual([26, 25, 25, 25], balancer.load)
servers = [('ham1', 0.5), ('ham2', 0.5)]
balancer = balancing.WeightedRoundRobin(*servers)
count = 201
while count > 0:
count -= 1
_ = balancer.get_next()
self.assertEqual(1, sum(balancer.load))
self.assertEqual([1, 0], balancer.load)
def test___repr__(self):
balancer = balancing.WeightedRoundRobin(*[('ham1', 0.2), ('ham2', 0.8)])
exp = ("<class 'mysql.connector.fabric.balancing.WeightedRoundRobin'>"
"(load=[0, 0], ratios=[400, 100])")
self.assertEqual(exp, repr(balancer))
def test___eq__(self):
servers = [('ham1', 0.2), ('ham2', 0.8)]
balancer1 = balancing.WeightedRoundRobin(*servers)
balancer2 = balancing.WeightedRoundRobin(*servers)
self.assertTrue(balancer1 == balancer2)
servers = [('ham1', 0.2), ('ham2', 0.3), ('ham3', 0.5)]
balancer3 = balancing.WeightedRoundRobin(*servers)
self.assertFalse(balancer1 == balancer3)
@unittest.skipIf(not tests.FABRIC_CONFIG, ERR_NO_FABRIC_CONFIG)
class FabricShardingTests(tests.MySQLConnectorTests):
"""Test Fabric's sharding"""
emp_data = {
1985: [
(10001, datetime.date(1953, 9, 2), u'Georgi', u'Facello', u'M',
datetime.date(1986, 6, 26)),
(10002, datetime.date(1964, 6, 2), u'Bezalel', u'Simmel', u'F',
datetime.date(1985, 11, 21)),
],
2000: [
(47291, datetime.date(1960, 9, 9), u'Ulf', u'Flexer', u'M',
datetime.date(2000, 1, 12)),
(60134, datetime.date(1964, 4, 21), u'Seshu', u'Rathonyi', u'F',
datetime.date(2000, 1, 2)),
]
}
def setUp(self):
self.cnx = mysql.connector.connect(
fabric=tests.FABRIC_CONFIG, user='root', database='employees'
)
def _check_table(self, table, shard_type):
fabric = self.cnx._fabric
fab_set = fabric.execute("sharding", "lookup_table", table)
found = False
if fab_set.rowcount:
for row in fab_set.rows():
if (row.table_name == table and row.type_name == shard_type):
found = True
break
if found == False:
raise ValueError(
"Table {table} not found or wrong sharding type".format(
table=table))
return True
def _populate(self, cnx, wait_gtid, table, insert, data, shard_key_index):
for employee in data:
cnx.set_property(tables=["employees." + table],
key=employee[shard_key_index],
scope=fabric.SCOPE_LOCAL,
mode=fabric.MODE_READWRITE)
cur = cnx.cursor()
wait_for_gtid(cur, wait_gtid)
cur.execute(insert, employee)
cnx.commit()
def _truncate(self, cur, table):
cur.execute("TRUNCATE TABLE {0}".format(table))
cur.execute("SELECT @@global.gtid_executed")
return cur.fetchone()[0]
def test_range(self):
self.assertTrue(self._check_table("employees.employees_range", 'RANGE'))
tbl_name = "employees_range"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
scope=fabric.SCOPE_GLOBAL,
mode=fabric.MODE_READWRITE)
cur = self.cnx.cursor()
gtid_executed = self._truncate(cur, tbl_name)
self.cnx.commit()
insert = ("INSERT INTO {0} "
"VALUES (%s, %s, %s, %s, %s, %s)").format(tbl_name)
self._populate(self.cnx, gtid_executed, tbl_name, insert,
self.emp_data[1985] + self.emp_data[2000], 0)
time.sleep(2)
# Year is key of self.emp_data, second value is emp_no for RANGE key
exp_keys = [(1985, 10002), (2000, 47291)]
for year, emp_no in exp_keys:
self.cnx.set_property(tables=tables,
scope=fabric.SCOPE_LOCAL,
key=emp_no, mode=fabric.MODE_READONLY)
cur = self.cnx.cursor()
cur.execute("SELECT * FROM {0}".format(tbl_name))
rows = cur.fetchall()
self.assertEqual(rows, self.emp_data[year])
self.cnx.set_property(tables=tables,
key='spam', mode=fabric.MODE_READONLY)
self.assertRaises(ValueError, self.cnx.cursor)
def test_range_datetime(self):
self.assertTrue(self._check_table(
"employees.employees_range_datetime", 'RANGE_DATETIME'))
tbl_name = "employees_range_datetime"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
scope=fabric.SCOPE_GLOBAL,
mode=fabric.MODE_READWRITE)
cur = self.cnx.cursor()
gtid_executed = self._truncate(cur, tbl_name)
self.cnx.commit()
insert = ("INSERT INTO {0} "
"VALUES (%s, %s, %s, %s, %s, %s)").format(tbl_name)
self._populate(self.cnx, gtid_executed, tbl_name, insert,
self.emp_data[1985] + self.emp_data[2000], 5)
time.sleep(2)
hire_dates = [datetime.date(1985, 1, 1), datetime.date(2000, 1, 1)]
for hire_date in hire_dates:
self.cnx.set_property(tables=tables,
key=hire_date, mode=fabric.MODE_READONLY)
cur = self.cnx.cursor()
cur.execute("SELECT * FROM {0}".format(tbl_name))
rows = cur.fetchall()
self.assertEqual(rows, self.emp_data[hire_date.year])
self.cnx.set_property(tables=tables,
key='2014-01-02', mode=fabric.MODE_READONLY)
self.assertRaises(ValueError, self.cnx.cursor)
def test_range_string(self):
self.assertTrue(self._check_table(
"employees.employees_range_string", 'RANGE_STRING'))
tbl_name = "employees_range_string"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
scope=fabric.SCOPE_GLOBAL,
mode=fabric.MODE_READWRITE)
cur = self.cnx.cursor()
gtid_executed = self._truncate(cur, tbl_name)
self.cnx.commit()
insert = ("INSERT INTO {0} "
"VALUES (%s, %s, %s, %s, %s, %s)").format(tbl_name)
self._populate(self.cnx, gtid_executed, tbl_name, insert,
self.emp_data[1985] + self.emp_data[2000], 3)
time.sleep(2)
emp_exp_range_string = {
'A': [self.emp_data[1985][0],
self.emp_data[2000][0]],
'M': [self.emp_data[1985][1],
self.emp_data[2000][1]],
}
str_keys = [u'A', u'M']
for str_key in str_keys:
self.cnx.set_property(tables=tables,
key=str_key, mode=fabric.MODE_READONLY)
cur = self.cnx.cursor()
cur.execute("SELECT * FROM {0}".format(tbl_name))
rows = cur.fetchall()
self.assertEqual(rows, emp_exp_range_string[str_key])
self.cnx.set_property(tables=tables,
key=b'not unicode str', mode=fabric.MODE_READONLY)
self.assertRaises(ValueError, self.cnx.cursor)
self.cnx.set_property(tables=tables,
key=12345, mode=fabric.MODE_READONLY)
self.assertRaises(ValueError, self.cnx.cursor)
if PY2:
self.cnx.set_property(tables=tables,
key='not unicode str',
mode=fabric.MODE_READONLY)
self.assertRaises(ValueError, self.cnx.cursor)
def test_bug19642249(self):
self.assertTrue(self._check_table(
"employees.employees_range_string", 'RANGE_STRING'))
# Invalid key for RANGE_STRING
tbl_name = "employees_range_string"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
key=u'1', mode=fabric.MODE_READONLY)
try:
cur = self.cnx.cursor()
except ValueError as exc:
self.assertEqual("Key invalid; was '1'", str(exc))
else:
self.fail("ValueError not raised")
# Invalid key for RANGE_DATETIME
tbl_name = "employees_range_datetime"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
key=datetime.date(1977, 1, 1),
mode=fabric.MODE_READONLY)
try:
cur = self.cnx.cursor()
except ValueError as exc:
self.assertEqual("Key invalid; was '1977-01-01'", str(exc))
else:
self.fail("ValueError not raised")
def test_bug19331658(self):
"""Pooling not working with fabric
"""
self.assertRaises(
AttributeError, mysql.connector.connect,
fabric=tests.FABRIC_CONFIG, user='root', database='employees',
pool_name='mypool')
pool_size = 2
cnx = mysql.connector.connect(
fabric=tests.FABRIC_CONFIG, user='root', database='employees',
pool_size=pool_size, pool_reset_session=False
)
tbl_name = "employees_range"
tables = ["employees.{0}".format(tbl_name)]
cnx.set_property(tables=tables,
scope=fabric.SCOPE_GLOBAL,
mode=fabric.MODE_READWRITE)
cnx.cursor()
self.assertTrue(isinstance(cnx._mysql_cnx, PooledMySQLConnection))
data = self.emp_data[1985]
for emp in data:
cnx.set_property(tables=tables,
key=emp[0],
scope=fabric.SCOPE_LOCAL,
mode=fabric.MODE_READWRITE)
cnx.cursor()
mysqlserver = cnx._fabric_mysql_server
config = cnx._mysql_config
self.assertEqual(
cnx._mysql_cnx.pool_name, "{0}_{1}_{2}_{3}".format(
mysqlserver.host, mysqlserver.port, config['user'],
config['database'])
)
def test_range_hash(self):
self.assertTrue(self._check_table(
"employees.employees_hash", 'HASH'))
tbl_name = "employees_hash"
tables = ["employees.{0}".format(tbl_name)]
self.cnx.set_property(tables=tables,
scope=fabric.SCOPE_GLOBAL,
mode=fabric.MODE_READWRITE)
cur = self.cnx.cursor()
gtid_executed = self._truncate(cur, tbl_name)
self.cnx.commit()
insert = ("INSERT INTO {0} "
"VALUES (%s, %s, %s, %s, %s, %s)").format(tbl_name)
self._populate(self.cnx, gtid_executed, tbl_name, insert,
self.emp_data[1985] + self.emp_data[2000], 3)
time.sleep(2)
emp_exp_hash = self.emp_data[1985] + self.emp_data[2000]
rows = []
self.cnx.reset_properties()
str_keys = ['group1', 'group2']
for str_key in str_keys:
self.cnx.set_property(group=str_key, mode=fabric.MODE_READONLY)
cur = self.cnx.cursor()
cur.execute("SELECT * FROM {0}".format(tbl_name))
rows += cur.fetchall()
self.assertEqual(rows, emp_exp_hash)