NSX|P: Fix transactions related issues

1. Call NSX transactional api only if partial updates are supported
(Or else some attributes of the main resource can be reset)
2. Fix bulk rule creation under transaction to provide all the attributes
of the existing rules.

Change-Id: Iff9d6b59fffeaca91e09d6eeff158748aaa5578a
This commit is contained in:
asarfaty 2020-03-05 14:14:16 +02:00
parent f1837f6766
commit 722f93b752
2 changed files with 80 additions and 38 deletions

View File

@ -2266,6 +2266,14 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# remove the edge cluster from the tier1 router
self.nsxpolicy.tier1.remove_edge_cluster(router_id)
def _run_under_transaction(self, method):
if self.nsxpolicy.feature_supported(
nsxlib_consts.FEATURE_PARTIAL_UPDATES):
with policy_trans.NsxPolicyTransaction():
method()
else:
method()
def _update_router_gw_info(self, context, router_id, info,
called_from=None):
# Get the original data of the router GW
@ -2313,7 +2321,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
if actions['add_service_router']:
self.create_service_router(context, router_id, router=router)
with policy_trans.NsxPolicyTransaction():
def _do_remove_nat():
if actions['remove_snat_rules']:
for subnet in router_subnets:
self._del_subnet_snat_rule(router_id, subnet)
@ -2321,6 +2329,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
for subnet in router_subnets:
self._del_subnet_no_dnat_rule(router_id, subnet)
self._run_under_transaction(_do_remove_nat)
if (actions['remove_router_link_port'] or
actions['add_router_link_port']):
# GW was changed. update GW and route advertisement
@ -2338,7 +2348,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
nat=actions['advertise_route_nat_flag'],
subnets=actions['advertise_route_connected_flag'])
with policy_trans.NsxPolicyTransaction():
def _do_add_nat():
if actions['add_snat_rules']:
# Add SNAT rules for all the subnets which are in different
# scope than the GW
@ -2348,12 +2358,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._add_subnet_snat_rule(context, router_id,
subnet, gw_address_scope,
newaddr)
if actions['add_no_dnat_rules']:
for subnet in router_subnets:
self._add_subnet_no_dnat_rule(
context, router_id, subnet)
self._run_under_transaction(_do_add_nat)
# always advertise ipv6 subnets if gateway is set
advertise_ipv6_subnets = True if info else False
self._update_router_advertisement_rules(router_id,
@ -2408,7 +2419,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
r, resource_type='os-neutron-router-id',
project_name=context.tenant_name)
try:
with policy_trans.NsxPolicyTransaction():
def _do_create_router():
self.nsxpolicy.tier1.create_or_overwrite(
router_name, router['id'],
tier0=None,
@ -2417,6 +2428,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# Also create the empty locale-service as it must always exist
self.nsxpolicy.tier1.create_locale_service(router['id'])
self._run_under_transaction(_do_create_router)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error('Failed to create router %(id)s '
@ -2480,7 +2493,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
route['nexthop'])
def _add_static_routes(self, router_id, routes):
with policy_trans.NsxPolicyTransaction():
def _do_add_routes():
for route in routes:
dest = route['destination']
self.nsxpolicy.tier1_static_route.create_or_overwrite(
@ -2490,6 +2503,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
network=dest,
next_hop=route['nexthop'])
self._run_under_transaction(_do_add_routes)
def _delete_static_routes(self, router_id, routes):
for route in routes:
self.nsxpolicy.tier1_static_route.delete(
@ -2825,8 +2840,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return policy_constants.NAT_FIREWALL_MATCH_EXTERNAL
def _add_fip_nat_rules(self, tier1_id, fip_id, ext_ip, int_ip):
firewall_match = self._get_nat_firewall_match()
with policy_trans.NsxPolicyTransaction():
def _do_add_fip_nat():
firewall_match = self._get_nat_firewall_match()
self.nsxpolicy.tier1_nat_rule.create_or_overwrite(
'snat for fip %s' % fip_id,
tier1_id,
@ -2846,8 +2861,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
sequence_number=NAT_RULE_PRIORITY_FIP,
firewall_match=firewall_match)
self._run_under_transaction(_do_add_fip_nat)
def _delete_fip_nat_rules(self, tier1_id, fip_id):
with policy_trans.NsxPolicyTransaction():
def _do_delete_fip_nat():
self.nsxpolicy.tier1_nat_rule.delete(
tier1_id,
nat_rule_id=self._get_fip_snat_rule_id(fip_id))
@ -2855,6 +2872,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
tier1_id,
nat_rule_id=self._get_fip_dnat_rule_id(fip_id))
self._run_under_transaction(_do_delete_fip_nat)
def _update_lb_vip(self, port, vip_address):
# update the load balancer virtual server's VIP with
# floating ip, but don't add NAT rules
@ -3205,7 +3224,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
category = NSX_P_PROVIDER_SECTION_CATEGORY
try:
with policy_trans.NsxPolicyTransaction():
def _do_create_sg():
# Create the group
self.nsxpolicy.group.create_or_overwrite_with_conditions(
nsx_name, NSX_P_GLOBAL_DOMAIN_ID, group_id=sg_id,
@ -3219,6 +3238,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
tags=tags, category=category)
for entry in entries:
self.nsxpolicy.comm_map.create_entry_from_def(entry)
self._run_under_transaction(_do_create_sg)
except Exception as e:
msg = (_("Failed to create NSX resources for SG %(sg)s: "
"%(e)s") % {'sg': sg_id, 'e': e})
@ -3289,7 +3310,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
def _create_security_group_backend_rule(self, context, map_id,
sg_rule, secgroup_logging,
is_provider_sg=False):
is_provider_sg=False,
create_related_resource=True):
"""Create backend resources for a DFW rule
All rule resources (service, groups) will be created
@ -3318,32 +3340,42 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# Create a group for the remote IPs
remote_ip = sg_rule['remote_ip_prefix']
remote_group_id = self._get_sg_rule_remote_ip_group_id(sg_rule)
expr = self.nsxpolicy.group.build_ip_address_expression(
[remote_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
remote_group_id, NSX_P_GLOBAL_DOMAIN_ID,
group_id=remote_group_id,
description='%s for OS rule %s' % (remote_ip, sg_rule['id']),
conditions=[expr], tags=tags)
if create_related_resource:
expr = self.nsxpolicy.group.build_ip_address_expression(
[remote_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
remote_group_id, NSX_P_GLOBAL_DOMAIN_ID,
group_id=remote_group_id,
description='%s for OS rule %s' % (remote_ip,
sg_rule['id']),
conditions=[expr], tags=tags)
source = remote_group_id
if sg_rule.get(sg_prefix.LOCAL_IP_PREFIX):
# Create a group for the local ips
local_ip = sg_rule[sg_prefix.LOCAL_IP_PREFIX]
local_group_id = self._get_sg_rule_local_ip_group_id(sg_rule)
expr = self.nsxpolicy.group.build_ip_address_expression(
[local_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
local_group_id, NSX_P_GLOBAL_DOMAIN_ID,
group_id=local_group_id,
description='%s for OS rule %s' % (local_ip, sg_rule['id']),
conditions=[expr], tags=tags)
if create_related_resource:
expr = self.nsxpolicy.group.build_ip_address_expression(
[local_ip])
self.nsxpolicy.group.create_or_overwrite_with_conditions(
local_group_id, NSX_P_GLOBAL_DOMAIN_ID,
group_id=local_group_id,
description='%s for OS rule %s' % (local_ip,
sg_rule['id']),
conditions=[expr], tags=tags)
destination = local_group_id
if direction == nsxlib_consts.OUT:
# Swap source and destination
source, destination = destination, source
service = self._get_rule_service_id(context, sg_rule, tags)
if create_related_resource:
service = self._get_rule_service_id(context, sg_rule, tags)
else:
if nsxlib_utils.get_l4_protocol_name(sg_rule['protocol']):
service = sg_rule['id']
else:
service = None
ip_protocol = self._get_rule_ip_protocol(sg_rule)
logging = (cfg.CONF.nsx_p.log_security_groups_allowed_traffic or
secgroup_logging)
@ -3399,13 +3431,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
sg_rules = secgroup_db['security_group_rules']
secgroup_logging = secgroup.get(sg_logging.LOGGING, False)
backend_rules = []
with policy_trans.NsxPolicyTransaction():
# Create all the rules resources in a single transaction
for sg_rule in sg_rules:
rule_entry = self._create_security_group_backend_rule(
context, secgroup_db['id'], sg_rule,
secgroup_logging)
backend_rules.append(rule_entry)
# Create all the rules resources in a single transaction
for sg_rule in sg_rules:
rule_entry = self._create_security_group_backend_rule(
context, secgroup_db['id'], sg_rule,
secgroup_logging)
backend_rules.append(rule_entry)
# Create Group & communication map on the NSX
self._create_security_group_backend_resources(
context, secgroup, backend_rules)
@ -3521,20 +3552,21 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
category = (NSX_P_PROVIDER_SECTION_CATEGORY if is_provider_sg
else NSX_P_REGULAR_SECTION_CATEGORY)
# Create the NSX backend rules in a single transaction
with policy_trans.NsxPolicyTransaction():
def _do_update_rules():
# Build new rules and relevant objects
backend_rules = []
for rule_data in rules_db:
rule_entry = self._create_security_group_backend_rule(
context, sg_id, rule_data, secgroup_logging,
is_provider_sg=is_provider_sg)
backend_rules.append(rule_entry)
# Add the old rules
for rule in sg['security_group_rules']:
rule_entry = self.nsxpolicy.comm_map.build_entry(
NSX_P_GLOBAL_DOMAIN_ID, sg_id, rule['id'])
rule_entry = self._create_security_group_backend_rule(
context, sg_id, rule, secgroup_logging,
is_provider_sg=is_provider_sg,
create_related_resource=False)
backend_rules.append(rule_entry)
# Update the policy with all the rules.
@ -3542,6 +3574,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
NSX_P_GLOBAL_DOMAIN_ID, sg_id, entries=backend_rules,
category=category)
self._run_under_transaction(_do_update_rules)
return rules_db
def _delete_security_group_rule_backend_resources(

View File

@ -24,6 +24,7 @@ from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
from vmware_nsx.shell.admin.plugins.nsxp.resources import utils as p_utils
from vmware_nsx.shell import resources as shell
from vmware_nsxlib.v3 import nsx_constants
from vmware_nsxlib.v3.policy import constants as policy_constants
from vmware_nsxlib.v3.policy import transaction as policy_trans
@ -146,11 +147,18 @@ def update_nat_firewall_match(resource, event, trigger, **kwargs):
for router in neutron_routers:
rules = nsxpolicy.tier1_nat_rule.list(router['id'])
for rule in rules:
with policy_trans.NsxPolicyTransaction():
if not nsxpolicy.feature_supported(
nsx_constants.FEATURE_PARTIAL_UPDATES):
if rule['firewall_match'] == old_firewall_match:
nsxpolicy.tier1_nat_rule.update(
router['id'], rule['id'],
firewall_match=new_firewall_match)
else:
with policy_trans.NsxPolicyTransaction():
if rule['firewall_match'] == old_firewall_match:
nsxpolicy.tier1_nat_rule.update(
router['id'], rule['id'],
firewall_match=new_firewall_match)
LOG.info("Done.")