[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