def __setup_app_instance(self, instances, step): device_id = (step.app, step.device) if device_id not in instances: instances[device_id] = AppInstance.create(step.app, step.device) data_sent.send(self, callback_name="App Instance Created", object_type="Workflow") logger.debug('Created new app instance: App {0}, device {1}'.format(step.app, step.device)) return device_id
def __swap_step_input(self, step, start_input): logger.debug('Swapping input to first step of workflow {0}'.format(self.name)) try: step.set_input(start_input) data_sent.send(self, callback_name="Workflow Input Validated", object_type="Workflow") except InvalidInput as e: logger.error('Cannot change input to workflow {0}. ' 'Invalid input. Error: {1}'.format(self.name, format_exception_message(e))) data_sent.send(self, callback_name="Workflow Input Invalid", object_type="Workflow")
def execute(self, execution_uid, start=None, start_input=''): """Executes a Workflow by executing all Steps in the Workflow list of Step objects. Args: execution_uid (str): The UUID4 hex string uniquely identifying this workflow instance start (str, optional): The name of the first Step. Defaults to None. start_input (str, optional): Input into the first Step. Defaults to an empty string. """ self._execution_uid = execution_uid logger.info('Executing workflow {0}'.format(self.name)) data_sent.send(self, callback_name="Workflow Execution Start", object_type="Workflow") start = start if start is not None else self.start executor = self.__execute(start, start_input) next(executor)
def get_next_step(self, accumulator): """Gets the NextStep object to be executed after the current Step. Args: accumulator (dict): A record of teh previously-executed steps. Of form {step_name: result} Returns: The NextStep object to be executed. """ for next_step in self.next_steps: next_step = next_step.execute(self._output, accumulator) if next_step is not None: self._next_up = next_step data_sent.send(self, callback_name="Conditionals Executed", object_type="Step") return next_step
def __execute_step(self, step, instance): data = {"app": step.app, "action": step.action, "name": step.name, "input": step.inputs} try: step.execute(instance=instance(), accumulator=self._accumulator) data['result'] = step.get_output().as_json() if step.get_output() is not None else None data['execution_uid'] = step.get_execution_uid() data_sent.send(self, callback_name="Step Execution Success", object_type="Workflow", data=json.dumps(data)) except Exception as e: data['result'] = step.get_output().as_json() if step.get_output() is not None else None data['execution_uid'] = step.get_execution_uid() data_sent.send(self, callback_name="Step Execution Error", object_type="Workflow", data=json.dumps(data)) if self._total_risk > 0: self.accumulated_risk += float(step.risk) / self._total_risk logger.debug('Step {0} of workflow {1} executed with error {2}'.format(step, self.name, format_exception_message(e)))
def execute(self, data_in, accumulator): if data_in is not None and data_in.status == self.status: if all( flag.execute(data_in=data_in.result, accumulator=accumulator) for flag in self.flags): data_sent.send(self, callback_name="Next Step Taken", object_type="NextStep") logger.debug('NextStep is valid for input {0}'.format(data_in)) return self.name else: logger.debug( 'NextStep is not valid for input {0}'.format(data_in)) data_sent.send(self, callback_name="Next Step Not Taken", object_type="NextStep") return None else: return None
def __execute(self, start, start_input): instances = {} total_steps = [] steps = self.__steps(start=start) first = True for step in (step_ for step_ in steps if step_ is not None): self._executing_step = step logger.debug('Executing step {0} of workflow {1}'.format(step, self.name)) data_sent.send(self, callback_name="Next Step Found", object_type="Workflow") if self._is_paused: data_sent.send(self, callback_name="Workflow Paused", object_type="Workflow") while self._is_paused: gevent.sleep(1) continue data_sent.send(self, callback_name="Workflow Resumed", object_type="Workflow") device_id = self.__setup_app_instance(instances, step) step.render_step(steps=total_steps) if first: first = False if start_input: self.__swap_step_input(step, start_input) self.__execute_step(step, instances[device_id]) total_steps.append(step) self._accumulator[step.name] = step.get_output().result self.__shutdown(instances) yield
def __shutdown(self, instances): # Upon finishing shuts down instances for instance in instances: try: logger.debug('Shutting down app instance: Device: {0}'.format(instance)) instances[instance].shutdown() except Exception as e: logger.error('Error caught while shutting down app instance. ' 'Device: {0}. Error {1}'.format(instance, format_exception_message(e))) result_str = {} for step, step_result in self._accumulator.items(): try: result_str[step] = json.dumps(step_result) except TypeError: logger.error('Result of workflow is neither string or a JSON-able. Cannot record') result_str[step] = 'error: could not convert to JSON' data = dict(self._accumulator) try: data_json = json.dumps(data) except TypeError: data_json = str(data) data_sent.send(self, callback_name="Workflow Shutdown", object_type="Workflow", data=data_json) logger.info('Workflow {0} completed. Result: {1}'.format(self.name, self._accumulator))
def execute(self, data_in, accumulator): """Executes the flag. Args: data_in: The input to the flag. Typically from the last step of the workflow or the input to a trigger. accumulator (dict): A record of executed steps and their results. Of form {step_name: result}. Returns: (bool): Is the flag true for the given data and accumulator """ original_data_in = deepcopy(data_in) try: data_in = validate_parameter(data_in, self._data_in_api, 'Filter {0}'.format(self.action)) args = dereference_step_routing(self.args, accumulator, 'In Filter {0}'.format(self.uid)) args.update({self._data_in_api['name']: data_in}) result = get_filter(self.action)(**args) data_sent.send(self, callback_name="Filter Success", object_type="Filter") return result except InvalidInput as e: data_sent.send(self, callback_name="Filter Error", object_type="Filter") logger.error('Filter {0} has invalid input {1}. Error: {2}. ' 'Returning unmodified data'.format( self.action, original_data_in, str(e))) except Exception as e: data_sent.send(self, callback_name="Filter Error", object_type="Filter") logger.error( 'Filter {0} encountered an error: {1}. Returning unmodified data' .format(self.action, str(e))) return original_data_in
def execute(self, data_in, accumulator): data = data_in for filter_element in self.filters: data = filter_element.execute(data, accumulator) try: data = validate_parameter(data, self._data_in_api, 'Flag {}'.format(self.action)) args = dereference_step_routing(self.args, accumulator, 'In Flag {}'.format(self.uid)) data_sent.send(self, callback_name="Flag Success", object_type="Flag") logger.debug('Arguments passed to flag {} are valid'.format( self.uid)) args.update({self._data_in_api['name']: data}) return get_flag(self.action)(**args) except InvalidInput as e: logger.error( 'Flag {0} has invalid input {1} which was converted to {2}. Error: {3}. ' 'Returning False'.format(self.action, data_in, data, format_exception_message(e))) data_sent.send(self, callback_name="Flag Error", object_type="Flag") return False except Exception as e: logger.error('Error encountered executing ' 'flag {0} with arguments {1} and value {2}: ' 'Error {3}. Returning False'.format( self.action, self.args, data, format_exception_message(e))) data_sent.send(self, callback_name="Flag Error", object_type="Flag") return False
def execute(self, instance, accumulator): """Executes a Step by calling the associated app function. Args: instance (App): The instance of an App object to be used to execute the associated function. accumulator (dict): Dict containing the results of the previous steps Returns: The result of the executed function. """ self._execution_uid = uuid.uuid4().hex data_sent.send(self, callback_name="Step Started", object_type="Step") if self.triggers: data_sent.send(self, callback_name="Trigger Step Awaiting Data", object_type="Step") logger.debug('Trigger Step {} is awaiting data'.format(self.name)) while True: try: data = self._incoming_data.get(timeout=1) self._incoming_data = AsyncResult() except gevent.Timeout: gevent.sleep(0.1) continue data_in = data['data_in'] inputs = data['inputs'] if 'inputs' in data else {} if all( flag.execute(data_in=data_in, accumulator=accumulator) for flag in self.triggers): data_sent.send(self, callback_name="Trigger Step Taken", object_type="Step") logger.debug( 'Trigger is valid for input {0}'.format(data_in)) accumulator[self.name] = data_in if inputs: self.inputs.update(inputs) break else: logger.debug( 'Trigger is not valid for input {0}'.format(data_in)) data_sent.send(self, callback_name="Trigger Step Not Taken", object_type="Step") gevent.sleep(0.1) try: args = dereference_step_routing(self.inputs, accumulator, 'In step {0}'.format(self.name)) args = validate_app_action_parameters(self._input_api, args, self.app, self.action) action = get_app_action(self.app, self._run) if is_app_action_bound(self.app, self._run): result = action(instance, **args) else: result = action(**args) data_sent.send(self, callback_name="Function Execution Success", object_type="Step", data=json.dumps({"result": result.as_json()})) except InvalidInput as e: formatted_error = format_exception_message(e) logger.error('Error calling step {0}. Error: {1}'.format( self.name, formatted_error)) data_sent.send(self, callback_name="Step Input Invalid", object_type="Step") self._output = ActionResult('error: {0}'.format(formatted_error), 'InvalidInput') raise except Exception as e: formatted_error = format_exception_message(e) logger.error('Error calling step {0}. Error: {1}'.format( self.name, formatted_error)) self._output = ActionResult('error: {0}'.format(formatted_error), 'UnhandledException') raise else: self._output = result for widget in self.widgets: get_widget_signal(widget.app, widget.name).send( self, data=json.dumps({"result": result.as_json()})) logger.debug('Step {0}-{1} (uid {2}) executed successfully'.format( self.app, self.action, self.uid)) return result