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 _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 _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_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 _enter_mode(self, target, keystrokes): """ Try to put the device into the specified mode. Args: keystrokes (string): Path to keystrokes file for booting target (string): Boot target: 'test_mode' or 'service_mode' Raises: aft.errors.AFTDeviceError if device fails to enter the mode or if keyboard emulator fails to connect """ if not (target == "test_mode" or target == "service_mode"): raise errors.AFTDeviceError( "Bad argument: target=" + target + " for pcdevice.py: _enter_mode function") # Sometimes booting to a mode fails. logger.info("Trying to enter " + target + " up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): try: self._power_cycle() if self.kb_emulator: logger.info("Using " + type(self.kb_emulator).__name__ + " to send keyboard sequence " + keystrokes) self.kb_emulator.send_keystrokes(keystrokes) else: logger.warning( "No keyboard emulator defined for the device") ip_address = self._wait_for_responsive_ip() if ip_address: if target == "test_mode" and not \ self._verify_mode(self._service_mode_name): logger.info("Correctly booted target image") return if target == "service_mode" and \ self._verify_mode(self._service_mode_name): logger.info("Correctly booted support image") return else: logger.warning("Failed entering " + target + ".") except KeyboardInterrupt: raise except: _err = sys.exc_info() logger.error(str(_err[0]).split("'")[1] + ": " + str(_err[1])) logger.critical("Unable to get the device in mode " + target) raise errors.AFTDeviceError("Could not set the device in mode " + target)
def _enter_mode(self, target, keystrokes): """ Try to put the device into the specified mode. Args: keystrokes (string): Path to keystrokes file for booting target (string): Boot target: 'test_mode' or 'service_mode' Raises: aft.errors.AFTDeviceError if device fails to enter the mode or if keyboard emulator fails to connect """ if not (target=="test_mode" or target=="service_mode"): raise errors.AFTDeviceError("Bad argument: target=" + target + " for pcdevice.py: _enter_mode function") # Sometimes booting to a mode fails. logger.info("Trying to enter " + target + " up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): try: self._power_cycle() if self.kb_emulator: logger.info("Using " + type(self.kb_emulator).__name__ + " to send keyboard sequence " + keystrokes) self.kb_emulator.send_keystrokes(keystrokes) else: logger.warning("No keyboard emulator defined for the device") ip_address = self._wait_for_responsive_ip() if ip_address: if target == "test_mode" and not \ self._verify_mode(self._service_mode_name): logger.info("Correctly booted target image") return if target == "service_mode" and \ self._verify_mode(self._service_mode_name): logger.info("Correctly booted support image") return else: logger.warning("Failed entering " + target + ".") except KeyboardInterrupt: raise except: _err = sys.exc_info() logger.error(str(_err[0]).split("'")[1] + ": " + str(_err[1])) logger.critical( "Unable to get the device in mode " + target) raise errors.AFTDeviceError( "Could not set the device in mode " + target)
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 _enter_mode(self, mode): """ Try to put the device into the specified mode. Args: mode (Dictionary): Dictionary that contains the mode specific information Returns: None Raises: aft.errors.AFTDeviceError if device fails to enter the mode or if keyboard emulator fails to connect """ # Sometimes booting to a mode fails. logger.info("Trying to enter " + mode["name"] + " mode up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): try: self._power_cycle() if self.kb_emulator: logger.info("Using " + type(self.kb_emulator).__name__ + " to send keyboard sequence " + mode["sequence"]) self.kb_emulator.send_keystrokes(mode["sequence"]) else: logger.warning( "No keyboard emulator defined for the device") ip_address = self._wait_for_responsive_ip() if ip_address: if self._verify_mode(mode["name"]): return else: logger.warning("Failed entering " + mode["name"] + " mode.") except KeyboardInterrupt: raise except: _err = sys.exc_info() logger.error(str(_err[0]).split("'")[1] + ": " + str(_err[1])) logger.critical("Unable to get the device in mode " + mode["name"]) raise errors.AFTDeviceError("Could not set the device in mode " + mode["name"])
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 recovery_flash(self): """ Execute the flashing of device-side DFU-tools Aborts if the flashing fails Note that only one Edison should be powered on when doing the recovery flashing Returns: None """ logger.info("Recovery flashing.") try: # This can cause race condition if multiple devices are booted at # the same time! attempts = 0 xfstk_parameters = ["xfstk-dldr-solo", "--gpflags", "0x80000007", "--osimage", os.path.join( self._MODULE_DATA_PATH, "u-boot-edison.img"), "--fwdnx", os.path.join( self._MODULE_DATA_PATH, "edison_dnx_fwr.bin"), "--fwimage", os.path.join( self._MODULE_DATA_PATH, "edison_ifwi-dbg-00.bin"), "--osdnx", os.path.join( self._MODULE_DATA_PATH, "edison_dnx_osr.bin")] self._power_cycle() while subprocess32.call(xfstk_parameters) and attempts < 10: logger.info( "Rebooting and trying recovery flashing again. " + str(attempts)) self._power_cycle() time.sleep(random.randint(10, 30)) attempts += 1 except subprocess32.CalledProcessError as err: common.log_subprocess32_error_and_abort(err) except OSError as err: logger.critical("Failed recovery flashing, errno = " + str(err.errno) + ". Is the xFSTK tool installed?") sys.exit(1)
def gst_playback(self, device, timeout=_DEFAULT_GST_PLAYBACK_TIMEOUT): """ Attempts to play a media file through the gstreamer interface. """ media_file = self.parameters if not self._deploy_file(payload=media_file, user=self.user, timeout=timeout, device=device): logger.critical("Failed to deploy file: " + str(media_file)) return False # Play the media with gstreamer self.output = device.execute( environment=self._MEDIA_ENV, command=('sudo', '-u', self.user, 'gst-play-1.0', os.path.join(self._DUT_TMP, media_file)), user="******", timeout=timeout) return self._check_for_success()
def _enter_mode(self, mode): """ Try to put the device into the specified mode. Args: mode (Dictionary): Dictionary that contains the mode specific information Returns: None Raises: aft.errors.AFTDeviceError if device fails to enter the mode or if PEM fails to connect """ # Sometimes booting to a mode fails. logger.info( "Trying to enter " + mode["name"] + " mode up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): self._power_cycle() logger.info( "Executing PEM with keyboard sequence " + mode["sequence"]) self._send_PEM_keystrokes(mode["sequence"]) ip_address = self._wait_for_responsive_ip() if ip_address: if self._verify_mode(mode["name"]): return else: logger.warning("Failed entering " + mode["name"] + " mode.") logger.critical( "Unable to get device " + self.dev_id + " in mode " + mode["name"]) raise errors.AFTDeviceError( "Could not set the device in mode " + mode["name"])
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. As only one device can be # powered on during recovery flashing, we just blacklist the device # and recover it later logger.critical( "Bootloader might be broken - blacklisting the " + "device as a precaution (Note: This could be a false positive)" ) common.blacklist_device( self._configuration["id"], self._configuration["name"], "Bootloader might be broken - recovery flashing " + "will be performed as a precaution (Note: This could be a " + "false positive") self._recover_edison() raise errors.AFTDeviceError( "Bootloader might be broken - blacklisting the " + "device as a precaution (Note: This could be a false positive)" ) return True
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. As only one device can be # powered on during recovery flashing, we just blacklist the device # and recover it later logger.critical( "Bootloader might be broken - blacklisting the " + "device as a precaution (Note: This could be a false positive)") common.blacklist_device( self._configuration["id"], self._configuration["name"], "Bootloader might be broken - recovery flashing " + "will be performed as a precaution (Note: This could be a " + "false positive") self._recover_edison() raise errors.AFTDeviceError( "Bootloader might be broken - blacklisting the " + "device as a precaution (Note: This could be a false positive)") return True
def recovery_flash(self): """ Execute the flashing of device-side DFU-tools Aborts if the flashing fails Note that only one Edison should be powered on when doing the recovery flashing Returns: None """ logger.info("Recovery flashing.") try: # This can cause race condition if multiple devices are booted at # the same time! attempts = 0 xfstk_parameters = [ "xfstk-dldr-solo", "--gpflags", "0x80000007", "--osimage", os.path.join(self._MODULE_DATA_PATH, "u-boot-edison.img"), "--fwdnx", os.path.join(self._MODULE_DATA_PATH, "edison_dnx_fwr.bin"), "--fwimage", os.path.join(self._MODULE_DATA_PATH, "edison_ifwi-dbg-00.bin"), "--osdnx", os.path.join(self._MODULE_DATA_PATH, "edison_dnx_osr.bin") ] self._power_cycle() while subprocess32.call(xfstk_parameters) and attempts < 10: logger.info("Rebooting and trying recovery flashing again. " + str(attempts)) self._power_cycle() time.sleep(random.randint(10, 30)) attempts += 1 except subprocess32.CalledProcessError as err: common.log_subprocess32_error_and_abort(err) except OSError as err: logger.critical("Failed recovery flashing, errno = " + str(err.errno) + ". Is the xFSTK tool installed?") sys.exit(1)
def _enter_mode(self, mode): """ Try to put the device into the specified mode. Args: mode (Dictionary): Dictionary that contains the mode specific information Returns: None Raises: aft.errors.AFTDeviceError if device fails to enter the mode or if PEM fails to connect """ # Sometimes booting to a mode fails. logger.info("Trying to enter " + mode["name"] + " mode up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): self._power_cycle() logger.info("Executing PEM with keyboard sequence " + mode["sequence"]) self._send_PEM_keystrokes(mode["sequence"]) ip_address = self._wait_for_responsive_ip() if ip_address: if self._verify_mode(mode["name"]): return else: logger.warning("Failed entering " + mode["name"] + " mode.") logger.critical("Unable to get device " + self.dev_id + " in mode " + mode["name"]) raise errors.AFTDeviceError("Could not set the device in mode " + mode["name"])
def _do_reserve(self, devices, name, timeout): """ Try to reserve and lock a device from devices list. """ if len(devices) == 0: raise errors.AFTConfigurationError( "No device configurations when reserving " + name + " - check that given machine type or name is correct ") start = time.time() while time.time() - start < timeout: for device in devices: logger.info("Attempting to acquire " + device.name) try: # This is a non-atomic operation which may cause trouble # Using a locking database system could be a viable fix. lockfile = os.fdopen( os.open( os.path.join(config.LOCK_FILE, "daft_dut_lock"), os.O_WRONLY | os.O_CREAT, 0o660), "w") fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) logger.info("Device acquired.") self._lockfiles.append(("daft_dut_lock", lockfile)) atexit.register(self.release, device) return device except IOError as err: if err.errno in {errno.EACCES, errno.EAGAIN}: logger.info("Device busy.") else: logger.critical("Cannot obtain lock file.") sys.exit(-1) logger.info( "All devices busy ... waiting 10 seconds and trying again.") time.sleep(10) raise errors.AFTTimeoutError("Could not reserve " + name + " in " + str(timeout) + " seconds.")
def _do_reserve(self, devices, name, timeout): """ Try to reserve and lock a device from devices list. """ if len(devices) == 0: raise errors.AFTConfigurationError( "No device configurations when reserving " + name + " - check that given machine type or name is correct") start = time.time() while time.time() - start < timeout: for device in devices: logger.info("Attempting to acquire " + device.name) try: # This is a non-atomic operation which may cause trouble # Using a locking database system could be a viable fix. lockfile = os.fdopen(os.open(os.path.join(config.LOCK_FILE, "aft_" + device.dev_id), os.O_WRONLY | os.O_CREAT, 0660), "w") fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) logger.info("Device acquired.") self._lockfiles.append((device.dev_id, lockfile)) atexit.register(self.release, device) return device except IOError as err: if err.errno in {errno.EACCES, errno.EAGAIN}: logger.info("Device busy.") else: logger.critical("Cannot obtain lock file.") sys.exit(-1) logger.info("All devices busy ... waiting 10 seconds and trying again.") time.sleep(10) raise errors.AFTTimeoutError("Could not reserve " + name + " in " + str(timeout) + " seconds.")
def _wait_until_ssh_visible(self, timeout=180): """ Wait until the DUT answers to ssh Args: timeout (integer): The timeout value in seconds Returns: None Raises: aft.errors.AFTConnectionError on timeout """ start = time.time() while time.time() - start < timeout: if ssh.test_ssh_connectivity(self.get_ip()): return logger.critical("Failed to establish ssh-connection in " + str(timeout) + " seconds after enabling the network interface.") raise errors.AFTConnectionError( "Failed to establish ssh-connection in " + str(timeout) + " seconds after enabling the network interface.")
def _wait_until_ssh_visible(self, timeout=180): """ Wait until the DUT answers to ssh Args: timeout (integer): The timeout value in seconds Returns: None Raises: aft.errors.AFTConnectionError on timeout """ start = time.time() while time.time() - start < timeout: if ssh.test_ssh_connectivity(self.get_ip()): return logger.critical( "Failed to establish ssh-connection in " + str(timeout) + " seconds after enabling the network interface.") raise errors.AFTConnectionError( "Failed to establish ssh-connection in " + str(timeout) + " seconds after enabling the network interface.")
def _add_usb_networking(self): """ Inject USB-networking service files Returns: None """ logger.info("Injecting USB-networking service.") source_file = os.path.join(self._MODULE_DATA_PATH, self._DUT_USB_SERVICE_FILE) target_file = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_LOCATION, self._DUT_USB_SERVICE_FILE) shutil.copy(source_file, target_file) # Copy UID and GID source_stat = os.stat(source_file) os.chown(target_file, source_stat.st_uid, source_stat.st_gid) # Set symlink to start the service at the end of boot try: os.symlink( os.path.join(os.sep, self._DUT_USB_SERVICE_LOCATION, self._DUT_USB_SERVICE_FILE), os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_LOCATION, "multi-user.target.wants", self._DUT_USB_SERVICE_FILE)) except OSError as err: if err.errno == 17: logger.critical( "The image file was not replaced. USB-networking service " + "already exists.") print("The image file was not replaced! The symlink for " "usb-networking service already exists.") # print "Aborting." # sys.exit(1) else: raise err # Create the service configuration file config_directory = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_CONFIG_DIR) common.make_directory(config_directory) config_file = os.path.join(config_directory, self._DUT_USB_SERVICE_CONFIG_FILE) # Service configuration options config_stream = open(config_file, 'w') config_options = [ "Interface=usb0", "Address=" + self._dut_ip, "MaskSize=30", "Broadcast=" + self._broadcast_ip, "Gateway=" + self._gateway_ip ] for line in config_options: config_stream.write(line + "\n") config_stream.close() # Ignore usb0 in connman original_connman = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_CONNMAN_SERVICE_FILE) output_file = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_CONNMAN_SERVICE_FILE + "_temp") connman_in = open(original_connman, "r") connman_out = open(output_file, "w") for line in connman_in: if "ExecStart=/usr/sbin/connmand" in line: line = line[0:-1] + " -I usb0 \n" connman_out.write(line) connman_in.close() connman_out.close() shutil.copy(output_file, original_connman) os.remove(output_file)
def _add_usb_networking(self): """ Inject USB-networking service files Returns: None """ logger.info("Injecting USB-networking service.") source_file = os.path.join(self._MODULE_DATA_PATH, self._DUT_USB_SERVICE_FILE) target_file = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_LOCATION, self._DUT_USB_SERVICE_FILE) shutil.copy(source_file, target_file) # Copy UID and GID source_stat = os.stat(source_file) os.chown(target_file, source_stat.st_uid, source_stat.st_gid) # Set symlink to start the service at the end of boot try: os.symlink(os.path.join(os.sep, self._DUT_USB_SERVICE_LOCATION, self._DUT_USB_SERVICE_FILE), os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_LOCATION, "multi-user.target.wants", self._DUT_USB_SERVICE_FILE)) except OSError as err: if err.errno == 17: logger.critical( "The image file was not replaced. USB-networking service " + "already exists.") print("The image file was not replaced! The symlink for " "usb-networking service already exists.") # print "Aborting." # sys.exit(1) else: raise err # Create the service configuration file config_directory = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_USB_SERVICE_CONFIG_DIR) common.make_directory(config_directory) config_file = os.path.join(config_directory, self._DUT_USB_SERVICE_CONFIG_FILE) # Service configuration options config_stream = open(config_file, 'w') config_options = ["Interface=usb0", "Address=" + self._dut_ip, "MaskSize=30", "Broadcast=" + self._broadcast_ip, "Gateway=" + self._gateway_ip] for line in config_options: config_stream.write(line + "\n") config_stream.close() # Ignore usb0 in connman original_connman = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_CONNMAN_SERVICE_FILE) output_file = os.path.join(os.curdir, self._LOCAL_MOUNT_DIR, self._DUT_CONNMAN_SERVICE_FILE + "_temp") connman_in = open(original_connman, "r") connman_out = open(output_file, "w") for line in connman_in: if "ExecStart=/usr/sbin/connmand" in line: line = line[0:-1] + " -I usb0 \n" connman_out.write(line) connman_in.close() connman_out.close() shutil.copy(output_file, original_connman) os.remove(output_file)