Use baremetal node id for node detail page

Currently, the node detail page is keyed off the
tuskar node id.  However, a tuskar node merely maps
a rack to a baremetal node, and does not persist if
the rack is deleted (while the baremetal node does).
This means that the detail page for an unracked node
will error out.

This patch updates the node detail page (and relevant
links) to use the baremetal node id.

Change-Id: I69a32226b3848b45b1a58417ea4e209c28c3e43a
Closes-Bug: #1252813
This commit is contained in:
Tzu-Mainn Chen 2013-11-19 16:22:11 -05:00
parent f4f51b2421
commit 1219999177
10 changed files with 82 additions and 64 deletions

View File

@ -233,6 +233,14 @@ class BaremetalNode(StringIdAPIResourceWrapper):
except requests.ConnectionError:
return []
@cached_property
def tuskar_node(self):
node = next((tuskar_node
for tuskar_node in TuskarNode.list(self.request)
if tuskar_node.nova_baremetal_node_id == self.id),
None)
return node
@property
def mac_address(self):
try:

View File

@ -29,8 +29,8 @@ class DeleteNodes(tables.DeleteAction):
def delete(self, request, obj_id):
try:
tuskar_node = tuskar.TuskarNode.get(request, obj_id)
tuskar_node.remove_from_rack(request)
baremetal_node = tuskar.BaremetalNode.get(request, obj_id)
baremetal_node.tuskar_node.remove_from_rack(request)
except Exception:
exceptions.handle(request, _("Error deleting node."))
return False

View File

@ -16,6 +16,7 @@ from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import messages
from horizon import tabs
import novaclient
import requests
@ -27,15 +28,23 @@ class OverviewTab(tabs.Tab):
preload = False
def get_context_data(self, request):
tuskar_node = self.tab_group.kwargs['tuskar_node']
try:
running_instances = len(tuskar_node.running_virtual_machines)
except requests.exceptions.ConnectionError:
running_instances = _("Unknown")
messages.warning(
request,
_("Can't retrieve the running instances from the overcloud."))
baremetal_node = self.tab_group.kwargs['baremetal_node']
tuskar_node = baremetal_node.tuskar_node
if tuskar_node:
try:
running_instances = len(tuskar_node.running_virtual_machines)
except (requests.exceptions.ConnectionError,
novaclient.exceptions.Unauthorized):
running_instances = _("Unknown")
messages.warning(
request,
_("Can't retrieve the running instances"
"from the overcloud."))
else:
running_instances = _("None")
return {
'baremetal_node': baremetal_node,
'tuskar_node': tuskar_node,
'running_instances': running_instances,
}

View File

@ -42,22 +42,22 @@ class NodeViewTests(test.BaseAdminViewTests):
self.assertItemsEqual(unracked_nodes_table, unracked_baremetal_nodes)
@test.create_stubs({
tuskar.TuskarNode: ('get', 'running_virtual_machines', 'list_flavors'),
tuskar.Rack: ('get',),
tuskar.BaremetalNode: ('get',),
tuskar.TuskarNode: ('list', 'running_virtual_machines',
'list_flavors'),
tuskar.Rack: ('get',),
})
def test_detail_node(self):
tuskar_node = self.tuskar_nodes.first()
tuskar_node.request = self.request
rack = self.tuskar_racks.first()
baremetal_node = self.baremetal_nodes.first()
tuskar_nodes = self.tuskar_nodes.list()
rack = self.tuskar_racks.first()
tuskar.TuskarNode.get(mox.IsA(http.HttpRequest),
tuskar_node.id).AndReturn(tuskar_node)
tuskar.Rack.get(mox.IsA(http.HttpRequest),
rack.id).AndReturn(rack)
tuskar.BaremetalNode.get(mox.IsA(http.HttpRequest),
baremetal_node.id).AndReturn(baremetal_node)
tuskar.TuskarNode.list(None).AndReturn(tuskar_nodes)
tuskar.Rack.get(None, rack.id).AndReturn(rack)
tuskar.BaremetalNode.get(None,
baremetal_node.id).AndReturn(baremetal_node)
self.mox.ReplayAll()
tuskar.TuskarNode.running_virtual_machines = []
@ -65,23 +65,24 @@ class NodeViewTests(test.BaseAdminViewTests):
url = urlresolvers.reverse(
'horizon:infrastructure:resource_management:nodes:detail',
args=[tuskar_node.id])
args=[baremetal_node.id])
res = self.client.get(url)
self.assertTemplateUsed(
res, 'infrastructure/resource_management/nodes/detail.html')
@test.create_stubs({tuskar.TuskarNode: ('get',)})
@test.create_stubs({tuskar.BaremetalNode: ('get',)})
def test_detail_node_exception(self):
tuskar_node = self.tuskar_nodes.first()
baremetal_node = self.baremetal_nodes.first()
tuskar.TuskarNode.get(mox.IsA(http.HttpRequest),
tuskar_node.id).AndRaise(self.exceptions.tuskar)
tuskar.BaremetalNode.get(
mox.IsA(http.HttpRequest),
baremetal_node.id).AndRaise(self.exceptions.tuskar)
self.mox.ReplayAll()
url = urlresolvers.reverse(
'horizon:infrastructure:resource_management:nodes:detail',
args=[tuskar_node.id])
args=[baremetal_node.id])
res = self.client.get(url)
self.assertRedirectsNoFollow(

View File

@ -44,25 +44,27 @@ class DetailView(horizon_tabs.TabView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["tuskar_node"] = self.get_data()
context["baremetal_node"] = self.get_data()
context["tuskar_node"] = self.get_data().tuskar_node
return context
def get_data(self):
if not hasattr(self, "_tuskar_node"):
tuskar_node_id = self.kwargs['node_id']
if not hasattr(self, "_baremetal_node"):
baremetal_node_id = self.kwargs['node_id']
try:
tuskar_node = tuskar.TuskarNode.get(self.request,
tuskar_node_id)
baremetal_node = tuskar.BaremetalNode.get(self.request,
baremetal_node_id)
except Exception:
redirect = urlresolvers.reverse(
'horizon:infrastructure:resource_management:index')
exceptions.handle(self.request,
_('Unable to retrieve details for '
'node "%s".') % tuskar_node_id,
'node "%s".') % baremetal_node_id,
redirect=redirect)
self._tuskar_node = tuskar_node
return self._tuskar_node
self._baremetal_node = baremetal_node
return self._baremetal_node
def get_tabs(self, request, *args, **kwargs):
tuskar_node = self.get_data()
return self.tab_group_class(request, tuskar_node=tuskar_node, **kwargs)
baremetal_node = self.get_data()
return self.tab_group_class(
request, baremetal_node=baremetal_node, **kwargs)

View File

@ -39,12 +39,12 @@ class NodesTab(tabs.TableTab):
def get_nodes_table_data(self):
try:
rack = self.tab_group.kwargs['rack']
tuskar_nodes = rack.list_tuskar_nodes
baremetal_nodes = rack.list_baremetal_nodes
except Exception:
tuskar_nodes = []
baremetal_nodes = []
exceptions.handle(self.tab_group.request,
_('Unable to retrieve node list.'))
return tuskar_nodes
return baremetal_nodes
class RackDetailTabs(tabs.TabGroup):

View File

@ -372,41 +372,39 @@ class RackViewTests(test.BaseAdminViewTests):
self.assertEquals(res.content, state_json)
@test.create_stubs({
tuskar.Rack: ('get', 'list_baremetal_nodes', 'list_flavors', 'update',
'tuskar_node_ids', 'list_tuskar_nodes'),
tuskar.TuskarNode: ('get',),
tuskar.Rack: ('get', 'list_baremetal_nodes', 'update',
'list_tuskar_nodes'),
tuskar.BaremetalNode: ('get',),
tuskar.TuskarNode: ('list',),
})
def test_node_delete(self):
rack = self.tuskar_racks.first()
rack.request = self.request
baremetal_nodes = self.baremetal_nodes.list()
baremetal_node = baremetal_nodes[0]
baremetal_node.request = self.request
tuskar_nodes = self.tuskar_nodes.list()
tuskar_node = tuskar_nodes[0]
tuskar.Rack.get(mox.IsA(http.HttpRequest), rack.id).AndReturn(rack)
tuskar.BaremetalNode.get(mox.IsA(http.HttpRequest),
baremetal_node.id).AndReturn(baremetal_node)
tuskar.TuskarNode.list(
mox.IsA(http.HttpRequest)).AndReturn(tuskar_nodes)
tuskar.Rack.get(None, rack.id).AndReturn(rack) # called by node.rack
tuskar.Rack.update(mox.IsA(http.HttpRequest), rack.id, {
'baremetal_nodes': [{'id': node.id}
for node in baremetal_nodes
if node.id != baremetal_node.id],
}).AndReturn(rack)
tuskar.Rack.list_baremetal_nodes = baremetal_nodes
tuskar.Rack.list_tuskar_nodes = tuskar_nodes
tuskar.Rack.tuskar_node_ids = [t_node.id for t_node in tuskar_nodes]
tuskar.Rack.list_flavors = []
tuskar.Rack.get(mox.IsA(http.HttpRequest), rack.id).AndReturn(rack)
tuskar.TuskarNode.get(mox.IsA(http.HttpRequest),
tuskar_node.id).AndReturn(tuskar_node)
tuskar.BaremetalNode.get(None,
baremetal_node.id).AndReturn(baremetal_node)
tuskar.Rack.get(None, rack.id).AndReturn(rack) # called by node.rack
tuskar.Rack.update(mox.IsA(http.HttpRequest), rack.id, {
'baremetal_nodes': [{'id': bm_node.id}
for bm_node in baremetal_nodes
if bm_node.id != baremetal_node.id],
}).AndReturn(rack)
self.mox.ReplayAll()
url = urlresolvers.reverse(
'horizon:infrastructure:resource_management:racks:detail',
args=[rack.id])
form_data = {'action': 'nodes_table__delete__%s' % tuskar_node.id}
form_data = {'action': 'nodes_table__delete__%s' % baremetal_node.id}
response = self.client.post(url, form_data)
self.assertNoFormErrors(response)
self.assertMessageCount(success=1)

View File

@ -8,15 +8,15 @@
<hr class="header_rule">
<dl>
<dt>{% trans "MAC Address" %}</dt>
<dd>{{ tuskar_node.mac_address|default:_("None") }}</dd>
<dd>{{ baremetal_node.mac_address|default:_("None") }}</dd>
<dt>{% trans "IPs" %}</dt>
<dd>{{ tuskar_node.ip_address_other|default:_("None") }}</dd>
<dd>{{ baremetal_node.ip_address_other|default:_("None") }}</dd>
<dt>{% trans "Management IP" %}</dt>
<dd>{{ tuskar_node.pm_address|default:_("None") }}</dd>
<dd>{{ baremetal_node.pm_address|default:_("None") }}</dd>
<dt>{% trans "Power Management" %}</dt>
<dd>{{ tuskar_node.rack.power_management|default:_("-") }}</dd>
<dt>{% trans "Status" %}</dt>
<dd>{{ tuskar_node.status|default:_("None") }}</dd>
<dd>{{ baremetal_node.status|default:_("None") }}</dd>
</dl>
</div>
<div class="span4">

View File

@ -13,7 +13,7 @@
<span class="separator"></span>
<a href="{% url 'horizon:infrastructure:resource_management:index' %}?tab=resource_management_tabs__racks_tab" >Racks</a>
<span class="separator"></span>
{% if tuskar_node.rack %}
{% if tuskar_node %}
<a href="{% url 'horizon:infrastructure:resource_management:racks:detail' tuskar_node.rack_id %}">{{ tuskar_node.rack.name }}</a>
{% else %}
<a href="{% url 'horizon:infrastructure:resource_management:nodes:unracked' %}" >Unracked Nodes</a>
@ -22,4 +22,4 @@
</div>
{% endblock breadcrumbs %}
{% block name %}{{ tuskar_node.name }}{% endblock %}
{% block name %}{{ baremetal_node.mac_address }}{% endblock %}

View File

@ -174,7 +174,7 @@
{% for tuskar_node in rack.aggregated_alerts %}
<li>
<i class="icon-warning-sign"></i>
Node <a href="{% url 'horizon:infrastructure:resource_management:nodes:detail' tuskar_node.id %}">{{ tuskar_node.name }}</a> has some problems
Node <a href="{% url 'horizon:infrastructure:resource_management:nodes:detail' tuskar_node.nova_baremetal_node_id %}">{{ tuskar_node.name }}</a> has some problems
</li>
{% endfor %}
</ul>