mael -- infer dynamic precedences between convergent scripts.
mael
--dryrun
--debug
--verbose
--exec
--conf=mael.conf
Mael (the ``Maelstrom'') is a tool for dynamically determining precedences
between convergent scripts. It runs these scripts and examines their exit
codes, interpreting 0 as success and nonzero as failure. It continues
trying to execute scripts that fail in a round-robin fashion until either
all scripts succeed or it has completed n+1 full cycles, where n is the
number of input scripts. It can be shown that this is equivalent with
trying the scripts in all possible permutation orders.
The input to mael is a set of scripts that assure various configuration parameters for a
system or network. For mael to work properly, these scripts must satisfy some simple criteria:
- awareness
-
A script should know whether it succeeded in making desired changes or not.
- convergence
-
A script should not make a change if not needed.
- homogeneity
-
Scripts should not undo the changes of other scripts.
These limits allow mael to make some assumptions about script behavior. Once a script succeeds, it
has successfully made its desired changes. No script will undo those
changes, so a script need only succeed exactly once to accomplish its task.
This is an ideal case, and special operators in the configuration file
allow one to encode deviations from this ideal for script behavior.
mael is a self-contained Perl-5 script. All one need do is to edit the first
line of mael to point to an appropriate Perl. Mael
only uses modules Getopt::Long
and Data::Dumper
, which are available from CPAN.
- --conf=mael.conf
-
Change the configuration file to be another file. The default is mael.conf in the user's current directory.
- --debug
-
Turn on debugging writes. This provides an enormous amount of internal
detail. Most people should use
--verbose
instead.
- --dryrun
-
Print the result of parsing the configuration file without interpreting the file.
- --exec
-
Check scripts for being executable at compile-time. This will raise an
error if any fail to be programs. This precludes any scripts that write
others, e.g.
- --verbose
-
Print a verbose log of actions as they occur. This shows how
mael works and what it's doing at any given time.
Mael's configuration file mael.conf looks much like a Makefile
with some important differences. First, mael.conf defines only precedences between scripts, unlike a Makefile, which defines precedences between outcomes. The simplest mael.conf is nothing more than a list of scripts to try, one per line. In the absence
of any other ordering they will be tried in the sequence in which they
appear. Repeating lines has no effect, as two identical lines are
interpreted as representing the same script.
By default, all commands in mael.conf have the same precedence and their order of appearance in the file is their
execution order. One may advise mael of possible precedence constraints by means of three precedence operators.
If A
and B are commands, then
- B:A
-
means that it is likely that B must follow
A
. This is treated as 'advisory'. Mael will try them in the suggested order before trying other orders.
- B::A
-
means that 'B must clean up after
A
'. Mael will react by invoking a cleanup cycle after finishing execution, in which
it redoes any B's whose successes preceded success of A
's. Normally, once a script succeeds, it's done.
This is used to compensate for lack of homogeneity between scripts. If two
scripts change the same parameter, one must decide which script's changes
will be permanent. The last script to be called will 'win' and its changes
will persist.
- B:::A
-
means that '
A
must always precede B'. Mael will not even try B until A
succeeds the first time. This is similar to ':' in make.
This is used to compensate for scripts whose actions require specific
preconditions of which the script is not completely aware. By coding these
preconditions one assures that it will not be invoked until the environment
is ready for it.
Ideally, one need only specify precedences with ':'; other operators are
mainly intended to cope with deficiencies of existing scripts.
Mael supports simple variable substitutions in the configuration file. One sets
a variable with an assignment statement <name> = <value> where 'name' is the name of the value and 'value' is a string
value corresponding to that name. Once set, variables can be substituted
into any context by use of standard shell substitution syntax.
$(name)
represents the value of the variable 'name'.
Variables are mainly useful for declaring precedences between a complex set
of scripts. If one places the complete invocation of each script into a
variable, one can declare multiple precedence relationships without copying
the invocations over several times.
One can declare several precedence relationships in one line of a
configuration file. The character ';' separates several commands to which
the same precedence directive applies.
A:B;C
means
A:B
and
A:C
while
A;B:C
means
A:C
and
B:C
Also,
A;B:C;D:E;F
expands to mean all of the following relationships:
A:C
A:D
B:C
B:D
C:E
C:F
D:E
D:F
Commands are separated by ':::', '::', ':', or ';'. The strongest
precedence is ';', while the weakest is ':::'. Thus
A::B:::C;D
means
A::B
B:::C
B:::D
This is nothing more than syntactic sugar.
Long lines can be broken by using \ at the end of each line as a
continuation character.
B \
:: \
C
is the same as the statement
B::C
A third syntax available in the configuration file allows conditional
execution of commands depending upon the outcome of others. This execution
is not related to precedence, but occurs immediately after the affected command
has succeeded or failed. The syntax:
A || B
works as expected, so that B is only executed if A fails. Likewise
A && B
only executes B if A succeeds. Commands can also be 'negated' with '!',
which reverses the sense of these directives. So
!A || B
only executes B if A succeeds, and is thus equivalent with
A && B
The most powerful syntax is that of explicit exit-code dispatching. The
syntax:
A [ 12=>C; 0=>D ]
executes C if A's exit code is 12, and D if A's exit code is 0. The default
values for exit codes are 0, 1, 2, ..., so that
A [ C; D ]
is equivalent with
A [ 0=>C; 1=>D ]
Limits on exit code dispatching are mainly intended to increase code
readability. A specific command can only have one associated exit code
action, either ||
, &&
, or []
. Specifying two different actions is an error.
Code dispatching requests can be embedded into any line of a configuration
file and can be nested. The results of nesting are the same as the results
of not nesting the declarations. For example,
A [ B ; C ] :: D : E && F ::: G || H
means exactly the same thing as
A [ B ; C ]
A :: D
D : E
E && F
F ::: G
G || H
where ||
, &&
, and []
always have higher precedence than
:
, ::
, :::
.
Alva L. Couch, couch@eecs.tufts.edu,
http://www.eecs.tufts.edu/~couch
, Copyright 2001 by Alva L. Couch.
Oct 31, 2001