Skip to content

Commit e2c1c20

Browse files
authored
Merge pull request #224 from stackhpc/upstream/2025.1-2025-09-08
Synchronise 2025.1 with upstream
2 parents abd1b57 + e11a326 commit e2c1c20

File tree

5 files changed

+122
-10
lines changed

5 files changed

+122
-10
lines changed

neutron/common/ovn/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,9 @@ def validate_and_get_data_from_binding_profile(port):
463463
if pbp_param_set.vnic_type:
464464
if pbp_param_set.vnic_type != vnic_type:
465465
continue
466-
if capabilities and pbp_param_set.capability not in capabilities:
466+
if (capabilities and
467+
pbp_param_set.capability is not None and
468+
pbp_param_set.capability not in capabilities):
467469
continue
468470
param_set = pbp_param_set.param_set
469471
param_keys = param_set.keys()

neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ def tunnel_update(self, context, **kwargs):
901901

902902
@profiler.trace("rpc")
903903
def tunnel_delete(self, context, **kwargs):
904-
LOG.debug("tunnel_delete received")
904+
LOG.debug("tunnel_delete received: %s", kwargs)
905905
if not self.enable_tunneling:
906906
return
907907
tunnel_ip = kwargs.get('tunnel_ip')

neutron/plugins/ml2/plugin.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,34 @@ def _reset_mac_for_direct_physical(self, orig_port, port, binding):
492492
return True
493493
return False
494494

495+
@registry.receives(resources.AGENT, [events.AFTER_DELETE])
496+
def delete_agent_notified(self, resource, event, trigger,
497+
payload=None):
498+
context = payload.context
499+
agent = payload.states[0]
500+
if agent.binary != const.AGENT_PROCESS_OVS:
501+
return
502+
tunnel_id = payload.resource_id
503+
tunnel_ip = agent.configurations.get('tunneling_ip')
504+
tunnel_types = agent.configurations.get('tunnel_types')
505+
if not tunnel_ip or not tunnel_types:
506+
return
507+
LOG.debug('Deleting tunnel id %s, and endpoints associated with '
508+
'it (tunnel_ip: %s tunnel_types: %s)',
509+
tunnel_id, tunnel_ip, tunnel_types)
510+
for t_type in tunnel_types:
511+
self.notifier.tunnel_delete(
512+
context=context,
513+
tunnel_ip=tunnel_ip,
514+
tunnel_type=t_type)
515+
try:
516+
driver = self.type_manager.drivers.get(t_type)
517+
except KeyError:
518+
LOG.warning('Tunnel type %s is not registered, cannot '
519+
'delete tunnel endpoint for it.', t_type)
520+
else:
521+
driver.obj.delete_endpoint(tunnel_ip)
522+
495523
@registry.receives(resources.AGENT, [events.AFTER_UPDATE])
496524
def _retry_binding_revived_agents(self, resource, event, trigger,
497525
payload=None):

neutron/tests/unit/common/ovn/test_utils.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -606,23 +606,24 @@ def setUp(self):
606606
'neutron_lib.plugins.directory.get_plugin').start()
607607
self.VNIC_FAKE_NORMAL = 'fake-vnic-normal'
608608
self.VNIC_FAKE_OTHER = 'fake-vnic-other'
609+
self.VNIC_FAKE_THIRD = 'fake-vnic-third'
609610

610611
# Replace constants.OVN_PORT_BINDING_PROFILE_PARAMS to allow synthesis
611612
_params = constants.OVN_PORT_BINDING_PROFILE_PARAMS.copy()
612613
_params.extend([
613614
constants.OVNPortBindingProfileParamSet(
614615
{'key': [str, type(None)]},
615616
self.VNIC_FAKE_NORMAL, None),
616-
constants.OVNPortBindingProfileParamSet(
617-
{'key': [str], 'other_key': [str]},
618-
self.VNIC_FAKE_OTHER, None),
619617
constants.OVNPortBindingProfileParamSet(
620618
{
621619
'key': [str],
622620
'other_key': [int],
623621
'third_key': [str]
624622
},
625623
self.VNIC_FAKE_OTHER, constants.PORT_CAP_SWITCHDEV),
624+
constants.OVNPortBindingProfileParamSet(
625+
{'key': [str], 'other_key': [str]},
626+
self.VNIC_FAKE_THIRD, None),
626627
])
627628
self.OVN_PORT_BINDING_PROFILE_PARAMS = mock.patch.object(
628629
constants,
@@ -737,6 +738,27 @@ def test_valid_input(self):
737738
{portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
738739
constants.OVN_PORT_BINDING_PROFILE: expect}))
739740

741+
def test_valid_input_surplus_capabilities(self):
742+
capabilities = ['rx', 'tx', 'sg', 'tso', 'gso', 'gro', 'rxvlan',
743+
'txvlan', 'rxhash', 'rdma', 'txudptnl']
744+
binding_profile = {
745+
'pci_vendor_info': 'dead:beef',
746+
'pci_slot': '0000:ca:fe.42',
747+
'physical_network': 'physnet1',
748+
'card_serial_number': 'AB2000X00042',
749+
'pf_mac_address': '00:53:00:00:00:42',
750+
'vf_num': 42,
751+
constants.PORT_CAP_PARAM: capabilities
752+
}
753+
expect = binding_profile.copy()
754+
del(expect[constants.PORT_CAP_PARAM])
755+
self.assertEqual(
756+
utils.BPInfo(expect, portbindings.VNIC_REMOTE_MANAGED,
757+
capabilities),
758+
utils.validate_and_get_data_from_binding_profile(
759+
{portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
760+
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
761+
740762
def test_valid_input_surplus_keys(self):
741763
# Confirm that extra keys are allowed
742764
binding_profile = {
@@ -810,12 +832,12 @@ def test_overlapping_param_set_different_vnic_type(self):
810832
utils.validate_and_get_data_from_binding_profile(
811833
{portbindings.VNIC_TYPE: self.VNIC_FAKE_NORMAL,
812834
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
813-
# It is valid for VNIC_FAKE_OTHER
835+
# It is valid for VNIC_FAKE_THIRD
814836
expected_bp = binding_profile.copy()
815837
self.assertEqual(
816-
utils.BPInfo(expected_bp, self.VNIC_FAKE_OTHER, []),
838+
utils.BPInfo(expected_bp, self.VNIC_FAKE_THIRD, []),
817839
utils.validate_and_get_data_from_binding_profile(
818-
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
840+
{portbindings.VNIC_TYPE: self.VNIC_FAKE_THIRD,
819841
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
820842

821843
def test_overlapping_param_set_different_vnic_type_and_capability(self):
@@ -825,13 +847,13 @@ def test_overlapping_param_set_different_vnic_type_and_capability(self):
825847
'other_key': 42,
826848
'third_key': 'value',
827849
}
828-
# This param set is not valid for VNIC_FAKE_OTHER without capability
850+
# This param set is not valid for VNIC_FAKE_THIRD without capability
829851
expect = binding_profile.copy()
830852
del(expect['third_key'])
831853
self.assertRaises(
832854
neutron_lib.exceptions.InvalidInput,
833855
utils.validate_and_get_data_from_binding_profile,
834-
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
856+
{portbindings.VNIC_TYPE: self.VNIC_FAKE_THIRD,
835857
constants.OVN_PORT_BINDING_PROFILE: binding_profile})
836858
# This param set is also not valid as the capabilities do not match
837859
binding_profile = {

neutron/tests/unit/plugins/ml2/test_plugin.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from oslo_config import cfg
4444
from oslo_db import exception as db_exc
4545
from oslo_utils import netutils
46+
from oslo_utils import timeutils
4647
from oslo_utils import uuidutils
4748
import testtools
4849
import webob
@@ -65,6 +66,7 @@
6566
from neutron.plugins.ml2.common import exceptions as ml2_exc
6667
from neutron.plugins.ml2 import db as ml2_db
6768
from neutron.plugins.ml2 import driver_context
69+
from neutron.plugins.ml2.drivers import type_tunnel
6870
from neutron.plugins.ml2.drivers import type_vlan
6971
from neutron.plugins.ml2 import managers
7072
from neutron.plugins.ml2 import models
@@ -596,6 +598,64 @@ def test_update_network_with_incorrect_resource_body(self):
596598
self.assertIn("network", res.json['NeutronError']['message'])
597599

598600

601+
class TestMl2AgentNotifications(Ml2PluginV2TestCase):
602+
603+
class Agent:
604+
def __init__(self, agent_dict):
605+
for field in agent_dict:
606+
setattr(self, field, agent_dict[field])
607+
608+
def test_delete_agent_notified(self):
609+
agent_status = {'agent_type': constants.AGENT_TYPE_OVS,
610+
'binary': constants.AGENT_PROCESS_OVS,
611+
'host': 'AHOST',
612+
'topic': 'N/A',
613+
'configurations': {'tunnel_types': ['vxlan'],
614+
'tunneling_ip': '100.101.2.3'}}
615+
agent = self.plugin.create_or_update_agent(self.context,
616+
dict(agent_status),
617+
timeutils.utcnow())
618+
agnt = self.Agent(agent[1])
619+
with mock.patch.object(
620+
self.plugin.notifier, 'tunnel_delete') as m_t_del:
621+
with mock.patch.object(
622+
type_tunnel.EndpointTunnelTypeDriver,
623+
'delete_endpoint') as m_del_ep:
624+
self.plugin.delete_agent_notified(
625+
resource='agent', event='after_delete', trigger=None,
626+
payload=events.DBEventPayload(
627+
self.context, states=(agnt,),
628+
resource_id=agent[1]['id']))
629+
m_t_del.assert_called_once_with(
630+
context=mock.ANY,
631+
tunnel_ip='100.101.2.3', tunnel_type='vxlan')
632+
m_del_ep.assert_called_once_with('100.101.2.3')
633+
634+
def test_delete_agent_notified_non_ovs(self):
635+
agent_status = {'agent_type': constants.AGENT_TYPE_NIC_SWITCH,
636+
'binary': constants.AGENT_PROCESS_NIC_SWITCH,
637+
'host': 'AHOST',
638+
'topic': 'N/A',
639+
'configurations': {'tunnel_types': ['vxlan'],
640+
'tunneling_ip': '100.101.2.3'}}
641+
agent = self.plugin.create_or_update_agent(self.context,
642+
dict(agent_status),
643+
timeutils.utcnow())
644+
agnt = self.Agent(agent[1])
645+
with mock.patch.object(
646+
self.plugin.notifier, 'tunnel_delete') as m_t_del:
647+
with mock.patch.object(
648+
type_tunnel.EndpointTunnelTypeDriver,
649+
'delete_endpoint') as m_del_ep:
650+
self.plugin.delete_agent_notified(
651+
resource='agent', event='after_delete', trigger=None,
652+
payload=events.DBEventPayload(
653+
self.context, states=(agnt,),
654+
resource_id=agent[1]['id']))
655+
m_t_del.assert_not_called()
656+
m_del_ep.assert_not_called()
657+
658+
599659
class TestMl2NetworksV2AgentMechDrivers(Ml2PluginV2TestCase):
600660

601661
_mechanism_drivers = ['logger', 'test', 'test_with_agent']

0 commit comments

Comments
 (0)