<commit_before># Copyright 2014 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Runs fio benchmarks.

Man: http://manpages.ubuntu.com/manpages/natty/man1/fio.1.html
Quick howto: http://www.bluestop.org/fio/HOWTO.txt
"""

import json
import logging
import os

from perfkitbenchmarker import flags
from perfkitbenchmarker import vm_util
from perfkitbenchmarker.packages import fio

LOCAL_JOB_FILE_NAME = 'fio.job'  # used with vm_util.PrependTempDir()
REMOTE_JOB_FILE_PATH = '~/fio.job'


FLAGS = flags.FLAGS

flags.DEFINE_string('fio_jobfile', None,
                    'Job file that fio will use. Giving a custom job file '
                    'overrides the other fio options.')
flags.DEFINE_boolean('against_device', False,
                     'Test direct against scratch disk. If set True, will '
                     'create a modified job file in temp directory, '
                     'which ignores directory and filename parameter.')
flags.DEFINE_string('device_fill_size', '100%',
                    'The amount of device to fill in prepare stage. '
                    'This flag is only valid when against_device=True. '
                    'The valid value can either be an integer, which '
                    'represents the number of bytes to fill or a '
                    'percentage, which represents the percentage '
                    'of the device.')
flags.DEFINE_string('io_depths', '1',
                    'IO queue depths to run on. Can specify a single number, '
                    'like --io_depths=1, or a range, like --io_depths=1-4')


BENCHMARK_INFO = {'name': 'fio',
                  'description': 'Runs fio in sequential, random, read '
                                 'and write modes.',
                  'scratch_disk': True,
                  'num_machines': 1}

GLOBALS_TEMPLATE = """
[global]
ioengine=libaio
invalidate=1
blocksize=4k
direct=1
runtime=10m
time_based
filename={filename}
do_verify=0
verify_fatal=0
randrepeat=0
size={size}

"""

SINGLE_JOB_TEMPLATE = """
[{rwkind}-io-depth-{iodepth}]
stonewall
rw={rwkind}
iodepth={iodepth}

"""


def GetIODepths(io_depths):
  """Parse the io_depths parameter.

  Args:
    io_depths: a string in the format of the --io_depths flag.

  Returns:
    An iterable of integers.

  Raises:
    ValueError if FLAGS.io_depths doesn't follow a format it recognizes.
  """

  try:
    return [int(io_depths)]
  except ValueError:
    bounds = io_depths.split('-', 1)

    if len(bounds) != 2:
      raise ValueError

    return range(int(bounds[0]), int(bounds[1]) + 1)


def WriteJobFile(mount_point):
  """Write a fio job file.

  Args:
    mount_point: the mount point of the disk we're testing against.

  Returns:
    The contents of a fio job file, as a string.
  """

  if FLAGS.against_device:
    filename = mount_point
    size = FLAGS.device_fill_size
  else:
    filename = os.path.join(mount_point, 'fio-temp-file')
    size = '100G'
  return (GLOBALS_TEMPLATE.format(filename=filename, size=size) +
          '\n'.join((SINGLE_JOB_TEMPLATE.format(rwkind='randread',
                                                iodepth=str(i))
                     for i in GetIODepths(FLAGS.io_depths))) +
          '\n'.join((SINGLE_JOB_TEMPLATE.format(rwkind='randwrite',
                                                iodepth=str(i))
                     for i in GetIODepths(FLAGS.io_depths))))


def JobFileString(vm):
  """Get the contents of our job file.

  Args:
    vm: the virtual_machine.BaseVirtualMachine that we will run on.

  Returns:
    A string containing the user's job file.
  """

  if FLAGS.fio_jobfile:
    return open(FLAGS.fio_jobfile, 'r').read()
  else:
    return WriteJobFile(vm.scratch_disks[0].mount_point)


def GetInfo():
  return BENCHMARK_INFO


def Prepare(benchmark_spec):
  """Prepare the virtual machine to run FIO.

     This includes installing fio, bc, and libaio1 and insuring that
     the attached disk is large enough to support the fio
     benchmark. We also make sure the job file is always located at
     the same path on the local machine.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.

  """

  if (FLAGS.fio_jobfile and
      (FLAGS.against_device or FLAGS.device_fill_size or FLAGS.io_depths)):
    logging.warning('Fio job file specified. Ignoring other fio options.')
  if FLAGS.device_fill_size and not FLAGS.against_device:
    logging.warning('--device_fill_size has no effect without --against_device')

  vm = benchmark_spec.vms[0]
  logging.info('FIO prepare on %s', vm)
  vm.Install('fio')

  mount_point = vm.scratch_disks[0].mount_point
  logging.info('Umount scratch disk on %s at %s', vm, mount_point)
  vm.RemoteCommand('sudo umount %s' % mount_point)

  job_file_path = vm_util.PrependTempDir(LOCAL_JOB_FILE_NAME)
  with open(job_file_path, 'w') as job_file:
    job_file.write(JobFileString(vm))
    logging.info('Wrote fio job file at %s', job_file_path)

  vm.PushFile(job_file_path, REMOTE_JOB_FILE_PATH)

<commit_msg>Better warnings in fio benchmark<commit_after># Copyright 2014 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Runs fio benchmarks.

Man: http://manpages.ubuntu.com/manpages/natty/man1/fio.1.html
Quick howto: http://www.bluestop.org/fio/HOWTO.txt
"""

import json
import logging
import os

from perfkitbenchmarker import flags
from perfkitbenchmarker import vm_util
from perfkitbenchmarker.packages import fio

LOCAL_JOB_FILE_NAME = 'fio.job'  # used with vm_util.PrependTempDir()
REMOTE_JOB_FILE_PATH = '~/fio.job'


FLAGS = flags.FLAGS

flags.DEFINE_string('fio_jobfile', None,
                    'Job file that fio will use. Giving a custom job file '
                    'overrides the other fio options.')
flags.DEFINE_boolean('against_device', False,
                     'Test direct against scratch disk. If set True, will '
                     'create a modified job file in temp directory, '
                     'which ignores directory and filename parameter.')
flags.DEFINE_string('device_fill_size', '100%',
                    'The amount of device to fill in prepare stage. '
                    'This flag is only valid when against_device=True. '
                    'The valid value can either be an integer, which '
                    'represents the number of bytes to fill or a '
                    'percentage, which represents the percentage '
                    'of the device.')
flags.DEFINE_string('io_depths', '1',
                    'IO queue depths to run on. Can specify a single number, '
                    'like --io_depths=1, or a range, like --io_depths=1-4')


BENCHMARK_INFO = {'name': 'fio',
                  'description': 'Runs fio in sequential, random, read '
                                 'and write modes.',
                  'scratch_disk': True,
                  'num_machines': 1}

GLOBALS_TEMPLATE = """
[global]
ioengine=libaio
invalidate=1
blocksize=4k
direct=1
runtime=10m
time_based
filename={filename}
do_verify=0
verify_fatal=0
randrepeat=0
size={size}

"""

SINGLE_JOB_TEMPLATE = """
[{rwkind}-io-depth-{iodepth}]
stonewall
rw={rwkind}
iodepth={iodepth}

"""


def GetIODepths(io_depths):
  """Parse the io_depths parameter.

  Args:
    io_depths: a string in the format of the --io_depths flag.

  Returns:
    An iterable of integers.

  Raises:
    ValueError if FLAGS.io_depths doesn't follow a format it recognizes.
  """

  try:
    return [int(io_depths)]
  except ValueError:
    bounds = io_depths.split('-', 1)

    if len(bounds) != 2:
      raise ValueError

    return range(int(bounds[0]), int(bounds[1]) + 1)


def WriteJobFile(mount_point):
  """Write a fio job file.

  Args:
    mount_point: the mount point of the disk we're testing against.

  Returns:
    The contents of a fio job file, as a string.
  """

  if FLAGS.against_device:
    filename = mount_point
    size = FLAGS.device_fill_size
  else:
    filename = os.path.join(mount_point, 'fio-temp-file')
    size = '100G'
  return (GLOBALS_TEMPLATE.format(filename=filename, size=size) +
          '\n'.join((SINGLE_JOB_TEMPLATE.format(rwkind='randread',
                                                iodepth=str(i))
                     for i in GetIODepths(FLAGS.io_depths))) +
          '\n'.join((SINGLE_JOB_TEMPLATE.format(rwkind='randwrite',
                                                iodepth=str(i))
                     for i in GetIODepths(FLAGS.io_depths))))


def JobFileString(vm):
  """Get the contents of our job file.

  Args:
    vm: the virtual_machine.BaseVirtualMachine that we will run on.

  Returns:
    A string containing the user's job file.
  """

  if FLAGS.fio_jobfile:
    return open(FLAGS.fio_jobfile, 'r').read()
  else:
    return WriteJobFile(vm.scratch_disks[0].mount_point)


def GetInfo():
  return BENCHMARK_INFO


def Prepare(benchmark_spec):
  """Prepare the virtual machine to run FIO.

     This includes installing fio, bc, and libaio1 and insuring that
     the attached disk is large enough to support the fio
     benchmark. We also make sure the job file is always located at
     the same path on the local machine.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.

  """

  if FLAGS.fio_jobfile:
    ignored_flags = []
    if FLAGS.against_device:
      ignored_flags.append('--against_device')
    if FLAGS.device_fill_size != '100%':
      ignored_flags.append('--device_fill_size')
    if FLAGS.io_depths != '1':
      ignored_flags.append('--io_depths')

    if ignored_flags:
      logging.warning('Fio job file specified. Ignoring options "%s"',
                      ', '.join(ignored_flags))
  if FLAGS.device_fill_size and not FLAGS.against_device:
    logging.warning('--device_fill_size has no effect without --against_device')

  vm = benchmark_spec.vms[0]
  logging.info('FIO prepare on %s', vm)
  vm.Install('fio')

  if FLAGS.against_device and not FLAGS.fio_jobfile:
    mount_point = vm.scratch_disks[0].mount_point
    logging.info('Umount scratch disk on %s at %s', vm, mount_point)
    vm.RemoteCommand('sudo umount %s' % mount_point)

  job_file_path = vm_util.PrependTempDir(LOCAL_JOB_FILE_NAME)
  with open(job_file_path, 'w') as job_file:
    job_file.write(JobFileString(vm))
    logging.info('Wrote fio job file at %s', job_file_path)

  vm.PushFile(job_file_path, REMOTE_JOB_FILE_PATH)


{'fraction_non_alphanumeric': 0.09554803436605051, 'fraction_numerical': 0.006942636466198039, 'mean_word_length': 2.8020455295282085, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3305398', 'n_tokens_mistral': 3726, 'n_tokens_neox': 3512, 'n_words': 1089}