From 551ba73aa49c070f95ba3fd6461ab85c5f2c7bd2 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 --- .../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 10cdc3f031c..160ac4026dd 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 @@ -922,3 +924,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 fbf0134afe3..e06334e626c 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 @@ -828,6 +828,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): @@ -948,3 +952,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)