summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2016-08-04 17:18:50 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2016-08-11 16:43:56 -0400
commit89532b3efd91bbf5f216fcb389f208fa85e7aaa0 (patch)
treec63935ce17d3e4dea06fd0603044dc9341975533
parente9a0e9d568fca39d88c64f203eeff8fc1eefcbf8 (diff)
downloadoslo-db-89532b3efd91bbf5f216fcb389f208fa85e7aaa0.tar.gz
Link enginefacade to test database provisioning
This is part of a series which allows enginefacade to be at the base of database provisioning. Test fixtures are not impacted here, however the global "enginefacade" is used to generate database connections in provision.py Change-Id: I94753ca103851e3fd79c1c6c9773f57e311a0273
-rw-r--r--oslo_db/sqlalchemy/provision.py136
1 files changed, 109 insertions, 27 deletions
diff --git a/oslo_db/sqlalchemy/provision.py b/oslo_db/sqlalchemy/provision.py
index 7a1eaee..79df8e0 100644
--- a/oslo_db/sqlalchemy/provision.py
+++ b/oslo_db/sqlalchemy/provision.py
@@ -1,3 +1,4 @@
+# Copyright 2014 Red Hat
# Copyright 2013 Mirantis.inc
# All Rights Reserved.
#
@@ -16,6 +17,7 @@
"""Provision test environment for specific DB backends"""
import abc
+import debtcollector
import logging
import os
import random
@@ -31,6 +33,7 @@ import testresources
from oslo_db._i18n import _LI
from oslo_db import exception
+from oslo_db.sqlalchemy import enginefacade
from oslo_db.sqlalchemy import session
from oslo_db.sqlalchemy import utils
@@ -38,7 +41,38 @@ LOG = logging.getLogger(__name__)
class ProvisionedDatabase(object):
- pass
+ """Represents a database engine pointing to a DB ready to run tests.
+
+ backend: an instance of :class:`.Backend`
+
+ enginefacade: an instance of :class:`._TransactionFactory`
+
+ engine: a SQLAlchemy :class:`.Engine`
+
+ db_token: if provision_new_database were used, this is the randomly
+ generated name of the database. Note that with SQLite memory
+ connections, this token is ignored. For a database that
+ wasn't actually created, will be None.
+
+ """
+
+ __slots__ = 'backend', 'enginefacade', 'engine', 'db_token'
+
+ def __init__(self, backend, enginefacade, engine, db_token):
+ self.backend = backend
+ self.enginefacade = enginefacade
+ self.engine = engine
+ self.db_token = db_token
+
+
+class Schema(object):
+ """"Represents a database schema that has or will be populated.
+
+ This is a marker object as required by testresources but otherwise
+ serves no purpose.
+
+ """
+ __slots__ = 'database',
class BackendResource(testresources.TestResourceManager):
@@ -55,23 +89,51 @@ class BackendResource(testresources.TestResourceManager):
class DatabaseResource(testresources.TestResourceManager):
+ """Database resource which connects and disconnects to a URL.
- def __init__(self, database_type):
+ For SQLite, this means the database is created implicitly, as a result
+ of SQLite's usual behavior. If the database is a file-based URL,
+ it will remain after the resource has been torn down.
+
+ For all other kinds of databases, the resource indicates to connect
+ and disconnect from that database.
+
+ """
+
+ def __init__(self, database_type, _enginefacade=None):
super(DatabaseResource, self).__init__()
self.database_type = database_type
+
+ # NOTE(zzzeek) the _enginefacade is an optional argument
+ # here in order to accomodate Neutron's current direct use
+ # of the DatabaseResource object. Within oslo_db's use,
+ # the "enginefacade" will always be passed in from the
+ # test and/or fixture.
+ if _enginefacade:
+ self._enginefacade = _enginefacade
+ else:
+ self._enginefacade = enginefacade._context_manager
self.resources = [
('backend', BackendResource(database_type))
]
def make(self, dependency_resources):
- dependency_resources['db_token'] = db_token = _random_ident()
backend = dependency_resources['backend']
+ _enginefacade = self._enginefacade.make_new_manager()
+
+ db_token = _random_ident()
+ url = backend.provisioned_database_url(db_token)
+
+ _enginefacade.configure(
+ logging_name="%s@%s" % (self.database_type, db_token))
+
LOG.info(
"CREATE BACKEND %s TOKEN %s", backend.engine.url, db_token)
backend.create_named_database(db_token, conditional=True)
- dependency_resources['engine'] = \
- backend.provisioned_engine(db_token)
- return ProvisionedDatabase()
+
+ _enginefacade._factory._start(connection=url)
+ engine = _enginefacade._factory._writer_engine
+ return ProvisionedDatabase(backend, _enginefacade, engine, db_token)
def clean(self, resource):
resource.engine.dispose()
@@ -104,10 +166,6 @@ class TransactionResource(testresources.TestResourceManager):
return True
-class Schema(object):
- pass
-
-
class SchemaResource(testresources.TestResourceManager):
def __init__(self, database_resource, generate_schema, teardown=False):
@@ -271,6 +329,20 @@ class Backend(object):
return self.impl.database_exists(self.engine, ident)
+ def provisioned_database_url(self, ident):
+ """Given the identifier of an anoymous database, return a URL.
+
+ For hostname-based URLs, this typically involves switching just the
+ 'database' portion of the URL with the given name and creating
+ a URL.
+
+ For SQLite URLs, the identifier may be used to create a filename
+ or may be ignored in the case of a memory database.
+
+ """
+ return self.impl.provisioned_database_url(self.url, ident)
+
+ @debtcollector.removals.remove()
def provisioned_engine(self, ident):
"""Given the URL of a particular database backend and the string
@@ -278,14 +350,6 @@ class Backend(object):
an Engine instance whose connections will refer directly to the
named database.
- For hostname-based URLs, this typically involves switching just the
- 'database' portion of the URL with the given name and creating
- an engine.
-
- For URLs that instead deal with DSNs, the rules may be more custom;
- for example, the engine may need to connect to the root URL and
- then emit a command to switch to the named database.
-
"""
return self.impl.provisioned_engine(self.url, ident)
@@ -427,6 +491,28 @@ class BackendImpl(object):
def drop_additional_objects(self, conn):
pass
+ def provisioned_database_url(self, base_url, ident):
+ """Return a provisioned database URL.
+
+ Given the URL of a particular database backend and the string
+ name of a particular 'database' within that backend, return
+ an URL which refers directly to the named database.
+
+ For hostname-based URLs, this typically involves switching just the
+ 'database' portion of the URL with the given name and creating
+ an engine.
+
+ For URLs that instead deal with DSNs, the rules may be more custom;
+ for example, the engine may need to connect to the root URL and
+ then emit a command to switch to the named database.
+
+ """
+
+ url = sa_url.make_url(str(base_url))
+ url.database = ident
+ return url
+
+ @debtcollector.removals.remove()
def provisioned_engine(self, base_url, ident):
"""Return a provisioned engine.
@@ -444,9 +530,8 @@ class BackendImpl(object):
then emit a command to switch to the named database.
"""
+ url = self.provisioned_database_url(base_url, ident)
- url = sa_url.make_url(str(base_url))
- url.database = ident
return session.create_engine(
url,
logging_name="%s@%s" % (self.drivername, ident),
@@ -457,6 +542,7 @@ class BackendImpl(object):
@BackendImpl.impl.dispatch_for("mysql")
class MySQLBackendImpl(BackendImpl):
+ # only used for deprecated provisioned_engine() function.
default_engine_kwargs = {'mysql_sql_mode': 'TRADITIONAL'}
def create_opportunistic_driver_url(self):
@@ -485,18 +571,14 @@ class SQLiteBackendImpl(BackendImpl):
return "sqlite://"
def create_named_database(self, engine, ident, conditional=False):
- url = self._provisioned_database_url(engine.url, ident)
+ url = self.provisioned_database_url(engine.url, ident)
filename = url.database
if filename and (not conditional or not os.access(filename, os.F_OK)):
eng = sqlalchemy.create_engine(url)
eng.connect().close()
- def provisioned_engine(self, base_url, ident):
- return session.create_engine(
- self._provisioned_database_url(base_url, ident))
-
def drop_named_database(self, engine, ident, conditional=False):
- url = self._provisioned_database_url(engine.url, ident)
+ url = self.provisioned_database_url(engine.url, ident)
filename = url.database
if filename and (not conditional or os.access(filename, os.F_OK)):
os.remove(filename)
@@ -506,7 +588,7 @@ class SQLiteBackendImpl(BackendImpl):
filename = url.database
return not filename or os.access(filename, os.F_OK)
- def _provisioned_database_url(self, base_url, ident):
+ def provisioned_database_url(self, base_url, ident):
if base_url.database:
return sa_url.make_url("sqlite:////tmp/%s.db" % ident)
else: