rename neutron/db to tacker/db
Change-Id: Iade9d9ef557c29d1f13cf631c8df16bd5d9be4cd
This commit is contained in:
parent
6125f5fcab
commit
b307e8a7c6
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
2db5203cb7a9
|
@ -1,5 +0,0 @@
|
||||
This directory contains the migration scripts for the Neutron project. Please
|
||||
see the README in neutron/db/migration on how to use and generate new
|
||||
migrations.
|
||||
|
||||
|
@ -1,204 +0,0 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.db import model_base
|
||||
from neutron.openstack.common import uuidutils
|
||||
|
||||
|
||||
class HasTenant(object):
|
||||
"""Tenant mixin, add to subclasses that have a tenant."""
|
||||
|
||||
# NOTE(jkoelker) tenant_id is just a free form string ;(
|
||||
tenant_id = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class HasId(object):
|
||||
"""id mixin, add to subclasses that have an id."""
|
||||
|
||||
id = sa.Column(sa.String(36),
|
||||
primary_key=True,
|
||||
default=uuidutils.generate_uuid)
|
||||
|
||||
|
||||
class HasStatusDescription(object):
|
||||
"""Status with description mixin."""
|
||||
|
||||
status = sa.Column(sa.String(16), nullable=False)
|
||||
status_description = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class IPAvailabilityRange(model_base.BASEV2):
|
||||
"""Internal representation of available IPs for Neutron subnets.
|
||||
|
||||
Allocation - first entry from the range will be allocated.
|
||||
If the first entry is equal to the last entry then this row
|
||||
will be deleted.
|
||||
Recycling ips involves reading the IPAllocationPool and IPAllocation tables
|
||||
and inserting ranges representing available ips. This happens after the
|
||||
final allocation is pulled from this table and a new ip allocation is
|
||||
requested. Any contiguous ranges of available ips will be inserted as a
|
||||
single range.
|
||||
"""
|
||||
|
||||
allocation_pool_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('ipallocationpools.id',
|
||||
ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
primary_key=True)
|
||||
first_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
last_ip = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s - %s" % (self.first_ip, self.last_ip)
|
||||
|
||||
|
||||
class IPAllocationPool(model_base.BASEV2, HasId):
|
||||
"""Representation of an allocation pool in a Neutron subnet."""
|
||||
|
||||
subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
|
||||
ondelete="CASCADE"),
|
||||
nullable=True)
|
||||
first_ip = sa.Column(sa.String(64), nullable=False)
|
||||
last_ip = sa.Column(sa.String(64), nullable=False)
|
||||
available_ranges = orm.relationship(IPAvailabilityRange,
|
||||
backref='ipallocationpool',
|
||||
lazy="joined",
|
||||
cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return "%s - %s" % (self.first_ip, self.last_ip)
|
||||
|
||||
|
||||
class IPAllocation(model_base.BASEV2):
|
||||
"""Internal representation of allocated IP addresses in a Neutron subnet.
|
||||
"""
|
||||
|
||||
port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id',
|
||||
ondelete="CASCADE"),
|
||||
nullable=True)
|
||||
ip_address = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
subnet_id = sa.Column(sa.String(36), sa.ForeignKey('subnets.id',
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
|
||||
|
||||
class Route(object):
|
||||
"""mixin of a route."""
|
||||
|
||||
destination = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
nexthop = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
|
||||
|
||||
class SubnetRoute(model_base.BASEV2, Route):
|
||||
|
||||
subnet_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('subnets.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class Port(model_base.BASEV2, HasId, HasTenant):
|
||||
"""Represents a port on a Neutron v2 network."""
|
||||
|
||||
name = sa.Column(sa.String(255))
|
||||
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
|
||||
nullable=False)
|
||||
fixed_ips = orm.relationship(IPAllocation, backref='ports', lazy='joined')
|
||||
mac_address = sa.Column(sa.String(32), nullable=False)
|
||||
admin_state_up = sa.Column(sa.Boolean(), nullable=False)
|
||||
status = sa.Column(sa.String(16), nullable=False)
|
||||
device_id = sa.Column(sa.String(255), nullable=False)
|
||||
device_owner = sa.Column(sa.String(255), nullable=False)
|
||||
|
||||
def __init__(self, id=None, tenant_id=None, name=None, network_id=None,
|
||||
mac_address=None, admin_state_up=None, status=None,
|
||||
device_id=None, device_owner=None, fixed_ips=None):
|
||||
self.id = id
|
||||
self.tenant_id = tenant_id
|
||||
self.name = name
|
||||
self.network_id = network_id
|
||||
self.mac_address = mac_address
|
||||
self.admin_state_up = admin_state_up
|
||||
self.device_owner = device_owner
|
||||
self.device_id = device_id
|
||||
# Since this is a relationship only set it if one is passed in.
|
||||
if fixed_ips:
|
||||
self.fixed_ips = fixed_ips
|
||||
|
||||
# NOTE(arosen): status must be set last as an event is triggered on!
|
||||
self.status = status
|
||||
|
||||
|
||||
class DNSNameServer(model_base.BASEV2):
|
||||
"""Internal representation of a DNS nameserver."""
|
||||
|
||||
address = sa.Column(sa.String(128), nullable=False, primary_key=True)
|
||||
subnet_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('subnets.id',
|
||||
ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class Subnet(model_base.BASEV2, HasId, HasTenant):
|
||||
"""Represents a neutron subnet.
|
||||
|
||||
When a subnet is created the first and last entries will be created. These
|
||||
are used for the IP allocation.
|
||||
"""
|
||||
|
||||
name = sa.Column(sa.String(255))
|
||||
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
|
||||
ip_version = sa.Column(sa.Integer, nullable=False)
|
||||
cidr = sa.Column(sa.String(64), nullable=False)
|
||||
gateway_ip = sa.Column(sa.String(64))
|
||||
allocation_pools = orm.relationship(IPAllocationPool,
|
||||
backref='subnet',
|
||||
lazy="joined",
|
||||
cascade='delete')
|
||||
enable_dhcp = sa.Column(sa.Boolean())
|
||||
dns_nameservers = orm.relationship(DNSNameServer,
|
||||
backref='subnet',
|
||||
cascade='all, delete, delete-orphan')
|
||||
routes = orm.relationship(SubnetRoute,
|
||||
backref='subnet',
|
||||
cascade='all, delete, delete-orphan')
|
||||
shared = sa.Column(sa.Boolean)
|
||||
ipv6_ra_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
|
||||
constants.DHCPV6_STATEFUL,
|
||||
constants.DHCPV6_STATELESS,
|
||||
name='ipv6_ra_modes'), nullable=True)
|
||||
ipv6_address_mode = sa.Column(sa.Enum(constants.IPV6_SLAAC,
|
||||
constants.DHCPV6_STATEFUL,
|
||||
constants.DHCPV6_STATELESS,
|
||||
name='ipv6_address_modes'), nullable=True)
|
||||
|
||||
|
||||
class Network(model_base.BASEV2, HasId, HasTenant):
|
||||
"""Represents a v2 neutron network."""
|
||||
|
||||
name = sa.Column(sa.String(255))
|
||||
ports = orm.relationship(Port, backref='networks')
|
||||
subnets = orm.relationship(Subnet, backref='networks',
|
||||
lazy="joined")
|
||||
status = sa.Column(sa.String(16))
|
||||
admin_state_up = sa.Column(sa.Boolean)
|
||||
shared = sa.Column(sa.Boolean)
|
@ -16,16 +16,14 @@
|
||||
from oslo.config import cfg
|
||||
import sqlalchemy as sql
|
||||
|
||||
from neutron.db import model_base
|
||||
from neutron.openstack.common.db.sqlalchemy import session
|
||||
from neutron.openstack.common import log as logging
|
||||
from tacker.db import model_base
|
||||
from tacker.openstack.common.db.sqlalchemy import session
|
||||
from tacker.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
BASE = model_base.BASEV2
|
||||
|
||||
cfg.CONF.import_opt('connection',
|
||||
'neutron.openstack.common.db.options',
|
||||
'tacker.openstack.common.db.options',
|
||||
group='database')
|
||||
|
||||
_FACADE = None
|
||||
@ -50,7 +48,7 @@ def configure_db():
|
||||
register_models()
|
||||
|
||||
|
||||
def clear_db(base=BASE):
|
||||
def clear_db(base=model_base.BASE):
|
||||
unregister_models(base)
|
||||
|
||||
|
||||
@ -67,7 +65,7 @@ def get_session(autocommit=True, expire_on_commit=False):
|
||||
expire_on_commit=expire_on_commit)
|
||||
|
||||
|
||||
def register_models(base=BASE):
|
||||
def register_models(base=model_base.BASE):
|
||||
"""Register Models and create properties."""
|
||||
try:
|
||||
facade = _create_facade_lazily()
|
||||
@ -79,7 +77,7 @@ def register_models(base=BASE):
|
||||
return True
|
||||
|
||||
|
||||
def unregister_models(base=BASE):
|
||||
def unregister_models(base=model_base.BASE):
|
||||
"""Unregister Models, useful clearing out data before testing."""
|
||||
try:
|
||||
facade = _create_facade_lazily()
|
199
tacker/db/db_base.py
Normal file
199
tacker/db/db_base.py
Normal file
@ -0,0 +1,199 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import weakref
|
||||
|
||||
from sqlalchemy import sql
|
||||
|
||||
from tacker.common import exceptions as n_exc
|
||||
from tacker.db import sqlalchemyutils
|
||||
from tacker.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CommonDbMixin(object):
|
||||
"""Common methods used in core and service plugins."""
|
||||
# Plugins, mixin classes implementing extension will register
|
||||
# hooks into the dict below for "augmenting" the "core way" of
|
||||
# building a query for retrieving objects from a model class.
|
||||
# To this aim, the register_model_query_hook and unregister_query_hook
|
||||
# from this class should be invoked
|
||||
_model_query_hooks = {}
|
||||
|
||||
# This dictionary will store methods for extending attributes of
|
||||
# api resources. Mixins can use this dict for adding their own methods
|
||||
# TODO(salvatore-orlando): Avoid using class-level variables
|
||||
_dict_extend_functions = {}
|
||||
|
||||
@classmethod
|
||||
def register_model_query_hook(cls, model, name, query_hook, filter_hook,
|
||||
result_filters=None):
|
||||
"""Register a hook to be invoked when a query is executed.
|
||||
|
||||
Add the hooks to the _model_query_hooks dict. Models are the keys
|
||||
of this dict, whereas the value is another dict mapping hook names to
|
||||
callables performing the hook.
|
||||
Each hook has a "query" component, used to build the query expression
|
||||
and a "filter" component, which is used to build the filter expression.
|
||||
|
||||
Query hooks take as input the query being built and return a
|
||||
transformed query expression.
|
||||
|
||||
Filter hooks take as input the filter expression being built and return
|
||||
a transformed filter expression
|
||||
"""
|
||||
model_hooks = cls._model_query_hooks.get(model)
|
||||
if not model_hooks:
|
||||
# add key to dict
|
||||
model_hooks = {}
|
||||
cls._model_query_hooks[model] = model_hooks
|
||||
model_hooks[name] = {'query': query_hook, 'filter': filter_hook,
|
||||
'result_filters': result_filters}
|
||||
|
||||
@property
|
||||
def safe_reference(self):
|
||||
"""Return a weakref to the instance.
|
||||
|
||||
Minimize the potential for the instance persisting
|
||||
unnecessarily in memory by returning a weakref proxy that
|
||||
won't prevent deallocation.
|
||||
"""
|
||||
return weakref.proxy(self)
|
||||
|
||||
def _model_query(self, context, model):
|
||||
query = context.session.query(model)
|
||||
# define basic filter condition for model query
|
||||
# NOTE(jkoelker) non-admin queries are scoped to their tenant_id
|
||||
# NOTE(salvatore-orlando): unless the model allows for shared objects
|
||||
query_filter = None
|
||||
if not context.is_admin and hasattr(model, 'tenant_id'):
|
||||
if hasattr(model, 'shared'):
|
||||
query_filter = ((model.tenant_id == context.tenant_id) |
|
||||
(model.shared == sql.true()))
|
||||
else:
|
||||
query_filter = (model.tenant_id == context.tenant_id)
|
||||
# Execute query hooks registered from mixins and plugins
|
||||
for _name, hooks in self._model_query_hooks.get(model,
|
||||
{}).iteritems():
|
||||
query_hook = hooks.get('query')
|
||||
if isinstance(query_hook, basestring):
|
||||
query_hook = getattr(self, query_hook, None)
|
||||
if query_hook:
|
||||
query = query_hook(context, model, query)
|
||||
|
||||
filter_hook = hooks.get('filter')
|
||||
if isinstance(filter_hook, basestring):
|
||||
filter_hook = getattr(self, filter_hook, None)
|
||||
if filter_hook:
|
||||
query_filter = filter_hook(context, model, query_filter)
|
||||
|
||||
# NOTE(salvatore-orlando): 'if query_filter' will try to evaluate the
|
||||
# condition, raising an exception
|
||||
if query_filter is not None:
|
||||
query = query.filter(query_filter)
|
||||
return query
|
||||
|
||||
def _fields(self, resource, fields):
|
||||
if fields:
|
||||
return dict(((key, item) for key, item in resource.items()
|
||||
if key in fields))
|
||||
return resource
|
||||
|
||||
def _get_tenant_id_for_create(self, context, resource):
|
||||
if context.is_admin and 'tenant_id' in resource:
|
||||
tenant_id = resource['tenant_id']
|
||||
elif ('tenant_id' in resource and
|
||||
resource['tenant_id'] != context.tenant_id):
|
||||
reason = _('Cannot create resource for another tenant')
|
||||
raise n_exc.AdminRequired(reason=reason)
|
||||
else:
|
||||
tenant_id = context.tenant_id
|
||||
return tenant_id
|
||||
|
||||
def _get_by_id(self, context, model, id):
|
||||
query = self._model_query(context, model)
|
||||
return query.filter(model.id == id).one()
|
||||
|
||||
def _apply_filters_to_query(self, query, model, filters):
|
||||
if filters:
|
||||
for key, value in filters.iteritems():
|
||||
column = getattr(model, key, None)
|
||||
if column:
|
||||
query = query.filter(column.in_(value))
|
||||
for _name, hooks in self._model_query_hooks.get(model,
|
||||
{}).iteritems():
|
||||
result_filter = hooks.get('result_filters', None)
|
||||
if isinstance(result_filter, basestring):
|
||||
result_filter = getattr(self, result_filter, None)
|
||||
|
||||
if result_filter:
|
||||
query = result_filter(query, filters)
|
||||
return query
|
||||
|
||||
def _apply_dict_extend_functions(self, resource_type,
|
||||
response, db_object):
|
||||
for func in self._dict_extend_functions.get(
|
||||
resource_type, []):
|
||||
args = (response, db_object)
|
||||
if isinstance(func, basestring):
|
||||
func = getattr(self, func, None)
|
||||
else:
|
||||
# must call unbound method - use self as 1st argument
|
||||
args = (self,) + args
|
||||
if func:
|
||||
func(*args)
|
||||
|
||||
def _get_collection_query(self, context, model, filters=None,
|
||||
sorts=None, limit=None, marker_obj=None,
|
||||
page_reverse=False):
|
||||
collection = self._model_query(context, model)
|
||||
collection = self._apply_filters_to_query(collection, model, filters)
|
||||
if limit and page_reverse and sorts:
|
||||
sorts = [(s[0], not s[1]) for s in sorts]
|
||||
collection = sqlalchemyutils.paginate_query(collection, model, limit,
|
||||
sorts,
|
||||
marker_obj=marker_obj)
|
||||
return collection
|
||||
|
||||
def _get_collection(self, context, model, dict_func, filters=None,
|
||||
fields=None, sorts=None, limit=None, marker_obj=None,
|
||||
page_reverse=False):
|
||||
query = self._get_collection_query(context, model, filters=filters,
|
||||
sorts=sorts,
|
||||
limit=limit,
|
||||
marker_obj=marker_obj,
|
||||
page_reverse=page_reverse)
|
||||
items = [dict_func(c, fields) for c in query]
|
||||
if limit and page_reverse:
|
||||
items.reverse()
|
||||
return items
|
||||
|
||||
def _get_collection_count(self, context, model, filters=None):
|
||||
return self._get_collection_query(context, model, filters).count()
|
||||
|
||||
def _get_marker_obj(self, context, resource, limit, marker):
|
||||
if limit and marker:
|
||||
return getattr(self, '_get_%s' % resource)(context, marker)
|
||||
return None
|
||||
|
||||
def _filter_non_model_columns(self, data, model):
|
||||
"""Remove all the attributes from data which are not columns of
|
||||
the model passed as second parameter.
|
||||
"""
|
||||
columns = [c.name for c in model.__table__.columns]
|
||||
return dict((k, v) for (k, v) in
|
||||
data.iteritems() if k in columns)
|
@ -15,56 +15,56 @@
|
||||
# @author Mark McClain (DreamHost)
|
||||
|
||||
The migrations in the alembic/versions contain the changes needed to migrate
|
||||
from older Neutron releases to newer versions. A migration occurs by executing
|
||||
from older Tacker releases to newer versions. A migration occurs by executing
|
||||
a script that details the changes needed to upgrade/downgrade the database. The
|
||||
migration scripts are ordered so that multiple scripts can run sequentially to
|
||||
update the database. The scripts are executed by Neutron's migration wrapper
|
||||
which uses the Alembic library to manage the migration. Neutron supports
|
||||
update the database. The scripts are executed by Tacker's migration wrapper
|
||||
which uses the Alembic library to manage the migration. Tacker supports
|
||||
migration from Folsom or later.
|
||||
|
||||
|
||||
If you are a deployer or developer and want to migrate from Folsom to Grizzly
|
||||
or later you must first add version tracking to the database:
|
||||
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini stamp folsom
|
||||
|
||||
You can then upgrade to the latest database version via:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini upgrade head
|
||||
|
||||
To check the current database version:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini current
|
||||
|
||||
To create a script to run the migration offline:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini upgrade head --sql
|
||||
|
||||
To run the offline migration between specific migration versions:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini upgrade \
|
||||
<start version>:<end version> --sql
|
||||
|
||||
Upgrade the database incrementally:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini upgrade --delta <# of revs>
|
||||
|
||||
Downgrade the database by a certain number of revisions:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini downgrade --delta <# of revs>
|
||||
|
||||
|
||||
DEVELOPERS:
|
||||
A database migration script is required when you submit a change to Neutron
|
||||
A database migration script is required when you submit a change to Tacker
|
||||
that alters the database model definition. The migration script is a special
|
||||
python file that includes code to update/downgrade the database to match the
|
||||
changes in the model definition. Alembic will execute these scripts in order to
|
||||
provide a linear migration path between revision. The neutron-db-manage command
|
||||
provide a linear migration path between revision. The tacker-db-manage command
|
||||
can be used to generate migration template for you to complete. The operations
|
||||
in the template are those supported by the Alembic migration library.
|
||||
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini revision \
|
||||
-m "description of revision" \
|
||||
--autogenerate
|
||||
@ -77,16 +77,16 @@ In rare circumstances, you may want to start with an empty migration template
|
||||
and manually author the changes necessary for an upgrade/downgrade. You can
|
||||
create a blank file via:
|
||||
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini revision \
|
||||
-m "description of revision"
|
||||
|
||||
The migration timeline should remain linear so that there is a clear path when
|
||||
upgrading/downgrading. To verify that the timeline does branch, you can run
|
||||
this command:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini check_migration
|
||||
|
||||
If the migration path does branch, you can find the branch point via:
|
||||
$ neutron-db-manage --config-file /path/to/neutron.conf \
|
||||
$ tacker-db-manage --config-file /path/to/tacker.conf \
|
||||
--config-file /path/to/plugin/config.ini history
|
@ -17,20 +17,6 @@
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
OVS_PLUGIN = ('neutron.plugins.openvswitch.ovs_neutron_plugin'
|
||||
'.OVSNeutronPluginV2')
|
||||
CISCO_PLUGIN = 'neutron.plugins.cisco.network_plugin.PluginV2'
|
||||
|
||||
|
||||
def should_run(active_plugins, migrate_plugins):
|
||||
if '*' in migrate_plugins:
|
||||
return True
|
||||
else:
|
||||
if (CISCO_PLUGIN not in migrate_plugins and
|
||||
OVS_PLUGIN in migrate_plugins):
|
||||
migrate_plugins.append(CISCO_PLUGIN)
|
||||
return set(active_plugins) & set(migrate_plugins)
|
||||
|
||||
|
||||
def alter_enum(table, column, enum_type, nullable):
|
||||
bind = op.get_bind()
|
@ -11,7 +11,7 @@ script_location = %(here)s/alembic
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# default to an empty string because the Neutron migration cli will
|
||||
# default to an empty string because the Tacker migration cli will
|
||||
# extract the correct value and set it programatically before alemic is fully
|
||||
# invoked.
|
||||
sqlalchemy.url =
|
@ -19,30 +19,20 @@ from logging import config as logging_config
|
||||
from alembic import context
|
||||
from sqlalchemy import create_engine, pool
|
||||
|
||||
from neutron.db import model_base
|
||||
from neutron.openstack.common import importutils
|
||||
from tacker.db import model_base
|
||||
|
||||
|
||||
DATABASE_QUOTA_DRIVER = 'neutron.extensions._quotav2_driver.DbQuotaDriver'
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
neutron_config = config.neutron_config
|
||||
tacker_config = config.tacker_config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
logging_config.fileConfig(config.config_file_name)
|
||||
|
||||
plugin_class_path = neutron_config.core_plugin
|
||||
active_plugins = [plugin_class_path]
|
||||
active_plugins += neutron_config.service_plugins
|
||||
|
||||
for class_path in active_plugins:
|
||||
importutils.import_class(class_path)
|
||||
|
||||
# set the target for 'autogenerate' support
|
||||
target_metadata = model_base.BASEV2.metadata
|
||||
target_metadata = model_base.BASE.metadata
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
@ -56,15 +46,14 @@ def run_migrations_offline():
|
||||
|
||||
"""
|
||||
kwargs = dict()
|
||||
if neutron_config.database.connection:
|
||||
kwargs['url'] = neutron_config.database.connection
|
||||
if tacker_config.database.connection:
|
||||
kwargs['url'] = tacker_config.database.connection
|
||||
else:
|
||||
kwargs['dialect_name'] = neutron_config.database.engine
|
||||
kwargs['dialect_name'] = tacker_config.database.engine
|
||||
context.configure(**kwargs)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations(active_plugins=active_plugins,
|
||||
options=build_options())
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
@ -75,7 +64,7 @@ def run_migrations_online():
|
||||
|
||||
"""
|
||||
engine = create_engine(
|
||||
neutron_config.database.connection,
|
||||
tacker_config.database.connection,
|
||||
poolclass=pool.NullPool)
|
||||
|
||||
connection = engine.connect()
|
||||
@ -86,20 +75,11 @@ def run_migrations_online():
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
context.run_migrations(active_plugins=active_plugins,
|
||||
options=build_options())
|
||||
context.run_migrations()
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
|
||||
def build_options():
|
||||
return {'folsom_quota_db_enabled': is_db_quota_enabled()}
|
||||
|
||||
|
||||
def is_db_quota_enabled():
|
||||
return neutron_config.QUOTAS.quota_driver == DATABASE_QUOTA_DRIVER
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
@ -25,28 +25,16 @@ Create Date: ${create_date}
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
|
||||
# Change to ['*'] if this migration applies to all plugins
|
||||
|
||||
migration_for_plugins = [
|
||||
'${config.neutron_config.core_plugin}'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
from neutron.db import migration
|
||||
from tacker.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
${downgrades if downgrades else "pass"}
|
1
tacker/db/migration/alembic_migrations/versions/HEAD
Normal file
1
tacker/db/migration/alembic_migrations/versions/HEAD
Normal file
@ -0,0 +1 @@
|
||||
81ffa86020d
|
5
tacker/db/migration/alembic_migrations/versions/README
Normal file
5
tacker/db/migration/alembic_migrations/versions/README
Normal file
@ -0,0 +1,5 @@
|
||||
This directory contains the migration scripts for the Tacker project. Please
|
||||
see the README in tacker/db/migration on how to use and generate new
|
||||
migrations.
|
||||
|
||||
|
@ -26,21 +26,6 @@ from oslo.config import cfg
|
||||
HEAD_FILENAME = 'HEAD'
|
||||
|
||||
|
||||
_core_opts = [
|
||||
cfg.StrOpt('core_plugin',
|
||||
default='',
|
||||
help=_('Neutron plugin provider module')),
|
||||
cfg.ListOpt('service_plugins',
|
||||
default=[],
|
||||
help=_("The service plugins Neutron will use")),
|
||||
]
|
||||
|
||||
_quota_opts = [
|
||||
cfg.StrOpt('quota_driver',
|
||||
default='',
|
||||
help=_('Neutron quota driver class')),
|
||||
]
|
||||
|
||||
_db_opts = [
|
||||
cfg.StrOpt('connection',
|
||||
deprecated_name='sql_connection',
|
||||
@ -53,9 +38,7 @@ _db_opts = [
|
||||
]
|
||||
|
||||
CONF = cfg.ConfigOpts()
|
||||
CONF.register_cli_opts(_core_opts)
|
||||
CONF.register_cli_opts(_db_opts, 'database')
|
||||
CONF.register_opts(_quota_opts, 'QUOTAS')
|
||||
|
||||
|
||||
def do_alembic_command(config, cmd, *args, **kwargs):
|
||||
@ -162,9 +145,9 @@ def main():
|
||||
os.path.join(os.path.dirname(__file__), 'alembic.ini')
|
||||
)
|
||||
config.set_main_option('script_location',
|
||||
'neutron.db.migration:alembic_migrations')
|
||||
# attach the Neutron conf to the Alembic conf
|
||||
config.neutron_config = CONF
|
||||
'tacker.db.migration:alembic_migrations')
|
||||
# attach the Tacker conf to the Alembic conf
|
||||
config.tacker_config = CONF
|
||||
|
||||
CONF()
|
||||
#TODO(gongysh) enable logging
|
@ -16,11 +16,11 @@
|
||||
from sqlalchemy.ext import declarative
|
||||
from sqlalchemy import orm
|
||||
|
||||
from neutron.openstack.common.db.sqlalchemy import models
|
||||
from tacker.openstack.common.db.sqlalchemy import models
|
||||
|
||||
|
||||
class NeutronBase(models.ModelBase):
|
||||
"""Base class for Neutron Models."""
|
||||
class TackerBase(models.ModelBase):
|
||||
"""Base class for Tacker Models."""
|
||||
|
||||
__table_args__ = {'mysql_engine': 'InnoDB'}
|
||||
|
||||
@ -41,7 +41,7 @@ class NeutronBase(models.ModelBase):
|
||||
id(self), ', '.join(items))
|
||||
|
||||
|
||||
class NeutronBaseV2(NeutronBase):
|
||||
class TackerBaseV1(TackerBase):
|
||||
|
||||
@declarative.declared_attr
|
||||
def __tablename__(cls):
|
||||
@ -49,4 +49,4 @@ class NeutronBaseV2(NeutronBase):
|
||||
return cls.__name__.lower() + 's'
|
||||
|
||||
|
||||
BASEV2 = declarative.declarative_base(cls=NeutronBaseV2)
|
||||
BASE = declarative.declarative_base(cls=TackerBaseV1)
|
40
tacker/db/models_v2.py
Normal file
40
tacker/db/models_v2.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from tacker.openstack.common import uuidutils
|
||||
|
||||
|
||||
class HasTenant(object):
|
||||
"""Tenant mixin, add to subclasses that have a tenant."""
|
||||
|
||||
# NOTE(jkoelker) tenant_id is just a free form string ;(
|
||||
tenant_id = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class HasId(object):
|
||||
"""id mixin, add to subclasses that have an id."""
|
||||
|
||||
id = sa.Column(sa.String(36),
|
||||
primary_key=True,
|
||||
default=uuidutils.generate_uuid)
|
||||
|
||||
|
||||
class HasStatusDescription(object):
|
||||
"""Status with description mixin."""
|
||||
|
||||
status = sa.Column(sa.String(16), nullable=False)
|
||||
status_description = sa.Column(sa.String(255))
|
@ -17,8 +17,8 @@ from six import moves
|
||||
import sqlalchemy
|
||||
from sqlalchemy.orm.properties import RelationshipProperty
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.openstack.common import log as logging
|
||||
from tacker.common import exceptions as n_exc
|
||||
from tacker.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
Loading…
Reference in New Issue
Block a user