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.
2013-08-09 04:14:10 +04:00
import ConfigParser
import ftplib
2013-03-18 01:06:52 +04:00
import logging
import os
import re
import socket
2013-08-09 04:14:10 +04:00
import stat
import subprocess
import tempfile
import urllib2
import urlparse
import urlgrabber . grabber as grabber
2013-03-18 01:06:52 +04:00
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-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-08-09 04:14:10 +04:00
class _ImageFetcher ( object ) :
"""
This is a generic base class for fetching / extracting files from
a media source , such as CD ISO , NFS server , or HTTP / FTP server
"""
def __init__ ( self , location , scratchdir ) :
self . location = location
self . scratchdir = scratchdir
def _make_path ( self , filename ) :
if hasattr ( self , " srcdir " ) :
path = getattr ( self , " srcdir " )
else :
path = self . location
if filename :
if not path . endswith ( " / " ) :
path + = " / "
path + = filename
return path
def saveTemp ( self , fileobj , prefix ) :
if not os . path . exists ( self . scratchdir ) :
os . makedirs ( self . scratchdir , 0750 )
( fd , fn ) = tempfile . mkstemp ( prefix = " virtinst- " + prefix ,
dir = self . scratchdir )
block_size = 16384
try :
while 1 :
buff = fileobj . read ( block_size )
if not buff :
break
os . write ( fd , buff )
finally :
os . close ( fd )
return fn
def prepareLocation ( self ) :
return True
def cleanupLocation ( self ) :
pass
def acquireFile ( self , filename , progresscb ) :
# URLGrabber works for all network and local cases
f = None
try :
path = self . _make_path ( filename )
base = os . path . basename ( filename )
logging . debug ( " Fetching URI: %s " , path )
try :
f = grabber . urlopen ( path ,
progress_obj = progresscb ,
text = _ ( " Retrieving file %s ... " ) % base )
except Exception , e :
raise ValueError ( _ ( " Couldn ' t acquire file %s : %s " ) %
( path , str ( e ) ) )
tmpname = self . saveTemp ( f , prefix = base + " . " )
logging . debug ( " Saved file to " + tmpname )
return tmpname
finally :
if f :
f . close ( )
def hasFile ( self , src ) :
raise NotImplementedError ( " Must be implemented in subclass " )
class _URIImageFetcher ( _ImageFetcher ) :
"""
Base class for downloading from FTP / HTTP
"""
def hasFile ( self , filename ) :
raise NotImplementedError
def prepareLocation ( self ) :
if not self . hasFile ( " " ) :
raise ValueError ( _ ( " Opening URL %s failed. " ) %
( self . location ) )
class _HTTPImageFetcher ( _URIImageFetcher ) :
def hasFile ( self , filename ) :
try :
path = self . _make_path ( filename )
request = urllib2 . Request ( path )
request . get_method = lambda : " HEAD "
urllib2 . urlopen ( request )
except Exception , e :
logging . debug ( " HTTP hasFile: didn ' t find %s : %s " , path , str ( e ) )
return False
return True
class _FTPImageFetcher ( _URIImageFetcher ) :
def __init__ ( self , location , scratchdir ) :
_URIImageFetcher . __init__ ( self , location , scratchdir )
self . ftp = None
def prepareLocation ( self ) :
url = urlparse . urlparse ( self . _make_path ( " " ) )
self . ftp = ftplib . FTP ( url [ 1 ] )
self . ftp . login ( )
def hasFile ( self , filename ) :
path = self . _make_path ( filename )
url = urlparse . urlparse ( path )
try :
try :
# If it's a file
self . ftp . size ( url [ 2 ] )
except ftplib . all_errors :
# If it's a dir
self . ftp . cwd ( url [ 2 ] )
except ftplib . all_errors , e :
logging . debug ( " FTP hasFile: couldn ' t access %s : %s " ,
path , str ( e ) )
return False
return True
class _LocalImageFetcher ( _ImageFetcher ) :
def __init__ ( self , location , scratchdir , srcdir = None ) :
_ImageFetcher . __init__ ( self , location , scratchdir )
self . srcdir = srcdir
def hasFile ( self , filename ) :
src = self . _make_path ( filename )
if os . path . exists ( src ) :
return True
else :
logging . debug ( " local hasFile: Couldn ' t find %s " , src )
return False
class _MountedImageFetcher ( _LocalImageFetcher ) :
"""
Fetcher capable of extracting files from a NFS server
or loopback mounted file , or local CDROM device
"""
def prepareLocation ( self ) :
cmd = None
self . srcdir = tempfile . mkdtemp ( prefix = " virtinstmnt. " ,
dir = self . scratchdir )
mountcmd = " /bin/mount "
logging . debug ( " Preparing mount at " + self . srcdir )
if self . location . startswith ( " nfs: " ) :
cmd = [ mountcmd , " -o " , " ro " , self . location [ 4 : ] , self . srcdir ]
else :
if stat . S_ISBLK ( os . stat ( self . location ) [ stat . ST_MODE ] ) :
mountopt = " ro "
else :
mountopt = " ro,loop "
cmd = [ mountcmd , " -o " , mountopt , self . location , self . srcdir ]
2013-09-24 05:23:36 +04:00
logging . debug ( " mount cmd: %s " , cmd )
2013-08-09 04:14:10 +04:00
ret = subprocess . call ( cmd )
if ret != 0 :
self . cleanupLocation ( )
raise ValueError ( _ ( " Mounting location ' %s ' failed " ) %
( self . location ) )
return True
def cleanupLocation ( self ) :
logging . debug ( " Cleaning up mount at " + self . srcdir )
cmd = [ " /bin/umount " , self . srcdir ]
subprocess . call ( cmd )
try :
os . rmdir ( self . srcdir )
except :
pass
class _DirectImageFetcher ( _LocalImageFetcher ) :
def prepareLocation ( self ) :
self . srcdir = self . location
2013-03-18 01:06:52 +04:00
def _fetcherForURI ( uri , scratchdir = None ) :
2013-09-26 18:24:28 +04:00
if uri . startswith ( " http:// " ) or uri . startswith ( " https:// " ) :
2013-08-09 04:14:10 +04:00
fclass = _HTTPImageFetcher
2013-03-18 01:06:52 +04:00
elif uri . startswith ( " ftp:// " ) :
2013-08-09 04:14:10 +04:00
fclass = _FTPImageFetcher
2013-09-24 05:23:36 +04:00
elif uri . startswith ( " nfs: " ) :
2013-08-09 04:14:10 +04:00
fclass = _MountedImageFetcher
2013-03-18 01:06:52 +04:00
else :
if os . path . isdir ( uri ) :
2013-08-09 04:14:10 +04:00
fclass = _DirectImageFetcher
2013-03-18 01:06:52 +04:00
else :
2013-08-09 04:14:10 +04:00
fclass = _MountedImageFetcher
2013-03-18 01:06:52 +04:00
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: " )
2013-08-09 05:42:44 +04:00
dist = distroFromTreeinfo ( fetcher , progresscb , baseuri ,
arch , typ , scratchdir )
2013-03-18 01:06:52 +04:00
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 )
2013-08-08 13:28:43 +04:00
if distro == " altlinux " or distro is None :
stores . append ( ALTLinuxDistro )
2013-03-18 01:06:52 +04:00
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 ) :
2013-08-11 02:48:43 +04:00
os_variant = store . get_osdict_info ( )
2013-03-18 01:06:52 +04:00
media = None
if iskernel :
media = store . acquireKernel ( guest , fetcher , progresscb )
else :
media = store . acquireBootDisk ( guest , fetcher , progresscb )
2013-08-11 02:48:43 +04:00
return [ store , os_variant , media ]
2013-03-18 01:06:52 +04:00
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-08-11 02:48:43 +04:00
def _check_osvariant_valid ( os_variant ) :
return osdict . lookup_os ( os_variant ) is not None
2013-03-18 01:06:52 +04:00
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
2013-08-11 02:48:43 +04:00
# Explictly call this, so we populate variant info
2013-03-18 01:06:52 +04:00
ob . isValidStore ( fetcher , progresscb )
return ob
2013-08-09 17:33:47 +04:00
class Distro ( object ) :
"""
An image store is a base class for retrieving either a bootable
ISO image , or a kernel + initrd pair for a particular OS distribution
"""
2013-03-18 01:06:52 +04:00
name = " "
# osdict type and variant values
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_variant :
2013-08-11 02:48:43 +04:00
return None
2013-03-18 01:06:52 +04:00
2013-08-11 02:48:43 +04:00
if not _check_osvariant_valid ( self . os_variant ) :
logging . debug ( " %s set os_variant to %s , which is not in osdict. " ,
self , self . os_variant )
return None
2013-03-18 01:06:52 +04:00
2013-08-11 02:48:43 +04:00
return self . os_variant
2013-03-18 01:06:52 +04:00
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 ) :
2013-08-09 17:33:47 +04:00
"""
Generic distro store . Check well known paths for kernel locations
as a last resort if we can ' t recognize any actual distro
"""
2013-03-18 01:06:52 +04:00
name = " Generic "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-03-18 01:06:52 +04:00
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 )
class RedHatDistro ( Distro ) :
2013-08-09 17:33:47 +04:00
"""
Base image store for any Red Hat related distros which have
a common layout
"""
2013-03-18 01:06:52 +04:00
name = " Red Hat "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-03-18 01:06:52 +04:00
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-09-26 18:24:28 +04:00
self . os_variant = lateststr
2013-03-18 01:06:52 +04:00
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
2013-08-11 22:52:30 +04:00
for osinfo in osdict . list_os ( typename = " linux " ) :
if osinfo . name . startswith ( " fedora " ) :
2013-03-18 01:06:52 +04:00
# First fedora* occurence should be the newest
2013-08-11 22:52:30 +04:00
ret = osinfo . name
2013-03-18 01:06:52 +04:00
break
return ret , int ( ret [ 6 : ] )
2013-04-13 22:34:52 +04:00
2013-08-09 17:33:47 +04:00
# Red Hat Enterprise Linux distro check
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 ) :
base = " rhel " + str ( version )
if update < 0 :
update = 0
ret = None
while update > = 0 :
tryvar = base + " . %s " % update
2013-08-11 02:48:43 +04:00
if not _check_osvariant_valid ( tryvar ) :
2013-03-18 01:06:52 +04:00
update - = 1
continue
ret = tryvar
break
if not ret :
# Try plain rhel5, rhel6, whatev
2013-08-11 02:48:43 +04:00
if _check_osvariant_valid ( base ) :
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-08-09 17:33:47 +04:00
# Scientific Linux distro check
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 "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-03-18 01:06:52 +04:00
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
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 "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
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 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 ) :
# http://archive.ubuntu.com/ubuntu/dists/natty/main/installer-amd64/
2013-08-09 17:33:47 +04:00
name = " Ubuntu "
2013-03-18 01:06:52 +04:00
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 ) :
2013-08-09 17:33:47 +04:00
# ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2007.1/x86_64/
2013-03-18 01:06:52 +04:00
name = " Mandriva "
2013-08-11 02:48:43 +04:00
os_variant = " 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 "
2013-08-08 13:28:43 +04:00
2013-08-09 04:37:46 +04:00
class ALTLinuxDistro ( Distro ) :
2013-08-08 13:28:43 +04:00
name = " ALT Linux "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-08-09 04:37:46 +04:00
_boot_iso_paths = [ ( " altinst " , " live " ) ]
_hvm_kernel_paths = [ ( " syslinux/alt0/vmlinuz " , " syslinux/alt0/full.cz " ) ]
2013-08-08 13:28:43 +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
if not fetcher . hasFile ( " .disk/info " ) :
return False
if self . _fetchAndMatchRegex ( fetcher , progresscb , " .disk/info " ,
" .* %s .* " % self . name ) :
2013-08-09 04:37:46 +04:00
logging . debug ( " Detected a %s distro " , self . name )
2013-08-08 13:28:43 +04:00
return True
return False
2013-03-18 01:06:52 +04:00
# Solaris and OpenSolaris distros
class SunDistro ( Distro ) :
name = " Solaris "
2013-08-11 02:48:43 +04:00
os_variant = " solaris "
2013-03-18 01:06:52 +04:00
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_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 , " " , " " )