Home Home > 2009 > 12 > 06 > KIWI RELAX NG Schema Explained
Sign up | Login

Deprecation notice: openSUSE Lizards user blog platform is deprecated, and will remain read only for the time being. Learn more...

KIWI RELAX NG Schema Explained

December 6th, 2009 by

KIWI, invented by Marcus Schäfer, is a magnificent tool to build your own SUSE Linux distribution. It is also the backend of SUSE Studio.

For those who has used KIWI manually already know the details: KIWI’s configuration file is XML and based on a RELAX NG schema. This article give developers a little background of the history, a short overview of some design decisions around KIWI’s RELAX NG schema, and how to customize it to your needs.

Invent it—The History

When KIWI was young, it used a W3C XML schema to validate its configuration file. Well, for several reasons (which is unimportant for this post) I don’t really like this schema language. Some time ago I discovered RELAX NG, a schema language which was used to develop DocBook 5.
RELAX NG has less than 30 elements, can be written in XML or in a compact syntax and is surprisingly simple. To become more familiar with this schema language, I thought it would be a good idea to rewrite KIWI’s W3C XML Schema into RELAX NG.

Although there are tools that can convert it, it is much more fun to do it manually. 🙂  Well, the old schema was also partely documented, so I thought this could be integrated as well. Well, after some testing I’ve sent the first draft of the RELAX NG schema to Marcus on November 11, 2007. I never thought he would integrate it into his production code. Well, you know the result. Thanks Marcus! 🙂

Design it—What We Need

Based on Marcus’ former W3C schema, the RELAX NG schema had the following design decisions in mind:

  • The Compact syntax (also known as RNC) was used.
  • Elements were defined as named patterns for easier customization.
  • Single attributes were also definied as named patterns.
  • A group of attributes were collected as named pattern.
  • A convention (naming schema) made the RELAX NG schema much more consistent. This naming schema is borrowed from DocBook 5.
  • Datatypes were used, if possible.
  • Annotations were integrated to document KIWI’s elements, attributes, and attribute values.

I will just focus on some principles. This makes it easier to find your way through the schema, if needed. To explain the complete schema would be too boring.

Investigate It—The Technical Details

Ok, let’s consider the image element, KIWI’s root element. KIWI’s RNC schema says:

k.image =
    ## The root element of the configuration file
    [
      db:para [
        "Each KIWI configuration file consists of a root element\x{a}" ~
        "        image."
      ]
    ]
    element image {
      k.image.attlist
      & k.description
      & k.preferences+
      & k.profiles?
      & k.instsource?
      & k.users*
      & k.drivers*
      & k.repository+
      & k.pxedeploy?
      & k.split?
      & k.packages*
      & k.vmwareconfig?
      & k.xenconfig?
    }

What does that mean?? Let’s go through it step by step:

  • k.image =
    This is a definition of a named pattern. I used the convention k.ELEMENTNAME for each element in the schema.
  • ## The root element of the configuration file
    Although it looks like a comment, it is an annotation actually. Annotations are used to document the corresponding object, which is always a good idea. In this case this is even better: any XML editor which supports annotations can read it and displays it as tool tips or the like on request. Usually annotations are short.
  • [
    db:para [ "Each KIWI configuration file ..." ]
    ]

    RELAX NG allows you to insert elements from foreign namespaces. The db:para element is from DocBook 5.  I used it to insert more descriptions or example when the object needs a more elaborate explanation. This element can also be create a kind of “API documentation”.

  • element image { ... }
    We want an image element and this line defines it. The KIWI elements do not belong to a namespace at the moment.
  • k.image.attlist
    This refers to all attributes of the image element. I used the convention k.ELEMENTNAME.attlist to group all attributes for the element ELEMENTNAME. A single attribute is named k.ELEMENTNAME.ATTRIBUTENAME.attribute. In its full beauty, the k.image.attlist pattern looks like this:

    k.image.attlist = k.image.name.attribute
    		& k.image.displayname.attribute?
    		& k.image.inherit.attribute?
    		& k.image.kiwirevision.attribute?
    		& k.image.id?
    		& k.image.schemaversion.attribute
    		& ( k.image.noNamespaceSchemaLocation.attribute?
    		  | k.image.schemaLocation.attribute? )?

    As you can see, the image element contains several attributes, some of them are optional (flagged with the “?” character.) Attributes in XML have no order. The compact syntax expresses this with the interleave pattern, available as “&” character.

  • k.description & k.preferences+ & ...
    The content model (relationships and structure) of the image element. The schema allows an unordered modell which is expressed with the interleave pattern (&).

To summerize it: each element in the KIWI schema contains a short annotation, a more verbose documentation in DocBook 5, and the corresponding content modell. Attributes have a similar structure.

Customize it—Modify The Schema

Why this effort you might ask? I made the KIWI RELAX NG schema extensible and added lots of named patterns so it is very easy to customize it.

Maybe you program a new functionality and need a new element or attribute. However, you still need the original, unchanged schema. How can you do this? One solution to this problem is to customize the KIWI schema: include the original schema and overwrite the named patterns with your changes. Some of these named patterns are introduced in the above list and it is straightforward to derive the name of a certain element or attribute according to the naming convention.

It is pretty easy to add or remove elements, attributes, or attribute values. For example, the following lines adds an optional remote attribute (definied in k.user.remote.attribute) to k.user.attlist which belongs to the user element:

include "KIWISchema.rnc"

k.user.remote.attribute =
  ## Is user a remote user?
  attribute remote { xsd:boolean }

k.user.attlist &= k.user.remote.attribute?

So what happens here?

First, the original KIWI RELAX NG schema is incorporated with include. As we want to add a new attribute, we define a new pattern and name it k.user.remote.attribute. There we insert the annotation and define the attribute remote.
Finally, we just extend the existing attribute collection k.user.attlist with our new attribute. This is done with the &= notation. If you used = you would overwrite the k.user.remote.attribute named pattern. The result is one attribute remote in the element user which is not what we intended.

I know, the example is a bit artifical. Normally you don’t need to touch the KIWI schema. However, if you need it, the article has demonstrated how you can extend the schema with just a few lines of code.

Save the above lines in a file and move it into the directory where the KIWI RNC schema is stored. Use the customization file in your code instead of the original schema to “activate” it. Your configuration file validates with the new, optional remote attribute in the user element.

Enjoy! 🙂

Both comments and pings are currently closed.

Comments are closed.