Merge pull request #25 from jetbrains-infra/hardware

CPU and RAM reservation
This commit is contained in:
Michael Kuzmin 2017-07-02 01:52:08 +03:00 committed by GitHub
commit 310e10b19b
6 changed files with 97 additions and 103 deletions

View File

@ -59,7 +59,11 @@ Destination:
Hardware customization:
* `CPUs` - number of CPU sockets. Inherited from source VM by default.
* `CPU_reservation` - Amount of reserved CPU resources in MHz. Inherited from source VM by default.
* `CPU_limit` - Upper limit of available CPU resources in MHz. Inherited from source VM by default, set to `-1` for reset.
* `RAM` - Amount of RAM in megabytes. Inherited from source VM by default.
* `RAM_reservation` - Amount of reserved RAM in MB. Inherited from source VM by default.
* `RAM_reserve_all` - Reserve all available RAM (bool). `false` by default. Cannot be used together with `RAM_reservation`.
Provisioning:
* `ssh_username` - [**mandatory**] username in guest OS.
@ -98,7 +102,10 @@ Post-processing:
"linked_clone": true,
"CPUs": 2,
"CPU_reservation": 1000,
"CPU_limit": 2000,
"RAM": 8192,
"RAM_reservation": 2048,
"ssh_username": "root",
"ssh_password": "{{user `guest_password`}}",

View File

@ -70,8 +70,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepCloneVM{
config: b.config,
},
&StepConfigureHW{
config: b.config,
&StepConfigureHardware{
config: &b.config.HardwareConfig,
},
&StepRun{},
&communicator.StepConnect{

View File

@ -8,7 +8,6 @@ import (
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"strconv"
"time"
)
@ -32,8 +31,7 @@ type Config struct {
LinkedClone bool `mapstructure:"linked_clone"`
// Customization
CPUs string `mapstructure:"CPUs"`
RAM string `mapstructure:"RAM"`
HardwareConfig `mapstructure:",squash"`
// Provisioning
communicator.Config `mapstructure:",squash"`
@ -83,16 +81,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("vSphere host is required"))
}
if c.CPUs != "" {
if _, err := strconv.Atoi(c.CPUs); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Invalid number of CPU sockets"))
}
}
if c.RAM != "" {
if _, err := strconv.Atoi(c.RAM); err != nil {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Invalid number for RAM"))
}
}
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
if c.RawShutdownTimeout != "" {
timeout, err := time.ParseDuration(c.RawShutdownTimeout)
if err != nil {

View File

@ -11,20 +11,6 @@ func TestMinimalConfig(t *testing.T) {
testConfigOk(t, warns, errs)
}
func TestInvalidCpu(t *testing.T) {
raw := minimalConfig()
raw["CPUs"] = "string"
_, warns, errs := NewConfig(raw)
testConfigErr(t, warns, errs)
}
func TestInvalidRam(t *testing.T) {
raw := minimalConfig()
raw["RAM"] = "string"
_, warns, errs := NewConfig(raw)
testConfigErr(t, warns, errs)
}
func TestTimeout(t *testing.T) {
raw := minimalConfig()
raw["shutdown_timeout"] = "3m"
@ -35,6 +21,14 @@ func TestTimeout(t *testing.T) {
}
}
func TestRAMReservation(t *testing.T) {
raw := minimalConfig()
raw["RAM_reservation"] = 1000
raw["RAM_reserve_all"] = true
_, warns, err := NewConfig(raw)
testConfigErr(t, warns, err)
}
func minimalConfig() map[string]interface{} {
return map[string]interface{}{
"vcenter_server": "vcenter.domain.local",
@ -50,18 +44,18 @@ func minimalConfig() map[string]interface{} {
func testConfigOk(t *testing.T, warns []string, err error) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
t.Fatalf("Should be no warnings: %#v", warns)
}
if err != nil {
t.Fatalf("bad: %s", err)
t.Fatalf("Unexpected error: %s", err)
}
}
func testConfigErr(t *testing.T, warns []string, err error) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
t.Fatalf("Should be no warnings: %#v", warns)
}
if err == nil {
t.Fatal("should error")
t.Fatal("An error is not raised")
}
}

View File

@ -1,70 +0,0 @@
package main
import (
"github.com/mitchellh/multistep"
"github.com/hashicorp/packer/packer"
"strconv"
"github.com/vmware/govmomi/vim25/types"
"context"
"github.com/vmware/govmomi/object"
)
type StepConfigureHW struct{
config *Config
}
type ConfigParametersFlag struct {
NumCPUsPtr *int32
MemoryMBPtr *int64
}
func (s *StepConfigureHW) Run(state multistep.StateBag) multistep.StepAction {
vm := state.Get("vm").(*object.VirtualMachine)
ctx := state.Get("ctx").(context.Context)
var confSpec types.VirtualMachineConfigSpec
parametersFlag := ConfigParametersFlag{}
// configure HW
if s.config.CPUs != "" {
CPUs, err := strconv.Atoi(s.config.CPUs)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
confSpec.NumCPUs = int32(CPUs)
parametersFlag.NumCPUsPtr = &(confSpec.NumCPUs)
}
if s.config.RAM != "" {
ram, err := strconv.Atoi(s.config.RAM)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
confSpec.MemoryMB = int64(ram)
parametersFlag.MemoryMBPtr = &(confSpec.MemoryMB)
}
ui := state.Get("ui").(packer.Ui)
if parametersFlag != (ConfigParametersFlag{}) {
ui.Say("configuring virtual hardware...")
// Reconfigure hardware
task, err := vm.Reconfigure(ctx, confSpec)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
} else {
ui.Say("skipping the virtual hardware configration...")
}
return multistep.ActionContinue
}
func (s *StepConfigureHW) Cleanup(multistep.StateBag) {}

73
step_hardware.go Normal file
View File

@ -0,0 +1,73 @@
package main
import (
"github.com/mitchellh/multistep"
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/vim25/types"
"context"
"github.com/vmware/govmomi/object"
"fmt"
)
type HardwareConfig struct {
CPUs int32 `mapstructure:"CPUs"`
CPUReservation int64 `mapstructure:"CPU_reservation"`
CPULimit int64 `mapstructure:"CPU_limit"`
RAM int64 `mapstructure:"RAM"`
RAMReservation int64 `mapstructure:"RAM_reservation"`
RAMReserveAll bool `mapstructure:"RAM_reserve_all"`
}
func (c *HardwareConfig) Prepare() []error {
var errs []error
if c.RAMReservation > 0 && c.RAMReserveAll != false {
errs = append(errs, fmt.Errorf("'RAM_reservation' and 'RAM_reserve_all' cannot be used together"))
}
return errs
}
type StepConfigureHardware struct {
config *HardwareConfig
}
func (s *StepConfigureHardware) Run(state multistep.StateBag) multistep.StepAction {
vm := state.Get("vm").(*object.VirtualMachine)
ctx := state.Get("ctx").(context.Context)
ui := state.Get("ui").(packer.Ui)
if *s.config != (HardwareConfig{}) {
ui.Say("Customizing hardware parameters...")
var confSpec types.VirtualMachineConfigSpec
confSpec.NumCPUs = s.config.CPUs
confSpec.MemoryMB = s.config.RAM
var cpuSpec types.ResourceAllocationInfo
cpuSpec.Reservation = s.config.CPUReservation
cpuSpec.Limit = s.config.CPULimit
confSpec.CpuAllocation = &cpuSpec
var ramSpec types.ResourceAllocationInfo
ramSpec.Reservation = s.config.RAMReservation
confSpec.MemoryAllocation = &ramSpec
confSpec.MemoryReservationLockedToMax = &s.config.RAMReserveAll
task, err := vm.Reconfigure(ctx, confSpec)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepConfigureHardware) Cleanup(multistep.StateBag) {}