github.com/vmware/govmomi@v0.51.0/cli/vm/instantclone.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package vm
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"fmt"
    11  
    12  	"github.com/vmware/govmomi/cli"
    13  	"github.com/vmware/govmomi/cli/flags"
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/vim25"
    16  	"github.com/vmware/govmomi/vim25/types"
    17  )
    18  
    19  type instantclone struct {
    20  	*flags.ClientFlag
    21  	*flags.DatacenterFlag
    22  	*flags.DatastoreFlag
    23  	*flags.ResourcePoolFlag
    24  	*flags.NetworkFlag
    25  	*flags.FolderFlag
    26  	*flags.VirtualMachineFlag
    27  
    28  	name        string
    29  	extraConfig extraConfig
    30  
    31  	Client         *vim25.Client
    32  	Datacenter     *object.Datacenter
    33  	Datastore      *object.Datastore
    34  	ResourcePool   *object.ResourcePool
    35  	Folder         *object.Folder
    36  	VirtualMachine *object.VirtualMachine
    37  }
    38  
    39  func init() {
    40  	cli.Register("vm.instantclone", &instantclone{})
    41  }
    42  
    43  func (cmd *instantclone) Register(ctx context.Context, f *flag.FlagSet) {
    44  	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
    45  	cmd.ClientFlag.Register(ctx, f)
    46  
    47  	cmd.DatacenterFlag, ctx = flags.NewDatacenterFlag(ctx)
    48  	cmd.DatacenterFlag.Register(ctx, f)
    49  
    50  	cmd.DatastoreFlag, ctx = flags.NewDatastoreFlag(ctx)
    51  	cmd.DatastoreFlag.Register(ctx, f)
    52  
    53  	cmd.ResourcePoolFlag, ctx = flags.NewResourcePoolFlag(ctx)
    54  	cmd.ResourcePoolFlag.Register(ctx, f)
    55  
    56  	cmd.NetworkFlag, ctx = flags.NewNetworkFlag(ctx)
    57  	cmd.NetworkFlag.Register(ctx, f)
    58  
    59  	cmd.FolderFlag, ctx = flags.NewFolderFlag(ctx)
    60  	cmd.FolderFlag.Register(ctx, f)
    61  
    62  	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
    63  	cmd.VirtualMachineFlag.Register(ctx, f)
    64  
    65  	f.Var(&cmd.extraConfig, "e", "ExtraConfig. <key>=<value>")
    66  }
    67  
    68  func (cmd *instantclone) Usage() string {
    69  	return "NAME"
    70  }
    71  
    72  func (cmd *instantclone) Description() string {
    73  	return `Instant Clone VM to NAME.
    74  
    75  Examples:
    76    govc vm.instantclone -vm source-vm new-vm
    77    # Configure ExtraConfig variables on a guest VM:
    78    govc vm.instantclone -vm source-vm -e guestinfo.ipaddress=192.168.0.1 -e guestinfo.netmask=255.255.255.0 new-vm
    79    # Read the variable set above inside the guest:
    80    vmware-rpctool "info-get guestinfo.ipaddress"
    81    vmware-rpctool "info-get guestinfo.netmask"`
    82  }
    83  
    84  func (cmd *instantclone) Process(ctx context.Context) error {
    85  	if err := cmd.ClientFlag.Process(ctx); err != nil {
    86  		return err
    87  	}
    88  	if err := cmd.DatacenterFlag.Process(ctx); err != nil {
    89  		return err
    90  	}
    91  	if err := cmd.DatastoreFlag.Process(ctx); err != nil {
    92  		return err
    93  	}
    94  	if err := cmd.ResourcePoolFlag.Process(ctx); err != nil {
    95  		return err
    96  	}
    97  	if err := cmd.NetworkFlag.Process(ctx); err != nil {
    98  		return err
    99  	}
   100  	if err := cmd.FolderFlag.Process(ctx); err != nil {
   101  		return err
   102  	}
   103  	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
   104  		return err
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (cmd *instantclone) Run(ctx context.Context, f *flag.FlagSet) error {
   111  	var err error
   112  
   113  	if len(f.Args()) != 1 {
   114  		return flag.ErrHelp
   115  	}
   116  
   117  	cmd.name = f.Arg(0)
   118  	if cmd.name == "" {
   119  		return flag.ErrHelp
   120  	}
   121  
   122  	cmd.Client, err = cmd.ClientFlag.Client()
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	cmd.Datacenter, err = cmd.DatacenterFlag.Datacenter()
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	cmd.Datastore, err = cmd.DatastoreFlag.Datastore()
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	cmd.Folder, err = cmd.FolderFlag.Folder()
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	cmd.ResourcePool, err = cmd.ResourcePoolFlag.ResourcePool()
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	cmd.VirtualMachine, err = cmd.VirtualMachineFlag.VirtualMachine()
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	if cmd.VirtualMachine == nil {
   153  		return flag.ErrHelp
   154  	}
   155  
   156  	_, err = cmd.instantcloneVM(ctx)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  func (cmd *instantclone) instantcloneVM(ctx context.Context) (*object.VirtualMachine, error) {
   165  	relocateSpec := types.VirtualMachineRelocateSpec{}
   166  
   167  	if cmd.NetworkFlag.IsSet() {
   168  		devices, err := cmd.VirtualMachine.Device(ctx)
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  
   173  		// prepare virtual device config spec for network card
   174  		configSpecs := []types.BaseVirtualDeviceConfigSpec{}
   175  
   176  		op := types.VirtualDeviceConfigSpecOperationAdd
   177  		card, derr := cmd.NetworkFlag.Device()
   178  		if derr != nil {
   179  			return nil, derr
   180  		}
   181  		// search for the first network card of the source
   182  		for _, device := range devices {
   183  			if _, ok := device.(types.BaseVirtualEthernetCard); ok {
   184  				op = types.VirtualDeviceConfigSpecOperationEdit
   185  				// set new backing info
   186  				cmd.NetworkFlag.Change(device, card)
   187  				card = device
   188  				break
   189  			}
   190  		}
   191  
   192  		configSpecs = append(configSpecs, &types.VirtualDeviceConfigSpec{
   193  			Operation: op,
   194  			Device:    card,
   195  		})
   196  
   197  		relocateSpec.DeviceChange = configSpecs
   198  	}
   199  
   200  	if cmd.FolderFlag.IsSet() {
   201  		folderref := cmd.Folder.Reference()
   202  		relocateSpec.Folder = &folderref
   203  	}
   204  
   205  	if cmd.ResourcePoolFlag.IsSet() {
   206  		poolref := cmd.ResourcePool.Reference()
   207  		relocateSpec.Pool = &poolref
   208  	}
   209  
   210  	if cmd.DatastoreFlag.IsSet() {
   211  		datastoreref := cmd.Datastore.Reference()
   212  		relocateSpec.Datastore = &datastoreref
   213  	}
   214  
   215  	instantcloneSpec := &types.VirtualMachineInstantCloneSpec{
   216  		Name:     cmd.name,
   217  		Location: relocateSpec,
   218  	}
   219  
   220  	if len(cmd.extraConfig) > 0 {
   221  		instantcloneSpec.Config = cmd.extraConfig
   222  	}
   223  
   224  	task, err := cmd.VirtualMachine.InstantClone(ctx, *instantcloneSpec)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	logger := cmd.ProgressLogger(fmt.Sprintf("Instant Cloning %s to %s...", cmd.VirtualMachine.InventoryPath, cmd.name))
   230  	defer logger.Wait()
   231  
   232  	info, err := task.WaitForResult(ctx, logger)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	return object.NewVirtualMachine(cmd.Client, info.Result.(types.ManagedObjectReference)), nil
   238  }