Browse Source

Routers can be created with availability zone

When 'availability_zone'-extension is enabled, we present the user with
a drop-down menu containing a list of available availability zones.
This sets the 'availability_zone_hints' parameter on router creation.

Change-Id: I96293202ddd855823e89c4c7ba0b1f6a6423aab2
Partial-bug: #1716638
Trygve Vea 1 year ago
parent
commit
b9664a1bf1

+ 12
- 0
openstack_dashboard/api/neutron.py View File

@@ -1728,3 +1728,15 @@ def policy_get(request, policy_id, **kwargs):
1728 1728
     policy = neutronclient(request).show_qos_policy(
1729 1729
         policy_id, **kwargs).get('policy')
1730 1730
     return QoSPolicy(policy)
1731
+
1732
+
1733
+@profiler.trace
1734
+def list_availability_zones(request, resource=None, state=None):
1735
+    az_list = neutronclient(request).list_availability_zones().get(
1736
+        'availability_zones')
1737
+    if resource:
1738
+        az_list = [az for az in az_list if az['resource'] == resource]
1739
+    if state:
1740
+        az_list = [az for az in az_list if az['state'] == state]
1741
+
1742
+    return sorted(az_list, key=lambda zone: zone['name'])

+ 19
- 0
openstack_dashboard/dashboards/project/routers/forms.py View File

@@ -41,6 +41,12 @@ class CreateForm(forms.SelfHandlingForm):
41 41
                                                  required=False)
42 42
     mode = forms.ChoiceField(label=_("Router Type"))
43 43
     ha = forms.ChoiceField(label=_("High Availability Mode"))
44
+    az_hints = forms.MultipleChoiceField(
45
+        label=_("Availability Zone Hints"),
46
+        required=False,
47
+        help_text=_("Availability Zones where the router may be scheduled. "
48
+                    "Leaving this unset is equivalent to selecting all "
49
+                    "Availability Zones"))
44 50
     failure_url = 'horizon:project:routers:index'
45 51
 
46 52
     def __init__(self, request, *args, **kwargs):
@@ -70,6 +76,17 @@ class CreateForm(forms.SelfHandlingForm):
70 76
         else:
71 77
             del self.fields['external_network']
72 78
 
79
+        az_supported = api.neutron.is_extension_supported(
80
+            self.request, 'router_availability_zone')
81
+
82
+        if az_supported:
83
+            zones = api.neutron.list_availability_zones(self.request, 'router',
84
+                                                        'available')
85
+            self.fields['az_hints'].choices = [(zone['name'], zone['name'])
86
+                                               for zone in zones]
87
+        else:
88
+            del self.fields['az_hints']
89
+
73 90
     def _get_network_list(self, request):
74 91
         search_opts = {'router:external': True}
75 92
         try:
@@ -94,6 +111,8 @@ class CreateForm(forms.SelfHandlingForm):
94 111
             if 'external_network' in data and data['external_network']:
95 112
                 params['external_gateway_info'] = {'network_id':
96 113
                                                    data['external_network']}
114
+            if 'az_hints' in data and data['az_hints']:
115
+                params['availability_zone_hints'] = data['az_hints']
97 116
             if (self.dvr_allowed and data['mode'] != 'server_default'):
98 117
                 params['distributed'] = (data['mode'] == 'distributed')
99 118
             if (self.ha_allowed and data['ha'] != 'server_default'):

+ 63
- 4
openstack_dashboard/dashboards/project/routers/tests.py View File

@@ -257,7 +257,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
257 257
 
258 258
     @test.create_stubs({api.neutron: ('router_create',
259 259
                                       'get_feature_permission',
260
-                                      'network_list')})
260
+                                      'network_list',
261
+                                      'is_extension_supported')})
261 262
     def test_router_create_post(self):
262 263
         router = self.routers.first()
263 264
         api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -268,6 +269,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
268 269
             .AndReturn(False)
269 270
         api.neutron.network_list(IsA(http.HttpRequest))\
270 271
             .AndReturn(self.networks.list())
272
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
273
+                                           "router_availability_zone")\
274
+            .AndReturn(False)
271 275
         params = {'name': router.name,
272 276
                   'admin_state_up': router.admin_state_up}
273 277
         api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -284,7 +288,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
284 288
 
285 289
     @test.create_stubs({api.neutron: ('router_create',
286 290
                                       'get_feature_permission',
287
-                                      'network_list')})
291
+                                      'network_list',
292
+                                      'is_extension_supported')})
288 293
     def test_router_create_post_mode_server_default(self):
289 294
         router = self.routers.first()
290 295
         api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -295,6 +300,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
295 300
             .AndReturn(True)
296 301
         api.neutron.network_list(IsA(http.HttpRequest))\
297 302
             .AndReturn(self.networks.list())
303
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
304
+                                           "router_availability_zone")\
305
+            .AndReturn(False)
298 306
         params = {'name': router.name,
299 307
                   'admin_state_up': router.admin_state_up}
300 308
         api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -313,7 +321,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
313 321
 
314 322
     @test.create_stubs({api.neutron: ('router_create',
315 323
                                       'get_feature_permission',
316
-                                      'network_list')})
324
+                                      'network_list',
325
+                                      'is_extension_supported')})
317 326
     def test_dvr_ha_router_create_post(self):
318 327
         router = self.routers.first()
319 328
         api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -324,6 +333,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
324 333
             .MultipleTimes().AndReturn(True)
325 334
         api.neutron.network_list(IsA(http.HttpRequest))\
326 335
             .AndReturn(self.networks.list())
336
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
337
+                                           "router_availability_zone")\
338
+            .AndReturn(False)
327 339
         param = {'name': router.name,
328 340
                  'distributed': True,
329 341
                  'ha': True,
@@ -344,7 +356,47 @@ class RouterActionTests(RouterMixin, test.TestCase):
344 356
 
345 357
     @test.create_stubs({api.neutron: ('router_create',
346 358
                                       'get_feature_permission',
347
-                                      'network_list')})
359
+                                      'network_list',
360
+                                      'is_extension_supported',
361
+                                      'list_availability_zones')})
362
+    def test_az_router_create_post(self):
363
+        router = self.routers.first()
364
+        api.neutron.get_feature_permission(IsA(http.HttpRequest),
365
+                                           "dvr", "create")\
366
+            .MultipleTimes().AndReturn(False)
367
+        api.neutron.get_feature_permission(IsA(http.HttpRequest),
368
+                                           "l3-ha", "create")\
369
+            .AndReturn(False)
370
+        api.neutron.network_list(IsA(http.HttpRequest))\
371
+            .AndReturn(self.networks.list())
372
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
373
+                                           "router_availability_zone")\
374
+            .AndReturn(True)
375
+        api.neutron.list_availability_zones(IsA(http.HttpRequest),
376
+                                            "router", "available")\
377
+            .AndReturn(self.neutron_availability_zones.list())
378
+        param = {'name': router.name,
379
+                 'availability_zone_hints': ['nova'],
380
+                 'admin_state_up': router.admin_state_up}
381
+        api.neutron.router_create(IsA(http.HttpRequest), **param)\
382
+            .AndReturn(router)
383
+        self.mox.ReplayAll()
384
+
385
+        form_data = {'name': router.name,
386
+                     'mode': 'server_default',
387
+                     'ha': 'server_default',
388
+                     'az_hints': 'nova',
389
+                     'admin_state_up': router.admin_state_up}
390
+        url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
391
+        res = self.client.post(url, form_data)
392
+
393
+        self.assertNoFormErrors(res)
394
+        self.assertRedirectsNoFollow(res, self.INDEX_URL)
395
+
396
+    @test.create_stubs({api.neutron: ('router_create',
397
+                                      'get_feature_permission',
398
+                                      'network_list',
399
+                                      'is_extension_supported')})
348 400
     def test_router_create_post_exception_error_case_409(self):
349 401
         router = self.routers.first()
350 402
         api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -356,6 +408,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
356 408
         self.exceptions.neutron.status_code = 409
357 409
         api.neutron.network_list(IsA(http.HttpRequest))\
358 410
             .MultipleTimes().AndReturn(self.networks.list())
411
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
412
+                                           "router_availability_zone")\
413
+            .AndReturn(False)
359 414
         params = {'name': router.name,
360 415
                   'admin_state_up': router.admin_state_up}
361 416
         api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -372,6 +427,7 @@ class RouterActionTests(RouterMixin, test.TestCase):
372 427
 
373 428
     @test.create_stubs({api.neutron: ('router_create',
374 429
                                       'get_feature_permission',
430
+                                      'is_extension_supported',
375 431
                                       'network_list')})
376 432
     def test_router_create_post_exception_error_case_non_409(self):
377 433
         router = self.routers.first()
@@ -384,6 +440,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
384 440
         self.exceptions.neutron.status_code = 999
385 441
         api.neutron.network_list(IsA(http.HttpRequest))\
386 442
             .MultipleTimes().AndReturn(self.networks.list())
443
+        api.neutron.is_extension_supported(IsA(http.HttpRequest),
444
+                                           "router_availability_zone")\
445
+            .MultipleTimes().AndReturn(False)
387 446
         params = {'name': router.name,
388 447
                   'admin_state_up': router.admin_state_up}
389 448
         api.neutron.router_create(IsA(http.HttpRequest), **params)\

+ 12
- 1
openstack_dashboard/test/test_data/neutron_data.py View File

@@ -46,6 +46,7 @@ def data(TEST):
46 46
     TEST.ip_availability = utils.TestDataContainer()
47 47
     TEST.qos_policies = utils.TestDataContainer()
48 48
     TEST.tp_ports = utils.TestDataContainer()
49
+    TEST.neutron_availability_zones = utils.TestDataContainer()
49 50
 
50 51
     # Data return by neutronclient.
51 52
     TEST.api_agents = utils.TestDataContainer()
@@ -361,7 +362,8 @@ def data(TEST):
361 362
                    'distributed': True,
362 363
                    'external_gateway_info':
363 364
                        {'network_id': ext_net['id']},
364
-                   'tenant_id': '1'}
365
+                   'tenant_id': '1',
366
+                   'availability_zone_hints': ['nova']}
365 367
     TEST.api_routers.add(router_dict)
366 368
     TEST.routers.add(neutron.Router(router_dict))
367 369
     router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c',
@@ -879,3 +881,12 @@ def data(TEST):
879 881
         {'trunk_id': tdata['trunk_id'],
880 882
          'segmentation_type': 'vlan',
881 883
          'segmentation_id': tdata['tag_2']}))
884
+
885
+    # Availability Zones
886
+    TEST.neutron_availability_zones.add(
887
+        {
888
+            'state': 'available',
889
+            'resource': 'router',
890
+            'name': 'nova'
891
+        }
892
+    )

Loading…
Cancel
Save