From 89de63de05e296af583032cb17a3d76b4b4d6a40 Mon Sep 17 00:00:00 2001 From: Carlos Goncalves Date: Mon, 23 Jan 2017 19:53:04 +0000 Subject: [PATCH] Port data plane status extension implementation Implements the port data plane status extension. Third parties can report via Neutron API issues in the underlying data plane affecting connectivity from/to Neutron ports. Supported statuses: - None: no status being reported; default value - ACTIVE: all is up and running - DOWN: no traffic can flow from/to the Neutron port Setting attribute available to admin or any user with specific role (default role: data_plane_integrator). ML2 extension driver loaded on request via configuration: [ml2] extension_drivers = data_plane_status Related-Bug: #1598081 Related-Bug: #1575146 DocImpact: users can get status of the underlying port data plane; attribute writable by admin users and users granted the 'data-plane-integrator' role. APIImpact: port now has data_plane_status attr, set on port update Implements: blueprint port-data-plane-status Depends-On: I04eef902b3310f799b1ce7ea44ed7cf77c74da04 Change-Id: Ic9e1e3ed9e3d4b88a4292114f4cb4192ac4b3502 --- neutron/db/data_plane_status_db.py | 48 ++++++++++++++++++++++ .../alembic_migrations/versions/EXPAND_HEAD | 2 +- .../804a3c76314c_add_data_plane_status_to_port.py | 39 ++++++++++++++++++ neutron/db/models/data_plane_status.py | 34 +++++++++++++++ neutron/extensions/data_plane_status.py | 47 +++++++++++++++++++++ .../objects/port/extensions/data_plane_status.py | 37 +++++++++++++++++ neutron/objects/ports.py | 14 ++++++- .../plugins/ml2/extensions/data_plane_status.py | 41 ++++++++++++++++++ 8 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 neutron/db/data_plane_status_db.py create mode 100644 neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.py create mode 100644 neutron/db/models/data_plane_status.py create mode 100644 neutron/extensions/data_plane_status.py create mode 100644 neutron/objects/port/extensions/data_plane_status.py create mode 100644 neutron/plugins/ml2/extensions/data_plane_status.py diff --git a/neutron/db/data_plane_status_db.py b/neutron/db/data_plane_status_db.py new file mode 100644 index 000000000..4e5c23aef --- /dev/null +++ b/neutron/db/data_plane_status_db.py @@ -0,0 +1,48 @@ +# Copyright (c) 2017 NEC Corporation. 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. + +from neutron_lib.api.definitions import data_plane_status as dps_lib + +from neutron.objects.port.extensions import data_plane_status as dps_obj + + +class DataPlaneStatusMixin(object): + """Mixin class to add data plane status to a port""" + + def _process_create_port_data_plane_status(self, context, data, res): + obj = dps_obj.PortDataPlaneStatus(context, port_id=res['id'], + data_plane_status=data[dps_lib.DATA_PLANE_STATUS]) + obj.create() + res[dps_lib.DATA_PLANE_STATUS] = data[dps_lib.DATA_PLANE_STATUS] + + def _process_update_port_data_plane_status(self, context, data, + res): + if dps_lib.DATA_PLANE_STATUS not in data: + return + + obj = dps_obj.PortDataPlaneStatus.get_object(context, + port_id=res['id']) + if obj: + obj.data_plane_status = data[dps_lib.DATA_PLANE_STATUS] + obj.update() + res[dps_lib.DATA_PLANE_STATUS] = data[dps_lib.DATA_PLANE_STATUS] + else: + self._process_create_port_data_plane_status(context, data, res) + + def _extend_port_data_plane_status(self, port_res, port_db): + port_res[dps_lib.DATA_PLANE_STATUS] = None + + if port_db.get(dps_lib.DATA_PLANE_STATUS): + port_res[dps_lib.DATA_PLANE_STATUS] = ( + port_db[dps_lib.DATA_PLANE_STATUS].data_plane_status) diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index 1c625bc83..8c1796ba3 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -a9c43481023c +804a3c76314c diff --git a/neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.py b/neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.py new file mode 100644 index 000000000..bd4d1472b --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/pike/expand/804a3c76314c_add_data_plane_status_to_port.py @@ -0,0 +1,39 @@ +# Copyright 2017 OpenStack Foundation +# +# 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. +# + +"""Add data_plane_status to Port + +Revision ID: 804a3c76314c +Revises: a9c43481023c +Create Date: 2017-01-17 13:51:45.737987 + +""" + +# revision identifiers, used by Alembic. +revision = '804a3c76314c' +down_revision = 'a9c43481023c' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table('portdataplanestatuses', + sa.Column('port_id', sa.String(36), + sa.ForeignKey('ports.id', + ondelete="CASCADE"), + primary_key=True, index=True), + sa.Column('data_plane_status', sa.String(length=16), + nullable=True)) diff --git a/neutron/db/models/data_plane_status.py b/neutron/db/models/data_plane_status.py new file mode 100644 index 000000000..ada10af55 --- /dev/null +++ b/neutron/db/models/data_plane_status.py @@ -0,0 +1,34 @@ +# Copyright (c) 2017 NEC Corporation. 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. + +from neutron_lib.db import model_base +import sqlalchemy as sa +from sqlalchemy import orm + +from neutron.db import models_v2 + + +class PortDataPlaneStatus(model_base.BASEV2): + __tablename__ = 'portdataplanestatuses' + + port_id = sa.Column(sa.String(36), + sa.ForeignKey('ports.id', ondelete="CASCADE"), + primary_key=True, index=True) + data_plane_status = sa.Column(sa.String(16), nullable=True) + port = orm.relationship( + models_v2.Port, load_on_pending=True, + backref=orm.backref("data_plane_status", + lazy='joined', uselist=False, + cascade='delete')) + revises_on_change = ('port', ) diff --git a/neutron/extensions/data_plane_status.py b/neutron/extensions/data_plane_status.py new file mode 100644 index 000000000..8e225e670 --- /dev/null +++ b/neutron/extensions/data_plane_status.py @@ -0,0 +1,47 @@ +# Copyright (c) 2017 NEC Corporation. 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. + +from neutron_lib.api.definitions import data_plane_status +from neutron_lib.api import extensions + + +class Data_plane_status(extensions.ExtensionDescriptor): + + @classmethod + def get_name(cls): + return data_plane_status.NAME + + @classmethod + def get_alias(cls): + return data_plane_status.ALIAS + + @classmethod + def get_description(cls): + return data_plane_status.DESCRIPTION + + @classmethod + def get_updated(cls): + return data_plane_status.UPDATED_TIMESTAMP + + def get_required_extensions(self): + return data_plane_status.REQUIRED_EXTENSIONS or [] + + def get_optional_extensions(self): + return data_plane_status.OPTIONAL_EXTENSIONS or [] + + def get_extended_resources(self, version): + if version == "2.0": + return data_plane_status.RESOURCE_ATTRIBUTE_MAP + else: + return {} diff --git a/neutron/objects/port/extensions/data_plane_status.py b/neutron/objects/port/extensions/data_plane_status.py new file mode 100644 index 000000000..bd5858123 --- /dev/null +++ b/neutron/objects/port/extensions/data_plane_status.py @@ -0,0 +1,37 @@ +# Copyright (c) 2017 NEC Corporation. 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. + +from oslo_versionedobjects import base as obj_base +from oslo_versionedobjects import fields as obj_fields + +from neutron.db.models import data_plane_status as db_models +from neutron.objects import base +from neutron.objects import common_types + + +@obj_base.VersionedObjectRegistry.register +class PortDataPlaneStatus(base.NeutronDbObject): + # Version 1.0: Initial version + VERSION = "1.0" + + db_model = db_models.PortDataPlaneStatus + + primary_keys = ['port_id'] + + fields = { + 'port_id': common_types.UUIDField(), + 'data_plane_status': obj_fields.StringField(), + } + + foreign_keys = {'Port': {'port_id': 'id'}} diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py index bbddb4dde..dd83db147 100644 --- a/neutron/objects/ports.py +++ b/neutron/objects/ports.py @@ -13,6 +13,7 @@ # under the License. import netaddr +from oslo_utils import versionutils from oslo_versionedobjects import base as obj_base from oslo_versionedobjects import fields as obj_fields @@ -206,7 +207,8 @@ class PortDNS(base.NeutronDbObject): @obj_base.VersionedObjectRegistry.register class Port(base.NeutronDbObject): # Version 1.0: Initial version - VERSION = '1.0' + # Version 1.1: Add data_plane_status field + VERSION = '1.1' db_model = models_v2.Port @@ -227,6 +229,9 @@ class Port(base.NeutronDbObject): 'binding': obj_fields.ObjectField( 'PortBinding', nullable=True ), + 'data_plane_status': obj_fields.ObjectField( + 'PortDataPlaneStatus', nullable=True + ), 'dhcp_options': obj_fields.ListOfObjectsField( 'ExtraDhcpOpt', nullable=True ), @@ -260,6 +265,7 @@ class Port(base.NeutronDbObject): 'allowed_address_pairs', 'binding', 'binding_levels', + 'data_plane_status', 'dhcp_options', 'distributed_binding', 'dns', @@ -374,3 +380,9 @@ class Port(base.NeutronDbObject): else: self.qos_policy_id = None self.obj_reset_changes(['qos_policy_id']) + + def obj_make_compatible(self, primitive, target_version): + _target_version = versionutils.convert_version_to_tuple(target_version) + + if _target_version < (1, 1): + primitive.pop('data_plane_status') diff --git a/neutron/plugins/ml2/extensions/data_plane_status.py b/neutron/plugins/ml2/extensions/data_plane_status.py new file mode 100644 index 000000000..850dafab6 --- /dev/null +++ b/neutron/plugins/ml2/extensions/data_plane_status.py @@ -0,0 +1,41 @@ +# Copyright (c) 2017 NEC Corporation. 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. + +from neutron_lib.api.definitions import data_plane_status as dps_lib +from oslo_log import log as logging + +from neutron.db import data_plane_status_db as dps_db +from neutron.plugins.ml2 import driver_api as api + +LOG = logging.getLogger(__name__) + + +class DataPlaneStatusExtensionDriver(api.ExtensionDriver, + dps_db.DataPlaneStatusMixin): + _supported_extension_alias = 'data-plane-status' + + def initialize(self): + LOG.info("DataPlaneStatusExtensionDriver initialization complete") + + @property + def extension_alias(self): + return self._supported_extension_alias + + def process_update_port(self, plugin_context, data, result): + if dps_lib.DATA_PLANE_STATUS in data: + self._process_update_port_data_plane_status(plugin_context, + data, result) + + def extend_port_dict(self, session, db_data, result): + self._extend_port_data_plane_status(result, db_data) -- 2.12.3