2013-03-18 01:06:52 +04:00
#
2015-03-31 18:59:06 +03:00
# Copyright 2008, 2013, 2015 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
import os
2018-02-23 03:12:23 +03:00
import threading
2013-03-18 01:06:52 +04:00
import libvirt
2019-06-08 01:16:53 +03:00
from . import generatename
2019-06-08 00:32:51 +03:00
from . import progress
2019-06-17 04:12:39 +03:00
from . logger import log
2019-06-08 00:32:51 +03:00
from . xmlbuilder import XMLBuilder , XMLChildProperty , XMLProperty
2013-03-18 01:06:52 +04:00
2015-05-03 02:54:14 +03:00
_DEFAULT_DEV_TARGET = " /dev "
_DEFAULT_SCSI_TARGET = " /dev/disk/by-path "
_DEFAULT_MPATH_TARGET = " /dev/mapper "
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
class _StoragePermissions ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " permissions "
2013-09-20 04:18:12 +04:00
_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
##############
# Properties #
##############
2013-03-18 01:06:52 +04:00
2018-09-03 22:45:26 +03:00
name = XMLProperty ( " ./name " )
2013-09-20 04:18:12 +04:00
permissions = XMLChildProperty ( _StoragePermissions ,
relative_xpath = " ./target " ,
is_single = True )
2013-03-18 01:06:52 +04:00
2019-06-11 14:30:34 +03:00
def _preferred_default_pool_path ( conn ) :
2014-02-06 04:09:26 +04:00
path = " /var/lib/libvirt/images "
if conn . is_session_uri ( ) :
2015-08-10 19:35:13 +03:00
path = os . path . expanduser ( " ~/.local/share/libvirt/images " )
2014-02-06 04:09:26 +04:00
return path
2019-06-11 14:30:34 +03:00
def _lookup_poolxml_by_path ( conn , path ) :
for poolxml in conn . fetch_all_pools ( ) :
xml_path = poolxml . target_path
if xml_path is not None and os . path . abspath ( xml_path ) == path :
return poolxml
return None
2014-12-10 00:05:02 +03:00
class _Host ( XMLBuilder ) :
_XML_PROP_ORDER = [ " name " , " port " ]
2018-03-21 17:53:34 +03:00
XML_NAME = " host "
2014-12-10 00:05:02 +03:00
name = XMLProperty ( " ./@name " )
port = XMLProperty ( " ./@port " , is_int = True )
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
"""
TYPE_DIR = " dir "
TYPE_FS = " fs "
TYPE_NETFS = " netfs "
TYPE_LOGICAL = " logical "
TYPE_DISK = " disk "
TYPE_ISCSI = " iscsi "
TYPE_SCSI = " scsi "
TYPE_MPATH = " mpath "
2014-02-12 13:39:50 +04:00
TYPE_GLUSTER = " gluster "
2014-12-10 00:05:02 +03:00
TYPE_RBD = " rbd "
TYPE_SHEEPDOG = " sheepdog "
2016-08-09 18:48:34 +03:00
TYPE_ZFS = " zfs "
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
@staticmethod
2019-07-04 00:55:01 +03:00
def pool_list_from_sources ( conn , pool_type ) :
2013-03-18 01:06:52 +04:00
"""
Return a list of StoragePool instances built from libvirt ' s pool
source enumeration ( if supported ) .
2018-02-14 15:17:31 +03:00
: param conn : Libvirt connection
: param pool_type : Pool type string from I { Types }
2013-03-18 01:06:52 +04:00
"""
2019-07-04 00:55:01 +03:00
source_xml = " <source/> "
2013-03-18 01:06:52 +04:00
try :
xml = conn . findStoragePoolSources ( pool_type , source_xml , 0 )
2019-07-04 00:55:01 +03:00
except Exception as e : # pragma: no cover
2019-06-07 23:48:21 +03:00
if conn . support . is_error_nosupport ( e ) :
2013-03-18 01:06:52 +04:00
return [ ]
2019-07-04 00:55:01 +03:00
raise
log . debug ( " Libvirt returned pool sources XML: \n %s " , xml )
import xml . etree . ElementTree as ET
root = ET . fromstring ( xml )
# We implicitly only support this for pool TYPE_LOGICAL
ret = [ e . text for e in root . findall ( " ./source/name " ) ]
log . debug ( " Sources returning: %s " , ret )
2015-04-03 23:22:25 +03:00
return ret
2013-09-20 04:18:12 +04:00
@staticmethod
2019-07-03 02:44:10 +03:00
def lookup_default_pool ( conn ) :
"""
Helper to lookup the default pool . It will return one of
* The pool named ' default '
* If that doesn ' t exist, the pool pointing to the default path
* Otherwise None
"""
name = " default "
path = _preferred_default_pool_path ( conn )
poolxml = None
for trypool in conn . fetch_all_pools ( ) :
if trypool . name == name :
poolxml = trypool
break
else :
poolxml = _lookup_poolxml_by_path ( conn , path )
if poolxml :
log . debug ( " Found default pool name= %s target= %s " ,
poolxml . name , poolxml . target_path )
return poolxml
@staticmethod
def build_default_pool ( conn ) :
2013-09-20 04:18:12 +04:00
"""
2019-06-11 14:30:34 +03:00
Attempt to lookup the ' default ' pool , but if it doesn ' t exist,
create it
2013-09-20 04:18:12 +04:00
"""
2019-07-03 02:44:10 +03:00
poolxml = StoragePool . lookup_default_pool ( conn )
2019-06-11 14:30:34 +03:00
if poolxml :
2019-03-26 18:12:31 +03:00
return poolxml
2013-09-20 04:18:12 +04:00
try :
2019-06-11 14:30:34 +03:00
name = " default "
path = _preferred_default_pool_path ( conn )
2019-06-17 04:12:39 +03:00
log . debug ( " Attempting to build default pool with target ' %s ' " ,
2013-09-20 04:18:12 +04:00
path )
defpool = StoragePool ( conn )
defpool . type = defpool . TYPE_DIR
defpool . name = name
defpool . target_path = path
2014-02-06 04:09:26 +04:00
defpool . install ( build = True , create = True , autostart = True )
return defpool
2019-07-03 02:38:09 +03:00
except Exception as e : # pragma: no cover
2013-09-20 04:18:12 +04:00
raise RuntimeError (
_ ( " Couldn ' t create default storage pool ' %s ' : %s " ) %
( path , str ( e ) ) )
2013-09-23 01:34:53 +04:00
@staticmethod
2014-12-05 03:52:32 +03:00
def lookup_pool_by_path ( conn , path ) :
2013-09-23 01:34:53 +04:00
"""
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 .
2014-09-12 21:04:22 +04:00
2018-02-14 15:17:31 +03:00
: returns : virStoragePool object if found , None otherwise
2013-09-23 01:34:53 +04:00
"""
2019-06-11 14:30:34 +03:00
poolxml = _lookup_poolxml_by_path ( conn , path )
if not poolxml :
2013-09-23 01:34:53 +04:00
return None
2019-06-11 14:30:34 +03:00
return conn . storagePoolLookupByName ( poolxml . name )
2013-09-23 01:34:53 +04:00
2014-02-09 01:36:45 +04:00
@staticmethod
def find_free_name ( conn , basename , * * kwargs ) :
"""
Finds a name similar ( or equal ) to passed ' basename ' that is not
in use by another pool . Extra params are passed to generate_name
"""
2017-07-19 02:09:58 +03:00
def cb ( name ) :
for pool in conn . fetch_all_pools ( ) :
if pool . name == name :
return True
return False
2019-06-08 01:16:53 +03:00
return generatename . generate_name ( basename , cb , * * kwargs )
2013-09-23 01:34:53 +04:00
2019-04-15 02:16:10 +03:00
@staticmethod
def ensure_pool_is_running ( pool_object , refresh = False ) :
"""
If the passed vmmStoragePool isn ' t running, start it.
: param pool_object : vmmStoragePool to check / start
: param refresh : If True , run refresh ( ) as well
"""
if pool_object . info ( ) [ 0 ] != libvirt . VIR_STORAGE_POOL_RUNNING :
2019-06-17 04:12:39 +03:00
log . debug ( " starting pool= %s " , pool_object . name ( ) )
2019-04-15 02:16:10 +03:00
pool_object . create ( 0 )
if refresh :
2019-06-17 04:12:39 +03:00
log . debug ( " refreshing pool= %s " , pool_object . name ( ) )
2019-04-15 02:16:10 +03:00
pool_object . refresh ( 0 )
2013-09-20 04:18:12 +04:00
######################
# Validation helpers #
######################
2018-09-03 22:45:26 +03:00
@staticmethod
def validate_name ( conn , name ) :
2019-06-08 01:02:42 +03:00
XMLBuilder . validate_generic_name ( _ ( " Storage object " ) , name )
2018-09-03 22:45:26 +03:00
2013-03-18 01:06:52 +04:00
try :
2018-09-03 22:45:26 +03:00
conn . storagePoolLookupByName ( name )
2013-03-18 01:06:52 +04:00
except libvirt . libvirtError :
2018-09-03 22:45:26 +03:00
return
raise ValueError ( _ ( " Name ' %s ' already in use by another pool. " %
2019-07-03 02:38:09 +03:00
name ) ) # pragma: no cover
2013-03-18 01:06:52 +04:00
2018-09-02 21:47:34 +03:00
def default_target_path ( self ) :
2019-07-03 00:03:39 +03:00
if not self . supports_target_path ( ) :
2014-02-12 13:29:22 +04:00
return None
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 ) :
2019-06-11 15:23:59 +03:00
return os . path . join (
_preferred_default_pool_path ( self . conn ) , self . name )
2013-09-20 04:18:12 +04:00
if self . type == self . TYPE_ISCSI or self . type == self . TYPE_SCSI :
2015-05-03 02:54:14 +03:00
return _DEFAULT_SCSI_TARGET
2013-09-20 04:18:12 +04:00
if self . type == self . TYPE_MPATH :
2015-05-03 02:54:14 +03:00
return _DEFAULT_MPATH_TARGET
2013-09-20 04:18:12 +04:00
2015-05-06 20:54:00 +03:00
def _type_to_source_prop ( self ) :
2014-12-10 00:05:02 +03:00
if ( self . type == self . TYPE_NETFS or
self . type == self . TYPE_GLUSTER ) :
2015-05-06 20:54:00 +03:00
return " _source_dir "
elif self . type == self . TYPE_SCSI :
return " _source_adapter "
else :
return " _source_device "
def _get_source ( self ) :
return getattr ( self , self . _type_to_source_prop ( ) )
def _set_source ( self , val ) :
return setattr ( self , self . _type_to_source_prop ( ) , val )
source_path = property ( _get_source , _set_source )
2013-09-20 04:18:12 +04:00
2018-09-02 21:47:34 +03:00
def default_source_name ( self ) :
2019-07-03 00:03:39 +03:00
if not self . supports_source_name ( ) :
2019-07-03 02:55:54 +03:00
return None
if self . type == StoragePool . TYPE_RBD :
return " rbd "
if self . type == StoragePool . TYPE_GLUSTER :
return " gv0 "
2013-09-20 04:18:12 +04:00
##############
# Properties #
##############
2018-03-21 17:53:34 +03:00
XML_NAME = " pool "
2013-09-20 04:18:12 +04:00
_XML_PROP_ORDER = [ " name " , " type " , " uuid " ,
" capacity " , " allocation " , " available " ,
2014-12-10 00:05:02 +03:00
" format " , " hosts " ,
2015-05-06 20:54:00 +03:00
" _source_dir " , " _source_adapter " , " _source_device " ,
" source_name " , " target_path " ,
2018-01-26 13:27:20 +03:00
" permissions " ,
" auth_type " , " auth_username " , " auth_secret_uuid " ]
2013-09-20 04:18:12 +04:00
2015-05-06 20:54:00 +03:00
_source_dir = XMLProperty ( " ./source/dir/@path " )
_source_adapter = XMLProperty ( " ./source/adapter/@name " )
_source_device = XMLProperty ( " ./source/device/@path " )
2018-02-23 04:44:09 +03:00
type = XMLProperty ( " ./@type " )
2017-12-14 20:12:35 +03:00
uuid = XMLProperty ( " ./uuid " )
2013-09-20 04:18:12 +04:00
capacity = XMLProperty ( " ./capacity " , is_int = True )
allocation = XMLProperty ( " ./allocation " , is_int = True )
available = XMLProperty ( " ./available " , is_int = True )
2018-09-02 21:47:34 +03:00
format = XMLProperty ( " ./source/format/@type " )
2018-02-23 04:44:09 +03:00
iqn = XMLProperty ( " ./source/initiator/iqn/@name " )
2018-09-02 21:47:34 +03:00
source_name = XMLProperty ( " ./source/name " )
2013-09-20 04:18:12 +04:00
2018-01-26 13:27:20 +03:00
auth_type = XMLProperty ( " ./source/auth/@type " )
auth_username = XMLProperty ( " ./source/auth/@username " )
auth_secret_uuid = XMLProperty ( " ./source/auth/secret/@uuid " )
2018-09-02 21:47:34 +03:00
target_path = XMLProperty ( " ./target/path " )
2014-12-10 00:05:02 +03:00
hosts = XMLChildProperty ( _Host , relative_xpath = " ./source " )
2013-09-20 04:18:12 +04:00
######################
# Public API helpers #
######################
2019-07-03 00:03:39 +03:00
def supports_target_path ( self ) :
return self . type in [
self . TYPE_DIR , self . TYPE_FS , self . TYPE_NETFS ,
2019-07-04 01:26:57 +03:00
self . TYPE_ISCSI ,
2019-07-03 00:03:39 +03:00
self . TYPE_SCSI , self . TYPE_MPATH ]
def supports_source_name ( self ) :
return self . type in [ self . TYPE_LOGICAL , self . TYPE_GLUSTER ,
self . TYPE_RBD , self . TYPE_SHEEPDOG , self . TYPE_ZFS ]
def supports_source_path ( self ) :
return self . type in [
2019-06-17 03:10:37 +03:00
self . TYPE_FS , self . TYPE_NETFS ,
2019-07-03 00:03:39 +03:00
self . TYPE_DISK , self . TYPE_ISCSI , self . TYPE_SCSI ,
self . TYPE_GLUSTER ]
def supports_hosts ( self ) :
return self . type in [
self . TYPE_NETFS , self . TYPE_ISCSI , self . TYPE_GLUSTER ,
self . TYPE_RBD , self . TYPE_SHEEPDOG ]
def supports_format ( self ) :
return self . type in [ self . TYPE_FS , self . TYPE_NETFS , self . TYPE_DISK ]
def supports_iqn ( self ) :
return self . type in [ self . TYPE_ISCSI ]
2013-09-20 04:18:12 +04:00
2017-03-06 11:28:48 +03:00
def get_disk_type ( self ) :
if ( self . type == StoragePool . TYPE_DISK or
self . type == StoragePool . TYPE_LOGICAL or
self . type == StoragePool . TYPE_SCSI or
self . type == StoragePool . TYPE_MPATH or
self . type == StoragePool . TYPE_ZFS ) :
return StorageVolume . TYPE_BLOCK
if ( self . type == StoragePool . TYPE_GLUSTER or
self . type == StoragePool . TYPE_RBD or
self . type == StoragePool . TYPE_ISCSI or
self . type == StoragePool . TYPE_SHEEPDOG ) :
return StorageVolume . TYPE_NETWORK
return StorageVolume . TYPE_FILE
2013-09-20 04:18:12 +04:00
2018-09-02 21:47:34 +03:00
2013-09-20 04:18:12 +04:00
##################
# Build routines #
##################
def validate ( self ) :
2018-09-03 22:45:26 +03:00
self . validate_name ( self . conn , self . name )
2018-09-02 21:47:34 +03:00
if not self . target_path :
2019-07-04 01:26:57 +03:00
if self . type == self . TYPE_DISK :
# disk is a bit special, in that it demands a target path,
# but basically can't handle anything other than /dev
self . target_path = _DEFAULT_DEV_TARGET
else :
self . target_path = self . default_target_path ( )
2018-09-02 21:47:34 +03:00
if not self . source_name :
self . source_name = self . default_source_name ( )
2019-07-03 00:03:39 +03:00
if not self . format and self . supports_format ( ) :
2018-09-02 21:47:34 +03:00
self . format = " auto "
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 .
"""
2018-08-31 23:52:02 +03:00
xml = self . get_xml ( )
2019-06-17 04:12:39 +03:00
log . debug ( " Creating storage pool ' %s ' with xml: \n %s " ,
2013-03-18 01:06:52 +04:00
self . name , xml )
2019-06-08 00:32:51 +03:00
meter = progress . ensure_meter ( meter )
2013-03-18 01:06:52 +04:00
try :
pool = self . conn . storagePoolDefineXML ( xml , 0 )
2019-07-03 02:38:09 +03:00
except Exception as e : # pragma: no cover
2015-06-02 15:21:58 +03:00
raise RuntimeError ( _ ( " Could not define storage pool: %s " ) % str ( e ) )
2013-03-18 01:06:52 +04:00
errmsg = None
if build :
try :
pool . build ( libvirt . VIR_STORAGE_POOL_BUILD_NEW )
2019-07-03 02:38:09 +03:00
except Exception as e : # pragma: no cover
2015-06-02 15:21:58 +03:00
errmsg = _ ( " Could not build storage pool: %s " ) % str ( e )
2013-03-18 01:06:52 +04:00
if create and not errmsg :
try :
pool . create ( 0 )
2019-07-03 02:38:09 +03:00
except Exception as e : # pragma: no cover
2015-06-02 15:21:58 +03:00
errmsg = _ ( " Could not start storage pool: %s " ) % str ( e )
2013-03-18 01:06:52 +04:00
if autostart and not errmsg :
try :
pool . setAutostart ( True )
2019-07-03 02:38:09 +03:00
except Exception as e : # pragma: no cover
2015-06-02 15:21:58 +03:00
errmsg = _ ( " Could not set pool autostart flag: %s " ) % str ( e )
2013-03-18 01:06:52 +04:00
2019-07-03 02:38:09 +03:00
if errmsg : # pragma: no cover
2013-03-18 01:06:52 +04:00
# Try and clean up the leftover pool
try :
pool . undefine ( )
2017-05-05 19:47:21 +03:00
except Exception as e :
2019-06-17 04:12:39 +03:00
log . debug ( " Error cleaning up pool after failure: %s " ,
2018-03-02 11:01:23 +03:00
str ( e ) )
2013-03-18 01:06:52 +04:00
raise RuntimeError ( errmsg )
2017-07-19 23:11:17 +03:00
self . conn . cache_new_pool ( pool )
2017-05-19 15:26:49 +03:00
2013-03-18 01:06:52 +04:00
return pool
2019-07-17 19:57:38 +03:00
def _progress_thread ( volname , pool , meter , event ) :
vol = None
if not meter :
2019-08-01 00:53:51 +03:00
return # pragma: no cover
2019-07-17 19:57:38 +03:00
while True :
try :
if not vol :
vol = pool . storageVolLookupByName ( volname )
vol . info ( ) # pragma: no cover
break # pragma: no cover
except Exception :
if event . wait ( .2 ) :
break
if vol is None :
log . debug ( " Couldn ' t lookup storage volume in prog thread. " )
return
while True : # pragma: no cover
ignore , ignore , alloc = vol . info ( )
meter . update ( alloc )
if event . wait ( 1 ) :
break
2013-03-18 01:06:52 +04:00
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-02-05 02:30:24 +04:00
@staticmethod
def get_file_extension_for_format ( fmt ) :
if not fmt :
return " "
if fmt == " raw " :
return " .img "
return " . " + fmt
2013-09-20 04:18:12 +04:00
@staticmethod
2019-06-11 15:52:12 +03:00
def find_free_name ( conn , pool_object , basename , collideguest = None , * * 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
2014-02-09 01:36:45 +04:00
in use by another volume . Extra params are passed to generate_name
2019-06-11 15:52:12 +03:00
: param collideguest : Guest object . If specified , also check to
ensure we don ' t collide with any disk paths there
2013-03-18 01:06:52 +04:00
"""
2019-06-11 15:52:12 +03:00
collidelist = [ ]
if collideguest :
pooltarget = None
poolname = pool_object . name ( )
for poolxml in conn . fetch_all_pools ( ) :
if poolxml . name == poolname :
pooltarget = poolxml . target_path
break
for disk in collideguest . devices . disk :
if ( pooltarget and disk . path and
os . path . dirname ( disk . path ) == pooltarget ) :
collidelist . append ( os . path . basename ( disk . path ) )
2019-06-11 15:56:50 +03:00
def cb ( tryname ) :
if tryname in collidelist :
return True
2019-06-11 17:05:15 +03:00
return generatename . check_libvirt_collision (
pool_object . storageVolLookupByName , tryname )
2019-06-11 15:56:50 +03:00
2019-04-15 02:16:10 +03:00
StoragePool . ensure_pool_is_running ( pool_object , refresh = True )
2019-06-11 15:56:50 +03:00
return generatename . generate_name ( basename , cb , * * 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 )
2014-12-10 00:05:02 +03:00
TYPE_DIR = getattr ( libvirt , " VIR_STORAGE_VOL_DIR " , 2 )
2014-12-10 20:55:08 +03:00
TYPE_NETWORK = getattr ( libvirt , " VIR_STORAGE_VOL_NETWORK " , 3 )
TYPE_NETDIR = getattr ( libvirt , " VIR_STORAGE_VOL_NETDIR " , 4 )
2013-09-20 04:18:12 +04:00
def __init__ ( self , * args , * * kwargs ) :
_StorageObject . __init__ ( self , * args , * * kwargs )
self . _input_vol = None
self . _pool = None
2014-12-10 00:05:02 +03:00
self . _pool_xml = None
2015-02-07 05:18:05 +03:00
self . _reflink = False
2013-09-20 04:18:12 +04:00
######################
# 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 ) :
2019-04-15 02:16:10 +03:00
StoragePool . ensure_pool_is_running ( newpool )
2013-03-18 01:06:52 +04:00
self . _pool = newpool
2014-12-10 00:05:02 +03:00
self . _pool_xml = StoragePool ( self . conn ,
parsexml = self . _pool . XMLDesc ( 0 ) )
2013-09-20 04:18:12 +04:00
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
self . _input_vol = vol
2018-02-23 04:44:09 +03:00
input_vol = property ( _get_input_vol , _set_input_vol )
2013-03-18 01:06:52 +04:00
2015-02-07 05:18:05 +03:00
def _get_reflink ( self ) :
return self . _reflink
def _set_reflink ( self , reflink ) :
self . _reflink = reflink
2018-02-23 04:44:09 +03:00
reflink = property ( _get_reflink , _set_reflink )
2015-02-07 05:18:05 +03:00
2015-03-31 18:59:06 +03:00
def sync_input_vol ( self , only_format = False ) :
2014-07-28 12:55:28 +04:00
# Pull parameters from input vol into this class
2013-09-20 04:18:12 +04:00
parsevol = StorageVolume ( self . conn ,
parsexml = self . _input_vol . XMLDesc ( 0 ) )
2015-03-31 18:59:06 +03:00
self . format = parsevol . format
2015-04-10 13:59:20 +03:00
self . capacity = parsevol . capacity
self . allocation = parsevol . allocation
2015-03-31 18:59:06 +03:00
if only_format :
return
2013-09-20 04:18:12 +04:00
self . pool = self . _input_vol . storagePoolLookupByVolume ( )
##########################
# XML validation helpers #
##########################
2013-03-18 01:06:52 +04:00
2018-09-03 22:45:26 +03:00
@staticmethod
def validate_name ( pool , name ) :
2019-06-08 01:02:42 +03:00
XMLBuilder . validate_generic_name ( _ ( " Storage object " ) , name )
2018-09-03 22:45:26 +03:00
2013-03-18 01:06:52 +04:00
try :
2018-09-03 22:45:26 +03:00
pool . storageVolLookupByName ( name )
2013-03-18 01:06:52 +04:00
except libvirt . libvirtError :
2018-09-03 22:45:26 +03:00
return
raise ValueError ( _ ( " Name ' %s ' already in use by another volume. " %
2019-07-03 02:38:09 +03:00
name ) ) # pragma: no cover
2013-03-18 01:06:52 +04:00
2013-09-20 04:18:12 +04:00
def _get_vol_type ( self ) :
2019-07-03 02:38:09 +03:00
if self . type : # pragma: no cover
2016-06-17 03:36:30 +03:00
if self . type == " file " :
return self . TYPE_FILE
elif self . type == " block " :
return self . TYPE_BLOCK
elif self . type == " dir " :
return self . TYPE_DIR
elif self . type == " network " :
return self . TYPE_NETWORK
2017-03-06 11:28:48 +03:00
return self . _pool_xml . get_disk_type ( )
2013-09-20 04:18:12 +04:00
file_type = property ( _get_vol_type )
##################
# XML properties #
##################
2018-03-21 17:53:34 +03:00
XML_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 " ]
2014-12-10 00:05:02 +03:00
type = XMLProperty ( " ./@type " )
2013-12-05 18:17:12 +04:00
key = XMLProperty ( " ./key " )
2017-03-17 19:00:03 +03:00
capacity = XMLProperty ( " ./capacity " , is_int = True )
allocation = XMLProperty ( " ./allocation " , is_int = True )
2018-09-02 21:47:34 +03:00
format = XMLProperty ( " ./target/format/@type " )
2013-09-20 04:18:12 +04:00
target_path = XMLProperty ( " ./target/path " )
2013-09-29 03:11:27 +04:00
backing_store = XMLProperty ( " ./backingStore/path " )
2016-06-17 03:08:53 +03:00
backing_format = XMLProperty ( " ./backingStore/format/@type " )
2018-09-02 21:47:34 +03:00
lazy_refcounts = XMLProperty (
" ./target/features/lazy_refcounts " , is_bool = True )
2014-02-03 23:52:23 +04:00
2013-03-18 01:06:52 +04:00
2016-06-17 03:36:30 +03:00
def _detect_backing_store_format ( self ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Attempting to detect format for backing_store= %s " ,
2016-06-17 03:36:30 +03:00
self . backing_store )
2018-10-12 02:23:18 +03:00
from . import diskbackend
vol , pool = diskbackend . manage_path ( self . conn , self . backing_store )
2016-06-17 03:36:30 +03:00
2019-07-03 02:38:09 +03:00
if not vol : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Didn ' t find any volume for backing_store " )
2016-06-17 03:36:30 +03:00
return None
# Only set backing format for volumes that support
# the 'format' parameter as we know it, like qcow2 etc.
volxml = StorageVolume ( self . conn , vol . XMLDesc ( 0 ) )
volxml . pool = pool
2019-06-17 04:12:39 +03:00
log . debug ( " Found backing store volume XML: \n %s " ,
2018-08-31 23:52:02 +03:00
volxml . get_xml ( ) )
2016-06-17 03:36:30 +03:00
2019-07-03 02:38:09 +03:00
if not volxml . supports_format ( ) : # pragma: no cover
log . debug ( " backing_store volume doesn ' t appear to have "
" a file format we can specify, returning None " )
return None
2016-06-17 03:36:30 +03:00
2019-07-03 02:38:09 +03:00
log . debug ( " Returning format= %s " , volxml . format )
return volxml . format
2016-06-17 03:36:30 +03:00
2013-09-20 04:18:12 +04:00
######################
# Public API helpers #
######################
2019-07-02 23:56:35 +03:00
def supports_format ( self ) :
2014-12-10 00:05:02 +03:00
if self . file_type == self . TYPE_FILE :
return True
if self . _pool_xml . type == StoragePool . TYPE_GLUSTER :
return True
return False
2013-09-20 04:18:12 +04:00
##################
# Build routines #
##################
def validate ( self ) :
2018-09-03 22:45:26 +03:00
self . validate_name ( self . pool , self . name )
2018-09-02 21:47:34 +03:00
if not self . format and self . file_type == self . TYPE_FILE :
self . format = " raw "
if self . _prop_is_unset ( " lazy_refcounts " ) and self . format == " qcow2 " :
2019-06-07 23:06:52 +03:00
self . lazy_refcounts = self . conn . support . conn_qcow2_lazy_refcounts ( )
2018-09-02 21:47:34 +03:00
2014-12-10 00:05:02 +03:00
if self . _pool_xml . type == StoragePool . TYPE_LOGICAL :
2013-09-20 04:18:12 +04:00
if self . allocation != self . capacity :
2019-06-17 04:12:39 +03:00
log . warning ( _ ( " Sparse logical volumes are not supported, "
2013-09-20 04:18:12 +04:00
" setting allocation equal to capacity " ) )
self . allocation = self . capacity
2013-03-18 01:06:52 +04:00
2017-03-17 19:00:03 +03:00
isfatal , errmsg = self . is_size_conflict ( )
if isfatal :
raise ValueError ( errmsg )
if errmsg :
2019-06-17 04:12:39 +03:00
log . warning ( errmsg )
2017-03-17 19:00:03 +03:00
2013-03-18 01:06:52 +04:00
def install ( self , meter = None ) :
"""
Build and install storage volume from xml
"""
2016-06-17 03:36:30 +03:00
if self . backing_store and not self . backing_format :
self . backing_format = self . _detect_backing_store_format ( )
2018-08-31 23:52:02 +03:00
xml = self . get_xml ( )
2019-06-17 04:12:39 +03:00
log . debug ( " Creating storage volume ' %s ' with xml: \n %s " ,
2013-03-18 01:06:52 +04:00
self . name , xml )
2013-10-01 22:29:58 +04:00
cloneflags = 0
createflags = 0
if ( self . format == " qcow2 " and
not self . backing_store and
2019-06-07 23:06:52 +03:00
self . conn . support . pool_metadata_prealloc ( self . pool ) ) :
2013-10-01 22:29:58 +04:00
createflags | = libvirt . VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
2019-03-04 21:20:51 +03:00
if self . capacity == self . allocation :
# For cloning, this flag will make libvirt+qemu-img preallocate
# the new disk image
cloneflags | = libvirt . VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA
2013-10-01 22:29:58 +04:00
2015-02-07 05:18:05 +03:00
if self . reflink :
2015-02-22 00:28:19 +03:00
cloneflags | = getattr ( libvirt ,
" VIR_STORAGE_VOL_CREATE_REFLINK " , 1 )
2015-02-07 05:18:05 +03:00
2019-07-17 19:57:38 +03:00
event = threading . Event ( )
meter = progress . ensure_meter ( meter )
t = threading . Thread ( target = _progress_thread ,
name = " Checking storage allocation " ,
args = ( self . name , self . pool , meter , event ) )
t . setDaemon ( True )
2013-03-18 01:06:52 +04:00
try :
2013-06-09 23:40:25 +04:00
t . start ( )
meter . start ( size = self . capacity ,
text = _ ( " Allocating ' %s ' " ) % self . name )
2019-07-03 02:38:09 +03:00
if self . conn . is_really_test ( ) :
# Test suite doesn't support any flags, so reset them
createflags = 0
cloneflags = 0
2013-06-09 23:40:25 +04:00
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 :
2019-06-17 04:12:39 +03:00
log . debug ( " Using vol create flags= %s " , createflags )
2013-10-01 22:29:58 +04:00
vol = self . pool . createXML ( xml , createflags )
2013-06-09 23:40:25 +04:00
meter . end ( self . capacity )
2019-07-03 02:38:09 +03:00
log . debug ( " Storage volume ' %s ' install complete. " , self . name )
2013-06-09 23:40:25 +04:00
return vol
2017-05-05 19:47:21 +03:00
except Exception as e :
2019-06-17 04:12:39 +03:00
log . debug ( " Error creating storage volume " , exc_info = True )
2013-06-09 23:40:25 +04:00
raise RuntimeError ( " Couldn ' t create storage volume "
" ' %s ' : ' %s ' " % ( self . name , str ( e ) ) )
2019-07-17 19:57:38 +03:00
finally :
event . set ( )
t . join ( )
2013-03-18 01:06:52 +04:00
2017-03-17 19:00:03 +03:00
def is_size_conflict ( self ) :
2013-03-18 01:06:52 +04:00
"""
Report if requested size exceeds its pool ' s available amount
2018-02-14 15:17:31 +03:00
: returns : 2 element tuple :
2013-03-18 01:06:52 +04:00
1. True if collision is fatal , false otherwise
2. String message if some collision was encountered .
"""
2013-09-20 04:18:12 +04:00
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 ]
2017-03-17 19:00:03 +03:00
if self . 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. "
2016-07-26 10:34:20 +03:00
" ( %d M requested allocation > %d M available) " ) %
2017-10-11 14:35:55 +03:00
( ( self . allocation / / ( 1024 * 1024 ) ) ,
( avail / / ( 1024 * 1024 ) ) ) )
2017-03-17 19:00:03 +03:00
elif self . 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. "
2016-07-26 10:34:20 +03:00
" ( %d M requested capacity > %d M available) " ) %
2017-10-11 14:35:55 +03:00
( ( self . capacity / / ( 1024 * 1024 ) ) ,
( avail / / ( 1024 * 1024 ) ) ) )
2013-03-18 01:06:52 +04:00
return ( False , " " )