Merge "Improve log browsing for deployments."

This commit is contained in:
Jenkins 2017-06-05 08:24:28 +00:00 committed by Gerrit Code Review
commit 4690b921e7
11 changed files with 260 additions and 7 deletions

View File

@ -389,6 +389,20 @@ def deployments_list(request, environment_id):
return deployments
def deployment_history(request):
LOG.debug('Deployment::History')
deployment_history = api.muranoclient(request).deployments.list(
None, all_environments=True)
for deployment in deployment_history:
reports = deployment_reports(request, deployment.environment_id,
deployment.id)
deployment.reports = reports
LOG.debug('Deployment::History {0}'.format(deployment_history))
return deployment_history
def deployment_reports(request, environment_id, deployment_id):
LOG.debug('Deployment::Reports::List')
reports = api.muranoclient(request).deployments.reports(environment_id,

View File

@ -17,6 +17,7 @@ import json
from django.core.urlresolvers import reverse
from django import http as django_http
from django import shortcuts
from django import template
from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy
@ -108,6 +109,19 @@ class CreateEnvironment(tables.LinkAction):
exceptions.handle(request, msg, redirect=redirect)
class DeploymentHistory(tables.LinkAction):
name = 'DeploymentHistory'
verbose_name = _('Deployment History')
url = 'horizon:app-catalog:environments:deployment_history'
classes = ('deployment-history')
redirect_url = "horizon:app-catalog:environments:index"
icon = 'history'
policy_rules = (("murano", "list_deployments_all_environments"),)
def allowed(self, request, datum):
return True
class DeleteEnvironment(policy.PolicyTargetMixin, tables.DeleteAction):
redirect_url = "horizon:app-catalog:environments:index"
policy_rules = (("murano", "delete_environment"),)
@ -155,6 +169,10 @@ class AbandonEnvironment(tables.DeleteAction):
redirect_url = "horizon:app-catalog:environments:index"
policy_rules = (("murano", "delete_environment"),)
def __init__(self, **kwargs):
super(AbandonEnvironment, self).__init__(**kwargs)
self.icon = 'stop'
@staticmethod
def action_present(count):
return ungettext_lazy(
@ -504,7 +522,8 @@ class EnvironmentsTable(tables.DataTable):
status_columns = ['status']
no_data_message = _('NO ENVIRONMENTS')
table_actions = (CreateEnvironment, DeployEnvironment,
DeleteEnvironment, AbandonEnvironment)
DeleteEnvironment, AbandonEnvironment,
DeploymentHistory)
row_actions = (ShowEnvironmentServices, DeployEnvironment,
DeleteEnvironment, AbandonEnvironment,
UpdateEnvMetadata)
@ -692,3 +711,45 @@ class EnvConfigTable(tables.DataTable):
class Meta(object):
name = 'environment_configuration'
verbose_name = _('Deployed Components')
def get_deployment_history_reports(deployment):
template_name = 'deployments/_cell_reports.html'
context = {
"reports": deployment.reports,
}
return template.loader.render_to_string(template_name, context)
def get_deployment_history_services(deployment):
template_name = 'deployments/_cell_services.html'
services = {}
for service in deployment.description['services']:
service_type = service['?']['type']
if service_type.find('/') != -1:
service_type = service_type[:service_type.find('/')]
services[service['name']] = service_type
context = {
"services": services,
}
return template.loader.render_to_string(template_name, context)
class DeploymentHistoryTable(tables.DataTable):
environment_name = tables.WrappingColumn(
lambda d: d.description['name'],
verbose_name=_('Environment'))
logs = tables.Column(get_deployment_history_reports,
verbose_name=_('Logs (Created, Message)'))
services = tables.Column(get_deployment_history_services,
verbose_name=_('Services (Name, Type)'))
status = tables.Column(
'state',
verbose_name=_('Status'),
status=True,
display_choices=consts.DEPLOYMENT_STATUS_DISPLAY_CHOICES)
class Meta(object):
name = 'deployment_history'
verbose_name = _('Deployment History')
row_actions = (ShowDeploymentDetails,)

View File

@ -37,6 +37,8 @@ urlpatterns = [
views.ActionResultView.as_view(), name='action_result'),
urls.url(r'^(?P<instance_id>[^/]+)/$',
inst_view.DetailView.as_view(), name='detail'),
urls.url(r'^deployment_history$', views.DeploymentHistoryView.as_view(),
name='deployment_history'),
urls.url(ENVIRONMENT_ID + r'/deployments/(?P<deployment_id>[^/]+)$',
views.DeploymentDetailsView.as_view(), name='deployment_details'),
]

View File

@ -190,6 +190,26 @@ class CreateEnvironmentView(views.ModalFormView):
return reverse_lazy('horizon:app-catalog:environments:index')
class DeploymentHistoryView(tables.DataTableView):
table_class = env_tables.DeploymentHistoryTable
template_name = 'environments/index.html'
page_title = _("Deployment History")
def get_data(self):
deployment_history = []
try:
deployment_history = api.deployment_history(self.request)
except exc.HTTPUnauthorized:
exceptions.handle(self.request)
except exc.HTTPForbidden:
redirect = reverse('horizon:app-catalog:environments:services',
args=[self.environment_id])
exceptions.handle(self.request,
_('Unable to retrieve deployment history.'),
redirect=redirect)
return deployment_history
class DeploymentDetailsView(tabs.TabbedTableView):
tab_group_class = env_tabs.DeploymentDetailsTabs
table_class = env_tables.EnvConfigTable

View File

@ -0,0 +1,8 @@
table.deployment_history_cell {
margin: 0 auto;
}
table.deployment_history_cell td, table.deployment_history_cell th {
border-top: none !important;
padding-bottom: 2px !important;
}

View File

@ -0,0 +1,22 @@
{% load i18n %}
{% load static %}
{% load compress %}
{% block css %}
{% compress css %}
<link rel="stylesheet" href="{% static 'muranodashboard/css/deployments.css' %}">
{% endcompress %}
{% endblock %}
{% block main %}
<table class="{% block table_css_classes %}table deployment_history_cell{% endblock %}">
<thead></thead>
<tbody>
{% for report in reports %}
<tr>
<td>{{report.created | parse_isotime}} &mdash; {{report.text | linebreaksbr | urlize}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% load i18n %}
{% load static %}
{% load compress %}
{% block css %}
{% compress css %}
<link rel="stylesheet" href="{% static 'muranodashboard/css/deployments.css' %}">
{% endcompress %}
{% endblock %}
{% block main %}
<table class="{% block table_css_classes %}table deployment_history_cell{% endblock %}">
<thead></thead>
<tbody>
{% for name, type in services.items %}
<tr>
<td>{{name}} &mdash; {{type | linebreaksbr}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -1,11 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Environment Deployments" %}{% endblock %}
{% block page_header %}
{% include "deployments/_page_header.html" %}
{% endblock page_header %}
{% block title %}{% trans "Environment Deployment History" %}{% endblock %}
{% block main %}
{{ table.render }}
{% endblock %}
{% endblock %}

View File

@ -17,6 +17,8 @@ from django import http as django_http
import mock
import testtools
from horizon import tables as hz_tables
from muranoclient.common import exceptions as exc
from muranodashboard.environments import consts
from muranodashboard.environments import tables
@ -830,3 +832,49 @@ class TestEnvConfigTable(testtools.TestCase):
env_config_table = tables.EnvConfigTable(None)
self.assertEqual('foo', env_config_table.get_object_id({
'?': {'id': 'foo'}}))
class TestDeploymentHistoryTable(testtools.TestCase):
def setUp(self):
super(TestDeploymentHistoryTable, self).setUp()
deployment_history_table = tables.DeploymentHistoryTable(
mock.Mock())
columns = deployment_history_table.columns
self.assertIsInstance(columns['environment_name'],
hz_tables.WrappingColumn)
self.assertIsInstance(columns['logs'], hz_tables.Column)
self.assertIsInstance(columns['services'], hz_tables.Column)
self.assertIsInstance(columns['status'], hz_tables.Column)
self.assertEqual('Environment', str(columns['environment_name']))
self.assertEqual('Logs (Created, Message)', str(columns['logs']))
self.assertEqual('Services (Name, Type)', str(columns['services']))
self.assertEqual('Status', str(columns['status']))
self.assertTrue(columns['status'].status)
self.assertEqual(consts.DEPLOYMENT_STATUS_DISPLAY_CHOICES,
columns['status'].display_choices)
@mock.patch.object(tables, 'template')
def test_get_deployment_history_services(self, mock_template):
mock_template.loader.render_to_string.return_value = \
mock.sentinel.rendered_template
test_description = {'services': [
{'name': 'foo_service', '?': {'type': 'foo/bar'}},
{'name': 'bar_service', '?': {'type': 'baz/qux'}}
]}
mock_deployment = mock.Mock(description=test_description)
result = tables.get_deployment_history_services(mock_deployment)
self.assertEqual(mock.sentinel.rendered_template, result)
expected_services = {
'services': {
'bar_service': 'baz', 'foo_service': 'foo'
}
}
mock_template.loader.render_to_string.assert_called_once_with(
'deployments/_cell_services.html', expected_services)

View File

@ -566,3 +566,56 @@ class TestActionResultView(testtools.TestCase):
mock_api_utils.muranoclient().actions.get_result.\
assert_called_once_with('foo_env_id', 'foo_task_id')
class TestDeploymentHistoryView(testtools.TestCase):
def setUp(self):
super(TestDeploymentHistoryView, self).setUp()
self.deployment_history_view = views.DeploymentHistoryView()
self.mock_request = mock.Mock()
self.deployment_history_view.request = self.mock_request
self.deployment_history_view.environment_id = mock.sentinel.env_id
self.assertEqual(env_tables.DeploymentHistoryTable,
self.deployment_history_view.table_class)
self.assertEqual('environments/index.html',
self.deployment_history_view.template_name)
self.assertEqual(_('Deployment History'),
self.deployment_history_view.page_title)
@mock.patch.object(views, 'api', autospec=True)
def test_get_data(self, mock_env_api):
mock_env_api.deployment_history.return_value = \
[mock.sentinel.deployment_history]
result = self.deployment_history_view.get_data()
self.assertEqual([mock.sentinel.deployment_history], result)
@mock.patch.object(views, 'exceptions', autospec=True)
@mock.patch.object(views, 'api', autospec=True)
def test_get_data_except_http_unauthorized(self, mock_env_api,
mock_exceptions):
mock_env_api.deployment_history.side_effect = \
exc.HTTPUnauthorized
self.assertEqual([], self.deployment_history_view.get_data())
mock_exceptions.handle.assert_called_once_with(self.mock_request)
@mock.patch.object(views, 'exceptions', autospec=True)
@mock.patch.object(views, 'reverse', autospec=True)
@mock.patch.object(views, 'api', autospec=True)
def test_get_data_except_http_forbidden(self, mock_env_api, mock_reverse,
mock_exceptions):
mock_env_api.deployment_history.side_effect = \
exc.HTTPForbidden
mock_reverse.return_value = mock.sentinel.redirect_url
self.assertEqual([], self.deployment_history_view.get_data())
mock_reverse.assert_called_once_with(
'horizon:app-catalog:environments:services',
args=[mock.sentinel.env_id])
mock_exceptions.handle.assert_called_once_with(
self.mock_request, _('Unable to retrieve deployment history.'),
redirect=mock.sentinel.redirect_url)

View File

@ -0,0 +1,7 @@
---
features:
- |
A new button, called "Deployment History", has been added to the
Environments > Applications view. When clicked, the deployment history view
is loaded, which shows deployments for all environments for the current
project (tenant).