def run_command(command, ignore_output=False): """Runs a command via subprocess communication. Args: command: The command. ignore_output: (OPTIONAL) Whether to ignore the output. Defaults to False. Returns: A tuple containing (the output, the return code). """ printf('Running command {}'.format(command), print_type=PrintType.DEBUG_LOG) try: args = shlex.split(command) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, preexec_fn=os.setsid) ProcessManager.add_process(command, p) if ignore_output: return None, _wait_for_process(command, p) return _communicate_to_process(command, p) except (ValueError, subprocess.CalledProcessError, FileNotFoundError) as err: printf('Command {} erred:\n{}'.format(command, err), print_type=PrintType.ERROR_LOG) return None, None finally: ProcessManager.clear_process(command)
def process_with_repetitions(self, output_configuration, file, device, scheduler, job_type, repetitions, template_setting_permutation, environment_setting_permutation, enable_blktrace): """Process the workload for the template permutation and repetitions. Args: output_configuration: The OutputConfiguration. file: The input file. device: The device to execute on. scheduler: The schedulers to execute with. job_type: The job type. repetitions: The number of repetitions. template_setting_permutation: The template setting permutation. environment_setting_permutation: The environment setting permutation. enable_blktrace: Whether blktrace is enabled. """ for rep in range(repetitions): printf( 'Executing file {} with device {}, scheduler {}, repetition ' '{} of {}'.format(file, device, scheduler, rep + 1, repetitions), print_type=PrintType.INFO_LOG) output = self._try_process(job_type, file, device, scheduler, enable_blktrace) output_configuration.process(output, self._name, device, scheduler, template_setting_permutation, environment_setting_permutation, enable_blktrace)
def run_command_nowait(command): """Runs a command via subprocess communication, and does not communicate with it for completion. It instead returns the process so the caller can handle it. Args: command: The command. Returns: The Process, or None if erred. """ printf('Running command {}'.format(command), print_type=PrintType.DEBUG_LOG) try: args = shlex.split(command) p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, preexec_fn=os.setsid) ProcessManager.add_process(command, p) return p except (ValueError, subprocess.CalledProcessError, FileNotFoundError) as err: printf('Command {} erred:\n{}'.format(command, err), print_type=PrintType.ERROR_LOG) ProcessManager.clear_process(command) return None
def main(): try: return dispatch(sys.argv[1:]) except IOBSBaseException as err: printf('Program encountered critical error\n{}'.format(err), print_type=PrintType.ERROR | PrintType.ERROR_LOG) return '{}: {}'.format(err.__class__.__name__, err.args[0])
def restore_system_environment(self): """Restores system environment. NOTE: This should be called after `save_environment` has been called. """ printf('Restoring system information...', print_type=PrintType.DEBUG_LOG) se = self._system_environment change_randomize_va_space(se['randomize_va_space'])
def validate(self): """Validates the configuration.""" printf('Validating input file {}'.format(self._input_file), print_type=PrintType.DEBUG_LOG) self._environment_configuration.validate() self._global_configuration.validate() self._output_configuration.validate() self._template_configuration.validate() for wc in self._workload_configurations: wc.validate()
def process(self): """Processes the configuration.""" printf('Processing input file {}'.format(self._input_file), print_type=PrintType.DEBUG_LOG) if self._workload_type == 'filebench': change_randomize_va_space(0) for wc in self._workload_configurations: wc.process(self._job_type, self._output_configuration, self._global_configuration, self._template_configuration, self._environment_configuration)
def parse_section(config_parser, section, config): """Parses a section of a config file. Args: config_parser: The config parser. section: The section name. config: The Configuration object. """ printf('Parsing section {}'.format(section), print_type=PrintType.DEBUG_LOG) for key, value in config_parser[section].items(): config.add_setting(key, value)
def save_system_environment(self): """Saves system environment information so it can be restored. NOTE: This should be called after `validate` has bee called. """ printf('Saving system information...', print_type=PrintType.DEBUG_LOG) self._system_environment = { 'randomize_va_space': get_randomize_va_space() } printf('Saving system environment: {}'.format( self._system_environment), print_type=PrintType.DEBUG_LOG)
def is_block_device(device): """Returns whether the given device is a valid block device. Args: device: The device. Returns: True if is a valid block device, else False. """ printf('Checking if device {} is a valid block device'.format(device), print_type=PrintType.DEBUG_LOG) try: if stat.S_ISBLK(os.stat(device).st_mode): printf('Device {} is a valid block device'.format(device), print_type=PrintType.DEBUG_LOG) return True printf('Device {} is not a valid block device'.format(device), print_type=PrintType.ERROR_LOG) return False except (FileNotFoundError, TypeError): printf('Device {} is not a valid block device'.format(device), print_type=PrintType.ERROR_LOG) return False
def sig_handler(signal, frame): """Signal handler for termination signals sent to main process. Args: signal: The signal. frame: The frame. """ printf('Program encountered termination signal, aborting...', print_type=PrintType.ERROR | PrintType.ERROR_LOG) ProcessManager.kill_processes() ProcessManager.clear_processes() colorama.deinit() sys.exit(1)
def get_randomize_va_space(): """Returns the current randomize_va_space for the system. Returns: The current randomize_va_space setting. """ printf('Retrieving randomize_va_space for system', print_type=PrintType.DEBUG_LOG) out, rc = run_command('cat /proc/sys/kernel/randomize_va_space') if rc != 0: printf('Unable to find randomize_va_space for system', print_type=PrintType.ERROR_LOG) return [] return int(out)
def save_device_environments(self): """Saves device environment information so it can be restored. NOTE: This should be called after `validate` has bee called. """ printf('Saving device information...', print_type=PrintType.DEBUG_LOG) for device in self._global_configuration.devices: self._device_environments[device] = { 'nomerges': get_device_nomerges(device), 'scheduler': get_device_scheduler(device) } de = self._device_environments[device] printf('Saving device {} environment: {}'.format(device, de), print_type=PrintType.DEBUG_LOG)
def _wait_for_process(command, p): """Waits for a process to complete. Args: command: The command. p: The process. Returns: The return code. """ rc = p.wait() if rc != 0: printf('Command {} [{}] erred with return code {}'.format( command, p.pid, rc), print_type=PrintType.ERROR_LOG) return rc
def terminate_process(process): """Terminates a process. Args: process: The process. Returns: A tuple of the output and return code. """ process.terminate() out, err = process.communicate() rc = process.returncode if err: printf('Process [{}] erred with return code {}:\n{}'.format( process.pid, rc, err.decode('utf-8')), print_type=PrintType.ERROR_LOG) return out.decode('utf-8'), rc
def _communicate_to_process(command, p): """Communicates to a process. Args: command: The command. p: The process. Returns: A tuple of the output and return code. """ out, err = p.communicate() rc = p.returncode if err: printf('Command {} [{}] erred with return code {}:\n{}'.format( command, p.pid, rc, err.decode('utf-8')), print_type=PrintType.ERROR_LOG) return out.decode('utf-8'), rc
def check_command(command): """Returns whether the given command exists on the system. Args: command: The command. Returns: True if exists, else False. """ printf('Checking if command {} exists'.format(command), print_type=PrintType.DEBUG_LOG) if run_system_command('command -v {}'.format(command)) == 0: return True printf('Command {} does not exist'.format(command), print_type=PrintType.ERROR_LOG) return False
def restore_device_environments(self): """Restores device environments. NOTE: This should be called after `save_device_environments` has been called. """ printf('Restoring device information...', print_type=PrintType.DEBUG_LOG) for device in self._global_configuration.devices: if device not in self._device_environments: continue de = self._device_environments[device] printf('Restoring device {} environment: {}'.format(device, de), print_type=PrintType.DEBUG_LOG) change_nomerges(device, de['nomerges']) change_scheduler(device, de['scheduler'])
def clear_caches(device): """Clears various data caches. Should be run before each benchmark. Args: device: The device. """ printf('Clearing caches for device {}'.format(device), print_type=PrintType.DEBUG_LOG) # Writes any data buffered in memory out to disk run_system_command('sync') # Drops clean caches run_system_command('echo 3 > /proc/sys/vm/drop_caches') # Calls block device ioctls to flush buffers run_system_command('blockdev --flushbufs {}'.format(device)) # Flushes the on-drive write cache buffer run_system_command('hdparm -F {}'.format(device))
def validate(args): """Validates workloads. Args: args: The parsed command-line arguments. Returns: 0 if successful. Raises: IOBSBaseException: If error occurs and `continue_on_failure` not set. """ validate_os() validate_privileges() printf('Beginning program validation...', print_type=PrintType.NORMAL | PrintType.INFO_LOG) for i, input_file in enumerate(args.inputs): printf('Validating input file {} ({} of {})' .format(input_file, i + 1, len(args.inputs))) configuration = parse_config_file(input_file) configuration.validate() printf('Finishing program validation...', print_type=PrintType.NORMAL | PrintType.INFO_LOG) return 0
def _try_process(self, job_type, file, device, scheduler, enable_blktrace): """Attempts to process a job with retrying if failure. Args: job_type: The job type. file: The input file. device: The device to execute on. scheduler: The scheduler to execute with. enable_blktrace: Whether blktrace is enabled. Returns: The output of processing the job. Raises: RetryCountExceededError: If job fails and retry counts are exceeded. """ retry_count = SettingsManager.get('retry_count') for retry in range(retry_count): if retry != 0: printf('Retrying job...', print_type=PrintType.DEBUG_LOG) job = job_type(file, device, scheduler) try: ret = job.process(enable_blktrace) if SettingsManager.get('cleanup_files'): device_name = match_regex(device, 'device_name') files = get_formatter('cleanup_blktrace').format( device_name) cleanup_files(files) return ret except (JobExecutionError, OutputParsingError) as err: printf('Unable to run job \n{}'.format(err), print_type=PrintType.ERROR_LOG) raise RetryCountExceededError( 'Unable to run job, exceeded retry counts')
def process(self, job_type, output_configuration, global_configuration, template_configuration, environment_configuration): """Process the workload. Args: job_type: The Job. output_configuration: The OutputConfiguration. template_configuration: The TemplateConfiguration. global_configuration: The GlobalConfiguration. environment_configuration: The EnvironmentConfiguration. """ printf('Processing workload {}'.format(self._name), print_type=PrintType.INFO_LOG) devices = global_configuration.devices schedulers = global_configuration.schedulers repetitions = global_configuration.repetitions enable_blktrace = global_configuration.enable_blktrace for device, scheduler in itertools.product(devices, schedulers): for file, tsp in template_configuration.get_file_permutations( self.file, device, scheduler): printf('Using template permutation {}'.format(tsp), print_type=PrintType.INFO_LOG) for esp in environment_configuration.get_environment_permutations( device): printf('Using environment permutation {}'.format(esp), print_type=PrintType.INFO_LOG) self.process_with_repetitions(output_configuration, file, device, scheduler, job_type, repetitions, tsp, esp, enable_blktrace)
def is_rotational_device(device): """Returns whether the given device is a rotational device. Args: device: The device. Returns: True if is a rotational device, else False. """ printf('Checking whether device {} is a rotational device'.format(device), print_type=PrintType.DEBUG_LOG) device_name = match_regex(device, 'device_name') if not device_name: return False out, rc = run_command( 'cat /sys/block/{}/queue/rotational'.format(device_name)) if rc != 0: return False if int(out) == 1: printf('Device {} is a rotational device'.format(device), print_type=PrintType.DEBUG_LOG) else: printf('Device {} is not a rotational device'.format(device), print_type=PrintType.DEBUG_LOG) return int(out) == 1
def get_schedulers_for_device(device): """Returns a list of available schedulers for a given device. Args: device: The device. Returns: A list of schedulers. """ printf('Retrieving schedulers for device {}'.format(device), print_type=PrintType.DEBUG_LOG) device_name = match_regex(device, 'device_name') out, rc = run_command( 'cat /sys/block/{}/queue/scheduler'.format(device_name)) if rc != 0: printf('Unable to find schedulers for device', print_type=PrintType.ERROR_LOG) return [] ret = out.strip().replace('[', '').replace(']', '') printf('Found the following schedulers for device {}: ' '{}'.format(device, ret), print_type=PrintType.DEBUG_LOG) return ret.split()
def get_device_scheduler(device): """Returns the current scheduler for the device. Args: device: The device. Returns: The current scheduler. """ printf('Retrieving schedulers for device {}'.format(device), print_type=PrintType.DEBUG_LOG) device_name = match_regex(device, 'device_name') out, rc = run_command( 'cat /sys/block/{}/queue/scheduler'.format(device_name)) if rc != 0: printf('Unable to find schedulers for device', print_type=PrintType.ERROR_LOG) return [] l, r = out.index('['), out.index(']') ret = out[l + 1:r] printf('Found the current scheduler for device {}: ' '{}'.format(device, ret), print_type=PrintType.DEBUG_LOG) return ret
def get_device_major_minor(device): """Returns a string of the major, minor of a given device. Args: device: The device. Returns: A string of major,minor. """ printf('Retrieving major,minor for device {}'.format(device), print_type=PrintType.DEBUG_LOG) out, _ = run_command('stat -c \'%%t,%%T\' {}'.format(device)) if not out: printf( 'Unable to retrieve major,minor information for device {}'.format( device), print_Type=PrintType.ERROR_LOG) return None out = out.strip() printf('major,minor for device {} is {}'.format(device, out), print_Type=PrintType.DEBUG_LOG) return out
def change_scheduler(device, scheduler): """Changes the I/O scheduler for the given device. Args: device: The device. scheduler: The I/O scheduler. Returns: True if successful, else False. """ printf('Changing scheduler for device {} to {}'.format(device, scheduler), print_type=PrintType.DEBUG_LOG) command = 'bash -c "echo {} > /sys/block/{}/queue/scheduler"' \ .format(scheduler, match_regex(device, 'device_name')) _, rc = run_command(command) if rc != 0: raise SchedulerChangeError( 'Unable to change scheduler to {} for device {}'.format( scheduler, device))
def change_nomerges(device, nomerges): """Changes the nomerges setting for the given device. Args: device: The device. nomerges: The nomerges setting. Returns: True if successful, else False. """ printf('Changing nomerges for device {} to {}'.format(device, nomerges), print_type=PrintType.DEBUG_LOG) command = 'bash -c "echo {} > /sys/block/{}/queue/nomerges"' \ .format(nomerges, match_regex(device, 'device_name')) _, rc = run_command(command) if rc != 0: raise DeviceSettingChangeError( 'Unable to change nomerges to {} for device {}'.format( nomerges, device))
def change_randomize_va_space(randomize_va_space): """Changes the randomize_va_space setting for the given device. Args: randomize_va_space: The randomize_va_space setting. Returns: True if successful, else False. """ printf('Changing randomize_va_space for system to {}'.format( randomize_va_space), print_type=PrintType.DEBUG_LOG) command = 'bash -c "echo {} > /proc/sys/kernel/randomize_va_space"' \ .format(randomize_va_space) _, rc = run_command(command) if rc != 0: raise SystemSettingChangeError( 'Unable to change randomize_va_space to {} for system'.format( randomize_va_space))
def run_system_command(command, silence=True): """Runs a system command. Args: command: The command. silence: (OPTIONAL) Whether to silence the console output. Defaults to True. Returns: The return code. """ if silence: command = '{} >/dev/null 2>&1'.format(command) printf('Running command {}'.format(command), print_type=PrintType.DEBUG_LOG) try: return os.system(command) except Exception as err: printf('Error occurred running command {}\n{}'.format(command, err), print_type=PrintType.ERROR_LOG) return -1