2013-03-18 01:06:52 +04:00
#
# Classes for building disk device xml
#
2013-10-28 00:59:47 +04:00
# Copyright 2006-2008, 2012-2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# 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
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.
import os
import stat
import pwd
import subprocess
import logging
import re
import urlgrabber . progress as progress
2013-07-13 18:09:00 +04:00
from virtinst import diskbackend
2013-04-11 03:48:07 +04:00
from virtinst import util
2013-08-09 04:47:17 +04:00
from virtinst import VirtualDevice
2013-07-14 02:56:09 +04:00
from virtinst . xmlbuilder import XMLProperty
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _qemu_sanitize_drvtype ( phystype , fmt , manual_format = False ) :
"""
Sanitize libvirt storage volume format to a valid qemu driver type
"""
2013-04-13 22:34:52 +04:00
raw_list = [ " iso " ]
2013-03-18 01:06:52 +04:00
if phystype == VirtualDisk . TYPE_BLOCK :
if not fmt :
return VirtualDisk . DRIVER_QEMU_RAW
if fmt and not manual_format :
return VirtualDisk . DRIVER_QEMU_RAW
if fmt in raw_list :
return VirtualDisk . DRIVER_QEMU_RAW
return fmt
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _name_uid ( user ) :
"""
Return UID for string username
"""
pwdinfo = pwd . getpwnam ( user )
return pwdinfo [ 2 ]
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _is_dir_searchable ( uid , username , path ) :
"""
Check if passed directory is searchable by uid
"""
try :
statinfo = os . stat ( path )
except OSError :
return False
if uid == statinfo . st_uid :
flag = stat . S_IXUSR
elif uid == statinfo . st_gid :
flag = stat . S_IXGRP
else :
flag = stat . S_IXOTH
if bool ( statinfo . st_mode & flag ) :
return True
# Check POSIX ACL (since that is what we use to 'fix' access)
cmd = [ " getfacl " , path ]
try :
proc = subprocess . Popen ( cmd ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE )
out , err = proc . communicate ( )
except OSError :
logging . debug ( " Didn ' t find the getfacl command. " )
return False
if proc . returncode != 0 :
logging . debug ( " Cmd ' %s ' failed: %s " , cmd , err )
return False
return bool ( re . search ( " user: %s :..x " % username , out ) )
2013-04-13 22:34:52 +04:00
2013-07-13 18:09:00 +04:00
def _distill_storage ( conn , do_create , nomanaged ,
2013-09-29 19:23:59 +04:00
path , vol_object , vol_install ,
clone_path , backing_store ,
* args ) :
2013-03-18 01:06:52 +04:00
"""
2013-07-13 18:09:00 +04:00
Validates and updates params when the backing storage is changed
2013-03-18 01:06:52 +04:00
"""
pool = None
path_is_pool = False
2013-10-06 18:08:04 +04:00
storage_capable = conn . check_support ( conn . SUPPORT_CONN_STORAGE )
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
if vol_object :
pass
elif not storage_capable :
pass
elif path and not nomanaged :
path = os . path . abspath ( path )
vol_object , pool , path_is_pool = diskbackend . check_if_path_managed (
conn , path )
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
creator = None
backend = diskbackend . StorageBackend ( conn , path , vol_object ,
path_is_pool and pool or None )
if not do_create :
return backend , None
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
if backend . exists ( ) and path is not None :
if vol_install :
raise ValueError ( " vol_install specified but %s exists. " %
backend . path )
elif not clone_path :
return backend , None
2013-04-13 22:34:52 +04:00
2013-07-13 18:09:00 +04:00
if path or vol_install or pool or clone_path :
creator = diskbackend . StorageCreator ( conn , path , pool ,
2013-09-29 19:23:59 +04:00
vol_install , clone_path ,
backing_store , * args )
2013-07-13 18:09:00 +04:00
return backend , creator
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
_TARGET_PROPS = [ " file " , " dev " , " dir " ]
2013-03-18 01:06:52 +04:00
class VirtualDisk ( VirtualDevice ) :
2013-07-16 17:14:37 +04:00
virtual_device_type = VirtualDevice . VIRTUAL_DEV_DISK
2013-03-18 01:06:52 +04:00
DRIVER_FILE = " file "
DRIVER_PHY = " phy "
DRIVER_TAP = " tap "
DRIVER_QEMU = " qemu "
driver_names = [ DRIVER_FILE , DRIVER_PHY , DRIVER_TAP , DRIVER_QEMU ]
DRIVER_QEMU_RAW = " raw "
# No list here, since there are many other valid values
DRIVER_TAP_RAW = " aio "
DRIVER_TAP_QCOW = " qcow "
DRIVER_TAP_VMDK = " vmdk "
DRIVER_TAP_VDISK = " vdisk "
driver_types = [ DRIVER_TAP_RAW , DRIVER_TAP_QCOW ,
DRIVER_TAP_VMDK , DRIVER_TAP_VDISK ]
CACHE_MODE_NONE = " none "
CACHE_MODE_WRITETHROUGH = " writethrough "
CACHE_MODE_WRITEBACK = " writeback "
2013-09-30 21:56:42 +04:00
CACHE_MODE_DIRECTSYNC = " directsync "
CACHE_MODE_UNSAFE = " unsafe "
2013-03-18 01:06:52 +04:00
cache_types = [ CACHE_MODE_NONE , CACHE_MODE_WRITETHROUGH ,
2013-09-30 21:56:42 +04:00
CACHE_MODE_WRITEBACK , CACHE_MODE_DIRECTSYNC , CACHE_MODE_UNSAFE ]
2013-03-18 01:06:52 +04:00
DEVICE_DISK = " disk "
DEVICE_LUN = " lun "
DEVICE_CDROM = " cdrom "
DEVICE_FLOPPY = " floppy "
devices = [ DEVICE_DISK , DEVICE_LUN , DEVICE_CDROM , DEVICE_FLOPPY ]
TYPE_FILE = " file "
TYPE_BLOCK = " block "
TYPE_DIR = " dir "
types = [ TYPE_FILE , TYPE_BLOCK , TYPE_DIR ]
IO_MODE_NATIVE = " native "
IO_MODE_THREADS = " threads "
io_modes = [ IO_MODE_NATIVE , IO_MODE_THREADS ]
2013-08-17 18:35:44 +04:00
error_policies = [ " ignore " , " stop " , " enospace " , " report " ]
2013-03-18 01:06:52 +04:00
@staticmethod
def disk_type_to_xen_driver_name ( disk_type ) :
"""
Convert a value of VirtualDisk . type to it ' s associated Xen
< driver name = / > property
"""
if disk_type == VirtualDisk . TYPE_BLOCK :
return " phy "
elif disk_type == VirtualDisk . TYPE_FILE :
return " file "
return " file "
@staticmethod
def disk_type_to_target_prop ( disk_type ) :
"""
Convert a value of VirtualDisk . type to it ' s associated XML
target property name
"""
if disk_type == VirtualDisk . TYPE_FILE :
return " file "
elif disk_type == VirtualDisk . TYPE_BLOCK :
return " dev "
elif disk_type == VirtualDisk . TYPE_DIR :
return " dir "
return " file "
@staticmethod
def path_exists ( conn , path ) :
"""
Check if path exists . If we can ' t determine, return False
"""
2014-02-04 01:43:25 +04:00
if path is None :
return False
2013-03-18 01:06:52 +04:00
try :
vol = None
path_is_pool = False
try :
2013-07-13 18:09:00 +04:00
vol , ignore , path_is_pool = diskbackend . check_if_path_managed (
conn , path )
2013-03-18 01:06:52 +04:00
except :
pass
if vol or path_is_pool :
return True
2013-07-06 04:36:28 +04:00
if not conn . is_remote ( ) :
2013-03-18 01:06:52 +04:00
return os . path . exists ( path )
except :
pass
return False
@staticmethod
def check_path_search_for_user ( conn , path , username ) :
"""
Check if the passed user has search permissions for all the
directories in the disk path .
@return : List of the directories the user cannot search , or empty list
@rtype : C { list }
"""
if path is None :
return [ ]
2013-07-06 04:36:28 +04:00
if conn . is_remote ( ) :
2013-03-18 01:06:52 +04:00
return [ ]
2013-11-10 20:09:22 +04:00
if username == " root " :
return [ ]
2013-03-18 01:06:52 +04:00
try :
uid = _name_uid ( username )
except Exception , e :
logging . debug ( " Error looking up username: %s " , str ( e ) )
return [ ]
fixlist = [ ]
if os . path . isdir ( path ) :
dirname = path
base = " - "
else :
dirname , base = os . path . split ( path )
while base :
if not _is_dir_searchable ( uid , username , dirname ) :
fixlist . append ( dirname )
dirname , base = os . path . split ( dirname )
return fixlist
@staticmethod
def fix_path_search_for_user ( conn , path , username ) :
"""
Try to fix any permission problems found by check_path_search_for_user
2013-04-13 22:34:52 +04:00
@return : Return a dictionary of entries { broken path : error msg }
2013-03-18 01:06:52 +04:00
@rtype : C { dict }
"""
def fix_perms ( dirname , useacl = True ) :
if useacl :
cmd = [ " setfacl " , " --modify " , " user: %s :x " % username , dirname ]
proc = subprocess . Popen ( cmd ,
stdout = subprocess . PIPE ,
stderr = subprocess . PIPE )
out , err = proc . communicate ( )
logging . debug ( " Ran command ' %s ' " , cmd )
if out or err :
logging . debug ( " out= %s \n err= %s " , out , err )
if proc . returncode != 0 :
raise ValueError ( err )
else :
logging . debug ( " Setting +x on %s " , dirname )
mode = os . stat ( dirname ) . st_mode
newmode = mode | stat . S_IXOTH
os . chmod ( dirname , newmode )
if os . stat ( dirname ) . st_mode != newmode :
# Trying to change perms on vfat at least doesn't work
# but also doesn't seem to error. Try and detect that
raise ValueError ( _ ( " Permissions on ' %s ' did not stick " ) %
dirname )
fixlist = VirtualDisk . check_path_search_for_user ( conn , path , username )
if not fixlist :
return [ ]
fixlist . reverse ( )
errdict = { }
useacl = True
for dirname in fixlist :
try :
try :
fix_perms ( dirname , useacl )
except :
# If acl fails, fall back to chmod and retry
if not useacl :
raise
useacl = False
logging . debug ( " setfacl failed, trying old fashioned way " )
fix_perms ( dirname , useacl )
except Exception , e :
errdict [ dirname ] = str ( e )
return errdict
@staticmethod
2014-01-15 01:32:05 +04:00
def path_in_use_by ( conn , path , shareable = False , read_only = False ) :
2013-03-18 01:06:52 +04:00
"""
Return a list of VM names that are using the passed path .
@param conn : virConnect to check VMs
@param path : Path to check for
2014-01-15 01:32:05 +04:00
@param shareable : Path we are checking is marked shareable , so
don ' t warn if it conflicts with another shareable source.
@param read_only : Path we are checking is marked read_only , so
don ' t warn if it conflicts with another read_only source.
2013-03-18 01:06:52 +04:00
"""
if not path :
2014-01-15 01:32:05 +04:00
return [ ]
2013-09-29 04:05:13 +04:00
2014-01-15 02:44:14 +04:00
# Find all volumes that have 'path' somewhere in their backing chain
2013-09-29 04:05:13 +04:00
vols = [ ]
2014-01-15 02:44:14 +04:00
volmap = dict ( ( vol . backing_store , vol )
for vol in conn . fetch_all_vols ( ) if vol . backing_store )
backpath = path
while backpath in volmap :
vol = volmap [ backpath ]
if vol in vols :
break
backpath = vol . target_path
vols . append ( backpath )
2013-03-18 01:06:52 +04:00
2014-01-15 01:32:05 +04:00
ret = [ ]
2013-07-07 22:54:48 +04:00
vms = conn . fetch_all_guests ( )
2013-03-18 01:06:52 +04:00
for vm in vms :
2014-01-15 01:32:05 +04:00
if not read_only :
if path in [ vm . os . kernel , vm . os . initrd , vm . os . dtb ] :
ret . append ( vm . name )
continue
2013-09-29 04:05:13 +04:00
2014-01-15 01:32:05 +04:00
for disk in vm . get_devices ( " disk " ) :
2013-09-29 04:05:13 +04:00
if disk . path in vols and vm . name not in ret :
2014-01-15 01:32:05 +04:00
# VM uses the path indirectly via backing store
2013-09-29 04:05:13 +04:00
ret . append ( vm . name )
2014-01-15 01:32:05 +04:00
break
if disk . path != path :
continue
if shareable and disk . shareable :
continue
if read_only and disk . read_only :
continue
ret . append ( vm . name )
break
2013-09-29 04:05:13 +04:00
return ret
2013-03-18 01:06:52 +04:00
@staticmethod
def stat_local_path ( path ) :
"""
Return tuple ( storage type , storage size ) for the passed path on
the local machine . This is a best effort attempt .
@return : tuple of
( True if regular file , False otherwise , default is True ,
max size of storage , default is 0 )
"""
try :
2013-04-11 18:27:02 +04:00
return util . stat_disk ( path )
2013-03-18 01:06:52 +04:00
except :
return ( True , 0 )
@staticmethod
def lookup_vol_object ( conn , name_tuple ) :
"""
2013-07-11 04:39:20 +04:00
Return a volume instance from a pool name , vol name tuple
2013-03-18 01:06:52 +04:00
"""
2013-10-06 18:08:04 +04:00
if not conn . check_support ( conn . SUPPORT_CONN_STORAGE ) :
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " Connection does not support storage lookup. " ) )
try :
pool = conn . storagePoolLookupByName ( name_tuple [ 0 ] )
return pool . storageVolLookupByName ( name_tuple [ 1 ] )
except Exception , e :
raise ValueError ( _ ( " Couldn ' t lookup volume object: %s " % str ( e ) ) )
2013-07-26 00:21:30 +04:00
@staticmethod
def build_vol_install ( * args , * * kwargs ) :
return diskbackend . build_vol_install ( * args , * * kwargs )
2013-07-15 17:49:46 +04:00
2013-10-03 15:20:54 +04:00
@staticmethod
def num_to_target ( num ) :
"""
Convert an index in range ( 1 , 1024 ) to a disk / dev number
( like hda , hdb , hdaa , etc . )
"""
digits = [ ]
for factor in range ( 0 , 3 ) :
amt = ( num % ( 26 * * ( factor + 1 ) ) ) / ( 26 * * factor )
if amt == 0 and num > = ( 26 * * ( factor + 1 ) ) :
amt = 26
num - = amt
digits . insert ( 0 , amt )
seen_valid = False
gen_t = " "
for digit in digits :
if digit == 0 :
if not seen_valid :
continue
digit = 1
seen_valid = True
gen_t + = " %c " % ( ord ( ' a ' ) + digit - 1 )
return gen_t
2013-07-15 17:49:46 +04:00
2013-07-16 20:48:52 +04:00
_XML_PROP_ORDER = [
" type " , " device " ,
" driver_name " , " driver_type " ,
" driver_cache " , " driver_io " , " error_policy " ,
" _xmlpath " , " target " , " bus " ,
]
2013-07-14 23:06:40 +04:00
2013-09-11 19:47:09 +04:00
def __init__ ( self , * args , * * kwargs ) :
VirtualDevice . __init__ ( self , * args , * * kwargs )
2013-03-18 01:06:52 +04:00
2013-07-24 16:46:55 +04:00
self . __storage_backend = None
2013-07-13 18:09:00 +04:00
self . _storage_creator = None
self . nomanaged = False
self . transient = False
2013-03-18 01:06:52 +04:00
2013-07-15 02:14:55 +04:00
#############################
# Public property-esque API #
#############################
2013-03-18 01:06:52 +04:00
def _get_path ( self ) :
2013-07-13 18:09:00 +04:00
if self . _storage_creator :
return self . _storage_creator . path
return self . _storage_backend . path
def _set_path ( self , val ) :
if self . _storage_creator :
raise ValueError ( " Can ' t change disk path if storage creation info "
" has been set. " )
2013-07-14 22:50:07 +04:00
self . _change_backend ( val , None )
2013-07-25 20:34:37 +04:00
self . _xmlpath = self . path
2013-07-15 02:14:55 +04:00
path = property ( _get_path , _set_path )
def get_sparse ( self ) :
if self . _storage_creator :
return self . _storage_creator . get_sparse ( )
return None
2013-03-18 01:06:52 +04:00
2013-07-15 02:14:55 +04:00
def get_vol_object ( self ) :
return self . _storage_backend . get_vol_object ( )
def get_vol_install ( self ) :
if not self . _storage_creator :
return None
return self . _storage_creator . get_vol_install ( )
def get_size ( self ) :
if self . _storage_creator :
return self . _storage_creator . get_size ( )
return self . _storage_backend . get_size ( )
2013-03-18 01:06:52 +04:00
2013-07-15 01:12:47 +04:00
2013-07-15 01:54:01 +04:00
#############################
# Internal defaults helpers #
#############################
def _get_default_type ( self ) :
if self . _storage_creator :
return self . _storage_creator . get_dev_type ( )
return self . _storage_backend . get_dev_type ( )
2013-07-15 02:00:43 +04:00
def _get_default_driver_name ( self ) :
2013-07-18 01:58:24 +04:00
if not self . path :
return None
2013-07-15 02:00:43 +04:00
if self . conn . is_qemu ( ) :
return self . DRIVER_QEMU
return None
def _get_default_driver_type ( self ) :
"""
Set driver type from passed parameters
Where possible , we want to force / driver / @type = " raw " if installing
a QEMU VM . Without telling QEMU to expect a raw file , the emulator
is forced to autodetect , which has security implications :
http : / / lists . gnu . org / archive / html / qemu - devel / 2008 - 04 / msg00675 . html
"""
if self . driver_name != self . DRIVER_QEMU :
return None
if self . _storage_creator :
drvtype = self . _storage_creator . get_driver_type ( )
else :
drvtype = self . _storage_backend . get_driver_type ( )
return _qemu_sanitize_drvtype ( self . type , drvtype )
2013-07-15 01:12:47 +04:00
2013-07-15 02:14:55 +04:00
##################
# XML properties #
##################
2013-09-24 17:25:05 +04:00
def _make_source_xpath ( self ) :
2013-07-15 02:14:55 +04:00
return " ./source/@ " + self . disk_type_to_target_prop ( self . type )
_xmlpath = XMLProperty ( name = " disk path " ,
2013-09-24 17:25:05 +04:00
make_xpath_cb = _make_source_xpath ,
2013-07-15 02:14:55 +04:00
clear_first = [ " ./source/@ " + target for target in
_TARGET_PROPS ] )
2013-07-14 05:04:27 +04:00
2013-10-03 16:48:09 +04:00
sourceStartupPolicy = XMLProperty ( " ./source/@startupPolicy " )
2013-09-19 21:27:30 +04:00
device = XMLProperty ( " ./@device " ,
2013-07-15 01:12:47 +04:00
default_cb = lambda s : s . DEVICE_DISK )
2013-09-19 21:27:30 +04:00
type = XMLProperty ( " ./@type " , default_cb = _get_default_type )
driver_name = XMLProperty ( " ./driver/@name " ,
2013-07-15 02:00:43 +04:00
default_cb = _get_default_driver_name )
2013-09-19 21:27:30 +04:00
driver_type = XMLProperty ( " ./driver/@type " ,
2013-07-15 02:00:43 +04:00
default_cb = _get_default_driver_type )
2013-07-15 01:12:47 +04:00
2013-09-19 21:27:30 +04:00
bus = XMLProperty ( " ./target/@bus " )
target = XMLProperty ( " ./target/@dev " )
2013-10-04 05:38:58 +04:00
removable = XMLProperty ( " ./target/@removable " , is_onoff = True )
2013-07-14 05:04:27 +04:00
2013-09-19 21:27:30 +04:00
read_only = XMLProperty ( " ./readonly " , is_bool = True )
shareable = XMLProperty ( " ./shareable " , is_bool = True )
driver_cache = XMLProperty ( " ./driver/@cache " )
driver_io = XMLProperty ( " ./driver/@io " )
2013-03-18 01:06:52 +04:00
2013-09-19 21:27:30 +04:00
error_policy = XMLProperty ( " ./driver/@error_policy " )
serial = XMLProperty ( " ./serial " )
2013-04-13 22:34:52 +04:00
2013-09-19 21:27:30 +04:00
iotune_rbs = XMLProperty ( " ./iotune/read_bytes_sec " , is_int = True )
iotune_ris = XMLProperty ( " ./iotune/read_iops_sec " , is_int = True )
iotune_tbs = XMLProperty ( " ./iotune/total_bytes_sec " , is_int = True )
iotune_tis = XMLProperty ( " ./iotune/total_iops_sec " , is_int = True )
iotune_wbs = XMLProperty ( " ./iotune/write_bytes_sec " , is_int = True )
iotune_wis = XMLProperty ( " ./iotune/write_iops_sec " , is_int = True )
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
#################################
# Validation assistance methods #
#################################
2013-03-18 01:06:52 +04:00
2013-07-24 16:46:55 +04:00
def _get_storage_backend ( self ) :
if self . __storage_backend is None :
self . __storage_backend = diskbackend . StorageBackend ( self . conn ,
self . _xmlpath ,
None , None )
return self . __storage_backend
def _set_storage_backend ( self , val ) :
self . __storage_backend = val
_storage_backend = property ( _get_storage_backend , _set_storage_backend )
2013-07-14 05:04:27 +04:00
def set_create_storage ( self , size = None , sparse = True ,
2013-09-29 19:23:59 +04:00
fmt = None , vol_install = None ,
clone_path = None , backing_store = None ,
2013-07-14 05:04:27 +04:00
fake = False ) :
"""
Function that sets storage creation parameters . If this isn ' t
called , we assume that no storage creation is taking place and
will error accordingly .
@size is in gigs
@fake : If true , make like we are creating storage but fail
if we ever asked to do so .
"""
2013-09-29 19:23:59 +04:00
def _validate_path ( p ) :
if p is None :
return
try :
d = VirtualDisk ( self . conn )
d . path = p
# If this disk isn't managed, make sure we only perform
# non-managed lookup.
if ( self . _storage_creator or
( self . path and self . _storage_backend . exists ( ) ) ) :
d . nomanaged = not self . __managed_storage ( )
d . set_create_storage ( fake = True )
d . validate ( )
except Exception , e :
raise ValueError ( _ ( " Error validating path %s : %s " ) % ( p , e ) )
2013-07-14 05:04:27 +04:00
path = self . path
# Validate clone_path
if clone_path is not None :
clone_path = os . path . abspath ( clone_path )
2013-09-29 19:23:59 +04:00
if backing_store is not None :
backing_store = os . path . abspath ( backing_store )
2013-07-14 05:04:27 +04:00
2013-09-29 19:23:59 +04:00
_validate_path ( clone_path )
_validate_path ( backing_store )
2013-07-14 05:04:27 +04:00
if fake and size is None :
size = .000001
2013-07-14 22:50:07 +04:00
ignore , creator = _distill_storage (
2013-07-14 05:04:27 +04:00
self . conn , True , self . nomanaged , path , None ,
2013-09-29 19:23:59 +04:00
vol_install , clone_path , backing_store ,
2013-07-14 05:04:27 +04:00
size , sparse , fmt )
2013-07-14 22:50:07 +04:00
self . _storage_creator = creator
if self . _storage_creator :
self . _storage_creator . fake = bool ( fake )
2013-07-25 20:34:37 +04:00
self . _xmlpath = self . path
2013-07-14 22:50:07 +04:00
else :
if ( vol_install or clone_path ) :
raise RuntimeError ( " Need storage creation but it "
" didn ' t happen. " )
if fmt and self . driver_name == self . DRIVER_QEMU :
2013-07-14 05:04:27 +04:00
self . driver_type = fmt
2013-08-17 22:21:30 +04:00
def is_cdrom ( self ) :
return self . device == self . DEVICE_CDROM
def is_floppy ( self ) :
return self . device == self . DEVICE_FLOPPY
def is_disk ( self ) :
return self . device == self . DEVICE_DISK
2013-07-14 05:04:27 +04:00
2013-03-18 01:06:52 +04:00
def can_be_empty ( self ) :
2013-08-17 22:21:30 +04:00
return self . is_floppy ( ) or self . is_cdrom ( )
2013-03-18 01:06:52 +04:00
2013-07-14 22:50:07 +04:00
def _change_backend ( self , path , vol_object ) :
2013-07-13 18:09:00 +04:00
backend , ignore = _distill_storage (
self . conn , False , self . nomanaged ,
2013-09-29 19:23:59 +04:00
path , vol_object , None , None , None )
2013-07-13 18:09:00 +04:00
self . _storage_backend = backend
2013-03-18 01:06:52 +04:00
2013-07-25 20:34:37 +04:00
def sync_path_props ( self ) :
"""
Fills in the values of type , driver_type , and driver_name for
the associated backing storage . This needs to be manually called
if changing an existing disk ' s media.
"""
self . type = self . _get_default_type ( )
self . driver_name = self . _get_default_driver_name ( )
self . driver_type = self . _get_default_driver_type ( )
2013-03-18 01:06:52 +04:00
def __managed_storage ( self ) :
"""
Return bool representing if managed storage parameters have
been explicitly specified or filled in
"""
2013-07-13 18:09:00 +04:00
if self . _storage_creator :
return self . _storage_creator . is_managed ( )
return self . _storage_backend . is_managed ( )
2013-03-18 01:06:52 +04:00
def creating_storage ( self ) :
"""
Return True if the user requested us to create a device
"""
2013-07-13 18:09:00 +04:00
return bool ( self . _storage_creator )
2013-03-18 01:06:52 +04:00
2013-07-13 18:09:00 +04:00
def validate ( self ) :
2013-03-18 01:06:52 +04:00
"""
function to validate all the complex interaction between the various
disk parameters .
"""
# No storage specified for a removable device type (CDROM, floppy)
2013-07-13 18:09:00 +04:00
if self . path is None :
2013-03-18 01:06:52 +04:00
if not self . can_be_empty ( ) :
raise ValueError ( _ ( " Device type ' %s ' requires a path " ) %
self . device )
return True
2013-10-06 18:08:04 +04:00
storage_capable = self . conn . check_support (
2013-07-06 19:20:28 +04:00
self . conn . SUPPORT_CONN_STORAGE )
2013-03-18 01:06:52 +04:00
2013-07-06 04:36:28 +04:00
if self . conn . is_remote ( ) :
2013-03-18 01:06:52 +04:00
if not storage_capable :
raise ValueError ( _ ( " Connection doesn ' t support remote "
" storage. " ) )
if not self . __managed_storage ( ) :
raise ValueError ( _ ( " Must specify libvirt managed storage "
" if on a remote connection " ) )
# The main distinctions from this point forward:
# - Are we doing storage API operations or local media checks?
# - Do we need to create the storage?
managed_storage = self . __managed_storage ( )
create_media = self . creating_storage ( )
# If not creating the storage, our job is easy
if not create_media :
2013-07-13 18:09:00 +04:00
if not self . _storage_backend . exists ( ) :
raise ValueError (
_ ( " Must specify storage creation parameters for "
" non-existent path ' %s ' . " ) % self . path )
2013-03-18 01:06:52 +04:00
# Make sure we have access to the local path
if not managed_storage :
2013-08-17 22:21:30 +04:00
if ( os . path . isdir ( self . path ) and not self . is_floppy ( ) ) :
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " The path ' %s ' must be a file or a "
" device, not a directory " ) % self . path )
return True
2013-07-13 18:09:00 +04:00
self . _storage_creator . validate ( self . device , self . type )
2013-03-18 01:06:52 +04:00
# Applicable for managed or local storage
ret = self . is_size_conflict ( )
if ret [ 0 ] :
raise ValueError ( ret [ 1 ] )
elif ret [ 1 ] :
logging . warn ( ret [ 1 ] )
2013-07-06 04:14:57 +04:00
def setup ( self , meter = None ) :
2013-03-18 01:06:52 +04:00
"""
Build storage ( if required )
If storage doesn ' t exist (a non-existent file ' path ' , or ' vol_install '
was specified ) , we create it .
@param meter : Progress meter to report file creation on
@type meter : instanceof urlgrabber . BaseMeter
"""
2013-07-06 04:14:57 +04:00
if not meter :
meter = progress . BaseMeter ( )
2013-07-13 18:09:00 +04:00
if not self . _storage_creator :
return
volobj = self . _storage_creator . create ( meter )
self . _storage_creator = None
if volobj :
2013-07-14 22:50:07 +04:00
self . _change_backend ( None , volobj )
2013-03-18 01:06:52 +04:00
2013-10-06 21:17:35 +04:00
def set_defaults ( self , guest ) :
2013-10-03 00:09:05 +04:00
if self . is_cdrom ( ) :
self . read_only = True
2013-10-06 21:17:35 +04:00
if ( guest . os . is_xenpv ( ) and
self . type == VirtualDisk . TYPE_FILE and
self . driver_name is None and
util . is_blktap_capable ( self . conn ) ) :
self . driver_name = VirtualDisk . DRIVER_TAP
2013-08-17 22:21:30 +04:00
if not self . conn . is_qemu ( ) :
return
if not self . is_disk ( ) :
2013-07-14 23:47:02 +04:00
return
2013-10-03 00:09:05 +04:00
if not self . type == self . TYPE_BLOCK :
return
2013-07-14 23:47:02 +04:00
2013-10-03 00:09:05 +04:00
# Enable cache=none and io=native for block devices. Would
# be nice if qemu did this for us but that time has long passed.
2013-08-17 22:21:30 +04:00
if not self . driver_cache :
2013-07-14 23:47:02 +04:00
self . driver_cache = self . CACHE_MODE_NONE
2013-10-03 00:09:05 +04:00
if not self . driver_io :
2013-07-14 23:47:02 +04:00
self . driver_io = self . IO_MODE_NATIVE
2013-08-17 22:21:30 +04:00
2013-03-18 01:06:52 +04:00
def is_size_conflict ( self ) :
"""
reports if disk size conflicts with available space
returns a two element tuple :
1. first element is True if fatal conflict occurs
2. second element is a string description of the conflict or None
Non fatal conflicts ( sparse disk exceeds available space ) will
return ( False , " description of collision " )
"""
2013-07-13 18:09:00 +04:00
if not self . _storage_creator :
2013-03-18 01:06:52 +04:00
return ( False , None )
2013-07-13 18:09:00 +04:00
return self . _storage_creator . is_size_conflict ( )
2013-03-18 01:06:52 +04:00
2014-01-15 02:11:51 +04:00
def is_conflict_disk ( self , conn = None ) :
2013-03-18 01:06:52 +04:00
"""
check if specified storage is in use by any other VMs on passed
connection .
2013-07-08 00:34:46 +04:00
@return : list of colliding VM names
@rtype : C { list }
2013-03-18 01:06:52 +04:00
"""
2013-07-13 18:09:00 +04:00
if not self . path :
2013-03-18 01:06:52 +04:00
return False
if not conn :
conn = self . conn
2013-07-13 18:09:00 +04:00
ret = self . path_in_use_by ( conn , self . path ,
2014-01-15 01:32:05 +04:00
shareable = self . shareable ,
read_only = self . read_only )
2013-03-18 01:06:52 +04:00
return ret
2014-01-26 06:08:11 +04:00
def get_target_prefix ( self , used_targets = None ) :
2013-03-18 01:06:52 +04:00
"""
Returns the suggested disk target prefix ( hd , xvd , sd . . . ) for the
disk .
@returns : str prefix , or None if no reasonable guess can be made
"""
# The upper limits here aren't necessarilly 1024, but let the HV
# error as appropriate.
2014-01-26 06:08:11 +04:00
def _return ( prefix ) :
nummap = {
" vd " : 1024 ,
" xvd " : 1024 ,
" fd " : 2 ,
" hd " : 4 ,
" sd " : 1024 ,
}
return prefix , nummap [ prefix ]
2013-03-18 01:06:52 +04:00
if self . bus == " virtio " :
2014-01-26 06:08:11 +04:00
return _return ( " vd " )
2013-03-18 01:06:52 +04:00
elif self . bus == " xen " :
2014-01-26 06:08:11 +04:00
return _return ( " xvd " )
2013-08-17 22:21:30 +04:00
elif self . bus == " fdc " or self . is_floppy ( ) :
2014-01-26 06:08:11 +04:00
return _return ( " fd " )
2013-03-18 01:06:52 +04:00
elif self . bus == " ide " :
2014-01-26 06:08:11 +04:00
return _return ( " hd " )
elif self . bus or not used_targets :
# sata, scsi, usb, sd
return _return ( " sd " )
# If guest already has some disks defined
preforder = [ " vd " , " xvd " , " sd " , " hd " ]
for pref in preforder :
for target in used_targets :
if target . startswith ( pref ) :
return _return ( pref )
return _return ( " sd " )
2013-03-18 01:06:52 +04:00
def generate_target ( self , skip_targets ) :
"""
Generate target device ( ' hda ' , ' sdb ' , etc . . ) for disk , excluding
any targets in ' skip_targets ' . Sets self . target , and returns the
generated value
@param skip_targets : list of targets to exclude
@type skip_targets : C { list }
@raise ValueError : can ' t determine target type, no targets available
@returns generated target
@rtype C { str }
"""
2014-01-26 06:08:11 +04:00
prefix , maxnode = self . get_target_prefix ( skip_targets )
2013-10-03 16:40:24 +04:00
skip_targets = [ t for t in skip_targets if t and t . startswith ( prefix ) ]
skip_targets . sort ( )
2013-03-18 01:06:52 +04:00
2013-10-03 16:40:24 +04:00
def get_target ( ) :
first_found = None
2013-03-18 01:06:52 +04:00
2013-10-03 16:40:24 +04:00
for i in range ( 1 , maxnode + 1 ) :
gen_t = prefix + self . num_to_target ( i )
if gen_t in skip_targets :
skip_targets . remove ( gen_t )
continue
if not skip_targets :
return gen_t
elif not first_found :
first_found = gen_t
if first_found :
return first_found
ret = get_target ( )
if ret :
self . target = ret
return ret
2014-01-27 06:22:08 +04:00
raise ValueError ( _ ( " Only %s disks of type ' %s ' are supported "
% ( maxnode , prefix ) ) )
2013-07-24 16:46:55 +04:00
VirtualDisk . register_type ( )