#!/usr/bin/env python # vsphere-vm-clone --- create new vm from existing vm or template # Author: Noah Friedman # Created: 2018-03-09 # Public domain # $Id: vsphere-vm-clone,v 1.22 2019/08/23 01:05:49 friedman Exp $ # Commentary: # Code: from __future__ import print_function from pyVmomi import vim import vspherelib as vsl import sys formats = [ 'sesparse', 'thin', 'zeroedthick', 'eagerzeroedthick' ] ########## def get_args(): p = vsl.ArgumentParser( loadrc=True ) p.add( '-t', '--template', required=True, help='Name of template or vm to clone from' ) p.add( '-v', '--vm-name', required=True, help='Name for new vm' ) p.add( '-f', '--folder', help='Path of vSphere folder for vm to appear' ) p.add( '-r', '--pool', help='Resource pool for vm' ) p.add( '-d', '--datastore', help='Name of datastore for vm storage' ) p.add( '-c', '--cluster', help='Name of the cluster or hypervisor host for new vm' ) p.add( '-n', '--network', help='Network label for first virtual nic' ) p.add( '-a', '--annotation', help='New annotation for cloned VM' ) p.add( '-F', '--disk-format', choices=formats, help='Format for cloned VM disks' ) return p.parse() ########## def vmclone( vsi, template, opt ): if opt.folder: p2sfm = vsi.path_to_subfolder_map( 'vm' ) folder = p2sfm[ opt.folder ] else: folder = template.parent cluster = None if opt.cluster: cluster = vsi.get_compute_resource( opt.cluster ) else: cluster = template.runtime.host.parent datastore = None if opt.datastore: try: datastore = vsi.get_datastore( opt.datastore, cluster.datastore ) except AttributeError: datastore = vsi.get_datastore( opt.datastore ) else: datastore = vsi.get_datastore( template.config.datastoreUrl[0].name, cluster.datastore ) pool = None if opt.pool or not template.resourcePool: if cluster.resourcePool.resourcePool: pool = vsi.get_pool( opt.pool, cluster.resourcePool ) else: pool = cluster.resourcePool elif template.resourcePool: pool = template.resourcePool relospec = vim.vm.RelocateSpec() relospec.datastore = datastore relospec.pool = pool relospec.deviceChange = [] nicspec = vsi.make_nic_changespec( template, opt.network, root=cluster.network ) relospec.deviceChange.append( nicspec ) if opt.disk_format: dspecs = vsi.make_disk_format_changespec( template, opt.disk_format ) for spec in dspecs: spec.datastore = datastore relospec.disk.extend( dspecs ) clonespec = vim.vm.CloneSpec() clonespec.location = relospec clonespec.powerOn = False if opt.annotation is not None: clonespec.config = vim.vm.ConfigSpec() clonespec.config.annotation = opt.annotation try: task = template.Clone( folder=folder, name=opt.vm_name, spec=clonespec ) except Exception as details: vsl.printerr( 'exception', details ) return return task ########## class myCallback(): def __init__( self, target="", msg=None ): self.msg = msg self.newmsg = "" self.target = target self.pct = 0 def callback( self, *param ): def status( fmt = "\r{0}: {1} ... {2}% " ): s = fmt.format( self.target, self.msg, self.pct ) print( s, end='' ) sys.stdout.flush() def failed( msg ): self.succ = 0 if ( ( sys.stdout.isatty() and sys.stderr.isatty() ) or ( sys.stdout.fileno() == sys.stderr.fileno() )): print() vsl.printerr( self.target, msg ) return change = param[ 0 ] key = change.name val = change.val if key == 'info': if val.error: return failed( val.error.msg ) elif val.description: self.newmsg = val.description.message self.pct = val.progress else: return elif key == 'info.description': if val: self.newmsg = val.message elif key == 'info.progress': self.pct = change.val elif key == 'info.error': return failed( val.msg ) elif key == 'info.state': if val == 'success': self.pct = 100 # doesn't always get this far status() print( "\n{0}: {1}\n".format( self.target, val ) ) sys.stdout.flush() return if self.newmsg != self.msg: if self.msg: print() self.msg = self.newmsg status() ########## def main(): args = get_args() vsi = vsl.vmomiConnect( args ) succ = 0 template = vsi.find_vm( args.template ) # If no template found, find_vm will already report error. if template: task = vmclone( vsi, template[ 0 ], args ) if task: succ = vsi.taskwait( task, printsucc = False, callback = myCallback( args.vm_name ).callback ) sys.exit( not succ ) if __name__ == "__main__": main() # vsphere-vm-clone ends here