Implement pagination in admin/proj network tab

Added pagination support to the networks page under
Project and Admin Dashboard.

To accomplish so, the method network_list_for_tenant
in api/neutron.py that is used for listing networks under
the Project Dashboard was refactored to merge the different
requests for shared, non-shared and external networks while
tracking the pagination for each of those requests, so for
instance when all the shared networks are listed and paginated
then the non-shared networks are queried and so on.

For the Admin dashboard all network types are retrieved under
a single request so it is a simpler pagination logic.

Partial-Bug: #1746184
Change-Id: I96a2d6cabed47c89bdc02ec922d7f9451e5ec025
This commit is contained in:
Rodrigo Barbieri 2021-08-25 17:52:29 -03:00
parent 9873655c05
commit 196de449b6
13 changed files with 1574 additions and 136 deletions

View File

@ -1029,9 +1029,32 @@ def trunk_update(request, trunk_id, old_trunk, new_trunk):
@profiler.trace @profiler.trace
def network_list(request, **params): def network_list_paged(request, page_data, **params):
page_data, marker_net = _configure_pagination(request, params, page_data)
query_kwargs = {
'request': request,
'page_data': page_data,
'params': params,
}
return _perform_query(_network_list_paged, query_kwargs, marker_net)
def _network_list_paged(request, page_data, params):
nets = network_list(
request, single_page=page_data['single_page'], **params)
return update_pagination(nets, page_data)
@profiler.trace
def network_list(request, single_page=False, **params):
LOG.debug("network_list(): params=%s", params) LOG.debug("network_list(): params=%s", params)
networks = neutronclient(request).list_networks(**params).get('networks') if single_page is True:
params['retrieve_all'] = False
result = neutronclient(request).list_networks(**params)
if single_page is True:
result = result.next()
networks = result.get('networks')
# Get subnet list to expand subnet info in network list. # Get subnet list to expand subnet info in network list.
subnets = subnet_list(request) subnets = subnet_list(request)
subnet_dict = dict((s['id'], s) for s in subnets) subnet_dict = dict((s['id'], s) for s in subnets)
@ -1070,54 +1093,366 @@ def _is_auto_allocated_network_supported(request):
return nova_auto_supported return nova_auto_supported
# TODO(ganso): consolidate this function with cinder's and nova's
@profiler.trace
def update_pagination(entities, page_data):
has_more_data, has_prev_data = False, False
# single_page=True is actually to have pagination enabled
if page_data.get('single_page') is not True:
return entities, has_more_data, has_prev_data
if len(entities) > page_data['page_size']:
has_more_data = True
entities.pop()
if page_data.get('marker_id') is not None:
has_prev_data = True
# first page condition when reached via prev back
elif (page_data.get('sort_dir') == 'desc' and
page_data.get('marker_id') is not None):
has_more_data = True
# last page condition
elif page_data.get('marker_id') is not None:
has_prev_data = True
# reverse to maintain same order when going backwards
if page_data.get('sort_dir') == 'desc':
entities.reverse()
return entities, has_more_data, has_prev_data
def _add_to_nets_and_return(
nets, obtained_nets, page_data, filter_tenant_id=None):
# remove project non-shared external nets that should
# be retrieved by project query
if filter_tenant_id:
obtained_nets = [net for net in obtained_nets
if net['tenant_id'] != filter_tenant_id]
if (page_data['single_page'] is True and
len(obtained_nets) + len(nets) > page_data['limit']):
# we need to trim results if we already surpassed the limit
# we use limit so we can call update_pagination
cut = page_data['limit'] - (len(obtained_nets) + len(nets))
nets += obtained_nets[0:cut]
return True
nets += obtained_nets
# we don't need to perform more queries if we already have enough nets
if page_data['single_page'] is True and len(nets) == page_data['limit']:
return True
return False
def _query_external_nets(request, include_external, page_data, **params):
# If the external filter is set to False we don't need to perform this
# query
# If the shared filter is set to True we don't need to perform this
# query (already retrieved)
# We are either paginating external nets or not pending more data
if (page_data['filter_external'] is not False and include_external and
page_data['filter_shared'] is not True and
page_data.get('marker_type') in (None, 'ext')):
# Grab only all external non-shared networks
params['router:external'] = True
params['shared'] = False
return _perform_net_query(request, {}, page_data, 'ext', **params)
return []
def _query_shared_nets(request, page_data, **params):
# If the shared filter is set to False we don't need to perform this query
# We are either paginating shared nets or not pending more data
if (page_data['filter_shared'] is not False and
page_data.get('marker_type') in (None, 'shr')):
if page_data['filter_external'] is None:
params.pop('router:external', None)
else:
params['router:external'] = page_data['filter_external']
# Grab only all shared networks
# May include shared external nets based on external filter
params['shared'] = True
return _perform_net_query(request, {}, page_data, 'shr', **params)
return []
def _query_project_nets(request, tenant_id, page_data, **params):
# We don't need to run this query if shared filter is True, as the networks
# will be retrieved by another query
# We are either paginating project nets or not pending more data
if (page_data['filter_shared'] is not True and
page_data.get('marker_type') in (None, 'proj')):
# Grab only non-shared project networks
# May include non-shared project external nets based on external filter
if page_data['filter_external'] is None:
params.pop('router:external', None)
else:
params['router:external'] = page_data['filter_external']
params['shared'] = False
return _perform_net_query(
request, {'tenant_id': tenant_id}, page_data, 'proj', **params)
return []
def _perform_net_query(
request, extra_param, page_data, query_marker_type, **params):
copy_req_params = copy.deepcopy(params)
copy_req_params.update(extra_param)
if page_data.get('marker_type') == query_marker_type:
copy_req_params['marker'] = page_data['marker_id']
# We clear the marker type to allow for other queries if
# this one does not fill up the page
page_data['marker_type'] = None
return network_list(
request, single_page=page_data['single_page'], **copy_req_params)
def _query_nets_for_tenant(request, include_external, tenant_id, page_data,
**params):
# Save variables
page_data['filter_external'] = params.get('router:external')
page_data['filter_shared'] = params.get('shared')
nets = []
# inverted direction (for prev page)
if (page_data.get('single_page') is True and
page_data.get('sort_dir') == 'desc'):
ext_nets = _query_external_nets(
request, include_external, page_data, **params)
if _add_to_nets_and_return(
nets, ext_nets, page_data, filter_tenant_id=tenant_id):
return update_pagination(nets, page_data)
proj_nets = _query_project_nets(
request, tenant_id, page_data, **params)
if _add_to_nets_and_return(nets, proj_nets, page_data):
return update_pagination(nets, page_data)
shr_nets = _query_shared_nets(
request, page_data, **params)
if _add_to_nets_and_return(nets, shr_nets, page_data):
return update_pagination(nets, page_data)
# normal direction (for next page)
else:
shr_nets = _query_shared_nets(
request, page_data, **params)
if _add_to_nets_and_return(nets, shr_nets, page_data):
return update_pagination(nets, page_data)
proj_nets = _query_project_nets(
request, tenant_id, page_data, **params)
if _add_to_nets_and_return(nets, proj_nets, page_data):
return update_pagination(nets, page_data)
ext_nets = _query_external_nets(
request, include_external, page_data, **params)
if _add_to_nets_and_return(
nets, ext_nets, page_data, filter_tenant_id=tenant_id):
return update_pagination(nets, page_data)
return update_pagination(nets, page_data)
def _configure_marker_type(marker_net, tenant_id=None):
if marker_net:
if marker_net['shared'] is True:
return 'shr'
if (marker_net['router:external'] is True and
marker_net['tenant_id'] != tenant_id):
return 'ext'
return 'proj'
return None
def _reverse_page_order(sort_dir):
if sort_dir == 'asc':
return 'desc'
return 'asc'
def _configure_pagination(request, params, page_data=None, tenant_id=None):
marker_net = None
# "single_page" is a neutron API parameter to disable automatic
# pagination done by the API. If it is False, it returns all the
# results. If page_data param is not present, the method is being
# called by someone that does not want/expect pagination.
if page_data is None:
page_data = {'single_page': False}
else:
page_data['single_page'] = True
if page_data['marker_id']:
# this next request is inefficient, but the alternative is for
# the UI to send the extra parameters in the request,
# maybe a future optimization
marker_net = network_get(request, page_data['marker_id'])
page_data['marker_type'] = _configure_marker_type(
marker_net, tenant_id=tenant_id)
else:
page_data['marker_type'] = None
# we query one more than we are actually displaying due to
# consistent pagination hack logic used in other services
page_data['page_size'] = setting_utils.get_page_size(request)
page_data['limit'] = page_data['page_size'] + 1
params['limit'] = page_data['limit']
# Neutron API sort direction is inverted compared to other services
page_data['sort_dir'] = page_data.get('sort_dir', "desc")
page_data['sort_dir'] = _reverse_page_order(page_data['sort_dir'])
# params are included in the request to the neutron API
params['sort_dir'] = page_data['sort_dir']
params['sort_key'] = 'id'
return page_data, marker_net
def _perform_query(
query_func, query_kwargs, marker_net, include_pre_auto_allocate=False):
networks, has_more_data, has_prev_data = query_func(**query_kwargs)
# Hack for auto allocated network
if include_pre_auto_allocate and not networks:
if _is_auto_allocated_network_supported(query_kwargs['request']):
networks.append(PreAutoAllocateNetwork(query_kwargs['request']))
# no pagination case, single_page=True means pagination is enabled
if query_kwargs['page_data'].get('single_page') is not True:
return networks
# handle case of full page deletes
deleted = query_kwargs['request'].session.pop('network_deleted', None)
if deleted and marker_net:
# contents of last page deleted, invert order, load previous page
# based on marker (which ends up not included), remove head and add
# marker at the end. Since it is the last page, also force
# has_more_data to False because the marker item would always be
# the "more_data" of the request.
# we do this only if there are no elements to be displayed
if ((networks is None or len(networks) == 0) and
has_prev_data and not has_more_data and
query_kwargs['page_data']['sort_dir'] == 'asc'):
# admin section params
if 'params' in query_kwargs:
query_kwargs['params']['sort_dir'] = 'desc'
else:
query_kwargs['page_data']['marker_type'] = (
_configure_marker_type(marker_net,
query_kwargs.get('tenant_id')))
query_kwargs['sort_dir'] = 'desc'
query_kwargs['page_data']['sort_dir'] = 'desc'
networks, has_more_data, has_prev_data = (
query_func(**query_kwargs))
if networks:
if has_prev_data:
# if we are back in the first page, we don't remove head
networks.pop(0)
networks.append(marker_net)
has_more_data = False
# contents of first page deleted (loaded by prev), invert order
# and remove marker as if the section was loaded for the first time
# we do this regardless of number of elements in the first page
elif (has_more_data and not has_prev_data and
query_kwargs['page_data']['sort_dir'] == 'desc'):
query_kwargs['page_data']['sort_dir'] = 'asc'
query_kwargs['page_data']['marker_id'] = None
query_kwargs['page_data']['marker_type'] = None
# admin section params
if 'params' in query_kwargs:
if 'marker' in query_kwargs['params']:
del query_kwargs['params']['marker']
query_kwargs['params']['sort_dir'] = 'asc'
else:
query_kwargs['sort_dir'] = 'asc'
networks, has_more_data, has_prev_data = (
query_func(**query_kwargs))
return networks, has_more_data, has_prev_data
@profiler.trace @profiler.trace
def network_list_for_tenant(request, tenant_id, include_external=False, def network_list_for_tenant(request, tenant_id, include_external=False,
include_pre_auto_allocate=False, include_pre_auto_allocate=False, page_data=None,
**params): **params):
"""Return a network list available for the tenant. """Return a network list available for the tenant.
The list contains networks owned by the tenant and public networks. The list contains networks owned by the tenant and public networks.
If requested_networks specified, it searches requested_networks only. If requested_networks specified, it searches requested_networks only.
page_data parameter format:
page_data = {
'marker_id': '<id>',
'sort_dir': '<desc(next)|asc(prev)>'
}
""" """
# Pagination is implemented consistently with nova and cinder views,
# which means it is a bit hacky:
# - it requests X units but displays X-1 units
# - it ignores the marker metadata from the API response and uses its own
# Here we have extra hacks on top of that, because we have to merge the
# results of 3 different queries, and decide which one of them we are
# actually paginating.
# The 3 queries consist of:
# 1. Shared=True networks
# 2. Project non-shared networks
# 3. External non-shared non-project networks
# The main reason behind that order is to maintain the current behavior
# for how external networks are retrieved and displayed.
# The include_external assumption of whether external networks should be
# displayed is "overridden" whenever the external network is shared or is
# the tenant's. Therefore it refers to only non-shared non-tenant external
# networks.
# To accomplish pagination, we check the type of network the provided
# marker is, to determine which query we have last run and whether we
# need to paginate it.
LOG.debug("network_list_for_tenant(): tenant_id=%(tenant_id)s, " LOG.debug("network_list_for_tenant(): tenant_id=%(tenant_id)s, "
"params=%(params)s", {'tenant_id': tenant_id, 'params': params}) "params=%(params)s, page_data=%(page_data)s", {
'tenant_id': tenant_id,
'params': params,
'page_data': page_data,
})
networks = [] page_data, marker_net = _configure_pagination(
shared = params.get('shared') request, params, page_data, tenant_id=tenant_id)
if shared is not None:
del params['shared']
if shared in (None, False): query_kwargs = {
# If a user has admin role, network list returned by Neutron API 'request': request,
# contains networks that do not belong to that tenant. 'include_external': include_external,
# So we need to specify tenant_id when calling network_list(). 'tenant_id': tenant_id,
networks += network_list(request, tenant_id=tenant_id, 'page_data': page_data,
shared=False, **params) **params,
}
if shared in (None, True): return _perform_query(
# In the current Neutron API, there is no way to retrieve _query_nets_for_tenant, query_kwargs, marker_net,
# both owner networks and public networks in a single API call. include_pre_auto_allocate)
networks += network_list(request, shared=True, **params)
# Hack for auto allocated network
if include_pre_auto_allocate and not networks:
if _is_auto_allocated_network_supported(request):
networks.append(PreAutoAllocateNetwork(request))
params['router:external'] = params.get('router:external', True)
if params['router:external'] and include_external:
if shared is not None:
params['shared'] = shared
fetched_net_ids = [n.id for n in networks]
# Retrieves external networks when router:external is not specified
# in (filtering) params or router:external=True filter is specified.
# When router:external=False is specified there is no need to query
# networking API because apparently nothing will match the filter.
ext_nets = network_list(request, **params)
networks += [n for n in ext_nets if
n.id not in fetched_net_ids]
return networks
@profiler.trace @profiler.trace
@ -1178,6 +1513,7 @@ def network_update(request, network_id, **kwargs):
def network_delete(request, network_id): def network_delete(request, network_id):
LOG.debug("network_delete(): netid=%s", network_id) LOG.debug("network_delete(): netid=%s", network_id)
neutronclient(request).delete_network(network_id) neutronclient(request).delete_network(network_id)
request.session['network_deleted'] = network_id
@profiler.trace @profiler.trace

View File

@ -68,7 +68,9 @@ class NetworkTests(test.BaseAdminViewTests):
networks = res.context['networks_table'].data networks = res.context['networks_table'].data
self.assertCountEqual(networks, self.networks.list()) self.assertCountEqual(networks, self.networks.list())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest()) self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), single_page=True,
limit=21, sort_dir='asc', sort_key='id')
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest()) self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self._check_is_extension_supported( self._check_is_extension_supported(
{'network_availability_zone': 1, {'network_availability_zone': 1,
@ -99,7 +101,9 @@ class NetworkTests(test.BaseAdminViewTests):
self.assertEqual(len(res.context['networks_table'].data), 0) self.assertEqual(len(res.context['networks_table'].data), 0)
self.assertMessageCount(res, error=1) self.assertMessageCount(res, error=1)
self.mock_network_list.assert_called_once_with(test.IsHttpRequest()) self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), single_page=True,
limit=21, sort_dir='asc', sort_key='id')
self._check_is_extension_supported( self._check_is_extension_supported(
{'network_availability_zone': 1, {'network_availability_zone': 1,
'dhcp_agent_scheduler': 1}) 'dhcp_agent_scheduler': 1})
@ -964,7 +968,9 @@ class NetworkTests(test.BaseAdminViewTests):
{'network_availability_zone': 1, {'network_availability_zone': 1,
'dhcp_agent_scheduler': 2}) 'dhcp_agent_scheduler': 2})
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest()) self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest()) self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), single_page=True,
limit=21, sort_dir='asc', sort_key='id')
self.mock_network_delete.assert_called_once_with(test.IsHttpRequest(), self.mock_network_delete.assert_called_once_with(test.IsHttpRequest(),
network.id) network.id)
@ -997,7 +1003,9 @@ class NetworkTests(test.BaseAdminViewTests):
{'network_availability_zone': 1, {'network_availability_zone': 1,
'dhcp_agent_scheduler': 2}) 'dhcp_agent_scheduler': 2})
self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest()) self.mock_tenant_list.assert_called_once_with(test.IsHttpRequest())
self.mock_network_list.assert_called_once_with(test.IsHttpRequest()) self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), single_page=True,
limit=21, sort_dir='asc', sort_key='id')
self.mock_network_delete.assert_called_once_with(test.IsHttpRequest(), self.mock_network_delete.assert_called_once_with(test.IsHttpRequest(),
network.id) network.id)

View File

@ -41,7 +41,7 @@ from openstack_dashboard.dashboards.admin.networks \
from openstack_dashboard.dashboards.admin.networks import workflows from openstack_dashboard.dashboards.admin.networks import workflows
class IndexView(tables.DataTableView): class IndexView(tables.PagedTableMixin, tables.DataTableView):
table_class = networks_tables.NetworksTable table_class = networks_tables.NetworksTable
page_title = _("Networks") page_title = _("Networks")
FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False}, FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False},
@ -84,8 +84,18 @@ class IndexView(tables.DataTableView):
def get_data(self): def get_data(self):
try: try:
marker, sort_dir = self._get_marker()
page_data = {
'marker_id': marker,
'sort_dir': sort_dir
}
search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING) search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING)
if marker:
search_opts['marker'] = marker
# If the tenant filter selected and the tenant does not exist. # If the tenant filter selected and the tenant does not exist.
# We do not need to retrieve the list from neutron,just return # We do not need to retrieve the list from neutron,just return
# an empty list. # an empty list.
@ -102,8 +112,11 @@ class IndexView(tables.DataTableView):
return [] return []
self._needs_filter_first = False self._needs_filter_first = False
networks = api.neutron.network_list(self.request, **search_opts) networks, self._has_more_data, self._has_prev_data = (
api.neutron.network_list_paged(
self.request, page_data, **search_opts))
except Exception: except Exception:
self._has_more_data = self._has_prev_data = False
networks = [] networks = []
msg = _('Network list can not be retrieved.') msg = _('Network list can not be retrieved.')
exceptions.handle(self.request, msg) exceptions.handle(self.request, msg)

View File

@ -103,19 +103,25 @@ class NetworkStubMixin(object):
all_networks = self.networks.list() all_networks = self.networks.list()
self.mock_network_list.side_effect = [ self.mock_network_list.side_effect = [
[network for network in all_networks [network for network in all_networks
if network['tenant_id'] == self.tenant.id], if network.get('shared') is True],
[network for network in all_networks [network for network in all_networks
if network.get('shared')], if network['tenant_id'] == self.tenant.id and
network.get('shared') is False],
[network for network in all_networks [network for network in all_networks
if network.get('router:external')], if network.get('router:external') is True and
network.get('shared') is False],
] ]
def _check_net_list(self): def _check_net_list(self):
self.mock_network_list.assert_has_calls([ self.mock_network_list.assert_has_calls([
mock.call(test.IsHttpRequest(), tenant_id=self.tenant.id, mock.call(test.IsHttpRequest(), single_page=True, limit=21,
shared=False), sort_dir='asc', sort_key='id', shared=True),
mock.call(test.IsHttpRequest(), shared=True), mock.call(test.IsHttpRequest(), single_page=True, limit=21,
mock.call(test.IsHttpRequest(), **{'router:external': True}), sort_dir='asc', sort_key='id',
shared=False, tenant_id=self.tenant.id),
mock.call(test.IsHttpRequest(), single_page=True, limit=21,
sort_dir='asc', sort_key='id',
**{'router:external': True}, shared=False),
]) ])
def _stub_is_extension_supported(self, features): def _stub_is_extension_supported(self, features):
@ -148,16 +154,19 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
res = self.client.get(INDEX_URL) res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, INDEX_TEMPLATE) self.assertTemplateUsed(res, INDEX_TEMPLATE)
networks = res.context['networks_table'].data networks = res.context['networks_table'].data
self.assertCountEqual(networks, self.networks.list())
self.mock_tenant_quota_usages.assert_has_calls([ self.mock_tenant_quota_usages.assert_has_calls([
mock.call(test.IsHttpRequest(), targets=('network', )), mock.call(test.IsHttpRequest(), targets=('network', )),
mock.call(test.IsHttpRequest(), targets=('subnet', )), mock.call(test.IsHttpRequest(), targets=('subnet', )),
]) ])
self.assertEqual(7, self.mock_tenant_quota_usages.call_count) self.assertEqual(11, self.mock_tenant_quota_usages.call_count)
self.mock_is_extension_supported.assert_called_once_with( self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'network_availability_zone') test.IsHttpRequest(), 'network_availability_zone')
self._check_net_list() self._check_net_list()
self.assertCountEqual(networks, [net for net in self.networks.list()
if net['tenant_id'] == '1' or
net['router:external'] is True or
net['shared'] is True])
@test.create_mocks({api.neutron: ('network_list', @test.create_mocks({api.neutron: ('network_list',
'is_extension_supported'), 'is_extension_supported'),
@ -175,8 +184,8 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
self.assertMessageCount(res, error=1) self.assertMessageCount(res, error=1)
self.mock_network_list.assert_called_once_with( self.mock_network_list.assert_called_once_with(
test.IsHttpRequest(), tenant_id=self.tenant.id, test.IsHttpRequest(), single_page=True, limit=21, sort_dir='asc',
shared=False) sort_key='id', shared=True)
self.assert_mock_multiple_calls_with_same_arguments( self.assert_mock_multiple_calls_with_same_arguments(
self.mock_tenant_quota_usages, 2, self.mock_tenant_quota_usages, 2,
mock.call(test.IsHttpRequest(), targets=('network', ))) mock.call(test.IsHttpRequest(), targets=('network', )))
@ -787,7 +796,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
def test_network_create_post_with_subnet_cidr_invalid_v6_range( def test_network_create_post_with_subnet_cidr_invalid_v6_range(
self, test_with_subnetpool=False): self, test_with_subnetpool=False):
network = self.networks.first() network = self.networks.first()
subnet_v6 = self.subnets.list()[4] subnet_v6 = self.subnets.list()[9]
self._stub_is_extension_supported({'network_availability_zone': False, self._stub_is_extension_supported({'network_availability_zone': False,
'subnet_allocation': True}) 'subnet_allocation': True})
@ -1144,7 +1153,6 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
self.assertTemplateUsed(res, INDEX_TEMPLATE) self.assertTemplateUsed(res, INDEX_TEMPLATE)
networks = res.context['networks_table'].data networks = res.context['networks_table'].data
self.assertCountEqual(networks, self.networks.list())
button = find_button_fn(res) button = find_button_fn(res)
self.assertFalse('disabled' in button.classes, self.assertFalse('disabled' in button.classes,
@ -1155,9 +1163,13 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
mock.call(test.IsHttpRequest(), targets=('network', )), mock.call(test.IsHttpRequest(), targets=('network', )),
mock.call(test.IsHttpRequest(), targets=('subnet', )), mock.call(test.IsHttpRequest(), targets=('subnet', )),
]) ])
self.assertEqual(8, self.mock_tenant_quota_usages.call_count) self.assertEqual(12, self.mock_tenant_quota_usages.call_count)
self.mock_is_extension_supported.assert_called_once_with( self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'network_availability_zone') test.IsHttpRequest(), 'network_availability_zone')
self.assertCountEqual(networks, [net for net in self.networks.list()
if net['tenant_id'] == '1' or
net['router:external'] is True or
net['shared'] is True])
return button return button
@ -1181,7 +1193,6 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
self.assertTemplateUsed(res, INDEX_TEMPLATE) self.assertTemplateUsed(res, INDEX_TEMPLATE)
networks = res.context['networks_table'].data networks = res.context['networks_table'].data
self.assertCountEqual(networks, self.networks.list())
button = find_button_fn(res) button = find_button_fn(res)
self.assertIn('disabled', button.classes, self.assertIn('disabled', button.classes,
@ -1192,9 +1203,13 @@ class NetworkViewTests(test.TestCase, NetworkStubMixin):
mock.call(test.IsHttpRequest(), targets=('network', )), mock.call(test.IsHttpRequest(), targets=('network', )),
mock.call(test.IsHttpRequest(), targets=('subnet', )), mock.call(test.IsHttpRequest(), targets=('subnet', )),
]) ])
self.assertEqual(8, self.mock_tenant_quota_usages.call_count) self.assertEqual(12, self.mock_tenant_quota_usages.call_count)
self.mock_is_extension_supported.assert_called_once_with( self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'network_availability_zone') test.IsHttpRequest(), 'network_availability_zone')
self.assertCountEqual(networks, [net for net in self.networks.list()
if net['tenant_id'] == '1' or
net['router:external'] is True or
net['shared'] is True])
return button return button

View File

@ -40,7 +40,7 @@ from openstack_dashboard.dashboards.project.networks \
import workflows as project_workflows import workflows as project_workflows
class IndexView(tables.DataTableView): class IndexView(tables.PagedTableMixin, tables.DataTableView):
table_class = project_tables.NetworksTable table_class = project_tables.NetworksTable
page_title = _("Networks") page_title = _("Networks")
FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False}, FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False},
@ -51,12 +51,23 @@ class IndexView(tables.DataTableView):
try: try:
tenant_id = self.request.user.tenant_id tenant_id = self.request.user.tenant_id
search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING) search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING)
networks = api.neutron.network_list_for_tenant(
self.request, tenant_id, marker, sort_dir = self._get_marker()
include_external=True, page_data = {
include_pre_auto_allocate=True, 'marker_id': marker,
**search_opts) 'sort_dir': sort_dir,
}
networks, self._has_more_data, self._has_prev_data = (
api.neutron.network_list_for_tenant(
self.request, tenant_id,
include_external=True,
include_pre_auto_allocate=True,
page_data=page_data,
**search_opts))
except Exception: except Exception:
self._has_more_data = self._has_prev_data = False
networks = [] networks = []
msg = _('Network list can not be retrieved.') msg = _('Network list can not be retrieved.')
exceptions.handle(self.request, msg) exceptions.handle(self.request, msg)

View File

@ -830,11 +830,9 @@ class RouterActionTests(test.TestCase):
test.IsHttpRequest(), device_id=router.id) test.IsHttpRequest(), device_id=router.id)
self.assertEqual(2, self.mock_network_list.call_count) self.assertEqual(2, self.mock_network_list.call_count)
self.mock_network_list.assert_has_calls([ self.mock_network_list.assert_has_calls([
mock.call(test.IsHttpRequest(), mock.call(test.IsHttpRequest(), single_page=False, shared=True),
shared=False, mock.call(test.IsHttpRequest(), single_page=False,
tenant_id=router['tenant_id']), shared=False, tenant_id=router['tenant_id']),
mock.call(test.IsHttpRequest(),
shared=True),
]) ])
@test.create_mocks({api.neutron: ('router_get', @test.create_mocks({api.neutron: ('router_get',

View File

@ -15,4 +15,24 @@ from openstack_dashboard.test.integration_tests.pages.project.network \
class NetworksPage(networkspage.NetworksPage): class NetworksPage(networkspage.NetworksPage):
pass
NETWORKS_TABLE_NAME_COLUMN = 'Network Name'
@property
def is_admin(self):
return True
@property
def networks_table(self):
return NetworksTable(self.driver, self.conf)
class NetworksTable(networkspage.NetworksTable):
CREATE_NETWORK_FORM_FIELDS = (("name", "admin_state",
"with_subnet", "az_hints", "tenant_id",
"network_type"),
("subnet_name", "cidr", "ip_version",
"gateway_ip", "no_gateway"),
("enable_dhcp", "allocation_pools",
"dns_nameservers", "host_routes"))

View File

@ -55,6 +55,10 @@ class NetworksPage(basepage.BaseNavigationPage):
return self.networks_table.get_row( return self.networks_table.get_row(
self.NETWORKS_TABLE_NAME_COLUMN, name) self.NETWORKS_TABLE_NAME_COLUMN, name)
@property
def is_admin(self):
return False
@property @property
def networks_table(self): def networks_table(self):
return NetworksTable(self.driver, self.conf) return NetworksTable(self.driver, self.conf)
@ -66,9 +70,15 @@ class NetworksPage(basepage.BaseNavigationPage):
gateway_ip=None, gateway_ip=None,
disable_gateway=DEFAULT_DISABLE_GATEWAY, disable_gateway=DEFAULT_DISABLE_GATEWAY,
enable_dhcp=DEFAULT_ENABLE_DHCP, allocation_pools=None, enable_dhcp=DEFAULT_ENABLE_DHCP, allocation_pools=None,
dns_name_servers=None, host_routes=None): dns_name_servers=None, host_routes=None,
project='admin', net_type='Local'):
create_network_form = self.networks_table.create_network() create_network_form = self.networks_table.create_network()
create_network_form.net_name.text = network_name if self.is_admin:
create_network_form.network_type.text = net_type
create_network_form.tenant_id.text = project
create_network_form.name.text = network_name
else:
create_network_form.net_name.text = network_name
create_network_form.admin_state.value = admin_state create_network_form.admin_state.value = admin_state
if not create_subnet: if not create_subnet:
create_network_form.with_subnet.unmark() create_network_form.with_subnet.unmark()

View File

@ -21,6 +21,10 @@ class TestNetworks(helpers.TestCase):
NETWORK_NAME = helpers.gen_random_resource_name("network") NETWORK_NAME = helpers.gen_random_resource_name("network")
SUBNET_NAME = helpers.gen_random_resource_name("subnet") SUBNET_NAME = helpers.gen_random_resource_name("subnet")
@property
def networks_page(self):
return self.home_pg.go_to_project_network_networkspage()
def test_private_network_create(self): def test_private_network_create(self):
"""tests the network creation and deletion functionalities: """tests the network creation and deletion functionalities:
@ -30,7 +34,7 @@ class TestNetworks(helpers.TestCase):
* verifies the network does not appear in the table after deletion * verifies the network does not appear in the table after deletion
""" """
networks_page = self.home_pg.go_to_project_network_networkspage() networks_page = self.networks_page
networks_page.create_network(self.NETWORK_NAME, self.SUBNET_NAME) networks_page.create_network(self.NETWORK_NAME, self.SUBNET_NAME)
self.assertTrue( self.assertTrue(
@ -46,3 +50,97 @@ class TestNetworks(helpers.TestCase):
self.assertFalse( self.assertFalse(
networks_page.find_message_and_dismiss(messages.ERROR)) networks_page.find_message_and_dismiss(messages.ERROR))
self.assertFalse(networks_page.is_network_present(self.NETWORK_NAME)) self.assertFalse(networks_page.is_network_present(self.NETWORK_NAME))
def test_networks_pagination(self):
"""This test checks networks pagination
Steps:
1) Login to Horizon Dashboard
2) Go to Project -> Network -> Networks tab and create
three networks
3) Navigate to user settings page
4) Change 'Items Per Page' value to 2
5) Go to Project -> Network -> Networks tab or
Admin -> Network -> Networks tab (depends on user)
6) Check that only 'Next' link is available, only one network is
available (and it has correct name)
7) Click 'Next' and check that both 'Prev' and 'Next' links are
available, only one network is available (and it has correct name)
8) Click 'Next' and check that only 'Prev' link is available,
only one network is visible (and it has correct name)
9) Click 'Prev' and check result (should be the same as for step7)
10) Click 'Prev' and check result (should be the same as for step6)
11) Go to user settings page and restore 'Items Per Page'
12) Delete created networks
"""
networks_page = self.networks_page
count = 6
items_per_page = 2
networks_names = ["{0}_{1}".format(self.NETWORK_NAME, i)
for i in range(count)]
for network_name in networks_names:
networks_page.create_network(network_name, self.SUBNET_NAME)
self.assertTrue(
networks_page.find_message_and_dismiss(messages.SUCCESS))
self.assertFalse(
networks_page.find_message_and_dismiss(messages.ERROR))
self.assertTrue(networks_page.is_network_present(network_name))
self.assertTrue(networks_page.is_network_active(network_name))
networks_page = self.networks_page
rows = networks_page.networks_table.get_column_data(
name_column=networks_page.NETWORKS_TABLE_NAME_COLUMN)
self._change_page_size_setting(items_per_page)
networks_page = self.networks_page
definitions = []
i = 0
while i < len(rows):
prev = i >= items_per_page
next = i < (len(rows) - items_per_page)
definition = {'Next': next, 'Prev': prev,
'Count': items_per_page,
'Names': rows[i:i + items_per_page]}
definitions.append(definition)
networks_page.networks_table.assert_definition(
definition,
name_column=networks_page.NETWORKS_TABLE_NAME_COLUMN)
if next:
networks_page.networks_table.turn_next_page()
i = i + items_per_page
definitions.reverse()
for definition in definitions:
networks_page.networks_table.assert_definition(
definition,
name_column=networks_page.NETWORKS_TABLE_NAME_COLUMN)
if definition['Prev']:
networks_page.networks_table.turn_prev_page()
self._change_page_size_setting()
networks_page = self.networks_page
for network_name in networks_names:
networks_page.delete_network(network_name)
self.assertTrue(
networks_page.find_message_and_dismiss(messages.SUCCESS))
self.assertFalse(
networks_page.find_message_and_dismiss(messages.ERROR))
self.assertFalse(networks_page.is_network_present(network_name))
def _change_page_size_setting(self, items_per_page=None):
settings_page = self.home_pg.go_to_settings_usersettingspage()
if items_per_page:
settings_page.change_pagesize(items_per_page)
else:
settings_page.change_pagesize()
settings_page.find_message_and_dismiss(messages.SUCCESS)
@decorators.services_required("neutron")
class TestAdminNetworks(helpers.AdminTestCase, TestNetworks):
NETWORK_NAME = helpers.gen_random_resource_name("network")
SUBNET_NAME = helpers.gen_random_resource_name("subnet")
@property
def networks_page(self):
return self.home_pg.go_to_admin_network_networkspage()

View File

@ -271,7 +271,7 @@ def data(TEST):
TEST.api_ports.add(port_dict) TEST.api_ports.add(port_dict)
TEST.ports.add(neutron.Port(port_dict)) TEST.ports.add(neutron.Port(port_dict))
# External network. # External not shared network.
network_dict = {'admin_state_up': True, network_dict = {'admin_state_up': True,
'id': '9b466b94-213a-4cda-badf-72c102a874da', 'id': '9b466b94-213a-4cda-badf-72c102a874da',
'name': 'ext_net', 'name': 'ext_net',
@ -303,6 +303,162 @@ def data(TEST):
TEST.networks.add(neutron.Network(network)) TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet) TEST.subnets.add(subnet)
# External shared network.
network_dict = {'admin_state_up': True,
'id': 'ed351877-4f7b-4672-8164-20a09e4873d3',
'name': 'ext_net_shared',
'status': 'ACTIVE',
'subnets': ['5c59f875-f242-4df2-96e6-7dcc09d6dfc8'],
'tenant_id': '4',
'router:external': True,
'shared': True}
subnet_dict = {'allocation_pools': [{'start': '172.24.14.226.',
'end': '172.24.14.238'}],
'dns_nameservers': [],
'host_routes': [],
'cidr': '172.24.14.0/28',
'enable_dhcp': False,
'gateway_ip': '172.24.14.225',
'id': '5c59f875-f242-4df2-96e6-7dcc09d6dfc8',
'ip_version': 4,
'name': 'ext_shr_subnet',
'network_id': network_dict['id'],
'tenant_id': network_dict['tenant_id']}
TEST.api_networks.add(network_dict)
TEST.api_subnets.add(subnet_dict)
network = copy.deepcopy(network_dict)
subnet = neutron.Subnet(subnet_dict)
network['subnets'] = [subnet]
TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet)
# tenant external shared network
network_dict = {'admin_state_up': True,
'id': '650de90f-d77f-4863-ae98-39e97ad3ea7a',
'name': 'ext_net_shared_tenant1',
'status': 'ACTIVE',
'subnets': ['d0a5bc19-16f0-45cc-a187-0d1bb36de4c6'],
'tenant_id': '1',
'router:external': True,
'shared': True}
subnet_dict = {'allocation_pools': [{'start': '172.34.14.226.',
'end': '172.34.14.238'}],
'dns_nameservers': [],
'host_routes': [],
'cidr': '172.34.14.0/28',
'enable_dhcp': False,
'gateway_ip': '172.34.14.225',
'id': 'd0a5bc19-16f0-45cc-a187-0d1bb36de4c6',
'ip_version': 4,
'name': 'ext_shr_tenant1_subnet',
'network_id': network_dict['id'],
'tenant_id': network_dict['tenant_id']}
TEST.api_networks.add(network_dict)
TEST.api_subnets.add(subnet_dict)
network = copy.deepcopy(network_dict)
subnet = neutron.Subnet(subnet_dict)
network['subnets'] = [subnet]
TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet)
# tenant external non-shared network
network_dict = {'admin_state_up': True,
'id': '19c3e662-1635-4876-be41-dbfdef0edd17',
'name': 'ext_net_tenant1',
'status': 'ACTIVE',
'subnets': ['5ba8895c-0b3b-482d-9e42-ce389e1e1fa6'],
'tenant_id': '1',
'router:external': True,
'shared': False}
subnet_dict = {'allocation_pools': [{'start': '172.44.14.226.',
'end': '172.44.14.238'}],
'dns_nameservers': [],
'host_routes': [],
'cidr': '172.44.14.0/28',
'enable_dhcp': False,
'gateway_ip': '172.44.14.225',
'id': '5ba8895c-0b3b-482d-9e42-ce389e1e1fa6',
'ip_version': 4,
'name': 'ext_tenant1_subnet',
'network_id': network_dict['id'],
'tenant_id': network_dict['tenant_id']}
TEST.api_networks.add(network_dict)
TEST.api_subnets.add(subnet_dict)
network = copy.deepcopy(network_dict)
subnet = neutron.Subnet(subnet_dict)
network['subnets'] = [subnet]
TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet)
# tenant non-external shared network
network_dict = {'admin_state_up': True,
'id': 'fd581273-2601-4057-9c22-1be38f44884e',
'name': 'shr_net_tenant1',
'status': 'ACTIVE',
'subnets': ['d2668892-bc32-4c89-9c63-961920a831d3'],
'tenant_id': '1',
'router:external': False,
'shared': True}
subnet_dict = {'allocation_pools': [{'start': '172.54.14.226.',
'end': '172.54.14.238'}],
'dns_nameservers': [],
'host_routes': [],
'cidr': '172.54.14.0/28',
'enable_dhcp': False,
'gateway_ip': '172.54.14.225',
'id': 'd2668892-bc32-4c89-9c63-961920a831d3',
'ip_version': 4,
'name': 'shr_tenant1_subnet',
'network_id': network_dict['id'],
'tenant_id': network_dict['tenant_id']}
TEST.api_networks.add(network_dict)
TEST.api_subnets.add(subnet_dict)
network = copy.deepcopy(network_dict)
subnet = neutron.Subnet(subnet_dict)
network['subnets'] = [subnet]
TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet)
# non-tenant non-external non-shared network
network_dict = {'admin_state_up': True,
'id': '7377e545-1527-4ce1-869e-caca192bc049',
'name': 'net_tenant20',
'status': 'ACTIVE',
'subnets': ['c2bbd65e-0c0f-4ab9-8723-2dd102104f3d'],
'tenant_id': '20',
'router:external': False,
'shared': False}
subnet_dict = {'allocation_pools': [{'start': '172.64.14.226.',
'end': '172.64.14.238'}],
'dns_nameservers': [],
'host_routes': [],
'cidr': '172.54.14.0/28',
'enable_dhcp': False,
'gateway_ip': '172.64.14.225',
'id': 'c2bbd65e-0c0f-4ab9-8723-2dd102104f3d',
'ip_version': 4,
'name': 'tenant20_subnet',
'network_id': network_dict['id'],
'tenant_id': network_dict['tenant_id']}
TEST.api_networks.add(network_dict)
TEST.api_subnets.add(subnet_dict)
network = copy.deepcopy(network_dict)
subnet = neutron.Subnet(subnet_dict)
network['subnets'] = [subnet]
TEST.networks.add(neutron.Network(network))
TEST.subnets.add(subnet)
# 1st v6 network. # 1st v6 network.
network_dict = {'admin_state_up': True, network_dict = {'admin_state_up': True,
'id': '96688ea1-ffa5-78ec-22ca-33aaabfaf775', 'id': '96688ea1-ffa5-78ec-22ca-33aaabfaf775',
@ -1002,3 +1158,92 @@ def data(TEST):
'name': 'nova' 'name': 'nova'
} }
) )
def list_nets_in_query_order(source_list):
return ([n for n in source_list if n['shared'] is True] +
[n for n in source_list if (n['tenant_id'] == '1' and
n['shared'] is False)] +
[n for n in source_list if n['router:external'] is True and
n['shared'] is False])
source_nets_pagination1 = sorted([
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net{}'.format(i),
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '1',
'router:external': False,
'shared': False}) for i in range(0, 58)
] + [
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net_ext',
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '2',
'router:external': True,
'shared': False})
] + [
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net_shr',
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '3',
'router:external': False,
'shared': True})
], key=lambda net: net['id'])
all_nets_pagination1 = list_nets_in_query_order(source_nets_pagination1)
source_nets_pagination2 = sorted([
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net{}'.format(i),
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '2',
'router:external': True,
'shared': False}) for i in range(0, 25)
] + [
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net{}'.format(i),
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '3',
'router:external': False,
'shared': True}) for i in range(0, 25)
] + [
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net{}'.format(i),
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '1',
'router:external': False,
'shared': False}) for i in range(0, 10)
], key=lambda net: net['id'])
all_nets_pagination2 = list_nets_in_query_order(source_nets_pagination2)
source_nets_pagination3 = sorted([
neutron.Network({
'admin_state_up': True,
'id': uuidutils.generate_uuid(),
'name': 'net{}'.format(i),
'status': 'ACTIVE',
'subnets': [],
'tenant_id': '1',
'router:external': False,
'shared': False}) for i in range(0, 5)
], key=lambda net: net['id'])

View File

@ -23,9 +23,11 @@ from django.test.utils import override_settings
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard import policy from openstack_dashboard import policy
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
from openstack_dashboard.test.test_data import neutron_data
class NeutronApiTests(test.APIMockTestCase): class NeutronApiTests(test.APIMockTestCase):
@mock.patch.object(api.neutron, 'neutronclient') @mock.patch.object(api.neutron, 'neutronclient')
def test_network_list(self, mock_neutronclient): def test_network_list(self, mock_neutronclient):
networks = {'networks': self.api_networks.list()} networks = {'networks': self.api_networks.list()}
@ -46,8 +48,8 @@ class NeutronApiTests(test.APIMockTestCase):
@test.create_mocks({api.neutron: ('network_list', @test.create_mocks({api.neutron: ('network_list',
'subnet_list')}) 'subnet_list')})
def _test_network_list_for_tenant( def _test_network_list_for_tenant(
self, include_external, self, include_external, filter_params, should_called,
filter_params, should_called, **extra_kwargs): expected_networks, source_networks=None, **extra_kwargs):
"""Convenient method to test network_list_for_tenant. """Convenient method to test network_list_for_tenant.
:param include_external: Passed to network_list_for_tenant. :param include_external: Passed to network_list_for_tenant.
@ -55,38 +57,74 @@ class NeutronApiTests(test.APIMockTestCase):
:param should_called: this argument specifies which methods :param should_called: this argument specifies which methods
should be called. Methods in this list should be called. should be called. Methods in this list should be called.
Valid values are non_shared, shared, and external. Valid values are non_shared, shared, and external.
:param expected_networks: the networks to be compared with the result.
:param source_networks: networks to override the mocks.
""" """
has_more_data = None
has_prev_data = None
marker_calls = []
filter_params = filter_params or {} filter_params = filter_params or {}
all_networks = self.networks.list() if 'page_data' not in extra_kwargs:
tenant_id = '1' call_args = {'single_page': False}
tenant_networks = [n for n in all_networks else:
if n['tenant_id'] == tenant_id] sort_dir = extra_kwargs['page_data']['sort_dir']
shared_networks = [n for n in all_networks if n['shared']] # invert sort_dir for calls
external_networks = [n for n in all_networks if n['router:external']] sort_dir = 'asc' if sort_dir == 'desc' else 'desc'
call_args = {'single_page': True, 'limit': 21, 'sort_key': 'id',
'sort_dir': sort_dir}
marker_id = extra_kwargs['page_data'].get('marker_id')
if extra_kwargs.get('marker_calls') is not None:
marker_calls = extra_kwargs.pop('marker_calls')
tenant_id = '1'
return_values = [] return_values = []
all_networks = (self.networks.list() if source_networks is None
else source_networks)
expected_calls = [] expected_calls = []
if 'non_shared' in should_called: call_order = ['shared', 'non_shared', 'external']
params = filter_params.copy() if call_args.get('sort_dir') == 'desc':
params['shared'] = False call_order.reverse()
return_values.append(tenant_networks)
expected_calls.append( for call in call_order:
mock.call(test.IsHttpRequest(), tenant_id=tenant_id, **params), if call in should_called:
) params = filter_params.copy()
if 'shared' in should_called: params.update(call_args)
params = filter_params.copy() if call in marker_calls:
params['shared'] = True params.update({'marker': marker_id})
return_values.append(shared_networks) if call == 'external':
expected_calls.append( params['router:external'] = True
mock.call(test.IsHttpRequest(), **params), params['shared'] = False
) return_values.append(
if 'external' in should_called: [n for n in all_networks
params = filter_params.copy() if n['router:external'] is True and
params['router:external'] = True n['shared'] is False])
return_values.append(external_networks) expected_calls.append(
expected_calls.append( mock.call(test.IsHttpRequest(), **params))
mock.call(test.IsHttpRequest(), **params), elif call == 'shared':
) params['shared'] = True
external = params.get('router:external')
return_values.append(
[n for n in all_networks
if (n['shared'] is True and
n['router:external'] == (
external if external is not None
else n['router:external']))])
expected_calls.append(
mock.call(test.IsHttpRequest(), **params))
elif call == 'non_shared':
params['shared'] = False
external = params.get('router:external')
return_values.append(
[n for n in all_networks
if (n['tenant_id'] == '1' and
n['shared'] is False and
n['router:external'] == (
external if external is not None
else n['router:external']))])
expected_calls.append(
mock.call(test.IsHttpRequest(),
tenant_id=tenant_id, **params))
self.mock_network_list.side_effect = return_values self.mock_network_list.side_effect = return_values
extra_kwargs.update(filter_params) extra_kwargs.update(filter_params)
@ -94,88 +132,186 @@ class NeutronApiTests(test.APIMockTestCase):
self.request, tenant_id, self.request, tenant_id,
include_external=include_external, include_external=include_external,
**extra_kwargs) **extra_kwargs)
if 'page_data' in extra_kwargs:
expected = [] has_more_data = ret_val[1]
if 'non_shared' in should_called: has_prev_data = ret_val[2]
expected += tenant_networks ret_val = ret_val[0]
if 'shared' in should_called:
expected += shared_networks
if 'external' in should_called and include_external:
expected += external_networks
self.assertEqual(set(n.id for n in expected),
set(n.id for n in ret_val))
self.mock_network_list.assert_has_calls(expected_calls) self.mock_network_list.assert_has_calls(expected_calls)
self.assertEqual(set(n.id for n in expected_networks),
set(n.id for n in ret_val))
self.assertNotIn(api.neutron.AUTO_ALLOCATE_ID,
[n.id for n in ret_val])
return ret_val, has_more_data, has_prev_data
@override_settings(OPENSTACK_NEUTRON_NETWORK={
'enable_auto_allocated_network': True})
@test.create_mocks({api.neutron: ('network_list',
'subnet_list')})
def _test_network_list_paged(
self, filter_params, expected_networks, page_data,
source_networks=None, **extra_kwargs):
"""Convenient method to test network_list_paged.
:param filter_params: Filters passed to network_list_for_tenant
:param expected_networks: the networks to be compared with the result.
:param page_data: dict provided by UI with pagination info
:param source_networks: networks to override the mocks.
"""
filter_params = filter_params or {}
sort_dir = page_data['sort_dir']
# invert sort_dir for calls
sort_dir = 'asc' if sort_dir == 'desc' else 'desc'
call_args = {'single_page': True, 'limit': 21, 'sort_key': 'id',
'sort_dir': sort_dir}
return_values = []
all_networks = (self.networks.list() if source_networks is None
else source_networks)
expected_calls = []
params = filter_params.copy()
params.update(call_args)
if page_data.get('marker_id'):
params.update({'marker': page_data.get('marker_id')})
extra_kwargs.update({'marker': page_data.get('marker_id')})
return_values.append(all_networks[0:21])
expected_calls.append(
mock.call(test.IsHttpRequest(), **params))
self.mock_network_list.side_effect = return_values
extra_kwargs.update(filter_params)
ret_val, has_more_data, has_prev_data = api.neutron.network_list_paged(
self.request, page_data, **extra_kwargs)
self.mock_network_list.assert_has_calls(expected_calls)
self.assertEqual(set(n.id for n in expected_networks),
set(n.id for n in ret_val))
self.assertNotIn(api.neutron.AUTO_ALLOCATE_ID,
[n.id for n in ret_val])
return ret_val, has_more_data, has_prev_data
def test_no_pre_auto_allocate_network(self):
# Ensure all three types of networks are not empty. This is required # Ensure all three types of networks are not empty. This is required
# to check 'pre_auto_allocate' network is not included. # to check 'pre_auto_allocate' network is not included.
tenant_id = '1'
all_networks = self.networks.list()
tenant_networks = [n for n in all_networks
if n['tenant_id'] == tenant_id]
shared_networks = [n for n in all_networks if n['shared']]
external_networks = [n for n in all_networks if n['router:external']]
self.assertTrue(tenant_networks) self.assertTrue(tenant_networks)
self.assertTrue(shared_networks) self.assertTrue(shared_networks)
self.assertTrue(external_networks) self.assertTrue(external_networks)
self.assertNotIn(api.neutron.AUTO_ALLOCATE_ID,
[n.id for n in ret_val])
def test_network_list_for_tenant(self): def test_network_list_for_tenant(self):
expected_networks = [n for n in self.networks.list()
if (n['tenant_id'] == '1' or n['shared'] is True)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=False, filter_params=None, include_external=False, filter_params=None,
should_called=['non_shared', 'shared']) should_called=['non_shared', 'shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_external(self): def test_network_list_for_tenant_with_external(self):
expected_networks = [n for n in self.networks.list()
if (n['tenant_id'] == '1' or
n['shared'] is True or
n['router:external'] is True)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, filter_params=None, include_external=True, filter_params=None,
should_called=['non_shared', 'shared', 'external']) should_called=['non_shared', 'shared', 'external'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_shared_false_wo_incext(self): def test_network_list_for_tenant_with_filters_shared_false_wo_incext(self):
expected_networks = [n for n in self.networks.list()
if (n['tenant_id'] == '1' and
n['shared'] is False)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=False, filter_params={'shared': True}, include_external=False, filter_params={'shared': False},
should_called=['shared']) should_called=['non_shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_shared_true_w_incext(self): def test_network_list_for_tenant_with_filters_shared_true_w_incext(self):
expected_networks = [n for n in self.networks.list()
if n['shared'] is True]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, filter_params={'shared': True}, include_external=True, filter_params={'shared': True},
should_called=['shared', 'external']) should_called=['shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_ext_false_wo_incext(self): def test_network_list_for_tenant_with_filters_ext_false_wo_incext(self):
expected_networks = [n for n in self.networks.list()
if ((n['tenant_id'] == '1' or
n['shared'] is True) and
n['router:external'] is False)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=False, filter_params={'router:external': False}, include_external=False, filter_params={'router:external': False},
should_called=['non_shared', 'shared']) should_called=['non_shared', 'shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_ext_true_wo_incext(self): def test_network_list_for_tenant_with_filters_ext_true_wo_incext(self):
expected_networks = [n for n in self.networks.list()
if ((n['tenant_id'] == '1' or
n['shared'] is True) and
n['router:external'] is True)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=False, filter_params={'router:external': True}, include_external=False, filter_params={'router:external': True},
should_called=['non_shared', 'shared']) should_called=['non_shared', 'shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_ext_false_w_incext(self): def test_network_list_for_tenant_with_filters_ext_false_w_incext(self):
expected_networks = [n for n in self.networks.list()
if ((n['tenant_id'] == '1' or
n['shared'] is True) and
n['router:external'] is False)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, filter_params={'router:external': False}, include_external=True, filter_params={'router:external': False},
should_called=['non_shared', 'shared']) should_called=['non_shared', 'shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_ext_true_w_incext(self): def test_network_list_for_tenant_with_filters_ext_true_w_incext(self):
expected_networks = [n for n in self.networks.list()
if n['router:external'] is True]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, filter_params={'router:external': True}, include_external=True, filter_params={'router:external': True},
should_called=['non_shared', 'shared', 'external']) should_called=['external', 'shared', 'non_shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_filters_both_shared_ext(self): def test_network_list_for_tenant_with_filters_both_shared_ext(self):
# To check 'shared' filter is specified in network_list # To check 'shared' filter is specified in network_list
# to look up external networks. # to look up external networks.
expected_networks = [n for n in self.networks.list()
if (n['shared'] is True and
n['router:external'] is True)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, include_external=True,
filter_params={'router:external': True, 'shared': True}, filter_params={'router:external': True, 'shared': True},
should_called=['shared', 'external']) should_called=['shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_with_other_filters(self): def test_network_list_for_tenant_with_other_filters(self):
# To check filter parameters other than shared and # To check filter parameters other than shared and
# router:external are passed as expected. # router:external are passed as expected.
expected_networks = [n for n in self.networks.list()
if (n['router:external'] is True and
n['shared'] is False)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, include_external=True,
filter_params={'router:external': True, 'shared': False, filter_params={'router:external': True, 'shared': False,
'foo': 'bar'}, 'foo': 'bar'},
should_called=['non_shared', 'external']) should_called=['external', 'non_shared'],
expected_networks=expected_networks)
def test_network_list_for_tenant_no_pre_auto_allocate_if_net_exists(self): def test_network_list_for_tenant_no_pre_auto_allocate_if_net_exists(self):
expected_networks = [n for n in self.networks.list()
if (n['tenant_id'] == '1' or
n['shared'] is True or
n['router:external'] is True)]
self._test_network_list_for_tenant( self._test_network_list_for_tenant(
include_external=True, filter_params=None, include_external=True, filter_params=None,
should_called=['non_shared', 'shared', 'external'], should_called=['non_shared', 'shared', 'external'],
include_pre_auto_allocate=True) include_pre_auto_allocate=True,
expected_networks=expected_networks)
@override_settings(OPENSTACK_NEUTRON_NETWORK={ @override_settings(OPENSTACK_NEUTRON_NETWORK={
'enable_auto_allocated_network': True}) 'enable_auto_allocated_network': True})
@ -197,9 +333,9 @@ class NeutronApiTests(test.APIMockTestCase):
self.assertEqual(2, self.mock_network_list.call_count) self.assertEqual(2, self.mock_network_list.call_count)
self.mock_network_list.assert_has_calls([ self.mock_network_list.assert_has_calls([
mock.call(test.IsHttpRequest(), tenant_id=tenant_id, mock.call(test.IsHttpRequest(), single_page=False, shared=True),
shared=False), mock.call(test.IsHttpRequest(), single_page=False,
mock.call(test.IsHttpRequest(), shared=True), shared=False, tenant_id=tenant_id)
]) ])
self.mock_is_extension_supported.assert_called_once_with( self.mock_is_extension_supported.assert_called_once_with(
test.IsHttpRequest(), 'auto-allocated-topology') test.IsHttpRequest(), 'auto-allocated-topology')
@ -219,11 +355,554 @@ class NeutronApiTests(test.APIMockTestCase):
self.assertEqual(2, self.mock_network_list.call_count) self.assertEqual(2, self.mock_network_list.call_count)
self.mock_network_list.assert_has_calls([ self.mock_network_list.assert_has_calls([
mock.call(test.IsHttpRequest(), tenant_id=tenant_id, mock.call(test.IsHttpRequest(), single_page=False, shared=True),
shared=False), mock.call(test.IsHttpRequest(), single_page=False,
mock.call(test.IsHttpRequest(), shared=True), shared=False, tenant_id=tenant_id),
]) ])
def test_network_list_for_tenant_first_page_has_more(self):
source_networks = neutron_data.source_nets_pagination1
all_nets = neutron_data.all_nets_pagination1
page1 = all_nets[0:20]
page_data = {
'sort_dir': 'desc',
'marker_id': None,
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['non_shared', 'shared'],
expected_networks=page1,
page_data=page_data,
source_networks=source_networks)
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
self.assertEqual('net_shr', result[0]['name'])
self.assertFalse(result[1]['shared'])
self.assertEqual(page1, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_second_page_has_more(self, mock_net_get):
all_nets = neutron_data.all_nets_pagination1
mock_net_get.return_value = all_nets[19]
page2 = all_nets[20:40]
page_data = {
'sort_dir': 'desc',
'marker_id': all_nets[19]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['non_shared'],
expected_networks=page2,
page_data=page_data,
source_networks=all_nets[20:41],
marker_calls=['non_shared'])
self.assertEqual(20, len(result))
self.assertFalse(result[0]['shared'])
self.assertEqual(page2[0]['name'], result[0]['name'])
self.assertTrue(more)
self.assertTrue(prev)
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_last_page(self, mock_net_get):
all_nets = neutron_data.all_nets_pagination1
mock_net_get.return_value = all_nets[39]
page3 = all_nets[40:60]
page_data = {
'sort_dir': 'desc',
'marker_id': all_nets[39]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['non_shared', 'external'],
expected_networks=page3,
page_data=page_data,
source_networks=page3,
marker_calls=['non_shared'])
self.assertEqual(20, len(result))
self.assertFalse(result[0]['router:external'])
self.assertEqual('net_ext', result[-1]['name'])
self.assertEqual(page3[0]['name'], result[0]['name'])
self.assertFalse(more)
self.assertTrue(prev)
self.assertEqual(page3, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_second_page_by_prev(self, mock_net_get):
all_nets = list(neutron_data.all_nets_pagination1)
all_nets.reverse()
mock_net_get.return_value = all_nets[19]
page2 = all_nets[20:40]
page_data = {
'sort_dir': 'asc',
'marker_id': all_nets[19]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['non_shared'],
expected_networks=page2,
page_data=page_data,
source_networks=all_nets[20:41],
marker_calls=['non_shared'])
self.assertEqual(20, len(result))
self.assertFalse(result[0]['router:external'])
self.assertFalse(result[0]['shared'])
self.assertFalse(result[-1]['router:external'])
self.assertFalse(result[-1]['shared'])
self.assertTrue(more)
self.assertTrue(prev)
page2.reverse()
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_first_page_by_prev(self, mock_net_get):
all_nets = list(neutron_data.all_nets_pagination1)
all_nets.reverse()
mock_net_get.return_value = all_nets[39]
page1 = all_nets[40:60]
page_data = {
'sort_dir': 'asc',
'marker_id': all_nets[39]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['non_shared', 'shared'],
expected_networks=page1,
page_data=page_data,
source_networks=page1,
marker_calls=['non_shared'])
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
self.assertFalse(result[1]['shared'])
self.assertEqual('net_shr', result[0]['name'])
page1.reverse()
self.assertEqual(page1, result)
def test_network_list_for_tenant_first_page_has_more2(self):
source_networks = neutron_data.source_nets_pagination2
all_nets = neutron_data.all_nets_pagination2
page1 = all_nets[0:20]
page_data = {
'sort_dir': 'desc',
'marker_id': None,
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['shared'],
expected_networks=page1,
page_data=page_data,
source_networks=source_networks)
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
self.assertTrue(result[0]['shared'])
self.assertTrue(result[-1]['shared'])
self.assertFalse(result[0]['router:external'])
self.assertFalse(result[-1]['router:external'])
self.assertEqual(page1, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_second_page_has_more2(self, mock_net_get):
all_nets = neutron_data.all_nets_pagination2
mock_net_get.return_value = all_nets[19]
page2 = all_nets[20:40]
page_data = {
'sort_dir': 'desc',
'marker_id': all_nets[19]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['shared', 'non_shared', 'external'],
expected_networks=page2,
page_data=page_data,
source_networks=all_nets[20:41],
marker_calls=['shared'])
self.assertEqual(20, len(result))
self.assertTrue(result[0]['shared'])
self.assertFalse(result[-1]['shared'])
self.assertTrue(result[-1]['router:external'])
self.assertFalse(result[0]['router:external'])
self.assertTrue(more)
self.assertTrue(prev)
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_last_page2(self, mock_net_get):
all_nets = neutron_data.all_nets_pagination2
mock_net_get.return_value = all_nets[39]
page3 = all_nets[40:60]
page_data = {
'sort_dir': 'desc',
'marker_id': all_nets[39]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['external'],
expected_networks=page3,
page_data=page_data,
source_networks=page3,
marker_calls=['external'])
self.assertEqual(20, len(result))
self.assertTrue(result[0]['router:external'])
self.assertFalse(result[0]['shared'])
self.assertFalse(result[-1]['shared'])
self.assertTrue(result[-1]['router:external'])
self.assertFalse(more)
self.assertTrue(prev)
self.assertEqual(page3, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_second_page_by_prev2(self, mock_net_get):
all_nets = list(neutron_data.all_nets_pagination2)
all_nets.reverse()
mock_net_get.return_value = all_nets[19]
page2 = all_nets[20:40]
page_data = {
'sort_dir': 'asc',
'marker_id': all_nets[19]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['shared', 'external', 'non_shared'],
expected_networks=page2,
page_data=page_data,
source_networks=all_nets[20:41],
marker_calls=['external'])
self.assertEqual(20, len(result))
self.assertTrue(result[0]['shared'])
self.assertFalse(result[-1]['shared'])
self.assertTrue(result[-1]['router:external'])
self.assertFalse(result[0]['router:external'])
self.assertTrue(more)
self.assertTrue(prev)
page2.reverse()
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_for_tenant_first_page_by_prev2(self, mock_net_get):
all_nets = list(neutron_data.all_nets_pagination2)
all_nets.reverse()
mock_net_get.return_value = all_nets[39]
page1 = all_nets[40:60]
page_data = {
'sort_dir': 'asc',
'marker_id': all_nets[39]['id'],
}
result, more, prev = self._test_network_list_for_tenant(
include_external=True, filter_params=None,
should_called=['shared'],
expected_networks=page1,
page_data=page_data,
source_networks=page1,
marker_calls=['shared'])
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
self.assertTrue(result[0]['shared'])
self.assertTrue(result[-1]['shared'])
self.assertFalse(result[0]['router:external'])
self.assertFalse(result[-1]['router:external'])
page1.reverse()
self.assertEqual(page1, result)
def test_network_list_paged_first_page_has_more(self):
source_networks = neutron_data.source_nets_pagination1
page1 = source_networks[0:20]
page_data = {
'sort_dir': 'desc',
'marker_id': None,
}
result, more, prev = self._test_network_list_paged(
filter_params=None,
expected_networks=page1,
page_data=page_data,
source_networks=source_networks)
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
self.assertEqual(page1, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_paged_second_page_has_more(self, mock_net_get):
source_networks = neutron_data.source_nets_pagination1
mock_net_get.return_value = source_networks[19]
page2 = source_networks[20:40]
page_data = {
'sort_dir': 'desc',
'marker_id': source_networks[19]['id'],
}
result, more, prev = self._test_network_list_paged(
filter_params=None,
expected_networks=page2,
page_data=page_data,
source_networks=source_networks[20:41])
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertTrue(prev)
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_paged_last_page(self, mock_net_get):
source_networks = neutron_data.source_nets_pagination1
mock_net_get.return_value = source_networks[39]
page3 = source_networks[40:60]
page_data = {
'sort_dir': 'desc',
'marker_id': source_networks[39]['id'],
}
result, more, prev = self._test_network_list_paged(
filter_params=None,
expected_networks=page3,
page_data=page_data,
source_networks=page3)
self.assertEqual(20, len(result))
self.assertFalse(more)
self.assertTrue(prev)
self.assertEqual(page3, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_paged_second_page_by_prev(self, mock_net_get):
source_networks = neutron_data.source_nets_pagination1
source_networks.reverse()
mock_net_get.return_value = source_networks[19]
page2 = source_networks[20:40]
page_data = {
'sort_dir': 'asc',
'marker_id': source_networks[19]['id'],
}
result, more, prev = self._test_network_list_paged(
filter_params=None,
expected_networks=page2,
page_data=page_data,
source_networks=source_networks[20:41])
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertTrue(prev)
page2.reverse()
self.assertEqual(page2, result)
@mock.patch.object(api.neutron, 'network_get')
def test_network_list_paged_first_page_by_prev(self, mock_net_get):
source_networks = neutron_data.source_nets_pagination1
source_networks.reverse()
mock_net_get.return_value = source_networks[39]
page1 = source_networks[40:60]
page_data = {
'sort_dir': 'asc',
'marker_id': source_networks[39]['id'],
}
result, more, prev = self._test_network_list_paged(
filter_params=None,
expected_networks=page1,
page_data=page_data,
source_networks=page1)
self.assertEqual(20, len(result))
self.assertTrue(more)
self.assertFalse(prev)
page1.reverse()
self.assertEqual(page1, result)
def test__perform_query_delete_last_project_without_marker(self):
marker_net = neutron_data.source_nets_pagination3[3]
query_result = (neutron_data.source_nets_pagination3[0:3], False, True)
query_func = mock.Mock(side_effect=[([], False, True), query_result])
self.request.session['network_deleted'] = \
neutron_data.source_nets_pagination3[4]
query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'asc'},
'sort_dir': 'asc',
}
modified_query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'desc'},
'sort_dir': 'desc',
}
result = api.neutron._perform_query(
query_func, dict(query_kwargs), marker_net)
self.assertEqual(query_result, result)
query_func.assert_has_calls([
mock.call(**query_kwargs), mock.call(**modified_query_kwargs)
])
def test__perform_query_delete_last_project_with_marker(self):
marker_net = neutron_data.source_nets_pagination3[3]
query_result = (neutron_data.source_nets_pagination3[0:4], False, False)
query_func = mock.Mock(side_effect=[([], False, True), query_result])
self.request.session['network_deleted'] = \
neutron_data.source_nets_pagination3[4]
query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'asc'},
'sort_dir': 'asc',
}
modified_query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'desc'},
'sort_dir': 'desc',
}
result = api.neutron._perform_query(
query_func, dict(query_kwargs), marker_net)
self.assertEqual(query_result, result)
query_func.assert_has_calls([
mock.call(**query_kwargs), mock.call(**modified_query_kwargs)
])
def test__perform_query_delete_last_admin_with_marker(self):
marker_net = neutron_data.source_nets_pagination3[3]
query_result = (neutron_data.source_nets_pagination3[0:4], False, False)
query_func = mock.Mock(side_effect=[([], False, True), query_result])
self.request.session['network_deleted'] = \
neutron_data.source_nets_pagination3[4]
query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'asc'},
'params': {'sort_dir': 'asc'},
}
modified_query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'desc'},
'params': {'sort_dir': 'desc'},
}
result = api.neutron._perform_query(
query_func, dict(query_kwargs), marker_net)
self.assertEqual(query_result, result)
query_func.assert_has_calls([
mock.call(**query_kwargs), mock.call(**modified_query_kwargs)
])
def test__perform_query_delete_first_admin(self):
marker_net = neutron_data.source_nets_pagination3[3]
query_result = (neutron_data.source_nets_pagination3[0:3], True, False)
query_func = mock.Mock(side_effect=[([], True, False), query_result])
self.request.session['network_deleted'] = \
neutron_data.source_nets_pagination3[0]
query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'desc',
'marker_id': marker_net['id']},
'params': {'sort_dir': 'desc',
'marker': marker_net['id']},
}
modified_query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': None,
'sort_dir': 'asc',
'marker_id': None},
'params': {'sort_dir': 'asc'},
}
result = api.neutron._perform_query(
query_func, dict(query_kwargs), marker_net)
self.assertEqual(query_result, result)
query_func.assert_has_calls([
mock.call(**query_kwargs), mock.call(**modified_query_kwargs)
])
def test__perform_query_delete_first_proj(self):
marker_net = neutron_data.source_nets_pagination3[3]
query_result = (neutron_data.source_nets_pagination3[0:3], True, False)
query_func = mock.Mock(side_effect=[([], True, False), query_result])
self.request.session['network_deleted'] = \
neutron_data.source_nets_pagination3[0]
query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': 'proj',
'sort_dir': 'desc',
'marker_id': marker_net['id']},
'sort_dir': 'desc',
}
modified_query_kwargs = {
'request': self.request,
'page_data': {'single_page': True,
'marker_type': None,
'sort_dir': 'asc',
'marker_id': None},
'sort_dir': 'asc',
}
result = api.neutron._perform_query(
query_func, dict(query_kwargs), marker_net)
self.assertEqual(query_result, result)
query_func.assert_has_calls([
mock.call(**query_kwargs), mock.call(**modified_query_kwargs)
])
def test__perform_query_normal_paginated(self):
query_result = (self.networks.list(), True, True)
query_func = mock.Mock(return_value=query_result)
query_kwargs = {'request': self.request,
'page_data': {'single_page': True}}
result = api.neutron._perform_query(query_func, query_kwargs, None)
self.assertEqual(query_result, result)
query_func.assert_called_once_with(**query_kwargs)
@override_settings(OPENSTACK_NEUTRON_NETWORK={
'enable_auto_allocated_network': True})
@test.create_mocks({api.neutron: ['is_extension_supported'],
api.nova: ['is_feature_available']})
def test__perform_query_with_preallocated(self):
self.mock_is_extension_supported.return_value = True
self.mock_is_feature_available.return_value = True
query_func = mock.Mock(return_value=([], False, False))
query_kwargs = {'request': self.request,
'page_data': {'single_page': True}}
result = api.neutron._perform_query(
query_func, query_kwargs, None, include_pre_auto_allocate=True)
self.assertIsInstance(result[0][0], api.neutron.PreAutoAllocateNetwork)
self.assertEqual(False, result[1])
self.assertEqual(False, result[2])
query_func.assert_called_once_with(**query_kwargs)
def test__perform_query_not_paginated(self):
query_result = self.networks.list()
query_func = mock.Mock(return_value=(query_result, False, False))
query_kwargs1 = {'page_data': {'single_page': False}}
query_kwargs2 = {'page_data': {}}
result = api.neutron._perform_query(query_func, query_kwargs1, None)
self.assertEqual(query_result, result)
query_func.assert_called_once_with(**query_kwargs1)
query_func.reset_mock()
result = api.neutron._perform_query(query_func, query_kwargs2, None)
self.assertEqual(query_result, result)
query_func.assert_called_once_with(**query_kwargs2)
@mock.patch.object(api.neutron, 'neutronclient') @mock.patch.object(api.neutron, 'neutronclient')
def test_network_get(self, mock_neutronclient): def test_network_get(self, mock_neutronclient):
network = {'network': self.api_networks.first()} network = {'network': self.api_networks.first()}

View File

@ -471,7 +471,7 @@ class QuotaTests(test.APITestCase):
target: { target: {
'used': used, 'used': used,
'quota': limit, 'quota': limit,
'available': limit - used 'available': max(limit - used, 0)
} }
} }

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixed lack of pagination for the networks page under Project and
Admin Dashboard.