From 1a0655e8fb2f20bbb5f8287e23361dd9e63253f3 Mon Sep 17 00:00:00 2001 From: Kobi Samoray Date: Wed, 3 Jun 2015 17:08:54 +0300 Subject: [PATCH] Distributed locking support Use tooz distributed locking mechanism to synchronize concurrent execution of neutron services over multiple nodes DocImpact Change-Id: Icbcec938c1c5ae7a528350f2f283388b81fa66b7 --- requirements.txt | 1 + .../neutron/plugins/vmware/common/config.py | 2 + .../neutron/plugins/vmware/common/locking.py | 51 +++++ .../neutron/plugins/vmware/plugins/nsx_v.py | 41 ++-- .../nsx_v_drivers/shared_router_driver.py | 129 ++++++----- .../plugins/vmware/vshield/edge_utils.py | 207 +++++++++--------- 6 files changed, 250 insertions(+), 181 deletions(-) create mode 100644 vmware_nsx/neutron/plugins/vmware/common/locking.py diff --git a/requirements.txt b/requirements.txt index 60eee364c7..0088ad2dd0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,4 @@ oslo.log>=0.1.0 # Apache-2.0 oslo.serialization>=1.0.0 # Apache-2.0 oslo.utils>=1.1.0 # Apache-2.0 oslo.vmware>=0.9.0 # Apache-2.0 +tooz>=0.13.1 # Apache-2.0 diff --git a/vmware_nsx/neutron/plugins/vmware/common/config.py b/vmware_nsx/neutron/plugins/vmware/common/config.py index 774fa2f771..cb24b6091d 100644 --- a/vmware_nsx/neutron/plugins/vmware/common/config.py +++ b/vmware_nsx/neutron/plugins/vmware/common/config.py @@ -260,6 +260,8 @@ nsxv_opts = [ cfg.IntOpt('dhcp_lease_time', default=86400, help=_('DHCP default lease time.')), + cfg.StrOpt('locking_coordinator_url', + help=_('A URL to a locking mechanism coordinator')), ] # Register the configuration options diff --git a/vmware_nsx/neutron/plugins/vmware/common/locking.py b/vmware_nsx/neutron/plugins/vmware/common/locking.py new file mode 100644 index 0000000000..29708e2a57 --- /dev/null +++ b/vmware_nsx/neutron/plugins/vmware/common/locking.py @@ -0,0 +1,51 @@ +# Copyright 2015 VMware, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_concurrency import lockutils +from oslo_config import cfg +from oslo_log import log +from tooz import coordination + +LOG = log.getLogger(__name__) + + +class LockManager: + _coordinator = None + _connect_string = cfg.CONF.nsxv.locking_coordinator_url + + def __init__(self): + LOG.debug('LockManager initialized!') + + @staticmethod + def get_lock(name, **kwargs): + if cfg.CONF.nsxv.locking_coordinator_url: + return LockManager._get_lock_distributed(name) + else: + return LockManager._get_lock_local(name, **kwargs) + + @staticmethod + def _get_lock_local(name, **kwargs): + return lockutils.lock(name, **kwargs) + + @staticmethod + def _get_lock_distributed(name): + if not LockManager._coordinator: + LOG.debug('Initialized coordinator with connect string %s', + LockManager._connect_string) + LockManager._coordinator = coordination.get_coordinator( + LockManager._connect_string, 'vmware-neutron-plugin') + + LOG.debug('Retrieved lock for ', name) + return LockManager._coordinator.get_lock(name) diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py index 49e6cf3fa8..8a1d1b804f 100644 --- a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v.py @@ -17,7 +17,6 @@ import six import uuid import netaddr -from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils @@ -56,6 +55,7 @@ from neutron.plugins.vmware.extensions import ( from vmware_nsx.neutron.plugins import vmware from vmware_nsx.neutron.plugins.vmware.common import config # noqa from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc +from vmware_nsx.neutron.plugins.vmware.common import locking from vmware_nsx.neutron.plugins.vmware.common import utils as c_utils from vmware_nsx.neutron.plugins.vmware.dbexts import ( routertype as rt_rtr) @@ -453,8 +453,8 @@ class NsxVPluginV2(agents_db.AgentDbMixin, return self._ensure_default_security_group(context, tenant_id) def _add_member_to_security_group(self, sg_id, vnic_id): - with lockutils.lock(str(sg_id), - lock_file_prefix='neutron-security-ops'): + with locking.LockManager.get_lock( + str(sg_id), lock_file_prefix='neutron-security-ops'): try: h, c = self.nsx_v.vcns.add_member_to_security_group( sg_id, vnic_id) @@ -480,8 +480,8 @@ class NsxVPluginV2(agents_db.AgentDbMixin, self._add_member_to_security_group(nsx_sg_id, vnic_id) def _remove_member_from_security_group(self, sg_id, vnic_id): - with lockutils.lock(str(sg_id), - lock_file_prefix='neutron-security-ops'): + with locking.LockManager.get_lock( + str(sg_id), lock_file_prefix='neutron-security-ops'): try: h, c = self.nsx_v.vcns.remove_member_from_security_group( sg_id, vnic_id) @@ -1026,9 +1026,8 @@ class NsxVPluginV2(agents_db.AgentDbMixin, err_msg = _("No support for DHCP for IPv6") raise n_exc.InvalidInput(error_message=err_msg) - with lockutils.lock('nsx-edge-pool', - lock_file_prefix='edge-bind-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-pool', lock_file_prefix='edge-bind-', external=True): s = super(NsxVPluginV2, self).create_subnet(context, subnet) if s['enable_dhcp']: try: @@ -1052,19 +1051,21 @@ class NsxVPluginV2(agents_db.AgentDbMixin, self.edge_manager.update_dhcp_edge_bindings(context, network_id) return subnet - @lockutils.synchronized('vmware', 'neutron-dhcp-') def _get_conflict_network_ids_by_overlapping(self, context, subnets): - conflict_network_ids = [] - subnet_ids = [subnet['id'] for subnet in subnets] - conflict_set = netaddr.IPSet([subnet['cidr'] for subnet in subnets]) - subnets_qry = context.session.query(models_v2.Subnet).all() - subnets_all = [subnet for subnet in subnets_qry - if subnet['id'] not in subnet_ids] - for subnet in subnets_all: - cidr_set = netaddr.IPSet([subnet['cidr']]) - if cidr_set & conflict_set: - conflict_network_ids.append(subnet['network_id']) - return conflict_network_ids + with locking.LockManager.get_lock( + 'vmware', lock_file_prefix='neutron-dhcp-'): + conflict_network_ids = [] + subnet_ids = [subnet['id'] for subnet in subnets] + conflict_set = netaddr.IPSet( + [subnet['cidr'] for subnet in subnets]) + subnets_qry = context.session.query(models_v2.Subnet).all() + subnets_all = [subnet for subnet in subnets_qry + if subnet['id'] not in subnet_ids] + for subnet in subnets_all: + cidr_set = netaddr.IPSet([subnet['cidr']]) + if cidr_set & conflict_set: + conflict_network_ids.append(subnet['network_id']) + return conflict_network_ids def _get_conflicting_networks_for_subnet(self, context, subnet): network_id = subnet['network_id'] diff --git a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py index 8189b4549d..b0b1a6bd5c 100644 --- a/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py +++ b/vmware_nsx/neutron/plugins/vmware/plugins/nsx_v_drivers/shared_router_driver.py @@ -13,7 +13,6 @@ # under the License. import netaddr -from oslo_concurrency import lockutils from oslo_config import cfg from neutron.api.v2 import attributes as attr @@ -22,6 +21,7 @@ from neutron.db import models_v2 from neutron.plugins.vmware.dbexts import nsxv_models from oslo_log import log as logging from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc +from vmware_nsx.neutron.plugins.vmware.common import locking from vmware_nsx.neutron.plugins.vmware.dbexts import nsxv_db from vmware_nsx.neutron.plugins.vmware.plugins import nsx_v from vmware_nsx.neutron.plugins.vmware.plugins.nsx_v_drivers import ( @@ -51,9 +51,9 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): return super(nsx_v.NsxVPluginV2, self.plugin).update_router( context, router_id, router) else: - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): gw_info = self.plugin._extract_external_gw( context, router, is_extract=True) super(nsx_v.NsxVPluginV2, self.plugin).update_router( @@ -206,9 +206,9 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): context, router_id, router_db.admin_state_up) new_edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(new_edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(new_edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, external=True): self._add_router_services_on_available_edge(context, router_id) router_ids = self.edge_manager.get_routers_on_same_edge( @@ -446,40 +446,42 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): optional_router_ids.append(router['id']) return optional_router_ids, conflict_router_ids - @lockutils.synchronized("router", "bind-", external=True) def _bind_router_on_available_edge(self, context, router_id, admin_state): - conflict_network_ids, conflict_router_ids, intf_num = ( - self._get_conflict_network_and_router_ids_by_intf(context, - router_id)) - conflict_network_ids_by_ext_net = ( - self._get_conflict_network_ids_by_ext_net(context, router_id)) - conflict_network_ids.extend(conflict_network_ids_by_ext_net) - optional_router_ids, new_conflict_router_ids = ( - self._get_available_and_conflicting_ids(context, router_id)) - conflict_router_ids.extend(new_conflict_router_ids) - conflict_router_ids = list(set(conflict_router_ids)) + with locking.LockManager.get_lock("router", lock_file_prefix="bind-", + external=True): + conflict_network_ids, conflict_router_ids, intf_num = ( + self._get_conflict_network_and_router_ids_by_intf(context, + router_id)) + conflict_network_ids_by_ext_net = ( + self._get_conflict_network_ids_by_ext_net(context, router_id)) + conflict_network_ids.extend(conflict_network_ids_by_ext_net) + optional_router_ids, new_conflict_router_ids = ( + self._get_available_and_conflicting_ids(context, router_id)) + conflict_router_ids.extend(new_conflict_router_ids) + conflict_router_ids = list(set(conflict_router_ids)) - new = self.edge_manager.bind_router_on_available_edge( - context, router_id, optional_router_ids, - conflict_router_ids, conflict_network_ids, intf_num) - # configure metadata service on the router. - metadata_proxy_handler = self.plugin.metadata_proxy_handler - if metadata_proxy_handler and new: - metadata_proxy_handler.configure_router_edge(router_id) - edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): - # add all internal interfaces of the router on edge - intf_net_ids = ( - self.plugin._get_internal_network_ids_by_router(context, - router_id)) - for network_id in intf_net_ids: - address_groups = self.plugin._get_address_groups( - context, router_id, network_id) - edge_utils.update_internal_interface( - self.nsx_v, context, router_id, network_id, - address_groups, admin_state) + new = self.edge_manager.bind_router_on_available_edge( + context, router_id, optional_router_ids, + conflict_router_ids, conflict_network_ids, intf_num) + # configure metadata service on the router. + metadata_proxy_handler = self.plugin.metadata_proxy_handler + if metadata_proxy_handler and new: + metadata_proxy_handler.configure_router_edge(router_id) + edge_id = edge_utils.get_router_edge_id(context, router_id) + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): + # add all internal interfaces of the router on edge + intf_net_ids = ( + self.plugin._get_internal_network_ids_by_router(context, + router_id)) + for network_id in intf_net_ids: + address_groups = self.plugin._get_address_groups( + context, router_id, network_id) + edge_utils.update_internal_interface( + self.nsx_v, context, router_id, network_id, + address_groups, admin_state) def _unbind_router_on_edge(self, context, router_id): self.edge_manager.unbind_router_on_edge(context, router_id) @@ -527,9 +529,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): # UPDATE gw info only if the router has been attached to an edge else: is_migrated = False - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): router_ids = self.edge_manager.get_routers_on_same_edge( context, router_id) org_ext_net_id = (router.gw_port_id and @@ -605,9 +608,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): self._bind_router_on_available_edge( context, router_id, router.admin_state_up) edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): self._add_router_services_on_available_edge(context, router_id) @@ -617,9 +621,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): router_db = self.plugin._get_router(context, router_id) if edge_id: is_migrated = False - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): router_ids = self.edge_manager.get_routers_on_same_edge( context, router_id) info = super(nsx_v.NsxVPluginV2, @@ -677,9 +682,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): self._bind_router_on_available_edge( context, router_id, router_db.admin_state_up) edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): self._add_router_services_on_available_edge(context, router_id) else: @@ -689,18 +695,20 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): self._bind_router_on_available_edge( context, router_id, router_db.admin_state_up) edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): self._add_router_services_on_available_edge(context, router_id) return info def remove_router_interface(self, context, router_id, interface_info): edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): info = super( nsx_v.NsxVPluginV2, self.plugin).remove_router_interface( context, router_id, interface_info) @@ -730,9 +738,10 @@ class RouterSharedDriver(router_driver.RouterBaseDriver): def _update_edge_router(self, context, router_id): edge_id = edge_utils.get_router_edge_id(context, router_id) - with lockutils.lock(str(edge_id), - lock_file_prefix=NSXV_ROUTER_RECONFIG, - external=True): + with locking.LockManager.get_lock( + str(edge_id), + lock_file_prefix=NSXV_ROUTER_RECONFIG, + external=True): router_ids = self.edge_manager.get_routers_on_same_edge( context, router_id) if router_ids: diff --git a/vmware_nsx/neutron/plugins/vmware/vshield/edge_utils.py b/vmware_nsx/neutron/plugins/vmware/vshield/edge_utils.py index a23f7512b0..2f902af7f4 100644 --- a/vmware_nsx/neutron/plugins/vmware/vshield/edge_utils.py +++ b/vmware_nsx/neutron/plugins/vmware/vshield/edge_utils.py @@ -18,7 +18,6 @@ import random from sqlalchemy import exc as db_base_exc import time -from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils @@ -31,6 +30,7 @@ from neutron import context as q_context from neutron.extensions import l3 from neutron.plugins.common import constants as plugin_const +from vmware_nsx.neutron.plugins.vmware.common import locking from vmware_nsx.neutron.plugins.vmware.common import nsxv_constants from vmware_nsx.neutron.plugins.vmware.dbexts import db as nsx_db from vmware_nsx.neutron.plugins.vmware.dbexts import nsxv_db @@ -446,9 +446,8 @@ class EdgeManager(object): task.wait(task_const.TaskState.RESULT) return - with lockutils.lock('nsx-edge-request', - lock_file_prefix='get-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-request', lock_file_prefix='get-', external=True): self._clean_all_error_edge_bindings(context) available_router_binding = self._get_available_router_binding( context, appliance_size=appliance_size, edge_type=edge_type) @@ -534,9 +533,8 @@ class EdgeManager(object): router_id, binding['edge_id'], jobdata=jobdata, dist=dist) return - with lockutils.lock('nsx-edge-request', - lock_file_prefix='get-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-request', lock_file_prefix='get-', external=True): self._clean_all_error_edge_bindings(context) backup_router_bindings = self._get_backup_edge_bindings( context, appliance_size=binding['appliance_size'], @@ -726,9 +724,8 @@ class EdgeManager(object): def allocate_new_dhcp_edge(self, context, network_id, resource_id): self._allocate_dhcp_edge_appliance(context, resource_id) - with lockutils.lock('nsx-edge-pool', - lock_file_prefix='edge-bind-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-pool', lock_file_prefix='edge-bind-', external=True): new_edge = nsxv_db.get_nsxv_router_binding(context.session, resource_id) nsxv_db.allocate_edge_vnic_with_tunnel_index( @@ -750,17 +747,17 @@ class EdgeManager(object): allocate_new_edge = False # case 1: update a subnet to an existing dhcp edge if dhcp_edge_binding: - with lockutils.lock('nsx-edge-pool', - lock_file_prefix='edge-bind-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-pool', lock_file_prefix='edge-bind-', + external=True): edge_id = dhcp_edge_binding['edge_id'] (conflict_edge_ids, available_edge_ids) = self._get_used_edges(context, subnet) LOG.debug('The available edges %s, the conflict edges %s', available_edge_ids, conflict_edge_ids) - with lockutils.lock(str(edge_id), - lock_file_prefix='nsxv-dhcp-config-', - external=True): + with locking.LockManager.get_lock( + str(edge_id), lock_file_prefix='nsxv-dhcp-config-', + external=True): # Delete the existing vnic interface if there is # and overlapping subnet if edge_id in conflict_edge_ids: @@ -781,9 +778,9 @@ class EdgeManager(object): allocate_new_edge = True # case 2: attach the subnet to a new edge and update vnic else: - with lockutils.lock('nsx-edge-pool', - lock_file_prefix='edge-bind-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-pool', lock_file_prefix='edge-bind-', + external=True): (conflict_edge_ids, available_edge_ids) = self._get_used_edges(context, subnet) LOG.debug('The available edges %s, the conflict edges %s', @@ -821,9 +818,9 @@ class EdgeManager(object): LOG.debug('Update the dhcp service for %s on vnic %d tunnel %d', edge_id, vnic_index, tunnel_index) try: - with lockutils.lock(str(edge_id), - lock_file_prefix='nsxv-dhcp-config-', - external=True): + with locking.LockManager.get_lock( + str(edge_id), lock_file_prefix='nsxv-dhcp-config-', + external=True): self._update_dhcp_internal_interface( context, edge_id, vnic_index, tunnel_index, network_id, address_groups) @@ -863,9 +860,9 @@ class EdgeManager(object): edge_id, network_id) try: - with lockutils.lock(str(edge_id), - lock_file_prefix='nsxv-dhcp-config-', - external=True): + with locking.LockManager.get_lock( + str(edge_id), lock_file_prefix='nsxv-dhcp-config-', + external=True): self._delete_dhcp_internal_interface(context, edge_id, vnic_index, tunnel_index, @@ -889,13 +886,13 @@ class EdgeManager(object): resource_id) if dhcp_edge_binding: - with lockutils.lock('nsx-edge-pool', - lock_file_prefix='edge-bind-', - external=True): + with locking.LockManager.get_lock( + 'nsx-edge-pool', lock_file_prefix='edge-bind-', + external=True): edge_id = dhcp_edge_binding['edge_id'] - with lockutils.lock(str(edge_id), - lock_file_prefix='nsxv-dhcp-config-', - external=True): + with locking.LockManager.get_lock( + str(edge_id), lock_file_prefix='nsxv-dhcp-config-', + external=True): self.remove_network_from_dhcp_edge(context, network_id, edge_id) @@ -1034,7 +1031,6 @@ class EdgeManager(object): else: return [] - @lockutils.synchronized("edge-router", "bind-", external=True) def bind_router_on_available_edge( self, context, target_router_id, optional_router_ids, conflict_router_ids, @@ -1042,88 +1038,97 @@ class EdgeManager(object): """Bind logical router on an available edge. Return True if the logical router is bound to a new edge. """ - optional_edge_ids = [] - conflict_edge_ids = [] - for router_id in optional_router_ids: - binding = nsxv_db.get_nsxv_router_binding( - context.session, router_id) - if (binding and binding.status == plugin_const.ACTIVE and - binding.edge_id not in optional_edge_ids): - optional_edge_ids.append(binding.edge_id) + with locking.LockManager.get_lock( + "edge-router", lock_file_prefix="bind-", external=True): + optional_edge_ids = [] + conflict_edge_ids = [] + for router_id in optional_router_ids: + binding = nsxv_db.get_nsxv_router_binding( + context.session, router_id) + if (binding and binding.status == plugin_const.ACTIVE and + binding.edge_id not in optional_edge_ids): + optional_edge_ids.append(binding.edge_id) - for router_id in conflict_router_ids: - binding = nsxv_db.get_nsxv_router_binding( - context.session, router_id) - if binding and binding.edge_id not in conflict_edge_ids: - conflict_edge_ids.append(binding.edge_id) - optional_edge_ids = list( - set(optional_edge_ids) - set(conflict_edge_ids)) + for router_id in conflict_router_ids: + binding = nsxv_db.get_nsxv_router_binding( + context.session, router_id) + if binding and binding.edge_id not in conflict_edge_ids: + conflict_edge_ids.append(binding.edge_id) + optional_edge_ids = list( + set(optional_edge_ids) - set(conflict_edge_ids)) - max_net_number = 0 - available_edge_id = None - for edge_id in optional_edge_ids: - edge_vnic_bindings = nsxv_db.get_edge_vnic_bindings_by_edge( - context.session, edge_id) - # one vnic is used to provide external access. - net_number = vcns_const.MAX_VNIC_NUM - len(edge_vnic_bindings) - 1 - if net_number > max_net_number and net_number >= network_number: - net_ids = [vnic_binding.network_id - for vnic_binding in edge_vnic_bindings] - if not (set(conflict_network_ids) & set(net_ids)): - max_net_number = net_number - available_edge_id = edge_id - else: - # TODO(yangyu): Remove conflict_network_ids - LOG.exception(_LE("Failed to query conflict_router_ids")) - if available_edge_id: - edge_binding = nsxv_db.get_nsxv_router_bindings_by_edge( - context.session, available_edge_id)[0] - nsxv_db.add_nsxv_router_binding( - context.session, target_router_id, - edge_binding.edge_id, None, - edge_binding.status, - edge_binding.appliance_size, - edge_binding.edge_type) - else: - router_name = ('shared' + '-' + _uuid())[:vcns_const.EDGE_NAME_LEN] - self._allocate_edge_appliance( - context, target_router_id, router_name, - appliance_size=vcns_const.SERVICE_SIZE_MAPPING['router']) - return True + max_net_number = 0 + available_edge_id = None + for edge_id in optional_edge_ids: + edge_vnic_bindings = nsxv_db.get_edge_vnic_bindings_by_edge( + context.session, edge_id) + # one vnic is used to provide external access. + net_number = ( + vcns_const.MAX_VNIC_NUM - len(edge_vnic_bindings) - 1) + if (net_number > max_net_number + and net_number >= network_number): + net_ids = [vnic_binding.network_id + for vnic_binding in edge_vnic_bindings] + if not (set(conflict_network_ids) & set(net_ids)): + max_net_number = net_number + available_edge_id = edge_id + else: + # TODO(yangyu): Remove conflict_network_ids + LOG.exception( + _LE("Failed to query conflict_router_ids")) + if available_edge_id: + edge_binding = nsxv_db.get_nsxv_router_bindings_by_edge( + context.session, available_edge_id)[0] + nsxv_db.add_nsxv_router_binding( + context.session, target_router_id, + edge_binding.edge_id, None, + edge_binding.status, + edge_binding.appliance_size, + edge_binding.edge_type) + else: + router_name = ('shared' + '-' + _uuid())[ + :vcns_const.EDGE_NAME_LEN] + self._allocate_edge_appliance( + context, target_router_id, router_name, + appliance_size=vcns_const.SERVICE_SIZE_MAPPING['router']) + return True - @lockutils.synchronized("edge-router", "bind-", external=True) def unbind_router_on_edge(self, context, router_id): """Unbind a logical router from edge. Return True if no logical router bound to the edge. """ - # free edge if no other routers bound to the edge - router_ids = self.get_routers_on_same_edge(context, router_id) - if router_ids == [router_id]: - self._free_edge_appliance(context, router_id) - return True - else: - nsxv_db.delete_nsxv_router_binding(context.session, router_id) + with locking.LockManager.get_lock( + "edge-router", lock_file_prefix="bind-", external=True): + # free edge if no other routers bound to the edge + router_ids = self.get_routers_on_same_edge(context, router_id) + if router_ids == [router_id]: + self._free_edge_appliance(context, router_id) + return True + else: + nsxv_db.delete_nsxv_router_binding(context.session, router_id) - @lockutils.synchronized("edge-router", "bind-", external=True) def is_router_conflict_on_edge(self, context, router_id, conflict_router_ids, conflict_network_ids, intf_num=0): - router_ids = self.get_routers_on_same_edge(context, router_id) - if set(router_ids) & set(conflict_router_ids): - return True - router_binding = nsxv_db.get_nsxv_router_binding(context.session, - router_id) - edge_vnic_bindings = nsxv_db.get_edge_vnic_bindings_by_edge( - context.session, router_binding.edge_id) - if vcns_const.MAX_VNIC_NUM - len(edge_vnic_bindings) - 1 < intf_num: - LOG.debug("There isn't available edge vnic for the router: %s", - router_id) - return True - for binding in edge_vnic_bindings: - if binding.network_id in conflict_network_ids: + with locking.LockManager.get_lock( + "edge-router", lock_file_prefix="bind-", external=True): + router_ids = self.get_routers_on_same_edge(context, router_id) + if set(router_ids) & set(conflict_router_ids): return True - return False + router_binding = nsxv_db.get_nsxv_router_binding(context.session, + router_id) + edge_vnic_bindings = nsxv_db.get_edge_vnic_bindings_by_edge( + context.session, router_binding.edge_id) + if (vcns_const.MAX_VNIC_NUM - len(edge_vnic_bindings + ) - 1 < intf_num): + LOG.debug("There isn't available edge vnic for the router: %s", + router_id) + return True + for binding in edge_vnic_bindings: + if binding.network_id in conflict_network_ids: + return True + return False def create_lrouter(nsxv_manager, context, lrouter, lswitch=None, dist=False):