[API][ADMIN_API] Support MySQL clusters
The API and Admin API server can now connect to multiple servers and picks one at random on each access. If one fails SQLAlchemy automatically tries 3 times to get a new connection. Change-Id: I961eee18fb402735684e2cfb83fb5dc416e46f87
This commit is contained in:
@@ -13,13 +13,20 @@ Configuration File
|
||||
.. code-block:: ini
|
||||
|
||||
[admin_api]
|
||||
db_host=localhost
|
||||
db_user=root
|
||||
db_pass=
|
||||
db_schema=lbaas
|
||||
db_section=mysql1
|
||||
ssl_certfile=/opt/server.crt
|
||||
ssl_keyfile=/opt/server.key
|
||||
|
||||
[mysql1]
|
||||
host=localhost
|
||||
port=3306
|
||||
username=root
|
||||
password=
|
||||
schema=lbaas
|
||||
ssl_cert=/opt/mysql_cert.crt
|
||||
ssl_key=/opt/mysql_key.key
|
||||
ssl_ca=/opt/mysql_ca.ca
|
||||
|
||||
Command Line Options
|
||||
--------------------
|
||||
.. program:: libra_admin_api
|
||||
@@ -32,41 +39,10 @@ Command Line Options
|
||||
|
||||
The port number to listen on, default is 8889
|
||||
|
||||
.. option:: --db_host <HOSTNAME>
|
||||
.. option:: --db_secions <SECTIONNAME>
|
||||
|
||||
The host name for the MySQL database server
|
||||
|
||||
.. option:: --db_port <PORT>
|
||||
|
||||
The port number for the MySQL database server
|
||||
|
||||
.. option:: --db_user <USERNAME>
|
||||
|
||||
The username for the MySQL database server
|
||||
|
||||
.. option:: --db_pass <PASSWORD>
|
||||
|
||||
The password for the MySQL database server
|
||||
|
||||
.. option:: --db_schema <SCHEMA>
|
||||
|
||||
The schema containing the LBaaS tables in the MySQL database server
|
||||
|
||||
.. option:: --db_ssl
|
||||
|
||||
Enable MySQL SSL support
|
||||
|
||||
.. option:: --db_ssl_cert <CERTIFICATE PATH>
|
||||
|
||||
The path for the MySQL SSL certificate
|
||||
|
||||
.. option:: --db_ssl_key <KEY PATH>
|
||||
|
||||
The path for the MySQL SSL key
|
||||
|
||||
.. option:: --db_ssl_ca <CA PATH>
|
||||
|
||||
The path for the MySQL SSL Certificate Authority
|
||||
Config file sections that describe the MySQL servers. This option can
|
||||
be specified multiple times for Galera or NDB clusters.
|
||||
|
||||
.. option:: --ssl_certfile <PATH>
|
||||
|
||||
|
||||
@@ -13,10 +13,7 @@ Configuration File
|
||||
.. code-block:: ini
|
||||
|
||||
[api]
|
||||
db_host=localhost
|
||||
db_user=root
|
||||
db_pass=
|
||||
db_schema=lbaas
|
||||
db_sections=mysql1
|
||||
gearman=127.0.0.1:4730
|
||||
keystone_module=keystoneclient.middleware.auth_token:AuthProtocol
|
||||
swift_basepath=lbaaslogs
|
||||
@@ -24,6 +21,16 @@ Configuration File
|
||||
ssl_certfile=/opt/certfile.crt
|
||||
ssl_keyfile=/opt/keyfile.key
|
||||
|
||||
[mysql1]
|
||||
host=localhost
|
||||
port=3306
|
||||
username=root
|
||||
password=
|
||||
schema=lbaas
|
||||
ssl_cert=/opt/mysql_cert.crt
|
||||
ssl_key=/opt/mysql_key.key
|
||||
ssl_ca=/opt/mysql_ca.ca
|
||||
|
||||
In addition to this any options that are specific to the given keystone
|
||||
module should be stored in the ``[keystone]`` section.
|
||||
|
||||
@@ -43,41 +50,10 @@ Command Line Options
|
||||
|
||||
Do not use keystone authentication, for testing purposes only
|
||||
|
||||
.. option:: --db_host <HOSTNAME>
|
||||
.. option:: --db_secions <SECTIONNAME>
|
||||
|
||||
The host name for the MySQL database server
|
||||
|
||||
.. option:: --db_port <PORT>
|
||||
|
||||
The port number for the MySQL database server
|
||||
|
||||
.. option:: --db_user <USERNAME>
|
||||
|
||||
The username for the MySQL database server
|
||||
|
||||
.. option:: --db_pass <PASSWORD>
|
||||
|
||||
The password for the MySQL database server
|
||||
|
||||
.. option:: --db_schema <SCHEMA>
|
||||
|
||||
The schema containing the LBaaS tables in the MySQL database server
|
||||
|
||||
.. option:: --db_ssl
|
||||
|
||||
Enable MySQL SSL support
|
||||
|
||||
.. option:: --db_ssl_cert <CERTIFICATE PATH>
|
||||
|
||||
The path for the MySQL SSL certificate
|
||||
|
||||
.. option:: --db_ssl_key <KEY PATH>
|
||||
|
||||
The path for the MySQL SSL key
|
||||
|
||||
.. option:: --db_ssl_ca <CA PATH>
|
||||
|
||||
The path for the MySQL SSL Certificate Authority
|
||||
Config file sections that describe the MySQL servers. This option can
|
||||
be specified multiple times for Galera or NDB clusters.
|
||||
|
||||
.. option:: --gearman <HOST:POST>
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ poll_timeout = 5
|
||||
poll_timeout_retry = 30
|
||||
|
||||
[admin_api]
|
||||
db_user=root
|
||||
db_pass=passwd
|
||||
db_schema=lbaas
|
||||
db_sections=mysql1
|
||||
ssl_certfile=certfile.crt
|
||||
ssl_keyfile=keyfile.key
|
||||
|
||||
@@ -83,9 +81,7 @@ ssl_keyfile=keyfile.key
|
||||
host=0.0.0.0
|
||||
port=8080
|
||||
disable_keystone=False
|
||||
db_user=root
|
||||
db_pass=passwd
|
||||
db_schema=lbaas
|
||||
db_sections=mysql1
|
||||
gearman=127.0.0.1:4730
|
||||
swift_basepath=lbaaslogs
|
||||
swift_endpoint=https://host.com:443/v1/
|
||||
@@ -94,5 +90,11 @@ ssl_keyfile=keyfile.key
|
||||
expire_days=7
|
||||
ip_filters=192.168.0.0/24
|
||||
|
||||
[mysql1]
|
||||
username=root
|
||||
password=
|
||||
schema=lbaas
|
||||
host=localhost
|
||||
|
||||
# Keystone options go here
|
||||
[keystone]
|
||||
|
||||
@@ -40,18 +40,8 @@ def setup_app(pecan_config, args):
|
||||
if not pecan_config:
|
||||
pecan_config = get_pecan_config()
|
||||
config = dict(pecan_config)
|
||||
config['database'] = {
|
||||
'username': args.db_user,
|
||||
'password': args.db_pass,
|
||||
'host': args.db_host,
|
||||
'schema': args.db_schema,
|
||||
'port': args.db_port,
|
||||
'schema': args.db_schema,
|
||||
'use_ssl': args.db_ssl,
|
||||
'ssl_cert': args.db_ssl_cert,
|
||||
'ssl_key': args.db_ssl_key,
|
||||
'ssl_ca': args.db_ssl_ca
|
||||
}
|
||||
config['database'] = args.db_sections
|
||||
config['conffile'] = args.config
|
||||
if args.debug:
|
||||
config['wsme'] = {'debug': True}
|
||||
config['app']['debug'] = True
|
||||
@@ -92,31 +82,8 @@ def main():
|
||||
'--port', help='Port number for API server', type=int, default=8889
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_user', help='MySQL database user'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_pass', help='MySQL database password'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_host', help='MySQL host name'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_port', help='MySQL port number', default=3306, type=int
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_schema', help='MySQL schema for libra'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl', help='Enable MySQL SSL connections', action='store_true'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_cert', help='MySQL SSL certificate'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_key', help='MySQL SSL key'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_ca', help='MySQL SSL certificate authority'
|
||||
'--db_sections', action='append', default=[],
|
||||
help='MySQL config sections in the config file'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--ssl_certfile',
|
||||
@@ -129,12 +96,7 @@ def main():
|
||||
|
||||
args = options.run()
|
||||
|
||||
required_args = [
|
||||
'db_user', 'db_pass', 'db_host', 'db_schema', 'ssl_certfile',
|
||||
'ssl_keyfile'
|
||||
]
|
||||
if args.db_ssl:
|
||||
required_args.extend(['db_ssl_cert', 'db_ssl_key', 'db_ssl_ca'])
|
||||
required_args = ['db_sections', 'ssl_certfile', 'ssl_keyfile']
|
||||
|
||||
missing_args = 0
|
||||
for req in required_args:
|
||||
@@ -147,6 +109,10 @@ def main():
|
||||
if missing_args:
|
||||
return 2
|
||||
|
||||
if not isinstance(args.db_sections, list):
|
||||
db_list = args.db_sections.split()
|
||||
args.db_sections = db_list
|
||||
|
||||
pc = get_pecan_config()
|
||||
if not args.nodaemon:
|
||||
pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10)
|
||||
|
||||
@@ -15,38 +15,47 @@
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, create_engine
|
||||
from sqlalchemy import INTEGER, VARCHAR, BIGINT
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship, backref, sessionmaker
|
||||
from sqlalchemy.orm import relationship, backref, sessionmaker, Session
|
||||
import sqlalchemy.types as types
|
||||
import random
|
||||
import ConfigParser
|
||||
from pecan import conf
|
||||
|
||||
# TODO replace this with something better
|
||||
conn_string = '''mysql://%s:%s@%s:%d/%s''' % (
|
||||
conf.database.username,
|
||||
conf.database.password,
|
||||
conf.database.host,
|
||||
conf.database.port,
|
||||
conf.database.schema
|
||||
)
|
||||
|
||||
if conf.database.use_ssl:
|
||||
ssl_args = {'ssl': {
|
||||
'cert': conf.database.ssl_cert,
|
||||
'key': conf.database.ssl_key,
|
||||
'ca': conf.database.ssl_ca
|
||||
}}
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read([conf.conffile])
|
||||
engines = []
|
||||
for section in conf.database:
|
||||
db_conf = config._sections[section]
|
||||
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", connect_args=ssl_args,
|
||||
pool_recycle=3600
|
||||
)
|
||||
else:
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_recycle=3600
|
||||
conn_string = '''mysql://%s:%s@%s:%d/%s''' % (
|
||||
db_conf['username'],
|
||||
db_conf['password'],
|
||||
db_conf['host'],
|
||||
db_conf.get('port', 3306),
|
||||
db_conf['schema']
|
||||
)
|
||||
|
||||
if 'ssl_key' in db_conf:
|
||||
ssl_args = {'ssl': {
|
||||
'cert': db_conf['ssl_cert'],
|
||||
'key': db_conf['ssl_key'],
|
||||
'ca': db_conf['ssl_ca']
|
||||
}}
|
||||
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
connect_args=ssl_args, pool_recycle=3600
|
||||
)
|
||||
else:
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
pool_recycle=3600
|
||||
)
|
||||
engines.append(engine)
|
||||
|
||||
DeclarativeBase = declarative_base()
|
||||
metadata = DeclarativeBase.metadata
|
||||
metadata.bind = engine
|
||||
|
||||
loadbalancers_devices = Table(
|
||||
'loadbalancers_devices',
|
||||
@@ -129,12 +138,17 @@ class Node(DeclarativeBase):
|
||||
weight = Column(u'weight', INTEGER(), nullable=False)
|
||||
|
||||
|
||||
class RoutingSession(Session):
|
||||
def get_bind(self, mapper=None, clause=None):
|
||||
return random.choice(engines)
|
||||
|
||||
|
||||
class db_session(object):
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
|
||||
def __enter__(self):
|
||||
self.session = sessionmaker(bind=engine)()
|
||||
self.session = sessionmaker(bind=engine, class_=RoutingSession)()
|
||||
return self.session
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
|
||||
@@ -46,17 +46,8 @@ def setup_app(pecan_config, args):
|
||||
if not pecan_config:
|
||||
pecan_config = get_pecan_config()
|
||||
config = dict(pecan_config)
|
||||
config['database'] = {
|
||||
'username': args.db_user,
|
||||
'password': args.db_pass,
|
||||
'host': args.db_host,
|
||||
'port': args.db_port,
|
||||
'schema': args.db_schema,
|
||||
'use_ssl': args.db_ssl,
|
||||
'ssl_cert': args.db_ssl_cert,
|
||||
'ssl_key': args.db_ssl_key,
|
||||
'ssl_ca': args.db_ssl_ca
|
||||
}
|
||||
config['database'] = args.db_sections
|
||||
config['conffile'] = args.config
|
||||
config['swift'] = {
|
||||
'swift_basepath': args.swift_basepath,
|
||||
'swift_endpoint': args.swift_endpoint
|
||||
@@ -115,31 +106,8 @@ def main():
|
||||
action='store_true'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_user', help='MySQL database user'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_pass', help='MySQL database password'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_host', help='MySQL host name'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_port', help='MySQL port number', default=3306, type=int
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_schema', help='MySQL schema for libra'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl', help='Enable MySQL SSL connections', action='store_true'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_cert', help='MySQL SSL certificate'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_key', help='MySQL SSL key'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--db_ssl_ca', help='MySQL SSL certificate authority'
|
||||
'--db_sections', action='append', default=[],
|
||||
help='MySQL config sections in the config file'
|
||||
)
|
||||
options.parser.add_argument(
|
||||
'--gearman', action='append', metavar='HOST:PORT', default=[],
|
||||
@@ -178,11 +146,9 @@ def main():
|
||||
args = options.run()
|
||||
|
||||
required_args = [
|
||||
'db_user', 'db_pass', 'db_host', 'db_schema', 'swift_basepath',
|
||||
'db_sections', 'swift_basepath',
|
||||
'swift_endpoint', 'ssl_certfile', 'ssl_keyfile'
|
||||
]
|
||||
if args.db_ssl:
|
||||
required_args.extend(['db_ssl_cert', 'db_ssl_key', 'db_ssl_ca'])
|
||||
|
||||
missing_args = 0
|
||||
for req in required_args:
|
||||
@@ -206,6 +172,10 @@ def main():
|
||||
svr_list = args.gearman.split()
|
||||
args.gearman = svr_list
|
||||
|
||||
if not isinstance(args.db_sections, list):
|
||||
db_list = args.db_sections.split()
|
||||
args.db_sections = db_list
|
||||
|
||||
if not isinstance(args.ip_filters, list):
|
||||
ip_list = args.ip_filters.split()
|
||||
args.ip_filters = ip_list
|
||||
|
||||
@@ -15,39 +15,47 @@
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, create_engine
|
||||
from sqlalchemy import INTEGER, VARCHAR, BIGINT
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import relationship, backref, sessionmaker
|
||||
from sqlalchemy.orm import relationship, backref, sessionmaker, Session
|
||||
import sqlalchemy.types as types
|
||||
import random
|
||||
import ConfigParser
|
||||
from pecan import conf
|
||||
|
||||
# TODO replace this with something better
|
||||
conn_string = '''mysql://%s:%s@%s:%d/%s''' % (
|
||||
conf.database.username,
|
||||
conf.database.password,
|
||||
conf.database.host,
|
||||
conf.database.port,
|
||||
conf.database.schema
|
||||
)
|
||||
|
||||
if conf.database.use_ssl:
|
||||
ssl_args = {'ssl': {
|
||||
'cert': conf.database.ssl_cert,
|
||||
'key': conf.database.ssl_key,
|
||||
'ca': conf.database.ssl_ca
|
||||
}}
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read([conf.conffile])
|
||||
engines = []
|
||||
for section in conf.database:
|
||||
db_conf = config._sections[section]
|
||||
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
connect_args=ssl_args, pool_recycle=3600
|
||||
)
|
||||
else:
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
pool_recycle=3600
|
||||
conn_string = '''mysql://%s:%s@%s:%d/%s''' % (
|
||||
db_conf['username'],
|
||||
db_conf['password'],
|
||||
db_conf['host'],
|
||||
db_conf.get('port', 3306),
|
||||
db_conf['schema']
|
||||
)
|
||||
|
||||
if 'ssl_key' in db_conf:
|
||||
ssl_args = {'ssl': {
|
||||
'cert': db_conf['ssl_cert'],
|
||||
'key': db_conf['ssl_key'],
|
||||
'ca': db_conf['ssl_ca']
|
||||
}}
|
||||
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
connect_args=ssl_args, pool_recycle=3600
|
||||
)
|
||||
else:
|
||||
engine = create_engine(
|
||||
conn_string, isolation_level="READ COMMITTED", pool_size=20,
|
||||
pool_recycle=3600
|
||||
)
|
||||
engines.append(engine)
|
||||
|
||||
DeclarativeBase = declarative_base()
|
||||
metadata = DeclarativeBase.metadata
|
||||
metadata.bind = engine
|
||||
|
||||
loadbalancers_devices = Table(
|
||||
'loadbalancers_devices',
|
||||
@@ -130,12 +138,17 @@ class Node(DeclarativeBase):
|
||||
weight = Column(u'weight', INTEGER(), nullable=False)
|
||||
|
||||
|
||||
class RoutingSession(Session):
|
||||
def get_bind(self, mapper=None, clause=None):
|
||||
return random.choice(engines)
|
||||
|
||||
|
||||
class db_session(object):
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
|
||||
def __enter__(self):
|
||||
self.session = sessionmaker(bind=engine)()
|
||||
self.session = sessionmaker(bind=engine, class_=RoutingSession)()
|
||||
return self.session
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
|
||||
Reference in New Issue
Block a user