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
This commit is contained in:
Trygve Vea 2017-10-13 23:30:16 +02:00
parent 5f21bd56af
commit b9664a1bf1
4 changed files with 106 additions and 5 deletions

View File

@ -1728,3 +1728,15 @@ def policy_get(request, policy_id, **kwargs):
policy = neutronclient(request).show_qos_policy(
policy_id, **kwargs).get('policy')
return QoSPolicy(policy)
@profiler.trace
def list_availability_zones(request, resource=None, state=None):
az_list = neutronclient(request).list_availability_zones().get(
'availability_zones')
if resource:
az_list = [az for az in az_list if az['resource'] == resource]
if state:
az_list = [az for az in az_list if az['state'] == state]
return sorted(az_list, key=lambda zone: zone['name'])

View File

@ -41,6 +41,12 @@ class CreateForm(forms.SelfHandlingForm):
required=False)
mode = forms.ChoiceField(label=_("Router Type"))
ha = forms.ChoiceField(label=_("High Availability Mode"))
az_hints = forms.MultipleChoiceField(
label=_("Availability Zone Hints"),
required=False,
help_text=_("Availability Zones where the router may be scheduled. "
"Leaving this unset is equivalent to selecting all "
"Availability Zones"))
failure_url = 'horizon:project:routers:index'
def __init__(self, request, *args, **kwargs):
@ -70,6 +76,17 @@ class CreateForm(forms.SelfHandlingForm):
else:
del self.fields['external_network']
az_supported = api.neutron.is_extension_supported(
self.request, 'router_availability_zone')
if az_supported:
zones = api.neutron.list_availability_zones(self.request, 'router',
'available')
self.fields['az_hints'].choices = [(zone['name'], zone['name'])
for zone in zones]
else:
del self.fields['az_hints']
def _get_network_list(self, request):
search_opts = {'router:external': True}
try:
@ -94,6 +111,8 @@ class CreateForm(forms.SelfHandlingForm):
if 'external_network' in data and data['external_network']:
params['external_gateway_info'] = {'network_id':
data['external_network']}
if 'az_hints' in data and data['az_hints']:
params['availability_zone_hints'] = data['az_hints']
if (self.dvr_allowed and data['mode'] != 'server_default'):
params['distributed'] = (data['mode'] == 'distributed')
if (self.ha_allowed and data['ha'] != 'server_default'):

View File

@ -257,7 +257,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'network_list')})
'network_list',
'is_extension_supported')})
def test_router_create_post(self):
router = self.routers.first()
api.neutron.get_feature_permission(IsA(http.HttpRequest),
@ -268,6 +269,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
.AndReturn(False)
api.neutron.network_list(IsA(http.HttpRequest))\
.AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.AndReturn(False)
params = {'name': router.name,
'admin_state_up': router.admin_state_up}
api.neutron.router_create(IsA(http.HttpRequest), **params)\
@ -284,7 +288,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'network_list')})
'network_list',
'is_extension_supported')})
def test_router_create_post_mode_server_default(self):
router = self.routers.first()
api.neutron.get_feature_permission(IsA(http.HttpRequest),
@ -295,6 +300,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
.AndReturn(True)
api.neutron.network_list(IsA(http.HttpRequest))\
.AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.AndReturn(False)
params = {'name': router.name,
'admin_state_up': router.admin_state_up}
api.neutron.router_create(IsA(http.HttpRequest), **params)\
@ -313,7 +321,8 @@ class RouterActionTests(RouterMixin, test.TestCase):
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'network_list')})
'network_list',
'is_extension_supported')})
def test_dvr_ha_router_create_post(self):
router = self.routers.first()
api.neutron.get_feature_permission(IsA(http.HttpRequest),
@ -324,6 +333,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
.MultipleTimes().AndReturn(True)
api.neutron.network_list(IsA(http.HttpRequest))\
.AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.AndReturn(False)
param = {'name': router.name,
'distributed': True,
'ha': True,
@ -344,7 +356,47 @@ class RouterActionTests(RouterMixin, test.TestCase):
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'network_list')})
'network_list',
'is_extension_supported',
'list_availability_zones')})
def test_az_router_create_post(self):
router = self.routers.first()
api.neutron.get_feature_permission(IsA(http.HttpRequest),
"dvr", "create")\
.MultipleTimes().AndReturn(False)
api.neutron.get_feature_permission(IsA(http.HttpRequest),
"l3-ha", "create")\
.AndReturn(False)
api.neutron.network_list(IsA(http.HttpRequest))\
.AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.AndReturn(True)
api.neutron.list_availability_zones(IsA(http.HttpRequest),
"router", "available")\
.AndReturn(self.neutron_availability_zones.list())
param = {'name': router.name,
'availability_zone_hints': ['nova'],
'admin_state_up': router.admin_state_up}
api.neutron.router_create(IsA(http.HttpRequest), **param)\
.AndReturn(router)
self.mox.ReplayAll()
form_data = {'name': router.name,
'mode': 'server_default',
'ha': 'server_default',
'az_hints': 'nova',
'admin_state_up': router.admin_state_up}
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, self.INDEX_URL)
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_router_create_post_exception_error_case_409(self):
router = self.routers.first()
api.neutron.get_feature_permission(IsA(http.HttpRequest),
@ -356,6 +408,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
self.exceptions.neutron.status_code = 409
api.neutron.network_list(IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.AndReturn(False)
params = {'name': router.name,
'admin_state_up': router.admin_state_up}
api.neutron.router_create(IsA(http.HttpRequest), **params)\
@ -372,6 +427,7 @@ class RouterActionTests(RouterMixin, test.TestCase):
@test.create_stubs({api.neutron: ('router_create',
'get_feature_permission',
'is_extension_supported',
'network_list')})
def test_router_create_post_exception_error_case_non_409(self):
router = self.routers.first()
@ -384,6 +440,9 @@ class RouterActionTests(RouterMixin, test.TestCase):
self.exceptions.neutron.status_code = 999
api.neutron.network_list(IsA(http.HttpRequest))\
.MultipleTimes().AndReturn(self.networks.list())
api.neutron.is_extension_supported(IsA(http.HttpRequest),
"router_availability_zone")\
.MultipleTimes().AndReturn(False)
params = {'name': router.name,
'admin_state_up': router.admin_state_up}
api.neutron.router_create(IsA(http.HttpRequest), **params)\

View File

@ -46,6 +46,7 @@ def data(TEST):
TEST.ip_availability = utils.TestDataContainer()
TEST.qos_policies = utils.TestDataContainer()
TEST.tp_ports = utils.TestDataContainer()
TEST.neutron_availability_zones = utils.TestDataContainer()
# Data return by neutronclient.
TEST.api_agents = utils.TestDataContainer()
@ -361,7 +362,8 @@ def data(TEST):
'distributed': True,
'external_gateway_info':
{'network_id': ext_net['id']},
'tenant_id': '1'}
'tenant_id': '1',
'availability_zone_hints': ['nova']}
TEST.api_routers.add(router_dict)
TEST.routers.add(neutron.Router(router_dict))
router_dict = {'id': '10e3dc42-1ce1-4d48-87cf-7fc333055d6c',
@ -879,3 +881,12 @@ def data(TEST):
{'trunk_id': tdata['trunk_id'],
'segmentation_type': 'vlan',
'segmentation_id': tdata['tag_2']}))
# Availability Zones
TEST.neutron_availability_zones.add(
{
'state': 'available',
'resource': 'router',
'name': 'nova'
}
)