From 08605e94a411d3dabcff63f40b71d6e34bb3ed72 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 13 May 2020 17:30:36 +0000 Subject: [PATCH] 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 --- ovsdbapp/schema/ovn_northbound/api.py | 13 ++++ ovsdbapp/schema/ovn_northbound/commands.py | 25 ++++++++ ovsdbapp/schema/ovn_northbound/impl_idl.py | 4 ++ .../schema/ovn_northbound/test_impl_idl.py | 63 +++++++++++++++++++ 4 files changed, 105 insertions(+) diff --git a/ovsdbapp/schema/ovn_northbound/api.py b/ovsdbapp/schema/ovn_northbound/api.py index 1c2b65ac..f5e7b706 100644 --- a/ovsdbapp/schema/ovn_northbound/api.py +++ b/ovsdbapp/schema/ovn_northbound/api.py @@ -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): diff --git a/ovsdbapp/schema/ovn_northbound/commands.py b/ovsdbapp/schema/ovn_northbound/commands.py index 66fd30ed..dee9d102 100644 --- a/ovsdbapp/schema/ovn_northbound/commands.py +++ b/ovsdbapp/schema/ovn_northbound/commands.py @@ -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' diff --git a/ovsdbapp/schema/ovn_northbound/impl_idl.py b/ovsdbapp/schema/ovn_northbound/impl_idl.py index 483a8ec1..a1bc7919 100644 --- a/ovsdbapp/schema/ovn_northbound/impl_idl.py +++ b/ovsdbapp/schema/ovn_northbound/impl_idl.py @@ -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, diff --git a/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py b/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py index e3496e81..08222b83 100644 --- a/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py +++ b/ovsdbapp/tests/functional/schema/ovn_northbound/test_impl_idl.py @@ -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):