summaryrefslogtreecommitdiffstats
path: root/tools/bcfg2-profile-templates.py
blob: 2b0ca6d63d26eac50e562066e09175cecca02e07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/python -Ott
# -*- coding: utf-8 -*-
""" Benchmark template rendering times """

import sys
import time
import math
import signal
import logging
import operator
import Bcfg2.Logger
import Bcfg2.Options
import Bcfg2.Server.Core


def stdev(nums):
    mean = float(sum(nums)) / len(nums)
    return math.sqrt(sum((n - mean)**2 for n in nums) / float(len(nums)))


def get_sigint_handler(core):
    """ Get a function that handles SIGINT/Ctrl-C by shutting down the
    core and exiting properly."""

    def hdlr(sig, frame):  # pylint: disable=W0613
        """ Handle SIGINT/Ctrl-C by shutting down the core and exiting
        properly. """
        core.shutdown()
        os._exit(1)  # pylint: disable=W0212

    return hdlr


def main():
    optinfo = dict(
        client=Bcfg2.Options.Option("Benchmark templates for one client",
                                    cmd="--client",
                                    odesc="<client>",
                                    long_arg=True,
                                    default=None),
        runs=Bcfg2.Options.Option("Number of rendering passes per template",
                                  cmd="--runs",
                                  odesc="<runs>",
                                  long_arg=True,
                                  default=5,
                                  cook=int))
    optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
    optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
    setup = Bcfg2.Options.OptionParser(optinfo)
    setup.parse(sys.argv[1:])

    if setup['debug']:
        level = logging.DEBUG
    elif setup['verbose']:
        level = logging.INFO
    else:
        level = logging.WARNING
    Bcfg2.Logger.setup_logging("bcfg2-test",
                               to_console=setup['verbose'] or setup['debug'],
                               to_syslog=False,
                               to_file=setup['logging'],
                               level=level)
    logger = logging.getLogger(sys.argv[0])

    core = Bcfg2.Server.Core.BaseCore(setup)
    signal.signal(signal.SIGINT, get_sigint_handler(core))
    logger.info("Bcfg2 server core loaded")
    core.load_plugins()
    logger.debug("Plugins loaded")
    core.block_for_fam_events(handle_events=True)
    logger.debug("Repository events processed")

    if setup['args']:
        templates = setup['args']
    else:
        templates = []

    if setup['client'] is None:
        clients = [core.build_metadata(c) for c in core.metadata.clients]
    else:
        clients = [core.build_metadata(setup['client'])]

    times = dict()
    client_count = 0
    for metadata in clients:
        client_count += 1
        logger.info("Rendering templates for client %s (%s/%s)" %
                    (metadata.hostname, client_count, len(clients)))
        structs = core.GetStructures(metadata)
        struct_count = 0
        for struct in structs:
            struct_count += 1
            logger.info("Rendering templates from structure %s:%s (%s/%s)" %
                        (struct.tag, struct.get("name"), struct_count,
                         len(structs)))
            entries = struct.xpath("//Path")
            entry_count = 0
            for entry in entries:
                entry_count += 1
                if templates and entry.get("name") not in templates:
                    continue
                logger.info("Rendering Path:%s (%s/%s)..." %
                            (entry.get("name"), entry_count, len(entries)))
                ptimes = times.setdefault(entry.get("name"), [])
                for i in range(setup['runs']):
                    start = time.time()
                    try:
                        core.Bind(entry, metadata)
                        ptimes.append(time.time() - start)
                    except:
                        break
                if ptimes:
                    avg = sum(ptimes) / len(ptimes)
                    if avg:
                        logger.debug("   %s: %.02f sec" %
                                     (metadata.hostname, avg))

    # print out per-file results
    tmpltimes = []
    for tmpl, ptimes in times.items():
        try:
            mean = float(sum(ptimes)) / len(ptimes)
        except ZeroDivisionError:
            continue
        ptimes.sort()
        median = ptimes[len(ptimes) / 2]
        std = stdev(ptimes)
        if mean > 0.01 or median > 0.01 or std > 1 or templates:
            tmpltimes.append((tmpl, mean, median, std))
    print("%-50s %-9s  %-11s  %6s" %
          ("Template", "Mean Time", "Median Time", "σ"))
    for info in reversed(sorted(tmpltimes, key=operator.itemgetter(1))):
        print("%-50s %9.02f  %11.02f  %6.02f" % info)
    core.shutdown()


if __name__ == "__main__":
    sys.exit(main())