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:
parent
9873655c05
commit
196de449b6
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
||||||
|
@ -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"))
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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'])
|
||||||
|
@ -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()}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fixed lack of pagination for the networks page under Project and
|
||||||
|
Admin Dashboard.
|
Loading…
Reference in New Issue
Block a user