Support log tail/save for instance
Support to show log content and save instance log files: openstack database log tail [--lines LINES] <instance> <log_name> openstack database log save [--file FILE] <instance> <log_name> Change-Id: I2a4eecca663f9126f770fae4c4be28b3667f91c4
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support to show log content and save instance log files:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
openstack database log tail [--lines LINES] <instance> <log_name>
|
||||||
|
openstack database log save [--file FILE] <instance> <log_name>
|
@@ -81,6 +81,8 @@ openstack.database.v1 =
|
|||||||
database_log_list = troveclient.osc.v1.database_logs:ListDatabaseLogs
|
database_log_list = troveclient.osc.v1.database_logs:ListDatabaseLogs
|
||||||
database_log_set = troveclient.osc.v1.database_logs:SetDatabaseInstanceLog
|
database_log_set = troveclient.osc.v1.database_logs:SetDatabaseInstanceLog
|
||||||
database_log_show = troveclient.osc.v1.database_logs:ShowDatabaseInstanceLog
|
database_log_show = troveclient.osc.v1.database_logs:ShowDatabaseInstanceLog
|
||||||
|
database_log_tail = troveclient.osc.v1.database_logs:ShowDatabaseInstanceLogContents
|
||||||
|
database_log_save = troveclient.osc.v1.database_logs:SaveDatabaseInstanceLog
|
||||||
database_quota_show = troveclient.osc.v1.database_quota:ShowDatabaseQuota
|
database_quota_show = troveclient.osc.v1.database_quota:ShowDatabaseQuota
|
||||||
database_quota_update = troveclient.osc.v1.database_quota:UpdateDatabaseQuota
|
database_quota_update = troveclient.osc.v1.database_quota:UpdateDatabaseQuota
|
||||||
database_root_disable = troveclient.osc.v1.database_root:DisableDatabaseRoot
|
database_root_disable = troveclient.osc.v1.database_root:DisableDatabaseRoot
|
||||||
|
@@ -10,12 +10,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Database v1 Logs action implementations"""
|
from __future__ import print_function
|
||||||
|
|
||||||
from osc_lib.command import command
|
from osc_lib.command import command
|
||||||
from osc_lib import utils as osc_utils
|
from osc_lib import utils as osc_utils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from troveclient import exceptions
|
||||||
from troveclient.i18n import _
|
from troveclient.i18n import _
|
||||||
|
|
||||||
|
|
||||||
@@ -134,3 +135,97 @@ class ShowDatabaseInstanceLog(command.ShowOne):
|
|||||||
result = log_info._info
|
result = log_info._info
|
||||||
|
|
||||||
return zip(*sorted(six.iteritems(result)))
|
return zip(*sorted(six.iteritems(result)))
|
||||||
|
|
||||||
|
|
||||||
|
class ShowDatabaseInstanceLogContents(command.Command):
|
||||||
|
_description = _("Show the content of log file.")
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ShowDatabaseInstanceLogContents, self).get_parser(
|
||||||
|
prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'instance',
|
||||||
|
metavar='<instance>',
|
||||||
|
type=str,
|
||||||
|
help=_('Id or Name of the instance.')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'log_name',
|
||||||
|
metavar='<log_name>',
|
||||||
|
type=str,
|
||||||
|
help=_('Name of log to operate.')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--lines', default=50, type=int,
|
||||||
|
help="The number of log lines can be shown in batch.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
db_instances = self.app.client_manager.database.instances
|
||||||
|
instance = osc_utils.find_resource(db_instances,
|
||||||
|
parsed_args.instance)
|
||||||
|
|
||||||
|
try:
|
||||||
|
log_gen = db_instances.log_generator(instance,
|
||||||
|
parsed_args.log_name,
|
||||||
|
lines=parsed_args.lines)
|
||||||
|
for log_part in log_gen():
|
||||||
|
print(log_part, end="")
|
||||||
|
except exceptions.GuestLogNotFoundError:
|
||||||
|
print(
|
||||||
|
"ERROR: No published '%(log_name)s' log was found for "
|
||||||
|
"%(instance)s" % {'log_name': parsed_args.log_name,
|
||||||
|
'instance': instance}
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
error_msg = ex.message.split('\n')
|
||||||
|
print(error_msg[0])
|
||||||
|
|
||||||
|
|
||||||
|
class SaveDatabaseInstanceLog(command.Command):
|
||||||
|
_description = _("Save the log file.")
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(SaveDatabaseInstanceLog, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
'instance',
|
||||||
|
metavar='<instance>',
|
||||||
|
type=str,
|
||||||
|
help=_('Id or Name of the instance.')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'log_name',
|
||||||
|
metavar='<log_name>',
|
||||||
|
type=str,
|
||||||
|
help=_('Name of log to operate.')
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--file',
|
||||||
|
help="Path of file to save log to for instance.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
db_instances = self.app.client_manager.database.instances
|
||||||
|
instance = osc_utils.find_resource(db_instances,
|
||||||
|
parsed_args.instance)
|
||||||
|
|
||||||
|
try:
|
||||||
|
filepath = db_instances.log_save(instance,
|
||||||
|
parsed_args.log_name,
|
||||||
|
filename=parsed_args.file)
|
||||||
|
print(_('Log "%(log_name)s" written to %(file_name)s')
|
||||||
|
% {'log_name': parsed_args.log_name,
|
||||||
|
'file_name': filepath})
|
||||||
|
except exceptions.GuestLogNotFoundError:
|
||||||
|
print(
|
||||||
|
"ERROR: No published '%(log_name)s' log was found for "
|
||||||
|
"%(instance)s" % {'log_name': parsed_args.log_name,
|
||||||
|
'instance': instance}
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
error_msg = ex.message.split('\n')
|
||||||
|
print(error_msg[0])
|
||||||
|
@@ -412,9 +412,9 @@ class Instances(base.ManagerWithFind):
|
|||||||
common.check_for_exceptions(resp, body, url)
|
common.check_for_exceptions(resp, body, url)
|
||||||
return DatastoreLog(self, body['log'], loaded=True)
|
return DatastoreLog(self, body['log'], loaded=True)
|
||||||
|
|
||||||
def _get_container_info(self, instance, log_name, publish):
|
def _get_container_info(self, instance, log_name):
|
||||||
try:
|
try:
|
||||||
log_info = self.log_action(instance, log_name, publish=publish)
|
log_info = self.log_show(instance, log_name)
|
||||||
container = log_info.container
|
container = log_info.container
|
||||||
prefix = log_info.prefix
|
prefix = log_info.prefix
|
||||||
metadata_file = log_info.metafile
|
metadata_file = log_info.metafile
|
||||||
@@ -424,33 +424,33 @@ class Instances(base.ManagerWithFind):
|
|||||||
raise exceptions.GuestLogNotFoundError()
|
raise exceptions.GuestLogNotFoundError()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def log_generator(self, instance, log_name, publish=None, lines=50,
|
def log_generator(self, instance, log_name, lines=50, swift=None):
|
||||||
swift=None):
|
|
||||||
"""Return generator to yield the last <lines> lines of guest log.
|
"""Return generator to yield the last <lines> lines of guest log.
|
||||||
|
|
||||||
:param instance: The :class:`Instance` (or its ID) of the database
|
:param instance: The :class:`Instance` (or its ID) of the database
|
||||||
instance to get the log for.
|
instance to get the log for.
|
||||||
:param log_name: The name of <log> to publish
|
:param log_name: The name of <log> to publish
|
||||||
:param publish: Publish updates before displaying log
|
|
||||||
:param lines: Display last <lines> lines of log (0 for all lines)
|
:param lines: Display last <lines> lines of log (0 for all lines)
|
||||||
:param swift: Connection to swift
|
:param swift: Connection to swift
|
||||||
:rtype: generator function to yield log as chunks.
|
:rtype: generator function to yield log as chunks.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not swift:
|
if not swift:
|
||||||
swift = self._get_swift_client()
|
swift = self._get_swift_client()
|
||||||
|
|
||||||
def _log_generator(instance, log_name, publish, lines, swift):
|
def _log_generator(instance, log_name, lines, swift):
|
||||||
try:
|
try:
|
||||||
container, prefix, metadata_file = self._get_container_info(
|
container, prefix, metadata_file = self._get_container_info(
|
||||||
instance, log_name, publish)
|
instance, log_name)
|
||||||
|
|
||||||
head, body = swift.get_container(container, prefix=prefix)
|
head, body = swift.get_container(container, prefix=prefix)
|
||||||
log_obj_to_display = []
|
log_obj_to_display = []
|
||||||
|
|
||||||
if lines:
|
if lines:
|
||||||
total_lines = lines
|
total_lines = lines
|
||||||
partial_results = False
|
partial_results = False
|
||||||
parts = sorted(body, key=lambda obj: obj['last_modified'],
|
parts = sorted(body, key=lambda obj: obj['last_modified'],
|
||||||
reverse=True)
|
reverse=True)
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
obj_hdrs = swift.head_object(container, part['name'])
|
obj_hdrs = swift.head_object(container, part['name'])
|
||||||
obj_lines = int(obj_hdrs['x-object-meta-lines'])
|
obj_lines = int(obj_hdrs['x-object-meta-lines'])
|
||||||
@@ -461,13 +461,16 @@ class Instances(base.ManagerWithFind):
|
|||||||
lines -= obj_lines
|
lines -= obj_lines
|
||||||
if not partial_results:
|
if not partial_results:
|
||||||
lines = total_lines
|
lines = total_lines
|
||||||
|
|
||||||
part = log_obj_to_display.pop(0)
|
part = log_obj_to_display.pop(0)
|
||||||
hdrs, log_obj = swift.get_object(container, part['name'])
|
hdrs, log_obj = swift.get_object(container, part['name'])
|
||||||
log_by_lines = log_obj.decode().splitlines()
|
log_by_lines = log_obj.decode().splitlines()
|
||||||
yield "\n".join(log_by_lines[-1 * lines:]) + "\n"
|
yield "\n".join(log_by_lines[-1 * lines:]) + "\n"
|
||||||
else:
|
else:
|
||||||
|
# Show all the logs
|
||||||
log_obj_to_display = sorted(
|
log_obj_to_display = sorted(
|
||||||
body, key=lambda obj: obj['last_modified'])
|
body, key=lambda obj: obj['last_modified'])
|
||||||
|
|
||||||
for log_part in log_obj_to_display:
|
for log_part in log_obj_to_display:
|
||||||
headers, log_obj = swift.get_object(container,
|
headers, log_obj = swift.get_object(container,
|
||||||
log_part['name'])
|
log_part['name'])
|
||||||
@@ -477,20 +480,19 @@ class Instances(base.ManagerWithFind):
|
|||||||
raise exceptions.GuestLogNotFoundError()
|
raise exceptions.GuestLogNotFoundError()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return lambda: _log_generator(instance, log_name, publish,
|
return lambda: _log_generator(instance, log_name, lines, swift)
|
||||||
lines, swift)
|
|
||||||
|
|
||||||
def log_save(self, instance, log_name, publish=None, filename=None):
|
def log_save(self, instance, log_name, filename=None):
|
||||||
"""Saves a guest log to a file.
|
"""Saves a guest log to a file.
|
||||||
|
|
||||||
:param instance: The :class:`Instance` (or its ID) of the database
|
:param instance: The :class:`Instance` (or its ID) of the database
|
||||||
instance to get the log for.
|
instance to get the log for.
|
||||||
:param log_name: The name of <log> to publish
|
:param log_name: The name of <log> to publish
|
||||||
:param publish: Publish updates before displaying log
|
|
||||||
:rtype: Filename to which log was saved
|
:rtype: Filename to which log was saved
|
||||||
"""
|
"""
|
||||||
written_file = filename or (instance.name + '-' + log_name + ".log")
|
written_file = filename or (
|
||||||
log_gen = self.log_generator(instance, log_name, publish, 0)
|
'trove-' + instance.id + '-' + log_name + ".log")
|
||||||
|
log_gen = self.log_generator(instance, log_name, lines=0)
|
||||||
with open(written_file, 'w') as f:
|
with open(written_file, 'w') as f:
|
||||||
for log_obj in log_gen():
|
for log_obj in log_gen():
|
||||||
f.write(log_obj)
|
f.write(log_obj)
|
||||||
|
@@ -2292,8 +2292,6 @@ def do_log_discard(cs, args):
|
|||||||
@utils.arg('instance', metavar='<instance>',
|
@utils.arg('instance', metavar='<instance>',
|
||||||
help=_('Id or Name of the instance.'))
|
help=_('Id or Name of the instance.'))
|
||||||
@utils.arg('log_name', metavar='<log_name>', help=_('Name of log to publish.'))
|
@utils.arg('log_name', metavar='<log_name>', help=_('Name of log to publish.'))
|
||||||
@utils.arg('--publish', action='store_true', default=False,
|
|
||||||
help=_('Publish latest entries from guest before display.'))
|
|
||||||
@utils.arg('--lines', metavar='<lines>', default=50, type=int,
|
@utils.arg('--lines', metavar='<lines>', default=50, type=int,
|
||||||
help=_('Publish latest entries from guest before display.'))
|
help=_('Publish latest entries from guest before display.'))
|
||||||
@utils.service_type('database')
|
@utils.service_type('database')
|
||||||
@@ -2302,7 +2300,7 @@ def do_log_tail(cs, args):
|
|||||||
try:
|
try:
|
||||||
instance = _find_instance(cs, args.instance)
|
instance = _find_instance(cs, args.instance)
|
||||||
log_gen = cs.instances.log_generator(instance, args.log_name,
|
log_gen = cs.instances.log_generator(instance, args.log_name,
|
||||||
args.publish, args.lines)
|
args.lines)
|
||||||
for log_part in log_gen():
|
for log_part in log_gen():
|
||||||
print(log_part, end="")
|
print(log_part, end="")
|
||||||
except exceptions.GuestLogNotFoundError:
|
except exceptions.GuestLogNotFoundError:
|
||||||
@@ -2316,8 +2314,6 @@ def do_log_tail(cs, args):
|
|||||||
@utils.arg('instance', metavar='<instance>',
|
@utils.arg('instance', metavar='<instance>',
|
||||||
help=_('Id or Name of the instance.'))
|
help=_('Id or Name of the instance.'))
|
||||||
@utils.arg('log_name', metavar='<log_name>', help=_('Name of log to publish.'))
|
@utils.arg('log_name', metavar='<log_name>', help=_('Name of log to publish.'))
|
||||||
@utils.arg('--publish', action='store_true', default=False,
|
|
||||||
help=_('Publish latest entries from guest before display.'))
|
|
||||||
@utils.arg('--file', metavar='<file>', default=None,
|
@utils.arg('--file', metavar='<file>', default=None,
|
||||||
help=_('Path of file to save log to for instance.'))
|
help=_('Path of file to save log to for instance.'))
|
||||||
@utils.service_type('database')
|
@utils.service_type('database')
|
||||||
@@ -2326,7 +2322,7 @@ def do_log_save(cs, args):
|
|||||||
try:
|
try:
|
||||||
instance = _find_instance(cs, args.instance)
|
instance = _find_instance(cs, args.instance)
|
||||||
filename = cs.instances.log_save(instance, args.log_name,
|
filename = cs.instances.log_save(instance, args.log_name,
|
||||||
args.publish, args.file)
|
filename=args.file)
|
||||||
print(_('Log "%(log_name)s" written to %(file_name)s')
|
print(_('Log "%(log_name)s" written to %(file_name)s')
|
||||||
% {'log_name': args.log_name,
|
% {'log_name': args.log_name,
|
||||||
'file_name': filename})
|
'file_name': filename})
|
||||||
|
Reference in New Issue
Block a user