2013-03-18 01:06:52 +04:00
#
2013-04-02 15:51:28 +04:00
# Copyright 2008, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Cole Robinson <crobinso@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 threading
import time
import logging
import libvirt
import urlgrabber
2013-09-20 04:18:12 +04:00
from virtinst . xmlbuilder import XMLBuilder , XMLChildProperty , XMLProperty
2013-04-11 03:48:07 +04:00
from virtinst import util
2013-03-18 01:06:52 +04:00
DEFAULT_DEV_TARGET = " /dev "
DEFAULT_LVM_TARGET_BASE = " /dev/ "
DEFAULT_DIR_TARGET_BASE = " /var/lib/libvirt/images/ "
DEFAULT_SCSI_TARGET = " /dev/disk/by-path "
DEFAULT_MPATH_TARGET = " /dev/mapper "
2013-09-20 04:18:12 +04:00
class _StoragePermissions ( XMLBuilder ) :
_XML_ROOT_NAME = " permissions "
_XML_PROP_ORDER = [ " mode " , " owner " , " group " , " label " ]
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
mode = XMLProperty ( " ./mode " )
owner = XMLProperty ( " ./owner " )
group = XMLProperty ( " ./group " )
label = XMLProperty ( " ./label " )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
class _StorageObject ( XMLBuilder ) :
2013-03-18 01:06:52 +04:00
"""
Base class for building any libvirt storage object .
2013-09-20 04:18:12 +04:00
Meaningless to directly instantiate .
2013-03-18 01:06:52 +04:00
"""
2013-09-20 04:18:12 +04:00
######################
# Validation helpers #
######################
2013-03-18 01:06:52 +04:00
def _check_name_collision ( self , name ) :
raise NotImplementedError ( )
2013-09-20 04:18:12 +04:00
def _validate_name ( self , name ) :
if name == self . name :
return
util . validate_name ( _ ( " Storage object " ) , name )
self . _check_name_collision ( name )
return name
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
##############
# Properties #
##############
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
name = XMLProperty ( " ./name " , validate_cb = _validate_name ,
doc = _ ( " Name for the storage object. " ) )
permissions = XMLChildProperty ( _StoragePermissions ,
relative_xpath = " ./target " ,
is_single = True )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
class StoragePool ( _StorageObject ) :
2013-03-18 01:06:52 +04:00
"""
Base class for building and installing libvirt storage pool xml
"""
2013-04-12 16:26:21 +04:00
# @group Types: TYPE_*
2013-03-18 01:06:52 +04:00
TYPE_DIR = " dir "
TYPE_FS = " fs "
TYPE_NETFS = " netfs "
TYPE_LOGICAL = " logical "
TYPE_DISK = " disk "
TYPE_ISCSI = " iscsi "
TYPE_SCSI = " scsi "
TYPE_MPATH = " mpath "
# Pool type descriptions for use in higher level programs
2013-09-20 04:18:12 +04:00
_descs = { }
_descs [ TYPE_DIR ] = _ ( " Filesystem Directory " )
_descs [ TYPE_FS ] = _ ( " Pre-Formatted Block Device " )
_descs [ TYPE_NETFS ] = _ ( " Network Exported Directory " )
_descs [ TYPE_LOGICAL ] = _ ( " LVM Volume Group " )
_descs [ TYPE_DISK ] = _ ( " Physical Disk Device " )
_descs [ TYPE_ISCSI ] = _ ( " iSCSI Target " )
_descs [ TYPE_SCSI ] = _ ( " SCSI Host Adapter " )
_descs [ TYPE_MPATH ] = _ ( " Multipath Device Enumerator " )
@staticmethod
def get_pool_types ( ) :
2013-03-18 01:06:52 +04:00
"""
2013-09-20 04:18:12 +04:00
Return list of appropriate pool types
2013-03-18 01:06:52 +04:00
"""
2013-09-20 04:18:12 +04:00
return StoragePool . _descs . keys ( )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
@staticmethod
2013-03-18 01:06:52 +04:00
def get_pool_type_desc ( pool_type ) :
2013-09-20 04:18:12 +04:00
"""
Return human readable description for passed pool type
"""
return StoragePool . _descs . get ( pool_type , " %s pool " % pool_type )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
@staticmethod
def pool_list_from_sources ( conn , pool_type , host = None ) :
2013-03-18 01:06:52 +04:00
"""
Return a list of StoragePool instances built from libvirt ' s pool
source enumeration ( if supported ) .
@param conn : Libvirt connection
@param name : Name for the new pool
@param pool_type : Pool type string from I { Types }
@param host : Option host string to poll for sources
"""
2013-10-06 18:08:04 +04:00
if not conn . check_support ( conn . SUPPORT_CONN_FINDPOOLSOURCES ) :
2013-03-18 01:06:52 +04:00
return [ ]
if host :
source_xml = " <source><host name= ' %s ' /></source> " % host
else :
source_xml = " <source/> "
try :
xml = conn . findStoragePoolSources ( pool_type , source_xml , 0 )
except libvirt . libvirtError , e :
2013-07-06 19:20:28 +04:00
if util . is_error_nosupport ( e ) :
2013-03-18 01:06:52 +04:00
return [ ]
raise
2013-09-20 04:18:12 +04:00
def source_parser ( node ) :
ret = [ ]
child = node . children
while child :
if child . name == " source " :
xml = " <pool> \n %s \n </pool> " % (
util . xml_indent ( child . serialize ( format = 1 ) , 2 ) )
parseobj = StoragePool ( conn , parsexml = xml )
parseobj . type = pool_type
obj = StoragePool ( conn )
obj . type = pool_type
obj . source_path = parseobj . source_path
obj . host = parseobj . host
obj . source_name = parseobj . source_name
obj . format = parseobj . format
ret . append ( obj )
child = child . next
return ret
return util . parse_node_helper ( xml , " sources " , source_parser )
@staticmethod
def build_default_pool ( conn ) :
"""
Helper to build the ' default ' storage pool
"""
2013-10-06 18:08:04 +04:00
if not conn . check_support ( conn . SUPPORT_CONN_STORAGE ) :
2013-09-20 04:18:12 +04:00
return
pool = None
name = " default "
path = " /var/lib/libvirt/images "
if conn . is_session_uri ( ) :
path = os . path . expanduser ( " ~/VirtualMachines " )
try :
pool = conn . storagePoolLookupByName ( name )
except libvirt . libvirtError :
pass
if pool :
return
try :
logging . debug ( " Attempting to build default pool with target ' %s ' " ,
path )
defpool = StoragePool ( conn )
defpool . type = defpool . TYPE_DIR
defpool . name = name
defpool . target_path = path
newpool = defpool . install ( build = True , create = True , autostart = True )
2014-01-18 23:57:39 +04:00
conn . clear_cache ( pools = True )
2013-09-20 04:18:12 +04:00
return newpool
except Exception , e :
raise RuntimeError (
_ ( " Couldn ' t create default storage pool ' %s ' : %s " ) %
( path , str ( e ) ) )
2013-09-23 01:34:53 +04:00
@staticmethod
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
"""
2013-10-06 18:08:04 +04:00
if not conn . check_support ( conn . SUPPORT_CONN_STORAGE ) :
2013-09-23 01:34:53 +04:00
return None
def check_pool ( pool , path ) :
2013-09-29 04:05:13 +04:00
xml_path = pool . target_path
2013-09-23 01:34:53 +04:00
if xml_path is not None and os . path . abspath ( xml_path ) == path :
2013-09-29 04:05:13 +04:00
return True
2013-09-23 01:34:53 +04:00
for pool in conn . fetch_all_pools ( ) :
2013-09-29 04:05:13 +04:00
if check_pool ( pool , path ) :
return conn . storagePoolLookupByName ( pool . name )
2013-09-23 01:34:53 +04:00
return None
2013-09-20 04:18:12 +04:00
def __init__ ( self , * args , * * kwargs ) :
_StorageObject . __init__ ( self , * args , * * kwargs )
self . _random_uuid = None
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
######################
# Validation helpers #
######################
2013-03-18 01:06:52 +04:00
def _check_name_collision ( self , name ) :
pool = None
try :
pool = self . conn . storagePoolLookupByName ( name )
except libvirt . libvirtError :
pass
if pool :
raise ValueError ( _ ( " Name ' %s ' already in use by another pool. " %
name ) )
def _get_default_target_path ( self ) :
2013-09-20 04:18:12 +04:00
if ( self . type == self . TYPE_DIR or
self . type == self . TYPE_NETFS or
self . type == self . TYPE_FS ) :
return ( DEFAULT_DIR_TARGET_BASE + self . name )
if self . type == self . TYPE_LOGICAL :
name = self . name
if self . source_name :
name = self . source_name
return DEFAULT_LVM_TARGET_BASE + name
if self . type == self . TYPE_DISK :
return DEFAULT_DEV_TARGET
if self . type == self . TYPE_ISCSI or self . type == self . TYPE_SCSI :
return DEFAULT_SCSI_TARGET
if self . type == self . TYPE_MPATH :
return DEFAULT_MPATH_TARGET
raise RuntimeError ( " No default target_path for type= %s " % self . type )
def _get_default_uuid ( self ) :
if self . _random_uuid is None :
self . _random_uuid = util . generate_uuid ( self . conn )
return self . _random_uuid
2013-09-24 17:25:05 +04:00
def _make_source_xpath ( self ) :
2013-09-20 04:18:12 +04:00
if self . type == self . TYPE_NETFS :
return " ./source/dir/@path "
if self . type == self . TYPE_SCSI :
return " ./source/adapter/@name "
return " ./source/device/@path "
def _default_source_name ( self ) :
if not self . supports_property ( " source_name " ) :
return None
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
# If a source name isn't explictly set, try to determine it from
# existing parameters
srcname = self . name
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
if ( " target_path " in self . _propstore and
self . target_path and
self . target_path . startswith ( DEFAULT_LVM_TARGET_BASE ) ) :
# If there is a target path, parse it for an expected VG
# location, and pull the name from there
vg = self . target_path [ len ( DEFAULT_LVM_TARGET_BASE ) : ]
srcname = vg . split ( " / " , 1 ) [ 0 ]
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
return srcname
def _default_format_cb ( self ) :
if not self . supports_property ( " format " ) :
return None
return " auto "
##############
# Properties #
##############
_XML_ROOT_NAME = " pool "
_XML_PROP_ORDER = [ " name " , " type " , " uuid " ,
" capacity " , " allocation " , " available " ,
" format " , " host " ,
" source_path " , " source_name " , " target_path " ,
" permissions " ]
type = XMLProperty ( " ./@type " ,
doc = _ ( " Storage device type the pool will represent. " ) )
uuid = XMLProperty ( " ./uuid " ,
validate_cb = lambda s , v : util . validate_uuid ( v ) ,
default_cb = _get_default_uuid )
capacity = XMLProperty ( " ./capacity " , is_int = True )
allocation = XMLProperty ( " ./allocation " , is_int = True )
available = XMLProperty ( " ./available " , is_int = True )
format = XMLProperty ( " ./source/format/@type " ,
default_cb = _default_format_cb )
host = XMLProperty ( " ./source/host/@name " )
iqn = XMLProperty ( " ./source/initiator/iqn/@name " ,
doc = _ ( " iSCSI initiator qualified name " ) )
source_path = XMLProperty ( name = " source path " ,
2013-09-24 17:25:05 +04:00
make_xpath_cb = _make_source_xpath )
2013-09-20 04:18:12 +04:00
source_name = XMLProperty ( " ./source/name " ,
default_cb = _default_source_name ,
doc = _ ( " Name of the Volume Group " ) )
target_path = XMLProperty ( " ./target/path " ,
default_cb = _get_default_target_path )
######################
# Public API helpers #
######################
def supports_property ( self , propname ) :
users = {
" source_path " : [ self . TYPE_FS , self . TYPE_NETFS , self . TYPE_LOGICAL ,
self . TYPE_DISK , self . TYPE_ISCSI , self . TYPE_SCSI ] ,
" source_name " : [ self . TYPE_LOGICAL ] ,
" host " : [ self . TYPE_NETFS , self . TYPE_ISCSI ] ,
" format " : [ self . TYPE_FS , self . TYPE_NETFS , self . TYPE_DISK ] ,
" iqn " : [ self . TYPE_ISCSI ] ,
}
if users . get ( propname ) :
return self . type in users [ propname ]
return hasattr ( self , propname )
def list_formats ( self ) :
if self . type == self . TYPE_FS :
return [ " auto " , " ext2 " , " ext3 " , " ext4 " , " ufs " , " iso9660 " , " udf " ,
" gfs " , " gfs2 " , " vfat " , " hfs+ " , " xfs " ]
if self . type == self . TYPE_NETFS :
return [ " auto " , " nfs " , " glusterfs " ]
if self . type == self . TYPE_DISK :
return [ " auto " , " bsd " , " dos " , " dvh " , " gpt " , " mac " , " pc98 " , " sun " ]
return [ ]
def supports_volume_creation ( self ) :
return self . type in [
StoragePool . TYPE_DIR , StoragePool . TYPE_FS ,
StoragePool . TYPE_NETFS , StoragePool . TYPE_LOGICAL ,
StoragePool . TYPE_DISK ]
2013-09-23 01:34:53 +04:00
def get_vm_disk_type ( self ) :
"""
Return the / disk / @type value if the pool source is used as
VirtualDisk path
"""
2013-09-24 17:25:05 +04:00
xpath = self . _make_source_xpath ( )
2013-09-23 01:34:53 +04:00
if " /dir/ " in xpath :
return " dir "
return " block "
2013-09-20 04:18:12 +04:00
##################
# Build routines #
##################
def validate ( self ) :
if self . supports_property ( " host " ) and not self . host :
raise RuntimeError ( _ ( " Hostname is required " ) )
if ( self . supports_property ( " source_path " ) and
not self . type == self . TYPE_LOGICAL and
not self . source_path ) :
raise RuntimeError ( _ ( " Source path is required " ) )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
if ( self . type == self . TYPE_DISK and self . format == " auto " ) :
# There is no explicit "auto" type for disk pools, but leaving out
# the format type seems to do the job for existing formatted disks
self . format = None
2013-03-18 01:06:52 +04:00
def install ( self , meter = None , create = False , build = False , autostart = False ) :
"""
Install storage pool xml .
"""
2013-09-20 04:18:12 +04:00
if ( self . type == self . TYPE_LOGICAL and
build and not self . source_path ) :
raise ValueError ( _ ( " Must explicitly specify source path if "
" building pool " ) )
if ( self . type == self . TYPE_DISK and
build and self . format == " auto " ) :
raise ValueError ( _ ( " Must explicitly specify disk format if "
" formatting disk device. " ) )
2013-03-18 01:06:52 +04:00
xml = self . get_xml_config ( )
logging . debug ( " Creating storage pool ' %s ' with xml: \n %s " ,
self . name , xml )
if not meter :
meter = urlgrabber . progress . BaseMeter ( )
try :
pool = self . conn . storagePoolDefineXML ( xml , 0 )
except Exception , e :
raise RuntimeError ( _ ( " Could not define storage pool: %s " % str ( e ) ) )
errmsg = None
if build :
try :
pool . build ( libvirt . VIR_STORAGE_POOL_BUILD_NEW )
except Exception , e :
errmsg = _ ( " Could not build storage pool: %s " % str ( e ) )
if create and not errmsg :
try :
pool . create ( 0 )
except Exception , e :
errmsg = _ ( " Could not start storage pool: %s " % str ( e ) )
if autostart and not errmsg :
try :
pool . setAutostart ( True )
except Exception , e :
errmsg = _ ( " Could not set pool autostart flag: %s " % str ( e ) )
if errmsg :
# Try and clean up the leftover pool
try :
pool . undefine ( )
except Exception , e :
logging . debug ( " Error cleaning up pool after failure: " +
" %s " % str ( e ) )
raise RuntimeError ( errmsg )
return pool
2013-09-20 04:18:12 +04:00
class StorageVolume ( _StorageObject ) :
2013-03-18 01:06:52 +04:00
"""
Base class for building and installing libvirt storage volume xml
"""
2014-01-24 14:40:09 +04:00
ALL_FORMATS = [ " raw " , " bochs " , " cloop " , " cow " , " dmg " , " iso " , " qcow " ,
" qcow2 " , " qed " , " vmdk " , " vpc " , " fat " , " vhd " , " vdi " ]
2013-09-20 04:18:12 +04:00
@staticmethod
2013-10-05 18:22:27 +04:00
def find_free_name ( pool_object , basename , * * kwargs ) :
2013-03-18 01:06:52 +04:00
"""
2013-10-01 00:21:23 +04:00
Finds a name similar ( or equal ) to passed ' basename ' that is not
2013-10-05 18:22:27 +04:00
in use by another pool . Extra params are passed to generate_name
2013-03-18 01:06:52 +04:00
"""
pool_object . refresh ( 0 )
2013-10-05 18:22:27 +04:00
return util . generate_name ( basename ,
pool_object . storageVolLookupByName ,
* * kwargs )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
TYPE_FILE = getattr ( libvirt , " VIR_STORAGE_VOL_FILE " , 0 )
TYPE_BLOCK = getattr ( libvirt , " VIR_STORAGE_VOL_BLOCK " , 1 )
def __init__ ( self , * args , * * kwargs ) :
_StorageObject . __init__ ( self , * args , * * kwargs )
self . _input_vol = None
self . _pool = None
self . _pool_type = None
# Indicate that the volume installation has finished. Used to
# definitively tell the storage progress thread to stop polling.
self . _install_finished = True
######################
# Non XML properties #
######################
def _get_pool ( self ) :
2013-03-18 01:06:52 +04:00
return self . _pool
2013-09-20 04:18:12 +04:00
def _set_pool ( self , newpool ) :
2013-03-18 01:06:52 +04:00
if newpool . info ( ) [ 0 ] != libvirt . VIR_STORAGE_POOL_RUNNING :
raise ValueError ( _ ( " pool ' %s ' must be active. " % newpool . name ( ) ) )
self . _pool = newpool
2013-09-20 04:18:12 +04:00
self . _pool_type = StoragePool ( self . conn ,
parsexml = self . _pool . XMLDesc ( 0 ) ) . type
pool = property ( _get_pool , _set_pool )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
def _get_input_vol ( self ) :
2013-03-18 01:06:52 +04:00
return self . _input_vol
2013-09-20 04:18:12 +04:00
def _set_input_vol ( self , vol ) :
2013-03-18 01:06:52 +04:00
if vol is None :
self . _input_vol = None
return
if not isinstance ( vol , libvirt . virStorageVol ) :
raise ValueError ( _ ( " input_vol must be a virStorageVol " ) )
2013-04-12 17:51:26 +04:00
2013-10-06 18:08:04 +04:00
if not self . conn . check_support (
self . conn . SUPPORT_POOL_CREATEVOLFROM , self . pool ) :
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " Creating storage from an existing volume is "
" not supported by this libvirt version. " ) )
2013-09-20 04:18:12 +04:00
2013-03-18 01:06:52 +04:00
self . _input_vol = vol
2013-09-20 04:18:12 +04:00
input_vol = property ( _get_input_vol , _set_input_vol ,
2013-03-18 01:06:52 +04:00
doc = _ ( " virStorageVolume pointer to clone/use as "
" input. " ) )
2013-09-20 04:18:12 +04:00
def sync_input_vol ( self ) :
# Pull paramaters from input vol into this class
parsevol = StorageVolume ( self . conn ,
parsexml = self . _input_vol . XMLDesc ( 0 ) )
self . pool = self . _input_vol . storagePoolLookupByVolume ( )
self . capacity = parsevol . capacity
self . allocation = parsevol . allocation
self . format = parsevol . format
##########################
# XML validation helpers #
##########################
2013-03-18 01:06:52 +04:00
def _check_name_collision ( self , name ) :
vol = None
try :
vol = self . pool . storageVolLookupByName ( name )
except libvirt . libvirtError :
pass
if vol :
raise ValueError ( _ ( " Name ' %s ' already in use by another volume. " %
name ) )
2013-09-20 04:18:12 +04:00
def _validate_allocation ( self , val ) :
ret = self . is_size_conflict ( allocation = val )
if ret [ 0 ] :
raise ValueError ( ret [ 1 ] )
elif ret [ 1 ] :
logging . warn ( ret [ 1 ] )
return val
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
def _validate_capacity ( self , val ) :
ret = self . is_size_conflict ( capacity = val )
if ret [ 0 ] :
raise ValueError ( ret [ 1 ] )
elif ret [ 1 ] :
logging . warn ( ret [ 1 ] )
return val
def _default_format ( self ) :
if self . file_type == self . TYPE_FILE :
return " raw "
return None
def _get_vol_type ( self ) :
if ( self . _pool_type == StoragePool . TYPE_DISK or
self . _pool_type == StoragePool . TYPE_LOGICAL ) :
return self . TYPE_BLOCK
return self . TYPE_FILE
file_type = property ( _get_vol_type )
##################
# XML properties #
##################
_XML_ROOT_NAME = " volume "
2013-12-05 18:17:12 +04:00
_XML_PROP_ORDER = [ " name " , " key " , " capacity " , " allocation " , " format " ,
2013-09-20 04:18:12 +04:00
" target_path " , " permissions " ]
2013-12-05 18:17:12 +04:00
key = XMLProperty ( " ./key " )
2013-09-20 04:18:12 +04:00
capacity = XMLProperty ( " ./capacity " , is_int = True ,
validate_cb = _validate_capacity )
allocation = XMLProperty ( " ./allocation " , is_int = True ,
validate_cb = _validate_allocation )
format = XMLProperty ( " ./target/format/@type " , default_cb = _default_format )
target_path = XMLProperty ( " ./target/path " )
2013-09-29 03:11:27 +04:00
backing_store = XMLProperty ( " ./backingStore/path " )
2013-03-18 01:06:52 +04:00
2014-02-03 23:52:23 +04:00
def _lazy_refcounts_default_cb ( self ) :
return self . conn . check_support (
self . conn . SUPPORT_CONN_QCOW2_LAZY_REFCOUNTS )
lazy_refcounts = XMLProperty ( " ./target/features/lazy_refcounts " ,
is_bool = True , default_cb = _lazy_refcounts_default_cb )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
######################
# Public API helpers #
######################
def supports_property ( self , propname ) :
users = {
" format " : [ self . TYPE_FILE ] ,
}
if users . get ( propname ) :
return self . file_type in users [ propname ]
return hasattr ( self , propname )
def list_formats ( self ) :
if self . file_type == self . TYPE_FILE :
2014-01-26 00:52:34 +04:00
return self . ALL_FORMATS
2013-09-20 04:18:12 +04:00
return [ ]
def list_create_formats ( self ) :
if self . file_type == self . TYPE_FILE :
2013-09-27 21:22:40 +04:00
return [ " raw " , " cow " , " qcow " , " qcow2 " , " qed " , " vmdk " , " vpc " , " vdi " ]
2013-09-20 04:18:12 +04:00
return None
##################
# Build routines #
##################
def validate ( self ) :
if self . _pool_type == StoragePool . TYPE_LOGICAL :
if self . allocation != self . capacity :
logging . warn ( _ ( " Sparse logical volumes are not supported, "
" setting allocation equal to capacity " ) )
self . allocation = self . capacity
2013-03-18 01:06:52 +04:00
def install ( self , meter = None ) :
"""
Build and install storage volume from xml
"""
xml = self . get_xml_config ( )
logging . debug ( " Creating storage volume ' %s ' with xml: \n %s " ,
self . name , xml )
t = threading . Thread ( target = self . _progress_thread ,
name = " Checking storage allocation " ,
args = ( meter , ) )
t . setDaemon ( True )
if not meter :
meter = urlgrabber . progress . BaseMeter ( )
2013-10-01 22:29:58 +04:00
cloneflags = 0
createflags = 0
if ( self . format == " qcow2 " and
not self . backing_store and
not self . conn . is_test ( ) and
2013-10-06 18:08:04 +04:00
self . conn . check_support (
self . conn . SUPPORT_POOL_METADATA_PREALLOC , self . pool ) ) :
2013-10-01 22:29:58 +04:00
createflags | = libvirt . VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
2013-03-18 01:06:52 +04:00
try :
2013-06-09 23:40:25 +04:00
self . _install_finished = False
t . start ( )
meter . start ( size = self . capacity ,
text = _ ( " Allocating ' %s ' " ) % self . name )
if self . input_vol :
2013-10-01 22:29:58 +04:00
vol = self . pool . createXMLFrom ( xml , self . input_vol , cloneflags )
2013-06-09 23:40:25 +04:00
else :
2013-10-01 22:29:58 +04:00
logging . debug ( " Using vol create flags= %s " , createflags )
vol = self . pool . createXML ( xml , createflags )
2013-06-09 23:40:25 +04:00
2013-07-23 16:02:45 +04:00
self . _install_finished = True
t . join ( )
2013-06-09 23:40:25 +04:00
meter . end ( self . capacity )
logging . debug ( " Storage volume ' %s ' install complete. " ,
self . name )
return vol
except libvirt . libvirtError , e :
2013-07-06 19:20:28 +04:00
if util . is_error_nosupport ( e ) :
2013-06-09 23:40:25 +04:00
raise RuntimeError ( " Libvirt version does not support "
" storage cloning. " )
raise
except Exception , e :
raise RuntimeError ( " Couldn ' t create storage volume "
" ' %s ' : ' %s ' " % ( self . name , str ( e ) ) )
2013-03-18 01:06:52 +04:00
def _progress_thread ( self , meter ) :
lookup_attempts = 10
vol = None
if not meter :
return
while lookup_attempts > 0 :
try :
2013-09-29 19:31:03 +04:00
if not vol :
vol = self . pool . storageVolLookupByName ( self . name )
vol . info ( )
2013-03-18 01:06:52 +04:00
break
except :
lookup_attempts - = 1
time . sleep ( .2 )
if self . _install_finished :
break
else :
continue
break
2013-04-13 22:34:52 +04:00
if vol is None :
2013-03-18 01:06:52 +04:00
logging . debug ( " Couldn ' t lookup storage volume in prog thread. " )
return
while not self . _install_finished :
ignore , ignore , alloc = vol . info ( )
meter . update ( alloc )
2013-06-09 23:19:35 +04:00
time . sleep ( 1 )
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
def is_size_conflict ( self , capacity = None , allocation = None ) :
2013-03-18 01:06:52 +04:00
"""
Report if requested size exceeds its pool ' s available amount
@returns : 2 element tuple :
1. True if collision is fatal , false otherwise
2. String message if some collision was encountered .
@rtype : 2 element C { tuple } : ( C { bool } , C { str } )
"""
2013-09-20 04:18:12 +04:00
if capacity is None :
capacity = self . capacity
if allocation is None :
allocation = self . allocation
if not self . pool :
return ( False , " " )
2013-04-13 22:34:52 +04:00
# pool info is [pool state, capacity, allocation, available]
2013-03-18 01:06:52 +04:00
avail = self . pool . info ( ) [ 3 ]
2013-09-20 04:18:12 +04:00
if allocation > avail :
2013-03-18 01:06:52 +04:00
return ( True , _ ( " There is not enough free space on the storage "
" pool to create the volume. "
2013-04-13 22:34:52 +04:00
" ( %d M requested allocation > %d M available) " %
2013-09-20 04:18:12 +04:00
( ( allocation / ( 1024 * 1024 ) ) ,
2013-03-18 01:06:52 +04:00
( avail / ( 1024 * 1024 ) ) ) ) )
2013-09-20 04:18:12 +04:00
elif capacity > avail :
2013-03-18 01:06:52 +04:00
return ( False , _ ( " The requested volume capacity will exceed the "
" available pool space when the volume is fully "
" allocated. "
2013-04-13 22:34:52 +04:00
" ( %d M requested capacity > %d M available) " %
2013-09-20 04:18:12 +04:00
( ( capacity / ( 1024 * 1024 ) ) ,
2013-03-18 01:06:52 +04:00
( avail / ( 1024 * 1024 ) ) ) ) )
return ( False , " " )