github.com/vmware/govmomi@v0.51.0/cli/vm/change.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  	"os"
    12  	"reflect"
    13  	"strings"
    14  
    15  	"github.com/vmware/govmomi/cli"
    16  	"github.com/vmware/govmomi/cli/flags"
    17  	"github.com/vmware/govmomi/vim25/types"
    18  )
    19  
    20  type extraConfig []types.BaseOptionValue
    21  
    22  func (e *extraConfig) String() string {
    23  	return fmt.Sprintf("%v", *e)
    24  }
    25  
    26  func (e *extraConfig) Set(v string) error {
    27  	r := strings.SplitN(v, "=", 2)
    28  	if len(r) < 2 {
    29  		return fmt.Errorf("failed to parse extraConfig: %s", v)
    30  	}
    31  	*e = append(*e, &types.OptionValue{Key: r[0], Value: r[1]})
    32  	return nil
    33  }
    34  
    35  type extraConfigFile []types.BaseOptionValue
    36  
    37  func (e *extraConfigFile) String() string {
    38  	return fmt.Sprintf("%v", *e)
    39  }
    40  
    41  func (e *extraConfigFile) Set(v string) error {
    42  	r := strings.SplitN(v, "=", 2)
    43  	if len(r) < 2 {
    44  		return fmt.Errorf("failed to parse extraConfigFile: %s", v)
    45  	}
    46  
    47  	var fileContents = ""
    48  	if len(r[1]) > 0 {
    49  		contents, err := os.ReadFile(r[1])
    50  		if err != nil {
    51  			return fmt.Errorf("failed to parse extraConfigFile '%s': %w", v, err)
    52  		}
    53  		fileContents = string(contents)
    54  	}
    55  
    56  	*e = append(*e, &types.OptionValue{Key: r[0], Value: fileContents})
    57  	return nil
    58  }
    59  
    60  type change struct {
    61  	*flags.VirtualMachineFlag
    62  	*flags.ResourceAllocationFlag
    63  
    64  	types.VirtualMachineConfigSpec
    65  	extraConfig     extraConfig
    66  	extraConfigFile extraConfigFile
    67  	Latency         string
    68  	hwUpgradePolicy string
    69  	managedBy       string
    70  }
    71  
    72  func init() {
    73  	cli.Register("vm.change", &change{})
    74  }
    75  
    76  var latencyLevels = types.LatencySensitivitySensitivityLevel("").Strings()
    77  
    78  // setLatency validates latency level if set
    79  func (cmd *change) setLatency() error {
    80  	if cmd.Latency == "" {
    81  		return nil
    82  	}
    83  	for _, l := range latencyLevels {
    84  		if l == cmd.Latency {
    85  			cmd.LatencySensitivity = &types.LatencySensitivity{
    86  				Level: types.LatencySensitivitySensitivityLevel(cmd.Latency),
    87  			}
    88  			return nil
    89  		}
    90  	}
    91  	return fmt.Errorf("latency must be one of: %s", strings.Join(latencyLevels, "|"))
    92  }
    93  
    94  var (
    95  	hwUpgradePolicies      = types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicy("").Strings()
    96  	ftEncryptionModes      = types.VirtualMachineConfigSpecEncryptedFtModes("").Strings()
    97  	migrateEncryptionModes = types.VirtualMachineConfigSpecEncryptedVMotionModes("").Strings()
    98  )
    99  
   100  // setHwUpgradePolicy validates hwUpgradePolicy if set
   101  func (cmd *change) setHwUpgradePolicy() error {
   102  	if cmd.hwUpgradePolicy == "" {
   103  		return nil
   104  	}
   105  	for _, l := range hwUpgradePolicies {
   106  		if l == cmd.hwUpgradePolicy {
   107  			cmd.ScheduledHardwareUpgradeInfo = &types.ScheduledHardwareUpgradeInfo{
   108  				UpgradePolicy: string(types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicy(cmd.hwUpgradePolicy)),
   109  			}
   110  			return nil
   111  		}
   112  	}
   113  	return fmt.Errorf("Hardware upgrade policy must be one of: %s", strings.Join(hwUpgradePolicies, "|"))
   114  }
   115  
   116  // setAllocation sets *info=nil if none of the fields have been set.
   117  // We need non-nil fields for use with flag.FlagSet, but we want the
   118  // VirtualMachineConfigSpec fields to be nil if none of the related flags were given.
   119  func setAllocation(info **types.ResourceAllocationInfo) {
   120  	r := *info
   121  
   122  	if r.Shares.Level == "" {
   123  		r.Shares = nil
   124  	} else {
   125  		return
   126  	}
   127  
   128  	if r.Limit != nil {
   129  		return
   130  	}
   131  
   132  	if r.Reservation != nil {
   133  		return
   134  	}
   135  
   136  	*info = nil
   137  }
   138  
   139  func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) {
   140  	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
   141  	cmd.VirtualMachineFlag.Register(ctx, f)
   142  
   143  	cmd.CpuAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
   144  	cmd.MemoryAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
   145  	cmd.ResourceAllocationFlag = flags.NewResourceAllocationFlag(cmd.CpuAllocation, cmd.MemoryAllocation)
   146  	cmd.ResourceAllocationFlag.ExpandableReservation = false
   147  	cmd.ResourceAllocationFlag.Register(ctx, f)
   148  
   149  	f.Int64Var(&cmd.MemoryMB, "m", 0, "Size in MB of memory")
   150  	f.Var(flags.NewInt32(&cmd.NumCPUs), "c", "Number of CPUs")
   151  	f.StringVar(&cmd.GuestId, "g", "", "Guest OS")
   152  	f.StringVar(&cmd.Name, "name", "", "Display name")
   153  	f.StringVar(&cmd.Latency, "latency", "", fmt.Sprintf("Latency sensitivity (%s)", strings.Join(latencyLevels, "|")))
   154  	f.StringVar(&cmd.Annotation, "annotation", "", "VM description")
   155  	f.StringVar(&cmd.Uuid, "uuid", "", "BIOS UUID")
   156  	f.StringVar(&cmd.managedBy, "managed-by", "", "Set or clear managed by VC Extension")
   157  	f.Var(&cmd.extraConfig, "e", "ExtraConfig. <key>=<value>")
   158  	f.Var(&cmd.extraConfigFile, "f", "ExtraConfig. <key>=<absolute path to file>")
   159  
   160  	f.Var(flags.NewOptionalBool(&cmd.NestedHVEnabled), "nested-hv-enabled", "Enable nested hardware-assisted virtualization")
   161  	cmd.Tools = &types.ToolsConfigInfo{}
   162  	f.Var(flags.NewOptionalBool(&cmd.Tools.SyncTimeWithHost), "sync-time-with-host", "Enable SyncTimeWithHost")
   163  	f.Var(flags.NewOptionalBool(&cmd.VPMCEnabled), "vpmc-enabled", "Enable CPU performance counters")
   164  	f.Var(flags.NewOptionalBool(&cmd.MemoryHotAddEnabled), "memory-hot-add-enabled", "Enable memory hot add")
   165  	f.Var(flags.NewOptionalBool(&cmd.MemoryReservationLockedToMax), "memory-pin", "Reserve all guest memory")
   166  	f.Var(flags.NewOptionalBool(&cmd.CpuHotAddEnabled), "cpu-hot-add-enabled", "Enable CPU hot add")
   167  	cmd.Flags = &types.VirtualMachineFlagInfo{}
   168  	f.Var(flags.NewOptionalBool(&cmd.Flags.VvtdEnabled), "iommu-enabled", "Enable IOMMU")
   169  
   170  	f.StringVar(&cmd.hwUpgradePolicy, "scheduled-hw-upgrade-policy", "", fmt.Sprintf("Schedule hardware upgrade policy (%s)", strings.Join(hwUpgradePolicies, "|")))
   171  
   172  	f.StringVar(&cmd.FtEncryptionMode, "ft-encryption-mode", "", fmt.Sprintf("Encrypted fault tolerance mode (%s)", strings.Join(ftEncryptionModes, "|")))
   173  	f.StringVar(&cmd.MigrateEncryption, "migrate-encryption", "", fmt.Sprintf("Encrypted vMotion mode (%s)", strings.Join(migrateEncryptionModes, "|")))
   174  }
   175  
   176  func (cmd *change) Description() string {
   177  	return `Change VM configuration.
   178  
   179  To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix.
   180  
   181  Examples:
   182    govc vm.change -vm $vm -mem.reservation 2048
   183    govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE
   184    # Enable both cpu and memory hotplug on a guest:
   185    govc vm.change -vm $vm -cpu-hot-add-enabled -memory-hot-add-enabled
   186    govc vm.change -vm $vm -e guestinfo.vmname $vm
   187    # Read the contents of a file and use them as ExtraConfig value
   188    govc vm.change -vm $vm -f guestinfo.data="$(realpath .)/vmdata.config"
   189    # Read the variable set above inside the guest:
   190    vmware-rpctool "info-get guestinfo.vmname"
   191    govc vm.change -vm $vm -latency high
   192    govc vm.change -vm $vm -latency normal
   193    govc vm.change -vm $vm -uuid 4139c345-7186-4924-a842-36b69a24159b
   194    govc vm.change -vm $vm -scheduled-hw-upgrade-policy always`
   195  }
   196  
   197  func (cmd *change) Process(ctx context.Context) error {
   198  	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
   199  		return err
   200  	}
   201  	return nil
   202  }
   203  
   204  func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error {
   205  	vm, err := cmd.VirtualMachine()
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	if vm == nil {
   211  		return flag.ErrHelp
   212  	}
   213  
   214  	cmd.VirtualMachineConfigSpec.ExtraConfig = append(cmd.extraConfig, cmd.extraConfigFile...)
   215  
   216  	setAllocation(&cmd.CpuAllocation)
   217  	setAllocation(&cmd.MemoryAllocation)
   218  	if reflect.DeepEqual(cmd.Tools, new(types.ToolsConfigInfo)) {
   219  		cmd.Tools = nil // no flags set, avoid sending <tools/> in the request
   220  	}
   221  
   222  	if reflect.DeepEqual(cmd.Flags, new(types.VirtualMachineFlagInfo)) {
   223  		cmd.Flags = nil // no flags set, avoid sending <flags/> in the request
   224  	}
   225  
   226  	if err = cmd.setLatency(); err != nil {
   227  		return err
   228  	}
   229  
   230  	if err = cmd.setHwUpgradePolicy(); err != nil {
   231  		return err
   232  	}
   233  
   234  	if cmd.managedBy != "" {
   235  		// From the VirtualMachineConfigSpec doc:
   236  		//   To unset this field pass a ManagedByInfo object with an empty extensionKey
   237  		if cmd.managedBy == "-" {
   238  			cmd.managedBy = ""
   239  		}
   240  		cmd.ManagedBy = &types.ManagedByInfo{
   241  			Type:         vm.Reference().Type,
   242  			ExtensionKey: cmd.managedBy,
   243  		}
   244  	}
   245  
   246  	task, err := vm.Reconfigure(ctx, cmd.VirtualMachineConfigSpec)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	return task.Wait(ctx)
   252  }