From 99b712b39707189ae92110afd58eb6a5796e9220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elvira=20Garc=C3=ADa?= Date: Mon, 15 May 2023 01:24:27 +0200 Subject: [PATCH] [stable-only][ovn] Fix ovsdbapp db_set command for stable branches The lack of an if_exists on the db_set function in ovsdbapp causes the network log object deletion to not be a asynchronous operation on the ML2/OVN plugin. The if_exists was recently added to ovsdbapp, but since that is an API change[0] it will not be backported to older branches, so this patch overrides the function. This way we avoid the possibility of getting errors when concurrently making operations with security groups and network log objects. Closes-bug: #2019887 [0] https://review.opendev.org/c/openstack/ovsdbapp/+/880687 Change-Id: I628591da75c1cc367076f5a3051ca3c1e131d216 (cherry picked from commit 551ba73aa49c070f95ba3fd6461ab85c5f2c7bd2) --- .../drivers/ovn/mech_driver/ovsdb/commands.py | 34 +++++++++++++++++++ .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 8 +++++ 2 files changed, 42 insertions(+) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py index a1e29e6e98b..2cea09298af 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py @@ -11,6 +11,8 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +import collections +from collections import abc from oslo_utils import timeutils from ovsdbapp.backend.ovs_idl import command @@ -894,3 +896,35 @@ class UnsetLSwitchPortToVirtualTypeCommand(command.BaseCommand): virtual_parents) setattr(lsp, 'options', options) + + +class DbSetCommand(command.BaseCommand): + def __init__(self, api, table, record, *col_values, if_exists=False, + **columns): + super().__init__(api) + self.table = table + self.record = record + self.col_values = col_values or columns.items() + self.if_exists = if_exists + + def run_idl(self, txn): + try: + record = self.api.lookup(self.table, self.record) + except idlutils.RowNotFound: + if self.if_exists: + return + raise + + for col, val in self.col_values: + if isinstance(val, abc.Mapping): + if isinstance(val, collections.OrderedDict): + val = dict(val) + existing = getattr(record, col, {}) + existing.update(val) + val = existing + # Since we are updating certain keys and leaving existing keys + # but rewriting the whole external_ids column, we must verify() + record.verify(col) + # For non-map columns, we unconditionally overwrite the values that + # exist, so prior state doesn't matter and we don't need verify() + self.set_column(record, col, val) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index c8fa9bb8c35..bf516620929 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -811,6 +811,10 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend): def update_lb_external_ids(self, lb_name, values, if_exists=True): return cmd.UpdateLbExternalIds(self, lb_name, values, if_exists) + def db_set(self, table, record, *col_values, if_exists=True, **columns): + return cmd.DbSetCommand(self, table, record, *col_values, + if_exists=if_exists, **columns) + class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): def __init__(self, connection): @@ -930,3 +934,7 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): # and just start using chassis objects so db_find_rows could be used rows = self.db_list_rows('Port_Binding').execute(check_error=True) return [r for r in rows if r.chassis and r.chassis[0].name == chassis] + + def db_set(self, table, record, *col_values, if_exists=True, **columns): + return cmd.DbSetCommand(self, table, record, *col_values, + if_exists=if_exists, **columns)