File: osprofilerchart.py

package info (click to toggle)
rally-openstack 3.0.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,968 kB
  • sloc: python: 53,131; sh: 262; makefile: 38
file content (143 lines) | stat: -rw-r--r-- 5,199 bytes parent folder | download | duplicates (4)
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
139
140
141
142
143
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import json
import os

from rally.common import cfg
from rally.common import logging
from rally.common import opts
from rally.common.plugin import plugin
from rally.task.processing import charts


OPTS = {
    "openstack": [
        cfg.StrOpt(
            "osprofiler_chart_mode",
            default=None,
            help="Mode of embedding OSProfiler's chart. Can be 'text' "
                 "(embed only trace id), 'raw' (embed raw osprofiler's native "
                 "report) or a path to directory (raw osprofiler's native "
                 "reports for each iteration will be saved separately there "
                 "to decrease the size of rally report itself)")
    ]
}


LOG = logging.getLogger(__name__)
CONF = cfg.CONF


def _datetime_json_serialize(obj):
    if hasattr(obj, "isoformat"):
        return obj.isoformat()
    else:
        return obj


@plugin.configure(name="OSProfiler")
class OSProfilerChart(charts.OutputEmbeddedChart,
                      charts.OutputEmbeddedExternalChart,
                      charts.OutputTextArea):
    """Chart for embedding OSProfiler data."""

    @classmethod
    def _fetch_osprofiler_data(cls, connection_str, trace_id):
        from osprofiler.drivers import base
        from osprofiler import opts as osprofiler_opts

        opts.register_opts(osprofiler_opts.list_opts())  # noqa

        try:  # noqa
            engine = base.get_driver(connection_str)
        except Exception:
            msg = "Error while fetching OSProfiler results."
            if logging.is_debug():
                LOG.exception(msg)
            else:
                LOG.error(msg)
            return None

        return engine.get_report(trace_id)

    @classmethod
    def _generate_osprofiler_report(cls, osp_data):
        from osprofiler import cmd

        path = "%s/template.html" % os.path.dirname(cmd.__file__)
        with open(path) as f:
            html_obj = f.read()

        osp_data = json.dumps(osp_data,
                              indent=4,
                              separators=(",", ": "),
                              default=_datetime_json_serialize)
        return html_obj.replace("$DATA", osp_data).replace("$LOCAL", "false")

    @classmethod
    def _return_raw_response_for_complete_data(cls, data):
        return charts.OutputTextArea.render_complete_data({
            "title": data["title"],
            "widget": "TextArea",
            "data": [data["data"]["trace_id"]]
        })

    @classmethod
    def render_complete_data(cls, data):
        mode = CONF.openstack.osprofiler_chart_mode

        if isinstance(data["data"]["trace_id"], list):
            # NOTE(andreykurilin): it is an adoption for the format that we
            #   used  before rally-openstack 1.5.0 .
            data["data"]["trace_id"] = data["data"]["trace_id"][0]

        if data["data"].get("conn_str") and mode != "text":
            osp_data = cls._fetch_osprofiler_data(
                data["data"]["conn_str"],
                trace_id=data["data"]["trace_id"]
            )
            if not osp_data:
                # for some reasons we failed to fetch data from OSProfiler's
                # backend. in this case we can display just trace ID
                return cls._return_raw_response_for_complete_data(data)

            osp_report = cls._generate_osprofiler_report(osp_data)
            title = "{0} : {1}".format(data["title"],
                                       data["data"]["trace_id"])

            if (mode and mode != "raw") and "workload_uuid" in data["data"]:
                # NOTE(andreykurilin): we need to rework our charts plugin
                #   mechanism so it is available out of box
                workload_uuid = data["data"]["workload_uuid"]
                iteration = data["data"]["iteration"]
                file_name = "w_%s-%s.html" % (workload_uuid, iteration)
                path = os.path.join(mode, file_name)
                with open(path, "w") as f:
                    f.write(osp_report)
                return charts.OutputEmbeddedExternalChart.render_complete_data(
                    {
                        "title": title,
                        "widget": "EmbeddedChart",
                        "data": path
                    }
                )
            else:
                return charts.OutputEmbeddedChart.render_complete_data(
                    {"title": title,
                     "widget": "EmbeddedChart",
                     "data": osp_report}
                )

        return cls._return_raw_response_for_complete_data(data)