Browse Source

Merge "Handle events in separate IDL instance"

tags/7.0.0.0b1
Zuul 10 months ago
committed by Gerrit Code Review
parent
commit
cb1d0d91d8
3 changed files with 100 additions and 20 deletions
  1. +33
    -18
      networking_ovn/octavia/ovn_driver.py
  2. +1
    -0
      networking_ovn/tests/functional/octavia/test_ovn_driver.py
  3. +66
    -2
      networking_ovn/tests/unit/octavia/test_ovn_driver.py

+ 33
- 18
networking_ovn/octavia/ovn_driver.py View File

@@ -29,7 +29,6 @@ from ovs.stream import Stream
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.schema.ovn_northbound import impl_idl as idl_ovn
from six.moves import queue as Queue
from stevedore import driver
import tenacity
@@ -38,6 +37,7 @@ from networking_ovn._i18n import _
from networking_ovn.common import config as ovn_cfg
from networking_ovn.common import constants as ovn_const
from networking_ovn.common import utils as ovn_utils
from networking_ovn.ovsdb import impl_idl_ovn
from networking_ovn.ovsdb import ovsdb_monitor

CONF = cfg.CONF # Gets Octavia Conf as it runs under o-api domain
@@ -81,6 +81,7 @@ OVN_NATIVE_LB_PROTOCOLS = [constants.PROTOCOL_TCP,
constants.PROTOCOL_UDP, ]
OVN_NATIVE_LB_ALGORITHMS = [constants.LB_ALGORITHM_ROUND_ROBIN, ]
EXCEPTION_MSG = "Exception occurred during %s"
OVN_EVENT_LOCK_NAME = "neutron_ovn_octavia_event_lock"


def get_network_driver():
@@ -140,20 +141,22 @@ class LogicalSwitchPortUpdateEvent(row_event.RowEvent):


class OvnNbIdlForLb(ovsdb_monitor.OvnIdl):

SCHEMA = "OVN_Northbound"
TABLES = ('Logical_Switch', 'Load_Balancer', 'Logical_Router',
'Logical_Switch_Port', 'Logical_Router_Port',
'Gateway_Chassis')

def __init__(self):
self.tables = ('Logical_Switch', 'Load_Balancer', 'Logical_Router',
'Logical_Switch_Port', 'Logical_Router_Port',
'Gateway_Chassis')
def __init__(self, event_lock_name=None):
self.conn_string = ovn_cfg.get_ovn_nb_connection()
helper = self._get_ovsdb_helper(self.conn_string)
for table in self.tables:
for table in OvnNbIdlForLb.TABLES:
helper.register_table(table)
super(OvnNbIdlForLb, self).__init__(
driver=None, remote=self.conn_string, schema=helper)
self.event_lock_name = "neutron_ovn_octavia_event_lock"
self.event_lock_name = event_lock_name
if self.event_lock_name:
self.set_lock(self.event_lock_name)
atexit.register(self.stop)

@tenacity.retry(
wait=tenacity.wait_exponential(max=180),
@@ -164,11 +167,12 @@ class OvnNbIdlForLb(ovsdb_monitor.OvnIdl):
def start(self):
self.conn = connection.Connection(
self, timeout=ovn_cfg.get_ovn_ovsdb_timeout())
return idl_ovn.OvnNbApiIdlImpl(self.conn)
return impl_idl_ovn.OvsdbNbOvnIdl(self.conn)

def stop(self):
# Close the running connection
if not self.conn.stop(timeout=ovn_cfg.get_ovn_ovsdb_timeout()):
# Close the running connection if it has been initalized
if ((hasattr(self, 'conn') and not
self.conn.stop(timeout=ovn_cfg.get_ovn_ovsdb_timeout()))):
LOG.debug("Connection terminated to OvnNb "
"but a thread is still alive")
# complete the shutdown for the event handler
@@ -179,6 +183,8 @@ class OvnNbIdlForLb(ovsdb_monitor.OvnIdl):

class OvnProviderHelper(object):

ovn_nbdb_api_for_events = None
ovn_nb_idl_for_events = None
ovn_nbdb_api = None

def __init__(self):
@@ -189,6 +195,8 @@ class OvnProviderHelper(object):
self._octavia_driver_lib = o_driver_lib.DriverLibrary()
self._check_and_set_ssl_files()
self._init_lb_actions()
self.events = [LogicalRouterPortEvent(self),
LogicalSwitchPortUpdateEvent(self)]
self.start()

def _init_lb_actions(self):
@@ -229,18 +237,24 @@ class OvnProviderHelper(object):
Stream.ssl_set_ca_cert_file(ca_cert_file)

def start(self):
self.ovn_nb_idl_for_lb = OvnNbIdlForLb()
# NOTE(mjozefcz): This API is only for handling octavia API requests.
if not OvnProviderHelper.ovn_nbdb_api:
OvnProviderHelper.ovn_nbdb_api = self.ovn_nb_idl_for_lb.start()
self.events = [LogicalRouterPortEvent(self),
LogicalSwitchPortUpdateEvent(self)]
self.ovn_nb_idl_for_lb.notify_handler.watch_events(self.events)
OvnProviderHelper.ovn_nbdb_api = OvnNbIdlForLb().start()

# NOTE(mjozefcz): This API is only for handling OVSDB events!
if not OvnProviderHelper.ovn_nbdb_api_for_events:
OvnProviderHelper.ovn_nb_idl_for_events = OvnNbIdlForLb(
event_lock_name=OVN_EVENT_LOCK_NAME)
(OvnProviderHelper.ovn_nb_idl_for_events.notify_handler.
watch_events(self.events))
OvnProviderHelper.ovn_nbdb_api_for_events = (
OvnProviderHelper.ovn_nb_idl_for_events.start())
self.helper_thread.start()

def shutdown(self):
self.requests.put({'type': REQ_TYPE_EXIT})
self.helper_thread.join()
self.ovn_nb_idl_for_lb.notify_handler.unwatch_events(self.events)
self.ovn_nb_idl_for_events.notify_handler.unwatch_events(self.events)

@staticmethod
def _map_val(row, col, key):
@@ -436,7 +450,8 @@ class OvnProviderHelper(object):
return self.ovn_nbdb_api.lookup('Load_Balancer', lb_id)

def _find_ovn_lb_with_pool_key(self, pool_key):
lbs = self.ovn_nbdb_api.db_list_rows('Load_Balancer').execute()
lbs = self.ovn_nbdb_api.db_list_rows(
'Load_Balancer').execute(check_error=True)
for lb in lbs:
if pool_key in lb.external_ids:
return lb


+ 1
- 0
networking_ovn/tests/functional/octavia/test_ovn_driver.py View File

@@ -41,6 +41,7 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
# use the old object.
idl_ovn.OvnNbApiIdlImpl.ovsdb_connection = None
ovn_driver.OvnProviderHelper.ovn_nbdb_api = None
ovn_driver.OvnProviderHelper.ovn_nbdb_api_for_events = None
# TODO(mjozefcz): Use octavia listeners to provide needed
# sockets and modify tests in order to verify if fake
# listener (status) has received valid value.


+ 66
- 2
networking_ovn/tests/unit/octavia/test_ovn_driver.py View File

@@ -11,6 +11,8 @@
# License for the specific language governing permissions and limitations
# under the License.
#
import os

import mock
from neutron.tests import base
from octavia_lib.api.drivers import data_models
@@ -18,12 +20,18 @@ from octavia_lib.api.drivers import driver_lib
from octavia_lib.api.drivers import exceptions
from octavia_lib.common import constants
from oslo_utils import uuidutils
from ovs.db import idl as ovs_idl
from ovsdbapp.backend.ovs_idl import idlutils

from networking_ovn.common import constants as ovn_const
from networking_ovn.octavia import ovn_driver
from networking_ovn.tests.unit import fakes

basedir = os.path.dirname(os.path.abspath(__file__))
schema_files = {
'OVN_Northbound': os.path.join(basedir.replace('octavia', 'ovsdb'),
'schemas', 'ovn-nb.ovsschema')}


# TODO(mjozefcz): Move it to unittest fakes.
class MockedLB(data_models.LoadBalancer):
@@ -37,6 +45,50 @@ class MockedLB(data_models.LoadBalancer):
return self.__sizeof__()


class TestOvnNbIdlForLb(base.BaseTestCase):
def setUp(self):
super(TestOvnNbIdlForLb, self).setUp()
self.mock_gsh = mock.patch.object(
idlutils, 'get_schema_helper',
side_effect=lambda x, y: ovs_idl.SchemaHelper(
location=schema_files['OVN_Northbound'])).start()
self.idl = ovn_driver.OvnNbIdlForLb()

def test__get_ovsdb_helper(self):
self.mock_gsh.reset_mock()
self.idl._get_ovsdb_helper('foo')
self.mock_gsh.assert_called_once_with('foo', 'OVN_Northbound')

def test_start(self):
with mock.patch('ovsdbapp.backend.ovs_idl.connection.Connection',
side_effect=lambda x, timeout: mock.Mock()):
idl1 = ovn_driver.OvnNbIdlForLb()
ret1 = idl1.start()
id1 = id(ret1.ovsdb_connection)
idl2 = ovn_driver.OvnNbIdlForLb()
ret2 = idl2.start()
id2 = id(ret2.ovsdb_connection)
self.assertNotEqual(id1, id2)

@mock.patch('ovsdbapp.backend.ovs_idl.connection.Connection')
def test_stop(self, mock_conn):
mock_conn.stop.return_value = False
with (
mock.patch.object(
self.idl.notify_handler, 'shutdown')) as mock_notify, (
mock.patch.object(self.idl, 'close')) as mock_close:
self.idl.start()
self.idl.stop()
mock_notify.assert_called_once_with()
mock_close.assert_called_once_with()

def test_setlock(self):
with mock.patch.object(ovn_driver.OvnNbIdlForLb,
'set_lock') as set_lock:
self.idl = ovn_driver.OvnNbIdlForLb(event_lock_name='foo')
set_lock.assert_called_once_with('foo')


class TestOvnOctaviaBase(base.BaseTestCase):

def setUp(self):
@@ -1287,12 +1339,24 @@ class TestOvnProviderHelper(TestOvnOctaviaBase):
'172.26.21.20:80': '192.168.2.149:1010'}))]
self.helper.ovn_nbdb_api.assert_has_calls(calls)

def test_single_ovsdb_connection(self):
@mock.patch.object(ovn_driver, 'atexit')
def test_ovsdb_connections(self, mock_atexit):
ovn_driver.OvnProviderHelper.ovn_nbdb_api = None
ovn_driver.OvnProviderHelper.ovn_nbdb_api_for_events = None
prov_helper1 = ovn_driver.OvnProviderHelper()
prov_helper2 = ovn_driver.OvnProviderHelper()
self.assertIs(prov_helper1.ovn_nbdb_api, prov_helper2.ovn_nbdb_api)
# One connection for API requests
self.assertIs(prov_helper1.ovn_nbdb_api,
prov_helper2.ovn_nbdb_api)
# One connection to handle events
self.assertIs(prov_helper1.ovn_nbdb_api_for_events,
prov_helper2.ovn_nbdb_api_for_events)
prov_helper2.shutdown()
prov_helper1.shutdown()
# Assert at_exit calls
mock_atexit.assert_has_calls([
mock.call.register(prov_helper1.shutdown),
mock.call.register(prov_helper2.shutdown)])

def test_create_vip_port_vip_selected(self):
expected_dict = {


Loading…
Cancel
Save