File: defining-the-first-task.rst

package info (click to toggle)
ecflow 5.15.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 51,868 kB
  • sloc: cpp: 269,341; python: 22,756; sh: 3,609; perl: 770; xml: 333; f90: 204; ansic: 141; makefile: 70
file content (162 lines) | stat: -rw-r--r-- 7,682 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
.. index::
   single: script (tutorial)
   single: ECF_HOME (tutorial)
    
.. _tutorial-defining-a-task:

Defining the first task
=======================

With the :term:`suite definition` ready, the next step is to create the :term:`task` script, also called
:term:`ecf script`. This script typically uses extension :code:`.ecf`.

Task Script Structure
---------------------

A common pattern used for defining an :term:`ecf script` is to consider three sections:

    1. A *head* section, that sets up the necessary execution environment, including error handling,
       for the :term:`task`; and eventually notifies the :term:`ecflow_server` that the :term:`task` has started
    2. A *body* section, which contains the actual work to be done by the :term:`task`
    3. A *tail* section, that performs the necessary clean up for the :term:`task`; and eventually notifies
       the :term:`ecflow_server` that the :term:`task` has completed

The *head* and *tail* sections are common amongst all :term:`tasks <task>`, while the *body* section is specific to each :term:`task`.

To avoid code duplication, ecFlow enables placing the *head* and *tail* sections in separate files, and then include them in the :term:`ecf script` using "C-like" preprocessing :term:`directives`.
The process of preprocessing the :term:`ecf script` and including *head* and *tail* files is called the :term:`job file <job file>` generation.

Task Head and Tail
------------------

The *head* and *tail* sections are placed in separate files, called include files.

These files use :term:`variables <variable>` as a configuration mechanism. The :term:`variables <variable>`
are introduced in between %% characters (e.g. :code:`%VARIABLE_NAME%`) and are substituted when the :term:`job file <job file>`
is created.

These files are introduced into the :term:`ecf script` using the :code:`%include` directive.

    .. note::

        The use of :code:`%include` directives is a generic code reuse mechanism, and can be used to avoid code duplication.

        Whenever several :term:`tasks <task>` need to perform the same operation, this can be placed in a separate file,
        and then included in the :term:`ecf script` using the :code:`%include` directive.

    .. tabs::

        .. tab:: head.h

            The following :file:`head.h` file, containing the *header* section, performs the following at the start of the :term:`ecf script`:

            * Setup the environment for communication with the :term:`ecflow_server`
            * Define script error handling, e.g. when the script fails a trap is raised, and the server is informed that the :term:`task` has :term:`aborted`.
            * Inform the server that job has started, using the :code:`init` :term:`child command` .

            .. note::

                Notice how variables, such as :code:`ECF_HOST` and :code:`ECF_PORT`, are used in the script.
                These variables are automatically generated by ecFlow and can be used as part of any section of the :term:`ecf script`.

            .. code-block:: shell
               :linenos:
               :caption: $HOME/course/head.h

               #!/usr/bin/env bash

               set -e # stop the shell on first error
               set -u # fail when using an undefined variable
               set -x # echo script lines as they are executed

               #
               # Important: Adjust the following to point at the ecFlow install location
               #
               ECFLOW_ROOT_DIR=/path/to/ecflow/%ECF_VERSION%

               # Define the variables that are needed for any communication with ECF
               export ECF_PORT=%ECF_PORT%    # The server port number
               export ECF_HOST=%ECF_HOST%    # The name of ecf host that issued this task
               export ECF_NAME=%ECF_NAME%    # The name of this current task
               export ECF_PASS=%ECF_PASS%    # A unique password
               export ECF_TRYNO=%ECF_TRYNO%  # Current try number of the task
               export ECF_RID=$$

               # Define the path where to find ecflow_client
               # make sure client and server use the *same* version.
               # Important when there are multiple versions of ecFlow
               export PATH=${ECFLOW_ROOT_DIR}/bin:$PATH

               # Tell ecFlow the task has started
               ecflow_client --init=$$

               # Define a error handler
               ERROR() {
                  set +e                      # Clear -e flag, so we don't fail
                  wait                        # wait for background process to stop
                  ecflow_client --abort=trap  # Notify ecFlow that something went wrong
                  trap 0                      # Remove the trap
                  exit 0                      # End the script
               }

               # Trap any calls to exit and errors caught by the -e flag
               trap ERROR 0

               # Trap any signal that may cause the script to fail
               trap '{ echo "Killed by a signal"; ERROR ; }' 1 2 3 4 5 6 7 8 10 12 13 15

        .. tab:: tail.h

            The following :code:`tail.h` file, containing the *tail* section, performs the following at the end of :term:`ecf script`:

            * Waits for any background processes to complete
            * Informs the server that the :term:`task` has completed successfully
            * Cleans up any error handling previously defined

            .. code-block:: shell
               :linenos:
               :caption: $HOME/course/tail.h

               wait                      # wait for background process to stop
               ecflow_client --complete  # Notify ecFlow of a normal end
               trap 0                    # Remove all traps
               exit 0                    # End the shell

Task Script
-----------

The following :file:`t1.ecf` file is the :term:`ecf script` for the :term:`task` :code:`t1`.

    .. tabs::

        .. tab:: t1.ecf

            This script includes the *head* and *tail* sections from the files :file:`head.h` and :file:`tail.h`, respectively.
            The *body* section of this script simply prints a message to standard output, including the value of the variable :code:`ECF_HOME`.

            ecFlow expects task files to be in a directory structure under :code:`ECF_HOME` that reflects the hierarchy in the suite.
            In the example, task :code:`t1` is part of the suite :code:`test`, so the script for task :code:`t1` is expected to
            be in sub-directory :code:`test`.

            .. code-block:: shell
               :linenos:
               :caption: $HOME/course/test/t1.ecf

               %include "../head.h"
               echo "I am part of a suite that lives in %ECF_HOME%"
               %include "../tail.h"

            .. important::

                When using the :code:`%include "<filename>"` directive, the path to the include file is given relative to
                the location of the :term:`ecf script` file, thus the :code:`../` in the example above.

            .. note::

                Notice how the :code:`ECF_HOME` variable is used in the script. This variable is defined in the :term:`suite definition` file.

**What to do:**

* Create the :code:`$HOME/course/head.h` and :code:`$HOME/course/tail.h` include files based on the examples provided above.
  Make sure to adjust the :code:`ECFLOW_ROOT_DIR` variable in the :file:`head.h` file to point to the correct ecFlow installation location.
* Create the directory structure, and the :code:`$HOME/course/test/t1.ecf` task script file based on the example provided above.