diff options
author | Lingxian Kong <anlin.kong@gmail.com> | 2019-12-16 14:31:13 +1300 |
---|---|---|
committer | Lingxian Kong <anlin.kong@gmail.com> | 2019-12-16 15:21:25 +1300 |
commit | adde0d0bedb5783be2c03bd9412d70684bff0612 (patch) | |
tree | 73ccbe4f16a10f2a529bbe1305db2692f03469b0 | |
parent | f5a57732a95e130689f6c5a16794e42266b206b0 (diff) | |
download | python-troveclient-adde0d0bedb5783be2c03bd9412d70684bff0612.tar.gz |
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
-rw-r--r-- | releasenotes/notes/ussuri-02-instance-log-tail-save-0b267a761faa11ea.yaml | 9 | ||||
-rw-r--r-- | setup.cfg | 2 | ||||
-rw-r--r-- | troveclient/osc/v1/database_logs.py | 97 | ||||
-rw-r--r-- | troveclient/v1/instances.py | 30 | ||||
-rw-r--r-- | troveclient/v1/shell.py | 8 |
5 files changed, 125 insertions, 21 deletions
diff --git a/releasenotes/notes/ussuri-02-instance-log-tail-save-0b267a761faa11ea.yaml b/releasenotes/notes/ussuri-02-instance-log-tail-save-0b267a761faa11ea.yaml new file mode 100644 index 0000000..5516d14 --- /dev/null +++ b/releasenotes/notes/ussuri-02-instance-log-tail-save-0b267a761faa11ea.yaml @@ -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_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 diff --git a/troveclient/osc/v1/database_logs.py b/troveclient/osc/v1/database_logs.py index eb342c3..d43d331 100644 --- a/troveclient/osc/v1/database_logs.py +++ b/troveclient/osc/v1/database_logs.py @@ -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]) diff --git a/troveclient/v1/instances.py b/troveclient/v1/instances.py index a122bc3..15725a3 100644 --- a/troveclient/v1/instances.py +++ b/troveclient/v1/instances.py @@ -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) diff --git a/troveclient/v1/shell.py b/troveclient/v1/shell.py index bb6f2f6..1b72d31 100644 --- a/troveclient/v1/shell.py +++ b/troveclient/v1/shell.py @@ -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}) |