Skip to content

Commit dbe9c68

Browse files
authored
Merge pull request #122 from stackhpc/upstream/2023.1-2024-02-12
Synchronise 2023.1 with upstream
2 parents 1e91c1e + f26a625 commit dbe9c68

File tree

31 files changed

+262
-768
lines changed

31 files changed

+262
-768
lines changed

doc/source/contributor/internals/openvswitch_agent.rst

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ The IDs used for bridge and port names are truncated.
494494
| tbr-trunk-id |
495495
| |
496496
| tpt-parent-id spt-subport-id |
497-
| (tag 100) |
497+
| (tag 0) (tag 100) |
498498
+-----+-----------------+--------+
499499
| |
500500
| |
@@ -514,27 +514,27 @@ spi-subport-id: int bridge side of the patch port that implements a subport.
514514
Trunk creation
515515
++++++++++++++
516516

517-
A VM is spawned passing to Nova the port-id of a parent port associated with
518-
a trunk. Neutron will pass to Nova the bridge where to plug the vif as part of the vif details.
519-
The os-vif driver creates the trunk bridge tbr-trunk-id if it does not exist in plug().
520-
It will create the tap interface tap1 and plug it into tbr-trunk-id setting the parent port ID in the external-ids.
521-
The OVS agent will be monitoring the creation of ports on the trunk bridges. When it detects
522-
that a new port has been created on the trunk bridge, it will do the following:
517+
A VM is spawned passing to Nova the port-id of a parent port associated
518+
with a trunk. Neutron will pass to Nova the bridge where to plug the
519+
vif as part of the vif details. The os-vif driver creates the trunk
520+
bridge tbr-trunk-id if it does not exist in plug(). It will create the
521+
tap interface tap1 and plug it into tbr-trunk-id setting the parent port
522+
ID in the external-ids. The trunk driver will wire the parent port via
523+
a patch port to connect the trunk bridge to the integration bridge:
523524

524525
::
525526

526-
ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id
527-
ovs-vsctl add-port br-int tpi-parent-id tag=3 -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id
527+
ovs-vsctl add-port tbr-trunk-id tpt-parent-id -- set Interface tpt-parent-id type=patch options:peer=tpi-parent-id -- set Port tpt-parent-id vlan_mode=access tag=0
528+
ovs-vsctl add-port br-int tpi-parent-id -- set Interface tpi-parent-id type=patch options:peer=tpt-parent-id
528529

529530

530-
A patch port is created to connect the trunk bridge to the integration bridge.
531-
tpt-parent-id, the trunk bridge side of the patch is not associated to any
532-
tag. It will carry untagged traffic.
533-
tpi-parent-id, the br-int side the patch port is tagged with VLAN 3. We assume that the
534-
trunk is on network1 that on this host is associated with VLAN 3.
535-
The OVS agent will set the trunk ID in the external-ids of tpt-parent-id and tpi-parent-id.
536-
If the parent port is associated with one or more subports the agent will process them as
537-
described in the next paragraph.
531+
tpt-parent-id, the trunk bridge side of the patch will carry untagged
532+
traffic (vlan_mode=access tag=0). The OVS agent will be monitoring the
533+
creation of ports on the integration bridge. tpi-parent-id, the br-int
534+
side the patch port is tagged with VLAN 3 by ovs-agent. We assume that
535+
the trunk is on network1 that on this host is associated with VLAN 3.
536+
If the parent port is associated with one or more subports the agent
537+
will process them as described in the next paragraph.
538538

539539
Subport creation
540540
++++++++++++++++
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2024 Red Hat, Inc.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from neutron_lib import rpc as n_rpc
17+
from oslo_messaging import Target
18+
19+
20+
class BasePluginApi(object):
21+
"""Base agent side of the rpc API"""
22+
def __init__(self, topic, namespace, version):
23+
target = Target(
24+
topic=topic,
25+
namespace=namespace,
26+
version=version)
27+
self.client = n_rpc.get_client(target)
28+
29+
def get_ports(self, context, port_filters):
30+
# NOTE(mtomaska): The MetadataRpcCallback (server side) API version 1.0
31+
# exposes get_ports, under the PLUGIN topic and None namespace.
32+
cctxt = self.client.prepare(version='1.0')
33+
return cctxt.call(context, 'get_ports', filters=port_filters)

neutron/agent/dhcp/agent.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from neutron_lib import constants
2626
from neutron_lib import context
2727
from neutron_lib import exceptions
28-
from neutron_lib import rpc as n_rpc
2928
from oslo_concurrency import lockutils
3029
from oslo_config import cfg
3130
from oslo_log import helpers as log_helpers
@@ -38,6 +37,7 @@
3837
from oslo_utils import timeutils
3938

4039
from neutron._i18n import _
40+
from neutron.agent.common import base_agent_rpc
4141
from neutron.agent.common import resource_processing_queue as queue
4242
from neutron.agent.linux import dhcp
4343
from neutron.agent.linux import external_process
@@ -838,7 +838,7 @@ def disable_isolated_metadata_proxy(self, network):
838838
del self._metadata_routers[network.id]
839839

840840

841-
class DhcpPluginApi(object):
841+
class DhcpPluginApi(base_agent_rpc.BasePluginApi):
842842
"""Agent side of the dhcp rpc API.
843843
844844
This class implements the client side of an rpc interface. The server side
@@ -858,11 +858,10 @@ class DhcpPluginApi(object):
858858

859859
def __init__(self, topic, host):
860860
self.host = host
861-
target = oslo_messaging.Target(
861+
super().__init__(
862862
topic=topic,
863863
namespace=constants.RPC_NAMESPACE_DHCP_PLUGIN,
864864
version='1.0')
865-
self.client = n_rpc.get_client(target)
866865

867866
@property
868867
def context(self):
@@ -918,6 +917,11 @@ def get_dhcp_port(self, port_id):
918917
if port:
919918
return dhcp.DictModel(port)
920919

920+
def get_ports(self, port_filters):
921+
ports = super().get_ports(self.context, port_filters)
922+
if ports:
923+
return [dhcp.DictModel(port) for port in ports]
924+
921925
def dhcp_ready_on_ports(self, port_ids):
922926
"""Notify the server that DHCP is configured for the port."""
923927
cctxt = self.client.prepare(version='1.5')

neutron/agent/linux/dhcp.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ def _get_process_manager(self, cmd_callback=None):
357357

358358
def disable(self, retain_port=False, block=False, **kwargs):
359359
"""Disable DHCP for this network by killing the local process."""
360-
self.process_monitor.unregister(self.network.id, DNSMASQ_SERVICE_NAME)
361360
pm = self._get_process_manager()
361+
self.process_monitor.unregister(pm.uuid, DNSMASQ_SERVICE_NAME)
362362
pm.disable(sig=str(int(signal.SIGTERM)))
363363
if block:
364364
try:
@@ -602,7 +602,7 @@ def _spawn_or_reload_process(self, reload_with_HUP):
602602

603603
pm.enable(reload_cfg=reload_with_HUP, ensure_active=True)
604604

605-
self.process_monitor.register(uuid=self.get_process_uuid(),
605+
self.process_monitor.register(uuid=pm.uuid,
606606
service_name=DNSMASQ_SERVICE_NAME,
607607
monitored_process=pm)
608608

@@ -1210,11 +1210,15 @@ def _output_opts_file(self):
12101210
return name
12111211

12121212
def _get_ovn_metadata_port_ip(self, subnet):
1213-
m_ports = [port for port in self.network.ports if
1214-
self._is_ovn_metadata_port(port, self.network.id)]
1215-
if m_ports:
1216-
port = self.device_manager.plugin.get_dhcp_port(m_ports[0].id)
1217-
for fixed_ip in port.fixed_ips:
1213+
"""Check if provided subnet contains OVN metadata port"""
1214+
ports_result = self.device_manager.plugin.get_ports(
1215+
port_filters={
1216+
'device_owner': [constants.DEVICE_OWNER_DISTRIBUTED],
1217+
'device_id': ['ovnmeta-' + self.network.id]
1218+
},
1219+
)
1220+
if ports_result:
1221+
for fixed_ip in ports_result[0].get('fixed_ips', []):
12181222
if fixed_ip.subnet_id == subnet.id:
12191223
return fixed_ip.ip_address
12201224

neutron/agent/linux/interface.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,6 @@ def _add_device_to_namespace(self, ip_wrapper, device, namespace):
374374
LOG.warning("Failed to set interface %s into namespace %s. "
375375
"Interface not found, attempt: %s, retrying.",
376376
device, namespace, i + 1)
377-
# NOTE(slaweq) In such case it's required to reset device's
378-
# namespace as it was already set to the "namespace"
379-
# and after retry neutron will look for it in that namespace
380-
# which is wrong
381-
device.namespace = None
382377
time.sleep(1)
383378
except utils.WaitTimeout:
384379
# NOTE(slaweq): if the exception was WaitTimeout then it means

neutron/agent/linux/ip_lib.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from neutron_lib import exceptions
2626
from oslo_config import cfg
2727
from oslo_log import log as logging
28+
from oslo_utils import excutils
2829
from oslo_utils import netutils
2930
from pyroute2.netlink import exceptions as netlink_exceptions
3031
from pyroute2.netlink import rtnl
@@ -477,16 +478,16 @@ def set_down(self):
477478
self.name, self._parent.namespace, state='down')
478479

479480
def set_netns(self, namespace, is_ovs_port=False):
480-
privileged.set_link_attribute(
481-
self.name, self._parent.namespace, net_ns_fd=namespace)
482-
self._parent.namespace = namespace
483-
if is_ovs_port:
484-
# NOTE(slaweq): because of the "shy port" which may dissapear for
485-
# short time after it's moved to the namespace we need to wait
486-
# a bit before checking if port really exists in the namespace
487-
time.sleep(1)
488-
common_utils.wait_until_true(lambda: self.exists, timeout=5,
489-
sleep=0.5)
481+
old_namespace = self._parent.namespace
482+
try:
483+
privileged.set_link_attribute(
484+
self.name, self._parent.namespace, net_ns_fd=namespace)
485+
self._parent.namespace = namespace
486+
common_utils.wait_until_true(lambda: self.exists, timeout=3,
487+
sleep=0.5)
488+
except common_utils.WaitTimeout:
489+
with excutils.save_and_reraise_exception():
490+
self._parent.namespace = old_namespace
490491

491492
def set_name(self, name):
492493
privileged.set_link_attribute(

neutron/agent/metadata/agent.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,16 @@
1919
from neutron_lib.agent import topics
2020
from neutron_lib import constants
2121
from neutron_lib import context
22-
from neutron_lib import rpc as n_rpc
2322
from neutron_lib.utils import host
2423
from oslo_config import cfg
2524
from oslo_log import log as logging
26-
import oslo_messaging
2725
from oslo_service import loopingcall
2826
from oslo_utils import netutils
2927
import requests
3028
import webob
3129

3230
from neutron._i18n import _
31+
from neutron.agent.common import base_agent_rpc
3332
from neutron.agent.linux import utils as agent_utils
3433
from neutron.agent import rpc as agent_rpc
3534
from neutron.common import cache_utils as cache
@@ -46,7 +45,7 @@
4645
}
4746

4847

49-
class MetadataPluginAPI(object):
48+
class MetadataPluginAPI(base_agent_rpc.BasePluginApi):
5049
"""Agent-side RPC for metadata agent-to-plugin interaction.
5150
5251
This class implements the client side of an rpc interface used by the
@@ -61,15 +60,10 @@ class MetadataPluginAPI(object):
6160
"""
6261

6362
def __init__(self, topic):
64-
target = oslo_messaging.Target(
63+
super().__init__(
6564
topic=topic,
6665
namespace=constants.RPC_NAMESPACE_METADATA,
6766
version='1.0')
68-
self.client = n_rpc.get_client(target)
69-
70-
def get_ports(self, context, filters):
71-
cctxt = self.client.prepare()
72-
return cctxt.call(context, 'get_ports', filters=filters)
7367

7468

7569
class MetadataProxyHandler(object):

neutron/db/l3_db.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,7 @@ def _make_floatingip_dict_with_scope(self, floatingip_obj, scope_id):
18891889
d['fixed_ip_address_scope'] = scope_id
18901890
return d
18911891

1892-
def _get_sync_floating_ips(self, context, router_ids):
1892+
def _get_sync_floating_ips(self, context, router_ids, host=None):
18931893
"""Query floating_ips that relate to list of router_ids with scope.
18941894
18951895
This is different than the regular get_floatingips in that it finds the
@@ -1905,7 +1905,7 @@ def _get_sync_floating_ips(self, context, router_ids):
19051905
return [
19061906
self._make_floatingip_dict_with_scope(*scoped_fip)
19071907
for scoped_fip in l3_obj.FloatingIP.get_scoped_floating_ips(
1908-
context, router_ids)
1908+
context, router_ids, host)
19091909
]
19101910

19111911
def _get_sync_interfaces(self, context, router_ids, device_owners=None):
@@ -2055,7 +2055,7 @@ def _process_interfaces(self, routers_dict, interfaces):
20552055
router[constants.INTERFACE_KEY] = router_interfaces
20562056

20572057
def _get_router_info_list(self, context, router_ids=None, active=None,
2058-
device_owners=None):
2058+
device_owners=None, fip_host_filter=None):
20592059
"""Query routers and their related floating_ips, interfaces."""
20602060
with db_api.CONTEXT_WRITER.using(context):
20612061
routers = self._get_sync_routers(context,
@@ -2064,7 +2064,8 @@ def _get_router_info_list(self, context, router_ids=None, active=None,
20642064
router_ids = [router['id'] for router in routers]
20652065
interfaces = self._get_sync_interfaces(
20662066
context, router_ids, device_owners)
2067-
floating_ips = self._get_sync_floating_ips(context, router_ids)
2067+
floating_ips = self._get_sync_floating_ips(
2068+
context, router_ids, host=fip_host_filter)
20682069
return (routers, interfaces, floating_ips)
20692070

20702071
def get_sync_data(self, context, router_ids=None, active=None):

neutron/db/l3_dvr_db.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,17 @@ def _get_fip_agent_gw_ports(self, context, fip_agent_id):
979979
@log_helper.log_method_call
980980
def _get_dvr_sync_data(self, context, host, agent, router_ids=None,
981981
active=None):
982+
# If the requesting agent is in normal dvr mode, we can fetch
983+
# only FIPs bound to the particular host requesting the update
984+
requesting_agent_mode = self._get_agent_mode(agent)
985+
fip_host_filter = None
986+
if requesting_agent_mode == const.L3_AGENT_MODE_DVR:
987+
fip_host_filter = host
988+
982989
routers, interfaces, floating_ips = self._get_router_info_list(
983990
context, router_ids=router_ids, active=active,
984-
device_owners=const.ROUTER_INTERFACE_OWNERS)
991+
device_owners=const.ROUTER_INTERFACE_OWNERS,
992+
fip_host_filter=fip_host_filter)
985993
dvr_router_ids = set(router['id'] for router in routers
986994
if is_distributed_router(router))
987995
floating_ip_port_ids = [fip['port_id'] for fip in floating_ips
@@ -1014,7 +1022,6 @@ def _get_dvr_sync_data(self, context, host, agent, router_ids=None,
10141022
if len(l3_agent_on_host):
10151023
l3_agent_mode = self._get_agent_mode(
10161024
l3_agent_on_host[0])
1017-
requesting_agent_mode = self._get_agent_mode(agent)
10181025
# Consider the ports where the portbinding host and
10191026
# request host match.
10201027
if port_host == host:

neutron/objects/router.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from oslo_utils import versionutils
2222
from oslo_versionedobjects import fields as obj_fields
2323
from sqlalchemy import func
24+
from sqlalchemy import or_
2425
from sqlalchemy import sql
2526

2627
from neutron.db.models import dvr as dvr_models
@@ -30,6 +31,7 @@
3031
from neutron.db import models_v2
3132
from neutron.objects import base
3233
from neutron.objects.qos import binding as qos_binding
34+
from neutron.plugins.ml2 import models as ml2_models
3335

3436

3537
@base.NeutronObjectRegistry.register
@@ -390,7 +392,7 @@ def obj_make_compatible(self, primitive, target_version):
390392

391393
@classmethod
392394
@db_api.CONTEXT_READER
393-
def get_scoped_floating_ips(cls, context, router_ids):
395+
def get_scoped_floating_ips(cls, context, router_ids, host=None):
394396
query = context.session.query(l3.FloatingIP,
395397
models_v2.SubnetPool.address_scope_id)
396398
query = query.join(
@@ -405,11 +407,28 @@ def get_scoped_floating_ips(cls, context, router_ids):
405407
models_v2.SubnetPool,
406408
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)
407409

410+
# If a host value is provided, filter output to a specific host
411+
if host is not None:
412+
query = query.outerjoin(
413+
ml2_models.PortBinding,
414+
models_v2.Port.id == ml2_models.PortBinding.port_id)
415+
# Also filter for ports with migrating_to as they may be relevant
416+
# to this host but might not yet have the 'host' column updated
417+
# if the migration is in a pre-live migration state
418+
query = query.filter(or_(
419+
ml2_models.PortBinding.host == host,
420+
ml2_models.PortBinding.profile.like('%migrating_to%'),
421+
))
422+
408423
# Filter out on router_ids
409424
query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
410425

411-
# Remove duplicate rows based on FIP IDs
412-
query = query.group_by(l3.FloatingIP.id)
426+
# Remove duplicate rows based on FIP IDs and the subnet pool address
427+
# scope. Only one subnet pool (per IP version, 4 in this case) can
428+
# be assigned to a subnet. The subnet pool address scope for a FIP is
429+
# unique.
430+
query = query.group_by(l3.FloatingIP.id,
431+
models_v2.SubnetPool.address_scope_id)
413432

414433
for row in query:
415434
yield (cls._load_object(context, row[0]), row[1])

0 commit comments

Comments
 (0)