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 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 upload_firmware_to_s3_bucket(self, localPathToFirmware, firmwareFileName): """ Upload a firmware image to the unsigned S3 bucket associated with this OTA agent. :param localPathToFirmware(str): The path on the machine this script is running of the firmware image. :param firmwareFileName(str): The name of the firmware, this is used as the key in the S3 bucket. """ logger.info('Uploading firmware to S3') self._s3Bucket.upload_file(localPathToFirmware, firmwareFileName)
def on_connect(client, userdata, flags, rc): """ Callback function to notify that the connection is successful """ logger.info( "Connected to IoT core. Device should now disconnect then reconnect." ) self.connected_to_iot = True mqtt_client.disconnect() mqtt_client.loop_stop()
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 _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_job_status(self, jobId): """ Get the status of the OTA Update's job from the AWS IoT jobId. :param jobId(str): The AWS IoT Job ID to get the status of. :return: The job status and the reason for the status in a namedtuple. """ JobStatus = namedtuple('JobStatus', 'status reason') try: response = {} response = self._awsIotClient.describe_job_execution(jobId=jobId, thingName=self.project.thing_name) except Exception as e: logger.warning(f"ID:{jobId[-12:]} aws iot describe-job-execution failed. Err: {e}") return JobStatus('UNDEFINED', 'aws iot describe-job-execution failed.') executionResponse = response['execution'] job_status = JobStatus(executionResponse['status'], executionResponse['statusDetails'].get('detailsMap', {}).get('reason', '')) logger.info(f"ID:{jobId[-12:]} {job_status}") if executionResponse['status'] == "SUCCEEDED" or executionResponse['status'] == "IN_PROGRESS": logger.debug(executionResponse) return job_status
def __init__(self, filename, logfilename=None): Thread.__init__(self) self.print_monitor = False # Set this to true to get output on CLI self._stop_read = False self._exit_run = False self.executable = Path(filename) self.initial_exe = filename self.run_indefinitely = True # Set this to false for 1 run self.file_version = 2 if logfilename: self.log_output = logfilename else: self.log_output = f'{datetime.now().strftime("%m%d%H%S")}_logfile.txt' self.clear_file(self.log_output) logger.info(f'Logging the run to {self.log_output}') self.mlog = setup_logger(name='monitor', file=True, log_file=self.log_output, format='raw', terminator='')
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