2020-01-27 01:12:09 +03:00
#!/usr/bin/env python3
#
# Copyright(c) FUJITSU Limited 2007.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import argparse
import sys
from . import cli
from . cli import fail , print_stdout , print_stderr
from . cloner import Cloner
2020-09-03 19:03:38 +03:00
def _process_src ( options ) :
src_name = options . src_name
src_xml = None
if options . original_xml :
src_xml = open ( options . original_xml ) . read ( )
elif not src_name :
2020-01-27 01:12:09 +03:00
fail ( _ ( " An original machine name is required, "
2020-09-03 19:03:38 +03:00
" use ' --original src_name ' and try again. " ) )
return src_name , src_xml
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
def _process_macs ( options , cloner ) :
new_macs = options . new_mac
if not new_macs or new_macs [ 0 ] == " RANDOM " :
2020-01-27 01:12:09 +03:00
return
2020-09-03 19:03:38 +03:00
for mac in new_macs :
cli . validate_mac ( cloner . conn , mac )
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
for iface in cloner . new_guest . devices . interface [ : ] :
iface . macaddr = new_macs . pop ( 0 )
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
def _process_disks ( options , cloner ) :
newpaths = ( options . new_diskfile or [ ] ) [ : ]
2020-09-04 21:13:52 +03:00
diskinfos = cloner . get_nonshare_diskinfos ( )
2020-09-03 19:03:38 +03:00
for diskinfo in diskinfos :
origpath = diskinfo . disk . path
newpath = None
if newpaths :
newpath = newpaths . pop ( 0 )
elif options . auto_clone :
break
2020-01-27 01:12:09 +03:00
if origpath is None :
newpath = None
2020-09-04 21:13:52 +03:00
diskinfo . set_new_path ( newpath , options . sparse )
2020-09-04 22:34:13 +03:00
diskinfo . raise_error ( )
2020-01-27 01:12:09 +03:00
2020-09-04 21:13:52 +03:00
def _validate_disks ( cloner ) :
2020-09-03 19:03:38 +03:00
# Extra CLI validation for specified disks
for diskinfo in cloner . get_diskinfos ( ) :
2020-09-04 22:34:13 +03:00
diskinfo . raise_error ( )
2020-09-04 20:54:54 +03:00
if not diskinfo . new_disk :
2020-09-03 19:03:38 +03:00
continue
2020-09-04 21:13:52 +03:00
warn_overwrite = not diskinfo . is_preserve_requested ( )
cli . validate_disk ( diskinfo . new_disk ,
warn_overwrite = warn_overwrite )
2020-01-27 01:12:09 +03:00
def parse_args ( ) :
desc = _ ( " Duplicate a virtual machine, changing all the unique "
" host side configuration like MAC address, name, etc. \n \n "
" The VM contents are NOT altered: virt-clone does not change "
" anything _inside_ the guest OS, it only duplicates disks and "
" does host side changes. So things like changing passwords, "
" changing static IP address, etc are outside the scope of "
" this tool. For these types of changes, please see virt-sysprep(1). " )
parser = cli . setupParser ( " %(prog)s --original [NAME] ... " , desc )
cli . add_connect_option ( parser )
geng = parser . add_argument_group ( _ ( " General Options " ) )
2020-09-03 19:03:38 +03:00
geng . add_argument ( " -o " , " --original " , dest = " src_name " ,
2020-01-30 00:14:55 +03:00
help = _ ( " Name of the original guest to clone. " ) )
2020-01-27 01:12:09 +03:00
geng . add_argument ( " --original-xml " ,
help = _ ( " XML file to use as the original guest. " ) )
geng . add_argument ( " --auto-clone " , action = " store_true " ,
help = _ ( " Auto generate clone name and storage paths from "
" the original guest configuration. " ) )
geng . add_argument ( " -n " , " --name " , dest = " new_name " ,
help = _ ( " Name for the new guest " ) )
geng . add_argument ( " -u " , " --uuid " , dest = " new_uuid " , help = argparse . SUPPRESS )
geng . add_argument ( " --reflink " , action = " store_true " ,
help = _ ( " use btrfs COW lightweight copy " ) )
stog = parser . add_argument_group ( _ ( " Storage Configuration " ) )
stog . add_argument ( " -f " , " --file " , dest = " new_diskfile " , action = " append " ,
help = _ ( " New file to use as the disk image for the "
" new guest " ) )
stog . add_argument ( " --force-copy " , dest = " target " , action = " append " ,
help = _ ( " Force to copy devices (eg, if ' hdc ' is a "
" readonly cdrom device, --force-copy=hdc) " ) )
stog . add_argument ( " --skip-copy " , action = " append " ,
help = _ ( " Skip copy of the device target. (eg, if ' vda ' is a "
" disk you don ' t want to copy and use the same path "
" in the new VM, use --skip-copy=vda) " ) )
stog . add_argument ( " --nonsparse " , action = " store_false " , dest = " sparse " ,
default = True ,
help = _ ( " Do not use a sparse file for the clone ' s "
" disk image " ) )
2020-09-04 21:13:52 +03:00
stog . add_argument ( " --preserve-data " , dest = " preserve " ,
action = " store_true " , default = False ,
2020-09-03 19:03:38 +03:00
help = _ ( " Do not clone storage contents to specified file paths, "
" their contents will be left untouched. "
" This requires specifying existing paths for "
2020-09-04 22:18:26 +03:00
" every cloneable disk image. " ) )
2020-01-27 01:12:09 +03:00
stog . add_argument ( " --nvram " , dest = " new_nvram " ,
help = _ ( " New file to use as storage for nvram VARS " ) )
netg = parser . add_argument_group ( _ ( " Networking Configuration " ) )
netg . add_argument ( " -m " , " --mac " , dest = " new_mac " , action = " append " ,
help = _ ( " New fixed MAC address for the clone guest. "
" Default is a randomly generated MAC " ) )
misc = parser . add_argument_group ( _ ( " Miscellaneous Options " ) )
# Just used for clone tests
2020-09-03 19:03:38 +03:00
misc . add_argument ( " --__test-nodry " , action = " store_true " , dest = " test_nodry " ,
2020-09-03 22:59:59 +03:00
default = False , help = argparse . SUPPRESS )
2020-01-27 01:12:09 +03:00
cli . add_misc_options ( misc , prompt = True , replace = True , printxml = True )
cli . autocomplete ( parser )
return parser . parse_args ( )
def main ( conn = None ) :
cli . earlyLogging ( )
options = parse_args ( )
options . quiet = options . quiet or options . xmlonly
cli . setupLogging ( " virt-clone " , options . debug , options . quiet )
cli . convert_old_force ( options )
cli . parse_check ( options . check )
cli . set_prompt ( options . prompt )
conn = cli . getConnection ( options . connect , conn = conn )
if ( options . new_diskfile is None and
2020-11-03 21:01:39 +03:00
options . auto_clone is False ) :
2020-01-27 01:12:09 +03:00
fail ( _ ( " Either --auto-clone or --file is required, "
" use ' --auto-clone or --file ' and try again. " ) )
2020-09-03 19:03:38 +03:00
src_name , src_xml = _process_src ( options )
cloner = Cloner ( conn , src_name , src_xml )
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
cloner . set_replace ( bool ( options . replace ) )
cloner . set_reflink ( bool ( options . reflink ) )
cloner . set_sparse ( bool ( options . sparse ) )
2020-01-27 01:12:09 +03:00
if options . new_uuid is not None :
2020-09-03 19:03:38 +03:00
cloner . set_clone_uuid ( options . new_uuid )
if options . new_nvram :
cloner . set_nvram_path ( options . new_nvram )
force_targets = options . target or [ ]
skip_targets = options . skip_copy or [ ]
for diskinfo in cloner . get_diskinfos ( ) :
if diskinfo . disk . target in force_targets :
2020-09-04 21:13:52 +03:00
diskinfo . set_clone_requested ( )
2020-09-03 19:03:38 +03:00
if diskinfo . disk . target in skip_targets :
2020-09-04 21:13:52 +03:00
diskinfo . set_share_requested ( )
if options . preserve :
for diskinfo in cloner . get_nonshare_diskinfos ( ) :
diskinfo . set_preserve_requested ( )
if cloner . nvram_diskinfo :
cloner . nvram_diskinfo . set_preserve_requested ( )
2020-09-03 19:03:38 +03:00
if options . new_name :
cloner . set_clone_name ( options . new_name )
elif not options . auto_clone :
fail ( _ ( " A name is required for the new virtual machine, "
" use ' --name NEW_VM_NAME ' to specify one. " ) )
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
_process_macs ( options , cloner )
_process_disks ( options , cloner )
2020-01-27 01:12:09 +03:00
2020-09-03 19:03:38 +03:00
cloner . prepare ( )
2020-01-27 01:12:09 +03:00
2020-09-04 21:13:52 +03:00
_validate_disks ( cloner )
2020-01-27 01:12:09 +03:00
2020-09-03 22:59:59 +03:00
run = True
2020-01-27 01:12:09 +03:00
if options . xmlonly :
2020-09-03 19:03:38 +03:00
run = options . test_nodry
print_stdout ( cloner . new_guest . get_xml ( ) , do_force = True )
2020-09-03 22:59:59 +03:00
if run :
2020-09-03 19:03:38 +03:00
cloner . start_duplicate ( cli . get_meter ( ) )
2020-11-03 21:14:13 +03:00
print_stdout ( " " )
print_stdout ( _ ( " Clone ' %s ' created successfully. " ) % cloner . new_guest . name )
2020-01-27 01:12:09 +03:00
return 0
def runcli ( ) : # pragma: no cover
try :
sys . exit ( main ( ) )
except SystemExit as sys_e :
sys . exit ( sys_e . code )
except KeyboardInterrupt :
print_stderr ( _ ( " Installation aborted at user request " ) )
except Exception as main_e :
fail ( main_e )