Partial fix for bug #1074093
engine and makers are stored in dictionnary in the sqlalchemy/session.py Change-Id: Ied2c13e9ed7117730eccc8db4c3a03b54c66d4b9
This commit is contained in:
parent
c8ea8fc554
commit
f8df098d30
@ -25,6 +25,7 @@ from moniker.central import api as central_api
|
||||
from moniker.context import MonikerContext
|
||||
from sqlalchemy.ext.sqlsoup import SqlSoup
|
||||
from sqlalchemy.engine.url import _parse_rfc1738_args
|
||||
from moniker.sqlalchemy.session import get_engine
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -73,7 +74,8 @@ class MySQLBind9Backend(base.Backend):
|
||||
super(MySQLBind9Backend, self).start()
|
||||
|
||||
if cfg.CONF[self.name].write_database:
|
||||
self._db = SqlSoup(cfg.CONF[self.name].database_connection)
|
||||
self._engine = get_engine(self.name)
|
||||
self._db = SqlSoup(self._engine)
|
||||
|
||||
self._sync_domains()
|
||||
|
||||
|
@ -30,6 +30,8 @@ HANDLER_NAMESPACE = 'moniker.notification.handler'
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('backend-driver', default='rpc',
|
||||
help='The backend driver to use'),
|
||||
cfg.StrOpt('storage-driver', default='sqlalchemy',
|
||||
help='The storage driver to use'),
|
||||
cfg.ListOpt('enabled-notification-handlers', default=[],
|
||||
help='Enabled Notification Handlers'),
|
||||
])
|
||||
|
@ -30,19 +30,25 @@ from moniker.openstack.common.gettextutils import _
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_MAKER = None
|
||||
_ENGINE = None
|
||||
_MAKERS = {}
|
||||
_ENGINES = {}
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False, autoflush=True):
|
||||
def get_session(config_group,
|
||||
autocommit=True,
|
||||
expire_on_commit=False,
|
||||
autoflush=True):
|
||||
"""Return a SQLAlchemy session."""
|
||||
global _MAKER
|
||||
global _MAKERS
|
||||
|
||||
if _MAKER is None:
|
||||
engine = get_engine()
|
||||
_MAKER = get_maker(engine, autocommit, expire_on_commit, autoflush)
|
||||
if config_group not in _MAKERS:
|
||||
engine = get_engine(config_group)
|
||||
_MAKERS[config_group] = get_maker(engine,
|
||||
autocommit,
|
||||
expire_on_commit,
|
||||
autoflush)
|
||||
|
||||
session = _MAKER()
|
||||
session = _MAKERS[config_group]()
|
||||
return session
|
||||
|
||||
|
||||
@ -89,56 +95,64 @@ def is_db_connection_error(args):
|
||||
return False
|
||||
|
||||
|
||||
def get_engine():
|
||||
def get_engine(config_group):
|
||||
"""Return a SQLAlchemy engine."""
|
||||
global _ENGINE
|
||||
if _ENGINE is None:
|
||||
global _ENGINES
|
||||
|
||||
database_connection = cfg.CONF[config_group].database_connection
|
||||
|
||||
if config_group not in _ENGINES:
|
||||
connection_dict = sqlalchemy.engine.url.make_url(
|
||||
cfg.CONF.database_connection)
|
||||
database_connection)
|
||||
|
||||
engine_args = {
|
||||
"pool_recycle": cfg.CONF['storage:sqlalchemy'].idle_timeout,
|
||||
"pool_recycle": cfg.CONF[config_group].idle_timeout,
|
||||
"echo": False,
|
||||
'convert_unicode': True,
|
||||
}
|
||||
|
||||
# Map our SQL debug level to SQLAlchemy's options
|
||||
if cfg.CONF['storage:sqlalchemy'].connection_debug >= 100:
|
||||
if cfg.CONF[config_group].connection_debug >= 100:
|
||||
engine_args['echo'] = 'debug'
|
||||
elif cfg.CONF['storage:sqlalchemy'].connection_debug >= 50:
|
||||
elif cfg.CONF[config_group].connection_debug >= 50:
|
||||
engine_args['echo'] = True
|
||||
|
||||
if "sqlite" in connection_dict.drivername:
|
||||
engine_args["poolclass"] = NullPool
|
||||
|
||||
if cfg.CONF.database_connection == "sqlite://":
|
||||
if database_connection == "sqlite://":
|
||||
engine_args["poolclass"] = StaticPool
|
||||
engine_args["connect_args"] = {'check_same_thread': False}
|
||||
|
||||
_ENGINE = sqlalchemy.create_engine(cfg.CONF.database_connection,
|
||||
**engine_args)
|
||||
_ENGINES[config_group] = sqlalchemy.create_engine(database_connection,
|
||||
**engine_args)
|
||||
|
||||
if 'mysql' in connection_dict.drivername:
|
||||
sqlalchemy.event.listen(_ENGINE, 'checkout', ping_listener)
|
||||
sqlalchemy.event.listen(_ENGINES[config_group],
|
||||
'checkout',
|
||||
ping_listener)
|
||||
elif "sqlite" in connection_dict.drivername:
|
||||
if not cfg.CONF['storage:sqlalchemy'].sqlite_synchronous:
|
||||
sqlalchemy.event.listen(_ENGINE, 'connect',
|
||||
if not cfg.CONF[config_group].sqlite_synchronous:
|
||||
sqlalchemy.event.listen(_ENGINES[config_group],
|
||||
'connect',
|
||||
synchronous_switch_listener)
|
||||
sqlalchemy.event.listen(_ENGINE, 'connect', add_regexp_listener)
|
||||
sqlalchemy.event.listen(_ENGINES[config_group],
|
||||
'connect',
|
||||
add_regexp_listener)
|
||||
|
||||
if (cfg.CONF['storage:sqlalchemy'].connection_trace and
|
||||
_ENGINE.dialect.dbapi.__name__ == 'MySQLdb'):
|
||||
if (cfg.CONF[config_group].connection_trace and
|
||||
_ENGINES[config_group].dialect.dbapi.__name__ == 'MySQLdb'):
|
||||
import MySQLdb.cursors
|
||||
_do_query = debug_mysql_do_query()
|
||||
setattr(MySQLdb.cursors.BaseCursor, '_do_query', _do_query)
|
||||
|
||||
try:
|
||||
_ENGINE.connect()
|
||||
_ENGINES[config_group].connect()
|
||||
except OperationalError, e:
|
||||
if not is_db_connection_error(e.args[0]):
|
||||
raise
|
||||
|
||||
remaining = cfg.CONF['storage:sqlalchemy'].max_retries
|
||||
remaining = cfg.CONF[config_group].max_retries
|
||||
if remaining == -1:
|
||||
remaining = 'infinite'
|
||||
while True:
|
||||
@ -146,15 +160,15 @@ def get_engine():
|
||||
LOG.warn(msg % remaining)
|
||||
if remaining != 'infinite':
|
||||
remaining -= 1
|
||||
time.sleep(cfg.CONF['storage:sqlalchemy'].retry_interval)
|
||||
time.sleep(cfg.CONF[config_group].retry_interval)
|
||||
try:
|
||||
_ENGINE.connect()
|
||||
_ENGINES[config_group].connect()
|
||||
break
|
||||
except OperationalError, e:
|
||||
if (remaining != 'infinite' and remaining == 0) or \
|
||||
not is_db_connection_error(e.args[0]):
|
||||
raise
|
||||
return _ENGINE
|
||||
return _ENGINES[config_group]
|
||||
|
||||
|
||||
def get_maker(engine, autocommit=True, expire_on_commit=False, autoflush=True):
|
||||
|
@ -13,19 +13,12 @@
|
||||
# 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 urlparse import urlparse
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.storage.base import StorageEngine
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
cfg.CONF.register_opts([
|
||||
cfg.StrOpt('database-connection',
|
||||
default='sqlite:///$state_path/moniker.sqlite',
|
||||
help='The database driver to use')
|
||||
])
|
||||
|
||||
|
||||
def get_engine_name(string):
|
||||
"""
|
||||
@ -34,15 +27,15 @@ def get_engine_name(string):
|
||||
return string.split("+")[0]
|
||||
|
||||
|
||||
def get_engine():
|
||||
scheme = urlparse(cfg.CONF.database_connection).scheme
|
||||
engine_name = get_engine_name(scheme)
|
||||
return StorageEngine.get_plugin(
|
||||
engine_name, invoke_on_load=True)
|
||||
def get_engine(engine_name):
|
||||
"""
|
||||
Return the engine class from the provided engine name
|
||||
"""
|
||||
return StorageEngine.get_plugin(engine_name, invoke_on_load=True)
|
||||
|
||||
|
||||
def get_connection():
|
||||
engine = get_engine()
|
||||
engine = get_engine(cfg.CONF.storage_driver)
|
||||
return engine.get_connection()
|
||||
|
||||
|
||||
|
@ -21,10 +21,12 @@ from moniker.storage import base
|
||||
from moniker.storage.impl_sqlalchemy import models
|
||||
from moniker.sqlalchemy.session import get_session
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SQL_OPTS = [
|
||||
cfg.StrOpt('database_connection',
|
||||
default='sqlite:///$state_path/moniker.sqlite',
|
||||
help='The database driver to use'),
|
||||
cfg.IntOpt('connection_debug', default=0,
|
||||
help='Verbosity of SQL debugging information. 0=None,'
|
||||
' 100=Everything'),
|
||||
@ -52,22 +54,21 @@ class SQLAlchemyStorage(base.StorageEngine):
|
||||
return opts
|
||||
|
||||
def get_connection(self):
|
||||
return Connection()
|
||||
return Connection(self.name)
|
||||
|
||||
|
||||
class Connection(base.Connection):
|
||||
"""
|
||||
SQLAlchemy connection
|
||||
"""
|
||||
def __init__(self):
|
||||
LOG.info('connecting to %s', cfg.CONF.database_connection)
|
||||
self.session = self._get_connection()
|
||||
def __init__(self, config_group):
|
||||
self.session = self._get_connection(config_group)
|
||||
|
||||
def _get_connection(self):
|
||||
def _get_connection(self, config_group):
|
||||
"""
|
||||
Return a connection to the database.
|
||||
"""
|
||||
return get_session()
|
||||
return get_session(config_group)
|
||||
|
||||
def setup_schema(self):
|
||||
""" Semi-Private Method to create the database schema """
|
||||
|
@ -15,14 +15,12 @@
|
||||
# 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 urlparse import urlparse
|
||||
from sqlalchemy import (Column, DateTime, String, Text, Integer, ForeignKey,
|
||||
Enum, Boolean, Unicode)
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import relationship, backref, object_mapper
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from moniker.openstack.common import cfg
|
||||
from moniker.openstack.common import log as logging
|
||||
from moniker.openstack.common import timeutils
|
||||
from moniker.openstack.common.uuidutils import generate_uuid
|
||||
@ -31,24 +29,12 @@ from moniker.sqlalchemy.types import UUID, Inet
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
sql_opts = [
|
||||
cfg.IntOpt('mysql_engine', default='InnoDB', help='MySQL engine')
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(sql_opts)
|
||||
|
||||
RECORD_TYPES = ['A', 'AAAA', 'CNAME', 'MX', 'SRV', 'TXT', 'NS']
|
||||
|
||||
|
||||
def table_args():
|
||||
engine_name = urlparse(cfg.CONF.database_connection).scheme
|
||||
if engine_name == 'mysql':
|
||||
return {'mysql_engine': cfg.CONF.mysql_engine}
|
||||
return None
|
||||
|
||||
|
||||
class Base(object):
|
||||
__abstract__ = True
|
||||
__table_initialized__ = False
|
||||
|
||||
id = Column(UUID, default=generate_uuid, primary_key=True)
|
||||
|
||||
@ -60,9 +46,6 @@ class Base(object):
|
||||
'version_id_col': version
|
||||
}
|
||||
|
||||
__table_args__ = table_args()
|
||||
__table_initialized__ = False
|
||||
|
||||
def save(self, session):
|
||||
""" Save this object """
|
||||
session.add(self)
|
||||
|
@ -25,6 +25,10 @@ from moniker.central import service as central_service
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# NOTE(kiall): Awful kludge, to be removed in the next patchset
|
||||
from moniker.storage.impl_sqlalchemy import SQLAlchemyStorage
|
||||
SQLAlchemyStorage.register_opts()
|
||||
|
||||
|
||||
class AssertMixin(object):
|
||||
"""
|
||||
@ -96,12 +100,17 @@ class TestCase(unittest2.TestCase, AssertMixin):
|
||||
|
||||
self.mox = mox.Mox()
|
||||
self.config(
|
||||
database_connection='sqlite://',
|
||||
rpc_backend='moniker.openstack.common.rpc.impl_fake',
|
||||
notification_driver=[],
|
||||
storage_driver='sqlalchemy',
|
||||
backend_driver='fake',
|
||||
notification_driver=[],
|
||||
rpc_backend='moniker.openstack.common.rpc.impl_fake',
|
||||
auth_strategy='noauth'
|
||||
)
|
||||
|
||||
self.config(
|
||||
database_connection='sqlite://',
|
||||
group='storage:sqlalchemy'
|
||||
)
|
||||
storage.setup_schema()
|
||||
|
||||
self.admin_context = self.get_admin_context()
|
||||
@ -114,6 +123,7 @@ class TestCase(unittest2.TestCase, AssertMixin):
|
||||
|
||||
def config(self, **kwargs):
|
||||
group = kwargs.pop('group', None)
|
||||
|
||||
for k, v in kwargs.iteritems():
|
||||
cfg.CONF.set_override(k, v, group)
|
||||
|
||||
|
@ -24,4 +24,5 @@ class SqlalchemyStorageTest(StorageDriverTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SqlalchemyStorageTest, self).setUp()
|
||||
self.config(database_connection='sqlite://')
|
||||
self.config(database_connection='sqlite://',
|
||||
group='storage:sqlalchemy')
|
||||
|
4
setup.py
4
setup.py
@ -57,9 +57,7 @@ setup(
|
||||
cmdclass=common_setup.get_cmdclass(),
|
||||
entry_points=textwrap.dedent("""
|
||||
[moniker.storage]
|
||||
mysql = moniker.storage.impl_sqlalchemy:SQLAlchemyStorage
|
||||
postgresql = moniker.storage.impl_sqlalchemy:SQLAlchemyStorage
|
||||
sqlite = moniker.storage.impl_sqlalchemy:SQLAlchemyStorage
|
||||
sqlalchemy = moniker.storage.impl_sqlalchemy:SQLAlchemyStorage
|
||||
|
||||
[moniker.notification.handler]
|
||||
nova_fixed = moniker.notification_handler.nova:NovaFixedHandler
|
||||
|
Loading…
Reference in New Issue
Block a user