diff options
Diffstat (limited to 'heat/db')
32 files changed, 724 insertions, 912 deletions
diff --git a/heat/db/alembic.ini b/heat/db/alembic.ini new file mode 100644 index 000000000..dbee79a3b --- /dev/null +++ b/heat/db/alembic.ini @@ -0,0 +1,72 @@ +[alembic] +# path to migration scripts +script_location = %(here)s/migrations + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# version location specification; This defaults +# to heat/db/migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:heat/db/migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +sqlalchemy.url = sqlite:///heat.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/heat/db/sqlalchemy/api.py b/heat/db/api.py index df627d43c..8b2c99967 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/api.py @@ -38,10 +38,9 @@ from sqlalchemy.orm import aliased as orm_aliased from heat.common import crypt from heat.common import exception from heat.common.i18n import _ -from heat.db.sqlalchemy import filters as db_filters -from heat.db.sqlalchemy import migration -from heat.db.sqlalchemy import models -from heat.db.sqlalchemy import utils as db_utils +from heat.db import filters as db_filters +from heat.db import models +from heat.db import utils as db_utils from heat.engine import environment as heat_environment from heat.rpc import api as rpc_api @@ -1622,19 +1621,6 @@ def sync_point_update_input_data(context, entity_id, return rows_updated -def db_sync(engine, version=None): - """Migrate the database to `version` or the most recent version.""" - if version is not None and int(version) < db_version(engine): - raise exception.Error(_("Cannot migrate to lower schema version.")) - - return migration.db_sync(engine, version=version) - - -def db_version(engine): - """Display the current database version.""" - return migration.db_version(engine) - - def _crypt_action(encrypt): if encrypt: return _('encrypt') diff --git a/heat/db/sqlalchemy/filters.py b/heat/db/filters.py index 6c7b8cf24..6c7b8cf24 100644 --- a/heat/db/sqlalchemy/filters.py +++ b/heat/db/filters.py diff --git a/heat/db/migration.py b/heat/db/migration.py new file mode 100644 index 000000000..910656ca1 --- /dev/null +++ b/heat/db/migration.py @@ -0,0 +1,117 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "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. + +import os + +from alembic import command as alembic_api +from alembic import config as alembic_config +from alembic import migration as alembic_migration +from oslo_log import log as logging +import sqlalchemy as sa + +from heat.db import api as db_api + +LOG = logging.getLogger(__name__) + +ALEMBIC_INIT_VERSION = 'c6214ca60943' + + +def _migrate_legacy_database(engine, connection, config): + """Check if database is a legacy sqlalchemy-migrate-managed database. + + If it is, migrate it by "stamping" the initial alembic schema. + """ + # If the database doesn't have the sqlalchemy-migrate legacy migration + # table, we don't have anything to do + if not sa.inspect(engine).has_table('migrate_version'): + return + + # Likewise, if we've already migrated to alembic, we don't have anything to + # do + context = alembic_migration.MigrationContext.configure(connection) + if context.get_current_revision(): + return + + # We have legacy migrations but no alembic migration. Stamp (dummy apply) + # the initial alembic migration. + + LOG.info( + 'The database is still under sqlalchemy-migrate control; ' + 'fake applying the initial alembic migration' + ) + alembic_api.stamp(config, ALEMBIC_INIT_VERSION) + + +def _find_alembic_conf(): + """Get the project's alembic configuration + + :returns: An instance of ``alembic.config.Config`` + """ + path = os.path.join( + os.path.abspath(os.path.dirname(__file__)), + 'alembic.ini', + ) + config = alembic_config.Config(os.path.abspath(path)) + # we don't want to use the logger configuration from the file, which is + # only really intended for the CLI + # https://stackoverflow.com/a/42691781/613428 + config.attributes['configure_logger'] = False + return config + + +def _upgrade_alembic(engine, config, version): + # re-use the connection rather than creating a new one + with engine.begin() as connection: + config.attributes['connection'] = connection + _migrate_legacy_database(engine, connection, config) + alembic_api.upgrade(config, version or 'head') + + +def db_sync(version=None, engine=None): + """Migrate the database to `version` or the most recent version.""" + # if the user requested a specific version, check if it's an integer: if + # so, we're almost certainly in sqlalchemy-migrate land and won't support + # that + if version is not None and version.isdigit(): + raise ValueError( + 'You requested an sqlalchemy-migrate database version; this is ' + 'no longer supported' + ) + + if engine is None: + engine = db_api.get_engine() + + config = _find_alembic_conf() + + # discard the URL encoded in alembic.ini in favour of the URL configured + # for the engine by the database fixtures, casting from + # 'sqlalchemy.engine.url.URL' to str in the process. This returns a + # RFC-1738 quoted URL, which means that a password like "foo@" will be + # turned into "foo%40". This in turns causes a problem for + # set_main_option() because that uses ConfigParser.set, which (by design) + # uses *python* interpolation to write the string out ... where "%" is the + # special python interpolation character! Avoid this mismatch by quoting + # all %'s for the set below. + engine_url = str(engine.url).replace('%', '%%') + config.set_main_option('sqlalchemy.url', str(engine_url)) + + LOG.info('Applying migration(s)') + _upgrade_alembic(engine, config, version) + LOG.info('Migration(s) applied') + + +def db_version(): + """Get database version.""" + engine = db_api.get_engine() + with engine.connect() as connection: + m_context = alembic_migration.MigrationContext.configure(connection) + return m_context.get_current_revision() diff --git a/heat/db/migrations/README.rst b/heat/db/migrations/README.rst new file mode 100644 index 000000000..b2283fc82 --- /dev/null +++ b/heat/db/migrations/README.rst @@ -0,0 +1,15 @@ +Database migrations +=================== + +This directory contains migrations for the database. These are implemented +using `alembic`__, a lightweight database migration tool designed for usage +with `SQLAlchemy`__. + +The best place to start understanding Alembic is with its own `tutorial`__. You +can also play around with the :command:`alembic` command:: + + $ alembic --help + +.. __: https://alembic.sqlalchemy.org/en/latest/ +.. __: https://www.sqlalchemy.org/ +.. __: https://alembic.sqlalchemy.org/en/latest/tutorial.html diff --git a/heat/db/migrations/env.py b/heat/db/migrations/env.py new file mode 100644 index 000000000..932b16a97 --- /dev/null +++ b/heat/db/migrations/env.py @@ -0,0 +1,94 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "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. + +from logging.config import fileConfig + +from alembic import context +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from heat.db import models + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.attributes.get('configure_logger', True): + fileConfig(config.config_file_name) + +# this is the MetaData object for the various models in the database +target_metadata = models.BASE.metadata + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL and not an Engine, though an + Engine is acceptable here as well. By skipping the Engine creation we + don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine and associate a connection + with the context. + + This is modified from the default based on the below, since we want to + share an engine when unit testing so in-memory database testing actually + works. + + https://alembic.sqlalchemy.org/en/latest/cookbook.html#connection-sharing + """ + connectable = config.attributes.get('connection', None) + + if connectable is None: + # only create Engine if we don't have a Connection from the outside + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + # when connectable is already a Connection object, calling connect() gives + # us a *branched connection* + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/heat/db/migrations/script.py.mako b/heat/db/migrations/script.py.mako new file mode 100644 index 000000000..120937fbc --- /dev/null +++ b/heat/db/migrations/script.py.mako @@ -0,0 +1,20 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} +""" + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} diff --git a/heat/db/migrations/versions/c6214ca60943_initial_revision.py b/heat/db/migrations/versions/c6214ca60943_initial_revision.py new file mode 100644 index 000000000..d3e9d757d --- /dev/null +++ b/heat/db/migrations/versions/c6214ca60943_initial_revision.py @@ -0,0 +1,392 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "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. + +"""Initial revision + +Revision ID: c6214ca60943 +Revises: +Create Date: 2023-03-22 18:04:02.387269 +""" + +from alembic import op +import sqlalchemy as sa + +import heat.db.types + +# revision identifiers, used by Alembic. +revision = 'c6214ca60943' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table( + 'raw_template_files', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('files', heat.db.types.Json(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'resource_properties_data', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('data', heat.db.types.Json(), nullable=True), + sa.Column('encrypted', sa.Boolean(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'service', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('engine_id', sa.String(length=36), nullable=False), + sa.Column('host', sa.String(length=255), nullable=False), + sa.Column('hostname', sa.String(length=255), nullable=False), + sa.Column('binary', sa.String(length=255), nullable=False), + sa.Column('topic', sa.String(length=255), nullable=False), + sa.Column('report_interval', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'software_config', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('group', sa.String(length=255), nullable=True), + sa.Column('config', heat.db.types.Json(), nullable=True), + sa.Column('tenant', sa.String(length=64), nullable=False), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_index( + op.f('ix_software_config_tenant'), + 'software_config', + ['tenant'], + unique=False, + ) + op.create_table( + 'user_creds', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('username', sa.String(length=255), nullable=True), + sa.Column('password', sa.String(length=255), nullable=True), + sa.Column('region_name', sa.String(length=255), nullable=True), + sa.Column('decrypt_method', sa.String(length=64), nullable=True), + sa.Column('tenant', sa.String(length=1024), nullable=True), + sa.Column('auth_url', sa.Text(), nullable=True), + sa.Column('tenant_id', sa.String(length=256), nullable=True), + sa.Column('trust_id', sa.String(length=255), nullable=True), + sa.Column('trustor_user_id', sa.String(length=64), nullable=True), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'raw_template', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('template', heat.db.types.Json(), nullable=True), + sa.Column('files', heat.db.types.Json(), nullable=True), + sa.Column( + 'environment', heat.db.types.Json(), nullable=True + ), + sa.Column('files_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ['files_id'], + ['raw_template_files.id'], + name='raw_tmpl_files_fkey_ref', + ), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'software_deployment', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('server_id', sa.String(length=36), nullable=False), + sa.Column('config_id', sa.String(length=36), nullable=False), + sa.Column( + 'input_values', heat.db.types.Json(), nullable=True + ), + sa.Column( + 'output_values', heat.db.types.Json(), nullable=True + ), + sa.Column('action', sa.String(length=255), nullable=True), + sa.Column('status', sa.String(length=255), nullable=True), + sa.Column('status_reason', sa.Text(), nullable=True), + sa.Column('tenant', sa.String(length=64), nullable=False), + sa.Column( + 'stack_user_project_id', sa.String(length=64), nullable=True + ), + sa.ForeignKeyConstraint( + ['config_id'], + ['software_config.id'], + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + 'ix_software_deployment_created_at', + 'software_deployment', + ['created_at'], + unique=False, + ) + op.create_index( + op.f('ix_software_deployment_server_id'), + 'software_deployment', + ['server_id'], + unique=False, + ) + op.create_index( + op.f('ix_software_deployment_tenant'), + 'software_deployment', + ['tenant'], + unique=False, + ) + op.create_table( + 'stack', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('raw_template_id', sa.Integer(), nullable=False), + sa.Column('prev_raw_template_id', sa.Integer(), nullable=True), + sa.Column('user_creds_id', sa.Integer(), nullable=True), + sa.Column('username', sa.String(length=256), nullable=True), + sa.Column('owner_id', sa.String(length=36), nullable=True), + sa.Column('action', sa.String(length=255), nullable=True), + sa.Column('status', sa.String(length=255), nullable=True), + sa.Column('status_reason', sa.Text(), nullable=True), + sa.Column('timeout', sa.Integer(), nullable=True), + sa.Column('tenant', sa.String(length=256), nullable=True), + sa.Column('disable_rollback', sa.Boolean(), nullable=False), + sa.Column( + 'stack_user_project_id', sa.String(length=64), nullable=True + ), + sa.Column('backup', sa.Boolean(), nullable=True), + sa.Column('nested_depth', sa.Integer(), nullable=True), + sa.Column('convergence', sa.Boolean(), nullable=True), + sa.Column('current_traversal', sa.String(length=36), nullable=True), + sa.Column( + 'current_deps', heat.db.types.Json(), nullable=True + ), + sa.Column( + 'parent_resource_name', sa.String(length=255), nullable=True + ), + sa.ForeignKeyConstraint( + ['prev_raw_template_id'], + ['raw_template.id'], + ), + sa.ForeignKeyConstraint( + ['raw_template_id'], + ['raw_template.id'], + ), + sa.ForeignKeyConstraint( + ['user_creds_id'], + ['user_creds.id'], + ), + sa.PrimaryKeyConstraint('id'), + ) + op.create_index( + 'ix_stack_name', 'stack', ['name'], unique=False, mysql_length=255 + ) + op.create_index( + 'ix_stack_tenant', 'stack', ['tenant'], unique=False, mysql_length=255 + ) + op.create_index( + op.f('ix_stack_owner_id'), 'stack', ['owner_id'], unique=False + ) + op.create_table( + 'event', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=True), + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('resource_action', sa.String(length=255), nullable=True), + sa.Column('resource_status', sa.String(length=255), nullable=True), + sa.Column('resource_name', sa.String(length=255), nullable=True), + sa.Column( + 'physical_resource_id', sa.String(length=255), nullable=True + ), + sa.Column( + 'resource_status_reason', sa.String(length=255), nullable=True + ), + sa.Column('resource_type', sa.String(length=255), nullable=True), + sa.Column('resource_properties', sa.PickleType(), nullable=True), + sa.Column('rsrc_prop_data_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ['rsrc_prop_data_id'], + ['resource_properties_data.id'], + name='ev_rsrc_prop_data_ref', + ), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uuid'), + mysql_engine='InnoDB', + ) + op.create_table( + 'resource', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=True), + sa.Column('nova_instance', sa.String(length=255), nullable=True), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('action', sa.String(length=255), nullable=True), + sa.Column('status', sa.String(length=255), nullable=True), + sa.Column('status_reason', sa.Text(), nullable=True), + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.Column( + 'rsrc_metadata', heat.db.types.Json(), nullable=True + ), + sa.Column( + 'properties_data', heat.db.types.Json(), nullable=True + ), + sa.Column('engine_id', sa.String(length=36), nullable=True), + sa.Column('atomic_key', sa.Integer(), nullable=True), + sa.Column('needed_by', heat.db.types.List(), nullable=True), + sa.Column('requires', heat.db.types.List(), nullable=True), + sa.Column('replaces', sa.Integer(), nullable=True), + sa.Column('replaced_by', sa.Integer(), nullable=True), + sa.Column('current_template_id', sa.Integer(), nullable=True), + sa.Column('properties_data_encrypted', sa.Boolean(), nullable=True), + sa.Column('root_stack_id', sa.String(length=36), nullable=True), + sa.Column('rsrc_prop_data_id', sa.Integer(), nullable=True), + sa.Column('attr_data_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ['attr_data_id'], + ['resource_properties_data.id'], + name='rsrc_attr_data_ref', + ), + sa.ForeignKeyConstraint( + ['current_template_id'], + ['raw_template.id'], + ), + sa.ForeignKeyConstraint( + ['rsrc_prop_data_id'], + ['resource_properties_data.id'], + name='rsrc_rsrc_prop_data_ref', + ), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uuid'), + mysql_engine='InnoDB', + ) + op.create_index( + op.f('ix_resource_root_stack_id'), + 'resource', + ['root_stack_id'], + unique=False, + ) + op.create_table( + 'snapshot', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('status', sa.String(length=255), nullable=True), + sa.Column('status_reason', sa.String(length=255), nullable=True), + sa.Column('data', heat.db.types.Json(), nullable=True), + sa.Column('tenant', sa.String(length=64), nullable=False), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_index( + op.f('ix_snapshot_tenant'), 'snapshot', ['tenant'], unique=False + ) + op.create_table( + 'stack_lock', + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('engine_id', sa.String(length=36), nullable=True), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('stack_id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'stack_tag', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('tag', sa.Unicode(length=80), nullable=True), + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) + op.create_table( + 'sync_point', + sa.Column('entity_id', sa.String(length=36), nullable=False), + sa.Column('traversal_id', sa.String(length=36), nullable=False), + sa.Column('is_update', sa.Boolean(), nullable=False), + sa.Column('atomic_key', sa.Integer(), nullable=False), + sa.Column('stack_id', sa.String(length=36), nullable=False), + sa.Column( + 'input_data', heat.db.types.Json(), nullable=True + ), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint( + ['stack_id'], + ['stack.id'], + ), + sa.PrimaryKeyConstraint('entity_id', 'traversal_id', 'is_update'), + ) + op.create_table( + 'resource_data', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('key', sa.String(length=255), nullable=True), + sa.Column('value', sa.Text(), nullable=True), + sa.Column('redact', sa.Boolean(), nullable=True), + sa.Column('decrypt_method', sa.String(length=64), nullable=True), + sa.Column('resource_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ['resource_id'], + ['resource.id'], + name='fk_resource_id', + ondelete='CASCADE', + ), + sa.PrimaryKeyConstraint('id'), + mysql_engine='InnoDB', + ) diff --git a/heat/db/sqlalchemy/models.py b/heat/db/models.py index ca208bef0..ebc235f5d 100644 --- a/heat/db/sqlalchemy/models.py +++ b/heat/db/models.py @@ -21,7 +21,7 @@ from sqlalchemy.ext import declarative from sqlalchemy.orm import backref from sqlalchemy.orm import relationship -from heat.db.sqlalchemy import types +from heat.db import types BASE = declarative.declarative_base() diff --git a/heat/db/sqlalchemy/__init__.py b/heat/db/sqlalchemy/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/heat/db/sqlalchemy/__init__.py +++ /dev/null diff --git a/heat/db/sqlalchemy/migrate_repo/README b/heat/db/sqlalchemy/migrate_repo/README deleted file mode 100644 index 131117104..000000000 --- a/heat/db/sqlalchemy/migrate_repo/README +++ /dev/null @@ -1,4 +0,0 @@ -This is a database migration repository. - -More information at -https://opendev.org/openstack/sqlalchemy-migrate diff --git a/heat/db/sqlalchemy/migrate_repo/__init__.py b/heat/db/sqlalchemy/migrate_repo/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/heat/db/sqlalchemy/migrate_repo/__init__.py +++ /dev/null diff --git a/heat/db/sqlalchemy/migrate_repo/manage.py b/heat/db/sqlalchemy/migrate_repo/manage.py deleted file mode 100755 index 41cba1adb..000000000 --- a/heat/db/sqlalchemy/migrate_repo/manage.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -from migrate.versioning.shell import main - -if __name__ == '__main__': - main(debug='False') diff --git a/heat/db/sqlalchemy/migrate_repo/migrate.cfg b/heat/db/sqlalchemy/migrate_repo/migrate.cfg deleted file mode 100644 index 134fc065d..000000000 --- a/heat/db/sqlalchemy/migrate_repo/migrate.cfg +++ /dev/null @@ -1,25 +0,0 @@ -[db_settings] -# Used to identify which repository this database is versioned under. -# You can use the name of your project. -repository_id=heat - -# The name of the database table used to track the schema version. -# This name shouldn't already be used by your project. -# If this is changed once a database is under version control, you'll need to -# change the table name in each database too. -version_table=migrate_version - -# When committing a change script, Migrate will attempt to generate the -# sql for all supported databases; normally, if one of them fails - probably -# because you don't have that database installed - it is ignored and the -# commit continues, perhaps ending successfully. -# Databases in this list MUST compile successfully during a commit, or the -# entire commit will fail. List the databases your application will actually -# be using to ensure your updates to that database work properly. -# This must be a list; example: ['postgres','sqlite'] -required_dbs=[] - -# When creating new change scripts, Migrate will stamp the new script with -# a version number. By default this is latest_version + 1. You can set this -# to 'true' to tell Migrate to use the UTC timestamp instead. -use_timestamp_numbering=False diff --git a/heat/db/sqlalchemy/migrate_repo/versions/073_newton.py b/heat/db/sqlalchemy/migrate_repo/versions/073_newton.py deleted file mode 100644 index 3293b91ce..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/073_newton.py +++ /dev/null @@ -1,387 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -import uuid - -import sqlalchemy - -from heat.db.sqlalchemy import types - - -def upgrade(migrate_engine): - meta = sqlalchemy.MetaData() - meta.bind = migrate_engine - - raw_template_files = sqlalchemy.Table( - 'raw_template_files', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, - nullable=False), - sqlalchemy.Column('files', types.Json), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - raw_template = sqlalchemy.Table( - 'raw_template', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('template', types.LongText), - sqlalchemy.Column('files', types.Json), - sqlalchemy.Column('environment', types.Json), - sqlalchemy.Column('files_id', sqlalchemy.Integer(), - sqlalchemy.ForeignKey( - 'raw_template_files.id', - name='raw_tmpl_files_fkey_ref')), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - user_creds = sqlalchemy.Table( - 'user_creds', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('username', sqlalchemy.String(255)), - sqlalchemy.Column('password', sqlalchemy.String(255)), - sqlalchemy.Column('region_name', sqlalchemy.String(length=255)), - sqlalchemy.Column('decrypt_method', sqlalchemy.String(length=64)), - sqlalchemy.Column('tenant', sqlalchemy.String(1024)), - sqlalchemy.Column('auth_url', sqlalchemy.Text), - sqlalchemy.Column('tenant_id', sqlalchemy.String(256)), - sqlalchemy.Column('trust_id', sqlalchemy.String(255)), - sqlalchemy.Column('trustor_user_id', sqlalchemy.String(64)), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - stack = sqlalchemy.Table( - 'stack', meta, - sqlalchemy.Column('id', sqlalchemy.String(36), - primary_key=True, nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('deleted_at', sqlalchemy.DateTime), - sqlalchemy.Column('name', sqlalchemy.String(255)), - sqlalchemy.Column('raw_template_id', - sqlalchemy.Integer, - sqlalchemy.ForeignKey('raw_template.id'), - nullable=False), - sqlalchemy.Column('prev_raw_template_id', - sqlalchemy.Integer, - sqlalchemy.ForeignKey('raw_template.id')), - sqlalchemy.Column('user_creds_id', sqlalchemy.Integer, - sqlalchemy.ForeignKey('user_creds.id')), - sqlalchemy.Column('username', sqlalchemy.String(256)), - sqlalchemy.Column('owner_id', sqlalchemy.String(36)), - sqlalchemy.Column('action', sqlalchemy.String(255)), - sqlalchemy.Column('status', sqlalchemy.String(255)), - sqlalchemy.Column('status_reason', sqlalchemy.Text), - sqlalchemy.Column('timeout', sqlalchemy.Integer), - sqlalchemy.Column('tenant', sqlalchemy.String(256)), - sqlalchemy.Column('disable_rollback', sqlalchemy.Boolean, - nullable=False), - sqlalchemy.Column('stack_user_project_id', - sqlalchemy.String(length=64)), - sqlalchemy.Column('backup', sqlalchemy.Boolean, default=False), - sqlalchemy.Column('nested_depth', sqlalchemy.Integer, default=0), - sqlalchemy.Column('convergence', sqlalchemy.Boolean, default=False), - sqlalchemy.Column('current_traversal', sqlalchemy.String(36)), - sqlalchemy.Column('current_deps', types.Json), - sqlalchemy.Column('parent_resource_name', sqlalchemy.String(255)), - sqlalchemy.Index('ix_stack_name', 'name', mysql_length=255), - sqlalchemy.Index('ix_stack_tenant', 'tenant', mysql_length=255), - sqlalchemy.Index('ix_stack_owner_id', 'owner_id', mysql_length=36), - - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - resource = sqlalchemy.Table( - 'resource', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('uuid', sqlalchemy.String(36), unique=True, - default=lambda: str(uuid.uuid4())), - sqlalchemy.Column('nova_instance', sqlalchemy.String(255)), - sqlalchemy.Column('name', sqlalchemy.String(255)), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('action', sqlalchemy.String(255)), - sqlalchemy.Column('status', sqlalchemy.String(255)), - sqlalchemy.Column('status_reason', sqlalchemy.Text), - sqlalchemy.Column('stack_id', sqlalchemy.String(36), - sqlalchemy.ForeignKey('stack.id'), nullable=False), - sqlalchemy.Column('rsrc_metadata', types.LongText), - sqlalchemy.Column('properties_data', types.Json), - sqlalchemy.Column('engine_id', sqlalchemy.String(length=36)), - sqlalchemy.Column('atomic_key', sqlalchemy.Integer), - sqlalchemy.Column('needed_by', types.List), - sqlalchemy.Column('requires', types.List), - sqlalchemy.Column('replaces', sqlalchemy.Integer), - sqlalchemy.Column('replaced_by', sqlalchemy.Integer), - sqlalchemy.Column('current_template_id', sqlalchemy.Integer, - sqlalchemy.ForeignKey('raw_template.id')), - sqlalchemy.Column('properties_data_encrypted', - sqlalchemy.Boolean, - default=False), - sqlalchemy.Column('root_stack_id', sqlalchemy.String(36)), - sqlalchemy.Index('ix_resource_root_stack_id', - 'root_stack_id', - mysql_length=36), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - resource_data = sqlalchemy.Table( - 'resource_data', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('key', sqlalchemy.String(255)), - sqlalchemy.Column('value', sqlalchemy.Text), - sqlalchemy.Column('redact', sqlalchemy.Boolean), - sqlalchemy.Column('decrypt_method', sqlalchemy.String(length=64)), - sqlalchemy.Column('resource_id', - sqlalchemy.Integer, - sqlalchemy.ForeignKey('resource.id', - name='fk_resource_id', - ondelete='CASCADE'), - nullable=False), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - event = sqlalchemy.Table( - 'event', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('uuid', sqlalchemy.String(36), - default=lambda: str(uuid.uuid4()), unique=True), - sqlalchemy.Column('stack_id', sqlalchemy.String(36), - sqlalchemy.ForeignKey('stack.id'), nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('resource_action', sqlalchemy.String(255)), - sqlalchemy.Column('resource_status', sqlalchemy.String(255)), - sqlalchemy.Column('resource_name', sqlalchemy.String(255)), - sqlalchemy.Column('physical_resource_id', sqlalchemy.String(255)), - sqlalchemy.Column('resource_status_reason', sqlalchemy.String(255)), - sqlalchemy.Column('resource_type', sqlalchemy.String(255)), - sqlalchemy.Column('resource_properties', sqlalchemy.PickleType), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - watch_rule = sqlalchemy.Table( - 'watch_rule', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('name', sqlalchemy.String(255)), - sqlalchemy.Column('state', sqlalchemy.String(255)), - sqlalchemy.Column('rule', types.LongText), - sqlalchemy.Column('last_evaluated', sqlalchemy.DateTime), - sqlalchemy.Column('stack_id', sqlalchemy.String(36), - sqlalchemy.ForeignKey('stack.id'), nullable=False), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - watch_data = sqlalchemy.Table( - 'watch_data', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('data', types.LongText), - sqlalchemy.Column('watch_rule_id', sqlalchemy.Integer, - sqlalchemy.ForeignKey('watch_rule.id'), - nullable=False), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - stack_lock = sqlalchemy.Table( - 'stack_lock', meta, - sqlalchemy.Column('stack_id', sqlalchemy.String(length=36), - sqlalchemy.ForeignKey('stack.id'), - primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('engine_id', sqlalchemy.String(length=36)), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - software_config = sqlalchemy.Table( - 'software_config', meta, - sqlalchemy.Column('id', sqlalchemy.String(36), - primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('name', sqlalchemy.String(255)), - sqlalchemy.Column('group', sqlalchemy.String(255)), - sqlalchemy.Column('config', types.LongText), - sqlalchemy.Column('tenant', sqlalchemy.String(64), - nullable=False, - index=True), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - software_deployment = sqlalchemy.Table( - 'software_deployment', meta, - sqlalchemy.Column('id', sqlalchemy.String(36), - primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime, - index=True), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('server_id', sqlalchemy.String(36), - nullable=False, - index=True), - sqlalchemy.Column('config_id', - sqlalchemy.String(36), - sqlalchemy.ForeignKey('software_config.id'), - nullable=False), - sqlalchemy.Column('input_values', types.Json), - sqlalchemy.Column('output_values', types.Json), - sqlalchemy.Column('action', sqlalchemy.String(255)), - sqlalchemy.Column('status', sqlalchemy.String(255)), - sqlalchemy.Column('status_reason', sqlalchemy.Text), - sqlalchemy.Column('tenant', sqlalchemy.String(64), - nullable=False, - index=True), - sqlalchemy.Column('stack_user_project_id', - sqlalchemy.String(length=64)), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - snapshot = sqlalchemy.Table( - 'snapshot', meta, - sqlalchemy.Column('id', sqlalchemy.String(36), - primary_key=True, - nullable=False), - sqlalchemy.Column('stack_id', - sqlalchemy.String(36), - sqlalchemy.ForeignKey('stack.id'), - nullable=False), - sqlalchemy.Column('name', sqlalchemy.String(255)), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('status', sqlalchemy.String(255)), - sqlalchemy.Column('status_reason', sqlalchemy.String(255)), - sqlalchemy.Column('data', types.Json), - sqlalchemy.Column('tenant', sqlalchemy.String(64), - nullable=False, - index=True), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - service = sqlalchemy.Table( - 'service', meta, - sqlalchemy.Column('id', sqlalchemy.String(36), primary_key=True, - default=lambda: str(uuid.uuid4())), - sqlalchemy.Column('engine_id', sqlalchemy.String(36), nullable=False), - sqlalchemy.Column('host', sqlalchemy.String(255), nullable=False), - sqlalchemy.Column('hostname', sqlalchemy.String(255), nullable=False), - sqlalchemy.Column('binary', sqlalchemy.String(255), nullable=False), - sqlalchemy.Column('topic', sqlalchemy.String(255), nullable=False), - sqlalchemy.Column('report_interval', sqlalchemy.Integer, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('deleted_at', sqlalchemy.DateTime), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - stack_tag = sqlalchemy.Table( - 'stack_tag', meta, - sqlalchemy.Column('id', - sqlalchemy.Integer, - primary_key=True, - nullable=False), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - sqlalchemy.Column('tag', sqlalchemy.Unicode(80)), - sqlalchemy.Column('stack_id', - sqlalchemy.String(36), - sqlalchemy.ForeignKey('stack.id'), - nullable=False), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - sync_point = sqlalchemy.Table( - 'sync_point', meta, - sqlalchemy.Column('entity_id', sqlalchemy.String(36)), - sqlalchemy.Column('traversal_id', sqlalchemy.String(36)), - sqlalchemy.Column('is_update', sqlalchemy.Boolean), - sqlalchemy.Column('atomic_key', sqlalchemy.Integer, - nullable=False), - sqlalchemy.Column('stack_id', sqlalchemy.String(36), - nullable=False), - sqlalchemy.Column('input_data', types.Json), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - - sqlalchemy.PrimaryKeyConstraint('entity_id', - 'traversal_id', - 'is_update'), - sqlalchemy.ForeignKeyConstraint(['stack_id'], ['stack.id'], - name='fk_stack_id'), - - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - - tables = ( - raw_template_files, - raw_template, - user_creds, - stack, - resource, - resource_data, - event, - watch_rule, - watch_data, - stack_lock, - software_config, - software_deployment, - snapshot, - service, - stack_tag, - sync_point, - ) - - for index, table in enumerate(tables): - try: - table.create() - except Exception: - # If an error occurs, drop all tables created so far to return - # to the previously existing state. - meta.drop_all(tables=tables[:index]) - raise diff --git a/heat/db/sqlalchemy/migrate_repo/versions/074_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/074_placeholder.py deleted file mode 100644 index e46d47580..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/074_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Newton backports. -# Do not use this number for new Ocata work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/075_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/075_placeholder.py deleted file mode 100644 index e46d47580..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/075_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Newton backports. -# Do not use this number for new Ocata work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/076_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/076_placeholder.py deleted file mode 100644 index e46d47580..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/076_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Newton backports. -# Do not use this number for new Ocata work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/077_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/077_placeholder.py deleted file mode 100644 index e46d47580..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/077_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Newton backports. -# Do not use this number for new Ocata work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/078_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/078_placeholder.py deleted file mode 100644 index e46d47580..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/078_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Newton backports. -# Do not use this number for new Ocata work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/079_resource_properties_data.py b/heat/db/sqlalchemy/migrate_repo/versions/079_resource_properties_data.py deleted file mode 100644 index 574450654..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/079_resource_properties_data.py +++ /dev/null @@ -1,55 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -from migrate.changeset import constraint -import sqlalchemy - -from heat.db.sqlalchemy import types - - -def upgrade(migrate_engine): - meta = sqlalchemy.MetaData(bind=migrate_engine) - - resource_properties_data = sqlalchemy.Table( - 'resource_properties_data', meta, - sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, - nullable=False), - sqlalchemy.Column('data', types.Json), - sqlalchemy.Column('encrypted', sqlalchemy.Boolean), - sqlalchemy.Column('created_at', sqlalchemy.DateTime), - sqlalchemy.Column('updated_at', sqlalchemy.DateTime), - mysql_engine='InnoDB', - mysql_charset='utf8' - ) - resource_properties_data.create() - - resource = sqlalchemy.Table('resource', meta, autoload=True) - rsrc_prop_data_id = sqlalchemy.Column('rsrc_prop_data_id', - sqlalchemy.Integer) - rsrc_prop_data_id.create(resource) - res_fkey = constraint.ForeignKeyConstraint( - columns=[resource.c.rsrc_prop_data_id], - refcolumns=[resource_properties_data.c.id], - name='rsrc_rsrc_prop_data_ref') - res_fkey.create() - - event = sqlalchemy.Table('event', meta, autoload=True) - rsrc_prop_data_id = sqlalchemy.Column('rsrc_prop_data_id', - sqlalchemy.Integer) - rsrc_prop_data_id.create(event) - ev_fkey = constraint.ForeignKeyConstraint( - columns=[event.c.rsrc_prop_data_id], - refcolumns=[resource_properties_data.c.id], - name='ev_rsrc_prop_data_ref') - ev_fkey.create() diff --git a/heat/db/sqlalchemy/migrate_repo/versions/080_resource_attrs_data.py b/heat/db/sqlalchemy/migrate_repo/versions/080_resource_attrs_data.py deleted file mode 100644 index 4636d1e76..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/080_resource_attrs_data.py +++ /dev/null @@ -1,31 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -from migrate.changeset import constraint -import sqlalchemy - - -def upgrade(migrate_engine): - meta = sqlalchemy.MetaData(bind=migrate_engine) - - resource = sqlalchemy.Table('resource', meta, autoload=True) - resource_properties_data = sqlalchemy.Table('resource_properties_data', - meta, autoload=True) - attr_data_id = sqlalchemy.Column('attr_data_id', - sqlalchemy.Integer) - attr_data_id.create(resource) - res_fkey = constraint.ForeignKeyConstraint( - columns=[resource.c.attr_data_id], - refcolumns=[resource_properties_data.c.id], - name='rsrc_attr_data_ref') - res_fkey.create() diff --git a/heat/db/sqlalchemy/migrate_repo/versions/082_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/082_placeholder.py deleted file mode 100644 index 4378ff335..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/082_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Pike backports. -# Do not use this number for new Queens work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/083_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/083_placeholder.py deleted file mode 100644 index 4378ff335..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/083_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Pike backports. -# Do not use this number for new Queens work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/084_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/084_placeholder.py deleted file mode 100644 index 4378ff335..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/084_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Pike backports. -# Do not use this number for new Queens work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/085_placeholder.py b/heat/db/sqlalchemy/migrate_repo/versions/085_placeholder.py deleted file mode 100644 index 4378ff335..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/085_placeholder.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# This is a placeholder for Pike backports. -# Do not use this number for new Queens work, which starts after -# all the placeholders. - - -def upgrade(migrate_engine): - pass diff --git a/heat/db/sqlalchemy/migrate_repo/versions/086_drop_watch_rule_watch_data_tables.py b/heat/db/sqlalchemy/migrate_repo/versions/086_drop_watch_rule_watch_data_tables.py deleted file mode 100644 index a99ac5c4e..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/086_drop_watch_rule_watch_data_tables.py +++ /dev/null @@ -1,53 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -from migrate import ForeignKeyConstraint -from sqlalchemy.engine import reflection -from sqlalchemy import MetaData -from sqlalchemy import Table - - -def upgrade(engine): - meta = MetaData() - meta.bind = engine - - def _get_columns(source_table, params): - columns = set() - for column in params: - columns.add(source_table.c[column]) - return columns - - def _remove_foreign_key_constraints(engine, meta, table_name): - inspector = reflection.Inspector.from_engine(engine) - - for fk in inspector.get_foreign_keys(table_name): - source_table = Table(table_name, meta, autoload=True) - target_table = Table(fk['referred_table'], meta, autoload=True) - - fkey = ForeignKeyConstraint( - columns=_get_columns(source_table, fk['constrained_columns']), - refcolumns=_get_columns(target_table, fk['referred_columns']), - name=fk['name']) - fkey.drop() - - def _drop_table_and_indexes(meta, table_name): - table = Table(table_name, meta, autoload=True) - for index in table.indexes: - index.drop() - table.drop() - - table_names = ('watch_data', 'watch_rule') - - for table_name in table_names: - _remove_foreign_key_constraints(engine, meta, table_name) - _drop_table_and_indexes(meta, table_name) diff --git a/heat/db/sqlalchemy/migrate_repo/versions/__init__.py b/heat/db/sqlalchemy/migrate_repo/versions/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/heat/db/sqlalchemy/migrate_repo/versions/__init__.py +++ /dev/null diff --git a/heat/db/sqlalchemy/migration.py b/heat/db/sqlalchemy/migration.py deleted file mode 100644 index 7f030d90a..000000000 --- a/heat/db/sqlalchemy/migration.py +++ /dev/null @@ -1,38 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -import os - -from oslo_db.sqlalchemy import migration as oslo_migration - - -INIT_VERSION = 72 - - -def db_sync(engine, version=None): - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'migrate_repo') - return oslo_migration.db_sync(engine, path, version, - init_version=INIT_VERSION) - - -def db_version(engine): - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'migrate_repo') - return oslo_migration.db_version(engine, path, INIT_VERSION) - - -def db_version_control(engine, version=None): - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'migrate_repo') - return oslo_migration.db_version_control(engine, path, version) diff --git a/heat/db/sqlalchemy/utils.py b/heat/db/sqlalchemy/utils.py deleted file mode 100644 index 5fff4f763..000000000 --- a/heat/db/sqlalchemy/utils.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "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. - -# SQLAlchemy helper functions - -import sqlalchemy -from sqlalchemy.orm import exc -import tenacity - - -def clone_table(name, parent, meta, newcols=None, ignorecols=None, - swapcols=None, ignorecons=None): - """Helper function that clones parent table schema onto new table. - - :param name: new table name - :param parent: parent table to copy schema from - :param newcols: names of new columns to be added - :param ignorecols: names of columns to be ignored while cloning - :param swapcols: alternative column schema - :param ignorecons: names of constraints to be ignored - - :return: sqlalchemy.Table instance - """ - - newcols = newcols or [] - ignorecols = ignorecols or [] - swapcols = swapcols or {} - ignorecons = ignorecons or [] - - cols = [c.copy() for c in parent.columns - if c.name not in ignorecols - if c.name not in swapcols] - cols.extend(swapcols.values()) - cols.extend(newcols) - new_table = sqlalchemy.Table(name, meta, *(cols)) - - def _is_ignorable(cons): - # consider constraints on columns only - if hasattr(cons, 'columns'): - for col in ignorecols: - if col in cons.columns: - return True - - return False - - constraints = [c.copy(target_table=new_table) for c in parent.constraints - if c.name not in ignorecons - if not _is_ignorable(c)] - - for c in constraints: - new_table.append_constraint(c) - - new_table.create() - return new_table - - -def migrate_data(migrate_engine, - table, - new_table, - skip_columns=None): - - table_name = table.name - - list_of_rows = list(table.select().execute()) - - colnames = [c.name for c in table.columns] - - for row in list_of_rows: - values = dict(zip(colnames, - map(lambda colname: getattr(row, colname), - colnames))) - if skip_columns is not None: - for column in skip_columns: - del values[column] - - migrate_engine.execute(new_table.insert(values)) - - table.drop() - - new_table.rename(table_name) - - -def retry_on_stale_data_error(func): - wrapper = tenacity.retry( - stop=tenacity.stop_after_attempt(3), - retry=tenacity.retry_if_exception_type(exc.StaleDataError), - reraise=True) - return wrapper(func) diff --git a/heat/db/sqlalchemy/types.py b/heat/db/types.py index d454024c6..d454024c6 100644 --- a/heat/db/sqlalchemy/types.py +++ b/heat/db/types.py diff --git a/heat/db/sqlalchemy/migrate_repo/versions/081_placeholder.py b/heat/db/utils.py index 4378ff335..67e2d541e 100644 --- a/heat/db/sqlalchemy/migrate_repo/versions/081_placeholder.py +++ b/heat/db/utils.py @@ -11,10 +11,15 @@ # License for the specific language governing permissions and limitations # under the License. -# This is a placeholder for Pike backports. -# Do not use this number for new Queens work, which starts after -# all the placeholders. +# SQLAlchemy helper functions +from sqlalchemy.orm import exc +import tenacity -def upgrade(migrate_engine): - pass + +def retry_on_stale_data_error(func): + wrapper = tenacity.retry( + stop=tenacity.stop_after_attempt(3), + retry=tenacity.retry_if_exception_type(exc.StaleDataError), + reraise=True) + return wrapper(func) |