Merge "Server-side filtering networks"
This commit is contained in:
commit
3bd7852746
@ -643,20 +643,34 @@ def network_list_for_tenant(request, tenant_id, include_external=False,
|
||||
LOG.debug("network_list_for_tenant(): tenant_id=%s, params=%s"
|
||||
% (tenant_id, params))
|
||||
|
||||
# If a user has admin role, network list returned by Neutron API
|
||||
# contains networks that do not belong to that tenant.
|
||||
# So we need to specify tenant_id when calling network_list().
|
||||
networks = network_list(request, tenant_id=tenant_id,
|
||||
shared=False, **params)
|
||||
networks = []
|
||||
shared = params.get('shared')
|
||||
if shared is not None:
|
||||
del params['shared']
|
||||
|
||||
# In the current Neutron API, there is no way to retrieve
|
||||
# both owner networks and public networks in a single API call.
|
||||
networks += network_list(request, shared=True, **params)
|
||||
if shared in (None, False):
|
||||
# If a user has admin role, network list returned by Neutron API
|
||||
# contains networks that do not belong to that tenant.
|
||||
# So we need to specify tenant_id when calling network_list().
|
||||
networks += network_list(request, tenant_id=tenant_id,
|
||||
shared=False, **params)
|
||||
|
||||
if include_external:
|
||||
if shared in (None, True):
|
||||
# In the current Neutron API, there is no way to retrieve
|
||||
# both owner networks and public networks in a single API call.
|
||||
networks += network_list(request, shared=True, **params)
|
||||
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]
|
||||
ext_nets = network_list(request, **{'router:external': True})
|
||||
networks += [n for n in ext_nets if n.id not in fetched_net_ids]
|
||||
# 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
|
||||
|
||||
|
@ -84,6 +84,12 @@ DISPLAY_CHOICES = (
|
||||
)
|
||||
|
||||
|
||||
class AdminNetworksFilterAction(project_tables.ProjectNetworksFilterAction):
|
||||
name = "filter_admin_networks"
|
||||
filter_choices = (('project', _("Project ="), True),) +\
|
||||
project_tables.ProjectNetworksFilterAction.filter_choices
|
||||
|
||||
|
||||
class NetworksTable(tables.DataTable):
|
||||
tenant = tables.Column("tenant_name", verbose_name=_("Project"))
|
||||
name = tables.WrappingColumn("name_or_id", verbose_name=_("Network Name"),
|
||||
@ -108,7 +114,7 @@ class NetworksTable(tables.DataTable):
|
||||
name = "networks"
|
||||
verbose_name = _("Networks")
|
||||
table_actions = (CreateNetwork, DeleteNetwork,
|
||||
project_tables.NetworksFilterAction)
|
||||
AdminNetworksFilterAction)
|
||||
row_actions = (EditNetwork, DeleteNetwork)
|
||||
|
||||
def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs):
|
||||
|
@ -44,6 +44,9 @@ class IndexView(tables.DataTableView):
|
||||
table_class = networks_tables.NetworksTable
|
||||
template_name = 'admin/networks/index.html'
|
||||
page_title = _("Networks")
|
||||
FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False},
|
||||
'router:external': {_("yes"): True, _("no"): False},
|
||||
'admin_state_up': {_("up"): True, _("down"): False}}
|
||||
|
||||
@memoized.memoized_method
|
||||
def _get_tenant_list(self):
|
||||
@ -78,7 +81,8 @@ class IndexView(tables.DataTableView):
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
networks = api.neutron.network_list(self.request)
|
||||
search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING)
|
||||
networks = api.neutron.network_list(self.request, **search_opts)
|
||||
except Exception:
|
||||
networks = []
|
||||
msg = _('Network list can not be retrieved.')
|
||||
@ -93,6 +97,18 @@ class IndexView(tables.DataTableView):
|
||||
n.num_agents = self._get_agents_data(n.id)
|
||||
return networks
|
||||
|
||||
def get_filters(self, filters=None, filters_map=None):
|
||||
filters = super(IndexView, self).get_filters(filters, filters_map)
|
||||
if 'project' in filters:
|
||||
tenants = api.keystone.tenant_list(self.request)[0]
|
||||
tenant_filter_ids = [t.id for t in tenants
|
||||
if t.name == filters['project']]
|
||||
if not tenant_filter_ids:
|
||||
return []
|
||||
del filters['project']
|
||||
filters['tenant_id'] = tenant_filter_ids
|
||||
return filters
|
||||
|
||||
|
||||
class CreateView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateNetwork
|
||||
|
@ -158,13 +158,17 @@ STATUS_DISPLAY_CHOICES = (
|
||||
)
|
||||
|
||||
|
||||
class NetworksFilterAction(tables.FilterAction):
|
||||
|
||||
def filter(self, table, networks, filter_string):
|
||||
"""Naive case-insensitive search."""
|
||||
query = filter_string.lower()
|
||||
return [network for network in networks
|
||||
if query in network.name.lower()]
|
||||
class ProjectNetworksFilterAction(tables.FilterAction):
|
||||
name = "filter_project_networks"
|
||||
filter_type = "server"
|
||||
filter_choices = (('name', _("Name ="), True),
|
||||
('shared', _("Shared ="), True,
|
||||
_("e.g. Yes / No")),
|
||||
('router:external', _("External ="), True,
|
||||
_("e.g. Yes / No")),
|
||||
('status', _("Status ="), True),
|
||||
('admin_state_up', _("Admin State ="), True,
|
||||
_("e.g. UP / DOWN")))
|
||||
|
||||
|
||||
class NetworksTable(tables.DataTable):
|
||||
@ -187,5 +191,5 @@ class NetworksTable(tables.DataTable):
|
||||
name = "networks"
|
||||
verbose_name = _("Networks")
|
||||
table_actions = (CreateNetwork, DeleteNetwork,
|
||||
NetworksFilterAction)
|
||||
ProjectNetworksFilterAction)
|
||||
row_actions = (EditNetwork, CreateSubnet, DeleteNetwork)
|
||||
|
@ -44,12 +44,16 @@ class IndexView(tables.DataTableView):
|
||||
table_class = project_tables.NetworksTable
|
||||
template_name = 'project/networks/index.html'
|
||||
page_title = _("Networks")
|
||||
FILTERS_MAPPING = {'shared': {_("yes"): True, _("no"): False},
|
||||
'router:external': {_("yes"): True, _("no"): False},
|
||||
'admin_state_up': {_("up"): True, _("down"): False}}
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
tenant_id = self.request.user.tenant_id
|
||||
search_opts = self.get_filters(filters_map=self.FILTERS_MAPPING)
|
||||
networks = api.neutron.network_list_for_tenant(
|
||||
self.request, tenant_id, include_external=True)
|
||||
self.request, tenant_id, include_external=True, **search_opts)
|
||||
except Exception:
|
||||
networks = []
|
||||
msg = _('Network list can not be retrieved.')
|
||||
|
@ -42,26 +42,44 @@ class NeutronApiTests(test.APITestCase):
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_list',
|
||||
'subnet_list')})
|
||||
def _test_network_list_for_tenant(self, include_external):
|
||||
def _test_network_list_for_tenant(
|
||||
self, include_external,
|
||||
filter_params, should_called):
|
||||
"""Convenient method to test network_list_for_tenant.
|
||||
|
||||
:param include_external: Passed to network_list_for_tenant.
|
||||
:param filter_params: Filters passed to network_list_for_tenant
|
||||
:param should_called: this argument specifies which methods
|
||||
should be called. Methods in this list should be called.
|
||||
Valid values are non_shared, shared, and external.
|
||||
"""
|
||||
filter_params = filter_params or {}
|
||||
all_networks = self.networks.list()
|
||||
tenant_id = '1'
|
||||
api.neutron.network_list(
|
||||
IsA(http.HttpRequest),
|
||||
tenant_id=tenant_id,
|
||||
shared=False).AndReturn([
|
||||
network for network in all_networks
|
||||
if network['tenant_id'] == tenant_id
|
||||
])
|
||||
api.neutron.network_list(
|
||||
IsA(http.HttpRequest),
|
||||
shared=True).AndReturn([
|
||||
network for network in all_networks
|
||||
if network.get('shared')
|
||||
])
|
||||
if include_external:
|
||||
if 'non_shared' in should_called:
|
||||
params = filter_params.copy()
|
||||
params['shared'] = False
|
||||
api.neutron.network_list(
|
||||
IsA(http.HttpRequest),
|
||||
**{'router:external': True}).AndReturn([
|
||||
tenant_id=tenant_id,
|
||||
**params).AndReturn([
|
||||
network for network in all_networks
|
||||
if network['tenant_id'] == tenant_id
|
||||
])
|
||||
if 'shared' in should_called:
|
||||
params = filter_params.copy()
|
||||
params['shared'] = True
|
||||
api.neutron.network_list(
|
||||
IsA(http.HttpRequest),
|
||||
**params).AndReturn([
|
||||
network for network in all_networks
|
||||
if network.get('shared')
|
||||
])
|
||||
if 'external' in should_called:
|
||||
params = filter_params.copy()
|
||||
params['router:external'] = True
|
||||
api.neutron.network_list(
|
||||
IsA(http.HttpRequest), **params).AndReturn([
|
||||
network for network in all_networks
|
||||
if network.get('router:external')
|
||||
])
|
||||
@ -69,19 +87,74 @@ class NeutronApiTests(test.APITestCase):
|
||||
|
||||
ret_val = api.neutron.network_list_for_tenant(
|
||||
self.request, tenant_id,
|
||||
include_external=include_external)
|
||||
include_external=include_external,
|
||||
**filter_params)
|
||||
|
||||
expected = [n for n in all_networks
|
||||
if (n['tenant_id'] == tenant_id or
|
||||
n['shared'] or
|
||||
(include_external and n['router:external']))]
|
||||
if (('non_shared' in should_called and
|
||||
n['tenant_id'] == tenant_id) or
|
||||
('shared' in should_called and n['shared']) or
|
||||
('external' in should_called and
|
||||
include_external and n['router:external']))]
|
||||
self.assertEqual(set(n.id for n in expected),
|
||||
set(n.id for n in ret_val))
|
||||
|
||||
def test_network_list_for_tenant(self):
|
||||
self._test_network_list_for_tenant(include_external=False)
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=False, filter_params=None,
|
||||
should_called=['non_shared', 'shared'])
|
||||
|
||||
def test_network_list_for_tenant_with_external(self):
|
||||
self._test_network_list_for_tenant(include_external=True)
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True, filter_params=None,
|
||||
should_called=['non_shared', 'shared', 'external'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_shared_false_wo_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=False, filter_params={'shared': True},
|
||||
should_called=['shared'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_shared_true_w_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True, filter_params={'shared': True},
|
||||
should_called=['shared', 'external'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_ext_false_wo_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=False, filter_params={'router:external': False},
|
||||
should_called=['non_shared', 'shared'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_ext_true_wo_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=False, filter_params={'router:external': True},
|
||||
should_called=['non_shared', 'shared'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_ext_false_w_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True, filter_params={'router:external': False},
|
||||
should_called=['non_shared', 'shared'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_ext_true_w_incext(self):
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True, filter_params={'router:external': True},
|
||||
should_called=['non_shared', 'shared', 'external'])
|
||||
|
||||
def test_network_list_for_tenant_with_filters_both_shared_ext(self):
|
||||
# To check 'shared' filter is specified in network_list
|
||||
# to look up external networks.
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True,
|
||||
filter_params={'router:external': True, 'shared': True},
|
||||
should_called=['shared', 'external'])
|
||||
|
||||
def test_network_list_for_tenant_with_other_filters(self):
|
||||
# To check filter parameters other than shared and
|
||||
# router:external are passed as expected.
|
||||
self._test_network_list_for_tenant(
|
||||
include_external=True,
|
||||
filter_params={'router:external': True, 'shared': False,
|
||||
'foo': 'bar'},
|
||||
should_called=['non_shared', 'external'])
|
||||
|
||||
def test_network_get(self):
|
||||
network = {'network': self.api_networks.first()}
|
||||
|
Loading…
Reference in New Issue
Block a user