diff --git a/neutron/notifiers/nova.py b/neutron/notifiers/nova.py index 1269187ec98..9cb6c3880e2 100644 --- a/neutron/notifiers/nova.py +++ b/neutron/notifiers/nova.py @@ -281,6 +281,9 @@ def send_events(self, batched_events): try: response = novaclient.server_external_events.create( batched_events) + except ks_exceptions.EndpointNotFound: + LOG.exception("Nova endpoint not found, invalidating the session") + self.session.invalidate() except nova_exceptions.NotFound: LOG.debug("Nova returned NotFound for event: %s", batched_events) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py index 32931841c12..55a43bcf9dc 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py @@ -73,10 +73,14 @@ def set_vf_state(self, vf_index, state, auto=False): @param auto: set link_state to auto (0) """ ip = self.device(self.dev_name) - if auto: + # NOTE(ralonsoh): the state=False --> "disable" (2) has precedence over + # "auto" (0) and "enable" (1). + if state is False: + link_state = 2 + elif auto: link_state = 0 else: - link_state = 1 if state else 2 + link_state = 1 vf_config = {'vf': vf_index, 'link_state': link_state} ip.link.set_vf_feature(vf_config) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py index c3a63bab80a..65527020732 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py @@ -19,7 +19,6 @@ import functools import multiprocessing import operator -import signal import threading import types import uuid @@ -43,6 +42,7 @@ from oslo_config import cfg from oslo_db import exception as os_db_exc from oslo_log import log +from oslo_service import service as oslo_service from oslo_utils import timeutils from ovsdbapp.backend.ovs_idl import idlutils @@ -312,8 +312,9 @@ def _setup_hash_ring(self): themselves to the hash ring. """ # Attempt to remove the node from the ring when the worker stops + sh = oslo_service.SignalHandler() atexit.register(self._remove_node_from_hash_ring) - signal.signal(signal.SIGTERM, self._remove_node_from_hash_ring) + sh.add_handler("SIGTERM", self._remove_node_from_hash_ring) admin_context = n_context.get_admin_context() if not self._hash_ring_probe_event.is_set(): diff --git a/neutron/plugins/ml2/ovo_rpc.py b/neutron/plugins/ml2/ovo_rpc.py index a63998c1698..788860bf180 100644 --- a/neutron/plugins/ml2/ovo_rpc.py +++ b/neutron/plugins/ml2/ovo_rpc.py @@ -13,7 +13,6 @@ import atexit import queue -import signal import threading import traceback import weakref @@ -24,6 +23,7 @@ from neutron_lib import context as n_ctx from neutron_lib.db import api as db_api from oslo_log import log as logging +from oslo_service import service from neutron.api.rpc.callbacks import events as rpc_events from neutron.api.rpc.handlers import resources_rpc @@ -38,8 +38,9 @@ def _setup_change_handlers_cleanup(): atexit.register(_ObjectChangeHandler.clean_up) - signal.signal(signal.SIGINT, _ObjectChangeHandler.clean_up) - signal.signal(signal.SIGTERM, _ObjectChangeHandler.clean_up) + sh = service.SignalHandler() + sh.add_handler("SIGINT", _ObjectChangeHandler.clean_up) + sh.add_handler("SIGTERM", _ObjectChangeHandler.clean_up) class _ObjectChangeHandler(object): diff --git a/neutron/services/tag/tag_plugin.py b/neutron/services/tag/tag_plugin.py index c44860034d5..ee2b468c8bb 100644 --- a/neutron/services/tag/tag_plugin.py +++ b/neutron/services/tag/tag_plugin.py @@ -47,7 +47,17 @@ def __new__(cls, *args, **kwargs): def _extend_tags_dict(response_data, db_data): if not directory.get_plugin(tagging.TAG_PLUGIN_TYPE): return - tags = [tag_db.tag for tag_db in db_data.standard_attr.tags] + try: + tags = [tag_db.tag for tag_db in db_data.standard_attr.tags] + except AttributeError: + # NOTE(ralonsoh): this method can be called from a "list" + # operation. If one resource and its "standardattr" register is + # deleted concurrently, the "standard_attr" field retrieval will + # fail. + # The "list" operation is protected with a READER transaction + # context; however this is failing with the DB PostgreSQL backend. + # https://bugs.launchpad.net/neutron/+bug/2078787 + tags = [] response_data['tags'] = tags @db_api.CONTEXT_READER diff --git a/neutron/tests/unit/notifiers/test_nova.py b/neutron/tests/unit/notifiers/test_nova.py index 88c9690c2dd..302081897f1 100644 --- a/neutron/tests/unit/notifiers/test_nova.py +++ b/neutron/tests/unit/notifiers/test_nova.py @@ -237,6 +237,16 @@ def test_no_notification_notify_nova_on_port_data_changes_false(self): {}, {}) self.assertFalse(send_events.called) + @mock.patch('novaclient.client.Client') + def test_nova_send_events_noendpoint_invalidate_session(self, mock_client): + create = mock_client().server_external_events.create + create.side_effect = ks_exc.EndpointNotFound + with mock.patch.object(self.nova_notifier.session, + 'invalidate', return_value=True) as mock_sess: + self.nova_notifier.send_events([]) + create.assert_called() + mock_sess.assert_called() + @mock.patch('novaclient.client.Client') def test_nova_send_events_returns_bad_list(self, mock_client): create = mock_client().server_external_events.create diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py index 8276e189a4f..962c0772ee3 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py @@ -67,20 +67,29 @@ def test_get_vf_state_not_present(self): self.assertEqual(pci_lib.LinkState.disable.name, result) def test_set_vf_state(self): + # state=True, auto=False --> link_state=enable self.pci_wrapper.set_vf_state(self.VF_INDEX, True) vf = {'vf': self.VF_INDEX, 'link_state': 1} self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf) + # state=False, auto=False --> link_state=disable self.mock_ip_device.link.set_vf_feature.reset_mock() self.pci_wrapper.set_vf_state(self.VF_INDEX, False) vf = {'vf': self.VF_INDEX, 'link_state': 2} self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf) + # state=True, auto=True --> link_state=auto self.mock_ip_device.link.set_vf_feature.reset_mock() - self.pci_wrapper.set_vf_state(self.VF_INDEX, False, auto=True) + self.pci_wrapper.set_vf_state(self.VF_INDEX, True, auto=True) vf = {'vf': self.VF_INDEX, 'link_state': 0} self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf) + # state=False, auto=True --> link_state=disable + self.mock_ip_device.link.set_vf_feature.reset_mock() + self.pci_wrapper.set_vf_state(self.VF_INDEX, False, auto=True) + vf = {'vf': self.VF_INDEX, 'link_state': 2} + self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf) + def test_set_vf_spoofcheck(self): self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, True) vf = {'vf': self.VF_INDEX, 'spoofchk': 1} diff --git a/releasenotes/notes/sriov-vf-state-disable-has-precedence-2adecdf959dc0f9e.yaml b/releasenotes/notes/sriov-vf-state-disable-has-precedence-2adecdf959dc0f9e.yaml new file mode 100644 index 00000000000..6ab968a767e --- /dev/null +++ b/releasenotes/notes/sriov-vf-state-disable-has-precedence-2adecdf959dc0f9e.yaml @@ -0,0 +1,7 @@ +--- +security: + - | + A ML2/SR-IOV port with status=DOWN will always set the VF link state to + "disable", regardless of the ``propagate_uplink_status`` port field value. + The port disabling, to stop any transmission, has precedence over the + link state "auto" value. diff --git a/requirements.txt b/requirements.txt index 58531dea3c3..0fc98413d18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ oslo.privsep>=2.3.0 # Apache-2.0 oslo.reports>=1.18.0 # Apache-2.0 oslo.rootwrap>=5.15.0 # Apache-2.0 oslo.serialization>=2.25.0 # Apache-2.0 -oslo.service>=2.8.0 # Apache-2.0 +oslo.service>=3.1.2 # Apache-2.0 oslo.upgradecheck>=1.3.0 # Apache-2.0 oslo.utils>=4.8.0 # Apache-2.0 oslo.versionedobjects>=1.35.1 # Apache-2.0