2013-03-18 01:06:52 +04:00
#
# Represents OS distribution specific install data
#
2013-04-22 18:54:57 +04:00
# Copyright 2006-2007, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# 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
2013-04-11 03:48:07 +04:00
from virtinst import osdict
2013-04-11 18:27:02 +04:00
from virtinst import util
2013-03-18 01:06:52 +04:00
2013-04-11 03:48:07 +04:00
from virtinst . ImageFetcher import MountedImageFetcher
from virtinst . ImageFetcher import FTPImageFetcher
from virtinst . ImageFetcher import HTTPImageFetcher
from virtinst . ImageFetcher import DirectImageFetcher
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def safeint ( c ) :
try :
val = int ( c )
except :
val = 0
return val
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _locationCheckWrapper ( guest , baseuri , progresscb ,
scratchdir , _type , arch , callback ) :
fetcher = _fetcherForURI ( baseuri , scratchdir )
if guest :
2013-07-17 15:53:47 +04:00
arch = guest . os . arch
2013-03-18 01:06:52 +04:00
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 ( )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 )
2013-04-13 22:34:52 +04:00
2013-07-17 15:53:47 +04:00
# 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 ) :
2013-03-18 01:06:52 +04:00
iskernel = True
return _acquireMedia ( iskernel , guest , baseuri , progresscb ,
2013-04-12 16:26:21 +04:00
scratchdir , typ )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-07-17 15:53:47 +04:00
# 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 ) :
2013-03-18 01:06:52 +04:00
iskernel = False
return _acquireMedia ( iskernel , guest , baseuri , progresscb ,
2013-04-12 16:26:21 +04:00
scratchdir )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _check_ostype_valid ( os_type ) :
return bool ( os_type in osdict . sort_helper ( osdict . OS_TYPES ) )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 " ] ) )
2013-04-13 22:34:52 +04:00
2013-07-17 15:53:47 +04:00
# Attempt to detect the os type + variant for the passed location
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-22 18:54:57 +04:00
try :
kernelpath = self . _getTreeinfoMedia ( " kernel " )
initrdpath = self . _getTreeinfoMedia ( " initrd " )
except ConfigParser . NoSectionError :
pass
if not kernelpath or not initrdpath :
2013-03-18 01:06:52 +04:00
# 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 "
2013-04-13 22:34:52 +04:00
" %(distro)s tree. " ) %
{ " distro " : self . name , " type " : self . type } )
2013-03-18 01:06:52 +04:00
return self . _kernelFetchHelper ( fetcher , guest , progresscb , kernelpath ,
initrdpath )
def acquireBootDisk ( self , guest , fetcher , progresscb ) :
2013-04-12 16:26:21 +04:00
ignore = guest
2013-03-18 01:06:52 +04:00
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 )
2013-04-13 22:34:52 +04:00
raise RuntimeError ( _ ( " Could not find boot.iso in %s tree. " %
2013-03-18 01:06:52 +04:00
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
2013-03-18 01:06:52 +04:00
kernel = fetcher . acquireFile ( kernelpath , progresscb )
args = ' '
if not fetcher . location . startswith ( " / " ) :
args + = " %s = %s " % ( self . method_arg , fetcher . location )
2013-07-17 15:53:47 +04:00
if guest . installer . extraargs :
args + = " " + guest . installer . extraargs
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
_xen_paths = [ ( " images/xen/vmlinuz " ,
2013-03-18 01:06:52 +04:00
" images/xen/initrd.img " ) , # Fedora
2013-04-13 22:34:52 +04:00
]
_hvm_paths = [ ( " images/pxeboot/vmlinuz " ,
2013-03-18 01:06:52 +04:00
" images/pxeboot/initrd.img " ) , # Fedora
2013-04-13 22:34:52 +04:00
]
_iso_paths = [ " images/boot.iso " , # RH/Fedora
2013-03-18 01:06:52 +04:00
" boot/boot.iso " , # Suse
" current/images/netboot/mini.iso " , # Debian
" install/images/boot.iso " , # Mandriva
2013-04-13 22:34:52 +04:00
]
2013-03-18 01:06:52 +04:00
# 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 :
2013-04-13 22:34:52 +04:00
if self . _valid_kernel_path is None \
2013-03-18 01:06:52 +04:00
and fetcher . hasFile ( kern ) and fetcher . hasFile ( init ) :
self . _valid_kernel_path = ( kern , init )
break
for iso in self . _iso_paths :
2013-04-13 22:34:52 +04:00
if self . _valid_iso_path is None \
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
if self . _valid_kernel_path is None :
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
if self . _valid_iso_path is None :
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
_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 " ) ]
2013-03-18 01:06:52 +04:00
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 " ) )
2013-04-13 22:34:52 +04:00
ret = ( m is not None )
2013-03-18 01:06:52 +04:00
if ret :
lateststr , latestnum = self . _latestFedoraVariant ( )
ver = self . treeinfo . get ( " general " , " version " )
2013-07-29 20:28:22 +04:00
if ver == " development " or ver == " rawhide " :
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 " ) )
2013-04-13 22:34:52 +04:00
ret = ( m is not None )
2013-03-18 01:06:52 +04:00
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 " ) )
2013-04-13 22:34:52 +04:00
ret = ( m is not None )
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
class SLDistro ( RHELDistro ) :
name = " Scientific Linux "
2013-04-13 22:34:52 +04:00
_boot_iso_paths = RHELDistro . _boot_iso_paths + [ " images/SL/boot.iso " ]
2013-03-18 01:06:52 +04:00
_hvm_kernel_paths = RHELDistro . _hvm_kernel_paths + \
2013-04-13 22:34:52 +04:00
[ ( " images/SL/pxeboot/vmlinuz " ,
" images/SL/pxeboot/initrd.img " ) ]
2013-03-18 01:06:52 +04:00
def isValidStore ( self , fetcher , progresscb ) :
if self . _hasTreeinfo ( fetcher , progresscb ) :
m = re . match ( " .*Scientific Linux.* " ,
self . treeinfo . get ( " general " , " family " ) )
2013-04-13 22:34:52 +04:00
ret = ( m is not None )
2013-03-18 01:06:52 +04:00
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 "
2013-04-13 22:34:52 +04:00
_boot_iso_paths = [ " boot/boot.iso " ]
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
self . _hvm_kernel_paths = [ ( " boot/ %s /loader/linux " % self . arch ,
" boot/ %s /loader/initrd " % self . arch ) ]
2013-03-18 01:06:52 +04:00
# 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
2013-04-13 22:34:52 +04:00
self . _xen_kernel_paths = [ ( " boot/ %s /vmlinuz-xen " % self . arch ,
" boot/ %s /initrd-xen " % self . arch ) ]
2013-03-18 01:06:52 +04:00
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 ) :
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
cmd = " cd " + cpiodir + " /initrd && (find . | cpio --quiet -o -H newc -A -F " + cpiodir + " /initrd.img) "
2013-03-18 01:06:52 +04:00
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 :
2013-04-13 22:34:52 +04:00
# pass
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
self . _boot_iso_paths = [ " %s /netboot/mini.iso " % self . _prefix ]
2013-03-18 01:06:52 +04:00
hvmroot = " %s /netboot/ %s / %s / " % ( self . _prefix ,
self . _installer_name ,
self . _treeArch )
xenroot = " %s /netboot/xen/ " % self . _prefix
2013-04-13 22:34:52 +04:00
self . _hvm_kernel_paths = [ ( hvmroot + " linux " , hvmroot + " initrd.gz " ) ]
self . _xen_kernel_paths = [ ( xenroot + " vmlinuz " ,
xenroot + " initrd.gz " ) ]
2013-03-18 01:06:52 +04:00
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 "
2013-04-13 22:34:52 +04:00
_boot_iso_paths = [ " install/images/boot.iso " ]
2013-03-18 01:06:52 +04:00
# Kernels for HVM: valid for releases 2007.1, 2008.*, 2009.0
2013-04-13 22:34:52 +04:00
_hvm_kernel_paths = [ ( " isolinux/alt0/vmlinuz " , " isolinux/alt0/all.rdz " ) ]
2013-03-18 01:06:52 +04:00
_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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
class MageiaDistro ( MandrivaDistro ) :
name = " Mageia "
# Solaris and OpenSolaris distros
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 )
2013-04-13 22:34:52 +04:00
args = [ ' ' ]
2013-03-18 01:06:52 +04:00
if kopts :
2013-04-13 22:34:52 +04:00
args + = [ ' - %s ' % kopts ]
2013-03-18 01:06:52 +04:00
if kbargs :
2013-04-13 22:34:52 +04:00
args + = [ ' -B ' , kbargs ]
2013-03-18 01:06:52 +04:00
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 :
2013-04-13 22:34:52 +04:00
args + = [ kargs ]
2013-03-18 01:06:52 +04:00
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
2013-07-03 20:15:28 +04:00
droute = util . default_route ( )
2013-03-18 01:06:52 +04:00
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 '
2013-04-13 22:34:52 +04:00
args + = [ ' - ' , iargs ]
2013-03-18 01:06:52 +04:00
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 )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
class OpenSolarisDistro ( SunDistro ) :
os_variant = " opensolaris "
kernelpath = " platform/i86xpv/kernel/unix "
2013-04-13 22:34:52 +04:00
initrdpaths = [ " platform/i86pc/boot_archive " , " boot/x86.microroot " ]
2013-03-18 01:06:52 +04:00
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 , " " , " " )