virt-manager/virtinst/OSDistro.py

1249 lines
42 KiB
Python
Raw Normal View History

#
# Represents OS distribution specific install data
#
# Copyright 2006-2007, 2013 Red Hat, Inc.
# Daniel P. Berrange <berrange@redhat.com>
#
# 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.os.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
2013-04-12 16:26:21 +04:00
def getKernel(guest, baseuri, progresscb, scratchdir, typ):
iskernel = True
return _acquireMedia(iskernel, guest, baseuri, progresscb,
2013-04-12 16:26:21 +04:00
scratchdir, typ)
# Helper method to lookup install media distro and fetch a boot iso
2013-04-12 16:26:21 +04:00
def getBootDisk(guest, baseuri, progresscb, scratchdir):
iskernel = False
return _acquireMedia(iskernel, guest, baseuri, progresscb,
2013-04-12 16:26:21 +04:00
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):
2013-04-12 16:26:21 +04:00
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
2013-06-30 22:33:01 +04:00
# cleanup if necessary
kernel = fetcher.acquireFile(kernelpath, progresscb)
args = ''
if not fetcher.location.startswith("/"):
args += "%s=%s" % (self.method_arg, fetcher.location)
if guest.installer.extraargs:
args += " " + guest.installer.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" or ver == "rawhide":
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 = {}
2013-04-12 16:26:21 +04:00
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()
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, "", "")