Merge "Trove add support for log retrieval"

This commit is contained in:
Jenkins 2016-05-18 19:19:59 +00:00 committed by Gerrit Code Review
commit 1c05349cb0
14 changed files with 845 additions and 3 deletions

View File

@ -0,0 +1,5 @@
---
features:
- Added support for logging features in the dashboard.
This includes listing logs that can be retrieved and
viewing, publishing and stop collection of log contents.

View File

@ -297,3 +297,31 @@ def datastore_list(request):
def datastore_version_list(request, datastore):
return troveclient(request).datastore_versions.list(datastore)
def log_list(request, instance_id):
return troveclient(request).instances.log_list(instance_id)
def log_enable(request, instance_id, log_name):
return troveclient(request).instances.log_enable(instance_id, log_name)
def log_disable(request, instance_id, log_name):
return troveclient(request).instances.log_disable(instance_id, log_name)
def log_publish(request, instance_id, log_name):
return troveclient(request).instances.log_publish(instance_id, log_name)
def log_discard(request, instance_id, log_name):
return troveclient(request).instances.log_discard(instance_id, log_name)
def log_tail(request, instance_id, log_name, publish, lines, swift=None):
return troveclient(request).instances.log_generator(instance_id,
log_name,
publish=publish,
lines=lines,
swift=swift)

View File

@ -0,0 +1,156 @@
# Copyright 2016 Tesora Inc.
#
# 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 _
from django.utils.translation import ungettext_lazy
from horizon import tables
from trove_dashboard import api
class PublishLog(tables.BatchAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Publish Log",
u"Publish Logs",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Published Log",
u"Published Logs",
count
)
name = "publish_log"
def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id']
api.trove.log_publish(request, instance_id, obj_id)
class DiscardLog(tables.BatchAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Discard Log",
u"Discard Logs",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Discarded Log",
u"Discarded Logs",
count
)
name = "discard_log"
def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id']
api.trove.log_discard(request, instance_id, obj_id)
class EnableLog(tables.BatchAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Enable Log",
u"Enable Logs",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Enabled Log",
u"Enabled Logs",
count
)
name = "enable_log"
def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id']
api.trove.log_enable(request, instance_id, obj_id)
class DisableLog(tables.BatchAction):
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Disable Log",
u"Disable Logs",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Disabled Log",
u"Disabled Logs",
count
)
name = "disable_log"
def action(self, request, obj_id):
instance_id = self.table.kwargs['instance_id']
api.trove.log_disable(request, instance_id, obj_id)
def allowed(self, request, datum=None):
if datum:
return datum.type != "SYS"
return False
class ViewLog(tables.LinkAction):
name = "view_log"
verbose_name = _("View Log")
url = "horizon:project:databases:logs:log_contents"
def get_link_url(self, datum):
instance_id = self.table.kwargs['instance_id']
return urlresolvers.reverse(self.url,
args=(instance_id,
datum.name))
def allowed(self, request, datum=None):
if datum:
return datum.published > 0
return False
class LogsTable(tables.DataTable):
name = tables.Column('name', verbose_name=_('Name'))
type = tables.Column('type', verbose_name=_("Type"))
status = tables.Column('status', verbose_name=_("Status"))
published = tables.Column('published', verbose_name=_('Published (bytes)'))
pending = tables.Column('pending', verbose_name=_('Publishable (bytes)'))
container = tables.Column('container', verbose_name=_('Container'))
class Meta(object):
name = "logs"
verbose_name = _("Logs")
row_actions = (ViewLog, PublishLog, EnableLog, DisableLog, DiscardLog)
def get_object_id(self, datum):
return datum.name

View File

@ -0,0 +1,361 @@
# Copyright 2016 Tesora Inc.
#
# 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 logging
from django.core.urlresolvers import reverse
from django import http
from mox3 import mox
from mox3.mox import IsA # noqa
import six
from trove_dashboard import api
from trove_dashboard.test import helpers as test
from swiftclient import client as swift_client
LINES = 50
class LogsTests(test.TestCase):
def stub_swiftclient(self, expected_calls=1):
if not hasattr(self, "swiftclient"):
self.mox.StubOutWithMock(swift_client, 'Connection')
self.swiftclient = self.mox.CreateMock(swift_client.Connection)
while expected_calls:
(swift_client.Connection(None,
mox.IgnoreArg(),
None,
preauthtoken=mox.IgnoreArg(),
preauthurl=mox.IgnoreArg(),
cacert=None,
insecure=False,
auth_version="2.0")
.AndReturn(self.swiftclient))
expected_calls -= 1
return self.swiftclient
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'root_show')})
def test_log_tab(self):
database = self.databases.first()
database_id = database.id
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.root_show(IsA(http.HttpRequest), database.id)
.AndReturn(self.database_user_roots.first()))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
res = self.client.get(url)
table_data = res.context['logs_table'].data
self.assertItemsEqual(self.logs.list(), table_data)
self.assertTemplateUsed(
res, 'horizon/common/_detail_table.html')
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'root_show')})
def test_log_tab_exception(self):
database = self.databases.first()
database_id = database.id
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndRaise(self.exceptions.trove))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.root_show(IsA(http.HttpRequest), database.id)
.AndReturn(self.database_user_roots.first()))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
toSuppress = ["trove_dashboard.content.databases.tabs"]
loggers = []
for cls in toSuppress:
logger = logging.getLogger(cls)
loggers.append((logger, logger.getEffectiveLevel()))
logger.setLevel(logging.CRITICAL)
try:
res = self.client.get(url)
table_data = res.context['logs_table'].data
self.assertNotEqual(len(self.logs.list()), len(table_data))
self.assertTemplateUsed(
res, 'horizon/common/_detail_table.html')
finally:
# Restore the previous log levels
for (log, level) in loggers:
log.setLevel(level)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_publish',)
})
def test_log_publish(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_publish(IsA(http.HttpRequest), database_id, log.name)
.AndReturn(None))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('publish', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_publish',)
})
def test_log_publish_exception(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_publish(IsA(http.HttpRequest), database_id, log.name)
.AndRaise(self.exceptions.trove))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('publish', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_enable',)
})
def test_log_enable(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_enable(IsA(http.HttpRequest), database_id, log.name)
.AndReturn(None))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('enable', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_enable',)
})
def test_log_enable_exception(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_enable(IsA(http.HttpRequest), database_id, log.name)
.AndRaise(self.exceptions.trove))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('enable', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_discard',)
})
def test_log_discard(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_discard(IsA(http.HttpRequest), database_id, log.name)
.AndReturn(None))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('discard', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_discard',)
})
def test_log_discard_exception(self):
database = self.databases.first()
database_id = database.id
log = self.logs.first()
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_discard(IsA(http.HttpRequest), database_id, log.name)
.AndRaise(self.exceptions.trove))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('discard', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_disable',)
})
def test_log_disable(self):
database = self.databases.first()
database_id = database.id
log = self.logs.list()[3]
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_disable(IsA(http.HttpRequest), database_id, log.name)
.AndReturn(None))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('disable', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({
api.trove: ('flavor_get', 'instance_get', 'log_list', 'log_disable',)
})
def test_log_disable_exception(self):
database = self.databases.first()
database_id = database.id
log = self.logs.list()[3]
(api.trove.instance_get(IsA(http.HttpRequest), IsA(six.text_type))
.AndReturn(database))
(api.trove.log_list(IsA(http.HttpRequest), database_id)
.AndReturn(self.logs.list()))
(api.trove.flavor_get(IsA(http.HttpRequest), database.flavor["id"])
.AndReturn(self.flavors.first()))
(api.trove.log_disable(IsA(http.HttpRequest), database_id, log.name)
.AndRaise(self.exceptions.trove))
self.mox.ReplayAll()
detail_url = reverse('horizon:project:databases:detail',
args=[database_id])
url = detail_url + '?tab=instance_details__logs_tab'
action_string = u"logs__%s_log__%s" % ('disable', log.name)
form_data = {'action': action_string}
res = self.client.post(url, form_data)
self.assertRedirectsNoFollow(res, url)
@test.create_stubs({api.trove: ('log_tail',)})
def test_view_log(self):
CONSOLE_OUTPUT = 'superspecialuniquetext'
(api.trove.log_tail(IsA(http.HttpRequest),
IsA(six.string_types),
'guest.log',
False,
LINES,
self.stub_swiftclient())
.AndReturn(lambda: [CONSOLE_OUTPUT]))
self.mox.ReplayAll()
url = reverse('horizon:project:databases:logs:log_contents',
args=('id', 'guest.log'))
res = self.client.get(url)
self.assertNoMessages()
self.assertIsInstance(res, http.HttpResponse)
self.assertContains(res, CONSOLE_OUTPUT)
@test.create_stubs({api.trove: ('log_tail',)})
def test_view_log_exception(self):
(api.trove.log_tail(IsA(http.HttpRequest),
IsA(six.string_types),
'guest.log',
False,
LINES,
self.stub_swiftclient())
.AndRaise(self.exceptions.trove))
self.mox.ReplayAll()
url = reverse('horizon:project:databases:logs:log_contents',
args=('id', 'guest.log'))
res = self.client.get(url)
self.assertContains(res, "Unable to load")

View File

@ -0,0 +1,32 @@
# Copyright 2016 Tesora Inc.
#
# 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 patterns
from django.conf.urls import url
from trove_dashboard.content.databases.logs import views
VIEWS_MOD = ('trove_dashboard.content.databases.logs.views')
LOGS = r'^(?P<filename>[^/]+)/%s$'
urlpatterns = patterns(
VIEWS_MOD,
url(LOGS % 'console', 'console', name='console'),
url(LOGS % 'download_log', 'download_log', name='download_log'),
url(LOGS % 'full_log', 'full_log', name='full_log'),
url(LOGS % 'log_contents',
views.LogContentsView.as_view(), name='log_contents'),
)

View File

@ -0,0 +1,129 @@
# Copyright 2016 Tesora Inc.
#
# 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 import http
from django import shortcuts
from django.utils.translation import ugettext_lazy as _
from django.views import generic
from horizon import exceptions
from horizon import messages
from openstack_dashboard import api as dash_api
from trove_dashboard import api
FULL_LOG_VALUE = 0
DEFAULT_LINES = 50
class LogContentsView(generic.TemplateView):
template_name = 'project/databases/logs/view_log.html'
preload = False
def get_context_data(self, **kwargs):
context = super(LogContentsView, self).get_context_data(**kwargs)
context["instance_id"] = kwargs['instance_id']
context["filename"] = kwargs['filename']
context["publish"] = ''
try:
log_length = int(kwargs['lines'])
except Exception:
log_length = DEFAULT_LINES
context["log_length"] = log_length
context["log_contents"] = get_contents(self.request,
kwargs['instance_id'],
kwargs['filename'],
False,
log_length)
return context
def get_contents(request, instance_id, filename, publish, lines):
try:
log_generator = api.trove.log_tail(request,
instance_id,
filename,
publish,
lines,
dash_api.swift.swift_api(request))
data = ""
for log_part in log_generator():
data += log_part
except Exception as e:
data = _('Unable to load {0} log\n{1}').format(filename, e.message)
return data
def build_response(request, instance_id, filename, tail):
data = (_('Unable to load {0} log for instance "{1}".')
.format(filename, instance_id))
if request.GET.get('publish'):
publish = True
else:
publish = False
try:
data = get_contents(request,
instance_id,
filename,
publish,
int(tail))
except Exception:
exceptions.handle(request, ignore=True)
return http.HttpResponse(data.encode('utf-8'), content_type='text/plain')
def console(request, instance_id, filename):
tail = request.GET.get('length')
if not tail or (tail and not tail.isdigit()):
msg = _('Log length must be a nonnegative integer.')
messages.warning(request, msg)
data = (_('Unable to load {0} log for instance "{1}".')
.format(filename, instance_id))
return http.HttpResponse(data.encode('utf-8'),
content_type='text/plain')
return build_response(request, instance_id, filename, tail)
def full_log(request, instance_id, filename):
return build_response(request, instance_id, filename, FULL_LOG_VALUE)
def download_log(request, instance_id, filename):
try:
publish_value = request.GET.get('publish')
if publish_value:
publish = True
else:
publish = False
data = get_contents(request,
instance_id,
filename,
publish,
FULL_LOG_VALUE)
response = http.HttpResponse()
response.write(data)
response['Content-Disposition'] = ('attachment; '
'filename="%s.log"' % filename)
response['Content-Length'] = str(len(response.content))
return response
except Exception as e:
messages.error(request, _('Error downloading log file: %s') % e)
return shortcuts.redirect(request.build_absolute_uri())

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from django import template
from django.utils.translation import ugettext_lazy as _
@ -19,9 +21,13 @@ from horizon import exceptions
from horizon import tabs
from trove_dashboard import api
from trove_dashboard.content.databases import db_capability
from trove_dashboard.content.databases.logs import tables as log_tables
from trove_dashboard.content.databases import tables
LOG = logging.getLogger(__name__)
class OverviewTab(tabs.Tab):
name = _("Overview")
slug = "overview"
@ -136,7 +142,26 @@ class BackupsTab(tabs.TableTab):
return request.user.has_perm('openstack.services.object-store')
class LogsTab(tabs.TableTab):
table_classes = [log_tables.LogsTable]
name = _("Logs")
slug = "logs_tab"
template_name = "horizon/common/_detail_table.html"
preload = False
def get_logs_data(self):
instance = self.tab_group.kwargs['instance']
try:
logs = api.trove.log_list(self.request, instance.id)
return logs
except Exception as e:
LOG.exception(
_('Unable to retrieve list of logs.\n%s') % e.message)
logs = []
return logs
class InstanceDetailTabs(tabs.TabGroup):
slug = "instance_details"
tabs = (OverviewTab, UserTab, DatabaseTab, BackupsTab)
tabs = (OverviewTab, UserTab, DatabaseTab, BackupsTab, LogsTab)
sticky = True

View File

@ -0,0 +1,22 @@
{% load i18n %}
{% load url from future %}
<div class="col-sm-12">
<div class="clearfix">
<form id="tail_length" action="{% url 'horizon:project:databases:logs:console' instance_id filename %}" class="form-inline pull-right">
<label for="tail_length_select">{% trans "Log Length" %}</label>
<input class="span1" type="text" name="length" value="{{ log_length }}" />
<label for="publish_check">{% trans "Publish" %}</label>
<input type="checkbox" name="publish" value="publish" {{ publish }}>
<button class="btn btn-default btn-sm btn-primary always-enabled" type="submit">{% trans "Go" %}</button>
<a href="{% url 'horizon:project:databases:detail' instance_id %}" class="btn btn-default btn-sm pull-right secondary">{% trans "Return to Log List" %}</a>
<a href="{% url 'horizon:project:databases:logs:download_log' instance_id filename %}" class="btn btn-default btn-sm pull-right">{% trans "Download" %}</a>
<a href="{% url 'horizon:project:databases:logs:full_log' instance_id filename %}" class="btn btn-default btn-sm pull-right" target="_blank">{% trans "View Full Log" %}</a>
</form>
</div>
<br>
<pre class="logs">
{{ log_contents }}
</pre>
</div>

View File

@ -0,0 +1,15 @@
{% extends "horizon/common/_modal.html" %}
{% load i18n %}
{% load url from future %}
{% block modal-header %}{% trans "Log Contents" %}{% endblock %}
{% block modal-body %}
<pre class="logs">
{{ log.data }}
</pre>
{% endblock %}
{% block modal-footer %}
<a href="{% url 'horizon:project:databases:index' %}" class="btn btn-default secondary cancel close">{% trans "Close" %}</a>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Log Contents" %}{% endblock %}
{% block main %}
<div class="row-fluid">
<div class="col-sm-12">
{% include "project/databases/logs/_log_contents.html" %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Log" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Log: ")|add:filename %}
{% endblock page_header %}
{% block main %}
{% include "project/databases/logs/_detail_log.html" %}
{% endblock %}

View File

@ -12,13 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from django.conf.urls import include
from django.conf.urls import patterns
from django.conf.urls import url
from trove_dashboard.content.databases.logs import urls as logs_urls
from trove_dashboard.content.databases import views
INSTANCES = r'^(?P<instance_id>[^/]+)/%s$'
BASEINSTANCES = r'^(?P<instance_id>[^/]+)/%s'
INSTANCES = BASEINSTANCES + '$'
USERS = r'^(?P<instance_id>[^/]+)/(?P<user_name>[^/]+)/%s$'
@ -44,4 +46,5 @@ urlpatterns = patterns(
name='promote_to_replica_source'),
url(INSTANCES % 'manage_root', views.ManageRootView.as_view(),
name='manage_root'),
url(BASEINSTANCES % 'logs/', include(logs_urls, namespace='logs')),
)

View File

@ -367,6 +367,42 @@ VERSION_VERTICA_7_1 = {
"id": "600a6d52-8347-4e00-8e4c-f4fa9cf96af1"
}
LOG_1 = {
"name": "guest",
"type": "SYS",
"status": "Partial",
"published": 5000,
"pending": 100,
"container": "guest_container"
}
LOG_2 = {
"name": "slow_query",
"type": "USER",
"status": "Disabled",
"published": 0,
"pending": 0,
"container": "None"
}
LOG_3 = {
"name": "error",
"type": "SYS",
"status": "Unavailable",
"published": 0,
"pending": 0,
"container": "None"
}
LOG_4 = {
"name": "general",
"type": "USER",
"status": "Ready",
"published": 0,
"pending": 1000,
"container": "None"
}
def data(TEST):
cluster1 = clusters.Cluster(clusters.Clusters(None),
@ -413,6 +449,11 @@ def data(TEST):
DatastoreVersion(datastores.DatastoreVersions(None),
VERSION_VERTICA_7_1)
log1 = instances.DatastoreLog(instances.Instances(None), LOG_1)
log2 = instances.DatastoreLog(instances.Instances(None), LOG_2)
log3 = instances.DatastoreLog(instances.Instances(None), LOG_3)
log4 = instances.DatastoreLog(instances.Instances(None), LOG_4)
TEST.trove_clusters = utils.TestDataContainer()
TEST.trove_clusters.add(cluster1)
TEST.trove_clusters.add(cluster2)
@ -443,3 +484,6 @@ def data(TEST):
TEST.datastore_versions.add(version_redis_3_0)
TEST.datastore_versions.add(version_mongodb_2_6)
TEST.datastore_versions.add(version1)
TEST.logs = utils.TestDataContainer()
TEST.logs.add(log1, log2, log3, log4)