2015-06-21 07:36:07 -04:00
// Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
package iso
import (
"errors"
"fmt"
"github.com/mitchellh/multistep"
hypervcommon "github.com/mitchellh/packer/builder/hyperv/common"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
2015-06-21 09:06:27 -04:00
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
2015-06-21 07:36:07 -04:00
powershell "github.com/mitchellh/packer/powershell"
"github.com/mitchellh/packer/powershell/hyperv"
2015-06-21 09:06:27 -04:00
"github.com/mitchellh/packer/template/interpolate"
2015-06-21 07:36:07 -04:00
"log"
2015-10-30 15:57:27 -04:00
"os"
2015-06-21 07:36:07 -04:00
"strings"
"time"
)
const (
2015-10-30 15:57:27 -04:00
DefaultDiskSize = 40 * 1024 // ~40GB
MinDiskSize = 256 // 256MB
2015-10-18 12:11:38 -04:00
MaxDiskSize = 64 * 1024 * 1024 // 64TB
2015-06-21 07:36:07 -04:00
2015-10-18 12:11:38 -04:00
DefaultRamSize = 1 * 1024 // 1GB
2015-10-30 15:57:27 -04:00
MinRamSize = 32 // 32MB
2015-10-18 12:11:38 -04:00
MaxRamSize = 32 * 1024 // 32GB
2015-06-21 07:36:07 -04:00
2015-10-18 12:11:38 -04:00
LowRam = 384 // 384MB
2015-06-21 07:36:07 -04:00
DefaultUsername = "vagrant"
DefaultPassword = "vagrant"
)
// Builder implements packer.Builder and builds the actual Hyperv
// images.
type Builder struct {
2015-06-21 09:06:27 -04:00
config Config
2015-06-21 07:36:07 -04:00
runner multistep . Runner
}
2015-06-21 09:06:27 -04:00
type Config struct {
common . PackerConfig ` mapstructure:",squash" `
hypervcommon . FloppyConfig ` mapstructure:",squash" `
hypervcommon . OutputConfig ` mapstructure:",squash" `
hypervcommon . SSHConfig ` mapstructure:",squash" `
hypervcommon . RunConfig ` mapstructure:",squash" `
2015-06-27 17:36:39 -04:00
hypervcommon . ShutdownConfig ` mapstructure:",squash" `
2015-06-21 09:06:27 -04:00
2015-06-21 07:36:07 -04:00
// The size, in megabytes, of the hard disk to create for the VM.
// By default, this is 130048 (about 127 GB).
DiskSize uint ` mapstructure:"disk_size" `
// The size, in megabytes, of the computer memory in the VM.
// By default, this is 1024 (about 1 GB).
RamSizeMB uint ` mapstructure:"ram_size_mb" `
// A list of files to place onto a floppy disk that is attached when the
// VM is booted. This is most useful for unattended Windows installs,
// which look for an Autounattend.xml file on removable media. By default,
// no floppy will be attached. All files listed in this setting get
// placed into the root directory of the floppy and the floppy is attached
// as the first floppy device. Currently, no support exists for creating
// sub-directories on the floppy. Wildcard characters (*, ?, and [])
// are allowed. Directory names are also allowed, which will add all
// the files found in the directory to the floppy.
FloppyFiles [ ] string ` mapstructure:"floppy_files" `
//
SecondaryDvdImages [ ] string ` mapstructure:"secondary_iso_images" `
2015-06-21 10:53:08 -04:00
2015-06-21 07:36:07 -04:00
// The checksum for the OS ISO file. Because ISO files are so large,
// this is required and Packer will verify it prior to booting a virtual
// machine with the ISO attached. The type of the checksum is specified
// with iso_checksum_type, documented below.
ISOChecksum string ` mapstructure:"iso_checksum" `
// The type of the checksum specified in iso_checksum. Valid values are
// "none", "md5", "sha1", "sha256", or "sha512" currently. While "none"
// will skip checksumming, this is not recommended since ISO files are
// generally large and corruption does happen from time to time.
ISOChecksumType string ` mapstructure:"iso_checksum_type" `
// A URL to the ISO containing the installation image. This URL can be
// either an HTTP URL or a file URL (or path to a file). If this is an
// HTTP URL, Packer will download it and cache it between runs.
RawSingleISOUrl string ` mapstructure:"iso_url" `
2015-10-30 15:57:27 -04:00
2015-06-21 07:36:07 -04:00
// Multiple URLs for the ISO to download. Packer will try these in order.
// If anything goes wrong attempting to download or while downloading a
// single URL, it will move on to the next. All URLs must point to the
// same file (same checksum). By default this is empty and iso_url is
// used. Only one of iso_url or iso_urls can be specified.
ISOUrls [ ] string ` mapstructure:"iso_urls" `
2015-10-30 15:57:27 -04:00
TargetPath string ` mapstructure:"iso_target_path" `
2015-10-30 13:19:25 -04:00
// Should integration services iso be mounted
2015-10-30 15:57:27 -04:00
GuestAdditionsMode string ` mapstructure:"guest_additions_mode" `
2015-10-30 13:19:25 -04:00
// The path to the integration services iso
2015-10-30 15:57:27 -04:00
GuestAdditionsPath string ` mapstructure:"guest_additions_path" `
2015-06-21 10:53:08 -04:00
2015-06-21 07:36:07 -04:00
// This is the name of the new virtual machine.
// By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build.
VMName string ` mapstructure:"vm_name" `
2015-06-29 16:18:25 -04:00
BootCommand [ ] string ` mapstructure:"boot_command" `
SwitchName string ` mapstructure:"switch_name" `
Cpu uint ` mapstructure:"cpu" `
Generation uint ` mapstructure:"generation" `
EnableSecureBoot bool ` mapstructure:"enable_secure_boot" `
2015-06-21 07:36:07 -04:00
Communicator string ` mapstructure:"communicator" `
// The time in seconds to wait for the virtual machine to report an IP address.
// This defaults to 120 seconds. This may have to be increased if your VM takes longer to boot.
IPAddressTimeout time . Duration ` mapstructure:"ip_address_timeout" `
SSHWaitTimeout time . Duration
2015-06-27 17:36:39 -04:00
SkipCompaction bool ` mapstructure:"skip_compaction" `
2015-06-21 09:06:27 -04:00
ctx interpolate . Context
2015-06-21 07:36:07 -04:00
}
// Prepare processes the build configuration parameters.
func ( b * Builder ) Prepare ( raws ... interface { } ) ( [ ] string , error ) {
2015-06-21 09:06:27 -04:00
err := config . Decode ( & b . config , & config . DecodeOpts {
Interpolate : true ,
InterpolateFilter : & interpolate . RenderFilter {
2015-06-27 17:36:39 -04:00
Exclude : [ ] string {
"boot_command" ,
} ,
2015-06-21 09:06:27 -04:00
} ,
} , raws ... )
2015-06-21 07:36:07 -04:00
if err != nil {
return nil , err
}
// Accumulate any errors and warnings
2015-06-21 09:06:27 -04:00
var errs * packer . MultiError
errs = packer . MultiErrorAppend ( errs , b . config . FloppyConfig . Prepare ( & b . config . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , b . config . RunConfig . Prepare ( & b . config . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , b . config . OutputConfig . Prepare ( & b . config . ctx , & b . config . PackerConfig ) ... )
errs = packer . MultiErrorAppend ( errs , b . config . SSHConfig . Prepare ( & b . config . ctx ) ... )
errs = packer . MultiErrorAppend ( errs , b . config . ShutdownConfig . Prepare ( & b . config . ctx ) ... )
2015-06-21 07:36:07 -04:00
warnings := make ( [ ] string , 0 )
err = b . checkDiskSize ( )
if err != nil {
errs = packer . MultiErrorAppend ( errs , err )
}
err = b . checkRamSize ( )
if err != nil {
errs = packer . MultiErrorAppend ( errs , err )
}
if b . config . VMName == "" {
2015-07-24 03:21:23 -04:00
b . config . VMName = fmt . Sprintf ( "packer-%s" , b . config . PackerBuildName )
2015-07-12 12:19:29 -04:00
}
2015-06-21 07:36:07 -04:00
2015-06-21 09:06:27 -04:00
log . Println ( fmt . Sprintf ( "%s: %v" , "VMName" , b . config . VMName ) )
2015-06-21 07:36:07 -04:00
if b . config . SwitchName == "" {
// no switch name, try to get one attached to a online network adapter
onlineSwitchName , err := hyperv . GetExternalOnlineVirtualSwitch ( )
if onlineSwitchName == "" || err != nil {
2015-06-21 10:53:08 -04:00
b . config . SwitchName = fmt . Sprintf ( "packer-%s" , b . config . PackerBuildName )
2015-06-21 07:36:07 -04:00
} else {
b . config . SwitchName = onlineSwitchName
}
}
2015-06-21 14:35:32 -04:00
if b . config . Cpu < 1 {
b . config . Cpu = 1
}
if b . config . Generation != 2 {
b . config . Generation = 1
}
2015-07-12 12:19:29 -04:00
if b . config . Generation == 2 {
2015-07-12 14:27:16 -04:00
if len ( b . config . FloppyFiles ) > 0 {
2015-07-12 12:19:29 -04:00
err = errors . New ( "Generation 2 vms don't support floppy drives. Use ISO image instead." )
errs = packer . MultiErrorAppend ( errs , err )
}
}
2015-06-21 07:36:07 -04:00
log . Println ( fmt . Sprintf ( "Using switch %s" , b . config . SwitchName ) )
2015-06-21 09:06:27 -04:00
log . Println ( fmt . Sprintf ( "%s: %v" , "SwitchName" , b . config . SwitchName ) )
2015-06-21 07:36:07 -04:00
if b . config . Communicator == "" {
b . config . Communicator = "ssh"
} else if b . config . Communicator == "ssh" || b . config . Communicator == "winrm" {
// good
} else {
err = errors . New ( "communicator must be either ssh or winrm" )
errs = packer . MultiErrorAppend ( errs , err )
}
2015-06-21 09:06:27 -04:00
log . Println ( fmt . Sprintf ( "%s: %v" , "Communicator" , b . config . Communicator ) )
2015-06-21 07:36:07 -04:00
// Errors
2015-06-21 09:06:27 -04:00
if b . config . ISOChecksumType == "" {
errs = packer . MultiErrorAppend (
errs , errors . New ( "The iso_checksum_type must be specified." ) )
} else {
b . config . ISOChecksumType = strings . ToLower ( b . config . ISOChecksumType )
if b . config . ISOChecksumType != "none" {
if b . config . ISOChecksum == "" {
errs = packer . MultiErrorAppend (
errs , errors . New ( "Due to large file sizes, an iso_checksum is required" ) )
} else {
b . config . ISOChecksum = strings . ToLower ( b . config . ISOChecksum )
}
if h := common . HashForType ( b . config . ISOChecksumType ) ; h == nil {
errs = packer . MultiErrorAppend (
errs ,
fmt . Errorf ( "Unsupported checksum type: %s" , b . config . ISOChecksumType ) )
}
}
}
if b . config . RawSingleISOUrl == "" && len ( b . config . ISOUrls ) == 0 {
errs = packer . MultiErrorAppend (
errs , errors . New ( "One of iso_url or iso_urls must be specified." ) )
} else if b . config . RawSingleISOUrl != "" && len ( b . config . ISOUrls ) > 0 {
errs = packer . MultiErrorAppend (
errs , errors . New ( "Only one of iso_url or iso_urls may be specified." ) )
} else if b . config . RawSingleISOUrl != "" {
b . config . ISOUrls = [ ] string { b . config . RawSingleISOUrl }
2015-06-21 07:36:07 -04:00
}
2015-06-21 09:06:27 -04:00
for i , url := range b . config . ISOUrls {
b . config . ISOUrls [ i ] , err = common . DownloadableURL ( url )
2015-06-21 07:36:07 -04:00
if err != nil {
2015-06-21 09:06:27 -04:00
errs = packer . MultiErrorAppend (
errs , fmt . Errorf ( "Failed to parse iso_url %d: %s" , i + 1 , err ) )
2015-06-21 07:36:07 -04:00
}
}
log . Println ( fmt . Sprintf ( "%s: %v" , "RawSingleISOUrl" , b . config . RawSingleISOUrl ) )
2015-10-30 13:19:25 -04:00
if b . config . GuestAdditionsMode == "" {
b . config . GuestAdditionsMode = "attach"
}
if b . config . GuestAdditionsPath == "" {
b . config . GuestAdditionsPath = os . Getenv ( "WINDIR" ) + "\\system32\\vmguest.iso"
}
2015-10-30 15:57:27 -04:00
2015-10-30 13:19:25 -04:00
for _ , isoPath := range b . config . SecondaryDvdImages {
if _ , err := os . Stat ( isoPath ) ; os . IsNotExist ( err ) {
2015-10-30 15:57:27 -04:00
if err != nil {
2015-10-30 13:19:25 -04:00
errs = packer . MultiErrorAppend (
errs , fmt . Errorf ( "Secondary Dvd image does not exist: %s" , err ) )
}
}
}
2015-10-30 15:57:27 -04:00
2015-10-30 13:19:25 -04:00
numberOfIsos := len ( b . config . SecondaryDvdImages )
2015-10-30 15:57:27 -04:00
2015-10-30 13:19:25 -04:00
if b . config . GuestAdditionsMode == "attach" {
if _ , err := os . Stat ( b . config . GuestAdditionsPath ) ; os . IsNotExist ( err ) {
2015-10-30 15:57:27 -04:00
if err != nil {
2015-10-30 13:19:25 -04:00
errs = packer . MultiErrorAppend (
errs , fmt . Errorf ( "Guest additions iso does not exist: %s" , err ) )
}
2015-10-30 15:57:27 -04:00
}
2015-10-30 13:19:25 -04:00
numberOfIsos = numberOfIsos + 1
}
2015-10-30 15:57:27 -04:00
2015-10-30 13:19:25 -04:00
if b . config . Generation < 2 && numberOfIsos > 2 {
if b . config . GuestAdditionsMode == "attach" {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "There are only 2 ide controllers available, so we can't support guest additions and these secondary dvds: %s" , strings . Join ( b . config . SecondaryDvdImages , ", " ) ) )
} else {
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "There are only 2 ide controllers available, so we can't support these secondary dvds: %s" , strings . Join ( b . config . SecondaryDvdImages , ", " ) ) )
}
} else if b . config . Generation > 1 && len ( b . config . SecondaryDvdImages ) > 16 {
if b . config . GuestAdditionsMode == "attach" {
2015-10-30 15:57:27 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "There are not enough drive letters available for scsi (limited to 16), so we can't support guest additions and these secondary dvds: %s" , strings . Join ( b . config . SecondaryDvdImages , ", " ) ) )
2015-10-30 13:19:25 -04:00
} else {
2015-10-30 15:57:27 -04:00
errs = packer . MultiErrorAppend ( errs , fmt . Errorf ( "There are not enough drive letters available for scsi (limited to 16), so we can't support these secondary dvds: %s" , strings . Join ( b . config . SecondaryDvdImages , ", " ) ) )
2015-10-30 13:19:25 -04:00
}
}
2015-06-21 07:36:07 -04:00
// Warnings
2015-06-21 09:06:27 -04:00
if b . config . ISOChecksumType == "none" {
warnings = append ( warnings ,
"A checksum type of 'none' was specified. Since ISO files are so big,\n" +
"a checksum is highly recommended." )
}
2015-06-21 07:36:07 -04:00
if b . config . ShutdownCommand == "" {
warnings = append ( warnings ,
"A shutdown_command was not specified. Without a shutdown command, Packer\n" +
"will forcibly halt the virtual machine, which may result in data loss." )
}
2015-06-21 10:53:08 -04:00
warning := b . checkHostAvailableMemory ( )
if warning != "" {
warnings = appendWarnings ( warnings , warning )
}
2015-06-21 07:36:07 -04:00
if errs != nil && len ( errs . Errors ) > 0 {
return warnings , errs
}
return warnings , nil
}
// Run executes a Packer build and returns a packer.Artifact representing
// a Hyperv appliance.
func ( b * Builder ) Run ( ui packer . Ui , hook packer . Hook , cache packer . Cache ) ( packer . Artifact , error ) {
// Create the driver that we'll use to communicate with Hyperv
driver , err := hypervcommon . NewHypervPS4Driver ( )
if err != nil {
return nil , fmt . Errorf ( "Failed creating Hyper-V driver: %s" , err )
}
// Set up the state.
state := new ( multistep . BasicStateBag )
2015-10-30 15:57:27 -04:00
state . Put ( "cache" , cache )
2015-06-21 07:36:07 -04:00
state . Put ( "config" , & b . config )
state . Put ( "driver" , driver )
state . Put ( "hook" , hook )
state . Put ( "ui" , ui )
2015-10-30 15:57:27 -04:00
steps := [ ] multistep . Step {
2015-06-21 07:36:07 -04:00
& hypervcommon . StepCreateTempDir { } ,
& hypervcommon . StepOutputDir {
Force : b . config . PackerForce ,
Path : b . config . OutputDir ,
} ,
2015-10-30 13:19:25 -04:00
& common . StepDownload {
Checksum : b . config . ISOChecksum ,
ChecksumType : b . config . ISOChecksumType ,
Description : "ISO" ,
ResultKey : "iso_path" ,
Url : b . config . ISOUrls ,
Extension : "iso" ,
TargetPath : b . config . TargetPath ,
2015-10-30 15:57:27 -04:00
} ,
2015-06-21 07:36:07 -04:00
& common . StepCreateFloppy {
Files : b . config . FloppyFiles ,
} ,
2015-06-27 17:36:39 -04:00
& hypervcommon . StepHTTPServer {
HTTPDir : b . config . HTTPDir ,
HTTPPortMin : b . config . HTTPPortMin ,
HTTPPortMax : b . config . HTTPPortMax ,
} ,
2015-06-21 07:36:07 -04:00
& hypervcommon . StepCreateSwitch {
SwitchName : b . config . SwitchName ,
} ,
& hypervcommon . StepCreateVM {
2015-06-29 16:18:25 -04:00
VMName : b . config . VMName ,
SwitchName : b . config . SwitchName ,
RamSizeMB : b . config . RamSizeMB ,
DiskSize : b . config . DiskSize ,
Generation : b . config . Generation ,
Cpu : b . config . Cpu ,
EnabeSecureBoot : b . config . EnableSecureBoot ,
2015-06-21 07:36:07 -04:00
} ,
& hypervcommon . StepEnableIntegrationService { } ,
& hypervcommon . StepMountDvdDrive {
2015-10-30 13:19:25 -04:00
Generation : b . config . Generation ,
} ,
& hypervcommon . StepMountFloppydrive {
Generation : b . config . Generation ,
} ,
& hypervcommon . StepMountGuestAdditions {
GuestAdditionsMode : b . config . GuestAdditionsMode ,
GuestAdditionsPath : b . config . GuestAdditionsPath ,
2015-10-30 15:57:27 -04:00
Generation : b . config . Generation ,
2015-06-21 07:36:07 -04:00
} ,
2015-07-12 12:19:29 -04:00
& hypervcommon . StepMountSecondaryDvdImages {
2015-10-30 15:57:27 -04:00
IsoPaths : b . config . SecondaryDvdImages ,
2015-07-12 13:57:42 -04:00
Generation : b . config . Generation ,
2015-07-12 12:19:29 -04:00
} ,
2015-06-21 07:36:07 -04:00
2015-06-27 17:36:39 -04:00
& hypervcommon . StepRun {
BootWait : b . config . BootWait ,
Headless : b . config . Headless ,
} ,
& hypervcommon . StepTypeBootCommand {
BootCommand : b . config . BootCommand ,
SwitchName : b . config . SwitchName ,
Ctx : b . config . ctx ,
2015-06-21 07:36:07 -04:00
} ,
// configure the communicator ssh, winrm
& communicator . StepConnect {
Config : & b . config . SSHConfig . Comm ,
Host : hypervcommon . CommHost ,
2015-06-22 17:26:06 -04:00
SSHConfig : hypervcommon . SSHConfigFunc ( & b . config . SSHConfig ) ,
2015-06-21 07:36:07 -04:00
} ,
// provision requires communicator to be setup
& common . StepProvision { } ,
& hypervcommon . StepShutdown {
Command : b . config . ShutdownCommand ,
Timeout : b . config . ShutdownTimeout ,
} ,
2015-06-21 14:35:32 -04:00
// wait for the vm to be powered off
& hypervcommon . StepWaitForPowerOff { } ,
2015-10-30 15:57:27 -04:00
2015-10-30 13:19:25 -04:00
// remove the secondary dvd images
2015-06-24 14:52:42 -04:00
// after we power down
& hypervcommon . StepUnmountSecondaryDvdImages { } ,
2015-10-30 13:19:25 -04:00
& hypervcommon . StepUnmountGuestAdditions { } ,
& hypervcommon . StepUnmountDvdDrive { } ,
2015-07-14 02:51:03 -04:00
& hypervcommon . StepUnmountFloppyDrive {
Generation : b . config . Generation ,
} ,
2015-06-21 07:36:07 -04:00
& hypervcommon . StepExportVm {
2015-06-27 17:36:39 -04:00
OutputDir : b . config . OutputDir ,
SkipCompaction : b . config . SkipCompaction ,
2015-06-21 07:36:07 -04:00
} ,
// the clean up actions for each step will be executed reverse order
}
// Run the steps.
if b . config . PackerDebug {
b . runner = & multistep . DebugRunner {
Steps : steps ,
PauseFn : common . MultistepDebugFn ( ui ) ,
}
} else {
b . runner = & multistep . BasicRunner { Steps : steps }
}
b . runner . Run ( state )
// Report any errors.
if rawErr , ok := state . GetOk ( "error" ) ; ok {
return nil , rawErr . ( error )
}
// If we were interrupted or cancelled, then just exit.
if _ , ok := state . GetOk ( multistep . StateCancelled ) ; ok {
return nil , errors . New ( "Build was cancelled." )
}
if _ , ok := state . GetOk ( multistep . StateHalted ) ; ok {
return nil , errors . New ( "Build was halted." )
}
return hypervcommon . NewArtifact ( b . config . OutputDir )
}
// Cancel.
func ( b * Builder ) Cancel ( ) {
if b . runner != nil {
log . Println ( "Cancelling the step runner..." )
b . runner . Cancel ( )
}
}
func appendWarnings ( slice [ ] string , data ... string ) [ ] string {
m := len ( slice )
n := m + len ( data )
if n > cap ( slice ) { // if necessary, reallocate
// allocate double what's needed, for future growth.
newSlice := make ( [ ] string , ( n + 1 ) * 2 )
copy ( newSlice , slice )
slice = newSlice
}
slice = slice [ 0 : n ]
copy ( slice [ m : n ] , data )
return slice
}
func ( b * Builder ) checkDiskSize ( ) error {
if b . config . DiskSize == 0 {
b . config . DiskSize = DefaultDiskSize
}
log . Println ( fmt . Sprintf ( "%s: %v" , "DiskSize" , b . config . DiskSize ) )
if b . config . DiskSize < MinDiskSize {
2015-10-18 12:11:38 -04:00
return fmt . Errorf ( "disk_size_gb: Virtual machine requires disk space >= %v GB, but defined: %v" , MinDiskSize , b . config . DiskSize / 1024 )
2015-06-21 07:36:07 -04:00
} else if b . config . DiskSize > MaxDiskSize {
2015-10-18 12:11:38 -04:00
return fmt . Errorf ( "disk_size_gb: Virtual machine requires disk space <= %v GB, but defined: %v" , MaxDiskSize , b . config . DiskSize / 1024 )
2015-06-21 07:36:07 -04:00
}
return nil
}
func ( b * Builder ) checkRamSize ( ) error {
if b . config . RamSizeMB == 0 {
b . config . RamSizeMB = DefaultRamSize
}
log . Println ( fmt . Sprintf ( "%s: %v" , "RamSize" , b . config . RamSizeMB ) )
if b . config . RamSizeMB < MinRamSize {
2015-10-18 12:11:38 -04:00
return fmt . Errorf ( "ram_size_mb: Virtual machine requires memory size >= %v MB, but defined: %v" , MinRamSize , b . config . RamSizeMB )
2015-06-21 07:36:07 -04:00
} else if b . config . RamSizeMB > MaxRamSize {
2015-10-18 12:11:38 -04:00
return fmt . Errorf ( "ram_size_mb: Virtual machine requires memory size <= %v MB, but defined: %v" , MaxRamSize , b . config . RamSizeMB )
2015-06-21 07:36:07 -04:00
}
return nil
}
func ( b * Builder ) checkHostAvailableMemory ( ) string {
freeMB := powershell . GetHostAvailableMemory ( )
if ( freeMB - float64 ( b . config . RamSizeMB ) ) < LowRam {
return fmt . Sprintf ( "Hyper-V might fail to create a VM if there is not enough free memory in the system." )
}
return ""
2015-06-21 09:06:27 -04:00
}