|
|
The details of `foo.bar()' in ARK
If foo is an ARK `thing' (host, package, etc.), and
bar is one of its fields that you can look up in
the ARK XML world, what does the ``method invocation''
foo.bar() mean? And how does it work?
Many invocations are conceptually simple. For example,
something like host.ip_addresses() would yield the
expected list of strings; no bother.
This document is to explain the hairier cases. (Much
hairier.) The en-hairying factors are:
- Constraints. A constraint may affect the value that gets
plucked from the XML world, or whether or not it is safe to
use it. Consider
foo.description() in the face of...:
<description>
<constraint><host-spec>sidai:solaris</host-spec></constraint>
<string>A Solaris box</string>
</description>
<description>
<constraint><host-spec>sidai:linux</host-spec></constraint>
<string>A Linux box</string>
</description>
If you're printing out this ``description'' information, you
might want access to the raw constraint info. Normally, however, you
just want the system to navigate through the constraints to
give you the correct <string>.
Constraints are normally associated with code values, but
they don't have to be.
A constraint may also impose an external
condition that is supposed to be true for a chosen value to
``make sense''. For example, ``don't try the
install method unless the compile method
has already been done''.
Some constraints are ignorable, usually under command-line
control (the ``--use-deps'' flag); i.e. the user can say
``don't bother checking those constraints (I'm in a
hurry)''.
- Code. There are several things you might want
to do with a code value... You might want to display it.
You might want to run it right now. You might want it
in a form that can be put in a script and run later.
(Normally, we ``run it now''.)
- Embedded references. Values may refer to other
fields. Those fields may have constraints, ...
- Environments. In checking environments,
satisfying constraints, and running code, you need an
environment to do it in. For example, what host
are you talking about and/or running on?
This latter point is sufficently horrible that we have a
whole section on ``host contexts''...
What is the result of foo.bar()? The problem
is that we want different things at different times.
In normal circumstances, if the XML machinery spits out a
string, list, or table (i.e. not code), then that
is the result.
Return values and exceptions that can be raised: see
the Python code.
With code ``values'', there are several other possibilities.
- The normal thing is to run it so that it has ``site at a
time'' effect (which may mean ``on many hosts''). We are
running it for its side effects; we simply want to know if
it succeeded.
- We may want to run the code for the value it
produces. In this case, the ``host context'' must be
exactly one host. We supply a special argument
ARK_RUN_FOR_VALUE=<hostname> to indicate
we want to do this, and what host we want to run on.
(`.' means this host)
- We may want to get a listing of the code which, for
example, we could save in a file and run later.
An ARK_RUN_FOR_SCRIPT=<hostname> does
this.
-
We may want to do nothing at all (!) -- we're running the
machinery to find out the constraints. We use this,
for example, to ``undo'' previous actions. We supply
an ARK_RUN_FOR_CONSTRAINTS to indicate this.
-
Finally, we may want the raw material
(constraints and all) that the XML machinery produced. For
example, if you wanted to display it on a web page.
We do this by supplying a special argument,
ARK_RAW_XML_INFO, e.g. foo.bar(ARK_RAW_XML_INFO=1).
As soon as we're tangling with <constraints>
and <code>, we have to have a host
context in which to operate. (See
ark.thing.ArkResult.__call__.)
Definitions:
- The host context is the host that we are
putatively trying to do a method for. (NB: `for',
not `on'.)
- The run host is the machine you happen
to be running on (i.e. where you typed ark package
...).
- The proxy-for host is a proto-host, and it
means that the corresponding build-host (below) ``stands
in'' for all real hosts derived from the proxy-for host.
- The build host is a real host that can do the
action for the proxy-for host.
Example: we do foo.bar(), and we are doing it for a
`host context' of host `linuxhost1'. It turns out the code
for bar does the proxy-for thing, and the relevant
proxy-for host is (the proto-) host `ia32-linux-rh6.2'. The
corresponding `build host' for `ia32-linux-rh6.2' might be
`linuxserver3'.
So the bar code would really run on `linuxserver3',
which is acting as a proxy for `linuxhost1' (and probably
many other hosts).
This is important for things like: We may have, say, 300
Linux hosts, but when we compile `bash', we only want to do
it once.
The proxying thing can only happen for code. (Of
course, the trouble is that it tends to be late in the game
that you know you have code, and not something
else...)
ToDo: Must say something about how even non-code values
do the host-context thing, which could give weird
results, if you tried.
We get through all of the above by breaking things into
pieces.
- The first part of foo.bar() is
foo.bar, and is done by
ark.thing.ArkThing.__getattr__.
It gives us back an ark.thing.ArkResult which has
self._raw_field_info filled in: that's just a list
of ArkXmlFileFields -- simply the raw goods that we
encountered for field bar as we walked the
prototype tree for foo.
(ToDo: explain special pleading for globbing, i.e.
what foo.* does...)
We really can't do any more at __getattr__ time
than accumulate the raw info. Everything after that
requires some runtime info (e.g. ``what hosts are you talking
about?''), which is simply not available to the
__getattr__ method.
The second part of foo.bar() is the
() part, and is done by
ark.thing.ArkResult.__call__.
This stuff is really hairy.
Why? First, as we saw above, we know very little
about where we're heading... Are we heading towards a simple
string value, or a ``complex
run-lots-of-code-on-many hosts'' situation? Until we've
processed the constraints, we won't know the answer to that!
Second, because something innocent like pkg.reveal()
actually entails running some code on perhaps hundreds
of hosts.
Third, in such a case, the code to be run can vary; witness:
<reveal>
<constraint><host-spec>sidai:linux</host-spec></constraint>
<code>echo yes</code>
</reveal>
<reveal>
<constraint><host-spec>sidai:solaris2</host-spec></constraint>
<code>echo no</code>
</reveal>
On the other hand, something like host.aliases()
really is a simple action, returning a list of aliases.
However, what does host.aliases() do in the
presence of the legitimate spec...?
<aliases>
<constraint><host-spec>hpux11</host-spec></constraint>
<constraint><dependency name="syslog-config" version-spec="*" /></constraint>
<list><item>loghost3</item></list>
</aliases>
(Throwing up hands in despair is OK in my books :-)
More sensibly, but not on the horizon, is to have some kind
of ``type checker'' that can say ``This makes no sense''...
|