1eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#!/usr/bin/python
2eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
3eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# steadystate_tests.py
4eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
5eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# Test option parsing and functonality for fio's steady state detection feature.
6eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
7eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# steadystate_tests.py ./fio file-for-read-testing file-for-write-testing
8eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
9eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# REQUIREMENTS
10eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# Python 2.6+
11eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# SciPy
12eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
13eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# KNOWN ISSUES
14eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# only option parsing and read tests are carried out
15eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# On Windows this script works under Cygwin but not from cmd.exe
16eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# On Windows I encounter frequent fio problems generating JSON output (nothing to decode)
17eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# min runtime:
18eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# if ss attained: min runtime = ss_dur + ss_ramp
19eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# if not attained: runtime = timeout
20eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
21eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport os
22eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport sys
23eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport json
24eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport uuid
25eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport pprint
26eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport argparse
27eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesimport subprocess
28eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesfrom scipy import stats
29eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
30eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesdef parse_args():
31eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    parser = argparse.ArgumentParser()
32eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    parser.add_argument('fio',
33eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        help='path to fio executable');
34eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    parser.add_argument('--read',
35eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        help='target for read testing')
36eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    parser.add_argument('--write',
37eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        help='target for write testing')
38eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    args = parser.parse_args()
39eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
40eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    return args
41eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
42eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
43eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesdef check(data, iops, slope, pct, limit, dur, criterion):
44eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    measurement = 'iops' if iops else 'bw'
45eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    data = data[measurement]
46eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    mean = sum(data) / len(data)
47eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    if slope:
48eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        x = range(len(data))
49eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        m, intercept, r_value, p_value, std_err = stats.linregress(x,data)
50eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        m = abs(m)
51eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        if pct:
52eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            target = m / mean * 100
53eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            criterion = criterion[:-1]
54eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        else:
55eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            target = m
56eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    else:
57eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        maxdev = 0
58eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        for x in data:
59eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            maxdev = max(abs(mean-x), maxdev)
60eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        if pct:
61eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            target = maxdev / mean * 100
62eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            criterion = criterion[:-1]
63eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        else:
64eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            target = maxdev
65eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
66eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    criterion = float(criterion)
67eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    return (abs(target - criterion) / criterion < 0.005), target < limit, mean, target
68eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
69eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
70eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughesif __name__ == '__main__':
71eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    args = parse_args()
72eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
73eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    pp = pprint.PrettyPrinter(indent=4)
74eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
75eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
76eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# test option parsing
77eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
78eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    parsing = [ { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:10", "--ss_ramp=5"],
79eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state IOPS threshold to 10.000000" },
80eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:10%", "--ss_ramp=5"],
81eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state threshold to 10.000000%" },
82eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:.1%", "--ss_ramp=5"],
83eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state threshold to 0.100000%" },
84eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:10%", "--ss_ramp=5"],
85eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state threshold to 10.000000%" },
86eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:.1%", "--ss_ramp=5"],
87eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state threshold to 0.100000%" },
88eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:12", "--ss_ramp=5"],
89eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                  'output': "set steady state BW threshold to 12" },
90eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes              ]
91eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    for test in parsing:
92eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        output = subprocess.check_output([args.fio] + test['args']);
93eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        if test['output'] in output:
94eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            print "PASSED '{0}' found with arguments {1}".format(test['output'], test['args'])
95eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        else:
96eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            print "FAILED '{0}' NOT found with arguments {1}".format(test['output'], test['args'])
97eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
98eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
99eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# test some read workloads
100eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
101eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# if ss active and attained,
102eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#   check that runtime is less than job time
103eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#   check criteria
104eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#   how to check ramp time?
105eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
106eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes# if ss inactive
107eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#   check that runtime is what was specified
108eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes#
109eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    reads = [ {'s': True, 'timeout': 100, 'numjobs': 1, 'ss_dur': 5, 'ss_ramp': 3, 'iops': True, 'slope': True, 'ss_limit': 0.1, 'pct': True},
110eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes              {'s': False, 'timeout': 20, 'numjobs': 2},
111eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes              {'s': True, 'timeout': 100, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 5, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
112eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes              {'s': True, 'timeout': 10, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 500, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
113eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            ]
114eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
115eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    if args.read == None:
116eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        if os.name == 'posix':
117eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            args.read = '/dev/zero'
118eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            extra = [ "--size=134217728" ]  # 128 MiB
119eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        else:
120eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            print "ERROR: file for read testing must be specified on non-posix systems"
121eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            sys.exit(1)
122eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    else:
123eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        extra = []
124eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
125eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    jobnum = 0
126eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes    for job in reads:
127eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
128eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        tf = uuid.uuid4().hex
129eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        parameters = [ "--name=job{0}".format(jobnum) ]
130eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        parameters.extend(extra)
131eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        parameters.extend([ "--thread",
132eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--output-format=json",
133eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--output={0}".format(tf),
134eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--filename={0}".format(args.read),
135eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--rw=randrw",
136eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--rwmixread=100",
137eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--stonewall",
138eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--group_reporting",
139eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--numjobs={0}".format(job['numjobs']),
140eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--time_based",
141eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            "--runtime={0}".format(job['timeout']) ])
142eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        if job['s']:
143eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           if job['iops']:
144eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes               ss = 'iops'
145eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           else:
146eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes               ss = 'bw'
147eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           if job['slope']:
148eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes               ss += "_slope"
149eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           ss += ":" + str(job['ss_limit'])
150eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           if job['pct']:
151eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes               ss += '%'
152eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes           parameters.extend([ '--ss_dur={0}'.format(job['ss_dur']),
153eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                               '--ss={0}'.format(ss),
154eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                               '--ss_ramp={0}'.format(job['ss_ramp']) ])
155eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
156eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        output = subprocess.call([args.fio] + parameters)
157eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        with open(tf, 'r') as source:
158eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            jsondata = json.loads(source.read())
159eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        os.remove(tf)
160eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes
161eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        for jsonjob in jsondata['jobs']:
162eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            line = "job {0}".format(jsonjob['job options']['name'])
163eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            if job['s']:
164eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                if jsonjob['steadystate']['attained'] == 1:
165eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    # check runtime >= ss_dur + ss_ramp, check criterion, check criterion < limit
166eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    mintime = (job['ss_dur'] + job['ss_ramp']) * 1000
167eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    actual = jsonjob['read']['runtime']
168eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    if mintime > actual:
169eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        line = 'FAILED ' + line + ' ss attained, runtime {0} < ss_dur {1} + ss_ramp {2}'.format(actual, job['ss_dur'], job['ss_ramp'])
170eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    else:
171eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        line = line + ' ss attained, runtime {0} > ss_dur {1} + ss_ramp {2},'.format(actual, job['ss_dur'], job['ss_ramp'])
172eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        objsame, met, mean, target = check(data=jsonjob['steadystate']['data'],
173eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            iops=job['iops'],
174eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            slope=job['slope'],
175eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            pct=job['pct'],
176eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            limit=job['ss_limit'],
177eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            dur=job['ss_dur'],
178eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            criterion=jsonjob['steadystate']['criterion'])
179eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        if not objsame:
180eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            line = 'FAILED ' + line + ' fio criterion {0} != calculated criterion {1} '.format(jsonjob['steadystate']['criterion'], target)
181eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        else:
182eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            if met:
183eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'PASSED ' + line + ' target {0} < limit {1}'.format(target, job['ss_limit'])
184eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            else:
185eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'FAILED ' + line + ' target {0} < limit {1} but fio reports ss not attained '.format(target, job['ss_limit'])
186eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                else:
187eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    # check runtime, confirm criterion calculation, and confirm that criterion was not met
188eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    expected = job['timeout'] * 1000
189eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    actual = jsonjob['read']['runtime']
190eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    if abs(expected - actual) > 10:
191eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        line = 'FAILED ' + line + ' ss not attained, expected runtime {0} != actual runtime {1}'.format(expected, actual)
192eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    else:
193eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        line = line + ' ss not attained, runtime {0} != ss_dur {1} + ss_ramp {2},'.format(actual, job['ss_dur'], job['ss_ramp'])
194eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        objsame, met, mean, target = check(data=jsonjob['steadystate']['data'],
195eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            iops=job['iops'],
196eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            slope=job['slope'],
197eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            pct=job['pct'],
198eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            limit=job['ss_limit'],
199eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            dur=job['ss_dur'],
200eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            criterion=jsonjob['steadystate']['criterion'])
201eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        if not objsame:
202eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            if actual > (job['ss_dur'] + job['ss_ramp'])*1000:
203eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'FAILED ' + line + ' fio criterion {0} != calculated criterion {1} '.format(jsonjob['steadystate']['criterion'], target)
204eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            else:
205eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'PASSED ' + line + ' fio criterion {0} == 0.0 since ss_dur + ss_ramp has not elapsed '.format(jsonjob['steadystate']['criterion'])
206eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                        else:
207eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            if met:
208eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'FAILED ' + line + ' target {0} < threshold {1} but fio reports ss not attained '.format(target, job['ss_limit'])
209eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                            else:
210eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                                line = 'PASSED ' + line + ' criterion {0} > threshold {1}'.format(target, job['ss_limit'])
211eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            else:
212eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                expected = job['timeout'] * 1000
213eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                actual = jsonjob['read']['runtime']
214eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                if abs(expected - actual) < 10:
215eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    result = 'PASSED '
216eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                else:
217eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                    result = 'FAILED '
218eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                line = result + line + ' no ss, expected runtime {0} ~= actual runtime {1}'.format(expected, actual)
219eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            print line
220eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes            if 'steadystate' in jsonjob:
221eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes                pp.pprint(jsonjob['steadystate'])
222eda3a60699e1d96bb68875ef2169ca819eb8f4f9Elliott Hughes        jobnum += 1
223