Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for cpu + mem allocation to vm.change command #916

Merged
merged 1 commit into from
Nov 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2739,16 +2739,23 @@ Change VM configuration.
To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix.

Examples:
govc vm.change -vm $vm -mem.reservation 2048
govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE
govc vm.change -vm $vm -e guestinfo.vmname $vm
# Read the variable set above inside the guest:
vmware-rpctool "info-get guestinfo.vmname"

Options:
-c=0 Number of CPUs
-cpu.limit=<nil> CPU limit in MHz
-cpu.reservation=<nil> CPU reservation in MHz
-cpu.shares= CPU shares level or number
-e=[] ExtraConfig. <key>=<value>
-g= Guest OS
-m=0 Size in MB of memory
-mem.limit=<nil> Memory limit in MB
-mem.reservation=<nil> Memory reservation in MB
-mem.shares= Memory shares level or number
-name= Display name
-nested-hv-enabled=<nil> Enable nested hardware-assisted virtualization
-sync-time-with-host=<nil> Enable SyncTimeWithHost
Expand Down
85 changes: 85 additions & 0 deletions govc/flags/resource_allocation_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flags

import (
"context"
"flag"
"strconv"
"strings"

"github.com/vmware/govmomi/vim25/types"
)

type sharesInfo types.SharesInfo

func (s *sharesInfo) String() string {
return string(s.Level)
}

func (s *sharesInfo) Set(val string) error {
switch val {
case string(types.SharesLevelNormal), string(types.SharesLevelLow), string(types.SharesLevelHigh):
s.Level = types.SharesLevel(val)
default:
n, err := strconv.Atoi(val)
if err != nil {
return err
}

s.Level = types.SharesLevelCustom
s.Shares = int32(n)
}

return nil
}

type ResourceAllocationFlag struct {
cpu, mem *types.ResourceAllocationInfo
ExpandableReservation bool
}

func NewResourceAllocationFlag(cpu, mem *types.ResourceAllocationInfo) *ResourceAllocationFlag {
return &ResourceAllocationFlag{cpu, mem, true}
}

func (r *ResourceAllocationFlag) Register(ctx context.Context, f *flag.FlagSet) {
opts := []struct {
name string
units string
*types.ResourceAllocationInfo
}{
{"CPU", "MHz", r.cpu},
{"Memory", "MB", r.mem},
}

for _, opt := range opts {
prefix := strings.ToLower(opt.name)[:3]
shares := (*sharesInfo)(opt.Shares)

f.Var(NewOptionalInt64(&opt.Limit), prefix+".limit", opt.name+" limit in "+opt.units)
f.Var(NewOptionalInt64(&opt.Reservation), prefix+".reservation", opt.name+" reservation in "+opt.units)
if r.ExpandableReservation {
f.Var(NewOptionalBool(&opt.ExpandableReservation), prefix+".expandable", opt.name+" expandable reservation")
}
f.Var(shares, prefix+".shares", opt.name+" shares level or number")
}
}

func (s *ResourceAllocationFlag) Process(ctx context.Context) error {
return nil
}
2 changes: 1 addition & 1 deletion govc/flags/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"strings"
)

const Version = "0.16.0"
const Version = "0.16.1"

type version []int

Expand Down
48 changes: 4 additions & 44 deletions govc/pool/resource_config_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,23 @@ package pool
import (
"context"
"flag"
"strconv"
"strings"

"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/vim25/types"
)

type sharesInfo types.SharesInfo

func (s *sharesInfo) String() string {
return string(s.Level)
}

func (s *sharesInfo) Set(val string) error {
switch val {
case string(types.SharesLevelNormal), string(types.SharesLevelLow), string(types.SharesLevelHigh):
s.Level = types.SharesLevel(val)
default:
n, err := strconv.Atoi(val)
if err != nil {
return err
}

s.Level = types.SharesLevelCustom
s.Shares = int32(n)
}

return nil
}

func NewResourceConfigSpecFlag() *ResourceConfigSpecFlag {
return &ResourceConfigSpecFlag{types.DefaultResourceConfigSpec()}
return &ResourceConfigSpecFlag{types.DefaultResourceConfigSpec(), nil}
}

type ResourceConfigSpecFlag struct {
types.ResourceConfigSpec
*flags.ResourceAllocationFlag
}

func (s *ResourceConfigSpecFlag) Register(ctx context.Context, f *flag.FlagSet) {
opts := []struct {
name string
units string
*types.ResourceAllocationInfo
}{
{"CPU", "MHz", &s.CpuAllocation},
{"Memory", "MB", &s.MemoryAllocation},
}

for _, opt := range opts {
prefix := strings.ToLower(opt.name)[:3]
shares := (*sharesInfo)(opt.Shares)

f.Var(flags.NewOptionalInt64(&opt.Limit), prefix+".limit", opt.name+" limit in "+opt.units)
f.Var(flags.NewOptionalInt64(&opt.Reservation), prefix+".reservation", opt.name+" reservation in "+opt.units)
f.Var(flags.NewOptionalBool(&opt.ExpandableReservation), prefix+".expandable", opt.name+" expandable reservation")
f.Var(shares, prefix+".shares", opt.name+" shares level or number")
}
s.ResourceAllocationFlag = flags.NewResourceAllocationFlag(&s.CpuAllocation, &s.MemoryAllocation)
s.ResourceAllocationFlag.Register(ctx, f)
}

func (s *ResourceConfigSpecFlag) Process(ctx context.Context) error {
Expand Down
8 changes: 8 additions & 0 deletions govc/test/vm.bats
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ load test_helper
assert_success
assert_line "SyncTimeWithHost: true"

run govc object.collect -s "vm/$id" config.memoryAllocation.reservation
assert_success 0

govc vm.change -vm "$id" -mem.reservation 1024

run govc object.collect -s "vm/$id" config.memoryAllocation.reservation
assert_success 1024

nid=$(new_id)
run govc vm.change -name $nid -vm $id
assert_success
Expand Down
34 changes: 34 additions & 0 deletions govc/vm/change.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (e *extraConfig) Set(v string) error {

type change struct {
*flags.VirtualMachineFlag
*flags.ResourceAllocationFlag

types.VirtualMachineConfigSpec
extraConfig extraConfig
Expand All @@ -53,10 +54,39 @@ func init() {
cli.Register("vm.change", &change{})
}

// setAllocation sets *info=nil if none of the fields have been set.
// We need non-nil fields for use with flag.FlagSet, but we want the
// VirtualMachineConfigSpec fields to be nil if none of the related flags were given.
func setAllocation(info **types.ResourceAllocationInfo) {
r := *info

if r.Shares.Level == "" {
r.Shares = nil
} else {
return
}

if r.Limit != nil {
return
}

if r.Reservation != nil {
return
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we care ExpandableReservation here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. It seems ExpandableReservation cannot be set to true for the VM fields. I'll see about just excluding the {cpu,mem}.expandable flags from the vm.change command.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please exclude, bc it´s a resource pool flag.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok done.

*info = nil
}

func (cmd *change) Register(ctx context.Context, f *flag.FlagSet) {
cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
cmd.VirtualMachineFlag.Register(ctx, f)

cmd.CpuAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
cmd.MemoryAllocation = &types.ResourceAllocationInfo{Shares: new(types.SharesInfo)}
cmd.ResourceAllocationFlag = flags.NewResourceAllocationFlag(cmd.CpuAllocation, cmd.MemoryAllocation)
cmd.ResourceAllocationFlag.ExpandableReservation = false
cmd.ResourceAllocationFlag.Register(ctx, f)

f.Int64Var(&cmd.MemoryMB, "m", 0, "Size in MB of memory")
f.Var(flags.NewInt32(&cmd.NumCPUs), "c", "Number of CPUs")
f.StringVar(&cmd.GuestId, "g", "", "Guest OS")
Expand All @@ -74,6 +104,7 @@ func (cmd *change) Description() string {
To add ExtraConfig variables that can read within the guest, use the 'guestinfo.' prefix.

Examples:
govc vm.change -vm $vm -mem.reservation 2048
govc vm.change -vm $vm -e smc.present=TRUE -e ich7m.present=TRUE
govc vm.change -vm $vm -e guestinfo.vmname $vm
# Read the variable set above inside the guest:
Expand Down Expand Up @@ -101,6 +132,9 @@ func (cmd *change) Run(ctx context.Context, f *flag.FlagSet) error {
cmd.VirtualMachineConfigSpec.ExtraConfig = cmd.extraConfig
}

setAllocation(&cmd.CpuAllocation)
setAllocation(&cmd.MemoryAllocation)

task, err := vm.Reconfigure(ctx, cmd.VirtualMachineConfigSpec)
if err != nil {
return err
Expand Down
24 changes: 12 additions & 12 deletions simulator/resource_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func NewResourcePool() *ResourcePool {
return pool
}

func (p *ResourcePool) allFieldsSet(info types.ResourceAllocationInfo) bool {
func allResourceFieldsSet(info *types.ResourceAllocationInfo) bool {
return info.Reservation != nil &&
info.Limit != nil &&
info.ExpandableReservation != nil &&
info.Shares != nil
}

func (p *ResourcePool) allFieldsValid(info types.ResourceAllocationInfo) bool {
func allResourceFieldsValid(info *types.ResourceAllocationInfo) bool {
if info.Reservation != nil {
if *info.Reservation < 0 {
return false
Expand Down Expand Up @@ -86,13 +86,13 @@ func (p *ResourcePool) createChild(name string, spec types.ResourceConfigSpec) (
})
}

if !(p.allFieldsSet(spec.CpuAllocation) && p.allFieldsValid(spec.CpuAllocation)) {
if !(allResourceFieldsSet(&spec.CpuAllocation) && allResourceFieldsValid(&spec.CpuAllocation)) {
return nil, Fault("", &types.InvalidArgument{
InvalidProperty: "spec.cpuAllocation",
})
}

if !(p.allFieldsSet(spec.MemoryAllocation) && p.allFieldsValid(spec.MemoryAllocation)) {
if !(allResourceFieldsSet(&spec.MemoryAllocation) && allResourceFieldsValid(&spec.MemoryAllocation)) {
return nil, Fault("", &types.InvalidArgument{
InvalidProperty: "spec.memoryAllocation",
})
Expand Down Expand Up @@ -130,11 +130,11 @@ func (p *ResourcePool) CreateResourcePool(c *types.CreateResourcePool) soap.HasF
return body
}

func (p *ResourcePool) updateAllocation(kind string, src types.ResourceAllocationInfo, dst *types.ResourceAllocationInfo) *soap.Fault {
if !p.allFieldsValid(src) {
return Fault("", &types.InvalidArgument{
func updateResourceAllocation(kind string, src, dst *types.ResourceAllocationInfo) types.BaseMethodFault {
if !allResourceFieldsValid(src) {
return &types.InvalidArgument{
InvalidProperty: fmt.Sprintf("spec.%sAllocation", kind),
})
}
}

if src.Reservation != nil {
Expand Down Expand Up @@ -170,13 +170,13 @@ func (p *ResourcePool) UpdateConfig(c *types.UpdateConfig) soap.HasFault {
spec := c.Config

if spec != nil {
if err := p.updateAllocation("memory", spec.MemoryAllocation, &p.Config.MemoryAllocation); err != nil {
body.Fault_ = err
if err := updateResourceAllocation("memory", &spec.MemoryAllocation, &p.Config.MemoryAllocation); err != nil {
body.Fault_ = Fault("", err)
return body
}

if err := p.updateAllocation("cpu", spec.CpuAllocation, &p.Config.CpuAllocation); err != nil {
body.Fault_ = err
if err := updateResourceAllocation("cpu", &spec.CpuAllocation, &p.Config.CpuAllocation); err != nil {
body.Fault_ = Fault("", err)
return body
}
}
Expand Down
18 changes: 8 additions & 10 deletions simulator/resource_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,48 +271,46 @@ func TestCreateVAppVPX(t *testing.T) {
}

func TestResourcePoolValidation(t *testing.T) {
var pool ResourcePool

tests := []func() bool{
func() bool {
return pool.allFieldsSet(types.ResourceAllocationInfo{})
return allResourceFieldsSet(&types.ResourceAllocationInfo{})
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Limit = nil
return pool.allFieldsSet(spec.CpuAllocation)
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Reservation = nil
return pool.allFieldsSet(spec.CpuAllocation)
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.ExpandableReservation = nil
return pool.allFieldsSet(spec.CpuAllocation)
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Shares = nil
return pool.allFieldsSet(spec.CpuAllocation)
return allResourceFieldsSet(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Reservation = types.NewInt64(-1)
return pool.allFieldsValid(spec.CpuAllocation)
return allResourceFieldsValid(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
spec.CpuAllocation.Limit = types.NewInt64(-100)
return pool.allFieldsValid(spec.CpuAllocation)
return allResourceFieldsValid(&spec.CpuAllocation)
},
func() bool {
spec := types.DefaultResourceConfigSpec()
shares := spec.CpuAllocation.Shares
shares.Level = types.SharesLevelCustom
shares.Shares = -1
return pool.allFieldsValid(spec.CpuAllocation)
return allResourceFieldsValid(&spec.CpuAllocation)
},
}

Expand Down
Loading