[Fedora-livecd-list] PATCH: Disk (appliance) creator tool

Daniel P. Berrange berrange at redhat.com
Sun Feb 17 21:26:04 UTC 2008


On Sun, Feb 17, 2008 at 02:14:43AM +0000, Daniel P. Berrange wrote:
> Future enhancements for this:
> 
>  - Allow splitting across multiple disks (sda, sdb, etc)
> 
>  - When staging final image, use qemu-img to generate qcow2, vmdk
>    or raw files at user's choice. qcow2 would give huge size savings
> 
>  - Output XML file for use with 'virt-image' tool, so that the entire
>    build and deploy process consists of nothing more than:
> 
>      # disk-creator  ovirt-wui-appliance.ks
>      # virt-image ovirt-wui-appliance.xml

This new version of the patch implements all three of the above features.

As an example, creating an appliance with 2 disks (sda & sdb), outputting
in qcow2 format:

   disk-creator --format qcow2 \
                --name ovirt-wui \
                --disk ovirt-wui-os --size 5000 \
                --disk ovirt-wui-data --size 1000 \
                ovirt-wui-appliance.ks 

You'll end up with ovirt-wui-os.qcow2, ovirt-wui-data.qcow2 and ovirt-wui.xml

This can be deployed as a VM with 

   virt-image --connect qemu:///system ovirt-wui.xml

Well, due to a bug in virt-image, it'll refuse to create a VM with qcow2 
disks, but I've fixed that upstream. For now just leave out the --format
arg to create in raw file.

I'm definitely now of opinion we should actually name it appliance-creator
instead of disk-creator, but not changed it in this patch yet.

The attached patch touches:

 API                    |    4 
 Makefile               |    3 
 imgcreate/__init__.py  |    3 
 imgcreate/creator.py   |   17 +
 imgcreate/disk.py      |  146 ++++++++++----
 imgcreate/fs.py        |  480 +++++++++++++++++++++++++++++++++++++++----------
 imgcreate/kickstart.py |    3 
 imgcreate/live.py      |    6 
 livecd-tools.spec      |    1 
 tools/disk-creator     |   49 ++++-
 10 files changed, 569 insertions(+), 143 deletions(-)

Also attaching the new kickstart file I'm using for multi-disks.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
-------------- next part --------------
diff --git a/API b/API
index 9783c74..ef9c1aa 100644
--- a/API
+++ b/API
@@ -76,3 +76,7 @@ switching from the LiveImageCreator to another ImageCreator object.
   build live images which use dm-snapshot, etc.  This is what is used
   by livecd-creator.
 
+* DiskImageCreator: This generates disk images containing multiple
+  partitions in a loopback file. It installs grub in the MBR and
+  can be directly booted in any virtual machine
+
diff --git a/Makefile b/Makefile
index 050fe88..d3d4ab7 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ all:
 install:
 	$(INSTALL_PROGRAM) -D tools/livecd-creator $(DESTDIR)/usr/bin/livecd-creator
 	$(INSTALL_PROGRAM) -D tools/image-creator $(DESTDIR)/usr/bin/image-creator
+	$(INSTALL_PROGRAM) -D tools/disk-creator $(DESTDIR)/usr/bin/disk-creator
 	$(INSTALL_PROGRAM) -D tools/livecd-iso-to-disk.sh $(DESTDIR)/usr/bin/livecd-iso-to-disk
 	$(INSTALL_PROGRAM) -D tools/livecd-iso-to-pxeboot.sh $(DESTDIR)/usr/bin/livecd-iso-to-pxeboot
 	$(INSTALL_PROGRAM) -D tools/mayflower $(DESTDIR)/usr/lib/livecd-creator/mayflower
@@ -34,6 +35,8 @@ install:
 uninstall:
 	rm -f $(DESTDIR)/usr/bin/livecd-creator
 	rm -rf $(DESTDIR)/usr/lib/livecd-creator
+	rm -rf $(DESTDIR)/usr/bin/disk-creator
+	rm -rf $(DESTDIR)/usr/bin/image-creator
 	rm -rf $(DESTDIR)/usr/share/doc/livecd-tools-$(VERSION)
 	rm -rf $(DESTDIR)/usr/share/livecd-tools
 
diff --git a/imgcreate/__init__.py b/imgcreate/__init__.py
index e535014..44f3ec5 100644
--- a/imgcreate/__init__.py
+++ b/imgcreate/__init__.py
@@ -21,6 +21,7 @@ from imgcreate.creator import *
 from imgcreate.yuminst import *
 from imgcreate.kickstart import *
 from imgcreate.fs import *
+from imgcreate.disk import *
 
 """A set of classes for building Fedora system images.
 
@@ -28,6 +29,7 @@ The following image creators are available:
   - ImageCreator - installs to a directory
   - LoopImageCreator - installs to an ext3 image
   - LiveImageCreator - installs to a bootable ISO
+  - DiskImageCreator - installs to a partitioned disk image
 
 Also exported are:
   - CreatorError - all exceptions throw are of this type
@@ -60,6 +62,7 @@ __all__ = (
     'ImageCreator',
     'LiveImageCreator',
     'LoopImageCreator',
+    'DiskImageCreator',
     'FSLABEL_MAXLEN',
     'read_kickstart',
     'construct_name'
diff --git a/imgcreate/creator.py b/imgcreate/creator.py
index c2ed770..fb3b309 100644
--- a/imgcreate/creator.py
+++ b/imgcreate/creator.py
@@ -207,7 +207,11 @@ class ImageCreator(object):
 
         """
         s =  "/dev/root  /         %s    defaults,noatime 0 0\n" %(self._fstype)
-        s += "devpts     /dev/pts  devpts  gid=5,mode=620   0 0\n"
+        s += self._get_fstab_special()
+        return s
+
+    def _get_fstab_special(self):
+        s = "devpts     /dev/pts  devpts  gid=5,mode=620   0 0\n"
         s += "tmpfs      /dev/shm  tmpfs   defaults         0 0\n"
         s += "proc       /proc     proc    defaults         0 0\n"
         s += "sysfs      /sys      sysfs   defaults         0 0\n"
@@ -816,12 +820,11 @@ class LoopImageCreator(ImageCreator):
         if not base_on is None:
             shutil.copyfile(base_on, self._image)
 
-        self.__instloop = SparseExtLoopbackMount(self._image,
-                                                 self._instroot,
-                                                 self.__image_size,
-                                                 self.__fstype,
-                                                 self.__blocksize,
-                                                 self.fslabel)
+        self.__instloop = ExtDiskMount(SparseLoopbackDisk(self._image, self.__image_size),
+                                       self._instroot,
+                                       self.__fstype,
+                                       self.__blocksize,
+                                       self.fslabel)
 
         try:
             self.__instloop.mount()
diff --git a/imgcreate/disk.py b/imgcreate/disk.py
index a0dd6e9..6027b75 100644
--- a/imgcreate/disk.py
+++ b/imgcreate/disk.py
@@ -37,7 +37,7 @@ class DiskImageCreator(ImageCreator):
 
     """
 
-    def __init__(self, ks, name):
+    def __init__(self, ks, name, disks, format="raw"):
         """Initialize a DiskImageCreator instance.
 
         This method takes the same arguments as ImageCreator.__init__()
@@ -47,32 +47,20 @@ class DiskImageCreator(ImageCreator):
 
         self.__instloop = None
         self.__imgdir = None
-
-        # XXX configure me :-)
-        self.__disk_size = 8192L * 1024 * 1024
-
-    def __get_disk(self):
-        if self.__imgdir is None:
-            raise CreatorError("_disk is not valid before calling mount()")
-        return self.__imgdir + "/disk.dsk"
-    _disk = property(__get_disk)
-    """The location of the disk file.
-
-    This is the path to the filesystem disk. Subclasses may use this path
-    in order to package the disk in _stage_final_disk().
-
-    Note, this directory does not exist before DiskCreator.mount() is called.
-
-    Note also, this is a read-only attribute.
-
-    """
+        self.__disks = disks
+        self.__format = format
 
     def _get_fstab(self):
         s = ""
-        # XXX > 1 disk, don't assume sda
-        for p in self.__instloop.partitions:
+        for mp in self.__instloop.mountOrder:
+            p = None
+            for p1 in self.__instloop.partitions:
+                if p1['mountpoint'] == mp:
+                    p = p1
+                    break
+
             s +=  "%(device)s  %(mountpoint)s %(fstype)s    defaults,noatime 0 0\n" %  {
-                'device': "/dev/sda%-d" % p['num'],
+                'device': "/dev/%s%-d" % (p['disk'], p['num']),
                 'mountpoint': p['mountpoint'],
                 'fstype': p['fstype'] }
 
@@ -86,18 +74,35 @@ class DiskImageCreator(ImageCreator):
         self.__imgdir = self._mkdtemp()
 
         parts = kickstart.get_partitions(self.ks)
+
+        devnames = []
+        for p in parts:
+            dev = p.disk
+            if not dev in devnames:
+                devnames.append(dev)
+        devnames.sort()
         
-        self.__instloop = PartitionedMount(SparseLoopbackDisk(self._disk, self.__disk_size),
+        disks = []
+
+        if len(devnames) != len(self.__disks):
+            raise CreatorError("Not enough disks for declared partitions")
+
+        for i in range(len(self.__disks)):
+            disk = SparseLoopbackDisk("%s/disk%d.raw" % (self.__imgdir, i),
+                                      self.__disks[i]['size'])
+            self.__disks[i]['disk'] = disk
+            disks.append(disk)
+
+        self.__instloop = PartitionedMount(disks, devnames,
                                            self._instroot)
 
         for p in parts:
-            self.__instloop.add_partition(int(p.size), p.mountpoint, p.fstype)
+            self.__instloop.add_partition(int(p.size), p.disk, p.mountpoint, p.fstype)
 
         try:
             self.__instloop.mount()
         except MountError, e:
-            raise CreatorError("Failed mount '%s' : %s" %
-                               (self._disk, e))
+            raise CreatorError("Failed mount disks : %s" % e)
 
     def _get_required_packages(self):
         return ["grub"]
@@ -110,6 +115,8 @@ class DiskImageCreator(ImageCreator):
             if not dev in devs:
                 devs.append(dev)
 
+        devs.sort()
+
         n = 0
         devmap = ""
         for dev in devs:
@@ -124,7 +131,6 @@ class DiskImageCreator(ImageCreator):
         bootdevnum = None
         rootdevnum = None
         rootdev = None
-        # XXX > 1 disk, don't assume sda
         for p in self.__instloop.partitions:
             if p['mountpoint'] == "/boot":
                 bootdevnum = p['num'] - 1
@@ -133,7 +139,7 @@ class DiskImageCreator(ImageCreator):
 
             if p['mountpoint'] == "/":
                 rootdevnum = p['num'] - 1
-                rootdev = "/dev/sda%-d" % p['num']
+                rootdev = "/dev/%s%-d" % (p['disk'], p['num'])
 
         prefix = ""
         if bootdevnum == rootdevnum:
@@ -144,11 +150,15 @@ class DiskImageCreator(ImageCreator):
     def _create_grub_config(self):
         (bootdevnum, rootdevnum, rootdev, prefix) = self._get_grub_boot_config()
 
+        # NB we're assuming that grub config is on the first physical disk
+        # ie /boot must be on sda, or if there's no /boot, then / must be sda
+
+        # XXX don't hardcode default kernel - see livecd code
         grub = ""
         grub += "default=0\n"
         grub += "timeout=5\n"
-        grub += "splashimage=(hd0,%-d)%s/grub/splash.xpm.gz\n" % (rootdevnum, prefix)
-        grub += "hiddenemnu\n"
+        grub += "splashimage=(hd0,%d)%s/grub/splash.xpm.gz\n" % (bootdevnum, prefix)
+        grub += "hiddenmenu\n"
         
         versions = []
         kernels = self._get_kernel_versions()
@@ -158,11 +168,11 @@ class DiskImageCreator(ImageCreator):
 
         for v in versions:
             grub += "title Fedora (%s)\n" % v
-            grub += "        root (hd0,%-d)\n" % bootdevnum
+            grub += "        root (hd0,%d)\n" % bootdevnum
             grub += "        kernel %s/vmlinuz-%s ro root=%s\n" % (prefix, v, rootdev)
             grub += "        initrd %s/initrd-%s.img\n" % (prefix, v)
 
-        cfg = open(self._instroot + "/boot/grub/grub.cfg", "w")
+        cfg = open(self._instroot + "/boot/grub/grub.conf", "w")
         cfg.write(grub)
         cfg.close()
 
@@ -185,11 +195,18 @@ class DiskImageCreator(ImageCreator):
     def _install_grub(self):
         (bootdevnum, rootdevnum, rootdev, prefix) = self._get_grub_boot_config()
 
+        # Ensure all data is flushed to disk before doing grub install
+        subprocess.call(["sync"])
+
+        stage2 = self._instroot + "/boot/grub/stage2"
+
         setup = ""
-        setup += "device (hd0) %s\n" % (self.__instloop.disk.device)
-        setup += "root (hd0,%d)\n" % rootdevnum
-        setup += "configfile (hd0,%d)%s/grub/grub.cfg" % (bootdevnum, prefix)
-        setup += "setup --prefix=%s/grub  (hd0) (hd0,%d)\n" % (prefix, bootdevnum)
+        for i in range(len(self.__disks)):
+            loopdev = self.__disks[i]['disk'].device
+            setup += "device (hd%d) %s\n" % (i, loopdev)
+        setup += "root (hd0,%d)\n" % bootdevnum
+        setup += "setup --stage2=%s --prefix=%s/grub  (hd0)\n" % (stage2, prefix)
+        setup += "quit\n"
 
         grub = subprocess.Popen(["grub", "--batch", "--no-floppy"],
                                 stdin=subprocess.PIPE)
@@ -211,7 +228,58 @@ class DiskImageCreator(ImageCreator):
     def _resparse(self, size = None):
         return self.__instloop.resparse(size)
 
+
+    def _write_image_xml(self):
+        xml = "<image>\n"
+        xml += "  <name>%s</name>\n" % self.name
+        xml += "  <domain>\n"
+        # XXX don't hardcode - determine based on the kernel we installed for grub
+        # baremetal vs xen
+        xml += "    <boot type='hvm'>\n"
+        xml += "      <guest>\n"
+        xml += "        <arch>%s</arch>\n" % os.uname()[4]
+        xml += "      </guest>\n"
+        xml += "      <os>\n"
+        xml += "        <loader dev='hd'/>\n"
+        xml += "      </os>\n"
+        for i in range(len(self.__disks)):
+            xml += "      <drive disk='%s.%s' target='hd%s'/>\n" % (self.__disks[i]['name'], self.__format, chr(ord('a')+i))
+        xml += "    </boot>\n"
+        xml += "    <devices>\n"
+        xml += "      <vcpu>1</vcpu>\n"
+        xml += "      <memory>%d</memory>\n" %(256 * 1024)
+        xml += "      <interface/>\n"
+        xml += "      <graphics/>\n"
+        xml += "    </devices>\n"
+        xml += "  </domain>\n"
+        xml += "  <storage>\n"
+        for i in range(len(self.__disks)):
+            # XXX don't hardcode raw
+            xml += "    <disk file='%s.%s' use='system' format='%s'/>\n" % (self.__disks[i]['name'], self.__format, self.__format)
+        xml += "  </storage>\n"
+        xml += "</image>\n"
+
+        print "Writing %s/%s.xml" %  (self._outdir, self.name)
+        cfg = open("%s/%s.xml" % (self._outdir, self.name), "w")
+        cfg.write(xml)
+        cfg.close()
+        
+
     def _stage_final_image(self):
         self._resparse()
-        print "Copying image to %s " % (self._outdir + "/" + self.name + ".dsk",)
-        shutil.move(self._disk, self._outdir + "/" + self.name + ".dsk")
+
+        print "Writing image XML"
+        self._write_image_xml()
+        print "Moving disks to final location"
+        for i in range(len(self.__disks)):
+            dst = "%s/%s.%s" % (self._outdir, self.__disks[i]['name'], self.__format)
+            if self.__format == "raw":
+                print "Moving tmp %s image to %s " % (self.__disks[i]['disk'].lofile, dst)
+                shutil.move(self.__disks[i]['disk'].lofile, dst)
+            else:
+                rc = subprocess.call(["qemu-img", "convert",
+                                      "-f", "raw", self.__disks[i]['disk'].lofile,
+                                      "-O", self.__format,  dst])
+                if rc != 0:
+                    raise CreatorError("Unable to convert disk to %s" % (self.__format))
+        print "All done"
diff --git a/imgcreate/fs.py b/imgcreate/fs.py
index 9ca3a3e..8d73102 100644
--- a/imgcreate/fs.py
+++ b/imgcreate/fs.py
@@ -86,42 +86,51 @@ class BindChrootMount:
         subprocess.call(["/bin/umount", self.dest])
         self.mounted = False
 
-class LoopbackMount:
-    def __init__(self, lofile, mountdir, fstype = None):
-        self.lofile = lofile
-        self.mountdir = mountdir
-        self.fstype = fstype
+class Disk:
+    def __init__(self, size, device = None):
+        self._device = device
+        self._size = size
 
-        self.mounted = False
-        self.losetup = False
-        self.rmdir   = False
-        self.loopdev = None
+    def create(self):
+        pass
 
     def cleanup(self):
-        self.unmount()
-        self.lounsetup()
+        pass
 
-    def unmount(self):
-        if self.mounted:
-            rc = subprocess.call(["/bin/umount", self.mountdir])
-            if rc == 0:
-                self.mounted = False
+    def get_device(self):
+        return self._device
+    def set_device(self, path):
+        self._device = path
+    device = property(get_device, set_device)
 
-        if self.rmdir and not self.mounted:
-            try:
-                os.rmdir(self.mountdir)
-            except OSError, e:
-                pass
-            self.rmdir = False
+    def get_size(self):
+        return self._size
+    size = property(get_size)
+
+
+class RawDisk(Disk):
+    def __init__(self, size, device):
+        Disk.__init__(self, size, device)
+
+    def fixed(self):
+        return True
 
-    def lounsetup(self):
-        if self.losetup:
-            rc = subprocess.call(["/sbin/losetup", "-d", self.loopdev])
-            self.losetup = False
-            self.loopdev = None
+    def exists(self):
+        return True
 
-    def loopsetup(self):
-        if self.losetup:
+class LoopbackDisk(Disk):
+    def __init__(self, lofile, size):
+        Disk.__init__(self, size)
+        self.lofile = lofile
+
+    def fixed(self):
+        return False
+
+    def exists(self):
+        return os.path.exists(self.lofile)
+
+    def create(self):
+        if self.device is not None:
             return
 
         losetupProc = subprocess.Popen(["/sbin/losetup", "-f"],
@@ -132,40 +141,27 @@ class LoopbackMount:
             raise MountError("Failed to allocate loop device for '%s'" %
                              self.lofile)
 
-        self.loopdev = losetupOutput.split()[0]
+        device = losetupOutput.split()[0]
 
-        rc = subprocess.call(["/sbin/losetup", self.loopdev, self.lofile])
+        print "Losetup add %s %s"  % (device, self.lofile)
+        rc = subprocess.call(["/sbin/losetup", device, self.lofile])
         if rc != 0:
             raise MountError("Failed to allocate loop device for '%s'" %
                              self.lofile)
+        self.device = device
 
-        self.losetup = True
-
-    def mount(self):
-        if self.mounted:
+    def cleanup(self):
+        if self.device is None:
             return
+        print "Losetup remove %s" % self.device
+        rc = subprocess.call(["/sbin/losetup", "-d", self.device])
+        self.device = None
 
-        self.loopsetup()
-
-        if not os.path.isdir(self.mountdir):
-            os.makedirs(self.mountdir)
-            self.rmdir = True
-
-        args = [ "/bin/mount", self.loopdev, self.mountdir ]
-        if self.fstype:
-            args.extend(["-t", self.fstype])
-
-        rc = subprocess.call(args)
-        if rc != 0:
-            raise MountError("Failed to mount '%s' to '%s'" %
-                             (self.loopdev, self.mountdir))
 
-        self.mounted = True
 
-class SparseLoopbackMount(LoopbackMount):
-    def __init__(self, lofile, mountdir, size, fstype = None):
-        LoopbackMount.__init__(self, lofile, mountdir, fstype)
-        self.size = size
+class SparseLoopbackDisk(LoopbackDisk):
+    def __init__(self, lofile, size):
+        LoopbackDisk.__init__(self, lofile, size)
 
     def expand(self, create = False, size = None):
         flags = os.O_WRONLY
@@ -191,10 +187,81 @@ class SparseLoopbackMount(LoopbackMount):
 
     def create(self):
         self.expand(create = True)
+        LoopbackDisk.create(self)
 
-class SparseExtLoopbackMount(SparseLoopbackMount):
-    def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel):
-        SparseLoopbackMount.__init__(self, lofile, mountdir, size, fstype)
+class Mount:
+    def __init__(self, mountdir):
+        self.mountdir = mountdir
+
+    def cleanup(self):
+        self.unmount()
+
+    def mount(self):
+        pass
+
+    def unmount(self):
+        pass
+
+class DiskMount(Mount):
+    def __init__(self, disk, mountdir, fstype = None, rmmountdir = True):
+        Mount.__init__(self, mountdir)
+
+        self.disk = disk
+        self.fstype = fstype
+        self.rmmountdir = rmmountdir
+
+        self.mounted = False
+        self.rmdir   = False
+
+    def cleanup(self):
+        Mount.cleanup(self)
+        self.disk.cleanup()
+
+    def unmount(self):
+        if self.mounted:
+            rc = subprocess.call(["/bin/umount", self.mountdir])
+            if rc == 0:
+                self.mounted = False
+
+        if self.rmdir and not self.mounted:
+            try:
+                os.rmdir(self.mountdir)
+            except OSError, e:
+                pass
+            self.rmdir = False
+
+
+    def __create(self):
+        print "Disk create %s" % str(self)
+        self.disk.create()
+
+
+    def mount(self):
+        if self.mounted:
+            return
+
+        print "Disk mount"
+        if not os.path.isdir(self.mountdir):
+            os.makedirs(self.mountdir)
+            self.rmdir = self.rmmountdir
+
+        self.__create()
+
+        print "Do mount %s " % self.disk.device
+        args = [ "/bin/mount", self.disk.device, self.mountdir ]
+        if self.fstype:
+            args.extend(["-t", self.fstype])
+
+        rc = subprocess.call(args)
+        if rc != 0:
+            raise MountError("Failed to mount '%s' to '%s'" %
+                             (self.disk.device, self.mountdir))
+
+        self.mounted = True
+
+class ExtDiskMount(DiskMount):
+    def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True):
+        DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir)
         self.blocksize = blocksize
         self.fslabel = fslabel
 
@@ -202,19 +269,15 @@ class SparseExtLoopbackMount(SparseLoopbackMount):
         rc = subprocess.call(["/sbin/mkfs." + self.fstype,
                               "-F", "-L", self.fslabel,
                               "-m", "1", "-b", str(self.blocksize),
-                              self.lofile,
-                              str(self.size / self.blocksize)])
+                              self.disk.device])
+        #                      str(self.disk.size / self.blocksize)])
         if rc != 0:
             raise MountError("Error creating %s filesystem" % (self.fstype,))
         subprocess.call(["/sbin/tune2fs", "-c0", "-i0", "-Odir_index",
-                         "-ouser_xattr,acl", self.lofile])
-
-    def create(self):
-        SparseLoopbackMount.create(self)
-        self.__format_filesystem()
+                         "-ouser_xattr,acl", self.disk.device])
 
-    def resize(self, size = None):
-        current_size = os.stat(self.lofile)[stat.ST_SIZE]
+    def __resize_filesystem(self, size = None):
+        current_size = os.stat(self.disk.lofile)[stat.ST_SIZE]
 
         if size is None:
             size = self.size
@@ -227,21 +290,30 @@ class SparseExtLoopbackMount(SparseLoopbackMount):
 
         self.__fsck()
 
-        resize2fs(self.lofile, size)
-
-        if size < current_size:
-            self.truncate(size)
+        resize2fs(self.disk.lofile, size)
         return size
 
-    def mount(self):
-        if not os.path.isfile(self.lofile):
-            self.create()
+    def __create(self):
+        resize = False
+        if not self.disk.fixed() and self.disk.exists():
+            resize = True
+
+        #DiskMount.__create(self)
+        self.disk.create()
+
+        if resize:
+            print "Fs resie"
+            self.__resize_filesystem()
         else:
-            self.resize()
-        return SparseLoopbackMount.mount(self)
+            print "FS format"
+            self.__format_filesystem()
+
+    def mount(self):
+        self.__create()
+        DiskMount.mount(self)
 
     def __fsck(self):
-        subprocess.call(["/sbin/e2fsck", "-f", "-y", self.lofile])
+        subprocess.call(["/sbin/e2fsck", "-f", "-y", self.disk.lofile])
 
     def __get_size_from_filesystem(self):
         def parse_field(output, field):
@@ -253,7 +325,7 @@ class SparseExtLoopbackMount(SparseLoopbackMount):
 
         dev_null = os.open("/dev/null", os.O_WRONLY)
         try:
-            out = subprocess.Popen(['/sbin/dumpe2fs', '-h', self.lofile],
+            out = subprocess.Popen(['/sbin/dumpe2fs', '-h', self.disk.lofile],
                                    stdout = subprocess.PIPE,
                                    stderr = dev_null).communicate()[0]
         finally:
@@ -273,7 +345,7 @@ class SparseExtLoopbackMount(SparseLoopbackMount):
         while top != (bot + 1):
             t = bot + ((top - bot) / 2)
 
-            if not resize2fs(self.lofile, t):
+            if not resize2fs(self.disk.lofile, t):
                 top = t
             else:
                 bot = t
@@ -281,12 +353,243 @@ class SparseExtLoopbackMount(SparseLoopbackMount):
 
     def resparse(self, size = None):
         self.cleanup()
-        
         minsize = self.__resize_to_minimal()
+        self.disk.truncate(minsize)
+        return minsize
+
+class PartitionedMount(Mount):
+    def __init__(self, disks, devnames, mountdir):
+        Mount.__init__(self, mountdir)
+        self.disks = {}
+        for i in range(len(disks)):
+            d = disks[i]
+            devname = devnames[i]
+            print "Assigned disk %s" % devname
+            self.disks[devname] = { 'disk': d,  # Disk object
+                                    'mapped': False, # True if kpartx mapping exists
+                                    'numpart': 0, # Number of allocate partitions
+                                    'partitions': [], # indexes to self.partitions
+                                    'extended': 0, # Size of extended partition
+                                    'offset': 0 } # Offset of next partition
+        self.partitions = []
+        self.mapped = False
+        self.mountOrder = []
+        self.unmountOrder = []
+
+    def add_partition(self, size, disk, mountpoint, fstype = None):
+        self.partitions.append({'size': size,
+                                'mountpoint': mountpoint, # Mount relative to chroot
+                                'fstype': fstype,
+                                'disk': disk,  # physical disk name holding partition
+                                'device': None, # kpartx device node for partition
+                                'mount': None, # Mount object
+                                'num': None}) # Partition number
+
+    def __format_disks(self):
+        print "Assigning disks"
+        for dev in self.disks.keys():
+            d = self.disks[dev]
+            print "Initializing partition table for %s" % (d['disk'].device)
+            rc = subprocess.call(["/sbin/parted", "-s", d['disk'].device, "mklabel", "msdos"])
+            if rc != 0:
+                raise MountError("Error writing partition table on %s" % d.device)
+
+        print "Assigning partitions"
+        for n in range(len(self.partitions)):
+            p = self.partitions[n]
+
+            if not self.disks.has_key(p['disk']):
+                raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint']))
+
+            d = self.disks[p['disk']]
+            d['numpart'] += 1
+            if d['numpart'] > 3:
+                # Increase allocation of extended partition to hold this partition
+                d['extended'] += p['size']
+                p['type'] = 'logical'
+                p['num'] = d['numpart'] + 1
+            else:
+                p['type'] = 'primary'
+                p['num'] = d['numpart']
+
+            p['start'] = d['offset']
+            d['offset'] += p['size']
+            d['partitions'].append(n)
+            print "Assigned %s to %s%d at %d at size %d" % (p['mountpoint'], p['disk'], p['num'], p['start'], p['size'])
+
+        # XXX we should probably work in cylinder units to keep fdisk happier..
+        start = 0
+        print "Creating partitions"
+        for p in self.partitions:
+            d = self.disks[p['disk']]
+            if p['num'] == 5:
+                print "Added extended part at %d of size %d" % (p['start'], d['extended'])
+                rc = subprocess.call(["/sbin/parted", "-s", d['disk'].device, "mkpart", "extended",
+                                      "%dM" % p['start'], "%dM" % (p['start'] + d['extended'])])
+            
+            print "Add %s part at %d of size %d" % (p['type'], p['start'], p['size'])
+            rc = subprocess.call(["/sbin/parted", "-s", d['disk'].device, "mkpart",
+                                  p['type'], "%dM" % p['start'], "%dM" % (p['start']+p['size'])])
+
+            # XXX disabled return code check because parted always fails to
+            # reload part table with loop devices. Annoying because we can't
+            # distinguish this failure from real partition failures :-(
+            if rc != 0 and 1 == 0: 
+                raise MountError("Error creating partition on %s" % d['disk'].device)
+
+    def __map_partitions(self):
+        for dev in self.disks.keys():
+            d = self.disks[dev]
+            if d['mapped']:
+                continue
+
+            kpartx = subprocess.Popen(["/sbin/kpartx", "-l", d['disk'].device],
+                                      stdout=subprocess.PIPE)
+
+            kpartxOutput = kpartx.communicate()[0].split("\n")
+            # Strip trailing blank
+            kpartxOutput = kpartxOutput[0:len(kpartxOutput)-1]
+            print "Partx planned mapping %s" % str(kpartxOutput)
+            if kpartx.returncode:
+                raise MountError("Failed to query partition mapping for '%s'" %
+                                 d.device)
+
+            # Quick sanity check that the number of partitions matches
+            # our expectation. If it doesn't, someone broke the code
+            # further up
+            if len(kpartxOutput) != d['numpart']:
+                raise MountError("Unexpected number of partitions from kpartx: %d != %d" %
+                                 (len(kpartxOutput), d['numpart']))
+
+            for i in range(len(kpartxOutput)):
+                print " Got %s" % (kpartxOutput[i])
+                line = kpartxOutput[i]
+                newdev = line.split()[0]
+                mapperdev = "/dev/mapper/" + newdev
+                loopdev = d['disk'].device + newdev[-1]
+
+                print "Dev %s: %s -> %s" % (newdev, loopdev, mapperdev)
+                pnum = d['partitions'][i]
+                self.partitions[pnum]['device'] = loopdev
+
+                # grub's install wants partitions to be named
+                # to match their parent device + partition num
+                # kpartx doesn't work like this, so we add compat
+                # symlinks to point to /dev/mapper
+                os.symlink(mapperdev, loopdev)
+
+            print "Adding partx mapping for %s" % d['disk'].device
+            rc = subprocess.call(["/sbin/kpartx", "-a", d['disk'].device])
+            if rc != 0:
+                raise MountError("Failed to map partitions for '%s'" %
+                                 d['disk'].device)
+            d['mapped'] = True
+
+
+    def __unmap_partitions(self):
+        for dev in self.disks.keys():
+            d = self.disks[dev]
+            if not d['mapped']:
+                continue
+
+            print "Removing compat symlinks"
+            for pnum in d['partitions']:
+                if self.partitions[pnum]['device'] != None:
+                    os.unlink(self.partitions[pnum]['device'])
+                    self.partitions[pnum]['device'] = None
+
+            print "Unmapping"
+            rc = subprocess.call(["/sbin/kpartx", "-d", d['disk'].device])
+            if rc != 0:
+                raise MountError("Failed to unmap partitions for '%s'" %
+                                 d['disk'].device)
+
+            d['mapped'] = False
+
+
+    def __calculate_mountorder(self):
+        for p in self.partitions:
+            self.mountOrder.append(p['mountpoint'])
+            self.unmountOrder.append(p['mountpoint'])
+
+        self.mountOrder.sort()
+        self.unmountOrder.sort()
+        self.unmountOrder.reverse()
+        print str(self.mountOrder)
 
-        self.truncate(minsize)
+    def cleanup(self):
+        print "Doing cleanup"
+        import time
+        #time.sleep(20)
+        Mount.cleanup(self)
+        print "Unmap parts"
+        self.__unmap_partitions()
+        print "Cleanup disks"
+        for dev in self.disks.keys():
+            d = self.disks[dev]
+            try:
+                print "Cleanup disk %s -> %s" % (d['disk'].lofile, d['disk'].device)
+                d['disk'].cleanup()
+            except:
+                pass
+
+    def unmount(self):
+        print "Do unmount"
+        for mp in self.unmountOrder:
+            if mp == 'swap':
+                continue
+            p = None
+            for p1 in self.partitions:
+                if p1['mountpoint'] == mp:
+                    p = p1
+                    break
+
+            if p['mount'] != None:
+                print "Clenaup %s " % p['mountpoint']
+                try:
+                    p['mount'].cleanup()
+                except:
+                    pass
+                p['mount'] = None
+        print "Done unmount"
+
+    def mount(self):
+        for dev in self.disks.keys():
+            d = self.disks[dev]
+            d['disk'].create()
+
+        self.__format_disks()
+        self.__map_partitions()
+        self.__calculate_mountorder()
+
+        for mp in self.mountOrder:
+            p = None
+            for p1 in self.partitions:
+                if p1['mountpoint'] == mp:
+                    p = p1
+                    break
+
+            if mp == 'swap':
+                subprocess.call(["/sbin/mkswap", p['device']])                                  
+                continue
+
+            print "Submount %s " % p['mountpoint']
+            rmmountdir = False
+            if p['mountpoint'] == "/":
+                rmmountdir = True
+            pdisk = ExtDiskMount(RawDisk(p['size'] * 1024 * 1024, p['device']),
+                                 self.mountdir + p['mountpoint'],
+                                 p['fstype'],
+                                 4096,
+                                 p['mountpoint'],
+                                 rmmountdir)
+            pdisk.mount()
+            p['mount'] = pdisk
+
+    def resparse(self, size = None):
+        # Can't re-sparse a disk image - too hard
+        pass
 
-        return self.resize(size)
 
 class DeviceMapperSnapshot(object):
     def __init__(self, imgloop, cowloop):
@@ -306,8 +609,8 @@ class DeviceMapperSnapshot(object):
         if self.__created:
             return
 
-        self.imgloop.loopsetup()
-        self.cowloop.loopsetup()
+        self.imgloop.create()
+        self.cowloop.create()
 
         self.__name = "imgcreate-%d-%d" % (os.getpid(),
                                            random.randint(0, 2**16))
@@ -315,8 +618,8 @@ class DeviceMapperSnapshot(object):
         size = os.stat(self.imgloop.lofile)[stat.ST_SIZE]
 
         table = "0 %d snapshot %s %s p 8" % (size / 512,
-                                             self.imgloop.loopdev,
-                                             self.cowloop.loopdev)
+                                             self.imgloop.device,
+                                             self.cowloop.device)
 
         args = ["/sbin/dmsetup", "create", self.__name, "--table", table]
         if subprocess.call(args) != 0:
@@ -382,15 +685,14 @@ class DeviceMapperSnapshot(object):
 #   8) Create a squashfs of the COW
 #
 def create_image_minimizer(path, image, minimal_size):
-    imgloop = LoopbackMount(image, "None")
+    imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter
 
-    cowloop = SparseLoopbackMount(os.path.join(os.path.dirname(path), "osmin"),
-                                  None, 64L * 1024L * 1024L)
+    cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"),
+                                 64L * 1024L * 1024L)
 
     snapshot = DeviceMapperSnapshot(imgloop, cowloop)
 
     try:
-        cowloop.create()
         snapshot.create()
 
         resize2fs(snapshot.path, minimal_size)
diff --git a/imgcreate/kickstart.py b/imgcreate/kickstart.py
index a7e0723..a92f877 100644
--- a/imgcreate/kickstart.py
+++ b/imgcreate/kickstart.py
@@ -468,6 +468,9 @@ def get_groups(ks, required = []):
 def get_excluded(ks, required = []):
     return ks.handler.packages.excludedList + required
 
+def get_partitions(ks, required = []):
+    return ks.handler.partition.partitions
+
 def ignore_missing(ks):
     return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE
 
diff --git a/imgcreate/live.py b/imgcreate/live.py
index bbb17ef..dc2aea9 100644
--- a/imgcreate/live.py
+++ b/imgcreate/live.py
@@ -130,7 +130,7 @@ class LiveImageCreatorBase(LoopImageCreator):
     #
     def __base_on_iso(self, base_on):
         """helper function to extract ext3 file system from a live CD ISO"""
-        isoloop = LoopbackMount(base_on, self._mkdtemp())
+        isoloop = Mount(LoopbackDisk(base_on), self._mkdtemp())
 
         try:
             isoloop.mount()
@@ -144,10 +144,10 @@ class LiveImageCreatorBase(LoopImageCreator):
         else:
             squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
             
-        squashloop = LoopbackMount(squashimg, self._mkdtemp(), "squashfs")
+        squashloop = Mount(LoopbackDisk(squashimg), self._mkdtemp(), "squashfs")
 
         try:
-            if not os.path.exists(squashloop.lofile):
+            if not squashloop.disk.exists():
                 raise CreatorError("'%s' is not a valid live CD ISO : "
                                    "squashfs.img doesn't exist" % base_on)
 
diff --git a/livecd-tools.spec b/livecd-tools.spec
index a789460..d9118ba 100644
--- a/livecd-tools.spec
+++ b/livecd-tools.spec
@@ -58,6 +58,7 @@ rm -rf $RPM_BUILD_ROOT
 %dir %{_datadir}/livecd-tools
 %{_datadir}/livecd-tools/*
 %{_bindir}/image-creator
+%{_bindir}/disk-creator
 %dir %{python_sitelib}/imgcreate
 %{python_sitelib}/imgcreate/*.py
 %{python_sitelib}/imgcreate/*.pyo
diff --git a/tools/disk-creator b/tools/disk-creator
index 52584f6..acd6bd7 100755
--- a/tools/disk-creator
+++ b/tools/disk-creator
@@ -25,10 +25,16 @@ import optparse
 import imgcreate
 
 def parse_options(args):
-    parser = optparse.OptionParser(usage = "%prog [--name=<name>] <kickstart>")
+    parser = optparse.OptionParser(usage = "%prog [--disk=<disk1>] [--size=<MB>] <kickstart>")
 
     parser.add_option("-n", "--name", type="string", dest="name",
-                      help="Disk name")
+                      help="Appliance name")
+    parser.add_option("-d", "--disk", type="string", dest="disk",
+                      action="append", help="Disk file name")
+    parser.add_option("-s", "--size", type="float", dest="size",
+                      action="append", help="Disk size in MB")
+    parser.add_option("-f", "--format", type="string", dest="format",
+                      help="Disk format (raw, qcow2, vmdk, ...)")
 
     (options, args) = parser.parse_args()
 
@@ -51,12 +57,45 @@ def main():
         print >> sys.stderr, "Error loading kickstart file '%s' : %s" % (kscfg, e)
         return 1
 
+    disks = []
+
+    print str(options.disk)
+    print str(options.size)
+    if options.disk is None:
+        name = imgcreate.build_name(kscfg)
+        size = 4096L * 1024L * 1024L
+        if options.size is not None:
+            if type(options.size) != float:
+                print >> sys.stderr, "Error too many disk sizes provided"
+                return 1
+            size = options.size * 1024L * 1024L
+
+        disks.append({ 'name': name, 'size': size })
+    elif type(options.disk) == list:
+        if type(options.size) != list or len(options.size) != len(options.disk):
+            print >> sys.stderr, "Error must provide a size for each disk"
+            return 1
+
+        for i in range(len(options.disk)):
+            disks.append({ 'name': options.disk[i], 'size': options.size[i] * 1024L * 1024L })
+    else:
+        size = 4096L * 1024L * 1024L
+        if options.size is not None:
+            if type(options.size) != float:
+                print >> sys.stderr, "Error too many disk sizes provided"
+                return 1
+            size = options.size * 1024L * 1024L
+        
+        disks.append({ 'name': options.disk, 'size': size })
+
+    name = imgcreate.build_name(kscfg)
     if options.name:
         name = options.name
-    else:
-        name = imgcreate.build_name(kscfg)
 
-    creator = imgcreate.DiskImageCreator(ks, name)
+    format = "raw"
+    if options.format:
+        format = options.format
+    creator = imgcreate.DiskImageCreator(ks, name, disks, format)
 
     try:
         creator.create()
-------------- next part --------------
# Kickstart file automatically generated by anaconda.

install
url --url http://download.fedora.redhat.com/pub/fedora/linux/releases/8/Fedora/x86_64/os/
lang en_US.UTF-8
keyboard us
network --device eth0 --bootproto dhcp
rootpw  --iscrypted $1$HNOucon/$m69RprODwQn4XjzVUi9TU0
firewall --disabled
authconfig --enableshadow --enablemd5
selinux --disabled
services --disabled=iptables,yum-updatesd,libvirtd,bluetooth,cups,gpm,pcscd --enabled=ntpd,dhcpd,xinetd,httpd,postgresql,ovirt-wui
timezone --utc America/New_York
text
bootloader --location=mbr --driveorder=sda
# The following is the partition information you requested
# Note that any partitions you deleted are not expressed
# here so unless you clear all partitions first, this is
# not guaranteed to work
zerombr
clearpart --all --drives=sda
part /boot --fstype ext3 --size=100 --ondisk=sda
part swap --fstype swap --size=500 --ondisk=sda
part /var/lib/pgsql --fstype ext3 --size=300 --ondisk=sdb
part / --fstype ext3 --size=3000 --ondisk=sda
part /home --fstype ext3 --size=500 --ondisk=sdb
part /var --fstype ext3 --size=1000 --ondisk=sda
#part /boot --fstype ext3 --size=100 --ondisk=sda
#part pv.2 --size=0 --grow --ondisk=sda
#volgroup VolGroup00 --pesize=32768 pv.2
#logvol swap --fstype swap --name=LogVol01 --vgname=VolGroup00 --size=512
#logvol / --fstype ext3 --name=LogVol00 --vgname=VolGroup00 --size=1024 --grow

repo --name=f8 --mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=fedora-8&arch=$basearch
repo --name=f8-updates --mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=updates-released-f8&arch=$basearch
repo --name=freeipa --baseurl=http://freeipa.com/downloads/devel/rpms/F7/$basearch/
repo --name=ovirt --baseurl=http://ovirt.et.redhat.com/repos/ovirt/$basearch/

%packages
@admin-tools
@editors
@system-tools
@text-internet
@core
@base
@hardware-support
@web-server
@sql-server
@development-libs
@legacy-fonts
@development-tools
radeontool
fuse
pax
imake
dhcp
tftp-server
tftp
dhclient
ipa-server
ipa-admintools
xinetd
libvirt
cyrus-sasl-gssapi
iscsi-initiator-utils
collectd
ruby-libvirt
ruby-postgres
ovirt-wui
firefox
xorg-x11-xauth
virt-viewer
-libgcj
-glib-java
-valgrind
-boost-devel
-frysk
-bittorrent
-fetchmail
-slrn
-cadaver
-mutt

%post

cat > /root/create_default_principals.py << \EOF
#!/usr/bin/python

import krbV
import os, string, re
import socket
import shutil

def kadmin_local(command):
        ret = os.system("/usr/kerberos/sbin/kadmin.local -q '" + command + "'")
        if ret != 0:
                raise

default_realm = krbV.Context().default_realm

# here, generate the libvirt/ principle for this machine, necessary
# for taskomatic and host-browser
this_libvirt_princ = 'libvirt/' + socket.gethostname() + '@' + default_realm
kadmin_local('addprinc -randkey +requires_preauth ' + this_libvirt_princ)
kadmin_local('ktadd -k /usr/share/ovirt-wui/ovirt.keytab ' + this_libvirt_princ)

# We need to replace the KrbAuthRealms in the ovirt-wui http configuration
# file to be the correct Realm (i.e. default_realm)
ovirtconfname = '/etc/httpd/conf.d/ovirt-wui.conf'
ipaconfname = '/etc/httpd/conf.d/ipa.conf'

# make sure we skip this on subsequent runs of this script
if string.find(file(ipaconfname, 'rb').read(), '<VirtualHost *:8089>') < 0:
    ipaconf = open(ipaconfname, 'r')
    ipatext = ipaconf.readlines()
    ipaconf.close()

    ipaconf2 = open(ipaconfname, 'w')
    print >>ipaconf2, "Listen 8089"
    print >>ipaconf2, "NameVirtualHost *:8089"
    print >>ipaconf2, "<VirtualHost *:8089>"
    for line in ipatext:
        newline = re.sub(r'(.*RewriteCond %{HTTP_HOST}.*)', r'#\1', line)
        newline = re.sub(r'(.*RewriteRule \^/\(.*\).*)', r'#\1', newline)
        newline = re.sub(r'(.*RewriteCond %{SERVER_PORT}.*)', r'#\1', newline)
        newline = re.sub(r'(.*RewriteCond %{REQUEST_URI}.*)', r'#\1', newline)
        ipaconf2.write(newline)
    print >>ipaconf2, "</VirtualHost>"
    ipaconf2.close()

if string.find(file(ovirtconfname, 'rb').read(), '<VirtualHost *:80>') < 0:
    ovirtconf = open(ovirtconfname, 'r')
    ovirttext = ovirtconf.readlines()
    ovirtconf.close()

    ovirtconf2 = open(ovirtconfname, 'w')
    print >>ovirtconf2, "NameVirtualHost *:80"
    print >>ovirtconf2, "<VirtualHost *:80>"
    for line in ovirttext:
        newline = re.sub(r'(.*)KrbAuthRealms.*', r'\1KrbAuthRealms ' + default_realm, line)
        newline = re.sub(r'(.*)Krb5KeyTab.*', r'\1Krb5KeyTab /etc/httpd/conf/ipa.keytab', newline)
        ovirtconf2.write(newline)
    print >>ovirtconf2, "</VirtualHost>"
    ovirtconf2.close()
EOF
chmod +x /root/create_default_principals.py

cat > /root/add_host_principal.py << \EOF
#!/usr/bin/python

import krbV
import os
import socket
import shutil
import sys

def kadmin_local(command):
        ret = os.system("/usr/kerberos/sbin/kadmin.local -q '" + command + "'")
        if ret != 0:
                raise

def get_ip(hostname):
        return socket.gethostbyname(hostname)

if len(sys.argv) != 2:
        print "Usage: add_host_principal.py <hostname>"
        sys.exit(1)


default_realm = krbV.Context().default_realm

ipaddr = get_ip(sys.argv[1])

libvirt_princ = 'libvirt/' + sys.argv[1] + '@' + default_realm
outname = '/usr/share/ipa/html/' + ipaddr + '-libvirt.tab'

# here, generate the libvirt/ principle for this machine, necessary
# for taskomatic and host-browser
kadmin_local('addprinc -randkey +requires_preauth ' + libvirt_princ)
kadmin_local('ktadd -k ' + outname + ' ' + libvirt_princ)

# make sure it is readable by apache
os.chmod(outname, 0644)
EOF
chmod +x /root/add_host_principal.py

cat > /usr/share/ovirt-wui/psql.cmds << \EOF
CREATE USER ovirt WITH PASSWORD 'v23zj59an';
CREATE DATABASE ovirt;
GRANT ALL PRIVILEGES ON DATABASE ovirt to ovirt;
CREATE DATABASE ovirt_test;
GRANT ALL PRIVILEGES ON DATABASE ovirt_test to ovirt;
EOF
chmod a+r /usr/share/ovirt-wui/psql.cmds

cat > /etc/init.d/ovirt-app-first-run << \EOF
#!/bin/bash
#
# ovirt-app-first-run First run configuration for Ovirt WUI appliance
#
# chkconfig: 3 99 01
# description: ovirt appliance first run configuration
#

# Source functions library
. /etc/init.d/functions

start() {
	service postgresql initdb
	echo "local all all trust" > /var/lib/pgsql/data/pg_hba.conf
	echo "host all all 127.0.0.1 255.255.255.0 trust" >> /var/lib/pgsql/data/pg_hba.conf
	service postgresql start

	su - postgres -c "/usr/bin/psql -f /usr/share/ovirt-wui/psql.cmds"

	cd /usr/share/ovirt-wui ; rake db:migrate
	/usr/bin/ovirt_grant_admin_privileges.sh admin
}

case "$1" in
  start)
        start
        ;;
  *)
        echo "Usage: ovirt {start}"
        exit 2
esac

chkconfig ovirt-app-first-run off
EOF
chmod +x /etc/init.d/ovirt-app-first-run
chkconfig ovirt-app-first-run on

sed -i -e 's/\(.*\)disable\(.*\)= yes/\1disable\2= no/' /etc/xinetd.d/tftp

# set up the yum repos
cat > /etc/yum.repos.d/freeipa.repo << \EOF
[freeipa]
name=FreeIPA Development
baseurl=http://freeipa.com/downloads/devel/rpms/F7/x86_64/
enabled=1
gpgcheck=0
EOF

cat > /etc/yum.repos.d/ovirt.repo << \EOF
[ovirt]
name=Ovirt
baseurl=http://ovirt.et.redhat.com/repos/ovirt/x86_64
enabled=1
gpgcheck=0
EOF

# pretty login screen..

echo -e "" > /etc/issue
echo -e "           888     888 \\033[0;32md8b\\033[0;39m         888    " >> /etc/issue
echo -e "           888     888 \\033[0;32mY8P\\033[0;39m         888    " >> /etc/issue
echo -e "           888     888             888    " >> /etc/issue
echo -e "   .d88b.  Y88b   d88P 888 888d888 888888 " >> /etc/issue
echo -e "  d88''88b  Y88b d88P  888 888P'   888    " >> /etc/issue
echo -e "  888  888   Y88o88P   888 888     888    " >> /etc/issue
echo -e "  Y88..88P    Y888P    888 888     Y88b.  " >> /etc/issue
echo -e "   'Y88P'      Y8P     888 888      'Y888 " >> /etc/issue
echo -e "" >> /etc/issue
echo -e "  Admin node \\\\n " >> /etc/issue
echo -e "" >> /etc/issue
echo -e "  Virtualization just got the \\033[0;32mGreen Light\\033[0;39m" >> /etc/issue
echo -e "" >> /etc/issue

cp /etc/issue /etc/issue.net

echo "0.fedora.pool.ntp.org" >> /etc/ntp/step-tickers

# remove the mod_auth_kerb, and make sure we get the one from freeipa
rpm -e --nodeps mod_auth_kerb
yum -y --disablerepo=* --enablerepo=freeipa install mod_auth_kerb

# with the new libvirt (0.4.0), make sure we we setup gssapi in the mech_list
sed -i -e 's/mech_list: digest-md5/#mech_list: digest-md5/' /etc/sasl2/libvirt.conf
sed -i -e 's/#mech_list: gssapi/mech_list: gssapi/' /etc/sasl2/libvirt.conf

# for firefox, we need to add the following to ~/.mozilla/firefox/zzzzz/prefs.js
#echo 'user_pref("network.negotiate-auth.delegation-uris", ".redhat.com");' >> ~/.mozilla/firefox/zzzz/prefs.js
#echo 'user_pref("network.negotiate-auth.trusted-uris", ".redhat.com");' >> ~/.mozilla/firefox/zzzz/prefs.js

%end


More information about the livecd mailing list