diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index 18525543e..548dbdc39 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 @@ -35,13 +35,28 @@ 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 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 @@ -49,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 @@ -100,19 +115,27 @@ 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 - if differencing_disk - env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path}) - else - FileUtils.cp(source_path, dest_path) + 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 image_path = dest_path # We have to normalize the paths to be Windows paths since # we're executing PowerShell. options = { - vm_xml_config: config_path.to_s.gsub("/", "\\"), - image_path: image_path.to_s.gsub("/", "\\") + vm_config_file: config_path.to_s.gsub("/", "\\"), + vm_config_type: config_type, + source_path: source_path.to_s, + dest_path: dest_path, + data_path: env[:machine].data_dir.to_s.gsub("/", "\\") } options[:switchname] = switch if switch options[:memory] = memory if memory @@ -121,6 +144,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 604c46bbd..1a2f1dd39 100644 --- a/plugins/providers/hyperv/driver.rb +++ b/plugins/providers/hyperv/driver.rb @@ -74,7 +74,15 @@ 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 + options.delete(:data_path) + options.delete(:source_path) + options.delete(:differencing_disk) + execute('import_vm_xml.ps1', options) + end end def net_set_vlan(vlan_id) 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..9554fb91e --- /dev/null +++ b/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 @@ -0,0 +1,154 @@ +Param( + [Parameter(Mandatory=$true)] + [string]$vm_config_file, + [Parameter(Mandatory=$true)] + [string]$source_path, + [Parameter(Mandatory=$true)] + [string]$dest_path, + [Parameter(Mandatory=$true)] + [string]$data_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, + [string]$differencing_disk=$null +) + +# Include the following modules +$Dir = Split-Path $script:MyInvocation.MyCommand.Path +. ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1")) + +$VmProperties = @{ + Path = $vm_config_file + SnapshotFilePath = Join-Path $data_path 'Snapshots' + VhdDestinationPath = Join-Path $data_path 'Virtual Hard Disks' + VirtualMachinePath = $data_path +} + +$vmConfig = (Compare-VM -Copy -GenerateNewID @VmProperties) + +$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 + + $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 +} + +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 + +If ($dynamicmemory) { + Set-VM -VM $vmConfig.VM -DynamicMemory + Set-VM -VM $vmConfig.VM -MemoryMinimumBytes $MemoryMinimumBytes -MemoryMaximumBytes $MemoryMaximumBytes +} else { + Set-VM -VM $vmConfig.VM -StaticMemory +} + +if ($notes) { + Set-VM -VM $vmConfig.VM -Notes $notes +} + +if ($auto_start_action) { + Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_start_action +} + +if ($auto_stop_action) { + Set-VM -VM $vmConfig.VM -AutomaticStartAction $auto_stop_action +} + +# Only set EFI secure boot for Gen 2 machines, not gen 1 +if ($generation -ne 1) { + Set-VMFirmware -VM $vmConfig.VM -EnableSecureBoot (Get-VMFirmware -VM $vmConfig.VM).SecureBoot +} + +$report = Compare-VM -CompatibilityReport $vmConfig + +# Stop if there are incompatibilities +if($report.Incompatibilities.Length -gt 0){ + Write-Error-Message $(ConvertTo-Json $($report.Incompatibilities | Select -ExpandProperty Message)) + exit 0 +} + +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) + } + + 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 = @{ + name = $vm_name + id = $vm_id +} + +$result = ConvertTo-Json $resultHash +Write-Output-Message $result diff --git a/plugins/providers/hyperv/scripts/import_vm.ps1 b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 similarity index 97% rename from plugins/providers/hyperv/scripts/import_vm.ps1 rename to plugins/providers/hyperv/scripts/import_vm_xml.ps1 index 659eb50aa..16055e135 100644 --- a/plugins/providers/hyperv/scripts/import_vm.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm_xml.ps1 @@ -1,8 +1,8 @@ Param( [Parameter(Mandatory=$true)] - [string]$vm_xml_config, + [string]$vm_config_file, [Parameter(Mandatory=$true)] - [string]$image_path, + [string]$dest_path, [string]$switchname=$null, [string]$memory=$null, @@ -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_file $generation = [int]($vmconfig.configuration.properties.subtype.'#text')+1 @@ -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") {