/usr/lib/python2.7/dist-packages/funkload/MergeResultFiles.py is in funkload 1.17.1-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 144 145 146 | # (C) Copyright 2010 Nuxeo SAS <http://nuxeo.com>
# Author: bdelbosc@nuxeo.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
"""Merge FunkLoad result files to produce a report for distributed bench
reports."""
from __future__ import print_function
from __future__ import absolute_import
import xml.parsers.expat
from .utils import trace
class EndOfConfig(Exception):
pass
class FunkLoadConfigXmlParser:
"""Parse the config part of a funkload xml results file."""
def __init__(self):
"""Init setup expat handlers."""
self.current_element = [{'name': 'root'}]
self.cycles = None
self.cycle_duration = 0
self.nodes = {}
self.config = {}
self.files = []
self.current_file = None
def parse(self, xml_file):
"""Do the parsing."""
self.current_file = xml_file
parser = xml.parsers.expat.ParserCreate()
parser.StartElementHandler = self.handleStartElement
try:
parser.ParseFile(file(xml_file))
except xml.parsers.expat.ExpatError as msg:
if (self.current_element[-1]['name'] == 'funkload'
and str(msg).startswith('no element found')):
print("Missing </funkload> tag.")
else:
print('Error: invalid xml bench result file')
if len(self.current_element) <= 1 or (
self.current_element[1]['name'] != 'funkload'):
print("""Note that you can generate a report only for a
bench result done with fl-run-bench (and not on a test
result done with fl-run-test).""")
else:
print("""You may need to remove non ascii char that comes
from error pages catched during the bench. iconv
or recode may help you.""")
print('Xml parser element stack: %s' % [
x['name'] for x in self.current_element])
raise
except EndOfConfig:
return
def handleStartElement(self, name, attrs):
"""Called by expat parser on start element."""
if name == 'funkload':
self.config['version'] = attrs['version']
self.config['time'] = attrs['time']
elif name == 'config':
self.config[attrs['key']] = attrs['value']
if attrs['key'] == 'duration':
if self.cycle_duration and attrs['value'] != self.cycle_duration:
trace('Skipping file %s with different cycle duration %s' % (self.current_file, attrs['value']))
raise EndOfConfig
self.cycle_duration = attrs['value']
elif attrs['key'] == 'cycles':
if self.cycles and attrs['value'] != self.cycles:
trace('Skipping file %s with different cycles %s != %s' % (self.current_file, attrs['value'], self.cycles))
raise EndOfConfig
self.cycles = attrs['value']
elif attrs['key'] == 'node':
self.nodes[self.current_file] = attrs['value']
else:
self.files.append(self.current_file)
raise EndOfConfig
def replace_all(text, dic):
for i, j in dic.iteritems():
if isinstance(text, str):
text = text.decode('utf-8', 'ignore')
text = text.replace(i, j)
return text.encode('utf-8')
class MergeResultFiles:
def __init__(self, input_files, output_file):
xml_parser = FunkLoadConfigXmlParser()
for input_file in input_files:
trace (".")
xml_parser.parse(input_file)
node_count = len(xml_parser.files)
# compute cumulated cycles
node_cycles = [int(item) for item in xml_parser.cycles[1:-1].split(',')]
cycles = map(lambda x: x * node_count, node_cycles)
# node names
node_names = []
i = 0
for input_file in xml_parser.files:
node_names.append(xml_parser.nodes.get(input_file, 'node-' + str(i)))
i += 1
trace("\nnodes: %s\n" % ', '.join(node_names))
trace("cycles for a node: %s\n" % node_cycles)
trace("cycles for all nodes: %s\n" % cycles)
output = open(output_file, 'w+')
i = 0
for input_file in xml_parser.files:
dic = {xml_parser.cycles: str(cycles),
'host="localhost"': 'host="%s"' % node_names[i],
'thread="': 'thread="' + str(i)}
if i == 0:
dic['key="node" value="%s"' % node_names[0]] = 'key="node" value="%s"' % (
', '.join(node_names))
c = 0
for cycle in node_cycles:
dic['cycle="%3.3i" cvus="%3.3i"' % (c, node_cycles[c])] = 'cycle="%3.3i" cvus="%3.3i"' % (c, cycles[c])
c += 1
f = open(input_file)
for line in f:
if "</funkload>" in line:
continue
elif i > 0 and ('<funkload' in line or '<config' in line):
continue
output.write(replace_all(line, dic))
f.close()
i += 1
output.write("</funkload>\n")
output.close()
|