horizon/openstack_dashboard/dashboards/project/routers/tests.py

1273 lines
55 KiB
Python

# Copyright 2012, Nachi Ueno, NTT MCL, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from unittest import mock
from django.urls import reverse
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
class RouterMixin(object):
def _get_detail(self, router, extraroute=True):
supported_extensions = {
'extraroute': extraroute,
'router_availability_zone': True,
}
def get_supported_extension(*args):
alias = args[1]
return supported_extensions[alias]
self.mock_is_extension_supported.side_effect = get_supported_extension
self.mock_router_get.return_value = router
self.mock_port_list.return_value = [self.ports.first()]
self._mock_external_network_get(router)
res = self.client.get(reverse('horizon:%s'
':routers:detail' % self.DASHBOARD,
args=[router.id]))
return res
def _check_get_detail(self, router, extraroute=True):
self.mock_is_extension_supported.assert_any_call(
test.IsHttpRequest(), 'extraroute')
self.mock_is_extension_supported.assert_any_call(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
self.mock_port_list.assert_called_once_with(test.IsHttpRequest(),
device_id=router.id)
self._check_mock_external_network_get(router)
def _mock_external_network_list(self, count=1, alter_ids=False):
ext_nets = [n for n in self.networks.list() if n['router:external']]
if alter_ids:
for ext_net in ext_nets:
ext_net.id += 'some extra garbage'
self.mock_network_list.side_effect = [ext_nets for i in range(count)]
def _check_mock_external_network_list(self, count=1):
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_network_list, count,
mock.call(test.IsHttpRequest(), **{'router:external': True}))
def _mock_external_network_get(self, router):
ext_net = self.networks.list()[2]
self.mock_network_get.return_value = ext_net
def _check_mock_external_network_get(self, router):
ext_net_id = router.external_gateway_info['network_id']
self.mock_network_get.assert_called_once_with(
test.IsHttpRequest(), ext_net_id, expand_subnet=False)
class RouterTestCase(object):
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_index(self):
quota_data = self.neutron_quota_usages.first()
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
routers = res.context['table'].data
self.assertCountEqual(routers, self.routers.list())
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 2,
mock.call(test.IsHttpRequest(), targets=('router',)))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_index_router_list_exception(self):
quota_data = self.neutron_quota_usages.first()
self.mock_router_list.side_effect = self.exceptions.neutron
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
self.assertEqual(len(res.context['table'].data), 0)
self.assertMessageCount(res, error=1)
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 2,
mock.call(test.IsHttpRequest(), targets=('router',)))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_set_external_network_empty(self):
router = self.routers.first()
quota_data = self.neutron_quota_usages.first()
self.mock_router_list.return_value = [router]
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list(alter_ids=True)
res = self.client.get(self.INDEX_URL)
table_data = res.context['table'].data
self.assertEqual(len(table_data), 1)
self.assertIn('(Not Found)',
table_data[0]['external_gateway_info']['network'])
self.assertTemplateUsed(res, INDEX_TEMPLATE)
self.assertMessageCount(res, error=1)
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 2,
mock.call(test.IsHttpRequest(), targets=('router',)))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()
@test.create_mocks({api.neutron: ('router_get',
'port_list',
'network_get',
'is_extension_supported')})
def test_router_detail(self):
router = self.routers.first()
res = self._get_detail(router)
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
ports = res.context['interfaces_table'].data
self.assertCountEqual(ports, [self.ports.first()])
self._check_get_detail(router)
@test.create_mocks({api.neutron: ('router_get',)})
def test_router_detail_exception(self):
router = self.routers.first()
self.mock_router_get.side_effect = self.exceptions.neutron
res = self.client.get(reverse('horizon:%s'
':routers:detail' % self.DASHBOARD,
args=[router.id]))
self.assertRedirectsNoFollow(res, self.INDEX_URL)
self.mock_router_get.assert_called_once_with(test.IsHttpRequest(),
router.id)
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'port_list',
'router_delete',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_router_delete(self):
router = self.routers.first()
quota_data = self.neutron_quota_usages.first()
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
# router_availability_zone ext
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list(count=3)
self.mock_port_list.return_value = []
self.mock_router_delete.return_value = None
res = self.client.get(self.INDEX_URL)
formData = {'action': 'routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name,
res.content.decode('utf-8'))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_router_list, 3,
mock.call(test.IsHttpRequest(), tenant_id=self.tenant.id))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 4,
mock.call(test.IsHttpRequest(), targets=('router', )))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_is_extension_supported, 3,
mock.call(test.IsHttpRequest(), 'router_availability_zone'))
self._check_mock_external_network_list(count=3)
self.mock_port_list.assert_called_once_with(
test.IsHttpRequest(), device_id=router.id, device_owner=mock.ANY)
self.mock_router_delete.assert_called_once_with(
test.IsHttpRequest(), router.id)
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'port_list',
'router_remove_interface',
'router_delete',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_router_with_interface_delete(self):
router = self.routers.first()
ports = self.ports.list()
quota_data = self.neutron_quota_usages.first()
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
# router_availability_zone ext
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list(count=3)
self.mock_port_list.return_value = ports
self.mock_router_remove_interface.return_value = None
self.mock_router_delete.return_value = None
res = self.client.get(self.INDEX_URL)
formData = {'action': 'routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name,
res.content.decode('utf-8'))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_router_list, 3,
mock.call(test.IsHttpRequest(), tenant_id=self.tenant.id))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 4,
mock.call(test.IsHttpRequest(), targets=('router', )))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_is_extension_supported, 3,
mock.call(test.IsHttpRequest(), 'router_availability_zone'))
self._check_mock_external_network_list(count=3)
self.mock_port_list.assert_called_once_with(
test.IsHttpRequest(), device_id=router.id, device_owner=mock.ANY)
self.assertEqual(len(ports),
self.mock_router_remove_interface.call_count)
self.mock_router_remove_interface.assert_has_calls(
[mock.call(test.IsHttpRequest(), router.id, port_id=port.id)
for port in ports]
)
self.mock_router_delete.assert_called_once_with(
test.IsHttpRequest(), router.id)
class RouterTests(RouterMixin, RouterTestCase, test.TestCase):
DASHBOARD = 'project'
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
class RouterActionTests(test.TestCase):
DASHBOARD = 'project'
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_router_create_post(self):
router = self.routers.first()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): False,
('l3-ha', 'create'): False,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = False
self.mock_router_create.return_value = router
form_data = {'name': router.name,
'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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name,
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_router_create_post_mode_server_default(self):
router = self.routers.first()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): True,
('l3-ha', 'create'): True,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = False
self.mock_router_create.return_value = router
form_data = {'name': router.name,
'mode': 'server_default',
'ha': 'server_default',
'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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name,
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_dvr_ha_router_create_post(self):
router = self.routers.first()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): True,
('l3-ha', 'create'): True,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = False
self.mock_router_create.return_value = router
form_data = {'name': router.name,
'mode': 'distributed',
'ha': 'enabled',
'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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name, distributed=True, ha=True,
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported',
'list_availability_zones')})
def test_az_router_create_post(self):
router = self.routers.first()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): False,
('l3-ha', 'create'): False,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = True
self.mock_list_availability_zones.return_value = \
self.neutron_availability_zones.list()
self.mock_router_create.return_value = router
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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_list_availability_zones.assert_called_once_with(
test.IsHttpRequest(), 'router', 'available')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name,
availability_zone_hints=['nova'],
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_router_create_post_with_admin_state_up(self):
router = self.routers.first()
self.mock_get_feature_permission.side_effect = [
False, # ext-gw-mode, create_router_enable_snat
False, # dvr, create
False, # l3-ha, create
]
self.mock_network_list.return_value = self.networks.list()
self.mock_is_extension_supported.return_value = False
self.mock_router_create.return_value = router
form_data = {'name': router.name,
'admin_state_up': False}
url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, self.INDEX_URL)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls(
[mock.call(test.IsHttpRequest(),
"ext-gw-mode", "create_router_enable_snat"),
mock.call(test.IsHttpRequest(), "dvr", "create"),
mock.call(test.IsHttpRequest(), "l3-ha", "create")])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), "router_availability_zone")
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(), name=router.name, admin_state_up=False)
@test.create_mocks({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()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): False,
('l3-ha', 'create'): False,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = False
self.exceptions.neutron.status_code = 409
self.mock_router_create.side_effect = self.exceptions.neutron
form_data = {'name': router.name,
'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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name,
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_create',
'get_feature_permission',
'network_list',
'is_extension_supported')})
def test_router_create_post_exception_error_case_non_409(self):
router = self.routers.first()
features = {
('ext-gw-mode', 'create_router_enable_snat'): True,
('dvr', 'create'): False,
('l3-ha', 'create'): False,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_network_list.return_value = self.networks.list()
# router_availability_zone ext
self.mock_is_extension_supported.return_value = False
self.exceptions.neutron.status_code = 999
self.mock_router_create.side_effect = self.exceptions.neutron
form_data = {'name': router.name,
'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)
self.assertEqual(3, self.mock_get_feature_permission.call_count)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(),
'ext-gw-mode', 'create_router_enable_snat'),
mock.call(test.IsHttpRequest(), 'dvr', 'create'),
mock.call(test.IsHttpRequest(), 'l3-ha', 'create'),
])
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self.mock_router_create.assert_called_once_with(
test.IsHttpRequest(),
name=router.name,
admin_state_up=router.admin_state_up)
@test.create_mocks({api.neutron: ('router_get',
'get_feature_permission')})
def _test_router_update_get(self, dvr_enabled=False,
current_dvr=False, ha_enabled=False):
router = [r for r in self.routers.list()
if r.distributed == current_dvr][0]
self.mock_router_get.return_value = router
features = {
('dvr', 'update'): dvr_enabled,
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# ('l3-ha', 'update'): ha_enabled,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
args=[router.id])
res = self.client.get(url)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(), "dvr", "update"),
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# mock.call(test.IsHttpRequest(), "l3-ha", "update"),
])
return res
def test_router_update_get_dvr_disabled(self):
res = self._test_router_update_get(dvr_enabled=False)
self.assertTemplateUsed(res, 'project/routers/update.html')
self.assertNotContains(res, 'Router Type')
self.assertNotContains(res, 'id="id_mode"')
def test_router_update_get_dvr_enabled_mode_centralized(self):
res = self._test_router_update_get(dvr_enabled=True, current_dvr=False)
self.assertTemplateUsed(res, 'project/routers/update.html')
self.assertContains(res, 'Router Type')
# Check both menu are displayed.
self.assertContains(
res,
'<option value="centralized" selected="selected">'
'Centralized</option>',
html=True)
self.assertContains(
res,
'<option value="distributed">Distributed</option>',
html=True)
def test_router_update_get_dvr_enabled_mode_distributed(self):
res = self._test_router_update_get(dvr_enabled=True, current_dvr=True)
self.assertTemplateUsed(res, 'project/routers/update.html')
self.assertContains(res, 'Router Type')
pattern = ('<input class="form-control" id="id_mode" name="mode" '
'readonly="readonly" type="text" value="distributed" '
'required/>')
self.assertContains(res, pattern, html=True)
self.assertNotContains(res, 'centralized')
@test.create_mocks({api.neutron: ('router_get',
'router_update',
'get_feature_permission')})
def test_router_update_post_dvr_ha_disabled(self):
router = self.routers.first()
features = {
('dvr', 'update'): False,
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# ('l3-ha', 'update'): False,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_router_update.return_value = router
self.mock_router_get.return_value = router
form_data = {'router_id': router.id,
'name': router.name,
'admin_state': router.admin_state_up}
url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
args=[router.id])
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, self.INDEX_URL)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(), "dvr", "update"),
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# mock.call(test.IsHttpRequest(), "l3-ha", "update"),
])
self.mock_router_update.assert_called_once_with(
test.IsHttpRequest(), router.id,
name=router.name, admin_state_up=router.admin_state_up)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
@test.create_mocks({api.neutron: ('router_get',
'router_update',
'get_feature_permission')})
def test_router_update_post_dvr_ha_enabled(self):
router = self.routers.first()
features = {
('dvr', 'update'): True,
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# ('l3-ha', 'update'): True,
}
def fake_get_feature_permission(*args):
return features[(args[1], args[2])]
self.mock_get_feature_permission.side_effect = \
fake_get_feature_permission
self.mock_router_update.return_value = router
self.mock_router_get.return_value = router
form_data = {'router_id': router.id,
'name': router.name,
'admin_state': router.admin_state_up,
'mode': 'distributed',
'ha': True}
url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
args=[router.id])
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, self.INDEX_URL)
self.mock_get_feature_permission.assert_has_calls([
mock.call(test.IsHttpRequest(), "dvr", "update"),
# TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
# PUT operation. It will be fixed in Kilo cycle.
# mock.call(test.IsHttpRequest(), "l3-ha", "update"),
])
self.mock_router_update.assert_called_once_with(
test.IsHttpRequest(), router.id,
name=router.name,
admin_state_up=router.admin_state_up,
# ha=True,
distributed=True)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
def _test_router_addinterface(self, raise_error=False):
router = self.routers.first()
subnet = self.subnets.first()
port = self.ports.first()
if raise_error:
self.mock_router_add_interface.side_effect = \
self.exceptions.neutron
else:
self.mock_router_add_interface.return_value = {
'subnet_id': subnet.id,
'port_id': port.id
}
self.mock_port_get.return_value = port
self._check_router_addinterface(router, subnet)
self.mock_router_add_interface.assert_called_once_with(
test.IsHttpRequest(), router.id, subnet_id=subnet.id)
if not raise_error:
self.mock_port_get.assert_called_once_with(
test.IsHttpRequest(), port.id)
def _check_router_addinterface(self, router, subnet, ip_address=''):
self.mock_router_get.return_value = router
self.mock_port_list.return_value = []
self.mock_network_list.side_effect = [self.networks.list(), []]
form_data = {'router_id': router.id,
'router_name': router.name,
'subnet_id': subnet.id,
'ip_address': ip_address}
url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
args=[router.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
detail_url = reverse(self.DETAIL_PATH, args=[router.id])
self.assertRedirectsNoFollow(res, detail_url)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
self.mock_port_list.assert_called_once_with(
test.IsHttpRequest(), device_id=router.id)
self.assertEqual(2, self.mock_network_list.call_count)
self.mock_network_list.assert_has_calls([
mock.call(test.IsHttpRequest(),
shared=False,
tenant_id=router['tenant_id']),
mock.call(test.IsHttpRequest(),
shared=True),
])
@test.create_mocks({api.neutron: ('router_get',
'router_add_interface',
'port_get',
'network_list',
'port_list')})
def test_router_addinterface(self):
self._test_router_addinterface()
@test.create_mocks({api.neutron: ('router_get',
'router_add_interface',
'network_list',
'port_list')})
def test_router_addinterface_exception(self):
self._test_router_addinterface(raise_error=True)
def _test_router_addinterface_ip_addr(self, errors=None):
errors = errors or []
router = self.routers.first()
subnet = self.subnets.first()
port = self.ports.first()
ip_addr = port['fixed_ips'][0]['ip_address']
self._setup_mock_addinterface_ip_addr(
router, subnet, port, ip_addr, errors)
self._check_router_addinterface(
router, subnet, ip_addr)
self._check_mock_addinterface_ip_addr(
router, subnet, port, ip_addr, errors)
def _setup_mock_addinterface_ip_addr(self, router, subnet, port, ip_addr,
errors=None):
errors = errors or []
if 'subnet_get' in errors:
self.mock_subnet_get.side_effect = self.exceptions.neutron
return
self.mock_subnet_get.return_value = subnet
if 'port_create' in errors:
self.mock_port_create.side_effect = self.exceptions.neutron
return
self.mock_port_create.return_value = port
if 'add_interface' not in errors:
self.mock_router_add_interface.return_value = None
return
self.mock_router_add_interface.side_effect = self.exceptions.neutron
if 'port_delete' in errors:
self.mock_port_delete.side_effect = self.exceptions.neutron
else:
self.mock_port_delete.return_value = None
def _check_mock_addinterface_ip_addr(self, router, subnet, port, ip_addr,
errors=None):
errors = errors or []
self.mock_subnet_get.assert_called_once_with(
test.IsHttpRequest(), subnet.id)
if 'subnet_get' in errors:
return
params = {'network_id': subnet.network_id,
'fixed_ips': [{'subnet_id': subnet.id,
'ip_address': ip_addr}]}
self.mock_port_create.assert_called_once_with(
test.IsHttpRequest(), **params)
if 'port_create' in errors:
return
self.mock_router_add_interface.assert_called_once_with(
test.IsHttpRequest(), router.id, port_id=port.id)
if 'add_interface' not in errors:
return
self.mock_port_delete.assert_called_once_with(
test.IsHttpRequest(), port.id)
@test.create_mocks({api.neutron: ('router_add_interface',
'subnet_get',
'port_create',
'router_get',
'network_list',
'port_list')})
def test_router_addinterface_ip_addr(self):
self._test_router_addinterface_ip_addr()
@test.create_mocks({api.neutron: ('subnet_get',
'router_get',
'network_list',
'port_list')})
def test_router_addinterface_ip_addr_exception_subnet_get(self):
self._test_router_addinterface_ip_addr(errors=['subnet_get'])
@test.create_mocks({api.neutron: ('subnet_get',
'port_create',
'router_get',
'network_list',
'port_list')})
def test_router_addinterface_ip_addr_exception_port_create(self):
self._test_router_addinterface_ip_addr(errors=['port_create'])
@test.create_mocks({api.neutron: ('router_add_interface',
'subnet_get',
'port_create',
'port_delete',
'router_get',
'network_list',
'port_list')})
def test_router_addinterface_ip_addr_exception_add_interface(self):
self._test_router_addinterface_ip_addr(errors=['add_interface'])
@test.create_mocks({api.neutron: ('router_add_interface',
'subnet_get',
'port_create',
'port_delete',
'router_get',
'network_list',
'port_list')})
def test_router_addinterface_ip_addr_exception_port_delete(self):
self._test_router_addinterface_ip_addr(errors=['add_interface',
'port_delete'])
@test.create_mocks({api.neutron: ('router_get',
'router_add_gateway',
'network_list',
'is_extension_supported',
'get_feature_permission')})
def test_router_add_gateway(self):
router = self.routers.first()
network = self.networks.first()
self.mock_router_add_gateway.return_value = None
self.mock_router_get.return_value = router
self.mock_network_list.return_value = [network]
# ext-gw-mode
self.mock_is_extension_supported.return_value = True
# ext-gw-mode and update_router_enable_snat
self.mock_get_feature_permission.return_value = True
form_data = {'router_id': router.id,
'router_name': router.name,
'network_id': network.id,
'enable_snat': True}
url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
args=[router.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
detail_url = self.INDEX_URL
self.assertRedirectsNoFollow(res, detail_url)
self.mock_router_add_gateway.assert_called_once_with(
test.IsHttpRequest(), router.id, network.id, True)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'ext-gw-mode')
self.mock_get_feature_permission.assert_called_once_with(
test.IsHttpRequest(), 'ext-gw-mode', 'update_router_enable_snat')
@test.create_mocks({api.neutron: ('router_get',
'router_add_gateway',
'network_list',
'is_extension_supported',
'get_feature_permission')})
def test_router_add_gateway_exception(self):
router = self.routers.first()
network = self.networks.first()
self.mock_router_add_gateway.side_effect = self.exceptions.neutron
self.mock_router_get.return_value = router
self.mock_network_list.return_value = [network]
# ext-gw-mode
self.mock_is_extension_supported.return_value = True
# ext-gw-mode and update_router_enable_snat
self.mock_get_feature_permission.return_value = True
form_data = {'router_id': router.id,
'router_name': router.name,
'network_id': network.id,
'enable_snat': True}
url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
args=[router.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
detail_url = self.INDEX_URL
self.assertRedirectsNoFollow(res, detail_url)
self.mock_router_add_gateway.assert_called_once_with(
test.IsHttpRequest(), router.id, network.id, True)
self.mock_router_get.assert_called_once_with(
test.IsHttpRequest(), router.id)
self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), **{'router:external': True})
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'ext-gw-mode')
self.mock_get_feature_permission.assert_called_once_with(
test.IsHttpRequest(), 'ext-gw-mode', 'update_router_enable_snat')
class RouterRouteTestCase(object):
@test.create_mocks({api.neutron: ('router_get',
'port_list',
'network_get',
'is_extension_supported')})
def test_extension_hides_without_routes(self):
router = self.routers_with_routes.first()
res = self._get_detail(router, extraroute=False)
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
self.assertNotIn('extra_routes_table', res.context)
self._check_get_detail(router, extraroute=False)
@test.create_mocks({api.neutron: ('router_get',
'port_list',
'network_get',
'is_extension_supported')})
def test_routerroute_detail(self):
router = self.routers_with_routes.first()
res = self._get_detail(router, extraroute=True)
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
routes = res.context['extra_routes_table'].data
routes_dict = [r._apidict for r in routes]
self.assertCountEqual(routes_dict, router['routes'])
self._check_get_detail(router, extraroute=True)
@test.create_mocks({api.neutron: ('router_get',
'router_update')})
def _test_router_addrouterroute(self, ipv6=False, raise_error=False):
pre_router = self.routers_with_routes.first()
post_router = copy.deepcopy(pre_router)
if ipv6:
route = {'nexthop': 'fdb6:b88a:488e::5', 'destination': '2002::/64'}
else:
route = {'nexthop': '10.0.0.5', 'destination': '40.0.1.0/24'}
post_router['routes'].insert(0, route)
self.mock_router_get.return_value = pre_router
if raise_error:
self.mock_router_update.side_effect = self.exceptions.neutron
else:
self.mock_router_update.return_value = {'router': post_router}
form_data = copy.deepcopy(route)
form_data['router_id'] = pre_router.id
url = reverse('horizon:%s:routers:addrouterroute' % self.DASHBOARD,
args=[pre_router.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
detail_url = reverse(self.DETAIL_PATH, args=[pre_router.id])
self.assertRedirectsNoFollow(res, detail_url)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_router_get, 2,
mock.call(test.IsHttpRequest(), pre_router.id))
self.mock_router_update.assert_called_once_with(
test.IsHttpRequest(), pre_router.id, routes=post_router['routes'])
def test_router_addrouterroute(self):
if self.DASHBOARD == 'project':
self._test_router_addrouterroute()
self.assertMessageCount(success=1)
def test_router_addrouterroute_exception(self):
if self.DASHBOARD == 'project':
self._test_router_addrouterroute(raise_error=True)
self.assertMessageCount(error=1)
def test_router_addrouteripv6route(self):
if self.DASHBOARD == 'project':
self._test_router_addrouterroute(ipv6=True)
self.assertMessageCount(success=1)
def test_router_addrouteripv6route_exception(self):
if self.DASHBOARD == 'project':
self._test_router_addrouterroute(ipv6=True, raise_error=True)
self.assertMessageCount(error=1)
@test.create_mocks({api.neutron: ('router_get',
'router_update',
'network_get',
'port_list',
'is_extension_supported')})
def test_router_removeroute(self):
if self.DASHBOARD == 'admin':
return
pre_router = self.routers_with_routes.first()
post_router = copy.deepcopy(pre_router)
route = post_router['routes'].pop()
self.mock_is_extension_supported.return_value = True
self.mock_router_get.return_value = pre_router
self.mock_router_update.return_value = {'router': post_router}
self.mock_port_list.return_value = [self.ports.first()]
self._mock_external_network_get(pre_router)
form_route_id = route['nexthop'] + ":" + route['destination']
form_data = {'action': 'extra_routes__delete__%s' % form_route_id}
url = reverse(self.DETAIL_PATH, args=[pre_router.id])
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self._check_mock_external_network_get(pre_router)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_is_extension_supported, 2,
mock.call(test.IsHttpRequest(), 'extraroute'))
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_router_get, 2,
mock.call(test.IsHttpRequest(), pre_router.id))
self.mock_router_update.assert_called_once_with(
test.IsHttpRequest(), pre_router.id,
routes=post_router['routes'])
self.mock_port_list.assert_called_once_with(
test.IsHttpRequest(), device_id=pre_router.id)
class RouterRouteTests(RouterMixin, RouterRouteTestCase, test.TestCase):
DASHBOARD = 'project'
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
class RouterViewTests(RouterMixin, test.TestCase):
DASHBOARD = 'project'
INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_create_button_disabled_when_quota_exceeded(self):
quota_data = self.neutron_quota_usages.first()
quota_data['router']['available'] = 0
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
routers = res.context['routers_table'].data
self.assertCountEqual(routers, self.routers.list())
create_action = self.getAndAssertTableAction(res, 'routers', 'create')
self.assertIn('disabled', create_action.classes,
'Create button is not disabled')
self.assertEqual('Create Router (Quota exceeded)',
create_action.verbose_name)
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 3,
mock.call(test.IsHttpRequest(), targets=('router', )))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_create_button_shown_when_quota_disabled(self):
quota_data = self.neutron_quota_usages.first()
quota_data['router'].pop('available')
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
routers = res.context['routers_table'].data
self.assertCountEqual(routers, self.routers.list())
create_action = self.getAndAssertTableAction(res, 'routers', 'create')
self.assertFalse('disabled' in create_action.classes,
'Create button should not be disabled')
self.assertEqual('Create Router',
create_action.verbose_name)
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 3,
mock.call(test.IsHttpRequest(), targets=('router', )))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()
@test.create_mocks({api.neutron: ('router_list',
'network_list',
'is_extension_supported'),
quotas: ('tenant_quota_usages',)})
def test_create_button_attributes(self):
quota_data = self.neutron_quota_usages.first()
quota_data['router']['available'] = 10
self.mock_router_list.return_value = self.routers.list()
self.mock_tenant_quota_usages.return_value = quota_data
self.mock_is_extension_supported.return_value = True
self._mock_external_network_list()
res = self.client.get(self.INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE)
routers = res.context['routers_table'].data
self.assertCountEqual(routers, self.routers.list())
create_action = self.getAndAssertTableAction(res, 'routers', 'create')
self.assertEqual(set(['ajax-modal']), set(create_action.classes))
self.assertEqual('Create Router', create_action.verbose_name)
self.assertEqual('horizon:project:routers:create', create_action.url)
self.assertEqual((('network', 'create_router'),),
create_action.policy_rules)
self.mock_router_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id)
self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 3,
mock.call(test.IsHttpRequest(), targets=('router', )))
self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'router_availability_zone')
self._check_mock_external_network_list()