Skip to content

Commit c0f1130

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Implement OVN agent metadata extension"
2 parents 2a196fe + fe31f4f commit c0f1130

File tree

8 files changed

+334
-51
lines changed

8 files changed

+334
-51
lines changed

neutron/agent/ovn/agent/ovn_neutron_agent.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ class OVNNeutronAgent(service.Service):
5454
def __init__(self, conf):
5555
super().__init__()
5656
self._conf = conf
57-
self.chassis = None
58-
self.chassis_id = None
59-
self.ovn_bridge = None
57+
self._chassis = None
58+
self._chassis_id = None
59+
self._ovn_bridge = None
6060
self.ext_manager_api = ext_mgr.OVNAgentExtensionAPI()
6161
self.ext_manager = ext_mgr.OVNAgentExtensionManager(self._conf)
6262
self.ext_manager.initialize(None, 'ovn', self)
@@ -65,6 +65,10 @@ def __getitem__(self, name):
6565
"""Return the named extension objet from ``self.ext_manager``"""
6666
return self.ext_manager[name].obj
6767

68+
@property
69+
def conf(self):
70+
return self._conf
71+
6872
@property
6973
def ovs_idl(self):
7074
if not self.ext_manager_api.ovs_idl:
@@ -87,15 +91,27 @@ def sb_idl(self):
8791
def sb_post_fork_event(self):
8892
return self.ext_manager_api.sb_post_fork_event
8993

94+
@property
95+
def chassis(self):
96+
return self._chassis
97+
98+
@property
99+
def chassis_id(self):
100+
return self._chassis_id
101+
102+
@property
103+
def ovn_bridge(self):
104+
return self._ovn_bridge
105+
90106
def load_config(self):
91-
self.chassis = ovsdb.get_own_chassis_name(self.ovs_idl)
107+
self._chassis = ovsdb.get_own_chassis_name(self.ovs_idl)
92108
try:
93-
self.chassis_id = uuid.UUID(self.chassis)
109+
self._chassis_id = uuid.UUID(self.chassis)
94110
except ValueError:
95111
# OVS system-id could be a non UUID formatted string.
96-
self.chassis_id = uuid.uuid5(OVN_MONITOR_UUID_NAMESPACE,
97-
self.chassis)
98-
self.ovn_bridge = ovsdb.get_ovn_bridge(self.ovs_idl)
112+
self._chassis_id = uuid.uuid5(OVN_MONITOR_UUID_NAMESPACE,
113+
self._chassis)
114+
self._ovn_bridge = ovsdb.get_ovn_bridge(self.ovs_idl)
99115
LOG.info("Loaded chassis name %s (UUID: %s) and ovn bridge %s.",
100116
self.chassis, self.chassis_id, self.ovn_bridge)
101117

neutron/agent/ovn/extensions/extension_manager.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
from neutron_lib.agent import extension
2020
from neutron_lib import exceptions
21+
from oslo_log import log as logging
2122

2223
from neutron._i18n import _
2324
from neutron.agent import agent_extensions_manager as agent_ext_mgr
2425

2526

27+
LOG = logging.getLogger(__name__)
2628
OVN_AGENT_EXT_MANAGER_NAMESPACE = 'neutron.agent.ovn.extensions'
2729

2830

@@ -45,13 +47,15 @@ def start(self):
4547
"""Start the extensions, once the OVN agent has been initialized."""
4648
for ext in self:
4749
ext.obj.start()
50+
LOG.info('Extension manager: %s started', ext.obj.name)
4851

4952

5053
class OVNAgentExtension(extension.AgentExtension, metaclass=abc.ABCMeta):
5154

52-
def __init__(self):
53-
super().__init__()
55+
def __init__(self, *args, **kwargs):
56+
super().__init__(*args, **kwargs)
5457
self.agent_api = None
58+
self._is_started = False
5559

5660
@property
5761
@abc.abstractmethod
@@ -77,6 +81,11 @@ def start(self):
7781
OVN agent and the extension manager API. It is executed at the end of
7882
the OVN agent ``start`` method.
7983
"""
84+
self._is_started = True
85+
86+
@property
87+
def is_started(self):
88+
return self._is_started
8089

8190
@property
8291
@abc.abstractmethod
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Copyright 2024 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import collections
16+
import functools
17+
import re
18+
19+
from oslo_concurrency import lockutils
20+
from oslo_config import cfg
21+
from oslo_log import log
22+
from ovsdbapp.backend.ovs_idl import vlog
23+
24+
from neutron.agent.linux import external_process
25+
from neutron.agent.ovn.extensions import extension_manager
26+
from neutron.agent.ovn.metadata import agent as metadata_agent
27+
from neutron.agent.ovn.metadata import server as metadata_server
28+
from neutron.common.ovn import constants as ovn_const
29+
from neutron.conf.agent.database import agents_db
30+
from neutron.conf.agent.metadata import config as meta_conf
31+
from neutron.conf.agent.ovn.metadata import config as ovn_meta
32+
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
33+
34+
35+
LOG = log.getLogger(__name__)
36+
EXT_NAME = 'metadata'
37+
agents_db.register_db_agents_opts()
38+
_SYNC_STATE_LOCK = lockutils.ReaderWriterLock()
39+
CHASSIS_METADATA_LOCK = 'chassis_metadata_lock'
40+
41+
SB_IDL_TABLES = ['Encap',
42+
'Port_Binding',
43+
'Datapath_Binding',
44+
'SB_Global',
45+
'Chassis',
46+
'Chassis_Private',
47+
]
48+
49+
NS_PREFIX = ovn_const.OVN_METADATA_PREFIX
50+
MAC_PATTERN = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
51+
OVN_VIF_PORT_TYPES = (
52+
"", ovn_const.LSP_TYPE_EXTERNAL, ovn_const.LSP_TYPE_LOCALPORT)
53+
54+
MetadataPortInfo = collections.namedtuple('MetadataPortInfo', ['mac',
55+
'ip_addresses',
56+
'logical_port'])
57+
58+
59+
def _sync_lock(f):
60+
"""Decorator to block all operations for a global sync call."""
61+
@functools.wraps(f)
62+
def wrapped(*args, **kwargs):
63+
with _SYNC_STATE_LOCK.write_lock():
64+
return f(*args, **kwargs)
65+
return wrapped
66+
67+
68+
class MetadataExtension(extension_manager.OVNAgentExtension,
69+
metadata_agent.MetadataAgent):
70+
71+
def __init__(self):
72+
super().__init__(conf=cfg.CONF)
73+
vlog.use_python_logger(max_level=config.get_ovn_ovsdb_log_level())
74+
self._process_monitor = None
75+
self._proxy = None
76+
# We'll restart all haproxy instances upon start so that they honor
77+
# any potential changes in their configuration.
78+
self.restarted_metadata_proxy_set = set()
79+
80+
@staticmethod
81+
def _register_config_options():
82+
ovn_meta.register_meta_conf_opts(meta_conf.SHARED_OPTS)
83+
ovn_meta.register_meta_conf_opts(
84+
meta_conf.UNIX_DOMAIN_METADATA_PROXY_OPTS)
85+
ovn_meta.register_meta_conf_opts(meta_conf.METADATA_PROXY_HANDLER_OPTS)
86+
ovn_meta.register_meta_conf_opts(meta_conf.METADATA_RATE_LIMITING_OPTS,
87+
group=meta_conf.RATE_LIMITING_GROUP)
88+
89+
def initialize(self, *args):
90+
self._register_config_options()
91+
self._process_monitor = external_process.ProcessMonitor(
92+
config=self.agent_api.conf, resource_type='metadata')
93+
94+
@property
95+
def name(self):
96+
return 'Metadata OVN agent extension'
97+
98+
@property
99+
def ovs_idl_events(self):
100+
return []
101+
102+
@property
103+
def nb_idl_tables(self):
104+
return []
105+
106+
@property
107+
def nb_idl_events(self):
108+
return []
109+
110+
@property
111+
def sb_idl_tables(self):
112+
return SB_IDL_TABLES
113+
114+
@property
115+
def sb_idl_events(self):
116+
return [metadata_agent.PortBindingUpdatedEvent,
117+
metadata_agent.PortBindingDeletedEvent,
118+
metadata_agent.SbGlobalUpdateEvent,
119+
metadata_agent.ChassisPrivateCreateEvent,
120+
]
121+
122+
# NOTE(ralonsoh): the following properties are needed during the migration
123+
# to the Metadata agent to the OVN agent, while sharing the code with
124+
# ``metadata_agent.MetadataAgent``
125+
@property
126+
def nb_idl(self):
127+
return self.agent_api.nb_idl
128+
129+
@property
130+
def sb_idl(self):
131+
return self.agent_api.sb_idl
132+
133+
@property
134+
def ovs_idl(self):
135+
return self.agent_api.ovs_idl
136+
137+
@property
138+
def conf(self):
139+
return self.agent_api.conf
140+
141+
@property
142+
def chassis(self):
143+
return self.agent_api.chassis
144+
145+
@property
146+
def ovn_bridge(self):
147+
return self.agent_api.ovn_bridge
148+
149+
@_sync_lock
150+
def resync(self):
151+
"""Resync the Metadata OVN agent extension.
152+
153+
Reload the configuration and sync the agent again.
154+
"""
155+
self.agent_api.load_config()
156+
self.sync()
157+
158+
def start(self):
159+
self._load_config()
160+
161+
# Launch the server that will act as a proxy between the VM's and Nova.
162+
self._proxy = metadata_server.UnixDomainMetadataProxy(
163+
self.agent_api.conf, self.agent_api.chassis,
164+
sb_idl=self.agent_api.sb_idl)
165+
self._proxy.run()
166+
167+
# Do the initial sync.
168+
self.sync()
169+
170+
# Register the agent with its corresponding Chassis
171+
self.register_metadata_agent()
172+
173+
# Raise the "is_started" flag.
174+
self._is_started = True

0 commit comments

Comments
 (0)