Add Role Panel
Moved OvercloudRoleView from overcloud into DetailView in roles Change-Id: I53f6d4873df728487d6567e0092c66761c84d864
This commit is contained in:
parent
ab767a61b0
commit
907210928c
@ -23,6 +23,7 @@ class BasePanels(horizon.PanelGroup):
|
||||
'overcloud',
|
||||
'plans',
|
||||
'parameters',
|
||||
'roles',
|
||||
'nodes',
|
||||
'flavors',
|
||||
'images',
|
||||
|
@ -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):
|
||||
|
@ -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>—</dd>
|
||||
{% endif %}
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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'),
|
||||
|
@ -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
|
||||
|
0
tuskar_ui/infrastructure/roles/__init__.py
Normal file
0
tuskar_ui/infrastructure/roles/__init__.py
Normal file
27
tuskar_ui/infrastructure/roles/panel.py
Normal file
27
tuskar_ui/infrastructure/roles/panel.py
Normal 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)
|
41
tuskar_ui/infrastructure/roles/tables.py
Normal file
41
tuskar_ui/infrastructure/roles/tables.py
Normal 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 = ()
|
17
tuskar_ui/infrastructure/roles/templates/roles/index.html
Normal file
17
tuskar_ui/infrastructure/roles/templates/roles/index.html
Normal 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 %}
|
69
tuskar_ui/infrastructure/roles/tests.py
Normal file
69
tuskar_ui/infrastructure/roles/tests.py
Normal 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')
|
25
tuskar_ui/infrastructure/roles/urls.py
Normal file
25
tuskar_ui/infrastructure/roles/urls.py
Normal 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'),
|
||||
)
|
102
tuskar_ui/infrastructure/roles/views.py
Normal file
102
tuskar_ui/infrastructure/roles/views.py
Normal 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
|
Loading…
x
Reference in New Issue
Block a user