168 lines
6.1 KiB
Python
168 lines
6.1 KiB
Python
# Copyright 2017 Red Hat, Inc.
|
|
# Copyright 2023 SysEleven GmbH
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import uuid
|
|
|
|
from neutron.agent.linux import external_process
|
|
from neutron.common.ovn import utils as ovn_utils
|
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
|
|
from oslo_log import log as logging
|
|
from oslo_service import service
|
|
from ovsdbapp.backend.ovs_idl import event as row_event
|
|
from ovsdbapp.backend.ovs_idl import vlog
|
|
|
|
from neutron_vpnaas.agent.ovn.vpn import ovsdb
|
|
from neutron_vpnaas.services.vpn.common import constants
|
|
from neutron_vpnaas.services.vpn import vpn_service
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
OVN_VPNAGENT_UUID_NAMESPACE = uuid.UUID('e1ce3b12-b1e0-4c81-ba27-07c0fec9c12b')
|
|
|
|
|
|
class ChassisCreateEventBase(row_event.RowEvent):
|
|
"""Row create event - Chassis name == our_chassis.
|
|
|
|
On connection, we get a dump of all chassis so if we catch a creation
|
|
of our own chassis it has to be a reconnection. In this case, we need
|
|
to do a full sync to make sure that we capture all changes while the
|
|
connection to OVSDB was down.
|
|
"""
|
|
table = None
|
|
|
|
def __init__(self, vpn_agent):
|
|
self.agent = vpn_agent
|
|
self.first_time = True
|
|
events = (self.ROW_CREATE,)
|
|
super().__init__(
|
|
events, self.table, (('name', '=', self.agent.chassis),))
|
|
self.event_name = self.__class__.__name__
|
|
|
|
def run(self, event, row, old):
|
|
if self.first_time:
|
|
self.first_time = False
|
|
else:
|
|
# NOTE(lucasagomes): Re-register the ovn vpn agent
|
|
# with the local chassis in case its entry was re-created
|
|
# (happens when restarting the ovn-controller)
|
|
self.agent.register_vpn_agent()
|
|
LOG.info("Connection to OVSDB established, doing a full sync")
|
|
self.agent.sync()
|
|
|
|
|
|
class ChassisCreateEvent(ChassisCreateEventBase):
|
|
table = 'Chassis'
|
|
|
|
|
|
class ChassisPrivateCreateEvent(ChassisCreateEventBase):
|
|
table = 'Chassis_Private'
|
|
|
|
|
|
class SbGlobalUpdateEvent(row_event.RowEvent):
|
|
"""Row update event on SB_Global table."""
|
|
|
|
def __init__(self, vpn_agent):
|
|
self.agent = vpn_agent
|
|
table = 'SB_Global'
|
|
events = (self.ROW_UPDATE,)
|
|
super().__init__(events, table, None)
|
|
self.event_name = self.__class__.__name__
|
|
|
|
def run(self, event, row, old):
|
|
table = ('Chassis_Private' if self.agent.has_chassis_private
|
|
else 'Chassis')
|
|
external_ids = {constants.OVN_AGENT_VPN_SB_CFG_KEY: str(row.nb_cfg)}
|
|
self.agent.sb_idl.db_set(
|
|
table, self.agent.chassis,
|
|
('external_ids', external_ids)).execute()
|
|
|
|
|
|
class OvnVpnAgent(service.Service):
|
|
def __init__(self, conf):
|
|
super().__init__()
|
|
self.conf = conf
|
|
vlog.use_python_logger(max_level=config.get_ovn_ovsdb_log_level())
|
|
self._process_monitor = external_process.ProcessMonitor(
|
|
config=self.conf,
|
|
resource_type='ipsec')
|
|
|
|
self.service = vpn_service.VPNService(self)
|
|
self.device_drivers = self.service.load_device_drivers(self.conf.host)
|
|
|
|
def _load_config(self):
|
|
self.chassis = self._get_own_chassis_name()
|
|
try:
|
|
self.chassis_id = uuid.UUID(self.chassis)
|
|
except ValueError:
|
|
# OVS system-id could be a non UUID formatted string.
|
|
self.chassis_id = uuid.uuid5(OVN_VPNAGENT_UUID_NAMESPACE,
|
|
self.chassis)
|
|
LOG.debug("Loaded chassis name %s (UUID: %s).",
|
|
self.chassis, self.chassis_id)
|
|
|
|
def start(self):
|
|
super().start()
|
|
|
|
self.ovs_idl = ovsdb.VPNAgentOvsIdl().start()
|
|
self._load_config()
|
|
|
|
tables = ('SB_Global', 'Chassis')
|
|
events = (SbGlobalUpdateEvent(self), )
|
|
# TODO(lucasagomes): Remove this in the future. Try to register
|
|
# the Chassis_Private table, if not present, fallback to the normal
|
|
# Chassis table.
|
|
# Open the connection to OVN SB database.
|
|
self.has_chassis_private = False
|
|
try:
|
|
self.sb_idl = ovsdb.VPNAgentOvnSbIdl(
|
|
chassis=self.chassis, tables=tables + ('Chassis_Private', ),
|
|
events=events + (ChassisPrivateCreateEvent(self), )).start()
|
|
self.has_chassis_private = True
|
|
except AssertionError:
|
|
self.sb_idl = ovsdb.VPNAgentOvnSbIdl(
|
|
chassis=self.chassis, tables=tables,
|
|
events=events + (ChassisCreateEvent(self), )).start()
|
|
|
|
# Register the agent with its corresponding Chassis
|
|
self.register_vpn_agent()
|
|
|
|
# Do the initial sync.
|
|
self.sync()
|
|
|
|
def sync(self):
|
|
for driver in self.device_drivers:
|
|
driver.sync(driver.context, [])
|
|
|
|
@ovn_utils.retry()
|
|
def register_vpn_agent(self):
|
|
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
|
|
# it's already set.
|
|
table = ('Chassis_Private' if self.has_chassis_private else 'Chassis')
|
|
# Generate unique, but consistent vpn agent id for chassis name
|
|
agent_id = uuid.uuid5(self.chassis_id, 'vpn_agent')
|
|
ext_ids = {constants.OVN_AGENT_VPN_ID_KEY: str(agent_id)}
|
|
self.sb_idl.db_add(table, self.chassis, 'external_ids',
|
|
ext_ids).execute(check_error=True)
|
|
|
|
def _get_own_chassis_name(self):
|
|
"""Return the external_ids:system-id value of the Open_vSwitch table.
|
|
|
|
As long as ovn-controller is running on this node, the key is
|
|
guaranteed to exist and will include the chassis name.
|
|
"""
|
|
ext_ids = self.ovs_idl.db_get(
|
|
'Open_vSwitch', '.', 'external_ids').execute()
|
|
return ext_ids['system-id']
|