2013-03-18 01:06:52 +04:00
#
2013-10-28 00:59:47 +04:00
# Copyright 2006, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Jeremy Katz <katzj@redhat.com>
#
2018-03-20 22:00:02 +03:00
# This work is licensed under the GNU GPLv2.
# See the COPYING file in the top-level directory.
2013-04-11 18:27:02 +04:00
#
2013-03-18 01:06:52 +04:00
2013-04-11 18:27:02 +04:00
import logging
import os
2013-03-18 01:06:52 +04:00
import random
import re
2015-09-06 21:26:50 +03:00
import sys
2013-03-18 01:06:52 +04:00
import libvirt
2013-04-11 18:27:02 +04:00
2013-04-13 22:34:52 +04:00
2013-04-11 18:27:02 +04:00
def listify ( l ) :
if l is None :
return [ ]
2017-10-11 14:35:41 +03:00
elif not isinstance ( l , list ) :
2013-04-13 22:34:52 +04:00
return [ l ]
2013-04-11 18:27:02 +04:00
else :
return l
2013-04-13 22:34:52 +04:00
2018-03-20 17:35:22 +03:00
def xml_indent ( xmlstr , level ) :
"""
Indent the passed str the specified number of spaces
"""
xml = " "
if not xmlstr :
return xml
if not level :
return xmlstr
return " \n " . join ( ( " " * level + l ) for l in xmlstr . splitlines ( ) )
2013-04-11 18:27:02 +04:00
def vm_uuid_collision ( conn , uuid ) :
"""
Check if passed UUID string is in use by another guest of the connection
Returns true / false
"""
return libvirt_collision ( conn . lookupByUUIDString , uuid )
2013-04-13 22:34:52 +04:00
2013-04-11 18:27:02 +04:00
def libvirt_collision ( collision_cb , val ) :
"""
Run the passed collision function with val as the only argument :
If libvirtError is raised , return False
If no libvirtError raised , return True
"""
check = False
if val is not None :
try :
if collision_cb ( val ) is not None :
check = True
except libvirt . libvirtError :
pass
return check
2013-04-13 22:34:52 +04:00
2013-04-11 18:27:02 +04:00
def validate_uuid ( val ) :
2017-10-11 14:35:41 +03:00
if not isinstance ( val , str ) :
2013-04-11 18:27:02 +04:00
raise ValueError ( _ ( " UUID must be a string. " ) )
form = re . match ( " [a-fA-F0-9] {8} [-]([a-fA-F0-9] {4} [-]) {3} [a-fA-F0-9] {12} $ " ,
val )
if form is None :
form = re . match ( " [a-fA-F0-9] {32} $ " , val )
if form is None :
raise ValueError (
_ ( " UUID must be a 32-digit hexadecimal number. It may take "
2013-04-17 17:09:53 +04:00
" the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx or may "
" omit hyphens altogether. " ) )
2013-04-11 18:27:02 +04:00
else : # UUID had no dashes, so add them in
val = ( val [ 0 : 8 ] + " - " + val [ 8 : 12 ] + " - " + val [ 12 : 16 ] +
" - " + val [ 16 : 20 ] + " - " + val [ 20 : 32 ] )
return val
2013-04-13 22:34:52 +04:00
2014-01-18 03:42:15 +04:00
def validate_name ( name_type , val ) :
2014-02-01 17:37:28 +04:00
# Rather than try and match libvirt's regex, just forbid things we
# know don't work
forbid = [ " " ]
2015-04-03 00:54:47 +03:00
if not val :
raise ValueError (
_ ( " A name must be specified for the %s " ) % name_type )
2014-02-01 17:37:28 +04:00
for c in forbid :
2014-02-06 03:58:55 +04:00
if c not in val :
continue
raise ValueError (
_ ( " %s name ' %s ' can not contain ' %s ' character. " ) %
( name_type , val , c ) )
2013-04-11 18:27:02 +04:00
2013-04-13 22:34:52 +04:00
2013-04-11 18:27:02 +04:00
def validate_macaddr ( val ) :
if val is None :
return
2017-10-11 14:35:41 +03:00
if not isinstance ( val , str ) :
2013-04-11 18:27:02 +04:00
raise ValueError ( _ ( " MAC address must be a string. " ) )
form = re . match ( " ^([0-9a-fA-F] { 1,2}:) {5} [0-9a-fA-F] { 1,2}$ " , val )
if form is None :
raise ValueError ( _ ( " MAC address must be of the format "
2014-02-06 04:09:26 +04:00
" AA:BB:CC:DD:EE:FF, was ' %s ' " ) % val )
2013-04-13 22:34:52 +04:00
2013-04-11 18:27:02 +04:00
def generate_name ( base , collision_cb , suffix = " " , lib_collision = True ,
2013-10-05 18:22:27 +04:00
start_num = 1 , sep = " - " , force_num = False , collidelist = None ) :
2013-04-11 18:27:02 +04:00
"""
Generate a new name from the passed base string , verifying it doesn ' t
collide with the collision callback .
This can be used to generate disk path names from the parent VM or pool
name . Names generated look like ' base-#suffix ' , ex :
If foobar , and foobar - 1. img already exist , and :
base = " foobar "
suffix = " .img "
output = " foobar-2.img "
2018-02-14 15:17:31 +03:00
: param base : The base string to use for the name ( e . g . " my-orig-vm-clone " )
: param collision_cb : A callback function to check for collision ,
2013-10-01 23:18:34 +04:00
receives the generated name as its only arg
2018-02-14 15:17:31 +03:00
: param lib_collision : If true , the collision_cb is not a boolean function ,
2013-10-01 23:18:34 +04:00
and instead throws a libvirt error on failure
2018-02-14 15:17:31 +03:00
: param start_num : The number to start at for generating non colliding names
: param sep : The seperator to use between the basename and the
2013-10-01 23:18:34 +04:00
generated number ( default is " - " )
2018-02-14 15:17:31 +03:00
: param force_num : Force the generated name to always end with a number
: param collidelist : An extra list of names to check for collision
2013-04-11 18:27:02 +04:00
"""
collidelist = collidelist or [ ]
def collide ( n ) :
if n in collidelist :
return True
if lib_collision :
return libvirt_collision ( collision_cb , tryname )
else :
return collision_cb ( tryname )
2017-10-11 14:35:57 +03:00
numrange = list ( range ( start_num , start_num + 100000 ) )
2013-10-05 18:22:27 +04:00
if not force_num :
numrange = [ None ] + numrange
for i in numrange :
2013-04-11 18:27:02 +04:00
tryname = base
2013-10-05 18:22:27 +04:00
if i is not None :
2013-04-11 18:27:02 +04:00
tryname + = ( " %s %d " % ( sep , i ) )
tryname + = suffix
if not collide ( tryname ) :
return tryname
raise ValueError ( _ ( " Name generation range exceeded. " ) )
def generate_uuid ( conn ) :
for ignore in range ( 256 ) :
2014-02-06 04:09:26 +04:00
uuid = randomUUID ( conn )
2013-04-11 18:27:02 +04:00
if not vm_uuid_collision ( conn , uuid ) :
return uuid
logging . error ( " Failed to generate non-conflicting UUID " )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-04-13 23:48:06 +04:00
def randomUUID ( conn ) :
2015-09-06 17:36:17 +03:00
if conn . fake_conn_predictable ( ) :
2013-03-18 01:06:52 +04:00
# Testing hack
return " 00000000-1111-2222-3333-444444444444 "
2013-04-13 23:48:06 +04:00
u = [ random . randint ( 0 , 255 ) for ignore in range ( 0 , 16 ) ]
2013-08-16 12:02:56 +04:00
u [ 6 ] = ( u [ 6 ] & 0x0F ) | ( 4 << 4 )
u [ 8 ] = ( u [ 8 ] & 0x3F ) | ( 2 << 6 )
2013-03-18 01:06:52 +04:00
return " - " . join ( [ " %02x " * 4 , " %02x " * 2 , " %02x " * 2 , " %02x " * 2 ,
" %02x " * 6 ] ) % tuple ( u )
2013-04-12 16:26:21 +04:00
def xml_escape ( xml ) :
"""
Replaces chars ' " < > & with xml safe counterparts
"""
if xml is None :
2013-03-18 01:06:52 +04:00
return None
2013-04-12 16:26:21 +04:00
xml = xml . replace ( " & " , " & " )
xml = xml . replace ( " ' " , " ' " )
xml = xml . replace ( " \" " , " " " )
xml = xml . replace ( " < " , " < " )
xml = xml . replace ( " > " , " > " )
return xml
2013-03-18 01:06:52 +04:00
2013-07-06 19:20:28 +04:00
def is_error_nosupport ( err ) :
"""
Check if passed exception indicates that the called libvirt command isn ' t
supported
2018-02-14 15:17:31 +03:00
: param err : Exception raised from command call
: returns : True if command isn ' t supported, False if we can ' t determine
2013-07-06 19:20:28 +04:00
"""
if not isinstance ( err , libvirt . libvirtError ) :
return False
if ( err . get_error_code ( ) == libvirt . VIR_ERR_RPC or
err . get_error_code ( ) == libvirt . VIR_ERR_NO_SUPPORT ) :
return True
return False
2013-07-06 23:39:00 +04:00
2014-01-27 02:42:24 +04:00
def exception_is_libvirt_error ( e , error ) :
return ( hasattr ( libvirt , error ) and
e . get_error_code ( ) == getattr ( libvirt , error ) )
2013-07-06 23:39:00 +04:00
def local_libvirt_version ( ) :
"""
Lookup the local libvirt library version , but cache the value since
it never changes .
"""
key = " __virtinst_cached_getVersion "
if not hasattr ( libvirt , key ) :
setattr ( libvirt , key , libvirt . getVersion ( ) )
return getattr ( libvirt , key )
2013-07-07 21:53:37 +04:00
2013-07-17 15:53:47 +04:00
def get_system_scratchdir ( hvtype ) :
2014-02-10 23:47:20 +04:00
if " VIRTINST_TEST_SUITE " in os . environ :
return os . getcwd ( )
2013-07-17 15:53:47 +04:00
if hvtype == " test " :
return " /tmp "
elif hvtype == " xen " :
return " /var/lib/xen "
else :
return " /var/lib/libvirt/boot "
def make_scratchdir ( conn , hvtype ) :
scratch = None
if not conn . is_session_uri ( ) :
scratch = get_system_scratchdir ( hvtype )
if ( not scratch or
not os . path . exists ( scratch ) or
not os . access ( scratch , os . W_OK ) ) :
2013-10-01 16:28:15 +04:00
scratch = os . path . join ( get_cache_dir ( ) , " boot " )
2013-07-17 15:53:47 +04:00
if not os . path . exists ( scratch ) :
2017-05-05 20:05:30 +03:00
os . makedirs ( scratch , 0o751 )
2013-07-17 15:53:47 +04:00
return scratch
2013-08-09 17:23:01 +04:00
def pretty_mem ( val ) :
val = int ( val )
if val > ( 10 * 1024 * 1024 ) :
2014-06-16 07:56:02 +04:00
return " %2.2f GiB " % ( val / ( 1024.0 * 1024.0 ) )
2013-08-09 17:23:01 +04:00
else :
2014-06-16 07:56:02 +04:00
return " %2.0f MiB " % ( val / 1024.0 )
2013-08-09 17:23:01 +04:00
def pretty_bytes ( val ) :
val = int ( val )
if val > ( 1024 * 1024 * 1024 ) :
2014-06-16 07:56:02 +04:00
return " %2.2f GiB " % ( val / ( 1024.0 * 1024.0 * 1024.0 ) )
2013-08-09 17:23:01 +04:00
else :
2014-06-16 07:56:02 +04:00
return " %2.2f MiB " % ( val / ( 1024.0 * 1024.0 ) )
2013-10-01 16:28:15 +04:00
def get_cache_dir ( ) :
ret = " "
try :
# We don't want to depend on glib for virt-install
2014-04-03 02:39:43 +04:00
from gi . repository import GLib
2013-10-01 16:28:15 +04:00
ret = GLib . get_user_cache_dir ( )
except ImportError :
pass
if not ret :
ret = os . environ . get ( " XDG_CACHE_HOME " )
if not ret :
ret = os . path . expanduser ( " ~/.cache " )
return os . path . join ( ret , " virt-manager " )
2014-01-24 14:40:08 +04:00
2014-09-20 19:37:23 +04:00
def register_libvirt_error_handler ( ) :
"""
Ignore libvirt error reporting , we just use exceptions
"""
def libvirt_callback ( userdata , err ) :
ignore = userdata
ignore = err
libvirt . registerErrorHandler ( f = libvirt_callback , ctx = None )
2015-09-06 21:26:50 +03:00
def ensure_meter ( meter ) :
if meter :
return meter
return make_meter ( quiet = True )
def make_meter ( quiet ) :
2015-09-19 03:28:55 +03:00
from virtinst import progress
2015-09-06 21:26:50 +03:00
if quiet :
return progress . BaseMeter ( )
return progress . TextMeter ( fo = sys . stdout )