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:
parent
6f7aed7098
commit
02a7b2b7a8
|
@ -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
|
||||
"""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue