Adding provision state of Ironic

Adding provision state and extra to API of Nodes.
Showing provision state stats in overview page.

Change-Id: Ic49f5e34c01ead0f7fc43cf1c073e1463105125c
This commit is contained in:
Ladislav Smola 2014-10-24 15:29:43 +02:00
parent 4a8736cf07
commit a0f9ee338f
5 changed files with 129 additions and 23 deletions

View File

@ -32,6 +32,7 @@ from tuskar_ui.utils import utils
ERROR_STATES = set(['deploy failed', 'error'])
POWER_ON_STATES = set(['on', 'power on'])
# TODO(tzumainn) this states doesn't seems to be correct, please fix this
# overall state of the node; not power states
DISCOVERING_STATE = 'discovering'
DISCOVERED_STATE = 'discovered'
@ -39,6 +40,13 @@ PROVISIONED_STATE = 'provisioned'
PROVISIONING_FAILED_STATE = 'provisioning failed'
PROVISIONING_STATE = 'provisioning'
FREE_STATE = 'free'
# TODO(tzumain) the below should be correct
PROVISION_STATE_FREE = ['deleted', None]
PROVISION_STATE_PROVISIONED = ['active']
PROVISION_STATE_PROVISIONING = [
'deploying', 'wait call-back', 'rebuild', 'deploy complete']
PROVISION_STATE_DELETING = ['deleting']
PROVISION_STATE_ERROR = ['error', 'deploy failed']
LOG = logging.getLogger(__name__)
@ -490,6 +498,16 @@ class BareMetalNode(base.APIResourceWrapper):
return [interface["address"] for interface in
self.interfaces]
@cached_property
def extra(self):
"""Ironic compatibility parameter."""
return {}
@cached_property
def provision_state(self):
"""Ironic compatibility parameter."""
return None
class NodeClient(object):
@ -507,7 +525,8 @@ class NodeClient(object):
class Node(base.APIResourceWrapper):
_attrs = ('id', 'uuid', 'instance_uuid', 'driver', 'driver_info', 'state',
'power_state', 'target_power_state', 'addresses', 'maintenance',
'cpus', 'memory_mb', 'local_gb', 'cpu_arch')
'cpus', 'memory_mb', 'local_gb', 'cpu_arch', 'extra',
'provision_state')
def __init__(self, apiresource, request=None, **kwargs):
"""Initialize a Node

View File

@ -40,23 +40,31 @@ class OverviewTab(tabs.Tab):
node.memory_mb)
local_gb = sum(int(node.local_gb) for node in nodes if node.local_gb)
nodes_provisioned = api.node.Node.list(request, associated=True)
nodes_free = api.node.Node.list(request, associated=False)
nodes_all_count = (utils.length(nodes_provisioned) +
utils.length(nodes_free))
nodes_provisioned = set(utils.filter_items(
nodes, provision_state__in=api.node.PROVISION_STATE_PROVISIONED))
nodes_free = set(utils.filter_items(
nodes, provision_state__in=api.node.PROVISION_STATE_FREE))
nodes_deleting = set(utils.filter_items(
nodes, provision_state__in=api.node.PROVISION_STATE_DELETING))
nodes_error = set(utils.filter_items(
nodes, provision_state__in=api.node.PROVISION_STATE_ERROR))
nodes_provisioned_maintenance = list(utils.filter_items(
nodes_provisioned_maintenance = set(utils.filter_items(
nodes_provisioned, maintenance=True))
nodes_provisioned_not_maintenance = list(
set(nodes_provisioned) - set(nodes_provisioned_maintenance))
nodes_provisioned_not_maintenance = (
nodes_provisioned - nodes_provisioned_maintenance)
nodes_free_maintenance = list(utils.filter_items(
nodes_provisioning = set(utils.filter_items(
nodes,
provision_state__in=api.node.PROVISION_STATE_PROVISIONING))
nodes_free_maintenance = set(utils.filter_items(
nodes_free, maintenance=True))
nodes_free_not_maintenance = list(
set(nodes_free) - set(nodes_free_maintenance))
nodes_free_not_maintenance = (
nodes_free - nodes_free_maintenance)
nodes_maintenance = (
nodes_provisioned_maintenance + nodes_free_maintenance)
nodes_provisioned_maintenance | nodes_free_maintenance)
nodes_provisioned_down = utils.filter_items(
nodes_provisioned, power_state__not_in=api.node.POWER_ON_STATES)
@ -67,18 +75,42 @@ class OverviewTab(tabs.Tab):
nodes_up = utils.filter_items(
nodes, power_state__in=api.node.POWER_ON_STATES)
nodes_free_count = len(nodes_free_not_maintenance)
nodes_provisioned_count = len(
nodes_provisioned_not_maintenance)
nodes_provisioning_count = len(nodes_provisioning)
nodes_maintenance_count = len(nodes_maintenance)
nodes_deleting_count = len(nodes_deleting)
nodes_error_count = len(nodes_error)
context = {
'cpus': cpus,
'memory_gb': memory_mb / 1024.0,
'local_gb': local_gb,
'nodes_up_count': utils.length(nodes_up),
'nodes_down_count': utils.length(nodes_down),
'nodes_provisioned_count': utils.length(
nodes_provisioned_not_maintenance),
'nodes_free_count': utils.length(nodes_free_not_maintenance),
'nodes_maintenance_count': utils.length(nodes_maintenance),
'nodes_all_count': nodes_all_count
'nodes_provisioned_count': nodes_provisioned_count,
'nodes_provisioning_count': nodes_provisioning_count,
'nodes_free_count': nodes_free_count,
'nodes_deleting_count': nodes_deleting_count,
'nodes_error_count': nodes_error_count,
'nodes_maintenance_count': nodes_maintenance_count,
'nodes_all_count': len(nodes),
'nodes_status_data':
'Provisioned={0}|Free={1}|Maintenance={2}'.format(
nodes_provisioned_count, nodes_free_count,
nodes_maintenance_count)
}
# additional node status pie chart data, showing only if it appears
if nodes_provisioning_count:
context['nodes_status_data'] += '|Provisioning={0}'.format(
nodes_provisioning_count)
if nodes_deleting_count:
context['nodes_status_data'] += '|Deleting={0}'.format(
nodes_deleting_count)
if nodes_error_count:
context['nodes_status_data'] += '|Error={0}'.format(
nodes_error_count)
if api_base.is_service_enabled(self.request, 'metering'):
context['meter_conf'] = (

View File

@ -17,7 +17,7 @@
</div>
<div class="widget">
<h3>{% trans 'Nodes Status' %}</h3>
<div class="d3_pie_chart_distribution" data-used="Provisioned={{ nodes_provisioned_count }}|Free={{ nodes_free_count }}|Maintenance={{ nodes_maintenance_count }}"></div>
<div class="d3_pie_chart_distribution" data-used="{{ nodes_status_data }}"></div>
</div>
<div class="widget">
<h3>{% trans 'Power Status' %}</h3>
@ -33,6 +33,54 @@
<span class="info">{{ nodes_provisioned_count }} {% trans 'provisioned nodes' %}</span>
</a>
</div>
{% if nodes_provisioning_count %}
<div class="widget-info">
<i class="fa fa-spinner fa-spin text-info"></i><a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
{% blocktrans count nodes_provisioning_count as counter %}
{{ counter }} node
{% plural %}
{{ counter }} nodes
{% endblocktrans %}
</a>
{% blocktrans count nodes_provisioning_count as counter %}
is being provisioned
{% plural %}
are being provisioned
{% endblocktrans %}
</div>
{% endif %}
{% if nodes_deleting_count %}
<div class="widget-info">
<i class="fa fa-spinner fa-spin text-info"></i><a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
{% blocktrans count nodes_deleting_count as counter %}
{{ counter }} node
{% plural %}
{{ counter }} nodes
{% endblocktrans %}
</a>
{% blocktrans count nodes_deleting_count as counter %}
is being deleted
{% plural %}
are being deleted
{% endblocktrans %}
</div>
{% endif %}
{% if nodes_error_count %}
<div class="widget-info">
<i class="fa fa-exclamation-circle text-info"></i><a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__registered">
{% blocktrans count nodes_error_count as counter %}
{{ counter }} node
{% plural %}
{{ counter }} nodes
{% endblocktrans %}
</a>
{% blocktrans count nodes_error_count as counter %}
{{ counter }} failed
{% plural %}
{{ counter }} failed
{% endblocktrans %}
</div>
{% endif %}
</div>
</div>
{% url 'horizon:infrastructure:nodes:nodes_performance' as node_perf_url %}

View File

@ -54,8 +54,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'list.return_value': [],
}) as mock:
res = self.client.get(INDEX_URL)
# FIXME(lsmola) optimize, this should call 1 time, what the hell
self.assertEqual(mock.list.call_count, 7)
self.assertEqual(mock.list.call_count, 5)
self.assertTemplateUsed(
res, 'infrastructure/nodes/index.html')
@ -76,8 +75,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'list.return_value': nodes,
}) as Node:
res = self.client.get(INDEX_URL + '?tab=nodes__' + tab_name)
# FIXME(lsmola) horrible count, optimize
self.assertEqual(Node.list.call_count, 7)
self.assertEqual(Node.list.call_count, 5)
self.assertTemplateUsed(
res, 'infrastructure/nodes/index.html')
@ -104,7 +102,7 @@ class NodesTests(test.BaseAdminViewTests, helpers.APITestCase):
'list.side_effect': self._raise_tuskar_exception,
}) as mock:
res = self.client.get(INDEX_URL + '?tab=nodes__' + tab_name)
self.assertEqual(mock.list.call_count, 4)
self.assertEqual(mock.list.call_count, 2)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -137,6 +137,7 @@ def data(TEST):
'provision_state': 'active',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'active',
'extra': {}
})
node_2 = node.Node(
@ -162,6 +163,7 @@ def data(TEST):
'provision_state': 'active',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'active',
'extra': {}
})
node_3 = node.Node(
@ -187,6 +189,7 @@ def data(TEST):
'provision_state': 'active',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'deploying',
'extra': {}
})
node_4 = node.Node(
@ -212,6 +215,7 @@ def data(TEST):
'provision_state': 'active',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'deploying',
'extra': {}
})
node_5 = node.Node(
@ -237,6 +241,7 @@ def data(TEST):
'provision_state': 'error',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'deploying',
'extra': {}
})
node_6 = node.Node(
@ -262,6 +267,7 @@ def data(TEST):
'provision_state': 'active',
'maintenance': None,
'newly_discovered': None,
'provision_state': 'active',
'extra': {}
})
node_7 = node.Node(
@ -286,6 +292,7 @@ def data(TEST):
'target_power_state': 'on',
'maintenance': True,
'newly_discovered': None,
'provision_state': 'deploying',
'extra': {}
})
node_8 = node.Node(
@ -310,6 +317,7 @@ def data(TEST):
'target_power_state': 'on',
'maintenance': True,
'newly_discovered': True,
'provision_state': 'active',
'extra': {}
})
node_9 = node.Node(
@ -334,6 +342,7 @@ def data(TEST):
'target_power_state': 'on',
'maintenance': True,
'newly_discovered': True,
'provision_state': 'active',
'extra': {}
})
TEST.ironicclient_nodes.add(node_1, node_2, node_3, node_4, node_5, node_6,