Blame t/steadystate_tests.py

Packit Service 0d30d9
#! /usr/libexec/platform-python
Packit Service 0e769b
# Note: this script is python2 and python3 compatible.
Packit Service 0e769b
#
Packit Service 0e769b
# steadystate_tests.py
Packit Service 0e769b
#
Packit Service 0e769b
# Test option parsing and functonality for fio's steady state detection feature.
Packit Service 0e769b
#
Packit Service 0e769b
# steadystate_tests.py --read file-for-read-testing --write file-for-write-testing ./fio
Packit Service 0e769b
#
Packit Service 0e769b
# REQUIREMENTS
Packit Service 0e769b
# Python 2.6+
Packit Service 0e769b
# SciPy
Packit Service 0e769b
#
Packit Service 0e769b
# KNOWN ISSUES
Packit Service 0e769b
# only option parsing and read tests are carried out
Packit Service 0e769b
# On Windows this script works under Cygwin but not from cmd.exe
Packit Service 0e769b
# On Windows I encounter frequent fio problems generating JSON output (nothing to decode)
Packit Service 0e769b
# min runtime:
Packit Service 0e769b
# if ss attained: min runtime = ss_dur + ss_ramp
Packit Service 0e769b
# if not attained: runtime = timeout
Packit Service 0e769b
Packit Service 0e769b
from __future__ import absolute_import
Packit Service 0e769b
from __future__ import print_function
Packit Service 0e769b
import os
Packit Service 0e769b
import sys
Packit Service 0e769b
import json
Packit Service 0e769b
import pprint
Packit Service 0e769b
import argparse
Packit Service 0e769b
import subprocess
Packit Service 0e769b
from scipy import stats
Packit Service 0e769b
Packit Service 0e769b
def parse_args():
Packit Service 0e769b
    parser = argparse.ArgumentParser()
Packit Service 0e769b
    parser.add_argument('fio', help='path to fio executable')
Packit Service 0e769b
    args = parser.parse_args()
Packit Service 0e769b
Packit Service 0e769b
    return args
Packit Service 0e769b
Packit Service 0e769b
Packit Service 0e769b
def check(data, iops, slope, pct, limit, dur, criterion):
Packit Service 0e769b
    measurement = 'iops' if iops else 'bw'
Packit Service 0e769b
    data = data[measurement]
Packit Service 0e769b
    mean = sum(data) / len(data)
Packit Service 0e769b
    if slope:
Packit Service 0e769b
        x = list(range(len(data)))
Packit Service 0e769b
        m, intercept, r_value, p_value, std_err = stats.linregress(x,data)
Packit Service 0e769b
        m = abs(m)
Packit Service 0e769b
        if pct:
Packit Service 0e769b
            target = (m / mean * 100) if mean != 0 else 0
Packit Service 0e769b
            criterion = criterion[:-1]
Packit Service 0e769b
        else:
Packit Service 0e769b
            target = m
Packit Service 0e769b
    else:
Packit Service 0e769b
        maxdev = 0
Packit Service 0e769b
        for x in data:
Packit Service 0e769b
            maxdev = max(abs(mean-x), maxdev)
Packit Service 0e769b
        if pct:
Packit Service 0e769b
            target = maxdev / mean * 100
Packit Service 0e769b
            criterion = criterion[:-1]
Packit Service 0e769b
        else:
Packit Service 0e769b
            target = maxdev
Packit Service 0e769b
Packit Service 0e769b
    criterion = float(criterion)
Packit Service 0e769b
    if criterion == 0.0:
Packit Service 0e769b
        objsame = False
Packit Service 0e769b
    else:
Packit Service 0e769b
        objsame = abs(target - criterion) / criterion < 0.005
Packit Service 0e769b
    return (objsame, target < limit, mean, target)
Packit Service 0e769b
Packit Service 0e769b
Packit Service 0e769b
if __name__ == '__main__':
Packit Service 0e769b
    args = parse_args()
Packit Service 0e769b
Packit Service 0e769b
    pp = pprint.PrettyPrinter(indent=4)
Packit Service 0e769b
Packit Service 0e769b
    passed = 0
Packit Service 0e769b
    failed = 0
Packit Service 0e769b
Packit Service 0e769b
#
Packit Service 0e769b
# test option parsing
Packit Service 0e769b
#
Packit Service 0e769b
    parsing = [ { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:10", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state IOPS threshold to 10.000000" },
Packit Service 0e769b
                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:10%", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state threshold to 10.000000%" },
Packit Service 0e769b
                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=iops:.1%", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state threshold to 0.100000%" },
Packit Service 0e769b
                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:10%", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state threshold to 10.000000%" },
Packit Service 0e769b
                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:.1%", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state threshold to 0.100000%" },
Packit Service 0e769b
                { 'args': ["--parse-only", "--debug=parse", "--ss_dur=10s", "--ss=bw:12", "--ss_ramp=5"],
Packit Service 0e769b
                  'output': "set steady state BW threshold to 12" },
Packit Service 0e769b
              ]
Packit Service 0e769b
    for test in parsing:
Packit Service 0e769b
        output = subprocess.check_output([args.fio] + test['args'])
Packit Service 0e769b
        if test['output'] in output.decode():
Packit Service 0e769b
            print("PASSED '{0}' found with arguments {1}".format(test['output'], test['args']))
Packit Service 0e769b
            passed = passed + 1
Packit Service 0e769b
        else:
Packit Service 0e769b
            print("FAILED '{0}' NOT found with arguments {1}".format(test['output'], test['args']))
Packit Service 0e769b
            failed = failed + 1
Packit Service 0e769b
Packit Service 0e769b
#
Packit Service 0e769b
# test some read workloads
Packit Service 0e769b
#
Packit Service 0e769b
# if ss active and attained,
Packit Service 0e769b
#   check that runtime is less than job time
Packit Service 0e769b
#   check criteria
Packit Service 0e769b
#   how to check ramp time?
Packit Service 0e769b
#
Packit Service 0e769b
# if ss inactive
Packit Service 0e769b
#   check that runtime is what was specified
Packit Service 0e769b
#
Packit Service 0e769b
    reads = [ {'s': True, 'timeout': 100, 'numjobs': 1, 'ss_dur': 5, 'ss_ramp': 3, 'iops': True, 'slope': True, 'ss_limit': 0.1, 'pct': True},
Packit Service 0e769b
              {'s': False, 'timeout': 20, 'numjobs': 2},
Packit Service 0e769b
              {'s': True, 'timeout': 100, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 5, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
Packit Service 0e769b
              {'s': True, 'timeout': 10, 'numjobs': 3, 'ss_dur': 10, 'ss_ramp': 500, 'iops': False, 'slope': True, 'ss_limit': 0.1, 'pct': True},
Packit Service 0e769b
            ]
Packit Service 0e769b
Packit Service 0e769b
    jobnum = 0
Packit Service 0e769b
    for job in reads:
Packit Service 0e769b
Packit Service 0e769b
        tf = "steadystate_job{0}.json".format(jobnum)
Packit Service 0e769b
        parameters = [ "--name=job{0}".format(jobnum) ]
Packit Service 0e769b
        parameters.extend([ "--thread",
Packit Service 0e769b
                            "--output-format=json",
Packit Service 0e769b
                            "--output={0}".format(tf),
Packit Service 0e769b
                            "--ioengine=null",
Packit Service 0e769b
                            "--size=1G",
Packit Service 0e769b
                            "--rw=randrw",
Packit Service 0e769b
                            "--rwmixread=100",
Packit Service 0e769b
                            "--stonewall",
Packit Service 0e769b
                            "--group_reporting",
Packit Service 0e769b
                            "--numjobs={0}".format(job['numjobs']),
Packit Service 0e769b
                            "--time_based",
Packit Service 0e769b
                            "--runtime={0}".format(job['timeout']) ])
Packit Service 0e769b
        if job['s']:
Packit Service 0e769b
           if job['iops']:
Packit Service 0e769b
               ss = 'iops'
Packit Service 0e769b
           else:
Packit Service 0e769b
               ss = 'bw'
Packit Service 0e769b
           if job['slope']:
Packit Service 0e769b
               ss += "_slope"
Packit Service 0e769b
           ss += ":" + str(job['ss_limit'])
Packit Service 0e769b
           if job['pct']:
Packit Service 0e769b
               ss += '%'
Packit Service 0e769b
           parameters.extend([ '--ss_dur={0}'.format(job['ss_dur']),
Packit Service 0e769b
                               '--ss={0}'.format(ss),
Packit Service 0e769b
                               '--ss_ramp={0}'.format(job['ss_ramp']) ])
Packit Service 0e769b
Packit Service 0e769b
        output = subprocess.call([args.fio] + parameters)
Packit Service 0e769b
        with open(tf, 'r') as source:
Packit Service 0e769b
            jsondata = json.loads(source.read())
Packit Service 0e769b
            source.close()
Packit Service 0e769b
Packit Service 0e769b
        for jsonjob in jsondata['jobs']:
Packit Service 0e769b
            line = "{0}".format(jsonjob['job options']['name'])
Packit Service 0e769b
            if job['s']:
Packit Service 0e769b
                if jsonjob['steadystate']['attained'] == 1:
Packit Service 0e769b
                    # check runtime >= ss_dur + ss_ramp, check criterion, check criterion < limit
Packit Service 0e769b
                    mintime = (job['ss_dur'] + job['ss_ramp']) * 1000
Packit Service 0e769b
                    actual = jsonjob['read']['runtime']
Packit Service 0e769b
                    if mintime > actual:
Packit Service 0e769b
                        line = 'FAILED ' + line + ' ss attained, runtime {0} < ss_dur {1} + ss_ramp {2}'.format(actual, job['ss_dur'], job['ss_ramp'])
Packit Service 0e769b
                        failed = failed + 1
Packit Service 0e769b
                    else:
Packit Service 0e769b
                        line = line + ' ss attained, runtime {0} > ss_dur {1} + ss_ramp {2},'.format(actual, job['ss_dur'], job['ss_ramp'])
Packit Service 0e769b
                        objsame, met, mean, target = check(data=jsonjob['steadystate']['data'],
Packit Service 0e769b
                            iops=job['iops'],
Packit Service 0e769b
                            slope=job['slope'],
Packit Service 0e769b
                            pct=job['pct'],
Packit Service 0e769b
                            limit=job['ss_limit'],
Packit Service 0e769b
                            dur=job['ss_dur'],
Packit Service 0e769b
                            criterion=jsonjob['steadystate']['criterion'])
Packit Service 0e769b
                        if not objsame:
Packit Service 0e769b
                            line = 'FAILED ' + line + ' fio criterion {0} != calculated criterion {1} '.format(jsonjob['steadystate']['criterion'], target)
Packit Service 0e769b
                            failed = failed + 1
Packit Service 0e769b
                        else:
Packit Service 0e769b
                            if met:
Packit Service 0e769b
                                line = 'PASSED ' + line + ' target {0} < limit {1}'.format(target, job['ss_limit'])
Packit Service 0e769b
                                passed = passed + 1
Packit Service 0e769b
                            else:
Packit Service 0e769b
                                line = 'FAILED ' + line + ' target {0} < limit {1} but fio reports ss not attained '.format(target, job['ss_limit'])
Packit Service 0e769b
                                failed = failed + 1
Packit Service 0e769b
                else:
Packit Service 0e769b
                    # check runtime, confirm criterion calculation, and confirm that criterion was not met
Packit Service 0e769b
                    expected = job['timeout'] * 1000
Packit Service 0e769b
                    actual = jsonjob['read']['runtime']
Packit Service 0e769b
                    if abs(expected - actual) > 50:
Packit Service 0e769b
                        line = 'FAILED ' + line + ' ss not attained, expected runtime {0} != actual runtime {1}'.format(expected, actual)
Packit Service 0e769b
                    else:
Packit Service 0e769b
                        line = line + ' ss not attained, runtime {0} != ss_dur {1} + ss_ramp {2},'.format(actual, job['ss_dur'], job['ss_ramp'])
Packit Service 0e769b
                        objsame, met, mean, target = check(data=jsonjob['steadystate']['data'],
Packit Service 0e769b
                            iops=job['iops'],
Packit Service 0e769b
                            slope=job['slope'],
Packit Service 0e769b
                            pct=job['pct'],
Packit Service 0e769b
                            limit=job['ss_limit'],
Packit Service 0e769b
                            dur=job['ss_dur'],
Packit Service 0e769b
                            criterion=jsonjob['steadystate']['criterion'])
Packit Service 0e769b
                        if not objsame:
Packit Service 0e769b
                            if actual > (job['ss_dur'] + job['ss_ramp'])*1000:
Packit Service 0e769b
                                line = 'FAILED ' + line + ' fio criterion {0} != calculated criterion {1} '.format(jsonjob['steadystate']['criterion'], target)
Packit Service 0e769b
                                failed = failed + 1
Packit Service 0e769b
                            else:
Packit Service 0e769b
                                line = 'PASSED ' + line + ' fio criterion {0} == 0.0 since ss_dur + ss_ramp has not elapsed '.format(jsonjob['steadystate']['criterion'])
Packit Service 0e769b
                                passed = passed + 1
Packit Service 0e769b
                        else:
Packit Service 0e769b
                            if met:
Packit Service 0e769b
                                line = 'FAILED ' + line + ' target {0} < threshold {1} but fio reports ss not attained '.format(target, job['ss_limit'])
Packit Service 0e769b
                                failed = failed + 1
Packit Service 0e769b
                            else:
Packit Service 0e769b
                                line = 'PASSED ' + line + ' criterion {0} > threshold {1}'.format(target, job['ss_limit'])
Packit Service 0e769b
                                passed = passed + 1
Packit Service 0e769b
            else:
Packit Service 0e769b
                expected = job['timeout'] * 1000
Packit Service 0e769b
                actual = jsonjob['read']['runtime']
Packit Service 0e769b
                if abs(expected - actual) > 50:
Packit Service 0e769b
                    result = 'FAILED '
Packit Service 0e769b
                    failed = failed + 1
Packit Service 0e769b
                else:
Packit Service 0e769b
                    result = 'PASSED '
Packit Service 0e769b
                    passed = passed + 1
Packit Service 0e769b
                line = result + line + ' no ss, expected runtime {0} ~= actual runtime {1}'.format(expected, actual)
Packit Service 0e769b
            print(line)
Packit Service 0e769b
            if 'steadystate' in jsonjob:
Packit Service 0e769b
                pp.pprint(jsonjob['steadystate'])
Packit Service 0e769b
        jobnum += 1
Packit Service 0e769b
Packit Service 0e769b
    print("{0} test(s) PASSED, {1} test(s) FAILED".format(passed,failed))
Packit Service 0e769b
    sys.exit(failed)