def run(self): try: while self._stop_read == False: logger.info(f'Running binary: {self.executable}') proc = subprocess.Popen(self.executable, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.executable.parent) with proc.stdout: self._read_simulator_output(proc.stdout) exit_status = proc.wait() if exit_status in [0, -13]: logger.info( f'Application terminated with Exit code:{exit_status}') else: logger.error(f'Closing serial read') self.close() raise RuntimeError( f'Application stopped with Exit code:{exit_status}') if self.run_indefinitely: self.executable = Path( f'{self.initial_exe}_{self.file_version}') self.file_version += 1 cmd = f'sudo chmod 777 {self.executable}'.split() result = subprocess.run(cmd, cwd=self.executable.parent, capture_output=True) print(result.stdout) except Exception as err: # pylint: disable=broad-except logger.error("Unexpected exception: " + str(err)) traceback.print_exc() sys.exit(1)
def build(self): logger.info("Building image") try: cmd = f'cmake -S . -B build && cmake --build build --target {self.ota_firmware_path_used_in_job}' with open( f'{self.ota_firmware_path_used_in_job}_{datetime.now().strftime("%m%d%H%S")}_build_log.txt', 'w') as buildlog: result = subprocess.run(cmd, stdout=buildlog, stderr=STDOUT, shell=True, encoding="utf-8", cwd=str(self.project.repository_root), check=True) file_path = f'{self.project.repository_root}/build/bin/' self.latest_build_firmware_path = f"{file_path}{self.ota_firmware_path_used_in_job}" except Exception as e: logger.error(f"Error occured: {e}") traceback.print_exc() return STATUS.ERROR, '' logger.info("Build completed") return STATUS.PASS, self.latest_build_firmware_path
def download_file(self, key, file_path): try: self._s3_client.Bucket(self.s3_name).download_file(key, file_path) except botocore.exceptions.ClientError as e: if e.response['Error']['Code'] == "404": logger.error("The object does not exist.") else: raise
def delete_ota_update(self, otaUpdateId): """ Cancel the input OTA Update. Cleans up the stream associated. :param otaUpdateId(str): The AWS IoT OTA Update ID to cancel. """ response = {} try: response = self._awsIotClient.delete_ota_update(otaUpdateId=otaUpdateId, deleteStream=True) except Exception as e: logger.error("Unable to delete ota update with ID: " + otaUpdateId) logger.error(f"Response: {response}, Exception: {e}")
def cancel_job(self, jobId): """ Cancel the input job ID. :param jobId(str): The AWS IoT job ID to cancel. """ response = {} try: response = self._awsIotClient.cancel_job(jobId=f'AFR_OTA-{jobId}', comment='OTA integration testing cancellation of incomplete job.', force=True) logger.info(f'AFR_OTA-{jobId} job cancelled') except Exception as e: logger.error("Unable to cancel job with ID: " + jobId) logger.error(f"Response: {response}, Exception: {e}")
def create_update(self, protocols, deployment_files, role_arn=None, url_expired=3600): """ Create an OTA update job. Returns the AWS IoT OTA Update ID. :param deviceImageFileName(str): The full path to the image in the device's file system. For devices not using a file system any string can be put in here. :param streamId (str): The AWS ID of the stream returned from AwsOtaAgent.create_iot_stream(). :param signerJobId(str): The AWS Job ID of the completed AWS Signer operation. This ID was returned from function AwsOtaAgent.sign_firmware_in_s3_bucket(). :param deploymentFiles (dict): An AWS CLI compliant dictionary of the deployment file(s) information to create an OTA update job for. """ # Timeout for the AWS Job service to create an OTA update job. AWS_CREATE_OTA_UPDATE_JOB_TIMEOUT = 60 create_ota_response = {} create_ota_response = self._awsIotClient.create_ota_update( otaUpdateId=str(uuid4()), targets=[ self.project.thing_arn ], targetSelection='SNAPSHOT', roleArn=self.project.ota_update_role_arn, files=deployment_files, protocols=protocols, awsJobPresignedUrlConfig={ 'expiresInSec': url_expired } ) # Confirm that the OTA update job is ready. timeout_end = time.perf_counter() + AWS_CREATE_OTA_UPDATE_JOB_TIMEOUT ota_create_in_progress = True ota_update_info = None while ota_create_in_progress and time.perf_counter() < timeout_end: time.sleep(1) otaGetStatusResponse = {} otaGetStatusResponse = self._awsIotClient.get_ota_update( otaUpdateId = create_ota_response.get('otaUpdateId')) ota_update_info = otaGetStatusResponse.get('otaUpdateInfo') if ota_update_info.get('otaUpdateStatus') in ('CREATE_COMPLETE', 'CREATE_FAILED'): ota_create_in_progress = False if ota_create_in_progress == True: logger.error(f"Error: OTA update creation timed out for OTA update ID {ota_update_info.get('otaUpdateId')}") return None # Check for errors and show us what those errors might be. if ota_update_info.get('otaUpdateStatus') != 'CREATE_COMPLETE': logger.error(f"OTA update creation failed for OTA update ID {ota_update_info.get('otaUpdateId')}") if ('errorInfo' in ota_update_info): logger.error(f"Code: {ota_update_info.get('errorInfo').get('code')}") logger.error(f"Details: {ota_update_info.get('errorInfo').get('message')}") else: logger.info(f"Created OTA Update ID {ota_update_info.get('otaUpdateId')} (AWS IoT job ID = {ota_update_info.get('awsIotJobId')}).") return ota_update_info.get('otaUpdateId')
def _read_simulator_output(self, outStream): for line in iter(outStream.readline, b''): try: line = line.decode() if self.print_monitor: logger.info(line) self.mlog.info(line) if self._exit_run: return except UnicodeDecodeError: logger.warn(f"Unable to debug line: {line}") except Exception as e: logger.error(f"Exception in binary: {e}") self._stop_read = True return
def get_ota_update_result(self, ota_update_id, timeout, sleep_time=5): ''' Non blocking call to get ota update status. Function return upon job completion. :param ota_update_id: AWS OTA job id :param timeout: Time after which the job fails :return: Status os the update once it is finished ''' seconds = 0 summary = None # Get the AWS Iot Job ID response = {} response = self._awsIotClient.get_ota_update(otaUpdateId=ota_update_id) job_id = response['otaUpdateInfo']['awsIotJobId'] finished_job_statuses = {'CANCELED', 'SUCCEEDED', 'FAILED', 'REJECTED', 'REMOVED'} start = time.perf_counter() summary = None while True: job_status = self.get_job_status(job_id) if job_status.status in finished_job_statuses: # logger.error(f"Version received from the cloud is : {self.get_version_number(job_id)}") break else: time.sleep(sleep_time) end = time.perf_counter() if end - start > timeout: logger.error(f"Timeout on OTA Update's job. (Timeout setting: {timeout})") logger.info(f'start: {start}, end: {end}') self.cancel_job(job_id) summary = 'Timeout on OTA Update\'s job.' break self.delete_ota_update(ota_update_id) return job_status, summary