Add QoSDelExtIdCommand

This command removes all QoS rules matching a set of keys and
expected values passed in the command instantiation. No exception
is thrown if no QoS is found with the required external IDs.

For example, this will be used in Neutron to delete all QoS
registers related to a floating IP, searching for
neutron.common.ovn.constants.OVN_FIP_EXT_ID_KEY key and
the floating IP register UUID.

Change-Id: I0eb7151eabaf40232f79797b2775c9bb7891013f
Related-Bug: #1877408
This commit is contained in:
Rodolfo Alonso Hernandez 2020-05-13 17:30:36 +00:00
parent 7736bac7dc
commit 08605e94a4
4 changed files with 105 additions and 0 deletions

View File

@ -249,6 +249,19 @@ class API(api.API, metaclass=abc.ABCMeta):
:returns: :class:`Command` with RowView list result
"""
@abc.abstractmethod
def qos_del_ext_ids(self, lswitch_name, external_ids, if_exists=True):
"""Delete all QoS rules related to a floating IP.
:param lswitch_name: The unique name of the logical switch
:type lswitch_name: string
:param external_ids: Pairs of key/value to find in the "external_ids"
:type external_ids: dict
:param if_exists: Do not fail if the Logical_Switch row does not exist
:type if_exists: bool
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def lsp_add(self, switch, port, parent_name=None, tag=None,
may_exist=False, **columns):

View File

@ -303,6 +303,31 @@ class QoSListCommand(cmd.ReadOnlyCommand):
self.result = [rowview.RowView(row) for row in ls.qos_rules]
class QoSDelExtIdCommand(cmd.BaseCommand):
def __init__(self, api, lswitch, external_ids, if_exists=False):
if not external_ids:
raise TypeError('external_ids dictionary cannot be empty')
super(QoSDelExtIdCommand, self).__init__(api)
self.lswitch = lswitch
self.external_ids = external_ids
self.if_exists = if_exists
def run_idl(self, txn):
try:
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.lswitch)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = 'Logical Switch %s does not exist' % self.lswitch
raise RuntimeError(msg)
for qos in lswitch.qos_rules:
if self.external_ids.items() <= qos.external_ids.items():
lswitch.delvalue('qos_rules', qos)
qos.delete()
class LspAddCommand(cmd.AddCommand):
table_name = 'Logical_Switch_Port'

View File

@ -89,6 +89,10 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
def qos_list(self, switch):
return cmd.QoSListCommand(self, switch)
def qos_del_ext_ids(self, lswitch_name, external_ids, if_exists=True):
return cmd.QoSDelExtIdCommand(self, lswitch_name, external_ids,
if_exists=if_exists)
def lsp_add(self, switch, port, parent_name=None, tag=None,
may_exist=False, **columns):
return cmd.LspAddCommand(self, switch, port, parent_name, tag,

View File

@ -318,6 +318,69 @@ class TestQoSOps(OvnNorthboundTest):
self.assertIn(r1, qos_rules)
self.assertIn(r2, qos_rules)
def _create_fip_qoses(self):
ext_ids_1 = {'key1': 'value1', 'key2': 'value2'}
self.qos_1 = self._qos_add('from-lport', 0, 'output == "fake_port1"',
dscp=11, external_ids=ext_ids_1)
ext_ids_2 = {'key3': 'value3', 'key4': 'value4'}
self.qos_2 = self._qos_add('from-lport', 1, 'output == "fake_port2"',
dscp=11, external_ids=ext_ids_2)
self.qos_3 = self._qos_add('from-lport', 2, 'output == "fake_port3"',
dscp=10)
def test_qos_delete_external_ids(self):
self._create_fip_qoses()
self.api.qos_del_ext_ids(self.switch.name,
{'key1': 'value1'}).execute(check_error=True)
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_2, self.qos_3], qos_rules)
self.api.qos_del_ext_ids(
self.switch.name,
{'key3': 'value3', 'key4': 'value4'}).execute(check_error=True)
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_3], qos_rules)
def test_qos_delete_external_ids_wrong_keys_or_values(self):
self._create_fip_qoses()
self.api.qos_del_ext_ids(self.switch.name,
{'key_z': 'value1'}).execute(check_error=True)
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_1, self.qos_2, self.qos_3], qos_rules)
self.api.qos_del_ext_ids(self.switch.name,
{'key1': 'value_z'}).execute(check_error=True)
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_1, self.qos_2, self.qos_3], qos_rules)
self.api.qos_del_ext_ids(
self.switch.name,
{'key3': 'value3', 'key4': 'value_z'}).execute(check_error=True)
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_1, self.qos_2, self.qos_3], qos_rules)
def test_qos_delete_external_ids_empty_dict(self):
self.assertRaises(TypeError, self.api.qos_del_ext_ids,
self.switch.name, {})
def test_qos_delete_external_ids_if_exists(self):
self._create_fip_qoses()
cmd = self.api.qos_del_ext_ids('wrong_ls_name',
{'key1': 'value1'}, if_exists=False)
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
self.api.qos_del_ext_ids('wrong_ls_name',
{'key1': 'value1'}).execute(check_error=True)
# No qos rule has been deleted from the correct logical switch.
qos_rules = self.api.qos_list(
self.switch.uuid).execute(check_error=True)
self.assertCountEqual([self.qos_1, self.qos_2, self.qos_3], qos_rules)
class TestLspOps(OvnNorthboundTest):
def setUp(self):