Implement setting availability zone hint on network creation

If the 'network_availability_zone'-extension is enabled, this patch adds a
field to the Create Network-workflow named 'Availability Zone Hints'.

Change-Id: Ic4206d5765c2d01afedf0e64f9aa50ffce76b661
Closes-Bug: #1725617
This commit is contained in:
Trygve Vea 2017-10-22 10:51:21 +00:00
parent 5d386afb1a
commit b5896d6fbb
5 changed files with 195 additions and 7 deletions

View File

@ -139,6 +139,12 @@ class CreateNetwork(forms.SelfHandlingForm):
}),
initial=True,
required=False)
az_hints = forms.MultipleChoiceField(
label=_("Availability Zone Hints"),
required=False,
help_text=_("Availability zones where the DHCP agents may be "
"scheduled. Leaving this unset is equivalent to "
"selecting all availability zones"))
@classmethod
def _instantiate(cls, request, *args, **kwargs):
@ -237,6 +243,20 @@ class CreateNetwork(forms.SelfHandlingForm):
else:
self.fields['network_type'].choices = network_type_choices
try:
if api.neutron.is_extension_supported(request,
'network_availability_zone'):
zones = api.neutron.list_availability_zones(
self.request, 'network', 'available')
self.fields['az_hints'].choices = [(zone['name'], zone['name'])
for zone in zones]
else:
del self.fields['az_hints']
except Exception:
msg = _('Failed to get availability zone list.')
messages.warning(request, msg)
del self.fields['az_hints']
def _hide_provider_network_type(self):
self.fields['network_type'].widget = forms.HiddenInput()
self.fields['physical_network'].widget = forms.HiddenInput()
@ -261,6 +281,8 @@ class CreateNetwork(forms.SelfHandlingForm):
if network_type in self.nettypes_with_seg_id:
params['provider:segmentation_id'] = (
data['segmentation_id'])
if 'az_hints' in data and data['az_hints']:
params['availability_zone_hints'] = data['az_hints']
network = api.neutron.network_create(request, **params)
LOG.debug('Network %s was successfully created.', data['name'])
return network

View File

@ -463,6 +463,9 @@ class NetworkTests(test.BaseAdminViewTests):
'with_subnet': False}
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
MultipleTimes().AndReturn(True)
@ -485,6 +488,57 @@ class NetworkTests(test.BaseAdminViewTests):
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.neutron: ('network_create',
'is_extension_supported',
'list_availability_zones',
'subnetpool_list'),
api.keystone: ('tenant_list',)})
def test_network_create_post_with_az(self):
tenants = self.tenants.list()
tenant_id = self.tenants.first().id
network = self.networks.first()
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
params = {'name': network.name,
'tenant_id': tenant_id,
'admin_state_up': network.admin_state_up,
'router:external': True,
'shared': True,
'provider:network_type': 'local',
'with_subnet': False,
'az_hints': ['nova']}
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(True)
api.neutron.list_availability_zones(IsA(http.HttpRequest),
"network", "available")\
.AndReturn(self.neutron_availability_zones.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
MultipleTimes().AndReturn(True)
api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
AndReturn(self.subnetpools.list())
api.neutron.network_create(IsA(http.HttpRequest), **params)\
.AndReturn(network)
self.mox.ReplayAll()
form_data = {'tenant_id': tenant_id,
'name': network.name,
'admin_state': network.admin_state_up,
'external': True,
'shared': True,
'network_type': 'local',
'availability_zone_hints': ['nova']}
url = reverse('horizon:admin:networks:create')
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.neutron: ('network_create',
'subnet_create',
'is_extension_supported',
@ -508,6 +562,9 @@ class NetworkTests(test.BaseAdminViewTests):
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
MultipleTimes().AndReturn(True)
@ -551,6 +608,9 @@ class NetworkTests(test.BaseAdminViewTests):
'with_subnet': False}
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
MultipleTimes().AndReturn(True)
@ -581,6 +641,9 @@ class NetworkTests(test.BaseAdminViewTests):
api.keystone.tenant_list(
IsA(http.HttpRequest)
).MultipleTimes().AndReturn([tenants, False])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
.MultipleTimes().AndReturn(True)
@ -611,6 +674,9 @@ class NetworkTests(test.BaseAdminViewTests):
IsA(http.HttpRequest)
).MultipleTimes().AndReturn([tenants, False])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
MultipleTimes().AndReturn(True)
self.mox.ReplayAll()
@ -642,6 +708,9 @@ class NetworkTests(test.BaseAdminViewTests):
IsA(http.HttpRequest)
).MultipleTimes().AndReturn([tenants, False])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
MultipleTimes().AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
.MultipleTimes().AndReturn(True)

View File

@ -44,7 +44,7 @@ class CreateNetworkInfoAction(network_workflows.CreateNetworkInfoAction):
class CreateNetworkInfo(network_workflows.CreateNetworkInfo):
action_class = CreateNetworkInfoAction
contributes = ("net_name", "admin_state", "with_subnet")
contributes = ("net_name", "admin_state", "with_subnet", "az_hints")
def __init__(self, workflow):
self.contributes = tuple(workflow.create_network_form.fields.keys())

View File

@ -362,6 +362,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
@test.create_stubs({api.neutron: ('is_extension_supported',
'subnetpool_list')})
def test_network_create_get(self):
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -388,6 +391,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
params = {'name': network.name,
'admin_state_up': network.admin_state_up,
'shared': False}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -409,6 +415,43 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.neutron: ('network_create',
'is_extension_supported',
'list_availability_zones',
'subnetpool_list')})
def test_network_create_post_with_az(self):
network = self.networks.first()
params = {'name': network.name,
'admin_state_up': network.admin_state_up,
'shared': False,
'az_hints': ['nova']}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(True)
api.neutron.list_availability_zones(IsA(http.HttpRequest),
"network", "available")\
.AndReturn(self.neutron_availability_zones.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
AndReturn(self.subnetpools.list())
api.neutron.network_create(IsA(http.HttpRequest),
**params).AndReturn(network)
self.mox.ReplayAll()
form_data = {'net_name': network.name,
'admin_state': network.admin_state_up,
'shared': False,
'with_subnet': False,
'availability_zone_hints': ['nova']}
form_data.update(form_data_no_subnet())
url = reverse('horizon:project:networks:create')
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.neutron: ('network_create',
'is_extension_supported',
'subnetpool_list')})
@ -417,6 +460,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
params = {'name': network.name,
'admin_state_up': network.admin_state_up,
'shared': True}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -458,6 +504,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
if not test_with_ipv6:
subnet.ip_version = 4
subnet_params['ip_version'] = subnet.ip_version
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -492,6 +541,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
params = {'name': network.name,
'shared': False,
'admin_state_up': network.admin_state_up}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -516,15 +568,15 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
@test.create_stubs({api.neutron: ('network_create',
'is_extension_supported',
'subnetpool_list')})
def test_network_create_post_with_subnet_network_exception(
self,
test_with_subnetpool=False,
):
def test_network_create_post_with_subnet_network_exception(self):
network = self.networks.first()
subnet = self.subnets.first()
params = {'name': network.name,
'shared': False,
'admin_state_up': network.admin_state_up}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -556,6 +608,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
params = {'name': network.name,
'shared': False,
'admin_state_up': network.admin_state_up}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -592,6 +647,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
test_with_snpool=False):
network = self.networks.first()
subnet = self.subnets.first()
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -627,6 +685,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
):
network = self.networks.first()
subnet = self.subnets.first()
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -665,6 +726,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
network = self.networks.first()
subnet = self.subnets.first()
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -707,6 +771,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
network = self.networks.first()
subnet_v6 = self.subnets.list()[4]
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -757,6 +824,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
'gateway_ip': gateway_ip,
'enable_dhcp': subnet.enable_dhcp}
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -791,6 +861,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
network = self.networks.first()
subnet = self.subnets.first()
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
@ -828,7 +901,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
):
network = self.networks.first()
subnet = self.subnets.first()
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'network_availability_zone').\
AndReturn(False)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)

View File

@ -58,12 +58,31 @@ class CreateNetworkInfoAction(workflows.Action):
}),
initial=True,
required=False)
az_hints = forms.MultipleChoiceField(
label=_("Availability Zone Hints"),
required=False,
help_text=_("Availability zones where the DHCP agents may be "
"scheduled. Leaving this unset is equivalent to "
"selecting all availability zones"))
def __init__(self, request, *args, **kwargs):
super(CreateNetworkInfoAction, self).__init__(request,
*args, **kwargs)
if not policy.check((("network", "create_network:shared"),), request):
self.fields['shared'].widget = forms.HiddenInput()
try:
if api.neutron.is_extension_supported(request,
'network_availability_zone'):
zones = api.neutron.list_availability_zones(
self.request, 'network', 'available')
self.fields['az_hints'].choices = [(zone['name'], zone['name'])
for zone in zones]
else:
del self.fields['az_hints']
except Exception:
msg = _('Failed to get availability zone list.')
messages.warning(request, msg)
del self.fields['az_hints']
class Meta(object):
name = _("Network")
@ -74,7 +93,8 @@ class CreateNetworkInfoAction(workflows.Action):
class CreateNetworkInfo(workflows.Step):
action_class = CreateNetworkInfoAction
contributes = ("net_name", "admin_state", "with_subnet", "shared")
contributes = ("net_name", "admin_state", "with_subnet", "shared",
"az_hints")
class CreateSubnetInfoAction(workflows.Action):
@ -463,6 +483,8 @@ class CreateNetwork(workflows.Workflow):
params = {'name': data['net_name'],
'admin_state_up': data['admin_state'],
'shared': data['shared']}
if 'az_hints' in data and data['az_hints']:
params['availability_zone_hints'] = data['az_hints']
network = api.neutron.network_create(request, **params)
self.context['net_id'] = network.id
LOG.debug('Network "%s" was successfully created.',