List views for Nodes in Resources management

Management Nodes, Resource nodes, Free nodes
BaremetalNode.list() is currently used to get data for the lists. This
should be modified once filtering by node type is available

Change-Id: I8499469b2487486e488990658f4ed5a75ee6983b
This commit is contained in:
Jiri Tomasek 2013-12-16 13:22:10 +01:00
parent 50e8be7075
commit 446346dc62
24 changed files with 490 additions and 29 deletions

View File

@ -6,7 +6,7 @@ set -o errexit
# Increment me any time the environment should be rebuilt.
# This includes dependncy changes, directory renames, etc.
# Simple integer secuence: 1, 2, 3...
environment_version=40
environment_version=41
#--------------------------------------------------------#
function usage {

View File

@ -6,6 +6,7 @@ hacking>=0.5.3,<0.6
# Testing Requirements
coverage>=3.6
django-nose
mock>=1.0.1
mox>=0.5.3
nose
nose-exclude

View File

@ -44,8 +44,7 @@ class Resources(horizon.PanelGroup):
'resources.overview',
'resources.resource',
'resources.management',
'resources.unallocated',
'resources.archived',
'resources.free',
)

View File

@ -19,9 +19,9 @@ import horizon
from tuskar_ui.infrastructure import dashboard
class ResourcesArchived(horizon.Panel):
name = _("Archived")
slug = "resources.archived"
class ResourcesFree(horizon.Panel):
name = _("Free Nodes")
slug = "resources.free"
dashboard.Infrastructure.register(ResourcesArchived)
dashboard.Infrastructure.register(ResourcesFree)

View File

@ -14,14 +14,13 @@
from django.utils.translation import ugettext_lazy as _ # noqa
import horizon
from tuskar_ui.infrastructure import dashboard
from tuskar_ui.infrastructure.resources import tables
class ResourcesUnallocated(horizon.Panel):
name = _("Unallocated")
slug = "resources.unallocated"
class FreeNodesTable(tables.NodesTable):
dashboard.Infrastructure.register(ResourcesUnallocated)
class Meta:
name = "free_nodes"
verbose_name = _("Free Nodes")
table_actions = ()
row_actions = ()

View File

@ -0,0 +1,12 @@
{% extends 'infrastructure/base_nodes_table.html' %}
{% load i18n %}
{% block title %}{% trans "Free Nodes" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Free Nodes") %}
{% endblock page_header %}
{% block name %}
{% trans "Free Nodes" %}
{% endblock %}

View File

@ -0,0 +1,56 @@
# -*- 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 django.core import urlresolvers
from mock import patch # noqa
from tuskar_ui.test import helpers as test
INDEX_URL = urlresolvers.reverse('horizon:infrastructure:resources.free'
':index')
RESOURCES_OVERVIEW_URL = urlresolvers.reverse('horizon:infrastructure:'
'resources.overview:index')
class FreeNodesTests(test.BaseAdminViewTests):
def setUp(self):
super(FreeNodesTests, self).setUp()
def test_index(self):
free_nodes = self.baremetal_nodes.list()
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'], # Only allow these attributes
'list.return_value': free_nodes,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.maxDiff = None
self.assertTemplateUsed(res,
'infrastructure/resources.free/index.html')
self.assertItemsEqual(res.context['free_nodes_table'].data,
free_nodes)
def test_index_nodes_list_exception(self):
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'],
'list.side_effect': self.exceptions.tuskar,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.assertRedirectsNoFollow(res, RESOURCES_OVERVIEW_URL)

View File

@ -1,4 +1,4 @@
# -*- coding: utf8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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
@ -11,8 +11,13 @@
# 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 django.views import generic
from django.conf.urls import defaults
from tuskar_ui.infrastructure.resources.free import views
class IndexView(generic.TemplateView):
template_name = 'infrastructure/base.html'
urlpatterns = defaults.patterns(
'',
defaults.url(r'^$', views.IndexView.as_view(), name='index'),
)

View File

@ -0,0 +1,41 @@
# -*- 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 django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tables as horizon_tables
from tuskar_ui import api as tuskar
from tuskar_ui.infrastructure.resources.free import tables
class IndexView(horizon_tables.DataTableView):
table_class = tables.FreeNodesTable
template_name = 'infrastructure/resources.free/index.html'
def get_data(self):
try:
# TODO(Jiri Tomasek): needs update when filtering by node type is
# available
free_nodes = tuskar.BaremetalNode.list(self.request)
except Exception:
free_nodes = []
redirect = urlresolvers.reverse(
'horizon:infrastructure:resources.overview:index')
exceptions.handle(self.request,
_('Unable to retrieve free nodes.'),
redirect=redirect)
return free_nodes

View File

@ -0,0 +1,26 @@
# -*- 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 django.utils.translation import ugettext_lazy as _ # noqa
from tuskar_ui.infrastructure.resources import tables
class ManagementNodesTable(tables.NodesTable):
class Meta:
name = "management_nodes"
verbose_name = _("Management Nodes")
table_actions = ()
row_actions = ()

View File

@ -0,0 +1,12 @@
{% extends 'infrastructure/base_nodes_table.html' %}
{% load i18n %}
{% block title %}{% trans "Management Nodes" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Management Nodes") %}
{% endblock page_header %}
{% block name %}
{% trans "Management Nodes" %}
{% endblock %}

View File

@ -0,0 +1,57 @@
# -*- 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 django.core import urlresolvers
from mock import patch # noqa
from tuskar_ui.test import helpers as test
INDEX_URL = urlresolvers.reverse('horizon:infrastructure:resources.management'
':index')
RESOURCES_OVERVIEW_URL = urlresolvers.reverse('horizon:infrastructure:'
'resources.overview:index')
class ManagementNodesTests(test.BaseAdminViewTests):
def setUp(self):
super(ManagementNodesTests, self).setUp()
def test_index(self):
management_nodes = self.baremetal_nodes.list()
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'], # Only allow these attributes
'list.return_value': management_nodes,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.maxDiff = None
self.assertTemplateUsed(
res, 'infrastructure/resources.management/index.html')
self.assertItemsEqual(res.context['management_nodes_table'].data,
management_nodes)
def test_index_nodes_list_exception(self):
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'],
'list.side_effect': self.exceptions.tuskar,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.assertRedirectsNoFollow(res, RESOURCES_OVERVIEW_URL)

View File

@ -0,0 +1,23 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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 django.conf.urls import defaults
from tuskar_ui.infrastructure.resources.management import views
urlpatterns = defaults.patterns(
'',
defaults.url(r'^$', views.IndexView.as_view(), name='index'),
)

View File

@ -11,8 +11,31 @@
# 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 django.views import generic
from django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tables as horizon_tables
from tuskar_ui import api as tuskar
from tuskar_ui.infrastructure.resources.management import tables
class IndexView(generic.TemplateView):
template_name = 'infrastructure/base.html'
class IndexView(horizon_tables.DataTableView):
table_class = tables.ManagementNodesTable
template_name = 'infrastructure/resources.management/index.html'
def get_data(self):
try:
# TODO(Jiri Tomasek): needs update when filtering by node type is
# available
management_nodes = tuskar.BaremetalNode.list(self.request)
except Exception:
management_nodes = []
redirect = urlresolvers.reverse(
'horizon:infrastructure:resources.overview:index')
exceptions.handle(self.request,
_('Unable to retrieve management nodes.'),
redirect=redirect)
return management_nodes

View File

@ -1,4 +1,4 @@
# -*- coding: utf8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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
@ -11,8 +11,13 @@
# 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 django.views import generic
from django.conf.urls import defaults
from tuskar_ui.infrastructure.resources.overview import views
class IndexView(generic.TemplateView):
template_name = 'infrastructure/base.html'
urlpatterns = defaults.patterns(
'',
defaults.url(r'^$', views.IndexView.as_view(), name='index'),
)

View File

@ -0,0 +1,26 @@
# -*- 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 django.utils.translation import ugettext_lazy as _ # noqa
from tuskar_ui.infrastructure.resources import tables
class ResourceNodesTable(tables.NodesTable):
class Meta:
name = "resource_nodes"
verbose_name = _("Resource Nodes")
table_actions = ()
row_actions = ()

View File

@ -0,0 +1,12 @@
{% extends 'infrastructure/base_nodes_table.html' %}
{% load i18n %}
{% block title %}{% trans "Resource Nodes" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Resource Nodes") %}
{% endblock page_header %}
{% block name %}
{% trans "Resource Nodes" %}
{% endblock %}

View File

@ -0,0 +1,57 @@
# -*- 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 django.core import urlresolvers
from mock import patch # noqa
from tuskar_ui.test import helpers as test
INDEX_URL = urlresolvers.reverse('horizon:infrastructure:resources.resource'
':index')
RESOURCES_OVERVIEW_URL = urlresolvers.reverse('horizon:infrastructure:'
'resources.overview:index')
class ResourceNodesTests(test.BaseAdminViewTests):
def setUp(self):
super(ResourceNodesTests, self).setUp()
def test_index(self):
resource_nodes = self.baremetal_nodes.list()
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'], # Only allow these attributes
'list.return_value': resource_nodes,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.maxDiff = None
self.assertTemplateUsed(
res, 'infrastructure/resources.resource/index.html')
self.assertItemsEqual(res.context['resource_nodes_table'].data,
resource_nodes)
def test_index_nodes_list_exception(self):
with patch('tuskar_ui.api.BaremetalNode', **{
'spec_set': ['list'],
'list.side_effect': self.exceptions.tuskar,
}) as mock:
res = self.client.get(INDEX_URL)
self.assertEqual(mock.list.call_count, 1)
self.assertRedirectsNoFollow(res, RESOURCES_OVERVIEW_URL)

View File

@ -0,0 +1,23 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# 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 django.conf.urls import defaults
from tuskar_ui.infrastructure.resources.resource import views
urlpatterns = defaults.patterns(
'',
defaults.url(r'^$', views.IndexView.as_view(), name='index'),
)

View File

@ -11,8 +11,31 @@
# 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 django.views import generic
from django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _ # noqa
from horizon import exceptions
from horizon import tables as horizon_tables
from tuskar_ui import api as tuskar
from tuskar_ui.infrastructure.resources.resource import tables
class IndexView(generic.TemplateView):
template_name = 'infrastructure/base.html'
class IndexView(horizon_tables.DataTableView):
table_class = tables.ResourceNodesTable
template_name = 'infrastructure/resources.resource/index.html'
def get_data(self):
try:
# TODO(Jiri Tomasek): needs update when filtering by node type is
# available
resource_nodes = tuskar.BaremetalNode.list(self.request)
except Exception:
resource_nodes = []
redirect = urlresolvers.reverse(
'horizon:infrastructure:resources.overview:index')
exceptions.handle(self.request,
_('Unable to retrieve resource nodes.'),
redirect=redirect)
return resource_nodes

View File

@ -0,0 +1,38 @@
# -*- 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 django.utils.translation import ugettext_lazy as _ # noqa
from horizon import tables
class NodesTable(tables.DataTable):
service_host = tables.Column(
"service_host",
verbose_name=_("Service Host"),
link=("horizon:infrastructure:resources.management:detail"))
mac_address = tables.Column("mac_address", verbose_name=_("MAC Address"))
pm_address = tables.Column("pm_address",
verbose_name=_("Management Address"))
status = tables.Column("status", verbose_name=_("Status"))
usage = tables.Column("usage", verbose_name=_("Usage"))
class Meta:
name = "nodes_table"
verbose_name = _("Nodes")
table_actions = ()
row_actions = ()
def get_object_display(self, datum):
return datum.service_host

View File

@ -0,0 +1,23 @@
{% extends 'infrastructure/base.html' %}
{% load i18n %}
{% block main %}
<div class="row-fluid">
<div class="span12">
<div class="breadcrumbs">
<a href="{% url 'horizon:infrastructure:overview:index' %}">{% trans "Infrastructure" %}</a>
<span class="separator"></span>
<a href="{% url 'horizon:infrastructure:resources.overview:index' %}">{% trans "Resources" %}</a>
<span class="separator"></span>
</div>
<h3>{% block name %}{% endblock %}</h3>
</div>
</div>
<div class="row-fluid">
<div class="span12">
{{ table.render }}
</div>
</div>
{% endblock %}