1/*
2Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package vm
18
19import (
20	"context"
21	"flag"
22	"fmt"
23	"strings"
24
25	"github.com/vmware/govmomi/govc/cli"
26	"github.com/vmware/govmomi/govc/flags"
27	"github.com/vmware/govmomi/vim25/types"
28)
29
30type extraConfig []types.BaseOptionValue
31
32func (e *extraConfig) String() string {
33	return fmt.Sprintf("%v", *e)
34}
35
36func (e *extraConfig) Set(v string) error {
37	r := strings.SplitN(v, "=", 2)
38	if len(r) < 2 {
39		return fmt.Errorf("failed to parse extraConfig: %s", v)
40	}
41	*e = append(*e, &types.OptionValue{Key: r[0], Value: r[1]})
42	return nil
43}
44
45type change struct {
46	*flags.VirtualMachineFlag
47	*flags.ResourceAllocationFlag
48
49	types.VirtualMachineConfigSpec
50	extraConfig extraConfig
51}
52
53func init() {
54	cli.Register("vm.change", &change{})
55}
56
57// setAllocation sets *info=nil if none of the fields have been set.
58// We need non-nil fields for use with flag.FlagSet, but we want the
59// VirtualMachineConfigSpec fields to be nil if none of the related flags were given.
60func setAllocation(info **types.ResourceAllocationInfo) {
61	r := *info
62
63	if r.Shares.Level == "" {
64		r.Shares = nil
65	} else {
66		return
67	}
68
69	if r.Limit != nil {
70		return
71	}
72
73	if r.Reservation != nil {
74		return
75	}
76
77	*info = nil
78}
79
80func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) {
81	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
82	cmd.VirtualMachineFlag.Register(ctx, f)
83
84	cmd.CpuAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
85	cmd.MemoryAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
86	cmd.ResourceAllocationFlag = flags.NewResourceAllocationFlag(cmd.CpuAllocation, cmd.MemoryAllocation)
87	cmd.ResourceAllocationFlag.ExpandableReservation = false
88	cmd.ResourceAllocationFlag.Register(ctx, f)
89
90	f.Int64Var(&cmd.MemoryMB, "m", 0, "Size in MB of memory")
91	f.Var(flags.NewInt32(&cmd.NumCPUs), "c", "Number of CPUs")
92	f.StringVar(&cmd.GuestId, "g", "", "Guest OS")
93	f.StringVar(&cmd.Name, "name", "", "Display name")
94	f.Var(&cmd.extraConfig, "e", "ExtraConfig. <key>=<value>")
95
96	f.Var(flags.NewOptionalBool(&cmd.NestedHVEnabled), "nested-hv-enabled", "Enable nested hardware-assisted virtualization")
97	cmd.Tools = &types.ToolsConfigInfo{}
98	f.Var(flags.NewOptionalBool(&cmd.Tools.SyncTimeWithHost), "sync-time-with-host", "Enable SyncTimeWithHost")
99}
100
101func (cmd *change) Description() string {
102	return `Change VM configuration.
103
104To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix.
105
106Examples:
107  govc vm.change -vm $vm -mem.reservation 2048
108  govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE
109  govc vm.change -vm $vm -e guestinfo.vmname $vm
110  # Read the variable set above inside the guest:
111  vmware-rpctool "info-get guestinfo.vmname"`
112}
113
114func (cmd *change) Process(ctx context.Context) error {
115	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
116		return err
117	}
118	return nil
119}
120
121func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error {
122	vm, err := cmd.VirtualMachine()
123	if err != nil {
124		return err
125	}
126
127	if vm == nil {
128		return flag.ErrHelp
129	}
130
131	if len(cmd.extraConfig) > 0 {
132		cmd.VirtualMachineConfigSpec.ExtraConfig = cmd.extraConfig
133	}
134
135	setAllocation(&cmd.CpuAllocation)
136	setAllocation(&cmd.MemoryAllocation)
137
138	task, err := vm.Reconfigure(ctx, cmd.VirtualMachineConfigSpec)
139	if err != nil {
140		return err
141	}
142
143	return task.Wait(ctx)
144}
145