# Copyright Tesora Inc.
# All rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); May
# not use this file except in compliance with the License. Obtain
# A copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# unless required by applicable or agreed to writing, software
# Distributed under the License is distributed on a "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.
"""
Tests database migration scripts for MySQL.
To run the tests, you'll need to set up DB user named ' Openstack_citest '
With password ' openstack_citest ' on localhost. This user needs db
Admin rights (i.e Create/drop database)
"""
Import Glob
Import OS
Import Migrate.versioning.api as Migration_api
From migrate.versioning Import repository
From oslo_concurrency import processutils
From Oslo_log import log as logging
From proboscis import After_class
From Proboscis.asserts import assert_equal
From Proboscis.asserts import assert_true
From proboscis import Before_class
From proboscis import skiptest
From proboscis Import test
Import SQLAlchemy
Import Sqlalchemy.exc
From trove.common.i18n Import _
Import Trove.db.sqlalchemy.migrate_repo
From Trove.tests.util import Event_simulator
GROUP = "Dbaas.db.migrations"
LOG = Logging.getlogger (__name__)
@test (Groups=[group])
Class Projecttestcase (object):
"" "Test Migration Scripts integrity." "" "
@test
def test_all_migrations_have_downgrade (self):
Topdir = Os.path.normpath (Os.path.join (Os.path.dirname (__file__),
Os.pardir, Os.pardir, Os.pardir))
Py_glob = Os.path.join (Topdir, "trove", "db", "SQLAlchemy",
"Migrate_repo", "Versions", "*.py")
Missing_downgrade = []
For path in Glob.iglob (PY_GLOB):
Has_upgrade = False
Has_downgrade = False
With open (path, "R") as F:
For line in F:
If ' Def upgrade (' in line:
Has_upgrade = True
If ' Def downgrade (' in line:
Has_downgrade = True
If Has_upgrade and not has_downgrade:
fname = os.path.basename (path)
Missing_downgrade.append (fname)
Helpful_msg = (_ ("The following migration scripts is missing a"
"Downgrade implementation:\n\t%s")%
' \n\t '. Join (sorted (Missing_downgrade)))
Assert_true (not Missing_downgrade, helpful_msg)
@test (Depends_on_classes=[projecttestcase],
Groups=[group])
Class Testtrovemigrations (object):
"" "Test sqlalchemy-migrate Migrations." ""
USER = "Openstack_citest"
PASSWD = "Openstack_citest"
DATABASE = "Openstack_citest"
@before_class
def setUp (self):
Event_simulator.allowable_empty_sleeps = 1
@after_class
def tearDown (self):
Event_simulator.allowable_empty_sleeps = 0
def __init__ (self):
Self. Migrate_file = trove.db.sqlalchemy.migrate_repo.__file__
Self. REPOSITORY = REPOSITORY. Repository (
Os.path.abspath (Os.path.dirname (self). Migrate_file)))
Self. init_version = 0
def _get_connect_string (self, Backend, database=none):
"" "Get database connection string." "" "
args = {' backend ': backend,
' User ': self. USER,
' passwd ': self. PASSWD}
Template = "% (backend) s://% (user) s:% (passwd) [email protected]"
If database is not None:
args[' database ' = database
Template + = "/% (database) S"
Return template% args
def _is_backend_avail (self, backend):
"" "Check Database Backend availability." "" "
Connect_uri = self._get_connect_string (backend)
Engine = Sqlalchemy.create_engine (Connect_uri)
Try
Connection = Engine.connect ()
Except Exception:
# any error here means the database backend are not available
Return False
Else
Connection.close ()
Return True
Finally
If engine is not None:
Engine.dispose ()
def _execute_cmd (self, Cmd=none):
"" "Shell out and run the given command." "
Out, err = Processutils.trycmd (cmd, shell=true)
# Until someone wants to rewrite the avoid
# We need to handle it for newer versions of MySQL
Valid_err = Err = = "or \
Err = = ' MySQL: [Warning] Using a password on the ' \
' Command line interface can insecure.\n '
Assert_true (Valid_err,
"Failed to run: '% (cmd) s '"
"Output: '% (stdout) s '"
"Error: '% (stderr) s '"%
{' cmd ': cmd, ' stdout ': Out, ' stderr ': Err})
def _reset_mysql (self):
"" Reset the MySQL test database
Drop the MySQL test database if it already exists and create
A new one.
"""
sql = ("drop database if exists% (database) s;")
"CREATE DATABASE" s; "% {' database ': self. DATABASE})
cmd = ("mysql-u \"% (user) s\ "-p% (password) s-h% (host) S"
"-e \"% (SQL) s\ ""% {' user ': self. USER, ' password ': self. PASSWD,
' Host ': ' localhost ', ' SQL ': SQL})
Self._execute_cmd (CMD)
@test
def test_mysql_migration (self):
Db_backend = "Mysql+pymysql"
# Gracefully Skip this test if the developer does not have
# MySQL running. MySQL should always being available on
# The Infrastructure
If not Self._is_backend_avail (db_backend):
Raise Skiptest ("MySQL is not available.")
Self._reset_mysql ()
connect_string = self._get_connect_string (Db_backend, self. DATABASE)
Engine = Sqlalchemy.create_engine (connect_string)
Self._walk_versions (Engine)
Engine.dispose ()
def _walk_versions (self, Engine=none)://Run version
"" "Walk through and test the migration scripts
Determine latest version script from the repo and then
Upgrade from 1 through to the latest and then downgrade from
The latest 1, with no data in the databases. This
Just checks that the schema itself upgrades and downgrades
Successfully.
"""
# Place the database under version control
Migration_api.version_control (engine, self.) REPOSITORY,
Self. Init_version)
Assert_equal (self. Init_version,
Migration_api.db_version (engine, self.) REPOSITORY))
Log.debug (' Latest version is%s '% self. Repository.latest)
Versions = Range (self. Init_version + 1, self. Repository.latest + 1)//From the beginning of the version ran to the latest version of the +1 termination, init_version Initial value is 0, so to + The termination of the 1,range function is the latter parameter, but not to that parameter, which is equivalent to ++i
# Snake walk from version 1 to the latest, testing the upgrade paths.
# upgrade, downgrade, upgrade
For version in versions:
SELF._MIGRATE_UP (engine, version)
Self._migrate_down (engine, version-1)
SELF._MIGRATE_UP (engine, version)
# Now Snake walk back to version 1 from the latest, testing the
# downgrade paths.
# downgrade, upgrade, downgrade
For version in reversed (versions):
Self._migrate_down (engine, version-1)
SELF._MIGRATE_UP (engine, version)
Self._migrate_down (engine, version-1)
def _migrate_down (self, Engine, version)://Version go up
"" "Migrate down-to-an-old version of database." "" "
Migration_api.downgrade (engine, self.) REPOSITORY, Version)
Assert_equal (version,
Migration_api.db_version (engine, self.) REPOSITORY))
def _migrate_up (self, Engine, version)://version run down
"" "Migrate up to a new version of database." "" "
Migration_api.upgrade (engine, self.) REPOSITORY, Version)
Assert_equal (version,
Migration_api.db_version (engine, self.) REPOSITORY))
This file feels like most of it is in the version of the problem, look at the upgrade version or downgrade version of the time, the data will not be destroyed, I understand that ...
Trove, test, DB small parse