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

     1  /*
     2  Copyright (c) 2015-2017 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 = []string{
    88  	string(types.LatencySensitivitySensitivityLevelLow),
    89  	string(types.LatencySensitivitySensitivityLevelNormal),
    90  	string(types.LatencySensitivitySensitivityLevelHigh),
    91  }
    92  
    93  // setLatency validates latency level if set
    94  func (cmd *change) setLatency() error {
    95  	if cmd.Latency == "" {
    96  		return nil
    97  	}
    98  	for _, l := range latencyLevels {
    99  		if l == cmd.Latency {
   100  			cmd.LatencySensitivity = &types.LatencySensitivity{
   101  				Level: types.LatencySensitivitySensitivityLevel(cmd.Latency),
   102  			}
   103  			return nil
   104  		}
   105  	}
   106  	return fmt.Errorf("latency must be one of: %s", strings.Join(latencyLevels, "|"))
   107  }
   108  
   109  var hwUpgradePolicies = []string{
   110  	string(types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicyOnSoftPowerOff),
   111  	string(types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicyNever),
   112  	string(types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicyAlways),
   113  }
   114  
   115  // setHwUpgradePolicy validates hwUpgradePolicy if set
   116  func (cmd *change) setHwUpgradePolicy() error {
   117  	if cmd.hwUpgradePolicy == "" {
   118  		return nil
   119  	}
   120  	for _, l := range hwUpgradePolicies {
   121  		if l == cmd.hwUpgradePolicy {
   122  			cmd.ScheduledHardwareUpgradeInfo = &types.ScheduledHardwareUpgradeInfo{
   123  				UpgradePolicy: string(types.ScheduledHardwareUpgradeInfoHardwareUpgradePolicy(cmd.hwUpgradePolicy)),
   124  			}
   125  			return nil
   126  		}
   127  	}
   128  	return fmt.Errorf("Hardware upgrade policy must be one of: %s", strings.Join(hwUpgradePolicies, "|"))
   129  }
   130  
   131  // setAllocation sets *info=nil if none of the fields have been set.
   132  // We need non-nil fields for use with flag.FlagSet, but we want the
   133  // VirtualMachineConfigSpec fields to be nil if none of the related flags were given.
   134  func setAllocation(info **types.ResourceAllocationInfo) {
   135  	r := *info
   136  
   137  	if r.Shares.Level == "" {
   138  		r.Shares = nil
   139  	} else {
   140  		return
   141  	}
   142  
   143  	if r.Limit != nil {
   144  		return
   145  	}
   146  
   147  	if r.Reservation != nil {
   148  		return
   149  	}
   150  
   151  	*info = nil
   152  }
   153  
   154  func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) {
   155  	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
   156  	cmd.VirtualMachineFlag.Register(ctx, f)
   157  
   158  	cmd.CpuAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
   159  	cmd.MemoryAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
   160  	cmd.ResourceAllocationFlag = flags.NewResourceAllocationFlag(cmd.CpuAllocation, cmd.MemoryAllocation)
   161  	cmd.ResourceAllocationFlag.ExpandableReservation = false
   162  	cmd.ResourceAllocationFlag.Register(ctx, f)
   163  
   164  	f.Int64Var(&cmd.MemoryMB, "m", 0, "Size in MB of memory")
   165  	f.Var(flags.NewInt32(&cmd.NumCPUs), "c", "Number of CPUs")
   166  	f.StringVar(&cmd.GuestId, "g", "", "Guest OS")
   167  	f.StringVar(&cmd.Name, "name", "", "Display name")
   168  	f.StringVar(&cmd.Latency, "latency", "", fmt.Sprintf("Latency sensitivity (%s)", strings.Join(latencyLevels, "|")))
   169  	f.StringVar(&cmd.Annotation, "annotation", "", "VM description")
   170  	f.StringVar(&cmd.Uuid, "uuid", "", "BIOS UUID")
   171  	f.Var(&cmd.extraConfig, "e", "ExtraConfig. <key>=<value>")
   172  	f.Var(&cmd.extraConfigFile, "f", "ExtraConfig. <key>=<absolute path to file>")
   173  
   174  	f.Var(flags.NewOptionalBool(&cmd.NestedHVEnabled), "nested-hv-enabled", "Enable nested hardware-assisted virtualization")
   175  	cmd.Tools = &types.ToolsConfigInfo{}
   176  	f.Var(flags.NewOptionalBool(&cmd.Tools.SyncTimeWithHost), "sync-time-with-host", "Enable SyncTimeWithHost")
   177  	f.Var(flags.NewOptionalBool(&cmd.VPMCEnabled), "vpmc-enabled", "Enable CPU performance counters")
   178  	f.Var(flags.NewOptionalBool(&cmd.MemoryHotAddEnabled), "memory-hot-add-enabled", "Enable memory hot add")
   179  	f.Var(flags.NewOptionalBool(&cmd.MemoryReservationLockedToMax), "memory-pin", "Reserve all guest memory")
   180  	f.Var(flags.NewOptionalBool(&cmd.CpuHotAddEnabled), "cpu-hot-add-enabled", "Enable CPU hot add")
   181  	cmd.Flags = &types.VirtualMachineFlagInfo{}
   182  	f.Var(flags.NewOptionalBool(&cmd.Flags.VvtdEnabled), "iommu-enabled", "Enable IOMMU")
   183  
   184  	f.StringVar(&cmd.hwUpgradePolicy, "scheduled-hw-upgrade-policy", "", fmt.Sprintf("Schedule hardware upgrade policy (%s)", strings.Join(hwUpgradePolicies, "|")))
   185  }
   186  
   187  func (cmd *change) Description() string {
   188  	return `Change VM configuration.
   189  
   190  To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix.
   191  
   192  Examples:
   193    govc vm.change -vm $vm -mem.reservation 2048
   194    govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE
   195    # Enable both cpu and memory hotplug on a guest:
   196    govc vm.change -vm $vm -cpu-hot-add-enabled -memory-hot-add-enabled
   197    govc vm.change -vm $vm -e guestinfo.vmname $vm
   198    # Read the contents of a file and use them as ExtraConfig value
   199    govc vm.change -vm $vm -f guestinfo.data="$(realpath .)/vmdata.config"
   200    # Read the variable set above inside the guest:
   201    vmware-rpctool "info-get guestinfo.vmname"
   202    govc vm.change -vm $vm -latency high
   203    govc vm.change -vm $vm -latency normal
   204    govc vm.change -vm $vm -uuid 4139c345-7186-4924-a842-36b69a24159b
   205    govc vm.change -vm $vm -scheduled-hw-upgrade-policy always`
   206  }
   207  
   208  func (cmd *change) Process(ctx context.Context) error {
   209  	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
   210  		return err
   211  	}
   212  	return nil
   213  }
   214  
   215  func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error {
   216  	vm, err := cmd.VirtualMachine()
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	if vm == nil {
   222  		return flag.ErrHelp
   223  	}
   224  
   225  	cmd.VirtualMachineConfigSpec.ExtraConfig = append(cmd.extraConfig, cmd.extraConfigFile...)
   226  
   227  	setAllocation(&cmd.CpuAllocation)
   228  	setAllocation(&cmd.MemoryAllocation)
   229  	if reflect.DeepEqual(cmd.Tools, new(types.ToolsConfigInfo)) {
   230  		cmd.Tools = nil // no flags set, avoid sending <tools/> in the request
   231  	}
   232  
   233  	if reflect.DeepEqual(cmd.Flags, new(types.VirtualMachineFlagInfo)) {
   234  		cmd.Flags = nil // no flags set, avoid sending <flags/> in the request
   235  	}
   236  
   237  	if err = cmd.setLatency(); err != nil {
   238  		return err
   239  	}
   240  
   241  	if err = cmd.setHwUpgradePolicy(); err != nil {
   242  		return err
   243  	}
   244  
   245  	task, err := vm.Reconfigure(ctx, cmd.VirtualMachineConfigSpec)
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	return task.Wait(ctx)
   251  }