Ejemplo n.º 1
0
    def attach_calculation(self, calc):
        """
        Adds a calculation to the caller step in the database. This is a lazy call, no
        calculations will be launched until the ``next`` method gets called. For a step to be
        completed all the calculations linked have to be in RETRIEVED state, after which the next
        method gets called from the workflow manager.
        :param calc: a JobCalculation object
        :raise: AiidaException: in case the input is not of JobCalculation type
        """

        if (not issubclass(calc.__class__, JobCalculation)
                and not isinstance(calc, JobCalculation)):
            raise AiidaException(
                "Cannot add a calculation not of type JobCalculation")

        for node in calc.get_inputs():
            if node.pk is None:
                raise AiidaException("Cannot add a calculation with "
                                     "unstored inputs")

                #        if calc.pk is None:
                #            raise AiiDAException("Cannot add an unstored calculation")

        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 2)
        caller_funct = calframe[1][3]

        if not caller_funct in self.attach_calc_lazy_storage:
            self.attach_calc_lazy_storage[caller_funct] = []
        self.attach_calc_lazy_storage[caller_funct].append(calc)
Ejemplo n.º 2
0
        def wrapper(self, *args, **kwargs):
            # Store the workflow at the first step executed
            if self._to_be_stored:
                self.store()

            if len(args) > 0:
                raise AiidaException(
                    "A step method cannot have any argument, use add_attribute to the workflow"
                )

            # If a method is launched and the step is RUNNING or INITIALIZED we should stop
            if self.has_step(wrapped_method) and \
                    not (self.get_step(wrapped_method).state == wf_states.ERROR or \
                         self.get_step(wrapped_method).state == wf_states.SLEEP or \
                         self.get_step(wrapped_method).nextcall == wf_default_call or \
                         self.get_step(wrapped_method).nextcall == wrapped_method \
                            # cls.has_step(wrapped_method) \
                    ):
                raise AiidaException(
                    "The step {0} has already been initialized, cannot change this outside the parent workflow !"
                    .format(wrapped_method))

            # If a method is launched and the step is halted for ERROR, then clean the step and re-launch
            if self.has_step(wrapped_method) and \
                    (self.get_step(wrapped_method).state == wf_states.ERROR or \
                     self.get_step(wrapped_method).state == wf_states.SLEEP):

                for w in self.get_step(wrapped_method).get_sub_workflows():
                    w.kill()
                self.get_step(wrapped_method).remove_sub_workflows()

                for c in self.get_step(wrapped_method).get_calculations():
                    c.kill()
                self.get_step(wrapped_method).remove_calculations()

                # self.get_steps(wrapped_method).set_nextcall(wf_exit_call)

            user = self._backend.users.get_automatic_user()
            method_step, created = self.dbworkflowinstance._get_or_create_step(
                name=wrapped_method, user=user.dbuser)

            try:
                fun(self)
            except:
                exc_type, exc_value, exc_traceback = sys.exc_info()
                self.append_to_report(
                    "ERROR ! This workflow got an error in the {0} method, we report down the stack trace"
                    .format(wrapped_method))
                self.append_to_report("full traceback: {0}".format(
                    traceback.format_exc()))
                method_step.set_state(wf_states.ERROR)
                self.set_state(wf_states.ERROR)
            return None
Ejemplo n.º 3
0
    def get_step(self, step_method):

        """
        Retrieves by name a step from the Workflow.
        :param step_method: a string with the name of the step to retrieve or a method
        :raise: ObjectDoesNotExist: if there is no step with the specific name.
        :return: a DbWorkflowStep object.
        """
        if isinstance(step_method, basestring):
            step_method_name = step_method
        else:

            if not getattr(step_method, "is_wf_step"):
                raise AiidaException("Cannot get step calculations from a method not decorated as Workflow method")

            step_method_name = step_method.wf_step_name

        if (step_method_name == wf_exit_call):
            raise InternalError("Cannot query a step with name {0}, reserved string".format(step_method_name))

        try:
            step = self.dbworkflowinstance.steps.get(name=step_method_name, user=get_automatic_user())
            return step
        except ObjectDoesNotExist:
            return None
Ejemplo n.º 4
0
    def get_step_calculations(self, step_method, calc_state=None):
        """
        Retrieves all the calculations connected to a specific step in the database. If the step
        is not existent it returns None, useful for simpler grammatic in the workflow definition.
        :param next_method: a Workflow step (decorated) method
        :param calc_state: a specific state to filter the calculations to retrieve
        :return: a list of JobCalculations objects
        """
        if not getattr(step_method, "is_wf_step"):
            raise AiidaException("Cannot get step calculations from a method not decorated as Workflow method")

        step_method_name = step_method.wf_step_name

        try:
            stp = self.get_step(step_method_name)
            return stp.get_calculations(state=calc_state)
        except:
            raise AiidaException("Cannot retrieve step's calculations")
Ejemplo n.º 5
0
    def sleep(self):
        """
        Changes the workflow state to SLEEP, only possible to call from a Workflow step decorated method.
        """
        # ATTENTION: Do not move this code outside or encapsulate it in a function
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 2)
        caller_method = calframe[1][3]

        if not self.has_step(caller_method):
            raise AiidaException("The caller method is either not a step or has not been registered as one")

        self.get_step(caller_method).set_state(wf_states.SLEEP)
Ejemplo n.º 6
0
    def get_step_workflows(self, step_method):
        """
        Retrieves all the workflows connected to a specific step in the database. If the step
        is not existent it returns None, useful for simpler grammatic in the workflow definition.
        :param next_method: a Workflow step (decorated) method
        """
        if not getattr(step_method, "is_wf_step"):
            raise AiidaException("Cannot get step calculations from a method not decorated as Workflow method")

        step_method_name = step_method.wf_step_name

        stp = self.get_step(step_method_name)
        return stp.get_sub_workflows()
Ejemplo n.º 7
0
    def next(self, next_method):
        """
        Adds the a new step to be called after the completion of the caller method's calculations and subworkflows.

        This method must be called inside a Workflow step, otherwise an error is thrown. The
        code finds the caller method and stores in the database the input next_method as the next
        method to be called. At this point no execution in made, only configuration updates in the database.

        If during the execution of the caller method the user launched calculations or subworkflows, this
        method will add them to the database, making them available to the workflow manager to be launched.
        In fact all the calculation and subworkflow submissions are lazy method, really executed by this call.

        :param next_method: a Workflow step method to execute after the caller method
        :raise: AiidaException: in case the caller method cannot be found or validated
        :return: the wrapped methods, decorated with the correct step name
        """

        md5 = self.dbworkflowinstance.script_md5
        script_path = self.dbworkflowinstance.script_path

        # TODO: in principles, the file containing the workflow description
        # should be copied in a repository, and, on that file, the workflow
        # should check to be sure of loading the same description of the
        # workflow. At the moment, this is not done and is checking the source
        # in aiida/workflows/... resulting essentially in the impossibility of
        # developing a workflow without rendering most of the trial run
        # unaccessible. I comment these lines for this moment.

        # if md5 != md5_file(script_path):
        #    raise ValidationError("Unable to load the original workflow module from {}, MD5 has changed".format(script_path))

        # ATTENTION: Do not move this code outside or encapsulate it in a function
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 2)
        caller_method = calframe[1][3]

        if next_method is None:
            raise AiidaException(
                "The next method is None, probably you passed a method with parenthesis ??"
            )

        if not self.has_step(caller_method):
            raise AiidaException(
                "The caller method is either not a step or has not been registered as one"
            )

        if not next_method.__name__ == wf_exit_call:
            try:
                is_wf_step = getattr(next_method, "is_wf_step", None)
            except AttributeError:
                raise AiidaException(
                    "Cannot add as next call a method not decorated as Workflow method"
                )

        # TODO SP: abstract this, this depends on the DB. The better would be
        # to add a method to the DbWorkflow from SQLA and Django to get steps
        # with particular filters, in order to avoid repetition of all the code
        # arround

        # Retrieve the caller method
        method_step = self.dbworkflowinstance.steps.get(
            name=caller_method, user=get_automatic_user())

        # Attach calculations
        if caller_method in self.attach_calc_lazy_storage:
            for c in self.attach_calc_lazy_storage[caller_method]:
                method_step.add_calculation(c)

        # Attach sub-workflows
        if caller_method in self.attach_subwf_lazy_storage:
            for w in self.attach_subwf_lazy_storage[caller_method]:
                method_step.add_sub_workflow(w)

        # Set the next method
        if not next_method.__name__ == wf_exit_call:
            next_method_name = next_method.wf_step_name
        else:
            next_method_name = wf_exit_call

        # logger.info("Adding step {0} after {1} in {2}".format(next_method_name, caller_method, self.uuid))
        method_step.set_nextcall(next_method_name)
        #
        self.dbworkflowinstance.set_state(wf_states.RUNNING)
        method_step.set_state(wf_states.RUNNING)