summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Vea <trygve.vea@gmail.com>2017-10-22 10:51:21 +0000
committerTrygve Vea <trygve.vea@gmail.com>2017-10-24 16:25:19 +0000
commitb5896d6fbbcd0d16e7c3d26c50426585e008d20e (patch)
tree85fca98990e03d15c858473b975f604f34b62b11
parent5d386afb1a14248c1c6b655056a4afceb7a0a0bd (diff)
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
Notes
Notes (review): Code-Review+2: Akihiro Motoki <amotoki@gmail.com> Workflow+1: Akihiro Motoki <amotoki@gmail.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Wed, 25 Oct 2017 02:46:57 +0000 Reviewed-on: https://review.openstack.org/514057 Project: openstack/horizon Branch: refs/heads/master
-rw-r--r--openstack_dashboard/dashboards/admin/networks/forms.py22
-rw-r--r--openstack_dashboard/dashboards/admin/networks/tests.py69
-rw-r--r--openstack_dashboard/dashboards/admin/networks/workflows.py2
-rw-r--r--openstack_dashboard/dashboards/project/networks/tests.py85
-rw-r--r--openstack_dashboard/dashboards/project/networks/workflows.py24
5 files changed, 195 insertions, 7 deletions
diff --git a/openstack_dashboard/dashboards/admin/networks/forms.py b/openstack_dashboard/dashboards/admin/networks/forms.py
index ae5dbca..b95bff2 100644
--- a/openstack_dashboard/dashboards/admin/networks/forms.py
+++ b/openstack_dashboard/dashboards/admin/networks/forms.py
@@ -139,6 +139,12 @@ class CreateNetwork(forms.SelfHandlingForm):
139 }), 139 }),
140 initial=True, 140 initial=True,
141 required=False) 141 required=False)
142 az_hints = forms.MultipleChoiceField(
143 label=_("Availability Zone Hints"),
144 required=False,
145 help_text=_("Availability zones where the DHCP agents may be "
146 "scheduled. Leaving this unset is equivalent to "
147 "selecting all availability zones"))
142 148
143 @classmethod 149 @classmethod
144 def _instantiate(cls, request, *args, **kwargs): 150 def _instantiate(cls, request, *args, **kwargs):
@@ -237,6 +243,20 @@ class CreateNetwork(forms.SelfHandlingForm):
237 else: 243 else:
238 self.fields['network_type'].choices = network_type_choices 244 self.fields['network_type'].choices = network_type_choices
239 245
246 try:
247 if api.neutron.is_extension_supported(request,
248 'network_availability_zone'):
249 zones = api.neutron.list_availability_zones(
250 self.request, 'network', 'available')
251 self.fields['az_hints'].choices = [(zone['name'], zone['name'])
252 for zone in zones]
253 else:
254 del self.fields['az_hints']
255 except Exception:
256 msg = _('Failed to get availability zone list.')
257 messages.warning(request, msg)
258 del self.fields['az_hints']
259
240 def _hide_provider_network_type(self): 260 def _hide_provider_network_type(self):
241 self.fields['network_type'].widget = forms.HiddenInput() 261 self.fields['network_type'].widget = forms.HiddenInput()
242 self.fields['physical_network'].widget = forms.HiddenInput() 262 self.fields['physical_network'].widget = forms.HiddenInput()
@@ -261,6 +281,8 @@ class CreateNetwork(forms.SelfHandlingForm):
261 if network_type in self.nettypes_with_seg_id: 281 if network_type in self.nettypes_with_seg_id:
262 params['provider:segmentation_id'] = ( 282 params['provider:segmentation_id'] = (
263 data['segmentation_id']) 283 data['segmentation_id'])
284 if 'az_hints' in data and data['az_hints']:
285 params['availability_zone_hints'] = data['az_hints']
264 network = api.neutron.network_create(request, **params) 286 network = api.neutron.network_create(request, **params)
265 LOG.debug('Network %s was successfully created.', data['name']) 287 LOG.debug('Network %s was successfully created.', data['name'])
266 return network 288 return network
diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py
index f3e4312..34f64af 100644
--- a/openstack_dashboard/dashboards/admin/networks/tests.py
+++ b/openstack_dashboard/dashboards/admin/networks/tests.py
@@ -464,6 +464,9 @@ class NetworkTests(test.BaseAdminViewTests):
464 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\ 464 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
465 MultipleTimes().AndReturn(True) 465 MultipleTimes().AndReturn(True)
466 api.neutron.is_extension_supported(IsA(http.HttpRequest), 466 api.neutron.is_extension_supported(IsA(http.HttpRequest),
467 'network_availability_zone').\
468 MultipleTimes().AndReturn(False)
469 api.neutron.is_extension_supported(IsA(http.HttpRequest),
467 'subnet_allocation').\ 470 'subnet_allocation').\
468 MultipleTimes().AndReturn(True) 471 MultipleTimes().AndReturn(True)
469 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 472 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -486,6 +489,57 @@ class NetworkTests(test.BaseAdminViewTests):
486 self.assertRedirectsNoFollow(res, INDEX_URL) 489 self.assertRedirectsNoFollow(res, INDEX_URL)
487 490
488 @test.create_stubs({api.neutron: ('network_create', 491 @test.create_stubs({api.neutron: ('network_create',
492 'is_extension_supported',
493 'list_availability_zones',
494 'subnetpool_list'),
495 api.keystone: ('tenant_list',)})
496 def test_network_create_post_with_az(self):
497 tenants = self.tenants.list()
498 tenant_id = self.tenants.first().id
499 network = self.networks.first()
500
501 api.keystone.tenant_list(IsA(http.HttpRequest))\
502 .AndReturn([tenants, False])
503 params = {'name': network.name,
504 'tenant_id': tenant_id,
505 'admin_state_up': network.admin_state_up,
506 'router:external': True,
507 'shared': True,
508 'provider:network_type': 'local',
509 'with_subnet': False,
510 'az_hints': ['nova']}
511 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
512 MultipleTimes().AndReturn(True)
513 api.neutron.is_extension_supported(IsA(http.HttpRequest),
514 'network_availability_zone').\
515 MultipleTimes().AndReturn(True)
516 api.neutron.list_availability_zones(IsA(http.HttpRequest),
517 "network", "available")\
518 .AndReturn(self.neutron_availability_zones.list())
519 api.neutron.is_extension_supported(IsA(http.HttpRequest),
520 'subnet_allocation').\
521 MultipleTimes().AndReturn(True)
522 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
523 AndReturn(self.subnetpools.list())
524 api.neutron.network_create(IsA(http.HttpRequest), **params)\
525 .AndReturn(network)
526
527 self.mox.ReplayAll()
528
529 form_data = {'tenant_id': tenant_id,
530 'name': network.name,
531 'admin_state': network.admin_state_up,
532 'external': True,
533 'shared': True,
534 'network_type': 'local',
535 'availability_zone_hints': ['nova']}
536 url = reverse('horizon:admin:networks:create')
537 res = self.client.post(url, form_data)
538
539 self.assertNoFormErrors(res)
540 self.assertRedirectsNoFollow(res, INDEX_URL)
541
542 @test.create_stubs({api.neutron: ('network_create',
489 'subnet_create', 543 'subnet_create',
490 'is_extension_supported', 544 'is_extension_supported',
491 'subnetpool_list'), 545 'subnetpool_list'),
@@ -509,6 +563,9 @@ class NetworkTests(test.BaseAdminViewTests):
509 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\ 563 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
510 MultipleTimes().AndReturn(True) 564 MultipleTimes().AndReturn(True)
511 api.neutron.is_extension_supported(IsA(http.HttpRequest), 565 api.neutron.is_extension_supported(IsA(http.HttpRequest),
566 'network_availability_zone').\
567 MultipleTimes().AndReturn(False)
568 api.neutron.is_extension_supported(IsA(http.HttpRequest),
512 'subnet_allocation').\ 569 'subnet_allocation').\
513 MultipleTimes().AndReturn(True) 570 MultipleTimes().AndReturn(True)
514 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 571 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -552,6 +609,9 @@ class NetworkTests(test.BaseAdminViewTests):
552 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\ 609 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
553 MultipleTimes().AndReturn(True) 610 MultipleTimes().AndReturn(True)
554 api.neutron.is_extension_supported(IsA(http.HttpRequest), 611 api.neutron.is_extension_supported(IsA(http.HttpRequest),
612 'network_availability_zone').\
613 MultipleTimes().AndReturn(False)
614 api.neutron.is_extension_supported(IsA(http.HttpRequest),
555 'subnet_allocation').\ 615 'subnet_allocation').\
556 MultipleTimes().AndReturn(True) 616 MultipleTimes().AndReturn(True)
557 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 617 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -581,6 +641,9 @@ class NetworkTests(test.BaseAdminViewTests):
581 api.keystone.tenant_list( 641 api.keystone.tenant_list(
582 IsA(http.HttpRequest) 642 IsA(http.HttpRequest)
583 ).MultipleTimes().AndReturn([tenants, False]) 643 ).MultipleTimes().AndReturn([tenants, False])
644 api.neutron.is_extension_supported(IsA(http.HttpRequest),
645 'network_availability_zone').\
646 MultipleTimes().AndReturn(False)
584 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\ 647 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
585 .MultipleTimes().AndReturn(True) 648 .MultipleTimes().AndReturn(True)
586 649
@@ -611,6 +674,9 @@ class NetworkTests(test.BaseAdminViewTests):
611 IsA(http.HttpRequest) 674 IsA(http.HttpRequest)
612 ).MultipleTimes().AndReturn([tenants, False]) 675 ).MultipleTimes().AndReturn([tenants, False])
613 676
677 api.neutron.is_extension_supported(IsA(http.HttpRequest),
678 'network_availability_zone').\
679 MultipleTimes().AndReturn(False)
614 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\ 680 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
615 MultipleTimes().AndReturn(True) 681 MultipleTimes().AndReturn(True)
616 self.mox.ReplayAll() 682 self.mox.ReplayAll()
@@ -642,6 +708,9 @@ class NetworkTests(test.BaseAdminViewTests):
642 IsA(http.HttpRequest) 708 IsA(http.HttpRequest)
643 ).MultipleTimes().AndReturn([tenants, False]) 709 ).MultipleTimes().AndReturn([tenants, False])
644 710
711 api.neutron.is_extension_supported(IsA(http.HttpRequest),
712 'network_availability_zone').\
713 MultipleTimes().AndReturn(False)
645 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\ 714 api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
646 .MultipleTimes().AndReturn(True) 715 .MultipleTimes().AndReturn(True)
647 716
diff --git a/openstack_dashboard/dashboards/admin/networks/workflows.py b/openstack_dashboard/dashboards/admin/networks/workflows.py
index ff98b33..d8925ae 100644
--- a/openstack_dashboard/dashboards/admin/networks/workflows.py
+++ b/openstack_dashboard/dashboards/admin/networks/workflows.py
@@ -44,7 +44,7 @@ class CreateNetworkInfoAction(network_workflows.CreateNetworkInfoAction):
44 44
45class CreateNetworkInfo(network_workflows.CreateNetworkInfo): 45class CreateNetworkInfo(network_workflows.CreateNetworkInfo):
46 action_class = CreateNetworkInfoAction 46 action_class = CreateNetworkInfoAction
47 contributes = ("net_name", "admin_state", "with_subnet") 47 contributes = ("net_name", "admin_state", "with_subnet", "az_hints")
48 48
49 def __init__(self, workflow): 49 def __init__(self, workflow):
50 self.contributes = tuple(workflow.create_network_form.fields.keys()) 50 self.contributes = tuple(workflow.create_network_form.fields.keys())
diff --git a/openstack_dashboard/dashboards/project/networks/tests.py b/openstack_dashboard/dashboards/project/networks/tests.py
index ac097fe..bd2c860 100644
--- a/openstack_dashboard/dashboards/project/networks/tests.py
+++ b/openstack_dashboard/dashboards/project/networks/tests.py
@@ -363,6 +363,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
363 'subnetpool_list')}) 363 'subnetpool_list')})
364 def test_network_create_get(self): 364 def test_network_create_get(self):
365 api.neutron.is_extension_supported(IsA(http.HttpRequest), 365 api.neutron.is_extension_supported(IsA(http.HttpRequest),
366 'network_availability_zone').\
367 AndReturn(False)
368 api.neutron.is_extension_supported(IsA(http.HttpRequest),
366 'subnet_allocation').\ 369 'subnet_allocation').\
367 AndReturn(True) 370 AndReturn(True)
368 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 371 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -389,6 +392,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
389 'admin_state_up': network.admin_state_up, 392 'admin_state_up': network.admin_state_up,
390 'shared': False} 393 'shared': False}
391 api.neutron.is_extension_supported(IsA(http.HttpRequest), 394 api.neutron.is_extension_supported(IsA(http.HttpRequest),
395 'network_availability_zone').\
396 AndReturn(False)
397 api.neutron.is_extension_supported(IsA(http.HttpRequest),
392 'subnet_allocation').\ 398 'subnet_allocation').\
393 AndReturn(True) 399 AndReturn(True)
394 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 400 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -411,6 +417,43 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
411 417
412 @test.create_stubs({api.neutron: ('network_create', 418 @test.create_stubs({api.neutron: ('network_create',
413 'is_extension_supported', 419 'is_extension_supported',
420 'list_availability_zones',
421 'subnetpool_list')})
422 def test_network_create_post_with_az(self):
423 network = self.networks.first()
424 params = {'name': network.name,
425 'admin_state_up': network.admin_state_up,
426 'shared': False,
427 'az_hints': ['nova']}
428 api.neutron.is_extension_supported(IsA(http.HttpRequest),
429 'network_availability_zone').\
430 AndReturn(True)
431 api.neutron.list_availability_zones(IsA(http.HttpRequest),
432 "network", "available")\
433 .AndReturn(self.neutron_availability_zones.list())
434 api.neutron.is_extension_supported(IsA(http.HttpRequest),
435 'subnet_allocation').\
436 AndReturn(True)
437 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
438 AndReturn(self.subnetpools.list())
439 api.neutron.network_create(IsA(http.HttpRequest),
440 **params).AndReturn(network)
441 self.mox.ReplayAll()
442
443 form_data = {'net_name': network.name,
444 'admin_state': network.admin_state_up,
445 'shared': False,
446 'with_subnet': False,
447 'availability_zone_hints': ['nova']}
448 form_data.update(form_data_no_subnet())
449 url = reverse('horizon:project:networks:create')
450 res = self.client.post(url, form_data)
451
452 self.assertNoFormErrors(res)
453 self.assertRedirectsNoFollow(res, INDEX_URL)
454
455 @test.create_stubs({api.neutron: ('network_create',
456 'is_extension_supported',
414 'subnetpool_list')}) 457 'subnetpool_list')})
415 def test_network_create_post_with_shared(self): 458 def test_network_create_post_with_shared(self):
416 network = self.networks.first() 459 network = self.networks.first()
@@ -418,6 +461,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
418 'admin_state_up': network.admin_state_up, 461 'admin_state_up': network.admin_state_up,
419 'shared': True} 462 'shared': True}
420 api.neutron.is_extension_supported(IsA(http.HttpRequest), 463 api.neutron.is_extension_supported(IsA(http.HttpRequest),
464 'network_availability_zone').\
465 AndReturn(False)
466 api.neutron.is_extension_supported(IsA(http.HttpRequest),
421 'subnet_allocation').\ 467 'subnet_allocation').\
422 AndReturn(True) 468 AndReturn(True)
423 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 469 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -459,6 +505,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
459 subnet.ip_version = 4 505 subnet.ip_version = 4
460 subnet_params['ip_version'] = subnet.ip_version 506 subnet_params['ip_version'] = subnet.ip_version
461 api.neutron.is_extension_supported(IsA(http.HttpRequest), 507 api.neutron.is_extension_supported(IsA(http.HttpRequest),
508 'network_availability_zone').\
509 AndReturn(False)
510 api.neutron.is_extension_supported(IsA(http.HttpRequest),
462 'subnet_allocation').\ 511 'subnet_allocation').\
463 AndReturn(True) 512 AndReturn(True)
464 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 513 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -493,6 +542,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
493 'shared': False, 542 'shared': False,
494 'admin_state_up': network.admin_state_up} 543 'admin_state_up': network.admin_state_up}
495 api.neutron.is_extension_supported(IsA(http.HttpRequest), 544 api.neutron.is_extension_supported(IsA(http.HttpRequest),
545 'network_availability_zone').\
546 AndReturn(False)
547 api.neutron.is_extension_supported(IsA(http.HttpRequest),
496 'subnet_allocation').\ 548 'subnet_allocation').\
497 AndReturn(True) 549 AndReturn(True)
498 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 550 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -516,16 +568,16 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
516 @test.create_stubs({api.neutron: ('network_create', 568 @test.create_stubs({api.neutron: ('network_create',
517 'is_extension_supported', 569 'is_extension_supported',
518 'subnetpool_list')}) 570 'subnetpool_list')})
519 def test_network_create_post_with_subnet_network_exception( 571 def test_network_create_post_with_subnet_network_exception(self):
520 self,
521 test_with_subnetpool=False,
522 ):
523 network = self.networks.first() 572 network = self.networks.first()
524 subnet = self.subnets.first() 573 subnet = self.subnets.first()
525 params = {'name': network.name, 574 params = {'name': network.name,
526 'shared': False, 575 'shared': False,
527 'admin_state_up': network.admin_state_up} 576 'admin_state_up': network.admin_state_up}
528 api.neutron.is_extension_supported(IsA(http.HttpRequest), 577 api.neutron.is_extension_supported(IsA(http.HttpRequest),
578 'network_availability_zone').\
579 AndReturn(False)
580 api.neutron.is_extension_supported(IsA(http.HttpRequest),
529 'subnet_allocation').\ 581 'subnet_allocation').\
530 AndReturn(True) 582 AndReturn(True)
531 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 583 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -557,6 +609,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
557 'shared': False, 609 'shared': False,
558 'admin_state_up': network.admin_state_up} 610 'admin_state_up': network.admin_state_up}
559 api.neutron.is_extension_supported(IsA(http.HttpRequest), 611 api.neutron.is_extension_supported(IsA(http.HttpRequest),
612 'network_availability_zone').\
613 AndReturn(False)
614 api.neutron.is_extension_supported(IsA(http.HttpRequest),
560 'subnet_allocation').\ 615 'subnet_allocation').\
561 AndReturn(True) 616 AndReturn(True)
562 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 617 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -593,6 +648,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
593 network = self.networks.first() 648 network = self.networks.first()
594 subnet = self.subnets.first() 649 subnet = self.subnets.first()
595 api.neutron.is_extension_supported(IsA(http.HttpRequest), 650 api.neutron.is_extension_supported(IsA(http.HttpRequest),
651 'network_availability_zone').\
652 AndReturn(False)
653 api.neutron.is_extension_supported(IsA(http.HttpRequest),
596 'subnet_allocation').\ 654 'subnet_allocation').\
597 AndReturn(True) 655 AndReturn(True)
598 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 656 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -628,6 +686,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
628 network = self.networks.first() 686 network = self.networks.first()
629 subnet = self.subnets.first() 687 subnet = self.subnets.first()
630 api.neutron.is_extension_supported(IsA(http.HttpRequest), 688 api.neutron.is_extension_supported(IsA(http.HttpRequest),
689 'network_availability_zone').\
690 AndReturn(False)
691 api.neutron.is_extension_supported(IsA(http.HttpRequest),
631 'subnet_allocation').\ 692 'subnet_allocation').\
632 AndReturn(True) 693 AndReturn(True)
633 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 694 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -666,6 +727,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
666 subnet = self.subnets.first() 727 subnet = self.subnets.first()
667 728
668 api.neutron.is_extension_supported(IsA(http.HttpRequest), 729 api.neutron.is_extension_supported(IsA(http.HttpRequest),
730 'network_availability_zone').\
731 AndReturn(False)
732 api.neutron.is_extension_supported(IsA(http.HttpRequest),
669 'subnet_allocation').\ 733 'subnet_allocation').\
670 AndReturn(True) 734 AndReturn(True)
671 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 735 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -708,6 +772,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
708 subnet_v6 = self.subnets.list()[4] 772 subnet_v6 = self.subnets.list()[4]
709 773
710 api.neutron.is_extension_supported(IsA(http.HttpRequest), 774 api.neutron.is_extension_supported(IsA(http.HttpRequest),
775 'network_availability_zone').\
776 AndReturn(False)
777 api.neutron.is_extension_supported(IsA(http.HttpRequest),
711 'subnet_allocation').\ 778 'subnet_allocation').\
712 AndReturn(True) 779 AndReturn(True)
713 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 780 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -758,6 +825,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
758 'enable_dhcp': subnet.enable_dhcp} 825 'enable_dhcp': subnet.enable_dhcp}
759 826
760 api.neutron.is_extension_supported(IsA(http.HttpRequest), 827 api.neutron.is_extension_supported(IsA(http.HttpRequest),
828 'network_availability_zone').\
829 AndReturn(False)
830 api.neutron.is_extension_supported(IsA(http.HttpRequest),
761 'subnet_allocation').\ 831 'subnet_allocation').\
762 AndReturn(True) 832 AndReturn(True)
763 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 833 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -792,6 +862,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
792 subnet = self.subnets.first() 862 subnet = self.subnets.first()
793 863
794 api.neutron.is_extension_supported(IsA(http.HttpRequest), 864 api.neutron.is_extension_supported(IsA(http.HttpRequest),
865 'network_availability_zone').\
866 AndReturn(False)
867 api.neutron.is_extension_supported(IsA(http.HttpRequest),
795 'subnet_allocation').\ 868 'subnet_allocation').\
796 AndReturn(True) 869 AndReturn(True)
797 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\ 870 api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
@@ -828,7 +901,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
828 ): 901 ):
829 network = self.networks.first() 902 network = self.networks.first()
830 subnet = self.subnets.first() 903 subnet = self.subnets.first()
831 904 api.neutron.is_extension_supported(IsA(http.HttpRequest),
905 'network_availability_zone').\
906 AndReturn(False)
832 api.neutron.is_extension_supported(IsA(http.HttpRequest), 907 api.neutron.is_extension_supported(IsA(http.HttpRequest),
833 'subnet_allocation').\ 908 'subnet_allocation').\
834 AndReturn(True) 909 AndReturn(True)
diff --git a/openstack_dashboard/dashboards/project/networks/workflows.py b/openstack_dashboard/dashboards/project/networks/workflows.py
index 8cde19e..58a3974 100644
--- a/openstack_dashboard/dashboards/project/networks/workflows.py
+++ b/openstack_dashboard/dashboards/project/networks/workflows.py
@@ -58,12 +58,31 @@ class CreateNetworkInfoAction(workflows.Action):
58 }), 58 }),
59 initial=True, 59 initial=True,
60 required=False) 60 required=False)
61 az_hints = forms.MultipleChoiceField(
62 label=_("Availability Zone Hints"),
63 required=False,
64 help_text=_("Availability zones where the DHCP agents may be "
65 "scheduled. Leaving this unset is equivalent to "
66 "selecting all availability zones"))
61 67
62 def __init__(self, request, *args, **kwargs): 68 def __init__(self, request, *args, **kwargs):
63 super(CreateNetworkInfoAction, self).__init__(request, 69 super(CreateNetworkInfoAction, self).__init__(request,
64 *args, **kwargs) 70 *args, **kwargs)
65 if not policy.check((("network", "create_network:shared"),), request): 71 if not policy.check((("network", "create_network:shared"),), request):
66 self.fields['shared'].widget = forms.HiddenInput() 72 self.fields['shared'].widget = forms.HiddenInput()
73 try:
74 if api.neutron.is_extension_supported(request,
75 'network_availability_zone'):
76 zones = api.neutron.list_availability_zones(
77 self.request, 'network', 'available')
78 self.fields['az_hints'].choices = [(zone['name'], zone['name'])
79 for zone in zones]
80 else:
81 del self.fields['az_hints']
82 except Exception:
83 msg = _('Failed to get availability zone list.')
84 messages.warning(request, msg)
85 del self.fields['az_hints']
67 86
68 class Meta(object): 87 class Meta(object):
69 name = _("Network") 88 name = _("Network")
@@ -74,7 +93,8 @@ class CreateNetworkInfoAction(workflows.Action):
74 93
75class CreateNetworkInfo(workflows.Step): 94class CreateNetworkInfo(workflows.Step):
76 action_class = CreateNetworkInfoAction 95 action_class = CreateNetworkInfoAction
77 contributes = ("net_name", "admin_state", "with_subnet", "shared") 96 contributes = ("net_name", "admin_state", "with_subnet", "shared",
97 "az_hints")
78 98
79 99
80class CreateSubnetInfoAction(workflows.Action): 100class CreateSubnetInfoAction(workflows.Action):
@@ -463,6 +483,8 @@ class CreateNetwork(workflows.Workflow):
463 params = {'name': data['net_name'], 483 params = {'name': data['net_name'],
464 'admin_state_up': data['admin_state'], 484 'admin_state_up': data['admin_state'],
465 'shared': data['shared']} 485 'shared': data['shared']}
486 if 'az_hints' in data and data['az_hints']:
487 params['availability_zone_hints'] = data['az_hints']
466 network = api.neutron.network_create(request, **params) 488 network = api.neutron.network_create(request, **params)
467 self.context['net_id'] = network.id 489 self.context['net_id'] = network.id
468 LOG.debug('Network "%s" was successfully created.', 490 LOG.debug('Network "%s" was successfully created.',