Arusha Project
ARK (mechanism)
 
How to..., etc.
ARK objects
Configuration language
Package mgmt
`ark' tool
ARK site gen
 
Design, etc.
foo.bar()
Grokking the code
Problems
Ideas
 
Admin, etc.
Glossary
Technologies
Conventions
 
Hosted by
SourceForge.net Logo

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.

The issues

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''...

The semantics of foo.bar()

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.

  1. 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.

  2. 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)

  3. 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.

  4. 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.

  5. 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).

Host contexts

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:

  1. The host context is the host that we are putatively trying to do a method for. (NB: `for', not `on'.)

  2. The run host is the machine you happen to be running on (i.e. where you typed ark package ...).

  3. 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.

  4. 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.

The --hosts=... command-line flag

The machinery

We get through all of the above by breaking things into pieces.

  1. 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''...


© The Arusha Project, 2000-2003; team: ARK; c/o partain@users.sourceforge.net; revision 1.7, 2004-05-26.