Browse Source

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
tags/13.0.0.0b2
Trygve Vea 1 year ago
parent
commit
b5896d6fbb

+ 22
- 0
openstack_dashboard/dashboards/admin/networks/forms.py View File

@@ -139,6 +139,12 @@ class CreateNetwork(forms.SelfHandlingForm):
139 139
                                      }),
140 140
                                      initial=True,
141 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 149
     @classmethod
144 150
     def _instantiate(cls, request, *args, **kwargs):
@@ -237,6 +243,20 @@ class CreateNetwork(forms.SelfHandlingForm):
237 243
             else:
238 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 260
     def _hide_provider_network_type(self):
241 261
         self.fields['network_type'].widget = forms.HiddenInput()
242 262
         self.fields['physical_network'].widget = forms.HiddenInput()
@@ -261,6 +281,8 @@ class CreateNetwork(forms.SelfHandlingForm):
261 281
                 if network_type in self.nettypes_with_seg_id:
262 282
                     params['provider:segmentation_id'] = (
263 283
                         data['segmentation_id'])
284
+            if 'az_hints' in data and data['az_hints']:
285
+                params['availability_zone_hints'] = data['az_hints']
264 286
             network = api.neutron.network_create(request, **params)
265 287
             LOG.debug('Network %s was successfully created.', data['name'])
266 288
             return network

+ 69
- 0
openstack_dashboard/dashboards/admin/networks/tests.py View File

@@ -463,6 +463,9 @@ class NetworkTests(test.BaseAdminViewTests):
463 463
                   'with_subnet': False}
464 464
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
465 465
             MultipleTimes().AndReturn(True)
466
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
467
+                                           'network_availability_zone').\
468
+            MultipleTimes().AndReturn(False)
466 469
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
467 470
                                            'subnet_allocation').\
468 471
             MultipleTimes().AndReturn(True)
@@ -485,6 +488,57 @@ class NetworkTests(test.BaseAdminViewTests):
485 488
         self.assertNoFormErrors(res)
486 489
         self.assertRedirectsNoFollow(res, INDEX_URL)
487 490
 
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
+
488 542
     @test.create_stubs({api.neutron: ('network_create',
489 543
                                       'subnet_create',
490 544
                                       'is_extension_supported',
@@ -508,6 +562,9 @@ class NetworkTests(test.BaseAdminViewTests):
508 562
 
509 563
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
510 564
             MultipleTimes().AndReturn(True)
565
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
566
+                                           'network_availability_zone').\
567
+            MultipleTimes().AndReturn(False)
511 568
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
512 569
                                            'subnet_allocation').\
513 570
             MultipleTimes().AndReturn(True)
@@ -551,6 +608,9 @@ class NetworkTests(test.BaseAdminViewTests):
551 608
                   'with_subnet': False}
552 609
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
553 610
             MultipleTimes().AndReturn(True)
611
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
612
+                                           'network_availability_zone').\
613
+            MultipleTimes().AndReturn(False)
554 614
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
555 615
                                            'subnet_allocation').\
556 616
             MultipleTimes().AndReturn(True)
@@ -581,6 +641,9 @@ class NetworkTests(test.BaseAdminViewTests):
581 641
         api.keystone.tenant_list(
582 642
             IsA(http.HttpRequest)
583 643
         ).MultipleTimes().AndReturn([tenants, False])
644
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
645
+                                           'network_availability_zone').\
646
+            MultipleTimes().AndReturn(False)
584 647
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
585 648
             .MultipleTimes().AndReturn(True)
586 649
 
@@ -611,6 +674,9 @@ class NetworkTests(test.BaseAdminViewTests):
611 674
             IsA(http.HttpRequest)
612 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 680
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider').\
615 681
             MultipleTimes().AndReturn(True)
616 682
         self.mox.ReplayAll()
@@ -642,6 +708,9 @@ class NetworkTests(test.BaseAdminViewTests):
642 708
             IsA(http.HttpRequest)
643 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 714
         api.neutron.is_extension_supported(IsA(http.HttpRequest), 'provider')\
646 715
             .MultipleTimes().AndReturn(True)
647 716
 

+ 1
- 1
openstack_dashboard/dashboards/admin/networks/workflows.py View File

@@ -44,7 +44,7 @@ class CreateNetworkInfoAction(network_workflows.CreateNetworkInfoAction):
44 44
 
45 45
 class CreateNetworkInfo(network_workflows.CreateNetworkInfo):
46 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 49
     def __init__(self, workflow):
50 50
         self.contributes = tuple(workflow.create_network_form.fields.keys())

+ 80
- 5
openstack_dashboard/dashboards/project/networks/tests.py View File

@@ -362,6 +362,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
362 362
     @test.create_stubs({api.neutron: ('is_extension_supported',
363 363
                                       'subnetpool_list')})
364 364
     def test_network_create_get(self):
365
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
366
+                                           'network_availability_zone').\
367
+            AndReturn(False)
365 368
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
366 369
                                            'subnet_allocation').\
367 370
             AndReturn(True)
@@ -388,6 +391,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
388 391
         params = {'name': network.name,
389 392
                   'admin_state_up': network.admin_state_up,
390 393
                   'shared': False}
394
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
395
+                                           'network_availability_zone').\
396
+            AndReturn(False)
391 397
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
392 398
                                            'subnet_allocation').\
393 399
             AndReturn(True)
@@ -409,6 +415,43 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
409 415
         self.assertNoFormErrors(res)
410 416
         self.assertRedirectsNoFollow(res, INDEX_URL)
411 417
 
418
+    @test.create_stubs({api.neutron: ('network_create',
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
+
412 455
     @test.create_stubs({api.neutron: ('network_create',
413 456
                                       'is_extension_supported',
414 457
                                       'subnetpool_list')})
@@ -417,6 +460,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
417 460
         params = {'name': network.name,
418 461
                   'admin_state_up': network.admin_state_up,
419 462
                   'shared': True}
463
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
464
+                                           'network_availability_zone').\
465
+            AndReturn(False)
420 466
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
421 467
                                            'subnet_allocation').\
422 468
             AndReturn(True)
@@ -458,6 +504,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
458 504
         if not test_with_ipv6:
459 505
             subnet.ip_version = 4
460 506
             subnet_params['ip_version'] = subnet.ip_version
507
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
508
+                                           'network_availability_zone').\
509
+            AndReturn(False)
461 510
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
462 511
                                            'subnet_allocation').\
463 512
             AndReturn(True)
@@ -492,6 +541,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
492 541
         params = {'name': network.name,
493 542
                   'shared': False,
494 543
                   'admin_state_up': network.admin_state_up}
544
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
545
+                                           'network_availability_zone').\
546
+            AndReturn(False)
495 547
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
496 548
                                            'subnet_allocation').\
497 549
             AndReturn(True)
@@ -516,15 +568,15 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
516 568
     @test.create_stubs({api.neutron: ('network_create',
517 569
                                       'is_extension_supported',
518 570
                                       'subnetpool_list')})
519
-    def test_network_create_post_with_subnet_network_exception(
520
-        self,
521
-        test_with_subnetpool=False,
522
-    ):
571
+    def test_network_create_post_with_subnet_network_exception(self):
523 572
         network = self.networks.first()
524 573
         subnet = self.subnets.first()
525 574
         params = {'name': network.name,
526 575
                   'shared': False,
527 576
                   'admin_state_up': network.admin_state_up}
577
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
578
+                                           'network_availability_zone').\
579
+            AndReturn(False)
528 580
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
529 581
                                            'subnet_allocation').\
530 582
             AndReturn(True)
@@ -556,6 +608,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
556 608
         params = {'name': network.name,
557 609
                   'shared': False,
558 610
                   'admin_state_up': network.admin_state_up}
611
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
612
+                                           'network_availability_zone').\
613
+            AndReturn(False)
559 614
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
560 615
                                            'subnet_allocation').\
561 616
             AndReturn(True)
@@ -592,6 +647,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
592 647
                                                     test_with_snpool=False):
593 648
         network = self.networks.first()
594 649
         subnet = self.subnets.first()
650
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
651
+                                           'network_availability_zone').\
652
+            AndReturn(False)
595 653
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
596 654
                                            'subnet_allocation').\
597 655
             AndReturn(True)
@@ -627,6 +685,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
627 685
     ):
628 686
         network = self.networks.first()
629 687
         subnet = self.subnets.first()
688
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
689
+                                           'network_availability_zone').\
690
+            AndReturn(False)
630 691
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
631 692
                                            'subnet_allocation').\
632 693
             AndReturn(True)
@@ -665,6 +726,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
665 726
         network = self.networks.first()
666 727
         subnet = self.subnets.first()
667 728
 
729
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
730
+                                           'network_availability_zone').\
731
+            AndReturn(False)
668 732
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
669 733
                                            'subnet_allocation').\
670 734
             AndReturn(True)
@@ -707,6 +771,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
707 771
         network = self.networks.first()
708 772
         subnet_v6 = self.subnets.list()[4]
709 773
 
774
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
775
+                                           'network_availability_zone').\
776
+            AndReturn(False)
710 777
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
711 778
                                            'subnet_allocation').\
712 779
             AndReturn(True)
@@ -757,6 +824,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
757 824
                          'gateway_ip': gateway_ip,
758 825
                          'enable_dhcp': subnet.enable_dhcp}
759 826
 
827
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
828
+                                           'network_availability_zone').\
829
+            AndReturn(False)
760 830
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
761 831
                                            'subnet_allocation').\
762 832
             AndReturn(True)
@@ -791,6 +861,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
791 861
         network = self.networks.first()
792 862
         subnet = self.subnets.first()
793 863
 
864
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
865
+                                           'network_availability_zone').\
866
+            AndReturn(False)
794 867
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
795 868
                                            'subnet_allocation').\
796 869
             AndReturn(True)
@@ -828,7 +901,9 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
828 901
     ):
829 902
         network = self.networks.first()
830 903
         subnet = self.subnets.first()
831
-
904
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
905
+                                           'network_availability_zone').\
906
+            AndReturn(False)
832 907
         api.neutron.is_extension_supported(IsA(http.HttpRequest),
833 908
                                            'subnet_allocation').\
834 909
             AndReturn(True)

+ 23
- 1
openstack_dashboard/dashboards/project/networks/workflows.py View File

@@ -58,12 +58,31 @@ class CreateNetworkInfoAction(workflows.Action):
58 58
                                      }),
59 59
                                      initial=True,
60 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 68
     def __init__(self, request, *args, **kwargs):
63 69
         super(CreateNetworkInfoAction, self).__init__(request,
64 70
                                                       *args, **kwargs)
65 71
         if not policy.check((("network", "create_network:shared"),), request):
66 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 87
     class Meta(object):
69 88
         name = _("Network")
@@ -74,7 +93,8 @@ class CreateNetworkInfoAction(workflows.Action):
74 93
 
75 94
 class CreateNetworkInfo(workflows.Step):
76 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
 
80 100
 class CreateSubnetInfoAction(workflows.Action):
@@ -463,6 +483,8 @@ class CreateNetwork(workflows.Workflow):
463 483
             params = {'name': data['net_name'],
464 484
                       'admin_state_up': data['admin_state'],
465 485
                       'shared': data['shared']}
486
+            if 'az_hints' in data and data['az_hints']:
487
+                params['availability_zone_hints'] = data['az_hints']
466 488
             network = api.neutron.network_create(request, **params)
467 489
             self.context['net_id'] = network.id
468 490
             LOG.debug('Network "%s" was successfully created.',

Loading…
Cancel
Save