diff --git a/neutron/common/constants.py b/neutron/common/constants.py index bb601958b42..2f2a10af93e 100644 --- a/neutron/common/constants.py +++ b/neutron/common/constants.py @@ -148,3 +148,9 @@ METADATA_CIDR = '169.254.169.254/32' IPAM_ALLOCATION_STATUS_ALLOCATED = 'ALLOCATED' VALID_IPAM_ALLOCATION_STATUSES = (IPAM_ALLOCATION_STATUS_ALLOCATED,) + +# Port binding states for Live Migration +PORT_BINDING_STATUS_ACTIVE = 'ACTIVE' +PORT_BINDING_STATUS_INACTIVE = 'INACTIVE' +PORT_BINDING_STATUSES = (PORT_BINDING_STATUS_ACTIVE, + PORT_BINDING_STATUS_INACTIVE) diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index c292dcc4491..1c625bc836d 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -929c968efe70 +a9c43481023c diff --git a/neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml2_port_bindings.py b/neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml2_port_bindings.py new file mode 100644 index 00000000000..6fa702ec874 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/ocata/expand/a9c43481023c_extend_ml2_port_bindings.py @@ -0,0 +1,69 @@ +# Copyright 2016 Intel Corporation +# +# 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. +# + +"""extend_pk_with_host_and_add_status_to_ml2_port_binding + +Revision ID: a9c43481023c +Revises: 5cd92597d11d +Create Date: 2016-11-22 11:48:43.479552 + +""" + +# revision identifiers, used by Alembic. +revision = 'a9c43481023c' +down_revision = '929c968efe70' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.engine.reflection import Inspector as insp + +from neutron.common import constants + +MYSQL_ENGINE = 'mysql' +ML2_PORT_BINDING = 'ml2_port_bindings' + + +def upgrade(): + bind = op.get_bind() + engine = bind.engine + + op.add_column(ML2_PORT_BINDING, + sa.Column('status', + sa.String(length=16), + nullable=False, + server_default=constants.PORT_BINDING_STATUS_ACTIVE)) + + if (engine.name == MYSQL_ENGINE): + op.execute("ALTER TABLE ml2_port_bindings DROP PRIMARY KEY," + "ADD PRIMARY KEY(port_id, host);") + else: + inspector = insp.from_engine(bind) + pk_constraint = inspector.get_pk_constraint(ML2_PORT_BINDING) + op.drop_constraint(pk_constraint.get('name'), ML2_PORT_BINDING, + type_='primary') + op.create_primary_key(op.f('pk_ml2_port_bindings'), + ML2_PORT_BINDING, ['port_id', 'host']) + + +def expand_drop_exceptions(): + """ + Drop the existing primary key constraint and then extend it to include + host as the primary key to support multiple bindings for the same port. + This is needed to use drop in expand migration to pass test_branches. + It is safe to recreate primary key in expand as it is backward compatible. + """ + return { + sa.Constraint: ['ml2_port_bindings_pkey'] + } diff --git a/neutron/objects/common_types.py b/neutron/objects/common_types.py index d51c4a8b231..a12d94a3c9d 100644 --- a/neutron/objects/common_types.py +++ b/neutron/objects/common_types.py @@ -185,6 +185,10 @@ class IpProtocolEnum(obj_fields.Enum): **kwargs) +class PortBindingStatusEnumField(obj_fields.AutoTypedField): + AUTO_TYPE = obj_fields.Enum(valid_values=constants.PORT_BINDING_STATUSES) + + class IpProtocolEnumField(obj_fields.AutoTypedField): AUTO_TYPE = IpProtocolEnum() diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py index d1c9391ffea..bbddb4ddeb2 100644 --- a/neutron/objects/ports.py +++ b/neutron/objects/ports.py @@ -16,6 +16,7 @@ import netaddr from oslo_versionedobjects import base as obj_base from oslo_versionedobjects import fields as obj_fields +from neutron.common import constants from neutron.common import utils from neutron.db import api as db_api from neutron.db.models import dns as dns_models @@ -72,9 +73,11 @@ class PortBinding(PortBindingBase): 'vif_type': obj_fields.StringField(), 'vif_details': common_types.DictOfMiscValuesField(nullable=True), 'vnic_type': obj_fields.StringField(), + 'status': common_types.PortBindingStatusEnumField( + default=constants.PORT_BINDING_STATUS_ACTIVE), } - primary_keys = ['port_id'] + primary_keys = ['port_id', 'host'] @obj_base.VersionedObjectRegistry.register diff --git a/neutron/plugins/ml2/models.py b/neutron/plugins/ml2/models.py index 7458e9290de..8fc5c2b80c2 100644 --- a/neutron/plugins/ml2/models.py +++ b/neutron/plugins/ml2/models.py @@ -17,6 +17,7 @@ from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy import orm +from neutron.common import constants from neutron.db import models_v2 from neutron.extensions import portbindings @@ -38,7 +39,7 @@ class PortBinding(model_base.BASEV2): sa.ForeignKey('ports.id', ondelete="CASCADE"), primary_key=True) host = sa.Column(sa.String(255), nullable=False, default='', - server_default='') + server_default='', primary_key=True) vnic_type = sa.Column(sa.String(64), nullable=False, default=portbindings.VNIC_NORMAL, server_default=portbindings.VNIC_NORMAL) @@ -47,6 +48,9 @@ class PortBinding(model_base.BASEV2): vif_type = sa.Column(sa.String(64), nullable=False) vif_details = sa.Column(sa.String(4095), nullable=False, default='', server_default='') + status = sa.Column(sa.String(16), nullable=False, + default=constants.PORT_BINDING_STATUS_ACTIVE, + server_default=constants.PORT_BINDING_STATUS_ACTIVE) # Add a relationship to the Port model in order to instruct SQLAlchemy to # eagerly load port bindings diff --git a/neutron/tests/tools.py b/neutron/tests/tools.py index b5ea0b168a5..7aa396f1577 100644 --- a/neutron/tests/tools.py +++ b/neutron/tests/tools.py @@ -299,6 +299,10 @@ def get_random_ip_protocol(): return random.choice(list(constants.IP_PROTOCOL_MAP.keys())) +def get_random_port_binding_statuses(): + return random.choice(n_const.PORT_BINDING_STATUSES) + + def is_bsd(): """Return True on BSD-based systems.""" diff --git a/neutron/tests/unit/objects/test_base.py b/neutron/tests/unit/objects/test_base.py index 5c11c377948..3c30a1d6fa5 100644 --- a/neutron/tests/unit/objects/test_base.py +++ b/neutron/tests/unit/objects/test_base.py @@ -439,6 +439,8 @@ FIELD_TYPE_VALUE_GENERATOR_MAP = { common_types.IpProtocolEnumField: tools.get_random_ip_protocol, common_types.ListOfIPNetworksField: get_list_of_random_networks, common_types.MACAddressField: tools.get_random_EUI, + common_types.PortBindingStatusEnumField: + tools.get_random_port_binding_statuses, common_types.PortRangeField: tools.get_random_port, common_types.PortRangeWith0Field: lambda: tools.get_random_port(0), common_types.SetOfUUIDsField: get_set_of_random_uuids, diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index e6c077c5092..77213d18d77 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -53,7 +53,7 @@ object_data = { 'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3', 'NetworkSegment': '1.0-40707ef6bd9a0bf095038158d995cc7d', 'Port': '1.0-638f6b09a3809ebd8b2b46293f56871b', - 'PortBinding': '1.0-189fa2f450a1f24b1423df660eb43d71', + 'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578', 'PortBindingLevel': '1.0-de66a4c61a083b8f34319fa9dde5b060', 'PortDNS': '1.0-201cf6d057fde75539c3d1f2bbf05902', 'PortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',