Add Role Panel

Moved OvercloudRoleView from overcloud into DetailView in roles

Change-Id: I53f6d4873df728487d6567e0092c66761c84d864
This commit is contained in:
Tzu-Mainn Chen 2014-08-05 15:49:15 +02:00
parent ab767a61b0
commit 907210928c
15 changed files with 286 additions and 75 deletions

View File

@ -23,6 +23,7 @@ class BasePanels(horizon.PanelGroup):
'overcloud',
'plans',
'parameters',
'roles',
'nodes',
'flavors',
'images',

View File

@ -54,9 +54,8 @@ def get_role_link(datum):
# TODO(tzumainn): this could probably be done more efficiently
# by getting the resource for all nodes at once
if datum.role_id:
return reverse('horizon:infrastructure:overcloud:role',
kwargs={'stack_id': datum.stack_id,
'role_id': datum.role_id})
return reverse('horizon:infrastructure:roles:detail',
kwargs={'role_id': datum.role_id})
class RegisteredNodesTable(tables.DataTable):

View File

@ -19,7 +19,7 @@
<dl class="clearfix">
<dt>{% trans "Deployment Role" %}</dt>
{% if stack and role %}
<dd><a href="{% url 'horizon:infrastructure:overcloud:role' stack.id role.id %}">{{ role.name }}</a></dd>
<dd><a href="{% url 'horizon:infrastructure:roles:detail' role.id %}">{{ role.name }}</a></dd>
{% else %}
<dd>&mdash;</dd>
{% endif %}

View File

@ -16,17 +16,6 @@ from django.utils.translation import ugettext_lazy as _
from horizon import tables
from tuskar_ui.infrastructure.nodes import tables as nodes_tables
class OvercloudRoleNodeTable(nodes_tables.RegisteredNodesTable):
class Meta:
name = "overcloud_role__nodetable"
verbose_name = _("Nodes")
table_actions = ()
row_actions = ()
class ConfigurationTable(tables.DataTable):

View File

@ -191,7 +191,7 @@ nova flavor-create m1.tiny 1 512 2 1
{% for role in roles %}
<tr>
<td><a
href="{% url 'horizon:infrastructure:overcloud:role' stack.id role.role.id %}"
href="{% url 'horizon:infrastructure:roles:detail' role.role.id %}"
>{{ role.name }} <span class="badge">({{ role.total_node_count }})</span></a>
</td>
<td>

View File

@ -25,8 +25,6 @@ urlpatterns = urls.patterns(
name='undeploy_in_progress'),
urls.url(r'^(?P<stack_id>[^/]+)/$', views.DetailView.as_view(),
name='detail'),
urls.url(r'^(?P<stack_id>[^/]+)/role/(?P<role_id>[^/]+)$',
views.OvercloudRoleView.as_view(), name='role'),
urls.url(r'^(?P<stack_id>[^/]+)/undeploy-confirmation$',
views.UndeployConfirmationView.as_view(),
name='undeploy_confirmation'),

View File

@ -18,13 +18,11 @@ from django.views.generic import base as base_views
from horizon import exceptions as horizon_exceptions
import horizon.forms
from horizon import messages
from horizon import tables as horizon_tables
from horizon import tabs as horizon_tabs
from horizon.utils import memoized
from tuskar_ui import api
from tuskar_ui.infrastructure.overcloud import forms
from tuskar_ui.infrastructure.overcloud import tables
from tuskar_ui.infrastructure.overcloud import tabs
@ -46,15 +44,6 @@ class StackMixin(object):
return stack
class OvercloudRoleMixin(object):
@memoized.memoized
def get_role(self, redirect=None):
role_id = self.kwargs['role_id']
role = api.tuskar.OvercloudRole.get(self.request, role_id,
_error_redirect=redirect)
return role
class IndexView(base_views.RedirectView):
permanent = False
@ -153,49 +142,3 @@ class UndeployInProgressView(horizon_tabs.TabView, StackMixin, ):
self).get_context_data(**kwargs)
context['stack'] = self.get_stack_or_redirect()
return context
class OvercloudRoleView(horizon_tables.DataTableView,
OvercloudRoleMixin, StackMixin):
table_class = tables.OvercloudRoleNodeTable
template_name = 'infrastructure/overcloud/overcloud_role.html'
@memoized.memoized
def _get_nodes(self, stack, role):
resources = stack.resources_by_role(role, with_joins=True)
nodes = [r.node for r in resources]
for node in nodes:
# TODO(tzumainn): this could probably be done more efficiently
# by getting the resource for all nodes at once
try:
resource = api.heat.Resource.get_by_node(self.request, node)
node.role_name = resource.role.name
node.role_id = resource.role.id
node.stack_id = resource.stack.id
except horizon_exceptions.NotFound:
node.role_name = '-'
return nodes
def get_data(self):
stack = self.get_stack()
redirect = reverse(DETAIL_URL,
args=(stack.id,))
role = self.get_role(redirect)
return self._get_nodes(stack, role)
def get_context_data(self, **kwargs):
context = super(OvercloudRoleView, self).get_context_data(**kwargs)
stack = self.get_stack()
redirect = reverse(DETAIL_URL,
args=(stack.id,))
role = self.get_role(redirect)
context['role'] = role
# TODO(tzumainn) we need to do this from plan parameters
context['image_name'] = 'FIXME'
context['nodes'] = self._get_nodes(stack, role)
context['flavor'] = None
return context

View File

@ -0,0 +1,27 @@
# -*- 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 _
import horizon
from tuskar_ui.infrastructure import dashboard
class Roles(horizon.Panel):
name = _("Deployment Roles")
slug = "roles"
dashboard.Infrastructure.register(Roles)

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.utils.translation import ugettext_lazy as _
from horizon import tables
from tuskar_ui.infrastructure.nodes import tables as nodes_tables
class RolesTable(tables.DataTable):
name = tables.Column('name',
link="horizon:infrastructure:roles:detail",
verbose_name=_("Image Name"))
class Meta:
name = "roles"
verbose_name = _("Deployment Roles")
table_actions = ()
row_actions = ()
class NodeTable(nodes_tables.RegisteredNodesTable):
class Meta:
name = "nodetable"
verbose_name = _("Nodes")
table_actions = ()
row_actions = ()

View File

@ -0,0 +1,17 @@
{% extends 'infrastructure/base.html' %}
{% load i18n %}
{% block title %}{% trans 'Deployment Roles' %}{% endblock %}
{% block page_header %}
{% include 'horizon/common/_page_header.html' with title=_('Deployment Roles') %}
{% endblock page_header %}
{% block main %}
<div class="row-fluid">
<div class="span12">
<h4>{% trans "Deployment Roles" %}</h4>
{{ table.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,69 @@
# -*- 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.
import contextlib
from django.core import urlresolvers
from mock import patch, call # noqa
from openstack_dashboard.test.test_data import utils
from tuskar_ui import api
from tuskar_ui.test import helpers as test
from tuskar_ui.test.test_data import heat_data
from tuskar_ui.test.test_data import node_data
from tuskar_ui.test.test_data import tuskar_data
INDEX_URL = urlresolvers.reverse(
'horizon:infrastructure:roles:index')
DETAIL_URL = urlresolvers.reverse(
'horizon:infrastructure:roles:detail', args=('role-1',))
TEST_DATA = utils.TestDataContainer()
node_data.data(TEST_DATA)
heat_data.data(TEST_DATA)
tuskar_data.data(TEST_DATA)
class RolesTest(test.BaseAdminViewTests):
def test_index_get(self):
roles = [api.tuskar.OvercloudRole(role)
for role in TEST_DATA.tuskarclient_roles.list()]
with patch('tuskar_ui.api.tuskar.OvercloudRole.list',
return_value=roles):
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'infrastructure/roles/index.html')
def test_detail_get(self):
roles = [api.tuskar.OvercloudRole(role)
for role in TEST_DATA.tuskarclient_roles.list()]
with contextlib.nested(
patch('tuskar_ui.api.tuskar.OvercloudRole', **{
'spec_set': ['list', 'get'],
'list.return_value': roles,
'get.return_value': roles[0],
}),
patch('tuskar_ui.api.heat.Stack.events',
return_value=[]),
):
res = self.client.get(DETAIL_URL)
self.assertTemplateUsed(
res, 'infrastructure/roles/detail.html')

View File

@ -0,0 +1,25 @@
# -*- 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.conf import urls
from tuskar_ui.infrastructure.roles import views
urlpatterns = urls.patterns(
'',
urls.url(r'^$', views.IndexView.as_view(), name='index'),
urls.url(r'^(?P<role_id>[^/]+)/$', views.DetailView.as_view(),
name='detail'),
)

View File

@ -0,0 +1,102 @@
# -*- 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.urlresolvers import reverse
from horizon import exceptions as horizon_exceptions
from horizon import tables as horizon_tables
from horizon.utils import memoized
from tuskar_ui import api
from tuskar_ui.infrastructure.roles import tables
INDEX_URL = 'horizon:infrastructure:roles:index'
class OvercloudRoleMixin(object):
@memoized.memoized
def get_role(self, redirect=None):
role_id = self.kwargs['role_id']
role = api.tuskar.OvercloudRole.get(self.request, role_id,
_error_redirect=redirect)
return role
class StackMixin(object):
@memoized.memoized
def get_stack(self, redirect=None):
if redirect is None:
redirect = reverse(INDEX_URL)
plan = api.tuskar.OvercloudPlan.get_the_plan(self.request)
stack = api.heat.Stack.get_by_plan(self.request, plan)
return stack
class IndexView(horizon_tables.DataTableView):
table_class = tables.RolesTable
template_name = "infrastructure/roles/index.html"
def get_data(self):
return api.tuskar.OvercloudRole.list(self.request)
class DetailView(horizon_tables.DataTableView, OvercloudRoleMixin, StackMixin):
table_class = tables.NodeTable
template_name = 'infrastructure/roles/detail.html'
@memoized.memoized
def _get_nodes(self, stack, role):
resources = stack.resources_by_role(role, with_joins=True)
nodes = [r.node for r in resources]
for node in nodes:
# TODO(tzumainn): this could probably be done more efficiently
# by getting the resource for all nodes at once
try:
resource = api.heat.Resource.get_by_node(self.request, node)
node.role_name = resource.role.name
node.role_id = resource.role.id
node.stack_id = resource.stack.id
except horizon_exceptions.NotFound:
node.role_name = '-'
return nodes
def get_data(self):
redirect = reverse(INDEX_URL)
stack = self.get_stack(redirect)
if stack:
role = self.get_role(redirect)
return self._get_nodes(stack, role)
return []
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
redirect = reverse(INDEX_URL)
stack = self.get_stack(redirect)
role = self.get_role(redirect)
context['role'] = role
# TODO(tzumainn) we need to do this from plan parameters
context['image_name'] = 'FIXME'
if stack:
context['nodes'] = self._get_nodes(stack, role)
else:
context['nodes'] = []
context['flavor'] = None
return context