Ejemplo n.º 1
0
    def _write_task_text(self,
                         fd,
                         attr,
                         indent,
                         view,
                         dependency,
                         time,
                         alarm_name,
                         manual_dependency=None):
        assert (view is not None)
        path = '.'.join(view.path[1:])
        space = self.__spacing
        fd.write(f'{space*indent}<task name="{path}"{attr}')
        if alarm_name:
            self.__alarms_used.add(alarm_name)
            fd.write(f' cycledefs="{alarm_name}"')
        more_attr = view.get('Rocoto_attr', '')
        if more_attr:
            fd.write(f' {more_attr}')
        fd.write('>\n')

        dep = self._as_rocoto_dep(simplify(dependency), view.path)

        dep_count = (dep != TRUE_DEPENDENCY) + (time > timedelta.min)

        if 'Rocoto' in view:
            for line in view.Rocoto.splitlines():
                fd.write(f'{space*(indent+1)}{line}\n')
        else:
            _logger.warning(
                f'{view.task_path_var}: no Rocoto item; <task> will be invalid.'
            )

        if manual_dependency is not None:
            for line in manual_dependency.splitlines():
                fd.write(f'{space*(indent+1)}{line}\n')
            fd.write(space * indent + '</task>\n')
            return

        if not dep_count:
            fd.write(space * (indent + 1) + '<!-- no dependencies -->\n')
        if dep_count:
            fd.write(space * (indent + 1) + '<dependency>\n')
        if dep_count > 1:
            fd.write(space * (indent + 2) + '<and>\n')

        if dep is not TRUE_DEPENDENCY:
            _to_rocoto_dep_impl(dep, fd, indent + 1 + dep_count)
        if time > timedelta.min:
            _to_rocoto_time_dep(time, fd, indent + 1 + dep_count)

        if dep_count > 1:
            fd.write(space * (indent + 2) + '</and>\n')
        if dep_count:
            fd.write(space * (indent + 1) + '</dependency>\n')
        fd.write(space * indent + '</task>\n')
Ejemplo n.º 2
0
    def _convert_item(self, fd, indent, view, trigger, complete, time,
                      alarm_name):
        if view.get('Disable', False): return
        if view.get('Dummy', False): return
        trigger = trigger & view.get_trigger_dep()
        complete = complete | view.get_complete_dep()
        time = max(time, view.get_time_dep())
        space = self.__spacing

        if 'AlarmName' in view:
            if alarm_name:
                raise ValueError(
                    '{view.task_path_var}: nested alarms are not supported in crow.metascheduler.to_rocoto()'
                )
            else:
                alarm_name = view.AlarmName

        dep = trigger & ~complete
        dep = simplify(dep)

        if view.is_task():
            maxtries = int(
                view.get('max_tries', self.suite.Rocoto.get('max_tries', 0)))
            attr = f' maxtries="{maxtries}"' if maxtries else ''
            self._write_task_text(fd, attr, indent, view, dep, time,
                                  alarm_name)
            return

        self.__dummy_var_count += 1
        dummy_var = "dummy_var_" + str(self.__dummy_var_count)

        path = _xml_quote('.'.join(view.path[1:]))
        if not isinstance(view, Suite):
            fd.write(f'''{space*indent}<metatask name="{path}"''')
            more_attr = view.get('Rocoto_attr', '')
            if more_attr:
                fd.write(f' {more_attr}')
            fd.write(f'''>
{space*indent}  <var name="{dummy_var}">DUMMY_VALUE</var>
''')
        for key, child in view.items():
            if key in ['up', 'this']: continue
            if not isinstance(child, SuiteView):
                continue
            if child.path[1:] == ['final']:
                if not child.is_task():
                    raise RocotoConfigError(
                        'The "final" task must be a Task, not a ' +
                        type(child.viewed).__name__)
                self.__final_task = child
            else:
                self._convert_item(fd, indent + 1, child, trigger, complete,
                                   time, alarm_name)

        if not isinstance(view, Suite):
            fd.write(f'{space*indent}</metatask>\n')
Ejemplo n.º 3
0
 def test_simp_extended_expr(self):
     self.assertEqual(ag.simplify((self.DEP1 | self.DEP2 | self.DEP4) & \
                      (self.DEP1 | self.DEP3 | self.DEP4)), \
                        self.DEP1 | self.DEP2 & self.DEP3 | self.DEP4)
Ejemplo n.º 4
0
 def test_simp_gobbledygook(self):
     self.assertEqual(ag.simplify(~self.DEP2 & ~(~self.DEP1 | ~self.DEP2)),
                      FALSE_DEPENDENCY)
Ejemplo n.º 5
0
 def test_simp_not_not_a_or_not_b(self):
     self.assertEqual(ag.simplify(~(~self.DEP1 | ~self.DEP2)),
                      self.DEP1 & self.DEP2)
Ejemplo n.º 6
0
 def test_simp_a_and_not_a(self):
     self.assertEqual(ag.simplify(~self.DEP1 & self.DEP1), FALSE_DEPENDENCY)
Ejemplo n.º 7
0
 def test_simp_a_or_not_a(self):
     self.assertEqual(ag.simplify(~self.DEP1 | self.DEP1), TRUE_DEPENDENCY)
Ejemplo n.º 8
0
    def _handle_final_task(self, fd, indent):
        # Find and validate the "final" task:
        final = None
        if 'final' in self.suite:
            final = self.suite.final
            if not final.is_task():
                raise RocotoConfigError(
                    'For a workflow suite to be expressed in Rocoto, it '
                    'must have a "final" task')
            for elem in ['Trigger', 'Complete', 'Time']:
                if elem in final:
                    raise RocotoConfigError(
                        f'{elem}: In a Rocoto workflow, the "final" task '
                        'must have no dependencies.')

        if final is None:
            if self.__completes:
                raise RocotoConfigError(
                    'If a workflow suite has any "complete" conditions, '
                    'then it must have a "final" task with no dependencies.')
            else:
                return  # No final task, and no need to generate one

        if len(self.__alarms_used) < 2:
            # There are no alarms in use, so there is only one final task.
            # Generate dependency for it:
            fd.write(
                f'\n{self.__spacing*indent}<!-- The final task dependencies are automatically generated to handle Complate and Trigger conditions. -->\n\n'
            )
            dep = self._final_task_deps_no_alarms(self.suite)
            self._write_task_text(fd, ' final="true"', indent, final, dep,
                                  timedelta.min, '')
            return

        fd.write(
            f'\n{self.__spacing*indent}<!-- These final tasks are automatically generated to handle Complate and Trigger conditions, and alarms. -->\n\n'
        )

        # There are alarms, so things get... complicated.
        manual_dependency = f'''<dependency>
{self.__spacing*indent}<and>
{self.__spacing*(indent+1)}<!-- All tasks must be complete or invalid for this cycle -->\n'''
        alarms = set(self.__alarms_used)
        alarms.add('')
        # Reverse-sort the alarm names so the final tasks show up in
        # the same order each time, with the task "final" at the end.
        for alarm_name in reversed(sorted(alarms)):
            #print(f'find final for {alarm_name}')
            dep = self._final_task_deps_for_alarm(self.suite, alarm_name)
            dep = simplify(dep)
            task_name = f'final_for_{alarm_name}' if alarm_name else 'final_no_alarm'
            new_task = copy(self.suite.final.viewed)
            new_task['_is_final_'] = True
            new_task['AlarmName'] = alarm_name
            invalidate_cache(self.suite, recurse=True)
            self.suite.viewed[task_name] = new_task
            new_task_view = self.suite[task_name]
            del new_task
            self.__all_defined.add(
                SuitePath([_ZERO_DT] + new_task_view.path[1:]))
            assert (dep is not FALSE_DEPENDENCY)
            self._write_task_text(fd, '', indent, new_task_view, dep,
                                  timedelta.min, alarm_name)

            manual_dependency += f'''{self.__spacing*(indent+1)}<or>
{self.__spacing*(indent+2)}<taskdep task="{task_name}"/>
{self.__spacing*(indent+2)}<not><taskvalid task="{task_name}"/></not>
{self.__spacing*(indent+1)}</or>\n'''
        manual_dependency += f'{self.__spacing*indent}</and>\n</dependency>\n'
        self._write_task_text(fd,
                              ' final="true"',
                              indent,
                              final,
                              TRUE_DEPENDENCY,
                              timedelta.min,
                              '',
                              manual_dependency=manual_dependency)
Ejemplo n.º 9
0
 def _as_rocoto_dep(self, dep, defining_path):
     dep = dep.copy_dependencies()
     dep = self.remove_undefined_tasks(dep)
     dep = self._rocotoify_dep(dep, defining_path)
     dep = simplify(dep)
     return dep