Add a generic function for filtering API objects
Replace the filter_nodes() function with a more generic solution. We can now filter a list of generic API object, by any attribute. Change-Id: I76f8226df5123a8038220658cb340b67461024b4
This commit is contained in:
parent
493d6c06d9
commit
fc0a6f77bc
@ -589,38 +589,3 @@ class Node(base.APIResourceWrapper):
|
||||
if self.instance_uuid:
|
||||
return _("Provisioned")
|
||||
return _("Free")
|
||||
|
||||
|
||||
def filter_nodes(nodes, healthy=None, power_state=None):
|
||||
"""Filters the list of Nodes and returns the filtered list.
|
||||
|
||||
:param nodes: list of tuskar_ui.api.node.Node objects to filter
|
||||
:type nodes: list
|
||||
:param healthy: retrieve all Nodes (healthy=None),
|
||||
only the healthly ones (healthy=True),
|
||||
or only those in an error state (healthy=False)
|
||||
:type healthy: None or bool
|
||||
:param power_state: retrieve all Nodes (power_state=None),
|
||||
only those that are running (power_state=True),
|
||||
or only those that are stopped (power_state=False)
|
||||
:type power_state: None or bool
|
||||
:return: list of filtered tuskar_ui.api.node.Node objects
|
||||
:rtype: list
|
||||
"""
|
||||
if healthy is not None:
|
||||
if healthy:
|
||||
nodes = [node for node in nodes
|
||||
if node.power_state not in ERROR_STATES]
|
||||
else:
|
||||
nodes = [node for node in nodes
|
||||
if node.power_state in ERROR_STATES]
|
||||
|
||||
if power_state is not None:
|
||||
if power_state:
|
||||
nodes = [node for node in nodes
|
||||
if node.power_state in POWER_ON_STATES]
|
||||
else:
|
||||
nodes = [node for node in nodes
|
||||
if node.power_state not in POWER_ON_STATES]
|
||||
|
||||
return nodes
|
||||
|
@ -12,6 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
|
||||
from django.core import urlresolvers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
@ -21,6 +23,7 @@ from openstack_dashboard.api import base as api_base
|
||||
from tuskar_ui import api
|
||||
from tuskar_ui.infrastructure.nodes import tables
|
||||
from tuskar_ui.utils import metering as metering_utils
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
@ -34,34 +37,26 @@ class OverviewTab(tabs.Tab):
|
||||
memory_mb = sum(int(node.memory_mb) for node in nodes if
|
||||
node.memory_mb)
|
||||
local_gb = sum(int(node.local_gb) for node in nodes if node.local_gb)
|
||||
deployed_nodes = api.node.Node.list(request, associated=True)
|
||||
free_nodes = api.node.Node.list(request, associated=False)
|
||||
deployed_nodes_error = api.node.filter_nodes(
|
||||
deployed_nodes, healthy=False)
|
||||
deployed_nodes_down = api.node.filter_nodes(
|
||||
deployed_nodes, power_state=False)
|
||||
free_nodes_error = api.node.filter_nodes(free_nodes, healthy=False)
|
||||
free_nodes_down = api.node.filter_nodes(free_nodes, power_state=False)
|
||||
total_nodes = deployed_nodes + free_nodes
|
||||
total_nodes_error = deployed_nodes_error + free_nodes_error
|
||||
total_nodes_down = deployed_nodes_down + free_nodes_down
|
||||
total_nodes_healthy = api.node.filter_nodes(total_nodes, healthy=True)
|
||||
total_nodes_up = api.node.filter_nodes(total_nodes, power_state=True)
|
||||
|
||||
nodes_provisioned = api.node.Node.list(request, associated=True)
|
||||
nodes_free = api.node.Node.list(request, associated=False)
|
||||
nodes_provisioned_down = utils.filter_items(
|
||||
nodes_provisioned, power_state__not_in=api.node.POWER_ON_STATES)
|
||||
nodes_free_down = utils.filter_items(
|
||||
nodes_free, power_state__not_in=api.node.POWER_ON_STATES)
|
||||
|
||||
nodes_down = itertools.chain(nodes_provisioned_down, nodes_free_down)
|
||||
nodes_up = utils.filter_items(
|
||||
nodes, power_state__in=api.node.POWER_ON_STATES)
|
||||
|
||||
context = {
|
||||
'cpus': cpus,
|
||||
'memory_gb': memory_mb / 1024.0,
|
||||
'local_gb': local_gb,
|
||||
'total_nodes_healthy': total_nodes_healthy,
|
||||
'total_nodes_up': total_nodes_up,
|
||||
'total_nodes_error': total_nodes_error,
|
||||
'total_nodes_down': total_nodes_down,
|
||||
'deployed_nodes': deployed_nodes,
|
||||
'deployed_nodes_error': deployed_nodes_error,
|
||||
'deployed_nodes_down': deployed_nodes_down,
|
||||
'free_nodes': free_nodes,
|
||||
'free_nodes_error': free_nodes_error,
|
||||
'free_nodes_down': free_nodes_down,
|
||||
'nodes_up_count': utils.length(nodes_up),
|
||||
'nodes_down_count': utils.length(nodes_down),
|
||||
'nodes_provisioned_count': utils.length(nodes_provisioned),
|
||||
'nodes_free_count': utils.length(nodes_free),
|
||||
}
|
||||
|
||||
if api_base.is_service_enabled(self.request, 'metering'):
|
||||
@ -99,9 +94,6 @@ class RegisteredTab(tabs.TableTab):
|
||||
nodes = api.node.Node.list(self.request, maintenance=False,
|
||||
_error_redirect=redirect)
|
||||
|
||||
if 'errors' in self.request.GET:
|
||||
return api.node.filter_nodes(nodes, healthy=False)
|
||||
|
||||
if nodes:
|
||||
all_resources = api.heat.Resource.list_all_resources(self.request)
|
||||
for node in nodes:
|
||||
|
@ -24,16 +24,16 @@
|
||||
<div class="col-xs-4">
|
||||
<div class="widget">
|
||||
<h3>{% trans 'Free Nodes' %}</h3>
|
||||
<div class="d3_pie_chart_distribution" data-used="Deployed={{ deployed_nodes|length }}|Free={{ free_nodes|length }}"></div>
|
||||
<div class="d3_pie_chart_distribution" data-used="Provisioned={{ nodes_provisioned_count }}|Free={{ nodes_free_count }}"></div>
|
||||
<div class="widget-info">
|
||||
<a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
|
||||
<span class="info">{{ free_nodes|length|default:0 }}</span>
|
||||
<span class="info">{{ nodes_free_count }}</span>
|
||||
{% trans 'Free Nodes' %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="widget-info">
|
||||
<a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
|
||||
<span class="info">{{ deployed_nodes|length|default:0 }}</span>
|
||||
<span class="info">{{ nodes_provisioned_count }}</span>
|
||||
{% trans 'Provisioned Nodes' %}
|
||||
</a>
|
||||
</div>
|
||||
@ -42,7 +42,7 @@
|
||||
<div class="col-xs-4">
|
||||
<div class="widget">
|
||||
<h2>{% trans 'Power Status' %}</h2>
|
||||
<div class="d3_pie_chart_distribution" data-used="Running={{ total_nodes_up|length }}|Stopped={{ total_nodes_down|length }}"></div>
|
||||
<div class="d3_pie_chart_distribution" data-used="Running={{ nodes_up_count }}|Stopped={{ nodes_down_count }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -129,7 +129,7 @@
|
||||
<div class="col-lg-1">
|
||||
<div class="widget-info">
|
||||
<a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
|
||||
<span class="info">{{ deployed_nodes|length|default:0 }}</span><br>
|
||||
<span class="info">{{ nodes_provisioned_count }}</span><br>
|
||||
{% trans 'Provisioned' %}<br>
|
||||
{% trans 'Nodes' %}<br>
|
||||
</a>
|
||||
|
@ -141,17 +141,3 @@ class NodeAPITests(test.APITestCase):
|
||||
node = self.baremetalclient_nodes.first()
|
||||
ret_val = api.node.BareMetalNode(node).addresses
|
||||
self.assertEqual(2, len(ret_val))
|
||||
|
||||
def test_filter_nodes(self):
|
||||
nodes = self.baremetalclient_nodes.list()
|
||||
nodes = [api.node.BareMetalNode(node) for node in nodes]
|
||||
num_nodes = len(nodes)
|
||||
|
||||
with patch('novaclient.v1_1.contrib.baremetal.'
|
||||
'BareMetalNodeManager.list', return_value=nodes):
|
||||
all_nodes = api.node.filter_nodes(nodes)
|
||||
healthy_nodes = api.node.filter_nodes(nodes, healthy=True)
|
||||
defective_nodes = api.node.filter_nodes(nodes, healthy=False)
|
||||
self.assertEqual(len(all_nodes), num_nodes)
|
||||
self.assertEqual(len(healthy_nodes), num_nodes - 1)
|
||||
self.assertEqual(len(defective_nodes), 1)
|
||||
|
34
tuskar_ui/test/utils_tests.py
Normal file
34
tuskar_ui/test/utils_tests.py
Normal file
@ -0,0 +1,34 @@
|
||||
# -*- coding: utf8 -*-
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tuskar_ui.test import helpers as test
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
class TestItem(object):
|
||||
def __init__(self, index):
|
||||
self.index = index
|
||||
|
||||
|
||||
class UtilsTests(test.TestCase):
|
||||
def test_filter_items(self):
|
||||
items = [TestItem(i) for i in range(7)]
|
||||
|
||||
first = utils.filter_items(items, index=0)
|
||||
even = utils.filter_items(items, index__in=(0, 2, 4, 6))
|
||||
last_two = utils.filter_items(items, index__not_in=range(5))
|
||||
|
||||
self.assertEqual(utils.length(first), 1)
|
||||
self.assertEqual(utils.length(even), 4)
|
||||
self.assertEqual(utils.length(last_two), 2)
|
@ -34,3 +34,44 @@ def list_to_dict(object_list, key_attribute='id'):
|
||||
:rtype: dict
|
||||
"""
|
||||
return dict((getattr(o, key_attribute), o) for o in object_list)
|
||||
|
||||
|
||||
def length(iterator):
|
||||
"""A length function for iterators
|
||||
|
||||
Returns the number of items in the specified iterator. Note that this
|
||||
function consumes the iterator in the process.
|
||||
"""
|
||||
return sum(1 for _item in iterator)
|
||||
|
||||
|
||||
def filter_items(items, **kwargs):
|
||||
"""Filters the list of items and returns the filtered list.
|
||||
|
||||
Example usage:
|
||||
>>> class Item(object):
|
||||
... def __init__(self, index):
|
||||
... self.index = index
|
||||
... def __repr__(self):
|
||||
... return '<Item index=%d>' % self.index
|
||||
>>> items = [Item(i) for i in range(7)]
|
||||
>>> list(filter_items(items, index=1))
|
||||
[<Item index=1>]
|
||||
>>> list(filter_items(items, index__in=(1, 2, 3)))
|
||||
[<Item index=1>, <Item index=2>, <Item index=3>]
|
||||
>>> list(filter_items(items, index__not_in=(1, 2, 3)))
|
||||
[<Item index=0>, <Item index=4>, <Item index=5>, <Item index=6>]
|
||||
"""
|
||||
for item in items:
|
||||
for name, value in kwargs.items():
|
||||
if name.endswith('__in'):
|
||||
if getattr(item, name[:-len('__in')]) not in value:
|
||||
break
|
||||
elif name.endswith('__not_in'):
|
||||
if getattr(item, name[:-len('__not_in')]) in value:
|
||||
break
|
||||
else:
|
||||
if getattr(item, name) != value:
|
||||
break
|
||||
else:
|
||||
yield item
|
||||
|
Loading…
x
Reference in New Issue
Block a user