summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openstack_dashboard/api/neutron.py12
-rw-r--r--openstack_dashboard/dashboards/project/routers/forms.py19
-rw-r--r--openstack_dashboard/dashboards/project/routers/tests.py67
-rw-r--r--openstack_dashboard/test/test_data/neutron_data.py13
4 files changed, 106 insertions, 5 deletions
diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py
index 49e9478..a573500 100644
--- a/openstack_dashboard/api/neutron.py
+++ b/openstack_dashboard/api/neutron.py
@@ -1728,3 +1728,15 @@ def policy_get(request, policy_id, **kwargs):
1728 policy = neutronclient(request).show_qos_policy( 1728 policy = neutronclient(request).show_qos_policy(
1729 policy_id, **kwargs).get('policy') 1729 policy_id, **kwargs).get('policy')
1730 return QoSPolicy(policy) 1730 return QoSPolicy(policy)
1731
1732
1733@profiler.trace
1734def 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'])
diff --git a/openstack_dashboard/dashboards/project/routers/forms.py b/openstack_dashboard/dashboards/project/routers/forms.py
index 27e9d95..f5ad224 100644
--- a/openstack_dashboard/dashboards/project/routers/forms.py
+++ b/openstack_dashboard/dashboards/project/routers/forms.py
@@ -41,6 +41,12 @@ class CreateForm(forms.SelfHandlingForm):
41 required=False) 41 required=False)
42 mode = forms.ChoiceField(label=_("Router Type")) 42 mode = forms.ChoiceField(label=_("Router Type"))
43 ha = forms.ChoiceField(label=_("High Availability Mode")) 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 failure_url = 'horizon:project:routers:index' 50 failure_url = 'horizon:project:routers:index'
45 51
46 def __init__(self, request, *args, **kwargs): 52 def __init__(self, request, *args, **kwargs):
@@ -70,6 +76,17 @@ class CreateForm(forms.SelfHandlingForm):
70 else: 76 else:
71 del self.fields['external_network'] 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 def _get_network_list(self, request): 90 def _get_network_list(self, request):
74 search_opts = {'router:external': True} 91 search_opts = {'router:external': True}
75 try: 92 try:
@@ -94,6 +111,8 @@ class CreateForm(forms.SelfHandlingForm):
94 if 'external_network' in data and data['external_network']: 111 if 'external_network' in data and data['external_network']:
95 params['external_gateway_info'] = {'network_id': 112 params['external_gateway_info'] = {'network_id':
96 data['external_network']} 113 data['external_network']}
114 if 'az_hints' in data and data['az_hints']:
115 params['availability_zone_hints'] = data['az_hints']
97 if (self.dvr_allowed and data['mode'] != 'server_default'): 116 if (self.dvr_allowed and data['mode'] != 'server_default'):
98 params['distributed'] = (data['mode'] == 'distributed') 117 params['distributed'] = (data['mode'] == 'distributed')
99 if (self.ha_allowed and data['ha'] != 'server_default'): 118 if (self.ha_allowed and data['ha'] != 'server_default'):
diff --git a/openstack_dashboard/dashboards/project/routers/tests.py b/openstack_dashboard/dashboards/project/routers/tests.py
index 4a9812d..b18bea5 100644
--- a/openstack_dashboard/dashboards/project/routers/tests.py
+++ b/openstack_dashboard/dashboards/project/routers/tests.py
@@ -257,7 +257,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
257 257
258 @test.create_stubs({api.neutron: ('router_create', 258 @test.create_stubs({api.neutron: ('router_create',
259 'get_feature_permission', 259 'get_feature_permission',
260 'network_list')}) 260 'network_list',
261 'is_extension_supported')})
261 def test_router_create_post(self): 262 def test_router_create_post(self):
262 router = self.routers.first() 263 router = self.routers.first()
263 api.neutron.get_feature_permission(IsA(http.HttpRequest), 264 api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -268,6 +269,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
268 .AndReturn(False) 269 .AndReturn(False)
269 api.neutron.network_list(IsA(http.HttpRequest))\ 270 api.neutron.network_list(IsA(http.HttpRequest))\
270 .AndReturn(self.networks.list()) 271 .AndReturn(self.networks.list())
272 api.neutron.is_extension_supported(IsA(http.HttpRequest),
273 "router_availability_zone")\
274 .AndReturn(False)
271 params = {'name': router.name, 275 params = {'name': router.name,
272 'admin_state_up': router.admin_state_up} 276 'admin_state_up': router.admin_state_up}
273 api.neutron.router_create(IsA(http.HttpRequest), **params)\ 277 api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -284,7 +288,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
284 288
285 @test.create_stubs({api.neutron: ('router_create', 289 @test.create_stubs({api.neutron: ('router_create',
286 'get_feature_permission', 290 'get_feature_permission',
287 'network_list')}) 291 'network_list',
292 'is_extension_supported')})
288 def test_router_create_post_mode_server_default(self): 293 def test_router_create_post_mode_server_default(self):
289 router = self.routers.first() 294 router = self.routers.first()
290 api.neutron.get_feature_permission(IsA(http.HttpRequest), 295 api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -295,6 +300,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
295 .AndReturn(True) 300 .AndReturn(True)
296 api.neutron.network_list(IsA(http.HttpRequest))\ 301 api.neutron.network_list(IsA(http.HttpRequest))\
297 .AndReturn(self.networks.list()) 302 .AndReturn(self.networks.list())
303 api.neutron.is_extension_supported(IsA(http.HttpRequest),
304 "router_availability_zone")\
305 .AndReturn(False)
298 params = {'name': router.name, 306 params = {'name': router.name,
299 'admin_state_up': router.admin_state_up} 307 'admin_state_up': router.admin_state_up}
300 api.neutron.router_create(IsA(http.HttpRequest), **params)\ 308 api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -313,7 +321,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
313 321
314 @test.create_stubs({api.neutron: ('router_create', 322 @test.create_stubs({api.neutron: ('router_create',
315 'get_feature_permission', 323 'get_feature_permission',
316 'network_list')}) 324 'network_list',
325 'is_extension_supported')})
317 def test_dvr_ha_router_create_post(self): 326 def test_dvr_ha_router_create_post(self):
318 router = self.routers.first() 327 router = self.routers.first()
319 api.neutron.get_feature_permission(IsA(http.HttpRequest), 328 api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -324,6 +333,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
324 .MultipleTimes().AndReturn(True) 333 .MultipleTimes().AndReturn(True)
325 api.neutron.network_list(IsA(http.HttpRequest))\ 334 api.neutron.network_list(IsA(http.HttpRequest))\
326 .AndReturn(self.networks.list()) 335 .AndReturn(self.networks.list())
336 api.neutron.is_extension_supported(IsA(http.HttpRequest),
337 "router_availability_zone")\
338 .AndReturn(False)
327 param = {'name': router.name, 339 param = {'name': router.name,
328 'distributed': True, 340 'distributed': True,
329 'ha': True, 341 'ha': True,
@@ -344,7 +356,47 @@ class RouterActionTests(RouterMixin, test.TestCase):
344 356
345 @test.create_stubs({api.neutron: ('router_create', 357 @test.create_stubs({api.neutron: ('router_create',
346 'get_feature_permission', 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 def test_router_create_post_exception_error_case_409(self): 400 def test_router_create_post_exception_error_case_409(self):
349 router = self.routers.first() 401 router = self.routers.first()
350 api.neutron.get_feature_permission(IsA(http.HttpRequest), 402 api.neutron.get_feature_permission(IsA(http.HttpRequest),
@@ -356,6 +408,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
356 self.exceptions.neutron.status_code = 409 408 self.exceptions.neutron.status_code = 409
357 api.neutron.network_list(IsA(http.HttpRequest))\ 409 api.neutron.network_list(IsA(http.HttpRequest))\
358 .MultipleTimes().AndReturn(self.networks.list()) 410 .MultipleTimes().AndReturn(self.networks.list())
411 api.neutron.is_extension_supported(IsA(http.HttpRequest),
412 "router_availability_zone")\
413 .AndReturn(False)
359 params = {'name': router.name, 414 params = {'name': router.name,
360 'admin_state_up': router.admin_state_up} 415 'admin_state_up': router.admin_state_up}
361 api.neutron.router_create(IsA(http.HttpRequest), **params)\ 416 api.neutron.router_create(IsA(http.HttpRequest), **params)\
@@ -372,6 +427,7 @@ class RouterActionTests(RouterMixin, test.TestCase):
372 427
373 @test.create_stubs({api.neutron: ('router_create', 428 @test.create_stubs({api.neutron: ('router_create',
374 'get_feature_permission', 429 'get_feature_permission',
430 'is_extension_supported',
375 'network_list')}) 431 'network_list')})
376 def test_router_create_post_exception_error_case_non_409(self): 432 def test_router_create_post_exception_error_case_non_409(self):
377 router = self.routers.first() 433 router = self.routers.first()
@@ -384,6 +440,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
384 self.exceptions.neutron.status_code = 999 440 self.exceptions.neutron.status_code = 999
385 api.neutron.network_list(IsA(http.HttpRequest))\ 441 api.neutron.network_list(IsA(http.HttpRequest))\
386 .MultipleTimes().AndReturn(self.networks.list()) 442 .MultipleTimes().AndReturn(self.networks.list())
443 api.neutron.is_extension_supported(IsA(http.HttpRequest),
444 "router_availability_zone")\
445 .MultipleTimes().AndReturn(False)
387 params = {'name': router.name, 446 params = {'name': router.name,
388 'admin_state_up': router.admin_state_up} 447 'admin_state_up': router.admin_state_up}
389 api.neutron.router_create(IsA(http.HttpRequest), **params)\ 448 api.neutron.router_create(IsA(http.HttpRequest), **params)\
diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py
index 62297e5..25928b5 100644
--- a/openstack_dashboard/test/test_data/neutron_data.py
+++ b/openstack_dashboard/test/test_data/neutron_data.py
@@ -46,6 +46,7 @@ def data(TEST):
46 TEST.ip_availability = utils.TestDataContainer() 46 TEST.ip_availability = utils.TestDataContainer()
47 TEST.qos_policies = utils.TestDataContainer() 47 TEST.qos_policies = utils.TestDataContainer()
48 TEST.tp_ports = utils.TestDataContainer() 48 TEST.tp_ports = utils.TestDataContainer()
49 TEST.neutron_availability_zones = utils.TestDataContainer()
49 50
50 # Data return by neutronclient. 51 # Data return by neutronclient.
51 TEST.api_agents = utils.TestDataContainer() 52 TEST.api_agents = utils.TestDataContainer()
@@ -361,7 +362,8 @@ def data(TEST):
361 'distributed': True, 362 'distributed': True,
362 'external_gateway_info': 363 'external_gateway_info':
363 {'network_id': ext_net['id']}, 364 {'network_id': ext_net['id']},
364 'tenant_id': '1'} 365 'tenant_id': '1',
366 'availability_zone_hints': ['nova']}
365 TEST.api_routers.add(router_dict) 367 TEST.api_routers.add(router_dict)
366 TEST.routers.add(neutron.Router(router_dict)) 368 TEST.routers.add(neutron.Router(router_dict))
367 router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c', 369 router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c',
@@ -879,3 +881,12 @@ def data(TEST):
879 {'trunk_id': tdata['trunk_id'], 881 {'trunk_id': tdata['trunk_id'],
880 'segmentation_type': 'vlan', 882 'segmentation_type': 'vlan',
881 'segmentation_id': tdata['tag_2']})) 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 )