Blame cloudinit/analyze/__main__.py

Packit Service a04d08
# Copyright (C) 2017 Canonical Ltd.
Packit Service a04d08
#
Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
import argparse
Packit Service a04d08
import re
Packit Service a04d08
import sys
Packit Service a04d08
Packit Service a04d08
from cloudinit.util import json_dumps
Packit Service a04d08
from datetime import datetime
Packit Service a04d08
from . import dump
Packit Service a04d08
from . import show
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def get_parser(parser=None):
Packit Service a04d08
    if not parser:
Packit Service a04d08
        parser = argparse.ArgumentParser(
Packit Service a04d08
            prog='cloudinit-analyze',
Packit Service a04d08
            description='Devel tool: Analyze cloud-init logs and data')
Packit Service a04d08
    subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand')
Packit Service a04d08
    subparsers.required = True
Packit Service a04d08
Packit Service a04d08
    parser_blame = subparsers.add_parser(
Packit Service a04d08
        'blame', help='Print list of executed stages ordered by time to init')
Packit Service a04d08
    parser_blame.add_argument(
Packit Service a04d08
        '-i', '--infile', action='store', dest='infile',
Packit Service a04d08
        default='/var/log/cloud-init.log',
Packit Service a04d08
        help='specify where to read input.')
Packit Service a04d08
    parser_blame.add_argument(
Packit Service a04d08
        '-o', '--outfile', action='store', dest='outfile', default='-',
Packit Service a04d08
        help='specify where to write output. ')
Packit Service a04d08
    parser_blame.set_defaults(action=('blame', analyze_blame))
Packit Service a04d08
Packit Service a04d08
    parser_show = subparsers.add_parser(
Packit Service a04d08
        'show', help='Print list of in-order events during execution')
Packit Service a04d08
    parser_show.add_argument('-f', '--format', action='store',
Packit Service a04d08
                             dest='print_format', default='%I%D @%Es +%ds',
Packit Service a04d08
                             help='specify formatting of output.')
Packit Service a04d08
    parser_show.add_argument('-i', '--infile', action='store',
Packit Service a04d08
                             dest='infile', default='/var/log/cloud-init.log',
Packit Service a04d08
                             help='specify where to read input.')
Packit Service a04d08
    parser_show.add_argument('-o', '--outfile', action='store',
Packit Service a04d08
                             dest='outfile', default='-',
Packit Service a04d08
                             help='specify where to write output.')
Packit Service a04d08
    parser_show.set_defaults(action=('show', analyze_show))
Packit Service a04d08
    parser_dump = subparsers.add_parser(
Packit Service a04d08
        'dump', help='Dump cloud-init events in JSON format')
Packit Service a04d08
    parser_dump.add_argument('-i', '--infile', action='store',
Packit Service a04d08
                             dest='infile', default='/var/log/cloud-init.log',
Packit Service a04d08
                             help='specify where to read input. ')
Packit Service a04d08
    parser_dump.add_argument('-o', '--outfile', action='store',
Packit Service a04d08
                             dest='outfile', default='-',
Packit Service a04d08
                             help='specify where to write output. ')
Packit Service a04d08
    parser_dump.set_defaults(action=('dump', analyze_dump))
Packit Service a04d08
    parser_boot = subparsers.add_parser(
Packit Service a04d08
        'boot', help='Print list of boot times for kernel and cloud-init')
Packit Service a04d08
    parser_boot.add_argument('-i', '--infile', action='store',
Packit Service a04d08
                             dest='infile', default='/var/log/cloud-init.log',
Packit Service a04d08
                             help='specify where to read input. ')
Packit Service a04d08
    parser_boot.add_argument('-o', '--outfile', action='store',
Packit Service a04d08
                             dest='outfile', default='-',
Packit Service a04d08
                             help='specify where to write output.')
Packit Service a04d08
    parser_boot.set_defaults(action=('boot', analyze_boot))
Packit Service a04d08
    return parser
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def analyze_boot(name, args):
Packit Service a04d08
    """Report a list of how long different boot operations took.
Packit Service a04d08
Packit Service a04d08
    For Example:
Packit Service a04d08
    -- Most Recent Boot Record --
Packit Service a04d08
        Kernel Started at: <time>
Packit Service a04d08
        Kernel ended boot at: <time>
Packit Service a04d08
        Kernel time to boot (seconds): <time>
Packit Service a04d08
        Cloud-init activated by systemd at: <time>
Packit Service a04d08
        Time between Kernel end boot and Cloud-init activation (seconds):<time>
Packit Service a04d08
        Cloud-init start: <time>
Packit Service a04d08
    """
Packit Service a04d08
    infh, outfh = configure_io(args)
Packit Service a04d08
    kernel_info = show.dist_check_timestamp()
Packit Service a04d08
    status_code, kernel_start, kernel_end, ci_sysd_start = \
Packit Service a04d08
        kernel_info
Packit Service a04d08
    kernel_start_timestamp = datetime.utcfromtimestamp(kernel_start)
Packit Service a04d08
    kernel_end_timestamp = datetime.utcfromtimestamp(kernel_end)
Packit Service a04d08
    ci_sysd_start_timestamp = datetime.utcfromtimestamp(ci_sysd_start)
Packit Service a04d08
    try:
Packit Service a04d08
        last_init_local = \
Packit Service a04d08
            [e for e in _get_events(infh) if e['name'] == 'init-local' and
Packit Service a04d08
                'starting search' in e['description']][-1]
Packit Service a04d08
        ci_start = datetime.utcfromtimestamp(last_init_local['timestamp'])
Packit Service a04d08
    except IndexError:
Packit Service a04d08
        ci_start = 'Could not find init-local log-line in cloud-init.log'
Packit Service a04d08
        status_code = show.FAIL_CODE
Packit Service a04d08
Packit Service a04d08
    FAILURE_MSG = 'Your Linux distro or container does not support this ' \
Packit Service a04d08
                  'functionality.\n' \
Packit Service a04d08
                  'You must be running a Kernel Telemetry supported ' \
Packit Service a04d08
                  'distro.\nPlease check ' \
Packit Service a04d08
                  'https://cloudinit.readthedocs.io/en/latest' \
Packit Service a04d08
                  '/topics/analyze.html for more ' \
Packit Service a04d08
                  'information on supported distros.\n'
Packit Service a04d08
Packit Service a04d08
    SUCCESS_MSG = '-- Most Recent Boot Record --\n' \
Packit Service a04d08
                  '    Kernel Started at: {k_s_t}\n' \
Packit Service a04d08
                  '    Kernel ended boot at: {k_e_t}\n' \
Packit Service a04d08
                  '    Kernel time to boot (seconds): {k_r}\n' \
Packit Service a04d08
                  '    Cloud-init activated by systemd at: {ci_sysd_t}\n' \
Packit Service a04d08
                  '    Time between Kernel end boot and Cloud-init ' \
Packit Service a04d08
                  'activation (seconds): {bt_r}\n' \
Packit Service a04d08
                  '    Cloud-init start: {ci_start}\n'
Packit Service a04d08
Packit Service a04d08
    CONTAINER_MSG = '-- Most Recent Container Boot Record --\n' \
Packit Service a04d08
                    '    Container started at: {k_s_t}\n' \
Packit Service a04d08
                    '    Cloud-init activated by systemd at: {ci_sysd_t}\n' \
Packit Service a04d08
                    '    Cloud-init start: {ci_start}\n' \
Packit Service a04d08
Packit Service a04d08
    status_map = {
Packit Service a04d08
        show.FAIL_CODE: FAILURE_MSG,
Packit Service a04d08
        show.CONTAINER_CODE: CONTAINER_MSG,
Packit Service a04d08
        show.SUCCESS_CODE: SUCCESS_MSG
Packit Service a04d08
    }
Packit Service a04d08
Packit Service a04d08
    kernel_runtime = kernel_end - kernel_start
Packit Service a04d08
    between_process_runtime = ci_sysd_start - kernel_end
Packit Service a04d08
Packit Service a04d08
    kwargs = {
Packit Service a04d08
        'k_s_t': kernel_start_timestamp,
Packit Service a04d08
        'k_e_t': kernel_end_timestamp,
Packit Service a04d08
        'k_r': kernel_runtime,
Packit Service a04d08
        'bt_r': between_process_runtime,
Packit Service a04d08
        'k_e': kernel_end,
Packit Service a04d08
        'k_s': kernel_start,
Packit Service a04d08
        'ci_sysd': ci_sysd_start,
Packit Service a04d08
        'ci_sysd_t': ci_sysd_start_timestamp,
Packit Service a04d08
        'ci_start': ci_start
Packit Service a04d08
    }
Packit Service a04d08
Packit Service a04d08
    outfh.write(status_map[status_code].format(**kwargs))
Packit Service a04d08
    return status_code
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def analyze_blame(name, args):
Packit Service a04d08
    """Report a list of records sorted by largest time delta.
Packit Service a04d08
Packit Service a04d08
    For example:
Packit Service a04d08
      30.210s (init-local) searching for datasource
Packit Service a04d08
       8.706s (init-network) reading and applying user-data
Packit Service a04d08
        166ms (modules-config) ....
Packit Service a04d08
        807us (modules-final) ...
Packit Service a04d08
Packit Service a04d08
    We generate event records parsing cloud-init logs, formatting the output
Packit Service a04d08
    and sorting by record data ('delta')
Packit Service a04d08
    """
Packit Service a04d08
    (infh, outfh) = configure_io(args)
Packit Service a04d08
    blame_format = '     %ds (%n)'
Packit Service a04d08
    r = re.compile(r'(^\s+\d+\.\d+)', re.MULTILINE)
Packit Service a04d08
    for idx, record in enumerate(show.show_events(_get_events(infh),
Packit Service a04d08
                                                  blame_format)):
Packit Service a04d08
        srecs = sorted(filter(r.match, record), reverse=True)
Packit Service a04d08
        outfh.write('-- Boot Record %02d --\n' % (idx + 1))
Packit Service a04d08
        outfh.write('\n'.join(srecs) + '\n')
Packit Service a04d08
        outfh.write('\n')
Packit Service a04d08
    outfh.write('%d boot records analyzed\n' % (idx + 1))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def analyze_show(name, args):
Packit Service a04d08
    """Generate output records using the 'standard' format to printing events.
Packit Service a04d08
Packit Service a04d08
    Example output follows:
Packit Service a04d08
        Starting stage: (init-local)
Packit Service a04d08
          ...
Packit Service a04d08
        Finished stage: (init-local) 0.105195 seconds
Packit Service a04d08
Packit Service a04d08
        Starting stage: (init-network)
Packit Service a04d08
          ...
Packit Service a04d08
        Finished stage: (init-network) 0.339024 seconds
Packit Service a04d08
Packit Service a04d08
        Starting stage: (modules-config)
Packit Service a04d08
          ...
Packit Service a04d08
        Finished stage: (modules-config) 0.NNN seconds
Packit Service a04d08
Packit Service a04d08
        Starting stage: (modules-final)
Packit Service a04d08
          ...
Packit Service a04d08
        Finished stage: (modules-final) 0.NNN seconds
Packit Service a04d08
    """
Packit Service a04d08
    (infh, outfh) = configure_io(args)
Packit Service a04d08
    for idx, record in enumerate(show.show_events(_get_events(infh),
Packit Service a04d08
                                                  args.print_format)):
Packit Service a04d08
        outfh.write('-- Boot Record %02d --\n' % (idx + 1))
Packit Service a04d08
        outfh.write('The total time elapsed since completing an event is'
Packit Service a04d08
                    ' printed after the "@" character.\n')
Packit Service a04d08
        outfh.write('The time the event takes is printed after the "+" '
Packit Service a04d08
                    'character.\n\n')
Packit Service a04d08
        outfh.write('\n'.join(record) + '\n')
Packit Service a04d08
    outfh.write('%d boot records analyzed\n' % (idx + 1))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def analyze_dump(name, args):
Packit Service a04d08
    """Dump cloud-init events in json format"""
Packit Service a04d08
    (infh, outfh) = configure_io(args)
Packit Service a04d08
    outfh.write(json_dumps(_get_events(infh)) + '\n')
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _get_events(infile):
Packit Service a04d08
    rawdata = None
Packit Service a04d08
    events, rawdata = show.load_events_infile(infile)
Packit Service a04d08
    if not events:
Packit Service a04d08
        events, _ = dump.dump_events(rawdata=rawdata)
Packit Service a04d08
    return events
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def configure_io(args):
Packit Service a04d08
    """Common parsing and setup of input/output files"""
Packit Service a04d08
    if args.infile == '-':
Packit Service a04d08
        infh = sys.stdin
Packit Service a04d08
    else:
Packit Service a04d08
        try:
Packit Service a04d08
            infh = open(args.infile, 'r')
Packit Service a04d08
        except OSError:
Packit Service a04d08
            sys.stderr.write('Cannot open file %s\n' % args.infile)
Packit Service a04d08
            sys.exit(1)
Packit Service a04d08
Packit Service a04d08
    if args.outfile == '-':
Packit Service a04d08
        outfh = sys.stdout
Packit Service a04d08
    else:
Packit Service a04d08
        try:
Packit Service a04d08
            outfh = open(args.outfile, 'w')
Packit Service a04d08
        except OSError:
Packit Service a04d08
            sys.stderr.write('Cannot open file %s\n' % args.outfile)
Packit Service a04d08
            sys.exit(1)
Packit Service a04d08
Packit Service a04d08
    return (infh, outfh)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
if __name__ == '__main__':
Packit Service a04d08
    parser = get_parser()
Packit Service a04d08
    args = parser.parse_args()
Packit Service a04d08
    (name, action_functor) = args.action
Packit Service a04d08
    action_functor(name, args)
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab