Use reader context for DB queries

In most cases FirewallPluginDb has opened reader/writer contexts for its
db access, but not in all.

Once instance in which this can fail is when the port AFTER_UPDATE
event handler is called. This method might call
get_fwg_attached_to_port() outside of a transaction, which currently
automatically opens one. This will explicitly fail when a transaction
guard is hit with an exception:

RuntimeError: Must not be called in a transaction

To avoid this, we add a reader context to get_fwg_attached_to_port() and
while we're at it we're adding this to all FirewallPluginDb methods that
do a model_query db query - just to be on the safe side.

Change-Id: Iabb9bc6877059bcc46d64b7fc2d0728e6002610b
Closes-Bug: #2114919
This commit is contained in:
Sebastian Lohff
2025-06-16 15:19:17 +02:00
parent ed98cd8a2c
commit 13c9c9a433

View File

@@ -206,18 +206,21 @@ class FirewallPluginDb:
result_filters=_list_firewall_policies_result_filter_hook)
return super().__new__(cls, *args, **kwargs)
@db_api.CONTEXT_READER
def _get_firewall_group(self, context, id):
try:
return model_query.get_by_id(context, FirewallGroup, id)
except exc.NoResultFound:
raise f_exc.FirewallGroupNotFound(firewall_id=id)
@db_api.CONTEXT_READER
def _get_firewall_policy(self, context, id):
try:
return model_query.get_by_id(context, FirewallPolicy, id)
except exc.NoResultFound:
raise f_exc.FirewallPolicyNotFound(firewall_policy_id=id)
@db_api.CONTEXT_READER
def _get_firewall_rule(self, context, id):
try:
return model_query.get_by_id(context, FirewallRuleV2, id)
@@ -612,6 +615,7 @@ class FirewallPluginDb:
policies = self.get_policies_with_rule(context, id) or None
return self._make_firewall_rule_dict(fwr, fields, policies=policies)
@db_api.CONTEXT_READER
def get_firewall_rules(self, context, filters=None, fields=None):
return model_query.get_collection(
context, FirewallRuleV2, self._make_firewall_rule_dict,
@@ -654,6 +658,7 @@ class FirewallPluginDb:
def _check_rules_for_policy_is_valid(self, context, fwp, fwp_db,
rule_id_list, filters):
with db_api.CONTEXT_READER.using(context):
rules_in_fwr_db = model_query.get_collection_query(
context, FirewallRuleV2, filters=filters)
rules_dict = {fwr_db['id']: fwr_db for fwr_db in rules_in_fwr_db}
@@ -834,6 +839,7 @@ class FirewallPluginDb:
fwp = self._get_firewall_policy(context, id)
return self._make_firewall_policy_dict(fwp, fields)
@db_api.CONTEXT_READER
def get_firewall_policies(self, context, filters=None, fields=None):
return model_query.get_collection(
context, FirewallPolicy, self._make_firewall_policy_dict,
@@ -875,6 +881,7 @@ class FirewallPluginDb:
firewall_group_id=firewall_group_id).delete()
return
@db_api.CONTEXT_READER
def _get_default_fwg_id(self, context, tenant_id):
"""Returns an id of default firewall group for given tenant or None"""
default_fwg = model_query.query_with_hooks(
@@ -886,6 +893,7 @@ class FirewallPluginDb:
def get_fwg_attached_to_port(self, context, port_id):
"""Return a firewall group ID that is attached to a given port"""
with db_api.CONTEXT_READER.using(context):
fwg_port = model_query.query_with_hooks(
context, FirewallGroupPortAssociation).\
filter_by(port_id=port_id).first()
@@ -1110,6 +1118,7 @@ class FirewallPluginDb:
tenant_id = filters.get('tenant_id') if filters else None
tenant_id = tenant_id[0] if tenant_id else context.tenant_id
self._ensure_default_firewall_group(context, tenant_id)
with db_api.CONTEXT_READER.using(context):
return model_query.get_collection(
context, FirewallGroup, self._make_firewall_group_dict,
filters=filters, fields=fields)