Make get_ports RPC method common for the DHCP and Metadata agent

This patch is the initial implementation on the suggestion
from this patch[1].

The DHCP agent can query the existing `get_ports` RPC method because
this method is already exposed in the MetadataRpcCallback(server side)
which runs under the same topic(PLUGIN) and namespace(None). The benefit
here is that there is no change needed to the API, however it does
go against how we historically setup the RPC layer between a server and client.

[1] https://review.opendev.org/c/openstack/neutron/+/903572/comments/3d4e0453_4b4d2ab6

Related-Bug: #1982569
Change-Id: Icd7c55d2a5103bdbd90907b1dbfb9ccfe34c020a
This commit is contained in:
Miro Tomaska 2024-01-05 13:42:12 -05:00
parent 0c251cce60
commit 637e7a5007
5 changed files with 62 additions and 22 deletions

View File

@ -0,0 +1,33 @@
# Copyright 2024 Red Hat, Inc.
# 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 neutron_lib import rpc as n_rpc
from oslo_messaging import Target
class BasePluginApi(object):
"""Base agent side of the rpc API"""
def __init__(self, topic, namespace, version):
target = Target(
topic=topic,
namespace=namespace,
version=version)
self.client = n_rpc.get_client(target)
def get_ports(self, context, port_filters):
# NOTE(mtomaska): The MetadataRpcCallback (server side) API version 1.0
# exposes get_ports, under the PLUGIN topic and None namespace.
cctxt = self.client.prepare(version='1.0')
return cctxt.call(context, 'get_ports', filters=port_filters)

View File

@ -25,7 +25,6 @@ from neutron_lib.agent import topics
from neutron_lib import constants
from neutron_lib import context
from neutron_lib import exceptions
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
@ -38,6 +37,7 @@ from oslo_utils import netutils
from oslo_utils import timeutils
from neutron._i18n import _
from neutron.agent.common import base_agent_rpc
from neutron.agent.common import resource_processing_queue as queue
from neutron.agent.linux import dhcp
from neutron.agent.linux import external_process
@ -844,7 +844,7 @@ class DhcpAgent(manager.Manager):
del self._metadata_routers[network.id]
class DhcpPluginApi(object):
class DhcpPluginApi(base_agent_rpc.BasePluginApi):
"""Agent side of the dhcp rpc API.
This class implements the client side of an rpc interface. The server side
@ -864,11 +864,10 @@ class DhcpPluginApi(object):
def __init__(self, topic, host):
self.host = host
target = oslo_messaging.Target(
super().__init__(
topic=topic,
namespace=constants.RPC_NAMESPACE_DHCP_PLUGIN,
version='1.0')
self.client = n_rpc.get_client(target)
@property
def context(self):
@ -924,6 +923,11 @@ class DhcpPluginApi(object):
if port:
return dhcp.DictModel(port)
def get_ports(self, port_filters):
ports = super().get_ports(self.context, port_filters)
if ports:
return [dhcp.DictModel(port) for port in ports]
def dhcp_ready_on_ports(self, port_ids):
"""Notify the server that DHCP is configured for the port."""
cctxt = self.client.prepare(version='1.5')

View File

@ -42,6 +42,7 @@ from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager
from neutron.cmd import runtime_checks as checks
from neutron.common import _constants as common_constants
from neutron.common.ovn import constants as ovn_constants
from neutron.common.ovn import utils as ovn_utils
from neutron.common import utils as common_utils
from neutron.ipam import utils as ipam_utils
@ -1210,11 +1211,16 @@ class Dnsmasq(DhcpLocalProcess):
return name
def _get_ovn_metadata_port_ip(self, subnet):
m_ports = [port for port in self.network.ports if
ovn_utils.is_ovn_metadata_port(port)]
if m_ports:
port = self.device_manager.plugin.get_dhcp_port(m_ports[0].id)
for fixed_ip in port.fixed_ips:
"""Check if provided subnet contains OVN metadata port"""
ports_result = self.device_manager.plugin.get_ports(
port_filters={
'device_owner': [constants.DEVICE_OWNER_DISTRIBUTED],
'device_id':
[ovn_constants.OVN_METADATA_PREFIX + self.network.id]
},
)
if ports_result:
for fixed_ip in ports_result[0].get('fixed_ips', []):
if fixed_ip.subnet_id == subnet.id:
return fixed_ip.ip_address

View File

@ -19,17 +19,16 @@ import netaddr
from neutron_lib.agent import topics
from neutron_lib import constants
from neutron_lib import context
from neutron_lib import rpc as n_rpc
from neutron_lib.utils import host
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_service import loopingcall
from oslo_utils import netutils
import requests
import webob
from neutron._i18n import _
from neutron.agent.common import base_agent_rpc
from neutron.agent.linux import utils as agent_utils
from neutron.agent import rpc as agent_rpc
from neutron.common import cache_utils as cache
@ -46,7 +45,7 @@ MODE_MAP = {
}
class MetadataPluginAPI(object):
class MetadataPluginAPI(base_agent_rpc.BasePluginApi):
"""Agent-side RPC for metadata agent-to-plugin interaction.
This class implements the client side of an rpc interface used by the
@ -61,15 +60,10 @@ class MetadataPluginAPI(object):
"""
def __init__(self, topic):
target = oslo_messaging.Target(
super().__init__(
topic=topic,
namespace=constants.RPC_NAMESPACE_METADATA,
version='1.0')
self.client = n_rpc.get_client(target)
def get_ports(self, context, filters):
cctxt = self.client.prepare()
return cctxt.call(context, 'get_ports', filters=filters)
class MetadataProxyHandler(object):

View File

@ -74,8 +74,11 @@ class DhcpOpt(object):
# A base class where class attributes can also be accessed by treating
# an instance as a dict.
class Dictable(object):
def __getitem__(self, k):
return self.__dict__.get(k)
def __getitem__(self, k, default_value=None):
return self.__dict__.get(k, default_value)
def get(self, k, default_value=None):
return self.__getitem__(k, default_value)
class FakeDhcpPort(Dictable):
@ -3207,8 +3210,8 @@ class TestDnsmasq(TestBase):
def test__generate_opts_per_subnet_with_metadata_port(self):
config = {'enable_isolated_metadata': False,
'force_metadata': False}
self.mock_mgr.return_value.plugin.get_dhcp_port.return_value = \
FakeOvnMetadataPort()
self.mock_mgr.return_value.plugin.get_ports.return_value = \
[FakeOvnMetadataPort()]
self._test__generate_opts_per_subnet_helper(config, True,
network_class=FakeNetworkDhcpandOvnMetadataPort)