Added alembic environment
Added alembic, and created an initial database revision. For the sake of testing with real databases, tools/test-setup.sh is added so that CI can start up a mysql and/or postgresql database on which to test migrations. The OpportunisticDBMixin from oslo_db was originally used for this, but because of the way placement configures the database this proved hard to debug and get correct, so we've used something that leverages the available oslo_db tools, but with more visibility. Because these tests mix sqlite, mysql and postgresql settings in the potentially the same process we need a way to insure that global settings for databases do not leak into other tests. This is done with a reset() on the placement db fixture, called by the msyql and postgresql tests before and after they run. We also need careful management of that cleanup when these tests are skipped (because db server or database is not there). Those tests will confirm that the models match the migrations so we also need to remove model files that no longer matter. Since we no longer need to distinguish among multiple database files, we can simplify the naming of these files. Co-Authored-By: Chris Dent <cdent@anticdent.org> Change-Id: I51ed1e4e7dbb76a3eab23af7d0d106f716059112
This commit is contained in:
parent
6bb6c0bbdd
commit
c8ab9ff906
76
placement/db/sqlalchemy/alembic.ini
Normal file
76
placement/db/sqlalchemy/alembic.ini
Normal file
@ -0,0 +1,76 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = %(here)s/alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# 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 alembic/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path
|
||||
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
# NOTE: this next line is commented out because it is set in
|
||||
# CONF.placement_database.connection
|
||||
#sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||
|
||||
|
||||
# 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
|
93
placement/db/sqlalchemy/alembic/env.py
Normal file
93
placement/db/sqlalchemy/alembic/env.py
Normal file
@ -0,0 +1,93 @@
|
||||
# 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 __future__ import with_statement
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
|
||||
from placement import conf
|
||||
from placement.db.sqlalchemy import models
|
||||
from placement import db_api as placement_db
|
||||
|
||||
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
# 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 = models.BASE.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
|
||||
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 = CONF.placement_database.connection
|
||||
context.configure(
|
||||
url=url, target_metadata=target_metadata, literal_binds=True)
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
# If CONF and the database are not already configured, set them up. This
|
||||
# can happen when using the alembic command line tool.
|
||||
if not CONF.placement_database.connection:
|
||||
CONF([], project="placement", default_config_files=None)
|
||||
placement_db.configure(CONF)
|
||||
connectable = placement_db.get_placement_engine()
|
||||
|
||||
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()
|
24
placement/db/sqlalchemy/alembic/script.py.mako
Normal file
24
placement/db/sqlalchemy/alembic/script.py.mako
Normal file
@ -0,0 +1,24 @@
|
||||
"""${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"}
|
188
placement/db/sqlalchemy/alembic/versions/b4ed3a175331_initial.py
Normal file
188
placement/db/sqlalchemy/alembic/versions/b4ed3a175331_initial.py
Normal file
@ -0,0 +1,188 @@
|
||||
# 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 ID: b4ed3a175331
|
||||
Revises: 158782c7f38c
|
||||
Create Date: 2018-10-19 18:27:55.950383
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b4ed3a175331'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('allocations',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('resource_provider_id', sa.Integer(), nullable=False),
|
||||
sa.Column('consumer_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('resource_class_id', sa.Integer(), nullable=False),
|
||||
sa.Column('used', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
)
|
||||
op.create_index('allocations_resource_provider_class_used_idx',
|
||||
'allocations', ['resource_provider_id', 'resource_class_id',
|
||||
'used'], unique=False)
|
||||
op.create_index('allocations_resource_class_id_idx', 'allocations',
|
||||
['resource_class_id'], unique=False)
|
||||
op.create_index('allocations_consumer_id_idx', 'allocations',
|
||||
['consumer_id'], unique=False)
|
||||
|
||||
op.create_table('consumers',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||
sa.Column('project_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('generation', sa.Integer(), server_default=sa.text('0'),
|
||||
nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_consumers0uuid'),
|
||||
)
|
||||
op.create_index('consumers_project_id_user_id_uuid_idx', 'consumers',
|
||||
['project_id', 'user_id', 'uuid'], unique=False)
|
||||
op.create_index('consumers_project_id_uuid_idx', 'consumers',
|
||||
['project_id', 'uuid'], unique=False)
|
||||
|
||||
op.create_table('inventories',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), 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.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('resource_provider_id', 'resource_class_id',
|
||||
name='uniq_inventories0resource_provider_resource_class'),
|
||||
)
|
||||
op.create_index('inventories_resource_class_id_idx', 'inventories',
|
||||
['resource_class_id'], unique=False)
|
||||
op.create_index('inventories_resource_provider_id_idx', 'inventories',
|
||||
['resource_provider_id'], unique=False)
|
||||
op.create_index('inventories_resource_provider_resource_class_idx',
|
||||
'inventories', ['resource_provider_id', 'resource_class_id'],
|
||||
unique=False)
|
||||
|
||||
op.create_table('placement_aggregates',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('uuid', sa.String(length=36), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_placement_aggregates0uuid')
|
||||
)
|
||||
op.create_index(op.f('ix_placement_aggregates_uuid'),
|
||||
'placement_aggregates', ['uuid'], unique=False)
|
||||
|
||||
op.create_table('projects',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('external_id', sa.String(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('external_id',
|
||||
name='uniq_projects0external_id'),
|
||||
)
|
||||
|
||||
op.create_table('resource_classes',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name', name='uniq_resource_classes0name'),
|
||||
)
|
||||
|
||||
op.create_table('resource_provider_aggregates',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('resource_provider_id', sa.Integer(), nullable=False),
|
||||
sa.Column('aggregate_id', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('resource_provider_id', 'aggregate_id'),
|
||||
)
|
||||
op.create_index('resource_provider_aggregates_aggregate_id_idx',
|
||||
'resource_provider_aggregates', ['aggregate_id'], unique=False)
|
||||
|
||||
op.create_table('resource_providers',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.Unicode(length=200), nullable=True),
|
||||
sa.Column('generation', sa.Integer(), nullable=True),
|
||||
sa.Column('root_provider_id', sa.Integer(), nullable=True),
|
||||
sa.Column('parent_provider_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['parent_provider_id'],
|
||||
['resource_providers.id']),
|
||||
sa.ForeignKeyConstraint(['root_provider_id'],
|
||||
['resource_providers.id']),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name', name='uniq_resource_providers0name'),
|
||||
sa.UniqueConstraint('uuid', name='uniq_resource_providers0uuid'),
|
||||
)
|
||||
op.create_index('resource_providers_name_idx', 'resource_providers',
|
||||
['name'], unique=False)
|
||||
op.create_index('resource_providers_parent_provider_id_idx',
|
||||
'resource_providers', ['parent_provider_id'], unique=False)
|
||||
op.create_index('resource_providers_root_provider_id_idx',
|
||||
'resource_providers', ['root_provider_id'], unique=False)
|
||||
op.create_index('resource_providers_uuid_idx', 'resource_providers',
|
||||
['uuid'], unique=False)
|
||||
|
||||
op.create_table('traits',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('name', sa.Unicode(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name', name='uniq_traits0name'),
|
||||
)
|
||||
|
||||
op.create_table('users',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('external_id', sa.String(length=255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('external_id', name='uniq_users0external_id'),
|
||||
)
|
||||
|
||||
op.create_table('resource_provider_traits',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('trait_id', sa.Integer(), nullable=False),
|
||||
sa.Column('resource_provider_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['resource_provider_id'],
|
||||
['resource_providers.id'], ),
|
||||
sa.ForeignKeyConstraint(['trait_id'], ['traits.id'], ),
|
||||
sa.PrimaryKeyConstraint('trait_id', 'resource_provider_id'),
|
||||
)
|
||||
op.create_index('resource_provider_traits_resource_provider_trait_idx',
|
||||
'resource_provider_traits', ['resource_provider_id', 'trait_id'],
|
||||
unique=False)
|
@ -1,658 +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 oslo_db.sqlalchemy import models
|
||||
from oslo_log import log as logging
|
||||
from sqlalchemy import Boolean
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy.dialects.mysql import MEDIUMTEXT
|
||||
from sqlalchemy import Enum
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Float
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import Index
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy.orm import backref
|
||||
from sqlalchemy import schema
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy import Text
|
||||
from sqlalchemy import Unicode
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def MediumText():
|
||||
return Text().with_variant(MEDIUMTEXT(), 'mysql')
|
||||
|
||||
|
||||
class _NovaAPIBase(models.ModelBase, models.TimestampMixin):
|
||||
pass
|
||||
|
||||
|
||||
API_BASE = declarative_base(cls=_NovaAPIBase)
|
||||
|
||||
|
||||
class AggregateHost(API_BASE):
|
||||
"""Represents a host that is member of an aggregate."""
|
||||
__tablename__ = 'aggregate_hosts'
|
||||
__table_args__ = (schema.UniqueConstraint(
|
||||
"host", "aggregate_id",
|
||||
name="uniq_aggregate_hosts0host0aggregate_id"
|
||||
),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
host = Column(String(255))
|
||||
aggregate_id = Column(Integer, ForeignKey('aggregates.id'), nullable=False)
|
||||
|
||||
|
||||
class AggregateMetadata(API_BASE):
|
||||
"""Represents a metadata key/value pair for an aggregate."""
|
||||
__tablename__ = 'aggregate_metadata'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("aggregate_id", "key",
|
||||
name="uniq_aggregate_metadata0aggregate_id0key"
|
||||
),
|
||||
Index('aggregate_metadata_key_idx', 'key'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255), nullable=False)
|
||||
value = Column(String(255), nullable=False)
|
||||
aggregate_id = Column(Integer, ForeignKey('aggregates.id'), nullable=False)
|
||||
|
||||
|
||||
class Aggregate(API_BASE):
|
||||
"""Represents a cluster of hosts that exists in this zone."""
|
||||
__tablename__ = 'aggregates'
|
||||
__table_args__ = (Index('aggregate_uuid_idx', 'uuid'),
|
||||
schema.UniqueConstraint(
|
||||
"name", name="uniq_aggregate0name")
|
||||
)
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
uuid = Column(String(36))
|
||||
name = Column(String(255))
|
||||
_hosts = orm.relationship(AggregateHost,
|
||||
primaryjoin='Aggregate.id == AggregateHost.aggregate_id',
|
||||
cascade='delete')
|
||||
_metadata = orm.relationship(AggregateMetadata,
|
||||
primaryjoin='Aggregate.id == AggregateMetadata.aggregate_id',
|
||||
cascade='delete')
|
||||
|
||||
@property
|
||||
def _extra_keys(self):
|
||||
return ['hosts', 'metadetails', 'availability_zone']
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return [h.host for h in self._hosts]
|
||||
|
||||
@property
|
||||
def metadetails(self):
|
||||
return {m.key: m.value for m in self._metadata}
|
||||
|
||||
@property
|
||||
def availability_zone(self):
|
||||
if 'availability_zone' not in self.metadetails:
|
||||
return None
|
||||
return self.metadetails['availability_zone']
|
||||
|
||||
|
||||
class CellMapping(API_BASE):
|
||||
"""Contains information on communicating with a cell"""
|
||||
__tablename__ = 'cell_mappings'
|
||||
__table_args__ = (Index('uuid_idx', 'uuid'),
|
||||
schema.UniqueConstraint('uuid',
|
||||
name='uniq_cell_mappings0uuid'))
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
name = Column(String(255))
|
||||
transport_url = Column(Text())
|
||||
database_connection = Column(Text())
|
||||
disabled = Column(Boolean, default=False)
|
||||
host_mapping = orm.relationship('HostMapping',
|
||||
backref=backref('cell_mapping', uselist=False),
|
||||
foreign_keys=id,
|
||||
primaryjoin=(
|
||||
'CellMapping.id == HostMapping.cell_id'))
|
||||
|
||||
|
||||
class InstanceMapping(API_BASE):
|
||||
"""Contains the mapping of an instance to which cell it is in"""
|
||||
__tablename__ = 'instance_mappings'
|
||||
__table_args__ = (Index('project_id_idx', 'project_id'),
|
||||
Index('instance_uuid_idx', 'instance_uuid'),
|
||||
schema.UniqueConstraint('instance_uuid',
|
||||
name='uniq_instance_mappings0instance_uuid'))
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
instance_uuid = Column(String(36), nullable=False)
|
||||
cell_id = Column(Integer, ForeignKey('cell_mappings.id'),
|
||||
nullable=True)
|
||||
project_id = Column(String(255), nullable=False)
|
||||
queued_for_delete = Column(Boolean)
|
||||
cell_mapping = orm.relationship('CellMapping',
|
||||
backref=backref('instance_mapping', uselist=False),
|
||||
foreign_keys=cell_id,
|
||||
primaryjoin=('InstanceMapping.cell_id == CellMapping.id'))
|
||||
|
||||
|
||||
class HostMapping(API_BASE):
|
||||
"""Contains mapping of a compute host to which cell it is in"""
|
||||
__tablename__ = "host_mappings"
|
||||
__table_args__ = (Index('host_idx', 'host'),
|
||||
schema.UniqueConstraint('host',
|
||||
name='uniq_host_mappings0host'))
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
cell_id = Column(Integer, ForeignKey('cell_mappings.id'),
|
||||
nullable=False)
|
||||
host = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class RequestSpec(API_BASE):
|
||||
"""Represents the information passed to the scheduler."""
|
||||
|
||||
__tablename__ = 'request_specs'
|
||||
__table_args__ = (
|
||||
Index('request_spec_instance_uuid_idx', 'instance_uuid'),
|
||||
schema.UniqueConstraint('instance_uuid',
|
||||
name='uniq_request_specs0instance_uuid'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
instance_uuid = Column(String(36), nullable=False)
|
||||
spec = Column(MediumText(), nullable=False)
|
||||
|
||||
|
||||
class Flavors(API_BASE):
|
||||
"""Represents possible flavors for instances"""
|
||||
__tablename__ = 'flavors'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("flavorid", name="uniq_flavors0flavorid"),
|
||||
schema.UniqueConstraint("name", name="uniq_flavors0name"))
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
memory_mb = Column(Integer, nullable=False)
|
||||
vcpus = Column(Integer, nullable=False)
|
||||
root_gb = Column(Integer)
|
||||
ephemeral_gb = Column(Integer)
|
||||
flavorid = Column(String(255), nullable=False)
|
||||
swap = Column(Integer, nullable=False, default=0)
|
||||
rxtx_factor = Column(Float, default=1)
|
||||
vcpu_weight = Column(Integer)
|
||||
disabled = Column(Boolean, default=False)
|
||||
is_public = Column(Boolean, default=True)
|
||||
description = Column(Text)
|
||||
|
||||
|
||||
class FlavorExtraSpecs(API_BASE):
|
||||
"""Represents additional specs as key/value pairs for a flavor"""
|
||||
__tablename__ = 'flavor_extra_specs'
|
||||
__table_args__ = (
|
||||
Index('flavor_extra_specs_flavor_id_key_idx', 'flavor_id', 'key'),
|
||||
schema.UniqueConstraint('flavor_id', 'key',
|
||||
name='uniq_flavor_extra_specs0flavor_id0key'),
|
||||
{'mysql_collate': 'utf8_bin'},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255), nullable=False)
|
||||
value = Column(String(255))
|
||||
flavor_id = Column(Integer, ForeignKey('flavors.id'), nullable=False)
|
||||
flavor = orm.relationship(Flavors, backref='extra_specs',
|
||||
foreign_keys=flavor_id,
|
||||
primaryjoin=(
|
||||
'FlavorExtraSpecs.flavor_id == Flavors.id'))
|
||||
|
||||
|
||||
class FlavorProjects(API_BASE):
|
||||
"""Represents projects associated with flavors"""
|
||||
__tablename__ = 'flavor_projects'
|
||||
__table_args__ = (schema.UniqueConstraint('flavor_id', 'project_id',
|
||||
name='uniq_flavor_projects0flavor_id0project_id'),)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
flavor_id = Column(Integer, ForeignKey('flavors.id'), nullable=False)
|
||||
project_id = Column(String(255), nullable=False)
|
||||
flavor = orm.relationship(Flavors, backref='projects',
|
||||
foreign_keys=flavor_id,
|
||||
primaryjoin=(
|
||||
'FlavorProjects.flavor_id == Flavors.id'))
|
||||
|
||||
|
||||
class BuildRequest(API_BASE):
|
||||
"""Represents the information passed to the scheduler."""
|
||||
|
||||
__tablename__ = 'build_requests'
|
||||
__table_args__ = (
|
||||
Index('build_requests_instance_uuid_idx', 'instance_uuid'),
|
||||
Index('build_requests_project_id_idx', 'project_id'),
|
||||
schema.UniqueConstraint('instance_uuid',
|
||||
name='uniq_build_requests0instance_uuid'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
instance_uuid = Column(String(36))
|
||||
project_id = Column(String(255), nullable=False)
|
||||
instance = Column(MediumText())
|
||||
block_device_mappings = Column(MediumText())
|
||||
tags = Column(Text())
|
||||
# TODO(alaski): Drop these from the db in Ocata
|
||||
# columns_to_drop = ['request_spec_id', 'user_id', 'display_name',
|
||||
# 'instance_metadata', 'progress', 'vm_state', 'task_state',
|
||||
# 'image_ref', 'access_ip_v4', 'access_ip_v6', 'info_cache',
|
||||
# 'security_groups', 'config_drive', 'key_name', 'locked_by',
|
||||
# 'reservation_id', 'launch_index', 'hostname', 'kernel_id',
|
||||
# 'ramdisk_id', 'root_device_name', 'user_data']
|
||||
|
||||
|
||||
class KeyPair(API_BASE):
|
||||
"""Represents a public key pair for ssh / WinRM."""
|
||||
__tablename__ = 'key_pairs'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("user_id", "name",
|
||||
name="uniq_key_pairs0user_id0name"),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
name = Column(String(255), nullable=False)
|
||||
|
||||
user_id = Column(String(255), nullable=False)
|
||||
|
||||
fingerprint = Column(String(255))
|
||||
public_key = Column(Text())
|
||||
type = Column(Enum('ssh', 'x509', name='keypair_types'),
|
||||
nullable=False, server_default='ssh')
|
||||
|
||||
|
||||
class ResourceClass(API_BASE):
|
||||
"""Represents the type of resource for an inventory or allocation."""
|
||||
__tablename__ = 'resource_classes'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("name", name="uniq_resource_classes0name"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
name = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class ResourceProvider(API_BASE):
|
||||
"""Represents a mapping to a providers of resources."""
|
||||
|
||||
__tablename__ = "resource_providers"
|
||||
__table_args__ = (
|
||||
Index('resource_providers_uuid_idx', 'uuid'),
|
||||
schema.UniqueConstraint('uuid',
|
||||
name='uniq_resource_providers0uuid'),
|
||||
Index('resource_providers_name_idx', 'name'),
|
||||
Index('resource_providers_root_provider_id_idx',
|
||||
'root_provider_id'),
|
||||
Index('resource_providers_parent_provider_id_idx',
|
||||
'parent_provider_id'),
|
||||
schema.UniqueConstraint('name',
|
||||
name='uniq_resource_providers0name')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
name = Column(Unicode(200), nullable=True)
|
||||
generation = Column(Integer, default=0)
|
||||
# Represents the root of the "tree" that the provider belongs to
|
||||
root_provider_id = Column(Integer, ForeignKey('resource_providers.id'),
|
||||
nullable=True)
|
||||
# The immediate parent provider of this provider, or NULL if there is no
|
||||
# parent. If parent_provider_id == NULL then root_provider_id == id
|
||||
parent_provider_id = Column(Integer, ForeignKey('resource_providers.id'),
|
||||
nullable=True)
|
||||
|
||||
|
||||
class Inventory(API_BASE):
|
||||
"""Represents a quantity of available resource."""
|
||||
|
||||
__tablename__ = "inventories"
|
||||
__table_args__ = (
|
||||
Index('inventories_resource_provider_id_idx',
|
||||
'resource_provider_id'),
|
||||
Index('inventories_resource_class_id_idx',
|
||||
'resource_class_id'),
|
||||
Index('inventories_resource_provider_resource_class_idx',
|
||||
'resource_provider_id', 'resource_class_id'),
|
||||
schema.UniqueConstraint('resource_provider_id', 'resource_class_id',
|
||||
name='uniq_inventories0resource_provider_resource_class')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
resource_provider_id = Column(Integer, nullable=False)
|
||||
resource_class_id = Column(Integer, nullable=False)
|
||||
total = Column(Integer, nullable=False)
|
||||
reserved = Column(Integer, nullable=False)
|
||||
min_unit = Column(Integer, nullable=False)
|
||||
max_unit = Column(Integer, nullable=False)
|
||||
step_size = Column(Integer, nullable=False)
|
||||
allocation_ratio = Column(Float, nullable=False)
|
||||
resource_provider = orm.relationship(
|
||||
"ResourceProvider",
|
||||
primaryjoin=('Inventory.resource_provider_id == '
|
||||
'ResourceProvider.id'),
|
||||
foreign_keys=resource_provider_id)
|
||||
|
||||
|
||||
class Allocation(API_BASE):
|
||||
"""A use of inventory."""
|
||||
|
||||
__tablename__ = "allocations"
|
||||
__table_args__ = (
|
||||
Index('allocations_resource_provider_class_used_idx',
|
||||
'resource_provider_id', 'resource_class_id',
|
||||
'used'),
|
||||
Index('allocations_resource_class_id_idx',
|
||||
'resource_class_id'),
|
||||
Index('allocations_consumer_id_idx', 'consumer_id')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
resource_provider_id = Column(Integer, nullable=False)
|
||||
consumer_id = Column(String(36), nullable=False)
|
||||
resource_class_id = Column(Integer, nullable=False)
|
||||
used = Column(Integer, nullable=False)
|
||||
resource_provider = orm.relationship(
|
||||
"ResourceProvider",
|
||||
primaryjoin=('Allocation.resource_provider_id == '
|
||||
'ResourceProvider.id'),
|
||||
foreign_keys=resource_provider_id)
|
||||
|
||||
|
||||
class ResourceProviderAggregate(API_BASE):
|
||||
"""Associate a resource provider with an aggregate."""
|
||||
|
||||
__tablename__ = 'resource_provider_aggregates'
|
||||
__table_args__ = (
|
||||
Index('resource_provider_aggregates_aggregate_id_idx',
|
||||
'aggregate_id'),
|
||||
)
|
||||
|
||||
resource_provider_id = Column(Integer, primary_key=True, nullable=False)
|
||||
aggregate_id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class PlacementAggregate(API_BASE):
|
||||
"""A grouping of resource providers."""
|
||||
__tablename__ = 'placement_aggregates'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("uuid", name="uniq_placement_aggregates0uuid"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
uuid = Column(String(36), index=True)
|
||||
|
||||
|
||||
class InstanceGroupMember(API_BASE):
|
||||
"""Represents the members for an instance group."""
|
||||
__tablename__ = 'instance_group_member'
|
||||
__table_args__ = (
|
||||
Index('instance_group_member_instance_idx', 'instance_uuid'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
instance_uuid = Column(String(255))
|
||||
group_id = Column(Integer, ForeignKey('instance_groups.id'),
|
||||
nullable=False)
|
||||
|
||||
|
||||
class InstanceGroupPolicy(API_BASE):
|
||||
"""Represents the policy type for an instance group."""
|
||||
__tablename__ = 'instance_group_policy'
|
||||
__table_args__ = (
|
||||
Index('instance_group_policy_policy_idx', 'policy'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
policy = Column(String(255))
|
||||
group_id = Column(Integer, ForeignKey('instance_groups.id'),
|
||||
nullable=False)
|
||||
rules = Column(Text)
|
||||
|
||||
|
||||
class InstanceGroup(API_BASE):
|
||||
"""Represents an instance group.
|
||||
|
||||
A group will maintain a collection of instances and the relationship
|
||||
between them.
|
||||
"""
|
||||
|
||||
__tablename__ = 'instance_groups'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('uuid', name='uniq_instance_groups0uuid'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id = Column(String(255))
|
||||
project_id = Column(String(255))
|
||||
uuid = Column(String(36), nullable=False)
|
||||
name = Column(String(255))
|
||||
_policies = orm.relationship(InstanceGroupPolicy,
|
||||
primaryjoin='InstanceGroup.id == InstanceGroupPolicy.group_id')
|
||||
_members = orm.relationship(InstanceGroupMember,
|
||||
primaryjoin='InstanceGroup.id == InstanceGroupMember.group_id')
|
||||
|
||||
@property
|
||||
def policy(self):
|
||||
if len(self._policies) > 1:
|
||||
msg = ("More than one policy (%(policies)s) is associated with "
|
||||
"group %(group_name)s, only the first one in the list "
|
||||
"would be returned.")
|
||||
LOG.warning(msg, {"policies": [p.policy for p in self._policies],
|
||||
"group_name": self.name})
|
||||
return self._policies[0] if self._policies else None
|
||||
|
||||
@property
|
||||
def members(self):
|
||||
return [m.instance_uuid for m in self._members]
|
||||
|
||||
|
||||
class Quota(API_BASE):
|
||||
"""Represents a single quota override for a project.
|
||||
|
||||
If there is no row for a given project id and resource, then the
|
||||
default for the quota class is used. If there is no row for a
|
||||
given quota class and resource, then the default for the
|
||||
deployment is used. If the row is present but the hard limit is
|
||||
Null, then the resource is unlimited.
|
||||
"""
|
||||
|
||||
__tablename__ = 'quotas'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("project_id", "resource",
|
||||
name="uniq_quotas0project_id0resource"
|
||||
),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
project_id = Column(String(255))
|
||||
|
||||
resource = Column(String(255), nullable=False)
|
||||
hard_limit = Column(Integer)
|
||||
|
||||
|
||||
class ProjectUserQuota(API_BASE):
|
||||
"""Represents a single quota override for a user with in a project."""
|
||||
|
||||
__tablename__ = 'project_user_quotas'
|
||||
uniq_name = "uniq_project_user_quotas0user_id0project_id0resource"
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("user_id", "project_id", "resource",
|
||||
name=uniq_name),
|
||||
Index('project_user_quotas_project_id_idx',
|
||||
'project_id'),
|
||||
Index('project_user_quotas_user_id_idx',
|
||||
'user_id',)
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
project_id = Column(String(255), nullable=False)
|
||||
user_id = Column(String(255), nullable=False)
|
||||
|
||||
resource = Column(String(255), nullable=False)
|
||||
hard_limit = Column(Integer)
|
||||
|
||||
|
||||
class QuotaClass(API_BASE):
|
||||
"""Represents a single quota override for a quota class.
|
||||
|
||||
If there is no row for a given quota class and resource, then the
|
||||
default for the deployment is used. If the row is present but the
|
||||
hard limit is Null, then the resource is unlimited.
|
||||
"""
|
||||
|
||||
__tablename__ = 'quota_classes'
|
||||
__table_args__ = (
|
||||
Index('quota_classes_class_name_idx', 'class_name'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
class_name = Column(String(255))
|
||||
|
||||
resource = Column(String(255))
|
||||
hard_limit = Column(Integer)
|
||||
|
||||
|
||||
class QuotaUsage(API_BASE):
|
||||
"""Represents the current usage for a given resource."""
|
||||
|
||||
__tablename__ = 'quota_usages'
|
||||
__table_args__ = (
|
||||
Index('quota_usages_project_id_idx', 'project_id'),
|
||||
Index('quota_usages_user_id_idx', 'user_id'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
project_id = Column(String(255))
|
||||
user_id = Column(String(255))
|
||||
resource = Column(String(255), nullable=False)
|
||||
|
||||
in_use = Column(Integer, nullable=False)
|
||||
reserved = Column(Integer, nullable=False)
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self.in_use + self.reserved
|
||||
|
||||
until_refresh = Column(Integer)
|
||||
|
||||
|
||||
class Reservation(API_BASE):
|
||||
"""Represents a resource reservation for quotas."""
|
||||
|
||||
__tablename__ = 'reservations'
|
||||
__table_args__ = (
|
||||
Index('reservations_project_id_idx', 'project_id'),
|
||||
Index('reservations_uuid_idx', 'uuid'),
|
||||
Index('reservations_expire_idx', 'expire'),
|
||||
Index('reservations_user_id_idx', 'user_id'),
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
|
||||
usage_id = Column(Integer, ForeignKey('quota_usages.id'), nullable=False)
|
||||
|
||||
project_id = Column(String(255))
|
||||
user_id = Column(String(255))
|
||||
resource = Column(String(255))
|
||||
|
||||
delta = Column(Integer, nullable=False)
|
||||
expire = Column(DateTime)
|
||||
|
||||
usage = orm.relationship(
|
||||
"QuotaUsage",
|
||||
foreign_keys=usage_id,
|
||||
primaryjoin='Reservation.usage_id == QuotaUsage.id')
|
||||
|
||||
|
||||
class Trait(API_BASE):
|
||||
"""Represents a trait."""
|
||||
|
||||
__tablename__ = "traits"
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('name', name='uniq_traits0name'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
name = Column(Unicode(255), nullable=False)
|
||||
|
||||
|
||||
class ResourceProviderTrait(API_BASE):
|
||||
"""Represents the relationship between traits and resource provider"""
|
||||
|
||||
__tablename__ = "resource_provider_traits"
|
||||
__table_args__ = (
|
||||
Index('resource_provider_traits_resource_provider_trait_idx',
|
||||
'resource_provider_id', 'trait_id'),
|
||||
)
|
||||
|
||||
trait_id = Column(Integer, ForeignKey('traits.id'), primary_key=True,
|
||||
nullable=False)
|
||||
resource_provider_id = Column(Integer,
|
||||
ForeignKey('resource_providers.id'),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class Project(API_BASE):
|
||||
"""The project is the Keystone project."""
|
||||
|
||||
__tablename__ = 'projects'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
'external_id',
|
||||
name='uniq_projects0external_id',
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
external_id = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class User(API_BASE):
|
||||
"""The user is the Keystone user."""
|
||||
|
||||
__tablename__ = 'users'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
'external_id',
|
||||
name='uniq_users0external_id',
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
external_id = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class Consumer(API_BASE):
|
||||
"""Represents a resource consumer."""
|
||||
|
||||
__tablename__ = 'consumers'
|
||||
__table_args__ = (
|
||||
Index('consumers_project_id_uuid_idx', 'project_id', 'uuid'),
|
||||
Index('consumers_project_id_user_id_uuid_idx', 'project_id', 'user_id',
|
||||
'uuid'),
|
||||
schema.UniqueConstraint('uuid', name='uniq_consumers0uuid'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
project_id = Column(Integer, nullable=False)
|
||||
user_id = Column(Integer, nullable=False)
|
||||
# FIXME(mriedem): Change this to server_default=text("0") to match the
|
||||
# 059_add_consumer_generation script once bug 1776527 is fixed.
|
||||
generation = Column(Integer, nullable=False, server_default="0", default=0)
|
@ -16,77 +16,42 @@
|
||||
|
||||
import os
|
||||
|
||||
from migrate import exceptions as versioning_exceptions
|
||||
from migrate.versioning import api as versioning_api
|
||||
from migrate.versioning.repository import Repository
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy
|
||||
import alembic
|
||||
from alembic import config as alembic_config
|
||||
from alembic import migration as alembic_migration
|
||||
|
||||
from placement import db_api as placement_db
|
||||
|
||||
INIT_VERSION = 0
|
||||
_REPOSITORY = None
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_engine(context=None):
|
||||
def get_engine():
|
||||
return placement_db.get_placement_engine()
|
||||
|
||||
|
||||
def db_sync(version=None, context=None):
|
||||
if version is not None:
|
||||
# Let ValueError raise
|
||||
version = int(version)
|
||||
|
||||
current_version = db_version(context=context)
|
||||
repository = _find_migrate_repo()
|
||||
if version is None or version > current_version:
|
||||
return versioning_api.upgrade(get_engine(context=context),
|
||||
repository, version)
|
||||
else:
|
||||
return versioning_api.downgrade(get_engine(context=context),
|
||||
repository, version)
|
||||
def _alembic_config():
|
||||
path = os.path.join(os.path.dirname(__file__), "alembic.ini")
|
||||
config = alembic_config.Config(path)
|
||||
return config
|
||||
|
||||
|
||||
def db_version(context=None):
|
||||
repository = _find_migrate_repo()
|
||||
try:
|
||||
return versioning_api.db_version(get_engine(context=context),
|
||||
repository)
|
||||
except versioning_exceptions.DatabaseNotControlledError as exc:
|
||||
meta = sqlalchemy.MetaData()
|
||||
engine = get_engine(context=context)
|
||||
meta.reflect(bind=engine)
|
||||
tables = meta.tables
|
||||
if len(tables) == 0:
|
||||
db_version_control(INIT_VERSION, context=context)
|
||||
return versioning_api.db_version(
|
||||
get_engine(context=context), repository)
|
||||
else:
|
||||
LOG.exception(exc)
|
||||
raise exc
|
||||
def version(config=None, engine=None):
|
||||
"""Current database version.
|
||||
|
||||
:returns: Database version
|
||||
:rtype: string
|
||||
"""
|
||||
if engine is None:
|
||||
engine = get_engine()
|
||||
with engine.connect() as conn:
|
||||
context = alembic_migration.MigrationContext.configure(conn)
|
||||
return context.get_current_revision()
|
||||
|
||||
|
||||
def db_initial_version():
|
||||
return INIT_VERSION
|
||||
def upgrade(revision, config=None):
|
||||
"""Used for upgrading database.
|
||||
|
||||
|
||||
def db_version_control(version=None, context=None):
|
||||
repository = _find_migrate_repo()
|
||||
versioning_api.version_control(get_engine(context=context),
|
||||
repository,
|
||||
version)
|
||||
return version
|
||||
|
||||
|
||||
def _find_migrate_repo():
|
||||
"""Get the path for the migrate repository."""
|
||||
global _REPOSITORY
|
||||
rel_path = os.path.join('api_migrations', 'migrate_repo')
|
||||
path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||
rel_path)
|
||||
assert os.path.exists(path)
|
||||
if _REPOSITORY is None:
|
||||
_REPOSITORY = Repository(path)
|
||||
return _REPOSITORY
|
||||
:param version: Desired database version
|
||||
:type version: string
|
||||
"""
|
||||
revision = revision or "head"
|
||||
config = config or _alembic_config()
|
||||
alembic.command.upgrade(config, revision)
|
||||
|
232
placement/db/sqlalchemy/models.py
Normal file
232
placement/db/sqlalchemy/models.py
Normal file
@ -0,0 +1,232 @@
|
||||
# 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 oslo_db.sqlalchemy import models
|
||||
from oslo_log import log as logging
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import Float
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import Index
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import orm
|
||||
from sqlalchemy import schema
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy import Unicode
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class _Base(models.ModelBase, models.TimestampMixin):
|
||||
pass
|
||||
|
||||
|
||||
BASE = declarative_base(cls=_Base)
|
||||
|
||||
|
||||
class ResourceClass(BASE):
|
||||
"""Represents the type of resource for an inventory or allocation."""
|
||||
__tablename__ = 'resource_classes'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("name", name="uniq_resource_classes0name"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
name = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class ResourceProvider(BASE):
|
||||
"""Represents a mapping to a providers of resources."""
|
||||
|
||||
__tablename__ = "resource_providers"
|
||||
__table_args__ = (
|
||||
Index('resource_providers_uuid_idx', 'uuid'),
|
||||
schema.UniqueConstraint('uuid',
|
||||
name='uniq_resource_providers0uuid'),
|
||||
Index('resource_providers_name_idx', 'name'),
|
||||
Index('resource_providers_root_provider_id_idx',
|
||||
'root_provider_id'),
|
||||
Index('resource_providers_parent_provider_id_idx',
|
||||
'parent_provider_id'),
|
||||
schema.UniqueConstraint('name',
|
||||
name='uniq_resource_providers0name')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
name = Column(Unicode(200), nullable=True)
|
||||
generation = Column(Integer, default=0)
|
||||
# Represents the root of the "tree" that the provider belongs to
|
||||
root_provider_id = Column(Integer, ForeignKey('resource_providers.id'),
|
||||
nullable=True)
|
||||
# The immediate parent provider of this provider, or NULL if there is no
|
||||
# parent. If parent_provider_id == NULL then root_provider_id == id
|
||||
parent_provider_id = Column(Integer, ForeignKey('resource_providers.id'),
|
||||
nullable=True)
|
||||
|
||||
|
||||
class Inventory(BASE):
|
||||
"""Represents a quantity of available resource."""
|
||||
|
||||
__tablename__ = "inventories"
|
||||
__table_args__ = (
|
||||
Index('inventories_resource_provider_id_idx',
|
||||
'resource_provider_id'),
|
||||
Index('inventories_resource_class_id_idx',
|
||||
'resource_class_id'),
|
||||
Index('inventories_resource_provider_resource_class_idx',
|
||||
'resource_provider_id', 'resource_class_id'),
|
||||
schema.UniqueConstraint('resource_provider_id', 'resource_class_id',
|
||||
name='uniq_inventories0resource_provider_resource_class')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
resource_provider_id = Column(Integer, nullable=False)
|
||||
resource_class_id = Column(Integer, nullable=False)
|
||||
total = Column(Integer, nullable=False)
|
||||
reserved = Column(Integer, nullable=False)
|
||||
min_unit = Column(Integer, nullable=False)
|
||||
max_unit = Column(Integer, nullable=False)
|
||||
step_size = Column(Integer, nullable=False)
|
||||
allocation_ratio = Column(Float, nullable=False)
|
||||
resource_provider = orm.relationship(
|
||||
"ResourceProvider",
|
||||
primaryjoin=('Inventory.resource_provider_id == '
|
||||
'ResourceProvider.id'),
|
||||
foreign_keys=resource_provider_id)
|
||||
|
||||
|
||||
class Allocation(BASE):
|
||||
"""A use of inventory."""
|
||||
|
||||
__tablename__ = "allocations"
|
||||
__table_args__ = (
|
||||
Index('allocations_resource_provider_class_used_idx',
|
||||
'resource_provider_id', 'resource_class_id',
|
||||
'used'),
|
||||
Index('allocations_resource_class_id_idx',
|
||||
'resource_class_id'),
|
||||
Index('allocations_consumer_id_idx', 'consumer_id')
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
resource_provider_id = Column(Integer, nullable=False)
|
||||
consumer_id = Column(String(36), nullable=False)
|
||||
resource_class_id = Column(Integer, nullable=False)
|
||||
used = Column(Integer, nullable=False)
|
||||
resource_provider = orm.relationship(
|
||||
"ResourceProvider",
|
||||
primaryjoin=('Allocation.resource_provider_id == '
|
||||
'ResourceProvider.id'),
|
||||
foreign_keys=resource_provider_id)
|
||||
|
||||
|
||||
class ResourceProviderAggregate(BASE):
|
||||
"""Associate a resource provider with an aggregate."""
|
||||
|
||||
__tablename__ = 'resource_provider_aggregates'
|
||||
__table_args__ = (
|
||||
Index('resource_provider_aggregates_aggregate_id_idx',
|
||||
'aggregate_id'),
|
||||
)
|
||||
|
||||
resource_provider_id = Column(Integer, primary_key=True, nullable=False)
|
||||
aggregate_id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class PlacementAggregate(BASE):
|
||||
"""A grouping of resource providers."""
|
||||
__tablename__ = 'placement_aggregates'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint("uuid", name="uniq_placement_aggregates0uuid"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
uuid = Column(String(36), index=True)
|
||||
|
||||
|
||||
class Trait(BASE):
|
||||
"""Represents a trait."""
|
||||
|
||||
__tablename__ = "traits"
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint('name', name='uniq_traits0name'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
name = Column(Unicode(255), nullable=False)
|
||||
|
||||
|
||||
class ResourceProviderTrait(BASE):
|
||||
"""Represents the relationship between traits and resource provider"""
|
||||
|
||||
__tablename__ = "resource_provider_traits"
|
||||
__table_args__ = (
|
||||
Index('resource_provider_traits_resource_provider_trait_idx',
|
||||
'resource_provider_id', 'trait_id'),
|
||||
)
|
||||
|
||||
trait_id = Column(Integer, ForeignKey('traits.id'), primary_key=True,
|
||||
nullable=False)
|
||||
resource_provider_id = Column(Integer,
|
||||
ForeignKey('resource_providers.id'),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
|
||||
|
||||
class Project(BASE):
|
||||
"""The project is the Keystone project."""
|
||||
|
||||
__tablename__ = 'projects'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
'external_id',
|
||||
name='uniq_projects0external_id',
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
external_id = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class User(BASE):
|
||||
"""The user is the Keystone user."""
|
||||
|
||||
__tablename__ = 'users'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
'external_id',
|
||||
name='uniq_users0external_id',
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
external_id = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class Consumer(BASE):
|
||||
"""Represents a resource consumer."""
|
||||
|
||||
__tablename__ = 'consumers'
|
||||
__table_args__ = (
|
||||
Index('consumers_project_id_uuid_idx', 'project_id', 'uuid'),
|
||||
Index('consumers_project_id_user_id_uuid_idx', 'project_id', 'user_id',
|
||||
'uuid'),
|
||||
schema.UniqueConstraint('uuid', name='uniq_consumers0uuid'),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
uuid = Column(String(36), nullable=False)
|
||||
project_id = Column(Integer, nullable=False)
|
||||
user_id = Column(Integer, nullable=False)
|
||||
generation = Column(Integer, nullable=False, server_default="0", default=0)
|
@ -9,9 +9,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Database context manager for placement database connection, kept in its
|
||||
own file so the nova db_api (which has cascading imports) is not imported.
|
||||
"""
|
||||
"""Database context manager for placement database connection."""
|
||||
|
||||
from oslo_db.sqlalchemy import enginefacade
|
||||
from oslo_log import log as logging
|
||||
|
@ -15,7 +15,7 @@ from oslo_versionedobjects import base
|
||||
from oslo_versionedobjects import fields
|
||||
import sqlalchemy as sa
|
||||
|
||||
from placement.db.sqlalchemy import api_models as models
|
||||
from placement.db.sqlalchemy import models
|
||||
from placement import db_api
|
||||
from placement import exception
|
||||
from placement.objects import project as project_obj
|
||||
|
@ -16,7 +16,7 @@ from oslo_versionedobjects import base
|
||||
from oslo_versionedobjects import fields
|
||||
import sqlalchemy as sa
|
||||
|
||||
from placement.db.sqlalchemy import api_models as models
|
||||
from placement.db.sqlalchemy import models
|
||||
from placement import db_api
|
||||
from placement import exception
|
||||
|
||||
|