[API][ADMIN_API][MGM]: Auto-failover code
* Refactor pool manager into a gearman worker * Add delete and floating IP functions to pool manager * Fix flake8 for API server * Add new table so that admin APIs can track current builders * Add support to Admin API to build nodes using gearman pool manager * Add auto-delete (Nova and DB) of used devices * Mark deleted load balancers as DELETED instead of OFFLINE/ERROR * Move expunge handler thread from API to ADMIN_API * Don't ERROR check all devices, just used ones * Add vip pool scheduler * Add vip support to node build/list/delete/floatingIP * Use vip ID instead of device ID for API output * Move DB and gearman from API into common section for Admin API * Make stats thread rebuild a bad device Change-Id: I11ee8d21610ccfdf551a0db6c4734d7fc44cced5
This commit is contained in:
@@ -1,191 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License'); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
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, Session
|
||||
import sqlalchemy.types as types
|
||||
import random
|
||||
import time
|
||||
import ConfigParser
|
||||
from pecan import conf
|
||||
import logging
|
||||
|
||||
|
||||
DeclarativeBase = declarative_base()
|
||||
metadata = DeclarativeBase.metadata
|
||||
|
||||
loadbalancers_devices = Table(
|
||||
'loadbalancers_devices',
|
||||
metadata,
|
||||
Column('loadbalancer', Integer, ForeignKey('loadbalancers.id')),
|
||||
Column('device', Integer, ForeignKey('devices.id'))
|
||||
)
|
||||
|
||||
|
||||
class FormatedDateTime(types.TypeDecorator):
|
||||
'''formats date to match iso 8601 standards
|
||||
'''
|
||||
|
||||
impl = types.DateTime
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
return value.strftime('%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
|
||||
class Limits(DeclarativeBase):
|
||||
__tablename__ = 'global_limits'
|
||||
id = Column(u'id', Integer, primary_key=True, nullable=False)
|
||||
name = Column(u'name', VARCHAR(length=128), nullable=False)
|
||||
value = Column(u'value', BIGINT(), nullable=False)
|
||||
|
||||
|
||||
class Device(DeclarativeBase):
|
||||
"""device model"""
|
||||
__tablename__ = 'devices'
|
||||
#column definitions
|
||||
az = Column(u'az', INTEGER(), nullable=False)
|
||||
created = Column(u'created', FormatedDateTime(), nullable=False)
|
||||
floatingIpAddr = Column(
|
||||
u'floatingIpAddr', VARCHAR(length=128), nullable=False
|
||||
)
|
||||
id = Column(u'id', BIGINT(), primary_key=True, nullable=False)
|
||||
name = Column(u'name', VARCHAR(length=128), nullable=False)
|
||||
publicIpAddr = Column(u'publicIpAddr', VARCHAR(length=128), nullable=False)
|
||||
status = Column(u'status', VARCHAR(length=128), nullable=False)
|
||||
type = Column(u'type', VARCHAR(length=128), nullable=False)
|
||||
updated = Column(u'updated', FormatedDateTime(), nullable=False)
|
||||
|
||||
|
||||
class LoadBalancer(DeclarativeBase):
|
||||
"""load balancer model"""
|
||||
__tablename__ = 'loadbalancers'
|
||||
#column definitions
|
||||
algorithm = Column(u'algorithm', VARCHAR(length=80), nullable=False)
|
||||
errmsg = Column(u'errmsg', VARCHAR(length=128))
|
||||
id = Column(u'id', BIGINT(), primary_key=True, nullable=False)
|
||||
name = Column(u'name', VARCHAR(length=128), nullable=False)
|
||||
port = Column(u'port', INTEGER(), nullable=False)
|
||||
protocol = Column(u'protocol', VARCHAR(length=128), nullable=False)
|
||||
status = Column(u'status', VARCHAR(length=50), nullable=False)
|
||||
tenantid = Column(u'tenantid', VARCHAR(length=128), nullable=False)
|
||||
updated = Column(u'updated', FormatedDateTime(), nullable=False)
|
||||
created = Column(u'created', FormatedDateTime(), nullable=False)
|
||||
|
||||
nodes = relationship(
|
||||
'Node', backref=backref('loadbalancers', order_by='Node.id')
|
||||
)
|
||||
devices = relationship(
|
||||
'Device', secondary=loadbalancers_devices, backref='loadbalancers',
|
||||
lazy='joined'
|
||||
)
|
||||
|
||||
|
||||
class Node(DeclarativeBase):
|
||||
"""node model"""
|
||||
__tablename__ = 'nodes'
|
||||
#column definitions
|
||||
address = Column(u'address', VARCHAR(length=128), nullable=False)
|
||||
enabled = Column(u'enabled', Integer(), nullable=False)
|
||||
id = Column(u'id', BIGINT(), primary_key=True, nullable=False)
|
||||
lbid = Column(
|
||||
u'lbid', BIGINT(), ForeignKey('loadbalancers.id'), nullable=False
|
||||
)
|
||||
port = Column(u'port', INTEGER(), nullable=False)
|
||||
status = Column(u'status', VARCHAR(length=128), nullable=False)
|
||||
weight = Column(u'weight', INTEGER(), nullable=False)
|
||||
|
||||
|
||||
class RoutingSession(Session):
|
||||
""" If an engine is already in use, re-use it. Otherwise we can end up
|
||||
with deadlocks in Galera, see http://tinyurl.com/9h6qlly
|
||||
switch engines every 60 seconds of idle time """
|
||||
|
||||
engines = []
|
||||
last_engine = None
|
||||
last_engine_time = 0
|
||||
|
||||
def get_bind(self, mapper=None, clause=None):
|
||||
if not RoutingSession.engines:
|
||||
self._build_engines()
|
||||
|
||||
if (
|
||||
RoutingSession.last_engine
|
||||
and time.time() < RoutingSession.last_engine_time + 60
|
||||
):
|
||||
RoutingSession.last_engine_time = time.time()
|
||||
return RoutingSession.last_engine
|
||||
engine = random.choice(RoutingSession.engines)
|
||||
RoutingSession.last_engine = engine
|
||||
RoutingSession.last_engine_time = time.time()
|
||||
return engine
|
||||
|
||||
def _build_engines(self):
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read([conf.conffile])
|
||||
for section in conf.database:
|
||||
db_conf = config._sections[section]
|
||||
|
||||
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
|
||||
)
|
||||
RoutingSession.engines.append(engine)
|
||||
|
||||
|
||||
class db_session(object):
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
def __enter__(self):
|
||||
for x in xrange(10):
|
||||
try:
|
||||
self.session = sessionmaker(class_=RoutingSession)()
|
||||
self.session.execute("SELECT 1")
|
||||
return self.session
|
||||
except:
|
||||
self.logger.error(
|
||||
'Could not connect to DB server: {0}'.format(
|
||||
RoutingSession.last_engine.url
|
||||
)
|
||||
)
|
||||
RoutingSession.last_engine = None
|
||||
self.logger.error('Could not connect to any DB server')
|
||||
return None
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.session.close()
|
||||
return False
|
||||
Reference in New Issue
Block a user