1 ##############################################################################
2 # Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com>
3 # Copyright (c) 2018 Intel Corporation.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
11 """ Handler for yardstick command 'report' """
18 from api.utils import influx
19 from oslo_utils import encodeutils
20 from oslo_utils import uuidutils
21 from yardstick.common import constants as consts
22 from yardstick.common.utils import cliargs
26 """Data structure to parse data for use with the JS library jsTree"""
28 self._created_nodes = ['#']
31 def _create_node(self, _id):
32 """Helper method for format_for_jstree to create each node.
34 Creates the node (and any required parents) and keeps track
37 :param _id: (string) id of the node to be created
40 components = _id.split(".")
42 if len(components) == 1:
47 parent_id = ".".join(components[:-1])
48 # make sure the parent has been created
49 if not parent_id in self._created_nodes:
50 self._create_node(parent_id)
52 self.jstree_data.append({"id": _id, "text": text, "parent": parent_id})
53 self._created_nodes.append(_id)
55 def format_for_jstree(self, data):
56 """Format the data into the required format for jsTree.
58 The data format expected is a list of key-value pairs which represent
59 the data and label for each metric e.g.:
61 [{'data': [0, ], 'label': 'tg__0.DropPackets'},
62 {'data': [548, ], 'label': 'tg__0.LatencyAvg.5'},]
64 This data is converted into the format required for jsTree to group and
65 display the metrics in a hierarchial fashion, including creating a
66 number of parent nodes e.g.::
68 [{"id": "tg__0", "text": "tg__0", "parent": "#"},
69 {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"},
70 {"id": "tg__0.LatencyAvg", "text": "LatencyAvg", "parent": "tg__0"},
71 {"id": "tg__0.LatencyAvg.5", "text": "5", "parent": "tg__0.LatencyAvg"},]
73 :param data: (list) data to be converted
76 self._created_nodes = ['#']
80 self._create_node(item["label"])
82 return self.jstree_data
88 Set of commands to manage reports.
96 def _validate(self, yaml_name, task_id):
97 if re.match(r"^[\w-]+$", yaml_name):
98 self.yaml_name = yaml_name
100 raise ValueError("invalid yaml_name", yaml_name)
102 if uuidutils.is_uuid_like(task_id):
103 task_id = '{' + task_id + '}'
104 task_uuid = (uuid.UUID(task_id))
105 self.task_id = task_uuid
107 raise ValueError("invalid task_id", task_id)
109 def _get_fieldkeys(self):
110 fieldkeys_cmd = "show field keys from \"%s\""
111 fieldkeys_query = fieldkeys_cmd % (self.yaml_name)
112 query_exec = influx.query(fieldkeys_query)
116 raise KeyError("Test case not found.")
118 def _get_tasks(self):
119 task_cmd = "select * from \"%s\" where task_id= '%s'"
120 task_query = task_cmd % (self.yaml_name, self.task_id)
121 query_exec = influx.query(task_query)
125 raise KeyError("Task ID or Test case not found.")
127 def _generate_common(self, args):
128 """Actions that are common to both report formats.
130 Create the necessary data structure for rendering
131 the report templates.
133 self._validate(args.yaml_name[0], args.task_id[0])
135 self.db_fieldkeys = self._get_fieldkeys()
137 self.db_task = self._get_tasks()
143 field_keys = [encodeutils.to_utf8(field['fieldKey'])
144 for field in self.db_fieldkeys]
146 for key in field_keys:
149 for task in self.db_task:
150 task_time = encodeutils.to_utf8(task['time'])
151 if not isinstance(task_time, str):
152 task_time = str(task_time, 'utf8')
153 if not isinstance(key, str):
154 key = str(key, 'utf8')
155 task_time = task_time[11:]
156 head, _, tail = task_time.partition('.')
157 task_time = head + "." + tail[:6]
158 self.Timestamp.append(task_time)
159 if task[key] is None:
161 elif isinstance(task[key], (int, float)):
162 values.append(task[key])
164 values.append(ast.literal_eval(task[key]))
165 datasets.append({'label': key, 'data': values})
166 table_vals['Timestamp'] = self.Timestamp
167 table_vals[key] = values
169 return datasets, table_vals
171 @cliargs("task_id", type=str, help=" task id", nargs=1)
172 @cliargs("yaml_name", type=str, help=" Yaml file Name", nargs=1)
173 def generate(self, args):
174 """Start report generation."""
175 datasets, table_vals = self._generate_common(args)
177 template_dir = consts.YARDSTICK_ROOT_PATH + "yardstick/common"
178 template_environment = jinja2.Environment(
180 loader=jinja2.FileSystemLoader(template_dir))
183 "datasets": datasets,
184 "Timestamps": self.Timestamp,
185 "task_id": self.task_id,
189 template_html = template_environment.get_template("report.html.j2")
191 with open(consts.DEFAULT_HTML_FILE, "w") as file_open:
192 file_open.write(template_html.render(context))
194 print("Report generated. View %s" % consts.DEFAULT_HTML_FILE)
196 @cliargs("task_id", type=str, help=" task id", nargs=1)
197 @cliargs("yaml_name", type=str, help=" Yaml file Name", nargs=1)
198 def generate_nsb(self, args):
199 """Start NSB report generation."""
200 datasets, table_vals = self._generate_common(args)
201 jstree_data = JSTree().format_for_jstree(datasets)
203 template_dir = consts.YARDSTICK_ROOT_PATH + "yardstick/common"
204 template_environment = jinja2.Environment(
206 loader=jinja2.FileSystemLoader(template_dir),
210 "Timestamps": self.Timestamp,
211 "task_id": self.task_id,
213 "jstree_nodes": jstree_data,
216 template_html = template_environment.get_template("nsb_report.html.j2")
218 with open(consts.DEFAULT_HTML_FILE, "w") as file_open:
219 file_open.write(template_html.render(context))
221 print("Report generated. View %s" % consts.DEFAULT_HTML_FILE)