summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-08-17 21:06:59 +0000
committerGerrit Code Review <review@openstack.org>2016-08-17 21:07:00 +0000
commit7ca416418472b4a1a8625f1ef7b83194d784c6da (patch)
tree56d1c71e7e090d9f571e217ef369616d1c5f8f86
parent2862b18313ba0868c0d8d5e0f3361d3b141dc70c (diff)
parent89532b3efd91bbf5f216fcb389f208fa85e7aaa0 (diff)
downloadoslo-db-4.12.0.tar.gz
Merge "Link enginefacade to test database provisioning"4.12.0
-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: