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:
Lingxian Kong
2019-12-16 14:31:13 +13:00
parent f5a57732a9
commit adde0d0bed
5 changed files with 125 additions and 21 deletions

View File

@@ -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>

View File

@@ -81,6 +81,8 @@ openstack.database.v1 =
database_log_list = troveclient.osc.v1.database_logs:ListDatabaseLogs
database_log_set = troveclient.osc.v1.database_logs:SetDatabaseInstanceLog
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_update = troveclient.osc.v1.database_quota:UpdateDatabaseQuota
database_root_disable = troveclient.osc.v1.database_root:DisableDatabaseRoot

View File

@@ -10,12 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Database v1 Logs action implementations"""
from __future__ import print_function
from osc_lib.command import command
from osc_lib import utils as osc_utils
import six
from troveclient import exceptions
from troveclient.i18n import _
@@ -134,3 +135,97 @@ class ShowDatabaseInstanceLog(command.ShowOne):
result = log_info._info
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])

View File

@@ -412,9 +412,9 @@ class Instances(base.ManagerWithFind):
common.check_for_exceptions(resp, body, url)
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:
log_info = self.log_action(instance, log_name, publish=publish)
log_info = self.log_show(instance, log_name)
container = log_info.container
prefix = log_info.prefix
metadata_file = log_info.metafile
@@ -424,33 +424,33 @@ class Instances(base.ManagerWithFind):
raise exceptions.GuestLogNotFoundError()
raise
def log_generator(self, instance, log_name, publish=None, lines=50,
swift=None):
def log_generator(self, instance, log_name, lines=50, swift=None):
"""Return generator to yield the last <lines> lines of guest log.
:param instance: The :class:`Instance` (or its ID) of the database
instance to get the log for.
: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 swift: Connection to swift
:rtype: generator function to yield log as chunks.
"""
if not swift:
swift = self._get_swift_client()
def _log_generator(instance, log_name, publish, lines, swift):
def _log_generator(instance, log_name, lines, swift):
try:
container, prefix, metadata_file = self._get_container_info(
instance, log_name, publish)
instance, log_name)
head, body = swift.get_container(container, prefix=prefix)
log_obj_to_display = []
if lines:
total_lines = lines
partial_results = False
parts = sorted(body, key=lambda obj: obj['last_modified'],
reverse=True)
for part in parts:
obj_hdrs = swift.head_object(container, part['name'])
obj_lines = int(obj_hdrs['x-object-meta-lines'])
@@ -461,13 +461,16 @@ class Instances(base.ManagerWithFind):
lines -= obj_lines
if not partial_results:
lines = total_lines
part = log_obj_to_display.pop(0)
hdrs, log_obj = swift.get_object(container, part['name'])
log_by_lines = log_obj.decode().splitlines()
yield "\n".join(log_by_lines[-1 * lines:]) + "\n"
else:
# Show all the logs
log_obj_to_display = sorted(
body, key=lambda obj: obj['last_modified'])
for log_part in log_obj_to_display:
headers, log_obj = swift.get_object(container,
log_part['name'])
@@ -477,20 +480,19 @@ class Instances(base.ManagerWithFind):
raise exceptions.GuestLogNotFoundError()
raise
return lambda: _log_generator(instance, log_name, publish,
lines, swift)
return lambda: _log_generator(instance, log_name, 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.
:param instance: The :class:`Instance` (or its ID) of the database
instance to get the log for.
:param log_name: The name of <log> to publish
:param publish: Publish updates before displaying log
:rtype: Filename to which log was saved
"""
written_file = filename or (instance.name + '-' + log_name + ".log")
log_gen = self.log_generator(instance, log_name, publish, 0)
written_file = filename or (
'trove-' + instance.id + '-' + log_name + ".log")
log_gen = self.log_generator(instance, log_name, lines=0)
with open(written_file, 'w') as f:
for log_obj in log_gen():
f.write(log_obj)

View File

@@ -2292,8 +2292,6 @@ def do_log_discard(cs, args):
@utils.arg('instance', metavar='<instance>',
help=_('Id or Name of the instance.'))
@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,
help=_('Publish latest entries from guest before display.'))
@utils.service_type('database')
@@ -2302,7 +2300,7 @@ def do_log_tail(cs, args):
try:
instance = _find_instance(cs, args.instance)
log_gen = cs.instances.log_generator(instance, args.log_name,
args.publish, args.lines)
args.lines)
for log_part in log_gen():
print(log_part, end="")
except exceptions.GuestLogNotFoundError:
@@ -2316,8 +2314,6 @@ def do_log_tail(cs, args):
@utils.arg('instance', metavar='<instance>',
help=_('Id or Name of the instance.'))
@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,
help=_('Path of file to save log to for instance.'))
@utils.service_type('database')
@@ -2326,7 +2322,7 @@ def do_log_save(cs, args):
try:
instance = _find_instance(cs, args.instance)
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')
% {'log_name': args.log_name,
'file_name': filename})