Fix transaction issues with network/subnet facade updates

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

View File

@ -1359,16 +1359,17 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'Reason: %(e)s', 'Reason: %(e)s',
{'port_id': port_id, 'e': e}) {'port_id': port_id, 'e': e})
self._process_l3_delete(context, id) with db_api.context_manager.writer.using(context):
# We would first delete subnet db if the backend dhcp service is self._process_l3_delete(context, id)
# deleted in case of entering delete_subnet logic and retrying # We would first delete subnet db if the backend dhcp service is
# to delete backend dhcp service again. # deleted in case of entering delete_subnet logic and retrying
if is_dhcp_backend_deleted: # to delete backend dhcp service again.
subnets = self._get_subnets_by_network(context, id) if is_dhcp_backend_deleted:
for subnet in subnets: subnets = self._get_subnets_by_network(context, id)
super(NsxVPluginV2, self).delete_subnet( for subnet in subnets:
context, subnet['id']) super(NsxVPluginV2, self).delete_subnet(
super(NsxVPluginV2, self).delete_network(context, id) context, subnet['id'])
super(NsxVPluginV2, self).delete_network(context, id)
# Do not delete a predefined port group that was attached to # Do not delete a predefined port group that was attached to
# an external network # an external network
@ -1679,6 +1680,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
with db_api.context_manager.writer.using(context): with db_api.context_manager.writer.using(context):
# First we allocate port in neutron database # First we allocate port in neutron database
neutron_db = super(NsxVPluginV2, self).create_port(context, port) 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 # Port port-security is decided by the port-security state on the
# network it belongs to, unless specifically specified here # network it belongs to, unless specifically specified here
if validators.is_attr_set(port_data.get(psec.PORTSECURITY)): 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( self._process_port_create_extra_dhcp_opts(
context, port_data, 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: try:
# Configure NSX - this should not be done in the DB transaction # Configure NSX - this should not be done in the DB transaction
# Configure the DHCP Edge service # Configure the DHCP Edge service

View File

@ -884,9 +884,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
first_try = True first_try = True
while True: while True:
try: try:
self._process_l3_delete(context, network_id) with db_api.context_manager.writer.using(context):
return super(NsxV3Plugin, self).delete_network( self._process_l3_delete(context, network_id)
context, network_id) return super(NsxV3Plugin, self).delete_network(
context, network_id)
except n_exc.NetworkInUse: except n_exc.NetworkInUse:
# There is a race condition in delete_network() that we need # There is a race condition in delete_network() that we need
# to work around here. delete_network() issues a query to # 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')) port_data, port_data.get('device_owner'))
neutron_db = super(NsxV3Plugin, self).create_port(context, port) 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) port["port"].update(neutron_db)
(is_psec_on, has_ip) = self._create_port_preprocess_security( (is_psec_on, has_ip) = self._create_port_preprocess_security(
@ -2068,19 +2071,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# ATTR_NOT_SPECIFIED # ATTR_NOT_SPECIFIED
port_data.pop(mac_ext.MAC_LEARNING) 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. # Operations to backend should be done outside of DB transaction.
# NOTE(arosen): ports on external networks are nat rules and do # NOTE(arosen): ports on external networks are nat rules and do
# not result in ports on the backend. # not result in ports on the backend.