# # Represents OS distribution specific install data # # Copyright 2006-2007, 2013 Red Hat, Inc. # Daniel P. Berrange # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # 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., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. import logging import os import gzip import re import tempfile import socket import ConfigParser import virtinst from virtinst import osdict from virtinst import util from virtinst.ImageFetcher import MountedImageFetcher from virtinst.ImageFetcher import FTPImageFetcher from virtinst.ImageFetcher import HTTPImageFetcher from virtinst.ImageFetcher import DirectImageFetcher def safeint(c): try: val = int(c) except: val = 0 return val def _fetcherForURI(uri, scratchdir=None): if uri.startswith("http://"): fclass = HTTPImageFetcher elif uri.startswith("ftp://"): fclass = FTPImageFetcher elif uri.startswith("nfs://"): fclass = MountedImageFetcher else: if os.path.isdir(uri): fclass = DirectImageFetcher else: fclass = MountedImageFetcher return fclass(uri, scratchdir) def _storeForDistro(fetcher, baseuri, typ, progresscb, arch, distro=None, scratchdir=None): stores = [] skip_treeinfo = False logging.debug("Attempting to detect distro:") dist = virtinst.OSDistro.distroFromTreeinfo(fetcher, progresscb, baseuri, arch, typ, scratchdir) if dist: return dist skip_treeinfo = True # FIXME: This 'distro ==' doesn't cut it. 'distro' is from our os # dictionary, so would look like 'fedora9' or 'rhel5', so this needs # to be a bit more intelligent if distro == "fedora" or distro is None: stores.append(FedoraDistro) if distro == "rhel" or distro is None: stores.append(RHELDistro) if distro == "centos" or distro is None: stores.append(CentOSDistro) if distro == "sl" or distro is None: stores.append(SLDistro) if distro == "suse" or distro is None: stores.append(SuseDistro) if distro == "debian" or distro is None: stores.append(DebianDistro) if distro == "ubuntu" or distro is None: stores.append(UbuntuDistro) if distro == "mandriva" or distro is None: stores.append(MandrivaDistro) if distro == "mageia" or distro is None: stores.append(MageiaDistro) if distro == "solaris" or distro is None: stores.append(SolarisDistro) if distro == "solaris" or distro is None: stores.append(OpenSolarisDistro) if distro == "netware" or distro is None: stores.append(NetWareDistro) stores.append(GenericDistro) for sclass in stores: store = sclass(baseuri, arch, typ, scratchdir) if skip_treeinfo: store.uses_treeinfo = False if store.isValidStore(fetcher, progresscb): return store raise ValueError( _("Could not find an installable distribution at '%s'\n" "The location must be the root directory of an install tree." % baseuri)) def _locationCheckWrapper(guest, baseuri, progresscb, scratchdir, _type, arch, callback): fetcher = _fetcherForURI(baseuri, scratchdir) if guest: arch = guest.arch try: fetcher.prepareLocation() except ValueError, e: logging.exception("Error preparing install location") raise ValueError(_("Invalid install location: ") + str(e)) try: store = _storeForDistro(fetcher=fetcher, baseuri=baseuri, typ=_type, progresscb=progresscb, scratchdir=scratchdir, arch=arch) return callback(store, fetcher) finally: fetcher.cleanupLocation() def _acquireMedia(iskernel, guest, baseuri, progresscb, scratchdir="/var/tmp", _type=None): def media_cb(store, fetcher): os_type, os_variant = store.get_osdict_info() media = None if iskernel: media = store.acquireKernel(guest, fetcher, progresscb) else: media = store.acquireBootDisk(guest, fetcher, progresscb) return [store, os_type, os_variant, media] return _locationCheckWrapper(guest, baseuri, progresscb, scratchdir, _type, None, media_cb) # Helper method to lookup install media distro and fetch an install kernel def getKernel(guest, baseuri, progresscb, scratchdir, typ): iskernel = True return _acquireMedia(iskernel, guest, baseuri, progresscb, scratchdir, typ) # Helper method to lookup install media distro and fetch a boot iso def getBootDisk(guest, baseuri, progresscb, scratchdir): iskernel = False return _acquireMedia(iskernel, guest, baseuri, progresscb, scratchdir) def _check_ostype_valid(os_type): return bool(os_type in osdict.sort_helper(osdict.OS_TYPES)) def _check_osvariant_valid(os_type, os_variant): return bool(_check_ostype_valid(os_type) and os_variant in osdict.sort_helper(osdict.OS_TYPES[os_type]["variants"])) # Attempt to detect the os type + variant for the passed location def detectMediaDistro(location, arch): import urlgrabber progresscb = urlgrabber.progress.BaseMeter() guest = None baseuri = location scratchdir = "/var/tmp" _type = None def media_cb(store, ignore): return store store = _locationCheckWrapper(guest, baseuri, progresscb, scratchdir, _type, arch, media_cb) return store.get_osdict_info() def distroFromTreeinfo(fetcher, progresscb, uri, arch, vmtype=None, scratchdir=None): # Parse treeinfo 'family' field, and return the associated Distro class # None if no treeinfo, GenericDistro if unknown family type. if not fetcher.hasFile(".treeinfo"): return None tmptreeinfo = fetcher.acquireFile(".treeinfo", progresscb) try: treeinfo = ConfigParser.SafeConfigParser() treeinfo.read(tmptreeinfo) finally: os.unlink(tmptreeinfo) try: fam = treeinfo.get("general", "family") except ConfigParser.NoSectionError: return None if re.match(".*Fedora.*", fam): dclass = FedoraDistro elif re.match(".*CentOS.*", fam): dclass = CentOSDistro elif re.match(".*Red Hat Enterprise Linux.*", fam): dclass = RHELDistro elif re.match(".*Scientific Linux.*", fam): dclass = SLDistro else: dclass = GenericDistro ob = dclass(uri, arch, vmtype, scratchdir) ob.treeinfo = treeinfo # Explictly call this, so we populate os_type/variant info ob.isValidStore(fetcher, progresscb) return ob # 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 Distro: name = "" # osdict type and variant values os_type = None os_variant = None _boot_iso_paths = [] _hvm_kernel_paths = [] _xen_kernel_paths = [] uses_treeinfo = False method_arg = "method" def __init__(self, uri, arch, vmtype=None, scratchdir=None): self.uri = uri self.type = vmtype self.scratchdir = scratchdir self.arch = arch self.treeinfo = None def isValidStore(self, fetcher, progresscb): """Determine if uri points to a tree of the store's distro""" raise NotImplementedError def acquireKernel(self, guest, fetcher, progresscb): kernelpath = None initrdpath = None if self._hasTreeinfo(fetcher, progresscb): try: kernelpath = self._getTreeinfoMedia("kernel") initrdpath = self._getTreeinfoMedia("initrd") except ConfigParser.NoSectionError: pass if not kernelpath or not initrdpath: # fall back to old code if self.type is None or self.type == "hvm": paths = self._hvm_kernel_paths else: paths = self._xen_kernel_paths for kpath, ipath in paths: if fetcher.hasFile(kpath) and fetcher.hasFile(ipath): kernelpath = kpath initrdpath = ipath if not kernelpath or not initrdpath: raise RuntimeError(_("Couldn't find %(type)s kernel for " "%(distro)s tree.") % {"distro": self.name, "type" : self.type}) return self._kernelFetchHelper(fetcher, guest, progresscb, kernelpath, initrdpath) def acquireBootDisk(self, guest, fetcher, progresscb): ignore = guest if self._hasTreeinfo(fetcher, progresscb): return fetcher.acquireFile(self._getTreeinfoMedia("boot.iso"), progresscb) else: for path in self._boot_iso_paths: if fetcher.hasFile(path): return fetcher.acquireFile(path, progresscb) raise RuntimeError(_("Could not find boot.iso in %s tree." % self.name)) def get_osdict_info(self): """ Return (distro, variant) tuple, checking to make sure they are valid osdict entries """ if not self.os_type: return (None, None) if not _check_ostype_valid(self.os_type): logging.debug("%s set os_type to %s, which is not in osdict.", self, self.os_type) return (None, None) if not self.os_variant: return (self.os_type, None) if not _check_osvariant_valid(self.os_type, self.os_variant): logging.debug("%s set os_variant to %s, which is not in osdict" " for distro %s.", self, self.os_variant, self.os_type) return (self.os_type, None) return (self.os_type, self.os_variant) def _hasTreeinfo(self, fetcher, progresscb): # all Red Hat based distros should have .treeinfo, perhaps others # will in time if not (self.treeinfo is None): return True if not self.uses_treeinfo or not fetcher.hasFile(".treeinfo"): return False logging.debug("Detected .treeinfo file") tmptreeinfo = fetcher.acquireFile(".treeinfo", progresscb) try: self.treeinfo = ConfigParser.SafeConfigParser() self.treeinfo.read(tmptreeinfo) finally: os.unlink(tmptreeinfo) return True def _getTreeinfoMedia(self, mediaName): if self.type == "xen": t = "xen" else: t = self.treeinfo.get("general", "arch") return self.treeinfo.get("images-%s" % t, mediaName) def _fetchAndMatchRegex(self, fetcher, progresscb, filename, regex): # Fetch 'filename' and return True/False if it matches the regex local_file = None try: try: local_file = fetcher.acquireFile(filename, progresscb) except: return False f = open(local_file, "r") try: while 1: buf = f.readline() if not buf: break if re.match(regex, buf): return True finally: f.close() finally: if local_file is not None: os.unlink(local_file) return False def _kernelFetchHelper(self, fetcher, guest, progresscb, kernelpath, initrdpath): # Simple helper for fetching kernel + initrd and performing # cleanup if neccessary kernel = fetcher.acquireFile(kernelpath, progresscb) args = '' if not fetcher.location.startswith("/"): args += "%s=%s" % (self.method_arg, fetcher.location) if guest.extraargs: args += " " + guest.extraargs try: initrd = fetcher.acquireFile(initrdpath, progresscb) return kernel, initrd, args except: os.unlink(kernel) class GenericDistro(Distro): """Generic distro store. Check well known paths for kernel locations as a last resort if we can't recognize any actual distro""" name = "Generic" os_type = "linux" uses_treeinfo = True _xen_paths = [("images/xen/vmlinuz", "images/xen/initrd.img"), # Fedora ] _hvm_paths = [("images/pxeboot/vmlinuz", "images/pxeboot/initrd.img"), # Fedora ] _iso_paths = ["images/boot.iso", # RH/Fedora "boot/boot.iso", # Suse "current/images/netboot/mini.iso", # Debian "install/images/boot.iso", # Mandriva ] # Holds values to use when actually pulling down media _valid_kernel_path = None _valid_iso_path = None def isValidStore(self, fetcher, progresscb): if self._hasTreeinfo(fetcher, progresscb): # Use treeinfo to pull down media paths if self.type == "xen": typ = "xen" else: typ = self.treeinfo.get("general", "arch") kernelSection = "images-%s" % typ isoSection = "images-%s" % self.treeinfo.get("general", "arch") if self.treeinfo.has_section(kernelSection): self._valid_kernel_path = (self._getTreeinfoMedia("kernel"), self._getTreeinfoMedia("initrd")) if self.treeinfo.has_section(isoSection): self._valid_iso_path = self.treeinfo.get(isoSection, "boot.iso") if self.type == "xen": kern_list = self._xen_paths else: kern_list = self._hvm_paths # If validated media paths weren't found (no treeinfo), check against # list of media location paths. for kern, init in kern_list: if self._valid_kernel_path is None \ and fetcher.hasFile(kern) and fetcher.hasFile(init): self._valid_kernel_path = (kern, init) break for iso in self._iso_paths: if self._valid_iso_path is None \ and fetcher.hasFile(iso): self._valid_iso_path = iso break if self._valid_kernel_path or self._valid_iso_path: return True return False def acquireKernel(self, guest, fetcher, progresscb): if self._valid_kernel_path is None: raise ValueError(_("Could not find a kernel path for virt type " "'%s'" % self.type)) return self._kernelFetchHelper(fetcher, guest, progresscb, self._valid_kernel_path[0], self._valid_kernel_path[1]) def acquireBootDisk(self, guest, fetcher, progresscb): if self._valid_iso_path is None: raise ValueError(_("Could not find a boot iso path for this tree.")) return fetcher.acquireFile(self._valid_iso_path, progresscb) # Base image store for any Red Hat related distros which have # a common layout class RedHatDistro(Distro): name = "Red Hat" os_type = "linux" uses_treeinfo = True _boot_iso_paths = ["images/boot.iso"] _hvm_kernel_paths = [("images/pxeboot/vmlinuz", "images/pxeboot/initrd.img")] _xen_kernel_paths = [("images/xen/vmlinuz", "images/xen/initrd.img")] def isValidStore(self, fetcher, progresscb): raise NotImplementedError # Fedora distro check class FedoraDistro(RedHatDistro): name = "Fedora" def isValidStore(self, fetcher, progresscb): if self._hasTreeinfo(fetcher, progresscb): m = re.match(".*Fedora.*", self.treeinfo.get("general", "family")) ret = (m is not None) if ret: lateststr, latestnum = self._latestFedoraVariant() ver = self.treeinfo.get("general", "version") if ver == "development": self.os_variant = self._latestFedoraVariant() elif ver: vernum = int(str(ver).split("-")[0]) if vernum > latestnum: self.os_variant = lateststr else: self.os_variant = "fedora" + str(vernum) return ret else: if fetcher.hasFile("Fedora"): logging.debug("Detected a Fedora distro") return True return False def _latestFedoraVariant(self): ret = None for var in osdict.sort_helper(osdict.OS_TYPES["linux"]["variants"]): if var.startswith("fedora"): # First fedora* occurence should be the newest ret = var break return ret, int(ret[6:]) # Red Hat Enterprise Linux distro check class RHELDistro(RedHatDistro): name = "Red Hat Enterprise Linux" def isValidStore(self, fetcher, progresscb): if self._hasTreeinfo(fetcher, progresscb): m = re.match(".*Red Hat Enterprise Linux.*", self.treeinfo.get("general", "family")) ret = (m is not None) if ret: self._variantFromVersion() return ret else: # fall back to old code if fetcher.hasFile("Server"): logging.debug("Detected a RHEL 5 Server distro") self.os_variant = "rhel5" return True if fetcher.hasFile("Client"): logging.debug("Detected a RHEL 5 Client distro") self.os_variant = "rhel5" return True if fetcher.hasFile("RedHat"): if fetcher.hasFile("dosutils"): self.os_variant = "rhel3" else: self.os_variant = "rhel4" logging.debug("Detected a %s distro", self.os_variant) return True return False def _parseTreeinfoVersion(self, verstr): version = safeint(verstr[0]) update = 0 updinfo = verstr.split(".") if len(updinfo) > 1: update = safeint(updinfo[1]) return version, update def _variantFromVersion(self): ver = self.treeinfo.get("general", "version") if not ver: return version, update = self._parseTreeinfoVersion(ver) self._setRHELVariant(version, update) def _setRHELVariant(self, version, update): if not _check_ostype_valid(self.os_type): return base = "rhel" + str(version) if update < 0: update = 0 ret = None while update >= 0: tryvar = base + ".%s" % update if not _check_osvariant_valid(self.os_type, tryvar): update -= 1 continue ret = tryvar break if not ret: # Try plain rhel5, rhel6, whatev if _check_osvariant_valid(self.os_type, base): ret = base if ret: self.os_variant = ret # CentOS distro check class CentOSDistro(RHELDistro): name = "CentOS" def isValidStore(self, fetcher, progresscb): if self._hasTreeinfo(fetcher, progresscb): m = re.match(".*CentOS.*", self.treeinfo.get("general", "family")) ret = (m is not None) if ret: self._variantFromVersion() return ret else: # fall back to old code if fetcher.hasFile("CentOS"): logging.debug("Detected a CentOS distro") return True return False # Scientific Linux distro check class SLDistro(RHELDistro): name = "Scientific Linux" _boot_iso_paths = RHELDistro._boot_iso_paths + ["images/SL/boot.iso"] _hvm_kernel_paths = RHELDistro._hvm_kernel_paths + \ [("images/SL/pxeboot/vmlinuz", "images/SL/pxeboot/initrd.img")] def isValidStore(self, fetcher, progresscb): if self._hasTreeinfo(fetcher, progresscb): m = re.match(".*Scientific Linux.*", self.treeinfo.get("general", "family")) ret = (m is not None) if ret: self._variantFromVersion() return ret else: if fetcher.hasFile("SL"): logging.debug("Detected a Scientific Linux distro") return True return False def _parseTreeinfoVersion(self, verstr): """ Overrides method in RHELDistro """ version = safeint(verstr[0]) update = 0 if len(verstr) > 1: update = safeint(verstr[1]) return version, update # 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(Distro): name = "SUSE" os_type = "linux" method_arg = "install" _boot_iso_paths = ["boot/boot.iso"] def __init__(self, uri, arch, vmtype=None, scratchdir=None): Distro.__init__(self, uri, arch, vmtype, scratchdir) if re.match(r'i[4-9]86', arch): self.arch = 'i386' oldkern = "linux" oldinit = "initrd" if arch == "x86_64": oldkern += "64" oldinit += "64" # Tested with Opensuse >= 10.2, 11, and sles 10 self._hvm_kernel_paths = [("boot/%s/loader/linux" % self.arch, "boot/%s/loader/initrd" % self.arch)] # Tested with Opensuse 10.0 self._hvm_kernel_paths.append(("boot/loader/%s" % oldkern, "boot/loader/%s" % oldinit)) # Matches Opensuse > 10.2 and sles 10 self._xen_kernel_paths = [("boot/%s/vmlinuz-xen" % self.arch, "boot/%s/initrd-xen" % self.arch)] 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 if fetcher.hasFile("directory.yast"): logging.debug("Detected a Suse distro.") return True return False def acquireKernel(self, guest, fetcher, progresscb): # If installing a fullvirt guest if self.type is None or self.type == "hvm" or \ fetcher.hasFile("boot/%s/vmlinuz-xen" % self.arch): return Distro.acquireKernel(self, guest, fetcher, progresscb) # For Opensuse <= 10.2, we need to perform some heinous stuff logging.debug("Trying Opensuse 10 PV rpm hacking") return self._findXenRPMS(fetcher, progresscb) def _findXenRPMS(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, initrdrpmname) = self._extractRPMNames(filelist) # Now fetch the two RPMs we want kernelrpm = fetcher.acquireFile(kernelrpmname, progresscb) installinitrdrpm = fetcher.acquireFile(initrdrpmname, 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: arches = [self.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) if self.arch == "i386": arches.append("i586") arches.append("i686") kernelname = "kernel-xenpae" else: kernelname = "kernel-xen" installinitrdrpm = None kernelrpm = None dirname = None while 1: data = filelistData.readline() if not data: break if dirname is None: for arch in arches: wantdir = "/suse/" + arch if data == "." + wantdir + ":\n": dirname = wantdir break else: if data == "\n": dirname = None else: if data[:5] != "total": filename = re.split("\s+", data)[8] if filename[:14] == "install-initrd": installinitrdrpm = dirname + "/" + filename elif filename[:len(kernelname)] == kernelname: kernelrpm = dirname + "/" + filename if kernelrpm is None: raise Exception(_("Unable to determine kernel RPM path")) if installinitrdrpm is None: raise Exception(_("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 merge 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, ignore, 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 modname in modpaths: 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) class DebianDistro(Distro): # ex. http://ftp.egr.msu.edu/debian/dists/sarge/main/installer-i386/ # daily builds: http://d-i.debian.org/daily-images/amd64/ name = "Debian" os_type = "linux" def __init__(self, uri, arch, vmtype=None, scratchdir=None): Distro.__init__(self, uri, arch, vmtype, scratchdir) if uri.count("i386"): self._treeArch = "i386" elif uri.count("amd64"): self._treeArch = "amd64" else: self._treeArch = "i386" if re.match(r'i[4-9]86', arch): self.arch = 'i386' self._installer_name = self.name.lower() + "-" + "installer" self._prefix = 'current/images' self._set_media_paths() def _set_media_paths(self): # Use self._prefix to set media paths self._boot_iso_paths = ["%s/netboot/mini.iso" % self._prefix] hvmroot = "%s/netboot/%s/%s/" % (self._prefix, self._installer_name, self._treeArch) xenroot = "%s/netboot/xen/" % self._prefix self._hvm_kernel_paths = [(hvmroot + "linux", hvmroot + "initrd.gz")] self._xen_kernel_paths = [(xenroot + "vmlinuz", xenroot + "initrd.gz")] def isValidStore(self, fetcher, progresscb): if fetcher.hasFile("%s/MANIFEST" % self._prefix): # For regular trees pass elif fetcher.hasFile("daily/MANIFEST"): # For daily trees self._prefix = "daily" self._set_media_paths() else: return False filename = "%s/MANIFEST" % self._prefix regex = ".*%s.*" % self._installer_name if self._fetchAndMatchRegex(fetcher, progresscb, filename, regex): logging.debug("Detected a %s distro", self.name) return True logging.debug("MANIFEST didn't match regex, not a %s distro", self.name) return False class UbuntuDistro(DebianDistro): name = "Ubuntu" # regular tree: # http://archive.ubuntu.com/ubuntu/dists/natty/main/installer-amd64/ def isValidStore(self, fetcher, progresscb): if fetcher.hasFile("%s/MANIFEST" % self._prefix): # For regular trees filename = "%s/MANIFEST" % self._prefix regex = ".*%s.*" % self._installer_name elif fetcher.hasFile("install/netboot/version.info"): # For trees based on ISO's self._prefix = "install" self._set_media_paths() filename = "%s/netboot/version.info" % self._prefix regex = "%s*" % self.name else: logging.debug("Doesn't look like an %s Distro.", self.name) return False if self._fetchAndMatchRegex(fetcher, progresscb, filename, regex): logging.debug("Detected an %s distro", self.name) return True logging.debug("Regex didn't match, not an %s distro", self.name) return False class MandrivaDistro(Distro): # Ex. ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2007.1/x86_64/ name = "Mandriva" os_type = "linux" _boot_iso_paths = ["install/images/boot.iso"] # Kernels for HVM: valid for releases 2007.1, 2008.*, 2009.0 _hvm_kernel_paths = [("isolinux/alt0/vmlinuz", "isolinux/alt0/all.rdz")] _xen_kernel_paths = [] def isValidStore(self, fetcher, progresscb): # Don't support any paravirt installs if self.type is not None and self.type != "hvm": return False # Mandriva websites / media appear to have a VERSION # file in top level which we can use as our 'magic' # check for validity if not fetcher.hasFile("VERSION"): return False if self._fetchAndMatchRegex(fetcher, progresscb, "VERSION", ".*%s.*" % self.name): logging.debug("Detected a %s distro", self.name) return True return False class MageiaDistro(MandrivaDistro): name = "Mageia" # Solaris and OpenSolaris distros class SunDistro(Distro): name = "Solaris" os_type = "solaris" def isValidStore(self, fetcher, progresscb): """Determine if uri points to a tree of the store's distro""" raise NotImplementedError def acquireBootDisk(self, guest, fetcher, progresscb): return fetcher.acquireFile("images/solarisdvd.iso", progresscb) def process_extra_args(self, argstr): """Collect additional arguments.""" if not argstr: return (None, None, None, None) kopts = '' kargs = '' smfargs = '' Bargs = '' args = argstr.split() i = 0 while i < len(args): exarg = args[i] if exarg == '-B': i += 1 if i == len(args): continue if not Bargs: Bargs = args[i] else: Bargs = ','.join([Bargs, args[i]]) elif exarg == '-m': i += 1 if i == len(args): continue smfargs = args[i] elif exarg.startswith('-'): if kopts is None: kopts = exarg[1:] else: kopts = kopts + exarg[1:] else: if kargs is None: kargs = exarg else: kargs = kargs + ' ' + exarg i += 1 return kopts, kargs, smfargs, Bargs class SolarisDistro(SunDistro): kernelpath = 'boot/platform/i86xpv/kernel/unix' initrdpath = 'boot/x86.miniroot' def isValidStore(self, fetcher, progresscb): if fetcher.hasFile(self.kernelpath): logging.debug('Detected Solaris') return True return False def install_args(self, guest): """Construct kernel cmdline args for the installer, consisting of: the pathname of the kernel (32/64) to load, kernel options and args, and '-B' boot properties.""" (kopts, kargs, ignore_smfargs, kbargs) = \ self.process_extra_args(guest.extraargs) args = [''] if kopts: args += ['-%s' % kopts] if kbargs: args += ['-B', kbargs] netmask = '' # Yuck. Non-default netmasks require this option to be passed. # It's distinctly not-trivial to work out the netmask to be used # automatically. if kargs: for karg in kargs.split(): if karg.startswith('subnet-mask'): netmask = karg.split('=')[1] else: args += [kargs] iargs = '' if not guest.graphics['enabled']: iargs += 'nowin ' if guest.location.startswith('nfs:'): try: guestIP = socket.gethostbyaddr(guest.name)[2][0] except: iargs += ' dhcp' else: iserver = guest.location.split(':')[1] ipath = guest.location.split(':')[2] iserverIP = socket.gethostbyaddr(iserver)[2][0] iargs += ' -B install_media=' + iserverIP + ':' + ipath iargs += ',host-ip=' + guestIP if netmask: iargs += ',subnet-mask=%s' % netmask droute = util.default_route(guest.nics[0].bridge) if droute: iargs += ',router-ip=' + droute if guest.nics[0].macaddr: en = guest.nics[0].macaddr.split(':') for i in range(len(en)): # remove leading '0' from mac address element if len(en[i]) > 1 and en[i][0] == '0': en[i] = en[i][1] boot_mac = ':'.join(en) iargs += ',boot-mac=' + boot_mac else: iargs += '-B install_media=cdrom' args += ['-', iargs] return ' '.join(args) def acquireKernel(self, guest, fetcher, progresscb): try: kernel = fetcher.acquireFile(self.kernelpath, progresscb) except: raise RuntimeError("Solaris PV kernel not found at %s" % self.kernelpath) # strip boot from the kernel path kpath = self.kernelpath.split('/')[1:] args = "/" + "/".join(kpath) + self.install_args(guest) try: initrd = fetcher.acquireFile(self.initrdpath, progresscb) return (kernel, initrd, args) except: os.unlink(kernel) raise RuntimeError(_("Solaris miniroot not found at %s") % self.initrdpath) class OpenSolarisDistro(SunDistro): os_variant = "opensolaris" kernelpath = "platform/i86xpv/kernel/unix" initrdpaths = ["platform/i86pc/boot_archive", "boot/x86.microroot"] def isValidStore(self, fetcher, progresscb): if fetcher.hasFile(self.kernelpath): logging.debug("Detected OpenSolaris") return True return False def install_args(self, guest): """Construct kernel cmdline args for the installer, consisting of: the pathname of the kernel (32/64) to load, kernel options and args, and '-B' boot properties.""" (kopts, ignore_kargs, ignore_smfargs, kbargs) = \ self.process_extra_args(guest.extraargs) args = '' if kopts: args += ' -' + kopts if kbargs: args += ' -B ' + kbargs return args def acquireKernel(self, guest, fetcher, progresscb): try: kernel = fetcher.acquireFile(self.kernelpath, progresscb) except: raise RuntimeError(_("OpenSolaris PV kernel not found at %s") % self.kernelpath) args = "/" + self.kernelpath + self.install_args(guest) try: initrd = fetcher.acquireFile(self.initrdpaths[0], progresscb) return (kernel, initrd, args) except Exception, e: try: initrd = fetcher.acquireFile(self.initrdpaths[1], progresscb) return (kernel, initrd, args) except: os.unlink(kernel) raise Exception("No OpenSolaris boot archive found: %s\n" % e) # NetWare 6 PV class NetWareDistro(Distro): name = "NetWare" os_type = "other" os_variant = "netware6" loaderpath = "STARTUP/XNLOADER.SYS" def isValidStore(self, fetcher, progresscb): if fetcher.hasFile(self.loaderpath): logging.debug("Detected NetWare") return True return False def acquireKernel(self, guest, fetcher, progresscb): loader = fetcher.acquireFile(self.loaderpath, progresscb) return (loader, "", "")