You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3653 lines
174 KiB
3653 lines
174 KiB
# Copyright (c) 2012 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. |
|
|
|
import sys |
|
import time |
|
|
|
import mock |
|
from neutron_lib.agent import constants as agent_consts |
|
from neutron_lib import constants as n_const |
|
from oslo_config import cfg |
|
from oslo_log import log |
|
import oslo_messaging |
|
import testtools |
|
|
|
from neutron._i18n import _ |
|
from neutron.agent.common import ovs_lib |
|
from neutron.agent.common import utils |
|
from neutron.agent.linux import async_process |
|
from neutron.agent.linux import ip_lib |
|
from neutron.common import constants as c_const |
|
from neutron.common import rpc as n_rpc |
|
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc |
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants |
|
from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \ |
|
as ovs_agent |
|
from neutron.tests import base |
|
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ |
|
import ovs_test_base |
|
from neutron.tests.unit.plugins.ml2.drivers.openvswitch.agent \ |
|
import test_vlanmanager |
|
|
|
|
|
NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' |
|
PULLAPI = 'neutron.api.rpc.handlers.resources_rpc.ResourcesPullRpcApi' |
|
OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0" |
|
|
|
FAKE_MAC = '00:11:22:33:44:55' |
|
FAKE_IP1 = '10.0.0.1' |
|
FAKE_IP2 = '10.0.0.2' |
|
FAKE_IP6 = '2001:db8:42:42::10' |
|
|
|
TEST_PORT_ID1 = 'port-id-1' |
|
TEST_PORT_ID2 = 'port-id-2' |
|
TEST_PORT_ID3 = 'port-id-3' |
|
|
|
TEST_NETWORK_ID1 = 'net-id-1' |
|
TEST_NETWORK_ID2 = 'net-id-2' |
|
|
|
DEVICE_OWNER_COMPUTE = n_const.DEVICE_OWNER_COMPUTE_PREFIX + 'fake' |
|
|
|
|
|
class FakeVif(object): |
|
ofport = 99 |
|
port_name = 'name' |
|
vif_mac = 'aa:bb:cc:11:22:33' |
|
|
|
|
|
class MockFixedIntervalLoopingCall(object): |
|
def __init__(self, f): |
|
self.f = f |
|
|
|
def start(self, interval=0): |
|
self.f() |
|
|
|
|
|
class ValidateTunnelTypes(ovs_test_base.OVSAgentConfigTestBase): |
|
|
|
def setUp(self): |
|
super(ValidateTunnelTypes, self).setUp() |
|
self.mock_validate_local_ip = mock.patch.object( |
|
self.mod_agent, 'validate_local_ip').start() |
|
|
|
def test_validate_tunnel_types_succeeds(self): |
|
cfg.CONF.set_override('local_ip', '10.10.10.10', group='OVS') |
|
cfg.CONF.set_override('tunnel_types', [n_const.TYPE_GRE], |
|
group='AGENT') |
|
self.mod_agent.validate_tunnel_config(cfg.CONF.AGENT.tunnel_types, |
|
cfg.CONF.OVS.local_ip) |
|
self.mock_validate_local_ip.assert_called_once_with('10.10.10.10') |
|
|
|
def test_validate_tunnel_types_fails_for_invalid_tunnel_type(self): |
|
cfg.CONF.set_override('local_ip', '10.10.10.10', group='OVS') |
|
cfg.CONF.set_override('tunnel_types', ['foobar'], group='AGENT') |
|
with testtools.ExpectedException(SystemExit): |
|
self.mod_agent.validate_tunnel_config(cfg.CONF.AGENT.tunnel_types, |
|
cfg.CONF.OVS.local_ip) |
|
|
|
|
|
class TestOvsNeutronAgent(object): |
|
|
|
def setUp(self): |
|
super(TestOvsNeutronAgent, self).setUp() |
|
self.useFixture(test_vlanmanager.LocalVlanManagerFixture()) |
|
mock.patch(PULLAPI).start() |
|
notifier_p = mock.patch(NOTIFIER) |
|
notifier_cls = notifier_p.start() |
|
self.notifier = mock.Mock() |
|
notifier_cls.return_value = self.notifier |
|
systemd_patch = mock.patch('oslo_service.systemd.notify_once') |
|
self.systemd_notify = systemd_patch.start() |
|
|
|
cfg.CONF.set_default('firewall_driver', |
|
'neutron.agent.firewall.NoopFirewallDriver', |
|
group='SECURITYGROUP') |
|
cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT') |
|
cfg.CONF.set_default('local_ip', '127.0.0.1', 'OVS') |
|
cfg.CONF.set_default('host', 'host') |
|
mock.patch( |
|
'neutron.agent.ovsdb.native.helpers.enable_connection_uri').start() |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.OVSBridge.get_ports_attributes', |
|
return_value=[]).start() |
|
|
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', |
|
new_callable=mock.PropertyMock, |
|
return_value={}).start() |
|
mock.patch('neutron.agent.ovsdb.impl_idl._connection').start() |
|
self.agent = self._make_agent() |
|
self.agent.sg_agent = mock.Mock() |
|
|
|
def _make_agent(self): |
|
with mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_integration_br'),\ |
|
mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_ancillary_bridges', |
|
return_value=[]),\ |
|
mock.patch('neutron.agent.linux.ip_lib.get_device_mac', |
|
return_value='00:00:00:00:00:01'),\ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'),\ |
|
mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', |
|
new=MockFixedIntervalLoopingCall),\ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', |
|
return_value=[]): |
|
ext_manager = mock.Mock() |
|
agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), |
|
ext_manager, cfg.CONF) |
|
agent.tun_br = self.br_tun_cls(br_name='br-tun') |
|
return agent |
|
|
|
def _mock_port_bound(self, ofport=None, new_local_vlan=None, |
|
old_local_vlan=None, db_get_val=None): |
|
port = mock.Mock() |
|
port.ofport = ofport |
|
net_uuid = 'my-net-uuid' |
|
fixed_ips = [{'subnet_id': 'my-subnet-uuid', |
|
'ip_address': '1.1.1.1'}] |
|
if old_local_vlan is not None: |
|
self.agent.vlan_manager.add( |
|
net_uuid, old_local_vlan, None, None, None) |
|
with mock.patch.object(self.agent, 'int_br', autospec=True) as int_br: |
|
int_br.db_get_val.return_value = db_get_val |
|
int_br.set_db_attribute.return_value = True |
|
needs_binding = self.agent.port_bound( |
|
port, net_uuid, 'local', None, None, |
|
fixed_ips, DEVICE_OWNER_COMPUTE, False) |
|
if db_get_val is None: |
|
self.assertEqual(0, int_br.set_db_attribute.call_count) |
|
self.assertFalse(needs_binding) |
|
else: |
|
vlan_mapping = {'net_uuid': net_uuid, |
|
'network_type': 'local', |
|
'physical_network': 'None'} |
|
int_br.set_db_attribute.assert_called_once_with( |
|
"Port", mock.ANY, "other_config", vlan_mapping) |
|
self.assertTrue(needs_binding) |
|
|
|
def test_setup_physical_bridges_during_agent_initialization(self): |
|
with mock.patch.object( |
|
self.mod_agent.OVSNeutronAgent, |
|
'setup_physical_bridges') as setup_physical_bridges,\ |
|
mock.patch.object( |
|
self.mod_agent.OVSNeutronAgent, 'setup_rpc') as setup_rpc: |
|
setup_rpc.side_effect = oslo_messaging.MessagingException( |
|
"Test communication failure") |
|
try: |
|
self._make_agent() |
|
except oslo_messaging.MessagingException: |
|
pass |
|
setup_physical_bridges.assert_called_once_with(mock.ANY) |
|
|
|
def test_datapath_type_system(self): |
|
# verify kernel datapath is default |
|
expected = constants.OVS_DATAPATH_SYSTEM |
|
self.assertEqual(expected, self.agent.int_br.datapath_type) |
|
|
|
def test_datapath_type_netdev(self): |
|
|
|
with mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_integration_br'), \ |
|
mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_ancillary_bridges', |
|
return_value=[]), \ |
|
mock.patch('neutron.agent.linux.ip_lib.get_device_mac', |
|
return_value='00:00:00:00:00:01'), \ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), \ |
|
mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', |
|
new=MockFixedIntervalLoopingCall), \ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', |
|
return_value=[]), \ |
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config', |
|
new_callable=mock.PropertyMock, |
|
return_value={'datapath_types': ['netdev']}): |
|
# validate setting non default datapath |
|
expected = constants.OVS_DATAPATH_NETDEV |
|
cfg.CONF.set_override('datapath_type', |
|
expected, |
|
group='OVS') |
|
ext_manager = mock.Mock() |
|
self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), |
|
ext_manager, cfg.CONF) |
|
self.assertEqual(expected, self.agent.int_br.datapath_type) |
|
|
|
def test_agent_type_ovs(self): |
|
# verify agent_type is default |
|
expected = n_const.AGENT_TYPE_OVS |
|
self.assertEqual(expected, |
|
self.agent.agent_state['agent_type']) |
|
|
|
def test_agent_available_local_vlans(self): |
|
expected = [n_const.MIN_VLAN_TAG, |
|
n_const.MIN_VLAN_TAG + 1, |
|
n_const.MAX_VLAN_TAG - 1, |
|
n_const.MAX_VLAN_TAG] |
|
exception = [n_const.MIN_VLAN_TAG - 1, |
|
n_const.MAX_VLAN_TAG + 1, |
|
n_const.MAX_VLAN_TAG + 2] |
|
available_vlan = self.agent.available_local_vlans |
|
for tag in expected: |
|
self.assertIn(tag, available_vlan) |
|
for tag in exception: |
|
self.assertNotIn(tag, available_vlan) |
|
|
|
def test_agent_type_alt(self): |
|
with mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_integration_br'),\ |
|
mock.patch.object(self.mod_agent.OVSNeutronAgent, |
|
'setup_ancillary_bridges', |
|
return_value=[]), \ |
|
mock.patch('neutron.agent.linux.ip_lib.get_device_mac', |
|
return_value='00:00:00:00:00:01'), \ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), \ |
|
mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall', |
|
new=MockFixedIntervalLoopingCall), \ |
|
mock.patch( |
|
'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports', |
|
return_value=[]): |
|
# validate setting non default agent_type |
|
expected = 'alt agent type' |
|
cfg.CONF.set_override('agent_type', |
|
expected, |
|
group='AGENT') |
|
ext_manager = mock.Mock() |
|
self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(), |
|
ext_manager, cfg.CONF) |
|
self.assertEqual(expected, |
|
self.agent.agent_state['agent_type']) |
|
|
|
def _test_restore_local_vlan_maps(self, tag, segmentation_id='1'): |
|
port = mock.Mock() |
|
port.port_name = 'fake_port' |
|
net_uuid = 'fake_network_id' |
|
local_vlan_map = {'net_uuid': net_uuid, |
|
'network_type': 'vlan', |
|
'physical_network': 'fake_network'} |
|
if segmentation_id is not None: |
|
local_vlan_map['segmentation_id'] = segmentation_id |
|
|
|
# this is for the call inside get_vif_ports() |
|
get_interfaces = [{'name': port.port_name, |
|
'ofport': '1', |
|
'external_ids': { |
|
'iface-id': '1', |
|
'attached-mac': 'mac1'}}, |
|
{'name': 'invalid', |
|
'ofport': ovs_lib.INVALID_OFPORT, |
|
'external_ids': { |
|
'iface-id': '2', |
|
'attached-mac': 'mac2'}}, |
|
{'name': 'unassigned', |
|
'ofport': ovs_lib.UNASSIGNED_OFPORT, |
|
'external_ids': { |
|
'iface-id': '3', |
|
'attached-mac': 'mac3'}}] |
|
# this is for the call inside _restore_local_vlan_map() |
|
get_ports = [{'name': port.port_name, |
|
'other_config': local_vlan_map, |
|
'tag': tag}] |
|
|
|
with mock.patch.object(self.agent.int_br, |
|
'get_ports_attributes', |
|
side_effect=[get_interfaces, get_ports]) as gpa: |
|
self.agent._restore_local_vlan_map() |
|
expected_hints = {} |
|
if tag: |
|
expected_hints[net_uuid] = tag |
|
self.assertEqual(expected_hints, self.agent._local_vlan_hints) |
|
# make sure invalid and unassigned ports were skipped |
|
gpa.assert_has_calls([ |
|
mock.call('Interface', columns=mock.ANY, if_exists=True), |
|
mock.call('Port', columns=mock.ANY, ports=['fake_port']) |
|
]) |
|
|
|
def test_restore_local_vlan_map_with_device_has_tag(self): |
|
self._test_restore_local_vlan_maps(2) |
|
|
|
def test_restore_local_vlan_map_with_device_no_tag(self): |
|
self._test_restore_local_vlan_maps([]) |
|
|
|
def test_restore_local_vlan_map_no_segmentation_id(self): |
|
self._test_restore_local_vlan_maps(2, segmentation_id=None) |
|
|
|
def test_restore_local_vlan_map_segmentation_id_compat(self): |
|
self._test_restore_local_vlan_maps(2, segmentation_id='None') |
|
|
|
def test_check_agent_configurations_for_dvr_raises(self): |
|
self.agent.enable_distributed_routing = True |
|
self.agent.enable_tunneling = True |
|
self.agent.l2_pop = False |
|
self.assertRaises(ValueError, |
|
self.agent._check_agent_configurations) |
|
|
|
def test_check_agent_configurations_for_dvr(self): |
|
self.agent.enable_distributed_routing = True |
|
self.agent.enable_tunneling = True |
|
self.agent.l2_pop = True |
|
self.assertIsNone(self.agent._check_agent_configurations()) |
|
|
|
def test_check_agent_configurations_for_dvr_with_vlan(self): |
|
self.agent.enable_distributed_routing = True |
|
self.agent.enable_tunneling = False |
|
self.agent.l2_pop = False |
|
self.assertIsNone(self.agent._check_agent_configurations()) |
|
|
|
def test_port_bound_deletes_flows_for_valid_ofport(self): |
|
self._mock_port_bound(ofport=1, new_local_vlan=1, db_get_val={}) |
|
|
|
def test_port_bound_ignores_flows_for_invalid_ofport(self): |
|
self._mock_port_bound(ofport=-1, new_local_vlan=1, db_get_val={}) |
|
|
|
def test_port_bound_does_not_rewire_if_already_bound(self): |
|
self._mock_port_bound( |
|
ofport=-1, new_local_vlan=1, old_local_vlan=1, db_get_val={}) |
|
|
|
def test_port_bound_not_found(self): |
|
self._mock_port_bound(ofport=1, new_local_vlan=1, db_get_val=None) |
|
|
|
def _test_port_dead(self, cur_tag=None): |
|
port = mock.Mock() |
|
port.ofport = 1 |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
int_br.db_get_val.return_value = cur_tag |
|
self.agent.port_dead(port) |
|
if cur_tag is None or cur_tag == constants.DEAD_VLAN_TAG: |
|
self.assertFalse(int_br.set_db_attribute.called) |
|
self.assertFalse(int_br.drop_port.called) |
|
else: |
|
int_br.assert_has_calls([ |
|
mock.call.set_db_attribute("Port", mock.ANY, "tag", |
|
constants.DEAD_VLAN_TAG, |
|
log_errors=True), |
|
mock.call.drop_port(in_port=port.ofport), |
|
]) |
|
|
|
def test_port_dead(self): |
|
self._test_port_dead() |
|
|
|
def test_port_dead_with_port_already_dead(self): |
|
self._test_port_dead(constants.DEAD_VLAN_TAG) |
|
|
|
def test_port_dead_with_valid_tag(self): |
|
self._test_port_dead(cur_tag=1) |
|
|
|
def mock_scan_ports(self, vif_port_set=None, registered_ports=None, |
|
updated_ports=None, port_tags_dict=None, sync=False): |
|
if port_tags_dict is None: # Because empty dicts evaluate as False. |
|
port_tags_dict = {} |
|
with mock.patch.object(self.agent.int_br, |
|
'get_vif_port_set', |
|
return_value=vif_port_set),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_port_tag_dict', |
|
return_value=port_tags_dict): |
|
return self.agent.scan_ports(registered_ports, sync, updated_ports) |
|
|
|
def test_scan_ports_returns_current_only_for_unchanged_ports(self): |
|
vif_port_set = set([1, 3]) |
|
registered_ports = set([1, 3]) |
|
expected = {'current': vif_port_set, |
|
'added': set(), |
|
'removed': set()} |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports) |
|
self.assertEqual(expected, actual) |
|
|
|
def test_scan_ports_returns_port_changes(self): |
|
vif_port_set = set([1, 3]) |
|
registered_ports = set([1, 2]) |
|
expected = dict(current=vif_port_set, added=set([3]), removed=set([2])) |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports) |
|
self.assertEqual(expected, actual) |
|
|
|
def test_scan_ports_returns_port_changes_with_sync(self): |
|
vif_port_set = set([1, 3]) |
|
registered_ports = set([1, 2]) |
|
expected = dict(current=vif_port_set, added=vif_port_set, |
|
removed=set([2])) |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports, |
|
sync=True) |
|
self.assertEqual(expected, actual) |
|
|
|
def _test_scan_ports_with_updated_ports(self, updated_ports): |
|
vif_port_set = set([1, 3, 4]) |
|
registered_ports = set([1, 2, 4]) |
|
expected = dict(current=vif_port_set, added=set([3]), |
|
removed=set([2]), updated=set([4])) |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports, |
|
updated_ports) |
|
self.assertEqual(expected, actual) |
|
|
|
def test_scan_ports_finds_known_updated_ports(self): |
|
self._test_scan_ports_with_updated_ports(set([4])) |
|
|
|
def test_scan_ports_ignores_unknown_updated_ports(self): |
|
# the port '5' was not seen on current ports. Hence it has either |
|
# never been wired or already removed and should be ignored |
|
self._test_scan_ports_with_updated_ports(set([4, 5])) |
|
|
|
def test_scan_ports_ignores_updated_port_if_removed(self): |
|
vif_port_set = set([1, 3]) |
|
registered_ports = set([1, 2]) |
|
updated_ports = set([1, 2]) |
|
expected = dict(current=vif_port_set, added=set([3]), |
|
removed=set([2]), updated=set([1])) |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports, |
|
updated_ports) |
|
self.assertEqual(expected, actual) |
|
|
|
def test_scan_ports_no_vif_changes_returns_updated_port_only(self): |
|
vif_port_set = set([1, 2, 3]) |
|
registered_ports = set([1, 2, 3]) |
|
updated_ports = set([2]) |
|
expected = dict(current=vif_port_set, updated=set([2]), |
|
added=set(), removed=set()) |
|
actual = self.mock_scan_ports(vif_port_set, registered_ports, |
|
updated_ports) |
|
self.assertEqual(expected, actual) |
|
|
|
def _test_process_ports_events(self, events, registered_ports, |
|
ancillary_ports, expected_ports, |
|
expected_ancillary, updated_ports=None, |
|
): |
|
with mock.patch.object(self.agent, 'check_changed_vlans', |
|
return_value=set()): |
|
devices_not_ready_yet = set() |
|
failed_devices = {'added': set(), 'removed': set()} |
|
failed_ancillary_devices = { |
|
'added': set(), 'removed': set()} |
|
actual = self.agent.process_ports_events( |
|
events, registered_ports, ancillary_ports, |
|
devices_not_ready_yet, failed_devices, |
|
failed_ancillary_devices, updated_ports) |
|
self.assertEqual( |
|
(expected_ports, expected_ancillary, devices_not_ready_yet), |
|
actual) |
|
|
|
def test_process_ports_events_port_removed_and_added(self): |
|
port_id = 'f6f104bd-37c7-4f7b-9d70-53a6bb42728f' |
|
events = { |
|
'removed': |
|
[{'ofport': 1, |
|
'external_ids': {'iface-id': port_id, |
|
'attached-mac': 'fa:16:3e:f6:1b:fb'}, |
|
'name': 'qvof6f104bd-37'}], |
|
'added': |
|
[{'ofport': 2, |
|
'external_ids': {'iface-id': port_id, |
|
'attached-mac': 'fa:16:3e:f6:1b:fb'}, |
|
'name': 'qvof6f104bd-37'}] |
|
} |
|
registered_ports = {port_id} |
|
expected_ancillary = dict(current=set(), added=set(), removed=set()) |
|
|
|
# port was removed and then added |
|
expected_ports = dict(current={port_id}, |
|
added={port_id}, |
|
removed=set()) |
|
with mock.patch.object(ovs_lib.BaseOVS, "port_exists", |
|
return_value=True): |
|
self._test_process_ports_events(events.copy(), registered_ports, |
|
set(), expected_ports, |
|
expected_ancillary) |
|
|
|
# port was added and then removed |
|
expected_ports = dict(current=set(), |
|
added=set(), |
|
removed={port_id}) |
|
with mock.patch.object(ovs_lib.BaseOVS, "port_exists", |
|
return_value=False): |
|
self._test_process_ports_events(events.copy(), registered_ports, |
|
set(), expected_ports, |
|
expected_ancillary) |
|
|
|
def test_process_ports_events_returns_current_for_unchanged_ports(self): |
|
events = {'added': [], 'removed': []} |
|
registered_ports = {1, 3} |
|
ancillary_ports = {2, 5} |
|
expected_ports = {'current': registered_ports, 'added': set(), |
|
'removed': set()} |
|
expected_ancillary = {'current': ancillary_ports, 'added': set(), |
|
'removed': set()} |
|
self._test_process_ports_events(events, registered_ports, |
|
ancillary_ports, expected_ports, |
|
expected_ancillary) |
|
|
|
def test_process_port_events_no_vif_changes_return_updated_port_only(self): |
|
events = {'added': [], 'removed': []} |
|
registered_ports = {1, 2, 3} |
|
updated_ports = {2} |
|
expected_ports = dict(current=registered_ports, updated={2}, |
|
added=set(), removed=set()) |
|
expected_ancillary = dict(current=set(), added=set(), removed=set()) |
|
self._test_process_ports_events(events, registered_ports, |
|
set(), expected_ports, |
|
expected_ancillary, updated_ports) |
|
|
|
def test_process_port_events_ignores_removed_port_if_never_added(self): |
|
events = {'added': [], |
|
'removed': [{'name': 'port2', 'ofport': 2, |
|
'external_ids': {'attached-mac': 'test-mac'}}]} |
|
registered_ports = {1} |
|
expected_ports = dict(current=registered_ports, added=set(), |
|
removed=set()) |
|
expected_ancillary = dict(current=set(), added=set(), removed=set()) |
|
devices_not_ready_yet = set() |
|
with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', |
|
side_effect=[2]), \ |
|
mock.patch.object(self.agent, 'check_changed_vlans', |
|
return_value=set()): |
|
failed_devices = {'added': set(), 'removed': set()} |
|
failed_ancillary_devices = { |
|
'added': set(), 'removed': set()} |
|
ports_not_ready_yet = set() |
|
actual = self.agent.process_ports_events( |
|
events, registered_ports, set(), ports_not_ready_yet, |
|
failed_devices, failed_ancillary_devices) |
|
self.assertEqual( |
|
(expected_ports, expected_ancillary, devices_not_ready_yet), |
|
actual) |
|
|
|
def test_process_port_events_port_not_ready_yet(self): |
|
events = {'added': [{'name': 'port5', 'ofport': [], |
|
'external_ids': {'attached-mac': 'test-mac'}}], |
|
'removed': []} |
|
old_devices_not_ready = {'port4'} |
|
registered_ports = set([1, 2, 3]) |
|
expected_ports = dict(current=set([1, 2, 3, 4]), |
|
added=set([4]), removed=set()) |
|
self.agent.ancillary_brs = [] |
|
expected_ancillary = dict(current=set(), added=set(), removed=set()) |
|
with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', |
|
side_effect=[5, 4]), \ |
|
mock.patch.object(self.agent, 'check_changed_vlans', |
|
return_value=set()), \ |
|
mock.patch.object(self.agent.int_br, 'get_ports_attributes', |
|
return_value=[{'name': 'port4', 'ofport': 4, |
|
'external_ids': { |
|
'attached-mac': 'mac4'}}]): |
|
expected_devices_not_ready = {'port5'} |
|
failed_devices = {'added': set(), 'removed': set()} |
|
failed_ancillary_devices = { |
|
'added': set(), 'removed': set()} |
|
actual = self.agent.process_ports_events( |
|
events, registered_ports, set(), old_devices_not_ready, |
|
failed_devices, failed_ancillary_devices) |
|
self.assertEqual( |
|
(expected_ports, expected_ancillary, |
|
expected_devices_not_ready), actual) |
|
|
|
def _test_process_port_events_with_updated_ports(self, updated_ports): |
|
events = {'added': [{'name': 'port3', 'ofport': 3, |
|
'external_ids': {'attached-mac': 'test-mac'}}, |
|
{'name': 'qg-port2', 'ofport': 6, |
|
'external_ids': {'attached-mac': 'test-mac'}}], |
|
'removed': [{'name': 'port2', 'ofport': 2, |
|
'external_ids': {'attached-mac': 'test-mac'}}, |
|
{'name': 'qg-port1', 'ofport': 5, |
|
'external_ids': {'attached-mac': 'test-mac'}}]} |
|
registered_ports = {1, 2, 4} |
|
ancillary_ports = {5, 8} |
|
expected_ports = dict(current={1, 3, 4}, added={3}, removed={2}) |
|
if updated_ports: |
|
expected_ports['updated'] = updated_ports |
|
expected_ancillary = dict(current={6, 8}, added={6}, |
|
removed={5}) |
|
ancillary_bridge = mock.Mock() |
|
ancillary_bridge.get_vif_port_set.return_value = {5, 6, 8} |
|
self.agent.ancillary_brs = [ancillary_bridge] |
|
with mock.patch.object(self.agent.int_br, 'portid_from_external_ids', |
|
side_effect=[3, 6, 2, 5]), \ |
|
mock.patch.object(self.agent, 'check_changed_vlans', |
|
return_value=set()): |
|
|
|
devices_not_ready_yet = set() |
|
failed_devices = {'added': set(), 'removed': set()} |
|
failed_ancillary_devices = { |
|
'added': set(), 'removed': set()} |
|
actual = self.agent.process_ports_events( |
|
events, registered_ports, ancillary_ports, |
|
devices_not_ready_yet, failed_devices, |
|
failed_ancillary_devices, updated_ports) |
|
self.assertEqual( |
|
(expected_ports, expected_ancillary, devices_not_ready_yet), |
|
actual) |
|
|
|
def test_process_port_events_returns_port_changes(self): |
|
self._test_process_port_events_with_updated_ports(set()) |
|
|
|
def test_process_port_events_finds_known_updated_ports(self): |
|
self._test_process_port_events_with_updated_ports({4}) |
|
|
|
def test_process_port_events_ignores_unknown_updated_ports(self): |
|
# the port '10' was not seen on current ports. Hence it has either |
|
# never been wired or already removed and should be ignored |
|
self._test_process_port_events_with_updated_ports({4, 10}) |
|
|
|
def test_process_port_events_ignores_updated_port_if_removed(self): |
|
self._test_process_port_events_with_updated_ports({4, 5}) |
|
|
|
def test_update_ports_returns_changed_vlan(self): |
|
br = self.br_int_cls('br-int') |
|
mac = "ca:fe:de:ad:be:ef" |
|
port = ovs_lib.VifPort(1, 1, 1, mac, br) |
|
self.agent.vlan_manager.add( |
|
'1', 1, '1', None, 1, {port.vif_id: port}) |
|
vif_port_set = set([1, 3]) |
|
registered_ports = set([1, 2]) |
|
port_tags_dict = {1: []} |
|
expected = dict( |
|
added=set([3]), current=vif_port_set, |
|
removed=set([2]), updated=set([1]) |
|
) |
|
with mock.patch.object(self.agent, 'tun_br', autospec=True), \ |
|
mock.patch.object(self.agent.plugin_rpc, |
|
'update_device_list') as upd_l: |
|
actual = self.mock_scan_ports( |
|
vif_port_set, registered_ports, port_tags_dict=port_tags_dict) |
|
self.assertEqual(expected, actual) |
|
upd_l.assert_called_once_with(mock.ANY, [], set([1]), |
|
self.agent.agent_id, |
|
self.agent.conf.host) |
|
|
|
def test_update_retries_map_and_remove_devs_not_to_retry(self): |
|
failed_devices_retries_map = { |
|
'device_not_to_retry': constants.MAX_DEVICE_RETRIES, |
|
'device_to_retry': 2, |
|
'ancillary_not_to_retry': constants.MAX_DEVICE_RETRIES, |
|
'ancillary_to_retry': 1} |
|
failed_devices = { |
|
'added': set(['device_not_to_retry']), |
|
'removed': set(['device_to_retry', 'new_device'])} |
|
failed_ancillary_devices = {'added': set(['ancillary_to_retry']), |
|
'removed': set(['ancillary_not_to_retry'])} |
|
expected_failed_devices_retries_map = { |
|
'device_to_retry': 3, 'new_device': 1, 'ancillary_to_retry': 2} |
|
(new_failed_devices_retries_map, devices_not_to_retry, |
|
ancillary_devices_not_t_retry) = self.agent._get_devices_not_to_retry( |
|
failed_devices, failed_ancillary_devices, |
|
failed_devices_retries_map) |
|
self.agent._remove_devices_not_to_retry( |
|
failed_devices, failed_ancillary_devices, devices_not_to_retry, |
|
ancillary_devices_not_t_retry) |
|
self.assertIn('device_to_retry', failed_devices['removed']) |
|
self.assertNotIn('device_not_to_retry', failed_devices['added']) |
|
self.assertEqual( |
|
expected_failed_devices_retries_map, |
|
new_failed_devices_retries_map) |
|
|
|
def test_add_port_tag_info(self): |
|
lvm = mock.Mock() |
|
lvm.vlan = 1 |
|
self.agent.vlan_manager.mapping["net1"] = lvm |
|
ovs_db_list = [{'name': 'tap1', |
|
'tag': [], |
|
'other_config': {'segmentation_id': '1'}}, |
|
{'name': 'tap2', |
|
'tag': [], |
|
'other_config': {}}, |
|
{'name': 'tap3', |
|
'tag': [], |
|
'other_config': None}] |
|
vif_port1 = mock.Mock() |
|
vif_port1.port_name = 'tap1' |
|
vif_port2 = mock.Mock() |
|
vif_port2.port_name = 'tap2' |
|
vif_port3 = mock.Mock() |
|
vif_port3.port_name = 'tap3' |
|
port_details = [ |
|
{'network_id': 'net1', 'vif_port': vif_port1}, |
|
{'network_id': 'net1', 'vif_port': vif_port2}, |
|
{'network_id': 'net1', 'vif_port': vif_port3}] |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
int_br.get_ports_attributes.return_value = ovs_db_list |
|
self.agent._add_port_tag_info(port_details) |
|
set_db_attribute_calls = \ |
|
[mock.call.set_db_attribute("Port", "tap1", |
|
"other_config", {"segmentation_id": "1", "tag": "1"}), |
|
mock.call.set_db_attribute("Port", "tap2", |
|
"other_config", {"tag": "1"}), |
|
mock.call.set_db_attribute("Port", "tap3", |
|
"other_config", {"tag": "1"})] |
|
int_br.assert_has_calls(set_db_attribute_calls, any_order=True) |
|
|
|
def test_add_port_tag_info_with_tagged_ports(self): |
|
lvm = mock.Mock() |
|
lvm.vlan = 1 |
|
self.agent.vlan_manager.mapping["net1"] = lvm |
|
ovs_db_list1 = [{'name': 'tap1', |
|
'tag': 1, |
|
'other_config': {'segmentation_id': '1', 'tag': '1'}}] |
|
ovs_db_list2 = [{'name': 'tap2', |
|
'tag': 2, |
|
'other_config': {'segmentation_id': '1', 'tag': '1'}}, |
|
{'name': 'tap3', |
|
'tag': 1, |
|
'other_config': {'segmentation_id': '2', 'tag': '2'}}] |
|
vif_port1 = mock.Mock() |
|
vif_port1.port_name = 'tap1' |
|
vif_port2 = mock.Mock() |
|
vif_port2.port_name = 'tap2' |
|
vif_port2.ofport = 7 |
|
vif_port3 = mock.Mock() |
|
vif_port3.port_name = 'tap3' |
|
vif_port3.ofport = 8 |
|
port_details1 = [{'network_id': 'net1', 'vif_port': vif_port1}] |
|
port_details2 = [{'network_id': 'net1', 'vif_port': vif_port2}, |
|
{'network_id': 'net1', 'vif_port': vif_port3}] |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
int_br.get_ports_attributes.return_value = ovs_db_list1 |
|
self.agent._add_port_tag_info(port_details1) |
|
int_br.set_db_attribute.assert_not_called() |
|
# Reset mock to check port with changed tag |
|
int_br.reset_mock() |
|
int_br.get_ports_attributes.return_value = ovs_db_list2 |
|
self.agent._add_port_tag_info(port_details2) |
|
expected_calls = \ |
|
[mock.call.set_db_attribute("Port", "tap2", |
|
"other_config", {'segmentation_id': '1', 'tag': '1'}), |
|
mock.call.uninstall_flows(in_port=7), |
|
mock.call.set_db_attribute("Port", "tap3", |
|
"other_config", {'segmentation_id': '2', 'tag': '1'}), |
|
mock.call.uninstall_flows(in_port=8)] |
|
int_br.assert_has_calls(expected_calls) |
|
|
|
def test_bind_devices(self): |
|
devices_up = ['tap1'] |
|
devices_down = ['tap2'] |
|
self.agent.vlan_manager.mapping["net1"] = mock.Mock() |
|
ovs_db_list = [{'name': 'tap1', 'tag': []}, |
|
{'name': 'tap2', 'tag': []}] |
|
vif_port1 = mock.Mock() |
|
vif_port1.port_name = 'tap1' |
|
vif_port2 = mock.Mock() |
|
vif_port2.port_name = 'tap2' |
|
port_details = [ |
|
{'network_id': 'net1', 'vif_port': vif_port1, |
|
'device': devices_up[0], |
|
'device_owner': 'network:dhcp', |
|
'admin_state_up': True}, |
|
{'network_id': 'net1', 'vif_port': vif_port2, |
|
'device': devices_down[0], |
|
'device_owner': 'network:dhcp', |
|
'admin_state_up': False}] |
|
with mock.patch.object( |
|
self.agent.plugin_rpc, 'update_device_list', |
|
return_value={'devices_up': devices_up, |
|
'devices_down': devices_down, |
|
'failed_devices_up': [], |
|
'failed_devices_down': []}) as update_devices, \ |
|
mock.patch.object(self.agent, |
|
'int_br') as int_br: |
|
int_br.get_ports_attributes.return_value = ovs_db_list |
|
self.agent._bind_devices(port_details) |
|
update_devices.assert_called_once_with(mock.ANY, devices_up, |
|
devices_down, |
|
mock.ANY, mock.ANY, |
|
agent_restarted=True) |
|
|
|
def _test_arp_spoofing(self, enable_prevent_arp_spoofing): |
|
self.agent.prevent_arp_spoofing = enable_prevent_arp_spoofing |
|
|
|
ovs_db_list = [{'name': 'fake_device', 'tag': []}] |
|
self.agent.vlan_manager.add('fake_network', 1, None, None, 1) |
|
vif_port = mock.Mock() |
|
vif_port.port_name = 'fake_device' |
|
vif_port.ofport = 1 |
|
need_binding_ports = [{'network_id': 'fake_network', |
|
'vif_port': vif_port, |
|
'device': 'fake_device', |
|
'admin_state_up': True}] |
|
with mock.patch.object( |
|
self.agent.plugin_rpc, 'update_device_list', |
|
return_value={'devices_up': [], |
|
'devices_down': [], |
|
'failed_devices_up': [], |
|
'failed_devices_down': []}), \ |
|
mock.patch.object(self.agent, |
|
'int_br') as int_br, \ |
|
mock.patch.object( |
|
self.agent, |
|
'setup_arp_spoofing_protection') as setup_arp: |
|
int_br.get_ports_attributes.return_value = ovs_db_list |
|
self.agent._bind_devices(need_binding_ports) |
|
self.assertEqual(enable_prevent_arp_spoofing, setup_arp.called) |
|
|
|
def test_setup_arp_spoofing_protection_enable(self): |
|
self._test_arp_spoofing(True) |
|
|
|
def test_setup_arp_spoofing_protection_disabled(self): |
|
self._test_arp_spoofing(False) |
|
|
|
def _mock_treat_devices_added_updated(self, details, port, func_name): |
|
"""Mock treat devices added or updated. |
|
|
|
:param details: the details to return for the device |
|
:param port: the port that get_vif_port_by_id should return |
|
:param func_name: the function that should be called |
|
:returns: whether the named function was called |
|
""" |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [details], |
|
'failed_devices': []}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={details['device']: port}),\ |
|
mock.patch.object(self.agent.plugin_rpc, 'update_device_list', |
|
return_value={'devices_up': [], |
|
'devices_down': details, |
|
'failed_devices_up': [], |
|
'failed_devices_down': []}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_port_tag_dict', |
|
return_value={}),\ |
|
mock.patch.object(self.agent, func_name) as func: |
|
skip_devs, _, need_bound_devices, _ = ( |
|
self.agent.treat_devices_added_or_updated([], False)) |
|
# The function should not raise |
|
self.assertFalse(skip_devs) |
|
return func.called |
|
|
|
def test_treat_devices_added_updated_no_active_binding(self): |
|
details = {'device': 'id', |
|
c_const.NO_ACTIVE_BINDING: True} |
|
port = mock.Mock() |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [details], |
|
'failed_devices': []}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={details['device']: port}),\ |
|
mock.patch.object(self.agent, 'port_dead') as func: |
|
skip_devs, binding_no_activated_devices, _, _ = ( |
|
self.agent.treat_devices_added_or_updated([], False)) |
|
self.assertFalse(skip_devs) |
|
self.assertTrue(func.called) |
|
self.assertIn('id', binding_no_activated_devices) |
|
|
|
def test_treat_devices_added_updated_ignores_invalid_ofport(self): |
|
port = mock.Mock() |
|
port.ofport = -1 |
|
self.assertFalse(self._mock_treat_devices_added_updated( |
|
mock.MagicMock(), port, 'port_dead')) |
|
|
|
def test_treat_devices_added_updated_marks_unknown_port_as_dead(self): |
|
port = mock.Mock() |
|
port.ofport = 1 |
|
self.assertTrue(self._mock_treat_devices_added_updated( |
|
mock.MagicMock(), port, 'port_dead')) |
|
|
|
def test_treat_devices_added_does_not_process_missing_port(self): |
|
with mock.patch.object( |
|
self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices') as get_dev_fn,\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vif_port_by_id', |
|
return_value=None): |
|
self.assertFalse(get_dev_fn.called) |
|
|
|
def test_treat_devices_added_updated_updates_known_port(self): |
|
details = mock.MagicMock() |
|
details.__contains__.side_effect = lambda x: True |
|
self.assertTrue(self._mock_treat_devices_added_updated( |
|
details, mock.Mock(), 'treat_vif_port')) |
|
|
|
def test_treat_devices_added_updated_sends_vif_port_into_extension_manager( |
|
self, *args): |
|
details = mock.MagicMock() |
|
details.__contains__.side_effect = lambda x: True |
|
port = mock.MagicMock() |
|
|
|
def fake_handle_port(context, port): |
|
self.assertIn('vif_port', port) |
|
|
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [details], |
|
'failed_devices': []}),\ |
|
mock.patch.object(self.agent.ext_manager, |
|
'handle_port', new=fake_handle_port),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={details['device']: port}),\ |
|
mock.patch.object(self.agent, 'treat_vif_port', |
|
return_value=False): |
|
|
|
self.agent.treat_devices_added_or_updated([], False) |
|
|
|
def test_treat_devices_added_updated_skips_if_port_not_found(self): |
|
dev_mock = mock.MagicMock() |
|
dev_mock.__getitem__.return_value = 'the_skipped_one' |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [dev_mock], |
|
'failed_devices': []}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_port_tag_dict', |
|
return_value={}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={}),\ |
|
mock.patch.object(self.agent.ext_manager, |
|
"delete_port") as ext_mgr_delete_port,\ |
|
mock.patch.object(self.agent, |
|
'treat_vif_port') as treat_vif_port: |
|
skip_devs = self.agent.treat_devices_added_or_updated([], False) |
|
# The function should return False for resync and no device |
|
# processed |
|
self.assertEqual((['the_skipped_one'], set(), [], set()), |
|
skip_devs) |
|
ext_mgr_delete_port.assert_called_once_with( |
|
self.agent.context, {'port_id': 'the_skipped_one'}) |
|
self.assertFalse(treat_vif_port.called) |
|
|
|
def test_treat_devices_added_failed_devices(self): |
|
dev_mock = 'the_failed_one' |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [], |
|
'failed_devices': [dev_mock]}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={}),\ |
|
mock.patch.object(self.agent, |
|
'treat_vif_port') as treat_vif_port: |
|
failed_devices = {'added': set(), 'removed': set()} |
|
(_, _, _, failed_devices['added']) = ( |
|
self.agent.treat_devices_added_or_updated([], False)) |
|
# The function should return False for resync and no device |
|
# processed |
|
self.assertEqual(set([dev_mock]), failed_devices.get('added')) |
|
self.assertFalse(treat_vif_port.called) |
|
|
|
def test_treat_devices_added_updated_put_port_down(self): |
|
fake_details_dict = {'admin_state_up': False, |
|
'port_id': 'xxx', |
|
'device': 'xxx', |
|
'network_id': 'yyy', |
|
'physical_network': 'foo', |
|
'segmentation_id': 'bar', |
|
'network_type': 'baz', |
|
'fixed_ips': [{'subnet_id': 'my-subnet-uuid', |
|
'ip_address': '1.1.1.1'}], |
|
'device_owner': DEVICE_OWNER_COMPUTE |
|
} |
|
|
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'get_devices_details_list_and_failed_devices', |
|
return_value={'devices': [fake_details_dict], |
|
'failed_devices': []}),\ |
|
mock.patch.object(self.agent.int_br, |
|
'get_vifs_by_ids', |
|
return_value={'xxx': mock.MagicMock()}),\ |
|
mock.patch.object(self.agent.int_br, 'get_port_tag_dict', |
|
return_value={}),\ |
|
mock.patch.object(self.agent, |
|
'treat_vif_port') as treat_vif_port: |
|
skip_devs, _, need_bound_devices, _ = ( |
|
self.agent.treat_devices_added_or_updated([], False)) |
|
# The function should return False for resync |
|
self.assertFalse(skip_devs) |
|
self.assertTrue(treat_vif_port.called) |
|
|
|
def _mock_treat_devices_removed(self, port_exists): |
|
details = dict(exists=port_exists) |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'update_device_list', |
|
return_value={'devices_up': [], |
|
'devices_down': details, |
|
'failed_devices_up': [], |
|
'failed_devices_down': []}): |
|
with mock.patch.object(self.agent, 'port_unbound') as port_unbound: |
|
self.assertFalse(self.agent.treat_devices_removed([{}])) |
|
self.assertTrue(port_unbound.called) |
|
|
|
def test_treat_devices_removed_unbinds_port(self): |
|
self._mock_treat_devices_removed(True) |
|
|
|
def test_treat_devices_removed_ignores_missing_port(self): |
|
self._mock_treat_devices_removed(False) |
|
|
|
def test_treat_devices_removed_failed_devices(self): |
|
dev_mock = 'the_failed_one' |
|
with mock.patch.object(self.agent.plugin_rpc, |
|
'update_device_list', |
|
return_value={'devices_up': [], |
|
'devices_down': [], |
|
'failed_devices_up': [], |
|
'failed_devices_down': [ |
|
dev_mock]}): |
|
failed_devices = {'added': set(), 'removed': set()} |
|
failed_devices['removed'] = self.agent.treat_devices_removed([{}]) |
|
self.assertEqual(set([dev_mock]), failed_devices.get('removed')) |
|
|
|
def test_treat_devices_removed_ext_delete_port(self): |
|
port_id = 'fake-id' |
|
|
|
m_delete = mock.patch.object(self.agent.ext_manager, 'delete_port') |
|
m_rpc = mock.patch.object(self.agent.plugin_rpc, 'update_device_list', |
|
return_value={'devices_up': [], |
|
'devices_down': [], |
|
'failed_devices_up': [], |
|
'failed_devices_down': []}) |
|
m_unbound = mock.patch.object(self.agent, 'port_unbound') |
|
|
|
with m_delete as delete, m_rpc, m_unbound: |
|
self.agent.treat_devices_removed([port_id]) |
|
delete.assert_called_with(mock.ANY, {'port_id': port_id}) |
|
|
|
def test_treat_vif_port_shut_down_port(self): |
|
details = mock.MagicMock() |
|
vif_port = type('vif_port', (object,), { |
|
"vif_id": "12", |
|
"iface-id": "407a79e0-e0be-4b7d-92a6-513b2161011b", |
|
"vif_mac": "fa:16:3e:68:46:7b", |
|
"port_name": "qr-407a79e0-e0", |
|
"ofport": -1, |
|
"bridge_name": "br-int"}) |
|
with mock.patch.object( |
|
self.agent.plugin_rpc, 'update_device_down' |
|
) as update_device_down, mock.patch.object( |
|
self.agent, "port_dead" |
|
) as port_dead: |
|
port_needs_binding = self.agent.treat_vif_port( |
|
vif_port, details['port_id'], |
|
details['network_id'], |
|
details['network_type'], |
|
details['physical_network'], |
|
details['segmentation_id'], |
|
False, |
|
details['fixed_ips'], |
|
details['device_owner'], False) |
|
self.assertFalse(port_needs_binding) |
|
port_dead.assert_called_once_with(vif_port) |
|
update_device_down.assert_called_once_with( |
|
self.agent.context, details['port_id'], self.agent.agent_id, |
|
self.agent.conf.host) |
|
|
|
def test_bind_port_with_missing_network(self): |
|
vif_port = mock.Mock() |
|
vif_port.name.return_value = 'port' |
|
self.agent._bind_devices([{'network_id': 'non-existent', |
|
'vif_port': vif_port}]) |
|
|
|
def _test_process_network_ports(self, port_info, skipped_devices=None, |
|
binding_no_activated_devices=None): |
|
failed_devices = {'added': set(), 'removed': set()} |
|
skipped_devices = skipped_devices or [] |
|
binding_no_activated_devices = binding_no_activated_devices or set() |
|
added_devices = port_info.get('added', set()) |
|
with mock.patch.object(self.agent.sg_agent, |
|
"setup_port_filters") as setup_port_filters,\ |
|
mock.patch.object( |
|
self.agent, "treat_devices_added_or_updated", |
|
return_value=( |
|
skipped_devices, binding_no_activated_devices, [], |
|
failed_devices['added'])) as device_added_updated,\ |
|
mock.patch.object(self.agent.int_br, "get_ports_attributes", |
|
return_value=[]),\ |
|
mock.patch.object(self.agent, |
|
"treat_devices_removed", |
|
return_value=( |
|
failed_devices[ |
|
'removed'])) as device_removed,\ |
|
mock.patch.object(self.agent, |
|
"treat_devices_skipped", |
|
return_value=( |
|
skipped_devices)) as device_skipped: |
|
self.assertEqual( |
|
failed_devices, |
|
self.agent.process_network_ports(port_info, False)) |
|
setup_port_filters.assert_called_once_with( |
|
(added_devices - set(skipped_devices) - |
|
binding_no_activated_devices), |
|
port_info.get('updated', set())) |
|
devices_added_updated = (added_devices | |
|
port_info.get('updated', set())) |
|
if devices_added_updated: |
|
device_added_updated.assert_called_once_with( |
|
devices_added_updated, False) |
|
if port_info.get('removed', set()): |
|
device_removed.assert_called_once_with(port_info['removed']) |
|
if skipped_devices: |
|
device_skipped.assert_called_once_with(set(skipped_devices)) |
|
|
|
def test_process_network_ports(self): |
|
self._test_process_network_ports( |
|
{'current': set(['tap0']), |
|
'removed': set(['eth0']), |
|
'added': set(['eth1'])}) |
|
|
|
def test_process_network_port_with_updated_ports(self): |
|
self._test_process_network_ports( |
|
{'current': set(['tap0', 'tap1']), |
|
'updated': set(['tap1', 'eth1']), |
|
'removed': set(['eth0']), |
|
'added': set(['eth1'])}) |
|
|
|
def test_process_network_port_with_skipped_ports(self): |
|
port_info = {'current': set(['tap0', 'tap1']), |
|
'removed': set(['eth0']), |
|
'added': set(['eth1', 'eth2'])} |
|
self._test_process_network_ports(port_info, skipped_devices=['eth1']) |
|
|
|
def test_process_network_port_with_binding_no_activated_devices(self): |
|
port_info = {'current': set(['tap0', 'tap1']), |
|
'removed': set(['eth0']), |
|
'added': set(['eth1', 'eth2', 'eth3'])} |
|
self._test_process_network_ports( |
|
port_info, skipped_devices=['eth1'], |
|
binding_no_activated_devices=set(['eth3'])) |
|
|
|
def test_process_network_port_with_empty_port(self): |
|
self._test_process_network_ports({}) |
|
|
|
def test_hybrid_plug_flag_based_on_firewall(self): |
|
cfg.CONF.set_default( |
|
'firewall_driver', |
|
'neutron.agent.firewall.NoopFirewallDriver', |
|
group='SECURITYGROUP') |
|
agt = self._make_agent() |
|
self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug']) |
|
cfg.CONF.set_default( |
|
'firewall_driver', |
|
'neutron.agent.linux.openvswitch_firewall.OVSFirewallDriver', |
|
group='SECURITYGROUP') |
|
with mock.patch('neutron.agent.linux.openvswitch_firewall.' |
|
'OVSFirewallDriver.initialize_bridge'): |
|
agt = self._make_agent() |
|
self.assertFalse(agt.agent_state['configurations']['ovs_hybrid_plug']) |
|
cfg.CONF.set_default( |
|
'firewall_driver', |
|
'neutron.agent.linux.iptables_firewall.' |
|
'OVSHybridIptablesFirewallDriver', |
|
group='SECURITYGROUP') |
|
with mock.patch('neutron.agent.linux.ip_conntrack.' |
|
'IpConntrackManager._populate_initial_zone_map'): |
|
agt = self._make_agent() |
|
self.assertTrue(agt.agent_state['configurations']['ovs_hybrid_plug']) |
|
|
|
def test_report_state(self): |
|
with mock.patch.object(self.agent.state_rpc, |
|
"report_state") as report_st: |
|
self.agent.int_br_device_count = 5 |
|
self.systemd_notify.assert_not_called() |
|
self.agent._report_state() |
|
report_st.assert_called_with(self.agent.context, |
|
self.agent.agent_state, True) |
|
self.systemd_notify.assert_called_once_with() |
|
self.systemd_notify.reset_mock() |
|
# agent keeps sending "start_flag" while iter 0 not completed |
|
self.assertIn("start_flag", self.agent.agent_state) |
|
self.assertEqual( |
|
self.agent.agent_state["configurations"]["devices"], |
|
self.agent.int_br_device_count |
|
) |
|
self.agent._report_state() |
|
report_st.assert_called_with(self.agent.context, |
|
self.agent.agent_state, True) |
|
self.systemd_notify.assert_not_called() |
|
|
|
def test_report_state_fail(self): |
|
with mock.patch.object(self.agent.state_rpc, |
|
"report_state") as report_st: |
|
report_st.side_effect = Exception() |
|
self.agent._report_state() |
|
report_st.assert_called_with(self.agent.context, |
|
self.agent.agent_state, True) |
|
self.agent._report_state() |
|
report_st.assert_called_with(self.agent.context, |
|
self.agent.agent_state, True) |
|
self.systemd_notify.assert_not_called() |
|
|
|
def test_report_state_revived(self): |
|
with mock.patch.object(self.agent.state_rpc, |
|
"report_state") as report_st: |
|
report_st.return_value = agent_consts.AGENT_REVIVED |
|
self.agent._report_state() |
|
self.assertTrue(self.agent.fullsync) |
|
|
|
def test_port_update(self): |
|
port = {"id": TEST_PORT_ID1, |
|
"network_id": TEST_NETWORK_ID1, |
|
"admin_state_up": False} |
|
self.agent.port_update("unused_context", |
|
port=port, |
|
network_type="vlan", |
|
segmentation_id="1", |
|
physical_network="physnet") |
|
self.assertEqual(set([TEST_PORT_ID1]), self.agent.updated_ports) |
|
|
|
def test_port_delete_after_update(self): |
|
"""Make sure a port is not marked for delete and update.""" |
|
port = {'id': TEST_PORT_ID1} |
|
|
|
self.agent.port_update(context=None, port=port) |
|
self.agent.port_delete(context=None, port_id=port['id']) |
|
self.assertEqual(set(), self.agent.updated_ports) |
|
self.assertEqual(set([port['id']]), self.agent.deleted_ports) |
|
|
|
def test_process_deleted_ports_cleans_network_ports(self): |
|
self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID1) |
|
self.agent.port_delete(context=None, port_id=TEST_PORT_ID1) |
|
self.agent.sg_agent = mock.Mock() |
|
self.agent.int_br = mock.Mock() |
|
self.agent.process_deleted_ports(port_info={}) |
|
self.assertEqual(set(), self.agent.network_ports[TEST_NETWORK_ID1]) |
|
|
|
def test_network_update(self): |
|
"""Network update marks port for update. """ |
|
network = {'id': TEST_NETWORK_ID1} |
|
port = {'id': TEST_PORT_ID1, 'network_id': network['id']} |
|
|
|
self.agent._update_port_network(port['id'], port['network_id']) |
|
self.agent.network_update(context=None, network=network) |
|
self.assertEqual(set([port['id']]), self.agent.updated_ports) |
|
|
|
def test_network_update_outoforder(self): |
|
"""Network update arrives later than port_delete. |
|
|
|
But the main agent loop still didn't process the ports, |
|
so we ensure the port is not marked for update. |
|
""" |
|
network = {'id': TEST_NETWORK_ID1} |
|
port = {'id': TEST_PORT_ID1, 'network_id': network['id']} |
|
|
|
self.agent._update_port_network(port['id'], port['network_id']) |
|
self.agent.port_delete(context=None, port_id=port['id']) |
|
self.agent.network_update(context=None, network=network) |
|
self.assertEqual(set(), self.agent.updated_ports) |
|
|
|
def test_update_port_network(self): |
|
"""Ensure ports are associated and moved across networks correctly.""" |
|
self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID1) |
|
self.agent._update_port_network(TEST_PORT_ID2, TEST_NETWORK_ID1) |
|
self.agent._update_port_network(TEST_PORT_ID3, TEST_NETWORK_ID2) |
|
self.agent._update_port_network(TEST_PORT_ID1, TEST_NETWORK_ID2) |
|
|
|
self.assertEqual(set([TEST_PORT_ID2]), |
|
self.agent.network_ports[TEST_NETWORK_ID1]) |
|
self.assertEqual(set([TEST_PORT_ID1, TEST_PORT_ID3]), |
|
self.agent.network_ports[TEST_NETWORK_ID2]) |
|
|
|
def test_port_delete(self): |
|
vif = FakeVif() |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
int_br.get_vif_by_port_id.return_value = vif.port_name |
|
int_br.get_vif_port_by_id.return_value = vif |
|
self.agent.port_delete("unused_context", |
|
port_id='id') |
|
self.agent.process_deleted_ports(port_info={}) |
|
# the main things we care about are that it gets put in the |
|
# dead vlan and gets blocked |
|
int_br.set_db_attribute.assert_any_call( |
|
'Port', vif.port_name, 'tag', constants.DEAD_VLAN_TAG, |
|
log_errors=False) |
|
int_br.drop_port.assert_called_once_with(in_port=vif.ofport) |
|
|
|
def test_port_delete_removed_port(self): |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
self.agent.port_delete("unused_context", |
|
port_id='id') |
|
# if it was removed from the bridge, we shouldn't be processing it |
|
self.agent.process_deleted_ports(port_info={'removed': {'id', }}) |
|
self.assertFalse(int_br.set_db_attribute.called) |
|
self.assertFalse(int_br.drop_port.called) |
|
|
|
def test_binding_deactivate_not_for_host(self): |
|
self.agent.binding_deactivate('unused_context', port_id='id', |
|
host='other_host') |
|
self.assertEqual(set(), self.agent.deactivated_bindings) |
|
|
|
def test_binding_deactivate(self): |
|
vif = FakeVif() |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
int_br.get_vif_port_by_id.return_value = vif |
|
self.agent.binding_deactivate('unused_context', port_id='id', |
|
host='host') |
|
self.assertEqual(set(['id']), self.agent.deactivated_bindings) |
|
self.agent.process_deactivated_bindings(port_info={}) |
|
int_br.get_vif_port_by_id.assert_called_once_with('id') |
|
int_br.delete_port.assert_called_once_with(vif.port_name) |
|
self.assertEqual(set(), self.agent.deactivated_bindings) |
|
|
|
def test_binding_deactivate_removed_port(self): |
|
with mock.patch.object(self.agent, 'int_br') as int_br: |
|
self.agent.binding_deactivate('unused_context', port_id='id', |
|
host='host') |
|
self.assertEqual(set(['id']), self.agent.deactivated_bindings) |
|
self.agent.process_deactivated_bindings( |
|
port_info={'removed': {'id', }}) |
|
int_br.get_vif_port_by_id.assert_not_called() |
|
int_br.delete_port.assert_not_called() |
|
self.assertEqual(set(), self.agent.deactivated_bindings) |
|
|
|
def test_binding_activate(self): |
|
self.agent.binding_activate('context', port_id='id', host='host') |
|
self.assertIn('id', self.agent.activated_bindings) |
|
|
|
def test_binding_activate_not_for_host(self): |
|
self.agent.binding_activate('context', port_id='id', host='other-host') |
|
self.assertEqual(set(), self.agent.activated_bindings) |
|
|
|
def test_process_activated_bindings(self): |
|
port_info = {} |
|
port_info['added'] = set(['added_port_id']) |
|
port_info['current'] = set(['activated_port_id']) |
|
self.agent.process_activated_bindings(port_info, |
|
set(['activated_port_id'])) |
|
self.assertIn('added_port_id', port_info['added']) |
|
self.assertIn('activated_port_id', port_info['added']) |
|
|
|
def test_process_activated_bindings_activated_port_not_present(self): |
|
port_info = {} |
|
port_info['added'] = set(['added_port_id']) |
|
port_info['current'] = set() |
|
self.agent.process_activated_bindings(port_info, |
|
set(['activated_port_id'])) |
|
self.assertIn('added_port_id', port_info['added']) |
|
self.assertNotIn('activated_port_id', port_info['added']) |
|
|
|
def _test_setup_physical_bridges(self, port_exists=False): |
|
with mock.patch.object(ip_lib.IPDevice, "exists") as devex_fn,\ |
|
mock.patch.object(sys, "exit"),\ |
|
mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ |
|
mock.patch.object(self.agent, 'int_br') as int_br,\ |
|
mock.patch.object(self.agent, '_check_bridge_datapath_id'),\ |
|
mock.patch.object(ovs_lib.BaseOVS, 'get_bridges'): |
|
devex_fn.return_value = True |
|
parent = mock.MagicMock() |
|
phys_br = phys_br_cls() |
|
parent.attach_mock(phys_br_cls, 'phys_br_cls') |
|
parent.attach_mock(phys_br, 'phys_br') |
|
parent.attach_mock(int_br, 'int_br') |
|
if port_exists: |
|
phys_br.get_port_ofport.return_value = "phy_ofport" |
|
int_br.get_port_ofport.return_value = "int_ofport" |
|
else: |
|
phys_br.add_patch_port.return_value = "phy_ofport" |
|
int_br.add_patch_port.return_value = "int_ofport" |
|
phys_br.port_exists.return_value = port_exists |
|
int_br.port_exists.return_value = port_exists |
|
self.agent.setup_physical_bridges({"physnet1": "br-eth"}) |
|
expected_calls = [ |
|
mock.call.phys_br_cls('br-eth'), |
|
mock.call.phys_br.create(), |
|
mock.call.phys_br.set_secure_mode(), |
|
mock.call.phys_br.setup_controllers(mock.ANY), |
|
mock.call.phys_br.setup_default_table(), |
|
mock.call.int_br.db_get_val('Interface', 'int-br-eth', |
|
'type', log_errors=False), |
|
# Have to use __getattr__ here to avoid mock._Call.__eq__ |
|
# method being called |
|
mock.call.int_br.db_get_val().__getattr__('__eq__')('veth'), |
|
mock.call.int_br.port_exists('int-br-eth'), |
|
] |
|
if port_exists: |
|
expected_calls += [ |
|
mock.call.int_br.get_port_ofport('int-br-eth'), |
|
] |
|
else: |
|
expected_calls += [ |
|
mock.call.int_br.add_patch_port( |
|
'int-br-eth', constants.NONEXISTENT_PEER), |
|
] |
|
expected_calls += [ |
|
mock.call.phys_br.port_exists('phy-br-eth'), |
|
] |
|
if port_exists: |
|
expected_calls += [ |
|
mock.call.phys_br.get_port_ofport('phy-br-eth'), |
|
] |
|
else: |
|
expected_calls += [ |
|
mock.call.phys_br.add_patch_port( |
|
'phy-br-eth', constants.NONEXISTENT_PEER), |
|
] |
|
expected_calls += [ |
|
mock.call.int_br.drop_port(in_port='int_ofport'), |
|
mock.call.phys_br.drop_port(in_port='phy_ofport'), |
|
mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', |
|
'options', |
|
{'peer': 'phy-br-eth'}), |
|
mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', |
|
'options', |
|
{'peer': 'int-br-eth'}), |
|
] |
|
parent.assert_has_calls(expected_calls) |
|
self.assertEqual("int_ofport", |
|
self.agent.int_ofports["physnet1"]) |
|
self.assertEqual("phy_ofport", |
|
self.agent.phys_ofports["physnet1"]) |
|
|
|
def test_setup_physical_bridges(self): |
|
self._test_setup_physical_bridges() |
|
|
|
def test_setup_physical_bridges_port_exists(self): |
|
self._test_setup_physical_bridges(port_exists=True) |
|
|
|
def test_setup_physical_bridges_using_veth_interconnection(self): |
|
self.agent.use_veth_interconnection = True |
|
with mock.patch.object(ip_lib.IPDevice, "exists") as devex_fn,\ |
|
mock.patch.object(sys, "exit"),\ |
|
mock.patch.object(utils, "execute") as utilsexec_fn,\ |
|
mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ |
|
mock.patch.object(self.agent, 'int_br') as int_br,\ |
|
mock.patch.object(self.agent, '_check_bridge_datapath_id'),\ |
|
mock.patch.object(ip_lib.IPWrapper, "add_veth") as addveth_fn,\ |
|
mock.patch.object(ip_lib.IpLinkCommand, |
|
"delete") as linkdel_fn,\ |
|
mock.patch.object(ip_lib.IpLinkCommand, "set_up"),\ |
|
mock.patch.object(ip_lib.IpLinkCommand, "set_mtu"),\ |
|
mock.patch.object(ovs_lib.BaseOVS, "get_bridges") as get_br_fn: |
|
devex_fn.return_value = True |
|
parent = mock.MagicMock() |
|
parent.attach_mock(utilsexec_fn, 'utils_execute') |
|
parent.attach_mock(linkdel_fn, 'link_delete') |
|
parent.attach_mock(addveth_fn, 'add_veth') |
|
addveth_fn.return_value = (ip_lib.IPDevice("int-br-eth1"), |
|
ip_lib.IPDevice("phy-br-eth1")) |
|
phys_br = phys_br_cls() |
|
phys_br.add_port.return_value = "phys_veth_ofport" |
|
int_br.add_port.return_value = "int_veth_ofport" |
|
get_br_fn.return_value = ["br-eth"] |
|
self.agent.setup_physical_bridges({"physnet1": "br-eth"}) |
|
expected_calls = [mock.call.link_delete(), |
|
mock.call.utils_execute(['udevadm', |
|
'settle', |
|
'--timeout=10']), |
|
mock.call.add_veth('int-br-eth', |
|
'phy-br-eth')] |
|
parent.assert_has_calls(expected_calls, any_order=False) |
|
self.assertEqual("int_veth_ofport", |
|
self.agent.int_ofports["physnet1"]) |
|
self.assertEqual("phys_veth_ofport", |
|
self.agent.phys_ofports["physnet1"]) |
|
int_br.add_port.assert_called_with("int-br-eth") |
|
phys_br.add_port.assert_called_with("phy-br-eth") |
|
|
|
def _test_setup_physical_bridges_change_from_veth_to_patch_conf( |
|
self, port_exists=False): |
|
with mock.patch.object(sys, "exit"),\ |
|
mock.patch.object(self.agent, 'br_phys_cls') as phys_br_cls,\ |
|
mock.patch.object(self.agent, 'int_br') as int_br,\ |
|
mock.patch.object(self.agent.int_br, 'db_get_val', |
|
return_value='veth'), \ |
|
mock.patch.object(self.agent, '_check_bridge_datapath_id'), \ |
|
mock.patch.object(ovs_lib.BaseOVS, 'get_bridges'): |
|
phys_br = phys_br_cls() |
|
parent = mock.MagicMock() |
|
parent.attach_mock(phys_br_cls, 'phys_br_cls') |
|
parent.attach_mock(phys_br, 'phys_br') |
|
parent.attach_mock(int_br, 'int_br') |
|
if port_exists: |
|
phys_br.get_port_ofport.return_value = "phy_ofport" |
|
int_br.get_port_ofport.return_value = "int_ofport" |
|
else: |
|
phys_br.add_patch_port.return_value = "phy_ofport" |
|
int_br.add_patch_port.return_value = "int_ofport" |
|
phys_br.port_exists.return_value = port_exists |
|
int_br.port_exists.return_value = port_exists |
|
self.agent.setup_physical_bridges({"physnet1": "br-eth"}) |
|
expected_calls = [ |
|
mock.call.phys_br_cls('br-eth'), |
|
mock.call.phys_br.create(), |
|
mock.call.phys_br.set_secure_mode(), |
|
mock.call.phys_br.setup_controllers(mock.ANY), |
|
mock.call.phys_br.setup_default_table(), |
|
mock.call.int_br.delete_port('int-br-eth'), |
|
mock.call.phys_br.delete_port('phy-br-eth'), |
|
mock.call.int_br.port_exists('int-br-eth'), |
|
] |
|
if port_exists: |
|
expected_calls += [ |
|
mock.call.int_br.get_port_ofport('int-br-eth'), |
|
] |
|
else: |
|
expected_calls += [ |
|
mock.call.int_br.add_patch_port( |
|
'int-br-eth', constants.NONEXISTENT_PEER), |
|
] |
|
expected_calls += [ |
|
mock.call.phys_br.port_exists('phy-br-eth'), |
|
] |
|
if port_exists: |
|
expected_calls += [ |
|
mock.call.phys_br.get_port_ofport('phy-br-eth'), |
|
] |
|
else: |
|
expected_calls += [ |
|
mock.call.phys_br.add_patch_port( |
|
'phy-br-eth', constants.NONEXISTENT_PEER), |
|
] |
|
expected_calls += [ |
|
mock.call.int_br.drop_port(in_port='int_ofport'), |
|
mock.call.phys_br.drop_port(in_port='phy_ofport'), |
|
mock.call.int_br.set_db_attribute('Interface', 'int-br-eth', |
|
'options', |
|
{'peer': 'phy-br-eth'}), |
|
mock.call.phys_br.set_db_attribute('Interface', 'phy-br-eth', |
|
'options', |
|
{'peer': 'int-br-eth'}), |
|
] |
|
parent.assert_has_calls(expected_calls) |
|
self.assertEqual("int_ofport", |
|
self.agent.int_ofports["physnet1"]) |
|
self.assertEqual("phy_ofport", |
|
self.agent.phys_ofports["physnet1"]) |
|
|
|
def test_setup_physical_bridges_change_from_veth_to_patch_conf(self): |
|
self._test_setup_physical_bridges_change_from_veth_to_patch_conf() |
|
|
|
def test_setup_physical_bridges_change_from_veth_to_patch_conf_port_exists( |
|
self): |
|
self._test_setup_physical_bridges_change_from_veth_to_patch_conf( |
|
port_exists=True) |
|
|
|
def test_setup_tunnel_br(self): |
|
self.tun_br = mock.Mock() |
|
with mock.patch.object(self.agent.int_br, |
|
"add_patch_port", |
|
return_value=1) as int_patch_port,\ |
|
mock.patch.object(self.agent.tun_br, |
|
"add_patch_port", |
|
return_value=1) as tun_patch_port,\ |
|
mock.patch.object(self.agent.tun_br, 'bridge_exists', |
|
return_value=False),\ |
|
mock.patch.object(self.agent.tun_br, 'create') as create_tun,\ |
|
mock.patch.object(self.agent.tun_br, |
|
'setup_controllers') as setup_controllers,\ |
|
mock.patch.object(self.agent.tun_br, 'port_exists', |
|
return_value=False),\ |
|
mock.patch.object(self.agent.int_br, 'port_exists', |
|
return_value=False),\ |
|
mock.patch.object(sys, "exit"): |
|
self.agent.setup_tunnel_br(None) |
|
self.agent.setup_tunnel_br() |
|
self.assertTrue(create_tun.called) |
|
self.assertTrue(setup_controllers.called) |
|
self.assertTrue(int_patch_port.called) |
|
self.assertTrue(tun_patch_port.called) |
|
|
|
def test_setup_tunnel_br_ports_exits_drop_flows(self): |
|
cfg.CONF.set_override('drop_flows_on_start', True, 'AGENT') |
|
with mock.patch.object(self.agent.tun_br, 'port_exists', |
|
return_value=True),\ |
|
mock.patch.object(self.agent, 'tun_br'),\ |
|
mock.patch.object(self.agent.int_br, 'port_exists', |
|
return_value=True),\ |
|
mock.patch.object(self.agent.tun_br, 'setup_controllers'),\ |
|
mock.patch.object(self.agent, 'patch_tun_ofport', new=2),\ |
|
mock.patch.object(self.agent, 'patch_int_ofport', new=2),\ |
|
mock.patch.object(self.agent.tun_br, |
|
'uninstall_flows') as delete,\ |
|
mock.patch.object(self.agent.int_br, |
|
"add_patch_port") as int_patch_port,\ |
|
mock.patch.object(self.agent.tun_br, |
|
"add_patch_port") as tun_patch_port,\ |
|
mock.patch.object(sys, "exit"): |
|
self.agent.setup_tunnel_br(None) |
|
self.agent.setup_tunnel_br() |
|
self.assertFalse(int_patch_port.called) |
|
self.assertFalse(tun_patch_port.called) |
|
self.assertTrue(delete.called) |
|
|
|
def test_setup_tunnel_port(self): |
|
self.agent.tun_br = mock.Mock() |
|
self.agent.l2_pop = False |
|
self.agent.udp_vxlan_port = 8472 |
|
self.agent.tun_br_ofports['vxlan'] = {} |
|
self.agent.local_ip = '2.3.4.5' |
|
with mock.patch.object(self.agent.tun_br, |
|
"add_tunnel_port", |
|
return_value='6') as add_tun_port_fn,\ |
|
mock.patch.object(self.agent.tun_br, "add_flow"): |
|
self.agent._setup_tunnel_port(self.agent.tun_br, 'portname', |
|
'1.2.3.4', 'vxlan') |
|
self.assertTrue(add_tun_port_fn.called) |
|
|
|
def test_port_unbound(self): |
|
with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn: |
|
self.agent.enable_tunneling = True |
|
lvm = mock.Mock() |
|
lvm.network_type = "gre" |
|
lvm.vif_ports = {"vif1": mock.Mock()} |
|
self.agent.vlan_manager.mapping["netuid12345"] = lvm |
|
self.agent.port_unbound("vif1", "netuid12345") |
|
self.assertTrue(reclvl_fn.called) |
|
|
|
lvm.vif_ports = {} |
|
self.agent.port_unbound("vif1", "netuid12345") |
|
self.assertEqual(2, reclvl_fn.call_count) |
|
|
|
lvm.vif_ports = {"vif1": mock.Mock()} |
|
self.agent.port_unbound("vif3", "netuid12345") |
|
self.assertEqual(2, reclvl_fn.call_count) |
|
|
|
def _prepare_l2_pop_ofports(self): |
|
lvm1 = mock.Mock() |
|
lvm1.network_type = 'gre' |
|
lvm1.vlan = 'vlan1' |
|
lvm1.segmentation_id = 'seg1' |
|
lvm1.tun_ofports = set(['1']) |
|
lvm2 = mock.Mock() |
|
lvm2.network_type = 'gre' |
|
lvm2.vlan = 'vlan2' |
|
lvm2.segmentation_id = 'seg2' |
|
lvm2.tun_ofports = set(['1', '2']) |
|
self.agent.vlan_manager.mapping = {'net1': lvm1, 'net2': lvm2} |
|
self.agent.tun_br_ofports = {'gre': |
|
{'1.1.1.1': '1', '2.2.2.2': '2'}} |
|
self.agent.arp_responder_enabled = True |
|
|
|
def test_fdb_ignore_network(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entry = {'net3': {}} |
|
with mock.patch.object(self.agent.tun_br, 'add_flow') as add_flow_fn,\ |
|
mock.patch.object(self.agent.tun_br, |
|
'uninstall_flows') as del_flow_fn,\ |
|
mock.patch.object(self.agent, |
|
'_setup_tunnel_port') as add_tun_fn,\ |
|
mock.patch.object(self.agent, |
|
'cleanup_tunnel_port') as clean_tun_fn: |
|
self.agent.fdb_add(None, fdb_entry) |
|
self.assertFalse(add_flow_fn.called) |
|
self.assertFalse(add_tun_fn.called) |
|
self.agent.fdb_remove(None, fdb_entry) |
|
self.assertFalse(del_flow_fn.called) |
|
self.assertFalse(clean_tun_fn.called) |
|
|
|
def test_fdb_ignore_self(self): |
|
self._prepare_l2_pop_ofports() |
|
self.agent.local_ip = 'agent_ip' |
|
fdb_entry = {'net2': |
|
{'network_type': 'gre', |
|
'segment_id': 'tun2', |
|
'ports': |
|
{'agent_ip': |
|
[l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), |
|
n_const.FLOODING_ENTRY]}}} |
|
with mock.patch.object(self.agent.tun_br, |
|
"deferred") as defer_fn: |
|
self.agent.fdb_add(None, fdb_entry) |
|
self.assertFalse(defer_fn.called) |
|
|
|
self.agent.fdb_remove(None, fdb_entry) |
|
self.assertFalse(defer_fn.called) |
|
|
|
def test_fdb_add_flows(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entry = {'net1': |
|
{'network_type': 'gre', |
|
'segment_id': 'tun1', |
|
'ports': |
|
{'2.2.2.2': |
|
[l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), |
|
n_const.FLOODING_ENTRY]}}} |
|
|
|
with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br,\ |
|
mock.patch.object(self.agent, |
|
'_setup_tunnel_port', |
|
autospec=True) as add_tun_fn: |
|
self.agent.fdb_add(None, fdb_entry) |
|
self.assertFalse(add_tun_fn.called) |
|
deferred_br_call = mock.call.deferred().__enter__() |
|
expected_calls = [ |
|
deferred_br_call.install_arp_responder('vlan1', FAKE_IP1, |
|
FAKE_MAC), |
|
deferred_br_call.install_unicast_to_tun('vlan1', 'seg1', '2', |
|
FAKE_MAC), |
|
deferred_br_call.install_flood_to_tun('vlan1', 'seg1', |
|
set(['1', '2'])), |
|
] |
|
tun_br.assert_has_calls(expected_calls) |
|
|
|
def test_fdb_del_flows(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entry = {'net2': |
|
{'network_type': 'gre', |
|
'segment_id': 'tun2', |
|
'ports': |
|
{'2.2.2.2': |
|
[l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), |
|
n_const.FLOODING_ENTRY]}}} |
|
with mock.patch.object(self.agent, 'tun_br', autospec=True) as br_tun: |
|
self.agent.fdb_remove(None, fdb_entry) |
|
deferred_br_call = mock.call.deferred().__enter__() |
|
expected_calls = [ |
|
mock.call.deferred(), |
|
mock.call.deferred().__enter__(), |
|
deferred_br_call.delete_arp_responder('vlan2', FAKE_IP1), |
|
deferred_br_call.delete_unicast_to_tun('vlan2', FAKE_MAC), |
|
deferred_br_call.install_flood_to_tun('vlan2', 'seg2', |
|
set(['1'])), |
|
deferred_br_call.delete_port('gre-02020202'), |
|
deferred_br_call.cleanup_tunnel_port('2'), |
|
mock.call.deferred().__exit__(None, None, None), |
|
] |
|
br_tun.assert_has_calls(expected_calls) |
|
|
|
def test_fdb_add_port(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entry = {'net1': |
|
{'network_type': 'gre', |
|
'segment_id': 'tun1', |
|
'ports': {'1.1.1.1': [l2pop_rpc.PortInfo(FAKE_MAC, |
|
FAKE_IP1)]}}} |
|
with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br,\ |
|
mock.patch.object(self.agent, |
|
'_setup_tunnel_port') as add_tun_fn: |
|
self.agent.fdb_add(None, fdb_entry) |
|
self.assertFalse(add_tun_fn.called) |
|
fdb_entry['net1']['ports']['10.10.10.10'] = [ |
|
l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)] |
|
self.agent.fdb_add(None, fdb_entry) |
|
deferred_br = tun_br.deferred().__enter__() |
|
add_tun_fn.assert_called_with( |
|
deferred_br, 'gre-0a0a0a0a', '10.10.10.10', 'gre') |
|
|
|
def test_fdb_del_port(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entry = {'net2': |
|
{'network_type': 'gre', |
|
'segment_id': 'tun2', |
|
'ports': {'2.2.2.2': [n_const.FLOODING_ENTRY]}}} |
|
with mock.patch.object(self.agent.tun_br, 'deferred') as defer_fn,\ |
|
mock.patch.object(self.agent.tun_br, |
|
'delete_port') as delete_port_fn: |
|
self.agent.fdb_remove(None, fdb_entry) |
|
deferred_br = defer_fn().__enter__() |
|
deferred_br.delete_port.assert_called_once_with('gre-02020202') |
|
self.assertFalse(delete_port_fn.called) |
|
|
|
def test_fdb_update_chg_ip(self): |
|
self._prepare_l2_pop_ofports() |
|
fdb_entries = {'chg_ip': |
|
{'net1': |
|
{'agent_ip': |
|
{'before': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)], |
|
'after': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP2)]}}}} |
|
with mock.patch.object(self.agent.tun_br, 'deferred') as deferred_fn: |
|
self.agent.fdb_update(None, fdb_entries) |
|
deferred_br = deferred_fn().__enter__() |
|
deferred_br.assert_has_calls([ |
|
mock.call.install_arp_responder('vlan1', FAKE_IP2, FAKE_MAC), |
|
mock.call.delete_arp_responder('vlan1', FAKE_IP1) |
|
]) |
|
|
|
def test_del_fdb_flow_idempotency(self): |
|
lvm = mock.Mock() |
|
lvm.network_type = 'gre' |
|
lvm.vlan = 'vlan1' |
|
lvm.segmentation_id = 'seg1' |
|
lvm.tun_ofports = set(['1', '2']) |
|
with mock.patch.object(self.agent.tun_br, 'mod_flow') as mod_flow_fn,\ |
|
mock.patch.object(self.agent.tun_br, |
|
'uninstall_flows') as uninstall_flows_fn: |
|
self.agent.del_fdb_flow(self.agent.tun_br, n_const.FLOODING_ENTRY, |
|
'1.1.1.1', lvm, '3') |
|
self.assertFalse(mod_flow_fn.called) |
|
self.assertFalse(uninstall_flows_fn.called) |
|
|
|
def test_recl_lv_port_to_preserve(self): |
|
self._prepare_l2_pop_ofports() |
|
self.agent.l2_pop = True |
|
self.agent.enable_tunneling = True |
|
with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: |
|
self.agent.reclaim_local_vlan('net1') |
|
self.assertFalse(tun_br.cleanup_tunnel_port.called) |
|
|
|
def test_recl_lv_port_to_remove(self): |
|
self._prepare_l2_pop_ofports() |
|
self.agent.l2_pop = True |
|
self.agent.enable_tunneling = True |
|
with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: |
|
self.agent.reclaim_local_vlan('net2') |
|
tun_br.delete_port.assert_called_once_with('gre-02020202') |
|
|
|
def _test_ext_br_recreated(self, setup_bridges_side_effect): |
|
bridge_mappings = {'physnet0': 'br-ex0', |
|
'physnet1': 'br-ex1'} |
|
ex_br_mocks = [mock.Mock(br_name='br-ex0'), |
|
mock.Mock(br_name='br-ex1')] |
|
phys_bridges = {'physnet0': ex_br_mocks[0], |
|
'physnet1': ex_br_mocks[1]}, |
|
bm_mock = mock.Mock() |
|
bridges_added = ['br-ex0'] |
|
expected_added_bridges = ( |
|
bridges_added if setup_bridges_side_effect else []) |
|
with mock.patch( |
|
'neutron.agent.linux.ovsdb_monitor.get_bridges_monitor', |
|
return_value=bm_mock),\ |
|
mock.patch.object( |
|
self.agent, |
|
'check_ovs_status', |
|
return_value=constants.OVS_NORMAL),\ |
|
mock.patch.object( |
|
self.agent, |
|
'_agent_has_updates', |
|
side_effect=TypeError('loop exit')),\ |
|
mock.patch.dict( |
|
self.agent.bridge_mappings, bridge_mappings, clear=True),\ |
|
mock.patch.dict( |
|
self.agent.phys_brs, phys_bridges, clear=True),\ |
|
mock.patch.object( |
|
self.agent, |
|
'setup_physical_bridges') as setup_physical_bridges: |
|
bm_mock.bridges_added = bridges_added |
|
setup_physical_bridges.side_effect = setup_bridges_side_effect |
|
try: |
|
self.agent.rpc_loop(polling_manager=mock.Mock(), |
|
bridges_monitor=bm_mock) |
|
except TypeError: |
|
pass |
|
# Setup bridges should be called once even if it will raise Runtime |
|
# Error because there is raised TypeError in _agent_has_updates to stop |
|
# agent after first loop iteration |
|
setup_physical_bridges.assert_called_once_with({'physnet0': 'br-ex0'}) |
|
self.assertEqual(expected_added_bridges, self.agent.added_bridges) |
|
|
|
def test_ext_br_recreated(self): |
|
self._test_ext_br_recreated(setup_bridges_side_effect=None) |
|
|
|
def test_ext_br_recreated_fail_setup_physical_bridge(self): |
|
self._test_ext_br_recreated(setup_bridges_side_effect=RuntimeError) |
|
|
|
def test_daemon_loop_uses_polling_manager(self): |
|
ex_br_mock = mock.Mock(br_name="br-ex0") |
|
with mock.patch( |
|
'neutron.agent.common.polling.get_polling_manager' |
|
) as mock_get_pm, mock.patch( |
|
'neutron.agent.linux.ovsdb_monitor.get_bridges_monitor' |
|
) as mock_get_bm, mock.patch.object( |
|
self.agent, 'rpc_loop' |
|
) as mock_loop, mock.patch.dict( |
|
self.agent.phys_brs, {'physnet0': ex_br_mock}, clear=True): |
|
|
|
self.agent.daemon_loop() |
|
mock_get_pm.assert_called_with(True, |
|
constants.DEFAULT_OVSDBMON_RESPAWN) |
|
mock_get_bm.assert_called_once_with( |
|
['br-ex0'], constants.DEFAULT_OVSDBMON_RESPAWN) |
|
mock_loop.assert_called_once_with( |
|
|