From ca9ba88a7a31e6feb906f9c84b47af2aed43c684 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 13 Apr 2021 12:22:19 +0100 Subject: [PATCH] db: Add initial alembic migration for main DB This wasn't as complicated as feared. We're mostly able to copy-paste the existing sqlalchemy-migrate migration and simply changes the pattern of calls from monkey-patched 'create' methods such as: agent_builds = Table('agent_builds', meta, sa.Column('created_at', sa.DateTime), ... mysql_charset='utf8' ) agent_builds.create() to explicit alembic APIs like: op.create_table( 'agent_builds', sa.Column('created_at', sa.DateTime), ... mysql_charset='utf8' ) Reviewers are encouraged to diff the old migration file, 'nova/db/main/legacy_migrations/versions/402_train.py' against the new one, 'nova/db/main/migrations/versions/8f2f1571d55b_initial_version.py', to ease their job. The only significant divergences are the removal of a single reference to the 'migrate_version' table created by sqlalchemy-migrate, which obviously won't exist in an alembic-only world, along with some reordering of tables. The latter step is necessary since Alembic is not smart enough to correctly order the creation of tables so that tables that reference (via a foreign key) one or more other tables are created after the table(s) they reference. Since we now have to create tables using the 'create_table' API as noted above, rather than by creating a 'Table' instance and calling the sqlalchemy-migrate-provided 'create' API as we could previously, we must reorder our calls. The alternative would be to leave the creation of any 'ForeignKeyConstraint' until later but that seems no better and would arguably be harder to read. Note that this isn't yet wired up to anything: users can run it manually but the nova-manage commands we provide are still connected to the sqlalchemy-migrate commands. This will change shortly when we add shims to handle the conversion. Change-Id: I1fa2feaee78213ad81f1889ce54888696f58d98c Signed-off-by: Stephen Finucane --- nova/db/main/alembic.ini | 89 + .../legacy_migrations/versions/402_train.py | 3 - nova/db/main/migrations/README.rst | 15 + nova/db/main/migrations/env.py | 79 + nova/db/main/migrations/script.py.mako | 36 + .../versions/8f2f1571d55b_initial_version.py | 1629 +++++++++++++++++ 6 files changed, 1848 insertions(+), 3 deletions(-) create mode 100644 nova/db/main/alembic.ini create mode 100644 nova/db/main/migrations/README.rst create mode 100644 nova/db/main/migrations/env.py create mode 100644 nova/db/main/migrations/script.py.mako create mode 100644 nova/db/main/migrations/versions/8f2f1571d55b_initial_version.py diff --git a/nova/db/main/alembic.ini b/nova/db/main/alembic.ini new file mode 100644 index 0000000000..3ebab34dc8 --- /dev/null +++ b/nova/db/main/alembic.ini @@ -0,0 +1,89 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = nova/db/main/migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to nova/db/main/migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat nova/db/main/migrations/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///nova.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 + +# 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/nova/db/main/legacy_migrations/versions/402_train.py b/nova/db/main/legacy_migrations/versions/402_train.py index 5573c098a8..31d0dd9ca4 100644 --- a/nova/db/main/legacy_migrations/versions/402_train.py +++ b/nova/db/main/legacy_migrations/versions/402_train.py @@ -25,9 +25,6 @@ from nova.objects import keypair LOG = logging.getLogger(__name__) -# Note on the autoincrement flag: this is defaulted for primary key columns -# of integral type, so is no longer set explicitly in such cases. - # NOTE(dprince): This wrapper allows us to easily match the Folsom MySQL # Schema. In Folsom we created tables as latin1 and converted them to utf8 # later. This conversion causes some of the Text columns on MySQL to get diff --git a/nova/db/main/migrations/README.rst b/nova/db/main/migrations/README.rst new file mode 100644 index 0000000000..4a21a1582e --- /dev/null +++ b/nova/db/main/migrations/README.rst @@ -0,0 +1,15 @@ +Migrations for the main database +================================ + +This directory contains migrations for the main 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/nova/db/main/migrations/env.py b/nova/db/main/migrations/env.py new file mode 100644 index 0000000000..49efd90f3f --- /dev/null +++ b/nova/db/main/migrations/env.py @@ -0,0 +1,79 @@ +# 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 + +# 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. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + + +def run_migrations_offline(): + """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(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine and associate a connection + with the context. + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + 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/nova/db/main/migrations/script.py.mako b/nova/db/main/migrations/script.py.mako new file mode 100644 index 0000000000..e7a6cf1348 --- /dev/null +++ b/nova/db/main/migrations/script.py.mako @@ -0,0 +1,36 @@ +# 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. + +"""${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(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/nova/db/main/migrations/versions/8f2f1571d55b_initial_version.py b/nova/db/main/migrations/versions/8f2f1571d55b_initial_version.py new file mode 100644 index 0000000000..96cf1679d0 --- /dev/null +++ b/nova/db/main/migrations/versions/8f2f1571d55b_initial_version.py @@ -0,0 +1,1629 @@ +# 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 version + +Revision ID: 8f2f1571d55b +Revises: +Create Date: 2021-04-13 11:59:19.234123 +""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import dialects +from sqlalchemy.ext import compiler +from sqlalchemy import types as sqla_types + +from nova.db import types +from nova.objects import keypair + +# revision identifiers, used by Alembic. +revision = '8f2f1571d55b' +down_revision = None +branch_labels = None +depends_on = None + + +# NOTE(dprince): This wrapper allows us to easily match the Folsom MySQL +# Schema. In Folsom we created tables as latin1 and converted them to utf8 +# later. This conversion causes some of the Text columns on MySQL to get +# created as mediumtext instead of just text. +def MediumText(): + return sa.Text().with_variant(dialects.mysql.MEDIUMTEXT(), 'mysql') + + +def Inet(): + return sa.String(length=43).with_variant( + dialects.postgresql.INET(), 'postgresql', + ) + + +def InetSmall(): + return sa.String(length=39).with_variant( + dialects.postgresql.INET(), 'postgresql', + ) + + +# We explicitly name many of our foreignkeys for MySQL so they match Havana +@compiler.compiles(sa.ForeignKeyConstraint, 'postgresql') +def process(element, compiler, **kw): + element.name = None + return compiler.visit_foreign_key_constraint(element, **kw) + + +def _create_shadow_tables(connection): + meta = sa.MetaData(connection) + meta.reflect(connection) + table_names = list(meta.tables.keys()) + + for table_name in table_names: + # Skip tables that are not soft-deletable + if table_name in ( + 'tags', + 'resource_providers', + 'inventories', + 'allocations', + 'resource_provider_aggregates', + 'console_auth_tokens', + ): + continue + + table = sa.Table(table_name, meta, autoload_with=connection) + + columns = [] + for column in table.columns: + column_copy = None + + # NOTE(boris-42): BigInteger is not supported by sqlite so + # after copy it will have NullType. The other types that are used + # in Nova are supported by SQLite + if isinstance(column.type, sqla_types.NullType): + column_copy = sa.Column( + column.name, sa.BigInteger(), default=0, + ) + + if table_name == 'instances' and column.name == 'locked_by': + enum = sa.Enum( + 'owner', 'admin', name='shadow_instances0locked_by', + ) + column_copy = sa.Column(column.name, enum) + + # TODO(stephenfin): Fix these various bugs in a follow-up + + # 244_increase_user_id_length_volume_usage_cache; this + # alteration should apply to shadow tables also + + if table_name == 'volume_usage_cache' and column.name == 'user_id': + # nullable should be True + column_copy = sa.Column('user_id', sa.String(36)) + + # 247_nullable_mismatch; these alterations should apply to shadow + # tables also + + if table_name == 'quota_usages' and column.name == 'resources': + # nullable should be False + column_copy = sa.Column('resource', sa.String(length=255)) + + if table_name == 'pci_devices': + if column.name == 'deleted': + # nullable should be True + column_copy = sa.Column( + 'deleted', sa.Integer, default=0, nullable=False, + ) + + if column.name == 'product_id': + # nullable should be False + column_copy = sa.Column('product_id', sa.String(4)) + + if column.name == 'vendor_id': + # nullable should be False + column_copy = sa.Column('vendor_id', sa.String(4)) + + if column.name == 'dev_type': + # nullable should be False + column_copy = sa.Column('dev_type', sa.String(8)) + + # 280_add_nullable_false_to_keypairs_name; this should apply to the + # shadow table also + + if table_name == 'key_pairs' and column.name == 'name': + # nullable should be False + column_copy = sa.Column('name', sa.String(length=255)) + + # NOTE(stephenfin): By default, 'sqlalchemy.Enum' will issue a + # 'CREATE TYPE' command on PostgreSQL, even if the type already + # exists. We work around this by using the PostgreSQL-specific + # 'sqlalchemy.dialects.postgresql.ENUM' type and setting + # 'create_type' to 'False'. See [1] for more information. + # + # [1] https://stackoverflow.com/a/28894354/613428 + if connection.engine.name == 'postgresql': + if table_name == 'key_pairs' and column.name == 'type': + enum = dialects.postgresql.ENUM( + 'ssh', 'x509', name='keypair_types', create_type=False) + column_copy = sa.Column( + column.name, enum, nullable=False, + server_default=keypair.KEYPAIR_TYPE_SSH) + elif ( + table_name == 'migrations' and + column.name == 'migration_type' + ): + enum = dialects.postgresql.ENUM( + 'migration', 'resize', 'live-migration', 'evacuation', + name='migration_type', create_type=False) + column_copy = sa.Column(column.name, enum, nullable=True) + + if column_copy is None: + column_copy = column.copy() + + columns.append(column_copy) + + op.create_table( + 'shadow_' + table_name, meta, *columns, mysql_engine='InnoDB', + ) + + # TODO(stephenfin): Fix these various bugs in a follow-up + + # 252_add_instance_extra_table; we don't create indexes for shadow tables + # in general and these should be removed + + op.create_index( + 'shadow_instance_extra_idx', + 'shadow_instance_extra', + ['instance_uuid']) + + # 373_migration_uuid; we should't create indexes for shadow tables + + op.create_index( + 'shadow_migrations_uuid', 'shadow_migrations', ['uuid'], unique=True) + + +def upgrade(): + bind = op.get_bind() + + op.create_table( + 'instances', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('internal_id', sa.Integer), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('image_ref', sa.String(length=255)), + sa.Column('kernel_id', sa.String(length=255)), + sa.Column('ramdisk_id', sa.String(length=255)), + sa.Column('launch_index', sa.Integer), + sa.Column('key_name', sa.String(length=255)), + sa.Column('key_data', MediumText()), + sa.Column('power_state', sa.Integer), + sa.Column('vm_state', sa.String(length=255)), + sa.Column('memory_mb', sa.Integer), + sa.Column('vcpus', sa.Integer), + sa.Column('hostname', sa.String(length=255)), + sa.Column('host', sa.String(length=255)), + sa.Column('user_data', MediumText()), + sa.Column('reservation_id', sa.String(length=255)), + sa.Column('launched_at', sa.DateTime), + sa.Column('terminated_at', sa.DateTime), + sa.Column('display_name', sa.String(length=255)), + sa.Column('display_description', sa.String(length=255)), + sa.Column('availability_zone', sa.String(length=255)), + sa.Column('locked', sa.Boolean), + sa.Column('os_type', sa.String(length=255)), + sa.Column('launched_on', MediumText()), + sa.Column('instance_type_id', sa.Integer), + sa.Column('vm_mode', sa.String(length=255)), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('architecture', sa.String(length=255)), + sa.Column('root_device_name', sa.String(length=255)), + sa.Column('access_ip_v4', InetSmall()), + sa.Column('access_ip_v6', InetSmall()), + sa.Column('config_drive', sa.String(length=255)), + sa.Column('task_state', sa.String(length=255)), + sa.Column('default_ephemeral_device', sa.String(length=255)), + sa.Column('default_swap_device', sa.String(length=255)), + sa.Column('progress', sa.Integer), + sa.Column('auto_disk_config', sa.Boolean), + sa.Column('shutdown_terminate', sa.Boolean), + sa.Column('disable_terminate', sa.Boolean), + sa.Column('root_gb', sa.Integer), + sa.Column('ephemeral_gb', sa.Integer), + sa.Column('cell_name', sa.String(length=255)), + sa.Column('node', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Column( + 'locked_by', + sa.Enum('owner', 'admin', name='instances0locked_by')), + sa.Column('cleaned', sa.Integer, default=0), + sa.Column('ephemeral_key_uuid', sa.String(36)), + # NOTE(danms): This column originally included default=False. We + # discovered in bug #1862205 that this will attempt to rewrite + # the entire instances table with that value, which can time out + # for large data sets (and does not even abort). + # NOTE(stephenfin): This was originally added by sqlalchemy-migrate + # which did not generate the constraints + sa.Column('hidden', sa.Boolean(create_constraint=False)), + sa.Index('uuid', 'uuid', unique=True), + sa.Index('instances_reservation_id_idx', 'reservation_id'), + sa.Index( + 'instances_terminated_at_launched_at_idx', + 'terminated_at', 'launched_at'), + sa.Index( + 'instances_task_state_updated_at_idx', + 'task_state', 'updated_at'), + sa.Index('instances_uuid_deleted_idx', 'uuid', 'deleted'), + sa.Index('instances_host_node_deleted_idx', 'host', 'node', 'deleted'), + sa.Index( + 'instances_host_deleted_cleaned_idx', + 'host', 'deleted', 'cleaned'), + sa.Index('instances_project_id_deleted_idx', 'project_id', 'deleted'), + sa.Index('instances_deleted_created_at_idx', 'deleted', 'created_at'), + sa.Index('instances_project_id_idx', 'project_id'), + sa.Index( + 'instances_updated_at_project_id_idx', + 'updated_at', 'project_id'), + sa.UniqueConstraint('uuid', name='uniq_instances0uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'agent_builds', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('hypervisor', sa.String(length=255)), + sa.Column('os', sa.String(length=255)), + sa.Column('architecture', sa.String(length=255)), + sa.Column('version', sa.String(length=255)), + sa.Column('url', sa.String(length=255)), + sa.Column('md5hash', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index( + 'agent_builds_hypervisor_os_arch_idx', + 'hypervisor', 'os', 'architecture'), + sa.UniqueConstraint( + 'hypervisor', 'os', 'architecture', 'deleted', + name='uniq_agent_builds0hypervisor0os0architecture0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'aggregates', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('name', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Column('uuid', sa.String(36)), + sa.Index('aggregate_uuid_idx', 'uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'aggregate_hosts', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('host', sa.String(length=255)), + sa.Column( + 'aggregate_id', sa.Integer, sa.ForeignKey('aggregates.id'), + nullable=False), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'host', 'aggregate_id', 'deleted', + name='uniq_aggregate_hosts0host0aggregate_id0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'aggregate_metadata', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'aggregate_id', sa.Integer, sa.ForeignKey('aggregates.id'), + nullable=False), + sa.Column('key', sa.String(length=255), nullable=False), + sa.Column('value', sa.String(length=255), nullable=False), + sa.Column('deleted', sa.Integer), + sa.Index('aggregate_metadata_key_idx', 'key'), + sa.Index('aggregate_metadata_value_idx', 'value'), + sa.UniqueConstraint( + 'aggregate_id', 'key', 'deleted', + name='uniq_aggregate_metadata0aggregate_id0key0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'allocations', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('resource_provider_id', sa.Integer, nullable=False), + sa.Column('consumer_id', sa.String(36), nullable=False), + sa.Column('resource_class_id', sa.Integer, nullable=False), + sa.Column('used', sa.Integer, nullable=False), + sa.Index( + 'allocations_resource_provider_class_used_idx', + 'resource_provider_id', 'resource_class_id', 'used'), + sa.Index('allocations_consumer_id_idx', 'consumer_id'), + sa.Index('allocations_resource_class_id_idx', 'resource_class_id'), + mysql_engine='InnoDB', + mysql_charset='latin1', + ) + + op.create_table( + 'block_device_mapping', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('device_name', sa.String(length=255), nullable=True), + sa.Column('delete_on_termination', sa.Boolean), + sa.Column('snapshot_id', sa.String(length=36), nullable=True), + sa.Column('volume_id', sa.String(length=36), nullable=True), + sa.Column('volume_size', sa.Integer), + sa.Column('no_device', sa.Boolean), + sa.Column('connection_info', MediumText()), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', + name='block_device_mapping_instance_uuid_fkey')), + sa.Column('deleted', sa.Integer), + sa.Column('source_type', sa.String(length=255), nullable=True), + sa.Column('destination_type', sa.String(length=255), nullable=True), + sa.Column('guest_format', sa.String(length=255), nullable=True), + sa.Column('device_type', sa.String(length=255), nullable=True), + sa.Column('disk_bus', sa.String(length=255), nullable=True), + sa.Column('boot_index', sa.Integer), + sa.Column('image_id', sa.String(length=36), nullable=True), + sa.Column('tag', sa.String(255)), + sa.Column('attachment_id', sa.String(36), nullable=True), + sa.Column('uuid', sa.String(36), nullable=True), + sa.Column('volume_type', sa.String(255), nullable=True), + sa.Index('snapshot_id', 'snapshot_id'), + sa.Index('volume_id', 'volume_id'), + sa.Index('block_device_mapping_instance_uuid_idx', 'instance_uuid'), + sa.Index( + 'block_device_mapping_instance_uuid_device_name_idx', + 'instance_uuid', 'device_name'), + sa.Index( + 'block_device_mapping_instance_uuid_volume_id_idx', + 'instance_uuid', 'volume_id'), + sa.UniqueConstraint('uuid', name='uniq_block_device_mapping0uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'bw_usage_cache', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('start_period', sa.DateTime, nullable=False), + sa.Column('last_refreshed', sa.DateTime), + sa.Column('bw_in', sa.BigInteger), + sa.Column('bw_out', sa.BigInteger), + sa.Column('mac', sa.String(length=255)), + sa.Column('uuid', sa.String(length=36)), + sa.Column('last_ctr_in', sa.BigInteger()), + sa.Column('last_ctr_out', sa.BigInteger()), + sa.Column('deleted', sa.Integer), + sa.Index( + 'bw_usage_cache_uuid_start_period_idx', + 'uuid', 'start_period'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'cells', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('api_url', sa.String(length=255)), + sa.Column('weight_offset', sa.Float), + sa.Column('weight_scale', sa.Float), + sa.Column('name', sa.String(length=255)), + sa.Column('is_parent', sa.Boolean), + sa.Column('deleted', sa.Integer), + sa.Column('transport_url', sa.String(length=255), nullable=False), + sa.UniqueConstraint( + 'name', 'deleted', + name='uniq_cells0name0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'certificates', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('file_name', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index( + 'certificates_project_id_deleted_idx', + 'project_id', 'deleted'), + sa.Index('certificates_user_id_deleted_idx', 'user_id', 'deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'compute_nodes', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('service_id', sa.Integer, nullable=True), + sa.Column('vcpus', sa.Integer, nullable=False), + sa.Column('memory_mb', sa.Integer, nullable=False), + sa.Column('local_gb', sa.Integer, nullable=False), + sa.Column('vcpus_used', sa.Integer, nullable=False), + sa.Column('memory_mb_used', sa.Integer, nullable=False), + sa.Column('local_gb_used', sa.Integer, nullable=False), + sa.Column('hypervisor_type', MediumText(), nullable=False), + sa.Column('hypervisor_version', sa.Integer, nullable=False), + sa.Column('cpu_info', MediumText(), nullable=False), + sa.Column('disk_available_least', sa.Integer), + sa.Column('free_ram_mb', sa.Integer), + sa.Column('free_disk_gb', sa.Integer), + sa.Column('current_workload', sa.Integer), + sa.Column('running_vms', sa.Integer), + sa.Column('hypervisor_hostname', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Column('host_ip', InetSmall()), + sa.Column('supported_instances', sa.Text), + sa.Column('pci_stats', sa.Text, nullable=True), + sa.Column('metrics', sa.Text, nullable=True), + sa.Column('extra_resources', sa.Text, nullable=True), + sa.Column('stats', sa.Text, default='{}'), + sa.Column('numa_topology', sa.Text, nullable=True), + sa.Column('host', sa.String(255), nullable=True), + sa.Column('ram_allocation_ratio', sa.Float, nullable=True), + sa.Column('cpu_allocation_ratio', sa.Float, nullable=True), + sa.Column('uuid', sa.String(36), nullable=True), + sa.Column('disk_allocation_ratio', sa.Float, nullable=True), + sa.Column('mapped', sa.Integer, default=0, nullable=True), + sa.Index('compute_nodes_uuid_idx', 'uuid', unique=True), + sa.UniqueConstraint( + 'host', 'hypervisor_hostname', 'deleted', + name='uniq_compute_nodes0host0hypervisor_hostname0deleted', + ), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'console_auth_tokens', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('token_hash', sa.String(255), nullable=False), + sa.Column('console_type', sa.String(255), nullable=False), + sa.Column('host', sa.String(255), nullable=False), + sa.Column('port', sa.Integer, nullable=False), + sa.Column('internal_access_path', sa.String(255)), + sa.Column('instance_uuid', sa.String(36), nullable=False), + sa.Column('expires', sa.Integer, nullable=False), + sa.Column('access_url_base', sa.String(255), nullable=True), + sa.Index('console_auth_tokens_instance_uuid_idx', 'instance_uuid'), + sa.Index('console_auth_tokens_host_expires_idx', 'host', 'expires'), + sa.Index('console_auth_tokens_token_hash_idx', 'token_hash'), + sa.Index( + 'console_auth_tokens_token_hash_instance_uuid_idx', + 'token_hash', 'instance_uuid'), + sa.UniqueConstraint( + 'token_hash', name='uniq_console_auth_tokens0token_hash'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'console_pools', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('address', InetSmall()), + sa.Column('username', sa.String(length=255)), + sa.Column('password', sa.String(length=255)), + sa.Column('console_type', sa.String(length=255)), + sa.Column('public_hostname', sa.String(length=255)), + sa.Column('host', sa.String(length=255)), + sa.Column('compute_host', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'host', 'console_type', 'compute_host', 'deleted', + name='uniq_console_pools0host0console_type0compute_host0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'consoles', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('instance_name', sa.String(length=255)), + sa.Column('password', sa.String(length=255)), + sa.Column('port', sa.Integer), + sa.Column('pool_id', sa.Integer, sa.ForeignKey('console_pools.id')), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='consoles_instance_uuid_fkey')), + sa.Column('deleted', sa.Integer), + sa.Index('consoles_instance_uuid_idx', 'instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'dns_domains', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Boolean), + sa.Column( + 'domain', sa.String(length=255), primary_key=True, nullable=False), + sa.Column('scope', sa.String(length=255)), + sa.Column('availability_zone', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Index('dns_domains_domain_deleted_idx', 'domain', 'deleted'), + sa.Index('dns_domains_project_id_idx', 'project_id'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'fixed_ips', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('address', InetSmall()), + sa.Column('network_id', sa.Integer), + sa.Column('allocated', sa.Boolean), + sa.Column('leased', sa.Boolean), + sa.Column('reserved', sa.Boolean), + sa.Column('virtual_interface_id', sa.Integer), + sa.Column('host', sa.String(length=255)), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='fixed_ips_instance_uuid_fkey'), + ), + sa.Column('deleted', sa.Integer), + sa.Index('network_id', 'network_id'), + sa.Index('address', 'address'), + sa.Index('fixed_ips_instance_uuid_fkey', 'instance_uuid'), + sa.Index( + 'fixed_ips_virtual_interface_id_fkey', + 'virtual_interface_id'), + sa.Index('fixed_ips_host_idx', 'host'), + sa.Index( + 'fixed_ips_network_id_host_deleted_idx', 'network_id', + 'host', 'deleted'), + sa.Index( + 'fixed_ips_address_reserved_network_id_deleted_idx', + 'address', 'reserved', + 'network_id', 'deleted'), + sa.Index( + 'fixed_ips_deleted_allocated_idx', + 'address', 'deleted', 'allocated'), + sa.Index( + 'fixed_ips_deleted_allocated_updated_at_idx', + 'deleted', 'allocated', 'updated_at'), + sa.UniqueConstraint( + 'address', 'deleted', + name='uniq_fixed_ips0address0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'floating_ips', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('address', InetSmall()), + sa.Column('fixed_ip_id', sa.Integer), + sa.Column('project_id', sa.String(length=255)), + sa.Column('host', sa.String(length=255)), + sa.Column('auto_assigned', sa.Boolean), + sa.Column('pool', sa.String(length=255)), + sa.Column('interface', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index('fixed_ip_id', 'fixed_ip_id'), + sa.Index('floating_ips_host_idx', 'host'), + sa.Index('floating_ips_project_id_idx', 'project_id'), + sa.Index( + 'floating_ips_pool_deleted_fixed_ip_id_project_id_idx', + 'pool', 'deleted', 'fixed_ip_id', 'project_id'), + sa.UniqueConstraint( + 'address', 'deleted', + name='uniq_floating_ips0address0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_faults', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='fk_instance_faults_instance_uuid')), + sa.Column('code', sa.Integer, nullable=False), + sa.Column('message', sa.String(length=255)), + sa.Column('details', MediumText()), + sa.Column('host', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index('instance_faults_host_idx', 'host'), + sa.Index( + 'instance_faults_instance_uuid_deleted_created_at_idx', + 'instance_uuid', 'deleted', 'created_at'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_id_mappings', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(36), nullable=False), + sa.Column('deleted', sa.Integer), + sa.Index('ix_instance_id_mappings_uuid', 'uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_info_caches', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('network_info', MediumText()), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', + name='instance_info_caches_instance_uuid_fkey'), + nullable=False), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'instance_uuid', + name='uniq_instance_info_caches0instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_groups', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=255)), + sa.UniqueConstraint( + 'uuid', 'deleted', + name='uniq_instance_groups0uuid0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'instance_group_policy', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('policy', sa.String(length=255)), + sa.Column( + 'group_id', sa.Integer, sa.ForeignKey('instance_groups.id'), + nullable=False), + sa.Index('instance_group_policy_policy_idx', 'policy'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'instance_group_member', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('instance_id', sa.String(length=255)), + sa.Column( + 'group_id', sa.Integer, sa.ForeignKey('instance_groups.id'), + nullable=False), + sa.Index( + 'instance_group_member_instance_idx', + 'instance_id'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'instance_metadata', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('key', sa.String(length=255)), + sa.Column('value', sa.String(length=255)), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='instance_metadata_instance_uuid_fkey'), + nullable=True), + sa.Column('deleted', sa.Integer), + sa.Index('instance_metadata_instance_uuid_idx', 'instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_system_metadata', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='instance_system_metadata_ibfk_1'), + nullable=False), + sa.Column('key', sa.String(length=255), nullable=False), + sa.Column('value', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index('instance_uuid', 'instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + # TODO(stephenfin): Remove this table since it has been moved to the API DB + op.create_table( + 'instance_types', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('name', sa.String(length=255)), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('memory_mb', sa.Integer, nullable=False), + sa.Column('vcpus', sa.Integer, nullable=False), + sa.Column('swap', sa.Integer, nullable=False), + sa.Column('vcpu_weight', sa.Integer), + sa.Column('flavorid', sa.String(length=255)), + sa.Column('rxtx_factor', sa.Float), + sa.Column('root_gb', sa.Integer), + sa.Column('ephemeral_gb', sa.Integer), + sa.Column('disabled', sa.Boolean), + sa.Column('is_public', sa.Boolean), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'name', 'deleted', + name='uniq_instance_types0name0deleted'), + sa.UniqueConstraint( + 'flavorid', 'deleted', + name='uniq_instance_types0flavorid0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + # TODO(stephenfin): Remove this table since it has been moved to the API DB + op.create_table( + 'instance_type_extra_specs', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'instance_type_id', sa.Integer, sa.ForeignKey('instance_types.id'), + nullable=False), + sa.Column('key', sa.String(length=255)), + sa.Column('value', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index( + 'instance_type_extra_specs_instance_type_id_key_idx', + 'instance_type_id', 'key'), + sa.UniqueConstraint( + 'instance_type_id', 'key', 'deleted', + name='uniq_instance_type_extra_specs0instance_type_id0key0deleted' + ), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + # TODO(stephenfin): Remove this table since it has been moved to the API DB + op.create_table( + 'instance_type_projects', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'instance_type_id', sa.Integer, + sa.ForeignKey( + 'instance_types.id', name='instance_type_projects_ibfk_1'), + nullable=False), + sa.Column('project_id', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'instance_type_id', 'project_id', 'deleted', + name='uniq_instance_type_projects0instance_type_id0project_id' + '0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'instance_actions', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('action', sa.String(length=255)), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='fk_instance_actions_instance_uuid')), + sa.Column('request_id', sa.String(length=255)), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('start_time', sa.DateTime), + sa.Column('finish_time', sa.DateTime), + sa.Column('message', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Index('instance_uuid_idx', 'instance_uuid'), + sa.Index('request_id_idx', 'request_id'), + sa.Index( + 'instance_actions_instance_uuid_updated_at_idx', + 'instance_uuid', 'updated_at'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'instance_actions_events', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('event', sa.String(length=255)), + sa.Column( + 'action_id', sa.Integer, sa.ForeignKey('instance_actions.id')), + sa.Column('start_time', sa.DateTime), + sa.Column('finish_time', sa.DateTime), + sa.Column('result', sa.String(length=255)), + sa.Column('traceback', sa.Text), + sa.Column('deleted', sa.Integer), + sa.Column('host', sa.String(255)), + sa.Column('details', sa.Text), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'instance_extra', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='instance_extra_instance_uuid_fkey'), + nullable=False), + sa.Column('numa_topology', sa.Text, nullable=True), + sa.Column('pci_requests', sa.Text, nullable=True), + sa.Column('flavor', sa.Text, nullable=True), + sa.Column('vcpu_model', sa.Text, nullable=True), + sa.Column('migration_context', sa.Text, nullable=True), + sa.Column('keypairs', sa.Text, nullable=True), + sa.Column('device_metadata', sa.Text, nullable=True), + sa.Column('trusted_certs', sa.Text, nullable=True), + sa.Column('vpmems', sa.Text, nullable=True), + sa.Column('resources', sa.Text, nullable=True), + sa.Index('instance_extra_idx', 'instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'inventories', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('resource_provider_id', sa.Integer, nullable=False), + sa.Column('resource_class_id', sa.Integer, nullable=False), + sa.Column('total', sa.Integer, nullable=False), + sa.Column('reserved', sa.Integer, nullable=False), + sa.Column('min_unit', sa.Integer, nullable=False), + sa.Column('max_unit', sa.Integer, nullable=False), + sa.Column('step_size', sa.Integer, nullable=False), + sa.Column('allocation_ratio', sa.Float, nullable=False), + sa.Index( + 'inventories_resource_provider_id_idx', 'resource_provider_id'), + sa.Index( + 'inventories_resource_class_id_idx', 'resource_class_id'), + sa.Index( + 'inventories_resource_provider_resource_class_idx', + 'resource_provider_id', 'resource_class_id'), + sa.UniqueConstraint( + 'resource_provider_id', 'resource_class_id', + name='uniq_inventories0resource_provider_resource_class'), + mysql_engine='InnoDB', + mysql_charset='latin1', + ) + + op.create_table( + 'key_pairs', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('user_id', sa.String(length=255)), + sa.Column('fingerprint', sa.String(length=255)), + sa.Column('public_key', MediumText()), + sa.Column('deleted', sa.Integer), + sa.Column( + 'type', sa.Enum('ssh', 'x509', name='keypair_types'), + nullable=False, server_default=keypair.KEYPAIR_TYPE_SSH), + sa.UniqueConstraint( + 'user_id', 'name', 'deleted', + name='uniq_key_pairs0user_id0name0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'migrations', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('source_compute', sa.String(length=255)), + sa.Column('dest_compute', sa.String(length=255)), + sa.Column('dest_host', sa.String(length=255)), + sa.Column('status', sa.String(length=255)), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', name='fk_migrations_instance_uuid')), + sa.Column('old_instance_type_id', sa.Integer), + sa.Column('new_instance_type_id', sa.Integer), + sa.Column('source_node', sa.String(length=255)), + sa.Column('dest_node', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.Column( + 'migration_type', + sa.Enum( + 'migration', 'resize', 'live-migration', 'evacuation', + name='migration_type'), + nullable=True), + # NOTE(stephenfin): This was originally added by sqlalchemy-migrate + # which did not generate the constraints + sa.Column( + 'hidden', sa.Boolean(create_constraint=False), default=False), + sa.Column('memory_total', sa.BigInteger, nullable=True), + sa.Column('memory_processed', sa.BigInteger, nullable=True), + sa.Column('memory_remaining', sa.BigInteger, nullable=True), + sa.Column('disk_total', sa.BigInteger, nullable=True), + sa.Column('disk_processed', sa.BigInteger, nullable=True), + sa.Column('disk_remaining', sa.BigInteger, nullable=True), + sa.Column('uuid', sa.String(36)), + # NOTE(stephenfin): This was originally added by sqlalchemy-migrate + # which did not generate the constraints + sa.Column( + 'cross_cell_move', sa.Boolean(create_constraint=False), + default=False), + sa.Column('user_id', sa.String(255), nullable=True), + sa.Column('project_id', sa.String(255), nullable=True), + sa.Index('migrations_uuid', 'uuid', unique=True), + sa.Index( + 'migrations_instance_uuid_and_status_idx', + 'deleted', 'instance_uuid', 'status'), + sa.Index('migrations_updated_at_idx', 'updated_at'), + # mysql-specific index by leftmost 100 chars. (mysql gets angry if the + # index key length is too long.) + sa.Index( + 'migrations_by_host_nodes_and_status_idx', + 'deleted', 'source_compute', 'dest_compute', 'source_node', + 'dest_node', 'status', + mysql_length={ + 'source_compute': 100, + 'dest_compute': 100, + 'source_node': 100, + 'dest_node': 100, + }), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'networks', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('injected', sa.Boolean), + sa.Column('cidr', Inet()), + sa.Column('netmask', InetSmall()), + sa.Column('bridge', sa.String(length=255)), + sa.Column('gateway', InetSmall()), + sa.Column('broadcast', InetSmall()), + sa.Column('dns1', InetSmall()), + sa.Column('vlan', sa.Integer), + sa.Column('vpn_public_address', InetSmall()), + sa.Column('vpn_public_port', sa.Integer), + sa.Column('vpn_private_address', InetSmall()), + sa.Column('dhcp_start', InetSmall()), + sa.Column('project_id', sa.String(length=255)), + sa.Column('host', sa.String(length=255)), + sa.Column('cidr_v6', Inet()), + sa.Column('gateway_v6', InetSmall()), + sa.Column('label', sa.String(length=255)), + sa.Column('netmask_v6', InetSmall()), + sa.Column('bridge_interface', sa.String(length=255)), + sa.Column('multi_host', sa.Boolean), + sa.Column('dns2', InetSmall()), + sa.Column('uuid', sa.String(length=36)), + sa.Column('priority', sa.Integer), + sa.Column('rxtx_base', sa.Integer), + sa.Column('deleted', sa.Integer), + sa.Column('mtu', sa.Integer), + sa.Column('dhcp_server', types.IPAddress), + # NOTE(stephenfin): These were originally added by sqlalchemy-migrate + # which did not generate the constraints + sa.Column( + 'enable_dhcp', sa.Boolean(create_constraint=False), default=True), + sa.Column( + 'share_address', sa.Boolean(create_constraint=False), + default=False), + sa.Index('networks_host_idx', 'host'), + sa.Index('networks_cidr_v6_idx', 'cidr_v6'), + sa.Index('networks_bridge_deleted_idx', 'bridge', 'deleted'), + sa.Index('networks_project_id_deleted_idx', 'project_id', 'deleted'), + sa.Index( + 'networks_uuid_project_id_deleted_idx', + 'uuid', 'project_id', 'deleted'), + sa.Index('networks_vlan_deleted_idx', 'vlan', 'deleted'), + sa.UniqueConstraint( + 'vlan', 'deleted', name='uniq_networks0vlan0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'pci_devices', + sa.Column('created_at', sa.DateTime(timezone=False)), + sa.Column('updated_at', sa.DateTime(timezone=False)), + sa.Column('deleted_at', sa.DateTime(timezone=False)), + sa.Column('deleted', sa.Integer, default=0, nullable=True), + sa.Column('id', sa.Integer, primary_key=True), + sa.Column( + 'compute_node_id', sa.Integer, + sa.ForeignKey( + 'compute_nodes.id', name='pci_devices_compute_node_id_fkey'), + nullable=False), + sa.Column('address', sa.String(12), nullable=False), + sa.Column('product_id', sa.String(4), nullable=False), + sa.Column('vendor_id', sa.String(4), nullable=False), + sa.Column('dev_type', sa.String(8), nullable=False), + sa.Column('dev_id', sa.String(255)), + sa.Column('label', sa.String(255), nullable=False), + sa.Column('status', sa.String(36), nullable=False), + sa.Column('extra_info', sa.Text, nullable=True), + sa.Column('instance_uuid', sa.String(36), nullable=True), + sa.Column('request_id', sa.String(36), nullable=True), + sa.Column('numa_node', sa.Integer, default=None), + sa.Column('parent_addr', sa.String(12), nullable=True), + sa.Column('uuid', sa.String(36)), + sa.Index( + 'ix_pci_devices_instance_uuid_deleted', + 'instance_uuid', 'deleted'), + sa.Index( + 'ix_pci_devices_compute_node_id_deleted', + 'compute_node_id', 'deleted'), + sa.Index( + 'ix_pci_devices_compute_node_id_parent_addr_deleted', + 'compute_node_id', 'parent_addr', 'deleted'), + sa.UniqueConstraint( + 'compute_node_id', 'address', 'deleted', + name='uniq_pci_devices0compute_node_id0address0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8') + + op.create_table( + 'provider_fw_rules', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('protocol', sa.String(length=5)), + sa.Column('from_port', sa.Integer), + sa.Column('to_port', sa.Integer), + sa.Column('cidr', Inet()), + sa.Column('deleted', sa.Integer), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'quota_classes', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('class_name', sa.String(length=255)), + sa.Column('resource', sa.String(length=255)), + sa.Column('hard_limit', sa.Integer), + sa.Column('deleted', sa.Integer), + sa.Index('ix_quota_classes_class_name', 'class_name'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'quota_usages', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('project_id', sa.String(length=255)), + sa.Column('resource', sa.String(length=255), nullable=False), + sa.Column('in_use', sa.Integer, nullable=False), + sa.Column('reserved', sa.Integer, nullable=False), + sa.Column('until_refresh', sa.Integer), + sa.Column('deleted', sa.Integer), + sa.Column('user_id', sa.String(length=255)), + sa.Index('ix_quota_usages_project_id', 'project_id'), + sa.Index('ix_quota_usages_user_id_deleted', 'user_id', 'deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'quotas', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('project_id', sa.String(length=255)), + sa.Column('resource', sa.String(length=255), nullable=False), + sa.Column('hard_limit', sa.Integer), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'project_id', 'resource', 'deleted', + name='uniq_quotas0project_id0resource0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'project_user_quotas', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('user_id', sa.String(length=255), nullable=False), + sa.Column('project_id', sa.String(length=255), nullable=False), + sa.Column('resource', sa.String(length=255), nullable=False), + sa.Column('hard_limit', sa.Integer, nullable=True), + sa.Index( + 'project_user_quotas_project_id_deleted_idx', + 'project_id', 'deleted'), + sa.Index( + 'project_user_quotas_user_id_deleted_idx', + 'user_id', 'deleted'), + sa.UniqueConstraint( + 'user_id', 'project_id', 'resource', 'deleted', + name='uniq_project_user_quotas0user_id0project_id0resource0' + 'deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'reservations', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column( + 'usage_id', sa.Integer, + sa.ForeignKey('quota_usages.id', name='reservations_ibfk_1'), + nullable=False), + sa.Column('project_id', sa.String(length=255)), + sa.Column('resource', sa.String(length=255)), + sa.Column('delta', sa.Integer, nullable=False), + sa.Column('expire', sa.DateTime), + sa.Column('deleted', sa.Integer), + sa.Column('user_id', sa.String(length=255)), + sa.Index('ix_reservations_project_id', 'project_id'), + sa.Index('ix_reservations_user_id_deleted', 'user_id', 'deleted'), + sa.Index('reservations_uuid_idx', 'uuid'), + sa.Index('reservations_deleted_expire_idx', 'deleted', 'expire'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'resource_providers', + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(36), nullable=False), + sa.Column('name', sa.Unicode(200), nullable=True), + sa.Column('generation', sa.Integer, default=0), + sa.Column('can_host', sa.Integer, default=0), + sa.UniqueConstraint('uuid', name='uniq_resource_providers0uuid'), + sa.UniqueConstraint('name', name='uniq_resource_providers0name'), + sa.Index('resource_providers_name_idx', 'name'), + sa.Index('resource_providers_uuid_idx', 'uuid'), + mysql_engine='InnoDB', + mysql_charset='latin1', + ) + + op.create_table( + 'resource_provider_aggregates', + sa.Column( + 'resource_provider_id', sa.Integer, primary_key=True, + nullable=False), + sa.Column( + 'aggregate_id', sa.Integer, primary_key=True, nullable=False), + sa.Index( + 'resource_provider_aggregates_aggregate_id_idx', 'aggregate_id'), + mysql_engine='InnoDB', + mysql_charset='latin1', + ) + + op.create_table( + 's3_images', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('deleted', sa.Integer), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'security_groups', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('name', sa.String(length=255)), + sa.Column('description', sa.String(length=255)), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('deleted', sa.Integer), + sa.UniqueConstraint( + 'project_id', 'name', 'deleted', + name='uniq_security_groups0project_id0name0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'security_group_instance_association', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'security_group_id', sa.Integer, + sa.ForeignKey( + 'security_groups.id', + name='security_group_instance_association_ibfk_1'), + ), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', + name='security_group_instance_association_instance_uuid_fkey'), + ), + sa.Column('deleted', sa.Integer), + sa.Index( + 'security_group_instance_association_instance_uuid_idx', + 'instance_uuid'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'security_group_rules', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column( + 'parent_group_id', sa.Integer, + sa.ForeignKey('security_groups.id')), + sa.Column('protocol', sa.String(length=255)), + sa.Column('from_port', sa.Integer), + sa.Column('to_port', sa.Integer), + sa.Column('cidr', Inet()), + sa.Column('group_id', sa.Integer, sa.ForeignKey('security_groups.id')), + sa.Column('deleted', sa.Integer), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'security_group_default_rules', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('deleted', sa.Integer, default=0), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('protocol', sa.String(length=5)), + sa.Column('from_port', sa.Integer), + sa.Column('to_port', sa.Integer), + sa.Column('cidr', Inet()), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'services', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('host', sa.String(length=255)), + sa.Column('binary', sa.String(length=255)), + sa.Column('topic', sa.String(length=255)), + sa.Column('report_count', sa.Integer, nullable=False), + sa.Column('disabled', sa.Boolean), + sa.Column('deleted', sa.Integer), + sa.Column('disabled_reason', sa.String(length=255)), + sa.Column('last_seen_up', sa.DateTime, nullable=True), + # NOTE(stephenfin): This was originally added by sqlalchemy-migrate + # which did not generate the constraints + sa.Column( + 'forced_down', sa.Boolean(create_constraint=False), default=False), + sa.Column('version', sa.Integer, default=0), + sa.Column('uuid', sa.String(36), nullable=True), + sa.Index('services_uuid_idx', 'uuid', unique=True), + sa.UniqueConstraint( + 'host', 'topic', 'deleted', + name='uniq_services0host0topic0deleted'), + sa.UniqueConstraint( + 'host', 'binary', 'deleted', + name='uniq_services0host0binary0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'snapshot_id_mappings', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('deleted', sa.Integer), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'snapshots', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column( + 'id', sa.String(length=36), primary_key=True, nullable=False), + sa.Column('volume_id', sa.String(length=36), nullable=False), + sa.Column('user_id', sa.String(length=255)), + sa.Column('project_id', sa.String(length=255)), + sa.Column('status', sa.String(length=255)), + sa.Column('progress', sa.String(length=255)), + sa.Column('volume_size', sa.Integer), + sa.Column('scheduled_at', sa.DateTime), + sa.Column('display_name', sa.String(length=255)), + sa.Column('display_description', sa.String(length=255)), + sa.Column('deleted', sa.String(length=36)), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'tags', + sa.Column( + 'resource_id', sa.String(36), primary_key=True, nullable=False), + sa.Column('tag', sa.Unicode(80), primary_key=True, nullable=False), + sa.Index('tags_tag_idx', 'tag'), + mysql_engine='InnoDB', + mysql_charset='utf8', + ) + + op.create_table( + 'task_log', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('task_name', sa.String(length=255), nullable=False), + sa.Column('state', sa.String(length=255), nullable=False), + sa.Column('host', sa.String(length=255), nullable=False), + sa.Column('period_beginning', sa.DateTime, nullable=False), + sa.Column('period_ending', sa.DateTime, nullable=False), + sa.Column('message', sa.String(length=255), nullable=False), + sa.Column('task_items', sa.Integer), + sa.Column('errors', sa.Integer), + sa.Column('deleted', sa.Integer), + sa.Index('ix_task_log_period_beginning', 'period_beginning'), + sa.Index('ix_task_log_host', 'host'), + sa.Index('ix_task_log_period_ending', 'period_ending'), + sa.UniqueConstraint( + 'task_name', 'host', 'period_beginning', 'period_ending', + name='uniq_task_log0task_name0host0period_beginning0period_ending', + ), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'virtual_interfaces', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('address', sa.String(length=255)), + sa.Column('network_id', sa.Integer), + sa.Column('uuid', sa.String(length=36)), + sa.Column( + 'instance_uuid', sa.String(length=36), + sa.ForeignKey( + 'instances.uuid', + name='virtual_interfaces_instance_uuid_fkey'), + nullable=True), + sa.Column('deleted', sa.Integer), + sa.Column('tag', sa.String(255)), + sa.Index('virtual_interfaces_instance_uuid_fkey', 'instance_uuid'), + sa.Index('virtual_interfaces_network_id_idx', 'network_id'), + sa.Index('virtual_interfaces_uuid_idx', 'uuid'), + sa.UniqueConstraint( + 'address', 'deleted', + name='uniq_virtual_interfaces0address0deleted'), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'volume_id_mappings', + sa.Column('created_at', sa.DateTime), + sa.Column('updated_at', sa.DateTime), + sa.Column('deleted_at', sa.DateTime), + sa.Column('id', sa.Integer, primary_key=True, nullable=False), + sa.Column('uuid', sa.String(length=36), nullable=False), + sa.Column('deleted', sa.Integer), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + op.create_table( + 'volume_usage_cache', + sa.Column('created_at', sa.DateTime(timezone=False)), + sa.Column('updated_at', sa.DateTime(timezone=False)), + sa.Column('deleted_at', sa.DateTime(timezone=False)), + sa.Column('id', sa.Integer(), primary_key=True, nullable=False), + sa.Column('volume_id', sa.String(36), nullable=False), + sa.Column('tot_last_refreshed', sa.DateTime(timezone=False)), + sa.Column('tot_reads', sa.BigInteger(), default=0), + sa.Column('tot_read_bytes', sa.BigInteger(), default=0), + sa.Column('tot_writes', sa.BigInteger(), default=0), + sa.Column('tot_write_bytes', sa.BigInteger(), default=0), + sa.Column('curr_last_refreshed', sa.DateTime(timezone=False)), + sa.Column('curr_reads', sa.BigInteger(), default=0), + sa.Column('curr_read_bytes', sa.BigInteger(), default=0), + sa.Column('curr_writes', sa.BigInteger(), default=0), + sa.Column('curr_write_bytes', sa.BigInteger(), default=0), + sa.Column('deleted', sa.Integer), + sa.Column('instance_uuid', sa.String(length=36)), + sa.Column('project_id', sa.String(length=36)), + sa.Column('user_id', sa.String(length=64)), + sa.Column('availability_zone', sa.String(length=255)), + mysql_engine='InnoDB', + mysql_charset='utf8' + ) + + # MySQL specific indexes + if bind.engine.name == 'mysql': + # NOTE(stephenfin): For some reason, we have to put this within the if + # statement to avoid it being evaluated for the sqlite case. Even + # though we don't call create except in the MySQL case... Failure to do + # this will result in the following ugly error message: + # + # sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such + # index: instance_type_id + # + # Yeah, I don't get it either... + op.create_index( + 'instance_type_id', + 'instance_type_projects', + ['instance_type_id'], + ), + op.create_index('usage_id', 'reservations', ['usage_id']), + op.create_index( + 'security_group_id', + 'security_group_instance_association', + ['security_group_id'], + ), + + if bind.engine.name == 'mysql': + # Set default DB charset to UTF8. + op.execute( + 'ALTER DATABASE `%s` DEFAULT CHARACTER SET utf8' % + bind.engine.url.database) + + # NOTE(cdent): The resource_providers table is defined as latin1 to be + # more efficient. Now we need the name column to be UTF8. We modify it + # here otherwise the declarative handling in sqlalchemy gets confused. + op.execute( + 'ALTER TABLE resource_providers MODIFY name ' + 'VARCHAR(200) CHARACTER SET utf8') + + _create_shadow_tables(bind) + + # TODO(stephenfin): Fix these various bugs in a follow-up + + # 298_mysql_extra_specs_binary_collation; we should update the shadow table + # also + + if bind.engine.name == 'mysql': + # Use binary collation for extra specs table + op.execute( + 'ALTER TABLE instance_type_extra_specs ' + 'CONVERT TO CHARACTER SET utf8 ' + 'COLLATE utf8_bin' + ) + + +def downgrade(): + pass