2013-03-18 01:06:52 +04:00
#
# Utility functions used for guest installation
#
# Copyright 2006 Red Hat, Inc.
# Jeremy Katz <katzj@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.
#
# WARNING: the contents of this file, somewhat unfortunately, are legacy
# API. No incompatible changes are allowed to this file, and no new
# code should be added here (utility functions live in _util.py).
# Clients of virtinst shouldn't use these functions: if you think you
# need to, tell us why.
#
import platform
import random
import os . path
import re
import libxml2
import logging
import subprocess
import libvirt
import virtinst
import CapabilitiesParser
import User
import support
KEYBOARD_DIR = " /etc/sysconfig/keyboard "
XORG_CONF = " /etc/X11/xorg.conf "
CONSOLE_SETUP_CONF = " /etc/default/console-setup "
def default_route ( nic = None ) :
if platform . system ( ) == ' SunOS ' :
cmd = [ ' /usr/bin/netstat ' , ' -rn ' ]
if nic :
cmd + = [ ' -I ' , nic ]
proc = subprocess . Popen ( cmd , stdout = subprocess . PIPE ,
stderr = subprocess . PIPE )
for line in proc . stdout . readlines ( ) :
vals = line . split ( )
if len ( vals ) > 1 and vals [ 0 ] == ' default ' :
return vals [ 1 ]
return None
route_file = " /proc/net/route "
d = file ( route_file )
defn = 0
for line in d . xreadlines ( ) :
info = line . split ( )
if ( len ( info ) != 11 ) : # 11 = typical num of fields in the file
logging . warn ( _ ( " Invalid line length while parsing %s . " ) ,
route_file )
logging . warn ( _ ( " Defaulting bridge to xenbr %d " ) , defn )
break
try :
route = int ( info [ 1 ] , 16 )
if route == 0 :
return info [ 0 ]
except ValueError :
continue
return None
def default_bridge ( ) :
ret = virtinst . _util . default_bridge2 ( None )
if not ret :
# Maintain this behavior for back compat
ret = " xenbr0 "
else :
ret = ret [ 1 ]
return ret
def default_network ( conn ) :
ret = virtinst . _util . default_bridge2 ( conn )
if not ret :
# FIXME: Check that this exists
ret = [ " network " , " default " ]
return ret
def default_connection ( ) :
if os . path . exists ( ' /var/lib/xend ' ) :
if os . path . exists ( ' /dev/xen/evtchn ' ) :
return ' xen '
if os . path . exists ( " /proc/xen " ) :
return ' xen '
if os . path . exists ( " /usr/bin/qemu " ) or \
os . path . exists ( " /usr/bin/qemu-kvm " ) or \
os . path . exists ( " /usr/bin/kvm " ) or \
os . path . exists ( " /usr/bin/xenner " ) :
if User . User . current ( ) . has_priv ( User . User . PRIV_QEMU_SYSTEM ) :
return " qemu:///system "
else :
return " qemu:///session "
return None
def get_cpu_flags ( ) :
if platform . system ( ) == ' SunOS ' :
raise OSError ( ' CPU flags not available ' )
f = open ( " /proc/cpuinfo " )
lines = f . readlines ( )
f . close ( )
for line in lines :
if not line . startswith ( " flags " ) :
continue
# get the actual flags
flags = line [ : - 1 ] . split ( " : " , 1 ) [ 1 ]
# and split them
flst = flags . split ( " " )
return flst
return [ ]
def is_pae_capable ( conn = None ) :
""" Determine if a machine is PAE capable or not. """
if not conn :
conn = libvirt . open ( ' ' )
return " pae " in conn . getCapabilities ( )
def is_hvm_capable ( ) :
""" Determine if a machine is HVM capable or not. """
if platform . system ( ) == ' SunOS ' :
raise OSError ( ' HVM capability not determinible ' )
caps = " "
if os . path . exists ( " /sys/hypervisor/properties/capabilities " ) :
caps = open ( " /sys/hypervisor/properties/capabilities " ) . read ( )
if caps . find ( " hvm " ) != - 1 :
return True
return False
def is_kqemu_capable ( ) :
return os . path . exists ( " /dev/kqemu " )
def is_kvm_capable ( ) :
return os . path . exists ( " /dev/kvm " )
def is_blktap_capable ( ) :
if platform . system ( ) == ' SunOS ' :
return False
#return os.path.exists("/dev/xen/blktapctrl")
f = open ( " /proc/modules " )
lines = f . readlines ( )
f . close ( )
for line in lines :
if line . startswith ( " blktap " ) or line . startswith ( " xenblktap " ) :
return True
return False
def get_default_arch ( ) :
arch = os . uname ( ) [ 4 ]
if arch == " x86_64 " :
return " x86_64 "
return " i686 "
# this function is directly from xend/server/netif.py and is thus
# available under the LGPL,
# Copyright 2004, 2005 Mike Wray <mike.wray@hp.com>
# Copyright 2005 XenSource Ltd
def randomMAC ( type = " xen " , conn = None ) :
""" Generate a random MAC address.
00 - 16 - 3 E allocated to xensource
52 - 54 - 00 used by qemu / kvm
The OUI list is available at http : / / standards . ieee . org / regauth / oui / oui . txt .
The remaining 3 fields are random , with the first bit of the first
random field set 0.
>> > randomMAC ( ) . startswith ( " 00:16:3E " )
True
>> > randomMAC ( " foobar " ) . startswith ( " 00:16:3E " )
True
>> > randomMAC ( " xen " ) . startswith ( " 00:16:3E " )
True
>> > randomMAC ( " qemu " ) . startswith ( " 52:54:00 " )
True
@return : MAC address string
"""
if conn and hasattr ( conn , " _virtinst__fake_conn_predictable " ) :
# Testing hack
return " 00:11:22:33:44:55 "
ouis = { ' xen ' : [ 0x00 , 0x16 , 0x3E ] , ' qemu ' : [ 0x52 , 0x54 , 0x00 ] }
try :
oui = ouis [ type ]
except KeyError :
oui = ouis [ ' xen ' ]
mac = oui + [
random . randint ( 0x00 , 0xff ) ,
random . randint ( 0x00 , 0xff ) ,
random . randint ( 0x00 , 0xff ) ]
return ' : ' . join ( map ( lambda x : " %02x " % x , mac ) )
# the following three functions are from xend/uuid.py and are thus
# available under the LGPL,
# Copyright 2005 Mike Wray <mike.wray@hp.com>
# Copyright 2005 XenSource Ltd
def randomUUID ( ) :
""" Generate a random UUID. """
return [ random . randint ( 0 , 255 ) for dummy in range ( 0 , 16 ) ]
def uuidToString ( u , conn = None ) :
if conn and hasattr ( conn , " _virtinst__fake_conn_predictable " ) :
# Testing hack
return " 00000000-1111-2222-3333-444444444444 "
return " - " . join ( [ " %02x " * 4 , " %02x " * 2 , " %02x " * 2 , " %02x " * 2 ,
" %02x " * 6 ] ) % tuple ( u )
def uuidFromString ( s ) :
s = s . replace ( ' - ' , ' ' )
return [ int ( s [ i : i + 2 ] , 16 ) for i in range ( 0 , 32 , 2 ) ]
# the following function quotes from python2.5/uuid.py
def get_host_network_devices ( ) :
device = [ ]
for dirname in [ ' ' , ' /sbin/ ' , ' /usr/sbin ' ] :
executable = os . path . join ( dirname , " ifconfig " )
if not os . path . exists ( executable ) :
continue
try :
cmd = ' LC_ALL=C %s -a 2>/dev/null ' % ( executable )
pipe = os . popen ( cmd )
except IOError :
continue
for line in pipe :
if line . find ( " encap:Ethernet " ) > 0 :
words = line . lower ( ) . split ( )
for i in range ( len ( words ) ) :
if words [ i ] == " hwaddr " :
device . append ( words )
return device
def get_max_vcpus ( conn , type = None ) :
""" @param conn: libvirt connection to poll for max possible vcpus
@type type : optional guest type ( kvm , etc . ) """
if type is None :
type = conn . getType ( )
try :
m = conn . getMaxVcpus ( type . lower ( ) )
except libvirt . libvirtError :
m = 32
return m
def get_phy_cpus ( conn ) :
""" Get number of physical CPUs. """
hostinfo = conn . getInfo ( )
pcpus = hostinfo [ 4 ] * hostinfo [ 5 ] * hostinfo [ 6 ] * hostinfo [ 7 ]
return pcpus
def system ( cmd ) :
st = os . system ( cmd )
if os . WIFEXITED ( st ) and os . WEXITSTATUS ( st ) != 0 :
raise OSError ( " Failed to run %s , exited with %d " %
( cmd , os . WEXITSTATUS ( st ) ) )
def xml_escape ( str ) :
""" Replaces chars ' " < > & with xml safe counterparts """
if str is None :
return None
str = str . replace ( " & " , " & " )
str = str . replace ( " ' " , " ' " )
str = str . replace ( " \" " , " " " )
str = str . replace ( " < " , " < " )
str = str . replace ( " > " , " > " )
return str
def compareMAC ( p , q ) :
""" Compare two MAC addresses """
pa = p . split ( " : " )
qa = q . split ( " : " )
if len ( pa ) != len ( qa ) :
if p > q :
return 1
else :
return - 1
for i in xrange ( len ( pa ) ) :
n = int ( pa [ i ] , 0x10 ) - int ( qa [ i ] , 0x10 )
if n > 0 :
return 1
elif n < 0 :
return - 1
return 0
def _xorg_keymap ( ) :
""" Look in /etc/X11/xorg.conf for the host machine ' s keymap, and attempt to
map it to a keymap supported by qemu """
kt = None
try :
f = open ( XORG_CONF , " r " )
except IOError , e :
logging . debug ( ' Could not open " %s " : %s ' , XORG_CONF , str ( e ) )
else :
keymap_re = re . compile ( r ' \ s*Option \ s+ " XkbLayout " \ s+ " (?P<kt>[a-z-]+) " ' )
for line in f :
m = keymap_re . match ( line )
if m :
kt = m . group ( ' kt ' )
break
else :
logging . debug ( " Didn ' t find keymap in ' %s ' ! " , XORG_CONF )
f . close ( )
return kt
def _console_setup_keymap ( ) :
""" Look in /etc/default/console-setup for the host machine ' s keymap, and attempt to
map it to a keymap supported by qemu """
return virtinst . _util . find_xkblayout ( CONSOLE_SETUP_CONF )
def default_keymap ( ) :
""" Look in /etc/sysconfig for the host machine ' s keymap, and attempt to
map it to a keymap supported by qemu """
# Set keymap to same as hosts
default = " en-us "
keymap = None
kt = None
try :
f = open ( KEYBOARD_DIR , " r " )
except IOError , e :
logging . debug ( ' Could not open " /etc/sysconfig/keyboard " ' + str ( e ) )
kt = _xorg_keymap ( )
if not kt :
kt = virtinst . _util . find_keymap_from_etc_default ( )
else :
while 1 :
s = f . readline ( )
if s == " " :
break
if re . search ( " KEYTABLE " , s ) != None or \
( re . search ( " KEYBOARD " , s ) != None and
re . search ( " KEYBOARDTYPE " , s ) == None ) :
if s . count ( ' " ' ) :
delim = ' " '
elif s . count ( ' = ' ) :
delim = ' = '
else :
continue
kt = s . split ( delim ) [ 1 ] . strip ( )
f . close ( )
if kt == None :
logging . debug ( " Did not parse any usable keymapping. " )
return default
kt = kt . lower ( )
keymap = check_keytable ( kt )
if not keymap :
logging . debug ( " Didn ' t match keymap ' %s ' in keytable! " , kt )
return default
return keymap
def pygrub_path ( conn = None ) :
"""
Return the pygrub path for the current host , or connection if
available .
"""
# FIXME: This should be removed/deprecated when capabilities are
# fixed to provide bootloader info
if conn :
cap = CapabilitiesParser . parse ( conn . getCapabilities ( ) )
if ( cap . host . arch == " i86pc " ) :
return " /usr/lib/xen/bin/pygrub "
else :
return " /usr/bin/pygrub "
if platform . system ( ) == " SunOS " :
return " /usr/lib/xen/bin/pygrub "
return " /usr/bin/pygrub "
def uri_split ( uri ) :
"""
Parse a libvirt hypervisor uri into it ' s individual parts
@returns : tuple of the form ( scheme ( ex . ' qemu ' , ' xen+ssh ' ) , username ,
hostname , path ( ex . ' /system ' ) , query ,
fragment )
"""
def splitnetloc ( url , start = 0 ) :
for c in ' /?# ' : # the order is important!
delim = url . find ( c , start )
if delim > = 0 :
break
else :
delim = len ( url )
return url [ start : delim ] , url [ delim : ]
username = netloc = query = fragment = ' '
i = uri . find ( " : " )
if i > 0 :
scheme , uri = uri [ : i ] . lower ( ) , uri [ i + 1 : ]
if uri [ : 2 ] == ' // ' :
netloc , uri = splitnetloc ( uri , 2 )
offset = netloc . find ( " @ " )
if offset > 0 :
username = netloc [ 0 : offset ]
netloc = netloc [ offset + 1 : ]
if ' # ' in uri :
uri , fragment = uri . split ( ' # ' , 1 )
if ' ? ' in uri :
uri , query = uri . split ( ' ? ' , 1 )
else :
scheme = uri . lower ( )
return scheme , username , netloc , uri , query , fragment
def is_uri_remote ( uri , conn = None ) :
if conn and hasattr ( conn , " _virtinst__fake_conn_remote " ) :
# Testing hack
return True
try :
split_uri = uri_split ( uri )
netloc = split_uri [ 2 ]
if netloc == " " :
return False
return True
except Exception , e :
logging . exception ( " Error parsing URI in is_remote: %s " , e )
return True
def get_uri_hostname ( uri ) :
try :
split_uri = uri_split ( uri )
netloc = split_uri [ 2 ]
if netloc != " " :
return netloc
except Exception , e :
logging . warning ( " Cannot parse URI %s : %s " , uri , str ( e ) )
return " localhost "
def get_uri_transport ( uri ) :
try :
split_uri = uri_split ( uri )
scheme = split_uri [ 0 ]
username = split_uri [ 1 ]
if scheme :
offset = scheme . index ( " + " )
if offset > 0 :
return [ scheme [ offset + 1 : ] , username ]
except :
pass
return [ None , None ]
def get_uri_driver ( uri ) :
try :
split_uri = uri_split ( uri )
scheme = split_uri [ 0 ]
if scheme :
offset = scheme . find ( " + " )
if offset > 0 :
return scheme [ : offset ]
return scheme
except Exception :
pass
return " xen "
def is_storage_capable ( conn ) :
""" check if virConnectPtr passed has storage API support """
return support . check_conn_support ( conn , support . SUPPORT_CONN_STORAGE )
def get_xml_path ( xml , path = None , func = None ) :
"""
Return the content from the passed xml xpath , or return the result
of a passed function ( receives xpathContext as its only arg )
"""
doc = None
ctx = None
result = None
try :
doc = libxml2 . parseDoc ( xml )
ctx = doc . xpathNewContext ( )
if path :
ret = ctx . xpathEval ( path )
if ret != None :
if type ( ret ) == list :
if len ( ret ) > = 1 :
result = ret [ 0 ] . content
else :
result = ret
elif func :
result = func ( ctx )
else :
raise ValueError ( _ ( " ' path ' or ' func ' is required. " ) )
finally :
if doc :
doc . freeDoc ( )
if ctx :
ctx . xpathFreeContext ( )
return result
def lookup_pool_by_path ( conn , path ) :
"""
Return the first pool with matching matching target path .
return the first we find , active or inactive . This iterates over
all pools and dumps their xml , so it is NOT quick .
Favor running pools over inactive pools .
@returns : virStoragePool object if found , None otherwise
"""
if not is_storage_capable ( conn ) :
return None
def check_pool ( poolname , path ) :
pool = conn . storagePoolLookupByName ( poolname )
xml_path = get_xml_path ( pool . XMLDesc ( 0 ) , " /pool/target/path " )
2013-03-26 04:03:08 +04:00
if xml_path is not None and os . path . abspath ( xml_path ) == path :
2013-03-18 01:06:52 +04:00
return pool
running_list = conn . listStoragePools ( )
inactive_list = conn . listDefinedStoragePools ( )
for plist in [ running_list , inactive_list ] :
for name in plist :
p = check_pool ( name , path )
if p :
return p
return None
def check_keytable ( kt ) :
import keytable
keymap = None
# Try a simple lookup in the keytable
if kt . lower ( ) in keytable . keytable :
return keytable . keytable [ kt ]
else :
# Try a more intelligent lookup: strip out all '-' and '_', sort
# the keytable keys putting the longest first, then compare
# by string prefix
def len_cmp ( a , b ) :
return len ( b ) - len ( a )
clean_kt = kt . replace ( " - " , " " ) . replace ( " _ " , " " )
sorted_keys = sorted ( keytable . keytable . keys ( ) , len_cmp )
for key in sorted_keys :
origkey = key
key = key . replace ( " - " , " " ) . replace ( " _ " , " " )
if clean_kt . startswith ( key ) :
return keytable . keytable [ origkey ]
return keymap
def _test ( ) :
import doctest
doctest . testmod ( )
if __name__ == " __main__ " :
_test ( )