Create auto allocated networks in disabled state

Under particular circumstances, multiple requests to the
auto-allocated-topology extension may lead to the transient
creation of duplicated resources. This is dealt with by the
service plugin code, which cleans them up once the condition
is detected. However the client may accidentally be impacted
and potentially left in error (recoverable on retry).

In order to address this error condition, the logic to
provision the network for any given tenant is tweaked
slightly so that the network is created in disabled state
and re-enabled when it is safe to do so. A Neutron client
should check the network status to see if the network is
ready for use before getting its hands on it.

Closes-bug: #1591766

Change-Id: Ia6ff5ad975673875216eb470080dfc0dcf6b9ab2
This commit is contained in:
Armando Migliaccio 2016-06-13 05:43:37 -07:00
parent 877778ee4c
commit d91a4e1930
3 changed files with 23 additions and 4 deletions

View File

@ -27,6 +27,9 @@ import webob.exc
from neutron._i18n import _, _LI
from neutron.api.v2 import attributes
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import exceptions as n_exc
from neutron.plugins.common import constants as p_const
@ -150,6 +153,17 @@ def create_network(core_plugin, context, net, check_allow_post=True):
return core_plugin.create_network(context, {'network': net_data})
def update_network(core_plugin, context, network_id, net_data):
network = core_plugin.update_network(
context, network_id, {resources.NETWORK: net_data})
# bundle the plugin API update with any other action required to
# reflect a state change on the network, e.g. DHCP notifications
registry.notify(resources.NETWORK, events.BEFORE_RESPONSE, core_plugin,
context=context, data={resources.NETWORK: network},
method_name='network.update.end')
return network
def create_subnet(core_plugin, context, subnet, check_allow_post=True):
subnet_data = _fixup_res_dict(context, attributes.SUBNETS,
subnet.get('subnet', {}),

View File

@ -223,7 +223,7 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin):
try:
network_args = {
'name': 'auto_allocated_network',
'admin_state_up': True,
'admin_state_up': False,
'tenant_id': tenant_id,
'shared': False
}
@ -285,13 +285,17 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin):
# NOTE(armax): saving the auto allocated topology in a
# separate transaction will keep the Neutron DB and the
# Neutron plugin backend in sync, thus allowing for a
# more bullet proof cleanup.
# more bullet proof cleanup. Any other error will have
# to bubble up.
with context.session.begin(subtransactions=True):
context.session.add(
models.AutoAllocatedTopology(
tenant_id=tenant_id,
network_id=network_id,
router_id=router_id))
p_utils.update_network(
self.core_plugin, context,
network_id, {'admin_state_up': True})
except db_exc.DBDuplicateEntry:
LOG.error(_LE("Multiple auto-allocated networks detected for "
"tenant %(tenant)s. Attempting clean up for "
@ -302,8 +306,7 @@ class AutoAllocatedTopologyMixin(common_db_mixin.CommonDbMixin):
self._cleanup(
context, network_id=network_id,
router_id=router_id, subnets=subnets)
network_id = self._get_auto_allocated_network(
context, tenant_id)
network_id = self._get_auto_allocated_network(context, tenant_id)
return network_id
def _cleanup(self, context, network_id=None, router_id=None, subnets=None):

View File

@ -87,6 +87,8 @@ class TestAutoAllocatedTopology(base.BaseAdminNetworkTest):
network_id1 = topology['id']
self.assertIsNotNone(network_id1)
network = self.client.show_network(topology['id'])['network']
self.assertTrue(network['admin_state_up'])
resources_after1 = self._count_topology_resources()
# One network, two subnets (v4 and v6) and one router
self.assertEqual((1, self.num_subnetpools, 1), resources_after1)