Can specify an iso, vhd or vhdx for download. If it is a vhd or vhdx it is used as the hard drive for spinning up a new machine, importing an exported virtual machine or cloning a virtual machine.

Can import a virtual machine from a folder
Can clone an existing virtual machine
This commit is contained in:
Taliesin Sisson 2017-05-21 17:29:26 +01:00 committed by Vijaya Bhaskar Reddy Kondreddi
parent 452fcbd9a1
commit efa62e1550
12 changed files with 309 additions and 128 deletions

View File

@ -64,9 +64,9 @@ type Driver interface {
DeleteVirtualSwitch(string) error
CreateVirtualMachine(string, string, string, int64, int64, string, uint) error
CreateVirtualMachine(string, string, string, string, int64, int64, string, uint) error
CloneVirtualMachine(string, string, bool, string, string, int64, string) error
CloneVirtualMachine(string, string, string, bool, string, string, string, int64, string) error
DeleteVirtualMachine(string) error

View File

@ -170,12 +170,12 @@ func (d *HypervPS4Driver) CreateVirtualSwitch(switchName string, switchType stri
return hyperv.CreateVirtualSwitch(switchName, switchType)
}
func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint) error {
return hyperv.CreateVirtualMachine(vmName, path, vhdPath, ram, diskSize, switchName, generation)
func (d *HypervPS4Driver) CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdPath string, ram int64, diskSize int64, switchName string, generation uint) error {
return hyperv.CreateVirtualMachine(vmName, path, harddrivePath, vhdPath, ram, diskSize, switchName, generation)
}
func (d *HypervPS4Driver) CloneVirtualMachine(cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, ram int64, switchName string) error {
return hyperv.CloneVirtualMachine(cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots, vmName, path, ram, switchName)
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) DeleteVirtualMachine(vmName string) error {

View File

@ -2,9 +2,12 @@ package common
import (
"fmt"
"log"
"strings"
"path/filepath"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/hashicorp/packer/packer"
)
// This step clones an existing virtual machine.
@ -12,6 +15,7 @@ import (
// Produces:
// VMName string - The name of the VM
type StepCloneVM struct {
CloneFromVMXCPath string
CloneFromVMName string
CloneFromSnapshotName string
CloneAllSnapshots bool
@ -31,11 +35,24 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Cloning virtual machine...")
path := state.Get("packerTempDir").(string)
// Determine if we even have an existing virtual harddrive to attach
harddrivePath := ""
if harddrivePathRaw, ok := state.GetOk("iso_path"); ok {
extension := strings.ToLower(filepath.Ext(harddrivePathRaw.(string)))
if extension == "vhd" || extension == "vhdx" {
harddrivePath = harddrivePathRaw.(string)
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
// convert the MB to bytes
ramSize := int64(s.RamSize * 1024 * 1024)
err := driver.CloneVirtualMachine(s.CloneFromVMName, s.CloneFromSnapshotName, s.CloneAllSnapshots, s.VMName, path, ramSize, s.SwitchName)
err := driver.CloneVirtualMachine(s.CloneFromVMXCPath, 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)

View File

@ -2,7 +2,10 @@ package common
import (
"fmt"
"log"
"strings"
"path/filepath"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
@ -14,6 +17,7 @@ import (
type StepCreateVM struct {
VMName string
SwitchName string
HarddrivePath string
RamSize uint
DiskSize uint
Generation uint
@ -30,13 +34,26 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Creating virtual machine...")
path := state.Get("packerTempDir").(string)
vhdPath := state.Get("packerVhdTempDir").(string)
// Determine if we even have an existing virtual harddrive to attach
harddrivePath := ""
if harddrivePathRaw, ok := state.GetOk("iso_path"); ok {
extension := strings.ToLower(filepath.Ext(harddrivePathRaw.(string)))
if extension == "vhd" || extension == "vhdx" {
harddrivePath = harddrivePathRaw.(string)
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
} else {
log.Println("No existing virtual harddrive, not attaching.")
}
vhdPath := state.Get("packerVhdTempDir").(string)
// convert the MB to bytes
ramSize := int64(s.RamSize * 1024 * 1024)
diskSize := int64(s.DiskSize * 1024 * 1024)
err := driver.CreateVirtualMachine(s.VMName, path, vhdPath, ramSize, diskSize, s.SwitchName, s.Generation)
err := driver.CreateVirtualMachine(s.VMName, path, harddrivePath, vhdPath, ramSize, diskSize, s.SwitchName, s.Generation)
if err != nil {
err := fmt.Errorf("Error creating virtual machine: %s", err)
state.Put("error", err)

View File

@ -6,6 +6,7 @@ import (
"log"
"os"
"strings"
"path/filepath"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
"github.com/hashicorp/packer/common"
@ -117,16 +118,26 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
warnings = append(warnings, isoWarnings...)
errs = packer.MultiErrorAppend(errs, isoErrs...)
if len(b.config.ISOConfig.ISOUrls) > 0 {
extension := strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0]))
if extension == "vhd" || extension == "vhdx" {
b.config.ISOConfig.TargetExtension = extension
}
}
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
err = b.checkDiskSize()
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
if b.config.ISOConfig.TargetExtension != "vhd" && b.config.ISOConfig.TargetExtension != "vhdx" {
//We only create a new hard drive if an existing one to copy from does not exist
err = b.checkDiskSize()
if err != nil {
errs = packer.MultiErrorAppend(errs, err)
}
}
err = b.checkRamSize()
@ -163,6 +174,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
log.Println(fmt.Sprintf("%s: %v", "SwitchName", b.config.SwitchName))
// Errors
if b.config.GuestAdditionsMode == "" {
if b.config.GuestAdditionsPath != "" {
b.config.GuestAdditionsMode = "attach"

View File

@ -235,7 +235,7 @@ func TestBuilderPrepare_ISOUrl(t *testing.T) {
delete(config, "iso_url")
delete(config, "iso_urls")
// Test both epty
// Test both empty
config["iso_url"] = ""
b = Builder{}
warns, err := b.Prepare(config)

View File

@ -7,15 +7,16 @@ import (
"os"
"strings"
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
"github.com/hashicorp/packer/common"
powershell "github.com/hashicorp/packer/common/powershell"
"github.com/hashicorp/packer/common/powershell/hyperv"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
hypervcommon "github.com/mitchellh/packer/builder/hyperv/common"
"github.com/mitchellh/packer/common"
powershell "github.com/mitchellh/packer/common/powershell"
"github.com/mitchellh/packer/common/powershell/hyperv"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
"path/filepath"
)
const (
@ -69,6 +70,9 @@ type Config struct {
// The path to the integration services iso
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"`
// This is the name of the virtual machine to clone from.
CloneFromVMName string `mapstructure:"clone_from_vm_name"`
@ -122,6 +126,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
warnings = append(warnings, isoWarnings...)
errs = packer.MultiErrorAppend(errs, isoErrs...)
extension := strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0]))
if extension == "vhd" || extension == "vhdx" {
b.config.ISOConfig.TargetExtension = extension
}
}
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
@ -153,7 +162,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.Generation = 1
if b.config.CloneFromVMName == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("The clone_from_vm_name must be specified."))
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."))
}
} else {
virtualMachineExists, err := powershell.DoesVirtualMachineExist(b.config.CloneFromVMName)
if err != nil {
@ -190,8 +201,21 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
}
}
if b.config.CloneFromVMXCPath == "" {
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."))
}
} else {
if _, err := os.Stat(b.config.CloneFromVMXCPath); os.IsNotExist(err) {
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("CloneFromVMXCPath does not exist: %s", err))
}
}
}
if b.config.Generation != 2 {
if b.config.Generation != 1 || b.config.Generation != 2 {
b.config.Generation = 1
}
@ -377,6 +401,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,
CloneFromVMName: b.config.CloneFromVMName,
CloneFromSnapshotName: b.config.CloneFromSnapshotName,
CloneAllSnapshots: b.config.CloneAllSnapshots,

View File

@ -4,7 +4,9 @@ import (
"reflect"
"testing"
"github.com/mitchellh/packer/packer"
"github.com/hashicorp/packer/packer"
"io/ioutil"
"os"
)
func testConfig() map[string]interface{} {
@ -15,8 +17,8 @@ func testConfig() map[string]interface{} {
"shutdown_command": "yes",
"ssh_username": "foo",
"ram_size": 64,
"disk_size": 256,
"guest_additions_mode": "none",
"clone_from_vmxc_path": "generated",
packer.BuildNameConfigKey: "foo",
}
}
@ -33,6 +35,14 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
var b Builder
config := testConfig()
//Create vmxc folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmxc_path"] = td
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
@ -46,42 +56,18 @@ func TestBuilderPrepare_Defaults(t *testing.T) {
}
}
func TestBuilderPrepare_DiskSize(t *testing.T) {
var b Builder
config := testConfig()
delete(config, "disk_size")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad err: %s", err)
}
if b.config.DiskSize != 40*1024 {
t.Fatalf("bad size: %d", b.config.DiskSize)
}
config["disk_size"] = 256
b = Builder{}
warns, err = b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.DiskSize != 256 {
t.Fatalf("bad size: %d", b.config.DiskSize)
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testConfig()
//Create vmxc folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmxc_path"] = td
// Add a random key
config["i_should_not_be_valid"] = true
warns, err := b.Prepare(config)
@ -97,6 +83,14 @@ func TestBuilderPrepare_ISOChecksum(t *testing.T) {
var b Builder
config := testConfig()
//Create vmxc folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmxc_path"] = td
// Test bad
config["iso_checksum"] = ""
warns, err := b.Prepare(config)
@ -127,6 +121,14 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
var b Builder
config := testConfig()
//Create vmxc folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmxc_path"] = td
// Test bad
config["iso_checksum_type"] = ""
warns, err := b.Prepare(config)
@ -182,18 +184,27 @@ func TestBuilderPrepare_ISOChecksumType(t *testing.T) {
func TestBuilderPrepare_ISOUrl(t *testing.T) {
var b Builder
config := testConfig()
//Create vmxc folder
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config["clone_from_vmxc_path"] = td
delete(config, "iso_url")
delete(config, "iso_urls")
// Test both epty
// Test both empty (should be allowed, as we cloning a vm so we probably don't need an ISO file)
config["iso_url"] = ""
b = Builder{}
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should have error")
if err != nil {
t.Fatal("should not have an error")
}
// Test iso_url set

View File

@ -26,8 +26,12 @@ import (
filebuilder "github.com/hashicorp/packer/builder/file"
googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute"
hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso"
<<<<<<< HEAD
lxcbuilder "github.com/hashicorp/packer/builder/lxc"
lxdbuilder "github.com/hashicorp/packer/builder/lxd"
=======
hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx"
>>>>>>> Can specify an iso, vhd or vhdx for download. If it is a vhd or vhdx it is used as the hard drive for spinning up a new machine, importing an exported virtual machine or cloning a virtual machine.
nullbuilder "github.com/hashicorp/packer/builder/null"
oneandonebuilder "github.com/hashicorp/packer/builder/oneandone"
openstackbuilder "github.com/hashicorp/packer/builder/openstack"

View File

@ -187,28 +187,38 @@ Set-VMFloppyDiskDrive -VMName $vmName -Path $null
return err
}
func CreateVirtualMachine(vmName string, path string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint) error {
func CreateVirtualMachine(vmName string, path string, harddrivePath string, vhdRoot string, ram int64, diskSize int64, switchName string, generation uint) error {
if generation == 2 {
var script = `
param([string]$vmName, [string]$path, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation)
param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName, [int]$generation)
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation
if ($harddrivePath){
Copy-Item -Path $harddrivePath -Destination $vhdPath
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName -Generation $generation
} else {
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName -Generation $generation
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, path, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10))
err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName, strconv.FormatInt(int64(generation), 10))
return err
} else {
var script = `
param([string]$vmName, [string]$path, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName)
param([string]$vmName, [string]$path, [string]$harddrivePath, [string]$vhdRoot, [long]$memoryStartupBytes, [long]$newVHDSizeBytes, [string]$switchName)
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $vhdRoot -ChildPath $vhdx
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName
if ($harddrivePath){
Copy-Item -Path $harddrivePath -Destination $vhdPath
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -VHDPath $vhdPath -SwitchName $switchName
} else {
New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHDPath $vhdPath -NewVHDSizeBytes $newVHDSizeBytes -SwitchName $switchName
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName, path, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName)
err := ps.Run(script, vmName, path, harddrivePath, vhdRoot, strconv.FormatInt(ram, 10), strconv.FormatInt(diskSize, 10), switchName)
if err != nil {
return err
}
@ -234,58 +244,111 @@ if ((Get-Command Set-Vm).Parameters["AutomaticCheckpointsEnabled"]) {
return err
}
func DisableAutomaticCheckpoints(vmName string) error {
func ExportVmxcVirtualMachine(exportPath string, vmName string, snapshotName string, allSnapshots bool) error {
var script = `
param([string]$vmName)
if ((Get-Command Set-Vm).Parameters["AutomaticCheckpointsEnabled"]) {
Set-Vm -Name $vmName -AutomaticCheckpointsEnabled $false }
`
var ps powershell.PowerShellCmd
err := ps.Run(script, vmName)
return err
param([string]$exportPath, [string]$vmName, [string]$snapshotName, [string]$allSnapshotsString)
$WorkingPath = Join-Path $exportPath $vmName
if (Test-Path $WorkingPath) {
throw "Export path working directory: $WorkingPath already exists!"
}
func CloneVirtualMachine(cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, ram int64, switchName string) error {
$allSnapshots = [System.Boolean]::Parse($allSnapshotsString)
var script = `
param([string]$CloneFromVMName, [string]$CloneFromSnapshotName, [string]$CloneAllSnapshotsString, [string]$vmName, [string]$path, [long]$memoryStartupBytes, [string]$switchName)
$CloneAllSnapshots = [System.Boolean]::Parse($CloneAllSnapshotsString)
$ExportPath = Join-Path $path $VMName
if ($CloneFromSnapshotName) {
$snapshot = Get-VMSnapshot -VMName $CloneFromVMName -Name $CloneFromSnapshotName
Export-VMSnapshot -VMSnapshot $snapshot -Path $ExportPath -ErrorAction Stop
if ($snapshotName) {
$snapshot = Get-VMSnapshot -VMName $vmName -Name $snapshotName
Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
} else {
if (!$CloneAllSnapshots) {
if (!$allSnapshots) {
#Use last snapshot if one was not specified
$snapshot = Get-VMSnapshot -VMName $CloneFromVMName | Select -Last 1
$snapshot = Get-VMSnapshot -VMName $vmName | Select -Last 1
} else {
$snapshot = $null
}
if (!$snapshot) {
#No snapshot clone
Export-VM -Name $CloneFromVMName -Path $ExportPath -ErrorAction Stop
Export-VM -Name $vmName -Path $exportPath -ErrorAction Stop
} else {
#Snapshot clone
Export-VMSnapshot -VMSnapshot $snapshot -Path $ExportPath -ErrorAction Stop
Export-VMSnapshot -VMSnapshot $snapshot -Path $exportPath -ErrorAction Stop
}
}
$result = Get-ChildItem -Path (Join-Path $ExportPath $CloneFromVMName) | Move-Item -Destination $ExportPath -Force
$result = Remove-Item -Path (Join-Path $ExportPath $CloneFromVMName)
$result = Get-ChildItem -Path $WorkingPath | Move-Item -Destination $exportPath -Force
$result = Remove-Item -Path $WorkingPath
`
$VirtualMachinePath = Get-ChildItem -Path (Join-Path $ExportPath 'Virtual Machines') -Filter *.vmcx -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path (Join-Path $ExportPath 'Virtual Machines') -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path $ExportPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
allSnapshotsString := "False"
if allSnapshots {
allSnapshotsString = "True"
}
var ps powershell.PowerShellCmd
err := ps.Run(script, exportPath, vmName, snapshotName, allSnapshotsString)
return err
}
$compatibilityReport = Compare-VM -Path $VirtualMachinePath -VirtualMachinePath $ExportPath -SmartPagingFilePath $ExportPath -SnapshotFilePath $ExportPath -VhdDestinationPath (Join-Path -Path $ExportPath -ChildPath 'Virtual Hard Disks') -GenerateNewId -Copy:$false
func CopyVmxcVirtualMachine(exportPath string, cloneFromVmxcPath string) error {
var script = `
param([string]$exportPath, [string]$cloneFromVmxcPath)
if (!(Test-Path $cloneFromVmxcPath)){
throw "Clone from vmxc directory: $cloneFromVmxcPath does not exist!"
}
if (!(Test-Path $exportPath)){
New-Item -ItemType Directory -Force -Path $exportPath
}
$cloneFromVmxcPath = Join-Path $cloneFromVmxcPath '\*'
Copy-Item $cloneFromVmxcPath $exportPath -Recurse -Force
`
var ps powershell.PowerShellCmd
err := ps.Run(script, exportPath, cloneFromVmxcPath)
return err
}
func ImportVmxcVirtualMachine(importPath string, vmName string, harddrivePath string, ram int64, switchName string) error {
var script = `
param([string]$importPath, [string]$vmName, [string]$harddrivePath, [long]$memoryStartupBytes, [string]$switchName)
$VirtualHarddisksPath = Join-Path -Path $importPath -ChildPath 'Virtual Hard Disks'
if (!(Test-Path $VirtualHarddisksPath)) {
New-Item -ItemType Directory -Force -Path $VirtualHarddisksPath
}
$vhdPath = ""
if ($harddrivePath){
$vhdx = $vmName + '.vhdx'
$vhdPath = Join-Path -Path $VirtualHarddisksPath -ChildPath $vhdx
}
$VirtualMachinesPath = Join-Path $importPath 'Virtual Machines'
if (!(Test-Path $VirtualMachinesPath)) {
New-Item -ItemType Directory -Force -Path $VirtualMachinesPath
}
$VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.vmcx -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path $VirtualMachinesPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
}
if (!$VirtualMachinePath){
$VirtualMachinePath = Get-ChildItem -Path $importPath -Filter *.xml -Recurse -ErrorAction SilentlyContinue | select -First 1 | %{$_.FullName}
}
$compatibilityReport = Compare-VM -Path $VirtualMachinePath -VirtualMachinePath $importPath -SmartPagingFilePath $importPath -SnapshotFilePath $importPath -VhdDestinationPath $VirtualHarddisksPath -GenerateNewId -Copy:$false
if ($vhdPath){
Copy-Item -Path $harddrivePath -Destination $vhdPath
$existingFirstHarddrive = $compatibilityReport.VM.HardDrives | Select -First 1
if ($existingFirstHarddrive) {
$existingFirstHarddrive | Set-VMHardDiskDrive -Path $vhdPath
} else {
Add-VMHardDiskDrive -VM $compatibilityReport.VM -Path $vhdPath
}
}
Set-VMMemory -VM $compatibilityReport.VM -StartupBytes $memoryStartupBytes
$networkAdaptor = $compatibilityReport.VM.NetworkAdapters | Select -First 1
Disconnect-VMNetworkAdapter -VMNetworkAdapter $networkAdaptor
@ -295,22 +358,35 @@ $vm = Import-VM -CompatibilityReport $compatibilityReport
if ($vm) {
$result = Rename-VM -VM $vm -NewName $VMName
}
`
CloneAllSnapshotsString := "False"
if cloneAllSnapshots {
CloneAllSnapshotsString = "True"
}
`
var ps powershell.PowerShellCmd
err := ps.Run(script, cloneFromVmName, cloneFromSnapshotName, CloneAllSnapshotsString, vmName, path, strconv.FormatInt(ram, 10), switchName)
err := ps.Run(script, importPath, vmName, harddrivePath, strconv.FormatInt(ram, 10), switchName)
return err
}
func CloneVirtualMachine(cloneFromVmxcPath string, cloneFromVmName string, cloneFromSnapshotName string, cloneAllSnapshots bool, vmName string, path string, harddrivePath string, ram int64, switchName string) error {
if cloneFromVmName != "" {
err := ExportVmxcVirtualMachine(path, cloneFromVmName, cloneFromSnapshotName, cloneAllSnapshots)
if err != nil {
return err
}
}
if cloneFromVmxcPath != "" {
err := CopyVmxcVirtualMachine(path, cloneFromVmxcPath)
if err != nil {
return err
}
}
err := ImportVmxcVirtualMachine(path, vmName, harddrivePath, ram, switchName)
if err != nil {
return err
}
return DeleteAllDvdDrives(vmName)
}
func GetVirtualMachineGeneration(vmName string) (uint, error) {

View File

@ -53,21 +53,22 @@ can be configured for this builder.
### Required:
- `iso_checksum` (string) - The checksum for the OS ISO file. Because ISO
files are so large, this is required and Packer will verify it prior
to booting a virtual machine with the ISO attached. The type of the
checksum is specified with `iso_checksum_type`, documented below.
- `iso_checksum` (string) - The checksum for the OS ISO file or virtual
harddrive file. Because these files are so large, this is required and
Packer will verify it prior to booting a virtual machine with the ISO or
virtual harddrive attached. The type of the checksum is specified with
`iso_checksum_type`, documented below.
- `iso_checksum_type` (string) - The type of the checksum specified in
`iso_checksum`. Valid values are "none", "md5", "sha1", "sha256", or
"sha512" currently. While "none" will skip checksumming, this is not
recommended since ISO files are generally large and corruption does happen
from time to time.
recommended since ISO files and virtual harddrive files are generally large
and corruption does happen from time to time.
- `iso_url` (string) - A URL to the ISO containing the installation image.
This URL can be either an HTTP URL or a file URL (or path to a file).
If this is an HTTP URL, Packer will download iso and cache it between
runs.
- `iso_url` (string) - A URL to the ISO containing the installation image or
virtual harddrive vhd or vhdx file to clone. This URL can be either an HTTP
URL or a file URL (or path to a file). If this is an HTTP URL, Packer will
download the file and cache it between runs.
### Optional:

View File

@ -9,12 +9,14 @@ page_title: "Hyper-V Builder (from an vmcx)"
Type: `hyperv-vmcx`
The Hyper-V Packer builder is able to clone [Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx)
virtual machines and export them.
The Hyper-V Packer builder is able to use exported virtual machines or clone existing
[Hyper-V](https://www.microsoft.com/en-us/server-cloud/solutions/virtualization.aspx)
virtual machines.
The builder clones an existing virtual machine boots it, and provisioning software within
the OS, then shutting it down. The result of the Hyper-V builder is a directory
containing all the files necessary to run the virtual machine portably.
The builder imports a virtual machine or clones an existing virtual machine boots it,
and provisioning software within the OS, then shutting 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
@ -22,6 +24,18 @@ 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:
Import from folder:
```javascript
{
"type": "hyperv-vmcx",
"clone_from_vmxc_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"
}
```
Clone from existing virtual machine:
```javascript
{
"type": "hyperv-vmcx",
@ -46,7 +60,11 @@ In addition to the options listed here, a
[communicator](/docs/templates/communicator.html)
can be configured for this builder.
### Required:
### Required for virtual machine import:
- `clone_from_vmxc_path` (string) - The path to the exported
virtual machine folder.
### Required for virtual machine clone:
- `clone_from_vm_name` (string) - The name of the vm to clone from.
Ideally the machine to clone from should be shutdown.