TAAS: Add commands for creating Mirrors

Note: mirrors are available only from OVN v22.12.0

Change-Id: I8d70d24beefb2813c977e9f89aa0535c3494ee85
Related-Bug: #2015471
This commit is contained in:
elajkat 2023-07-28 09:42:26 +02:00
parent 6f7aed7098
commit 02a7b2b7a8
4 changed files with 331 additions and 0 deletions

View File

@ -1470,3 +1470,71 @@ class API(api.API, metaclass=abc.ABCMeta):
:type uuid: uuid.UUID
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def mirror_get(self, uuid):
"""Get the Mirror entry"""
@abc.abstractmethod
def mirror_del(self, mirror):
"""Delete a Mirror"""
@abc.abstractmethod
def mirror_add(self, name, mirror_type, index, direction_filter, dest,
external_ids=None,
may_exist=False):
"""Create a Mirror entry
:param name: Name of the Mirror to create.
:type name: str
:param mirror_type: The type of the mirroring can be gre or erspan.
:type mirror_type: str
:param index: The index filed will be used for the Index field in
ERSPAN header as decimal, and as hexadecimal value in
the SpanID field, for GRE mirrors it will be the Key
field.
:type index: int
:param direction_filter: The direction of the traffic to be mirrored,
can be from-lport and to-lprt.
:type direction_filter: str
:param dest: The destination IP address of the mirroring.
:type dest: str
:param external_ids: Values to be added as external_id pairs.
:type external_ids: Optional[Dict[str,str]]
:param may_exist: If True, update any existing Mirror entry if it
already exists. Default is False which will raise
an error if a Mirror entry with same logical_port,
sink pair already exists.
:type may_exist: Optional[bool]
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def lsp_attach_mirror(self, port, mirror, may_exist=False):
"""Attaches an lsp to the given mirror
:param port: the id of the lsp
:type port: str
:param mirror: the name or ID of the mirror.
:type mirror: str
:param may_exist: If True, don't fail if the mirror_rule already
exists.
:type may_exist: Optional[bool]
:returns: :class:`Command` with RowView result
"""
@abc.abstractmethod
def lsp_detach_mirror(self, port, mirror, if_exist=False):
"""Detaches an lsp from the given mirror
:param port: the id of the lsp
:type port: str
:param mirror: the name or ID of the mirror
:type mirror: str
:param if_exist: If True, don't fail if the mirror_rules entry
doesn't exist.
:type if_exist: Optional[bool]
:returns: :class:`Command` with RowView result
"""

View File

@ -1137,6 +1137,113 @@ class BFDGetCommand(cmd.BaseGetRowCommand):
table = 'BFD'
class MirrorAddCommand(cmd.AddCommand):
# cmd.AddCommand uses self.table_name, other base commands use self.table
table_name = 'Mirror'
def __init__(self, api, name, mirror_type, index, direction_filter, dest,
external_ids=None, may_exist=False):
self.name = name
self.direction_filter = direction_filter
self.mirror_type = mirror_type
if mirror_type != 'local':
self.dest = str(netaddr.IPAddress(dest))
else:
self.dest = dest
self.index = index
super().__init__(api)
self.columns = {
'name': name,
'filter': direction_filter,
'sink': dest,
'type': mirror_type,
'index': index,
'external_ids': external_ids or {},
}
self.may_exist = may_exist
def run_idl(self, txn):
try:
mirror_result = self.api.lookup(self.table_name, self.name)
self.result = rowview.RowView(mirror_result)
if self.may_exist:
self.set_columns(mirror_result, **self.columns)
self.result = rowview.RowView(mirror_result)
return
raise RuntimeError("Mirror %s already exists" % self.name)
except idlutils.RowNotFound:
pass
mirror = txn.insert(self.api.tables[self.table_name])
mirror.name = self.name
mirror.type = self.mirror_type
mirror.filter = self.direction_filter
mirror.sink = self.dest
mirror.index = self.index
self.set_columns(mirror, **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 = mirror.uuid
class MirrorDelCommand(cmd.DbDestroyCommand):
table = 'Mirror'
def __init__(self, api, record):
super().__init__(api, self.table, record)
class MirrorGetCommand(cmd.BaseGetRowCommand):
table = 'Mirror'
class LspAttachMirror(cmd.BaseCommand):
def __init__(self, api, port, mirror, may_exist=False):
super().__init__(api)
self.port = port
self.mirror = mirror
self.may_exist = may_exist
def run_idl(self, txn):
try:
lsp = self.api.lookup('Logical_Switch_Port', self.port)
mirror = self.api.lookup('Mirror', self.mirror)
if mirror in lsp.mirror_rules and not self.may_exist:
msg = "Mirror Rule %s is already set on LSP %s" % (self.mirror,
self.port)
raise RuntimeError(msg)
lsp.addvalue('mirror_rules', self.mirror)
except idlutils.RowNotFound as e:
raise RuntimeError("LSP %s not found" % self.port) from e
class LspDetachMirror(cmd.BaseCommand):
def __init__(self, api, port, mirror, if_exist=False):
super().__init__(api)
self.port = port
self.mirror = mirror
self.if_exist = if_exist
def run_idl(self, txn):
try:
lsp = self.api.lookup('Logical_Switch_Port', self.port)
mirror = self.api.lookup('Mirror', self.mirror)
if mirror not in lsp.mirror_rules and not self.if_exist:
msg = "Mirror Rule %s doesn't exist on LSP %s" % (self.mirror,
self.port)
raise RuntimeError(msg)
lsp.delvalue('mirror_rules', self.mirror)
except idlutils.RowNotFound as e:
if self.if_exists:
return
msg = "LSP %s doesn't exist" % self.port
raise RuntimeError(msg) from e
class LrRouteAddCommand(cmd.BaseCommand):
def __init__(self, api, router, prefix, nexthop, port=None,
policy='dst-ip', may_exist=False, ecmp=False):

View File

@ -432,3 +432,25 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def bfd_get(self, uuid):
return cmd.BFDGetCommand(self, uuid)
def mirror_get(self, uuid):
return cmd.MirrorGetCommand(self, uuid)
def mirror_del(self, mirror):
return cmd.MirrorDelCommand(self, mirror)
def mirror_add(self, name, mirror_type, index, direction_filter,
dest, external_ids=None, may_exist=False):
return cmd.MirrorAddCommand(self, name=name,
mirror_type=mirror_type,
index=index,
direction_filter=direction_filter,
dest=dest,
external_ids=external_ids,
may_exist=may_exist)
def lsp_attach_mirror(self, port, mirror, may_exist=False):
return cmd.LspAttachMirror(self, port, mirror, may_exist)
def lsp_detach_mirror(self, port, mirror, if_exist=False):
return cmd.LspDetachMirror(self, port, mirror, if_exist)

View File

@ -2532,3 +2532,137 @@ class TestBFDOps(OvnNorthboundTest):
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)
class TestMirrorOps(OvnNorthboundTest):
def setUp(self):
super(TestMirrorOps, self).setUp()
self.table = self.api.tables['Mirror']
self.switch = self.useFixture(
fixtures.LogicalSwitchFixture(self.api)).obj
lsp_add_cmd = self.api.lsp_add(self.switch.uuid, 'testport')
with self.api.transaction(check_error=True) as txn:
txn.add(lsp_add_cmd)
self.port_uuid = lsp_add_cmd.result.uuid
def _mirror_add(self, name=None, direction_filter='to-lport',
dest='10.11.1.1', mirror_type='gre', index=42, **kwargs):
if not name:
name = utils.get_rand_name()
cmd = self.api.mirror_add(name, mirror_type, index, direction_filter,
dest, **kwargs)
row = cmd.execute(check_error=True)
self.assertEqual(cmd.name, row.name)
self.assertEqual(cmd.direction_filter, row.filter)
self.assertEqual(cmd.dest, row.sink)
self.assertEqual(cmd.mirror_type, row.type)
self.assertEqual(cmd.index, row.index)
return idlutils.frozen_row(row)
def test_mirror_addx(self):
self._mirror_add(dest='10.13.1.1')
def test_mirror_add_duplicate(self):
name = utils.get_rand_name()
cmd = self.api.mirror_add(name, 'gre', 100, 'from-lport',
'192.169.1.1')
cmd.execute(check_error=True)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
def test_mirror_add_may_exist_no_change(self):
name = utils.get_rand_name()
mirror1 = self._mirror_add(name=name, dest='10.18.1.1')
mirror2 = self._mirror_add(name=name, dest='10.18.1.1',
may_exist=True)
self.assertEqual(mirror1, mirror2)
def test_mirror_add_may_exist_change(self):
name = utils.get_rand_name()
mirror1 = self._mirror_add(name=name, dest='10.12.1.0')
mirror2 = self._mirror_add(
name=name, direction_filter='from-lport', dest='10.12.1.0',
mirror_type='gre', index=100, may_exist=True,
)
self.assertNotEqual(mirror1, mirror2)
self.assertEqual(mirror1.uuid, mirror2.uuid)
def test_mirror_del(self):
name = utils.get_rand_name()
mirror1 = self._mirror_add(name=name, dest='10.14.1.0')
self.assertIn(mirror1.uuid, self.table.rows)
self.api.mirror_del(mirror1.uuid).execute(check_error=True)
self.assertNotIn(mirror1.uuid, self.table.rows)
def test_mirror_get(self):
name = utils.get_rand_name()
mirror1 = self.api.mirror_add(name, 'gre', 100, 'from-lport',
'10.15.1.1').execute(check_error=True)
mirror2 = self.api.mirror_get(mirror1.uuid).execute(check_error=True)
self.assertEqual(mirror1, mirror2)
def test_lsp_attach_detach_mirror(self):
mirror = self._mirror_add(name='my_mirror')
self.api.lsp_attach_mirror(
self.port_uuid, mirror.uuid).execute(check_error=True)
port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
self.assertEqual(1, len(port.mirror_rules))
mir_rule = self.api.lookup('Mirror', port.mirror_rules[0].uuid)
self.assertEqual(mirror.uuid, mir_rule.uuid)
self.api.lsp_detach_mirror(
self.port_uuid, mirror.uuid).execute(check_error=True)
port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
self.assertEqual(0, len(port.mirror_rules))
def test_lsp_attach_detach_may_exist(self):
mirror1 = self._mirror_add(name='mirror1')
self.api.lsp_attach_mirror(
self.port_uuid, mirror1.uuid).execute(check_error=True)
mirror2 = self._mirror_add(name='mirror2', dest='10.17.1.0')
# Try to attach a mirror to a port which already has mirror_rule
# attached
failing_cmd = self.api.lsp_attach_mirror(
self.port_uuid, mirror1.uuid,
may_exist=False)
self.assertRaises(
RuntimeError,
failing_cmd.execute,
check_error=True
)
self.api.lsp_attach_mirror(
self.port_uuid, mirror2.uuid,
may_exist=True).execute(check_error=True)
check_res = self.api.lsp_get(self.port_uuid).execute(check_error=True)
rule_on_lsp = False
for m_rule in check_res.mirror_rules:
if mirror2.uuid == m_rule.uuid:
rule_on_lsp = True
self.assertTrue(rule_on_lsp)
self.api.lsp_detach_mirror(
self.port_uuid, mirror2.uuid).execute(check_error=True)
port = self.api.lsp_get(self.port_uuid).execute(check_error=True)
self.assertEqual(1, len(port.mirror_rules))
self.assertEqual(mirror1.uuid, port.mirror_rules[0].uuid)
# Try to detach a rule that is already detached
failing_cmd = self.api.lsp_detach_mirror(
self.port_uuid, mirror2.uuid)
self.assertRaises(
RuntimeError,
failing_cmd.execute,
check_error=True
)
# detach with if_exist=True, and check the result, to be as previously
self.api.lsp_detach_mirror(
self.port_uuid, mirror2.uuid,
if_exist=True).execute(check_error=True)
self.assertEqual(1, len(port.mirror_rules))
self.assertEqual(mirror1.uuid, port.mirror_rules[0].uuid)