From b041c1955ce0116b08e18f5398d72c43d95ad3f4 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sun, 2 Oct 2016 16:10:16 +0200 Subject: [PATCH 01/15] Add support for vmcx files, new binary format for Hyper-V configurations. --- .../hyperv/scripts/import_vm_vmcx.ps1 | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 new file mode 100644 index 000000000..33c961e33 --- /dev/null +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -0,0 +1,167 @@ +Param( + [Parameter(Mandatory=$true)] + [string]$vm_config, + [Parameter(Mandatory=$true)] + [string]$image_path, + + [string]$switchname=$null, + [string]$memory=$null, + [string]$maxmemory=$null, + [string]$cpus=$null, + [string]$vmname=$null, + [string]$auto_start_action=$null, + [string]$auto_stop_action=$null +) + +# Include the following modules +$Dir = Split-Path $script:MyInvocation.MyCommand.Path +. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) + +# load the config from the vmcx and make a copy for editing, use TMP path so we are sure there is no vhd at the destination +$vmConfig = (Compare-VM -Copy -Path $vm_config -GenerateNewID -VhdDestinationPath $env:Temp) + +$generation = $vmConfig.VM.Generation + +if (!$vmname) { + # Get the name of the vm + $vm_name = $vmconfig.VM.VMName +}else { + $vm_name = $vmname +} + +if (!$cpus) { + # Get the processorcount of the VM + $processors = (Get-VMProcessor -VM $vmConfig.VM).Count + +}else { + $processors = $cpus +} + +function GetUniqueName($name) { + Get-VM | ForEach-Object -Process { + if ($name -eq $_.Name) { + $name = $name + "_1" + } + } + return $name +} + +do { + $name = $vm_name + $vm_name = GetUniqueName $name +} while ($vm_name -ne $name) + +if (!$memory) { + $configMemory = Get-VMMemory -VM $vmConfig.VM + $dynamicmemory = $configMemory.DynamicMemoryEnabled + + # Memory values need to be in bytes + $MemoryMaximumBytes = ($configMemory.Maximum) + $MemoryStartupBytes = ($configMemory.Startup) + $MemoryMinimumBytes = ($configMemory.Minimum) +} +else { + if (!$maxmemory){ + $dynamicmemory = $False + $MemoryMaximumBytes = ($memory -as [int]) * 1MB + $MemoryStartupBytes = ($memory -as [int]) * 1MB + $MemoryMinimumBytes = ($memory -as [int]) * 1MB + } + else { + $dynamicmemory = $True + $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB + $MemoryStartupBytes = ($memory -as [int]) * 1MB + $MemoryMinimumBytes = ($memory -as [int]) * 1MB + } +} + + +if (!$switchname) { + $switchname = (Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName +} + +$vm_params = @{ + Name = $vm_name + NoVHD = $True + MemoryStartupBytes = $MemoryStartupBytes + SwitchName = $switchname + ErrorAction = "Stop" +} + +# Generation parameter was added in ps v4 +if((get-command New-VM).Parameters.Keys.Contains("generation")) { + $vm_params.Generation = $generation +} + +# Create the VM using the values in the hash map +$vm = New-VM @vm_params + +$notes = $vmConfig.VM.Notes + +# Set-VM parameters to configure new VM with old values + +$more_vm_params = @{ + ProcessorCount = $processors + MemoryStartupBytes = $MemoryStartupBytes +} + +If ($dynamicmemory) { + $more_vm_params.Add("DynamicMemory",$True) + $more_vm_params.Add("MemoryMinimumBytes",$MemoryMinimumBytes) + $more_vm_params.Add("MemoryMaximumBytes", $MemoryMaximumBytes) +} else { + $more_vm_params.Add("StaticMemory",$True) +} + +if ($notes) { + $more_vm_params.Add("Notes",$notes) +} + +if ($auto_start_action) { + $more_vm_params.Add("AutomaticStartAction",$auto_start_action) +} + +if ($auto_stop_action) { + $more_vm_params.Add("AutomaticStopAction",$auto_stop_action) +} + +# Set the values on the VM +$vm | Set-VM @more_vm_params -Passthru + +# Only set EFI secure boot for Gen 2 machines, not gen 1 +if ($generation -ne 1) { + Set-VMFirmware -VM $vm -EnableSecureBoot (Get-VMFirmware -VM $vmConfig.VM).SecureBoot +} + +# Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device +$controllers = Get-VMScsiController -VM $vmConfig.VM +if($generation -eq 1){ + $controllers = @($controllers) + @(Get-VMIdeController -VM $vmConfig.VM) +} + +foreach($controller in $controllers){ + foreach($drive in $controller.Drives){ + $addDriveParam = @{ + ControllerNumber = $drive.ControllerNumber + Path = $image_path + } + if($drive.PoolName){ + $addDriveParam.Add("ResourcePoolname",$drive.PoolName) + } + + # If the drive path is set, it is a harddisk, only support single harddisk + if ($drive.Path) { + $addDriveParam.add("ControllerType", $ControllerType) + $vm | Add-VMHardDiskDrive @AddDriveparam + } + } +} + +$vm_id = (Get-VM $vm_name).id.guid +$resultHash = @{ + name = $vm_name + id = $vm_id +} + +$result = ConvertTo-Json $resultHash +Write-Output-Message $result From 1ec1cf8177c8de954711811e085001363323f084 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sun, 2 Oct 2016 16:10:58 +0200 Subject: [PATCH 02/15] Update Hyper-V import command and the driver to check for configuration type and call the correct import script. --- plugins/providers/hyperv/action/import.rb | 13 +++++++++++-- plugins/providers/hyperv/driver.rb | 7 ++++++- .../scripts/{import_vm.ps1 => import_vm_xml.ps1} | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) rename plugins/providers/hyperv/scripts/{import_vm.ps1 => import_vm_xml.ps1} (98%) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index 18525543e..97351e12a 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -35,9 +35,17 @@ module VagrantPlugins end config_path = nil + config_type = nil vm_dir.each_child do |f| - if f.extname.downcase == ".xml" + + if f.extname.downcase == '.xml' config_path = f + config_type = 'xml' + break + end + if f.extname.downcase == '.vmcx' + config_path = f + config_type = 'vmcx' break end end @@ -111,7 +119,8 @@ module VagrantPlugins # We have to normalize the paths to be Windows paths since # we're executing PowerShell. options = { - vm_xml_config: config_path.to_s.gsub("/", "\\"), + vm_config: config_path.to_s.gsub("/", "\\"), + vm_config_type: config_type, image_path: image_path.to_s.gsub("/", "\\") } options[:switchname] = switch if switch diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb index 604c46bbd..73fb2eb74 100644 --- a/plugins/providers/hyperv/driver.rb +++ b/plugins/providers/hyperv/driver.rb @@ -74,7 +74,12 @@ module VagrantPlugins end def import(options) - execute('import_vm.ps1', options) + config_type = options.delete(:vm_config_type) + if config_type === "vmcx" + execute('import_vm_vmcx.ps1', options) + else + execute('import_vm_xml.ps1', options) + end end def net_set_vlan(vlan_id) diff --git a/plugins/providers/hyperv/scripts/import_vm.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 similarity index 98% rename from plugins/providers/hyperv/scripts/import_vm.ps1 rename to plugins/providers/hyperv/scripts/import_vm_xml.ps1 index 659eb50aa..18d99d203 100644 --- a/plugins/providers/hyperv/scripts/import_vm.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 @@ -1,6 +1,6 @@ Param( [Parameter(Mandatory=$true)] - [string]$vm_xml_config, + [string]$vm_config, [Parameter(Mandatory=$true)] [string]$image_path, @@ -17,7 +17,7 @@ Param( $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) -[xml]$vmconfig = Get-Content -Path $vm_xml_config +[xml]$vmconfig = Get-Content -Path $vm_config $generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1 From 2fc93277fd5a22257cbe70032b2d06a14ccdd49f Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sun, 2 Oct 2016 18:45:51 +0200 Subject: [PATCH 03/15] change argument to vm_config_file so it isn't the same as variable in powershell script. --- plugins/providers/hyperv/action/import.rb | 2 +- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 2 +- plugins/providers/hyperv/scripts/import_vm_xml.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index 97351e12a..abc6d02c5 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -119,7 +119,7 @@ module VagrantPlugins # We have to normalize the paths to be Windows paths since # we're executing PowerShell. options = { - vm_config: config_path.to_s.gsub("/", "\\"), + vm_config_file: config_path.to_s.gsub("/", "\\"), vm_config_type: config_type, image_path: image_path.to_s.gsub("/", "\\") } diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index 33c961e33..36f7eae69 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -1,6 +1,6 @@ Param( [Parameter(Mandatory=$true)] - [string]$vm_config, + [string]$vm_config_file, [Parameter(Mandatory=$true)] [string]$image_path, diff --git a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 index 18d99d203..89383c254 100644 --- a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 @@ -1,6 +1,6 @@ Param( [Parameter(Mandatory=$true)] - [string]$vm_config, + [string]$vm_config_file, [Parameter(Mandatory=$true)] [string]$image_path, From 133e2a7b0fd827b1dca683d81ce0b60e662fc639 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Tue, 4 Oct 2016 20:06:10 +0200 Subject: [PATCH 04/15] Broke Path argument in mini refactor. --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 2 +- plugins/providers/hyperv/scripts/import_vm_xml.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index 36f7eae69..21e3132de 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -18,7 +18,7 @@ $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) # load the config from the vmcx and make a copy for editing, use TMP path so we are sure there is no vhd at the destination -$vmConfig = (Compare-VM -Copy -Path $vm_config -GenerateNewID -VhdDestinationPath $env:Temp) +$vmConfig = (Compare-VM -Copy -Path $vm_config_file -GenerateNewID -VhdDestinationPath $env:Temp) $generation = $vmConfig.VM.Generation diff --git a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 index 89383c254..cccb0a75c 100644 --- a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 @@ -17,7 +17,7 @@ Param( $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) -[xml]$vmconfig = Get-Content -Path $vm_config +[xml]$vmconfig = Get-Content -Path $vm_config_file $generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1 From a77da314e5fe7054a06a4bb4bf3baf4dd850b609 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Fri, 7 Oct 2016 20:38:05 +0200 Subject: [PATCH 05/15] Only check for .vmcx if there is no XML found to not risk breaking older vagrant boxes that added an XML file manually --- plugins/providers/hyperv/action/import.rb | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index abc6d02c5..e31949475 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -37,19 +37,28 @@ module VagrantPlugins config_path = nil config_type = nil vm_dir.each_child do |f| - if f.extname.downcase == '.xml' config_path = f config_type = 'xml' break end - if f.extname.downcase == '.vmcx' - config_path = f - config_type = 'vmcx' - break + end + + + # Only check for .vmcx if there is no XML found to not + # risk breaking older vagrant boxes that added an XML + # file manually + if config_type == nil + vm_dir.each_child do |f| + if f.extname.downcase == '.vmcx' + config_path = f + config_type = 'vmcx' + break + end end end + image_path = nil image_ext = nil image_filename = nil From debe50957b4991cb5d81347a970a3df0d6e43fb6 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sat, 8 Oct 2016 16:38:42 +0200 Subject: [PATCH 06/15] Refactor the import script to fully use Compare-VM for creating the new VM. Implemented the differencing disk for vmcx. This means the disk is now copied by Hyper-V (Powershell) instead of Ruby for new machines. This does mean EFI Firmware now does work for machines since it is quite a feep copy. Compare-VM will report incompatibilities should they be found. --- plugins/providers/hyperv/action/import.rb | 23 +++-- plugins/providers/hyperv/driver.rb | 3 + .../hyperv/scripts/import_vm_vmcx.ps1 | 99 +++++++++---------- .../hyperv/scripts/import_vm_xml.ps1 | 4 +- 4 files changed, 63 insertions(+), 66 deletions(-) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index e31949475..7be7265e0 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -44,7 +44,6 @@ module VagrantPlugins end end - # Only check for .vmcx if there is no XML found to not # risk breaking older vagrant boxes that added an XML # file manually @@ -58,7 +57,6 @@ module VagrantPlugins end end - image_path = nil image_ext = nil image_filename = nil @@ -116,12 +114,16 @@ module VagrantPlugins end env[:ui].detail("Cloning virtual hard drive...") - source_path = image_path.to_s - dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s - if differencing_disk - env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path}) - else - FileUtils.cp(source_path, dest_path) + source_path = image_path.to_s + dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s + + # Still hard copy the disk of old XML configurations + if config_type == 'xml' + if differencing_disk + env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path}) + else + FileUtils.cp(source_path, dest_path) + end end image_path = dest_path @@ -130,7 +132,9 @@ module VagrantPlugins options = { vm_config_file: config_path.to_s.gsub("/", "\\"), vm_config_type: config_type, - image_path: image_path.to_s.gsub("/", "\\") + source_path: source_path.to_s, + dest_path: env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s, + data_path: env[:machine].data_dir.to_s.gsub("/", "\\") } options[:switchname] = switch if switch options[:memory] = memory if memory @@ -139,6 +143,7 @@ module VagrantPlugins options[:vmname] = vmname if vmname options[:auto_start_action] = auto_start_action if auto_start_action options[:auto_stop_action] = auto_stop_action if auto_stop_action + options[:differencing_disk] = differencing_disk if differencing_disk env[:ui].detail("Creating and registering the VM...") server = env[:machine].provider.driver.import(options) diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb index 73fb2eb74..d348cb79e 100644 --- a/plugins/providers/hyperv/driver.rb +++ b/plugins/providers/hyperv/driver.rb @@ -78,6 +78,9 @@ module VagrantPlugins if config_type === "vmcx" execute('import_vm_vmcx.ps1', options) else + option.delete(:data_path) + option.delete(:source_path) + option.delete(:differencing_disk) execute('import_vm_xml.ps1', options) end end diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index 21e3132de..b31c1528d 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -2,7 +2,11 @@ [Parameter(Mandatory=$true)] [string]$vm_config_file, [Parameter(Mandatory=$true)] - [string]$image_path, + [string]$source_path, + [Parameter(Mandatory=$true)] + [string]$dest_path, + [Parameter(Mandatory=$true)] + [string]$data_path, [string]$switchname=$null, [string]$memory=$null, @@ -10,15 +14,19 @@ [string]$cpus=$null, [string]$vmname=$null, [string]$auto_start_action=$null, - [string]$auto_stop_action=$null + [string]$auto_stop_action=$null, + [string]$differencing_disk=$null ) +"$($data_path)/Snapshots" + # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) # load the config from the vmcx and make a copy for editing, use TMP path so we are sure there is no vhd at the destination -$vmConfig = (Compare-VM -Copy -Path $vm_config_file -GenerateNewID -VhdDestinationPath $env:Temp) +$vmConfig = (Compare-VM -Copy -Path $vm_config_file -GenerateNewID -SnapshotFilePath "$($data_path)Snapshots" -VhdDestinationPath "$($data_path)Virtual Hard Disks" -VirtualMachinePath "$($data_path)Virtual Machines") + $generation = $vmConfig.VM.Generation @@ -32,7 +40,6 @@ if (!$vmname) { if (!$cpus) { # Get the processorcount of the VM $processors = (Get-VMProcessor -VM $vmConfig.VM).Count - }else { $processors = $cpus } @@ -80,82 +87,64 @@ if (!$switchname) { $switchname = (Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName } -$vm_params = @{ - Name = $vm_name - NoVHD = $True - MemoryStartupBytes = $MemoryStartupBytes - SwitchName = $switchname - ErrorAction = "Stop" -} -# Generation parameter was added in ps v4 -if((get-command New-VM).Parameters.Keys.Contains("generation")) { - $vm_params.Generation = $generation -} - -# Create the VM using the values in the hash map -$vm = New-VM @vm_params - -$notes = $vmConfig.VM.Notes - -# Set-VM parameters to configure new VM with old values - -$more_vm_params = @{ - ProcessorCount = $processors - MemoryStartupBytes = $MemoryStartupBytes -} +Connect-VMNetworkAdapter -VMNetworkAdapter (Get-VMNetworkAdapter -VM $vmConfig.VM) -SwitchName $switchname +Set-VM -VM $vmConfig.VM -NewVMName $vm_name -MemoryStartupBytes $MemoryStartupBytes +Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes +Set-VM -VM $vmConfig.VM -ErrorAction "Stop" -ProcessorCount $processors If ($dynamicmemory) { - $more_vm_params.Add("DynamicMemory",$True) - $more_vm_params.Add("MemoryMinimumBytes",$MemoryMinimumBytes) - $more_vm_params.Add("MemoryMaximumBytes", $MemoryMaximumBytes) + Set-VM -VM $vmConfig.VM -DynamicMemory } else { - $more_vm_params.Add("StaticMemory",$True) + Set-VM -VM $vmConfig.VM -StaticMemory } if ($notes) { - $more_vm_params.Add("Notes",$notes) + Set-VM -VM $vmConfig.VM -Notes $notes } if ($auto_start_action) { - $more_vm_params.Add("AutomaticStartAction",$auto_start_action) + Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_start_action } if ($auto_stop_action) { - $more_vm_params.Add("AutomaticStopAction",$auto_stop_action) + Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_stop_action } -# Set the values on the VM -$vm | Set-VM @more_vm_params -Passthru - # Only set EFI secure boot for Gen 2 machines, not gen 1 if ($generation -ne 1) { - Set-VMFirmware -VM $vm -EnableSecureBoot (Get-VMFirmware -VM $vmConfig.VM).SecureBoot + Set-VMFirmware -VM $vmConfig.VM -EnableSecureBoot (Get-VMFirmware -VM $vmConfig.VM).SecureBoot } -# Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device -$controllers = Get-VMScsiController -VM $vmConfig.VM -if($generation -eq 1){ - $controllers = @($controllers) + @(Get-VMIdeController -VM $vmConfig.VM) +$report = Compare-VM -CompatibilityReport $vmConfig +if($report.Incompatibilities.Length -gt 0){ +$report.Incompatibilities + Write-Error-Message ConvertTo-Json $report.Incompatibilities } -foreach($controller in $controllers){ - foreach($drive in $controller.Drives){ - $addDriveParam = @{ - ControllerNumber = $drive.ControllerNumber - Path = $image_path - } - if($drive.PoolName){ - $addDriveParam.Add("ResourcePoolname",$drive.PoolName) - } +# Differencing disk +if($differencing_disk){ + # Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device + $controllers = Get-VMScsiController -VM $vmConfig.VM + if($generation -eq 1){ + $controllers = @($controllers) + @(Get-VMIdeController -VM $vmConfig.VM) + } - # If the drive path is set, it is a harddisk, only support single harddisk - if ($drive.Path) { - $addDriveParam.add("ControllerType", $ControllerType) - $vm | Add-VMHardDiskDrive @AddDriveparam + foreach($controller in $controllers){ + foreach($drive in $controller.Drives){ + if([System.IO.Path]::GetFileName($drive.Path) -eq [System.IO.Path]::GetFileName($source_path)){ + # Remove the old disk and replace it with a differencing version + $path = $drive.Path + Remove-VMHardDiskDrive $drive + New-VHD -Path $dest_path -ParentPath $source_path -ErrorAction Stop + Add-VMHardDiskDrive -VM $vmConfig.VM -Path $dest_path + } } } + } + +Import-VM -CompatibilityReport $vmConfig $vm_id = (Get-VM $vm_name).id.guid $resultHash = @{ diff --git a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 index cccb0a75c..16055e135 100644 --- a/plugins/providers/hyperv/scripts/import_vm_xml.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 @@ -2,7 +2,7 @@ Param( [Parameter(Mandatory=$true)] [string]$vm_config_file, [Parameter(Mandatory=$true)] - [string]$image_path, + [string]$dest_path, [string]$switchname=$null, [string]$memory=$null, @@ -190,7 +190,7 @@ foreach ($controller in $controllers) { $addDriveParam = @{ ControllerNumber = $rx.Match($controller.node.name).value - Path = $image_path + Path = $dest_path } if ($drive.pool_id."#text") { From 7b03bf335baa6f50bb48d46e092480ee63d3add0 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sat, 8 Oct 2016 16:44:24 +0200 Subject: [PATCH 07/15] Whitespace --- plugins/providers/hyperv/action/import.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index 7be7265e0..f2e2bfade 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -7,7 +7,7 @@ module VagrantPlugins module Action class Import def initialize(app, env) - @app = app + @app = app @logger = Log4r::Logger.new("vagrant::hyperv::import") end @@ -64,7 +64,7 @@ module VagrantPlugins if %w{.vhd .vhdx}.include?(f.extname.downcase) image_path = f image_ext = f.extname.downcase - image_filename = File.basename(f,image_ext) + image_filename = File.basename(f, image_ext) break end end @@ -114,8 +114,8 @@ module VagrantPlugins end env[:ui].detail("Cloning virtual hard drive...") - source_path = image_path.to_s - dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s + source_path = image_path.to_s + dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s # Still hard copy the disk of old XML configurations if config_type == 'xml' @@ -130,11 +130,11 @@ module VagrantPlugins # We have to normalize the paths to be Windows paths since # we're executing PowerShell. options = { - vm_config_file: config_path.to_s.gsub("/", "\\"), - vm_config_type: config_type, - source_path: source_path.to_s, - dest_path: env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s, - data_path: env[:machine].data_dir.to_s.gsub("/", "\\") + vm_config_file: config_path.to_s.gsub("/", "\\"), + vm_config_type: config_type, + source_path: source_path.to_s, + dest_path: env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s, + data_path: env[:machine].data_dir.to_s.gsub("/", "\\") } options[:switchname] = switch if switch options[:memory] = memory if memory From 8744caebcf50095e808233fd7282849115eddacb Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sun, 9 Oct 2016 20:39:17 +0200 Subject: [PATCH 08/15] Small typo in options --- plugins/providers/hyperv/driver.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb index d348cb79e..1a2f1dd39 100644 --- a/plugins/providers/hyperv/driver.rb +++ b/plugins/providers/hyperv/driver.rb @@ -78,9 +78,9 @@ module VagrantPlugins if config_type === "vmcx" execute('import_vm_vmcx.ps1', options) else - option.delete(:data_path) - option.delete(:source_path) - option.delete(:differencing_disk) + options.delete(:data_path) + options.delete(:source_path) + options.delete(:differencing_disk) execute('import_vm_xml.ps1', options) end end From 42efd5c198eda098a7e489db8ac5a1c7289f6bb8 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Sun, 9 Oct 2016 21:10:56 +0200 Subject: [PATCH 09/15] Make the Virtual Hard Disks directory if importing from XML to keep it consistant across config types --- plugins/providers/hyperv/action/import.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index f2e2bfade..548dbdc39 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -115,13 +115,14 @@ module VagrantPlugins env[:ui].detail("Cloning virtual hard drive...") source_path = image_path.to_s - dest_path = env[:machine].data_dir.join("#{image_filename}#{image_ext}").to_s + dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s # Still hard copy the disk of old XML configurations if config_type == 'xml' if differencing_disk env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path}) else + FileUtils.mkdir_p(env[:machine].data_dir.join("Virtual Hard Disks")) FileUtils.cp(source_path, dest_path) end end @@ -133,7 +134,7 @@ module VagrantPlugins vm_config_file: config_path.to_s.gsub("/", "\\"), vm_config_type: config_type, source_path: source_path.to_s, - dest_path: env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s, + dest_path: dest_path, data_path: env[:machine].data_dir.to_s.gsub("/", "\\") } options[:switchname] = switch if switch From 803fff7f0381a1092ab0f20cdd14afb0f424438d Mon Sep 17 00:00:00 2001 From: Jake Ballard Date: Tue, 11 Oct 2016 15:31:28 -0500 Subject: [PATCH 10/15] Fix issue with missing slash in paths It was failing to create files below the machines\default\hyperv folder, rather it was creating folders named 'hypervSnapshots', 'hypervVirtual Hard Disks', and 'hypervVirtual Machines'. This was causing the files to not be removed when destroying the vm, and an error when subsequently bringing it up again. --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index b31c1528d..b87077bdc 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -25,7 +25,14 @@ $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) # load the config from the vmcx and make a copy for editing, use TMP path so we are sure there is no vhd at the destination -$vmConfig = (Compare-VM -Copy -Path $vm_config_file -GenerateNewID -SnapshotFilePath "$($data_path)Snapshots" -VhdDestinationPath "$($data_path)Virtual Hard Disks" -VirtualMachinePath "$($data_path)Virtual Machines") +$VmProperties = @{ + Path = $vm_config_file + SnapshotFilePath = Join-Path $data_path 'Snapshots' + VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks' + VirtualMachinePath = Join-Path $data_path 'Virtual Machines' +} + +$vmConfig = (Compare-VM -Copy -GenerateNewID @VmProperties) $generation = $vmConfig.VM.Generation From 418064519df5ab119f15cf031d1b5e33b1285078 Mon Sep 17 00:00:00 2001 From: Brian Retford Date: Fri, 14 Oct 2016 18:53:44 -0700 Subject: [PATCH 11/15] Move dynamic memory settings into conditional block --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index b87077bdc..f66c181f6 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -97,11 +97,11 @@ if (!$switchname) { Connect-VMNetworkAdapter -VMNetworkAdapter (Get-VMNetworkAdapter -VM $vmConfig.VM) -SwitchName $switchname Set-VM -VM $vmConfig.VM -NewVMName $vm_name -MemoryStartupBytes $MemoryStartupBytes -Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes Set-VM -VM $vmConfig.VM -ErrorAction "Stop" -ProcessorCount $processors If ($dynamicmemory) { Set-VM -VM $vmConfig.VM -DynamicMemory + Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes } else { Set-VM -VM $vmConfig.VM -StaticMemory } From 055bed59184700fc27479c11d02c3e7bcd7aa582 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Wed, 19 Oct 2016 20:55:56 +0200 Subject: [PATCH 12/15] removed an extra echo and added proper error message when there is incompatibilities. Appearantly importing into a compressed folder wont work and will result in an error. --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index f66c181f6..9855763ec 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -18,8 +18,6 @@ [string]$differencing_disk=$null ) -"$($data_path)/Snapshots" - # Include the following modules $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) @@ -124,9 +122,11 @@ if ($generation -ne 1) { } $report = Compare-VM -CompatibilityReport $vmConfig + +£ Stop if there is incomatibilities which would fail anyhow. if($report.Incompatibilities.Length -gt 0){ -$report.Incompatibilities - Write-Error-Message ConvertTo-Json $report.Incompatibilities + Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message)) + exit 0 } # Differencing disk @@ -150,7 +150,7 @@ if($differencing_disk){ } } - + Import-VM -CompatibilityReport $vmConfig $vm_id = (Get-VM $vm_name).id.guid From c075c026be75bd78b607700ce9368df51927d752 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Wed, 19 Oct 2016 20:56:52 +0200 Subject: [PATCH 13/15] Keyboard language setting made my comment bad --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index 9855763ec..b499285c8 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -123,7 +123,7 @@ if ($generation -ne 1) { $report = Compare-VM -CompatibilityReport $vmConfig -£ Stop if there is incomatibilities which would fail anyhow. +# Stop if there is incompatibilities which would fail anyhow. if($report.Incompatibilities.Length -gt 0){ Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message)) exit 0 From e6093dd709e303cab081c99b84cf4dd5499984b9 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Wed, 19 Oct 2016 21:03:48 +0200 Subject: [PATCH 14/15] The virtual machine doesnt need 'Virtual Machines' in its path, this is created when importing. --- plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index b499285c8..55cf9db8e 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -27,7 +27,7 @@ $VmProperties = @{ Path = $vm_config_file SnapshotFilePath = Join-Path $data_path 'Snapshots' VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks' - VirtualMachinePath = Join-Path $data_path 'Virtual Machines' + VirtualMachinePath = $data_path } $vmConfig = (Compare-VM -Copy -GenerateNewID @VmProperties) From 37a525fbbcf111cb826c63b1a6cdac2253c5c229 Mon Sep 17 00:00:00 2001 From: Bjorn Brala Date: Thu, 20 Oct 2016 22:43:00 +0200 Subject: [PATCH 15/15] Little bit of cleanup. --- .../providers/hyperv/scripts/import_vm_vmcx.ps1 | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 index 55cf9db8e..9554fb91e 100644 --- a/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -22,7 +22,6 @@ $Dir = Split-Path $script:MyInvocation.MyCommand.Path . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) -# load the config from the vmcx and make a copy for editing, use TMP path so we are sure there is no vhd at the destination $VmProperties = @{ Path = $vm_config_file SnapshotFilePath = Join-Path $data_path 'Snapshots' @@ -32,7 +31,6 @@ $VmProperties = @{ $vmConfig = (Compare-VM -Copy -GenerateNewID @VmProperties) - $generation = $vmConfig.VM.Generation if (!$vmname) { @@ -67,19 +65,16 @@ if (!$memory) { $configMemory = Get-VMMemory -VM $vmConfig.VM $dynamicmemory = $configMemory.DynamicMemoryEnabled - # Memory values need to be in bytes $MemoryMaximumBytes = ($configMemory.Maximum) $MemoryStartupBytes = ($configMemory.Startup) $MemoryMinimumBytes = ($configMemory.Minimum) -} -else { +} else { if (!$maxmemory){ $dynamicmemory = $False $MemoryMaximumBytes = ($memory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB $MemoryMinimumBytes = ($memory -as [int]) * 1MB - } - else { + } else { $dynamicmemory = $True $MemoryMaximumBytes = ($maxmemory -as [int]) * 1MB $MemoryStartupBytes = ($memory -as [int]) * 1MB @@ -87,12 +82,10 @@ else { } } - if (!$switchname) { $switchname = (Get-VMNetworkAdapter -VM $vmConfig.VM).SwitchName } - Connect-VMNetworkAdapter -VMNetworkAdapter (Get-VMNetworkAdapter -VM $vmConfig.VM) -SwitchName $switchname Set-VM -VM $vmConfig.VM -NewVMName $vm_name -MemoryStartupBytes $MemoryStartupBytes Set-VM -VM $vmConfig.VM -ErrorAction "Stop" -ProcessorCount $processors @@ -123,13 +116,12 @@ if ($generation -ne 1) { $report = Compare-VM -CompatibilityReport $vmConfig -# Stop if there is incompatibilities which would fail anyhow. +# Stop if there are incompatibilities if($report.Incompatibilities.Length -gt 0){ Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message)) exit 0 } -# Differencing disk if($differencing_disk){ # Get all controller on the VM, first scsi, then IDE if it is a Gen 1 device $controllers = Get-VMScsiController -VM $vmConfig.VM @@ -148,7 +140,6 @@ if($differencing_disk){ } } } - } Import-VM -CompatibilityReport $vmConfig