github.com/vmware/govmomi@v0.43.0/govc/vm/change.go (about)

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