Linux Archive

Linux Archive (http://www.linux-archive.org/)
-   Fedora/Linux Management Tools (http://www.linux-archive.org/fedora-linux-management-tools/)
-   -   Virt-Manager: Supporting additional para-virtual OS's (http://www.linux-archive.org/fedora-linux-management-tools/3392-virt-manager-supporting-additional-para-virtual-oss.html)

Robert Nelson 11-23-2007 06:06 AM

Virt-Manager: Supporting additional para-virtual OS's
 
Daniel P. Berrange wrote:

On Thu, Nov 15, 2007 at 11:49:17AM -0800, Robert Nelson wrote:


Hugh O. Brock wrote:


On Wed, Nov 14, 2007 at 11:50:47PM -0800, Robert Nelson wrote:



I've been trying to add support for additional para-virtual operating
systems (OpenSolaris and Debian) to virt-manager. There is no problem
getting the initial kernel and ramdisk, however there is no way to
specify the target disk node based on the OS or distro. Also some OS's
don't support the virtual frame buffer driver but there is no way to
prevent the XML for it being created.

I can't see how this could be done without doing some major
restructuring of the code and creating a new class to encapsulate all
the target specific information. Is there any plan on doing something
like this? If someone were to do it, are the changes likely to be
incorporated?



Hello Robert, thanks for your interest.

I'm not sure I understand what you mean by "specify the target disk
node based on the OS or distro"?




Virt-Manager names the target disknodes xvda, xvdb, etc. OpenSolaris
requires 0, 1, 2, etc.



I would have no problem with adding a checkbox to virt-manager that
would have the effect of passing the --nographics flag to virtinst if
you can determine a good place to do it. I don't think we want to
spend a whole ton of time creating a new class to take different PV
guest OSes into account, though. Having said that, have you looked at
the *Installer class family in virtinst? Is there a way we could
extend that to do what you need?




I'll take another look at the Installer class and see if it can be done
there.



In the virtinst/FullVirtGuest.py class, there is already a bunch of
OS specific metadata, eg what of mouse to use, apic/acpi/pae settings,
whether the installer is multi-stage reboots (eg Windows). I'd recommend
moving this metadata into virtinst/DistroManager and have a bunch of
methods in that module for querying distro specific metadata, from the
Installer class.

Dan.


I spent some time reworking the virtinst code to support OpenSolaris
and make it much easier to support additional OSes.* I've attached the
patch file to get some feedback on the work so far.



Unfortunately a bunch for the code from virtinst is duplicated in
virt-manager in the Add Device code.* This means either moving it back
to virtinst with the appropriate additional APIs or duplicating work in
virt-manager.





--- virtinst-0.300.1-orig/virtinst/DistroManager.py 2007-11-14 20:22:40.000000000 -0800
+++ virtinst-0.300.1/virtinst/DistroManager.py 2007-11-22 22:07:42.000000000 -0800
@@ -15,596 +15,24 @@

import logging
import os
-import gzip
-import re
-import stat
import struct
-import subprocess
-import urlgrabber.grabber as grabber
-import urlgrabber.progress as progress
import tempfile
import Guest
-from virtinst import _virtinst as _
-
-# This is a generic base class for fetching/extracting files from
-# a media source, such as CD ISO, NFS server, or HTTP/FTP server
-class ImageFetcher:
-
- def __init__(self, location, scratchdir):
- self.location = location
- self.scratchdir = scratchdir
-
- def saveTemp(self, fileobj, prefix):
- (fd, fn) = tempfile.mkstemp(prefix="virtinst-" + prefix, dir=self.scratchdir)
- block_size = 16384
- try:
- while 1:
- buff = fileobj.read(block_size)
- if not buff:
- break
- os.write(fd, buff)
- finally:
- os.close(fd)
- return fn
-
- def prepareLocation(self, progresscb):
- return True
-
- def cleanupLocation(self):
- pass

- def acquireFile(self, src, progresscb):
- raise "Must be implemented in subclass"
-
- def hasFile(self, src, progresscb):
- raise "Must be implemented in subclass"
-
-# This is a fetcher capable of downloading from FTP / HTTP
-class URIImageFetcher(ImageFetcher):
-
- def prepareLocation(self, progresscb):
- try:
- grabber.urlopen(self.location,
- progress_obj = progresscb,
- text = _("Verifying install location..."))
- return True
- except IOError, e:
- logging.debug("Opening URL %s failed." % (self.location,) + " " + str(e))
- raise ValueError(_("Opening URL %s failed.") % (self.location,))
- return False
-
- def acquireFile(self, filename, progresscb):
- file = None
- try:
- base = os.path.basename(filename)
- logging.debug("Fetching URI " + self.location + "/" + filename)
- try:
- file = grabber.urlopen(self.location + "/" + filename,
- progress_obj = progresscb,
- text = _("Retrieving file %s...") % base)
- except IOError, e:
- raise ValueError, _("Invalid URL location given: ") + str(e)
- tmpname = self.saveTemp(file, prefix=base + ".")
- logging.debug("Saved file to " + tmpname)
- return tmpname
- finally:
- if file:
- file.close()
-
- def hasFile(self, filename, progresscb):
- try:
- tmpfile = self.acquireFile(filename, progresscb)
- os.unlink(tmpfile)
- return True
- except Exception, e:
- logging.debug("Cannot find file %s" % filename)
- return False
-
-# This is a fetcher capable of extracting files from a NFS server
-# or loopback mounted file, or local CDROM device
-class MountedImageFetcher(ImageFetcher):
-
- def prepareLocation(self, progresscb):
- cmd = None
- self.mntdir = tempfile.mkdtemp(prefix="virtinstmnt.", dir=self.scratchdir)
- logging.debug("Preparing mount at " + self.mntdir)
- if self.location.startswith("nfs:"):
- cmd = ["mount", "-o", "ro", self.location[4:], self.mntdir]
- else:
- if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]):
- cmd = ["mount", "-o", "ro", self.location, self.mntdir]
- else:
- cmd = ["mount", "-o", "ro,loop", self.location, self.mntdir]
- ret = subprocess.call(cmd)
- if ret != 0:
- self.cleanupLocation()
- logging.debug("Mounting location %s failed" % (self.location,))
- raise ValueError(_("Mounting location %s failed") % (self.location))
- return False
- return True
-
- def cleanupLocation(self):
- logging.debug("Cleaning up mount at " + self.mntdir)
- cmd = ["umount", self.mntdir]
- ret = subprocess.call(cmd)
- try:
- os.rmdir(self.mntdir)
- except:
- pass
-
- def acquireFile(self, filename, progresscb):
- file = None
- try:
- logging.debug("Acquiring file from " + self.mntdir + "/" + filename)
- base = os.path.basename(filename)
- try:
- src = self.mntdir + "/" + filename
- if stat.S_ISDIR(os.stat(src)[stat.ST_MODE]):
- logging.debug("Found a directory")
- return None
- else:
- file = open(src, "r")
- except IOError, e:
- raise ValueError, _("Invalid file location given: ") + str(e)
- except OSError, (errno, msg):
- raise ValueError, _("Invalid file location given: ") + msg
- tmpname = self.saveTemp(file, prefix=base + ".")
- logging.debug("Saved file to " + tmpname)
- return tmpname
- finally:
- if file:
- file.close()
-
- def hasFile(self, filename, progresscb):
- try:
- tmpfile = self.acquireFile(filename, progresscb)
- if tmpfile is not None:
- os.unlink(tmpfile)
- return True
- except Exception, e:
- logging.debug("Cannot find file %s" % filename)
- return False
-
-
-# An image store is a base class for retrieving either a bootable
-# ISO image, or a kernel+initrd pair for a particular OS distribution
-class ImageStore:
-
- def __init__(self, uri, type=None, scratchdir=None):
- self.uri = uri
- self.type = type
- self.scratchdir = scratchdir
-
- def acquireBootDisk(self, fetcher, progresscb):
- raise "Not implemented"
-
- def acquireKernel(self, fetcher, progresscb):
- raise "Not implemented"
-
- def isValidStore(self, fetcher, progresscb):
- raise "Not implemented"
-
-
-# Base image store for any Red Hat related distros which have
-# a common layout
-class RedHatImageStore(ImageStore):
-
- def acquireKernel(self, fetcher, progresscb):
- if self.type is None:
- kernelpath = "images/pxeboot/vmlinuz"
- initrdpath = "images/pxeboot/initrd.img"
- else:
- kernelpath = "images/%s/vmlinuz" % (self.type)
- initrdpath = "images/%s/initrd.img" % (self.type)
+from virtinst import _virtinst as _
+from OSDistro import OSDistro

- kernel = fetcher.acquireFile(kernelpath, progresscb)
- try:
- initrd = fetcher.acquireFile(initrdpath, progresscb)
- return (kernel, initrd, "method=" + fetcher.location)
- except:
- os.unlink(kernel)
-
- def acquireBootDisk(self, fetcher, progresscb):
- return fetcher.acquireFile("images/boot.iso", progresscb)
-
-# Fedora distro check
-class FedoraImageStore(RedHatImageStore):
- def isValidStore(self, fetcher, progresscb):
- if fetcher.hasFile("fedora.css", progresscb):
- logging.debug("Detected a Fedora distro")
- return True
- if fetcher.hasFile("Fedora", progresscb):
- logging.debug("Detected a Fedora distro")
- return True
- return False
-
-# Fedora distro check
-class RHELImageStore(RedHatImageStore):
- def isValidStore(self, fetcher, progresscb):
- if fetcher.hasFile("Server", progresscb):
- logging.debug("Detected a RHEL 5 Server distro")
- return True
- if fetcher.hasFile("Client", progresscb):
- logging.debug("Detected a RHEL 5 Client distro")
- return True
- if fetcher.hasFile("RedHat", progresscb):
- logging.debug("Detected a RHEL 4 distro")
- return True
- return False
-
-# CentOS distro check
-class CentOSImageStore(RedHatImageStore):
- def isValidStore(self, fetcher, progresscb):
- if fetcher.hasFile("CentOS", progresscb):
- logging.debug("Detected a CentOS distro")
- return True
- return False
-
-
-
-# Suse image store is harder - we fetch the kernel RPM and a helper
-# RPM and then munge bits together to generate a initrd
-class SuseImageStore(ImageStore):
- def acquireBootDisk(self, fetcher, progresscb):
- return fetcher.acquireFile("boot/boot.iso", progresscb)
-
- def acquireKernel(self, fetcher, progresscb):
- kernelrpm = None
- installinitrdrpm = None
- filelist = None
- try:
- # There is no predictable filename for kernel/install-initrd RPMs
- # so we have to grok the filelist and find them
- filelist = fetcher.acquireFile("ls-lR.gz", progresscb)
- (kernelrpmname, installinitrdrpmname) = self.extractRPMNames(filelist)
-
- # Now fetch the two RPMs we want
- kernelrpm = fetcher.acquireFile(kernelrpmname, progresscb)
- installinitrdrpm = fetcher.acquireFile(installinitrdrpmname, progresscb)
-
- # Process the RPMs to extract the kernel & generate an initrd
- return self.buildKernelInitrd(fetcher, kernelrpm, installinitrdrpm, progresscb)
- finally:
- if filelist is not None:
- os.unlink(filelist)
- if kernelrpm is not None:
- os.unlink(kernelrpm)
- if installinitrdrpm is not None:
- os.unlink(installinitrdrpm)
-
- # We need to parse the ls-lR.gz file, looking for the kernel &
- # install-initrd RPM entries - capturing the directory they are
- # in and the version'd filename.
- def extractRPMNames(self, filelist):
- filelistData = gzip.GzipFile(filelist, mode = "r")
- try:
- arch = os.uname()[4]
- arches = [arch]
- # On i686 arch, we also look under i585 and i386 dirs
- # in case the RPM is built for a lesser arch. We also
- # need the PAE variant (for Fedora dom0 at least)
- #
- # XXX shouldn't hard code that dom0 is PAE
- if arch == "i686":
- arches.append("i586")
- arches.append("i386")
- kernelname = "kernel-xenpae"
-
- installinitrdrpm = None
- kernelrpm = None
- dir = None
- while 1:
- data = filelistData.readline()
- if not data:
- break
- if dir is None:
- for arch in arches:
- wantdir = "/suse/" + arch
- if data == "." + wantdir + ":
":
- dir = wantdir
- break
- else:
- if data == "
":
- dir = None
- else:
- if data[:5] != "total":
- filename = re.split("s+", data)[8]
-
- if filename[:14] == "install-initrd":
- installinitrdrpm = dir + "/" + filename
- elif filename[:len(kernelname)] == kernelname:
- kernelrpm = dir + "/" + filename
-
- if kernelrpm is None:
- raise _("Unable to determine kernel RPM path")
- if installinitrdrpm is None:
- raise _("Unable to determine install-initrd RPM path")
- return (kernelrpm, installinitrdrpm)
- finally:
- filelistData.close()
-
- # We have a kernel RPM and a install-initrd RPM with a generic initrd in it
- # Now we have to munge the two together to build an initrd capable of
- # booting the installer.
- #
- # Yes, this is crazy ass stuff :-)
- def buildKernelInitrd(self, fetcher, kernelrpm, installinitrdrpm, progresscb):
- progresscb.start(text=_("Building initrd"), size=11)
- progresscb.update(1)
- cpiodir = tempfile.mkdtemp(prefix="virtinstcpio.", dir=self.scratchdir)
- try:
- # Extract the kernel RPM contents
- os.mkdir(cpiodir + "/kernel")
- cmd = "cd " + cpiodir + "/kernel && (rpm2cpio " + kernelrpm + " | cpio --quiet -idm)"
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(2)
-
- # Determine the raw kernel version
- kernelinfo = None
- for f in os.listdir(cpiodir + "/kernel/boot"):
- if f.startswith("System.map-"):
- kernelinfo = re.split("-", f)
- kernel_override = kernelinfo[1] + "-override-" + kernelinfo[3]
- kernel_version = kernelinfo[1] + "-" + kernelinfo[2] + "-" + kernelinfo[3]
- logging.debug("Got kernel version " + str(kernelinfo))
-
- # Build a list of all .ko files
- modpaths = {}
- for root, dirs, files in os.walk(cpiodir + "/kernel/lib/modules", topdown=False):
- for name in files:
- if name.endswith(".ko"):
- modpaths[name] = os.path.join(root, name)
- progresscb.update(3)
-
- # Extract the install-initrd RPM contents
- os.mkdir(cpiodir + "/installinitrd")
- cmd = "cd " + cpiodir + "/installinitrd && (rpm2cpio " + installinitrdrpm + " | cpio --quiet -idm)"
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(4)
-
- # Read in list of mods required for initrd
- modnames = []
- fn = open(cpiodir + "/installinitrd/usr/lib/install-initrd/" + kernelinfo[3] + "/module.list", "r")
- try:
- while 1:
- line = fn.readline()
- if not line:
- break
- line = line[:len(line)-1]
- modnames.append(line)
- finally:
- fn.close()
- progresscb.update(5)
-
- # Uncompress the basic initrd
- cmd = "gunzip -c " + cpiodir + "/installinitrd/usr/lib/install-initrd/initrd-base.gz > " + cpiodir + "/initrd.img"
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(6)
-
- # Create temp tree to hold stuff we're adding to initrd
- moddir = cpiodir + "/initrd/lib/modules/" + kernel_override + "/initrd/"
- moddepdir = cpiodir + "/initrd/lib/modules/" + kernel_version
- os.makedirs(moddir)
- os.makedirs(moddepdir)
- os.symlink("../" + kernel_override, moddepdir + "/updates")
- os.symlink("lib/modules/" + kernel_override + "/initrd", cpiodir + "/initrd/modules")
- cmd = "cp " + cpiodir + "/installinitrd/usr/lib/install-initrd/" + kernelinfo[3] + "/module.config" + " " + moddir
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(7)
-
- # Copy modules we need into initrd staging dir
- for modname in modnames:
- if modpaths.has_key(modname):
- src = modpaths[modname]
- dst = moddir + "/" + modname
- os.system("cp " + src + " " + dst)
- progresscb.update(8)
-
- # Run depmod across the staging area
- cmd = "depmod -a -b " + cpiodir + "/initrd -F " + cpiodir + "/kernel/boot/System.map-" + kernel_version + " " + kernel_version
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(9)
-
- # Add the extra modules to the basic initrd
- cmd = "cd " + cpiodir + "/initrd && ( find . | cpio --quiet -o -H newc -A -F " + cpiodir + "/initrd.img)"
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.update(10)
-
- # Compress the final initrd
- cmd = "gzip -f9N " + cpiodir + "/initrd.img"
- logging.debug("Running " + cmd)
- os.system(cmd)
- progresscb.end(11)
-
- # Save initrd & kernel to temp files for booting...
- initrdname = fetcher.saveTemp(open(cpiodir + "/initrd.img.gz", "r"), "initrd.img")
- logging.debug("Saved " + initrdname)
- try:
- kernelname = fetcher.saveTemp(open(cpiodir + "/kernel/boot/vmlinuz-" + kernel_version, "r"), "vmlinuz")
- logging.debug("Saved " + kernelname)
- return (kernelname, initrdname, "install=" + fetcher.location)
- except:
- os.unlink(initrdname)
- finally:
- #pass
- os.system("rm -rf " + cpiodir)
-
-
- def isValidStore(self, fetcher, progresscb):
- # Suse distros always have a 'directory.yast' file in the top
- # level of install tree, which we use as the magic check
- ignore = None
- try:
- try:
- ignore = fetcher.acquireFile("directory.yast", progresscb)
- logging.debug("Detected a Suse distro")
- return True
- except RuntimeError, e:
- logging.debug("Doesn't look like a Suse distro " + str(e))
- pass
- finally:
- if ignore is not None:
- os.unlink(ignore)
- return False
-
-
-class DebianImageStore(ImageStore):
- def isValidStore(self, fetcher, progresscb):
- # Don't support any paravirt installs
- if self.type is not None:
- return False
-
- file = None
- try:
- try:
- file = fetcher.acquireFile("current/images/MANIFEST", progresscb)
- except RuntimeError, e:
- logging.debug("Doesn't look like a Debian distro " + str(e))
- return False
- f = open(file, "r")
- try:
- while 1:
- buf = f.readline()
- if not buf:
- break
- if re.match(".*debian.*", buf):
- logging.debug("Detected a Debian distro")
- return True
- finally:
- f.close()
- finally:
- if file is not None:
- os.unlink(file)
- return False
-
- def acquireBootDisk(self, fetcher, progresscb):
- # eg from http://ftp.egr.msu.edu/debian/dists/sarge/main/installer-i386/
- return fetcher.acquireFile("current/images/netboot/mini.iso", progresscb)
-
-
-class UbuntuImageStore(ImageStore):
- def isValidStore(self, fetcher, progresscb):
- # Don't support any paravirt installs
- if self.type is not None:
- return False
- return False
-
-class GentooImageStore(ImageStore):
- def isValidStore(self, fetcher, progresscb):
- # Don't support any paravirt installs
- if self.type is not None:
- return False
- return False
-
-class MandrivaImageStore(ImageStore):
- def isValidStore(self, fetcher, progresscb):
- # Don't support any paravirt installs
- if self.type is not None:
- return False
-
- # Mandriva websites / media appear to have a VERSION
- # file in top level which we can use as our 'magic'
- # check for validity
- version = None
- try:
- try:
- version = fetcher.acquireFile("VERSION")
- except:
- return False
- f = open(version, "r")
- try:
- info = f.readline()
- if info.startswith("Mandriva"):
- logging.debug("Detected a Mandriva distro")
- return True
- finally:
- f.close()
- finally:
- if version is not None:
- os.unlink(version)
-
- return False
-
- def acquireBootDisk(self, fetcher, progresscb):
- #
- return fetcher.acquireFile("install/images/boot.iso", progresscb)
-
-def _fetcherForURI(uri, scratchdir=None):
- if uri.startswith("http://") or uri.startswith("ftp://"):
- return URIImageFetcher(uri, scratchdir)
- else:
- return MountedImageFetcher(uri, scratchdir)
-
-def _storeForDistro(fetcher, baseuri, type, progresscb, distro=None, scratchdir=None):
- stores = []
- if distro == "fedora" or distro is None:
- stores.append(FedoraImageStore(baseuri, type, scratchdir))
- if distro == "rhel" or distro is None:
- stores.append(RHELImageStore(baseuri, type, scratchdir))
- if distro == "centos" or distro is None:
- stores.append(CentOSImageStore(baseuri, type, scratchdir))
- if distro == "suse" or distro is None:
- stores.append(SuseImageStore(baseuri, type, scratchdir))
- if distro == "debian" or distro is None:
- stores.append(DebianImageStore(baseuri, type, scratchdir))
- if distro == "ubuntu" or distro is None:
- stores.append(UbuntuImageStore(baseuri, type, scratchdir))
- if distro == "gentoo" or distro is None:
- stores.append(GentooImageStore(baseuri, type, scratchdir))
- if distro == "mandriva" or distro is None:
- stores.append(MandrivaImageStore(baseuri, type, scratchdir))
-
- for store in stores:
- if store.isValidStore(fetcher, progresscb):
- return store
-
- raise ValueError, _("Could not find an installable distribution the install location")
-
-
-# Method to fetch a krenel & initrd pair for a particular distro / HV type
-def acquireKernel(baseuri, progresscb, scratchdir="/var/tmp", type=None, distro=None):
- fetcher = _fetcherForURI(baseuri, scratchdir)
-
- try:
- fetcher.prepareLocation(progresscb)
- except ValueError, e:
- raise ValueError, _("Invalid install location: ") + str(e)
-
- try:
- store = _storeForDistro(fetcher=fetcher, baseuri=baseuri, type=type,
- progresscb=progresscb, distro=distro, scratchdir=scratchdir)
- return store.acquireKernel(fetcher, progresscb)
- finally:
- fetcher.cleanupLocation()
-
-# Method to fetch a bootable ISO image for a particular distro / HV type
-def acquireBootDisk(baseuri, progresscb, scratchdir="/var/tmp", type=None, distro=None):
- fetcher = _fetcherForURI(baseuri, scratchdir)
-
- try:
- fetcher.prepareLocation(progresscb)
- except ValueError, e:
- raise ValueError, _("Invalid install location: ") + str(e)
-
- try:
- store = _storeForDistro(fetcher=fetcher, baseuri=baseuri, type=type,
- progresscb=progresscb, distro=distro, scratchdir=scratchdir)
- return store.acquireBootDisk(fetcher, progresscb)
- finally:
- fetcher.cleanupLocation()

class DistroInstaller(Guest.Installer):
+
def __init__(self, type = "xen", location = None, boot = None, extraargs = None):
Guest.Installer.__init__(self, type, location, boot, extraargs)
+ self._distro = None
+ self._tmpfiles = []

def get_location(self):
return self._location
+
def set_location(self, val):
if not (val.startswith("http://") or val.startswith("ftp://") or
val.startswith("nfs:") or val.startswith("/")):
@@ -612,18 +40,22 @@
if os.geteuid() != 0 and val.startswith("nfs:"):
raise ValueError(_("NFS installations are only supported as root"))
self._location = val
+
location = property(get_location, set_location)

- def _prepare_cdrom(self, guest, distro, meter):
+ def cleanup(self):
+ for f in self._tmpfiles:
+ logging.debug("Removing " + f)
+ self._distro.releaseFile(f)
+ self._tmpfiles = []
+
+ def _prepare_cdrom(self, guest, meter):
if self.location.startswith("/") and os.path.exists(self.location):
# Huzzah, a local file/device
cdrom = self.location
else:
# Xen needs a boot.iso if its a http://, ftp://, or nfs:/ url
- cdrom = acquireBootDisk(self.location,
- meter,
- scratchdir = self.scratchdir,
- distro = distro)
+ cdrom = self._distro.acquireBootDisk(meter)
self._tmpfiles.append(cdrom)

guest.disks.append(Guest.VirtualDisk(cdrom,
@@ -631,42 +63,35 @@
readOnly=True,
transient=True))

- def _prepare_kernel_and_initrd(self, guest, distro, meter):
+ def _prepare_kernel_and_initrd(self, guest, meter):
if self.boot is not None:
# Got a local kernel/initrd already
- self.install["kernel"] = self.boot["kernel"]
- self.install["initrd"] = self.boot["initrd"]
- if not self.extraargs is None:
- self.install["extraargs"] = self.extraargs
+ kernelfn = self.boot["kernel"]
+ initrdfn = self.boot["initrd"]
+ args = None
else:
- ostype = None
- if self.type == "xen":
- ostype = "xen"
# Need to fetch the kernel & initrd from a remote site, or
# out of a loopback mounted disk image/device
- (kernelfn, initrdfn, args) = acquireKernel(self.location,
- meter,
- scratchdir = self.scratchdir,
- type = ostype,
- distro = distro)
- self.install["kernel"] = kernelfn
- self.install["initrd"] = initrdfn
- if not self.extraargs is None:
- self.install["extraargs"] = self.extraargs + " " + args
- else:
- self.install["extraargs"] = args
-
+ (kernelfn, initrdfn, args) = self._distro.acquireKernel(meter)
self._tmpfiles.append(kernelfn)
self._tmpfiles.append(initrdfn)

+ self.install["kernel"] = kernelfn
+ self.install["initrd"] = initrdfn
+ if not self.extraargs is None:
+ self.install["extraargs"] = self.extraargs + " " + args
+ else:
+ self.install["extraargs"] = args
+
# If they're installing off a local file/device, we map it
- # through to a virtual harddisk
+ # through to a virtual cdrom
if self.location is not None and self.location.startswith("/"):
guest.disks.append(Guest.VirtualDisk(self.location ,
+ device=Guest.VirtualDisk.DEVICE_CDROM,
readOnly=True,
transient=True))

- def prepare(self, guest, meter, distro = None):
+ def prepare(self, guest, meter, distroName = None):
self.cleanup()

self.install = {
@@ -675,10 +100,18 @@
"extraargs" : "",
}

+ ostype = self.type
+ if ostype != "xen":
+ ostype = None
+
+ self._distro = OSDistro.create(self.location, ostype, distroName, guest.scratchdir, meter)
+
+ self._distro.prepare(guest)
+
if self.cdrom:
- self._prepare_cdrom(guest, distro, meter)
+ self._prepare_cdrom(guest, meter)
else:
- self._prepare_kernel_and_initrd(guest, distro, meter)
+ self._prepare_kernel_and_initrd(guest, meter)

def _get_osblob(self, install, hvm, arch = None, loader = None):
osblob = ""
@@ -714,6 +147,12 @@

return osblob

+ def getFullTarget(self, type, index):
+ return self._distro.getFullTarget(type, index)
+
+ def getParaTarget(self, type, index):
+ return self._distro.getParaTarget(type, index)
+
def post_install_check(self, guest):
# Check for the 0xaa55 signature at the end of the MBR
fd = os.open(guest.disks[0].path, os.O_RDONLY)
@@ -722,8 +161,8 @@
return len(buf) == 512 and struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,)


-
class PXEInstaller(Guest.Installer):
+
def __init__(self, type = "xen", location = None, boot = None, extraargs = None):
Guest.Installer.__init__(self, type, location, boot, extraargs)

@@ -765,4 +204,3 @@
buf = os.read(fd, 512)
os.close(fd)
return len(buf) == 512 and struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,)
-
--- virtinst-0.300.1-orig/virtinst/FullVirtGuest.py 2007-11-14 20:22:40.000000000 -0800
+++ virtinst-0.300.1/virtinst/FullVirtGuest.py 2007-11-22 22:07:42.000000000 -0800
@@ -104,7 +104,6 @@
if not installer:
installer = DistroManager.DistroInstaller(type = type)
Guest.Guest.__init__(self, type, connection, hypervisorURI, installer)
- self.disknode = "hd"
self.features = { "acpi": None, "pae": util.is_pae_capable(), "apic": None }
self.arch = arch
if emulator is None:
@@ -124,6 +123,7 @@

def get_os_type(self):
return self._os_type
+
def set_os_type(self, val):
if FullVirtGuest.OS_TYPES.has_key(val):
self._os_type = val
@@ -133,6 +133,7 @@

def get_os_variant(self):
return self._os_variant
+
def set_os_variant(self, val):
if FullVirtGuest.OS_TYPES[self._os_type]["variants"].has_key(val):
self._os_variant = val
@@ -241,42 +242,24 @@
def _get_disk_xml(self, install = True):
"""Get the disk config in the libvirt XML format"""
ret = ""
- nodes = {}
- for i in range(4):
- n = "%s%c" % (self.disknode, ord('a') + i)
- nodes[n] = None
+ diskIndex = 0

# First assign CDROM device nodes, since they're scarce resource
- cdnode = self.disknode + "c"
+ cdnode = self._installer.getFullTarget("cdrom", 0)
for d in self.disks:
if d.device != Guest.VirtualDisk.DEVICE_CDROM:
continue

- if d.target:
- if d.target != cdnode:
- raise ValueError, "The CDROM must be device %s" % cdnode
- else:
- d.target = cdnode
-
- if nodes[d.target] != None:
- raise ValueError, "The CDROM device %s is already used" % d.target
- nodes[d.target] = d
+ d.target = cdnode
+ break

# Now assign regular disk node with remainder
for d in self.disks:
if d.device == Guest.VirtualDisk.DEVICE_CDROM:
continue

- if d.target is None: # Auto-assign disk
- for n in sorted(nodes.keys()):
- if nodes[n] is None:
- d.target = n
- nodes[d.target] = d
- break
- else:
- if nodes[d.target] != None: # Verify pre-assigned
- raise ValueError, "The disk device %s is already used" % d.target
- nodes[d.target] = d
+ d.target = self._installer.getFullTarget("disk", diskIndex)
+ diskIndex = diskIndex + 1

for d in self.disks:
saved_path = None
--- virtinst-0.300.1-orig/virtinst/Guest.py 2007-09-25 08:01:12.000000000 -0700
+++ virtinst-0.300.1/virtinst/Guest.py 2007-11-22 22:07:42.000000000 -0800
@@ -171,7 +171,11 @@
# get working domain's name
ids = conn.listDomainsID();
for id in ids:
- vm = conn.lookupByID(id)
+ try:
+ vm = conn.lookupByID(id)
+ except libvirt.libvirtError:
+ # logging.error("LookupDomainByID(%d) failed - probably active domain not in xenstore" % id)
+ continue
vms.append(vm)
# get defined domain
names = conn.listDefinedDomains()
@@ -242,7 +246,11 @@
ids = conn.listDomainsID();
vms = []
for id in ids:
- vm = conn.lookupByID(id)
+ try:
+ vm = conn.lookupByID(id)
+ except libvirt.libvirtError:
+ # logging.error("LookupDomainByID(%d) failed - probably active domain not in xenstore" % id)
+ continue
vms.append(vm)
# get inactive Domains
inactive_vm = []
@@ -388,13 +396,8 @@
if not extraargs is None:
self.extraargs = extraargs

- self._tmpfiles = []
-
def cleanup(self):
- for f in self._tmpfiles:
- logging.debug("Removing " + f)
- os.unlink(f)
- self._tmpfiles = []
+ pass

def get_type(self):
return self._type
@@ -855,4 +858,4 @@

# Back compat class to avoid ABI break
class XenGuest(Guest):
- pass
+ pass
--- virtinst-0.300.1-orig/virtinst/ImageFetcher.py 1969-12-31 16:00:00.000000000 -0800
+++ virtinst-0.300.1/virtinst/ImageFetcher.py 2007-11-21 17:15:11.000000000 -0800
@@ -0,0 +1,220 @@
+#!/usr/bin/python -tt
+#
+# Convenience module for fetching/creating kernel/initrd files
+# or bootable CD images.
+#
+# Copyright 2006-2007 Red Hat, Inc.
+# Daniel P. Berrange <berrange@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import logging
+import os
+import stat
+import subprocess
+import urlgrabber.grabber as grabber
+import urlgrabber.progress as progress
+import tempfile
+from virtinst import _virtinst as _
+
+# This is a generic base class for fetching/extracting files from
+# a media source, such as CD ISO, NFS server, or HTTP/FTP server
+class ImageFetcher:
+
+ @staticmethod
+ def create(location, scratchdir):
+ if location.startswith("http://") or location.startswith("ftp://"):
+ return URIImageFetcher(location, scratchdir)
+ elif location.startswith("/"):
+ if os.path.isdir(location):
+ return LocalImageFetcher(location, scratchdir)
+ else:
+ return MountedImageFetcher(location, scratchdir)
+ return None
+
+ def __init__(self, location, scratchdir):
+ self.location = location
+ self.scratchdir = scratchdir
+
+ def saveTemp(self, fileobj, prefix):
+ (fd, fn) = tempfile.mkstemp(prefix="virtinst-" + prefix, dir=self.scratchdir)
+ block_size = 16384
+ try:
+ while 1:
+ buff = fileobj.read(block_size)
+ if not buff:
+ break
+ os.write(fd, buff)
+ finally:
+ os.close(fd)
+ return fn
+
+ def prepareLocation(self, progresscb):
+ return True
+
+ def cleanupLocation(self):
+ pass
+
+ def acquireFile(self, src, progresscb):
+ raise "Must be implemented in subclass"
+
+ def releaseFile(self, filename):
+ raise "Must be implemented in subclass"
+
+ def hasFile(self, src, progresscb):
+ raise "Must be implemented in subclass"
+
+
+# This is a fetcher capable of downloading from FTP / HTTP
+class URIImageFetcher(ImageFetcher):
+
+ def prepareLocation(self, progresscb):
+ try:
+ grabber.urlopen(self.location,
+ progress_obj = progresscb,
+ text = _("Verifying install location..."))
+ return True
+ except IOError, e:
+ logging.debug("Opening URL %s failed." % (self.location,) + " " + str(e))
+ raise ValueError(_("Opening URL %s failed.") % (self.location,))
+ return False
+
+ def acquireFile(self, filename, progresscb):
+ file = None
+ try:
+ base = os.path.basename(filename)
+ logging.debug("Fetching URI " + self.location + "/" + filename)
+ try:
+ file = grabber.urlopen(self.location + "/" + filename,
+ progress_obj = progresscb,
+ text = _("Retrieving file %s...") % base)
+ except IOError, e:
+ raise ValueError, _("Invalid URL location given: ") + str(e)
+ tmpname = self.saveTemp(file, prefix=base + ".")
+ logging.debug("Saved file to " + tmpname)
+ return tmpname
+ finally:
+ if file:
+ file.close()
+
+ def releaseFile(self, filename):
+ os.unlink(filename)
+
+ def hasFile(self, filename, progresscb):
+ try:
+ tmpfile = self.acquireFile(filename, progresscb)
+ if tmpfile is not None:
+ self.releaseFile(tmpfile)
+ return True
+ except Exception, e:
+ logging.debug("Cannot find file %s" % filename)
+ return False
+
+
+# This is a fetcher capable of extracting files from a NFS server
+# or loopback mounted file, or local CDROM device
+class MountedImageFetcher(ImageFetcher):
+
+ def prepareLocation(self, progresscb):
+ cmd = None
+ self.mntdir = tempfile.mkdtemp(prefix="virtinstmnt.", dir=self.scratchdir)
+ logging.debug("Preparing mount at " + self.mntdir)
+ if self.location.startswith("nfs:"):
+ cmd = ["mount", "-o", "ro", self.location[4:], self.mntdir]
+ else:
+ if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]):
+ cmd = ["mount", "-o", "ro", self.location, self.mntdir]
+ else:
+ cmd = ["mount", "-o", "ro,loop", self.location, self.mntdir]
+ ret = subprocess.call(cmd)
+ if ret != 0:
+ self.cleanupLocation()
+ logging.debug("Mounting location %s failed" % (self.location,))
+ raise ValueError(_("Mounting location %s failed") % (self.location))
+ return False
+ return True
+
+ def cleanupLocation(self):
+ logging.debug("Cleaning up mount at " + self.mntdir)
+ cmd = ["umount", self.mntdir]
+ ret = subprocess.call(cmd)
+ try:
+ os.rmdir(self.mntdir)
+ except:
+ pass
+
+ def acquireFile(self, filename, progresscb):
+ file = None
+ try:
+ logging.debug("Acquiring file from " + self.mntdir + "/" + filename)
+ base = os.path.basename(filename)
+ try:
+ src = self.mntdir + "/" + filename
+ if stat.S_ISDIR(os.stat(src)[stat.ST_MODE]):
+ logging.debug("Found a directory")
+ return None
+ else:
+ file = open(src, "r")
+ except IOError, e:
+ raise ValueError, _("Invalid file location given: ") + str(e)
+ except OSError, (errno, msg):
+ raise ValueError, _("Invalid file location given: ") + msg
+ tmpname = self.saveTemp(file, prefix=base + ".")
+ logging.debug("Saved file to " + tmpname)
+ return tmpname
+ finally:
+ if file:
+ file.close()
+
+ def releaseFile(self, filename):
+ os.unlink(filename)
+
+ def hasFile(self, filename, progresscb):
+ try:
+ tmpfile = self.acquireFile(filename, progresscb)
+ if tmpfile is not None:
+ self.releaseFile(tmpfile)
+ return True
+ except Exception, e:
+ logging.debug("Cannot find file %s" % filename)
+ return False
+
+
+class LocalImageFetcher(ImageFetcher):
+
+ def acquireFile(self, filename, progresscb):
+ fullName = None
+ file = None
+ try:
+ fullName = self.location + "/" + filename
+ logging.debug("Acquiring file from " + fullName)
+ try:
+ if stat.S_ISDIR(os.stat(fullName)[stat.ST_MODE]):
+ logging.debug("Found a directory")
+ return None
+ else:
+ file = open(fullName, "r")
+ except IOError, e:
+ raise ValueError, _("Invalid file location given: ") + str(e)
+ except OSError, (errno, msg):
+ raise ValueError, _("Invalid file location given: ") + msg
+ return fullName
+ finally:
+ if file:
+ file.close()
+
+ def releaseFile(self, filename):
+ pass
+
+ def hasFile(self, filename, progresscb):
+ try:
+ tmpfile = self.acquireFile(filename, progresscb)
+ return True
+ except ValueError, e:
+ logging.debug("Cannot find file %s" % filename)
+ return False
--- virtinst-0.300.1-orig/virtinst/__init__.py 2007-09-25 08:01:12.000000000 -0700
+++ virtinst-0.300.1/virtinst/__init__.py 2007-10-11 10:58:39.000000000 -0700
@@ -1,6 +1,6 @@
import gettext

-gettext_dir = "::LOCALEDIR::"
+gettext_dir = "/usr/share/locale"
gettext_app = "virtinst"

gettext.bindtextdomain(gettext_app, gettext_dir)
--- virtinst-0.300.1-orig/virtinst/OSDistro.py 1969-12-31 16:00:00.000000000 -0800
+++ virtinst-0.300.1/virtinst/OSDistro.py 2007-11-22 20:13:27.000000000 -0800
@@ -0,0 +1,509 @@
+#!/usr/bin/python -tt
+#
+# Abstraction of OS and Distribution specifics
+#
+# Copyright 2006-2007 Red Hat, Inc.
+# Daniel P. Berrange <berrange@redhat.com>
+#
+# This software may be freely redistributed under the terms of the GNU
+# general public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import logging
+import os
+import gzip
+import re
+import tempfile
+
+from virtinst import _virtinst as _
+from ImageFetcher import ImageFetcher
+
+# This is a generic base class for retrieving information particular to an
+# OS Distribution
+class OSDistro:
+
+ @staticmethod
+ def create(location, type, distroName, scratchdir, progresscb):
+ fetcher = ImageFetcher.create(location, scratchdir)
+
+ try:
+ fetcher.prepareLocation(progresscb)
+ except ValueError, e:
+ raise ValueError, _("Invalid install location: ") + str(e)
+
+ if distroName is None:
+ for c in distros.values():
+ d = c(fetcher, type, scratchdir)
+ if d.isValidStore(progresscb):
+ return d
+
+ raise ValueError, _("Could not find an installable distribution at the install location")
+ else:
+ d = distros[distroName](fetcher, type, scratchdir)
+ if d is None:
+ raise ValueError, _("Unknown distribution")
+ elif d.isValidStore(progresscb):
+ return d
+ else:
+ raise ValueError, _("Install location isn't the expected distribution")
+ return
+
+ def __init__(self, fetcher, type, scratchdir):
+ self.fetcher = fetcher
+ self.type = type
+ self.scratchdir = scratchdir
+
+ def releaseFile(self, filename):
+ self.fetcher.releaseFile(filename)
+
+ def prepare(self, guest):
+ pass
+
+ def acquireBootDisk(self, progresscb):
+ raise "Not implemented"
+
+ def acquireKernel(self, progresscb):
+ raise "Not implemented"
+
+ def isValidStore(self, progresscb):
+ raise "Not implemented"
+
+ def getFullTarget(self, type, index):
+ if type == cdrom:
+ if index == 0:
+ return "hdc"
+ return None
+ else:
+ if index == 0:
+ return "hda"
+ elif index == 1:
+ return "hdb"
+ elif index == 2:
+ return "hdd"
+ return None
+
+ def getParaTarget(self, type, index):
+ return "xvd%c" % (ord('a') + index)
+
+
+# Base image store for any Red Hat related distros which have
+# a common layout
+class RedHatDistro(OSDistro):
+
+ def acquireKernel(self, progresscb):
+ if self.type is None:
+ kernelpath = "images/pxeboot/vmlinuz"
+ initrdpath = "images/pxeboot/initrd.img"
+ else:
+ kernelpath = "images/%s/vmlinuz" % (self.type)
+ initrdpath = "images/%s/initrd.img" % (self.type)
+
+ kernel = self.fetcher.acquireFile(kernelpath, progresscb)
+ try:
+ initrd = self.fetcher.acquireFile(initrdpath, progresscb)
+ return (kernel, initrd, "method=" + self.fetcher.location)
+ except:
+ self.fetcher.releaseFile(kernel)
+
+ def acquireBootDisk(self, progresscb):
+ return self.fetcher.acquireFile("images/boot.iso", progresscb)
+
+
+# Fedora distro check
+class FedoraDistro(RedHatDistro):
+
+ def isValidStore(self, progresscb):
+ if self.fetcher.hasFile("fedora.css", progresscb):
+ logging.debug("Detected a Fedora distro")
+ return True
+ if self.fetcher.hasFile("Fedora", progresscb):
+ logging.debug("Detected a Fedora distro")
+ return True
+ return False
+
+
+# Fedora distro check
+class RHELDistro(RedHatDistro):
+
+ def isValidStore(self, progresscb):
+ if self.fetcher.hasFile("Server", progresscb):
+ logging.debug("Detected a RHEL 5 Server distro")
+ return True
+ if self.fetcher.hasFile("Client", progresscb):
+ logging.debug("Detected a RHEL 5 Client distro")
+ return True
+ if self.fetcher.hasFile("RedHat", progresscb):
+ logging.debug("Detected a RHEL 4 distro")
+ return True
+ return False
+
+
+# CentOS distro check
+class CentOSDistro(RedHatDistro):
+
+ def isValidStore(self, progresscb):
+ if self.fetcher.hasFile("CentOS", progresscb):
+ logging.debug("Detected a CentOS distro")
+ return True
+ return False
+
+
+# Suse image store is harder - we fetch the kernel RPM and a helper
+# RPM and then munge bits together to generate a initrd
+class SuseDistro(OSDistro):
+
+ def acquireBootDisk(self, progresscb):
+ return self.fetcher.acquireFile("boot/boot.iso", progresscb)
+
+ def acquireKernel(self, progresscb):
+ kernelrpm = None
+ installinitrdrpm = None
+ filelist = None
+ try:
+ # There is no predictable filename for kernel/install-initrd RPMs
+ # so we have to grok the filelist and find them
+ filelist = self.fetcher.acquireFile("ls-lR.gz", progresscb)
+ (kernelrpmname, installinitrdrpmname) = self.extractRPMNames(filelist)
+
+ # Now fetch the two RPMs we want
+ kernelrpm = self.fetcher.acquireFile(kernelrpmname, progresscb)
+ installinitrdrpm = self.fetcher.acquireFile(installinitrdrpmname, progresscb)
+
+ # Process the RPMs to extract the kernel & generate an initrd
+ return self.buildKernelInitrd(fetcher, kernelrpm, installinitrdrpm, progresscb)
+ finally:
+ if filelist is not None:
+ self.fetcher.releaseFile(filelist)
+ if kernelrpm is not None:
+ self.fetcher.releaseFile(kernelrpm)
+ if installinitrdrpm is not None:
+ self.fetcher.releaseFile(installinitrdrpm)
+
+ # We need to parse the ls-lR.gz file, looking for the kernel &
+ # install-initrd RPM entries - capturing the directory they are
+ # in and the version'd filename.
+ def extractRPMNames(self, filelist):
+ filelistData = gzip.GzipFile(filelist, mode = "r")
+ try:
+ arch = os.uname()[4]
+ arches = [arch]
+ # On i686 arch, we also look under i585 and i386 dirs
+ # in case the RPM is built for a lesser arch. We also
+ # need the PAE variant (for Fedora dom0 at least)
+ #
+ # XXX shouldn't hard code that dom0 is PAE
+ if arch == "i686":
+ arches.append("i586")
+ arches.append("i386")
+ kernelname = "kernel-xenpae"
+
+ installinitrdrpm = None
+ kernelrpm = None
+ dir = None
+ while 1:
+ data = filelistData.readline()
+ if not data:
+ break
+ if dir is None:
+ for arch in arches:
+ wantdir = "/suse/" + arch
+ if data == "." + wantdir + ":
":
+ dir = wantdir
+ break
+ else:
+ if data == "
":
+ dir = None
+ else:
+ if data[:5] != "total":
+ filename = re.split("s+", data)[8]
+
+ if filename[:14] == "install-initrd":
+ installinitrdrpm = dir + "/" + filename
+ elif filename[:len(kernelname)] == kernelname:
+ kernelrpm = dir + "/" + filename
+
+ if kernelrpm is None:
+ raise _("Unable to determine kernel RPM path")
+ if installinitrdrpm is None:
+ raise _("Unable to determine install-initrd RPM path")
+ return (kernelrpm, installinitrdrpm)
+ finally:
+ filelistData.close()
+
+ # We have a kernel RPM and a install-initrd RPM with a generic initrd in it
+ # Now we have to munge the two together to build an initrd capable of
+ # booting the installer.
+ #
+ # Yes, this is crazy ass stuff :-)
+ def buildKernelInitrd(self, kernelrpm, installinitrdrpm, progresscb):
+ progresscb.start(text=_("Building initrd"), size=11)
+ progresscb.update(1)
+ cpiodir = tempfile.mkdtemp(prefix="virtinstcpio.", dir=self.scratchdir)
+ try:
+ # Extract the kernel RPM contents
+ os.mkdir(cpiodir + "/kernel")
+ cmd = "cd " + cpiodir + "/kernel && (rpm2cpio " + kernelrpm + " | cpio --quiet -idm)"
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(2)
+
+ # Determine the raw kernel version
+ kernelinfo = None
+ for f in os.listdir(cpiodir + "/kernel/boot"):
+ if f.startswith("System.map-"):
+ kernelinfo = re.split("-", f)
+ kernel_override = kernelinfo[1] + "-override-" + kernelinfo[3]
+ kernel_version = kernelinfo[1] + "-" + kernelinfo[2] + "-" + kernelinfo[3]
+ logging.debug("Got kernel version " + str(kernelinfo))
+
+ # Build a list of all .ko files
+ modpaths = {}
+ for root, dirs, files in os.walk(cpiodir + "/kernel/lib/modules", topdown=False):
+ for name in files:
+ if name.endswith(".ko"):
+ modpaths[name] = os.path.join(root, name)
+ progresscb.update(3)
+
+ # Extract the install-initrd RPM contents
+ os.mkdir(cpiodir + "/installinitrd")
+ cmd = "cd " + cpiodir + "/installinitrd && (rpm2cpio " + installinitrdrpm + " | cpio --quiet -idm)"
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(4)
+
+ # Read in list of mods required for initrd
+ modnames = []
+ fn = open(cpiodir + "/installinitrd/usr/lib/install-initrd/" + kernelinfo[3] + "/module.list", "r")
+ try:
+ while 1:
+ line = fn.readline()
+ if not line:
+ break
+ line = line[:len(line)-1]
+ modnames.append(line)
+ finally:
+ fn.close()
+ progresscb.update(5)
+
+ # Uncompress the basic initrd
+ cmd = "gunzip -c " + cpiodir + "/installinitrd/usr/lib/install-initrd/initrd-base.gz > " + cpiodir + "/initrd.img"
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(6)
+
+ # Create temp tree to hold stuff we're adding to initrd
+ moddir = cpiodir + "/initrd/lib/modules/" + kernel_override + "/initrd/"
+ moddepdir = cpiodir + "/initrd/lib/modules/" + kernel_version
+ os.makedirs(moddir)
+ os.makedirs(moddepdir)
+ os.symlink("../" + kernel_override, moddepdir + "/updates")
+ os.symlink("lib/modules/" + kernel_override + "/initrd", cpiodir + "/initrd/modules")
+ cmd = "cp " + cpiodir + "/installinitrd/usr/lib/install-initrd/" + kernelinfo[3] + "/module.config" + " " + moddir
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(7)
+
+ # Copy modules we need into initrd staging dir
+ for modname in modnames:
+ if modpaths.has_key(modname):
+ src = modpaths[modname]
+ dst = moddir + "/" + modname
+ os.system("cp " + src + " " + dst)
+ progresscb.update(8)
+
+ # Run depmod across the staging area
+ cmd = "depmod -a -b " + cpiodir + "/initrd -F " + cpiodir + "/kernel/boot/System.map-" + kernel_version + " " + kernel_version
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(9)
+
+ # Add the extra modules to the basic initrd
+ cmd = "cd " + cpiodir + "/initrd && ( find . | cpio --quiet -o -H newc -A -F " + cpiodir + "/initrd.img)"
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.update(10)
+
+ # Compress the final initrd
+ cmd = "gzip -f9N " + cpiodir + "/initrd.img"
+ logging.debug("Running " + cmd)
+ os.system(cmd)
+ progresscb.end(11)
+
+ # Save initrd & kernel to temp files for booting...
+ initrdname = self.fetcher.saveTemp(open(cpiodir + "/initrd.img.gz", "r"), "initrd.img")
+ logging.debug("Saved " + initrdname)
+ try:
+ kernelname = self.fetcher.saveTemp(open(cpiodir + "/kernel/boot/vmlinuz-" + kernel_version, "r"), "vmlinuz")
+ logging.debug("Saved " + kernelname)
+ return (kernelname, initrdname, "install=" + self.fetcher.location)
+ except:
+ self.fetcher.releaseFile(initrdname)
+ finally:
+ #pass
+ os.system("rm -rf " + cpiodir)
+
+
+ def isValidStore(self, progresscb):
+ # Suse distros always have a 'directory.yast' file in the top
+ # level of install tree, which we use as the magic check
+ if self.fetcher.hasFile("directory.yast", progresscb):
+ logging.debug("Detected a Suse distro")
+ return True
+ return False
+
+
+class DebianDistro(OSDistro):
+
+ def isValidStore(self, progresscb):
+ # Don't support any paravirt installs
+ if self.type is not None:
+ return False
+
+ file = None
+ try:
+ try:
+ file = self.fetcher.acquireFile("current/images/MANIFEST", progresscb)
+ except ValueError, e:
+ logging.debug("Doesn't look like a Debian distro " + str(e))
+ return False
+ f = open(file, "r")
+ try:
+ while 1:
+ buf = f.readline()
+ if not buf:
+ break
+ if re.match(".*debian.*", buf):
+ logging.debug("Detected a Debian distro")
+ return True
+ finally:
+ f.close()
+ finally:
+ if file is not None:
+ self.fetcher.releaseFile(file)
+ return False
+
+ def acquireBootDisk(self, progresscb):
+ # eg from http://ftp.egr.msu.edu/debian/dists/sarge/main/installer-i386/
+ return self.fetcher.acquireFile("current/images/netboot/mini.iso", progresscb)
+
+
+class UbuntuDistro(OSDistro):
+
+ def isValidStore(self, progresscb):
+ # Don't support any paravirt installs
+ if self.type is not None:
+ return False
+ return False
+
+
+class GentooDistro(OSDistro):
+
+ def isValidStore(self, progresscb):
+ # Don't support any paravirt installs
+ if self.type is not None:
+ return False
+ return False
+
+class MandrivaDistro(OSDistro):
+
+ def isValidStore(self, progresscb):
+ # Don't support any paravirt installs
+ if self.type is not None:
+ return False
+
+ # Mandriva websites / media appear to have a VERSION
+ # file in top level which we can use as our 'magic'
+ # check for validity
+ version = None
+ try:
+ try:
+ version = self.fetcher.acquireFile("VERSION")
+ except:
+ return False
+ f = open(version, "r")
+ try:
+ info = f.readline()
+ if info.startswith("Mandriva"):
+ logging.debug("Detected a Mandriva distro")
+ return True
+ finally:
+ f.close()
+ finally:
+ if version is not None:
+ self.fetcher.releaseFile(version)
+
+ return False
+
+ def acquireBootDisk(self, fetcher, progresscb):
+ #
+ return self.fetcher.acquireFile("install/images/boot.iso", progresscb)
+
+
+class OpenSolarisDistro(OSDistro):
+
+ def prepare(self, guest):
+ guest.graphics = False
+
+ def acquireKernel(self, progresscb):
+ arch = os.uname()[4]
+ if arch == "i386":
+ kernelpath = "boot/platform/i86%s/kernel/unix"
+ initrdname = "boot/x86.miniroot"
+ else:
+ kernelpath = "boot/platform/i86%s/kernel/amd64/unix"
+ initrdname = "boot/amd64/x86.miniroot"
+
+ if self.type is None:
+ kernelname = kernelpath % "pc"
+ else:
+ kernelname = kernelpath % "xpv"
+
+ kernel = self.fetcher.acquireFile(kernelname, progresscb)
+ try:
+ initrd = self.fetcher.acquireFile(initrdname, progresscb)
+ if self.fetcher.location.startswith("nfs://"):
+ install_media = self.fetcher.location[6:]
+ else:
+ install_media = "cdrom"
+ return (kernel, initrd, kernelname + " - nowin -B install_media=" + install_media + ",console=ttya")
+ except:
+ os.unlink(kernel)
+
+ def acquireBootDisk(self, progresscb):
+ return self.fetcher.acquireFile("images/boot.iso", progresscb)
+
+ def isValidStore(self, progresscb):
+ if self.fetcher.hasFile("boot/platform/i86xpv/kernel/unix", progresscb):
+ logging.debug("Detected a Solaris distro")
+ return True
+ return False
+
+ def _getTargetDevice(self, type, index):
+ if index >= 0 and index < 6:
+ if type == "disk":
+ return str(index)
+ else:
+ return str(index + 6)
+ return None
+
+ def getFullTarget(self, type, index):
+ return self._getTargetDevice(type, index)
+
+ def getParaTarget(self, type, index):
+ return self._getTargetDevice(type, index)
+
+
+distros = {}
+distros["fedora"] = FedoraDistro
+distros["rhel"] = RHELDistro
+distros["centos"] = CentOSDistro
+distros["suse"] = SuseDistro
+distros["debian"] = DebianDistro
+distros["ubuntu"] = UbuntuDistro
+distros["gentoo"] = GentooDistro
+distros["mandriva"] = MandrivaDistro
+distros["opensolaris"] = OpenSolarisDistro
+
--- virtinst-0.300.1-orig/virtinst/ParaVirtGuest.py 2007-09-25 08:01:12.000000000 -0700
+++ virtinst-0.300.1/virtinst/ParaVirtGuest.py 2007-11-22 22:07:42.000000000 -0800
@@ -23,7 +23,6 @@
if not installer:
installer = DistroManager.DistroInstaller(type = type)
Guest.Guest.__init__(self, type, connection, hypervisorURI, installer)
- self.disknode = "xvd"

def _get_osblob(self, install):
return self.installer._get_osblob(install, hvm = False)
@@ -50,21 +49,19 @@
def _get_disk_xml(self, install = True):
"""Get the disk config in the libvirt XML format"""
ret = ""
- nodes = {}
- for i in range(16):
- n = "%s%c" % (self.disknode, ord('a') + i)
- nodes[n] = None
+ diskIndex = 0
+ cdromIndex = 0
for d in self.disks:
if d.transient and not install:
continue
- target = d.target
+ if d.device == Guest.VirtualDisk.DEVICE_CDROM:
+ target = self._installer.getParaTarget("cdrom", cdromIndex)
+ cdromIndex = cdromIndex + 1
+ else:
+ target = self._installer.getParaTarget("disk", diskIndex)
+ diskIndex = diskIndex + 1
+
if target is None:
- for t in sorted(nodes.keys()):
- if nodes[t] is None:
- target = t
- break
- if target is None or nodes[target] is not None:
raise ValueError, _("Can't use more than 16 disks on a PV guest")
- nodes[target] = True
ret += d.get_xml_config(target)
return ret
_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

Cole Robinson 11-27-2007 07:29 PM

Virt-Manager: Supporting additional para-virtual OS's
 
Robert Nelson wrote:
> I spent some time reworking the virtinst code to support OpenSolaris and
> make it much easier to support additional OSes. I've attached the patch
> file to get some feedback on the work so far.
>
> Unfortunately a bunch for the code from virtinst is duplicated in
> virt-manager in the Add Device code. This means either moving it back
> to virtinst with the appropriate additional APIs or duplicating work in
> virt-manager.
>
>
>

Hi Robert,

Could you take a couple paragraphs explaining exactly how you reorganized
everything? And also I think posting the patch to its own thread will get
it more attention.

One small thing before you repost: __init__.py had ::LOCALEDIR:: replaced
with /usr/share/locale. This shouldn't be hardcoded and is filled in as
appropriate at build time by setup.py.

Thanks,
Cole

--
Cole Robinson
crobinso@redhat.com

_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

"Daniel P. Berrange" 11-29-2007 07:42 PM

Virt-Manager: Supporting additional para-virtual OS's
 
On Thu, Nov 22, 2007 at 11:06:58PM -0800, Robert Nelson wrote:
> Daniel P. Berrange wrote:
> >
> >In the virtinst/FullVirtGuest.py class, there is already a bunch of
> >OS specific metadata, eg what of mouse to use, apic/acpi/pae settings,
> >whether the installer is multi-stage reboots (eg Windows). I'd recommend
> >moving this metadata into virtinst/DistroManager and have a bunch of
> >methods in that module for querying distro specific metadata, from the
> >Installer class.
> >
> >Dan.
> >
> I spent some time reworking the virtinst code to support OpenSolaris and
> make it much easier to support additional OSes. I've attached the patch
> file to get some feedback on the work so far.

Thanks for this. Took me a little while to get to grips with it, but I
like the resulting structure. I'm going todo a few tests and if it
works I'll commit it to the repo.

BTW, for any large patches in the future, its handy for review if you
do separate patches for re-factoring existing code, vs adding new
capabilities. It would have made it easier to review if the splitting
up of the DistroManager.py file were a simple no-functional-change
refactoring. Also helps people browsing the SCM history in the future
to distinguish the changes. No need to change this existing patch
though, I'll just add as is...

> Unfortunately a bunch for the code from virtinst is duplicated in
> virt-manager in the Add Device code. This means either moving it back
> to virtinst with the appropriate additional APIs or duplicating work in
> virt-manager.

We should make virt-manager call into the virtinst APIs really. virtinst
is intended as the library for virt-manager to get all OS install/setup
logic from.

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 -=|

_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

Robert Nelson 11-30-2007 03:14 AM

Virt-Manager: Supporting additional para-virtual OS's
 
Daniel P. Berrange wrote:

On Thu, Nov 22, 2007 at 11:06:58PM -0800, Robert Nelson wrote:


Daniel P. Berrange wrote:


In the virtinst/FullVirtGuest.py class, there is already a bunch of
OS specific metadata, eg what of mouse to use, apic/acpi/pae settings,
whether the installer is multi-stage reboots (eg Windows). I'd recommend
moving this metadata into virtinst/DistroManager and have a bunch of
methods in that module for querying distro specific metadata, from the
Installer class.

Dan.



I spent some time reworking the virtinst code to support OpenSolaris and
make it much easier to support additional OSes. I've attached the patch
file to get some feedback on the work so far.



Thanks for this. Took me a little while to get to grips with it, but I
like the resulting structure. I'm going todo a few tests and if it
works I'll commit it to the repo.

BTW, for any large patches in the future, its handy for review if you
do separate patches for re-factoring existing code, vs adding new
capabilities. It would have made it easier to review if the splitting
up of the DistroManager.py file were a simple no-functional-change
refactoring. Also helps people browsing the SCM history in the future
to distinguish the changes. No need to change this existing patch
though, I'll just add as is...





Please hold off on committing this patch.* I have an updated and better
version coming shortly.* I'll post it as two patches to make the SCM
history more readable.* I'll also post it to a new thread.






Unfortunately a bunch for the code from virtinst is duplicated in
virt-manager in the Add Device code. This means either moving it back
to virtinst with the appropriate additional APIs or duplicating work in
virt-manager.



We should make virt-manager call into the virtinst APIs really. virtinst
is intended as the library for virt-manager to get all OS install/setup
logic from.





If you don't mind, I'll put together a prototype of this for review.






Regards,
Dan.






_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

Robert Nelson 11-30-2007 03:17 AM

Virt-Manager: Supporting additional para-virtual OS's
 
Cole Robinson wrote:

Robert Nelson wrote:


I spent some time reworking the virtinst code to support OpenSolaris and
make it much easier to support additional OSes. I've attached the patch
file to get some feedback on the work so far.

Unfortunately a bunch for the code from virtinst is duplicated in
virt-manager in the Add Device code. This means either moving it back
to virtinst with the appropriate additional APIs or duplicating work in
virt-manager.






Hi Robert,

Could you take a couple paragraphs explaining exactly how you reorganized
everything? And also I think posting the patch to its own thread will get
it more attention.





I'll put more explanation in the next version of the patch.




One small thing before you repost: __init__.py had ::LOCALEDIR:: replaced
with /usr/share/locale. This shouldn't be hardcoded and is filled in as
appropriate at build time by setup.py.





This happens during the build.* Since I diffed against my built version
it had been updated.* Usually I delete that diff from the patch but I
guess I missed it in that version.




Thanks,
Cole







_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

"Daniel P. Berrange" 11-30-2007 01:57 PM

Virt-Manager: Supporting additional para-virtual OS's
 
On Thu, Nov 29, 2007 at 08:14:50PM -0800, Robert Nelson wrote:
> Daniel P. Berrange wrote:
> >>I spent some time reworking the virtinst code to support OpenSolaris and
> >>make it much easier to support additional OSes. I've attached the patch
> >>file to get some feedback on the work so far.
> >>
> >
> >Thanks for this. Took me a little while to get to grips with it, but I
> >like the resulting structure. I'm going todo a few tests and if it
> >works I'll commit it to the repo.
> >
> >BTW, for any large patches in the future, its handy for review if you
> >do separate patches for re-factoring existing code, vs adding new
> >capabilities. It would have made it easier to review if the splitting
> >up of the DistroManager.py file were a simple no-functional-change
> >refactoring. Also helps people browsing the SCM history in the future
> >to distinguish the changes. No need to change this existing patch
> >though, I'll just add as is...
>
> Please hold off on committing this patch. I have an updated and better
> version coming shortly. I'll post it as two patches to make the SCM
> history more readable. I'll also post it to a new thread.

So it turned out to be a little more complicated than I expected. At first
I liked the idea of having OSDistro create & use the ImageFetcher class
directly, however, there are circumstances where we need to use the OSDistro
class without the ImageFetcher, so this dependancy causes problems.

Second, the FullVirtGuest/ParaVirtGuest classes now call into the installer
class to get the disk target - this is a problem because this code is only
relevant for the DistroInstaller class - the ImageInstaller works in a
different way.

Really, theFullVirt/ParaVirt guest class should not try todo any assignement
of disk targets at all. The 'prepare' method fo the DistroInstaller class
should contain the code to assign disk targets directly. This simplifies
the get_disk_xml methods in the Full/ParaVirtGuest classes, and avoids
the hard dependancy.

So, in the end I just committed two small parts. I pulled the ImageFetcher
classes out into a separate python module, and pulled the OSDistro
classes out into a separate python module. So just 2 no-functional-change
refactorings for now.

If you post your latest patches I'll give them another try & see if there's
more bits we can safely commit as we go.

> >>Unfortunately a bunch for the code from virtinst is duplicated in
> >>virt-manager in the Add Device code. This means either moving it back
> >>to virtinst with the appropriate additional APIs or duplicating work in
> >>virt-manager.
> >>
> >
> >We should make virt-manager call into the virtinst APIs really. virtinst
> >is intended as the library for virt-manager to get all OS install/setup
> >logic from.
> >
> >
>
> If you don't mind, I'll put together a prototype of this for review.

Sure. The complicated thing about the 'Add device' code is that we no
longer have any record of the OS Distro type at that point - this info
is only available at install time. So its not clear how we'd be able
to get an instance of the OSDistro class neccessary to figure out the
disk target names here. Also this is one use case I mentioned where
you need OSDistro to be independant of the ImageFetcher class.


BTW, I'm not convinced the Solaris distro class is correct. I know the
paravirt guests have to name their disk targets 0, 1, 2 ... etc isntead
of xvda, xvdb, etc. For fully-virt Solaris installs though it must
still use hda, hdb, hdc, etc as per Linux. This is because the hda, hdb,
etc names translate directly to QEMU command line args - they have no
resemblance to what the guest OS calls its disks - indeed modern Linux
will call them sda, sdb, sdc, etc regardless of what QEMU calls tem.

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 -=|

_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools

"Daniel P. Berrange" 12-01-2007 04:10 PM

Virt-Manager: Supporting additional para-virtual OS's
 
On Fri, Nov 30, 2007 at 02:57:16PM +0000, Daniel P. Berrange wrote:
> On Thu, Nov 29, 2007 at 08:14:50PM -0800, Robert Nelson wrote:
> > Daniel P. Berrange wrote:
> > >>I spent some time reworking the virtinst code to support OpenSolaris and
> > >>make it much easier to support additional OSes. I've attached the patch
> > >>file to get some feedback on the work so far.
> > >>
> > >
> > >Thanks for this. Took me a little while to get to grips with it, but I
> > >like the resulting structure. I'm going todo a few tests and if it
> > >works I'll commit it to the repo.
> > >
> > >BTW, for any large patches in the future, its handy for review if you
> > >do separate patches for re-factoring existing code, vs adding new
> > >capabilities. It would have made it easier to review if the splitting
> > >up of the DistroManager.py file were a simple no-functional-change
> > >refactoring. Also helps people browsing the SCM history in the future
> > >to distinguish the changes. No need to change this existing patch
> > >though, I'll just add as is...
> >
> > Please hold off on committing this patch. I have an updated and better
> > version coming shortly. I'll post it as two patches to make the SCM
> > history more readable. I'll also post it to a new thread.
>
> So it turned out to be a little more complicated than I expected. At first
> I liked the idea of having OSDistro create & use the ImageFetcher class
> directly, however, there are circumstances where we need to use the OSDistro
> class without the ImageFetcher, so this dependancy causes problems.
>
> Second, the FullVirtGuest/ParaVirtGuest classes now call into the installer
> class to get the disk target - this is a problem because this code is only
> relevant for the DistroInstaller class - the ImageInstaller works in a
> different way.
>
> Really, theFullVirt/ParaVirt guest class should not try todo any assignement
> of disk targets at all. The 'prepare' method fo the DistroInstaller class
> should contain the code to assign disk targets directly. This simplifies
> the get_disk_xml methods in the Full/ParaVirtGuest classes, and avoids
> the hard dependancy.
>
> So, in the end I just committed two small parts. I pulled the ImageFetcher
> classes out into a separate python module, and pulled the OSDistro
> classes out into a separate python module. So just 2 no-functional-change
> refactorings for now.

I've also just commited the 'LocalImageFetcher' and the modifications to
the Guest.py class to catch exceptions when looking up guests for the
disk check since they're both worthwhile changes.


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 -=|

_______________________________________________
et-mgmt-tools mailing list
et-mgmt-tools@redhat.com
https://www.redhat.com/mailman/listinfo/et-mgmt-tools


All times are GMT. The time now is 02:57 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.