Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion neutron/common/ovn/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,9 @@ def validate_and_get_data_from_binding_profile(port):
if pbp_param_set.vnic_type:
if pbp_param_set.vnic_type != vnic_type:
continue
if capabilities and pbp_param_set.capability not in capabilities:
if (capabilities and
pbp_param_set.capability is not None and
pbp_param_set.capability not in capabilities):
continue
param_set = pbp_param_set.param_set
param_keys = param_set.keys()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ def tunnel_update(self, context, **kwargs):

@profiler.trace("rpc")
def tunnel_delete(self, context, **kwargs):
LOG.debug("tunnel_delete received")
LOG.debug("tunnel_delete received: %s", kwargs)
if not self.enable_tunneling:
return
tunnel_ip = kwargs.get('tunnel_ip')
Expand Down
28 changes: 28 additions & 0 deletions neutron/plugins/ml2/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,34 @@ def _reset_mac_for_direct_physical(self, orig_port, port, binding):
return True
return False

@registry.receives(resources.AGENT, [events.AFTER_DELETE])
def delete_agent_notified(self, resource, event, trigger,
payload=None):
context = payload.context
agent = payload.states[0]
if agent.binary != const.AGENT_PROCESS_OVS:
return
tunnel_id = payload.resource_id
tunnel_ip = agent.configurations.get('tunneling_ip')
tunnel_types = agent.configurations.get('tunnel_types')
if not tunnel_ip or not tunnel_types:
return
LOG.debug('Deleting tunnel id %s, and endpoints associated with '
'it (tunnel_ip: %s tunnel_types: %s)',
tunnel_id, tunnel_ip, tunnel_types)
for t_type in tunnel_types:
self.notifier.tunnel_delete(
context=context,
tunnel_ip=tunnel_ip,
tunnel_type=t_type)
try:
driver = self.type_manager.drivers.get(t_type)
except KeyError:
LOG.warning('Tunnel type %s is not registered, cannot '
'delete tunnel endpoint for it.', t_type)
else:
driver.obj.delete_endpoint(tunnel_ip)

@registry.receives(resources.AGENT, [events.AFTER_UPDATE])
def _retry_binding_revived_agents(self, resource, event, trigger,
payload=None):
Expand Down
38 changes: 30 additions & 8 deletions neutron/tests/unit/common/ovn/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,23 +606,24 @@ def setUp(self):
'neutron_lib.plugins.directory.get_plugin').start()
self.VNIC_FAKE_NORMAL = 'fake-vnic-normal'
self.VNIC_FAKE_OTHER = 'fake-vnic-other'
self.VNIC_FAKE_THIRD = 'fake-vnic-third'

# Replace constants.OVN_PORT_BINDING_PROFILE_PARAMS to allow synthesis
_params = constants.OVN_PORT_BINDING_PROFILE_PARAMS.copy()
_params.extend([
constants.OVNPortBindingProfileParamSet(
{'key': [str, type(None)]},
self.VNIC_FAKE_NORMAL, None),
constants.OVNPortBindingProfileParamSet(
{'key': [str], 'other_key': [str]},
self.VNIC_FAKE_OTHER, None),
constants.OVNPortBindingProfileParamSet(
{
'key': [str],
'other_key': [int],
'third_key': [str]
},
self.VNIC_FAKE_OTHER, constants.PORT_CAP_SWITCHDEV),
constants.OVNPortBindingProfileParamSet(
{'key': [str], 'other_key': [str]},
self.VNIC_FAKE_THIRD, None),
])
self.OVN_PORT_BINDING_PROFILE_PARAMS = mock.patch.object(
constants,
Expand Down Expand Up @@ -737,6 +738,27 @@ def test_valid_input(self):
{portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
constants.OVN_PORT_BINDING_PROFILE: expect}))

def test_valid_input_surplus_capabilities(self):
capabilities = ['rx', 'tx', 'sg', 'tso', 'gso', 'gro', 'rxvlan',
'txvlan', 'rxhash', 'rdma', 'txudptnl']
binding_profile = {
'pci_vendor_info': 'dead:beef',
'pci_slot': '0000:ca:fe.42',
'physical_network': 'physnet1',
'card_serial_number': 'AB2000X00042',
'pf_mac_address': '00:53:00:00:00:42',
'vf_num': 42,
constants.PORT_CAP_PARAM: capabilities
}
expect = binding_profile.copy()
del(expect[constants.PORT_CAP_PARAM])
self.assertEqual(
utils.BPInfo(expect, portbindings.VNIC_REMOTE_MANAGED,
capabilities),
utils.validate_and_get_data_from_binding_profile(
{portbindings.VNIC_TYPE: portbindings.VNIC_REMOTE_MANAGED,
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))

def test_valid_input_surplus_keys(self):
# Confirm that extra keys are allowed
binding_profile = {
Expand Down Expand Up @@ -810,12 +832,12 @@ def test_overlapping_param_set_different_vnic_type(self):
utils.validate_and_get_data_from_binding_profile(
{portbindings.VNIC_TYPE: self.VNIC_FAKE_NORMAL,
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))
# It is valid for VNIC_FAKE_OTHER
# It is valid for VNIC_FAKE_THIRD
expected_bp = binding_profile.copy()
self.assertEqual(
utils.BPInfo(expected_bp, self.VNIC_FAKE_OTHER, []),
utils.BPInfo(expected_bp, self.VNIC_FAKE_THIRD, []),
utils.validate_and_get_data_from_binding_profile(
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
{portbindings.VNIC_TYPE: self.VNIC_FAKE_THIRD,
constants.OVN_PORT_BINDING_PROFILE: binding_profile}))

def test_overlapping_param_set_different_vnic_type_and_capability(self):
Expand All @@ -825,13 +847,13 @@ def test_overlapping_param_set_different_vnic_type_and_capability(self):
'other_key': 42,
'third_key': 'value',
}
# This param set is not valid for VNIC_FAKE_OTHER without capability
# This param set is not valid for VNIC_FAKE_THIRD without capability
expect = binding_profile.copy()
del(expect['third_key'])
self.assertRaises(
neutron_lib.exceptions.InvalidInput,
utils.validate_and_get_data_from_binding_profile,
{portbindings.VNIC_TYPE: self.VNIC_FAKE_OTHER,
{portbindings.VNIC_TYPE: self.VNIC_FAKE_THIRD,
constants.OVN_PORT_BINDING_PROFILE: binding_profile})
# This param set is also not valid as the capabilities do not match
binding_profile = {
Expand Down
60 changes: 60 additions & 0 deletions neutron/tests/unit/plugins/ml2/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_utils import netutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
import testtools
import webob
Expand All @@ -65,6 +66,7 @@
from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import db as ml2_db
from neutron.plugins.ml2 import driver_context
from neutron.plugins.ml2.drivers import type_tunnel
from neutron.plugins.ml2.drivers import type_vlan
from neutron.plugins.ml2 import managers
from neutron.plugins.ml2 import models
Expand Down Expand Up @@ -596,6 +598,64 @@ def test_update_network_with_incorrect_resource_body(self):
self.assertIn("network", res.json['NeutronError']['message'])


class TestMl2AgentNotifications(Ml2PluginV2TestCase):

class Agent:
def __init__(self, agent_dict):
for field in agent_dict:
setattr(self, field, agent_dict[field])

def test_delete_agent_notified(self):
agent_status = {'agent_type': constants.AGENT_TYPE_OVS,
'binary': constants.AGENT_PROCESS_OVS,
'host': 'AHOST',
'topic': 'N/A',
'configurations': {'tunnel_types': ['vxlan'],
'tunneling_ip': '100.101.2.3'}}
agent = self.plugin.create_or_update_agent(self.context,
dict(agent_status),
timeutils.utcnow())
agnt = self.Agent(agent[1])
with mock.patch.object(
self.plugin.notifier, 'tunnel_delete') as m_t_del:
with mock.patch.object(
type_tunnel.EndpointTunnelTypeDriver,
'delete_endpoint') as m_del_ep:
self.plugin.delete_agent_notified(
resource='agent', event='after_delete', trigger=None,
payload=events.DBEventPayload(
self.context, states=(agnt,),
resource_id=agent[1]['id']))
m_t_del.assert_called_once_with(
context=mock.ANY,
tunnel_ip='100.101.2.3', tunnel_type='vxlan')
m_del_ep.assert_called_once_with('100.101.2.3')

def test_delete_agent_notified_non_ovs(self):
agent_status = {'agent_type': constants.AGENT_TYPE_NIC_SWITCH,
'binary': constants.AGENT_PROCESS_NIC_SWITCH,
'host': 'AHOST',
'topic': 'N/A',
'configurations': {'tunnel_types': ['vxlan'],
'tunneling_ip': '100.101.2.3'}}
agent = self.plugin.create_or_update_agent(self.context,
dict(agent_status),
timeutils.utcnow())
agnt = self.Agent(agent[1])
with mock.patch.object(
self.plugin.notifier, 'tunnel_delete') as m_t_del:
with mock.patch.object(
type_tunnel.EndpointTunnelTypeDriver,
'delete_endpoint') as m_del_ep:
self.plugin.delete_agent_notified(
resource='agent', event='after_delete', trigger=None,
payload=events.DBEventPayload(
self.context, states=(agnt,),
resource_id=agent[1]['id']))
m_t_del.assert_not_called()
m_del_ep.assert_not_called()


class TestMl2NetworksV2AgentMechDrivers(Ml2PluginV2TestCase):

_mechanism_drivers = ['logger', 'test', 'test_with_agent']
Expand Down