Merge pull request #6393 from DanHam/fix-hyperv-export
Various improvements to Hyper-V ISO and VMCX builders
This commit is contained in:
commit
c5b346b07b
|
@ -70,7 +70,7 @@ type Driver interface {
|
|||
|
||||
DeleteVirtualSwitch(string) error
|
||||
|
||||
CreateVirtualMachine(string, string, string, string, int64, int64, int64, string, uint, bool, bool) error
|
||||
CreateVirtualMachine(string, string, string, int64, int64, int64, string, uint, bool, bool) error
|
||||
|
||||
AddVirtualMachineHardDrive(string, string, string, int64, int64, string) error
|
||||
|
||||
|
@ -94,9 +94,11 @@ type Driver interface {
|
|||
|
||||
ExportVirtualMachine(string, string) error
|
||||
|
||||
CompactDisks(string, string) error
|
||||
PreserveLegacyExportBehaviour(string, string) error
|
||||
|
||||
CopyExportedVirtualMachine(string, string, string, string) error
|
||||
MoveCreatedVHDsToOutputDir(string, string) error
|
||||
|
||||
CompactDisks(string) (string, error)
|
||||
|
||||
RestartVirtualMachine(string) error
|
||||
|
||||
|
|
|
@ -124,7 +124,6 @@ type DriverMock struct {
|
|||
CreateVirtualMachine_VmName string
|
||||
CreateVirtualMachine_Path string
|
||||
CreateVirtualMachine_HarddrivePath string
|
||||
CreateVirtualMachine_VhdPath string
|
||||
CreateVirtualMachine_Ram int64
|
||||
CreateVirtualMachine_DiskSize int64
|
||||
CreateVirtualMachine_DiskBlockSize int64
|
||||
|
@ -135,7 +134,7 @@ type DriverMock struct {
|
|||
CreateVirtualMachine_Err error
|
||||
|
||||
CloneVirtualMachine_Called bool
|
||||
CloneVirtualMachine_CloneFromVmxcPath string
|
||||
CloneVirtualMachine_CloneFromVmcxPath string
|
||||
CloneVirtualMachine_CloneFromVmName string
|
||||
CloneVirtualMachine_CloneFromSnapshotName string
|
||||
CloneVirtualMachine_CloneAllSnapshots bool
|
||||
|
@ -186,17 +185,20 @@ type DriverMock struct {
|
|||
ExportVirtualMachine_Path string
|
||||
ExportVirtualMachine_Err error
|
||||
|
||||
CompactDisks_Called bool
|
||||
CompactDisks_ExpPath string
|
||||
CompactDisks_VhdDir string
|
||||
CompactDisks_Err error
|
||||
PreserveLegacyExportBehaviour_Called bool
|
||||
PreserveLegacyExportBehaviour_SrcPath string
|
||||
PreserveLegacyExportBehaviour_DstPath string
|
||||
PreserveLegacyExportBehaviour_Err error
|
||||
|
||||
CopyExportedVirtualMachine_Called bool
|
||||
CopyExportedVirtualMachine_ExpPath string
|
||||
CopyExportedVirtualMachine_OutputPath string
|
||||
CopyExportedVirtualMachine_VhdDir string
|
||||
CopyExportedVirtualMachine_VmDir string
|
||||
CopyExportedVirtualMachine_Err error
|
||||
MoveCreatedVHDsToOutputDir_Called bool
|
||||
MoveCreatedVHDsToOutputDir_SrcPath string
|
||||
MoveCreatedVHDsToOutputDir_DstPath string
|
||||
MoveCreatedVHDsToOutputDir_Err error
|
||||
|
||||
CompactDisks_Called bool
|
||||
CompactDisks_Path string
|
||||
CompactDisks_Result string
|
||||
CompactDisks_Err error
|
||||
|
||||
RestartVirtualMachine_Called bool
|
||||
RestartVirtualMachine_VmName string
|
||||
|
@ -393,7 +395,8 @@ func (d *DriverMock) CreateVirtualSwitch(switchName string, switchType string) (
|
|||
return d.CreateVirtualSwitch_Return, d.CreateVirtualSwitch_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, vhdDiskBlockSize int64, controllerType string) error {
|
||||
func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string,
|
||||
vhdSizeBytes int64, vhdDiskBlockSize int64, controllerType string) error {
|
||||
d.AddVirtualMachineHardDrive_Called = true
|
||||
d.AddVirtualMachineHardDrive_VmName = vmName
|
||||
d.AddVirtualMachineHardDrive_VhdFile = vhdFile
|
||||
|
@ -404,12 +407,13 @@ func (d *DriverMock) AddVirtualMachineHardDrive(vmName string, vhdFile string, v
|
|||
return d.AddVirtualMachineHardDrive_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool) error {
|
||||
func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddrivePath string,
|
||||
ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint,
|
||||
diffDisks bool, fixedVHD bool) error {
|
||||
d.CreateVirtualMachine_Called = true
|
||||
d.CreateVirtualMachine_VmName = vmName
|
||||
d.CreateVirtualMachine_Path = path
|
||||
d.CreateVirtualMachine_HarddrivePath = harddrivePath
|
||||
d.CreateVirtualMachine_VhdPath = vhdPath
|
||||
d.CreateVirtualMachine_Ram = ram
|
||||
d.CreateVirtualMachine_DiskSize = diskSize
|
||||
d.CreateVirtualMachine_DiskBlockSize = diskBlockSize
|
||||
|
@ -419,9 +423,11 @@ func (d *DriverMock) CreateVirtualMachine(vmName string, path string, harddriveP
|
|||
return d.CreateVirtualMachine_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
|
||||
func (d *DriverMock) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string,
|
||||
cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string,
|
||||
harddrivePath string, ram int64, switchName string) error {
|
||||
d.CloneVirtualMachine_Called = true
|
||||
d.CloneVirtualMachine_CloneFromVmxcPath = cloneFromVmxcPath
|
||||
d.CloneVirtualMachine_CloneFromVmcxPath = cloneFromVmcxPath
|
||||
d.CloneVirtualMachine_CloneFromVmName = cloneFromVmName
|
||||
d.CloneVirtualMachine_CloneFromSnapshotName = cloneFromSnapshotName
|
||||
d.CloneVirtualMachine_CloneAllSnapshots = cloneAllSnapshots
|
||||
|
@ -489,20 +495,25 @@ func (d *DriverMock) ExportVirtualMachine(vmName string, path string) error {
|
|||
return d.ExportVirtualMachine_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) CompactDisks(expPath string, vhdDir string) error {
|
||||
d.CompactDisks_Called = true
|
||||
d.CompactDisks_ExpPath = expPath
|
||||
d.CompactDisks_VhdDir = vhdDir
|
||||
return d.CompactDisks_Err
|
||||
func (d *DriverMock) PreserveLegacyExportBehaviour(srcPath string, dstPath string) error {
|
||||
d.PreserveLegacyExportBehaviour_Called = true
|
||||
d.PreserveLegacyExportBehaviour_SrcPath = srcPath
|
||||
d.PreserveLegacyExportBehaviour_DstPath = dstPath
|
||||
return d.PreserveLegacyExportBehaviour_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
|
||||
d.CopyExportedVirtualMachine_Called = true
|
||||
d.CopyExportedVirtualMachine_ExpPath = expPath
|
||||
d.CopyExportedVirtualMachine_OutputPath = outputPath
|
||||
d.CopyExportedVirtualMachine_VhdDir = vhdDir
|
||||
d.CopyExportedVirtualMachine_VmDir = vmDir
|
||||
return d.CopyExportedVirtualMachine_Err
|
||||
func (d *DriverMock) MoveCreatedVHDsToOutputDir(srcPath string, dstPath string) error {
|
||||
d.MoveCreatedVHDsToOutputDir_Called = true
|
||||
d.MoveCreatedVHDsToOutputDir_SrcPath = srcPath
|
||||
d.MoveCreatedVHDsToOutputDir_DstPath = dstPath
|
||||
return d.MoveCreatedVHDsToOutputDir_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) CompactDisks(path string) (result string, err error) {
|
||||
d.CompactDisks_Called = true
|
||||
d.CompactDisks_Path = path
|
||||
d.CompactDisks_Result = "Mock compact result msg: mockdisk.vhdx. Disk size reduced by 20%"
|
||||
return d.CompactDisks_Result, d.CompactDisks_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) RestartVirtualMachine(vmName string) error {
|
||||
|
@ -519,7 +530,8 @@ func (d *DriverMock) CreateDvdDrive(vmName string, isoPath string, generation ui
|
|||
return d.CreateDvdDrive_ControllerNumber, d.CreateDvdDrive_ControllerLocation, d.CreateDvdDrive_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error {
|
||||
func (d *DriverMock) MountDvdDrive(vmName string, path string, controllerNumber uint,
|
||||
controllerLocation uint) error {
|
||||
d.MountDvdDrive_Called = true
|
||||
d.MountDvdDrive_VmName = vmName
|
||||
d.MountDvdDrive_Path = path
|
||||
|
@ -528,7 +540,8 @@ func (d *DriverMock) MountDvdDrive(vmName string, path string, controllerNumber
|
|||
return d.MountDvdDrive_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error {
|
||||
func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint,
|
||||
generation uint) error {
|
||||
d.SetBootDvdDrive_Called = true
|
||||
d.SetBootDvdDrive_VmName = vmName
|
||||
d.SetBootDvdDrive_ControllerNumber = controllerNumber
|
||||
|
|
|
@ -175,16 +175,24 @@ func (d *HypervPS4Driver) CreateVirtualSwitch(switchName string, switchType stri
|
|||
return hyperv.CreateVirtualSwitch(switchName, switchType)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string, vhdSizeBytes int64, diskBlockSize int64, controllerType string) error {
|
||||
return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes, diskBlockSize, controllerType)
|
||||
func (d *HypervPS4Driver) AddVirtualMachineHardDrive(vmName string, vhdFile string, vhdName string,
|
||||
vhdSizeBytes int64, diskBlockSize int64, controllerType string) error {
|
||||
return hyperv.AddVirtualMachineHardDiskDrive(vmName, vhdFile, vhdName, vhdSizeBytes,
|
||||
diskBlockSize, controllerType)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool) error {
|
||||
return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, diskBlockSize, switchName, generation, diffDisks, fixedVHD)
|
||||
func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64,
|
||||
diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool,
|
||||
fixedVHD bool) error {
|
||||
return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, ram, diskSize, diskBlockSize, switchName,
|
||||
generation, diffDisks, fixedVHD)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
|
||||
return hyperv.CloneVirtualMachine(cloneFromVmxcPath, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName)
|
||||
func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string,
|
||||
cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string,
|
||||
ram int64, switchName string) error {
|
||||
return hyperv.CloneVirtualMachine(cloneFromVmcxPath, cloneFromVmName, cloneFromSnapshotName,
|
||||
cloneAllSnapshots, vmName, path, harddrivePath, ram, switchName)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) DeleteVirtualMachine(vmName string) error {
|
||||
|
@ -211,7 +219,8 @@ func (d *HypervPS4Driver) SetVirtualMachineVirtualizationExtensions(vmName strin
|
|||
return hyperv.SetVirtualMachineVirtualizationExtensions(vmName, enable)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error {
|
||||
func (d *HypervPS4Driver) EnableVirtualMachineIntegrationService(vmName string,
|
||||
integrationServiceName string) error {
|
||||
return hyperv.EnableVirtualMachineIntegrationService(vmName, integrationServiceName)
|
||||
}
|
||||
|
||||
|
@ -219,12 +228,16 @@ func (d *HypervPS4Driver) ExportVirtualMachine(vmName string, path string) error
|
|||
return hyperv.ExportVirtualMachine(vmName, path)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) CompactDisks(expPath string, vhdDir string) error {
|
||||
return hyperv.CompactDisks(expPath, vhdDir)
|
||||
func (d *HypervPS4Driver) PreserveLegacyExportBehaviour(srcPath string, dstPath string) error {
|
||||
return hyperv.PreserveLegacyExportBehaviour(srcPath, dstPath)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
|
||||
return hyperv.CopyExportedVirtualMachine(expPath, outputPath, vhdDir, vmDir)
|
||||
func (d *HypervPS4Driver) MoveCreatedVHDsToOutputDir(srcPath string, dstPath string) error {
|
||||
return hyperv.MoveCreatedVHDsToOutputDir(srcPath, dstPath)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) CompactDisks(path string) (result string, err error) {
|
||||
return hyperv.CompactDisks(path)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) RestartVirtualMachine(vmName string) error {
|
||||
|
@ -235,11 +248,13 @@ func (d *HypervPS4Driver) CreateDvdDrive(vmName string, isoPath string, generati
|
|||
return hyperv.CreateDvdDrive(vmName, isoPath, generation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) MountDvdDrive(vmName string, path string, controllerNumber uint, controllerLocation uint) error {
|
||||
func (d *HypervPS4Driver) MountDvdDrive(vmName string, path string, controllerNumber uint,
|
||||
controllerLocation uint) error {
|
||||
return hyperv.MountDvdDrive(vmName, path, controllerNumber, controllerLocation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint, generation uint) error {
|
||||
func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint, controllerLocation uint,
|
||||
generation uint) error {
|
||||
return hyperv.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, generation)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// Produces:
|
||||
// VMName string - The name of the VM
|
||||
type StepCloneVM struct {
|
||||
CloneFromVMXCPath string
|
||||
CloneFromVMCXPath string
|
||||
CloneFromVMName string
|
||||
CloneFromSnapshotName string
|
||||
CloneAllSnapshots bool
|
||||
|
@ -37,7 +37,7 @@ func (s *StepCloneVM) Run(_ context.Context, state multistep.StateBag) multistep
|
|||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Cloning virtual machine...")
|
||||
|
||||
path := state.Get("packerTempDir").(string)
|
||||
path := state.Get("build_dir").(string)
|
||||
|
||||
// Determine if we even have an existing virtual harddrive to attach
|
||||
harddrivePath := ""
|
||||
|
@ -55,7 +55,8 @@ func (s *StepCloneVM) Run(_ context.Context, state multistep.StateBag) multistep
|
|||
// convert the MB to bytes
|
||||
ramSize := int64(s.RamSize * 1024 * 1024)
|
||||
|
||||
err := driver.CloneVirtualMachine(s.CloneFromVMXCPath, s.CloneFromVMName, s.CloneFromSnapshotName, s.CloneAllSnapshots, s.VMName, path, harddrivePath, ramSize, s.SwitchName)
|
||||
err := driver.CloneVirtualMachine(s.CloneFromVMCXPath, s.CloneFromVMName, s.CloneFromSnapshotName,
|
||||
s.CloneAllSnapshots, s.VMName, path, harddrivePath, ramSize, s.SwitchName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error cloning virtual machine: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepCollateArtifacts struct {
|
||||
OutputDir string
|
||||
SkipExport bool
|
||||
}
|
||||
|
||||
// Runs the step required to collate all build artifacts under the
|
||||
// specified output directory
|
||||
func (s *StepCollateArtifacts) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Collating build artifacts...")
|
||||
|
||||
if s.SkipExport {
|
||||
// Get the path to the main build directory from the statebag
|
||||
var buildDir string
|
||||
if v, ok := state.GetOk("build_dir"); ok {
|
||||
buildDir = v.(string)
|
||||
}
|
||||
// If the user has chosen to skip a full export of the VM the only
|
||||
// artifacts that they are interested in will be the VHDs. The
|
||||
// called function searches for all disks under the given source
|
||||
// directory and moves them to a 'Virtual Hard Disks' folder under
|
||||
// the destination directory
|
||||
err := driver.MoveCreatedVHDsToOutputDir(buildDir, s.OutputDir)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error moving VHDs from build dir to output dir: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
} else {
|
||||
// Get the full path to the export directory from the statebag
|
||||
var exportPath string
|
||||
if v, ok := state.GetOk("export_path"); ok {
|
||||
exportPath = v.(string)
|
||||
}
|
||||
// The export process exports the VM into a folder named 'vm name'
|
||||
// under the output directory. However, to maintain backwards
|
||||
// compatibility, we now need to shuffle around the exported folders
|
||||
// so the 'Snapshots', 'Virtual Hard Disks' and 'Virtual Machines'
|
||||
// directories appear *directly* under <output directory>.
|
||||
// The empty '<output directory>/<vm name>' directory is removed
|
||||
// when complete.
|
||||
// The 'Snapshots' folder will not be moved into the output
|
||||
// directory if it is empty.
|
||||
err := driver.PreserveLegacyExportBehaviour(exportPath, s.OutputDir)
|
||||
if err != nil {
|
||||
// No need to halt here; Just warn the user instead
|
||||
err = fmt.Errorf("WARNING: Error restoring legacy export dir structure: %s", err)
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Cleanup does nothing
|
||||
func (s *StepCollateArtifacts) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,92 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepCollateArtifacts_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCollateArtifacts)
|
||||
}
|
||||
|
||||
func TestStepCollateArtifacts_exportedArtifacts(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCollateArtifacts)
|
||||
|
||||
step.OutputDir = "foopath"
|
||||
vmName := "foo"
|
||||
|
||||
// Uses export path from the state bag
|
||||
exportPath := filepath.Join(step.OutputDir, vmName)
|
||||
state.Put("export_path", exportPath)
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if !driver.PreserveLegacyExportBehaviour_Called {
|
||||
t.Fatal("Should have called PreserveLegacyExportBehaviour")
|
||||
}
|
||||
if driver.PreserveLegacyExportBehaviour_SrcPath != exportPath {
|
||||
t.Fatalf("Should call with correct srcPath. Got: %s Wanted: %s",
|
||||
driver.PreserveLegacyExportBehaviour_SrcPath, exportPath)
|
||||
}
|
||||
if driver.PreserveLegacyExportBehaviour_DstPath != step.OutputDir {
|
||||
t.Fatalf("Should call with correct dstPath. Got: %s Wanted: %s",
|
||||
driver.PreserveLegacyExportBehaviour_DstPath, step.OutputDir)
|
||||
}
|
||||
|
||||
// Should only be called when skip_export is true
|
||||
if driver.MoveCreatedVHDsToOutputDir_Called {
|
||||
t.Fatal("Should NOT have called MoveCreatedVHDsToOutputDir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCollateArtifacts_skipExportArtifacts(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCollateArtifacts)
|
||||
|
||||
// Needs the path to the main output directory and build directory
|
||||
step.OutputDir = "foopath"
|
||||
buildDir := "fooBuildPath"
|
||||
state.Put("build_dir", buildDir)
|
||||
// Export has been skipped
|
||||
step.SkipExport = true
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if !driver.MoveCreatedVHDsToOutputDir_Called {
|
||||
t.Fatal("Should have called MoveCreatedVHDsToOutputDir")
|
||||
}
|
||||
if driver.MoveCreatedVHDsToOutputDir_SrcPath != buildDir {
|
||||
t.Fatalf("Should call with correct srcPath. Got: %s Wanted: %s",
|
||||
driver.MoveCreatedVHDsToOutputDir_SrcPath, buildDir)
|
||||
}
|
||||
if driver.MoveCreatedVHDsToOutputDir_DstPath != step.OutputDir {
|
||||
t.Fatalf("Should call with correct dstPath. Got: %s Wanted: %s",
|
||||
driver.MoveCreatedVHDsToOutputDir_DstPath, step.OutputDir)
|
||||
}
|
||||
|
||||
if driver.PreserveLegacyExportBehaviour_Called {
|
||||
t.Fatal("Should NOT have called PreserveLegacyExportBehaviour")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepCompactDisk struct {
|
||||
SkipCompaction bool
|
||||
}
|
||||
|
||||
// Run runs a compaction/optimisation process on attached VHD/VHDX disks
|
||||
func (s *StepCompactDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if s.SkipCompaction {
|
||||
ui.Say("Skipping disk compaction...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Get the dir used to store the VMs files during the build process
|
||||
var buildDir string
|
||||
if v, ok := state.GetOk("build_dir"); ok {
|
||||
buildDir = v.(string)
|
||||
}
|
||||
|
||||
ui.Say("Compacting disks...")
|
||||
// CompactDisks searches for all VHD/VHDX files under the supplied
|
||||
// path and runs the compacting process on each of them. If no disks
|
||||
// are found under the supplied path this is treated as a 'soft' error
|
||||
// and a warning message is printed. All other errors halt the build.
|
||||
result, err := driver.CompactDisks(buildDir)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error compacting disks: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
// Report disk compaction results/warn if no disks were found
|
||||
ui.Message(result)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Cleanup does nothing
|
||||
func (s *StepCompactDisk) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,63 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepCompactDisk_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCompactDisk)
|
||||
}
|
||||
|
||||
func TestStepCompactDisk(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCompactDisk)
|
||||
|
||||
// Set up the path to the build directory
|
||||
buildDir := "foopath"
|
||||
state.Put("build_dir", buildDir)
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if !driver.CompactDisks_Called {
|
||||
t.Fatal("Should have called CompactDisks")
|
||||
}
|
||||
if driver.CompactDisks_Path != buildDir {
|
||||
t.Fatalf("Should call with correct path. Got: %s Wanted: %s", driver.CompactDisks_Path, buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCompactDisk_skip(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCompactDisk)
|
||||
step.SkipCompaction = true
|
||||
|
||||
// Set up the path to the build directory
|
||||
state.Put("build_dir", "foopath")
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if driver.CompactDisks_Called {
|
||||
t.Fatal("Should NOT have called CompactDisks")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepCreateBuildDir struct {
|
||||
// User supplied directory under which we create the main build
|
||||
// directory. The build directory is used to house the VM files and
|
||||
// folders during the build. If unspecified the default temp directory
|
||||
// for the OS is used
|
||||
TempPath string
|
||||
// The full path to the build directory. This is the concatenation of
|
||||
// TempPath plus a directory uniquely named for the build
|
||||
buildDir string
|
||||
}
|
||||
|
||||
// Creates the main directory used to house the VMs files and folders
|
||||
// during the build
|
||||
func (s *StepCreateBuildDir) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Creating build directory...")
|
||||
|
||||
if s.TempPath == "" {
|
||||
s.TempPath = os.TempDir()
|
||||
}
|
||||
|
||||
var err error
|
||||
s.buildDir, err = ioutil.TempDir(s.TempPath, "packerhv")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating build directory: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Created build directory: %s", s.buildDir)
|
||||
|
||||
// Record the build directory location for later steps
|
||||
state.Put("build_dir", s.buildDir)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Cleanup removes the build directory
|
||||
func (s *StepCreateBuildDir) Cleanup(state multistep.StateBag) {
|
||||
if s.buildDir == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Deleting build directory...")
|
||||
|
||||
err := os.RemoveAll(s.buildDir)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting build directory: %s", err))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepCreateBuildDir_imp(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCreateBuildDir)
|
||||
}
|
||||
|
||||
func TestStepCreateBuildDir_Defaults(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateBuildDir)
|
||||
|
||||
// Default is for the user not to supply value for TempPath. When
|
||||
// nothing is set the step should use the OS temp directory as the root
|
||||
// for the build directory
|
||||
step.TempPath = ""
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
if v, ok := state.GetOk("build_dir"); !ok {
|
||||
t.Fatal("Should store path to build directory in statebag as 'build_dir'")
|
||||
} else {
|
||||
// On windows convert everything to forward slash separated paths
|
||||
// This prevents the regexp interpreting backslashes as escape sequences
|
||||
stateBuildDir := filepath.ToSlash(v.(string))
|
||||
expectedBuildDirRe := regexp.MustCompile(
|
||||
filepath.ToSlash(filepath.Join(os.TempDir(), "packerhv") + `[[:digit:]]{9}$`))
|
||||
match := expectedBuildDirRe.MatchString(stateBuildDir)
|
||||
if !match {
|
||||
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.buildDir); err == nil {
|
||||
t.Fatalf("Build directory should NOT exist after Cleanup: %s", step.buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateBuildDir_UserDefinedTempPath(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateBuildDir)
|
||||
|
||||
// Create a directory we'll use as the user supplied temp_path
|
||||
step.TempPath = genTestDirPath("userTempDir")
|
||||
err := os.Mkdir(step.TempPath, 0755) // The directory must exist
|
||||
if err != nil {
|
||||
t.Fatal("Error creating test directory")
|
||||
}
|
||||
defer os.RemoveAll(step.TempPath)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
if v, ok := state.GetOk("build_dir"); !ok {
|
||||
t.Fatal("Should store path to build directory in statebag as 'build_dir'")
|
||||
} else {
|
||||
// On windows convert everything to forward slash separated paths
|
||||
// This prevents the regexp interpreting backslashes as escape sequences
|
||||
stateBuildDir := filepath.ToSlash(v.(string))
|
||||
expectedBuildDirRe := regexp.MustCompile(
|
||||
filepath.ToSlash(filepath.Join(step.TempPath, "packerhv") + `[[:digit:]]{9}$`))
|
||||
match := expectedBuildDirRe.MatchString(stateBuildDir)
|
||||
if !match {
|
||||
t.Fatalf("Got path that doesn't match expected format in 'build_dir': %s", stateBuildDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.buildDir); err == nil {
|
||||
t.Fatalf("Build directory should NOT exist after Cleanup: %s", step.buildDir)
|
||||
}
|
||||
if _, err := os.Stat(step.TempPath); err != nil {
|
||||
t.Fatal("User supplied root for build directory should NOT be deleted by Cleanup")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateBuildDir_BadTempPath(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateBuildDir)
|
||||
|
||||
// Bad
|
||||
step.TempPath = genTestDirPath("iDontExist")
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("Should have error due to bad path")
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// This step creates switch for VM.
|
||||
// This step creates an external switch for the VM.
|
||||
//
|
||||
// Produces:
|
||||
// SwitchName string - The name of the Switch
|
||||
|
@ -18,6 +18,9 @@ type StepCreateExternalSwitch struct {
|
|||
oldSwitchName string
|
||||
}
|
||||
|
||||
// Run runs the step required to create an external switch. Depending on
|
||||
// the connectivity of the host machine, the external switch will allow the
|
||||
// build VM to connect to the outside world.
|
||||
func (s *StepCreateExternalSwitch) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
@ -30,10 +33,12 @@ func (s *StepCreateExternalSwitch) Run(_ context.Context, state multistep.StateB
|
|||
|
||||
packerExternalSwitchName := "paes_" + uuid.TimeOrderedUUID()
|
||||
|
||||
// CreateExternalVirtualSwitch checks for an existing external switch,
|
||||
// creating one if required, and connects the VM to it
|
||||
err = driver.CreateExternalVirtualSwitch(vmName, packerExternalSwitchName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating switch: %s", err)
|
||||
state.Put(errorMsg, err)
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
s.SwitchName = ""
|
||||
return multistep.ActionHalt
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepCreateTempDir struct {
|
||||
// The user-supplied root directories into which we create subdirectories.
|
||||
TempPath string
|
||||
VhdTempPath string
|
||||
// The subdirectories with the randomly generated name.
|
||||
dirPath string
|
||||
vhdDirPath string
|
||||
}
|
||||
|
||||
func (s *StepCreateTempDir) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Creating temporary directory...")
|
||||
|
||||
if s.TempPath == "" {
|
||||
s.TempPath = os.TempDir()
|
||||
}
|
||||
|
||||
packerTempDir, err := ioutil.TempDir(s.TempPath, "packerhv")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary directory: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.dirPath = packerTempDir
|
||||
state.Put("packerTempDir", packerTempDir)
|
||||
|
||||
if s.VhdTempPath == "" {
|
||||
// Fall back to regular temp dir if no separate VHD temp dir set.
|
||||
state.Put("packerVhdTempDir", packerTempDir)
|
||||
} else {
|
||||
packerVhdTempDir, err := ioutil.TempDir(s.VhdTempPath, "packerhv-vhd")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary VHD directory: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.vhdDirPath = packerVhdTempDir
|
||||
state.Put("packerVhdTempDir", packerVhdTempDir)
|
||||
}
|
||||
|
||||
// ui.Say("packerTempDir = '" + packerTempDir + "'")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateTempDir) Cleanup(state multistep.StateBag) {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if s.dirPath != "" {
|
||||
ui.Say("Deleting temporary directory...")
|
||||
|
||||
err := os.RemoveAll(s.dirPath)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting temporary directory: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if s.vhdDirPath != "" && s.dirPath != s.vhdDirPath {
|
||||
ui.Say("Deleting temporary VHD directory...")
|
||||
|
||||
err := os.RemoveAll(s.vhdDirPath)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error deleting temporary VHD directory: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,6 @@ type StepCreateVM struct {
|
|||
AdditionalDiskSize []uint
|
||||
DifferencingDisk bool
|
||||
MacAddress string
|
||||
SkipExport bool
|
||||
OutputDir string
|
||||
FixedVHD bool
|
||||
}
|
||||
|
||||
|
@ -42,7 +40,10 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Creating virtual machine...")
|
||||
|
||||
path := state.Get("packerTempDir").(string)
|
||||
var path string
|
||||
if v, ok := state.GetOk("build_dir"); ok {
|
||||
path = v.(string)
|
||||
}
|
||||
|
||||
// Determine if we even have an existing virtual harddrive to attach
|
||||
harddrivePath := ""
|
||||
|
@ -57,19 +58,13 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
log.Println("No existing virtual harddrive, not attaching.")
|
||||
}
|
||||
|
||||
vhdPath := state.Get("packerVhdTempDir").(string)
|
||||
|
||||
// inline vhd path if export is skipped
|
||||
if s.SkipExport {
|
||||
vhdPath = filepath.Join(s.OutputDir, "Virtual Hard Disks")
|
||||
}
|
||||
|
||||
// convert the MB to bytes
|
||||
ramSize := int64(s.RamSize * 1024 * 1024)
|
||||
diskSize := int64(s.DiskSize * 1024 * 1024)
|
||||
diskBlockSize := int64(s.DiskBlockSize * 1024 * 1024)
|
||||
|
||||
err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, diskBlockSize, s.SwitchName, s.Generation, s.DifferencingDisk, s.FixedVHD)
|
||||
err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, ramSize, diskSize, diskBlockSize,
|
||||
s.SwitchName, s.Generation, s.DifferencingDisk, s.FixedVHD)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating virtual machine: %s", err)
|
||||
state.Put("error", err)
|
||||
|
@ -128,7 +123,7 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
for index, size := range s.AdditionalDiskSize {
|
||||
diskSize := int64(size * 1024 * 1024)
|
||||
diskFile := fmt.Sprintf("%s-%d.vhdx", s.VMName, index)
|
||||
err = driver.AddVirtualMachineHardDrive(s.VMName, vhdPath, diskFile, diskSize, diskBlockSize, "SCSI")
|
||||
err = driver.AddVirtualMachineHardDrive(s.VMName, path, diskFile, diskSize, diskBlockSize, "SCSI")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating and attaching additional disk drive: %s", err)
|
||||
state.Put("error", err)
|
||||
|
|
|
@ -3,84 +3,49 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
const (
|
||||
vhdDir string = "Virtual Hard Disks"
|
||||
vmDir string = "Virtual Machines"
|
||||
)
|
||||
|
||||
type StepExportVm struct {
|
||||
OutputDir string
|
||||
SkipCompaction bool
|
||||
SkipExport bool
|
||||
OutputDir string
|
||||
SkipExport bool
|
||||
}
|
||||
|
||||
func (s *StepExportVm) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
var err error
|
||||
var errorMsg string
|
||||
|
||||
vmName := state.Get("vmName").(string)
|
||||
tmpPath := state.Get("packerTempDir").(string)
|
||||
outputPath := s.OutputDir
|
||||
expPath := s.OutputDir
|
||||
if s.SkipExport {
|
||||
ui.Say("Skipping export of virtual machine...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// create temp path to export vm
|
||||
errorMsg = "Error creating temp export path: %s"
|
||||
vmExportPath, err := ioutil.TempDir(tmpPath, "export")
|
||||
ui.Say("Exporting virtual machine...")
|
||||
|
||||
// The VM name is needed for the export command
|
||||
var vmName string
|
||||
if v, ok := state.GetOk("vmName"); ok {
|
||||
vmName = v.(string)
|
||||
}
|
||||
|
||||
// The export process exports the VM to a folder named 'vmName' under
|
||||
// the output directory. This contains the usual 'Snapshots', 'Virtual
|
||||
// Hard Disks' and 'Virtual Machines' directories.
|
||||
err := driver.ExportVirtualMachine(vmName, s.OutputDir)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
err = fmt.Errorf("Error exporting vm: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if !s.SkipExport {
|
||||
ui.Say("Exporting vm...")
|
||||
|
||||
err = driver.ExportVirtualMachine(vmName, vmExportPath)
|
||||
if err != nil {
|
||||
errorMsg = "Error exporting vm: %s"
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
// copy to output dir
|
||||
expPath = filepath.Join(vmExportPath, vmName)
|
||||
}
|
||||
// Store the path to the export directory for later steps
|
||||
exportPath := filepath.Join(s.OutputDir, vmName)
|
||||
state.Put("export_path", exportPath)
|
||||
|
||||
if s.SkipCompaction {
|
||||
ui.Say("Skipping disk compaction...")
|
||||
} else {
|
||||
ui.Say("Compacting disks...")
|
||||
err = driver.CompactDisks(expPath, vhdDir)
|
||||
if err != nil {
|
||||
errorMsg = "Error compacting disks: %s"
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if !s.SkipExport {
|
||||
ui.Say("Copying to output dir...")
|
||||
err = driver.CopyExportedVirtualMachine(expPath, outputPath, vhdDir, vmDir)
|
||||
if err != nil {
|
||||
errorMsg = "Error exporting vm: %s"
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepExportVm_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepExportVm)
|
||||
}
|
||||
|
||||
func TestStepExportVm(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepExportVm)
|
||||
|
||||
// ExportVirtualMachine needs the VM name and a path to export to
|
||||
vmName := "foo"
|
||||
state.Put("vmName", vmName)
|
||||
outputDir := "foopath"
|
||||
step.OutputDir = outputDir
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if !driver.ExportVirtualMachine_Called {
|
||||
t.Fatal("Should have called ExportVirtualMachine")
|
||||
}
|
||||
if driver.ExportVirtualMachine_Path != outputDir {
|
||||
t.Fatalf("Should call with correct path. Got: %s Wanted: %s",
|
||||
driver.ExportVirtualMachine_Path, outputDir)
|
||||
}
|
||||
if driver.ExportVirtualMachine_VmName != vmName {
|
||||
t.Fatalf("Should call with correct vm name. Got: %s Wanted: %s",
|
||||
driver.ExportVirtualMachine_VmName, vmName)
|
||||
}
|
||||
|
||||
// Test we stored the export path in the statebag and it is correct
|
||||
expectedPath := filepath.Join(outputDir, vmName)
|
||||
if exportPath, ok := state.GetOk("export_path"); !ok {
|
||||
t.Fatal("Should set export_path")
|
||||
} else if exportPath != expectedPath {
|
||||
t.Fatalf("Bad path stored for export_path. Got: %#v Wanted: %#v", exportPath, expectedPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepExportVm_skip(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepExportVm)
|
||||
step.SkipExport = true
|
||||
|
||||
// ExportVirtualMachine needs the VM name and a path to export to
|
||||
vmName := "foo"
|
||||
state.Put("vmName", vmName)
|
||||
outputDir := "foopath"
|
||||
step.OutputDir = outputDir
|
||||
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("Should NOT have error")
|
||||
}
|
||||
|
||||
// Test the driver
|
||||
if driver.ExportVirtualMachine_Called {
|
||||
t.Fatal("Should NOT have called ExportVirtualMachine")
|
||||
}
|
||||
|
||||
// Should not store the export path in the statebag
|
||||
if _, ok := state.GetOk("export_path"); ok {
|
||||
t.Fatal("Should NOT have stored export_path in the statebag")
|
||||
}
|
||||
}
|
|
@ -56,7 +56,8 @@ func (s *StepMountGuestAdditions) Run(_ context.Context, state multistep.StateBa
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", s.GuestAdditionsPath, controllerNumber, controllerLocation))
|
||||
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", s.GuestAdditionsPath,
|
||||
controllerNumber, controllerLocation))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ func (s *StepMountSecondaryDvdImages) Run(_ context.Context, state multistep.Sta
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", isoPath, controllerNumber, controllerLocation))
|
||||
log.Println(fmt.Sprintf("ISO %s mounted on DVD controller %v, location %v", isoPath, controllerNumber,
|
||||
controllerLocation))
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepOutputDir_imp(t *testing.T) {
|
||||
var _ multistep.Step = new(StepOutputDir)
|
||||
}
|
||||
|
||||
func TestStepOutputDir_Default(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
step.Path = genTestDirPath("packerHypervOutput")
|
||||
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatal("Should NOT have error")
|
||||
}
|
||||
|
||||
// The directory should have been created
|
||||
if _, err := os.Stat(step.Path); err != nil {
|
||||
t.Fatal("Should have created output directory")
|
||||
}
|
||||
|
||||
// Remove the directory created due to test
|
||||
err := os.RemoveAll(step.Path)
|
||||
if err != nil {
|
||||
t.Fatalf("Error encountered removing directory created by test: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_DirectoryAlreadyExistsNoForce(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
step.Path = genTestDirPath("packerHypervOutput")
|
||||
|
||||
// Create the directory so that we can test
|
||||
err := os.Mkdir(step.Path, 0755)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
|
||||
}
|
||||
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
|
||||
|
||||
step.Force = false // Default
|
||||
// Test the run
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("Should halt when directory exists and 'Force' is false. Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("Should error when directory exists and 'Force' is false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_DirectoryAlreadyExistsForce(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
step.Path = genTestDirPath("packerHypervOutput")
|
||||
|
||||
// Create the directory so that we can test
|
||||
err := os.Mkdir(step.Path, 0755)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
|
||||
}
|
||||
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
|
||||
|
||||
step.Force = true // User specified that existing directory and contents should be discarded
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Should complete when directory exists and 'Force' is true. Bad action: %v", action)
|
||||
}
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("Should NOT error when directory exists and 'Force' is true: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_CleanupBuildCancelled(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
step.Path = genTestDirPath("packerHypervOutput")
|
||||
|
||||
// Create the directory so that we can test the cleanup
|
||||
err := os.Mkdir(step.Path, 0755)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
|
||||
}
|
||||
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
|
||||
|
||||
// 'Cancel' the build
|
||||
state.Put(multistep.StateCancelled, true)
|
||||
|
||||
// Ensure the directory isn't removed if the cleanup flag is false
|
||||
step.cleanup = false
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.Path); err != nil {
|
||||
t.Fatal("Output dir should NOT be removed if on 'Cancel' if cleanup flag is unset/false")
|
||||
}
|
||||
|
||||
// Ensure the directory is removed if the cleanup flag is true
|
||||
step.cleanup = true
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.Path); err == nil {
|
||||
t.Fatalf("Output directory should NOT exist after 'Cancel' and Cleanup: %s", step.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepOutputDir_CleanupBuildHalted(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepOutputDir)
|
||||
|
||||
step.Path = genTestDirPath("packerHypervOutput")
|
||||
|
||||
// Create the directory so that we can test the cleanup
|
||||
err := os.Mkdir(step.Path, 0755)
|
||||
if err != nil {
|
||||
t.Fatal("Test failed to create directory for test of Cancel and Cleanup")
|
||||
}
|
||||
defer os.RemoveAll(step.Path) // Ensure we clean up if something goes wrong
|
||||
|
||||
// 'Halt' the build and test the directory is removed
|
||||
state.Put(multistep.StateHalted, true)
|
||||
|
||||
// Ensure the directory isn't removed if the cleanup flag is false
|
||||
step.cleanup = false
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.Path); err != nil {
|
||||
t.Fatal("Output dir should NOT be removed if on 'Halt' if cleanup flag is unset/false")
|
||||
}
|
||||
|
||||
// Ensure the directory is removed if the cleanup flag is true
|
||||
step.cleanup = true
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(step.Path); err == nil {
|
||||
t.Fatalf("Output directory should NOT exist after 'Halt' and Cleanup: %s", step.Path)
|
||||
}
|
||||
}
|
|
@ -17,10 +17,9 @@ import (
|
|||
//
|
||||
// Uses:
|
||||
// communicator packer.Communicator
|
||||
// dir OutputDir
|
||||
// driver Driver
|
||||
// ui packer.Ui
|
||||
// vmx_path string
|
||||
// driver Driver
|
||||
// ui packer.Ui
|
||||
// vmName string
|
||||
//
|
||||
// Produces:
|
||||
// <nothing>
|
||||
|
|
|
@ -18,7 +18,8 @@ func (s *StepSleep) Run(_ context.Context, state multistep.StateBag) multistep.S
|
|||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if len(s.ActionName) > 0 {
|
||||
ui.Say(s.ActionName + "! Waiting for " + fmt.Sprintf("%v", uint(s.Minutes)) + " minutes to let the action to complete...")
|
||||
ui.Say(s.ActionName + "! Waiting for " + fmt.Sprintf("%v", uint(s.Minutes)) +
|
||||
" minutes to let the action to complete...")
|
||||
}
|
||||
time.Sleep(time.Minute * s.Minutes)
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func testState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", new(DriverMock))
|
||||
state.Put("ui", &packer.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
||||
|
||||
// Generates an absolute path to a directory under OS temp with a name
|
||||
// beginning with prefix and a UUID appended to the end
|
||||
func genTestDirPath(prefix string) string {
|
||||
return filepath.Join(os.TempDir(), prefix+"-"+uuid.TimeOrderedUUID())
|
||||
}
|
|
@ -27,7 +27,8 @@ func (s *StepUnmountDvdDrive) Run(_ context.Context, state multistep.StateBag) m
|
|||
dvdController := dvdControllerState.(DvdControllerProperties)
|
||||
|
||||
if dvdController.Existing {
|
||||
ui.Say(fmt.Sprintf("Unmounting os dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Unmounting os dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error unmounting os dvd drive: %s", err)
|
||||
|
@ -36,7 +37,8 @@ func (s *StepUnmountDvdDrive) Run(_ context.Context, state multistep.StateBag) m
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
} else {
|
||||
ui.Say(fmt.Sprintf("Delete os dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Delete os dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting os dvd drive: %s", err)
|
||||
|
|
|
@ -27,7 +27,8 @@ func (s *StepUnmountGuestAdditions) Run(_ context.Context, state multistep.State
|
|||
dvdController := dvdControllerState.(DvdControllerProperties)
|
||||
|
||||
if dvdController.Existing {
|
||||
ui.Say(fmt.Sprintf("Unmounting Integration Services dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Unmounting Integration Services dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error unmounting Integration Services dvd drive: %s", err)
|
||||
|
@ -36,7 +37,8 @@ func (s *StepUnmountGuestAdditions) Run(_ context.Context, state multistep.State
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
} else {
|
||||
ui.Say(fmt.Sprintf("Delete Integration Services dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Delete Integration Services dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting Integration Services dvd drive: %s", err)
|
||||
|
|
|
@ -28,7 +28,8 @@ func (s *StepUnmountSecondaryDvdImages) Run(_ context.Context, state multistep.S
|
|||
|
||||
for _, dvdController := range dvdControllers {
|
||||
if dvdController.Existing {
|
||||
ui.Say(fmt.Sprintf("Unmounting secondary dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Unmounting secondary dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.UnmountDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error unmounting secondary dvd drive: %s", err)
|
||||
|
@ -37,7 +38,8 @@ func (s *StepUnmountSecondaryDvdImages) Run(_ context.Context, state multistep.S
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
} else {
|
||||
ui.Say(fmt.Sprintf("Delete secondary dvd drives controller %d location %d ...", dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
ui.Say(fmt.Sprintf("Delete secondary dvd drives controller %d location %d ...",
|
||||
dvdController.ControllerNumber, dvdController.ControllerLocation))
|
||||
err := driver.DeleteDvdDrive(vmName, dvdController.ControllerNumber, dvdController.ControllerLocation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error deleting secondary dvd drive: %s", err)
|
||||
|
|
|
@ -96,11 +96,6 @@ type Config struct {
|
|||
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
|
||||
TempPath string `mapstructure:"temp_path"`
|
||||
|
||||
// A separate path can be used for storing the VM's disk image. The purpose is to enable
|
||||
// reading and writing to take place on different physical disks (read from VHD temp path
|
||||
// write to regular temp path while exporting the VM) to eliminate a single-disk bottleneck.
|
||||
VhdTempPath string `mapstructure:"vhd_temp_path"`
|
||||
|
||||
Communicator string `mapstructure:"communicator"`
|
||||
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
||||
|
@ -150,7 +145,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if len(b.config.ISOConfig.ISOUrls) < 1 || (strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhd" && strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhdx") {
|
||||
if len(b.config.ISOConfig.ISOUrls) < 1 ||
|
||||
(strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhd" &&
|
||||
strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhdx") {
|
||||
//We only create a new hard drive if an existing one to copy from does not exist
|
||||
err = b.checkDiskSize()
|
||||
if err != nil {
|
||||
|
@ -254,25 +251,35 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
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" {
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
} else {
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.EnableVirtualizationExtensions {
|
||||
hasVirtualMachineVirtualizationExtensions, err := powershell.HasVirtualMachineVirtualizationExtensions()
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization extensions support: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization "+
|
||||
"extensions support: %s", err))
|
||||
} else {
|
||||
if !hasVirtualMachineVirtualizationExtensions {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 or newer."))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support "+
|
||||
"virtual machine virtualization extension. Please use Windows 10 or Windows Server "+
|
||||
"2016 or newer."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,24 +314,29 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
if b.config.EnableVirtualizationExtensions {
|
||||
if b.config.EnableDynamicMemory {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, dynamic memory should not be allowed.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"dynamic memory should not be allowed.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
|
||||
if !b.config.EnableMacSpoofing {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, mac spoofing should be allowed.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"mac spoofing should be allowed.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
|
||||
if b.config.RamSize < MinNestedVirtualizationRamSize {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start any nested VMs.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start " +
|
||||
"any nested VMs.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.SwitchVlanId != "" {
|
||||
if b.config.SwitchVlanId != b.config.VlanId {
|
||||
warning = fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor vlan. The switch will not be able to see traffic from the VM.")
|
||||
warning = fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor " +
|
||||
"vlan. The switch will not be able to see traffic from the VM.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
}
|
||||
|
@ -355,9 +367,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
state.Put("ui", ui)
|
||||
|
||||
steps := []multistep.Step{
|
||||
&hypervcommon.StepCreateTempDir{
|
||||
TempPath: b.config.TempPath,
|
||||
VhdTempPath: b.config.VhdTempPath,
|
||||
&hypervcommon.StepCreateBuildDir{
|
||||
TempPath: b.config.TempPath,
|
||||
},
|
||||
&hypervcommon.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
|
@ -399,8 +410,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions,
|
||||
AdditionalDiskSize: b.config.AdditionalDiskSize,
|
||||
DifferencingDisk: b.config.DifferencingDisk,
|
||||
SkipExport: b.config.SkipExport,
|
||||
OutputDir: b.config.OutputDir,
|
||||
MacAddress: b.config.MacAddress,
|
||||
FixedVHD: b.config.FixedVHD,
|
||||
},
|
||||
|
@ -467,10 +476,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&hypervcommon.StepUnmountFloppyDrive{
|
||||
Generation: b.config.Generation,
|
||||
},
|
||||
&hypervcommon.StepExportVm{
|
||||
OutputDir: b.config.OutputDir,
|
||||
&hypervcommon.StepCompactDisk{
|
||||
SkipCompaction: b.config.SkipCompaction,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&hypervcommon.StepExportVm{
|
||||
OutputDir: b.config.OutputDir,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&hypervcommon.StepCollateArtifacts{
|
||||
OutputDir: b.config.OutputDir,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
|
||||
// the clean up actions for each step will be executed reverse order
|
||||
|
@ -527,11 +542,14 @@ func (b *Builder) checkDiskSize() error {
|
|||
log.Println(fmt.Sprintf("%s: %v", "DiskSize", b.config.DiskSize))
|
||||
|
||||
if b.config.DiskSize < MinDiskSize {
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space >= %v GB, but defined: %v", MinDiskSize, b.config.DiskSize/1024)
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space >= %v GB, but defined: %v",
|
||||
MinDiskSize, b.config.DiskSize/1024)
|
||||
} else if b.config.DiskSize > MaxDiskSize && !b.config.FixedVHD {
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v", MaxDiskSize, b.config.DiskSize/1024)
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v",
|
||||
MaxDiskSize, b.config.DiskSize/1024)
|
||||
} else if b.config.DiskSize > MaxVHDSize && b.config.FixedVHD {
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v", MaxVHDSize/1024, b.config.DiskSize/1024)
|
||||
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v",
|
||||
MaxVHDSize/1024, b.config.DiskSize/1024)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -545,9 +563,11 @@ func (b *Builder) checkDiskBlockSize() error {
|
|||
log.Println(fmt.Sprintf("%s: %v", "DiskBlockSize", b.config.DiskBlockSize))
|
||||
|
||||
if b.config.DiskBlockSize < MinDiskBlockSize {
|
||||
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size >= %v MB, but defined: %v", MinDiskBlockSize, b.config.DiskBlockSize)
|
||||
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size >= %v MB, but defined: %v",
|
||||
MinDiskBlockSize, b.config.DiskBlockSize)
|
||||
} else if b.config.DiskBlockSize > MaxDiskBlockSize {
|
||||
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size <= %v MB, but defined: %v", MaxDiskBlockSize, b.config.DiskBlockSize)
|
||||
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size <= %v MB, but defined: %v",
|
||||
MaxDiskBlockSize, b.config.DiskBlockSize)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -561,9 +581,11 @@ func (b *Builder) checkRamSize() error {
|
|||
log.Println(fmt.Sprintf("%s: %v", "RamSize", b.config.RamSize))
|
||||
|
||||
if b.config.RamSize < MinRamSize {
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size >= %v MB, but defined: %v", MinRamSize, b.config.RamSize)
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size >= %v MB, but defined: %v",
|
||||
MinRamSize, b.config.RamSize)
|
||||
} else if b.config.RamSize > MaxRamSize {
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size <= %v MB, but defined: %v", MaxRamSize, b.config.RamSize)
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size <= %v MB, but defined: %v",
|
||||
MaxRamSize, b.config.RamSize)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -104,7 +104,8 @@ func TestBuilderPrepare_DiskBlockSize(t *testing.T) {
|
|||
t.Fatalf("bad err: %s", err)
|
||||
}
|
||||
if b.config.DiskBlockSize != expected_default_block_size {
|
||||
t.Fatalf("bad default block size with empty config: %d. Expected %d", b.config.DiskBlockSize, expected_default_block_size)
|
||||
t.Fatalf("bad default block size with empty config: %d. Expected %d", b.config.DiskBlockSize,
|
||||
expected_default_block_size)
|
||||
}
|
||||
|
||||
test_sizes := []uint{0, 1, 32, 256, 512, 1 * 1024, 32 * 1024}
|
||||
|
@ -117,7 +118,8 @@ func TestBuilderPrepare_DiskBlockSize(t *testing.T) {
|
|||
t.Fatalf("bad, should have no warns: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatalf("bad, should have error but didn't. disk_block_size=%d outside expected valid range [%d,%d]", test_size, expected_min_block_size, expected_max_block_size)
|
||||
t.Fatalf("bad, should have error. disk_block_size=%d outside expected valid range [%d,%d]",
|
||||
test_size, expected_min_block_size, expected_max_block_size)
|
||||
}
|
||||
} else {
|
||||
if len(warns) > 0 {
|
||||
|
@ -128,11 +130,13 @@ func TestBuilderPrepare_DiskBlockSize(t *testing.T) {
|
|||
}
|
||||
if test_size == 0 {
|
||||
if b.config.DiskBlockSize != expected_default_block_size {
|
||||
t.Fatalf("bad default block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize, expected_default_block_size)
|
||||
t.Fatalf("bad default block size with 0 value config: %d. Expected: %d",
|
||||
b.config.DiskBlockSize, expected_default_block_size)
|
||||
}
|
||||
} else {
|
||||
if b.config.DiskBlockSize != test_size {
|
||||
t.Fatalf("bad block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize, expected_default_block_size)
|
||||
t.Fatalf("bad block size with 0 value config: %d. Expected: %d", b.config.DiskBlockSize,
|
||||
expected_default_block_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +151,8 @@ func TestBuilderPrepare_FixedVHDFormat(t *testing.T) {
|
|||
config["skip_compaction"] = true
|
||||
config["differencing_disk"] = false
|
||||
|
||||
//use_fixed_vhd_format should work with generation = 1, skip_compaction = true, and differencing_disk = false
|
||||
// use_fixed_vhd_format should work with generation = 1, skip_compaction
|
||||
// = true, and differencing_disk = false
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
|
|
|
@ -62,7 +62,7 @@ type Config struct {
|
|||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
|
||||
// This is the path to a directory containing an exported virtual machine.
|
||||
CloneFromVMXCPath string `mapstructure:"clone_from_vmxc_path"`
|
||||
CloneFromVMCXPath string `mapstructure:"clone_from_vmcx_path"`
|
||||
|
||||
// This is the name of the virtual machine to clone from.
|
||||
CloneFromVMName string `mapstructure:"clone_from_vm_name"`
|
||||
|
@ -91,6 +91,7 @@ type Config struct {
|
|||
EnableSecureBoot bool `mapstructure:"enable_secure_boot"`
|
||||
SecureBootTemplate string `mapstructure:"secure_boot_template"`
|
||||
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
|
||||
TempPath string `mapstructure:"temp_path"`
|
||||
|
||||
Communicator string `mapstructure:"communicator"`
|
||||
|
||||
|
@ -157,36 +158,45 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
b.config.Generation = 1
|
||||
|
||||
if b.config.CloneFromVMName == "" {
|
||||
if b.config.CloneFromVMXCPath == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vm_name must be specified if clone_from_vmxc_path is not specified."))
|
||||
if b.config.CloneFromVMCXPath == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vm_name must be specified if "+
|
||||
"clone_from_vmcx_path is not specified."))
|
||||
}
|
||||
} else {
|
||||
virtualMachineExists, err := powershell.DoesVirtualMachineExist(b.config.CloneFromVMName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to clone from exists: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to clone "+
|
||||
"from exists: %s", err))
|
||||
} else {
|
||||
if !virtualMachineExists {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Virtual machine '%s' to clone from does not exist.", b.config.CloneFromVMName))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Virtual machine '%s' to clone from does not "+
|
||||
"exist.", b.config.CloneFromVMName))
|
||||
} else {
|
||||
b.config.Generation, err = powershell.GetVirtualMachineGeneration(b.config.CloneFromVMName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine to clone from generation: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine to clone "+
|
||||
"from generation: %s", err))
|
||||
}
|
||||
|
||||
if b.config.CloneFromSnapshotName != "" {
|
||||
virtualMachineSnapshotExists, err := powershell.DoesVirtualMachineSnapshotExist(b.config.CloneFromVMName, b.config.CloneFromSnapshotName)
|
||||
virtualMachineSnapshotExists, err := powershell.DoesVirtualMachineSnapshotExist(
|
||||
b.config.CloneFromVMName, b.config.CloneFromSnapshotName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine snapshot to clone from exists: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine "+
|
||||
"snapshot to clone from exists: %s", err))
|
||||
} else {
|
||||
if !virtualMachineSnapshotExists {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Virtual machine snapshot '%s' on virtual machine '%s' to clone from does not exist.", b.config.CloneFromSnapshotName, b.config.CloneFromVMName))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Virtual machine snapshot '%s' on "+
|
||||
"virtual machine '%s' to clone from does not exist.",
|
||||
b.config.CloneFromSnapshotName, b.config.CloneFromVMName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtualMachineOn, err := powershell.IsVirtualMachineOn(b.config.CloneFromVMName)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to clone is running: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting if virtual machine to "+
|
||||
"clone is running: %s", err))
|
||||
} else {
|
||||
if virtualMachineOn {
|
||||
warning := fmt.Sprintf("Cloning from a virtual machine that is running.")
|
||||
|
@ -197,15 +207,16 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if b.config.CloneFromVMXCPath == "" {
|
||||
if b.config.CloneFromVMCXPath == "" {
|
||||
if b.config.CloneFromVMName == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vmxc_path be specified if clone_from_vm_name must is not specified."))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vmcx_path be specified if "+
|
||||
"clone_from_vm_name must is not specified."))
|
||||
}
|
||||
} else {
|
||||
if _, err := os.Stat(b.config.CloneFromVMXCPath); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(b.config.CloneFromVMCXPath); os.IsNotExist(err) {
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("CloneFromVMXCPath does not exist: %s", err))
|
||||
errs, fmt.Errorf("CloneFromVMCXPath does not exist: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,25 +287,36 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
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" {
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
} else {
|
||||
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, ", ")))
|
||||
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, ", ")))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.EnableVirtualizationExtensions {
|
||||
hasVirtualMachineVirtualizationExtensions, err := powershell.HasVirtualMachineVirtualizationExtensions()
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization extensions support: %s", err))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization "+
|
||||
"extensions support: %s", err))
|
||||
} else {
|
||||
if !hasVirtualMachineVirtualizationExtensions {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 or newer."))
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support "+
|
||||
"virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 "+
|
||||
"or newer."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,24 +336,29 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
|
||||
if b.config.EnableVirtualizationExtensions {
|
||||
if b.config.EnableDynamicMemory {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, dynamic memory should not be allowed.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"dynamic memory should not be allowed.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
|
||||
if !b.config.EnableMacSpoofing {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, mac spoofing should be allowed.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"mac spoofing should be allowed.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
|
||||
if b.config.RamSize < MinNestedVirtualizationRamSize {
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start any nested VMs.")
|
||||
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
"there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start " +
|
||||
"any nested VMs.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.SwitchVlanId != "" {
|
||||
if b.config.SwitchVlanId != b.config.VlanId {
|
||||
warning = fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor vlan. The switch will not be able to see traffic from the VM.")
|
||||
warning = fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor " +
|
||||
"vlan. The switch will not be able to see traffic from the VM.")
|
||||
warnings = appendWarnings(warnings, warning)
|
||||
}
|
||||
}
|
||||
|
@ -362,7 +389,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
state.Put("ui", ui)
|
||||
|
||||
steps := []multistep.Step{
|
||||
&hypervcommon.StepCreateTempDir{},
|
||||
&hypervcommon.StepCreateBuildDir{
|
||||
TempPath: b.config.TempPath,
|
||||
},
|
||||
&hypervcommon.StepOutputDir{
|
||||
Force: b.config.PackerForce,
|
||||
Path: b.config.OutputDir,
|
||||
|
@ -397,7 +426,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
SwitchName: b.config.SwitchName,
|
||||
},
|
||||
&hypervcommon.StepCloneVM{
|
||||
CloneFromVMXCPath: b.config.CloneFromVMXCPath,
|
||||
CloneFromVMCXPath: b.config.CloneFromVMCXPath,
|
||||
CloneFromVMName: b.config.CloneFromVMName,
|
||||
CloneFromSnapshotName: b.config.CloneFromSnapshotName,
|
||||
CloneAllSnapshots: b.config.CloneAllSnapshots,
|
||||
|
@ -476,10 +505,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&hypervcommon.StepUnmountFloppyDrive{
|
||||
Generation: b.config.Generation,
|
||||
},
|
||||
&hypervcommon.StepExportVm{
|
||||
OutputDir: b.config.OutputDir,
|
||||
&hypervcommon.StepCompactDisk{
|
||||
SkipCompaction: b.config.SkipCompaction,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&hypervcommon.StepExportVm{
|
||||
OutputDir: b.config.OutputDir,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
&hypervcommon.StepCollateArtifacts{
|
||||
OutputDir: b.config.OutputDir,
|
||||
SkipExport: b.config.SkipExport,
|
||||
},
|
||||
|
||||
// the clean up actions for each step will be executed reverse order
|
||||
|
@ -536,9 +571,11 @@ func (b *Builder) checkRamSize() error {
|
|||
log.Println(fmt.Sprintf("%s: %v", "RamSize", b.config.RamSize))
|
||||
|
||||
if b.config.RamSize < MinRamSize {
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size >= %v MB, but defined: %v", MinRamSize, b.config.RamSize)
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size >= %v MB, but defined: %v",
|
||||
MinRamSize, b.config.RamSize)
|
||||
} else if b.config.RamSize > MaxRamSize {
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size <= %v MB, but defined: %v", MaxRamSize, b.config.RamSize)
|
||||
return fmt.Errorf("ram_size: Virtual machine requires memory size <= %v MB, but defined: %v",
|
||||
MaxRamSize, b.config.RamSize)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -23,7 +23,7 @@ func testConfig() map[string]interface{} {
|
|||
"ssh_username": "foo",
|
||||
"ram_size": 64,
|
||||
"guest_additions_mode": "none",
|
||||
"clone_from_vmxc_path": "generated",
|
||||
"clone_from_vmcx_path": "generated",
|
||||
packer.BuildNameConfigKey: "foo",
|
||||
}
|
||||
}
|
||||
|
@ -40,13 +40,13 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
|
@ -65,13 +65,13 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
// Add a random key
|
||||
config["i_should_not_be_valid"] = true
|
||||
|
@ -87,7 +87,7 @@ func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|||
func TestBuilderPrepare_CloneFromExistingMachineOrImportFromExportedMachineSettingsRequired(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
delete(config, "clone_from_vmxc_path")
|
||||
delete(config, "clone_from_vmcx_path")
|
||||
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
|
@ -102,7 +102,7 @@ func TestBuilderPrepare_ExportedMachinePathDoesNotExist(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
@ -111,7 +111,7 @@ func TestBuilderPrepare_ExportedMachinePathDoesNotExist(t *testing.T) {
|
|||
//Delete the folder immediately
|
||||
os.RemoveAll(td)
|
||||
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
|
@ -126,7 +126,7 @@ func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
|
@ -135,7 +135,7 @@ func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) {
|
|||
//Only delete afterwards
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
|
@ -146,10 +146,10 @@ func TestBuilderPrepare_ExportedMachinePathExists(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func disabled_TestBuilderPrepare_CloneFromVmSettingUsedSoNoCloneFromVmxcPathRequired(t *testing.T) {
|
||||
func disabled_TestBuilderPrepare_CloneFromVmSettingUsedSoNoCloneFromVmcxPathRequired(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
delete(config, "clone_from_vmxc_path")
|
||||
delete(config, "clone_from_vmcx_path")
|
||||
|
||||
config["clone_from_vm_name"] = "test_machine_name_that_does_not_exist"
|
||||
|
||||
|
@ -162,7 +162,8 @@ func disabled_TestBuilderPrepare_CloneFromVmSettingUsedSoNoCloneFromVmxcPathRequ
|
|||
t.Fatal("should have error")
|
||||
} else {
|
||||
errorMessage := err.Error()
|
||||
if errorMessage != "1 error(s) occurred:\n\n* Virtual machine 'test_machine_name_that_does_not_exist' to clone from does not exist." {
|
||||
if errorMessage != "1 error(s) occurred:\n\n* Virtual machine 'test_machine_name_that_does_not_exist' "+
|
||||
"to clone from does not exist." {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -172,13 +173,13 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
// Test bad
|
||||
config["iso_checksum"] = ""
|
||||
|
@ -210,13 +211,13 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
// Test bad
|
||||
config["iso_checksum_type"] = ""
|
||||
|
@ -274,13 +275,13 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
delete(config, "iso_url")
|
||||
delete(config, "iso_urls")
|
||||
|
@ -353,13 +354,13 @@ func TestBuilderPrepare_FloppyFiles(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
delete(config, "floppy_files")
|
||||
warns, err := b.Prepare(config)
|
||||
|
@ -395,13 +396,13 @@ func TestBuilderPrepare_InvalidFloppies(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
config["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"}
|
||||
b = Builder{}
|
||||
|
@ -420,13 +421,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) {
|
|||
{
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
config["communicator"] = "winrm"
|
||||
config["winrm_username"] = "username"
|
||||
|
@ -457,13 +458,13 @@ func TestBuilderPrepare_CommConfig(t *testing.T) {
|
|||
{
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
config["communicator"] = "ssh"
|
||||
config["ssh_username"] = "username"
|
||||
|
@ -495,13 +496,13 @@ func TestUserVariablesInBootCommand(t *testing.T) {
|
|||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Create vmxc folder
|
||||
//Create vmcx folder
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
config["clone_from_vmxc_path"] = td
|
||||
config["clone_from_vmcx_path"] = td
|
||||
|
||||
config[packer.UserVariablesConfigKey] = map[string]string{"test-variable": "test"}
|
||||
config["boot_command"] = []string{"blah {{user `test-variable`}} blah"}
|
||||
|
|
|
@ -153,6 +153,10 @@ Fixes that are run:
|
|||
docker-email Removes "login_email" from the Docker builder
|
||||
powershell-escapes Removes PowerShell escapes from user env vars and
|
||||
elevated username and password strings
|
||||
hyperv-deprecations Removes the deprecated "vhd_temp_path" setting from
|
||||
Hyper-V ISO builder templates
|
||||
hyperv-vmxc-typo Corrects a typo in the "clone_from_vmxc_path"
|
||||
setting. Replaces with "clone_from_vmcx_path".
|
||||
|
||||
Options:
|
||||
|
||||
|
|
|
@ -111,7 +111,8 @@ Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -Cont
|
|||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, path, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
|
||||
err := ps.Run(script, vmName, path, strconv.FormatInt(int64(controllerNumber), 10),
|
||||
strconv.FormatInt(int64(controllerLocation), 10))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -124,7 +125,8 @@ Hyper-V\Set-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -Cont
|
|||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10),
|
||||
strconv.FormatInt(int64(controllerLocation), 10))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -146,7 +148,8 @@ if (!$vmDvdDrive) {throw 'unable to find dvd drive'}
|
|||
Hyper-V\Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDvdDrive -ErrorAction SilentlyContinue
|
||||
`
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10),
|
||||
strconv.FormatInt(int64(controllerLocation), 10))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +163,8 @@ Hyper-V\Remove-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -C
|
|||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
|
||||
err := ps.Run(script, vmName, strconv.FormatInt(int64(controllerNumber), 10),
|
||||
strconv.FormatInt(int64(controllerLocation), 10))
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -198,13 +202,15 @@ Hyper-V\Set-VMFloppyDiskDrive -VMName $vmName -Path $null
|
|||
return err
|
||||
}
|
||||
|
||||
func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, diskBlockSize int64, switchName string, generation uint, diffDisks bool, fixedVHD bool) error {
|
||||
func CreateVirtualMachine(vmName string, path string, harddrivePath string, ram int64,
|
||||
diskSize int64, diskBlockSize int64, switchName string, generation uint,
|
||||
diffDisks bool, fixedVHD bool) error {
|
||||
|
||||
if generation == 2 {
|
||||
var script = `
|
||||
param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks)
|
||||
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [int]$generation, [string]$diffDisks)
|
||||
$vhdx = $vmName + '.vhdx'
|
||||
$vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
|
||||
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
|
||||
if ($harddrivePath){
|
||||
if($diffDisks -eq "true"){
|
||||
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
|
||||
|
@ -218,21 +224,24 @@ if ($harddrivePath){
|
|||
}
|
||||
`
|
||||
var ps powershell.PowerShellCmd
|
||||
if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10), switchName, strconv.FormatInt(int64(generation), 10), strconv.FormatBool(diffDisks)); err != nil {
|
||||
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10),
|
||||
strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10),
|
||||
switchName, strconv.FormatInt(int64(generation), 10),
|
||||
strconv.FormatBool(diffDisks)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return DisableAutomaticCheckpoints(vmName)
|
||||
} else {
|
||||
var script = `
|
||||
param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [string]$diffDisks, [string]$fixedVHD)
|
||||
param([string]$vmName, [string]$path, [string]$harddrivePath, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [long]$vhdBlockSizeBytes, [string]$switchName, [string]$diffDisks, [string]$fixedVHD)
|
||||
if($fixedVHD -eq "true"){
|
||||
$vhdx = $vmName + '.vhd'
|
||||
}
|
||||
else{
|
||||
$vhdx = $vmName + '.vhdx'
|
||||
}
|
||||
$vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
|
||||
$vhdPath = Join-Path -Path $path -ChildPath $vhdx
|
||||
if ($harddrivePath){
|
||||
if($diffDisks -eq "true"){
|
||||
New-VHD -Path $vhdPath -ParentPath $harddrivePath -Differencing -BlockSizeBytes $vhdBlockSizeBytes
|
||||
|
@ -252,7 +261,9 @@ if ($harddrivePath){
|
|||
}
|
||||
`
|
||||
var ps powershell.PowerShellCmd
|
||||
if err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10), switchName, strconv.FormatBool(diffDisks), strconv.FormatBool(fixedVHD)); err != nil {
|
||||
if err := ps.Run(script, vmName, path, harddrivePath, strconv.FormatInt(ram, 10),
|
||||
strconv.FormatInt(diskSize, 10), strconv.FormatInt(diskBlockSize, 10),
|
||||
switchName, strconv.FormatBool(diffDisks), strconv.FormatBool(fixedVHD)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -275,7 +286,7 @@ if ((Get-Command Hyper-V\Set-Vm).Parameters["AutomaticCheckpointsEnabled"]) {
|
|||
return err
|
||||
}
|
||||
|
||||
func ExportVmxcVirtualMachine(exportPath string, vmName string, snapshotName string, allSnapshots bool) error {
|
||||
func ExportVmcxVirtualMachine(exportPath string, vmName string, snapshotName string, allSnapshots bool) error {
|
||||
var script = `
|
||||
param([string]$exportPath, [string]$vmName, [string]$snapshotName, [string]$allSnapshotsString)
|
||||
|
||||
|
@ -322,22 +333,22 @@ $result = Remove-Item -Path $WorkingPath
|
|||
return err
|
||||
}
|
||||
|
||||
func CopyVmxcVirtualMachine(exportPath string, cloneFromVmxcPath string) error {
|
||||
func CopyVmcxVirtualMachine(exportPath string, cloneFromVmcxPath string) error {
|
||||
var script = `
|
||||
param([string]$exportPath, [string]$cloneFromVmxcPath)
|
||||
if (!(Test-Path $cloneFromVmxcPath)){
|
||||
throw "Clone from vmxc directory: $cloneFromVmxcPath does not exist!"
|
||||
param([string]$exportPath, [string]$cloneFromVmcxPath)
|
||||
if (!(Test-Path $cloneFromVmcxPath)){
|
||||
throw "Clone from vmcx directory: $cloneFromVmcxPath does not exist!"
|
||||
}
|
||||
|
||||
if (!(Test-Path $exportPath)){
|
||||
New-Item -ItemType Directory -Force -Path $exportPath
|
||||
}
|
||||
$cloneFromVmxcPath = Join-Path $cloneFromVmxcPath '\*'
|
||||
Copy-Item $cloneFromVmxcPath $exportPath -Recurse -Force
|
||||
$cloneFromVmcxPath = Join-Path $cloneFromVmcxPath '\*'
|
||||
Copy-Item $cloneFromVmcxPath $exportPath -Recurse -Force
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, exportPath, cloneFromVmxcPath)
|
||||
err := ps.Run(script, exportPath, cloneFromVmcxPath)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -354,7 +365,9 @@ Hyper-V\Set-VMNetworkAdapter $vmName -staticmacaddress $mac
|
|||
return err
|
||||
}
|
||||
|
||||
func ImportVmxcVirtualMachine(importPath string, vmName string, harddrivePath string, ram int64, switchName string) error {
|
||||
func ImportVmcxVirtualMachine(importPath string, vmName string, harddrivePath string,
|
||||
ram int64, switchName string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName)
|
||||
|
||||
|
@ -409,20 +422,24 @@ if ($vm) {
|
|||
return err
|
||||
}
|
||||
|
||||
func CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
|
||||
func CloneVirtualMachine(cloneFromVmcxPath string, cloneFromVmName string,
|
||||
cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string,
|
||||
path string, harddrivePath string, ram int64, switchName string) error {
|
||||
|
||||
if cloneFromVmName != "" {
|
||||
if err := ExportVmxcVirtualMachine(path, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots); err != nil {
|
||||
if err := ExportVmcxVirtualMachine(path, cloneFromVmName,
|
||||
cloneFromSnapshotName, cloneAllSnapshots); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cloneFromVmxcPath != "" {
|
||||
if err := CopyVmxcVirtualMachine(path, cloneFromVmxcPath); err != nil {
|
||||
if cloneFromVmcxPath != "" {
|
||||
if err := CopyVmcxVirtualMachine(path, cloneFromVmcxPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ImportVmxcVirtualMachine(path, vmName, harddrivePath, ram, switchName); err != nil {
|
||||
if err := ImportVmcxVirtualMachine(path, vmName, harddrivePath, ram, switchName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -665,33 +682,146 @@ if (Test-Path -Path ([IO.Path]::Combine($path, $vmName, 'Virtual Machines', '*.V
|
|||
return err
|
||||
}
|
||||
|
||||
func CompactDisks(expPath string, vhdDir string) error {
|
||||
func PreserveLegacyExportBehaviour(srcPath, dstPath string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$srcPath, [string]$vhdDirName)
|
||||
Get-ChildItem "$srcPath/$vhdDirName" -Filter *.vhd* | %{
|
||||
Optimize-VHD -Path $_.FullName -Mode Full
|
||||
param([string]$srcPath, [string]$dstPath)
|
||||
|
||||
# Validate the paths returning an error if they are empty or don't exist
|
||||
$srcPath, $dstPath | % {
|
||||
if ($_) {
|
||||
if (! (Test-Path $_)) {
|
||||
[System.Console]::Error.WriteLine("Path $_ does not exist")
|
||||
exit
|
||||
}
|
||||
} else {
|
||||
[System.Console]::Error.WriteLine("A supplied path is empty")
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
# Export-VM should just create directories at the root of the export path
|
||||
# but, just in case, move all files as well...
|
||||
Move-Item -Path (Join-Path (Get-Item $srcPath).FullName "*.*") -Destination (Get-Item $dstPath).FullName
|
||||
|
||||
# Move directories with content; Delete empty directories
|
||||
$dirObj = Get-ChildItem $srcPath -Directory | % {
|
||||
New-Object PSObject -Property @{
|
||||
FullName=$_.FullName;
|
||||
HasContent=$(if ($_.GetFileSystemInfos().Count -gt 0) {$true} else {$false})
|
||||
}
|
||||
}
|
||||
foreach ($directory in $dirObj) {
|
||||
if ($directory.HasContent) {
|
||||
Move-Item -Path $directory.FullName -Destination (Get-Item $dstPath).FullName
|
||||
} else {
|
||||
Remove-Item -Path $directory.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Only remove the source directory if it is now empty
|
||||
if ( $((Get-Item $srcPath).GetFileSystemInfos().Count) -eq 0 ) {
|
||||
Remove-Item -Path $srcPath
|
||||
} else {
|
||||
# 'Return' an error message to PowerShellCmd as the directory should
|
||||
# always be empty at the end of the script. The check is here to stop
|
||||
# the Remove-Item command from doing any damage if some unforeseen
|
||||
# error has occured
|
||||
[System.Console]::Error.WriteLine("Refusing to remove $srcPath as it is not empty")
|
||||
exit
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, expPath, vhdDir)
|
||||
err := ps.Run(script, srcPath, dstPath)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func CopyExportedVirtualMachine(expPath string, outputPath string, vhdDir string, vmDir string) error {
|
||||
func MoveCreatedVHDsToOutputDir(srcPath, dstPath string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$srcPath, [string]$dstPath, [string]$vhdDirName, [string]$vmDir)
|
||||
Move-Item -Path (Join-Path (Get-Item $srcPath).FullName "*.*") -Destination $dstPath
|
||||
Move-Item -Path (Join-Path (Get-Item $srcPath).FullName $vhdDirName) -Destination $dstPath
|
||||
Move-Item -Path (Join-Path (Get-Item $srcPath).FullName $vmDir) -Destination $dstPath
|
||||
param([string]$srcPath, [string]$dstPath)
|
||||
|
||||
# Validate the paths returning an error if the supplied path is empty
|
||||
# or if the paths don't exist
|
||||
$srcPath, $dstPath | % {
|
||||
if ($_) {
|
||||
if (! (Test-Path $_)) {
|
||||
[System.Console]::Error.WriteLine("Path $_ does not exist")
|
||||
exit
|
||||
}
|
||||
} else {
|
||||
[System.Console]::Error.WriteLine("A supplied path is empty")
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
# Convert to absolute paths if required
|
||||
$srcPathAbs = (Get-Item($srcPath)).FullName
|
||||
$dstPathAbs = (Get-Item($dstPath)).FullName
|
||||
|
||||
# Get the full path to all disks under the directory or exit if none are found
|
||||
$disks = Get-ChildItem -Path $srcPathAbs -Recurse -Filter *.vhd* -ErrorAction SilentlyContinue | % { $_.FullName }
|
||||
if ($disks.Length -eq 0) {
|
||||
[System.Console]::Error.WriteLine("No disks found under $srcPathAbs")
|
||||
exit
|
||||
}
|
||||
|
||||
# Set up directory for VHDs in the destination directory
|
||||
$vhdDstDir = Join-Path -Path $dstPathAbs -ChildPath 'Virtual Hard Disks'
|
||||
if (! (Test-Path $vhdDstDir)) {
|
||||
New-Item -ItemType Directory -Force -Path $vhdDstDir
|
||||
}
|
||||
|
||||
# Move the disks
|
||||
foreach ($disk in $disks) {
|
||||
Move-Item -Path $disk -Destination $vhdDstDir
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, expPath, outputPath, vhdDir, vmDir)
|
||||
err := ps.Run(script, srcPath, dstPath)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func CompactDisks(path string) (result string, err error) {
|
||||
var script = `
|
||||
param([string]$srcPath)
|
||||
|
||||
$disks = Get-ChildItem -Path $srcPath -Recurse -Filter *.vhd* -ErrorAction SilentlyContinue | % { $_.FullName }
|
||||
# Failure to find any disks is treated as a 'soft' error. Simply print out
|
||||
# a warning and exit
|
||||
if ($disks.Length -eq 0) {
|
||||
Write-Output "WARNING: No disks found under $srcPath"
|
||||
exit
|
||||
}
|
||||
|
||||
foreach ($disk in $disks) {
|
||||
Write-Output "Compacting disk: $(Split-Path $disk -leaf)"
|
||||
|
||||
$sizeBefore = $disk.Length
|
||||
Optimize-VHD -Path $disk -Mode Full
|
||||
$sizeAfter = $disk.Length
|
||||
|
||||
# Calculate the percentage change in disk size
|
||||
if ($sizeAfter -gt 0) { # Protect against division by zero
|
||||
$percentChange = ( ( $sizeAfter / $sizeBefore ) * 100 ) - 100
|
||||
switch($percentChange) {
|
||||
{$_ -lt 0} {Write-Output "Disk size reduced by: $(([math]::Abs($_)).ToString("#.#"))%"}
|
||||
{$_ -eq 0} {Write-Output "Disk size is unchanged"}
|
||||
{$_ -gt 0} {Write-Output "WARNING: Disk size increased by: $($_.ToString("#.#"))%"}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
result, err = ps.Output(script, path)
|
||||
return
|
||||
}
|
||||
|
||||
func CreateVirtualSwitch(switchName string, switchType string) (bool, error) {
|
||||
|
||||
var script = `
|
||||
|
@ -905,7 +1035,8 @@ Hyper-V\Get-VMNetworkAdapter -VMName $vmName | Hyper-V\Connect-VMNetworkAdapter
|
|||
return err
|
||||
}
|
||||
|
||||
func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64, vhdBlockSize int64, controllerType string) error {
|
||||
func AddVirtualMachineHardDiskDrive(vmName string, vhdRoot string, vhdName string, vhdSizeBytes int64,
|
||||
vhdBlockSize int64, controllerType string) error {
|
||||
|
||||
var script = `
|
||||
param([string]$vmName,[string]$vhdRoot, [string]$vhdName, [string]$vhdSizeInBytes, [string]$vhdBlockSizeInByte, [string]$controllerType)
|
||||
|
|
|
@ -36,6 +36,8 @@ func init() {
|
|||
"amazon-private-ip": new(FixerAmazonPrivateIP),
|
||||
"docker-email": new(FixerDockerEmail),
|
||||
"powershell-escapes": new(FixerPowerShellEscapes),
|
||||
"hyperv-deprecations": new(FixerHypervDeprecations),
|
||||
"hyperv-vmxc-typo": new(FixerHypervVmxcTypo),
|
||||
}
|
||||
|
||||
FixerOrder = []string{
|
||||
|
@ -55,5 +57,7 @@ func init() {
|
|||
"amazon-private-ip",
|
||||
"docker-email",
|
||||
"powershell-escapes",
|
||||
"hyperv-deprecations",
|
||||
"hyperv-vmxc-typo",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// FixerHypervDeprecations removes the deprecated "vhd_temp_path" setting
|
||||
// from Hyper-V ISO builder templates
|
||||
type FixerHypervDeprecations struct{}
|
||||
|
||||
func (FixerHypervDeprecations) Fix(input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// The type we'll decode into; we only care about builders
|
||||
type template struct {
|
||||
Builders []map[string]interface{}
|
||||
}
|
||||
|
||||
// Decode the input into our structure, if we can
|
||||
var tpl template
|
||||
if err := mapstructure.Decode(input, &tpl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, builder := range tpl.Builders {
|
||||
builderTypeRaw, ok := builder["type"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
builderType, ok := builderTypeRaw.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if builderType != "hyperv-iso" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok = builder["vhd_temp_path"]
|
||||
if ok {
|
||||
delete(builder, "vhd_temp_path")
|
||||
}
|
||||
}
|
||||
|
||||
input["builders"] = tpl.Builders
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (FixerHypervDeprecations) Synopsis() string {
|
||||
return `Removes the deprecated "vhd_temp_path" setting from Hyper-V ISO builder templates`
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFixerHypervDeprecations_impl(t *testing.T) {
|
||||
var _ Fixer = new(FixerHypervDeprecations)
|
||||
}
|
||||
|
||||
func TestFixerHypervDeprecations_Fix(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]interface{}
|
||||
Expected map[string]interface{}
|
||||
}{
|
||||
// No vhd_temp_path field in template - noop
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "hyperv-iso",
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "hyperv-iso",
|
||||
},
|
||||
},
|
||||
|
||||
// Deprecated vhd_temp_path field in template should be deleted
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "hyperv-iso",
|
||||
"vhd_temp_path": "foopath",
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "hyperv-iso",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
var f FixerHypervDeprecations
|
||||
|
||||
input := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Input},
|
||||
}
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Expected},
|
||||
}
|
||||
|
||||
output, err := f.Fix(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, output, "Should be equal")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// FixerHypervVmxcTypo fixes the typo in "clone_from_vmxc_path" replacing
|
||||
// it with "clone_from_vmcx_path" in Hyper-V VMCX builder templates
|
||||
type FixerHypervVmxcTypo struct{}
|
||||
|
||||
func (FixerHypervVmxcTypo) Fix(input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// The type we'll decode into; we only care about builders
|
||||
type template struct {
|
||||
Builders []map[string]interface{}
|
||||
}
|
||||
|
||||
// Decode the input into our structure, if we can
|
||||
var tpl template
|
||||
if err := mapstructure.Decode(input, &tpl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, builder := range tpl.Builders {
|
||||
builderTypeRaw, ok := builder["type"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
builderType, ok := builderTypeRaw.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if builderType != "hyperv-vmcx" {
|
||||
continue
|
||||
}
|
||||
|
||||
path, ok := builder["clone_from_vmxc_path"]
|
||||
if ok {
|
||||
delete(builder, "clone_from_vmxc_path")
|
||||
builder["clone_from_vmcx_path"] = path
|
||||
}
|
||||
}
|
||||
|
||||
input["builders"] = tpl.Builders
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func (FixerHypervVmxcTypo) Synopsis() string {
|
||||
return `Fixes a typo replacing "clone_from_vmxc_path" with "clone_from_vmcx_path" ` +
|
||||
`in Hyper-V VMCX builder templates`
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFixerHypervVmxcTypo_impl(t *testing.T) {
|
||||
var _ Fixer = new(FixerHypervVmxcTypo)
|
||||
}
|
||||
|
||||
func TestFixerHypervVmxcTypo_Fix(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input map[string]interface{}
|
||||
Expected map[string]interface{}
|
||||
}{
|
||||
// No "clone_from_vmxc_path" in template - noop
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "hyperv-vmcx",
|
||||
"temp_path": "C:/some/temp/path",
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "hyperv-vmcx",
|
||||
"temp_path": "C:/some/temp/path",
|
||||
},
|
||||
},
|
||||
|
||||
// "clone_from_vmxc_path" should be replaced with
|
||||
// "clone_from_vmcx_path" in template
|
||||
{
|
||||
Input: map[string]interface{}{
|
||||
"type": "hyperv-vmcx",
|
||||
"clone_from_vmxc_path": "C:/some/vmcx/path",
|
||||
},
|
||||
|
||||
Expected: map[string]interface{}{
|
||||
"type": "hyperv-vmcx",
|
||||
"clone_from_vmcx_path": "C:/some/vmcx/path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
var f FixerHypervVmxcTypo
|
||||
|
||||
input := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Input},
|
||||
}
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"builders": []map[string]interface{}{tc.Expected},
|
||||
}
|
||||
|
||||
output, err := f.Fix(input)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, output, "Should be equal")
|
||||
}
|
||||
}
|
|
@ -201,12 +201,13 @@ builder.
|
|||
the default virtual network card. The MAC address must be a string with
|
||||
no delimiters, for example "0000deadbeef".
|
||||
|
||||
- `output_directory` (string) - This is the path to the directory where the
|
||||
resulting virtual machine will be created. This may be relative or
|
||||
absolute. If relative, the path is relative to the working directory when
|
||||
`packer` is executed. This directory must not exist or be empty prior to
|
||||
running the builder. By default this is "output-BUILDNAME" where
|
||||
"BUILDNAME" is the name of the build.
|
||||
- `output_directory` (string) - This setting specifies the directory that
|
||||
artifacts from the build, such as the virtual machine files and disks,
|
||||
will be output to. The path to the directory may be relative or
|
||||
absolute. If relative, the path is relative to the working directory
|
||||
`packer` is executed from. This directory must not exist or, if
|
||||
created, must be empty prior to running the builder. By default this is
|
||||
"output-BUILDNAME" where "BUILDNAME" is the name of the build.
|
||||
|
||||
- `ram_size` (number) - The amount, in megabytes, of RAM to assign to the
|
||||
VM. By default, this is 1 GB.
|
||||
|
@ -238,11 +239,11 @@ builder.
|
|||
- `skip_compaction` (boolean) - If `true` skip compacting the hard disk for
|
||||
the virtual machine when exporting. This defaults to `false`.
|
||||
|
||||
- `skip_export` (boolean) - If `true` Packer will skip the export of the
|
||||
VM. If you are interested only in the VHD/VHDX files, you can enable
|
||||
this option. This will create inline disks which improves the build
|
||||
performance. There will not be any copying of source VHDs to the temp
|
||||
directory. This defaults to `false`.
|
||||
- `skip_export` (boolean) - If `true` Packer will skip the export of the VM.
|
||||
If you are interested only in the VHD/VHDX files, you can enable this
|
||||
option. The resulting VHD/VHDX file will be output to
|
||||
`<output_directory>/Virtual Hard Disks`. By default this option is `false`
|
||||
and Packer will export the VM to `output_directory`.
|
||||
|
||||
- `switch_name` (string) - The name of the switch to connect the virtual
|
||||
machine to. By default, leaving this value unset will cause Packer to
|
||||
|
@ -254,8 +255,15 @@ builder.
|
|||
set on the switch's network card. If this value is set it should match
|
||||
the VLAN specified in by `vlan_id`.
|
||||
|
||||
- `temp_path` (string) - This is the temporary path in which Packer will
|
||||
create the virtual machine. By default the value is the system `%temp%`.
|
||||
- `temp_path` (string) - The location under which Packer will create a
|
||||
directory to house all the VM files and folders during the build.
|
||||
By default `%TEMP%` is used which, for most systems, will evaluate to
|
||||
`%USERPROFILE%/AppData/Local/Temp`.
|
||||
|
||||
The build directory housed under `temp_path` will have a name similar
|
||||
to `packerhv1234567`. The seven digit number at the end of the name is
|
||||
automatically generated by Packer to ensure the directory name is
|
||||
unique.
|
||||
|
||||
- `use_fixed_vhd_format` (boolean) - If true, creates the boot disk on the
|
||||
virtual machine as a fixed VHD format disk. The default is `false`, which
|
||||
|
@ -266,11 +274,6 @@ builder.
|
|||
option is outputing a disk that is in the format required for upload to
|
||||
Azure.
|
||||
|
||||
- `vhd_temp_path` (string) - A separate path to be used for storing the VM's
|
||||
disk image. The purpose is to enable reading and writing to take place on
|
||||
different physical disks (read from VHD temp path, write to regular temp
|
||||
path while exporting the VM) to eliminate a single-disk bottleneck.
|
||||
|
||||
- `vlan_id` (string) - This is the VLAN of the virtual machine's network
|
||||
card for the new virtual machine. By default none is set. If none is set
|
||||
then VLANs are not set on the virtual machine's network card.
|
||||
|
|
|
@ -22,18 +22,18 @@ boots it, provisions software within the OS, and then shuts it down. The
|
|||
result of the Hyper-V builder is a directory containing all the files
|
||||
necessary to run the virtual machine portably.
|
||||
|
||||
## Basic Example
|
||||
## Basic Examples
|
||||
|
||||
Here is a basic example. This example is not functional. It will start the OS
|
||||
installer but then fail because we don't provide the preseed file for Ubuntu
|
||||
to self-install. Still, the example serves to show the basic configuration:
|
||||
Here are some basic examples. Neither example would really do anything more
|
||||
than producing a copy of the source virtual machine. However, the examples
|
||||
could be used as a starting point for more advanced templates.
|
||||
|
||||
Import from folder:
|
||||
|
||||
``` json
|
||||
{
|
||||
"type": "hyperv-vmcx",
|
||||
"clone_from_vmxc_path": "c:\\virtual machines\\ubuntu-12.04.5-server-amd64",
|
||||
"clone_from_vmcx_path": "c:/virtual machines/ubuntu-12.04.5-server-amd64",
|
||||
"ssh_username": "packer",
|
||||
"ssh_password": "packer",
|
||||
"shutdown_command": "echo 'packer' | sudo -S shutdown -P now"
|
||||
|
@ -71,12 +71,14 @@ builder.
|
|||
|
||||
### Required for virtual machine import:
|
||||
|
||||
- `clone_from_vmxc_path` (string) - The path to the exported virtual machine
|
||||
folder.
|
||||
- `clone_from_vmcx_path` (string) - The path to a directory containing a
|
||||
previously exported virtual machine. The exported machine will be used
|
||||
as the source for new VM.
|
||||
|
||||
|
||||
### Required for virtual machine clone:
|
||||
|
||||
- `clone_from_vm_name` (string) - The name of the vm to clone from. Ideally
|
||||
- `clone_from_vm_name` (string) - The name of the VM to clone from. Ideally
|
||||
the machine to clone from should be shutdown.
|
||||
|
||||
### Optional:
|
||||
|
@ -94,11 +96,25 @@ builder.
|
|||
Packer to wait for 1 minute 30 seconds before typing the boot command.
|
||||
The default duration is "10s" (10 seconds).
|
||||
|
||||
- `clone_all_snapshots` (boolean) - If set to `true` all snapshots will be
|
||||
cloned when the machine is cloned.
|
||||
- `clone_all_snapshots` (boolean) - If set to `true` all snapshots
|
||||
present in the source machine will be copied when the machine is
|
||||
cloned. The final result of the build will be an exported virtual
|
||||
machine that contains all the snapshots of the parent.
|
||||
|
||||
- `clone_from_snapshot_name` (string) - The name of the snapshot to clone
|
||||
from.
|
||||
If `clone_from_vmcx_path` is set, this setting has no effect and any
|
||||
snapshots associated with the source VM will also be present in the
|
||||
resultant VM.
|
||||
|
||||
If `clone_from_snapshot_name` is configured, this setting has no
|
||||
effect and the resultant machine will not contain any snapshots.
|
||||
|
||||
- `clone_from_snapshot_name` (string) - The name of a snapshot in the
|
||||
source machine to use as a starting point for the clone. If the value
|
||||
given is an empty string, the last snapshot present in the source will
|
||||
be chosen as the starting point for the new VM.
|
||||
|
||||
This setting only has an effect if using `clone_from_vm_name` and is
|
||||
ignored otherwise.
|
||||
|
||||
- `cpu` (number) - The number of CPUs the virtual machine should use. If
|
||||
this isn't specified, the default is 1 CPU.
|
||||
|
@ -202,12 +218,13 @@ builder.
|
|||
the default virtual network card. The MAC address must be a string with
|
||||
no delimiters, for example "0000deadbeef".
|
||||
|
||||
- `output_directory` (string) - This is the path to the directory where the
|
||||
resulting virtual machine will be created. This may be relative or
|
||||
absolute. If relative, the path is relative to the working directory when
|
||||
`packer` is executed. This directory must not exist or be empty prior to
|
||||
running the builder. By default this is "output-BUILDNAME" where
|
||||
"BUILDNAME" is the name of the build.
|
||||
- `output_directory` (string) - This setting specifies the directory that
|
||||
artifacts from the build, such as the virtual machine files and disks,
|
||||
will be output to. The path to the directory may be relative or
|
||||
absolute. If relative, the path is relative to the working directory
|
||||
`packer` is executed from. This directory must not exist or, if
|
||||
created, must be empty prior to running the builder. By default this is
|
||||
"output-BUILDNAME" where "BUILDNAME" is the name of the build.
|
||||
|
||||
- `ram_size` (number) - The amount, in megabytes, of RAM to assign to the
|
||||
VM. By default, this is 1 GB.
|
||||
|
@ -239,11 +256,11 @@ builder.
|
|||
- `skip_compaction` (boolean) - If `true` skip compacting the hard disk for
|
||||
the virtual machine when exporting. This defaults to `false`.
|
||||
|
||||
- `skip_export` (boolean) - If `true` Packer will skip the export of the
|
||||
VM. If you are interested only in the VHD/VHDX files, you can enable
|
||||
this option. This will create inline disks which improves the build
|
||||
performance. There will not be any copying of source VHDs to the temp
|
||||
directory. This defaults to `false`.
|
||||
- `skip_export` (boolean) - If `true` Packer will skip the export of the VM.
|
||||
If you are interested only in the VHD/VHDX files, you can enable this
|
||||
option. The resulting VHD/VHDX file will be output to
|
||||
`<output_directory>/Virtual Hard Disks`. By default this option is `false`
|
||||
and Packer will export the VM to `output_directory`.
|
||||
|
||||
- `switch_name` (string) - The name of the switch to connect the virtual
|
||||
machine to. By default, leaving this value unset will cause Packer to
|
||||
|
@ -255,6 +272,16 @@ builder.
|
|||
set on the switch's network card. If this value is set it should match
|
||||
the VLAN specified in by `vlan_id`.
|
||||
|
||||
- `temp_path` (string) - The location under which Packer will create a
|
||||
directory to house all the VM files and folders during the build.
|
||||
By default `%TEMP%` is used which, for most systems, will evaluate to
|
||||
`%USERPROFILE%/AppData/Local/Temp`.
|
||||
|
||||
The build directory housed under `temp_path` will have a name similar
|
||||
to `packerhv1234567`. The seven digit number at the end of the name is
|
||||
automatically generated by Packer to ensure the directory name is
|
||||
unique.
|
||||
|
||||
- `vlan_id` (string) - This is the VLAN of the virtual machine's network
|
||||
card for the new virtual machine. By default none is set. If none is set
|
||||
then VLANs are not set on the virtual machine's network card.
|
||||
|
|
Loading…
Reference in New Issue