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
2015-09-19 01:31:56 +03:00
import requests
2013-08-09 04:14:10 +04:00
import stat
2015-09-19 01:01:57 +03:00
import StringIO
2013-08-09 04:14:10 +04:00
import subprocess
import tempfile
import urllib2
import urlparse
2015-04-04 19:04:11 +03:00
from . osdict import OSDB
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
2015-09-18 23:49:18 +03:00
class _URLFetcher ( object ) :
2013-08-09 04:14:10 +04:00
"""
This is a generic base class for fetching / extracting files from
a media source , such as CD ISO , NFS server , or HTTP / FTP server
"""
2015-09-19 01:31:56 +03:00
_block_size = 16384
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
2013-08-09 04:14:10 +04:00
2015-09-18 23:49:18 +03:00
self . _srcdir = None
2013-08-09 04:14:10 +04:00
2015-09-18 23:49:18 +03:00
logging . debug ( " Using scratchdir= %s " , scratchdir )
2014-02-14 20:02:35 +04:00
2015-09-19 01:31:56 +03:00
2015-09-18 23:49:18 +03:00
####################
# Internal helpers #
####################
def _make_full_url ( self , filename ) :
"""
Generate a full fetchable URL from the passed filename , which
is relative to the self . location
"""
ret = self . _srcdir or self . location
if not filename :
return ret
if not ret . endswith ( " / " ) :
ret + = " / "
return ret + filename
2015-09-19 01:31:56 +03:00
def _grabURL ( self , filename , fileobj ) :
2015-09-18 23:49:18 +03:00
"""
2015-09-19 01:31:56 +03:00
Download the filename from self . location , and write contents to
fileobj
2015-09-18 23:49:18 +03:00
"""
2015-09-19 01:31:56 +03:00
url = self . _make_full_url ( filename )
try :
urlobj , size = self . _grabber ( url )
except Exception , e :
raise ValueError ( _ ( " Couldn ' t acquire file %s : %s " ) %
( url , str ( e ) ) )
logging . debug ( " Fetching URI: %s " , url )
self . meter . start (
text = _ ( " Retrieving file %s ... " ) % os . path . basename ( filename ) ,
size = size )
total = self . _write ( urlobj , fileobj )
self . meter . end ( total )
def _write ( self , urlobj , fileobj ) :
"""
Write the contents of urlobj to python file like object fileobj
"""
total = 0
2015-09-19 00:11:37 +03:00
while 1 :
2015-09-19 01:31:56 +03:00
buff = urlobj . read ( self . _block_size )
2015-09-19 00:11:37 +03:00
if not buff :
break
fileobj . write ( buff )
2015-09-19 01:31:56 +03:00
total + = len ( buff )
self . meter . update ( total )
return total
2013-08-09 04:14:10 +04:00
2015-09-19 01:31:56 +03:00
def _grabber ( self , url ) :
2015-09-19 01:01:57 +03:00
"""
2015-09-19 01:31:56 +03:00
Returns the urlobj , size for the passed URL . urlobj is whatever
data needs to be passed to self . _write
2015-09-19 01:01:57 +03:00
"""
2015-09-19 01:31:56 +03:00
raise NotImplementedError ( " must be implemented in subclass " )
2015-09-19 01:01:57 +03:00
2015-09-18 23:49:18 +03:00
##############
# Public API #
##############
2013-08-09 04:14:10 +04:00
def prepareLocation ( self ) :
2015-09-18 23:49:18 +03:00
"""
Perform any necessary setup
"""
2014-09-07 22:22:56 +04:00
pass
2013-08-09 04:14:10 +04:00
def cleanupLocation ( self ) :
2015-09-18 23:49:18 +03:00
"""
Perform any necessary cleanup
"""
2013-08-09 04:14:10 +04:00
pass
2015-09-18 23:49:18 +03:00
def hasFile ( self , filename ) :
"""
Return True if self . location has the passed filename
"""
raise NotImplementedError ( " Must be implemented in subclass " )
2013-09-26 21:04:28 +04:00
def acquireFile ( self , filename ) :
2015-09-18 23:49:18 +03:00
"""
Grab the passed filename from self . location and save it to
a temporary file , returning the temp filename
"""
2015-09-19 01:01:57 +03:00
prefix = " virtinst- " + os . path . basename ( filename ) + " . "
2013-08-09 04:14:10 +04:00
2015-09-19 01:01:57 +03:00
if " VIRTINST_TEST_SUITE " in os . environ :
2015-09-19 01:31:56 +03:00
fn = os . path . join ( " /tmp " , prefix )
fileobj = file ( fn , " w " )
2015-09-19 01:01:57 +03:00
else :
fileobj = tempfile . NamedTemporaryFile (
dir = self . scratchdir , prefix = prefix , delete = False )
2015-09-19 01:31:56 +03:00
fn = fileobj . name
2013-08-09 04:14:10 +04:00
2015-09-19 01:31:56 +03:00
self . _grabURL ( filename , fileobj )
logging . debug ( " Saved file to " + fn )
return fn
2015-09-19 01:01:57 +03:00
def acquireFileContent ( self , filename ) :
"""
Grab the passed filename from self . location and return it as a string
"""
fileobj = StringIO . StringIO ( )
2015-09-19 01:31:56 +03:00
self . _grabURL ( filename , fileobj )
2015-09-19 01:01:57 +03:00
return fileobj . getvalue ( )
2013-08-09 04:14:10 +04:00
2015-09-18 23:49:18 +03:00
class _HTTPURLFetcher ( _URLFetcher ) :
2013-08-09 04:14:10 +04:00
def hasFile ( self , filename ) :
2015-09-19 01:31:56 +03:00
"""
We just do a HEAD request to see if the file exists
"""
2015-09-18 23:49:18 +03:00
url = self . _make_full_url ( filename )
2013-08-09 04:14:10 +04:00
try :
2015-09-19 01:31:56 +03:00
response = requests . head ( url )
response . raise_for_status ( )
2013-08-09 04:14:10 +04:00
except Exception , e :
2015-09-18 23:49:18 +03:00
logging . debug ( " HTTP hasFile: didn ' t find %s : %s " , url , str ( e ) )
2013-08-09 04:14:10 +04:00
return False
return True
2015-09-19 01:31:56 +03:00
def _grabber ( self , url ) :
"""
Use requests for this
"""
response = requests . get ( url , stream = True )
response . raise_for_status ( )
size = response . headers . get ( ' content-length ' )
return response , size . isdigit ( ) and int ( size ) or None
def _write ( self , urlobj , fileobj ) :
"""
The requests object doesn ' t have a file-like read() option, so
we need to implemente it ourselves
"""
total = 0
for data in urlobj . iter_content ( chunk_size = self . _block_size ) :
fileobj . write ( data )
total + = len ( data )
self . meter . update ( total )
return total
2013-08-09 04:14:10 +04:00
2015-09-18 23:49:18 +03:00
class _FTPURLFetcher ( _URLFetcher ) :
_ftp = None
2013-08-09 04:14:10 +04:00
def prepareLocation ( self ) :
2015-09-18 23:49:18 +03:00
if self . _ftp :
2014-09-07 22:22:56 +04:00
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 :
2015-09-19 01:31:56 +03:00
server = urlparse . urlparse ( self . location ) [ 1 ]
self . _ftp = ftplib . FTP ( server )
2015-09-18 23:49:18 +03:00
self . _ftp . login ( )
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
except Exception , e :
raise ValueError ( _ ( " Opening URL %s failed: %s . " ) %
( self . location , str ( e ) ) )
2015-09-19 01:31:56 +03:00
def _grabber ( self , url ) :
"""
Use urllib2 and ftplib to grab the file
"""
request = urllib2 . Request ( url )
urlobj = urllib2 . urlopen ( request )
size = self . _ftp . size ( urlparse . urlparse ( url ) [ 2 ] )
return urlobj , size
2014-09-07 22:22:56 +04:00
def cleanupLocation ( self ) :
2015-09-18 23:49:18 +03:00
if not self . _ftp :
2014-09-07 22:22:56 +04:00
return
try :
2015-09-18 23:49:18 +03:00
self . _ftp . quit ( )
2014-09-07 22:22:56 +04:00
except :
logging . debug ( " Error quitting ftp connection " , exc_info = True )
2015-09-18 23:49:18 +03:00
self . _ftp = None
2013-08-09 04:14:10 +04:00
def hasFile ( self , filename ) :
2015-09-18 23:49:18 +03:00
url = self . _make_full_url ( filename )
2015-09-19 01:31:56 +03:00
path = urlparse . urlparse ( url ) [ 2 ]
2013-08-09 04:14:10 +04:00
try :
try :
# If it's a file
2015-09-19 01:31:56 +03:00
self . _ftp . size ( path )
2013-08-09 04:14:10 +04:00
except ftplib . all_errors :
# If it's a dir
2015-09-19 01:31:56 +03:00
self . _ftp . cwd ( path )
2013-08-09 04:14:10 +04:00
except ftplib . all_errors , e :
logging . debug ( " FTP hasFile: couldn ' t access %s : %s " ,
2015-09-18 23:49:18 +03:00
url , str ( e ) )
2013-08-09 04:14:10 +04:00
return False
return True
2015-09-18 23:49:18 +03:00
class _LocalURLFetcher ( _URLFetcher ) :
"""
For grabbing files from a local directory
"""
2013-08-09 04:14:10 +04:00
def hasFile ( self , filename ) :
2015-09-18 23:49:18 +03:00
url = self . _make_full_url ( filename )
ret = os . path . exists ( url )
if not ret :
logging . debug ( " local hasFile: Couldn ' t find %s " , url )
return ret
2013-08-09 04:14:10 +04:00
2015-09-19 01:31:56 +03:00
def _grabber ( self , url ) :
urlobj = file ( url , " r " )
size = os . path . getsize ( url )
return urlobj , size
2013-08-09 04:14:10 +04:00
2015-09-18 23:49:18 +03:00
class _MountedURLFetcher ( _LocalURLFetcher ) :
2013-08-09 04:14:10 +04:00
"""
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 :
2015-09-18 23:49:18 +03:00
self . _srcdir = os . environ [ " VIRTINST_TEST_URL_DIR " ]
2014-03-06 21:35:01 +04:00
else :
2015-09-18 23:49:18 +03:00
self . _srcdir = tempfile . mkdtemp ( prefix = " virtinstmnt. " ,
2014-03-06 21:35:01 +04:00
dir = self . scratchdir )
2013-08-09 04:14:10 +04:00
mountcmd = " /bin/mount "
2015-09-18 23:49:18 +03:00
logging . debug ( " Preparing mount at " + self . _srcdir )
2013-08-09 04:14:10 +04:00
if self . location . startswith ( " nfs: " ) :
2015-09-18 23:49:18 +03:00
cmd = [ mountcmd , " -o " , " ro " , self . location [ 4 : ] , self . _srcdir ]
2013-08-09 04:14:10 +04:00
else :
if stat . S_ISBLK ( os . stat ( self . location ) [ stat . ST_MODE ] ) :
mountopt = " ro "
else :
mountopt = " ro,loop "
2015-09-18 23:49:18 +03:00
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
2015-09-18 23:49:18 +03:00
logging . debug ( " Cleaning up mount at " + self . _srcdir )
2014-09-07 22:22:56 +04:00
try :
if not self . _in_test_suite :
2015-09-18 23:49:18 +03:00
cmd = [ " /bin/umount " , self . _srcdir ]
2014-09-07 22:22:56 +04:00
subprocess . call ( cmd )
try :
2015-09-18 23:49:18 +03:00
os . rmdir ( self . _srcdir )
2014-09-07 22:22:56 +04:00
except :
pass
finally :
self . _mounted = False
2013-08-09 04:14:10 +04:00
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:// " ) :
2015-09-18 23:49:18 +03:00
fclass = _HTTPURLFetcher
2013-03-18 01:06:52 +04:00
elif uri . startswith ( " ftp:// " ) :
2015-09-18 23:49:18 +03:00
fclass = _FTPURLFetcher
2013-09-24 05:23:36 +04:00
elif uri . startswith ( " nfs: " ) :
2015-09-18 23:49:18 +03:00
fclass = _MountedURLFetcher
elif os . path . isdir ( uri ) :
# Pointing to a local tree
fclass = _LocalURLFetcher
2013-03-18 01:06:52 +04:00
else :
2015-09-18 23:49:18 +03:00
# Pointing to a path, like an .iso to mount
fclass = _MountedURLFetcher
2013-09-26 21:04:28 +04:00
return fclass ( uri , * args , * * kwargs )
###############################################
# Helpers for detecting distro from given URL #
###############################################
2015-09-19 01:01:57 +03:00
def _grabTreeinfo ( fetcher ) :
2013-09-26 21:04:28 +04:00
"""
2015-09-19 01:01:57 +03:00
See if the URL has treeinfo , and if so return it as a ConfigParser
object .
2013-09-26 21:04:28 +04:00
"""
2015-09-19 01:01:57 +03:00
try :
tmptreeinfo = fetcher . acquireFile ( " .treeinfo " )
except ValueError :
2013-09-26 21:04:28 +04:00
return None
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 :
2015-09-19 01:01:57 +03:00
treeinfo . get ( " general " , " family " )
2013-09-26 21:04:28 +04:00
except ConfigParser . NoSectionError :
2015-09-19 01:01:57 +03:00
logging . debug ( " Did not find ' family ' section in treeinfo " )
2013-09-26 21:04:28 +04:00
return None
2013-04-13 22:34:52 +04:00
2015-09-19 01:01:57 +03:00
return treeinfo
2013-09-26 21:04:28 +04:00
2015-05-29 11:51:48 +03:00
def _distroFromSUSEContent ( fetcher , arch , vmtype = None ) :
# Parse content file for the 'LABEL' field containing the distribution name
# None if no content, GenericDistro if unknown label type.
2015-09-19 01:01:57 +03:00
try :
cbuf = fetcher . acquireFileContent ( " content " )
except ValueError :
2015-05-29 11:51:48 +03:00
return None
distribution = None
distro_version = None
distro_summary = None
distro_distro = None
distro_arch = None
lines = cbuf . splitlines ( ) [ 1 : ]
for line in lines :
if line . startswith ( " LABEL " ) :
distribution = line . split ( ' ' , 1 )
elif line . startswith ( " DISTRO " ) :
distro_distro = line . rsplit ( ' , ' , 1 )
elif line . startswith ( " VERSION " ) :
distro_version = line . split ( ' ' , 1 )
elif line . startswith ( " SUMMARY " ) :
distro_summary = line . split ( ' ' , 1 )
elif line . startswith ( " BASEARCHS " ) :
distro_arch = line . split ( ' ' , 1 )
elif line . startswith ( " DEFAULTBASE " ) :
distro_arch = line . split ( ' ' , 1 )
elif line . startswith ( " REPOID " ) :
distro_arch = line . rsplit ( ' / ' , 1 )
if distribution and distro_version and distro_arch :
break
if not distribution :
if distro_summary :
distribution = distro_summary
elif distro_distro :
distribution = distro_distro
if distro_arch :
arch = distro_arch [ 1 ] . strip ( )
# Fix for 13.2 official oss repo
if arch . find ( " i586-x86_64 " ) != - 1 :
arch = " x86_64 "
else :
if cbuf . find ( " x86_64 " ) != - 1 :
arch = " x86_64 "
elif cbuf . find ( " i586 " ) != - 1 :
arch = " i586 "
2015-09-11 18:41:09 +03:00
elif cbuf . find ( " s390x " ) != - 1 :
arch = " s390x "
2015-05-29 11:51:48 +03:00
dclass = GenericDistro
if distribution :
if re . match ( " .*SUSE Linux Enterprise Server* " , distribution [ 1 ] ) or \
re . match ( " .*SUSE SLES* " , distribution [ 1 ] ) :
dclass = SLESDistro
if distro_version is None :
distro_version = [ ' VERSION ' , distribution [ 1 ] . strip ( ) . rsplit ( ' ' ) [ 4 ] ]
elif re . match ( " .*SUSE Linux Enterprise Desktop* " , distribution [ 1 ] ) :
dclass = SLEDDistro
if distro_version is None :
distro_version = [ ' VERSION ' , distribution [ 1 ] . strip ( ) . rsplit ( ' ' ) [ 4 ] ]
elif re . match ( " .*openSUSE.* " , distribution [ 1 ] ) :
dclass = OpensuseDistro
if distro_version is None :
distro_version = [ ' VERSION ' , distribution [ 0 ] . strip ( ) . rsplit ( ' : ' ) [ 4 ] ]
# For tumbleweed we only have an 8 character date string so default to 13.2
if distro_version [ 1 ] and len ( distro_version [ 1 ] ) == 8 :
distro_version = [ ' VERSION ' , ' 13.2 ' ]
if distro_version is None :
return None
ob = dclass ( fetcher , arch , vmtype )
if dclass != GenericDistro :
ob . version_from_content = distro_version
# Explictly call this, so we populate os_type/variant info
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 :
2015-04-04 19:04:11 +03:00
urldistro = OSDB . lookup_os ( guest . os_variant ) . urldistro
2013-09-27 01:44:30 +04:00
2015-09-19 01:01:57 +03:00
treeinfo = _grabTreeinfo ( fetcher )
if not treeinfo :
dist = _distroFromSUSEContent ( fetcher , arch , _type )
if dist :
return dist
2013-03-18 01:06:52 +04:00
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
2015-09-19 01:01:57 +03:00
if treeinfo :
stores . sort ( key = lambda x : not x . uses_treeinfo )
2013-03-18 01:06:52 +04:00
for sclass in stores :
2013-09-26 21:04:28 +04:00
store = sclass ( fetcher , arch , _type )
2015-09-19 01:01:57 +03:00
store . treeinfo = treeinfo
2013-09-26 21:04:28 +04:00
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
2015-09-19 01:01:57 +03:00
uses_treeinfo = False
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 = [ ]
2015-06-05 23:30:57 +03:00
version_from_content = None
2013-03-18 01:06:52 +04:00
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
2015-09-19 01:01:57 +03:00
# This is set externally
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
2015-09-19 01:01:57 +03:00
if self . treeinfo :
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
2015-09-19 01:01:57 +03:00
if self . treeinfo :
2013-09-26 21:04:28 +04:00
return self . fetcher . acquireFile ( self . _getTreeinfoMedia ( " boot.iso " ) )
2015-09-19 01:01:57 +03:00
for path in self . _boot_iso_paths :
if self . fetcher . hasFile ( path ) :
return self . fetcher . acquireFile ( path )
raise RuntimeError ( _ ( " Could not find boot.iso in %s tree. " %
self . name ) )
2013-03-18 01:06:52 +04:00
2013-09-26 23:15:59 +04:00
def _check_osvariant_valid ( self , os_variant ) :
2015-04-04 19:04:11 +03:00
return OSDB . lookup_os ( os_variant ) is not None
2013-09-26 23:15:59 +04:00
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-03-18 01:06:52 +04:00
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
try :
2015-09-19 01:01:57 +03:00
content = self . fetcher . acquireFileContent ( filename )
except ValueError :
return False
2013-03-18 01:06:52 +04:00
2015-09-19 01:01:57 +03:00
for line in content . splitlines ( ) :
if re . match ( regex , line ) :
return True
2013-03-18 01:06:52 +04:00
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 ) :
2015-09-19 01:01:57 +03:00
if self . treeinfo :
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 " )
2015-09-19 01:01:57 +03:00
2013-03-18 01:06:52 +04:00
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
"""
2015-09-19 01:01:57 +03:00
uses_treeinfo = True
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
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
"""
2015-04-14 00:05:25 +03:00
latest = OSDB . latest_fedora_version ( )
return latest , int ( latest [ 6 : ] )
2013-03-18 01:06:52 +04:00
2014-01-18 22:04:47 +04:00
def isValidStore ( self ) :
2015-09-19 01:01:57 +03:00
if not self . treeinfo :
2014-01-18 22:04:47 +04:00
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 ) :
2015-09-19 01:01:57 +03:00
if self . treeinfo :
2015-10-05 10:41:09 +03:00
# Matches:
# Red Hat Enterprise Linux
# RHEL Atomic Host
m = re . match ( " .*(Red Hat Enterprise Linux|RHEL).* " ,
2013-03-18 01:06:52 +04:00
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 "
2015-04-04 22:41:13 +03:00
urldistro = " centos "
2013-03-18 01:06:52 +04:00
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2015-09-19 01:01:57 +03:00
if not self . treeinfo :
2014-09-06 23:35:30 +04:00
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 ( )
2015-04-04 22:41:13 +03:00
if self . os_variant :
new_variant = self . os_variant . replace ( " rhel " , " centos " )
if self . _check_osvariant_valid ( new_variant ) :
self . os_variant = new_variant
2014-09-06 23:35:30 +04:00
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 ) :
2015-09-19 01:01:57 +03:00
if self . treeinfo :
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
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 '
2015-05-29 11:51:49 +03:00
oldkern = " linux "
oldinit = " initrd "
if self . arch == " x86_64 " :
oldkern + = " 64 "
oldinit + = " 64 "
2015-09-11 18:41:09 +03:00
if self . arch == " s390x " :
self . _hvm_kernel_paths = [ ( " boot/ %s /linux " % self . arch ,
" boot/ %s /initrd " % self . arch ) ]
# No Xen on s390x
self . _xen_kernel_paths = [ ]
else :
# Tested with Opensuse >= 10.2, 11, and sles 10
self . _hvm_kernel_paths = [ ( " boot/ %s /loader/linux " % self . arch ,
" boot/ %s /loader/initrd " % self . arch ) ]
# Tested with Opensuse 10.0
self . _hvm_kernel_paths . append ( ( " boot/loader/ %s " % oldkern ,
" boot/loader/ %s " % oldinit ) )
2015-11-02 15:43:59 +03:00
# Tested with SLES 12 for ppc64le
self . _hvm_kernel_paths . append ( ( " boot/ %s /linux " % self . arch ,
" boot/ %s /initrd " % self . arch ) )
2013-03-18 01:06:52 +04:00
2015-09-11 18:41:09 +03:00
# Matches Opensuse > 10.2 and sles 10
self . _xen_kernel_paths = [ ( " boot/ %s /vmlinuz-xen " % self . arch ,
" boot/ %s /initrd-xen " % self . arch ) ]
2013-03-18 01:06:52 +04:00
2015-05-29 11:51:49 +03:00
def _variantFromVersion ( self ) :
distro_version = self . version_from_content [ 1 ] . strip ( )
version = distro_version . split ( ' . ' , 1 ) [ 0 ] . strip ( )
self . os_variant = self . urldistro
if int ( version ) > = 10 :
if self . os_variant . startswith ( ( " sles " , " sled " ) ) :
sp_version = None
if len ( distro_version . split ( ' . ' , 1 ) ) == 2 :
sp_version = ' sp ' + distro_version . split ( ' . ' , 1 ) [ 1 ] . strip ( )
self . os_variant + = version
if sp_version :
self . os_variant + = sp_version
else :
self . os_variant + = distro_version
else :
self . os_variant + = " 9 "
2013-09-26 21:04:28 +04:00
def isValidStore ( self ) :
2015-05-29 11:51:49 +03:00
# self.version_from_content is the VERSION line from the contents file
if self . version_from_content is None or \
self . version_from_content [ 1 ] is None :
2013-09-27 01:06:51 +04:00
return False
2015-05-29 11:51:49 +03:00
self . _variantFromVersion ( )
2013-09-27 01:06:51 +04:00
self . os_variant = self . _detect_osdict_from_url ( )
2015-09-11 18:41:09 +03:00
# Reset kernel name for sle11 source on s390x
if self . arch == " s390x " :
if self . os_variant == " sles11 " or self . os_variant == " sled11 " :
self . _hvm_kernel_paths = [ ( " boot/ %s /vmrdr.ikr " % self . arch ,
" boot/ %s /initrd " % self . arch ) ]
2013-09-27 01:06:51 +04:00
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 "
2015-04-04 19:04:11 +03:00
oses = [ n for n in OSDB . 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
2015-05-29 11:51:47 +03:00
class SLESDistro ( SuseDistro ) :
urldistro = " sles "
class SLEDDistro ( SuseDistro ) :
urldistro = " sled "
# Suse image store is harder - we fetch the kernel RPM and a helper
# RPM and then munge bits together to generate a initrd
class OpensuseDistro ( SuseDistro ) :
urldistro = " opensuse "
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
2015-09-24 01:21:26 +03:00
self . os_variant = self . _detect_debian_osdict_from_url ( )
2013-09-27 01:06:51 +04:00
return True
2013-03-18 01:06:52 +04:00
2013-09-27 01:06:51 +04:00
################################
# osdict autodetection helpers #
################################
2015-09-24 01:21:26 +03:00
def _detect_debian_osdict_from_url ( self ) :
2013-09-27 01:06:51 +04:00
root = self . name . lower ( )
2015-04-04 19:04:11 +03:00
oses = [ n for n in OSDB . 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 " :
2015-09-24 01:21:26 +03:00
logging . debug ( " Appears to be debian ' daily ' URL, using latest "
" debian OS " )
2014-09-06 23:35:30 +04:00
return oses [ 0 ] . name
2015-09-24 01:21:26 +03:00
# We grab the distro code name from the libosinfo label, and
# see if we can find that in the URL
2014-09-06 23:35:30 +04:00
for osobj in oses :
2015-09-24 01:21:26 +03:00
# name looks like 'Debian Sarge' or 'Ubuntu Vivid Varvet'
2014-09-06 23:35:30 +04:00
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 :
2015-09-24 01:21:26 +03:00
logging . debug ( " Found codename= %s in the URL string " , codename )
2014-09-06 23:35:30 +04:00
return osobj . name
2015-09-24 01:21:26 +03:00
logging . debug ( " Didn ' t find any known codename in the URL string " )
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
2015-09-24 01:21:26 +03:00
self . os_variant = self . _detect_debian_osdict_from_url ( )
2013-09-27 01:06:51 +04:00
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 )
2015-09-19 01:01:57 +03:00
# Always stick GenericDistro at the end, since it's a catchall
allstores . remove ( GenericDistro )
allstores . append ( GenericDistro )
2013-09-27 01:44:30 +04:00
return allstores
_allstores = _build_distro_list ( )