def _find_active_serial_ports_from(self, wait_duration, device_files): """ Find and returns list of active USB serial ports. This spawns a process that actually does the work. Args: device_files (list of strings): List of device files that will be checked for serial ports. Note that any other device file than ttyUSBx will be ignored. Returns: List of device files that have active serial port. Example: ["ttyUSB2", "ttyUSB4", "ttyUSB7"] """ serial_results = Queue() serial_finder = Process( target=TopologyBuilder._get_active_serial_device_files, args=(self, serial_results, wait_duration, device_files)) if self._verbose: print("Serial thread - Finding active serial ports") logger.info("Finding active serial ports") serial_finder.start() return serial_results
def _enter_test_mode(self): """ Enter test mode by booting from sd card Returns: None Raises: aft.errors.AFTDeviceError if the device failed to enter the test mode """ # device by default boots from sd card, so if everything has gone well, # we can just power cycle to boot the testable image logger.info("Entering test mode") for _ in range(self._TEST_MODE_RETRY_ATTEMPTS): self._power_cycle() self.dev_ip = self._wait_for_responsive_ip() if self.dev_ip and self._verify_mode(self.parameters["test_mode"]): return else: logger.warning("Failed to enter test mode") raise errors.AFTDeviceError("Could not set the device in test mode")
def _create_configuration(self): """ Create and return ConfigParser object containing the device configurations Return: ConfigParser object containing the configurations """ logger.info("Creating configuration object") config = SafeConfigParser() device_ids = {} for device in self._devices: # lack of model generally means that there was an unused power # cutter socket if not "model" in device: continue if not device["model"] in device_ids: device_ids[device["model"]] = 1 dev_id = device_ids[device["model"]] device_ids[device["model"]] = dev_id + 1 section = device["model"].upper() + "_" + str(dev_id) config.add_section(section) for key in device: config.set(section, key, str(device[key])) return config
def _remove_blacklisted_devices(self, devices): """ Remove blacklisted devices from the device list Args: List of devices Returns: Filtered list of devices """ _device_blacklist = self._construct_blacklist() filtered_devices = [] for device in devices: for blacklisted_device in _device_blacklist: if blacklisted_device["id"] == device.dev_id: msg = ("Removed blacklisted device " + blacklisted_device["name"] + " from device pool " + "(Reason: " + blacklisted_device["reason"] + ")") logger.info(msg) print(msg) break else: # else clause for the for loop filtered_devices.append(device) return filtered_devices
def __init__(self, device): self._device = device self.test_cases = [] self._results = [] self._start_time = None self._end_time = None test_plan_name = device.test_plan test_plan_file = os.path.join("/etc/aft/test_plan/", device.test_plan + ".cfg") test_plan_config = ConfigParser.SafeConfigParser() test_plan_config.read(test_plan_file) if len(test_plan_config.sections()) == 0: raise errors.AFTConfigurationError( "Test plan " + str(test_plan_name) + " (" + str(test_plan_file) + ") doesn't " + "have any test cases. Does the file exist?") for test_case_name in test_plan_config.sections(): test_case_config = dict(test_plan_config.items(test_case_name)) test_case_config["name"] = test_case_name test_case = aft.testcasefactory.build_test_case(test_case_config) self.test_cases.append(test_case) logger.info("Built test plan with " + str(len(self.test_cases)) + " test cases.")
def disconnect(self): try: self.rpscontrol.turn_outlet_off(self.cutter_channel) except self.RPSError as e: logger.error(e) logger.error("Unable to turn off outlet " + self.cutter_channel) raise e
def _wait_for_device(self, timeout=15): """ Wait until the testing harness detects the Edison after boot Args: timeout (integer): Timeout for the detection process Returns: None Raises: aft.errors.AFTDeviceError on timeout """ start = time.time() while time.time() - start < timeout: output = subprocess32.check_output( ["dfu-util", "-l", "-d", self._EDISON_DEV_ID]) output_lines = output.decode().split("\n") fitting_lines = [ line for line in output_lines if 'path="' + self._usb_path + '"' in line ] if fitting_lines: return else: continue err_str = "Could not find the device in DFU-mode in " + str(timeout) + \ " seconds." logger.critical(err_str) raise errors.AFTDeviceError(err_str)
def check_poweron(self): """ Checks if device powers on sucessfully by checking if it enters DFU mode correctly Returns: None Raises: aft.errors.AFTDeviceError on failure to connect to the device after running out of retries aft.errors.AFTConfigurationError if for some reason all retries fail and no other exception is raised """ attempts = 3 exception = None for i in range(attempts): logger.info("Attempt " + str(i + 1) + " of " + str(attempts) + " to power on the device " + self._configuration["name"]) try: self._power_cycle() self._wait_for_device() except errors.AFTDeviceError as error: exception = error pass else: return if exception: raise exception raise errors.AFTConfigurationError("Failed to power on the device")
def _find_active_serial_ports_from(self, wait_duration, device_files): """ Find and returns list of active USB serial ports. This spawns a process that actually does the work. Args: device_files (list of strings): List of device files that will be checked for serial ports. Note that any other device file than ttyUSBx will be ignored. Returns: List of device files that have active serial port. Example: ["ttyUSB2", "ttyUSB4", "ttyUSB7"] """ serial_results = Queue() serial_finder = Process( target=TopologyBuilder._get_active_serial_device_files, args=(self, serial_results, wait_duration, device_files), ) if self._verbose: print("Serial thread - Finding active serial ports") logger.info("Finding active serial ports") serial_finder.start() return serial_results
def get_root_partition_path(self, image_file_name): """ Select either the default config value to be the root_partition or if the disk layout file exists, use the rootfs from it. Args: image_file_name (str): The name of the image file. Disk layout file name is based on this Returns: (str): path to the disk pseudo file """ layout_file_name = self.get_layout_file_name(image_file_name) if not os.path.isfile(layout_file_name): logger.info("Disk layout file " + layout_file_name + " doesn't exist. Finding root partition.") return self.find_root_partition() layout_file = open(layout_file_name, "r") disk_layout = json.load(layout_file) rootfs_partition = next( partition for partition in list(disk_layout.values()) \ if isinstance(partition, dict) and \ partition["name"] == "rootfs") return os.path.join("/dev", "disk", "by-partuuid", rootfs_partition["uuid"])
def write_image(self, file_name): """ Writes the new image into the Edison Args: file_name (str): The file name of the image that will be flashed on the device Returns: True Raises: aft.errors.AFTDeviceError on various failures aft.errors.AFTConnectionError if the ssh connection could not be formed """ file_name_no_extension = os.path.splitext(file_name)[0] self._mount_local(file_name_no_extension) self._add_usb_networking() self._add_ssh_key() self._unmount_local() # self._flashing_attempts = 0 # dfu-util may occasionally fail. Extra # attempts could be used? logger.info("Executing flashing sequence.") return self._flash_image(file_name_no_extension)
def get_root_partition_path(self, image_file_name): """ Select either the default config value to be the root_partition or if the disk layout file exists, use the rootfs from it. Args: image_file_name (str): The name of the image file. Disk layout file name is based on this Returns: (str): path to the disk pseudo file """ layout_file_name = self.get_layout_file_name(image_file_name) if not os.path.isfile(layout_file_name): logger.info("Disk layout file " + layout_file_name + " doesn't exist. Finding root partition.") return self.find_root_partition() layout_file = open(layout_file_name, "r") disk_layout = json.load(layout_file) rootfs_partition = next( partition for partition in list(disk_layout.values()) \ if isinstance(partition, dict) and \ partition["name"] == "rootfs") return os.path.join( "/dev", "disk", "by-partuuid", rootfs_partition["uuid"])
def _mount_local(self, file_name_no_extension): """ Mount a image-file to a class-defined folder. Aborts if the mount command fails. Args: file_name_no_extension (str): The file name of the image that will be flashed on the device Returns: None """ logger.info( "Mounting the root partition for ssh-key and USB-networking " + "service injection.") try: common.make_directory(self._LOCAL_MOUNT_DIR) root_file_system_file = file_name_no_extension + "." + \ self._root_extension subprocess32.check_call( ["mount", root_file_system_file, self._LOCAL_MOUNT_DIR]) except subprocess32.CalledProcessError as err: logger.info("Failed to mount.") common.log_subprocess32_error_and_abort(err)
def _deploy_file(self, payload, user, timeout, device): """ Deploys a file to the target device. """ # Test for presence of the file full_path_to_payload = os.path.join(self._TEST_DATA_PATH, payload) if not os.path.isfile(full_path_to_payload): self.output = "Error: media file \"{0}\" not found.".\ format(full_path_to_payload) return False # Push the file to the device self.output = device.push(source=full_path_to_payload, destination=self._DUT_TMP, user=user) if self.output != None: logger.critical("Couldn't copy " + str(full_path_to_payload) + " to " + str(self._DUT_TMP) + ".\n" + str(self.output) .format(full_path_to_payload, self._DUT_TMP, self.output)) return False self.output = device.execute( environment=self._MEDIA_ENV, command=('chown', '-R', self.user, os.path.join(self._DUT_TMP, payload)), user="******", timeout=timeout) return True
def wait_for_responsive_ip_for_pc_device( leases_file_path, timeout, polling_interval): """ Attempt to acquire active ip address for the device with the given mac address up to timeout seconds. Args: leases_file_path (str): Path to dnsmasq leases file timeout (integer): Timeout in seconds polling_interval (integer): Time between retries in seconds. Returns: Ip address as a string, or None if ip address was not responsive. """ logger.info("Waiting for the device to become responsive") logger.debug("Timeout: " + str(timeout)) logger.debug("Polling interval: " + str(polling_interval)) for _ in range(timeout // polling_interval): responsive_ip = get_ip_for_pc_device(leases_file_path) if not responsive_ip: time.sleep(polling_interval) continue logger.info("Got a response from " + responsive_ip) return responsive_ip logger.info("No responsive ip was found")
def _wait_for_device(self, timeout=15): """ Wait until the testing harness detects the Edison after boot Args: timeout (integer): Timeout for the detection process Returns: None Raises: aft.errors.AFTDeviceError on timeout """ start = time.time() while time.time() - start < timeout: output = subprocess32.check_output( ["dfu-util", "-l", "-d", self._EDISON_DEV_ID]) output_lines = output.split("\n") fitting_lines = [ line for line in output_lines if 'path="' + self._usb_path + '"' in line] if fitting_lines: return else: continue err_str = "Could not find the device in DFU-mode in " + str(timeout) + \ " seconds." logger.critical(err_str) raise errors.AFTDeviceError(err_str)
def wait_for_responsive_ip_for_pc_device(leases_file_path, timeout, polling_interval): """ Attempt to acquire active ip address for the device with the given mac address up to timeout seconds. Args: leases_file_path (str): Path to dnsmasq leases file timeout (integer): Timeout in seconds polling_interval (integer): Time between retries in seconds. Returns: Ip address as a string, or None if ip address was not responsive. """ logger.info("Waiting for the device to become responsive") logger.debug("Timeout: " + str(timeout)) logger.debug("Polling interval: " + str(polling_interval)) for _ in range(timeout // polling_interval): responsive_ip = get_ip_for_pc_device(leases_file_path) if not responsive_ip: time.sleep(polling_interval) continue logger.info("Got a response from " + responsive_ip) return responsive_ip logger.info("No responsive ip was found")
def _flash_image(self, file_name_no_extension): """ Flash the new bootloader and image Args: file_name_no_extension (str): Image name without the extension (eg. edison-image.ext4 -> edison-image) Returns: True Raises: errors.aft.AFTDeviceError if flashing fails """ self._power_cycle() try: self._flash_partitions(file_name_no_extension) except errors.AFTPotentiallyBrokenBootloader as err: # if the bootloader is broken, the device is bricked until it is # recovered through recovery flashing. logger.critical("Bootloader might be broken " + "(Note: This could be a false positive)") raise errors.AFTDeviceError( "Bootloader might be broken " + "(Note: This could be a false positive)") return True
def run_remote_command(self, device): """ Executes a command remotely, on the device. """ self.output = device.execute(self.parameters.split(), timeout=120) logger.info("Command: " + str(self.parameters) + "\nresult: " + str(self.output) + ".") return self._check_for_success()
def open_interface(self): """ Open the host's network interface for testing Returns: None """ interface = self._get_usb_nic() ip_subnet = self._host_ip + "/30" logger.info("Opening the host network interface for testing.") # The ifconfig command requires root privileges to run, and in general # we would like to run AFT without root privileges. However, we can add # a shell script to the sudoers file, which allows us to invoke it with # sudo, without the whole program requiring sudo. Hence, the below commands # will succeed even without root privileges # Note: Assumes that this file is under aft/devices, and that the shell # script is under aft/tools interface_script = os.path.join(os.path.dirname(__file__), os.path.pardir, "tools", "interface_script.sh") subprocess32.check_call(["sudo", interface_script, interface, "up"]) subprocess32.check_call( ["sudo", interface_script, interface, ip_subnet])
def __init__(self, config): super(ArduinoKeyboard, self).__init__() logger.set_root_logger_settings() self.emulator_path = config["pem_port"] self.interface = config["pem_interface"]
def _stop_vm(self): if not self._is_powered_on: return logger.info("Stopping the vm") misc.local_execute(( "VBoxManage controlvm " + self._vm_name + " poweroff").split())
def _mount_local(self, file_name_no_extension): """ Mount a image-file to a class-defined folder. Aborts if the mount command fails. Args: file_name_no_extension (str): The file name of the image that will be flashed on the device Returns: None """ logger.info( "Mounting the root partition for ssh-key and USB-networking " + "service injection.") try: common.make_directory(self._LOCAL_MOUNT_DIR) root_file_system_file = file_name_no_extension + "." + \ self._root_extension # guestmount allows us to mount the image without root privileges subprocess32.check_call( ["guestmount", "-a", root_file_system_file, "-m", "/dev/sda", self._LOCAL_MOUNT_DIR]) except subprocess32.CalledProcessError as err: logger.info("Failed to mount.") common.log_subprocess32_error_and_abort(err)
def _get_device_configuration(self, cutter): """ Disconnects a cutter, then checks if any ip, serial port or PEM has stopped responding. These will then be associated with each other. Args: cutter (aft.Cutter): The cutter that will be disconnected Returns: Dictionary containing all the associated information (ports, cutters etc). This varies depending on actual device type and physical connections. Edisons for example use USB networking and have different attributes present as a result. Example dictionary (content can and will vary): { "model": "MinnowboardMAX" "id": "12:34:56:78:90:ab", "cutter": "123456", "channel": "4", "pem_interface": "serialconnection", "pem_port": "/dev/ttyUSB9", "serial_port" = "/dev/ttyUSB2", "serial_bauds": "115200" } """ logger.info("Shutting down a cutter") if self._verbose: print("Disconnected cutter") pprint.pprint(cutter.get_cutter_config()) print("") print("Pinging addresses and checking ports for dead ones") cutter.disconnect() # start the threads as soon as possible so that their results are # available as soon as possible wait_duration = 30 pem_results = self._find_active_pem_ports_from(wait_duration, self._pem_ports) serial_results = self._find_active_serial_ports_from(wait_duration, self._serial_ports) device = {} self._set_device_cutter_config(device, cutter) self._set_device_network_and_type(device) self._set_device_serial_port(device, serial_results) self._set_device_pem_port(device, pem_results) if self._verbose: print("Created device configuration:") print("") pprint.pprint(device) print("") return device
def stop_image_usb_emulation(self, leases_file): """ Stop using the image with USB mass storage emulation """ self.free_dnsmasq_leases(leases_file) local_execute("stop_libcomposite".split()) local_execute("systemctl start libcomposite.service".split()) logger.info("Stopped USB mass storage emulation with an image")
def _power_cycle(self): """ Reboot the device. """ logger.info("Rebooting the device.") self.detach() sleep(self._POWER_CYCLE_DELAY) self.attach()
def __init__(self, config): super(GadgetKeyboard, self).__init__() self.emulator = config["pem_port"] # Initialize path to HID keyboard emulator self.filepath = "" # Initialize filepath for send_keystrokes_from_file() self.delay_between_keys = 0 # Delay (seconds) between keystrokes self.modifier = 0 # On default don't use modifier key self.line_number = 0 # Line number we are parsing from filepath logger.set_process_prefix()
def __init__(self, emulator_path="/dev/hidg0", write_mode="w"): self.emulator = emulator_path # Initialize path to HID keyboard emulator self.write_mode = write_mode # Change send_key() file write mode self.filepath = "" # Initialize filepath for send_keystrokes_from_file() self.delay_between_keys = 0 # Delay (seconds) between keystrokes self.modifier = 0 # On default don't use modifier key self.line_number = 0 # Line number we are parsing from filepath logger.set_process_prefix()
def try_flash_model(self, args): ''' Reserve and flash a machine. By default it tries to flash 2 times, Args: args: AFT arguments Returns: device, tester: Reserved machine and tester handles. ''' device = self.reserve() if args.testplan: device.test_plan = args.testplan tester = Tester(device) if args.record: device.record_serial() if not self.check_libcomposite_service_running(): self.stop_image_usb_emulation(device.leases_file_name) if args.emulateusb: self.start_image_usb_emulation(args, device.leases_file_name) inject_ssh_keys_to_image(args.file_name) return device, tester if args.noflash: return device, tester flash_attempt = 0 flash_retries = args.flash_retries while flash_attempt < flash_retries: flash_attempt += 1 try: print("Flashing " + str(device.name) + ", attempt " + str(flash_attempt) + " of " + str(flash_retries) + ".") device.write_image(args.file_name) print("Flashing successful.") return device, tester except KeyboardInterrupt: raise except: _err = sys.exc_info() _err = str(_err[0]).split("'")[1] + ": " + str(_err[1]) logger.error(_err) print(_err) if (flash_retries - flash_attempt) == 0: print("Flashing failed " + str(flash_attempt) + " times") self.release(device) raise elif (flash_retries - flash_attempt) == 1: print("Flashing failed, trying again one more time") elif (flash_retries - flash_attempt) > 1: print("Flashing failed, trying again " + str(flash_retries - flash_attempt) + " more times")
def __init__(self, config): super(GadgetKeyboard, self).__init__() self.emulator = config[ "pem_port"] # Initialize path to HID keyboard emulator self.filepath = "" # Initialize filepath for send_keystrokes_from_file() self.delay_between_keys = 0 # Delay (seconds) between keystrokes self.modifier = 0 # On default don't use modifier key self.line_number = 0 # Line number we are parsing from filepath logger.set_process_prefix()
def send_a_key(self, key, timeout=20): ''' HID keyboard message length is 8 bytes and format is: [modifier, reserved, Key1, Key2, Key3, Key4, Key6, Key7] So first byte is for modifier key and all bytes after third one are for normal keys. After sending a key stroke, empty message with zeroes has to be sent to stop the key being pressed. Messages are sent by writing to the emulated HID usb port in /dev/. US HID keyboard hex codes are used for translating keys. Args: key: A key to send, for example: "a", "z", "3", "F2", "ENTER" timeout: how long sending a key will be tried until quitting [s] ''' def writer(path, message, empty): while True: try: with open(path, "w") as emulator: emulator.write(message.decode()) # Send the key emulator.write(empty) # Stop the key being pressed except IOError: sleep(1) else: return 0 # Empty message which will be sent to stop any keys being pressed empty = "\x00\x00\x00\x00\x00\x00\x00\x00" usb_message = bytearray(empty.encode()) # Initialize usb message hex_key, _modifier = self.key_to_hex(key) # Translate key to hex code # Override self.modifier if the key needs a specific one if _modifier: modifier = _modifier else: modifier = self.modifier usb_message[2] = hex_key usb_message[0] = modifier # Do the writing in a subprocess as it hangs in some rare cases writer = Process(target=writer, args=(self.emulator, usb_message, empty)) writer.start() writer.join(20) if writer.is_alive(): writer.terminate() msg = "Keyboard emulator couldn't connect to host or it froze" logger.error(msg, "kb_emulator.log") raise TimeoutError(msg) logger.info( "Sent key: " + key.ljust(5) + " hex code: " + format(hex_key, '#04x') + " modifier: " + format(modifier, '#04x'), "kb_emulator.log") return 0
def _save_test_results(self): """ Store the test results. """ logger.info("Storing the test results.") xunit_results = self._results_to_xunit() results_filename = self.get_results_location() with open(results_filename, "w") as results_file: results_file.write(xunit_results) logger.info("Results saved to " + str(results_filename) + ".")
def disconnect(self): """ Turns power off """ try: self._set_gpio_pin(self._GPIO_CUTTER_OFF) except GpioCutterError as e: logger.error(e) logger.error("Unable to set GPIO controlled cutter off") raise e
def _result_has_zero_fails(self): """ Test if there are FAILED test cases in the QA-test case output """ logger.info(self.output) failed_matches = re.findall("FAILED", self.output) result = True if len(failed_matches) > 0: result = False return result
def send_a_key(self, key, timeout=20): ''' HID keyboard message length is 8 bytes and format is: [modifier, reserved, Key1, Key2, Key3, Key4, Key6, Key7] So first byte is for modifier key and all bytes after third one are for normal keys. After sending a key stroke, empty message with zeroes has to be sent to stop the key being pressed. Messages are sent by writing to the emulated HID usb port in /dev/. US HID keyboard hex codes are used for translating keys. Args: key: A key to send, for example: "a", "z", "3", "F2", "ENTER" timeout: how long sending a key will be tried until quitting [s] ''' def writer(path, message, empty): while True: try: with open(path, "w") as emulator: emulator.write(message.decode()) # Send the key emulator.write(empty) # Stop the key being pressed except IOError: sleep(1) else: return 0 # Empty message which will be sent to stop any keys being pressed empty = "\x00\x00\x00\x00\x00\x00\x00\x00" usb_message = bytearray(empty.encode()) # Initialize usb message hex_key, _modifier = self.key_to_hex(key) # Translate key to hex code # Override self.modifier if the key needs a specific one if _modifier: modifier = _modifier else: modifier = self.modifier usb_message[2] = hex_key usb_message[0] = modifier # Do the writing in a subprocess as it hangs in some rare cases writer = Process(target=writer, args=(self.emulator, usb_message, empty)) writer.start() writer.join(20) if writer.is_alive(): writer.terminate() msg = "Keyboard emulator couldn't connect to host or it froze" logger.error(msg, "kb_emulator.log") raise TimeoutError(msg) logger.info("Sent key: " + key.ljust(5) + " hex code: " + format(hex_key, '#04x') + " modifier: " + format(modifier, '#04x'), "kb_emulator.log") return 0
def test_ssh_connectivity(remote_ip, timeout = 10): """ Test whether remote_ip is accessible over ssh. """ try: remote_execute(remote_ip, ["echo", "$?"], connect_timeout = timeout) return True except subprocess32.CalledProcessError as err: logger.warning("Could not establish ssh-connection to " + remote_ip + ". SSH return code: " + str(err.returncode) + ".") return False
def test_ssh_connectivity(remote_ip, timeout=10): """ Test whether remote_ip is accessible over ssh. """ try: remote_execute(remote_ip, ["echo", "$?"], connect_timeout=timeout) return True except subprocess32.CalledProcessError as err: logger.warning("Could not establish ssh-connection to " + remote_ip + ". SSH return code: " + str(err.returncode) + ".") return False
def log_subprocess32_error_and_abort(err): """ Log subprocess32 error cleanly and abort Args: err (subprocess32.CalledProcessError): The exception """ logger.critical(str(err.cmd) + " failed with error code: " + str(err.returncode) + " and output: " + str(err.output)) logger.critical("Aborting") sys.exit(1)
def _do_import_vm(self, ova_appliance): """ Import the .ova appliance and grab the virtual hard drive name and VM name Args: ova_appliance (str): The ova appliance file Returns: None Raises: aft.errors.AFTDeviceError: If hard drive name or vm name were not set """ output = misc.local_execute( ("VBoxManage import " + ova_appliance + "").split()) # Get virtual hard drive name and VM name from output output = output.split("\n") for line in output: # Get hard drive name if "Hard disk image" in line: hdd_path_portion = line.split()[7].split("=") if hdd_path_portion[0] != "path": break self._vhdd = hdd_path_portion[1] if self._vhdd.endswith(","): self._vhdd = self._vhdd[:-1] # get VM name if "Suggested VM name" in line: self._vm_name = line.split()[4] # Strip starting ", if present if self._vm_name.startswith('"'): self._vm_name = self._vm_name[1:] # Strip ending ", if present if self._vm_name.endswith('"'): self._vm_name = self._vm_name[:-1] if self._vhdd and self._vm_name: logger.info("VM name: " + self._vm_name) logger.info("VHDD name: " + self._vhdd) return raise errors.AFTDeviceError( "Failed to find the VM name or virtual hard drive path. Has the " + "VirtualBox output format changed?")
def log_subprocess32_error_and_abort(err): """ Log subprocess32 error cleanly and abort Args: err (subprocess32.CalledProcessError): The exception """ logger.critical( str(err.cmd) + " failed with error code: " + str(err.returncode) + " and output: " + str(err.output)) logger.critical("Aborting") sys.exit(1)
def _unmount_over_ssh(self): """ Unmount the mounted directory at self.mount_dir Returns: None """ logger.info("Unmounting " + self.mount_dir) try: ssh.remote_execute(self.dev_ip, ["umount", self.mount_dir]) except subprocess32.CalledProcessError as err: common.log_subprocess32_error_and_abort(err)
def free_dnsmasq_leases(self, leases_file): """ dnsmasq.leases file needs to be cleared and dnsmasq.service restarted or there will be no IP address to give for DUT if same image is used in quick succession with and without --emulateusb """ local_execute("systemctl stop dnsmasq.service".split()) with open(leases_file, "w") as f: f.write("") f.flush() local_execute("systemctl start dnsmasq.service".split()) logger.info("Freed dnsmasq leases")
def _import_vm(self, ova_appliance): """ Set default VirtualBox directory and import the .ova appliance into VirtualBox as a VM """ logger.info("Importing VM appliance") self._set_default_directory( os.path.join( os.getcwd(), self._VM_DIRECTORY)) self._do_import_vm(ova_appliance)
def _mount_single_layer(self, image_file_name): """ Mount a hdddirect partition Returns: None """ logger.info("Mount one layer.") ssh.remote_execute(self.dev_ip, ["mount", self.get_root_partition_path(image_file_name), self._ROOT_PARTITION_MOUNT_POINT])
def _result_has_zero_fails(self): """ Test if there are FAILED test cases in the QA-test case output """ logger.info(self.output) # qa_log_file = open("results-runtest.py.log", "r") # qa_log = qa_log_file.read() # qa_log_file.close() failed_matches = re.findall("FAILED", self.output) result = True if len(failed_matches) > 0: result = False return result
def execute(self, device): """ Prepare and executes the test case, storing the results. """ start_time = datetime.datetime.now() logger.info("Test case start time: " + str(start_time)) self._prepare() # Test cases are run using the Visitor pattern to allow last-minute # preparation of the device for the test. self.result = device.test(self) self.duration = datetime.datetime.now() - start_time logger.info("Test Duration: " + str(self.duration)) self._build_xunit_section()
def _send_PEM_keystrokes(self, keystrokes, attempts=1, timeout=60): """ Try to send keystrokes within the time limit Args: keystrokes (str): PEM keystroke file attempts (integer): How many attempts will be made timeout (integer): Timeout for a single attempt Returns: None Raises: aft.errors.AFTDeviceError if PEM connection times out """ def call_pem(exceptions): try: pem_main( [ "pem", "--interface", self.pem_interface, "--port", self.pem_port, "--playback", keystrokes ]) except Exception as err: exceptions.put(err) for i in range(attempts): logger.info( "Attempt " + str(i + 1) + " of " + str(attempts) + " to send " + "keystrokes through PEM") exception_queue = Queue() process = Process(target=call_pem, args=(exception_queue,)) # ensure python process is closed in case main process dies but # the subprocess is still waiting for timeout process.daemon = True process.start() process.join(timeout) if not exception_queue.empty(): raise exception_queue.get() if process.is_alive(): process.terminate() else: return raise errors.AFTDeviceError("Failed to connect to PEM")