2013-03-18 01:06:52 +04:00
#
# Represents OS distribution specific install data
#
2013-10-28 00:59:47 +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
2013-10-28 00:59:47 +04:00
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
2013-03-18 01:06:52 +04:00
#
# 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
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
2014-09-12 23:59:22 +04:00
from . import osdict
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-09-26 21:04:28 +04:00
#########################################################################
# Backends for the various URL types we support (http, ftp, nfs, local) #
#########################################################################
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
"""
2013-09-26 21:04:28 +04:00
def __init__ ( self , location , scratchdir , meter ) :
2013-08-09 04:14:10 +04:00
self . location = location
self . scratchdir = scratchdir
2013-09-26 21:04:28 +04:00
self . meter = meter
self . srcdir = None
2013-08-09 04:14:10 +04:00
2014-09-07 22:40:09 +04:00
logging . debug ( " Using scratchdir= %s " , scratchdir )
2013-08-09 04:14:10 +04:00
def _make_path ( self , filename ) :
2013-09-26 21:04:28 +04:00
path = self . srcdir or self . location
2013-08-09 04:14:10 +04:00
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 )
2014-02-14 20:02:35 +04:00
prefix = " virtinst- " + prefix
if " VIRTINST_TEST_SUITE " in os . environ :
2014-09-23 22:25:14 +04:00
fn = os . path . join ( " /tmp " , prefix )
2014-06-29 10:46:36 +04:00
fd = os . open ( fn , os . O_RDWR | os . O_CREAT , 0640 )
2014-02-14 20:02:35 +04:00
else :
( fd , fn ) = tempfile . mkstemp ( prefix = prefix ,
dir = self . scratchdir )
2013-08-09 04:14:10 +04:00
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 ) :
2014-09-07 22:22:56 +04:00
pass
2013-08-09 04:14:10 +04:00
def cleanupLocation ( self ) :
pass
2013-09-26 21:04:28 +04:00
def acquireFile ( self , filename ) :
2013-08-09 04:14:10 +04:00
# 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 ,
2013-09-26 21:04:28 +04:00
progress_obj = self . meter ,
2013-08-09 04:14:10 +04:00
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
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 ) :
2014-09-07 22:22:56 +04:00
ftp = None
2013-08-09 04:14:10 +04:00
def prepareLocation ( self ) :
2014-09-07 22:22:56 +04:00
if self . ftp :
return
virtinst: early detect ftp connection errors
It fixes two problems:
i) "ftp://" was accepted as valid URL but then it causes this
exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.7/ftplib.py", line 387, in login
resp = self.sendcmd('USER ' + user)
File "/usr/lib64/python2.7/ftplib.py", line 243, in sendcmd
self.putcmd(cmd)
File "/usr/lib64/python2.7/ftplib.py", line 178, in putcmd
self.putline(line)
File "/usr/lib64/python2.7/ftplib.py", line 173, in putline
self.sock.sendall(line)
AttributeError: 'NoneType' object has no attribute 'sendall'
ii) only a cryptic error message "Unable to complete install: '[Errno
-2] Name or service not known'" was showed to users when the DNS
lookup failed. The exception is now intercepted and decorated with
more information.
Closes: https://bugzilla.redhat.com/show_bug.cgi?id=1086554
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2014-04-14 16:49:21 +04:00
try :
url = urlparse . urlparse ( self . _make_path ( " " ) )
if not url [ 1 ] :
raise ValueError ( _ ( " Invalid install location " ) )
self . ftp = ftplib . FTP ( url [ 1 ] )
self . ftp . login ( )
except Exception , e :
raise ValueError ( _ ( " Opening URL %s failed: %s . " ) %
( self . location , str ( e ) ) )
2014-09-07 22:22:56 +04:00
def cleanupLocation ( self ) :
if not self . ftp :
return
try :
self . ftp . quit ( )
except :
logging . debug ( " Error quitting ftp connection " , exc_info = True )
2013-08-09 04:14:10 +04:00
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 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
"""
2014-03-06 21:35:01 +04:00
_in_test_suite = bool ( " VIRTINST_TEST_SUITE " in os . environ )
2014-09-07 22:22:56 +04:00
_mounted = False
2014-03-06 21:35:01 +04:00
2013-08-09 04:14:10 +04:00
def prepareLocation ( self ) :
2014-09-07 22:22:56 +04:00
if self . _mounted :
return
2014-03-06 21:35:01 +04:00
if self . _in_test_suite :
self . srcdir = os . environ [ " VIRTINST_TEST_URL_DIR " ]
else :
self . srcdir = tempfile . mkdtemp ( prefix = " virtinstmnt. " ,
dir = self . scratchdir )
2013-08-09 04:14:10 +04:00
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 )
2014-03-06 21:35:01 +04:00
if not self . _in_test_suite :
ret = subprocess . call ( cmd )
if ret != 0 :
self . cleanupLocation ( )
raise ValueError ( _ ( " Mounting location ' %s ' failed " ) %
( self . location ) )
2014-09-07 22:22:56 +04:00
self . _mounted = True
2013-08-09 04:14:10 +04:00
def cleanupLocation ( self ) :
2014-09-07 22:22:56 +04:00
if not self . _mounted :
return
2014-03-06 21:35:01 +04:00
2014-09-07 22:22:56 +04:00
logging . debug ( " Cleaning up mount at " + self . srcdir )
try :
if not self . _in_test_suite :
cmd = [ " /bin/umount " , self . srcdir ]
subprocess . call ( cmd )
try :
os . rmdir ( self . srcdir )
except :
pass
finally :
self . _mounted = False
2013-08-09 04:14:10 +04:00
class _DirectImageFetcher ( _LocalImageFetcher ) :
def prepareLocation ( self ) :
self . srcdir = self . location
2013-09-26 21:04:28 +04:00
def fetcherForURI ( uri , * args , * * kwargs ) :
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-09-26 21:04:28 +04:00
return fclass ( uri , * args , * * kwargs )
###############################################
# Helpers for detecting distro from given URL #
###############################################
def _distroFromTreeinfo ( fetcher , arch , vmtype = 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 " )
try :
treeinfo = ConfigParser . SafeConfigParser ( )
treeinfo . read ( tmptreeinfo )
finally :
os . unlink ( tmptreeinfo )
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
try :
fam = treeinfo . get ( " general " , " family " )
except ConfigParser . NoSectionError :
return None
2013-04-13 22:34:52 +04:00
2013-09-26 21:04:28 +04:00
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 ( fetcher , arch , vmtype )
ob . treeinfo = treeinfo
2014-06-30 06:58:53 +04:00
# Explicitly call this, so we populate variant info
2013-09-26 21:04:28 +04:00
ob . isValidStore ( )
return ob
2013-09-27 01:44:30 +04:00
def getDistroStore ( guest , fetcher ) :
2013-03-18 01:06:52 +04:00
stores = [ ]
2013-11-10 03:17:29 +04:00
logging . debug ( " Finding distro store for location= %s " , fetcher . location )
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
arch = guest . os . arch
_type = guest . os . os_type
2013-09-27 01:44:30 +04:00
urldistro = None
if guest . os_variant :
urldistro = osdict . lookup_os ( guest . os_variant ) . urldistro
2013-09-26 21:04:28 +04:00
dist = _distroFromTreeinfo ( fetcher , arch , _type )
2013-03-18 01:06:52 +04:00
if dist :
return dist
2013-09-27 01:44:30 +04:00
stores = _allstores [ : ]
# If user manually specified an os_distro, bump it's URL class
# to the top of the list
if urldistro :
for store in stores :
if store . urldistro == urldistro :
logging . debug ( " Prioritizing distro store= %s " , store )
stores . remove ( store )
stores . insert ( 0 , store )
break
2013-03-18 01:06:52 +04:00
2013-09-27 01:44:30 +04:00
# Always stick GenericDistro at the end, since it's a catchall
stores . remove ( GenericDistro )
2013-03-18 01:06:52 +04:00
stores . append ( GenericDistro )
for sclass in stores :
2013-09-26 21:04:28 +04:00
store = sclass ( fetcher , arch , _type )
# We already tried the treeinfo short circuit, so skip it here
store . uses_treeinfo = False
if store . isValidStore ( ) :
2013-09-26 23:15:59 +04:00
logging . debug ( " Detected distro name= %s osvariant= %s " ,
store . name , store . os_variant )
2013-03-18 01:06:52 +04:00
return store
2014-12-10 21:57:10 +03:00
# No distro was detected. See if the URL even resolves, and if not
# give the user a hint that maybe they mistyped. This won't always
# be true since some webservers don't allow directory listing.
# http://www.redhat.com/archives/virt-tools-list/2014-December/msg00048.html
extramsg = " "
if not fetcher . hasFile ( " " ) :
extramsg = ( " : " +
_ ( " The URL could not be accessed, maybe you mistyped? " ) )
2013-03-18 01:06:52 +04:00
raise ValueError (
2014-12-10 21:57:10 +03:00
_ ( " Could not find an installable distribution at ' %s ' %s \n \n "
2014-09-07 22:01:40 +04:00
" The location must be the root directory of an install tree. \n "
" See virt-install man page for various distro examples. " %
2014-12-10 21:57:10 +03:00
( fetcher . location , extramsg ) ) )
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
##################
# Distro classes #
##################
2013-03-18 01:06:52 +04:00
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-09-27 01:44:30 +04:00
name = None
urldistro = None
2013-03-18 01:06:52 +04:00
2013-09-27 01:44:30 +04:00
# osdict variant value
2013-03-18 01:06:52 +04:00
os_variant = None
_boot_iso_paths = [ ]
_hvm_kernel_paths = [ ]
_xen_kernel_paths = [ ]
uses_treeinfo = False
2013-09-26 21:04:28 +04:00
def __init__ ( self , fetcher , arch , vmtype ) :
self . fetcher = fetcher
2013-03-18 01:06:52 +04:00
self . type = vmtype
self . arch = arch
2013-09-26 21:04:28 +04:00
self . uri = fetcher . location
2013-03-18 01:06:52 +04:00
self . treeinfo = None
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2013-03-18 01:06:52 +04:00
""" Determine if uri points to a tree of the store ' s distro """
raise NotImplementedError
2013-09-26 21:04:28 +04:00
def acquireKernel ( self , guest ) :
2013-03-18 01:06:52 +04:00
kernelpath = None
initrdpath = None
2013-09-26 21:04:28 +04:00
if self . _hasTreeinfo ( ) :
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 :
2013-09-26 21:04:28 +04:00
if self . fetcher . hasFile ( kpath ) and self . fetcher . hasFile ( ipath ) :
2013-03-18 01:06:52 +04:00
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
2013-09-26 21:04:28 +04:00
return self . _kernelFetchHelper ( guest , kernelpath , initrdpath )
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def acquireBootDisk ( self , guest ) :
2013-04-12 16:26:21 +04:00
ignore = guest
2013-09-26 21:04:28 +04:00
if self . _hasTreeinfo ( ) :
return self . fetcher . acquireFile ( self . _getTreeinfoMedia ( " boot.iso " ) )
2013-03-18 01:06:52 +04:00
else :
for path in self . _boot_iso_paths :
2013-09-26 21:04:28 +04:00
if self . fetcher . hasFile ( path ) :
return self . fetcher . acquireFile ( path )
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 ) )
2013-09-26 23:15:59 +04:00
def _check_osvariant_valid ( self , os_variant ) :
return osdict . lookup_os ( os_variant ) is not None
2013-03-18 01:06:52 +04:00
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-09-26 21:04:28 +04:00
if not self . _check_osvariant_valid ( self . os_variant ) :
2013-08-11 02:48:43 +04:00
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
2014-01-18 22:04:47 +04:00
def _get_method_arg ( self ) :
return " method "
2013-09-26 21:04:28 +04:00
def _hasTreeinfo ( self ) :
2013-03-18 01:06:52 +04:00
# all Red Hat based distros should have .treeinfo, perhaps others
# will in time
if not ( self . treeinfo is None ) :
return True
2013-09-26 21:04:28 +04:00
if not self . uses_treeinfo or not self . fetcher . hasFile ( " .treeinfo " ) :
2013-03-18 01:06:52 +04:00
return False
logging . debug ( " Detected .treeinfo file " )
2013-09-26 21:04:28 +04:00
tmptreeinfo = self . fetcher . acquireFile ( " .treeinfo " )
2013-03-18 01:06:52 +04:00
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 )
2013-09-26 21:04:28 +04:00
def _fetchAndMatchRegex ( self , filename , regex ) :
2013-03-18 01:06:52 +04:00
# Fetch 'filename' and return True/False if it matches the regex
local_file = None
try :
try :
2013-09-26 21:04:28 +04:00
local_file = self . fetcher . acquireFile ( filename )
2013-03-18 01:06:52 +04:00
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
2013-09-26 21:04:28 +04:00
def _kernelFetchHelper ( self , guest , kernelpath , initrdpath ) :
2013-03-18 01:06:52 +04:00
# Simple helper for fetching kernel + initrd and performing
2013-06-30 22:33:01 +04:00
# cleanup if necessary
2013-09-26 21:04:28 +04:00
kernel = self . fetcher . acquireFile ( kernelpath )
2013-03-18 01:06:52 +04:00
args = ' '
2013-09-26 21:04:28 +04:00
if not self . fetcher . location . startswith ( " / " ) :
2014-01-18 22:04:47 +04:00
args + = " %s = %s " % ( self . _get_method_arg ( ) , self . fetcher . location )
2013-03-18 01:06:52 +04:00
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 :
2013-09-26 21:04:28 +04:00
initrd = self . fetcher . acquireFile ( initrdpath )
2013-03-18 01:06:52 +04:00
return kernel , initrd , args
2013-11-10 20:35:22 +04:00
except :
2013-03-18 01:06:52 +04:00
os . unlink ( kernel )
2013-11-10 20:35:22 +04:00
raise
2013-03-18 01:06:52 +04:00
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
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
if self . _hasTreeinfo ( ) :
2013-03-18 01:06:52 +04:00
# 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 ) :
2014-01-12 01:24:13 +04:00
try :
self . _valid_kernel_path = (
self . _getTreeinfoMedia ( " kernel " ) ,
self . _getTreeinfoMedia ( " initrd " ) )
except ( ConfigParser . NoSectionError ,
ConfigParser . NoOptionError ) , e :
logging . debug ( e )
2013-03-18 01:06:52 +04:00
if self . treeinfo . has_section ( isoSection ) :
2014-01-12 01:24:13 +04:00
try :
self . _valid_iso_path = self . treeinfo . get ( isoSection ,
" boot.iso " )
except ConfigParser . NoOptionError , e :
logging . debug ( e )
2013-03-18 01:06:52 +04:00
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-09-26 23:15:59 +04:00
if ( self . _valid_kernel_path is None and
self . fetcher . hasFile ( kern ) and
self . fetcher . hasFile ( init ) ) :
2013-03-18 01:06:52 +04:00
self . _valid_kernel_path = ( kern , init )
break
2013-09-26 23:15:59 +04:00
2013-03-18 01:06:52 +04:00
for iso in self . _iso_paths :
2013-09-26 23:15:59 +04:00
if ( self . _valid_iso_path is None and
self . fetcher . hasFile ( iso ) ) :
2013-03-18 01:06:52 +04:00
self . _valid_iso_path = iso
break
if self . _valid_kernel_path or self . _valid_iso_path :
return True
return False
2013-09-26 21:04:28 +04:00
def acquireKernel ( self , guest ) :
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 ) )
2013-09-26 21:04:28 +04:00
return self . _kernelFetchHelper ( guest ,
2013-03-18 01:06:52 +04:00
self . _valid_kernel_path [ 0 ] ,
self . _valid_kernel_path [ 1 ] )
2013-09-26 21:04:28 +04:00
def acquireBootDisk ( self , guest ) :
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. " ) )
2013-09-26 21:04:28 +04:00
return self . fetcher . acquireFile ( self . _valid_iso_path )
2013-03-18 01:06:52 +04:00
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-08-11 02:48:43 +04:00
os_variant = " linux "
2014-01-18 22:04:47 +04:00
_version_number = None
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
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2013-09-26 23:15:59 +04:00
raise NotImplementedError ( )
2013-03-18 01:06:52 +04:00
2014-01-18 22:04:47 +04:00
def _get_method_arg ( self ) :
if ( self . _version_number is not None and
( ( self . urldistro is " rhel " and self . _version_number > = 7 ) or
( self . urldistro is " fedora " and self . _version_number > = 19 ) ) ) :
return " inst.repo "
return " method "
2013-03-18 01:06:52 +04:00
# Fedora distro check
class FedoraDistro ( RedHatDistro ) :
name = " Fedora "
2013-09-27 01:44:30 +04:00
urldistro = " fedora "
2013-03-18 01:06:52 +04:00
def _latestFedoraVariant ( self ) :
2014-01-18 22:04:47 +04:00
"""
Search osdict list , find newest fedora version listed
"""
2013-03-18 01:06:52 +04:00
ret = None
2013-08-11 22:52:30 +04:00
for osinfo in osdict . list_os ( typename = " linux " ) :
2014-02-17 19:40:03 +04:00
if osinfo . name . startswith ( " fedora " ) and " unknown " not in osinfo . name :
2014-10-29 11:00:28 +03:00
# First fedora* occurrence 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 : ] )
2014-01-18 22:04:47 +04:00
def isValidStore ( self ) :
if not self . _hasTreeinfo ( ) :
return self . fetcher . hasFile ( " Fedora " )
if not re . match ( " .*Fedora.* " , self . treeinfo . get ( " general " , " family " ) ) :
return False
lateststr , latestnum = self . _latestFedoraVariant ( )
ver = self . treeinfo . get ( " general " , " version " )
2014-09-06 23:35:30 +04:00
if not ver :
return False
2014-01-18 22:04:47 +04:00
if ver == " development " or ver == " rawhide " :
2014-09-06 23:35:30 +04:00
self . _version_number = latestnum
2014-01-18 22:04:47 +04:00
self . os_variant = lateststr
2014-09-06 23:35:30 +04:00
return
if " _ " in ver :
ver = ver . split ( " _ " ) [ 0 ]
vernum = int ( str ( ver ) . split ( " - " ) [ 0 ] )
if vernum > latestnum :
self . os_variant = lateststr
else :
self . os_variant = " fedora " + str ( vernum )
2014-01-18 22:04:47 +04:00
self . _version_number = vernum
return True
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 "
2013-09-27 01:44:30 +04:00
urldistro = " rhel "
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
if self . _hasTreeinfo ( ) :
2013-03-18 01:06:52 +04:00
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
2013-09-26 23:15:59 +04:00
if ( self . fetcher . hasFile ( " Server " ) or
self . fetcher . hasFile ( " Client " ) ) :
self . os_variant = " rhel5 "
return True
return self . fetcher . hasFile ( " RedHat " )
2013-03-18 01:06:52 +04:00
2013-09-27 01:06:51 +04:00
################################
# osdict autodetection helpers #
################################
2013-03-18 01:06:52 +04:00
def _parseTreeinfoVersion ( self , verstr ) :
2013-09-26 21:04:28 +04:00
def _safeint ( c ) :
try :
val = int ( c )
except :
val = 0
return val
version = _safeint ( verstr [ 0 ] )
2013-03-18 01:06:52 +04:00
update = 0
2013-09-26 21:04:28 +04:00
# RHEL has version=5.4, scientific linux=54
2013-03-18 01:06:52 +04:00
updinfo = verstr . split ( " . " )
if len ( updinfo ) > 1 :
2013-09-26 21:04:28 +04:00
update = _safeint ( updinfo [ 1 ] )
elif len ( verstr ) > 1 :
update = _safeint ( verstr [ 1 ] )
2013-03-18 01:06:52 +04:00
return version , update
def _variantFromVersion ( self ) :
ver = self . treeinfo . get ( " general " , " version " )
2014-11-21 00:18:13 +03:00
name = None
if self . treeinfo . has_option ( " general " , " name " ) :
name = self . treeinfo . get ( " general " , " name " )
2013-03-18 01:06:52 +04:00
if not ver :
return
2014-11-21 00:18:13 +03:00
if name and name . startswith ( " Red Hat Enterprise Linux Server for ARM " ) :
# Kind of a hack, but good enough for the time being
version = 7
update = 0
else :
version , update = self . _parseTreeinfoVersion ( ver )
2014-01-18 22:04:47 +04:00
self . _version_number = version
2013-03-18 01:06:52 +04:00
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-09-26 21:04:28 +04:00
if not self . _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-09-26 21:04:28 +04:00
if self . _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 "
2013-09-27 01:44:30 +04:00
urldistro = None
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2014-09-06 23:35:30 +04:00
if not self . _hasTreeinfo ( ) :
return self . fetcher . hasFile ( " CentOS " )
2013-09-26 23:15:59 +04:00
2014-09-06 23:35:30 +04:00
m = re . match ( " .*CentOS.* " , self . treeinfo . get ( " general " , " family " ) )
ret = ( m is not None )
if ret :
self . _variantFromVersion ( )
return ret
2013-03-18 01:06:52 +04:00
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-09-27 01:44:30 +04:00
urldistro = None
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
_boot_iso_paths = RHELDistro . _boot_iso_paths + [ " images/SL/boot.iso " ]
2013-09-26 23:15:59 +04:00
_hvm_kernel_paths = RHELDistro . _hvm_kernel_paths + [
( " images/SL/pxeboot/vmlinuz " , " images/SL/pxeboot/initrd.img " ) ]
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
if self . _hasTreeinfo ( ) :
2013-03-18 01:06:52 +04:00
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
2013-09-26 23:15:59 +04:00
return self . fetcher . hasFile ( " SL " )
2013-03-18 01:06:52 +04:00
class SuseDistro ( Distro ) :
name = " SUSE "
2013-09-27 01:44:30 +04:00
urldistro = " suse "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-09-27 01:44:30 +04:00
2013-04-13 22:34:52 +04:00
_boot_iso_paths = [ " boot/boot.iso " ]
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def __init__ ( self , * args , * * kwargs ) :
Distro . __init__ ( self , * args , * * kwargs )
if re . match ( r ' i[4-9]86 ' , self . arch ) :
2013-03-18 01:06:52 +04:00
self . arch = ' i386 '
# 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
# 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
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2013-09-27 01:06:51 +04:00
if not self . fetcher . hasFile ( " directory.yast " ) :
return False
self . os_variant = self . _detect_osdict_from_url ( )
return True
2014-01-18 22:04:47 +04:00
def _get_method_arg ( self ) :
return " install "
2013-09-27 01:06:51 +04:00
################################
# osdict autodetection helpers #
################################
def _detect_osdict_from_url ( self ) :
root = " opensuse "
2014-09-06 23:35:30 +04:00
oses = [ n for n in osdict . list_os ( ) if n . name . startswith ( root ) ]
2013-09-27 01:06:51 +04:00
2014-09-06 23:35:30 +04:00
for osobj in oses :
codename = osobj . name [ len ( root ) : ]
if re . search ( " / %s / " % codename , self . uri ) :
return osobj . name
2013-09-27 01:06:51 +04:00
return self . os_variant
2013-03-18 01:06:52 +04:00
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-09-27 01:44:30 +04:00
urldistro = " debian "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def __init__ ( self , * args , * * kwargs ) :
Distro . __init__ ( self , * args , * * kwargs )
2013-03-18 01:06:52 +04:00
2015-03-27 20:06:26 +03:00
# Pull the tree's arch out of the URL text
self . _treeArch = " i386 "
for pattern in [ " ^.*/installer-( \ w+)/?$ " ,
" ^.*/daily-images/( \ w+)/?$ " ] :
arch = re . findall ( pattern , self . uri )
if arch :
self . _treeArch = arch [ 0 ]
break
2013-03-18 01:06:52 +04:00
2015-03-27 20:06:26 +03:00
self . _url_prefix = ' current/images '
self . _installer_dirname = self . name . lower ( ) + " -installer "
2013-03-18 01:06:52 +04:00
self . _set_media_paths ( )
def _set_media_paths ( self ) :
2015-03-27 20:06:26 +03:00
self . _boot_iso_paths = [ " %s /netboot/mini.iso " % self . _url_prefix ]
hvmroot = " %s /netboot/ %s / %s / " % ( self . _url_prefix ,
self . _installer_dirname ,
2013-03-18 01:06:52 +04:00
self . _treeArch )
2015-03-27 20:06:26 +03:00
initrd_basename = " initrd.gz "
kernel_basename = " linux "
if self . _treeArch in [ " ppc64el " ] :
kernel_basename = " vmlinux "
self . _hvm_kernel_paths = [
( hvmroot + kernel_basename , hvmroot + initrd_basename ) ]
xenroot = " %s /netboot/xen/ " % self . _url_prefix
self . _xen_kernel_paths = [ ( xenroot + " vmlinuz " , xenroot + " initrd.gz " ) ]
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2015-03-27 20:06:26 +03:00
if self . fetcher . hasFile ( " %s /MANIFEST " % self . _url_prefix ) :
2013-03-18 01:06:52 +04:00
# For regular trees
pass
2013-09-26 21:04:28 +04:00
elif self . fetcher . hasFile ( " daily/MANIFEST " ) :
2013-03-18 01:06:52 +04:00
# For daily trees
2015-03-27 20:06:26 +03:00
self . _url_prefix = " daily "
2013-03-18 01:06:52 +04:00
self . _set_media_paths ( )
else :
return False
2015-03-27 20:06:26 +03:00
filename = " %s /MANIFEST " % self . _url_prefix
regex = " .* %s .* " % self . _installer_dirname
2013-09-27 01:06:51 +04:00
if not self . _fetchAndMatchRegex ( filename , regex ) :
logging . debug ( " Regex didn ' t match, not a %s distro " , self . name )
return False
self . os_variant = self . _detect_osdict_from_url ( )
return True
2013-03-18 01:06:52 +04:00
2013-09-27 01:06:51 +04:00
################################
# osdict autodetection helpers #
################################
def _detect_osdict_from_url ( self ) :
root = self . name . lower ( )
2014-09-06 23:35:30 +04:00
oses = [ n for n in osdict . list_os ( ) if n . name . startswith ( root ) ]
2013-09-27 01:06:51 +04:00
2015-03-27 20:06:26 +03:00
if self . _url_prefix == " daily " :
2014-09-06 23:35:30 +04:00
return oses [ 0 ] . name
for osobj in oses :
# name looks like 'Debian Sarge'
if " " not in osobj . label :
continue
codename = osobj . label . lower ( ) . split ( ) [ 1 ]
2013-09-27 01:06:51 +04:00
if ( " / %s / " % codename ) in self . uri :
2014-09-06 23:35:30 +04:00
return osobj . name
2013-09-27 01:06:51 +04:00
return self . os_variant
2013-03-18 01:06:52 +04:00
class UbuntuDistro ( DebianDistro ) :
# http://archive.ubuntu.com/ubuntu/dists/natty/main/installer-amd64/
2013-08-09 17:33:47 +04:00
name = " Ubuntu "
2013-09-27 01:06:51 +04:00
urldistro = " ubuntu "
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2015-03-27 20:06:26 +03:00
if self . fetcher . hasFile ( " %s /MANIFEST " % self . _url_prefix ) :
2013-03-18 01:06:52 +04:00
# For regular trees
2015-03-27 20:06:26 +03:00
filename = " %s /MANIFEST " % self . _url_prefix
regex = " .* %s .* " % self . _installer_dirname
2013-09-26 21:04:28 +04:00
elif self . fetcher . hasFile ( " install/netboot/version.info " ) :
2013-03-18 01:06:52 +04:00
# For trees based on ISO's
2015-03-27 20:06:26 +03:00
self . _url_prefix = " install "
2013-03-18 01:06:52 +04:00
self . _set_media_paths ( )
2015-03-27 20:06:26 +03:00
filename = " %s /netboot/version.info " % self . _url_prefix
2013-03-18 01:06:52 +04:00
regex = " %s * " % self . name
else :
return False
2013-09-27 01:06:51 +04:00
if not self . _fetchAndMatchRegex ( filename , regex ) :
logging . debug ( " Regex didn ' t match, not a %s distro " , self . name )
return False
2013-03-18 01:06:52 +04:00
2013-09-27 01:06:51 +04:00
self . os_variant = self . _detect_osdict_from_url ( )
return True
2013-03-18 01:06:52 +04:00
class MandrivaDistro ( Distro ) :
2013-08-09 17:33:47 +04:00
# ftp://ftp.uwsg.indiana.edu/linux/mandrake/official/2007.1/x86_64/
2013-09-27 01:44:30 +04:00
name = " Mandriva/Mageia "
urldistro = " mandriva "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-09-27 01:44:30 +04:00
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 = [ ]
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2013-03-18 01:06:52 +04:00
# 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
2013-09-26 21:04:28 +04:00
if not self . fetcher . hasFile ( " VERSION " ) :
2013-03-18 01:06:52 +04:00
return False
2013-09-27 01:44:30 +04:00
for name in [ " Mandriva " , " Mageia " ] :
if self . _fetchAndMatchRegex ( " VERSION " , " .* %s .* " % name ) :
return True
2013-03-18 01:06:52 +04:00
2013-09-26 23:15:59 +04:00
logging . debug ( " Regex didn ' t match, not a %s distro " , self . name )
2013-03-18 01:06:52 +04:00
return False
2013-04-13 22:34:52 +04:00
2013-08-09 04:37:46 +04:00
class ALTLinuxDistro ( Distro ) :
2013-10-15 19:21:53 +04:00
# altlinux doesn't have installable URLs, so this is just for a
# mounted ISO
2013-08-08 13:28:43 +04:00
name = " ALT Linux "
2013-09-27 01:44:30 +04:00
urldistro = " altlinux "
2013-08-11 02:48:43 +04:00
os_variant = " linux "
2013-09-27 01:44:30 +04:00
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 = [ ]
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2013-08-08 13:28:43 +04:00
# Don't support any paravirt installs
if self . type is not None and self . type != " hvm " :
return False
2013-09-26 21:04:28 +04:00
if not self . fetcher . hasFile ( " .disk/info " ) :
2013-08-08 13:28:43 +04:00
return False
2013-09-26 21:04:28 +04:00
if self . _fetchAndMatchRegex ( " .disk/info " , " .* %s .* " % self . name ) :
2013-08-08 13:28:43 +04:00
return True
2013-09-26 23:15:59 +04:00
logging . debug ( " Regex didn ' t match, not a %s distro " , self . name )
2013-08-08 13:28:43 +04:00
return False
2013-09-27 01:44:30 +04:00
# Build list of all *Distro classes
def _build_distro_list ( ) :
allstores = [ ]
for obj in globals ( ) . values ( ) :
if type ( obj ) is type and issubclass ( obj , Distro ) and obj . name :
allstores . append ( obj )
seen_urldistro = [ ]
for obj in allstores :
if obj . urldistro and obj . urldistro in seen_urldistro :
raise RuntimeError ( " programming error: duplicate urldistro= %s " %
obj . urldistro )
seen_urldistro . append ( obj . urldistro )
return allstores
_allstores = _build_distro_list ( )