2013-03-18 01:06:52 +04:00
#
2015-03-31 18:59:06 +03:00
# Copyright 2013, 2015 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
# Copyright(c) FUJITSU Limited 2007.
#
# Cloning a virtual machine module.
#
# 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 logging
import re
import os
import libvirt
2014-09-12 23:59:22 +04:00
from . import util
from . guest import Guest
from . deviceinterface import VirtualNetworkInterface
from . devicedisk import VirtualDisk
from . storage import StorageVolume
2015-11-02 15:20:15 +03:00
from . devicechar import VirtualChannelDevice
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-07-03 02:30:46 +04:00
class Cloner ( object ) :
2013-03-18 01:06:52 +04:00
# Reasons why we don't default to cloning.
CLONE_POLICY_NO_READONLY = 1
CLONE_POLICY_NO_SHAREABLE = 2
CLONE_POLICY_NO_EMPTYMEDIA = 3
2013-07-03 02:30:46 +04:00
def __init__ ( self , conn ) :
self . conn = conn
2013-03-18 01:06:52 +04:00
# original guest name or uuid
2013-07-03 02:30:46 +04:00
self . _original_guest = None
self . original_dom = None
self . _original_disks = [ ]
self . _original_xml = None
self . _guest = None
2013-03-18 01:06:52 +04:00
# clone guest
2013-07-03 02:30:46 +04:00
self . _clone_name = None
self . _clone_disks = [ ]
self . _clone_macs = [ ]
self . _clone_uuid = None
self . _clone_sparse = True
self . _clone_xml = None
self . _force_target = [ ]
self . _skip_target = [ ]
self . _preserve = True
self . _clone_running = False
2013-08-09 22:16:59 +04:00
self . _replace = False
2015-02-07 05:18:05 +03:00
self . _reflink = False
2013-03-18 01:06:52 +04:00
# Default clone policy for back compat: don't clone readonly,
# shareable, or empty disks
2013-07-03 02:30:46 +04:00
self . _clone_policy = [ self . CLONE_POLICY_NO_READONLY ,
self . CLONE_POLICY_NO_SHAREABLE ,
self . CLONE_POLICY_NO_EMPTYMEDIA ]
2013-03-18 01:06:52 +04:00
# Generate a random UUID at the start
2013-04-11 18:27:02 +04:00
self . clone_uuid = util . generate_uuid ( conn )
2013-03-18 01:06:52 +04:00
# Getter/Setter methods
def get_original_guest ( self ) :
return self . _original_guest
def set_original_guest ( self , original_guest ) :
if self . _lookup_vm ( original_guest ) :
self . _original_guest = original_guest
original_guest = property ( get_original_guest , set_original_guest ,
doc = " Original guest name. " )
def set_original_xml ( self , val ) :
if type ( val ) is not str :
raise ValueError ( _ ( " Original xml must be a string. " ) )
self . _original_xml = val
2013-09-23 01:34:53 +04:00
self . _original_guest = Guest ( self . conn ,
parsexml = self . _original_xml ) . name
2013-03-18 01:06:52 +04:00
def get_original_xml ( self ) :
return self . _original_xml
original_xml = property ( get_original_xml , set_original_xml ,
doc = " XML of the original guest. " )
def get_clone_name ( self ) :
return self . _clone_name
def set_clone_name ( self , name ) :
try :
2013-08-09 22:16:59 +04:00
Guest . validate_name ( self . conn , name ,
2014-01-18 03:44:26 +04:00
check_collision = not self . replace ,
validate = False )
2013-03-18 01:06:52 +04:00
except ValueError , e :
raise ValueError ( _ ( " Invalid name for new guest: %s " ) % e )
self . _clone_name = name
clone_name = property ( get_clone_name , set_clone_name ,
doc = " Name to use for the new guest clone. " )
def set_clone_uuid ( self , uuid ) :
try :
2013-08-09 22:16:59 +04:00
util . validate_uuid ( uuid )
2013-03-18 01:06:52 +04:00
except ValueError , e :
raise ValueError ( _ ( " Invalid uuid for new guest: %s " ) % e )
2013-07-03 02:30:46 +04:00
if util . vm_uuid_collision ( self . conn , uuid ) :
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " UUID ' %s ' is in use by another guest. " ) %
uuid )
self . _clone_uuid = uuid
def get_clone_uuid ( self ) :
return self . _clone_uuid
clone_uuid = property ( get_clone_uuid , set_clone_uuid ,
doc = " UUID to use for the new guest clone " )
2013-07-03 02:30:46 +04:00
def set_clone_paths ( self , paths ) :
2013-03-18 01:06:52 +04:00
disklist = [ ]
2013-07-03 02:30:46 +04:00
for path in util . listify ( paths ) :
2013-03-18 01:06:52 +04:00
try :
device = VirtualDisk . DEVICE_DISK
if not path :
device = VirtualDisk . DEVICE_CDROM
2013-07-13 18:09:00 +04:00
disk = VirtualDisk ( self . conn )
disk . path = path
disk . device = device
2014-12-06 05:09:26 +03:00
if ( not self . preserve_dest_disks and
disk . wants_storage_creation ( ) ) :
vol_install = VirtualDisk . build_vol_install (
self . conn , os . path . basename ( disk . path ) ,
disk . get_parent_pool ( ) , .000001 , False )
disk . set_vol_install ( vol_install )
2013-07-13 18:09:00 +04:00
disk . validate ( )
2013-03-18 01:06:52 +04:00
disklist . append ( disk )
except Exception , e :
2013-07-13 18:09:00 +04:00
logging . debug ( " Error setting clone path. " , exc_info = True )
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " Could not use path ' %s ' for cloning: %s " ) %
( path , str ( e ) ) )
2013-07-03 02:30:46 +04:00
self . _clone_disks = disklist
def get_clone_paths ( self ) :
return [ d . path for d in self . clone_disks ]
clone_paths = property ( get_clone_paths , set_clone_paths ,
2013-03-18 01:06:52 +04:00
doc = " Paths to use for the new disk locations. " )
2013-07-03 02:30:46 +04:00
def get_clone_disks ( self ) :
return self . _clone_disks
clone_disks = property ( get_clone_disks ,
doc = " VirtualDisk instances for the new "
" disk paths " )
2013-03-18 01:06:52 +04:00
2013-07-03 02:30:46 +04:00
def set_clone_macs ( self , mac ) :
maclist = util . listify ( mac )
2013-03-18 01:06:52 +04:00
for m in maclist :
2013-07-15 22:14:05 +04:00
msg = VirtualNetworkInterface . is_conflict_net ( self . conn , m ) [ 1 ]
if msg :
raise RuntimeError ( msg )
2013-07-03 02:30:46 +04:00
self . _clone_macs = maclist
def get_clone_macs ( self ) :
return self . _clone_macs
clone_macs = property ( get_clone_macs , set_clone_macs ,
doc = " MAC address for the new guest clone. " )
def get_original_disks ( self ) :
return self . _original_disks
original_disks = property ( get_original_disks ,
doc = " VirtualDisk instances of the "
" original disks being cloned. " )
2013-03-18 01:06:52 +04:00
def get_clone_xml ( self ) :
return self . _clone_xml
def set_clone_xml ( self , clone_xml ) :
self . _clone_xml = clone_xml
clone_xml = property ( get_clone_xml , set_clone_xml ,
doc = " Generated XML for the guest clone. " )
def get_clone_sparse ( self ) :
return self . _clone_sparse
def set_clone_sparse ( self , flg ) :
self . _clone_sparse = flg
clone_sparse = property ( get_clone_sparse , set_clone_sparse ,
doc = " Whether to attempt sparse allocation during "
" cloning. " )
def get_preserve ( self ) :
return self . _preserve
def set_preserve ( self , flg ) :
self . _preserve = flg
preserve = property ( get_preserve , set_preserve ,
doc = " If true, preserve ALL original disk devices. " )
def get_preserve_dest_disks ( self ) :
return not self . preserve
preserve_dest_disks = property ( get_preserve_dest_disks ,
2014-12-15 04:42:08 +03:00
doc = " If true, preserve ALL disk devices for the "
2013-03-18 01:06:52 +04:00
" NEW guest. This means no storage cloning. "
" This is a convenience access for "
2013-07-03 02:30:46 +04:00
" (not Cloner.preserve) " )
2013-03-18 01:06:52 +04:00
def set_force_target ( self , dev ) :
if type ( dev ) is list :
self . _force_target = dev [ : ]
else :
self . _force_target . append ( dev )
def get_force_target ( self ) :
return self . _force_target
force_target = property ( get_force_target , set_force_target ,
doc = " List of disk targets that we force cloning "
2013-07-03 02:30:46 +04:00
" despite Cloner ' s recommendation. " )
2013-03-18 01:06:52 +04:00
def set_skip_target ( self , dev ) :
if type ( dev ) is list :
self . _skip_target = dev [ : ]
else :
self . _skip_target . append ( dev )
def get_skip_target ( self ) :
return self . _skip_target
skip_target = property ( get_skip_target , set_skip_target ,
doc = " List of disk targets that we skip cloning "
2013-07-03 02:30:46 +04:00
" despite Cloner ' s recommendation. This "
2013-03-18 01:06:52 +04:00
" takes precedence over force_target. " )
def set_clone_policy ( self , policy_list ) :
if type ( policy_list ) != list :
raise ValueError ( _ ( " Cloning policy must be a list of rules. " ) )
self . _clone_policy = policy_list
def get_clone_policy ( self ) :
return self . _clone_policy
clone_policy = property ( get_clone_policy , set_clone_policy ,
doc = " List of policy rules for determining which "
" vm disks to clone. See CLONE_POLICY_* " )
def get_clone_running ( self ) :
return self . _clone_running
def set_clone_running ( self , val ) :
self . _clone_running = bool ( val )
clone_running = property ( get_clone_running , set_clone_running ,
doc = " Allow cloning a running VM. If enabled, "
" domain state is not checked before "
" cloning. " )
def _get_replace ( self ) :
2013-08-09 22:16:59 +04:00
return self . _replace
2013-03-18 01:06:52 +04:00
def _set_replace ( self , val ) :
2013-08-09 22:16:59 +04:00
self . _replace = bool ( val )
2013-03-18 01:06:52 +04:00
replace = property ( _get_replace , _set_replace ,
2013-07-03 02:30:46 +04:00
doc = " If enabled, don ' t check for clone name collision, "
2013-03-18 01:06:52 +04:00
" simply undefine any conflicting guest. " )
2015-02-07 05:18:05 +03:00
def _get_reflink ( self ) :
return self . _reflink
def _set_reflink ( self , reflink ) :
self . _reflink = reflink
reflink = property ( _get_reflink , _set_reflink ,
doc = " If true, use COW lightweight copy " )
2013-03-18 01:06:52 +04:00
# Functional methods
def setup_original ( self ) :
"""
Validate and setup all parameters needed for the original ( cloned ) VM
"""
logging . debug ( " Validating original guest parameters " )
2013-04-13 22:34:52 +04:00
if self . original_guest is None and self . original_xml is None :
2013-03-18 01:06:52 +04:00
raise RuntimeError ( _ ( " Original guest name or xml is required. " ) )
2013-04-13 22:34:52 +04:00
if self . original_guest is not None and not self . original_xml :
2013-07-03 02:30:46 +04:00
self . original_dom = self . _lookup_vm ( self . original_guest )
self . original_xml = self . original_dom . XMLDesc ( 0 )
2013-03-18 01:06:52 +04:00
logging . debug ( " Original XML: \n %s " , self . original_xml )
2013-07-13 18:09:00 +04:00
self . _guest = Guest ( self . conn , parsexml = self . original_xml )
2013-09-24 05:27:54 +04:00
self . _guest . id = None
2013-03-18 01:06:52 +04:00
self . _guest . replace = self . replace
# Pull clonable storage info from the original xml
2013-07-03 02:30:46 +04:00
self . _original_disks = self . _get_original_disks_info ( )
2013-03-18 01:06:52 +04:00
2013-07-03 02:30:46 +04:00
logging . debug ( " Original paths: %s " ,
[ d . path for d in self . original_disks ] )
logging . debug ( " Original sizes: %s " ,
2013-07-13 18:09:00 +04:00
[ d . get_size ( ) for d in self . original_disks ] )
2013-03-18 01:06:52 +04:00
# If domain has devices to clone, it must be 'off' or 'paused'
if ( not self . clone_running and
2013-07-03 02:30:46 +04:00
( self . original_dom and len ( self . original_disks ) != 0 ) ) :
status = self . original_dom . info ( ) [ 0 ]
2013-03-18 01:06:52 +04:00
if status not in [ libvirt . VIR_DOMAIN_SHUTOFF ,
libvirt . VIR_DOMAIN_PAUSED ] :
raise RuntimeError ( _ ( " Domain with devices to clone must be "
" paused or shutoff. " ) )
2013-04-24 18:47:56 +04:00
def _setup_disk_clone_destination ( self , orig_disk , clone_disk ) :
"""
Helper that validates the new path location
"""
if self . preserve_dest_disks :
return
2013-07-13 18:09:00 +04:00
if clone_disk . get_vol_object ( ) :
2013-04-24 18:47:56 +04:00
# XXX We could always do this with vol upload?
# Special case: non remote cloning of a guest using
# managed block devices: fall back to local cloning if
# we have permissions to do so. This validation check
# caused a few bug reports in a short period of time,
# so must be a common case.
2013-07-06 04:36:28 +04:00
if ( self . conn . is_remote ( ) or
2013-04-24 18:47:56 +04:00
clone_disk . type != clone_disk . TYPE_BLOCK or
not orig_disk . path or
not os . access ( orig_disk . path , os . R_OK ) or
not clone_disk . path or
not os . access ( clone_disk . path , os . W_OK ) ) :
raise RuntimeError (
_ ( " Clone onto existing storage volume is not "
" currently supported: ' %s ' " ) % clone_disk . path )
# Setup proper cloning inputs for the new virtual disks
2013-07-13 18:09:00 +04:00
if ( orig_disk . get_vol_object ( ) and
clone_disk . get_vol_install ( ) ) :
clone_vol_install = clone_disk . get_vol_install ( )
2013-04-24 18:47:56 +04:00
# Source and dest are managed. If they share the same pool,
# replace vol_install with a CloneVolume instance, otherwise
# simply set input_vol on the dest vol_install
2013-07-13 18:09:00 +04:00
if ( clone_vol_install . pool . name ( ) ==
orig_disk . get_vol_object ( ) . storagePoolLookupByVolume ( ) . name ( ) ) :
2013-09-20 04:18:12 +04:00
vol_install = StorageVolume ( self . conn )
vol_install . input_vol = orig_disk . get_vol_object ( )
vol_install . sync_input_vol ( )
vol_install . name = clone_vol_install . name
2013-04-24 18:47:56 +04:00
else :
2013-09-20 04:18:12 +04:00
# Cross pool cloning
2015-03-31 18:59:06 +03:00
# Sync only the format of the image.
2013-07-13 18:09:00 +04:00
clone_vol_install . input_vol = orig_disk . get_vol_object ( )
vol_install = clone_vol_install
2015-03-31 18:59:06 +03:00
vol_install . input_vol = orig_disk . get_vol_object ( )
vol_install . sync_input_vol ( only_format = True )
2013-07-13 18:09:00 +04:00
2015-02-07 05:18:05 +03:00
vol_install . reflink = self . reflink
2014-12-06 05:09:26 +03:00
clone_disk . set_vol_install ( vol_install )
elif orig_disk . path :
clone_disk . set_local_disk_to_clone ( orig_disk , self . clone_sparse )
2013-07-13 18:09:00 +04:00
clone_disk . validate ( )
2013-04-24 18:47:56 +04:00
2013-03-18 01:06:52 +04:00
def setup_clone ( self ) :
"""
Validate and set up all parameters needed for the new ( clone ) VM
"""
logging . debug ( " Validating clone parameters. " )
self . _clone_xml = self . original_xml
2013-07-03 02:30:46 +04:00
if len ( self . clone_disks ) < len ( self . original_disks ) :
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " More disks to clone than new paths specified. "
" ( %(passed)d specified, %(need)d needed " ) %
2013-07-03 02:30:46 +04:00
{ " passed " : len ( self . clone_disks ) ,
" need " : len ( self . original_disks ) } )
2013-03-18 01:06:52 +04:00
2013-07-03 02:30:46 +04:00
logging . debug ( " Clone paths: %s " , [ d . path for d in self . clone_disks ] )
2013-03-18 01:06:52 +04:00
self . _guest . name = self . _clone_name
self . _guest . uuid = self . _clone_uuid
2013-07-03 02:30:46 +04:00
self . _clone_macs . reverse ( )
2013-03-18 01:06:52 +04:00
for dev in self . _guest . get_devices ( " graphics " ) :
if dev . port and dev . port != - 1 :
logging . warn ( _ ( " Setting the graphics device port to autoport, "
" in order to avoid conflicting. " ) )
dev . port = - 1
2014-07-21 15:54:38 +04:00
clone_macs = self . _clone_macs [ : ]
2013-03-18 01:06:52 +04:00
for iface in self . _guest . get_devices ( " interface " ) :
iface . target_dev = None
2014-07-21 15:54:38 +04:00
if clone_macs :
mac = clone_macs . pop ( )
2013-03-18 01:06:52 +04:00
else :
2013-07-12 17:54:01 +04:00
mac = VirtualNetworkInterface . generate_mac ( self . conn )
2013-03-18 01:06:52 +04:00
iface . macaddr = mac
# Changing storage XML
2013-07-03 02:30:46 +04:00
for i in range ( len ( self . _original_disks ) ) :
orig_disk = self . _original_disks [ i ]
clone_disk = self . _clone_disks [ i ]
2013-03-18 01:06:52 +04:00
for disk in self . _guest . get_devices ( " disk " ) :
if disk . target == orig_disk . target :
xmldisk = disk
2013-04-24 18:47:56 +04:00
self . _setup_disk_clone_destination ( orig_disk , clone_disk )
2013-03-18 01:06:52 +04:00
# Change the XML
xmldisk . path = None
xmldisk . type = clone_disk . type
2013-07-13 18:09:00 +04:00
xmldisk . driver_name = orig_disk . driver_name
2013-03-18 01:06:52 +04:00
xmldisk . driver_type = orig_disk . driver_type
2013-07-13 18:09:00 +04:00
xmldisk . path = clone_disk . path
2013-03-18 01:06:52 +04:00
2015-11-02 15:20:15 +03:00
# For guest agent channel, remove a path to generate a new one with
# new guest name
for channel in self . _guest . get_devices ( " channel " ) :
if channel . type == VirtualChannelDevice . TYPE_UNIX :
2015-11-03 13:48:26 +03:00
channel . source_path = None
2015-11-02 15:20:15 +03:00
2013-03-18 01:06:52 +04:00
# Save altered clone xml
self . _clone_xml = self . _guest . get_xml_config ( )
logging . debug ( " Clone guest xml is \n %s " , self . _clone_xml )
def setup ( self ) :
"""
Helper function that wraps setup_original and setup_clone , with
additional debug logging .
"""
self . setup_original ( )
self . setup_clone ( )
2013-07-03 02:30:46 +04:00
def start_duplicate ( self , meter = None ) :
"""
Actually perform the duplication : cloning disks if needed and defining
the new clone xml .
"""
logging . debug ( " Starting duplicate. " )
2015-09-06 21:26:50 +03:00
meter = util . ensure_meter ( meter )
2013-07-03 02:30:46 +04:00
dom = None
try :
# Replace orig VM if required
2013-08-09 22:16:59 +04:00
Guest . check_vm_collision ( self . conn , self . clone_name ,
do_remove = self . replace )
2013-07-03 02:30:46 +04:00
# Define domain early to catch any xml errors before duping storage
dom = self . conn . defineXML ( self . clone_xml )
if self . preserve :
for dst_dev in self . clone_disks :
2013-07-06 04:14:57 +04:00
dst_dev . setup ( meter = meter )
2013-07-03 02:30:46 +04:00
except Exception , e :
logging . debug ( " Duplicate failed: %s " , str ( e ) )
if dom :
dom . undefine ( )
raise
logging . debug ( " Duplicating finished. " )
def generate_clone_disk_path ( self , origpath , newname = None ) :
origname = self . original_guest
newname = newname or self . clone_name
path = origpath
suffix = " "
# Try to split the suffix off the existing disk name. Ex.
# foobar.img -> foobar-clone.img
#
# If the suffix is greater than 7 characters, assume it isn't
# a file extension and is part of the disk name, at which point
# just stick '-clone' on the end.
if origpath . count ( " . " ) and len ( origpath . rsplit ( " . " , 1 ) [ 1 ] ) < = 7 :
path , suffix = origpath . rsplit ( " . " , 1 )
suffix = " . " + suffix
dirname = os . path . dirname ( path )
basename = os . path . basename ( path )
clonebase = basename + " -clone "
if origname and basename == origname :
clonebase = newname
clonebase = os . path . join ( dirname , clonebase )
return util . generate_name (
clonebase ,
2014-12-05 04:00:34 +03:00
lambda p : VirtualDisk . path_definitely_exists ( self . conn , p ) ,
2013-07-03 02:30:46 +04:00
suffix ,
lib_collision = False )
def generate_clone_name ( self ) :
# If the orig name is "foo-clone", we don't want the clone to be
# "foo-clone-clone", we want "foo-clone1"
basename = self . original_guest
match = re . search ( " -clone[1-9]*$ " , basename )
2013-10-05 18:22:27 +04:00
start_num = 1
2013-07-03 02:30:46 +04:00
if match :
num_match = re . search ( " [1-9]+$ " , match . group ( ) )
if num_match :
start_num = int ( str ( num_match . group ( ) ) )
basename = basename . replace ( match . group ( ) , " " )
basename = basename + " -clone "
return util . generate_name ( basename ,
self . conn . lookupByName ,
sep = " " , start_num = start_num )
############################
# Private helper functions #
############################
2013-03-18 01:06:52 +04:00
# Parse disk paths that need to be cloned from the original guest's xml
# Return a list of VirtualDisk instances pointing to the original
# storage
2013-07-03 02:30:46 +04:00
def _get_original_disks_info ( self ) :
2013-03-18 01:06:52 +04:00
clonelist = [ ]
retdisks = [ ]
for disk in self . _guest . get_devices ( " disk " ) :
if self . _do_we_clone_device ( disk ) :
clonelist . append ( disk )
continue
# Set up virtual disk to encapsulate all relevant path info
for disk in clonelist :
validate = not self . preserve_dest_disks
try :
device = VirtualDisk . DEVICE_DISK
if not disk . path :
# Tell VirtualDisk we are a cdrom to allow empty media
device = VirtualDisk . DEVICE_CDROM
2013-07-13 18:09:00 +04:00
newd = VirtualDisk ( self . conn )
newd . path = disk . path
newd . device = device
2013-07-14 23:53:23 +04:00
newd . driver_name = disk . driver_name
2013-07-13 18:09:00 +04:00
newd . driver_type = disk . driver_type
newd . target = disk . target
if validate :
2014-12-06 05:09:26 +03:00
if newd . wants_storage_creation ( ) :
2016-02-05 18:18:16 +03:00
raise ValueError ( _ ( " Disk path ' %s ' does not exist. " ) %
2013-07-13 18:09:00 +04:00
newd . path )
2013-03-18 01:06:52 +04:00
except Exception , e :
2014-02-09 01:36:45 +04:00
logging . debug ( " Exception creating clone disk objects " ,
exc_info = True )
2013-03-18 01:06:52 +04:00
raise ValueError ( _ ( " Could not determine original disk "
" information: %s " % str ( e ) ) )
2013-07-13 18:09:00 +04:00
retdisks . append ( newd )
2013-03-18 01:06:52 +04:00
return retdisks
# Pull disk #i from the original guest xml, return it's source path
# if it should be cloned
# Cloning policy based on 'clone_policy', 'force_target' and 'skip_target'
def _do_we_clone_device ( self , disk ) :
if not disk . target :
2016-02-05 18:18:16 +03:00
raise ValueError ( _ ( " XML has no ' dev ' attribute in disk target " ) )
2013-03-18 01:06:52 +04:00
if disk . target in self . skip_target :
return False
if disk . target in self . force_target :
return True
# No media path
if ( not disk . path and
self . CLONE_POLICY_NO_EMPTYMEDIA in self . clone_policy ) :
return False
# Readonly disks
if ( disk . read_only and
self . CLONE_POLICY_NO_READONLY in self . clone_policy ) :
return False
# Shareable disks
if ( disk . shareable and
self . CLONE_POLICY_NO_SHAREABLE in self . clone_policy ) :
return False
return True
# Simple wrapper for checking a vm exists and returning the domain
def _lookup_vm ( self , name ) :
try :
2013-07-03 02:30:46 +04:00
return self . conn . lookupByName ( name )
2013-03-18 01:06:52 +04:00
except libvirt . libvirtError :
raise ValueError ( _ ( " Domain ' %s ' was not found. " ) % str ( name ) )