<?xml version="1.0" encoding="UTF-8"?>
<!-- A relax-ng grammar for virtual machine image descriptors -->
<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0"
         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
  <start>
    <!-- An entire image consists of two parts: a description of the
         virtual machine in the image and a description of the
         storage backing the machine -->
    <element name="image">
      <!-- A machine-usable name for this image -->
      <element name="name">
        <optional>
          <attribute name="version"><ref name="verString"/></attribute>
        </optional>
        <optional>
          <attribute name="release"><ref name="verString"/></attribute>
        </optional>
        <ref name="genericName"/>
      </element>
      <!--  A human-readable label and description, mostly
            to support UI's -->
      <optional>
        <element name="label"><data type="string"/></element>
      </optional>
      <optional>
        <element name="description"><data type="string"/></element>
      </optional>
      <element name="domain">
        <ref name="domain"/>
      </element>
      <element name="storage">
        <ref name="storage"/>
      </element>
    </element>
  </start>

  <!-- The basics of the virtual machine: a list of boot descriptors
       that indicate how the virtual machine can be booted on various hosts,
       and a few statements listing what hardware should be available to the VM
  -->
  <define name="domain">
    <oneOrMore>
      <element name="boot"><ref name="boot"/></element>
    </oneOrMore>
    <element name="devices">
      <!-- Number of virtual CPU's -->
      <element name="vcpu"><ref name="countCPU"/></element>
      <!-- Size of memory (in kB) -->
      <element name="memory"><ref name="memoryKB"/></element>
      <!--The number of network interfaces which should exist -->
      <zeroOrMore>
        <element name="interface"><empty/></element>
      </zeroOrMore>
      <!-- Whether the VM has a graphical interface -->
      <element name="graphics"><empty/></element>
    </element>
  </define>

  <!--
       The boot descriptor for a virtual machine contains what capabilties
       the host must provide (in the guest element), how to boot the
       machine on such a host (in the os element) and how the disks from
       the storage section are to be mapped to drives in the machine
  -->
  <define name="boot">
    <attribute name="type">
      <choice>
        <value>xen</value> <!-- for a paravirt Xen host -->
        <value>hvm</value> <!-- for various fully-virt host platforms -->
      </choice>
    </attribute>
    <element name="guest">
      <!-- The architecture we require to boot -->
      <element name="arch">
        <choice>
          <value>i686</value>
          <value>x86_64</value>
          <value>mips</value>
          <value>sparc</value>
          <value>ppc</value>
        </choice>
      </element>
      <!-- Host features we need -->
      <optional>
        <element name="features">
          <optional>
            <element name="pae"><ref name="feature-content"/></element>
          </optional>
          <optional>
            <element name="acpi"><ref name="feature-content"/></element>
          </optional>
          <optional>
            <element name="apic"><ref name="feature-content"/></element>
          </optional>
        </element>
      </optional>
    </element>

    <element name="os">
      <!-- The details of how to boot the machine: either a loader or
           a kernel/initrd (for paravirt)
      -->
      <choice>
        <!--
             An explicit loader: for hvm machines, should not have any content,
             since the loader is chosen depending on the host platform.
             Use the dev attribute to indicate from which device to boot
             For paravirt, specify 'pygrub'; the device to boot from needs to
             be the first one mapped with a 'drive' element
        -->
        <element name="loader">
          <optional>
            <attribute name="dev">
              <choice>
                <value>hd</value>
                <value>cdrom</value>
              </choice>
            </attribute>
          </optional>
          <choice>
            <empty/>              <!-- for fullyvirt -->
            <value>pygrub</value> <!-- for paravirt -->
          </choice>
        </element>
        <!--
             Boot a kernel/initrd contained in the image
        -->
        <group>
          <element name="kernel"><ref name="relFilePath"/></element>
          <optional>
            <element name="initrd"><ref name="relFilePath"/></element>
          </optional>
          <optional>
            <element name="cmdline"><text/></element>
          </optional>
        </group>
      </choice>
    </element>

    <!--
        Mapping of disks from the storage section into drives in the
        virtual machine. The disk attribute names the disk, and the
        optional target attribute defines as which device that disk should
        appear inside the VM
    -->
    <oneOrMore>
      <element name="drive">
        <attribute name="disk"><ref name="relFilePath"/></attribute>
        <optional>
          <attribute name="target"><ref name="deviceName"/></attribute>
        </optional>
      </element>
    </oneOrMore>
  </define>

  <!--
       The storage section of the image description, consisting of a simple
       list of disks.
  -->
  <define name="storage">
    <oneOrMore>
      <!-- A disk for mapping into a virtual machine
           id:   a shorthand for referencing the disk. If this is missing,
                 the value of the file attribute is used to reference  the
                 disk
           file: file name, must be a legal Unix path name. Only relative 
                 paths are allowed, and they are interpreted relative to the 
                 location of the image descriptor.
           use:  only important for updating existing images
                 through replacement of entire disks
           size: size of the disk in MB, needed only for user and scratch
                 disks that are not contained in the image; a blank disk
                 of that size will be created automatically.
           format: raw for an uncompressed disk image (default)
                   iso for an ISO
      -->
      <element name="disk">
        <optional>
          <attribute name="id"><data type="ID"/></attribute>
        </optional>
        <attribute name="file"><ref name="relFilePath"/></attribute>
        <attribute name="use">
          <choice>
            <value>system</value>
            <value>user</value>
            <value>scratch</value>
          </choice>
        </attribute>
        <optional>
          <attribute name="size"><ref name="unsignedInt"/></attribute>
        </optional>
        <optional>
          <attribute name="format">
            <choice>
              <value>raw</value>
              <value>iso</value>
            </choice>
          </attribute>
        </optional>
        <optional>
          <element name="checksum">
            <attribute name="type">
              <choice>
                <value>sha1</value>
                <value>sha256</value>
              </choice>
            </attribute>
            <text/>
          </element>
        </optional>
      </element>
    </oneOrMore>
  </define>

  <!-- The content for individual features like pae, acpi and apic.
       The state attribute defaults to 'on'
  -->
  <define name="feature-content">
    <optional>
      <attribute name="state">
        <choice>
          <value>on</value>
          <value>off</value>
        </choice>
      </attribute>
    </optional>
    <empty/>
  </define>

  <!--
       Type library
    -->
  <define name='unsignedInt'>
    <data type='unsignedInt'>
      <param name="pattern">[0-9]+</param>
    </data>
  </define>
  <define name='countCPU'>
    <data type='unsignedShort'>
      <param name="pattern">[0-9]+</param>
      <param name="minInclusive">1</param>
    </data>
  </define>
  <define name='memoryKB'>
    <data type='unsignedInt'>
      <param name="pattern">[0-9]+</param>
      <param name="minInclusive">4000</param>
    </data>
  </define>
  <define name='genericName'>
    <data type='string'>
      <param name="pattern">[A-Za-z0-9_\.\+\-:/]+</param>
    </data>
  </define>
  <define name='fileName'>
    <data type='string'>
      <param name="pattern">[^/]+</param>
    </data>
  </define>
  <define name='relFilePath'>
    <data type='string'>
      <param name="pattern">[a-zA-Z0-9_\.\+\-%][a-zA-Z0-9_\.\+\-%/]*</param>
    </data>
  </define>
  <define name='deviceName'>
    <data type='string'>
      <param name="pattern">[a-zA-Z0-9_\.\-:/]+</param>
    </data>
  </define>
  <define name="verString">
    <data type="string">
      <param name="pattern">[0-9\.]+(-[0-9\.]+)?</param>
    </data>
  </define>
</grammar>
