/**
  * returns run options represents as a hash
  * @return array
  */
 public static function getRunOptions()
 {
     // default run argument values
     $sysInfo = get_sys_info();
     $ini = get_benchmark_ini();
     $defaults = array('active_range' => 100, 'collectd_rrd_dir' => '/var/lib/collectd/rrd', 'fio' => 'fio', 'fio_options' => array('direct' => TRUE, 'ioengine' => 'libaio', 'refill_buffers' => FALSE, 'scramble_buffers' => TRUE), 'font_size' => 9, 'highcharts_js_url' => 'http://code.highcharts.com/highcharts.js', 'highcharts3d_js_url' => 'http://code.highcharts.com/highcharts-3d.js', 'jquery_url' => 'http://code.jquery.com/jquery-2.1.0.min.js', 'meta_compute_service' => 'Not Specified', 'meta_cpu' => $sysInfo['cpu'], 'meta_instance_id' => 'Not Specified', 'meta_memory' => $sysInfo['memory_gb'] > 0 ? $sysInfo['memory_gb'] . ' GB' : $sysInfo['memory_mb'] . ' MB', 'meta_os' => $sysInfo['os_info'], 'meta_provider' => 'Not Specified', 'meta_storage_config' => 'Not Specified', 'meta_test_sw' => isset($ini['meta-id']) ? 'ch-' . $ini['meta-id'] . (isset($ini['meta-version']) ? ' ' . $ini['meta-version'] : '') : '', 'oio_per_thread' => 64, 'output' => trim(shell_exec('pwd')), 'precondition_passes' => 2, 'ss_max_rounds' => 25, 'ss_verification' => 10, 'test' => array('iops'), 'threads' => '{cpus}', 'threads_per_core_max' => 2, 'threads_per_target_max' => 8, 'timeout' => 86400, 'wd_test_duration' => 60);
     $opts = array('active_range:', 'collectd_rrd', 'collectd_rrd_dir:', 'fio:', 'font_size:', 'highcharts_js_url:', 'highcharts3d_js_url:', 'jquery_url:', 'meta_compute_service:', 'meta_compute_service_id:', 'meta_cpu:', 'meta_drive_interface:', 'meta_drive_model:', 'meta_drive_type:', 'meta_instance_id:', 'meta_memory:', 'meta_os:', 'meta_notes_storage:', 'meta_notes_test:', 'meta_provider:', 'meta_provider_id:', 'meta_region:', 'meta_resource_id:', 'meta_run_id:', 'meta_storage_config:', 'meta_storage_vol_info:', 'meta_test_id:', 'meta_test_sw:', 'no3dcharts', 'nojson', 'nopdfreport', 'noprecondition', 'nopurge', 'norandom', 'noreport', 'nosecureerase', 'notrim', 'nozerofill', 'oio_per_thread:', 'output:', 'precondition_passes:', 'randommap', 'savefio', 'secureerase_pswd:', 'sequential_only', 'skip_blocksize:', 'skip_workload:', 'ss_max_rounds:', 'ss_verification:', 'target:', 'target_skip_not_present', 'test:', 'threads:', 'threads_per_core_max:', 'threads_per_target_max:', 'timeout:', 'trim_offset_end:', 'v' => 'verbose', 'wd_test_duration:', 'wd_sleep_between:');
     $options = parse_args($opts, array('skip_blocksize', 'skip_workload', 'target', 'test'));
     $verbose = isset($options['verbose']) && $options['verbose'];
     // explicit fio command
     foreach ($defaults as $key => $val) {
         if (!isset($options[$key])) {
             $options[$key] = $val;
         }
     }
     // target/test argument (expand comma separated values)
     foreach (array('target', 'test') as $key) {
         if (isset($options[$key])) {
             $targets = array();
             foreach ($options[$key] as $temp) {
                 foreach (explode(',', $temp) as $target) {
                     $targets[] = trim($target);
                 }
             }
             $options[$key] = $targets;
         }
     }
     foreach (get_prefixed_params('fio_') as $key => $val) {
         $options['fio_options'][$key] = $val;
     }
     // don't use random IO
     if (isset($options['norandom']) && $options['norandom']) {
         if (isset($options['fio']['refill_buffers'])) {
             unset($options['fio']['refill_buffers']);
         }
         if (isset($options['fio']['scramble_buffers'])) {
             unset($options['fio']['scramble_buffers']);
         }
     }
     // implicit nosecureerase
     if (!isset($options['secureerase_pswd'])) {
         $options['nosecureerase'] = TRUE;
     }
     // implicit nopurge
     if (isset($options['nosecureerase']) && $options['nosecureerase'] && isset($options['notrim']) && $options['notrim'] && isset($options['nozerofill']) && $options['nozerofill']) {
         $options['nopurge'] = TRUE;
     }
     // threads is based on number of CPUs
     if (isset($options['threads']) && preg_match('/{cpus}/', $options['threads'])) {
         $options['threads'] = str_replace(' ', '', str_replace('{cpus}', BlockStorageTest::getCpuCount(), $options['threads']));
         // expression
         if (preg_match('/[\\*\\+\\-\\/]/', $options['threads'])) {
             eval(sprintf('$options["threads"]=%s;', $options['threads']));
         }
         $options['threads'] *= 1;
         if ($options['threads'] <= 0) {
             $options['threads'] = 1;
         }
     }
     // remove targets that are not present
     if (isset($options['target_skip_not_present']) && isset($options['target']) && count($options['target']) > 1) {
         print_msg(sprintf('Checking targets %s because --target_skip_not_present argument was set', implode(', ', $options['target'])), $verbose, __FILE__, __LINE__);
         $targets = array();
         foreach ($options['target'] as $i => $target) {
             if (!is_dir($target) && !file_exists($target)) {
                 print_msg(sprintf('Skipped test target %s because it does not exist and the --target_skip_not_present argument was set', $target), $verbose, __FILE__, __LINE__);
             } else {
                 $targets[] = $target;
             }
         }
         $options['target'] = $targets;
         print_msg(sprintf('Adjusted test targets is %s', implode(', ', $options['target'])), $verbose, __FILE__, __LINE__);
     }
     // adjust threads for number of targets
     if (isset($options['target']) && count($options['target']) > 1) {
         $options['threads'] = round($options['threads'] / count($options['target']));
         if ($options['threads'] == 0) {
             $options['threads'] = 1;
         }
     }
     // adjust for threads_per_target_max
     if (isset($options['threads_per_target_max']) && $options['threads'] > $options['threads_per_target_max']) {
         $threads = $options['threads'];
         $options['threads'] = $options['threads_per_target_max'];
         print_msg(sprintf('Reduced threads from %d to %d for threads_per_target_max constraint %d', $threads, $options['threads'], $options['threads_per_target_max']), $verbose, __FILE__, __LINE__);
     }
     $options['threads_total'] = $options['threads'] * count($options['target']);
     // adjust for threads_per_core_max
     if (isset($options['threads_per_core_max']) && $options['threads_total'] > $options['threads_per_core_max'] * BlockStorageTest::getCpuCount()) {
         $threads_total = $options['threads_total'];
         $threads = $options['threads'];
         $options['threads'] = round($options['threads_per_core_max'] * BlockStorageTest::getCpuCount() / count($options['target']));
         if (!$options['threads']) {
             $options['threads'] = 1;
         }
         $options['threads_total'] = round($options['threads'] * count($options['target']));
         if ($threads != $options['threads']) {
             print_msg(sprintf('Reduced total threads from %d to %d [threads per target from %d to %s] for threads_per_core_max constraint %d, %d CPU cores, and %d targets', $threads_total, $options['threads_total'], $threads, $options['threads'], $options['threads_per_core_max'], BlockStorageTest::getCpuCount(), count($options['target'])), $verbose, __FILE__, __LINE__);
         } else {
             print_msg(sprintf('Ignoring threads_per_core_max constraint %d because at least 1 thread per target is required', $options['threads_per_core_max']), $verbose, __FILE__, __LINE__);
         }
     }
     return $options;
 }
 /**
  * this method substitutes any tokens present in $str the the corresponding 
  * benchmark or row metadata. Benchmark metata tokens include {benchmark} and
  * {version}
  * @param string $str the string containing tokens
  * @param array $row the data row (hash of key/value pairs)
  * @return string
  */
 protected function substituteTokens($str, &$row)
 {
     if (preg_match_all('/\\{([^\\}]+)\\}/', $str, $m)) {
         if (!isset($this->benchmarkIni)) {
             $this->benchmarkIni = get_benchmark_ini();
         }
         $meta = array('benchmark' => isset($this->benchmarkIni['meta-id']) ? $this->benchmarkIni['meta-id'] : NULL, 'version' => isset($this->benchmarkIni['meta-version']) ? $this->benchmarkIni['meta-version'] : NULL);
         foreach ($m[1] as $i => $token) {
             $sub = '';
             if (isset($row[$token])) {
                 $sub = $row[$token];
             } else {
                 if (isset($meta[$token])) {
                     $sub = $meta[$token];
                 }
             }
             $str = str_replace($m[0][$i], $sub, $str);
         }
         // remove leading and trailing dash (-) and underscore (_)
         $str = trim($str);
         $str = trim(str_replace('--', '-', $str));
         $str = trim(str_replace('[]', '', $str));
         $str = trim(str_replace('()', '', $str));
         $str = trim($str);
         while ($str && (substr($str, 0, 1) == '-' || substr($str, 0, 1) == '_')) {
             $str = trim(substr($str, 1));
         }
         while ($str && (substr($str, -1) == '-' || substr($str, -1) == '_')) {
             $str = trim(substr($str, 0, -1));
         }
         $str = trim($str);
     }
     return $str;
 }
 /**
  * returns the object URI (minus container) for $file (appends --store_prefix
  * if applicable). Includes substitution of the following dynamic values:
  *   {date[_format]} => a date string (optionally 
  *                      formatted per [format] - see
  *                      http://php.net/manual/en/function.date.php
  *                      for valid format options - 
  *                      default format is Y-m-d)
  *   {benchmark}     => benchmark name (block-storage)
  *                      (meta-id value in benchmark.ini)
  *   {version}       => benchmark version (1.0)
  *                      (meta-version value in benchmark.ini)
  *   {iteration}     => iteration number
  *   {hostname}      => the compute instance hostname
  *   {meta_*}        => any of the meta_* runtime 
  *                      parameters. If a meta_* value
  *                      is designated but was not set, 
  *                      at runtime, it will be removed 
  *                      from the prefix
  *   {rand}          => a random number
  * @param string $file the file to return the URI
  * @return string
  */
 protected final function getObjectUri($file)
 {
     $prefix = isset($this->options['store_prefix']) ? $this->options['store_prefix'] : BenchmarkArchiver::DEFAULT_PREFIX;
     if (preg_match_all('/{([^}]+)}/', $prefix, $m)) {
         $ini = get_benchmark_ini();
         $options = file_exists($options = dirname($file) . '/.options') ? unserialize(file_get_contents($options)) : NULL;
         foreach ($m[1] as $value) {
             $sub = '';
             foreach (explode('|', $value) as $check) {
                 $check = trim($check);
                 if (preg_match('/^date_?(.*)$/', $check, $d)) {
                     $format = $d[1] ? $d[1] : 'Y-m-d';
                     $sub = date($format, filemtime($file));
                 } else {
                     if ($check == 'benchmark') {
                         $sub = isset($ini['meta-id']) ? $ini['meta-id'] : '';
                     } else {
                         if ($check == 'version') {
                             $sub = isset($ini['meta-version']) ? str_replace('.', '_', $ini['meta-version']) : '';
                         } else {
                             if ($check == 'iteration') {
                                 $sub = is_numeric(basename(dirname($file))) ? basename(dirname($file)) * 1 : 1;
                             } else {
                                 if ($check == 'hostname') {
                                     $sub = trim(shell_exec('hostname'));
                                 } else {
                                     if ($check == 'rand') {
                                         $sub = '[rand]';
                                     } else {
                                         if ($options && isset($options[$check])) {
                                             $sub = trim(strtolower(str_replace(' ', '_', $options[$check])));
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
                 if ($sub == 'not_specified' || $sub == 'na') {
                     $sub = '';
                 }
                 if ($sub) {
                     break;
                 }
             }
             $prefix = str_replace('{' . $value . '}', $sub, $prefix);
             if (!$sub) {
                 $prefix = str_replace('//', '/', $prefix);
             }
         }
         if (!isset($this->randStrings[$prefix])) {
             $this->randStrings[$prefix] = rand();
         }
         $prefix = str_replace('[rand]', $this->randStrings[$prefix], $prefix);
     }
     return str_replace('//', '/', sprintf('%s/%s', $prefix, basename($file)));
 }