Andrey Kurilin 33eb9ffee2 Implement storing osprofiler reports separately from rally report
Change-Id: I8c95e7f433a6f22ae818a78ede4b12bb84cf1f5d
2019-05-17 19:40:55 -07:00

163 lines
5.8 KiB
Python

# 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
import rally_openstack
if rally_openstack.__rally_version__ < (1, 5, 0):
# NOTE(andreykurilin): this is a workaround to make inheritance of
# OSProfilerChart clear.
OutputEmbeddedChart = type("OutputEmbeddedChart", (object, ), {})
OutputEmbeddedExternalChart = type("OutputEmbeddedExternalChart",
(object, ), {})
else:
OutputEmbeddedChart = charts.OutputEmbeddedChart
OutputEmbeddedExternalChart = charts.OutputEmbeddedExternalChart
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(OutputEmbeddedChart,
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())
try:
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 rally_openstack.__rally_version__ < (1, 5, 0):
return {
"title": title,
"widget": "EmbeddedChart",
"data": osp_report.replace("/script>", "\\/script>")
}
elif (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 OutputEmbeddedExternalChart.render_complete_data(
{
"title": title,
"widget": "EmbeddedChart",
"data": path
}
)
else:
return OutputEmbeddedChart.render_complete_data(
{"title": title,
"widget": "EmbeddedChart",
"data": osp_report}
)
return cls._return_raw_response_for_complete_data(data)