Inheritance

Verdad implements ``prototype-style multiple inheritance''. If you are not PARC Alumni, you'll need some help understanding what it is and why it's useful. This document is for you.

Learning by Example

Here's an example of how inheritance works, and why it's useful.

There are two ways to look at any item in Verdad. One way is the simple item. This is just the way it looks when you check it in with the verdad ci command. Here's a set of simple items:

        item pkg-production
                pkg = tellme-platform-20010101-0101
                pkg = vim-5.6
        item users-production
                user = jra
                user = mattd
                user = verber
        item production
                is = ( pkg-production users-production )
        item exsc4.tellme.com
                is = production
        item p1.exsc4.tellme.com
                is = exsc4.tellme.com
                in-service = true
        item tel01.p1.exsc4.tellme.com
                is = p1.exsc4.tellme.com
        item tel02.p1.exsc4.tellme.com
                is = p1.exsc4.tellme.com

The other way to look at these items is in their complete form. The complete form of an item has all the tags contributed by all the items it inherits from collapsed into one big item. For instance, given the previous set of items, the complete form of the item ``tel01.p1.exsc4.tellme.com'' would be:

        item tel01.p1.exsc4.tellme.com
                user = jra
                user = mattd
                user = verber   
                pkg      = tellme-platform-20010101-0101
                pkg      = vim-5.6
                in-service = true

So, imagine a script that dynamically writes the configuration script for a monitoring system. That script might look something like this:

        foreach item (where in-service is true) {
                add to monitoring config file
        }

By changing the in-service tag from true to false in the item named p1.exsc4.tellme.com, we can quickly and easily take every machine that is a p1.exsc4.tellme.com out of the monitoring system config file.

This is the point, and the power of inheritance. Because several items inherit from one, a change made there will take effect in several places at once.

Verdad implements multiple inheritance to make it possible for it to hold information about different kinds of data simultaneously. See the production item above for an example of multiple inheritance.

For instance, taking a machine out of service should in no way effect the security policy related to which users are allowed to login to it. In our example above, it's easy to see that the complete form of the item remains unchanged with respect to the user tags.

Handling Conflicts -- Overlay

What if you set up two parent items which both had the same tags in them? You'd have a conflict, and Verdad has several policies available to resolve them. You choose which one is appropriate based on the kind of data stored in a particular tag. Every tag can have a different inheritance policy.

The default inheritance policy is ``overlay''. The rule it implements is ``last wins''. This is best illustrated in the following example:

        item mom
                eyes = blue
                hair = blond
        item dad
                eyes = green
        item kid
                is = ( mom dad )

In this case, the complete form of the item kid is:

        item kid
                eyes = green
                hair = blond

Because Verdad processed the is tag in order, the first value eyes had was blue. But it was immediately replaced by the value green when the second is, dad, was processed. Because the dad was apparently bald, the child inherited the hair from the mother.

Handling Conflicts -- Append

Sometimes it makes sense for all the ancestor items to contribute to a certain tag. In this case, the correct inheritance policy to choose is ``append''. It is denoted by using the ``+='' separator instead of the usual ``='' one.

For instance:

        item module-vi
                pkg += vi
        item module-emacs
                pkg += emacs
        item production-pkgs
                is = ( module-vi module-emacs)

The complete form of the production-pkgs item is this:

        item production-pkgs
                pkg = ( vi emacs )
                is = ( module-vi module-emacs)

As you can see, the pkg tag had each of the values of the pkg tag from the ancestors added into it.

Handling Conflicts -- Rename

Sometimes it makes sense to keep track of where a tag came from by changing it's name. Here's an example of the ``rename'' inheritance policy:

        item module-vi
                pkg += vi
                contact ~= jra
        item production-pkgs
                is = module-vi

Here is the complete form of the production-pkgs item:

        item production-pkgs
                pkg = vi
                module-vi-contact = jra
                is = module-vi

Rename will act different when the tag named ``type'' exists in the parent item. Instead of prepending the item name, both the value of the ``type'' tag and the item name get prepended.

Don't pass on a tag

If you want to put a tag in a parent item, but don't want that tag to appear in any children items, use the inheritance policy ``:''. For instance:

        item hardware:rackable
                comment := "An item to hold hardware details about rackables."
                ram = "2 gigs"
        item web01.p1.exsc4.tellme.com
                is = hardware:rackable

The comment tag from hardware:rackable does not end up in the complete item for web01.

Pruning Inheritance

Another type of inheritance policy is the ``don't inherit'' policy. It can be used to trim tags inherited at higher levels. The difference between this feature and the colon inheritance policy above is that this can be used in (some) children to prune inheritance. Colon is used in the parent to prevent the tag from ever being passed on to children at all.

Here are two simple items as they might appear in the database:

        item parent
                tag1 = value
                tag2 = value
        item child
                is = parent
                tag1 x= ''

The complete item named child would look like this:

        item child
                is = parent
                tag2 = value

The ``x='' says ``as you incorporate this item's tags into the complete item, remove all previous tags named tag1''. Note that the value following the X= in this case is completely ignored. It is conventional to make it an empty string.

If you were to use ``X='' instead (i.e. tag X= ''), then all tags with ``tag'' as the prefix would be removed from the current item.

Overrides

A very useful application of the ``last wins'' rule is to use an overlapping tag name to override a setting inherited from an ancestor. For instance, say the simple form of the child was changed to this:

        item kid
                is = mom
                is = dad
                eyes = hazel

In this case, the complete item will look like this:

        item kid
                eyes = hazel
                hair = blond

The eyes tag in the child item overrides the eyes passed on from the dad.

A common use for an override like this would be to take one host out of service while all the others remained in service. To do so, you'd temporarily add an in-service = false line to the host item. This would override the in-service tag inherited from the p1.exsc4.tellme.com tag, taking the machine out of service.

How the magic happens

Here's the algorithm to go from an item name to a complete item:

        using a depth first search across all the is'es for this
        item, assemble an "is path"
        the earliest is'es end up at the beginning of the "is path",
        and the latest one (the item itself) ends up at the end.
        foreach item in the "is path"
                fetch the simple form of the ancestor, as well as the
                desired inheritance policy. Using the policy,
                make a new tag and value.

The result is a complete item for the given item name.

The depth-first-search is implemented with a stack, instead of recursion. It uses a hash of already-visited items to avoid falling into loops. The database stores all items in their simple form, and a table indicating which items inherit from which others, so that the ``is path'' can be quickly determined.

This table is one of the tables rebuilt using the vdadm index command. You should consider using it if inheritance does not seem to be working correctly. When an item is added with a forward reference to it's parent item, the index will be inconsistent until either the item is edited, or the index is rebuilt.


Author

Jeff R. Allen <jra@nella.org>