mirror of
https://github.com/ceph/s3-tests.git
synced 2024-12-25 23:43:50 +00:00
143 lines
4.5 KiB
Python
143 lines
4.5 KiB
Python
|
#!/usr/bin/python
|
||
|
import sys
|
||
|
import os
|
||
|
import yaml
|
||
|
import optparse
|
||
|
|
||
|
NANOSECONDS = int(1e9)
|
||
|
|
||
|
# Output stats in a format similar to siege
|
||
|
# see http://www.joedog.org/index/siege-home
|
||
|
OUTPUT_FORMAT = """Stats for type: [{type}]
|
||
|
Transactions: {trans:>11} hits
|
||
|
Availability: {avail:>11.2f} %
|
||
|
Elapsed time: {elapsed:>11.2f} secs
|
||
|
Data transferred: {data:>11.2f} MB
|
||
|
Response time: {resp_time:>11.2f} secs
|
||
|
Transaction rate: {trans_rate:>11.2f} trans/sec
|
||
|
Throughput: {data_rate:>11.2f} MB/sec
|
||
|
Concurrency: {conc:>11.2f}
|
||
|
Successful transactions: {trans_success:>11}
|
||
|
Failed transactions: {trans_fail:>11}
|
||
|
Longest transaction: {trans_long:>11.2f}
|
||
|
Shortest transaction: {trans_short:>11.2f}
|
||
|
"""
|
||
|
|
||
|
def parse_options():
|
||
|
usage = "usage: %prog [options]"
|
||
|
parser = optparse.OptionParser(usage=usage)
|
||
|
parser.add_option(
|
||
|
"-f", "--file", dest="input", metavar="FILE",
|
||
|
help="Name of input YAML file. Default uses sys.stdin")
|
||
|
parser.add_option(
|
||
|
"-v", "--verbose", dest="verbose", action="store_true",
|
||
|
help="Enable verbose output")
|
||
|
|
||
|
(options, args) = parser.parse_args()
|
||
|
|
||
|
if not options.input and os.isatty(sys.stdin.fileno()):
|
||
|
parser.error("option -f required if no data is provided "
|
||
|
"in stdin")
|
||
|
|
||
|
return (options, args)
|
||
|
|
||
|
def main():
|
||
|
(options, args) = parse_options()
|
||
|
|
||
|
total = {}
|
||
|
durations = {}
|
||
|
min_time = {}
|
||
|
max_time = {}
|
||
|
errors = {}
|
||
|
success = {}
|
||
|
|
||
|
calculate_stats(options, total, durations, min_time, max_time, errors,
|
||
|
success)
|
||
|
print_results(total, durations, min_time, max_time, errors, success)
|
||
|
|
||
|
def calculate_stats(options, total, durations, min_time, max_time, errors,
|
||
|
success):
|
||
|
print 'Calculating statistics...'
|
||
|
|
||
|
f = sys.stdin
|
||
|
if options.input:
|
||
|
f = file(options.input, 'r')
|
||
|
|
||
|
for item in yaml.safe_load_all(f):
|
||
|
type_ = item.get('type')
|
||
|
if type_ not in ('r', 'w'):
|
||
|
continue # ignore any invalid items
|
||
|
|
||
|
if 'error' in item:
|
||
|
errors[type_] = errors.get(type_, 0) + 1
|
||
|
continue # skip rest of analysis for this item
|
||
|
else:
|
||
|
success[type_] = success.get(type_, 0) + 1
|
||
|
|
||
|
# parse the item
|
||
|
data_size = item['chunks'][-1][0]
|
||
|
duration = item['duration']
|
||
|
start = item['start']
|
||
|
end = start + duration / float(NANOSECONDS)
|
||
|
|
||
|
if options.verbose:
|
||
|
print "[{type}] POSIX time: {start:>18.2f} - {end:<18.2f} " \
|
||
|
"{data:>11.2f} KB".format(
|
||
|
type=type_,
|
||
|
start=start,
|
||
|
end=end,
|
||
|
data=data_size / 1024.0, # convert to KB
|
||
|
)
|
||
|
|
||
|
# update time boundaries
|
||
|
prev = min_time.setdefault(type_, start)
|
||
|
if start < prev:
|
||
|
min_time[type_] = start
|
||
|
prev = max_time.setdefault(type_, end)
|
||
|
if end > prev:
|
||
|
max_time[type_] = end
|
||
|
|
||
|
# save the duration
|
||
|
if type_ not in durations:
|
||
|
durations[type_] = []
|
||
|
durations[type_].append(duration)
|
||
|
|
||
|
# add to running totals
|
||
|
total[type_] = total.get(type_, 0) + data_size
|
||
|
|
||
|
def print_results(total, durations, min_time, max_time, errors, success):
|
||
|
for type_ in total.keys():
|
||
|
trans_success = success.get(type_, 0)
|
||
|
trans_fail = errors.get(type_, 0)
|
||
|
trans = trans_success + trans_fail
|
||
|
avail = trans_success * 100.0 / trans
|
||
|
elapsed = max_time[type_] - min_time[type_]
|
||
|
data = total[type_] / 1024.0 / 1024.0 # convert to MB
|
||
|
resp_time = sum(durations[type_]) / float(NANOSECONDS) / \
|
||
|
len(durations[type_])
|
||
|
trans_rate = trans / elapsed
|
||
|
data_rate = data / elapsed
|
||
|
conc = trans_rate * resp_time
|
||
|
trans_long = max(durations[type_]) / float(NANOSECONDS)
|
||
|
trans_short = min(durations[type_]) / float(NANOSECONDS)
|
||
|
|
||
|
print OUTPUT_FORMAT.format(
|
||
|
type=type_,
|
||
|
trans_success=trans_success,
|
||
|
trans_fail=trans_fail,
|
||
|
trans=trans,
|
||
|
avail=avail,
|
||
|
elapsed=elapsed,
|
||
|
data=data,
|
||
|
resp_time=resp_time,
|
||
|
trans_rate=trans_rate,
|
||
|
data_rate=data_rate,
|
||
|
conc=conc,
|
||
|
trans_long=trans_long,
|
||
|
trans_short=trans_short,
|
||
|
)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|
||
|
|