Merge "Add support for manipulating BFD entries"
This commit is contained in:
commit
e77599280b
|
@ -1397,3 +1397,72 @@ class API(api.API, metaclass=abc.ABCMeta):
|
|||
:type meter: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def bfd_add(self, logical_port, dst_ip, min_tx=None, min_rx=None,
|
||||
detect_mult=None, external_ids=None, options=None,
|
||||
may_exist=False):
|
||||
"""Create a BFD entry
|
||||
|
||||
:param logical_port: Name of logical port where BFD engine should run.
|
||||
:type logical_port: str
|
||||
:param dst_ip: BFD peer IP address.
|
||||
:type dst_ip: str
|
||||
:param min_tx: This is the minimum interval, in milliseconds,
|
||||
that the local system would like to use when
|
||||
transmitting BFD Control packets, less any jitter
|
||||
applied. The value zero is reserved. Default value
|
||||
is 1000 ms.
|
||||
:type min_tx: Optional[int]
|
||||
:param min_rx: This is the minimum interval, in milliseconds,
|
||||
between received BFD Control packets that this
|
||||
system is capable of supporting, less any jitter
|
||||
applied by the sender. If this value is zero, the
|
||||
transmitting system does not want the remote
|
||||
system to send any periodic BFD Control packets.
|
||||
:type min_rx: Optional[int]
|
||||
:param detect_mult: Detection time multiplier. The negotiated
|
||||
transmit interval, multiplied by this value,
|
||||
provides the Detection Time for the receiving
|
||||
system in Asynchronous mode. Default value is 5.
|
||||
:type detect_mult: Optional[int]
|
||||
:param external_ids: Values to be added as external_id pairs.
|
||||
:type external_ids: Optional[Dict[str,str]]
|
||||
:param options: Reserved for future use.
|
||||
:type options: Optional[Dict[str,str]]
|
||||
:param may_exist: If True, update any existing BFD entry if it
|
||||
already exists. Default is False which will raise
|
||||
an error if a BFD entry with same logical_port,
|
||||
dst_ip pair already exists.
|
||||
:type may_exist: Optional[bool]
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def bfd_del(self, uuid):
|
||||
"""Delete a BFD entry
|
||||
|
||||
:param uuid: The uuid of the BFD entry
|
||||
:type uuid: uuid.UUID
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def bfd_find(self, logical_port, dst_ip):
|
||||
"""Find BFD entry.
|
||||
|
||||
:param logical_port: Name of logical port where BFD engine runs.
|
||||
:type logical_port: str
|
||||
:param dst_ip: BFD peer IP address.
|
||||
:type dst_ip: str
|
||||
:returns: :class:`Command` with List[Dict[str,any]] result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def bfd_get(self, uuid):
|
||||
"""Get the BFD entry
|
||||
|
||||
:param uuid: The uuid of the BFD entry
|
||||
:type uuid: uuid.UUID
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
|
|
@ -1045,6 +1045,98 @@ class LrpDelNetworksCommand(_LrpNetworksCommand):
|
|||
lrp.delvalue('networks', network)
|
||||
|
||||
|
||||
class BFDFindCommand(cmd.DbFindCommand):
|
||||
table = 'BFD'
|
||||
|
||||
def __init__(self, api, port, dst_ip):
|
||||
super().__init__(
|
||||
api,
|
||||
self.table,
|
||||
('logical_port', '=', port),
|
||||
('dst_ip', '=', dst_ip),
|
||||
row=True,
|
||||
)
|
||||
|
||||
|
||||
class BFDAddCommand(cmd.AddCommand):
|
||||
# cmd.AddCommand uses self.table_name, other base commands use self.table
|
||||
table_name = 'BFD'
|
||||
|
||||
def __init__(self, api, logical_port, dst_ip, min_tx=None, min_rx=None,
|
||||
detect_mult=None, external_ids=None, options=None,
|
||||
may_exist=False):
|
||||
for attr in ('logical_port', 'dst_ip'):
|
||||
if not isinstance(locals().get(attr), str):
|
||||
raise ValueError("%s must be of type str" % attr)
|
||||
for attr in ('min_tx', 'min_rx', 'detect_mult'):
|
||||
value = locals().get(attr)
|
||||
if value and (not isinstance(value, int) or value < 1):
|
||||
raise ValueError("%s must be of type int and > 0" % attr)
|
||||
for attr in ('external_ids', 'options'):
|
||||
value = locals().get(attr)
|
||||
if value and not isinstance(value, dict):
|
||||
raise ValueError("%s must be of type dict" % attr)
|
||||
super().__init__(api)
|
||||
self.logical_port = logical_port
|
||||
self.dst_ip = dst_ip
|
||||
self.columns = {
|
||||
'logical_port': logical_port,
|
||||
'dst_ip': dst_ip,
|
||||
'min_tx': [min_tx] if min_tx else [],
|
||||
'min_rx': [min_rx] if min_rx else [],
|
||||
'detect_mult': [detect_mult] if detect_mult else [],
|
||||
'external_ids': external_ids or {},
|
||||
'options': options or {},
|
||||
}
|
||||
self.may_exist = may_exist
|
||||
|
||||
def run_idl(self, txn):
|
||||
cmd = BFDFindCommand(self.api, self.logical_port, self.dst_ip)
|
||||
cmd.run_idl(txn)
|
||||
bfd_result = cmd.result
|
||||
if bfd_result:
|
||||
if len(bfd_result) > 1:
|
||||
# With the current database schema, this cannot happen, but
|
||||
# better safe than sorry.
|
||||
raise RuntimeError(
|
||||
"Unexpected duplicates in database for port %s "
|
||||
"and dst_ip %s" % (self.logical_port, self.dst_ip))
|
||||
bfd = bfd_result[0]
|
||||
if self.may_exist:
|
||||
self.set_columns(bfd, **self.columns)
|
||||
# When no changes are made to a record, the parent
|
||||
# `post_commit` method will not be called.
|
||||
#
|
||||
# Ensure consistent return to caller of `Command.execute()`
|
||||
# even when no changes have been applied.
|
||||
self.result = rowview.RowView(bfd)
|
||||
return
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"BFD entry for port %s and dst_ip %s exists" % (
|
||||
self.logical_port, self.dst_ip))
|
||||
bfd = txn.insert(self.api.tables[self.table_name])
|
||||
bfd.logical_port = self.logical_port
|
||||
bfd.dst_ip = self.dst_ip
|
||||
self.set_columns(bfd, **self.columns)
|
||||
# Setting the result to something other than a :class:`rowview.RowView`
|
||||
# or :class:`ovs.db.idl.Row` typed value will make the parent
|
||||
# `post_commit` method retrieve the newly insterted row from IDL and
|
||||
# return that to the caller.
|
||||
self.result = bfd.uuid
|
||||
|
||||
|
||||
class BFDDelCommand(cmd.DbDestroyCommand):
|
||||
table = 'BFD'
|
||||
|
||||
def __init__(self, api, uuid):
|
||||
super().__init__(api, self.table, uuid)
|
||||
|
||||
|
||||
class BFDGetCommand(cmd.BaseGetRowCommand):
|
||||
table = 'BFD'
|
||||
|
||||
|
||||
class LrRouteAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, prefix, nexthop, port=None,
|
||||
policy='dst-ip', may_exist=False):
|
||||
|
|
|
@ -415,3 +415,20 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
|
|||
|
||||
def meter_get(self, meter):
|
||||
return cmd.MeterGetCommand(self, meter)
|
||||
|
||||
def bfd_add(self, logical_port, dst_ip, min_tx=None, min_rx=None,
|
||||
detect_mult=None, external_ids=None, options=None,
|
||||
may_exist=False):
|
||||
return cmd.BFDAddCommand(self, logical_port, dst_ip, min_tx=min_tx,
|
||||
min_rx=min_rx, detect_mult=detect_mult,
|
||||
external_ids=external_ids, options=options,
|
||||
may_exist=may_exist)
|
||||
|
||||
def bfd_del(self, uuid):
|
||||
return cmd.BFDDelCommand(self, uuid)
|
||||
|
||||
def bfd_find(self, logical_port, dst_ip):
|
||||
return cmd.BFDFindCommand(self, logical_port, dst_ip)
|
||||
|
||||
def bfd_get(self, uuid):
|
||||
return cmd.BFDGetCommand(self, uuid)
|
||||
|
|
|
@ -2424,3 +2424,105 @@ class TestMeterOps(OvnNorthboundTest):
|
|||
|
||||
def test_meter_get_name(self):
|
||||
self._meter_get('name')
|
||||
|
||||
|
||||
class TestBFDOps(OvnNorthboundTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBFDOps, self).setUp()
|
||||
self.table = self.api.tables['BFD']
|
||||
|
||||
def _bfd_add(self, *args, **kwargs):
|
||||
cmd = self.api.bfd_add(*args, **kwargs)
|
||||
row = cmd.execute(check_error=True)
|
||||
self.assertEqual(cmd.logical_port, row.logical_port)
|
||||
self.assertEqual(cmd.dst_ip, row.dst_ip)
|
||||
self.assertEqual(cmd.columns['min_tx'] if cmd.columns[
|
||||
'min_tx'] else [], row.min_tx)
|
||||
self.assertEqual(cmd.columns['min_rx'] if cmd.columns[
|
||||
'min_rx'] else [], row.min_rx)
|
||||
self.assertEqual(cmd.columns['detect_mult'] if cmd.columns[
|
||||
'detect_mult'] else [], row.detect_mult)
|
||||
self.assertEqual(cmd.columns['external_ids'] or {}, row.external_ids)
|
||||
self.assertEqual(cmd.columns['options'] or {}, row.options)
|
||||
return idlutils.frozen_row(row)
|
||||
|
||||
def test_bfd_add(self):
|
||||
name = utils.get_rand_name()
|
||||
self._bfd_add(name, name)
|
||||
|
||||
def test_bfd_add_non_defaults(self):
|
||||
name = utils.get_rand_name()
|
||||
self._bfd_add(
|
||||
name,
|
||||
name,
|
||||
min_rx=1,
|
||||
min_tx=2,
|
||||
detect_mult=3,
|
||||
external_ids={'a': 'A'},
|
||||
options={'b': 'B'},
|
||||
may_exist=True,
|
||||
)
|
||||
|
||||
def test_bfd_add_duplicate(self):
|
||||
name = utils.get_rand_name()
|
||||
cmd = self.api.bfd_add(name, name)
|
||||
cmd.execute(check_error=True)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_bfd_add_may_exist_no_change(self):
|
||||
name = utils.get_rand_name()
|
||||
b1 = self._bfd_add(name, name)
|
||||
b2 = self._bfd_add(name, name, may_exist=True)
|
||||
self.assertEqual(b1, b2)
|
||||
|
||||
def test_bfd_add_may_exist_change(self):
|
||||
name = utils.get_rand_name()
|
||||
b1 = self._bfd_add(name, name)
|
||||
b2 = self._bfd_add(
|
||||
name,
|
||||
name,
|
||||
min_rx=11,
|
||||
min_tx=22,
|
||||
detect_mult=33,
|
||||
external_ids={'aa': 'AA'},
|
||||
options={'bb': 'BB'},
|
||||
may_exist=True,
|
||||
)
|
||||
self.assertNotEqual(b1, b2)
|
||||
self.assertEqual(b1.uuid, b2.uuid)
|
||||
|
||||
def test_bfd_del(self):
|
||||
name = utils.get_rand_name()
|
||||
b1 = self._bfd_add(name, name)
|
||||
self.assertIn(b1.uuid, self.table.rows)
|
||||
self.api.bfd_del(b1.uuid).execute(check_error=True)
|
||||
self.assertNotIn(b1.uuid, self.table.rows)
|
||||
|
||||
def test_bfd_find(self):
|
||||
name1 = utils.get_rand_name()
|
||||
name2 = utils.get_rand_name()
|
||||
b1 = self._bfd_add(name1, name1)
|
||||
b2 = self._bfd_add(name1, name2)
|
||||
b3 = self._bfd_add(name2, name1)
|
||||
b4 = self._bfd_add(name2, name2)
|
||||
self.assertIn(b1.uuid, self.table.rows)
|
||||
self.assertIn(b2.uuid, self.table.rows)
|
||||
self.assertIn(b3.uuid, self.table.rows)
|
||||
self.assertIn(b4.uuid, self.table.rows)
|
||||
found = self.api.bfd_find(
|
||||
b1.logical_port,
|
||||
b1.dst_ip).execute(check_error=True)
|
||||
self.assertEqual(1, len(found))
|
||||
self.assertEqual(b1.uuid, found[0].uuid)
|
||||
for col in set(self.api.tables['BFD'].columns.keys()) - set(
|
||||
('_uuid', 'status')):
|
||||
self.assertEqual(
|
||||
getattr(b1, col),
|
||||
getattr(found[0], col))
|
||||
|
||||
def test_bfd_get(self):
|
||||
name = utils.get_rand_name()
|
||||
b1 = self.api.bfd_add(name, name).execute(check_error=True)
|
||||
b2 = self.api.bfd_get(b1.uuid).execute(check_error=True)
|
||||
self.assertEqual(b1, b2)
|
||||
|
|
Loading…
Reference in New Issue