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 _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 _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): try: 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") except KeyboardInterrupt: raise except: _err = sys.exc_info() logger.error(str(_err[0]).split("'")[1] + ": " + str(_err[1])) raise errors.AFTDeviceError("Could not set the device in test mode")
def find_root_partition(self): ''' Find _target_device partition that has /home/root ''' # Find all _target_device partitions partitions = [] target = self._target_device.split("/")[-1] lsblk = ssh.remote_execute(self.dev_ip, ["lsblk"]) lsblk = lsblk.split() for line in lsblk: if (target + "p") in line: line = ''.join(x for x in line if x.isalnum()) partitions.append(line) # Check through partitions if it contains '/home/root' directory for partition in partitions: ssh.remote_execute(self.dev_ip, [ "mount", "/dev/" + partition, self._ROOT_PARTITION_MOUNT_POINT ]) files = ssh.remote_execute( self.dev_ip, ["ls", self._ROOT_PARTITION_MOUNT_POINT]) if "home" in files: files = ssh.remote_execute( self.dev_ip, ["ls", self._ROOT_PARTITION_MOUNT_POINT + "home/"]) ssh.remote_execute(self.dev_ip, ["umount", self._ROOT_PARTITION_MOUNT_POINT]) if "root" in files: partition_path = "/dev/" + partition return partition_path raise errors.AFTDeviceError("Couldn't find root partition")
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 _start_vm(self): if self._is_powered_on: return output = misc.local_execute(( "VBoxManage startvm " + self._vm_name + " --type headless").split()) if "error" in output: raise errors.AFTDeviceError("Failed to start the VM:\n" + output) self._is_powered_on = True
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 send_keystrokes(self, _file): """ Method to send keystrokes from a file """ try: kbemu = self.kbemucontrol.KBEMUControl(_file, kbemu_model ='usbkm232') kbemu.open() kbemu.perform('seq') except: raise errors.AFTDeviceError("KM232 Keyboard emulator failed.")
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")
def _flash_image(self): """ Flash boot and root partitions Returns: None """ logger.info("Flashing image") if not self.dev_ip: logger.warning("Unable to get ip address for the device") raise errors.AFTDeviceError( "Could not get device ip (dhcp error or device " + "failed to boot?)") self._write_boot_partition() self._write_root_partition()
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 _get_usb_nic(self, timeout=120): """ Search and return for the network interface attached to the DUT's USB-path Args: timeout (integer): The timeout value in seconds Returns: (str): The usb network interface Raises: aft.errors.AFTDeviceError if USB network interface was not found """ logging.info( "Searching for the host network interface from usb path " + self._usb_path) start = time.time() while time.time() - start < timeout: interfaces = netifaces.interfaces() for interface in interfaces: try: # Test if the interface is the correct USB-ethernet NIC nic_path = os.path.realpath(os.path.join( self._NIC_FILESYSTEM_LOCATION, interface)) usb_path = _get_nth_parent_dir(nic_path, 3) if os.path.basename(usb_path) == self._usb_path: return interface except IOError as err: print "IOError: " + str(err.errno) + " " + err.message print ( "Error likely caused by jittering network interface." " Ignoring.") logging.warning( "An IOError occured when testing network interfaces. " + " IOERROR: " + str(err.errno) + " " + err.message) time.sleep(1) raise errors.AFTDeviceError( "Could not find a network interface from USB-path " + self._usb_path + " in 120 seconds.")
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. logging.info("Trying to enter " + mode["name"] + " mode up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): self._power_cycle() logging.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: logging.warning("Failed entering " + mode["name"] + " mode.") logging.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 send_keystrokes(self, _file): """ Method to send keystrokes from a file Args: keystrokes (str): PEM keystroke file Returns: None Raises: aft.errors.AFTDeviceError if PEM connection times out """ from pem.main import main as pem_main def call_pem(exceptions): try: pem_main([ "pem", "--interface", self.interface, "--port", self.emulator_path, "--playback", _file ]) except Exception as err: exceptions.put(err) 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(60) if not exception_queue.empty(): raise exception_queue.get() if process.is_alive(): process.terminate() else: return raise errors.AFTDeviceError("Failed to connect to Arduino keyboard " + "emulator - check the connections, " + "AFT settings and emulator hardware")
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, 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 logging.critical( "Bootloader seems to have been broken - blacklisting the " + "device") common.blacklist_device( self._configuration["id"], self._configuration["name"], "Bootloader seems to be broken - recovery flashing " + "required") self._recover_edison() raise errors.AFTDeviceError( "Bootloader seems to have been broken - blacklisting the " + "device")
def _find_mac_address(self): """ Find VM mac address from showvminfo output Returns: None Raises: aft.errors.AFTDeviceError: If mac address could not be found from showvminfo output """ output = misc.local_execute( ("VBoxManage showvminfo " + self._vm_name).split()) output = output.split("\n") # should contain line like: # NIC 1: MAC: 080027F3FDC2, Attachment: Host-only Interface 'vboxnet0', # Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported # speed: 0 Mbps, Boot priority: 0, Promisc Policy: deny, Bandwidth # group: none # # We grab the mac address from it for line in output: if " MAC: " in line: self._mac_address = line.split()[3] if self._mac_address.endswith(","): self._mac_address = self._mac_address[:-1] # Add colons after every two symbols as_array = [self._mac_address[i:i+2] for i in range(0, len(self._mac_address), 2)] self._mac_address = ":".join(as_array) logger.info("Device mac address: " + self._mac_address) return raise errors.AFTDeviceError( "Failed to find mac address from showvminfo output. Has the " + "output format changed")
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 logging.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: logging.warning("Failed to enter test mode") raise errors.AFTDeviceError("Could not set the device in test mode")
def _run_tests_on_know_good_image(args, device): if device.model.lower() == "edison": return (True, "Skipped - produces too many false negatives") if args.verbose: print("Flashing and testing a known good image") logger.info("Flashing and testing a known good image") image_directory_path = os.path.join(config.KNOWN_GOOD_IMAGE_FOLDER, device.model.lower()) image = None if device.model.lower() == "beagleboneblack": image = image_directory_path elif device.model.lower() == "edison": image = os.path.join(image_directory_path, "ostro-image-edison.ext4") else: image = os.path.join(image_directory_path, "good-image.dsk") if args.verbose: print("Image file: " + str(image)) print("Flashing " + str(device.name)) logger.info("Image file: " + str(image)) try: results_queue = multiprocessing_queue() def worker(results): os.chdir(os.path.join(image_directory_path, "iottest")) # nuke test logs for f in os.listdir("."): if "ssh_target_log" in f: os.remove(f) os.chdir(image_directory_path) ## Remove all log and xml files from previous run to prevent clutter #for f in os.listdir("."): # if f.endswith(".log") or f.endswith(".xml"): # os.remove(f) device.write_image(image) tester = Tester(device) tester.execute() results.put((tester.get_results(), tester.get_results_str())) # Run this in a separate process, so that we can change its working # directory, without changing the working directory for the rest of # the program process = Process(target=worker, args=(results_queue, )) process.start() process.join() if results_queue.empty(): raise errors.AFTDeviceError("No results from test run") results = results_queue.get() result = reduce(lambda x, y: x and y, results[0]) result_str = "Image test result: " if result: result_str += "Ok" else: result_str += " Failure(s): \n\n" + results[1] return (result, result_str) except Exception as error: return (False, "Image Test result: " + str(error))
def _enter_test_mode(self): logger.info("Entering test mode") self._set_host_only_nic() self._start_vm() if self.get_ip() == None: raise errors.AFTDeviceError("Failed to get responsive ip")
class PCDevice(Device): """ Class representing a PC-like device. Attributes: _RETRY_ATTEMPTS (integer): How many times the device attempts to enter the requested mode (testing/service) before givingup _BOOT_TIMEOUT (integer): The device boot timeout. Used when waiting for responsive ip address _POLLING_INTERVAL (integer): The polling interval used when waiting for responsive ip address _SSH_IMAGE_WRITING_TIMEOUT (integer): The timeout for flashing the image. _IMG_NFS_MOUNT_POINT (str): The location where the service OS mounts the nfs filesystem so that it can access the image file etc. _ROOT_PARTITION_MOUNT_POINT (str): The location where the service OS mounts the image root filesystem for SSH key injection. _SUPER_ROOT_MOUNT_POINT (str): Mount location used when having to mount two layers """ _RETRY_ATTEMPTS = 8 _BOOT_TIMEOUT = 240 _POLLING_INTERVAL = 10 _SSH_IMAGE_WRITING_TIMEOUT = 1440 _IMG_NFS_MOUNT_POINT = "/mnt/img_data_nfs" _ROOT_PARTITION_MOUNT_POINT = "/mnt/target_root/" _SUPER_ROOT_MOUNT_POINT = "/mnt/super_target_root/" def __init__(self, parameters, channel): """ Constructor Args: parameters (Dictionary): Device configuration parameters channel (aft.Cutter): Power cutter object """ super(PCDevice, self).__init__(device_descriptor=parameters, channel=channel) self.retry_attempts = 8 self._leases_file_name = parameters["leases_file_name"] self.default_root_patition = parameters["root_partition"] self._service_mode_name = parameters["service_mode"] self._test_mode_name = parameters["test_mode"] self.pem_interface = parameters["pem_interface"] self.pem_port = parameters["pem_port"] self._test_mode = { "name": self._test_mode_name, "sequence": parameters["test_mode_keystrokes"] } self._service_mode = { "name": self._service_mode_name, "sequence": parameters["service_mode_keystrokes"] } self._target_device = \ parameters["target_device"] self._config_check_keystrokes = parameters["config_check_keystrokes"] self.dev_ip = None self._uses_hddimg = None # pylint: disable=no-self-use # pylint: enable=no-self-use def write_image(self, file_name): """ Method for writing an image to a device. Args: file_name (str): The file name of the image that will be flashed on the device Returns: None """ # NOTE: it is expected that the image is located somewhere # underneath config.NFS_FOLDER (default: /home/tester), # therefore symlinks outside of it will not work # The config.NFS_FOLDER path is exported as nfs and mounted remotely as # _IMG_NFS_MOUNT_POINT # Bubblegum fix to support both .hddimg and .hdddirect at the same time self._uses_hddimg = os.path.splitext(file_name)[-1] == ".hddimg" self._enter_mode(self._service_mode) file_on_nfs = os.path.abspath(file_name).replace( config.NFS_FOLDER, self._IMG_NFS_MOUNT_POINT) self._flash_image(nfs_file_name=file_on_nfs) self._install_tester_public_key(file_name) def _run_tests(self, test_case): """ Boot to test-mode and execute testplan. Args: test_case (aft.TestCase): Test case object Returns: The return value of the test_case run()-method (implementation class specific) """ self._enter_mode(self._test_mode) return test_case.run(self) def get_ip(self): """ Returns device ip address Returns: (str): The device ip address """ return common.get_ip_for_pc_device(self.dev_id, self.parameters["leases_file_name"]) 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. logging.info("Trying to enter " + mode["name"] + " mode up to " + str(self._RETRY_ATTEMPTS) + " times.") for _ in range(self._RETRY_ATTEMPTS): self._power_cycle() logging.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: logging.warning("Failed entering " + mode["name"] + " mode.") logging.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 _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, err: exceptions.put(err) for i in range(attempts): logging.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")
def _dfu_call(self, alt, source, extras=[], attempts=4, timeout=1800, ignore_errors=False): """ Call DFU-util successively with arguments until it succeeds Args: alt (str): The --alt-argument for the dfu-util program. See relevant man page for more information on dfu-util and its arguments. source (str): The source file, which will be flashed extras (list(str)): Extra arguments for the dfu-util program attempts (interer): How many times flashing will be attempted timeout (integer): The timeout value for a single flashing attempt ignore_errors (boolean): Ignores error codes from dfu-util. This is a workaround for flashing IFWI. Original flashall script checks if the usb device is present before attempting to flash by checking that vendor and device USB ids are present. This however does not work here, as we may have multipe Edisons with identical vendor and device ids. Instead, we just try to flash the partition, and ignore the errors that occur when the device isn't present. Returns: None Raises: aft.errors.AFTDeviceError if flashing has not succeeded after the number of attempts specified by the method argument """ attempt = 0 while attempt < attempts: flashing_log_file = open(self._FLASHER_OUTPUT_LOG, "a") self._wait_for_device() execution = subprocess32.Popen([ "dfu-util", "-v", "--path", self._usb_path, "--alt", alt, "-D", source ] + extras, stdout=flashing_log_file, stderr=flashing_log_file) start = time.time() while time.time() - start < timeout: status = execution.poll() if status == None: continue else: flashing_log_file.close() if not ignore_errors and execution.returncode != 0: logger.warning("Return value was non-zero - retrying") break # dfu-util does not return non-zero value when flashing # fails due to download error. Instead, check if last few # lines in the log contain "Error during download" with open(self._FLASHER_OUTPUT_LOG) as flash_log: last_lines = flash_log.readlines()[-10:] break_outer = False for line in last_lines: if "Error during download" in line: logger.warning("Error in log - retrying") break_outer = True break if break_outer: break return try: execution.kill() except OSError as err: if err.errno == 3: # 3 = errno.ESRCH = no such process pass else: raise attempt += 1 if time.time() - start >= timeout: logger.warning("Flashing timeout") logger.warning("Flashing failed on alt " + alt + " for file " + source + " on USB-path " + self._usb_path + ". Rebooting and attempting again for " + str(attempt) + "/" + str(attempts) + " time.") self._power_cycle() flashing_log_file.close() raise errors.AFTDeviceError("Flashing failed " + str(attempts) + " times. Raising error (aborting).")
def _dfu_call( self, alt, source, extras=[], attempts=4, timeout=600, ignore_errors=False): """ Call DFU-util successively with arguments until it succeeds Args: alt (str): The --alt-argument for the dfu-util program. See relevant man page for more information on dfu-util and its arguments. source (str): The source file, which will be flashed extras (list(str)): Extra arguments for the dfu-util program attempts (interer): How many times flashing will be attempted timeout (integer): The timeout value for a single flashing attempt ignore_errors (boolean): Ignores error codes from dfu-util. This is a workaround for flashing IFWI. Original flashall script checks if the usb device is present before attempting to flash by checking that vendor and device USB ids are present. This however does not work here, as we may have multipe Edisons with identical vendor and device ids. Instead, we just try to flash the partition, and ignore the errors that occur when the device isn't present. Returns: None Raises: aft.errors.AFTDeviceError if flashing has not succeeded after the number of attempts specified by the method argument """ attempt = 0 while attempt < attempts: flashing_log_file = open(self._FLASHER_OUTPUT_LOG, "a") self._wait_for_device() execution = subprocess32.Popen( [ "dfu-util", "-v", "--path", self._usb_path, "--alt", alt, "-D", source ] + extras, stdout=flashing_log_file, stderr=flashing_log_file) start = time.time() while time.time() - start < timeout: status = execution.poll() if status == None: continue else: flashing_log_file.close() if ignore_errors or execution.returncode == 0: return # There was a warning here that dfu-util always returns 0 # and as such we should grep ~5 last lines in flash log for # 'Done!' instead. However, my brief experimentation with # dfu-util seems to indicate that dfu-util infact does # return nonzero value on failure, and as such I removed # this log check in favor of return value check. I'm leaving # this comment here in case this turns out to be a bad idea # and some future maintainer can revert this decision break try: execution.kill() except OSError as err: if err.errno == 3: # 3 = errno.ESRCH = no such process pass else: raise attempt += 1 logging.warning( "Flashing failed on alt " + alt + " for file " + source + " on USB-path " + self._usb_path + ". Rebooting and attempting again for " + str(attempt) + "/" + str(attempts) + " time.") self._power_cycle() flashing_log_file.close() raise errors.AFTDeviceError( "Flashing failed " + str(attempts) + " times. Raising error (aborting).")
def _enter_service_mode(self): """ Enter service mode by booting support image over nfs Interrupts regular boot, and enters the necessary u-boot commands to boot the nfs based support image rather than the image stored on the SD-card Returns: None Raises: aft.errors.AFTDeviceError if the device failed to enter the service mode """ logging.info("Trying to enter service mode up to " + str(self._SERVICE_MODE_RETRY_ATTEMPTS) + " times.") for _ in range(self._SERVICE_MODE_RETRY_ATTEMPTS): self._power_cycle() tftp_path = self.parameters["support_fs"] kernel_image_path = self.parameters["support_kernel_path"] dtb_path = self.parameters["support_dtb_path"] console = "ttyO0,115200n8" stream = serial.Serial(self.parameters["serial_port"], self.parameters["serial_bauds"], timeout=0.01, xonxoff=True) counter = 100 # enter uboot console for _ in range(counter): serial_write(stream, " ", 0.1) # if autoload is on, dhcp command attempts to download kernel # as well. We do this later manually over tftp serial_write(stream, "setenv autoload no", 1) # get ip from dhcp server # NOTE: This seems to occasionally fail. This doesn't matter # too much, as the next retry attempt propably works. serial_write(stream, "dhcp", 15) # setup kernel boot arguments (nfs related args and console so that # process is printed in case something goes wrong) serial_write( stream, "setenv bootargs console=" + console + ", root=/dev/nfs nfsroot=${serverip}:" + self.nfs_path + ",vers=3 rw ip=${ipaddr}", 1) # download kernel image into the specified memory address serial_write( stream, "tftp 0x81000000 " + os.path.join(tftp_path, kernel_image_path), 15) # download device tree binary into the specified memory location # IMPORTANT NOTE: Make sure that the kernel image and device tree # binary files do not end up overlapping in the memory, as this # ends up overwriting one of the files and boot unsurprisingly fails serial_write( stream, "tftp 0x80000000 " + os.path.join(tftp_path, dtb_path), 5) # boot, give kernel image and dtb as args (middle arg is ignored, # hence the '-') serial_write(stream, "bootz 0x81000000 - 0x80000000", 1) stream.close() self.dev_ip = self._wait_for_responsive_ip() if (self.dev_ip and self._verify_mode(self.parameters["service_mode"])): return else: logging.warning("Failed to enter service mode") raise errors.AFTDeviceError("Could not set the device in service mode")
def _enter_service_mode(self): """ Enter service mode by booting support image over nfs Interrupts regular boot, and enters the necessary u-boot commands to boot the nfs based support image rather than the image stored on the SD-card Returns: None Raises: aft.errors.AFTDeviceError if the device failed to enter the service mode """ logger.info("Trying to enter service mode up to " + str(self._SERVICE_MODE_RETRY_ATTEMPTS) + " times.") for _ in range(self._SERVICE_MODE_RETRY_ATTEMPTS): try: self._power_cycle() stream = serial.Serial(self.parameters["serial_port"], self.parameters["serial_bauds"], timeout=0.01, xonxoff=True) counter = 100 # enter uboot console for _ in range(counter): serial_write(stream, " ", 0.1) # Load kernel image and device tree binary from usb serial_write( stream, "setenv bootargs 'console=ttyO0,115200n8, root=/dev/sda1 rootwait rootfstype=ext4 rw'", 1) serial_write(stream, "usb start", 5) serial_write( stream, "ext4load usb 0:1 0x81000000 /boot/vmlinuz-4.4.9-ti-r25", 10) serial_write( stream, "ext4load usb 0:1 0x80000000 /boot/dtbs/4.4.9-ti-r25/am335x-boneblack.dtb", 5) stream.close() self.dev_ip = self._wait_for_responsive_ip() if (self.dev_ip and self._verify_mode( self.parameters["service_mode"])): return else: logger.warning("Failed to enter service mode") except KeyboardInterrupt: raise except: _err = sys.exc_info() logger.error(str(_err[0]).split("'")[1] + ": " + str(_err[1])) raise errors.AFTDeviceError("Could not set the device in service mode")