virt-manager/virt-clone
Cole Robinson 4e4fb15a2f virt-*: Drop all --prompt handling
This stuff is not very helpful and a pain to maintain. Let's drop it once
and for all. We still accept the CLI options and log a warning so people
hopefully take the hint.
2014-02-04 17:01:27 -05:00

247 lines
8.2 KiB
Python
Executable File

#!/usr/bin/python -tt
#
# Copyright(c) FUJITSU Limited 2007.
#
# Script to set up an cloning guest configuration and kick off an cloning
#
# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 argparse
import logging
import sys
import urlgrabber.progress as progress
import virtinst.cli as cli
from virtinst import Cloner, VirtualDisk
from virtinst.cli import fail, print_stdout, print_stderr
### General input gathering functions
def get_clone_name(new_name, auto_clone, design):
if not new_name and auto_clone:
# Generate a name to use
new_name = design.generate_clone_name()
logging.debug("Auto-generated clone name '%s'", new_name)
if not new_name:
fail(_("A name is required for the new virtual machine."))
design.clone_name = new_name
def get_original_guest(guest_name, origfile, design):
origxml = None
if origfile:
f = open(origfile, "r")
origxml = f.read()
f.close()
try:
design.original_xml = origxml
return
except (ValueError, RuntimeError), e:
fail(e)
if not guest_name:
fail(_("An original machine name or xml file is required."))
design.original_guest = guest_name
def get_clone_macaddr(new_mac, design):
if new_mac is None or new_mac[0] == "RANDOM":
return
design.clone_macs = new_mac
def get_clone_uuid(new_uuid, design):
if new_uuid is not None:
design.clone_uuid = new_uuid
def _build_disk(conn, new_path, orig_path, preserve):
if not new_path:
fail(_("A disk path must be specified to clone '%s'.") % orig_path)
try:
dev = VirtualDisk(conn)
dev.path = new_path
dev.set_create_storage(size=.0001, sparse=False)
dev.validate()
except ValueError, e:
fail(_("Error with storage parameters: %s" % str(e)))
cli.validate_disk(dev, warn_overwrite=not preserve)
return dev
def get_clone_diskfile(new_diskfiles, design, preserve=False,
auto_clone=False):
if new_diskfiles is None:
new_diskfiles = [None]
newidx = 0
clonepaths = []
for origpath in [d.path for d in design.original_disks]:
if len(new_diskfiles) <= newidx:
# Extend the new/passed paths list with None if it's not
# long enough
new_diskfiles.append(None)
disk = new_diskfiles[newidx]
if disk is None and auto_clone:
disk = design.generate_clone_disk_path(origpath)
if origpath is None:
devpath = None
else:
dev = _build_disk(design.conn, disk, origpath, preserve)
devpath = dev.path
clonepaths.append(devpath)
newidx += 1
design.clone_paths = clonepaths
def get_clone_sparse(sparse, design):
design.clone_sparse = sparse
def get_preserve(preserve, design):
design.preserve = preserve
def get_force_target(target, design):
for i in target or []:
design.force_target = i
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"))
geng.add_argument("-o", "--original", dest="original_guest",
help=_("Name of the original guest; "
"The status must be shut off or paused."))
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)
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("--nonsparse", action="store_false", dest="sparse",
default=True,
help=_("Do not use a sparse file for the clone's "
"disk image"))
stog.add_argument("--preserve-data", action="store_false",
dest="preserve", default=True,
help=_("Do not clone storage, new disk images specified "
"via --file are preserved unchanged"))
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
misc.add_argument("--clone-running", action="store_true",
default=False, help=argparse.SUPPRESS)
cli.add_misc_options(misc, prompt=True, replace=True, printxml=True)
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.set_prompt(options.prompt)
cli.set_force(options.force)
if conn is None:
conn = cli.getConnection(options.connect)
design = Cloner(conn)
design.clone_running = options.clone_running
design.replace = bool(options.replace)
get_original_guest(options.original_guest, options.original_xml,
design)
get_clone_name(options.new_name, options.auto_clone, design)
get_clone_macaddr(options.new_mac, design)
get_clone_uuid(options.new_uuid, design)
get_clone_sparse(options.sparse, design)
get_force_target(options.target, design)
get_preserve(options.preserve, design)
# This determines the devices that need to be cloned, so that
# get_clone_diskfile knows how many new disk paths it needs
design.setup_original()
get_clone_diskfile(options.new_diskfile, design,
not options.preserve, options.auto_clone)
# setup design object
design.setup_clone()
if options.xmlonly:
print_stdout(design.clone_xml, do_force=True)
else:
# start cloning
meter = progress.TextMeter(fo=sys.stdout)
design.start_duplicate(meter)
print_stdout("")
print_stdout(_("Clone '%s' created successfully.") % design.clone_name)
logging.debug("end clone")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except SystemExit, sys_e:
sys.exit(sys_e.code)
except KeyboardInterrupt:
print_stderr(_("Installation aborted at user request"))
except Exception, main_e:
fail(main_e)