# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-import pkg_resources
+from __future__ import absolute_import
+from __future__ import print_function
+
import logging
-import json
+
+import pkg_resources
+from oslo_serialization import jsonutils
import yardstick.ssh as ssh
from yardstick.benchmark.scenarios import base
LOG = logging.getLogger(__name__)
-LOG.setLevel(logging.DEBUG)
class Fio(base.Scenario):
- """Execute fio benchmark on host
+ """Execute fio benchmark in a host
Parameters
filename - file name for fio workload
type: string
unit: na
- default: /home/ec2-user/data.raw
+ default: /home/ubuntu/data.raw
+ job_file - fio job configuration file
+ type: string
+ unit: na
+ default: None
+ job_file_config - content of job configuration file
+ type: list
+ unit: na
+ default: None
+ directory - mount directoey for test volume
+ type: string
+ unit: na
+ default: None
bs - block size used for the io units
type: int
unit: bytes
default: 4k
+ iodepth - number of iobuffers to keep in flight
+ type: int
+ unit: na
+ default: 1
rw - type of io pattern [read, write, randwrite, randread, rw, randrw]
type: string
unit: na
default: write
+ rwmixwrite - percentage of a mixed workload that should be writes
+ type: int
+ unit: percentage
+ default: 50
ramp_time - run time before logging any performance
type: int
unit: seconds
default: 20
+ direct - whether use non-buffered I/O or not
+ type: boolean
+ unit: na
+ default: 1
+ size - total size of I/O for this job.
+ type: string
+ unit: na
+ default: 1g
+ numjobs - number of clones (processes/threads performing the same workload) of this job
+ type: int
+ unit: na
+ default: 1
Read link below for more fio args description:
http://www.bluestop.org/fio/HOWTO.txt
TARGET_SCRIPT = "fio_benchmark.bash"
- def __init__(self, context):
- self.context = context
+ def __init__(self, scenario_cfg, context_cfg):
+ self.scenario_cfg = scenario_cfg
+ self.context_cfg = context_cfg
+ self.options = self.scenario_cfg["options"]
self.setup_done = False
def setup(self):
- '''scenario setup'''
- self.target_script = pkg_resources.resource_filename(
- "yardstick.benchmark.scenarios.storage",
- Fio.TARGET_SCRIPT)
- user = self.context.get("user", "root")
- host = self.context.get("host", None)
- key_filename = self.context.get("key_filename", "~/.ssh/id_rsa")
-
- LOG.debug("user:%s, host:%s", user, host)
- self.client = ssh.SSH(user, host, key_filename=key_filename)
+ """scenario setup"""
+ host = self.context_cfg["host"]
+
+ self.client = ssh.SSH.from_node(host, defaults={"user": "root"})
self.client.wait(timeout=600)
- # copy script to host
- self.client.run("cat > ~/fio.sh",
- stdin=open(self.target_script, "rb"))
+ self.job_file = self.options.get("job_file", None)
+ config_lines = self.options.get("job_file_config", None)
+
+ if self.job_file:
+ self.job_file_script = pkg_resources.resource_filename(
+ "yardstick.resources", 'files/' + self.job_file)
+
+ # copy job file to host
+ self.client._put_file_shell(self.job_file_script, '~/job_file.ini')
+ elif config_lines:
+ LOG.debug("Job file configuration received, Fio job file will be created.")
+ self.job_file = 'tmp_job_file.ini'
+ self.job_file_script = pkg_resources.resource_filename(
+ "yardstick.resources", 'files/' + self.job_file)
+ with open(self.job_file_script, 'w') as f:
+ f.write('\n'.join(str(line) for line in config_lines))
+
+ # copy job file to host
+ self.client._put_file_shell(self.job_file_script, '~/job_file.ini')
+ else:
+ LOG.debug("No job file configuration received, Fio will use parameters.")
+ self.target_script = pkg_resources.resource_filename(
+ "yardstick.benchmark.scenarios.storage", Fio.TARGET_SCRIPT)
+
+ # copy script to host
+ self.client._put_file_shell(self.target_script, '~/fio.sh')
+
+ mount_dir = self.options.get("directory", None)
+
+ if mount_dir:
+ LOG.debug("Formating volume...")
+ self.client.execute("sudo mkfs.ext4 /dev/vdb")
+ cmd = "sudo mkdir %s" % mount_dir
+ self.client.execute(cmd)
+ LOG.debug("Mounting volume at: %s", mount_dir)
+ cmd = "sudo mount /dev/vdb %s" % mount_dir
+ self.client.execute(cmd)
self.setup_done = True
- def run(self, args):
+ def run(self, result):
"""execute the benchmark"""
- default_args = "-ioengine=libaio -direct=1 " \
- "-group_reporting -numjobs=1 -time_based"
+ default_args = "-ioengine=libaio -group_reporting -time_based -time_based " \
+ "--output-format=json"
+ timeout = 3600
if not self.setup_done:
self.setup()
- options = args["options"]
- filename = options.get("filename", "/home/ec2-user/data.raw")
- bs = options.get("bs", "4k")
- rw = options.get("rw", "write")
- ramp_time = options.get("ramp_time", 20)
- name = "yardstick-fio"
- # if run by a duration runner
- duration_time = self.context.get("duration", None)
- # if run by an arithmetic runner
- arithmetic_time = options.get("duration", None)
- if duration_time:
- runtime = duration_time
- elif arithmetic_time:
- runtime = arithmetic_time
+ if self.job_file:
+ cmd = "sudo fio job_file.ini --output-format=json"
else:
- runtime = 30
+ filename = self.options.get("filename", "/home/ubuntu/data.raw")
+ bs = self.options.get("bs", "4k")
+ iodepth = self.options.get("iodepth", "1")
+ rw = self.options.get("rw", "write")
+ ramp_time = self.options.get("ramp_time", 20)
+ size = self.options.get("size", "1g")
+ direct = self.options.get("direct", "1")
+ numjobs = self.options.get("numjobs", "1")
+ rwmixwrite = self.options.get("rwmixwrite", 50)
+ name = "yardstick-fio"
+ # if run by a duration runner
+ duration_time = self.scenario_cfg["runner"].get("duration", None) \
+ if "runner" in self.scenario_cfg else None
+ # if run by an arithmetic runner
+ arithmetic_time = self.options.get("duration", None)
+ if duration_time:
+ runtime = duration_time
+ elif arithmetic_time:
+ runtime = arithmetic_time
+ else:
+ runtime = 30
+ # Set timeout, so that the cmd execution does not exit incorrectly
+ # when the test run time is last long
+ timeout = int(ramp_time) + int(runtime) + 600
+
+ cmd_args = "-filename=%s -direct=%s -bs=%s -iodepth=%s -rw=%s -rwmixwrite=%s " \
+ "-size=%s -ramp_time=%s -numjobs=%s -runtime=%s -name=%s %s" \
+ % (filename, direct, bs, iodepth, rw, rwmixwrite, size, ramp_time, numjobs,
+ runtime, name, default_args)
+ cmd = "sudo bash fio.sh %s %s" % (filename, cmd_args)
- args = "-filename=%s -bs=%s -rw=%s -ramp_time=%s -runtime=%s -name=%s" \
- % (filename, bs, rw, ramp_time, runtime, name)
- cmd = "sudo bash fio.sh %s %s %s" \
- % (filename, args, default_args)
LOG.debug("Executing command: %s", cmd)
- status, stdout, stderr = self.client.execute(cmd)
+ status, stdout, stderr = self.client.execute(cmd, timeout=timeout)
if status:
raise RuntimeError(stderr)
- data = json.loads(stdout)
+ raw_data = jsonutils.loads(stdout)
+
+ if self.job_file:
+ result["read_bw"] = raw_data["jobs"][0]["read"]["bw"]
+ result["read_iops"] = raw_data["jobs"][0]["read"]["iops"]
+ result["read_lat"] = raw_data["jobs"][0]["read"]["lat"]["mean"]
+ result["write_bw"] = raw_data["jobs"][0]["write"]["bw"]
+ result["write_iops"] = raw_data["jobs"][0]["write"]["iops"]
+ result["write_lat"] = raw_data["jobs"][0]["write"]["lat"]["mean"]
+ else:
+ # The bandwidth unit is KB/s, and latency unit is us
+ if rw in ["read", "randread", "rw", "randrw"]:
+ result["read_bw"] = raw_data["jobs"][0]["read"]["bw"]
+ result["read_iops"] = raw_data["jobs"][0]["read"]["iops"]
+ result["read_lat"] = raw_data["jobs"][0]["read"]["lat"]["mean"]
+ if rw in ["write", "randwrite", "rw", "randrw"]:
+ result["write_bw"] = raw_data["jobs"][0]["write"]["bw"]
+ result["write_iops"] = raw_data["jobs"][0]["write"]["iops"]
+ result["write_lat"] = raw_data["jobs"][0]["write"]["lat"]["mean"]
- # TODO: add sla check
+ if "sla" in self.scenario_cfg:
+ sla_error = ""
+ for k, v in result.items():
+ if k not in self.scenario_cfg['sla']:
+ continue
- return data
+ if "lat" in k:
+ # For lattency small value is better
+ max_v = float(self.scenario_cfg['sla'][k])
+ if v > max_v:
+ sla_error += "%s %f > sla:%s(%f); " % (k, v, k, max_v)
+ else:
+ # For bandwidth and iops big value is better
+ min_v = int(self.scenario_cfg['sla'][k])
+ if v < min_v:
+ sla_error += "%s %d < " \
+ "sla:%s(%d); " % (k, v, k, min_v)
+
+ assert sla_error == "", sla_error
def _test():
- '''internal test function'''
+ """internal test function"""
key_filename = pkg_resources.resource_filename("yardstick.resources",
"files/yardstick_key")
ctx = {
- "host": "10.0.0.101",
- "user": "ec2-user",
- "key_filename": key_filename
+ "host": {
+ "ip": "10.229.47.137",
+ "user": "root",
+ "key_filename": key_filename
+ }
}
logger = logging.getLogger("yardstick")
logger.setLevel(logging.DEBUG)
- fio = Fio(ctx)
-
options = {
- "filename": "/home/ec2-user/data.raw",
+ "filename": "/home/ubuntu/data.raw",
"bs": "4k",
- "rw": "write",
- "ramp_time": 10,
+ "iodepth": "1",
+ "rw": "rw",
+ "ramp_time": 1,
+ "duration": 10
}
+ result = {}
args = {"options": options}
- result = fio.run(args)
- print result
+ fio = Fio(args, ctx)
+ fio.run(result)
+ print(result)
+
if __name__ == '__main__':
_test()