Bug#987267: unblock: neutron/17.1.1-3
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
Please unblock package neutron
I've packaged the latest point release upstream, because it addresses
some important issues. Here's a link to the release notes for this
point release:
http://lists.openstack.org/pipermail/release-announce/2021-March/010755.html
I'm not particularly interested in one commit in particular, just the
set of all bugfixes described (ie: lost of small annoying bug fixes).
On top of this, the package fixes RC bug #987196. Sorry that it took
me 3 Debian version of the package to get the fix right this afternoon. :/
However, this is completely fixed, as I've tested a fresh compute node
install with version 17.1.1-3.
Debdiff from 17.1.0-2 to 17.1.1-3 attached.
Cheers,
Thomas Goirand (zigo)
diff -Nru neutron-17.1.0/debian/changelog neutron-17.1.1/debian/changelog
--- neutron-17.1.0/debian/changelog 2021-03-15 21:18:42.000000000 +0100
+++ neutron-17.1.1/debian/changelog 2021-04-20 18:59:02.000000000 +0200
@@ -1,3 +1,28 @@
+neutron (2:17.1.1-3) unstable; urgency=medium
+
+ * Remove previous (wrong) fix and correctly generate metadata_agent.ini in
+ the /usr/share/neutron-metadata-agent/ folder of the neutron-metadata-agent
+ package.
+
+ -- Thomas Goirand <zigo@debian.org> Tue, 20 Apr 2021 18:59:02 +0200
+
+neutron (2:17.1.1-2) unstable; urgency=medium
+
+ * Add missing "pkgos_write_new_conf neutron metadata_agent.ini" in the
+ neutron-metadata-agent.postinst.
+
+ -- Thomas Goirand <zigo@debian.org> Tue, 20 Apr 2021 17:44:05 +0200
+
+neutron (2:17.1.1-1) unstable; urgency=medium
+
+ * Tune neutron-api-uwsgi.ini for performance.
+ * New upstream release.
+ * neutron-common: do not manage metadata_agent.ini, and let the
+ neutron-metadata-agent package do it. Thanks to Andreas Beckmann for the
+ bug report. (Closes: #987196).
+
+ -- Thomas Goirand <zigo@debian.org> Tue, 20 Apr 2021 12:31:47 +0200
+
neutron (2:17.1.0-2) unstable; urgency=medium
* Add Breaks: python3-neutron-fwaas (Closes: #985293).
diff -Nru neutron-17.1.0/debian/neutron-api-uwsgi.ini neutron-17.1.1/debian/neutron-api-uwsgi.ini
--- neutron-17.1.0/debian/neutron-api-uwsgi.ini 2021-03-15 21:18:42.000000000 +0100
+++ neutron-17.1.1/debian/neutron-api-uwsgi.ini 2021-04-20 18:59:02.000000000 +0200
@@ -12,11 +12,6 @@
# This is running standalone
master = true
-# Threads and processes
-enable-threads = true
-
-processes = 8
-
# uwsgi recommends this to prevent thundering herd on accept.
thunder-lock = true
@@ -37,6 +32,24 @@
# exit instead of brutal reload on SIGTERM
die-on-term = true
+##########################
+### Performance tuning ###
+##########################
+# Threads and processes
+enable-threads = true
+
+# For max perf, set this to number of core*2
+processes = 8
+
+# This was benchmarked as a good value
+threads = 32
+
+# This is the number of sockets in the queue.
+# It improves a lot performances. This is comparable
+# to the Apache ServerLimit/MaxClients option.
+listen = 100
+
+
##################################
### OpenStack service specific ###
##################################
diff -Nru neutron-17.1.0/debian/neutron-common.postinst.in neutron-17.1.1/debian/neutron-common.postinst.in
--- neutron-17.1.0/debian/neutron-common.postinst.in 2021-03-15 21:18:42.000000000 +0100
+++ neutron-17.1.1/debian/neutron-common.postinst.in 2021-04-20 18:59:02.000000000 +0200
@@ -45,7 +45,6 @@
# Agents:
pkgos_write_new_conf neutron dhcp_agent.ini
pkgos_write_new_conf neutron l3_agent.ini
- pkgos_write_new_conf neutron metadata_agent.ini
# As pkgos_write_new_conf doesn't support different path
# let's workaround installation of config
if [ ! -e /etc/neutron/plugins/ml2/openvswitch_agent.ini ] ; then
diff -Nru neutron-17.1.0/debian/neutron-common.postrm.in neutron-17.1.1/debian/neutron-common.postrm.in
--- neutron-17.1.0/debian/neutron-common.postrm.in 2021-03-15 21:18:42.000000000 +0100
+++ neutron-17.1.1/debian/neutron-common.postrm.in 2021-04-20 18:59:02.000000000 +0200
@@ -18,7 +18,6 @@
rm -f /etc/neutron/dhcp_agent.ini
rm -f /etc/neutron/plugins/ml2/openvswitch_agent.ini
rm -f /etc/neutron/plugins/ml2/ml2_conf.ini
- rm -f /etc/neutron/metadata_agent.ini
[ -d /etc/neutron/plugins/ml2 ] && rmdir --ignore-fail-on-non-empty /etc/neutron/plugins/ml2
[ -d /etc/neutron/plugins ] && rmdir --ignore-fail-on-non-empty /etc/neutron/plugins
diff -Nru neutron-17.1.0/debian/rules neutron-17.1.1/debian/rules
--- neutron-17.1.0/debian/rules 2021-03-15 21:18:42.000000000 +0100
+++ neutron-17.1.1/debian/rules 2021-04-20 18:59:02.000000000 +0200
@@ -124,9 +124,9 @@
--namespace oslo.log
# ml2_conf.ini
- mkdir -p $(CURDIR)/debian/neutron-common/usr/share/neutron-common/plugins/ml2
+ mkdir -p $(CURDIR)/debian/neutron-metadata-agent/usr/share/neutron-metadata-agent
PYTHONPATH=$(CURDIR)/debian/tmp/usr/lib/python3/dist-packages oslo-config-generator \
- --output-file $(CURDIR)/debian/neutron-common/usr/share/neutron-common/plugins/ml2/ml2_conf.ini \
+ --output-file $(CURDIR)/debian/neutron-metadata-agent/usr/share/neutron-metadata-agent/metadata_agent.ini \
--wrap-width 140 \
--namespace neutron.ml2 \
--namespace oslo.log
diff -Nru neutron-17.1.0/doc/source/contributor/internals/openvswitch_firewall.rst neutron-17.1.1/doc/source/contributor/internals/openvswitch_firewall.rst
--- neutron-17.1.0/doc/source/contributor/internals/openvswitch_firewall.rst 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/doc/source/contributor/internals/openvswitch_firewall.rst 2021-03-13 02:26:48.000000000 +0100
@@ -245,16 +245,16 @@
::
- table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=130 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=131 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=132 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=135 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=136 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=130 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=131 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=132 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=135 actions=resubmit(,94)
- table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=136 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=130 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=131 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=132 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=135 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=136 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=130 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=131 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=132 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=135 actions=resubmit(,94)
+ table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=136 actions=resubmit(,94)
Following rules implement ARP spoofing protection
diff -Nru neutron-17.1.0/neutron/agent/common/ovs_lib.py neutron-17.1.1/neutron/agent/common/ovs_lib.py
--- neutron-17.1.0/neutron/agent/common/ovs_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/common/ovs_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -528,7 +528,7 @@
if tunnel_type == TYPE_GRE_IP6:
# NOTE(slaweq) According to the OVS documentation L3 GRE tunnels
# over IPv6 are not supported.
- options['packet_type'] = 'legacy'
+ options['packet_type'] = 'legacy_l2'
attrs.append(('options', options))
return self.add_port(port_name, *attrs)
diff -Nru neutron-17.1.0/neutron/agent/dhcp/agent.py neutron-17.1.1/neutron/agent/dhcp/agent.py
--- neutron-17.1.0/neutron/agent/dhcp/agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/dhcp/agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -27,6 +27,7 @@
from neutron_lib import rpc as n_rpc
from oslo_concurrency import lockutils
from oslo_config import cfg
+from oslo_log import helpers as log_helpers
from oslo_log import log as logging
import oslo_messaging
from oslo_service import loopingcall
@@ -74,6 +75,35 @@
return wrapped
+class DHCPResourceUpdate(queue.ResourceUpdate):
+
+ def __init__(self, _id, priority, action=None, resource=None,
+ timestamp=None, tries=5, obj_type=None):
+ super().__init__(_id, priority, action=action, resource=resource,
+ timestamp=timestamp, tries=tries)
+ self.obj_type = obj_type
+
+ def __lt__(self, other):
+ if other.obj_type == self.obj_type == 'port':
+ # NOTE(ralonsoh): both resources should have "fixed_ips"
+ # information. That key was added to the deleted ports in this
+ # patch but this code runs in the Neutron API (server). Both the
+ # server and the DHCP agent should be updated.
+ # This check could be removed in Y release.
+ if ('fixed_ips' not in self.resource or
+ 'fixed_ips' not in other.resource):
+ return super().__lt__(other)
+
+ self_ips = set(str(fixed_ip['ip_address']) for
+ fixed_ip in self.resource['fixed_ips'])
+ other_ips = set(str(fixed_ip['ip_address']) for
+ fixed_ip in other.resource['fixed_ips'])
+ if self_ips & other_ips:
+ return self.timestamp < other.timestamp
+
+ return super().__lt__(other)
+
+
class DhcpAgent(manager.Manager):
"""DHCP agent service manager.
@@ -445,28 +475,28 @@
def network_create_end(self, context, payload):
"""Handle the network.create.end notification event."""
- update = queue.ResourceUpdate(payload['network']['id'],
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_network_create',
- resource=payload)
+ update = DHCPResourceUpdate(payload['network']['id'],
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_network_create',
+ resource=payload, obj_type='network')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _network_create(self, payload):
network_id = payload['network']['id']
self.enable_dhcp_helper(network_id)
def network_update_end(self, context, payload):
"""Handle the network.update.end notification event."""
- update = queue.ResourceUpdate(payload['network']['id'],
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_network_update',
- resource=payload)
+ update = DHCPResourceUpdate(payload['network']['id'],
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_network_update',
+ resource=payload, obj_type='network')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _network_update(self, payload):
network_id = payload['network']['id']
if payload['network']['admin_state_up']:
@@ -476,28 +506,28 @@
def network_delete_end(self, context, payload):
"""Handle the network.delete.end notification event."""
- update = queue.ResourceUpdate(payload['network_id'],
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_network_delete',
- resource=payload)
+ update = DHCPResourceUpdate(payload['network_id'],
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_network_delete',
+ resource=payload, obj_type='network')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _network_delete(self, payload):
network_id = payload['network_id']
self.disable_dhcp_helper(network_id)
def subnet_update_end(self, context, payload):
"""Handle the subnet.update.end notification event."""
- update = queue.ResourceUpdate(payload['subnet']['network_id'],
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_subnet_update',
- resource=payload)
+ update = DHCPResourceUpdate(payload['subnet']['network_id'],
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_subnet_update',
+ resource=payload, obj_type='subnet')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _subnet_update(self, payload):
network_id = payload['subnet']['network_id']
self.refresh_dhcp_helper(network_id)
@@ -528,14 +558,14 @@
network_id = self._get_network_lock_id(payload)
if not network_id:
return
- update = queue.ResourceUpdate(network_id,
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_subnet_delete',
- resource=payload)
+ update = DHCPResourceUpdate(network_id,
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_subnet_delete',
+ resource=payload, obj_type='subnet')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _subnet_delete(self, payload):
network_id = self._get_network_lock_id(payload)
if not network_id:
@@ -572,17 +602,19 @@
def port_update_end(self, context, payload):
"""Handle the port.update.end notification event."""
updated_port = dhcp.DictModel(payload['port'])
+ if not dhcp.port_requires_dhcp_configuration(updated_port):
+ return
if self.cache.is_port_message_stale(updated_port):
LOG.debug("Discarding stale port update: %s", updated_port)
return
- update = queue.ResourceUpdate(updated_port.network_id,
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_port_update',
- resource=updated_port)
+ update = DHCPResourceUpdate(updated_port.network_id,
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_port_update',
+ resource=updated_port, obj_type='port')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _port_update(self, updated_port):
if self.cache.is_port_message_stale(updated_port):
LOG.debug("Discarding stale port update: %s", updated_port)
@@ -594,7 +626,10 @@
self.reload_allocations(updated_port, network, prio=True)
def reload_allocations(self, port, network, prio=False):
- LOG.info("Trigger reload_allocations for port %s", port)
+ LOG.info("Trigger reload_allocations for port %s on network %s",
+ port, network)
+ if not dhcp.port_requires_dhcp_configuration(port):
+ return
driver_action = 'reload_allocations'
if self._is_port_on_this_agent(port):
orig = self.cache.get_port_by_id(port['id'])
@@ -633,14 +668,16 @@
def port_create_end(self, context, payload):
"""Handle the port.create.end notification event."""
created_port = dhcp.DictModel(payload['port'])
- update = queue.ResourceUpdate(created_port.network_id,
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_port_create',
- resource=created_port)
+ if not dhcp.port_requires_dhcp_configuration(created_port):
+ return
+ update = DHCPResourceUpdate(created_port.network_id,
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_port_create',
+ resource=created_port, obj_type='port')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _port_create(self, created_port):
network = self.cache.get_network_by_id(created_port.network_id)
if not network:
@@ -655,9 +692,18 @@
if (new_ips.intersection(cached_ips) and
(created_port['id'] != port_cached['id'] or
created_port['mac_address'] != port_cached['mac_address'])):
- self.schedule_resync("Duplicate IP addresses found, "
- "DHCP cache is out of sync",
- created_port.network_id)
+ resync_reason = (
+ "Duplicate IP addresses found, "
+ "Port in cache: {cache_port_id}, "
+ "Created port: {port_id}, "
+ "IPs in cache: {cached_ips}, "
+ "new IPs: {new_ips}."
+ "DHCP cache is out of sync").format(
+ cache_port_id=port_cached['id'],
+ port_id=created_port['id'],
+ cached_ips=cached_ips,
+ new_ips=new_ips)
+ self.schedule_resync(resync_reason, created_port.network_id)
return
self.reload_allocations(created_port, network, prio=True)
@@ -666,14 +712,14 @@
network_id = self._get_network_lock_id(payload)
if not network_id:
return
- update = queue.ResourceUpdate(network_id,
- payload.get('priority',
- DEFAULT_PRIORITY),
- action='_port_delete',
- resource=payload)
+ update = DHCPResourceUpdate(network_id,
+ payload.get('priority', DEFAULT_PRIORITY),
+ action='_port_delete',
+ resource=payload, obj_type='port')
self._queue.add(update)
@_wait_if_syncing
+ @log_helpers.log_method_call
def _port_delete(self, payload):
network_id = self._get_network_lock_id(payload)
if not network_id:
diff -Nru neutron-17.1.0/neutron/agent/l3/agent.py neutron-17.1.1/neutron/agent/l3/agent.py
--- neutron-17.1.0/neutron/agent/l3/agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/l3/agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -450,7 +450,6 @@
if router.get('ha'):
features.append('ha')
- kwargs['state_change_callback'] = self.enqueue_state_change
if router.get('distributed') and router.get('ha'):
# Case 1: If the router contains information about the HA interface
@@ -465,7 +464,6 @@
if (not router.get(lib_const.HA_INTERFACE_KEY) or
self.conf.agent_mode != lib_const.L3_AGENT_MODE_DVR_SNAT):
features.remove('ha')
- kwargs.pop('state_change_callback')
return self.router_factory.create(features, **kwargs)
diff -Nru neutron-17.1.0/neutron/agent/l3/dvr_edge_router.py neutron-17.1.1/neutron/agent/l3/dvr_edge_router.py
--- neutron-17.1.0/neutron/agent/l3/dvr_edge_router.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/l3/dvr_edge_router.py 2021-03-13 02:26:48.000000000 +0100
@@ -71,8 +71,10 @@
if self.snat_namespace.exists():
LOG.debug("SNAT was rescheduled to host %s. Clearing snat "
"namespace.", self.router.get('gw_port_host'))
- return self.external_gateway_removed(
- ex_gw_port, interface_name)
+ self.driver.unplug(interface_name,
+ namespace=self.snat_namespace.name,
+ prefix=router.EXTERNAL_DEV_PREFIX)
+ self.snat_namespace.delete()
return
if not self.snat_namespace.exists():
@@ -185,8 +187,8 @@
# TODO(mlavalle): in the near future, this method should contain the
# code in the L3 agent that creates a gateway for a dvr. The first step
# is to move the creation of the snat namespace here
- self.snat_namespace.create()
- return self.snat_namespace
+ if self._is_this_snat_host():
+ self.snat_namespace.create()
def _get_snat_int_device_name(self, port_id):
long_name = lib_constants.SNAT_INT_DEV_PREFIX + port_id
diff -Nru neutron-17.1.0/neutron/agent/l3/ha.py neutron-17.1.1/neutron/agent/l3/ha.py
--- neutron-17.1.0/neutron/agent/l3/ha.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/l3/ha.py 2021-03-13 02:26:48.000000000 +0100
@@ -20,6 +20,7 @@
from neutron_lib import constants
from oslo_log import log as logging
from oslo_utils import fileutils
+from oslo_utils import netutils
import webob
from neutron.agent.linux import utils as agent_utils
@@ -217,9 +218,12 @@
# routers needs to serve metadata requests to local ports.
if state == 'primary' or ri.router.get('distributed', False):
LOG.debug('Spawning metadata proxy for router %s', router_id)
+ spawn_kwargs = {}
+ if netutils.is_ipv6_enabled():
+ spawn_kwargs['bind_address'] = '::'
self.metadata_driver.spawn_monitored_metadata_proxy(
self.process_monitor, ri.ns_name, self.conf.metadata_port,
- self.conf, router_id=ri.router_id)
+ self.conf, router_id=ri.router_id, **spawn_kwargs)
else:
LOG.debug('Closing metadata proxy for router %s', router_id)
self.metadata_driver.destroy_monitored_metadata_proxy(
diff -Nru neutron-17.1.0/neutron/agent/l3/ha_router.py neutron-17.1.1/neutron/agent/l3/ha_router.py
--- neutron-17.1.0/neutron/agent/l3/ha_router.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/l3/ha_router.py 2021-03-13 02:26:48.000000000 +0100
@@ -66,12 +66,11 @@
class HaRouter(router.RouterInfo):
- def __init__(self, state_change_callback, *args, **kwargs):
+ def __init__(self, *args, **kwargs):
super(HaRouter, self).__init__(*args, **kwargs)
self.ha_port = None
self.keepalived_manager = None
- self.state_change_callback = state_change_callback
self._ha_state = None
self._ha_state_path = None
@@ -156,7 +155,6 @@
self._init_keepalived_manager(process_monitor)
self._check_and_set_real_state()
self.ha_network_added()
- self.update_initial_state(self.state_change_callback)
self.spawn_state_change_monitor(process_monitor)
def _init_keepalived_manager(self, process_monitor):
@@ -449,15 +447,6 @@
except common_utils.WaitTimeout:
pm.disable(sig=str(int(signal.SIGKILL)))
- def update_initial_state(self, callback):
- addresses = ip_lib.get_devices_with_ip(self.ha_namespace,
- name=self.get_ha_device_name())
- cidrs = (address['cidr'] for address in addresses)
- ha_cidr = self._get_primary_vip()
- state = 'primary' if ha_cidr in cidrs else 'backup'
- self.ha_state = state
- callback(self.router_id, state)
-
@staticmethod
def _gateway_ports_equal(port1, port2):
def _get_filtered_dict(d, ignore):
@@ -553,8 +542,11 @@
if ex_gw_port_id:
interface_name = self.get_external_device_name(ex_gw_port_id)
ns_name = self.get_gw_ns_name()
- self.driver.set_link_status(interface_name, ns_name,
- link_up=link_up)
+ if (not self.driver.set_link_status(
+ interface_name, namespace=ns_name, link_up=link_up) and
+ link_up):
+ LOG.error('Gateway interface for router %s was not set up; '
+ 'router will not work properly', self.router_id)
if link_up and set_gw:
preserve_ips = self.get_router_preserve_ips()
self._external_gateway_settings(ex_gw_port, interface_name,
diff -Nru neutron-17.1.0/neutron/agent/l3/keepalived_state_change.py neutron-17.1.1/neutron/agent/l3/keepalived_state_change.py
--- neutron-17.1.0/neutron/agent/l3/keepalived_state_change.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/l3/keepalived_state_change.py 2021-03-13 02:26:48.000000000 +0100
@@ -107,12 +107,12 @@
for address in ip.addr.list():
if address.get('cidr') == self.cidr:
state = 'primary'
- self.write_state_change(state)
- self.notify_agent(state)
break
LOG.debug('Initial status of router %s is %s',
self.router_id, state)
+ self.write_state_change(state)
+ self.notify_agent(state)
except Exception:
LOG.exception('Failed to get initial status of router %s',
self.router_id)
diff -Nru neutron-17.1.0/neutron/agent/linux/dhcp.py neutron-17.1.1/neutron/agent/linux/dhcp.py
--- neutron-17.1.0/neutron/agent/linux/dhcp.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/dhcp.py 2021-03-13 02:26:48.000000000 +0100
@@ -59,6 +59,23 @@
DHCP_OPT_CLIENT_ID_NUM = 61
+def port_requires_dhcp_configuration(port):
+ if not getattr(port, 'device_owner', None):
+ # We can't check if port needs dhcp entry, so it will be better
+ # to create one
+ return True
+ # TODO(slaweq): define this list as a constant in neutron_lib.constants
+ # NOTE(slaweq): Not all port types which belongs e.g. to the routers can be
+ # excluded from that list. For some of them, like router interfaces used to
+ # plug subnet to the router should be configured in dnsmasq to provide DNS
+ # naming resolution. Otherwise it may slowdown e.g. traceroutes from the VM
+ return port.device_owner not in [
+ constants.DEVICE_OWNER_ROUTER_HA_INTF,
+ constants.DEVICE_OWNER_FLOATINGIP,
+ constants.DEVICE_OWNER_DHCP,
+ constants.DEVICE_OWNER_DISTRIBUTED]
+
+
class DictModel(collections.abc.MutableMapping):
"""Convert dict into an object that provides attribute access to values."""
@@ -723,6 +740,9 @@
if subnet.ip_version == 6)
for port in self.network.ports:
+ if not port_requires_dhcp_configuration(port):
+ continue
+
fixed_ips = self._sort_fixed_ips_for_dnsmasq(port.fixed_ips,
v6_nets)
# TODO(hjensas): Drop this conditional and option once distros
diff -Nru neutron-17.1.0/neutron/agent/linux/interface.py neutron-17.1.1/neutron/agent/linux/interface.py
--- neutron-17.1.0/neutron/agent/linux/interface.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/interface.py 2021-03-13 02:26:48.000000000 +0100
@@ -322,14 +322,20 @@
def set_link_status(self, device_name, namespace=None, link_up=True):
ns_dev = ip_lib.IPWrapper(namespace=namespace).device(device_name)
- if not ns_dev.exists():
- LOG.debug("Device %s may concurrently be deleted.", device_name)
- return
+ try:
+ utils.wait_until_true(ns_dev.exists, timeout=3)
+ except utils.WaitTimeout:
+ LOG.debug('Device %s may have been deleted concurrently',
+ device_name)
+ return False
+
if link_up:
ns_dev.link.set_up()
else:
ns_dev.link.set_down()
+ return True
+
class NullDriver(LinuxInterfaceDriver):
def plug_new(self, network_id, port_id, device_name, mac_address,
diff -Nru neutron-17.1.0/neutron/agent/linux/ip_conntrack.py neutron-17.1.1/neutron/agent/linux/ip_conntrack.py
--- neutron-17.1.0/neutron/agent/linux/ip_conntrack.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/ip_conntrack.py 2021-03-13 02:26:48.000000000 +0100
@@ -116,6 +116,7 @@
ethertype = rule.get('ethertype')
protocol = rule.get('protocol')
direction = rule.get('direction')
+ mark = rule.get('mark')
cmd = ['conntrack', '-D']
if protocol is not None:
# 0 is IP in /etc/protocols, but conntrack will throw an error
@@ -123,6 +124,8 @@
protocol = 'ip'
cmd.extend(['-p', str(protocol)])
cmd.extend(['-f', str(ethertype).lower()])
+ if mark is not None:
+ cmd.extend(['-m', str(mark)])
cmd.append('-d' if direction == 'ingress' else '-s')
cmd_ns = []
if namespace:
@@ -173,10 +176,12 @@
self._process(device_info_list, rule)
def delete_conntrack_state_by_remote_ips(self, device_info_list,
- ethertype, remote_ips):
+ ethertype, remote_ips, mark=None):
for direction in ['ingress', 'egress']:
rule = {'ethertype': str(ethertype).lower(),
'direction': direction}
+ if mark:
+ rule['mark'] = mark
self._process(device_info_list, rule, remote_ips)
def _populate_initial_zone_map(self):
@@ -254,3 +259,21 @@
return index + ZONE_START
# conntrack zones exhausted :( :(
raise exceptions.CTZoneExhaustedError()
+
+
+class OvsIpConntrackManager(IpConntrackManager):
+
+ def __init__(self, execute=None):
+ super(OvsIpConntrackManager, self).__init__(
+ get_rules_for_table_func=None,
+ filtered_ports={}, unfiltered_ports={},
+ execute=execute, namespace=None, zone_per_port=False)
+
+ def _populate_initial_zone_map(self):
+ self._device_zone_map = {}
+
+ def get_device_zone(self, port, create=False):
+ of_port = port.get('of_port')
+ if of_port is None:
+ return
+ return of_port.vlan_tag
diff -Nru neutron-17.1.0/neutron/agent/linux/ip_lib.py neutron-17.1.1/neutron/agent/linux/ip_lib.py
--- neutron-17.1.0/neutron/agent/linux/ip_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/ip_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -1340,35 +1340,37 @@
'event': event}
-def _parse_link_device(namespace, device, **kwargs):
- """Parse pytoute2 link device information
-
- For each link device, the IP address information is retrieved and returned
- in a dictionary.
- IP address scope: http://linux-ip.net/html/tools-ip-address.html
- """
- retval = []
- name = get_attr(device, 'IFLA_IFNAME')
- ip_addresses = privileged.get_ip_addresses(namespace,
- index=device['index'],
- **kwargs)
- for ip_address in ip_addresses:
- retval.append(_parse_ip_address(ip_address, name))
- return retval
-
-
def get_devices_with_ip(namespace, name=None, **kwargs):
+ retval = []
link_args = {}
if name:
link_args['ifname'] = name
scope = kwargs.pop('scope', None)
if scope:
kwargs['scope'] = IP_ADDRESS_SCOPE_NAME[scope]
- devices = privileged.get_link_devices(namespace, **link_args)
- retval = []
- for parsed_ips in (_parse_link_device(namespace, device, **kwargs)
- for device in devices):
- retval += parsed_ips
+
+ if not link_args:
+ ip_addresses = privileged.get_ip_addresses(namespace, **kwargs)
+ else:
+ device = get_devices_info(namespace, **link_args)
+ if not device:
+ return retval
+ ip_addresses = privileged.get_ip_addresses(
+ namespace, index=device[0]['index'], **kwargs)
+
+ devices = {} # {device index: name}
+ for ip_address in ip_addresses:
+ index = ip_address['index']
+ name = get_attr(ip_address, 'IFA_LABEL') or devices.get(index)
+ if not name:
+ device = get_devices_info(namespace, index=index)
+ if not device:
+ continue
+ name = device[0]['name']
+
+ retval.append(_parse_ip_address(ip_address, name))
+ devices[index] = name
+
return retval
diff -Nru neutron-17.1.0/neutron/agent/linux/iptables_manager.py neutron-17.1.1/neutron/agent/linux/iptables_manager.py
--- neutron-17.1.0/neutron/agent/linux/iptables_manager.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/iptables_manager.py 2021-03-13 02:26:48.000000000 +0100
@@ -308,7 +308,8 @@
_random_fully = None
def __init__(self, _execute=None, state_less=False, use_ipv6=False,
- nat=True, namespace=None, binary_name=binary_name):
+ nat=True, namespace=None, binary_name=binary_name,
+ external_lock=True):
if _execute:
self.execute = _execute
else:
@@ -318,6 +319,7 @@
self.namespace = namespace
self.iptables_apply_deferred = False
self.wrap_name = binary_name[:16]
+ self.external_lock = external_lock
self.ipv4 = {'filter': IptablesTable(binary_name=self.wrap_name)}
self.ipv6 = {'filter': IptablesTable(binary_name=self.wrap_name)}
@@ -463,7 +465,8 @@
# NOTE(ihrachys) we may get rid of the lock once all supported
# platforms get iptables with 999eaa241212d3952ddff39a99d0d55a74e3639e
# ("iptables-restore: support acquiring the lock.")
- with lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX, True):
+ with lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX,
+ external=self.external_lock):
first = self._apply_synchronized()
if not cfg.CONF.AGENT.debug_iptables_rules:
return first
diff -Nru neutron-17.1.0/neutron/agent/linux/openvswitch_firewall/firewall.py neutron-17.1.1/neutron/agent/linux/openvswitch_firewall/firewall.py
--- neutron-17.1.0/neutron/agent/linux/openvswitch_firewall/firewall.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/linux/openvswitch_firewall/firewall.py 2021-03-13 02:26:48.000000000 +0100
@@ -32,6 +32,7 @@
from neutron._i18n import _
from neutron.agent.common import ovs_lib
from neutron.agent import firewall
+from neutron.agent.linux import ip_conntrack
from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts
from neutron.agent.linux.openvswitch_firewall import exceptions
from neutron.agent.linux.openvswitch_firewall import iptables
@@ -476,13 +477,12 @@
"""
self.permitted_ethertypes = cfg.CONF.SECURITYGROUP.permitted_ethertypes
self.int_br = self.initialize_bridge(integration_bridge)
- self.sg_port_map = SGPortMap()
- self.conj_ip_manager = ConjIPFlowManager(self)
- self.sg_to_delete = set()
+ self._initialize_sg()
self._update_cookie = None
self._deferred = False
self.iptables_helper = iptables.Helper(self.int_br.br)
self.iptables_helper.load_driver_if_needed()
+ self.ipconntrack = ip_conntrack.OvsIpConntrackManager()
self._initialize_firewall()
callbacks_registry.subscribe(
@@ -492,8 +492,14 @@
def _init_firewall_callback(self, resource, event, trigger, payload=None):
LOG.info("Reinitialize Openvswitch firewall after OVS restart.")
+ self._initialize_sg()
self._initialize_firewall()
+ def _initialize_sg(self):
+ self.sg_port_map = SGPortMap()
+ self.conj_ip_manager = ConjIPFlowManager(self)
+ self.sg_to_delete = set()
+
def _initialize_firewall(self):
self._drop_all_unmatched_flows()
self._initialize_common_flows()
@@ -608,6 +614,12 @@
return get_physical_network_from_other_config(
self.int_br.br, port_name)
+ def _delete_invalid_conntrack_entries_for_port(self, port, of_port):
+ port['of_port'] = of_port
+ for ethertype in [lib_const.IPv4, lib_const.IPv6]:
+ self.ipconntrack.delete_conntrack_state_by_remote_ips(
+ [port], ethertype, set(), mark=ovsfw_consts.CT_MARK_INVALID)
+
def get_ofport(self, port):
port_id = port['device']
return self.sg_port_map.ports.get(port_id)
@@ -662,6 +674,7 @@
self._update_flows_for_port(of_port, old_of_port)
else:
self._set_port_filters(of_port)
+ self._delete_invalid_conntrack_entries_for_port(port, of_port)
except exceptions.OVSFWPortNotFound as not_found_error:
LOG.info("port %(port_id)s does not exist in ovsdb: %(err)s.",
{'port_id': port['device'],
@@ -701,6 +714,8 @@
else:
self._set_port_filters(of_port)
+ self._delete_invalid_conntrack_entries_for_port(port, of_port)
+
except exceptions.OVSFWPortNotFound as not_found_error:
LOG.info("port %(port_id)s does not exist in ovsdb: %(err)s.",
{'port_id': port['device'],
@@ -894,19 +909,24 @@
self._initialize_egress(port)
self._initialize_ingress(port)
- def _initialize_egress_ipv6_icmp(self, port):
- for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
- self._add_flow(
- table=ovs_consts.BASE_EGRESS_TABLE,
- priority=95,
- in_port=port.ofport,
- reg_port=port.ofport,
- dl_type=lib_const.ETHERTYPE_IPV6,
- nw_proto=lib_const.PROTO_NUM_IPV6_ICMP,
- icmp_type=icmp_type,
- actions='resubmit(,%d)' % (
- ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
- )
+ def _initialize_egress_ipv6_icmp(self, port, allowed_pairs):
+ # NOTE(slaweq): should we include also fe80::/64 (link-local) subnet
+ # in the allowed pairs here?
+ for mac_addr, ip_addr in allowed_pairs:
+ for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
+ self._add_flow(
+ table=ovs_consts.BASE_EGRESS_TABLE,
+ priority=95,
+ in_port=port.ofport,
+ reg_port=port.ofport,
+ dl_type=lib_const.ETHERTYPE_IPV6,
+ nw_proto=lib_const.PROTO_NUM_IPV6_ICMP,
+ icmp_type=icmp_type,
+ dl_src=mac_addr,
+ ipv6_src=ip_addr,
+ actions='resubmit(,%d)' % (
+ ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
+ )
def _initialize_egress_no_port_security(self, port_id, ovs_ports=None):
try:
@@ -979,7 +999,6 @@
def _initialize_egress(self, port):
"""Identify egress traffic and send it to egress base"""
- self._initialize_egress_ipv6_icmp(port)
# Apply mac/ip pairs for IPv4
allowed_pairs = port.allowed_pairs_v4.union(
@@ -1012,6 +1031,7 @@
# Apply mac/ip pairs for IPv6
allowed_pairs = port.allowed_pairs_v6.union(
{(port.mac, ip_addr) for ip_addr in port.ipv6_addresses})
+ self._initialize_egress_ipv6_icmp(port, allowed_pairs)
for mac_addr, ip_addr in allowed_pairs:
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,
diff -Nru neutron-17.1.0/neutron/agent/ovn/metadata/agent.py neutron-17.1.1/neutron/agent/ovn/metadata/agent.py
--- neutron-17.1.0/neutron/agent/ovn/metadata/agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/ovn/metadata/agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -16,20 +16,23 @@
import functools
import re
+from neutron_lib import constants as n_const
+from oslo_concurrency import lockutils
+from oslo_log import log
+from oslo_utils import uuidutils
+from ovsdbapp.backend.ovs_idl import event as row_event
+from ovsdbapp.backend.ovs_idl import vlog
+import tenacity
+
from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib
+from neutron.agent.linux import iptables_manager
from neutron.agent.ovn.metadata import driver as metadata_driver
from neutron.agent.ovn.metadata import ovsdb
from neutron.agent.ovn.metadata import server as metadata_server
from neutron.common.ovn import constants as ovn_const
from neutron.common import utils
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
-from neutron_lib import constants as n_const
-from oslo_concurrency import lockutils
-from oslo_log import log
-from oslo_utils import uuidutils
-from ovsdbapp.backend.ovs_idl import event as row_event
-from ovsdbapp.backend.ovs_idl import vlog
LOG = log.getLogger(__name__)
@@ -248,6 +251,10 @@
proxy.wait()
+ @tenacity.retry(
+ wait=tenacity.wait_exponential(
+ max=config.get_ovn_ovsdb_retry_max_interval()),
+ reraise=True)
def register_metadata_agent(self):
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
# it's already set.
@@ -361,6 +368,24 @@
else:
self.teardown_datapath(datapath)
+ def _ensure_datapath_checksum(self, namespace):
+ """Ensure the correct checksum in the metadata packets in DPDK bridges
+
+ (LP#1904871) In DPDK deployments (integration bridge datapath_type ==
+ "netdev"), the checksum between the metadata namespace and OVS is not
+ correctly populated.
+ """
+ if (self.ovs_idl.db_get(
+ 'Bridge', self.ovn_bridge, 'datapath_type').execute() !=
+ ovn_const.CHASSIS_DATAPATH_NETDEV):
+ return
+
+ iptables_mgr = iptables_manager.IptablesManager(
+ use_ipv6=True, nat=False, namespace=namespace, external_lock=False)
+ rule = '-p tcp -m tcp -j CHECKSUM --checksum-fill'
+ iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', rule, wrap=False)
+ iptables_mgr.apply()
+
def provision_datapath(self, datapath):
"""Provision the datapath so that it can serve metadata.
@@ -468,6 +493,9 @@
'Interface', veth_name[0],
('external_ids', {'iface-id': port.logical_port})).execute()
+ # Ensure the correct checksum in the metadata traffic.
+ self._ensure_datapath_checksum(namespace)
+
# Spawn metadata proxy if it's not already running.
metadata_driver.MetadataDriver.spawn_monitored_metadata_proxy(
self._process_monitor, namespace, n_const.METADATA_PORT,
diff -Nru neutron-17.1.0/neutron/agent/securitygroups_rpc.py neutron-17.1.1/neutron/agent/securitygroups_rpc.py
--- neutron-17.1.0/neutron/agent/securitygroups_rpc.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/agent/securitygroups_rpc.py 2021-03-13 02:26:48.000000000 +0100
@@ -18,6 +18,7 @@
from neutron_lib.api.definitions import rbac_security_groups as rbac_sg_apidef
from neutron_lib.api.definitions import stateful_security_group as stateful_sg
+from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
@@ -62,6 +63,9 @@
self.context = context
self.plugin_rpc = plugin_rpc
self.init_firewall(defer_refresh_firewall, integration_bridge)
+ # _latest_port_filter_lock will point to the lock created for the
+ # most recent thread to enter _apply_port_filters().
+ self._latest_port_filter_lock = lockutils.ReaderWriterLock()
def _get_trusted_devices(self, device_ids, devices):
trusted_devices = []
@@ -77,6 +81,27 @@
trusted_devices.append(device_id)
return trusted_devices
+ def _port_filter_lock(func):
+ """Decorator to acquire a new lock while applying port filters"""
+ @functools.wraps(func)
+ def decorated_function(self, *args, **kwargs):
+ lock = lockutils.ReaderWriterLock()
+ # Tracking the most recent lock at the instance level allows
+ # waiters to only wait for the most recent lock to be released
+ # instead of waiting until all locks have been released.
+ self._latest_port_filter_lock = lock
+ with lock.write_lock():
+ return func(self, *args, **kwargs)
+ return decorated_function
+
+ def _port_filter_wait(func):
+ """Decorator to wait for the latest port filter lock to be released"""
+ @functools.wraps(func)
+ def decorated_function(self, *args, **kwargs):
+ with self._latest_port_filter_lock.read_lock():
+ return func(self, *args, **kwargs)
+ return decorated_function
+
def init_firewall(self, defer_refresh_firewall=False,
integration_bridge=None):
firewall_driver = cfg.CONF.SECURITYGROUP.firewall_driver or 'noop'
@@ -138,6 +163,7 @@
LOG.info("Preparing filters for devices %s", device_ids)
self._apply_port_filter(device_ids)
+ @_port_filter_lock
def _apply_port_filter(self, device_ids, update_filter=False):
step = common_constants.AGENT_RES_PROCESSING_STEP
if self.use_enhanced_rpc:
@@ -195,6 +221,7 @@
'security_group_source_groups',
'sg_member')
+ @_port_filter_wait
def _security_group_updated(self, security_groups, attribute, action_type):
devices = []
sec_grp_set = set(security_groups)
diff -Nru neutron-17.1.0/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py neutron-17.1.1/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py
--- neutron-17.1.0/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/api/rpc/agentnotifiers/dhcp_rpc_agent_api.py 2021-03-13 02:26:48.000000000 +0100
@@ -268,7 +268,8 @@
def _after_router_interface_deleted(self, resource, event, trigger,
**kwargs):
self._notify_agents(kwargs['context'], 'port_delete_end',
- {'port_id': kwargs['port']['id']},
+ {'port_id': kwargs['port']['id'],
+ 'fixed_ips': kwargs['port']['fixed_ips']},
kwargs['port']['network_id'])
def _native_event_send_dhcp_notification(self, resource, event, trigger,
@@ -343,6 +344,8 @@
payload = {obj_type + '_id': obj_value['id']}
if obj_type != 'network':
payload['network_id'] = network_id
+ if obj_type == 'port':
+ payload['fixed_ips'] = obj_value['fixed_ips']
self._notify_agents(context, method_name, payload, network_id)
else:
self._notify_agents(context, method_name, data, network_id)
diff -Nru neutron-17.1.0/neutron/common/_constants.py neutron-17.1.1/neutron/common/_constants.py
--- neutron-17.1.0/neutron/common/_constants.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/common/_constants.py 2021-03-13 02:26:48.000000000 +0100
@@ -76,4 +76,5 @@
# with these owners, it will allow subnet deletion to proceed with the
# IP allocations being cleaned up by cascade.
AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP,
- constants.DEVICE_OWNER_DISTRIBUTED]
+ constants.DEVICE_OWNER_DISTRIBUTED,
+ constants.DEVICE_OWNER_AGENT_GW]
diff -Nru neutron-17.1.0/neutron/common/ovn/constants.py neutron-17.1.1/neutron/common/ovn/constants.py
--- neutron-17.1.0/neutron/common/ovn/constants.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/common/ovn/constants.py 2021-03-13 02:26:48.000000000 +0100
@@ -274,6 +274,8 @@
LSP_TYPE_EXTERNAL = 'external'
LSP_OPTIONS_VIRTUAL_PARENTS_KEY = 'virtual-parents'
LSP_OPTIONS_VIRTUAL_IP_KEY = 'virtual-ip'
+LSP_OPTIONS_MCAST_FLOOD_REPORTS = 'mcast_flood_reports'
+LSP_OPTIONS_MCAST_FLOOD = 'mcast_flood'
HA_CHASSIS_GROUP_DEFAULT_NAME = 'default_ha_chassis_group'
HA_CHASSIS_GROUP_HIGHEST_PRIORITY = 32767
diff -Nru neutron-17.1.0/neutron/db/db_base_plugin_common.py neutron-17.1.1/neutron/db/db_base_plugin_common.py
--- neutron-17.1.0/neutron/db/db_base_plugin_common.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/db/db_base_plugin_common.py 2021-03-13 02:26:48.000000000 +0100
@@ -217,7 +217,8 @@
def _make_port_dict(self, port, fields=None,
process_extensions=True,
- with_fixed_ips=True):
+ with_fixed_ips=True,
+ bulk=False):
mac = port["mac_address"]
if isinstance(mac, netaddr.EUI):
mac.dialect = netaddr.mac_unix_expanded
@@ -240,8 +241,10 @@
port_data = port
if isinstance(port, port_obj.Port):
port_data = port.db_obj
+ res['bulk'] = bulk
resource_extend.apply_funcs(
port_def.COLLECTION_NAME, res, port_data)
+ res.pop('bulk')
return db_utils.resource_fields(res, fields)
def _get_network(self, context, id):
diff -Nru neutron-17.1.0/neutron/db/db_base_plugin_v2.py neutron-17.1.1/neutron/db/db_base_plugin_v2.py
--- neutron-17.1.0/neutron/db/db_base_plugin_v2.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/db/db_base_plugin_v2.py 2021-03-13 02:26:48.000000000 +0100
@@ -1563,7 +1563,9 @@
sorts=sorts, limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
- items = [self._make_port_dict(c, fields) for c in query]
+ items = [self._make_port_dict(c, fields, bulk=True) for c in query]
+ # TODO(obondarev): use neutron_lib constant
+ resource_extend.apply_funcs('ports_bulk', items, None)
if limit and page_reverse:
items.reverse()
return items
diff -Nru neutron-17.1.0/neutron/db/l3_dvr_db.py neutron-17.1.1/neutron/db/l3_dvr_db.py
--- neutron-17.1.0/neutron/db/l3_dvr_db.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/db/l3_dvr_db.py 2021-03-13 02:26:48.000000000 +0100
@@ -13,6 +13,8 @@
# under the License.
import collections
+import netaddr
+from neutron_lib.api.definitions import external_net as extnet_apidef
from neutron_lib.api.definitions import l3 as l3_apidef
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import portbindings_extended
@@ -48,6 +50,7 @@
from neutron.extensions import _admin_state_down_before_update_lib
from neutron.ipam import utils as ipam_utils
from neutron.objects import agent as ag_obj
+from neutron.objects import base as base_obj
from neutron.objects import l3agent as rb_obj
from neutron.objects import router as l3_obj
@@ -409,6 +412,15 @@
if host_id:
return
+ @registry.receives(resources.NETWORK, [events.AFTER_DELETE])
+ def delete_fip_namespaces_for_ext_net(self, rtype, event, trigger,
+ context, network, **kwargs):
+ if network.get(extnet_apidef.EXTERNAL):
+ # Send the information to all the L3 Agent hosts
+ # to clean up the fip namespace as it is no longer required.
+ self.l3plugin.l3_rpc_notifier.delete_fipnamespace_for_ext_net(
+ context, network['id'])
+
def _get_ports_for_allowed_address_pair_ip(self, context, network_id,
fixed_ip):
"""Return all active ports associated with the allowed_addr_pair ip."""
@@ -469,6 +481,17 @@
fixed_ip_address))
if not addr_pair_active_service_port_list:
return
+ self._inherit_service_port_and_arp_update(
+ context, addr_pair_active_service_port_list[0])
+
+ def _inherit_service_port_and_arp_update(self, context, service_port):
+ """Function inherits port host bindings for allowed_address_pair."""
+ service_port_dict = self.l3plugin._core_plugin._make_port_dict(
+ service_port)
+ address_pair_list = service_port_dict.get('allowed_address_pairs')
+ for address_pair in address_pair_list:
+ self.update_arp_entry_for_dvr_service_port(context,
+ service_port_dict)
@registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_CREATE])
@db_api.retry_if_session_inactive()
@@ -1104,6 +1127,21 @@
self._populate_mtu_and_subnets_for_ports(context, [agent_port])
return agent_port
+ def _generate_arp_table_and_notify_agent(self, context, fixed_ip,
+ mac_address, notifier):
+ """Generates the arp table entry and notifies the l3 agent."""
+ ip_address = fixed_ip['ip_address']
+ subnet = fixed_ip['subnet_id']
+ arp_table = {'ip_address': ip_address,
+ 'mac_address': mac_address,
+ 'subnet_id': subnet}
+ filters = {'fixed_ips': {'subnet_id': [subnet]},
+ 'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]}
+ ports = self._core_plugin.get_ports(context, filters=filters)
+ routers = [port['device_id'] for port in ports]
+ for router_id in routers:
+ notifier(context, router_id, arp_table)
+
def _get_subnet_id_for_given_fixed_ip(self, context, fixed_ip, port_dict):
"""Returns the subnet_id that matches the fixedip on a network."""
filters = {'network_id': [port_dict['network_id']]}
@@ -1112,6 +1150,78 @@
if ipam_utils.check_subnet_ip(subnet['cidr'], fixed_ip):
return subnet['id']
+ def _get_allowed_address_pair_fixed_ips(self, context, port_dict):
+ """Returns all fixed_ips associated with the allowed_address_pair."""
+ aa_pair_fixed_ips = []
+ if port_dict.get('allowed_address_pairs'):
+ for address_pair in port_dict['allowed_address_pairs']:
+ aap_ip_cidr = address_pair['ip_address'].split("/")
+ if len(aap_ip_cidr) == 1 or int(aap_ip_cidr[1]) == 32:
+ subnet_id = self._get_subnet_id_for_given_fixed_ip(
+ context, aap_ip_cidr[0], port_dict)
+ if subnet_id is not None:
+ fixed_ip = {'subnet_id': subnet_id,
+ 'ip_address': aap_ip_cidr[0]}
+ aa_pair_fixed_ips.append(fixed_ip)
+ else:
+ LOG.debug("Subnet does not match for the given "
+ "fixed_ip %s for arp update", aap_ip_cidr[0])
+ return aa_pair_fixed_ips
+
+ def update_arp_entry_for_dvr_service_port(self, context, port_dict):
+ """Notify L3 agents of ARP table entry for dvr service port.
+
+ When a dvr service port goes up, look for the DVR router on
+ the port's subnet, and send the ARP details to all
+ L3 agents hosting the router to add it.
+ If there are any allowed_address_pairs associated with the port
+ those fixed_ips should also be updated in the ARP table.
+ """
+ fixed_ips = port_dict['fixed_ips']
+ if not fixed_ips:
+ return
+ allowed_address_pair_fixed_ips = (
+ self._get_allowed_address_pair_fixed_ips(context, port_dict))
+ changed_fixed_ips = fixed_ips + allowed_address_pair_fixed_ips
+ for fixed_ip in changed_fixed_ips:
+ self._generate_arp_table_and_notify_agent(
+ context, fixed_ip, port_dict['mac_address'],
+ self.l3_rpc_notifier.add_arp_entry)
+
+ def delete_arp_entry_for_dvr_service_port(self, context, port_dict,
+ fixed_ips_to_delete=None):
+ """Notify L3 agents of ARP table entry for dvr service port.
+
+ When a dvr service port goes down, look for the DVR
+ router on the port's subnet, and send the ARP details to all
+ L3 agents hosting the router to delete it.
+ If there are any allowed_address_pairs associated with the
+ port, those fixed_ips should be removed from the ARP table.
+ """
+ fixed_ips = port_dict['fixed_ips']
+ if not fixed_ips:
+ return
+ if not fixed_ips_to_delete:
+ allowed_address_pair_fixed_ips = (
+ self._get_allowed_address_pair_fixed_ips(context, port_dict))
+ fixed_ips_to_delete = fixed_ips + allowed_address_pair_fixed_ips
+ for fixed_ip in fixed_ips_to_delete:
+ self._generate_arp_table_and_notify_agent(
+ context, fixed_ip, port_dict['mac_address'],
+ self.l3_rpc_notifier.del_arp_entry)
+
+ def _get_address_pair_active_port_with_fip(
+ self, context, port_dict, port_addr_pair_ip):
+ port_valid_state = (port_dict['admin_state_up'] or
+ port_dict['status'] == const.PORT_STATUS_ACTIVE)
+ if not port_valid_state:
+ return
+ fips = l3_obj.FloatingIP.get_objects(
+ context, _pager=base_obj.Pager(limit=1),
+ fixed_ip_address=netaddr.IPAddress(port_addr_pair_ip))
+ return self._core_plugin.get_port(
+ context, fips[0].fixed_port_id) if fips else None
+
class L3_NAT_with_dvr_db_mixin(_DVRAgentInterfaceMixin,
DVRResourceOperationHandler,
diff -Nru neutron-17.1.0/neutron/db/l3_dvrscheduler_db.py neutron-17.1.1/neutron/db/l3_dvrscheduler_db.py
--- neutron-17.1.0/neutron/db/l3_dvrscheduler_db.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/db/l3_dvrscheduler_db.py 2021-03-13 02:26:48.000000000 +0100
@@ -518,6 +518,19 @@
return any([r in dvr_routers for r in related_routers])
+def _dvr_handle_unbound_allowed_addr_pair_add(
+ plugin, context, port, allowed_address_pair):
+ plugin.update_arp_entry_for_dvr_service_port(context, port)
+
+
+def _dvr_handle_unbound_allowed_addr_pair_del(
+ plugin, context, port, allowed_address_pair):
+ aa_fixed_ips = plugin._get_allowed_address_pair_fixed_ips(context, port)
+ if aa_fixed_ips:
+ plugin.delete_arp_entry_for_dvr_service_port(
+ context, port, fixed_ips_to_delete=aa_fixed_ips)
+
+
def _notify_l3_agent_new_port(resource, event, trigger, **kwargs):
LOG.debug('Received %(resource)s %(event)s', {
'resource': resource,
@@ -530,6 +543,7 @@
l3plugin = directory.get_plugin(plugin_constants.L3)
context = kwargs['context']
l3plugin.dvr_handle_new_service_port(context, port)
+ l3plugin.update_arp_entry_for_dvr_service_port(context, port)
def _notify_port_delete(event, resource, trigger, **kwargs):
@@ -537,6 +551,14 @@
port = kwargs['port']
get_related_hosts_info = kwargs.get("get_related_hosts_info", True)
l3plugin = directory.get_plugin(plugin_constants.L3)
+ if port:
+ port_host = port.get(portbindings.HOST_ID)
+ allowed_address_pairs_list = port.get('allowed_address_pairs')
+ if allowed_address_pairs_list and port_host:
+ for address_pair in allowed_address_pairs_list:
+ _dvr_handle_unbound_allowed_addr_pair_del(
+ l3plugin, context, port, address_pair)
+ l3plugin.delete_arp_entry_for_dvr_service_port(context, port)
removed_routers = l3plugin.get_dvr_routers_to_remove(
context, port, get_related_hosts_info)
for info in removed_routers:
@@ -625,7 +647,32 @@
context, new_port,
dest_host=dest_host,
router_id=fip_router_id)
+ l3plugin.update_arp_entry_for_dvr_service_port(
+ context, new_port)
return
+ # Check for allowed_address_pairs and port state
+ new_port_host = new_port.get(portbindings.HOST_ID)
+ allowed_address_pairs_list = new_port.get('allowed_address_pairs')
+ if allowed_address_pairs_list and new_port_host:
+ new_port_state = new_port.get('admin_state_up')
+ original_port_state = original_port.get('admin_state_up')
+ if new_port_state:
+ # Case were we activate the port from inactive state,
+ # or the same port has additional address_pairs added.
+ for address_pair in allowed_address_pairs_list:
+ _dvr_handle_unbound_allowed_addr_pair_add(
+ l3plugin, context, new_port, address_pair)
+ return
+ elif original_port_state:
+ # Case were we deactivate the port from active state.
+ for address_pair in allowed_address_pairs_list:
+ _dvr_handle_unbound_allowed_addr_pair_del(
+ l3plugin, context, original_port, address_pair)
+ return
+
+ if kwargs.get('mac_address_updated') or is_fixed_ips_changed:
+ l3plugin.update_arp_entry_for_dvr_service_port(
+ context, new_port)
def subscribe():
diff -Nru neutron-17.1.0/neutron/db/securitygroups_db.py neutron-17.1.1/neutron/db/securitygroups_db.py
--- neutron-17.1.0/neutron/db/securitygroups_db.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/db/securitygroups_db.py 2021-03-13 02:26:48.000000000 +0100
@@ -14,6 +14,7 @@
import netaddr
from neutron_lib.api.definitions import port as port_def
+from neutron_lib.api import extensions
from neutron_lib.api import validators
from neutron_lib.callbacks import events
from neutron_lib.callbacks import exceptions
@@ -860,6 +861,8 @@
:returns: the default security group id for given tenant.
"""
+ if not extensions.is_extension_supported(self, 'security-group'):
+ return
default_group_id = self._get_default_sg_id(context, tenant_id)
if default_group_id:
return default_group_id
@@ -918,7 +921,8 @@
port_project = port.get('tenant_id')
default_sg = self._ensure_default_security_group(context,
port_project)
- port[ext_sg.SECURITYGROUPS] = [default_sg]
+ if default_sg:
+ port[ext_sg.SECURITYGROUPS] = [default_sg]
def _check_update_deletes_security_groups(self, port):
"""Return True if port has as a security group and it's value
diff -Nru neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/backports.py neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/backports.py
--- neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/backports.py 1970-01-01 01:00:00.000000000 +0100
+++ neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/backports.py 2021-03-13 02:26:48.000000000 +0100
@@ -0,0 +1,36 @@
+# Copyright 2021 Red Hat, Inc.
+#
+# 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.
+#
+# We don't technically require ovsdbapp that has these fixes so
+# just include them here for stable releases
+try:
+ from ovsdbapp.backend.ovs_idl import idlutils
+ frozen_row = idlutils.frozen_row
+except AttributeError:
+ def frozen_row(row):
+ return row._table.rows.IndexEntry(
+ uuid=row.uuid,
+ **{col: getattr(row, col)
+ for col in row._table.columns if hasattr(row, col)})
+
+try:
+ from ovsdbapp.backend.ovs_idl import event as row_event
+ from ovsdbapp import event as ovsdb_event
+
+ RowEventHandler = row_event.RowEventHandler
+except AttributeError:
+ class RowEventHandler(ovsdb_event.RowEventHandler):
+ def notify(self, event, row, updates=None):
+ row = frozen_row(row)
+ super().notify(event, row, updates)
diff -Nru neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py
--- neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py 2021-03-13 02:26:48.000000000 +0100
@@ -661,6 +661,34 @@
txn.add(cmd)
raise periodics.NeverAgain()
+ # TODO(lucasagomes): Remove this in the Y cycle
+ # A static spacing value is used here, but this method will only run
+ # once per lock due to the use of periodics.NeverAgain().
+ @periodics.periodic(spacing=600, run_immediately=True)
+ def check_for_mcast_flood_reports(self):
+ cmds = []
+ for port in self._nb_idl.lsp_list().execute(check_error=True):
+ port_type = port.type.strip()
+ if port_type in ("vtep", "localport", "router"):
+ continue
+
+ options = port.options
+ if ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS in options:
+ continue
+
+ options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
+ if port_type == ovn_const.LSP_TYPE_LOCALNET:
+ options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'})
+
+ cmds.append(self._nb_idl.lsp_set_options(port.name, **options))
+
+ if cmds:
+ with self._nb_idl.transaction(check_error=True) as txn:
+ for cmd in cmds:
+ txn.add(cmd)
+
+ raise periodics.NeverAgain()
+
class HashRingHealthCheckPeriodics(object):
diff -Nru neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
--- neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py 2021-03-13 02:26:48.000000000 +0100
@@ -298,6 +298,14 @@
options.update({'requested-chassis':
port.get(portbindings.HOST_ID, '')})
+ # TODO(lucasagomes): Enable the mcast_flood_reports by default,
+ # according to core OVN developers it shouldn't cause any harm
+ # and will be ignored when mcast_snoop is False. We can revise
+ # this once https://bugzilla.redhat.com/show_bug.cgi?id=1933990
+ # (see comment #3) is fixed in Core OVN.
+ if port_type not in ('vtep', 'localport', 'router'):
+ options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
+
device_owner = port.get('device_owner', '')
sg_ids = ' '.join(utils.get_lsp_security_groups(port))
return OvnPortInfo(port_type, options, addresses, port_security,
@@ -1557,6 +1565,9 @@
def create_provnet_port(self, network_id, segment, txn=None):
tag = segment.get(segment_def.SEGMENTATION_ID, [])
physnet = segment.get(segment_def.PHYSICAL_NETWORK)
+ options = {'network_name': physnet,
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'}
cmd = self._nb_idl.create_lswitch_port(
lport_name=utils.ovn_provnet_port_name(segment['id']),
lswitch_name=utils.ovn_name(network_id),
@@ -1564,7 +1575,7 @@
external_ids={},
type=ovn_const.LSP_TYPE_LOCALNET,
tag=tag,
- options={'network_name': physnet})
+ options=options)
self._transaction([cmd], txn=txn)
def delete_provnet_port(self, network_id, segment):
@@ -1951,7 +1962,8 @@
def create_subnet(self, context, subnet, network):
if subnet['enable_dhcp']:
if subnet['ip_version'] == 4:
- self.update_metadata_port(context, network['id'])
+ self.update_metadata_port(context, network['id'],
+ subnet_id=subnet['id'])
self._add_subnet_dhcp_options(subnet, network)
db_rev.bump_revision(context, subnet, ovn_const.TYPE_SUBNETS)
@@ -1968,7 +1980,8 @@
subnet['id'])['subnet']
if subnet['enable_dhcp'] or ovn_subnet:
- self.update_metadata_port(context, network['id'])
+ self.update_metadata_port(context, network['id'],
+ subnet_id=subnet['id'])
check_rev_cmd = self._nb_idl.check_revision_number(
subnet['id'], subnet, ovn_const.TYPE_SUBNETS)
@@ -2076,12 +2089,24 @@
# TODO(boden): rehome create_port into neutron-lib
p_utils.create_port(self._plugin, context, port)
- def update_metadata_port(self, context, network_id):
+ def update_metadata_port(self, context, network_id, subnet_id=None):
"""Update metadata port.
This function will allocate an IP address for the metadata port of
- the given network in all its IPv4 subnets.
+ the given network in all its IPv4 subnets or the given subnet.
"""
+ def update_metadata_port_fixed_ips(metadata_port, subnet_ids):
+ wanted_fixed_ips = [
+ {'subnet_id': fixed_ip['subnet_id'],
+ 'ip_address': fixed_ip['ip_address']} for fixed_ip in
+ metadata_port['fixed_ips']]
+ wanted_fixed_ips.extend({'subnet_id': s_id} for s_id in subnet_ids)
+ port = {'id': metadata_port['id'],
+ 'port': {'network_id': network_id,
+ 'fixed_ips': wanted_fixed_ips}}
+ self._plugin.update_port(n_context.get_admin_context(),
+ metadata_port['id'], port)
+
if not ovn_conf.is_ovn_metadata_enabled():
return
@@ -2092,31 +2117,28 @@
network_id)
return
+ port_subnet_ids = set(ip['subnet_id'] for ip in
+ metadata_port['fixed_ips'])
+
+ # If this method is called from "create_subnet" or "update_subnet",
+ # only the fixed IP address from this subnet should be updated in the
+ # metadata port.
+ if subnet_id:
+ if subnet_id not in port_subnet_ids:
+ update_metadata_port_fixed_ips(metadata_port, [subnet_id])
+ return
+
# Retrieve all subnets in this network
subnets = self._plugin.get_subnets(context, filters=dict(
network_id=[network_id], ip_version=[4]))
subnet_ids = set(s['id'] for s in subnets)
- port_subnet_ids = set(ip['subnet_id'] for ip in
- metadata_port['fixed_ips'])
# Find all subnets where metadata port doesn't have an IP in and
# allocate one.
if subnet_ids != port_subnet_ids:
- wanted_fixed_ips = []
- for fixed_ip in metadata_port['fixed_ips']:
- wanted_fixed_ips.append(
- {'subnet_id': fixed_ip['subnet_id'],
- 'ip_address': fixed_ip['ip_address']})
- wanted_fixed_ips.extend(
- dict(subnet_id=s)
- for s in subnet_ids - port_subnet_ids)
-
- port = {'id': metadata_port['id'],
- 'port': {'network_id': network_id,
- 'fixed_ips': wanted_fixed_ips}}
- self._plugin.update_port(n_context.get_admin_context(),
- metadata_port['id'], port)
+ update_metadata_port_fixed_ips(metadata_port,
+ subnet_ids - port_subnet_ids)
def get_parent_port(self, port_id):
return self._nb_idl.get_parent_port(port_id)
diff -Nru neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py
--- neutron-17.1.0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py 2021-03-13 02:26:48.000000000 +0100
@@ -27,7 +27,6 @@
from ovsdbapp.backend.ovs_idl import connection
from ovsdbapp.backend.ovs_idl import event as row_event
from ovsdbapp.backend.ovs_idl import idlutils
-from ovsdbapp import event
from neutron.common.ovn import constants as ovn_const
from neutron.common.ovn import exceptions
@@ -35,6 +34,7 @@
from neutron.common.ovn import utils
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
from neutron.db import ovn_hash_ring_db
+from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import backports
CONF = cfg.CONF
@@ -358,7 +358,7 @@
self.event_name = 'PortGroupCreated'
-class OvnDbNotifyHandler(event.RowEventHandler):
+class OvnDbNotifyHandler(backports.RowEventHandler):
def __init__(self, driver):
super(OvnDbNotifyHandler, self).__init__()
self.driver = driver
@@ -374,7 +374,7 @@
class BaseOvnIdl(Ml2OvnIdlBase):
def __init__(self, remote, schema):
- self.notify_handler = event.RowEventHandler()
+ self.notify_handler = backports.RowEventHandler()
super(BaseOvnIdl, self).__init__(remote, schema)
@classmethod
diff -Nru neutron-17.1.0/neutron/services/portforwarding/pf_plugin.py neutron-17.1.1/neutron/services/portforwarding/pf_plugin.py
--- neutron-17.1.0/neutron/services/portforwarding/pf_plugin.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/portforwarding/pf_plugin.py 2021-03-13 02:26:48.000000000 +0100
@@ -33,6 +33,7 @@
from neutron_lib.plugins import constants
from neutron_lib.plugins import directory
from oslo_config import cfg
+from oslo_db import exception as oslo_db_exc
from oslo_log import log as logging
from neutron._i18n import _
@@ -430,7 +431,7 @@
pf_obj.update_fields(port_forwarding, reset_changes=True)
self._check_port_forwarding_update(context, pf_obj)
pf_obj.update()
- except obj_exc.NeutronDbObjectDuplicateEntry:
+ except oslo_db_exc.DBDuplicateEntry:
(__, conflict_params) = self._find_existing_port_forwarding(
context, floatingip_id, pf_obj.to_dict())
message = _("A duplicate port forwarding entry with same "
diff -Nru neutron-17.1.0/neutron/services/qos/drivers/manager.py neutron-17.1.1/neutron/services/qos/drivers/manager.py
--- neutron-17.1.0/neutron/services/qos/drivers/manager.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/qos/drivers/manager.py 2021-03-13 02:26:48.000000000 +0100
@@ -154,6 +154,18 @@
return False
+ def validate_rule_for_network(self, context, rule, network_id):
+ for driver in self._drivers:
+ if driver.is_rule_supported(rule):
+ # https://review.opendev.org/c/openstack/neutron-lib/+/774083
+ # is not present, in this release, in neutron-lib.
+ if hasattr(driver, 'validate_rule_for_network'):
+ return driver.validate_rule_for_network(context, rule,
+ network_id)
+ return True
+
+ return False
+
@property
def supported_rule_types(self):
if not self._drivers:
diff -Nru neutron-17.1.0/neutron/services/qos/drivers/openvswitch/driver.py neutron-17.1.1/neutron/services/qos/drivers/openvswitch/driver.py
--- neutron-17.1.0/neutron/services/qos/drivers/openvswitch/driver.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/qos/drivers/openvswitch/driver.py 2021-03-13 02:26:48.000000000 +0100
@@ -60,11 +60,14 @@
requires_rpc_notifications=True)
def validate_rule_for_port(self, context, rule, port):
+ return self.validate_rule_for_network(context, rule, port.network_id)
+
+ def validate_rule_for_network(self, context, rule, network_id):
# Minimum-bandwidth rule is only supported on networks whose
# first segment is backed by a physnet.
if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
net = network_object.Network.get_object(
- context, id=port.network_id)
+ context, id=network_id)
physnet = net.segments[0].physical_network
if physnet is None:
return False
diff -Nru neutron-17.1.0/neutron/services/qos/qos_plugin.py neutron-17.1.1/neutron/services/qos/qos_plugin.py
--- neutron-17.1.0/neutron/services/qos/qos_plugin.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/qos/qos_plugin.py 2021-03-13 02:26:48.000000000 +0100
@@ -44,10 +44,16 @@
from neutron.objects import ports as ports_object
from neutron.objects.qos import policy as policy_object
from neutron.objects.qos import qos_policy_validator as checker
+from neutron.objects.qos import rule as rule_object
from neutron.objects.qos import rule_type as rule_type_object
from neutron.services.qos.drivers import manager
+class QosRuleNotSupportedByNetwork(lib_exc.Conflict):
+ message = _("Rule %(rule_type)s is not supported "
+ "by network %(network_id)s")
+
+
@resource_extend.has_resource_extenders
class QoSPlugin(qos.QoSPluginBase):
"""Implementation of the Neutron QoS Service Plugin.
@@ -85,6 +91,10 @@
self._validate_update_network_callback,
callbacks_resources.NETWORK,
callbacks_events.PRECOMMIT_UPDATE)
+ callbacks_registry.subscribe(
+ self._validate_create_network_callback,
+ callbacks_resources.NETWORK,
+ callbacks_events.PRECOMMIT_CREATE)
@staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME])
@@ -102,9 +112,34 @@
port_res['resource_request'] = None
if not qos_id:
return port_res
- qos_policy = policy_object.QosPolicy.get_object(
- context.get_admin_context(), id=qos_id)
+ if port_res.get('bulk'):
+ port_res['resource_request'] = {
+ 'qos_id': qos_id,
+ 'network_id': port_db.network_id,
+ 'vnic_type': port_res[portbindings.VNIC_TYPE]}
+ return port_res
+
+ min_bw_rules = rule_object.QosMinimumBandwidthRule.get_objects(
+ context.get_admin_context(), qos_policy_id=qos_id)
+ resources = QoSPlugin._get_resources(min_bw_rules)
+ if not resources:
+ return port_res
+
+ segments = network_object.NetworkSegment.get_objects(
+ context.get_admin_context(), network_id=port_db.network_id)
+ traits = QoSPlugin._get_traits(port_res[portbindings.VNIC_TYPE],
+ segments)
+ if not traits:
+ return port_res
+
+ port_res['resource_request'] = {
+ 'required': traits,
+ 'resources': resources}
+ return port_res
+
+ @staticmethod
+ def _get_resources(min_bw_rules):
resources = {}
# NOTE(ralonsoh): we should move this translation dict to n-lib.
rule_direction_class = {
@@ -113,34 +148,71 @@
nl_constants.EGRESS_DIRECTION:
pl_constants.CLASS_NET_BW_EGRESS_KBPS
}
- for rule in qos_policy.rules:
- if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
- resources[rule_direction_class[rule.direction]] = rule.min_kbps
- if not resources:
- return port_res
-
- # NOTE(ralonsoh): we should not rely on the current execution order of
- # the port extending functions. Although here we have
- # port_res[VNIC_TYPE], we should retrieve this value from the port DB
- # object instead.
- vnic_trait = pl_utils.vnic_type_trait(
- port_res[portbindings.VNIC_TYPE])
+ for rule in min_bw_rules:
+ resources[rule_direction_class[rule.direction]] = rule.min_kbps
+ return resources
+ @staticmethod
+ def _get_traits(vnic_type, segments):
# TODO(lajoskatona): Change to handle all segments when any traits
# support will be available. See Placement spec:
# https://review.opendev.org/565730
- first_segment = network_object.NetworkSegment.get_objects(
- context.get_admin_context(), network_id=port_db.network_id)[0]
-
+ first_segment = segments[0]
if not first_segment or not first_segment.physical_network:
- return port_res
+ return []
physnet_trait = pl_utils.physnet_trait(
first_segment.physical_network)
+ # NOTE(ralonsoh): we should not rely on the current execution order of
+ # the port extending functions. Although here we have
+ # port_res[VNIC_TYPE], we should retrieve this value from the port DB
+ # object instead.
+ vnic_trait = pl_utils.vnic_type_trait(vnic_type)
- port_res['resource_request'] = {
- 'required': [physnet_trait, vnic_trait],
- 'resources': resources}
- return port_res
+ return [physnet_trait, vnic_trait]
+
+ @staticmethod
+ # TODO(obondarev): use neutron_lib constant
+ @resource_extend.extends(['ports_bulk'])
+ def _extend_port_resource_request_bulk(ports_res, noop):
+ """Add resource request to a list of ports."""
+ min_bw_rules = dict()
+ net_segments = dict()
+
+ for port_res in ports_res:
+ if port_res.get('resource_request') is None:
+ continue
+ qos_id = port_res['resource_request'].pop('qos_id', None)
+ if not qos_id:
+ port_res['resource_request'] = None
+ continue
+
+ net_id = port_res['resource_request'].pop('network_id')
+ vnic_type = port_res['resource_request'].pop('vnic_type')
+
+ if qos_id not in min_bw_rules:
+ rules = rule_object.QosMinimumBandwidthRule.get_objects(
+ context.get_admin_context(), qos_policy_id=qos_id)
+ min_bw_rules[qos_id] = rules
+
+ resources = QoSPlugin._get_resources(min_bw_rules[qos_id])
+ if not resources:
+ continue
+
+ if net_id not in net_segments:
+ segments = network_object.NetworkSegment.get_objects(
+ context.get_admin_context(),
+ network_id=net_id)
+ net_segments[net_id] = segments
+
+ traits = QoSPlugin._get_traits(vnic_type, net_segments[net_id])
+ if not traits:
+ continue
+
+ port_res['resource_request'] = {
+ 'required': traits,
+ 'resources': resources}
+
+ return ports_res
def _get_ports_with_policy(self, context, policy):
networks_ids = policy.get_bound_networks()
@@ -189,6 +261,20 @@
self.validate_policy_for_port(context, policy, updated_port)
+ def _validate_create_network_callback(self, resource, event, trigger,
+ **kwargs):
+ context = kwargs['context']
+ network_id = kwargs['network']['id']
+ network = network_object.Network.get_object(context, id=network_id)
+
+ policy_id = network.qos_policy_id
+ if policy_id is None:
+ return
+
+ policy = policy_object.QosPolicy.get_object(
+ context.elevated(), id=policy_id)
+ self.validate_policy_for_network(context, policy, network_id)
+
def _validate_update_network_callback(self, resource, event, trigger,
payload=None):
context = payload.context
@@ -203,6 +289,9 @@
policy = policy_object.QosPolicy.get_object(
context.elevated(), id=policy_id)
+ self.validate_policy_for_network(
+ context, policy, network_id=updated_network['id'])
+
ports = ports_object.Port.get_objects(
context, network_id=updated_network['id'])
# Filter only this ports which don't have overwritten policy
@@ -226,6 +315,13 @@
raise qos_exc.QosRuleNotSupported(rule_type=rule.rule_type,
port_id=port['id'])
+ def validate_policy_for_network(self, context, policy, network_id):
+ for rule in policy.rules:
+ if not self.driver_manager.validate_rule_for_network(
+ context, rule, network_id):
+ raise QosRuleNotSupportedByNetwork(
+ rule_type=rule.rule_type, network_id=network_id)
+
def reject_min_bw_rule_updates(self, context, policy):
ports = self._get_ports_with_policy(context, policy)
for port in ports:
diff -Nru neutron-17.1.0/neutron/services/trunk/plugin.py neutron-17.1.1/neutron/services/trunk/plugin.py
--- neutron-17.1.0/neutron/services/trunk/plugin.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/trunk/plugin.py 2021-03-13 02:26:48.000000000 +0100
@@ -79,17 +79,44 @@
'port_id': x.port_id}
for x in port_db.trunk_port.sub_ports
}
- core_plugin = directory.get_plugin()
- ports = core_plugin.get_ports(
- context.get_admin_context(), filters={'id': subports})
- for port in ports:
- subports[port['id']]['mac_address'] = port['mac_address']
+ if not port_res.get('bulk'):
+ core_plugin = directory.get_plugin()
+ ports = core_plugin.get_ports(
+ context.get_admin_context(), filters={'id': subports})
+ for port in ports:
+ subports[port['id']]['mac_address'] = port['mac_address']
trunk_details = {'trunk_id': port_db.trunk_port.id,
'sub_ports': list(subports.values())}
port_res['trunk_details'] = trunk_details
return port_res
+ @staticmethod
+ # TODO(obondarev): use neutron_lib constant
+ @resource_extend.extends(['ports_bulk'])
+ def _extend_port_trunk_details_bulk(ports_res, noop):
+ """Add trunk subport details to a list of ports."""
+ subport_ids = []
+ trunk_ports = []
+ for p in ports_res:
+ if 'trunk_details' in p and 'subports' in p['trunk_details']:
+ trunk_ports.append(p)
+ for subp in p['trunk_details']['subports']:
+ subport_ids.append(subp['port_id'])
+ if not subport_ids:
+ return ports_res
+
+ core_plugin = directory.get_plugin()
+ subports = core_plugin.get_ports(
+ context.get_admin_context(), filters={'id': subport_ids})
+ subport_macs = {p['id']: p['mac_address'] for p in subports}
+
+ for tp in trunk_ports:
+ for subp in tp['trunk_details']['subports']:
+ subp['mac_address'] = subport_macs[subp['port_id']]
+
+ return ports_res
+
def check_compatibility(self):
"""Verify the plugin can load correctly and fail otherwise."""
self.check_driver_compatibility()
diff -Nru neutron-17.1.0/neutron/services/trunk/rpc/server.py neutron-17.1.1/neutron/services/trunk/rpc/server.py
--- neutron-17.1.0/neutron/services/trunk/rpc/server.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/services/trunk/rpc/server.py 2021-03-13 02:26:48.000000000 +0100
@@ -127,6 +127,12 @@
trunk_port_id = trunk.port_id
trunk_port = self.core_plugin.get_port(context, trunk_port_id)
trunk_host = trunk_port.get(portbindings.HOST_ID)
+ migrating_to_host = trunk_port.get(
+ portbindings.PROFILE, {}).get('migrating_to')
+ if migrating_to_host and trunk_host != migrating_to_host:
+ # Trunk is migrating now, so lets update host of the subports
+ # to the new host already
+ trunk_host = migrating_to_host
# NOTE(status_police) Set the trunk in BUILD state before
# processing subport bindings. The trunk will stay in BUILD
diff -Nru neutron-17.1.0/neutron/tests/fullstack/resources/client.py neutron-17.1.1/neutron/tests/fullstack/resources/client.py
--- neutron-17.1.0/neutron/tests/fullstack/resources/client.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/fullstack/resources/client.py 2021-03-13 02:26:48.000000000 +0100
@@ -81,7 +81,7 @@
def create_network(self, tenant_id, name=None, external=False,
network_type=None, segmentation_id=None,
- physical_network=None, mtu=None):
+ physical_network=None, mtu=None, qos_policy_id=None):
resource_type = 'network'
name = name or utils.get_rand_name(prefix=resource_type)
@@ -96,6 +96,8 @@
spec['provider:physical_network'] = physical_network
if mtu is not None:
spec['mtu'] = mtu
+ if qos_policy_id is not None:
+ spec['qos_policy_id'] = qos_policy_id
return self._create_resource(resource_type, spec)
diff -Nru neutron-17.1.0/neutron/tests/fullstack/resources/config.py neutron-17.1.1/neutron/tests/fullstack/resources/config.py
--- neutron-17.1.0/neutron/tests/fullstack/resources/config.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/fullstack/resources/config.py 2021-03-13 02:26:48.000000000 +0100
@@ -82,7 +82,7 @@
'password': rabbitmq_environment.password,
'host': rabbitmq_environment.host,
'vhost': rabbitmq_environment.vhost},
- 'api_workers': '2',
+ 'api_workers': str(env_desc.api_workers),
},
'database': {
'connection': connection,
diff -Nru neutron-17.1.0/neutron/tests/fullstack/resources/environment.py neutron-17.1.1/neutron/tests/fullstack/resources/environment.py
--- neutron-17.1.0/neutron/tests/fullstack/resources/environment.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/fullstack/resources/environment.py 2021-03-13 02:26:48.000000000 +0100
@@ -40,7 +40,8 @@
global_mtu=constants.DEFAULT_NETWORK_MTU,
debug_iptables=False, log=False, report_bandwidths=False,
has_placement=False, placement_port=None,
- dhcp_scheduler_class=None, ml2_extension_drivers=None):
+ dhcp_scheduler_class=None, ml2_extension_drivers=None,
+ api_workers=1):
self.network_type = network_type
self.l2_pop = l2_pop
self.qos = qos
@@ -62,6 +63,7 @@
if self.log:
self.service_plugins += ',log'
self.ml2_extension_drivers = ml2_extension_drivers
+ self.api_workers = api_workers
@property
def tunneling_enabled(self):
diff -Nru neutron-17.1.0/neutron/tests/fullstack/test_dhcp_agent.py neutron-17.1.1/neutron/tests/fullstack/test_dhcp_agent.py
--- neutron-17.1.0/neutron/tests/fullstack/test_dhcp_agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/fullstack/test_dhcp_agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -38,6 +38,7 @@
]
boot_vm_for_test = True
dhcp_scheduler_class = None
+ api_workers = 1
def setUp(self):
host_descriptions = [
@@ -52,6 +53,7 @@
arp_responder=False,
agent_down_time=self.agent_down_time,
dhcp_scheduler_class=self.dhcp_scheduler_class,
+ api_workers=self.api_workers,
),
host_descriptions)
@@ -205,6 +207,7 @@
agent_down_time = 30
number_of_hosts = 2
boot_vm_for_test = False
+ api_workers = 2
dhcp_scheduler_class = ('neutron.tests.fullstack.schedulers.dhcp.'
'AlwaysTheOtherAgentScheduler')
diff -Nru neutron-17.1.0/neutron/tests/fullstack/test_qos.py neutron-17.1.1/neutron/tests/fullstack/test_qos.py
--- neutron-17.1.0/neutron/tests/fullstack/test_qos.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/fullstack/test_qos.py 2021-03-13 02:26:48.000000000 +0100
@@ -707,6 +707,31 @@
queues = '\nList of OVS Queue registers:\n%s' % '\n'.join(queues)
self.fail(queuenum + qoses + queues)
+ def test_min_bw_qos_create_network_vxlan_not_supported(self):
+ qos_policy = self._create_qos_policy()
+ qos_policy_id = qos_policy['id']
+ self.safe_client.create_minimum_bandwidth_rule(
+ self.tenant_id, qos_policy_id, MIN_BANDWIDTH, self.direction)
+ network_args = {'network_type': 'vxlan',
+ 'qos_policy_id': qos_policy_id}
+ self.assertRaises(
+ exceptions.Conflict,
+ self.safe_client.create_network,
+ self.tenant_id, name='network-test', **network_args)
+
+ def test_min_bw_qos_update_network_vxlan_not_supported(self):
+ network_args = {'network_type': 'vxlan'}
+ network = self.safe_client.create_network(
+ self.tenant_id, name='network-test', **network_args)
+ qos_policy = self._create_qos_policy()
+ qos_policy_id = qos_policy['id']
+ self.safe_client.create_minimum_bandwidth_rule(
+ self.tenant_id, qos_policy_id, MIN_BANDWIDTH, self.direction)
+ self.assertRaises(
+ exceptions.Conflict,
+ self.client.update_network, network['id'],
+ body={'network': {'qos_policy_id': qos_policy_id}})
+
def test_min_bw_qos_port_removed(self):
"""Test if min BW limit config is properly removed when port removed.
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/common/test_ovs_lib.py neutron-17.1.1/neutron/tests/functional/agent/common/test_ovs_lib.py
--- neutron-17.1.0/neutron/tests/functional/agent/common/test_ovs_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/common/test_ovs_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -485,4 +485,4 @@
ipv6_port_options = interface['options']
self.assertEqual(p_const.TYPE_GRE, ipv4_port_type)
self.assertEqual(ovs_lib.TYPE_GRE_IP6, ipv6_port_type)
- self.assertEqual('legacy', ipv6_port_options.get('packet_type'))
+ self.assertEqual('legacy_l2', ipv6_port_options.get('packet_type'))
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/l3/test_dvr_router.py neutron-17.1.1/neutron/tests/functional/agent/l3/test_dvr_router.py
--- neutron-17.1.0/neutron/tests/functional/agent/l3/test_dvr_router.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/l3/test_dvr_router.py 2021-03-13 02:26:48.000000000 +0100
@@ -781,6 +781,54 @@
self._assert_iptables_rules_exist(
iptables_mgr, 'nat', expected_rules)
+ def test_dvr_router_fip_associations_exist_when_router_reenabled(self):
+ """Test to validate the fip associations when router is re-enabled.
+
+ This test validates the fip associations when the router is disabled
+ and enabled back again. This test is specifically for the host where
+ snat namespace is not created or gateway port is binded on other host.
+ """
+ self.agent.conf.agent_mode = 'dvr_snat'
+ router_info = self.generate_dvr_router_info(enable_snat=True)
+ # Ensure agent does not create snat namespace by changing gw_port_host
+ router_info['gw_port_host'] = 'agent2'
+ router_info_copy = copy.deepcopy(router_info)
+ router1 = self.manage_router(self.agent, router_info)
+
+ fip_ns_name = router1.fip_ns.name
+ self.assertTrue(self._namespace_exists(router1.fip_ns.name))
+
+ # Simulate disable router
+ self.agent._safe_router_removed(router1.router['id'])
+ self.assertFalse(self._namespace_exists(router1.ns_name))
+ self.assertTrue(self._namespace_exists(fip_ns_name))
+
+ # Simulated enable router
+ router_updated = self.manage_router(self.agent, router_info_copy)
+ self._assert_dvr_floating_ips(router_updated)
+
+ def test_dvr_router_fip_associations_exist_when_snat_removed(self):
+ """Test to validate the fip associations when snat is removed.
+
+ This test validates the fip associations when the snat is removed from
+ the agent. The fip associations should exist when the snat is moved to
+ another l3 agent.
+ """
+ self.agent.conf.agent_mode = 'dvr_snat'
+ router_info = self.generate_dvr_router_info(enable_snat=True)
+ router_info_copy = copy.deepcopy(router_info)
+ router1 = self.manage_router(self.agent, router_info)
+
+ # Remove gateway port host and the binding host_id to simulate
+ # removal of snat from l3 agent
+ router_info_copy['gw_port_host'] = ''
+ router_info_copy['gw_port']['binding:host_id'] = ''
+ router_info_copy['gw_port']['binding:vif_type'] = 'unbound'
+ router_info_copy['gw_port']['binding:vif_details'] = {}
+ self.agent._process_updated_router(router_info_copy)
+ router_updated = self.agent.router_info[router1.router['id']]
+ self._assert_dvr_floating_ips(router_updated)
+
def test_dvr_router_with_ha_for_fip_disassociation(self):
"""Test to validate the fip rules are deleted in dvr_snat_ha router.
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/linux/test_ip_lib.py neutron-17.1.1/neutron/tests/functional/agent/linux/test_ip_lib.py
--- neutron-17.1.0/neutron/tests/functional/agent/linux/test_ip_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/linux/test_ip_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -14,6 +14,7 @@
# under the License.
import collections
+import copy
import itertools
import signal
@@ -1013,3 +1014,83 @@
devices_filtered = self.device.addr.list(scope=scope)
devices_cidr = {device['cidr'] for device in devices_filtered}
self.assertIn(_ip, devices_cidr)
+
+
+class GetDevicesWithIpTestCase(functional_base.BaseSudoTestCase):
+
+ def setUp(self):
+ super().setUp()
+ self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name
+ self.devices = []
+ self.num_devices = 5
+ self.num_devices_with_ip = 3
+ for idx in range(self.num_devices):
+ dev_name = 'test_device_%s' % idx
+ ip_lib.IPWrapper(self.namespace).add_dummy(dev_name)
+ device = ip_lib.IPDevice(dev_name, namespace=self.namespace)
+ device.link.set_up()
+ self.devices.append(device)
+
+ self.cidrs = [netaddr.IPNetwork('10.10.0.0/24'),
+ netaddr.IPNetwork('10.20.0.0/24'),
+ netaddr.IPNetwork('2001:db8:1234:1111::/64'),
+ netaddr.IPNetwork('2001:db8:1234:2222::/64')]
+ for idx in range(self.num_devices_with_ip):
+ for cidr in self.cidrs:
+ self.devices[idx].addr.add(str(cidr.ip + idx) + '/' +
+ str(cidr.netmask.netmask_bits()))
+
+ @staticmethod
+ def _remove_loopback_interface(ip_addresses):
+ return [ipa for ipa in ip_addresses if
+ ipa['name'] != ip_lib.LOOPBACK_DEVNAME]
+
+ @staticmethod
+ def _remove_ipv6_scope_link(ip_addresses):
+ # Remove all IPv6 addresses with scope link (fe80::...).
+ return [ipa for ipa in ip_addresses if not (
+ ipa['scope'] == 'link' and utils.get_ip_version(ipa['cidr']))]
+
+ @staticmethod
+ def _pop_ip_address(ip_addresses, cidr):
+ for idx, ip_address in enumerate(copy.deepcopy(ip_addresses)):
+ if cidr == ip_address['cidr']:
+ ip_addresses.pop(idx)
+ return
+
+ def test_get_devices_with_ip(self):
+ ip_addresses = ip_lib.get_devices_with_ip(self.namespace)
+ ip_addresses = self._remove_loopback_interface(ip_addresses)
+ ip_addresses = self._remove_ipv6_scope_link(ip_addresses)
+ self.assertEqual(self.num_devices_with_ip * len(self.cidrs),
+ len(ip_addresses))
+ for idx in range(self.num_devices_with_ip):
+ for cidr in self.cidrs:
+ cidr = (str(cidr.ip + idx) + '/' +
+ str(cidr.netmask.netmask_bits()))
+ self._pop_ip_address(ip_addresses, cidr)
+
+ self.assertEqual(0, len(ip_addresses))
+
+ def test_get_devices_with_ip_name(self):
+ for idx in range(self.num_devices_with_ip):
+ dev_name = 'test_device_%s' % idx
+ ip_addresses = ip_lib.get_devices_with_ip(self.namespace,
+ name=dev_name)
+ ip_addresses = self._remove_loopback_interface(ip_addresses)
+ ip_addresses = self._remove_ipv6_scope_link(ip_addresses)
+
+ for cidr in self.cidrs:
+ cidr = (str(cidr.ip + idx) + '/' +
+ str(cidr.netmask.netmask_bits()))
+ self._pop_ip_address(ip_addresses, cidr)
+
+ self.assertEqual(0, len(ip_addresses))
+
+ for idx in range(self.num_devices_with_ip, self.num_devices):
+ dev_name = 'test_device_%s' % idx
+ ip_addresses = ip_lib.get_devices_with_ip(self.namespace,
+ name=dev_name)
+ ip_addresses = self._remove_loopback_interface(ip_addresses)
+ ip_addresses = self._remove_ipv6_scope_link(ip_addresses)
+ self.assertEqual(0, len(ip_addresses))
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py neutron-17.1.1/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py
--- neutron-17.1.0/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/linux/test_ovsdb_monitor.py 2021-03-13 02:26:48.000000000 +0100
@@ -21,6 +21,7 @@
- sudo testing is enabled (see neutron.tests.functional.base for details)
"""
+import time
from oslo_config import cfg
@@ -129,6 +130,9 @@
lambda: self._expected_devices_events(removed_devices, 'removed'))
# restart
self.monitor.stop(block=True)
+ # NOTE(slaweq): lets give async process few more seconds to receive
+ # "error" from the old ovsdb monitor process and then start new one
+ time.sleep(5)
self.monitor.start(block=True, timeout=60)
try:
utils.wait_until_true(
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py neutron-17.1.1/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py
--- neutron-17.1.0/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/ovn/metadata/test_metadata_agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
from unittest import mock
from oslo_config import fixture as fixture_config
@@ -21,6 +22,7 @@
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.tests.functional.schema.ovn_southbound import event as test_event
+from neutron.agent.linux import iptables_manager
from neutron.agent.ovn.metadata import agent
from neutron.agent.ovn.metadata import ovsdb
from neutron.agent.ovn.metadata import server as metadata_server
@@ -28,6 +30,7 @@
from neutron.common import utils as n_utils
from neutron.conf.agent.metadata import config as meta_config
from neutron.conf.agent.ovn.metadata import config as meta_config_ovn
+from neutron.tests.common import net_helpers
from neutron.tests.functional import base
@@ -56,7 +59,12 @@
super(TestMetadataAgent, self).setUp()
self.handler = self.sb_api.idl.notify_handler
# We only have OVN NB and OVN SB running for functional tests
- mock.patch.object(ovsdb, 'MetadataAgentOvsIdl').start()
+ self.mock_ovsdb_idl = mock.Mock()
+ mock_metadata_instance = mock.Mock()
+ mock_metadata_instance.start.return_value = self.mock_ovsdb_idl
+ mock_metadata = mock.patch.object(
+ ovsdb, 'MetadataAgentOvsIdl').start()
+ mock_metadata.return_value = mock_metadata_instance
self._mock_get_ovn_br = mock.patch.object(
agent.MetadataAgent,
'_get_ovn_bridge',
@@ -303,3 +311,19 @@
('external_ids', {'test': 'value'})).execute(check_error=True)
self.assertTrue(event2.wait())
self.assertFalse(event.wait())
+
+ def test__ensure_datapath_checksum_if_dpdk(self):
+ self.mock_ovsdb_idl.db_get.return_value.execute.return_value = (
+ ovn_const.CHASSIS_DATAPATH_NETDEV)
+ regex = re.compile(r'-A POSTROUTING -p tcp -m tcp '
+ r'-j CHECKSUM --checksum-fill')
+ namespace = self.useFixture(net_helpers.NamespaceFixture()).name
+ self.agent._ensure_datapath_checksum(namespace)
+ iptables_mgr = iptables_manager.IptablesManager(
+ use_ipv6=True, nat=False, namespace=namespace, external_lock=False)
+ for rule in iptables_mgr.get_rules_for_table('mangle'):
+ if regex.match(rule):
+ return
+ else:
+ self.fail('Rule not found in "mangle" table, in namespace %s' %
+ namespace)
diff -Nru neutron-17.1.0/neutron/tests/functional/agent/test_ovs_lib.py neutron-17.1.1/neutron/tests/functional/agent/test_ovs_lib.py
--- neutron-17.1.0/neutron/tests/functional/agent/test_ovs_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/agent/test_ovs_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -233,6 +233,7 @@
attrs = {
'remote_ip': '2001:db8:200::1',
'local_ip': '2001:db8:100::1',
+ 'packet_type': 'legacy_l2',
}
self._test_add_tunnel_port(
attrs, expected_tunnel_type=ovs_lib.TYPE_GRE_IP6)
diff -Nru neutron-17.1.0/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py neutron-17.1.1/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py
--- neutron-17.1.0/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py 2021-03-13 02:26:48.000000000 +0100
@@ -19,6 +19,7 @@
from neutron_lib import constants
from oslo_config import cfg
from oslo_utils import uuidutils
+from ovsdbapp.backend.ovs_idl import event
from ovsdbapp.tests.functional import base as ovs_base
from neutron.common.ovn import constants as ovn_const
@@ -715,13 +716,11 @@
ovn_localnetport = self._find_port_row_by_name(
utils.ovn_provnet_port_name(seg_db[0]['id']))
self.assertEqual(ovn_localnetport.tag, [100])
- self.assertEqual(ovn_localnetport.options,
- {'network_name': 'physnet1'})
+ self.assertEqual(ovn_localnetport.options['network_name'], 'physnet1')
seg_2 = self.create_segment(n1['id'], 'physnet2', '222')
ovn_localnetport = self._find_port_row_by_name(
utils.ovn_provnet_port_name(seg_2['id']))
- self.assertEqual(ovn_localnetport.options,
- {'network_name': 'physnet2'})
+ self.assertEqual(ovn_localnetport.options['network_name'], 'physnet2')
self.assertEqual(ovn_localnetport.tag, [222])
# Delete segments and ensure that localnet
@@ -744,23 +743,70 @@
self.assertIsNone(ovn_localnetport)
+class AgentWaitEvent(event.WaitEvent):
+ """Wait for a list of Chassis to be created"""
+
+ ONETIME = False
+
+ def __init__(self, driver, chassis_names):
+ table = driver.agent_chassis_table
+ events = (self.ROW_CREATE,)
+ self.chassis_names = chassis_names
+ super().__init__(events, table, None)
+ self.event_name = 'AgentWaitEvent'
+
+ def match_fn(self, event, row, old):
+ return row.name in self.chassis_names
+
+ def run(self, event, row, old):
+ self.chassis_names.remove(row.name)
+ if not self.chassis_names:
+ self.event.set()
+
+
class TestAgentApi(base.TestOVNFunctionalBase):
+ TEST_AGENT = 'test'
- def setUp(self):
+ def setUp(self, *args):
super().setUp()
- self.host = 'test-host'
- self.controller_agent = self.add_fake_chassis(self.host)
+ self.host = n_utils.get_rand_name(prefix='testhost-')
self.plugin = self.mech_driver._plugin
- agent = {'agent_type': 'test', 'binary': '/bin/test',
- 'host': self.host, 'topic': 'test_topic'}
- _, status = self.plugin.create_or_update_agent(self.context, agent)
- self.test_agent = status['id']
mock.patch.object(self.mech_driver, 'ping_all_chassis',
return_value=False).start()
- def test_agent_show_non_ovn(self):
- self.assertTrue(self.plugin.get_agent(self.context, self.test_agent))
+ metadata_agent_id = uuidutils.generate_uuid()
+ # To be *mostly* sure the agent cache has been updated, we need to
+ # wait for the Chassis events to run. So add a new event that should
+ # run afterthey do and wait for it. I've only had to do this when
+ # adding *a bunch* of Chassis at a time, but better safe than sorry.
+ chassis_name = uuidutils.generate_uuid()
+ agent_event = AgentWaitEvent(self.mech_driver, [chassis_name])
+ self.sb_api.idl.notify_handler.watch_event(agent_event)
+
+ self.chassis = self.add_fake_chassis(
+ self.host, name=chassis_name,
+ external_ids={
+ ovn_const.OVN_AGENT_METADATA_ID_KEY: metadata_agent_id})
+
+ self.assertTrue(agent_event.wait())
+
+ self.agent_types = {
+ self.TEST_AGENT: self._create_test_agent(),
+ ovn_const.OVN_CONTROLLER_AGENT: self.chassis,
+ ovn_const.OVN_METADATA_AGENT: metadata_agent_id,
+ }
+
+ def _create_test_agent(self):
+ agent = {'agent_type': self.TEST_AGENT, 'binary': '/bin/test',
+ 'host': self.host, 'topic': 'test_topic'}
+ _, status = self.plugin.create_or_update_agent(self.context, agent)
+ return status['id']
- def test_agent_show_ovn_controller(self):
- self.assertTrue(self.plugin.get_agent(self.context,
- self.controller_agent))
+ def test_agent_show(self):
+ for agent_id in self.agent_types.values():
+ self.assertTrue(self.plugin.get_agent(self.context, agent_id))
+
+ def test_agent_list(self):
+ agent_ids = [a['id'] for a in self.plugin.get_agents(
+ self.context, filters={'host': self.host})]
+ self.assertCountEqual(list(self.agent_types.values()), agent_ids)
diff -Nru neutron-17.1.0/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py neutron-17.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py
--- neutron-17.1.0/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py 2021-03-13 02:26:48.000000000 +0100
@@ -1025,6 +1025,145 @@
floatingips = router_sync_info[0][constants.FLOATINGIP_KEY]
self.assertTrue(floatingips[0][constants.DVR_SNAT_BOUND])
+ def test_allowed_addr_pairs_delayed_fip_and_update_arp_entry(self):
+ HOST1 = 'host1'
+ helpers.register_l3_agent(
+ host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR)
+ HOST2 = 'host2'
+ helpers.register_l3_agent(
+ host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR)
+ router = self._create_router(ha=False)
+ private_net1 = self._make_network(self.fmt, 'net1', True)
+ test_allocation_pools = [{'start': '10.1.0.2',
+ 'end': '10.1.0.20'}]
+ fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}]
+ kwargs = {'arg_list': (extnet_apidef.EXTERNAL,),
+ extnet_apidef.EXTERNAL: True}
+ ext_net = self._make_network(self.fmt, '', True, **kwargs)
+ self._make_subnet(
+ self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24',
+ ip_version=constants.IP_VERSION_4, enable_dhcp=True)
+ self.l3_plugin.schedule_router(self.context,
+ router['id'],
+ candidates=[self.l3_agent])
+
+ # Set gateway to router
+ self.l3_plugin._update_router_gw_info(
+ self.context, router['id'],
+ {'network_id': ext_net['network']['id']})
+ private_subnet1 = self._make_subnet(
+ self.fmt,
+ private_net1,
+ '10.1.0.1',
+ cidr='10.1.0.0/24',
+ ip_version=constants.IP_VERSION_4,
+ allocation_pools=test_allocation_pools,
+ enable_dhcp=True)
+ vrrp_port = self._make_port(
+ self.fmt,
+ private_net1['network']['id'],
+ fixed_ips=fixed_vrrp_ip)
+ allowed_address_pairs = [
+ {'ip_address': '10.1.0.201',
+ 'mac_address': vrrp_port['port']['mac_address']}]
+ with self.port(
+ subnet=private_subnet1,
+ device_owner=DEVICE_OWNER_COMPUTE) as int_port,\
+ self.port(subnet=private_subnet1,
+ device_owner=DEVICE_OWNER_COMPUTE) as int_port2:
+ self.l3_plugin.add_router_interface(
+ self.context, router['id'],
+ {'subnet_id': private_subnet1['subnet']['id']})
+ router_handle = (
+ self.l3_plugin.list_active_sync_routers_on_active_l3_agent(
+ self.context, self.l3_agent['host'], [router['id']]))
+ self.assertEqual(self.l3_agent['host'],
+ router_handle[0]['gw_port_host'])
+ with mock.patch.object(self.l3_plugin,
+ '_l3_rpc_notifier') as l3_notifier:
+ vm_port = self.core_plugin.update_port(
+ self.context, int_port['port']['id'],
+ {'port': {portbindings.HOST_ID: HOST1}})
+ vm_port_mac = vm_port['mac_address']
+ vm_port_fixed_ips = vm_port['fixed_ips']
+ vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id']
+ vm_arp_table = {
+ 'ip_address': vm_port_fixed_ips[0]['ip_address'],
+ 'mac_address': vm_port_mac,
+ 'subnet_id': vm_port_subnet_id}
+ vm_port2 = self.core_plugin.update_port(
+ self.context, int_port2['port']['id'],
+ {'port': {portbindings.HOST_ID: HOST2}})
+ # Now update the VM port with the allowed_address_pair
+ self.core_plugin.update_port(
+ self.context, vm_port['id'],
+ {'port': {
+ 'allowed_address_pairs': allowed_address_pairs}})
+ self.core_plugin.update_port(
+ self.context, vm_port2['id'],
+ {'port': {
+ 'allowed_address_pairs': allowed_address_pairs}})
+ self.assertEqual(
+ 2, l3_notifier.routers_updated_on_host.call_count)
+ updated_vm_port1 = self.core_plugin.get_port(
+ self.context, vm_port['id'])
+ updated_vm_port2 = self.core_plugin.get_port(
+ self.context, vm_port2['id'])
+ expected_allowed_address_pairs = updated_vm_port1.get(
+ 'allowed_address_pairs')
+ self.assertEqual(expected_allowed_address_pairs,
+ allowed_address_pairs)
+ expected_allowed_address_pairs_2 = updated_vm_port2.get(
+ 'allowed_address_pairs')
+ self.assertEqual(expected_allowed_address_pairs_2,
+ allowed_address_pairs)
+ # Now the VRRP port is attached to the VM port. At this
+ # point, the VRRP port should not have inherited the
+ # port host bindings from the parent VM port.
+ cur_vrrp_port_db = self.core_plugin.get_port(
+ self.context, vrrp_port['port']['id'])
+ self.assertNotEqual(
+ cur_vrrp_port_db[portbindings.HOST_ID], HOST1)
+ self.assertNotEqual(
+ cur_vrrp_port_db[portbindings.HOST_ID], HOST2)
+ # Next we can try to associate the floatingip to the
+ # VRRP port that is already attached to the VM port
+ floating_ip = {'floating_network_id': ext_net['network']['id'],
+ 'router_id': router['id'],
+ 'port_id': vrrp_port['port']['id'],
+ 'tenant_id': vrrp_port['port']['tenant_id']}
+ floating_ip = self.l3_plugin.create_floatingip(
+ self.context, {'floatingip': floating_ip})
+
+ post_update_vrrp_port_db = self.core_plugin.get_port(
+ self.context, vrrp_port['port']['id'])
+ vrrp_port_fixed_ips = post_update_vrrp_port_db['fixed_ips']
+ vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id']
+ vrrp_arp_table1 = {
+ 'ip_address': vrrp_port_fixed_ips[0]['ip_address'],
+ 'mac_address': vm_port_mac,
+ 'subnet_id': vrrp_port_subnet_id}
+
+ expected_calls = [
+ mock.call(self.context,
+ router['id'], vm_arp_table),
+ mock.call(self.context,
+ router['id'], vrrp_arp_table1)]
+ l3_notifier.add_arp_entry.assert_has_calls(
+ expected_calls)
+ expected_routers_updated_calls = [
+ mock.call(self.context, mock.ANY, HOST1),
+ mock.call(self.context, mock.ANY, HOST2),
+ mock.call(self.context, mock.ANY, 'host0')]
+ l3_notifier.routers_updated_on_host.assert_has_calls(
+ expected_routers_updated_calls, any_order=True)
+ self.assertFalse(l3_notifier.routers_updated.called)
+ router_info = (
+ self.l3_plugin.list_active_sync_routers_on_active_l3_agent(
+ self.context, self.l3_agent['host'], [router['id']]))
+ floatingips = router_info[0][constants.FLOATINGIP_KEY]
+ self.assertTrue(floatingips[0][constants.DVR_SNAT_BOUND])
+
def test_dvr_gateway_host_binding_is_set(self):
router = self._create_router(ha=False)
private_net1 = self._make_network(self.fmt, 'net1', True)
@@ -1059,6 +1198,110 @@
self.assertEqual(self.l3_agent['host'],
router_handle[0]['gw_port_host'])
+ def test_allowed_address_pairs_update_arp_entry(self):
+ HOST1 = 'host1'
+ helpers.register_l3_agent(
+ host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR)
+ router = self._create_router(ha=False)
+ private_net1 = self._make_network(self.fmt, 'net1', True)
+ test_allocation_pools = [{'start': '10.1.0.2',
+ 'end': '10.1.0.20'}]
+ fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}]
+ kwargs = {'arg_list': (extnet_apidef.EXTERNAL,),
+ extnet_apidef.EXTERNAL: True}
+ ext_net = self._make_network(self.fmt, '', True, **kwargs)
+ self._make_subnet(
+ self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24',
+ ip_version=constants.IP_VERSION_4, enable_dhcp=True)
+ self.l3_plugin.schedule_router(self.context,
+ router['id'],
+ candidates=[self.l3_agent])
+ # Set gateway to router
+ self.l3_plugin._update_router_gw_info(
+ self.context, router['id'],
+ {'network_id': ext_net['network']['id']})
+ private_subnet1 = self._make_subnet(
+ self.fmt,
+ private_net1,
+ '10.1.0.1',
+ cidr='10.1.0.0/24',
+ ip_version=constants.IP_VERSION_4,
+ allocation_pools=test_allocation_pools,
+ enable_dhcp=True)
+ vrrp_port = self._make_port(
+ self.fmt,
+ private_net1['network']['id'],
+ fixed_ips=fixed_vrrp_ip)
+ allowed_address_pairs = [
+ {'ip_address': '10.1.0.201',
+ 'mac_address': vrrp_port['port']['mac_address']}]
+ with self.port(
+ subnet=private_subnet1,
+ device_owner=DEVICE_OWNER_COMPUTE) as int_port:
+ self.l3_plugin.add_router_interface(
+ self.context, router['id'],
+ {'subnet_id': private_subnet1['subnet']['id']})
+ router_handle = (
+ self.l3_plugin.list_active_sync_routers_on_active_l3_agent(
+ self.context, self.l3_agent['host'], [router['id']]))
+ self.assertEqual(self.l3_agent['host'],
+ router_handle[0]['gw_port_host'])
+ with mock.patch.object(self.l3_plugin,
+ '_l3_rpc_notifier') as l3_notifier:
+ vm_port = self.core_plugin.update_port(
+ self.context, int_port['port']['id'],
+ {'port': {portbindings.HOST_ID: HOST1}})
+ vm_port_mac = vm_port['mac_address']
+ vm_port_fixed_ips = vm_port['fixed_ips']
+ vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id']
+ vm_arp_table = {
+ 'ip_address': vm_port_fixed_ips[0]['ip_address'],
+ 'mac_address': vm_port_mac,
+ 'subnet_id': vm_port_subnet_id}
+ self.assertEqual(1, l3_notifier.add_arp_entry.call_count)
+ floating_ip = {'floating_network_id': ext_net['network']['id'],
+ 'router_id': router['id'],
+ 'port_id': vrrp_port['port']['id'],
+ 'tenant_id': vrrp_port['port']['tenant_id']}
+ floating_ip = self.l3_plugin.create_floatingip(
+ self.context, {'floatingip': floating_ip})
+ vrrp_port_db = self.core_plugin.get_port(
+ self.context, vrrp_port['port']['id'])
+ self.assertNotEqual(vrrp_port_db[portbindings.HOST_ID], HOST1)
+ # Now update the VM port with the allowed_address_pair
+ self.core_plugin.update_port(
+ self.context, vm_port['id'],
+ {'port': {
+ 'allowed_address_pairs': allowed_address_pairs}})
+ updated_vm_port = self.core_plugin.get_port(
+ self.context, vm_port['id'])
+ expected_allowed_address_pairs = updated_vm_port.get(
+ 'allowed_address_pairs')
+ self.assertEqual(expected_allowed_address_pairs,
+ allowed_address_pairs)
+ cur_vrrp_port_db = self.core_plugin.get_port(
+ self.context, vrrp_port['port']['id'])
+ vrrp_port_fixed_ips = cur_vrrp_port_db['fixed_ips']
+ vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id']
+ vrrp_arp_table1 = {
+ 'ip_address': vrrp_port_fixed_ips[0]['ip_address'],
+ 'mac_address': vm_port_mac,
+ 'subnet_id': vrrp_port_subnet_id}
+
+ expected_calls = [
+ mock.call(self.context,
+ router['id'], vm_arp_table),
+ mock.call(self.context,
+ router['id'], vrrp_arp_table1)]
+ l3_notifier.add_arp_entry.assert_has_calls(
+ expected_calls)
+ expected_routers_updated_calls = [
+ mock.call(self.context, mock.ANY, HOST1),
+ mock.call(self.context, mock.ANY, 'host0')]
+ l3_notifier.routers_updated_on_host.assert_has_calls(
+ expected_routers_updated_calls)
+ self.assertFalse(l3_notifier.routers_updated.called)
+
def test_update_vm_port_host_router_update(self):
# register l3 agents in dvr mode in addition to existing dvr_snat agent
HOST1 = 'host1'
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/dhcp/test_agent.py neutron-17.1.1/neutron/tests/unit/agent/dhcp/test_agent.py
--- neutron-17.1.0/neutron/tests/unit/agent/dhcp/test_agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/dhcp/test_agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -15,6 +15,7 @@
import collections
import copy
+import datetime
import sys
from unittest import mock
import uuid
@@ -2376,3 +2377,63 @@
self.assertEqual(2, device.route.get_gateway.call_count)
self.assertFalse(device.route.delete_gateway.called)
device.route.add_gateway.assert_has_calls(expected)
+
+
+class TestDHCPResourceUpdate(base.BaseTestCase):
+
+ date1 = datetime.datetime(year=2021, month=2, day=1, hour=9, minute=1,
+ second=2)
+ date2 = datetime.datetime(year=2021, month=2, day=1, hour=9, minute=1,
+ second=1) # older than date1
+
+ def test__lt__no_port_event(self):
+ # Lower numerical priority always gets precedence. DHCPResourceUpdate
+ # (and ResourceUpdate) objects with more precedence will return as
+ # "lower" in a "__lt__" method comparison.
+ update1 = dhcp_agent.DHCPResourceUpdate('id1', 5, obj_type='network')
+ update2 = dhcp_agent.DHCPResourceUpdate('id2', 6, obj_type='network')
+ self.assertLess(update1, update2)
+
+ def test__lt__no_port_event_timestamp(self):
+ update1 = dhcp_agent.DHCPResourceUpdate(
+ 'id1', 5, timestamp=self.date1, obj_type='network')
+ update2 = dhcp_agent.DHCPResourceUpdate(
+ 'id2', 6, timestamp=self.date2, obj_type='network')
+ self.assertLess(update1, update2)
+
+ def test__lt__port_no_fixed_ips(self):
+ update1 = dhcp_agent.DHCPResourceUpdate(
+ 'id1', 5, timestamp=self.date1, resource={}, obj_type='port')
+ update2 = dhcp_agent.DHCPResourceUpdate(
+ 'id2', 6, timestamp=self.date2, resource={}, obj_type='port')
+ self.assertLess(update1, update2)
+
+ def test__lt__port_fixed_ips_not_matching(self):
+ resource1 = {'fixed_ips': [
+ {'subnet_id': 'subnet1', 'ip_address': '10.0.0.1'}]}
+ resource2 = {'fixed_ips': [
+ {'subnet_id': 'subnet1', 'ip_address': '10.0.0.2'},
+ {'subnet_id': 'subnet2', 'ip_address': '10.0.1.1'}]}
+ update1 = dhcp_agent.DHCPResourceUpdate(
+ 'id1', 5, timestamp=self.date1, resource=resource1,
+ obj_type='port')
+ update2 = dhcp_agent.DHCPResourceUpdate(
+ 'id2', 6, timestamp=self.date2, resource=resource2,
+ obj_type='port')
+ self.assertLess(update1, update2)
+
+ def test__lt__port_fixed_ips_matching(self):
+ resource1 = {'fixed_ips': [
+ {'subnet_id': 'subnet1', 'ip_address': '10.0.0.1'}]}
+ resource2 = {'fixed_ips': [
+ {'subnet_id': 'subnet1', 'ip_address': '10.0.0.1'},
+ {'subnet_id': 'subnet2', 'ip_address': '10.0.0.2'}]}
+ update1 = dhcp_agent.DHCPResourceUpdate(
+ 'id1', 5, timestamp=self.date1, resource=resource1,
+ obj_type='port')
+ update2 = dhcp_agent.DHCPResourceUpdate(
+ 'id2', 6, timestamp=self.date2, resource=resource2,
+ obj_type='port')
+ # In this case, both "port" events have matching IPs. "__lt__" method
+ # uses the timestamp: date2 < date1
+ self.assertLess(update2, update1)
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/l3/test_dvr_local_router.py neutron-17.1.1/neutron/tests/unit/agent/l3/test_dvr_local_router.py
--- neutron-17.1.0/neutron/tests/unit/agent/l3/test_dvr_local_router.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/l3/test_dvr_local_router.py 2021-03-13 02:26:48.000000000 +0100
@@ -861,7 +861,7 @@
fip = {'id': _uuid()}
fip_cidr = '11.22.33.44/24'
- ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri.is_router_primary = mock.Mock(return_value=False)
ri._add_vip = mock.Mock()
interface_name = ri.get_snat_external_device_interface_name(
@@ -872,7 +872,7 @@
router[lib_constants.HA_INTERFACE_KEY]['status'] = 'DOWN'
self._set_ri_kwargs(agent, router['id'], router)
- ri_1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri_1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri_1.is_router_primary = mock.Mock(return_value=True)
ri_1._add_vip = mock.Mock()
interface_name = ri_1.get_snat_external_device_interface_name(
@@ -883,7 +883,7 @@
router[lib_constants.HA_INTERFACE_KEY]['status'] = 'ACTIVE'
self._set_ri_kwargs(agent, router['id'], router)
- ri_2 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri_2 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri_2.is_router_primary = mock.Mock(return_value=True)
ri_2._add_vip = mock.Mock()
interface_name = ri_2.get_snat_external_device_interface_name(
@@ -905,14 +905,14 @@
self._set_ri_kwargs(agent, router['id'], router)
fip_cidr = '11.22.33.44/24'
- ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri.is_router_primary = mock.Mock(return_value=False)
ri._remove_vip = mock.Mock()
ri.remove_centralized_floatingip(fip_cidr)
ri._remove_vip.assert_called_once_with(fip_cidr)
super_remove_centralized_floatingip.assert_not_called()
- ri1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri1 = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri1.is_router_primary = mock.Mock(return_value=True)
ri1._remove_vip = mock.Mock()
ri1.remove_centralized_floatingip(fip_cidr)
@@ -928,10 +928,9 @@
router[lib_constants.HA_INTERFACE_KEY]['status'] = 'ACTIVE'
self.mock_driver.unplug.reset_mock()
self._set_ri_kwargs(agent, router['id'], router)
- ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri._ha_state_path = self.get_temp_file_path('router_ha_state')
ri._create_snat_namespace = mock.Mock()
- ri.update_initial_state = mock.Mock()
ri._plug_external_gateway = mock.Mock()
ri.initialize(mock.Mock())
ri._create_dvr_gateway(mock.Mock(), mock.Mock())
@@ -947,14 +946,13 @@
self.mock_driver.unplug.reset_mock()
self._set_ri_kwargs(agent, router['id'], router)
- ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, [], **self.ri_kwargs)
+ ri = dvr_edge_ha_rtr.DvrEdgeHaRouter(HOSTNAME, **self.ri_kwargs)
ri._ha_state_path = self.get_temp_file_path('router_ha_state')
with open(ri._ha_state_path, "w") as f:
f.write("primary")
ri._create_snat_namespace = mock.Mock()
- ri.update_initial_state = mock.Mock()
ri._plug_external_gateway = mock.Mock()
with mock.patch("neutron.agent.linux.keepalived."
"KeepalivedManager.check_processes",
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/l3/test_ha_router.py neutron-17.1.1/neutron/tests/unit/agent/l3/test_ha_router.py
--- neutron-17.1.0/neutron/tests/unit/agent/l3/test_ha_router.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/l3/test_ha_router.py 2021-03-13 02:26:48.000000000 +0100
@@ -40,8 +40,7 @@
router = mock.MagicMock()
self.agent_conf = mock.Mock()
self.router_id = _uuid()
- return ha_router.HaRouter(mock.sentinel.enqueue_state,
- mock.sentinel.agent,
+ return ha_router.HaRouter(mock.sentinel.agent,
self.router_id,
router,
self.agent_conf,
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py neutron-17.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py
--- neutron-17.1.0/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py 2021-03-13 02:26:48.000000000 +0100
@@ -24,6 +24,7 @@
from neutron.agent.common import ovs_lib
from neutron.agent.common import utils
+from neutron.agent import firewall as agent_firewall
from neutron.agent.linux.openvswitch_firewall import constants as ovsfw_consts
from neutron.agent.linux.openvswitch_firewall import exceptions
from neutron.agent.linux.openvswitch_firewall import firewall as ovsfw
@@ -40,8 +41,14 @@
def create_ofport(port_dict, network_type=None,
physical_network=None, segment_id=TESTING_SEGMENT):
+ allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(
+ port_dict, version=constants.IPv4)
+ allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(
+ port_dict, version=constants.IPv6)
ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1,
- port_name="port-name")
+ port_name="port-name",
+ allowed_pairs_v4=allowed_pairs_v4,
+ allowed_pairs_v6=allowed_pairs_v6)
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG,
segment_id=segment_id,
network_type=network_type,
@@ -440,6 +447,9 @@
ovs_lib, 'OVSBridge', autospec=True).start()
securitygroups_rpc.register_securitygroups_opts()
self.firewall = ovsfw.OVSFirewallDriver(mock_bridge)
+ self.delete_invalid_conntrack_entries_mock = mock.patch.object(
+ self.firewall.ipconntrack,
+ "delete_conntrack_state_by_remote_ips").start()
self.mock_bridge = self.firewall.int_br
self.mock_bridge.reset_mock()
self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00')
@@ -464,6 +474,16 @@
'direction': constants.EGRESS_DIRECTION}]
self.firewall.update_security_group_rules(2, security_group_rules)
+ def _assert_invalid_conntrack_entries_deleted(self, port_dict):
+ port_dict['of_port'] = mock.Mock(vlan_tag=10)
+ self.delete_invalid_conntrack_entries_mock.assert_has_calls([
+ mock.call(
+ [port_dict], constants.IPv4, set(),
+ mark=ovsfw_consts.CT_MARK_INVALID),
+ mock.call(
+ [port_dict], constants.IPv6, set(),
+ mark=ovsfw_consts.CT_MARK_INVALID)])
+
@property
def port_ofport(self):
return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport
@@ -621,6 +641,7 @@
calls = self.mock_bridge.br.add_flow.call_args_list
for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
self.assertIn(call, calls)
+ self._assert_invalid_conntrack_entries_deleted(port_dict)
def test_prepare_port_filter_port_security_disabled(self):
port_dict = {'device': 'port-id',
@@ -631,6 +652,7 @@
self.firewall, 'initialize_port_flows') as m_init_flows:
self.firewall.prepare_port_filter(port_dict)
self.assertFalse(m_init_flows.called)
+ self.delete_invalid_conntrack_entries_mock.assert_not_called()
def _test_initialize_port_flows_dvr_conntrack_direct(self, network_type):
port_dict = {
@@ -802,6 +824,7 @@
self.assertFalse(self.mock_bridge.br.delete_flows.called)
self.firewall.prepare_port_filter(port_dict)
self.assertTrue(self.mock_bridge.br.delete_flows.called)
+ self._assert_invalid_conntrack_entries_deleted(port_dict)
def test_update_port_filter(self):
port_dict = {'device': 'port-id',
@@ -833,6 +856,7 @@
table=ovs_consts.RULES_EGRESS_TABLE)]
self.mock_bridge.br.add_flow.assert_has_calls(
filter_rules, any_order=True)
+ self._assert_invalid_conntrack_entries_deleted(port_dict)
def test_update_port_filter_create_new_port_if_not_present(self):
port_dict = {'device': 'port-id',
@@ -852,15 +876,18 @@
self.assertFalse(self.mock_bridge.br.delete_flows.called)
self.assertTrue(initialize_port_flows_mock.called)
self.assertTrue(add_flows_from_rules_mock.called)
+ self._assert_invalid_conntrack_entries_deleted(port_dict)
def test_update_port_filter_port_security_disabled(self):
port_dict = {'device': 'port-id',
'security_groups': [1]}
self._prepare_security_group()
self.firewall.prepare_port_filter(port_dict)
+ self.delete_invalid_conntrack_entries_mock.reset_mock()
port_dict['port_security_enabled'] = False
self.firewall.update_port_filter(port_dict)
self.assertTrue(self.mock_bridge.br.delete_flows.called)
+ self.delete_invalid_conntrack_entries_mock.assert_not_called()
def test_update_port_filter_applies_added_flows(self):
"""Check flows are applied right after _set_flows is called."""
@@ -881,6 +908,7 @@
self.mock_bridge.br.get_vif_port_by_id.return_value = None
self.firewall.update_port_filter(port_dict)
self.assertTrue(self.mock_bridge.br.delete_flows.called)
+ self._assert_invalid_conntrack_entries_deleted(port_dict)
def test_remove_port_filter(self):
port_dict = {'device': 'port-id',
@@ -981,6 +1009,38 @@
with testtools.ExpectedException(exceptions.OVSFWPortNotHandled):
self.firewall._remove_egress_no_port_security('foo')
+ def test__initialize_egress_ipv6_icmp(self):
+ port_dict = {
+ 'device': 'port-id',
+ 'security_groups': [1],
+ 'fixed_ips': ["10.0.0.1"],
+ 'allowed_address_pairs': [
+ {'mac_address': 'aa:bb:cc:dd:ee:ff',
+ 'ip_address': '192.168.1.1'},
+ {'mac_address': 'aa:bb:cc:dd:ee:ff',
+ 'ip_address': '2003::1'}
+ ]}
+ of_port = create_ofport(port_dict)
+ self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG}
+ self.firewall._initialize_egress_ipv6_icmp(
+ of_port, set([('aa:bb:cc:dd:ee:ff', '2003::1')]))
+ expected_calls = []
+ for icmp_type in agent_firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
+ expected_calls.append(
+ mock.call(
+ table=ovs_consts.BASE_EGRESS_TABLE,
+ priority=95,
+ in_port=TESTING_VLAN_TAG,
+ reg5=TESTING_VLAN_TAG,
+ dl_type='0x86dd',
+ nw_proto=constants.PROTO_NUM_IPV6_ICMP,
+ icmp_type=icmp_type,
+ dl_src='aa:bb:cc:dd:ee:ff',
+ ipv6_src='2003::1',
+ actions='resubmit(,%d)' % (
+ ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
+ self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
+
def test_process_trusted_ports_caches_port_id(self):
vif_port = ovs_lib.VifPort('name', 1, 'id', 'mac', mock.ANY)
with mock.patch.object(self.firewall.int_br.br, 'get_vifs_by_ids',
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/linux/test_dhcp.py neutron-17.1.1/neutron/tests/unit/agent/linux/test_dhcp.py
--- neutron-17.1.0/neutron/tests/unit/agent/linux/test_dhcp.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/linux/test_dhcp.py 2021-03-13 02:26:48.000000000 +0100
@@ -308,6 +308,19 @@
for ip in self.fixed_ips]
+class FakeRouterHAPort(object):
+ def __init__(self):
+ self.id = 'hahahaha-haha-haha-haha-hahahahahaha'
+ self.admin_state_up = True
+ self.device_owner = constants.DEVICE_OWNER_ROUTER_HA_INTF
+ self.mac_address = '00:00:0f:aa:aa:aa'
+ self.device_id = 'fake_router_ha_port'
+ self.dns_assignment = []
+ self.extra_dhcp_opts = []
+ self.fixed_ips = [FakeIPAllocation(
+ '169.254.169.20', 'dddddddd-dddd-dddd-dddd-dddddddddddd')]
+
+
class FakeRouterPortNoDHCP(object):
def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF,
ip_address='192.168.0.1', domain='openstacklocal'):
@@ -694,6 +707,7 @@
self.namespace = 'qdhcp-ns'
self.ports = [FakePort1(domain=domain), FakeV6Port(domain=domain),
FakeDualPort(domain=domain),
+ FakeRouterHAPort(),
FakeRouterPort(domain=domain)]
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/linux/test_ip_conntrack.py neutron-17.1.1/neutron/tests/unit/agent/linux/test_ip_conntrack.py
--- neutron-17.1.0/neutron/tests/unit/agent/linux/test_ip_conntrack.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/linux/test_ip_conntrack.py 2021-03-13 02:26:48.000000000 +0100
@@ -39,3 +39,26 @@
dev_info_list = [dev_info for _ in range(10)]
self.mgr._delete_conntrack_state(dev_info_list, rule)
self.assertEqual(1, len(self.execute.mock_calls))
+
+
+class OvsIPConntrackTestCase(IPConntrackTestCase):
+
+ def setUp(self):
+ super(IPConntrackTestCase, self).setUp()
+ self.execute = mock.Mock()
+ self.mgr = ip_conntrack.OvsIpConntrackManager(self.execute)
+
+ def test_delete_conntrack_state_dedupes(self):
+ rule = {'ethertype': 'IPv4', 'direction': 'ingress'}
+ dev_info = {
+ 'device': 'tapdevice',
+ 'fixed_ips': ['1.2.3.4'],
+ 'of_port': mock.Mock(of_port=10)}
+ dev_info_list = [dev_info for _ in range(10)]
+ self.mgr._delete_conntrack_state(dev_info_list, rule)
+ self.assertEqual(1, len(self.execute.mock_calls))
+
+ def test_get_device_zone(self):
+ of_port = mock.Mock(vlan_tag=10)
+ port = {'id': 'port-id', 'of_port': of_port}
+ self.assertEqual(10, self.mgr.get_device_zone(port))
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/linux/test_ip_lib.py neutron-17.1.1/neutron/tests/unit/agent/linux/test_ip_lib.py
--- neutron-17.1.0/neutron/tests/unit/agent/linux/test_ip_lib.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/linux/test_ip_lib.py 2021-03-13 02:26:48.000000000 +0100
@@ -24,7 +24,6 @@
from oslo_utils import netutils
from oslo_utils import uuidutils
import pyroute2
-from pyroute2.netlink.rtnl import ifaddrmsg
from pyroute2.netlink.rtnl import ifinfmsg
from pyroute2.netlink.rtnl import ndmsg
from pyroute2 import NetlinkError
@@ -1603,39 +1602,6 @@
self.assertEqual(reference, retval)
-class ParseLinkDeviceTestCase(base.BaseTestCase):
-
- def setUp(self):
- super(ParseLinkDeviceTestCase, self).setUp()
- self._mock_get_ip_addresses = mock.patch.object(priv_lib,
- 'get_ip_addresses')
- self.mock_get_ip_addresses = self._mock_get_ip_addresses.start()
- self.addCleanup(self._stop_mock)
-
- def _stop_mock(self):
- self._mock_get_ip_addresses.stop()
-
- def test_parse_link_devices(self):
- device = ({'index': 1, 'attrs': [['IFLA_IFNAME', 'int_name']]})
- self.mock_get_ip_addresses.return_value = [
- {'prefixlen': 24, 'scope': 200, 'event': 'RTM_NEWADDR', 'attrs': [
- ['IFA_ADDRESS', '192.168.10.20'],
- ['IFA_FLAGS', ifaddrmsg.IFA_F_PERMANENT]]},
- {'prefixlen': 64, 'scope': 200, 'event': 'RTM_DELADDR', 'attrs': [
- ['IFA_ADDRESS', '2001:db8::1'],
- ['IFA_FLAGS', ifaddrmsg.IFA_F_PERMANENT]]}]
-
- retval = ip_lib._parse_link_device('namespace', device)
- expected = [{'scope': 'site', 'cidr': '192.168.10.20/24',
- 'dynamic': False, 'dadfailed': False, 'name': 'int_name',
- 'broadcast': None, 'tentative': False, 'event': 'added'},
- {'scope': 'site', 'cidr': '2001:db8::1/64',
- 'dynamic': False, 'dadfailed': False, 'name': 'int_name',
- 'broadcast': None, 'tentative': False,
- 'event': 'removed'}]
- self.assertEqual(expected, retval)
-
-
class GetDevicesInfoTestCase(base.BaseTestCase):
DEVICE_LO = {
diff -Nru neutron-17.1.0/neutron/tests/unit/agent/ovn/metadata/test_agent.py neutron-17.1.1/neutron/tests/unit/agent/ovn/metadata/test_agent.py
--- neutron-17.1.0/neutron/tests/unit/agent/ovn/metadata/test_agent.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/agent/ovn/metadata/test_agent.py 2021-03-13 02:26:48.000000000 +0100
@@ -235,7 +235,9 @@
'update_chassis_metadata_networks') as update_chassis,\
mock.patch.object(
driver.MetadataDriver,
- 'spawn_monitored_metadata_proxy') as spawn_mdp:
+ 'spawn_monitored_metadata_proxy') as spawn_mdp, \
+ mock.patch.object(
+ self.agent, '_ensure_datapath_checksum') as mock_checksum:
# Simulate that the VETH pair was already present in 'br-fake'.
# We need to assert that it was deleted first.
@@ -268,6 +270,7 @@
bind_address=n_const.METADATA_V4_IP, network_id='1')
# Check that the chassis has been updated with the datapath.
update_chassis.assert_called_once_with('1')
+ mock_checksum.assert_called_once_with('namespace')
def _test_update_chassis_metadata_networks_helper(
self, dp, remove, expected_dps, txn_called=True):
diff -Nru neutron-17.1.0/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py neutron-17.1.1/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py
--- neutron-17.1.0/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/api/rpc/agentnotifiers/test_dhcp_rpc_agent_api.py 2021-03-13 02:26:48.000000000 +0100
@@ -247,7 +247,9 @@
self._test__notify_agents_with_function(
lambda: self.notifier._after_router_interface_deleted(
mock.ANY, mock.ANY, mock.ANY, context=mock.Mock(),
- port={'id': 'foo_port_id', 'network_id': 'foo_network_id'}),
+ port={'id': 'foo_port_id', 'network_id': 'foo_network_id',
+ 'fixed_ips': {'subnet_id': 'subnet1',
+ 'ip_address': '10.0.0.1'}}),
expected_scheduling=0, expected_casts=1)
def test__fanout_message(self):
diff -Nru neutron-17.1.0/neutron/tests/unit/db/test_db_base_plugin_v2.py neutron-17.1.1/neutron/tests/unit/db/test_db_base_plugin_v2.py
--- neutron-17.1.0/neutron/tests/unit/db/test_db_base_plugin_v2.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/db/test_db_base_plugin_v2.py 2021-03-13 02:26:48.000000000 +0100
@@ -1611,17 +1611,25 @@
res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
- def test_delete_network_port_exists_owned_by_network(self):
+ def _test_delete_network_port_exists_owned_by_network(self, device_owner):
res = self._create_network(fmt=self.fmt, name='net',
admin_state_up=True)
network = self.deserialize(self.fmt, res)
network_id = network['network']['id']
self._create_port(self.fmt, network_id,
- device_owner=constants.DEVICE_OWNER_DHCP)
+ device_owner=device_owner)
req = self.new_delete_request('networks', network_id)
res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int)
+ def test_test_delete_network_port_exists_dhcp(self):
+ self._test_delete_network_port_exists_owned_by_network(
+ constants.DEVICE_OWNER_DHCP)
+
+ def test_test_delete_network_port_exists_fip_gw(self):
+ self._test_delete_network_port_exists_owned_by_network(
+ constants.DEVICE_OWNER_AGENT_GW)
+
def test_delete_network_port_exists_owned_by_network_race(self):
res = self._create_network(fmt=self.fmt, name='net',
admin_state_up=True)
diff -Nru neutron-17.1.0/neutron/tests/unit/db/test_l3_dvr_db.py neutron-17.1.1/neutron/tests/unit/db/test_l3_dvr_db.py
--- neutron-17.1.0/neutron/tests/unit/db/test_l3_dvr_db.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/db/test_l3_dvr_db.py 2021-03-13 02:26:48.000000000 +0100
@@ -1173,6 +1173,72 @@
port=mock.ANY,
interface_info=interface_info)
+ def test__generate_arp_table_and_notify_agent(self):
+ fixed_ip = {
+ 'ip_address': '1.2.3.4',
+ 'subnet_id': _uuid()}
+ mac_address = "00:11:22:33:44:55"
+ expected_arp_table = {
+ 'ip_address': fixed_ip['ip_address'],
+ 'subnet_id': fixed_ip['subnet_id'],
+ 'mac_address': mac_address}
+ notifier = mock.Mock()
+ ports = [{'id': _uuid(), 'device_id': 'router_1'},
+ {'id': _uuid(), 'device_id': 'router_2'}]
+ with mock.patch.object(self.core_plugin, "get_ports",
+ return_value=ports):
+ self.mixin._generate_arp_table_and_notify_agent(
+ self.ctx, fixed_ip, mac_address, notifier)
+ notifier.assert_has_calls([
+ mock.call(self.ctx, "router_1", expected_arp_table),
+ mock.call(self.ctx, "router_2", expected_arp_table)])
+
+ def _test_update_arp_entry_for_dvr_service_port(
+ self, device_owner, action):
+ router_dict = {'name': 'test_router', 'admin_state_up': True,
+ 'distributed': True}
+ router = self._create_router(router_dict)
+ plugin = mock.Mock()
+ directory.add_plugin(plugin_constants.CORE, plugin)
+ l3_notify = self.mixin.l3_rpc_notifier = mock.Mock()
+ port = {
+ 'id': 'my_port_id',
+ 'fixed_ips': [
+ {'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323',
+ 'ip_address': '10.0.0.11'},
+ {'subnet_id': '2b7c8a07-6f8e-4937-8701-f1d5da1a807c',
+ 'ip_address': '10.0.0.21'},
+ {'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b',
+ 'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111'}],
+ 'mac_address': 'my_mac',
+ 'device_owner': device_owner
+ }
+ dvr_port = {
+ 'id': 'dvr_port_id',
+ 'fixed_ips': mock.ANY,
+ 'device_owner': const.DEVICE_OWNER_DVR_INTERFACE,
+ 'device_id': router['id']
+ }
+ plugin.get_ports.return_value = [dvr_port]
+ if action == 'add':
+ self.mixin.update_arp_entry_for_dvr_service_port(
+ self.ctx, port)
+ self.assertEqual(3, l3_notify.add_arp_entry.call_count)
+ elif action == 'del':
+ self.mixin.delete_arp_entry_for_dvr_service_port(
+ self.ctx, port)
+ self.assertEqual(3, l3_notify.del_arp_entry.call_count)
+
+ def test_update_arp_entry_for_dvr_service_port_added(self):
+ action = 'add'
+ device_owner = const.DEVICE_OWNER_LOADBALANCER
+ self._test_update_arp_entry_for_dvr_service_port(device_owner, action)
+
+ def test_update_arp_entry_for_dvr_service_port_deleted(self):
+ action = 'del'
+ device_owner = const.DEVICE_OWNER_LOADBALANCER
+ self._test_update_arp_entry_for_dvr_service_port(device_owner, action)
+
def test_add_router_interface_csnat_ports_failure(self):
router_dict = {'name': 'test_router', 'admin_state_up': True,
'distributed': True}
diff -Nru neutron-17.1.0/neutron/tests/unit/db/test_securitygroups_db.py neutron-17.1.1/neutron/tests/unit/db/test_securitygroups_db.py
--- neutron-17.1.0/neutron/tests/unit/db/test_securitygroups_db.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/db/test_securitygroups_db.py 2021-03-13 02:26:48.000000000 +0100
@@ -77,6 +77,10 @@
self.mock_quota_make_res = make_res.start()
commit_res = mock.patch.object(quota.QuotaEngine, 'commit_reservation')
self.mock_quota_commit_res = commit_res.start()
+ is_ext_supported = mock.patch(
+ 'neutron_lib.api.extensions.is_extension_supported')
+ self.is_ext_supported = is_ext_supported.start()
+ self.is_ext_supported.return_value = True
def test_create_security_group_conflict(self):
with mock.patch.object(registry, "publish") as mock_publish:
@@ -601,3 +605,13 @@
get_default_sg_id.assert_has_calls([
mock.call(self.ctx, 'tenant_1'),
mock.call(self.ctx, 'tenant_1')])
+
+ def test__ensure_default_security_group_when_disabled(self):
+ with mock.patch.object(
+ self.mixin, '_get_default_sg_id') as get_default_sg_id,\
+ mock.patch.object(
+ self.mixin, 'create_security_group') as create_sg:
+ self.is_ext_supported.return_value = False
+ self.mixin._ensure_default_security_group(self.ctx, 'tenant_1')
+ create_sg.assert_not_called()
+ get_default_sg_id.assert_not_called()
diff -Nru neutron-17.1.0/neutron/tests/unit/extensions/test_floating_ip_port_forwarding.py neutron-17.1.1/neutron/tests/unit/extensions/test_floating_ip_port_forwarding.py
--- neutron-17.1.0/neutron/tests/unit/extensions/test_floating_ip_port_forwarding.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/extensions/test_floating_ip_port_forwarding.py 2021-03-13 02:26:48.000000000 +0100
@@ -71,6 +71,21 @@
return fip_pf_req.get_response(self.ext_api)
+ def _update_fip_port_forwarding(self, fmt, floating_ip_id,
+ port_forwarding_id, **kwargs):
+ port_forwarding = {}
+ for k, v in kwargs.items():
+ port_forwarding[k] = v
+ data = {'port_forwarding': port_forwarding}
+
+ fip_pf_req = self._req(
+ 'PUT', 'floatingips', data,
+ fmt or self.fmt, id=floating_ip_id,
+ sub_id=port_forwarding_id,
+ subresource='port_forwardings')
+
+ return fip_pf_req.get_response(self.ext_api)
+
def test_create_floatingip_port_forwarding_with_port_number_0(self):
with self.network() as ext_net:
network_id = ext_net['network']['id']
@@ -136,3 +151,46 @@
pf_body = self.deserialize(self.fmt, res)
self.assertEqual(
"blablablabla", pf_body['port_forwarding']['description'])
+
+ def test_update_floatingip_port_forwarding_with_dup_internal_port(self):
+ with self.network() as ext_net:
+ network_id = ext_net['network']['id']
+ self._set_net_external(network_id)
+ with self.subnet(ext_net, cidr='10.10.10.0/24'), \
+ self.router() as router, \
+ self.subnet(cidr='11.0.0.0/24') as private_subnet, \
+ self.port(private_subnet) as port:
+ self._add_external_gateway_to_router(
+ router['router']['id'],
+ network_id)
+ self._router_interface_action(
+ 'add', router['router']['id'],
+ private_subnet['subnet']['id'],
+ None)
+ fip1 = self._make_floatingip(
+ self.fmt,
+ network_id)
+ self.assertIsNone(fip1['floatingip'].get('port_id'))
+ self._create_fip_port_forwarding(
+ self.fmt, fip1['floatingip']['id'],
+ 2222, 22,
+ 'tcp',
+ port['port']['fixed_ips'][0]['ip_address'],
+ port['port']['id'],
+ description="blablablabla")
+ fip2 = self._make_floatingip(
+ self.fmt,
+ network_id)
+ fip_pf_response = self._create_fip_port_forwarding(
+ self.fmt, fip2['floatingip']['id'],
+ 2222, 23,
+ 'tcp',
+ port['port']['fixed_ips'][0]['ip_address'],
+ port['port']['id'],
+ description="blablablabla")
+ update_res = self._update_fip_port_forwarding(
+ self.fmt, fip2['floatingip']['id'],
+ fip_pf_response.json['port_forwarding']['id'],
+ **{'internal_port': 22})
+ self.assertEqual(exc.HTTPBadRequest.code,
+ update_res.status_int)
diff -Nru neutron-17.1.0/neutron/tests/unit/extensions/test_l3_conntrack_helper.py neutron-17.1.1/neutron/tests/unit/extensions/test_l3_conntrack_helper.py
--- neutron-17.1.0/neutron/tests/unit/extensions/test_l3_conntrack_helper.py 1970-01-01 01:00:00.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/extensions/test_l3_conntrack_helper.py 2021-03-13 02:26:48.000000000 +0100
@@ -0,0 +1,141 @@
+# Copyright 2021 Troila
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from unittest import mock
+
+from webob import exc
+
+from neutron_lib.api.definitions import l3 as l3_apidef
+from neutron_lib.api.definitions import l3_conntrack_helper as l3_ct
+from neutron_lib import context
+from oslo_utils import uuidutils
+
+from neutron.extensions import l3
+from neutron.extensions import l3_conntrack_helper
+from neutron.tests.unit.api import test_extensions
+from neutron.tests.unit.extensions import test_l3
+
+_uuid = uuidutils.generate_uuid
+
+
+class TestL3ConntrackHelperServicePlugin(test_l3.TestL3NatServicePlugin):
+ supported_extension_aliases = [l3_apidef.ALIAS, l3_ct.ALIAS]
+
+
+class ExtendL3ConntrackHelperExtensionManager(object):
+
+ def get_resources(self):
+ return (l3.L3.get_resources() +
+ l3_conntrack_helper.L3_conntrack_helper.get_resources())
+
+ def get_actions(self):
+ return []
+
+ def get_request_extensions(self):
+ return []
+
+
+class L3NConntrackHelperTestCase(test_l3.L3BaseForIntTests,
+ test_l3.L3NatTestCaseMixin):
+ tenant_id = _uuid()
+ fmt = "json"
+
+ def setUp(self):
+ mock.patch('neutron.api.rpc.handlers.resources_rpc.'
+ 'ResourcesPushRpcApi').start()
+ svc_plugins = ('neutron.services.conntrack_helper.plugin.Plugin',
+ 'neutron.tests.unit.extensions.'
+ 'test_l3_conntrack_helper.'
+ 'TestL3ConntrackHelperServicePlugin')
+ plugin = ('neutron.tests.unit.extensions.test_l3.TestL3NatIntPlugin')
+ ext_mgr = ExtendL3ConntrackHelperExtensionManager()
+ super(L3NConntrackHelperTestCase, self).setUp(
+ ext_mgr=ext_mgr, service_plugins=svc_plugins, plugin=plugin)
+ self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
+
+ def _create_router_conntrack_helper(self, fmt, router_id,
+ protocol, port, helper):
+ tenant_id = self.tenant_id or _uuid()
+ data = {'conntrack_helper': {
+ "protocol": protocol,
+ "port": port,
+ "helper": helper}
+ }
+ router_ct_req = self._req(
+ 'POST', 'routers', data,
+ fmt or self.fmt, id=router_id,
+ subresource='conntrack_helpers')
+
+ router_ct_req.environ['neutron.context'] = context.Context(
+ '', tenant_id, is_admin=True)
+
+ return router_ct_req.get_response(self.ext_api)
+
+ def _update_router_conntrack_helper(self, fmt, router_id,
+ conntrack_helper_id, **kwargs):
+ conntrack_helper = {}
+ for k, v in kwargs.items():
+ conntrack_helper[k] = v
+ data = {'conntrack_helper': conntrack_helper}
+
+ router_ct_req = self._req(
+ 'PUT', 'routers', data,
+ fmt or self.fmt, id=router_id,
+ sub_id=conntrack_helper_id,
+ subresource='conntrack_helpers')
+ return router_ct_req.get_response(self.ext_api)
+
+ def test_create_ct_with_duplicate_entry(self):
+ with self.router() as router:
+ ct1 = self._create_router_conntrack_helper(
+ self.fmt, router['router']['id'],
+ "udp", 69, "tftp")
+ self.assertEqual(exc.HTTPCreated.code, ct1.status_code)
+ ct2 = self._create_router_conntrack_helper(
+ self.fmt, router['router']['id'],
+ "udp", 69, "tftp")
+ self.assertEqual(exc.HTTPBadRequest.code, ct2.status_code)
+ expect_msg = ("Bad conntrack_helper request: A duplicate "
+ "conntrack helper entry with same attributes "
+ "already exists, conflicting values are "
+ "{'router_id': '%s', 'protocol': 'udp', "
+ "'port': 69, 'helper': "
+ "'tftp'}.") % router['router']['id']
+ self.assertEqual(
+ expect_msg, ct2.json_body['NeutronError']['message'])
+
+ def test_update_ct_with_duplicate_entry(self):
+ with self.router() as router:
+ ct1 = self._create_router_conntrack_helper(
+ self.fmt, router['router']['id'],
+ "udp", 69, "tftp")
+ self.assertEqual(exc.HTTPCreated.code, ct1.status_code)
+ ct2 = self._create_router_conntrack_helper(
+ self.fmt, router['router']['id'],
+ "udp", 68, "tftp")
+ self.assertEqual(exc.HTTPCreated.code, ct2.status_code)
+ result = self._update_router_conntrack_helper(
+ self.fmt, router['router']['id'],
+ ct1.json['conntrack_helper']['id'],
+ **{'port': 68})
+ self.assertEqual(exc.HTTPBadRequest.code, result.status_code)
+ expect_msg = ("Bad conntrack_helper request: A duplicate "
+ "conntrack helper entry with same attributes "
+ "already exists, conflicting values are "
+ "{'router_id': '%s', 'protocol': 'udp', "
+ "'port': 68, 'helper': "
+ "'tftp'}.") % router['router']['id']
+ self.assertEqual(
+ expect_msg, result.json_body['NeutronError']['message'])
diff -Nru neutron-17.1.0/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py neutron-17.1.1/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py
--- neutron-17.1.0/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py 2021-03-13 02:26:48.000000000 +0100
@@ -395,3 +395,40 @@
priority=constants.HA_CHASSIS_GROUP_HIGHEST_PRIORITY - 1)
]
nb_idl.ha_chassis_group_add_chassis.assert_has_calls(expected_calls)
+
+ def test_check_for_mcast_flood_reports(self):
+ nb_idl = self.fake_ovn_client._nb_idl
+ lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp0',
+ 'options': {'mcast_flood_reports': 'true'},
+ 'type': ""})
+ lsp1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp1', 'options': {}, 'type': ""})
+ lsp2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp2', 'options': {},
+ 'type': "vtep"})
+ lsp3 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp3', 'options': {},
+ 'type': "localport"})
+ lsp4 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp4', 'options': {},
+ 'type': "router"})
+ lsp5 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
+ attrs={'name': 'lsp5', 'options': {}, 'type': 'localnet'})
+
+ nb_idl.lsp_list.return_value.execute.return_value = [
+ lsp0, lsp1, lsp2, lsp3, lsp4, lsp5]
+
+ # Invoke the periodic method, it meant to run only once at startup
+ # so NeverAgain will be raised at the end
+ self.assertRaises(periodics.NeverAgain,
+ self.periodic.check_for_mcast_flood_reports)
+
+ # Assert only lsp1 and lsp5 were called because they are the only
+ # ones meeting the criteria ("mcast_flood_reports" not yet set,
+ # and type "" or localnet)
+ expected_calls = [
+ mock.call('lsp1', mcast_flood_reports='true'),
+ mock.call('lsp5', mcast_flood_reports='true', mcast_flood='true')]
+
+ nb_idl.lsp_set_options.assert_has_calls(expected_calls)
diff -Nru neutron-17.1.0/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py neutron-17.1.1/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py
--- neutron-17.1.0/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py 2021-03-13 02:26:48.000000000 +0100
@@ -715,7 +715,9 @@
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(segments[0]['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
- options={'network_name': 'physnet1'},
+ options={'network_name': 'physnet1',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=2,
type='localnet')
@@ -1504,13 +1506,13 @@
self.mech_driver.update_subnet_postcommit(context)
esd.assert_called_once_with(
context.current, context.network.current, mock.ANY)
- umd.assert_called_once_with(mock.ANY, 'id')
+ umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
def test_update_subnet_postcommit_disable_dhcp(self):
self.mech_driver._nb_ovn.get_subnet_dhcp_options.return_value = {
'subnet': mock.sentinel.subnet, 'ports': []}
context = fakes.FakeSubnetContext(
- subnet={'enable_dhcp': False, 'id': 'fake_id', 'ip_version': 4,
+ subnet={'enable_dhcp': False, 'id': 'subnet_id', 'ip_version': 4,
'network_id': 'id'},
network={'id': 'id'})
with mock.patch.object(
@@ -1521,7 +1523,7 @@
'update_metadata_port') as umd:
self.mech_driver.update_subnet_postcommit(context)
dsd.assert_called_once_with(context.current['id'], mock.ANY)
- umd.assert_called_once_with(mock.ANY, 'id')
+ umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
def test_update_subnet_postcommit_update_dhcp(self):
self.mech_driver._nb_ovn.get_subnet_dhcp_options.return_value = {
@@ -1539,7 +1541,63 @@
self.mech_driver.update_subnet_postcommit(context)
usd.assert_called_once_with(
context.current, context.network.current, mock.ANY)
- umd.assert_called_once_with(mock.ANY, 'id')
+ umd.assert_called_once_with(mock.ANY, 'id', subnet_id='subnet_id')
+
+ def test_update_metadata_port_with_subnet_present_in_port(self):
+ ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
+ group='ovn')
+ fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
+ with mock.patch.object(
+ self.mech_driver._ovn_client, '_find_metadata_port',
+ return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
+ mock.patch.object(self.mech_driver._plugin, 'get_subnets',
+ return_value=[{'id': 'subnet1'},
+ {'id': 'subnet2'}]), \
+ mock.patch.object(self.mech_driver._plugin, 'update_port') as \
+ mock_update_port:
+ self.mech_driver._ovn_client.update_metadata_port(
+ self.context, 'net_id', subnet_id='subnet1')
+ mock_update_port.assert_not_called()
+
+ def test_update_metadata_port_with_subnet_not_present_in_port(self):
+ ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
+ group='ovn')
+ fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
+ with mock.patch.object(
+ self.mech_driver._ovn_client, '_find_metadata_port',
+ return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
+ mock.patch.object(self.mech_driver._plugin, 'get_subnets',
+ return_value=[{'id': 'subnet1'},
+ {'id': 'subnet2'}]), \
+ mock.patch.object(self.mech_driver._plugin, 'update_port') as \
+ mock_update_port:
+ self.mech_driver._ovn_client.update_metadata_port(
+ self.context, 'net_id', subnet_id='subnet3')
+ fixed_ips.append({'subnet_id': 'subnet3'})
+ port = {'id': 'metadata_id', 'port': {
+ 'network_id': 'net_id', 'fixed_ips': fixed_ips}}
+ mock_update_port.assert_called_once_with(
+ mock.ANY, 'metadata_id', port)
+
+ def test_update_metadata_port_no_subnet(self):
+ ovn_conf.cfg.CONF.set_override('ovn_metadata_enabled', True,
+ group='ovn')
+ fixed_ips = [{'subnet_id': 'subnet1', 'ip_address': 'ip_add1'}]
+ with mock.patch.object(
+ self.mech_driver._ovn_client, '_find_metadata_port',
+ return_value={'fixed_ips': fixed_ips, 'id': 'metadata_id'}), \
+ mock.patch.object(self.mech_driver._plugin, 'get_subnets',
+ return_value=[{'id': 'subnet1'},
+ {'id': 'subnet2'}]), \
+ mock.patch.object(self.mech_driver._plugin, 'update_port') as \
+ mock_update_port:
+ self.mech_driver._ovn_client.update_metadata_port(self.context,
+ 'net_id')
+ fixed_ips.append({'subnet_id': 'subnet2'})
+ port = {'id': 'metadata_id', 'port': {
+ 'network_id': 'net_id', 'fixed_ips': fixed_ips}}
+ mock_update_port.assert_called_once_with(
+ mock.ANY, 'metadata_id', port)
@mock.patch.object(provisioning_blocks, 'is_object_blocked')
@mock.patch.object(provisioning_blocks, 'provisioning_complete')
@@ -2076,7 +2134,9 @@
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
- options={'network_name': 'phys_net1'},
+ options={'network_name': 'phys_net1',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=200,
type='localnet')
ovn_nb_api.create_lswitch_port.reset_mock()
@@ -2088,7 +2148,9 @@
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
- options={'network_name': 'phys_net2'},
+ options={'network_name': 'phys_net2',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
+ ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=300,
type='localnet')
segments = segments_db.get_network_segments(
diff -Nru neutron-17.1.0/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py neutron-17.1.1/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py
--- neutron-17.1.0/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py 2021-03-13 02:26:48.000000000 +0100
@@ -841,12 +841,20 @@
'device_owner': DEVICE_OWNER_COMPUTE,
},
}
+ port = kwargs.get('original_port')
l3plugin = mock.Mock()
directory.add_plugin(plugin_constants.L3, l3plugin)
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', mock.ANY, **kwargs)
l3plugin._get_allowed_address_pair_fixed_ips.return_value = (
['10.1.0.21'])
+ self.assertFalse(
+ l3plugin.update_arp_entry_for_dvr_service_port.called)
+ l3plugin.delete_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext,
+ port,
+ fixed_ips_to_delete=mock.ANY)
def test__notify_l3_agent_update_port_with_allowed_address_pairs(self):
port_id = uuidutils.generate_uuid()
@@ -874,6 +882,8 @@
directory.add_plugin(plugin_constants.L3, l3plugin)
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', mock.ANY, **kwargs)
+ self.assertTrue(
+ l3plugin.update_arp_entry_for_dvr_service_port.called)
def test__notify_l3_agent_when_unbound_port_migrates_to_bound_host(self):
port_id = 'fake-port'
@@ -930,6 +940,8 @@
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', plugin, **kwargs)
self.assertFalse(
+ l3plugin.update_arp_entry_for_dvr_service_port.called)
+ self.assertFalse(
l3plugin.dvr_handle_new_service_port.called)
self.assertFalse(l3plugin.remove_router_from_l3_agent.called)
self.assertFalse(l3plugin.get_dvr_routers_to_remove.called)
@@ -946,6 +958,9 @@
directory.add_plugin(plugin_constants.L3, l3plugin)
l3_dvrscheduler_db._notify_l3_agent_new_port(
'port', 'after_create', mock.ANY, **kwargs)
+ l3plugin.update_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, kwargs.get('port'))
l3plugin.dvr_handle_new_service_port.assert_called_once_with(
self.adminContext, kwargs.get('port'))
@@ -962,6 +977,8 @@
l3_dvrscheduler_db._notify_l3_agent_new_port(
'port', 'after_create', mock.ANY, **kwargs)
self.assertFalse(
+ l3plugin.update_arp_entry_for_dvr_service_port.called)
+ self.assertFalse(
l3plugin.dvr_handle_new_service_port.called)
def test__notify_l3_agent_update_port_with_migration_port_profile(self):
@@ -987,6 +1004,9 @@
l3plugin.dvr_handle_new_service_port.assert_called_once_with(
self.adminContext, kwargs.get('port'),
dest_host='vm-host2', router_id=None)
+ l3plugin.update_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, kwargs.get('port'))
def test__notify_l3_agent_update_port_no_action(self):
kwargs = {
@@ -1006,6 +1026,8 @@
'port', 'after_update', mock.ANY, **kwargs)
self.assertFalse(
+ l3plugin.update_arp_entry_for_dvr_service_port.called)
+ self.assertFalse(
l3plugin.dvr_handle_new_service_port.called)
self.assertFalse(l3plugin.remove_router_from_l3_agent.called)
self.assertFalse(l3plugin.get_dvr_routers_to_remove.called)
@@ -1029,6 +1051,10 @@
directory.add_plugin(plugin_constants.L3, l3plugin)
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', mock.ANY, **kwargs)
+
+ l3plugin.update_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, kwargs.get('port'))
self.assertFalse(l3plugin.dvr_handle_new_service_port.called)
def test__notify_l3_agent_update_port_with_ip_update(self):
@@ -1053,6 +1079,9 @@
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', mock.ANY, **kwargs)
+ l3plugin.update_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, kwargs.get('port'))
self.assertFalse(l3plugin.dvr_handle_new_service_port.called)
def test__notify_l3_agent_update_port_without_ip_change(self):
@@ -1074,6 +1103,7 @@
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', mock.ANY, **kwargs)
+ self.assertFalse(l3plugin.update_arp_entry_for_dvr_service_port.called)
self.assertFalse(l3plugin.dvr_handle_new_service_port.called)
def test__notify_l3_agent_port_binding_change(self):
@@ -1159,10 +1189,15 @@
if routers_to_remove:
(l3plugin.l3_rpc_notifier.router_removed_from_agent.
assert_called_once_with(mock.ANY, 'foo_id', source_host))
+ self.assertEqual(
+ 1,
+ l3plugin.delete_arp_entry_for_dvr_service_port.call_count)
if fip and is_distributed and not (routers_to_remove and
fip['router_id'] is routers_to_remove[0]['router_id']):
(l3plugin.l3_rpc_notifier.routers_updated_on_host.
assert_called_once_with(mock.ANY, ['router_id'], source_host))
+ self.assertEqual(
+ 1, l3plugin.update_arp_entry_for_dvr_service_port.call_count)
l3plugin.dvr_handle_new_service_port.assert_called_once_with(
self.adminContext, kwargs.get('port'),
dest_host=None, router_id=router_id)
@@ -1203,6 +1238,12 @@
l3_dvrscheduler_db._notify_l3_agent_port_update(
'port', 'after_update', plugin, **kwargs)
+ self.assertEqual(
+ 1, l3plugin.delete_arp_entry_for_dvr_service_port.call_count)
+ l3plugin.delete_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, mock.ANY)
+
self.assertFalse(
l3plugin.dvr_handle_new_service_port.called)
(l3plugin.l3_rpc_notifier.router_removed_from_agent.
@@ -1236,6 +1277,9 @@
l3plugin.get_dvr_routers_to_remove.return_value = removed_routers
l3_dvrscheduler_db._notify_port_delete(
'port', 'after_delete', plugin, **kwargs)
+ l3plugin.delete_arp_entry_for_dvr_service_port.\
+ assert_called_once_with(
+ self.adminContext, mock.ANY)
(l3plugin.l3_rpc_notifier.router_removed_from_agent.
assert_called_once_with(mock.ANY, 'foo_id', 'foo_host'))
diff -Nru neutron-17.1.0/neutron/tests/unit/services/qos/drivers/openvswitch/test_driver.py neutron-17.1.1/neutron/tests/unit/services/qos/drivers/openvswitch/test_driver.py
--- neutron-17.1.0/neutron/tests/unit/services/qos/drivers/openvswitch/test_driver.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/services/qos/drivers/openvswitch/test_driver.py 2021-03-13 02:26:48.000000000 +0100
@@ -43,3 +43,5 @@
return_value=net):
test_method(self.driver.validate_rule_for_port(
mock.Mock(), rule, port))
+ test_method(self.driver.validate_rule_for_network(
+ mock.Mock(), rule, network_id=mock.Mock()))
diff -Nru neutron-17.1.0/neutron/tests/unit/services/qos/drivers/test_manager.py neutron-17.1.1/neutron/tests/unit/services/qos/drivers/test_manager.py
--- neutron-17.1.0/neutron/tests/unit/services/qos/drivers/test_manager.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/services/qos/drivers/test_manager.py 2021-03-13 02:26:48.000000000 +0100
@@ -119,6 +119,29 @@
else:
is_rule_supported_mock.assert_not_called()
+ def test_validate_rule_for_network(self):
+ driver_manager = self._create_manager_with_drivers({
+ 'driver-A': {
+ 'is_loaded': True,
+ 'rules': {
+ qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH: {
+ "min_kbps": {'type:values': None},
+ 'direction': {
+ 'type:values': lib_consts.VALID_DIRECTIONS}
+ }
+ }
+ }
+ })
+ rule = rule_object.QosMinimumBandwidthRule(
+ self.ctxt, id=uuidutils.generate_uuid())
+
+ is_rule_supported_mock = mock.Mock()
+ is_rule_supported_mock.return_value = True
+ driver_manager._drivers[0].is_rule_supported = is_rule_supported_mock
+ self.assertTrue(driver_manager.validate_rule_for_network(
+ mock.Mock(), rule, mock.Mock()))
+ is_rule_supported_mock.assert_called_once_with(rule)
+
def test_validate_rule_for_port_rule_vif_type_supported(self):
port = self._get_port(
portbindings.VIF_TYPE_OVS, portbindings.VNIC_NORMAL)
diff -Nru neutron-17.1.0/neutron/tests/unit/services/qos/test_qos_plugin.py neutron-17.1.1/neutron/tests/unit/services/qos/test_qos_plugin.py
--- neutron-17.1.0/neutron/tests/unit/services/qos/test_qos_plugin.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/services/qos/test_qos_plugin.py 2021-03-13 02:26:48.000000000 +0100
@@ -128,10 +128,8 @@
if has_qos_policy:
self.port_data['port']['qos_policy_id'] = self.policy.id
- self.policy.rules = bw_rules
elif has_net_qos_policy:
self.port_data['port']['qos_network_policy_id'] = self.policy.id
- self.policy.rules = bw_rules
self.port = ports_object.Port(
self.ctxt, **self.port_data['port'])
@@ -142,8 +140,10 @@
with mock.patch('neutron.objects.network.NetworkSegment.get_objects',
return_value=[segment_mock]), \
- mock.patch('neutron.objects.qos.policy.QosPolicy.get_object',
- return_value=self.policy):
+ mock.patch(
+ 'neutron.objects.qos.rule.QosMinimumBandwidthRule.'
+ 'get_objects',
+ return_value=bw_rules):
return qos_plugin.QoSPlugin._extend_port_resource_request(
port_res, self.port)
@@ -184,7 +184,7 @@
)
def test__extend_port_resource_request_non_min_bw_rule(self):
- port = self._create_and_extend_port([self.rule])
+ port = self._create_and_extend_port([])
self.assertIsNone(port.get('resource_request'))
@@ -202,7 +202,6 @@
def test__extend_port_resource_request_inherited_policy(self):
self.min_rule.direction = lib_constants.EGRESS_DIRECTION
- self.policy.rules = [self.min_rule]
self.min_rule.qos_policy_id = self.policy.id
port = self._create_and_extend_port([self.min_rule],
@@ -327,6 +326,8 @@
'neutron.objects.qos.policy.QosPolicy.get_object',
return_value=policy_mock
) as get_policy, mock.patch.object(
+ self.qos_plugin, "validate_policy_for_network"
+ ) as validate_policy_for_network, mock.patch.object(
self.qos_plugin, "validate_policy_for_ports"
) as validate_policy_for_ports, mock.patch.object(
self.ctxt, "elevated", return_value=admin_ctxt
@@ -338,6 +339,7 @@
states=(kwargs['original_network'],)))
if policy_id is None or policy_id == original_policy_id:
get_policy.assert_not_called()
+ validate_policy_for_network.assert_not_called()
get_ports.assert_not_called()
validate_policy_for_ports.assert_not_called()
else:
@@ -385,6 +387,20 @@
except qos_exc.QosRuleNotSupported:
self.fail("QosRuleNotSupported exception unexpectedly raised")
+ def test_validate_policy_for_network(self):
+ network = uuidutils.generate_uuid()
+ with mock.patch.object(
+ self.qos_plugin.driver_manager, "validate_rule_for_network",
+ return_value=True
+ ):
+ self.policy.rules = [self.rule]
+ try:
+ self.qos_plugin.validate_policy_for_network(
+ self.ctxt, self.policy, network_id=network)
+ except qos_exc.QosRuleNotSupportedByNetwork:
+ self.fail("QosRuleNotSupportedByNetwork "
+ "exception unexpectedly raised")
+
def test_create_min_bw_rule_on_bound_port(self):
policy = self._get_policy()
policy.rules = [self.min_rule]
@@ -1237,6 +1253,35 @@
network.create()
return network
+ def _test_validate_create_network_callback(self, network_qos=False):
+ net_qos_obj = self._make_qos_policy()
+ net_qos_id = net_qos_obj.id if network_qos else None
+ network = self._make_network(qos_policy_id=net_qos_id)
+ kwargs = {"context": self.context,
+ "network": network}
+
+ with mock.patch.object(self.qos_plugin,
+ 'validate_policy_for_network') \
+ as mock_validate_policy:
+ self.qos_plugin._validate_create_network_callback(
+ 'NETWORK', 'precommit_create', 'test_plugin', **kwargs)
+
+ qos_policy = None
+ if network_qos:
+ qos_policy = net_qos_obj
+
+ if qos_policy:
+ mock_validate_policy.assert_called_once_with(
+ self.context, qos_policy, network.id)
+ else:
+ mock_validate_policy.assert_not_called()
+
+ def test_validate_create_network_callback(self):
+ self._test_validate_create_network_callback(network_qos=True)
+
+ def test_validate_create_network_callback_no_qos(self):
+ self._test_validate_create_network_callback(network_qos=False)
+
def _test_validate_create_port_callback(self, port_qos=False,
network_qos=False):
net_qos_obj = self._make_qos_policy()
diff -Nru neutron-17.1.0/neutron/tests/unit/services/trunk/rpc/test_server.py neutron-17.1.1/neutron/tests/unit/services/trunk/rpc/test_server.py
--- neutron-17.1.0/neutron/tests/unit/services/trunk/rpc/test_server.py 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/neutron/tests/unit/services/trunk/rpc/test_server.py 2021-03-13 02:26:48.000000000 +0100
@@ -104,6 +104,43 @@
for port in updated_subports[trunk['id']]:
self.assertEqual('trunk_host_id', port[portbindings.HOST_ID])
+ def test_update_subport_bindings_during_migration(self):
+ with self.port() as _parent_port:
+ parent_port = _parent_port
+ trunk = self._create_test_trunk(parent_port)
+ subports = []
+ for vid in range(0, 3):
+ with self.port() as new_port:
+ obj = trunk_obj.SubPort(
+ context=self.context,
+ trunk_id=trunk['id'],
+ port_id=new_port['port']['id'],
+ segmentation_type='vlan',
+ segmentation_id=vid)
+ subports.append(obj)
+
+ expected_calls = [
+ mock.call(
+ mock.ANY, subport['port_id'],
+ {'port': {portbindings.HOST_ID: 'new_trunk_host_id',
+ 'device_owner': constants.TRUNK_SUBPORT_OWNER}})
+ for subport in subports]
+
+ test_obj = server.TrunkSkeleton()
+ test_obj._trunk_plugin = self.trunk_plugin
+ test_obj._core_plugin = self.core_plugin
+ port_data = {
+ portbindings.HOST_ID: 'trunk_host_id',
+ portbindings.PROFILE: {'migrating_to': 'new_trunk_host_id'}}
+ with mock.patch.object(
+ self.core_plugin, "get_port",
+ return_value=port_data), \
+ mock.patch.object(
+ test_obj, "_safe_update_trunk"):
+ test_obj.update_subport_bindings(self.context, subports=subports)
+ for expected_call in expected_calls:
+ self.assertIn(expected_call, self.mock_update_port.mock_calls)
+
def test__handle_port_binding_binding_error(self):
with self.port() as _trunk_port:
trunk = self._create_test_trunk(_trunk_port)
diff -Nru neutron-17.1.0/releasenotes/notes/do-not-create-dhcp-entries-for-all-types-of-ports-39c03b3782d2753e.yaml neutron-17.1.1/releasenotes/notes/do-not-create-dhcp-entries-for-all-types-of-ports-39c03b3782d2753e.yaml
--- neutron-17.1.0/releasenotes/notes/do-not-create-dhcp-entries-for-all-types-of-ports-39c03b3782d2753e.yaml 1970-01-01 01:00:00.000000000 +0100
+++ neutron-17.1.1/releasenotes/notes/do-not-create-dhcp-entries-for-all-types-of-ports-39c03b3782d2753e.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -0,0 +1,6 @@
+---
+other:
+ - |
+ To improve performance of the DHCP agent, it will no longer configure the DHCP server
+ for every port type created in Neutron. For example, for floating IP or router HA
+ interfaces there is no need since a client will not make a DHCP request for them
diff -Nru neutron-17.1.0/releasenotes/notes/ovn-mcast-flood-reports-80fb529120f2af1c.yaml neutron-17.1.1/releasenotes/notes/ovn-mcast-flood-reports-80fb529120f2af1c.yaml
--- neutron-17.1.0/releasenotes/notes/ovn-mcast-flood-reports-80fb529120f2af1c.yaml 1970-01-01 01:00:00.000000000 +0100
+++ neutron-17.1.1/releasenotes/notes/ovn-mcast-flood-reports-80fb529120f2af1c.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -0,0 +1,7 @@
+---
+fixes:
+ - |
+ Fixes a configuration problem in the OVN driver that prevented
+ external IGMP queries from reaching the Virtual Machines. See
+ `bug 1918108 <https://bugs.launchpad.net/neutron/+bug/1918108>`_
+ for details.
diff -Nru neutron-17.1.0/tox.ini neutron-17.1.1/tox.ini
--- neutron-17.1.0/tox.ini 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/tox.ini 2021-03-13 02:26:48.000000000 +0100
@@ -71,12 +71,15 @@
# workaround for DB teardown lock contention (bug/1541742)
OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:600}
OS_TEST_PATH=./neutron/tests/fullstack
+# Because of issue with stestr and Python3, we need to avoid too much output
+# to be produced during tests, so we will ignore python warnings here
+ PYTHONWARNINGS=ignore
deps =
{[testenv:functional]deps}
commands =
{toxinidir}/tools/generate_dhclient_script_for_fullstack.sh {envdir}
{toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin
- stestr run --concurrency 4 {posargs}
+ stestr run --concurrency 3 {posargs}
[testenv:dsvm-fullstack-gate]
setenv = {[testenv:dsvm-fullstack]setenv}
diff -Nru neutron-17.1.0/zuul.d/base.yaml neutron-17.1.1/zuul.d/base.yaml
--- neutron-17.1.0/zuul.d/base.yaml 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/zuul.d/base.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -24,6 +24,7 @@
devstack_services:
# Ignore any default set by devstack. Emit a "disable_all_services".
base: false
+ etcd3: false
devstack_localrc:
INSTALL_TESTONLY_PACKAGES: true
DATABASE_PASSWORD: stackdb
diff -Nru neutron-17.1.0/zuul.d/rally.yaml neutron-17.1.1/zuul.d/rally.yaml
--- neutron-17.1.0/zuul.d/rally.yaml 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/zuul.d/rally.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -41,7 +41,6 @@
parent: rally-task-at-devstack
required-projects:
- name: openstack/devstack
- - name: openstack/devstack-gate
- name: openstack/rally
- name: openstack/rally-openstack
irrelevant-files: *irrelevant-files
diff -Nru neutron-17.1.0/zuul.d/tempest-multinode.yaml neutron-17.1.1/zuul.d/tempest-multinode.yaml
--- neutron-17.1.0/zuul.d/tempest-multinode.yaml 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/zuul.d/tempest-multinode.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -6,7 +6,6 @@
roles:
- zuul: openstack/neutron-tempest-plugin
required-projects:
- - openstack/devstack-gate
- openstack/neutron
- openstack/tempest
pre-run: playbooks/dvr-multinode-scenario-pre-run.yaml
@@ -108,7 +107,6 @@
parent: tempest-multinode-full-py3
timeout: 10800
required-projects:
- - openstack/devstack-gate
- openstack/neutron
- openstack/neutron-tempest-plugin
- openstack/tempest
diff -Nru neutron-17.1.0/zuul.d/tempest-singlenode.yaml neutron-17.1.1/zuul.d/tempest-singlenode.yaml
--- neutron-17.1.0/zuul.d/tempest-singlenode.yaml 2021-01-22 03:31:35.000000000 +0100
+++ neutron-17.1.1/zuul.d/tempest-singlenode.yaml 2021-03-13 02:26:48.000000000 +0100
@@ -5,7 +5,6 @@
abstract: true
timeout: 10800
required-projects:
- - openstack/devstack-gate
- openstack/neutron
- openstack/tempest
pre-run: playbooks/configure_ebtables.yaml
@@ -126,7 +125,6 @@
parent: tempest-integrated-networking
timeout: 10800
required-projects:
- - openstack/devstack-gate
- openstack/neutron
- openstack/tempest
vars:
@@ -144,7 +142,6 @@
parent: tempest-integrated-networking
timeout: 10800
required-projects:
- - openstack/devstack-gate
- openstack/neutron
- openstack/tempest
vars:
@@ -181,7 +178,6 @@
parent: tempest-integrated-networking
timeout: 10800
required-projects: &ovn-base-required-projects
- - openstack/devstack-gate
- openstack/neutron
- openstack/tempest
irrelevant-files: *irrelevant-files
Reply to: