Move to openstack.common.db
Change-Id: I74a2e1d25b378b57e2e0ba181eee95bf96a5afea
This commit is contained in:
parent
6673ceeb3a
commit
5a57f15ded
@ -14,8 +14,13 @@ bind_port = 8082
|
|||||||
# Log to this file. Make sure the user has permissions to write to this file!
|
# Log to this file. Make sure the user has permissions to write to this file!
|
||||||
log_file = /tmp/murano-api.log
|
log_file = /tmp/murano-api.log
|
||||||
|
|
||||||
|
[database]
|
||||||
#A valid SQLAlchemy connection string for the metadata database
|
#A valid SQLAlchemy connection string for the metadata database
|
||||||
sql_connection = sqlite:///murano.sqlite
|
#connection = mysql://root:password@localhost:3306/murano
|
||||||
|
connection = sqlite:///murano.sqlite
|
||||||
|
|
||||||
|
#A boolean that determines if the database will be automatically created
|
||||||
|
auto_create = True
|
||||||
|
|
||||||
#A boolean that determines if the database will be automatically created
|
#A boolean that determines if the database will be automatically created
|
||||||
db_auto_create = True
|
db_auto_create = True
|
||||||
|
@ -59,16 +59,7 @@ rabbit_opts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
db_opts = [
|
db_opts = [
|
||||||
cfg.IntOpt('sql_idle_timeout', default=3600,
|
cfg.BoolOpt('auto_create', default=False,
|
||||||
help=_('Period in seconds after which SQLAlchemy should '
|
|
||||||
'reestablish its connection to the database.')),
|
|
||||||
cfg.IntOpt('sql_max_retries', default=60,
|
|
||||||
help=_('The number of times to retry a connection to the SQL'
|
|
||||||
'server.')),
|
|
||||||
cfg.IntOpt('sql_retry_interval', default=1,
|
|
||||||
help=_('The amount of time to wait (in seconds) before '
|
|
||||||
'attempting to retry the SQL connection.')),
|
|
||||||
cfg.BoolOpt('db_auto_create', default=False,
|
|
||||||
help=_('A boolean that determines if the database will be '
|
help=_('A boolean that determines if the database will be '
|
||||||
'automatically created.')),
|
'automatically created.')),
|
||||||
]
|
]
|
||||||
@ -78,7 +69,7 @@ CONF.register_opts(paste_deploy_opts, group='paste_deploy')
|
|||||||
CONF.register_cli_opts(bind_opts)
|
CONF.register_cli_opts(bind_opts)
|
||||||
CONF.register_opts(reports_opts, group='reports')
|
CONF.register_opts(reports_opts, group='reports')
|
||||||
CONF.register_opts(rabbit_opts, group='rabbitmq')
|
CONF.register_opts(rabbit_opts, group='rabbitmq')
|
||||||
CONF.register_opts(db_opts)
|
CONF.register_opts(db_opts, group='database')
|
||||||
|
|
||||||
|
|
||||||
CONF.import_opt('verbose', 'muranoapi.openstack.common.log')
|
CONF.import_opt('verbose', 'muranoapi.openstack.common.log')
|
||||||
|
@ -11,16 +11,3 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo.config import cfg
|
|
||||||
|
|
||||||
sql_connection_opt = cfg.StrOpt('sql_connection',
|
|
||||||
default='sqlite:///muranoapi.sqlite',
|
|
||||||
secret=True,
|
|
||||||
metavar='CONNECTION',
|
|
||||||
help='A valid SQLAlchemy connection '
|
|
||||||
'string for the metadata database. '
|
|
||||||
'Default: %(default)s')
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
CONF.register_opt(sql_connection_opt)
|
|
||||||
|
@ -20,172 +20,32 @@
|
|||||||
"""Session management functions."""
|
"""Session management functions."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
import sqlalchemy
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from migrate.versioning import api as versioning_api
|
from migrate.versioning import api as versioning_api
|
||||||
from migrate import exceptions as versioning_exceptions
|
from migrate import exceptions as versioning_exceptions
|
||||||
from sqlalchemy.exc import DisconnectionError
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
|
|
||||||
|
from muranoapi.openstack.common.db.sqlalchemy import session
|
||||||
from muranoapi.common.config import CONF as conf
|
from muranoapi.common.config import CONF as conf
|
||||||
|
from muranoapi.openstack.common import log as logging
|
||||||
from muranoapi.db import migrate_repo
|
from muranoapi.db import migrate_repo
|
||||||
from muranoapi.openstack.common import log as mlogging
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
_ENGINE = None
|
|
||||||
_MAKER = None
|
|
||||||
_MAX_RETRIES = None
|
|
||||||
_RETRY_INTERVAL = None
|
|
||||||
_IDLE_TIMEOUT = None
|
|
||||||
_CONNECTION = None
|
|
||||||
|
|
||||||
sa_logger = None
|
|
||||||
log = mlogging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _ping_listener(dbapi_conn, connection_rec, connection_proxy):
|
|
||||||
|
|
||||||
"""
|
|
||||||
Ensures that MySQL connections checked out of the
|
|
||||||
pool are alive.
|
|
||||||
|
|
||||||
Borrowed from:
|
|
||||||
http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
dbapi_conn.cursor().execute('select 1')
|
|
||||||
except dbapi_conn.OperationalError as ex:
|
|
||||||
if ex.args[0] in (2006, 2013, 2014, 2045, 2055):
|
|
||||||
msg = 'Got mysql server has gone away: %s' % ex
|
|
||||||
log.warn(msg)
|
|
||||||
raise DisconnectionError(msg)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def setup_db_env():
|
|
||||||
"""
|
|
||||||
Setup configuration for database
|
|
||||||
"""
|
|
||||||
global sa_logger, _IDLE_TIMEOUT, _MAX_RETRIES, _RETRY_INTERVAL, _CONNECTION
|
|
||||||
|
|
||||||
_IDLE_TIMEOUT = conf.sql_idle_timeout
|
|
||||||
_MAX_RETRIES = conf.sql_max_retries
|
|
||||||
_RETRY_INTERVAL = conf.sql_retry_interval
|
|
||||||
_CONNECTION = conf.sql_connection
|
|
||||||
sa_logger = logging.getLogger('sqlalchemy.engine')
|
|
||||||
if conf.debug:
|
|
||||||
sa_logger.setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
|
|
||||||
def get_session(autocommit=True, expire_on_commit=False):
|
def get_session(autocommit=True, expire_on_commit=False):
|
||||||
"""Helper method to grab session"""
|
if not session._MAKER:
|
||||||
global _MAKER
|
if conf.database.auto_create:
|
||||||
if not _MAKER:
|
|
||||||
get_engine()
|
|
||||||
_get_maker(autocommit, expire_on_commit)
|
|
||||||
assert _MAKER
|
|
||||||
session = _MAKER()
|
|
||||||
return session
|
|
||||||
|
|
||||||
|
|
||||||
def get_engine():
|
|
||||||
"""Return a SQLAlchemy engine."""
|
|
||||||
"""May assign _ENGINE if not already assigned"""
|
|
||||||
global _ENGINE, sa_logger, _CONNECTION, _IDLE_TIMEOUT, _MAX_RETRIES,\
|
|
||||||
_RETRY_INTERVAL
|
|
||||||
|
|
||||||
if not _ENGINE:
|
|
||||||
setup_db_env()
|
|
||||||
|
|
||||||
connection_dict = sqlalchemy.engine.url.make_url(_CONNECTION)
|
|
||||||
|
|
||||||
engine_args = {
|
|
||||||
'pool_recycle': _IDLE_TIMEOUT,
|
|
||||||
'echo': False,
|
|
||||||
'convert_unicode': True}
|
|
||||||
|
|
||||||
try:
|
|
||||||
_ENGINE = sqlalchemy.create_engine(_CONNECTION, **engine_args)
|
|
||||||
|
|
||||||
if 'mysql' in connection_dict.drivername:
|
|
||||||
sqlalchemy.event.listen(_ENGINE, 'checkout', _ping_listener)
|
|
||||||
|
|
||||||
_ENGINE.connect = _wrap_db_error(_ENGINE.connect)
|
|
||||||
_ENGINE.connect()
|
|
||||||
except Exception as err:
|
|
||||||
msg = _("Error configuring registry database with supplied "
|
|
||||||
"sql_connection. Got error: %s") % err
|
|
||||||
log.error(msg)
|
|
||||||
raise
|
|
||||||
|
|
||||||
if conf.db_auto_create:
|
|
||||||
log.info(_('auto-creating DB'))
|
log.info(_('auto-creating DB'))
|
||||||
_auto_create_db()
|
_auto_create_db()
|
||||||
else:
|
else:
|
||||||
log.info(_('not auto-creating DB'))
|
log.info(_('not auto-creating DB'))
|
||||||
|
return session.get_session(autocommit, expire_on_commit)
|
||||||
return _ENGINE
|
|
||||||
|
|
||||||
|
|
||||||
def _get_maker(autocommit=True, expire_on_commit=False):
|
|
||||||
"""Return a SQLAlchemy sessionmaker."""
|
|
||||||
"""May assign __MAKER if not already assigned"""
|
|
||||||
global _MAKER, _ENGINE
|
|
||||||
assert _ENGINE
|
|
||||||
if not _MAKER:
|
|
||||||
_MAKER = sessionmaker(bind=_ENGINE, autocommit=autocommit,
|
|
||||||
expire_on_commit=expire_on_commit)
|
|
||||||
return _MAKER
|
|
||||||
|
|
||||||
|
|
||||||
def _is_db_connection_error(args):
|
|
||||||
"""Return True if error in connecting to db."""
|
|
||||||
# NOTE(adam_g): This is currently MySQL specific and needs to be extended
|
|
||||||
# to support Postgres and others.
|
|
||||||
conn_err_codes = ('2002', '2003', '2006')
|
|
||||||
for err_code in conn_err_codes:
|
|
||||||
if args.find(err_code) != -1:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap_db_error(f):
|
|
||||||
"""Retry DB connection. Copied from nova and modified."""
|
|
||||||
def _wrap(*args, **kwargs):
|
|
||||||
try:
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
except sqlalchemy.exc.OperationalError as e:
|
|
||||||
if not _is_db_connection_error(e.args[0]):
|
|
||||||
raise
|
|
||||||
|
|
||||||
remaining_attempts = _MAX_RETRIES
|
|
||||||
while True:
|
|
||||||
log.warning(_('SQL connection failed. %d attempts left.'),
|
|
||||||
remaining_attempts)
|
|
||||||
remaining_attempts -= 1
|
|
||||||
time.sleep(_RETRY_INTERVAL)
|
|
||||||
try:
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
except sqlalchemy.exc.OperationalError as e:
|
|
||||||
if (remaining_attempts == 0 or
|
|
||||||
not _is_db_connection_error(e.args[0])):
|
|
||||||
raise
|
|
||||||
except sqlalchemy.exc.DBAPIError:
|
|
||||||
raise
|
|
||||||
except sqlalchemy.exc.DBAPIError:
|
|
||||||
raise
|
|
||||||
_wrap.func_name = f.func_name
|
|
||||||
return _wrap
|
|
||||||
|
|
||||||
|
|
||||||
def _auto_create_db():
|
def _auto_create_db():
|
||||||
repo_path = os.path.abspath(os.path.dirname(migrate_repo.__file__))
|
repo_path = os.path.abspath(os.path.dirname(migrate_repo.__file__))
|
||||||
try:
|
try:
|
||||||
versioning_api.upgrade(conf.sql_connection, repo_path)
|
versioning_api.upgrade(conf.database.connection, repo_path)
|
||||||
except versioning_exceptions.DatabaseNotControlledError:
|
except versioning_exceptions.DatabaseNotControlledError:
|
||||||
versioning_api.version_control(conf.sql_connection, repo_path)
|
versioning_api.version_control(conf.database.connection, repo_path)
|
||||||
versioning_api.upgrade(conf.sql_connection, repo_path)
|
versioning_api.upgrade(conf.database.connection, repo_path)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2011 OpenStack LLC.
|
# Copyright 2011 OpenStack Foundation.
|
||||||
# Copyright 2010 United States Government as represented by the
|
# Copyright 2010 United States Government as represented by the
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2011 OpenStack LLC.
|
# Copyright 2011 OpenStack Foundation.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -66,7 +66,7 @@ def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
|
|||||||
|
|
||||||
|
|
||||||
def normalize_time(timestamp):
|
def normalize_time(timestamp):
|
||||||
"""Normalize time in arbitrary timezone to UTC naive object"""
|
"""Normalize time in arbitrary timezone to UTC naive object."""
|
||||||
offset = timestamp.utcoffset()
|
offset = timestamp.utcoffset()
|
||||||
if offset is None:
|
if offset is None:
|
||||||
return timestamp
|
return timestamp
|
||||||
@ -103,7 +103,7 @@ def utcnow():
|
|||||||
|
|
||||||
|
|
||||||
def iso8601_from_timestamp(timestamp):
|
def iso8601_from_timestamp(timestamp):
|
||||||
"""Returns a iso8601 formated date from timestamp"""
|
"""Returns a iso8601 formated date from timestamp."""
|
||||||
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
|
return isotime(datetime.datetime.utcfromtimestamp(timestamp))
|
||||||
|
|
||||||
|
|
||||||
@ -111,9 +111,9 @@ utcnow.override_time = None
|
|||||||
|
|
||||||
|
|
||||||
def set_time_override(override_time=datetime.datetime.utcnow()):
|
def set_time_override(override_time=datetime.datetime.utcnow()):
|
||||||
"""
|
"""Overrides utils.utcnow.
|
||||||
Override utils.utcnow to return a constant time or a list thereof,
|
|
||||||
one at a time.
|
Make it return a constant time or a list thereof, one at a time.
|
||||||
"""
|
"""
|
||||||
utcnow.override_time = override_time
|
utcnow.override_time = override_time
|
||||||
|
|
||||||
@ -141,7 +141,8 @@ def clear_time_override():
|
|||||||
def marshall_now(now=None):
|
def marshall_now(now=None):
|
||||||
"""Make an rpc-safe datetime with microseconds.
|
"""Make an rpc-safe datetime with microseconds.
|
||||||
|
|
||||||
Note: tzinfo is stripped, but not required for relative times."""
|
Note: tzinfo is stripped, but not required for relative times.
|
||||||
|
"""
|
||||||
if not now:
|
if not now:
|
||||||
now = utcnow()
|
now = utcnow()
|
||||||
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
|
return dict(day=now.day, month=now.month, year=now.year, hour=now.hour,
|
||||||
@ -161,7 +162,8 @@ def unmarshall_time(tyme):
|
|||||||
|
|
||||||
|
|
||||||
def delta_seconds(before, after):
|
def delta_seconds(before, after):
|
||||||
"""
|
"""Return the difference between two timing objects.
|
||||||
|
|
||||||
Compute the difference in seconds between two date, time, or
|
Compute the difference in seconds between two date, time, or
|
||||||
datetime objects (as a float, to microsecond resolution).
|
datetime objects (as a float, to microsecond resolution).
|
||||||
"""
|
"""
|
||||||
@ -174,8 +176,7 @@ def delta_seconds(before, after):
|
|||||||
|
|
||||||
|
|
||||||
def is_soon(dt, window):
|
def is_soon(dt, window):
|
||||||
"""
|
"""Determines if time is going to happen in the next window seconds.
|
||||||
Determines if time is going to happen in the next window seconds.
|
|
||||||
|
|
||||||
:params dt: the time
|
:params dt: the time
|
||||||
:params window: minimum seconds to remain to consider the time not soon
|
:params window: minimum seconds to remain to consider the time not soon
|
||||||
|
Loading…
Reference in New Issue
Block a user