Add extension "uplink-status-propagation-updatable"

This extension allows to update the port flag "propagate_uplink_status",
used by the ML2/SR-IOV agent to define a VF port link state as "auto"
(the VF will mimic the PF port link status).

The neutron-lib version required is at least 3.16.0, that includes [1].

[1]https://review.opendev.org/c/openstack/neutron-lib/+/927820

Closes-Bug: #2078661
Change-Id: Ic3c85d296cf3391b157c531d7bde90c6dbee2f8e
This commit is contained in:
Rodolfo Alonso Hernandez 2024-10-04 09:48:54 +00:00
parent ed7431fe43
commit 498be543e0
10 changed files with 241 additions and 0 deletions

View File

@ -1,3 +1,4 @@
function configure_uplink_status_propagation_extension {
neutron_ml2_extension_driver_add "uplink_status_propagation"
neutron_ml2_extension_driver_add "uplink_status_propagation_updatable"
}

View File

@ -93,6 +93,7 @@ from neutron_lib.api.definitions import tag_creation
from neutron_lib.api.definitions import tap_mirror
from neutron_lib.api.definitions import trunk
from neutron_lib.api.definitions import uplink_status_propagation
from neutron_lib.api.definitions import uplink_status_propagation_updatable
from neutron_lib.api.definitions import vlantransparent
from neutron_lib.api.definitions import vpn
from neutron_lib.api.definitions import vpn_endpoint_groups
@ -209,5 +210,6 @@ ML2_SUPPORTED_API_EXTENSIONS = [
firewall_v2.ALIAS,
firewall_v2_stdattrs.ALIAS,
uplink_status_propagation.ALIAS,
uplink_status_propagation_updatable.ALIAS,
tap_mirror.ALIAS,
]

View File

@ -26,6 +26,17 @@ class UplinkStatusPropagationMixin:
obj.create()
res[usp.PROPAGATE_UPLINK_STATUS] = data[usp.PROPAGATE_UPLINK_STATUS]
def _process_update_port(self, context, data, res):
obj = usp_obj.PortUplinkStatusPropagation.get_object(
context, port_id=res['id'])
if obj:
obj.propagate_uplink_status = data[usp.PROPAGATE_UPLINK_STATUS]
obj.update()
res[usp.PROPAGATE_UPLINK_STATUS] = data[
usp.PROPAGATE_UPLINK_STATUS]
else:
self._process_create_port(context, data, res)
@staticmethod
def _extend_port_dict(port_res, port_db):
# NOTE(ralonsoh): the default value is "True". Ports created before

View File

@ -0,0 +1,19 @@
# 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 uplink_status_propagation_updatable \
as apidef
from neutron_lib.api import extensions
class Uplink_status_propagation_updatable(extensions.APIExtensionDescriptor):
api_definition = apidef

View File

@ -0,0 +1,42 @@
# Copyright (c) 2024 Red Hat Inc.
# 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 uplink_status_propagation_updatable \
as uspu
from neutron_lib.plugins.ml2 import api
from oslo_log import log as logging
from neutron.db import uplink_status_propagation_db as usp_db
LOG = logging.getLogger(__name__)
class UplinkStatusPropagationUpdatableExtensionDriver(
api.ExtensionDriver, usp_db.UplinkStatusPropagationMixin):
_supported_extension_alias = uspu.ALIAS
def initialize(self):
LOG.info('UplinkStatusPropagationUpdatableExtensionDriver '
'initialization complete')
@property
def extension_alias(self):
return self._supported_extension_alias
def process_update_port(self, context, data, result):
if uspu.PROPAGATE_UPLINK_STATUS in data:
self._process_update_port(context, data, result)

View File

@ -84,3 +84,4 @@ NETWORK_API_EXTENSIONS+=",tag-ports-during-bulk-creation"
NETWORK_API_EXTENSIONS+=",trunk"
NETWORK_API_EXTENSIONS+=",trunk-details"
NETWORK_API_EXTENSIONS+=",uplink-status-propagation"
NETWORK_API_EXTENSIONS+=",uplink-status-propagation-updatable"

View File

@ -0,0 +1,97 @@
# 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 ddt
from neutron_lib.api.definitions import uplink_status_propagation as usp
from neutron_lib.api.definitions import uplink_status_propagation_updatable \
as apidef
from neutron_lib.db import api as db_api
from neutron_lib.db import resource_extend
from neutron.db import db_base_plugin_v2
from neutron.db import uplink_status_propagation_db as usp_db
from neutron.tests.common import test_db_base_plugin_v2
class UplinkStatusPropagationUpdatableExtensionTestPlugin(
db_base_plugin_v2.NeutronDbPluginV2,
usp_db.UplinkStatusPropagationMixin):
"""Test plugin to mixin the uplink status propagation extension.
"""
supported_extension_aliases = [usp.ALIAS, apidef.ALIAS]
# TODO(ralonsoh): update ``uplink_status_propagation_updatable`` with
# COLLECTION_NAME=neutron_lib.api.definitions.port.COLLECTION_NAME
@staticmethod
@resource_extend.extends([usp.COLLECTION_NAME])
def _extend_network_project_default(port_res, port_db):
return usp_db.UplinkStatusPropagationMixin._extend_port_dict(
port_res, port_db)
def create_port(self, context, port):
with db_api.CONTEXT_WRITER.using(context):
new_port = super().create_port(context, port)
# Update the propagate_uplink_status in the database
p = port['port']
if 'propagate_uplink_status' not in p:
p['propagate_uplink_status'] = False
self._process_create_port(context, p, new_port)
return new_port
def update_port(self, context, port_id, port, **kwargs):
with db_api.CONTEXT_WRITER.using(context):
new_port = super().update_port(context, port_id, port)
# Update the propagate_uplink_status in the database
p = port['port']
if 'propagate_uplink_status' not in p:
p['propagate_uplink_status'] = False
self._process_update_port(context, p, new_port)
return new_port
@ddt.ddt
class UplinkStatusPropagationUpdatableExtensionTestCase(
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
"""Test API extension ``uplink-status-propagation-updatable`` attributes.
"""
def setUp(self, **kwargs):
plugin = ('neutron.tests.unit.extensions.test_uplink_status_'
'propagation_updatable.'
'UplinkStatusPropagationUpdatableExtensionTestPlugin')
super().setUp(plugin=plugin)
@ddt.data(True, False)
def test_update_port_propagate_uplink_status(
self, propagate_uplink_status):
name = 'propagate_uplink_status'
keys = [('name', name), ('admin_state_up', True),
('status', self.port_create_status),
(usp.PROPAGATE_UPLINK_STATUS, propagate_uplink_status)]
with self.port(name=name,
propagate_uplink_status=propagate_uplink_status
) as port:
for k, v in keys:
self.assertEqual(v, port['port'][k])
# Update the port with the opposite ``propagate_uplink_status``
# value and check it.
data = {'port': {usp.PROPAGATE_UPLINK_STATUS:
not propagate_uplink_status}}
req = self.new_update_request('ports', data, port['port']['id'])
req.get_response(self.api)
req = self.new_show_request('ports', port['port']['id'])
res = req.get_response(self.api)
port = self.deserialize(self.fmt, res)['port']
self.assertEqual(not propagate_uplink_status,
port[usp.PROPAGATE_UPLINK_STATUS])

View File

@ -0,0 +1,60 @@
# Copyright (c) 2024 Red Hat Inc.
# 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 ddt
from neutron_lib.api.definitions import uplink_status_propagation as usp_def
from neutron_lib.api.definitions import uplink_status_propagation_updatable \
as uspu_def
from neutron_lib.plugins import directory
from oslo_config import cfg
from neutron.tests.unit.plugins.ml2 import test_plugin
@ddt.ddt
class UplinkStatusPropagationUpdatableML2ExtDriverTestCase(
test_plugin.Ml2PluginV2TestCase):
_extension_drivers = [usp_def.ALIAS.replace('-', '_'),
uspu_def.ALIAS.replace('-', '_')]
def setUp(self):
cfg.CONF.set_override('extension_drivers',
self._extension_drivers,
group='ml2')
super().setUp()
self.plugin = directory.get_plugin()
@ddt.data(True, False)
def test_port_update_propagate_uplink_status(self, _status):
with self.network() as n:
args = {'port': {'name': 'test',
'network_id': n['network']['id'],
'tenant_id': n['network']['id'],
'device_id': '',
'device_owner': '',
'fixed_ips': '',
'propagate_uplink_status': _status,
'admin_state_up': True,
'status': 'ACTIVE'}}
try:
port = self.plugin.create_port(self.context, args)
args = {'port': {'propagate_uplink_status': not _status}}
self.plugin.update_port(self.context, port['id'], args)
port = self.plugin.get_port(self.context, port['id'])
self.assertEqual(not _status, port['propagate_uplink_status'])
finally:
if port:
self.plugin.delete_port(self.context, port['id'])

View File

@ -0,0 +1,7 @@
---
features:
- |
Added a new API extension ``uplink_status_propagation_updatable``. Now the
port attribute `propagate_uplink_status`` can be updated once the port is
created. The backend (ML2/SR-IOV) will receive the update and update the
VF state.

View File

@ -124,6 +124,7 @@ neutron.ml2.extension_drivers =
port_numa_affinity_policy = neutron.plugins.ml2.extensions.port_numa_affinity_policy:PortNumaAffinityPolicyExtensionDriver
port_trusted = neutron.plugins.ml2.extensions.port_trusted:PortTrustedExtensionDriver
uplink_status_propagation = neutron.plugins.ml2.extensions.uplink_status_propagation:UplinkStatusPropagationExtensionDriver
uplink_status_propagation_updatable = neutron.plugins.ml2.extensions.uplink_status_propagation_updatable:UplinkStatusPropagationUpdatableExtensionDriver
tag_ports_during_bulk_creation = neutron.plugins.ml2.extensions.tag_ports_during_bulk_creation:TagPortsDuringBulkCreationExtensionDriver
subnet_dns_publish_fixed_ip = neutron.plugins.ml2.extensions.subnet_dns_publish_fixed_ip:SubnetDNSPublishFixedIPExtensionDriver
dns_domain_keywords = neutron.plugins.ml2.extensions.dns_domain_keywords:DnsDomainKeywordsExtensionDriver