Fix transaction issues with network/subnet facade updates

Commit b8d98a5764 moved code
out of transactions to unblock the gate. This issues has been
resolved in the following:
1. Commit 61d0082f9ae9f9a935b5c3517c0b9036c40dbb00
2. Commit 72a2e9df96998f2198b2aba44e4daa2b466b1158

This also fixes dns transaction issues

Switch of context to admin context under db writer is causing
issues with table constraints.
Some usage of admin context for dns is still in place due to
extend_port_dict external API

Co-Authored-By: Anna Khmelnitsky <akhmelnitsky@vmware.com>

Change-Id: I21ec2df09c6b375973ce6f19e06e88318cf19452
This commit is contained in:
Gary Kotton 2017-04-01 23:57:10 +03:00
parent 232fb88221
commit bf3bbb5c4c
3 changed files with 43 additions and 63 deletions

View File

@ -83,7 +83,7 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
if not request_data.get(dns.DNSNAME):
return
dns_name, is_dns_domain_default = self._get_request_dns_name(
request_data, db_data['network_id'])
request_data, db_data['network_id'], plugin_context)
if is_dns_domain_default:
return
network = self._get_network(plugin_context, db_data['network_id'])
@ -145,10 +145,9 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
return
if dns_name is not None:
dns_name, is_dns_domain_default = self._get_request_dns_name(
request_data, db_data['network_id'])
request_data, db_data['network_id'], plugin_context)
if is_dns_domain_default:
self._extend_port_dict(plugin_context.session, db_data,
db_data, None)
self._extend_port_dict(db_data, db_data, None, plugin_context)
return
network = self._get_network(plugin_context, db_data['network_id'])
dns_domain = network[dns.DNSDOMAIN]
@ -163,8 +162,7 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
else:
dns_data_db = self._update_dns_db(dns_name, dns_domain, db_data,
plugin_context, has_fixed_ips)
self._extend_port_dict(plugin_context.session, db_data, db_data,
dns_data_db)
self._extend_port_dict(db_data, db_data, dns_data_db, plugin_context)
def _process_only_dns_name_update(self, plugin_context, db_data, dns_name):
dns_data_db = port_obj.PortDNS.get_object(
@ -200,31 +198,32 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
response_data[dns.DNSDOMAIN] = db_data.dns_domain[dns.DNSDOMAIN]
return response_data
def _get_dns_domain(self, network_id):
def _get_dns_domain(self, network_id, context=None):
if not cfg.CONF.dns_domain:
return ''
if cfg.CONF.dns_domain.endswith('.'):
return cfg.CONF.dns_domain
return '%s.' % cfg.CONF.dns_domain
def _get_request_dns_name(self, port, network_id):
dns_domain = self._get_dns_domain(network_id)
def _get_request_dns_name(self, port, network_id, context):
dns_domain = self._get_dns_domain(network_id, context)
if ((dns_domain and dns_domain != DNS_DOMAIN_DEFAULT)):
return (port.get(dns.DNSNAME, ''), False)
return ('', True)
def _get_request_dns_name_and_domain_name(self, dns_data_db, network_id):
dns_domain = self._get_dns_domain(network_id)
def _get_request_dns_name_and_domain_name(self, dns_data_db,
network_id, context):
dns_domain = self._get_dns_domain(network_id, context)
dns_name = ''
if ((dns_domain and dns_domain != DNS_DOMAIN_DEFAULT)):
if dns_data_db:
dns_name = dns_data_db.dns_name
return dns_name, dns_domain
def _get_dns_names_for_port(self, ips, dns_data_db, network_id):
def _get_dns_names_for_port(self, ips, dns_data_db, network_id, context):
dns_assignment = []
dns_name, dns_domain = self._get_request_dns_name_and_domain_name(
dns_data_db, network_id)
dns_data_db, network_id, context)
for ip in ips:
if dns_name:
hostname = dns_name
@ -242,25 +241,26 @@ class DNSExtensionDriver(driver_api.ExtensionDriver):
'fqdn': fqdn})
return dns_assignment
def _get_dns_name_for_port_get(self, port, dns_data_db):
def _get_dns_name_for_port_get(self, port, dns_data_db, context):
if port['fixed_ips']:
return self._get_dns_names_for_port(
port['fixed_ips'], dns_data_db, port['network_id'])
port['fixed_ips'], dns_data_db,
port['network_id'], context)
return []
def _extend_port_dict(self, session, db_data, response_data, dns_data_db):
def _extend_port_dict(self, db_data, response_data,
dns_data_db, context=None):
if not dns_data_db:
response_data[dns.DNSNAME] = ''
else:
response_data[dns.DNSNAME] = dns_data_db[dns.DNSNAME]
response_data['dns_assignment'] = self._get_dns_name_for_port_get(
db_data, dns_data_db)
db_data, dns_data_db, context)
return response_data
def extend_port_dict(self, session, db_data, response_data):
dns_data_db = db_data.dns
return self._extend_port_dict(session, db_data, response_data,
dns_data_db)
return self._extend_port_dict(db_data, response_data, dns_data_db)
def _get_network(self, context, network_id):
plugin = directory.get_plugin()
@ -287,18 +287,19 @@ class DNSExtensionDriverNSXv3(DNSExtensionDriver):
self._availability_zones = nsx_az.NsxV3AvailabilityZones()
LOG.info("DNSExtensionDriverNSXv3 initialization complete")
def _get_network_az(self, network_id):
context = n_context.get_admin_context()
def _get_network_az(self, network_id, context):
if not context:
context = n_context.get_admin_context()
network = self._get_network(context, network_id)
if az_ext.AZ_HINTS in network and network[az_ext.AZ_HINTS]:
az_name = network[az_ext.AZ_HINTS][0]
return self._availability_zones.get_availability_zone(az_name)
return self._availability_zones.get_default_availability_zone()
def _get_dns_domain(self, network_id):
def _get_dns_domain(self, network_id, context=None):
# try to get the dns-domain from the specific availability zone
# of this network
az = self._get_network_az(network_id)
az = self._get_network_az(network_id, context)
if az.dns_domain:
dns_domain = az.dns_domain
elif cfg.CONF.nsx_v3.dns_domain:

View File

@ -1359,16 +1359,17 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'Reason: %(e)s',
{'port_id': port_id, 'e': e})
self._process_l3_delete(context, id)
# We would first delete subnet db if the backend dhcp service is
# deleted in case of entering delete_subnet logic and retrying
# to delete backend dhcp service again.
if is_dhcp_backend_deleted:
subnets = self._get_subnets_by_network(context, id)
for subnet in subnets:
super(NsxVPluginV2, self).delete_subnet(
context, subnet['id'])
super(NsxVPluginV2, self).delete_network(context, id)
with db_api.context_manager.writer.using(context):
self._process_l3_delete(context, id)
# We would first delete subnet db if the backend dhcp service is
# deleted in case of entering delete_subnet logic and retrying
# to delete backend dhcp service again.
if is_dhcp_backend_deleted:
subnets = self._get_subnets_by_network(context, id)
for subnet in subnets:
super(NsxVPluginV2, self).delete_subnet(
context, subnet['id'])
super(NsxVPluginV2, self).delete_network(context, id)
# Do not delete a predefined port group that was attached to
# an external network
@ -1679,6 +1680,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
with db_api.context_manager.writer.using(context):
# First we allocate port in neutron database
neutron_db = super(NsxVPluginV2, self).create_port(context, port)
self._extension_manager.process_create_port(
context, port_data, neutron_db)
# Port port-security is decided by the port-security state on the
# network it belongs to, unless specifically specified here
if validators.is_attr_set(port_data.get(psec.PORTSECURITY)):
@ -1739,20 +1742,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self._process_port_create_extra_dhcp_opts(
context, port_data, dhcp_opts)
# Invoking the manager callback under transaction fails so here
# we do it outside. If this fails we will blow away the port
try:
with db_api.context_manager.writer.using(context):
self._extension_manager.process_create_port(
context, port_data, neutron_db)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Failed to create port %(id)s. '
'Exception: %(e)s',
{'id': neutron_db['id'], 'e': e})
# Revert what we have created and raise the exception
self.delete_port(context, port_data['id'])
try:
# Configure NSX - this should not be done in the DB transaction
# Configure the DHCP Edge service

View File

@ -884,9 +884,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
first_try = True
while True:
try:
self._process_l3_delete(context, network_id)
return super(NsxV3Plugin, self).delete_network(
context, network_id)
with db_api.context_manager.writer.using(context):
self._process_l3_delete(context, network_id)
return super(NsxV3Plugin, self).delete_network(
context, network_id)
except n_exc.NetworkInUse:
# There is a race condition in delete_network() that we need
# to work around here. delete_network() issues a query to
@ -2035,6 +2036,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
port_data, port_data.get('device_owner'))
neutron_db = super(NsxV3Plugin, self).create_port(context, port)
self._extension_manager.process_create_port(
context, port_data, neutron_db)
port["port"].update(neutron_db)
(is_psec_on, has_ip) = self._create_port_preprocess_security(
@ -2068,19 +2071,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# ATTR_NOT_SPECIFIED
port_data.pop(mac_ext.MAC_LEARNING)
# Invoking the manager callback under transaction fails so here
# we do it outside. If this fails we will blow away the port
try:
with db_api.context_manager.writer.using(context):
self._extension_manager.process_create_port(
context, port_data, neutron_db)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Failed to create port %(id)s. '
'Exception: %(e)s',
{'id': neutron_db['id'], 'e': e})
self._cleanup_port(context, neutron_db['id'], None)
# Operations to backend should be done outside of DB transaction.
# NOTE(arosen): ports on external networks are nat rules and do
# not result in ports on the backend.