From e4dc350ace0df840a9f390dc3f3a5aa986ea8501 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Mon, 7 Apr 2025 15:56:02 -0700 Subject: [PATCH] Add dvd disk support to Hyper-V provider --- plugins/providers/hyperv/cap/cleanup_disks.rb | 39 +- .../providers/hyperv/cap/configure_disks.rb | 46 +- plugins/providers/hyperv/driver.rb | 30 ++ plugins/providers/hyperv/scripts/add_dvd.ps1 | 18 + .../hyperv/scripts/get_scsi_controller.ps1 | 19 + .../providers/hyperv/scripts/remove_dvd.ps1 | 21 + .../hyperv/cap/cleanup_disks_test.rb | 185 ++++++- .../hyperv/cap/configure_disks_test.rb | 450 ++++++++++++++---- 8 files changed, 705 insertions(+), 103 deletions(-) create mode 100644 plugins/providers/hyperv/scripts/add_dvd.ps1 create mode 100644 plugins/providers/hyperv/scripts/get_scsi_controller.ps1 create mode 100644 plugins/providers/hyperv/scripts/remove_dvd.ps1 diff --git a/plugins/providers/hyperv/cap/cleanup_disks.rb b/plugins/providers/hyperv/cap/cleanup_disks.rb index 1dcc1f2db..0ff978429 100644 --- a/plugins/providers/hyperv/cap/cleanup_disks.rb +++ b/plugins/providers/hyperv/cap/cleanup_disks.rb @@ -17,7 +17,8 @@ module VagrantPlugins return if disk_meta_file.values.flatten.empty? handle_cleanup_disk(machine, defined_disks, disk_meta_file["disk"]) - # TODO: Floppy and DVD disks + handle_cleanup_dvd(machine, defined_disks, disk_meta_file["dvd"]) + # TODO: Floppy disks end protected @@ -49,6 +50,42 @@ module VagrantPlugins end end end + + def self.handle_cleanup_dvd(machine, defined_disks, disk_meta) + # Get a list of all attached DVD drives + attached = machine.provider.driver.read_scsi_controllers.map do |controller| + controller["Drives"].map do |drive| + drive if drive["DvdMediaType"].to_i == 1 + end.compact + end.flatten.compact + + # Generate list of dvd disks that previously + # existed but are no longer defined + orphan_attachments = disk_meta.find_all do |mdisk| + defined_disks.none? do |defined_disk| + defined_disk.type == :dvd && + File.expand_path(mdisk["Path"]) == File.expand_path(defined_disk.file) + end + end + + # Remove any entries that are not currently + # attached + orphan_attachments.delete_if do |mdisk| + attached.any? do |attachment| + File.expand_path(attachment["Path"]) == mdisk["Path"] + end + end + + # Now remove any orphan attachments that remain + orphan_attachments.each do |mdisk| + LOGGER.debug("removing dvd attachment: #{mdisk}") + + machine.provider.driver.detach_dvd( + mdisk["ControllerLocation"], + mdisk["ControllerNumber"] + ) + end + end end end end diff --git a/plugins/providers/hyperv/cap/configure_disks.rb b/plugins/providers/hyperv/cap/configure_disks.rb index f6a42e20b..28a7a664a 100644 --- a/plugins/providers/hyperv/cap/configure_disks.rb +++ b/plugins/providers/hyperv/cap/configure_disks.rb @@ -27,13 +27,13 @@ module VagrantPlugins defined_disks.each do |disk| if disk.type == :disk disk_data = handle_configure_disk(machine, disk, current_disks) - configured_disks[:disk] << disk_data unless disk_data.empty? + configured_disks[:disk] << disk_data if !disk_data.empty? elsif disk.type == :floppy # TODO: Write me machine.ui.info(I18n.t("vagrant.cap.configure_disks.floppy_not_supported", name: disk.name)) elsif disk.type == :dvd - # TODO: Write me - machine.ui.info(I18n.t("vagrant.cap.configure_disks.dvd_not_supported", name: disk.name)) + disk_data = handle_configure_dvd(machine, disk) + configured_disks[:dvd] << disk_data if !disk_data.empty? end end @@ -98,6 +98,46 @@ module VagrantPlugins disk_metadata end + def self.handle_configure_dvd(machine, dvd) + dvd_location = File.expand_path(dvd.file) + + find_dvd_disk = proc { + catch(:found) do + machine.provider.driver.read_scsi_controllers.each do |controller| + controller["Drives"].each do |disk| + throw :found, disk if File.expand_path(disk["Path"]) == dvd_location + end + end + + nil + end + } + + generate_dvd_metadata = proc { |disk| + disk.slice( + "Name", "Id", "Path", + "ControllerLocation", "ControllerNumber", + "ControllerType" + ) + } + + # If the disk is already attached just + # return the information + if dvd_attached = find_dvd_disk.call + return generate_dvd_metadata.call(dvd_attached) + end + + # Attach the disk + machine.provider.driver.attach_dvd(dvd_location) + + # Find disk and return information + disk = find_dvd_disk.call + return generate_dvd_metadata.call(disk) if disk + + LOGGER.warn("failed to locate dvd on controller, stubbing") + {"Path" => dvd_location} + end + # Check to see if current disk is configured based on defined_disks # # @param [Kernel_V2::VagrantConfigDisk] disk_config diff --git a/plugins/providers/hyperv/driver.rb b/plugins/providers/hyperv/driver.rb index 23395bc16..c859e2f26 100644 --- a/plugins/providers/hyperv/driver.rb +++ b/plugins/providers/hyperv/driver.rb @@ -267,6 +267,27 @@ module VagrantPlugins execute(:get_vhd, DiskFilePath: disk_file_path) end + # Add a DVD drive to VM + # + # @param [String] iso_path + # @return [nil] + def attach_dvd(iso_path) + execute(:add_dvd, VmId: vm_id, ISOPath: iso_path) + end + + # Remove a DVD drive from VM + # + # @param [Integer] controller_location + # @param [Integer] controller_number + # @return [nil] + def detach_dvd(controller_location, controller_number) + execute(:remove_dvd, + VmId: vm_id, + ControllerLocation: controller_location, + ControllerNumber: controller_number + ) + end + # @return [Array[Hash]] def list_hdds execute(:list_hdds, VmId: @vm_id) @@ -305,6 +326,15 @@ module VagrantPlugins end end + # Get SCSI controllers attached to VM + # + # @return [Array] + def read_scsi_controllers + result = execute(:get_scsi_controller, VmId: vm_id) + return result if result.is_a?(Array) + [result] + end + protected def execute_powershell(path, options, &block) diff --git a/plugins/providers/hyperv/scripts/add_dvd.ps1 b/plugins/providers/hyperv/scripts/add_dvd.ps1 new file mode 100644 index 000000000..308e0c920 --- /dev/null +++ b/plugins/providers/hyperv/scripts/add_dvd.ps1 @@ -0,0 +1,18 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +#Requires -Modules VagrantMessages + +param( + [Parameter(Mandatory=$true)] + [string]$VmId, + [Parameter(Mandatory=$true)] + [string]$ISOPath +) + +try { + Hyper-V\Get-VM -ID $VmId | Hyper-V\Add-VMDvdDrive -Path $ISOPath +} catch { + Write-ErrorMessage "Failed to add DVD drive for path - ${ISOPath}: ${PSItem}" + exit 1 +} diff --git a/plugins/providers/hyperv/scripts/get_scsi_controller.ps1 b/plugins/providers/hyperv/scripts/get_scsi_controller.ps1 new file mode 100644 index 000000000..166d70172 --- /dev/null +++ b/plugins/providers/hyperv/scripts/get_scsi_controller.ps1 @@ -0,0 +1,19 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +#Requires -Modules VagrantMessages + +param( + [Parameter(Mandatory=$true)] + [string]$VmId +) + +try { + $Controller = Hyper-V\Get-VM -ID $VmId | Hyper-V\Get-VMScsiController +} catch { + Write-ErrorMessage "Failed to retrieve scsi controller info for ${VmId}: ${PSItem}" + exit 1 +} + +$result = ConvertTo-json $Controller -Depth 20 +Write-OutputMessage $result diff --git a/plugins/providers/hyperv/scripts/remove_dvd.ps1 b/plugins/providers/hyperv/scripts/remove_dvd.ps1 new file mode 100644 index 000000000..f765e9deb --- /dev/null +++ b/plugins/providers/hyperv/scripts/remove_dvd.ps1 @@ -0,0 +1,21 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +#Requires -Modules VagrantMessages + +param( + [Parameter(Mandatory=$true)] + [string]$VmId, + [Parameter(Mandatory=$true)] + [Int32]$ControllerNumber, + [Parameter(Mandatory=$true)] + [Int32]$ControllerLocation +) + +try { + $vm = Hyper-V\Get-VM -ID $VmId + Hyper-V\Remove-VMDvdDrive -ControllerNumber $ControllerNumber -ControllerLocation $ControllerLocation -VMName $vm.Name +} catch { + Write-ErrorMessage "Failed to remove DVD drive (Location: '${ControllerLocation}' Number '${ControllerNumber}'): ${PSItem}" + exit 1 +} diff --git a/test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb b/test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb index 3a3e658f7..ea5babfad 100644 --- a/test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb +++ b/test/unit/plugins/providers/hyperv/cap/cleanup_disks_test.rb @@ -32,10 +32,6 @@ describe VagrantPlugins::HyperV::Cap::CleanupDisks do let(:disk_meta_file) { {disk: [], floppy: [], dvd: []} } let(:defined_disks) { {} } - before do - allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true) - end - context "#cleanup_disks" do it "returns if there's no data in meta file" do subject.cleanup_disks(machine, defined_disks, disk_meta_file) @@ -43,7 +39,21 @@ describe VagrantPlugins::HyperV::Cap::CleanupDisks do end describe "with disks to clean up" do - let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} } + let(:disk_meta_file) do + { + "disk" => [ + { + "UUID" => "1234", + "Path" => "c:\\users\\vagrant\\storage.vhdx", + "Name" => "storage" + } + ], + "floppy" => [], + "dvd" => [] + } + end + + before { allow(driver).to receive(:read_scsi_controllers).and_return([]) } it "calls the cleanup method if a disk_meta file is defined" do expect(subject).to receive(:handle_cleanup_disk). @@ -52,21 +62,142 @@ describe VagrantPlugins::HyperV::Cap::CleanupDisks do subject.cleanup_disks(machine, defined_disks, disk_meta_file) end + + context "with dvd to clean up" do + let(:disk_meta_file) do + { + "disk" => [], + "floppy" => [], + "dvd" => [ + { + "Path" => "test.iso" + } + ] + } + + it "calls the cleamup method if a disk_meta file is defined" do + expect(subject).to receive(:handle_cleanup_dvd). + with(machine, defined_disks, disk_meta_file["dvd"]). + and_return(true) + + subject.cleanup_disks(machine, defined_disks, disk_meta_file) + end + end + + end + end + end + + context "handle_cleanup_dvd" do + let(:disk_meta_file) do + { + "disk" => [], + "floppy" => [], + "dvd" => [] + } + end + let(:scsi_controllers) do + [ + { + "ControllerNumber" => 0, + "Name" => "SCSI Controller", + "Drives" => drives + } + ] + end + let(:drives) do + [ + { + "DvdMediaType" => 1, + "Path" => "test.iso", + "ControllerLocation" => 1, + "ControllerNumber" => 0, + "ControllerType" => 1 + } + ] + end + let(:defined_disks) { [] } + + before do + allow(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers) + end + + it "should not remove disk" do + expect(driver).not_to receive(:detach_dvd) + + subject.handle_cleanup_dvd(machine, defined_disks, disk_meta_file["dvd"]) + end + + context "when disk is defined in meta file" do + let(:disk_meta_file) do + { + "disk" => [], + "floppy" => [], + "dvd" => [ + "Path" => "test.iso", + "ControllerLocation" => 1, + "ControllerNumber" => 0, + "ControllerType" => 1 + ] + } + end + + it "should remove the disk" do + expect(driver).to receive(:detach_dvd).with(1, 0) + + subject.handle_cleanup_dvd(machine, defined_disks, disk_meta_file["dvd"]) + end + + context "when disk is defined in defined disks" do + let(:defined_disks) do + [ + double("dvd", name: "test-dvd", type: :dvd, file: "test.iso") + ] + end + + it "should not remove disk" do + expect(driver).not_to receive(:detach_dvd) + + subject.handle_cleanup_dvd(machine, defined_disks, disk_meta_file["dvd"]) + end + end end end context "#handle_cleanup_disk" do - let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} } - let(:defined_disks) { [] } - let(:all_disks) { [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage", - "ControllerType"=>"IDE", "ControllerNumber"=>1, "ControllerLocation"=>0}] } - let(:path) { "C:\\Users\\vagrant\\storage.vhdx" } + let(:disk_meta_file) do + { + "disk" => [ + { + "UUID" => "1234", + "Path" => "c:\\users\\vagrant\\storage.vhdx", + "Name" => "storage" + } + ], + "floppy" => [], + "dvd" => [] + } + end + let(:defined_disks) { [] } + let(:all_disks) do + [ + { + "UUID" => "1234", + "Path" => "c:\\users\\vagrant\\storage.vhdx", + "Name"=>"storage", + "ControllerType" => "IDE", + "ControllerNumber" => 1, + "ControllerLocation" => 0 + } + ] + end + let(:path) { "C:\\Users\\vagrant\\storage.vhdx" } it "removes and closes medium from guest" do expect(driver).to receive(:list_hdds).and_return(all_disks) expect(driver).to receive(:remove_disk).with("IDE", 1, 0, "c:\\users\\vagrant\\storage.vhdx").and_return(true) - subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk]) + subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file["disk"]) end it "displays a warning if the disk could not be determined" do @@ -76,14 +207,36 @@ describe VagrantPlugins::HyperV::Cap::CleanupDisks do expect(driver).not_to receive(:remove_disk) expect(machine.ui).to receive(:warn).twice - subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk]) + subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file["disk"]) end describe "when windows paths mix cases" do - let(:disk_meta_file) { {disk: [{"UUID"=>"1234", "Path"=> "c:\\users\\vagrant\\storage.vhdx", "Name"=>"storage"}], floppy: [], dvd: []} } + let(:disk_meta_file) do + { + "disk" => [ + { + "UUID" => "1234", + "Path" => "c:\\users\\vagrant\\storage.vhdx", + "Name" => "storage" + } + ], + "floppy" => [], + "dvd" => [] + } + end let(:defined_disks) { [] } - let(:all_disks) { [{"UUID"=>"1234", "Path"=> "C:\\Users\\vagrant\\storage.vhdx", "Name"=>"storage", - "ControllerType"=>"IDE", "ControllerNumber"=>1, "ControllerLocation"=>0}] } + let(:all_disks) do + [ + { + "UUID" => "1234", + "Path" => "C:\\Users\\vagrant\\storage.vhdx", + "Name" => "storage", + "ControllerType" => "IDE", + "ControllerNumber" => 1, + "ControllerLocation" => 0 + } + ] + end let(:path) { "C:\\Users\\vagrant\\storage.vhdx" } @@ -92,7 +245,7 @@ describe VagrantPlugins::HyperV::Cap::CleanupDisks do expect(File).to receive(:realdirpath).twice.and_return(path) expect(driver).to receive(:remove_disk).with("IDE", 1, 0, path).and_return(true) - subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file[:disk]) + subject.handle_cleanup_disk(machine, defined_disks, disk_meta_file["disk"]) end end end diff --git a/test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb b/test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb index 573d42e0c..46049a5ae 100644 --- a/test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb +++ b/test/unit/plugins/providers/hyperv/cap/configure_disks_test.rb @@ -27,34 +27,50 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do double(:state) end - let(:defined_disks) { [double("disk", name: "vagrant_primary", size: "5GB", primary: true, type: :disk), - double("disk", name: "disk-0", size: "5GB", primary: false, type: :disk), - double("disk", name: "disk-1", size: "5GB", primary: false, type: :disk), - double("disk", name: "disk-2", size: "5GB", primary: false, type: :disk)] } + let(:defined_disks) do + [ + double("disk", name: "vagrant_primary", size: "5GB", primary: true, type: :disk), + double("disk", name: "disk-0", size: "5GB", primary: false, type: :disk), + double("disk", name: "disk-1", size: "5GB", primary: false, type: :disk), + double("disk", name: "disk-2", size: "5GB", primary: false, type: :disk) + ] + end let(:subject) { described_class } - let(:all_disks) { [{"UUID"=>"12345", - "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", - "ControllerLocation"=>0, - "ControllerNumber"=>0}, - {"UUID"=>"67890", - "Name"=>"disk-0", - "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", - "ControllerLocation"=>1, - "ControllerNumber"=>0}, - {"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", - "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", - "Name"=>"disk-1", - "ControllerLocation"=>2, - "ControllerNumber"=>0}] } - - before do - allow(Vagrant::Util::Experimental).to receive(:feature_enabled?).and_return(true) + let(:all_disks) do + [ + { + "UUID"=>"12345", + "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", + "ControllerLocation"=>0, + "ControllerNumber"=>0 + }, + { + "UUID"=>"67890", + "Name"=>"disk-0", + "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", + "ControllerLocation"=>1, + "ControllerNumber"=>0 + }, + { + "UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", + "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", + "Name"=>"disk-1", + "ControllerLocation"=>2, + "ControllerNumber"=>0 + } + ] end context "#configure_disks" do - let(:dsk_data) { {"UUID"=>"1234", "Name"=>"disk", "Path"=> "C:/Users/vagrant/storage.vhdx"} } + let(:dsk_data) do + { + "UUID"=>"1234", + "Name"=>"disk", + "Path"=> "C:/Users/vagrant/storage.vhdx" + } + end it "configures disks and returns the disks defined" do allow(driver).to receive(:list_hdds).and_return([]) @@ -69,6 +85,22 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do expect(subject.configure_disks(machine, defined_disks)).to eq({}) end end + + context "with dvd" do + before do + defined_disks.push( + double("dvd", name: "test-dvd", type: :dvd, file: "test.iso") + ) + end + + it "should configure the dvd disk" do + allow(driver).to receive(:list_hdds).and_return([]) + allow(subject).to receive(:handle_configure_disk).and_return({}) + expect(subject).to receive(:handle_configure_dvd).and_return({}) + + subject.configure_disks(machine, defined_disks) + end + end end context "#get_current_disk" do @@ -89,14 +121,126 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end end + context "#handle_configure_dvd" do + let(:scsi_controllers_current) do + [ + { + "ControllerNumber" => 0, + "Name" => "SCSI Controller", + "Drives" => drives_current + } + ] + end + let(:drives_current) { [] } + let(:scsi_controllers_updated) do + [ + { + "ControllerNumber" => 0, + "Name" => "SCSI Controller", + "Drives" => drives_updated + } + ] + end + let(:drives_updated) do + [ + { + "DvdMediaType" => 1, + "Path" => "test.iso", + "ControllerLocation" => 1, + "ControllerNumber" => 0, + "ControllerType" => 1 + } + ] + end + + let(:defined_disk) do + double("dvd", name: "test-dvd", type: :dvd, file: "test.iso") + end + + it "should add disk to guest" do + expect(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers_current) + expect(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers_updated) + expect(driver).to receive(:attach_dvd).with(/test.iso$/) + + subject.handle_configure_dvd(machine, defined_disk) + end + + context "when disk is already attached" do + let(:drives_current) do + [ + { + "DvdMediaType" => 1, + "Path" => "test.iso", + "ControllerLocation" => 1, + "ControllerNumber" => 0, + "ControllerType" => 1 + } + ] + end + + it "should not add disk to guest" do + expect(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers_current) + expect(driver).not_to receive(:attach_dvd) + + subject.handle_configure_dvd(machine, defined_disk) + end + + context "when additional disk is defined" do + let(:defined_disk) do + double("dvd", name: "other-dvd", type: :dvd, file: "other-test.iso") + end + + let(:drives_updated) do + [ + { + "DvdMediaType" => 1, + "Path" => "test.iso", + "ControllerLocation" => 1, + "ControllerNumber" => 0, + "ControllerType" => 1 + }, + { + "DvdMediaType" => 1, + "Path" => "other-test.iso", + "ControllerLocation" => 2, + "ControllerNumber" => 0, + "ControllerType" => 1 + } + ] + end + + + it "should add disk to guest" do + expect(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers_current) + expect(driver).to receive(:read_scsi_controllers).and_return(scsi_controllers_updated) + expect(driver).to receive(:attach_dvd).with(/other-test.iso$/) + + subject.handle_configure_dvd(machine, defined_disk) + end + end + end + end + context "#handle_configure_disk" do describe "when creating a new disk" do - let(:all_disks) { [{"UUID"=>"12345", - "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", - "ControllerLocation"=>0, - "ControllerNumber"=>0}] } + let(:all_disks) do + [ + { + "UUID"=>"12345", + "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", + "ControllerLocation"=>0, + "ControllerNumber"=>0 + } + ] + end - let(:disk_meta) { {"UUID"=>"12345", "Name"=>"vagrant_primary", "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx" } } + let(:disk_meta) do + { + "UUID" => "12345", + "Name" => "vagrant_primary", + "Path" => "C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx" + } + end it "creates a new disk if it doesn't yet exist" do expect(subject).to receive(:create_disk).with(machine, defined_disks[1]) @@ -107,20 +251,29 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end describe "when a disk needs to be resized" do - let(:all_disks) { [{"UUID"=>"12345", - "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", - "ControllerLocation"=>0, - "ControllerNumber"=>0}, - {"UUID"=>"67890", - "Name"=>"disk-0", - "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", - "ControllerLocation"=>1, - "ControllerNumber"=>0}, - {"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", - "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", - "Name"=>"disk-1", - "ControllerLocation"=>2, - "ControllerNumber"=>0}] } + let(:all_disks) do + [ + {"UUID"=>"12345", + "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", + "ControllerLocation"=>0, + "ControllerNumber"=>0 + }, + { + "UUID"=>"67890", + "Name"=>"disk-0", + "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", + "ControllerLocation"=>1, + "ControllerNumber"=>0 + }, + { + "UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", + "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", + "Name"=>"disk-1", + "ControllerLocation"=>2, + "ControllerNumber"=>0 + } + ] + end it "resizes a disk" do expect(subject).to receive(:get_current_disk). @@ -137,20 +290,30 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end describe "if no additional disk configuration is required" do - let(:all_disks) { [{"UUID"=>"12345", - "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", - "ControllerLocation"=>0, - "ControllerNumber"=>0}, - {"UUID"=>"67890", - "Name"=>"disk-0", - "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", - "ControllerLocation"=>1, - "ControllerNumber"=>0}, - {"UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", - "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", - "Name"=>"disk-1", - "ControllerLocation"=>2, - "ControllerNumber"=>0}] } + let(:all_disks) do + [ + { + "UUID"=>"12345", + "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", + "ControllerLocation"=>0, + "ControllerNumber"=>0 + }, + { + "UUID"=>"67890", + "Name"=>"disk-0", + "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", + "ControllerLocation"=>1, + "ControllerNumber"=>0 + }, + { + "UUID"=>"324bbb53-d5ad-45f8-9bfa-1f2468b199a8", + "Path"=>"C:/Users/vagrant/disks/disk-1.vhdx", + "Name"=>"disk-1", + "ControllerLocation"=>2, + "ControllerNumber"=>0 + } + ] + end it "does nothing if all disks are properly configured" do expect(subject).to receive(:get_current_disk). @@ -165,19 +328,43 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end context "#compare_disk_size" do - let(:disk_config_small) { double("disk", name: "disk-0", size: 41824.0, primary: false, type: :disk) } - let(:disk_config_large) { double("disk", name: "disk-0", size: 123568719476736.0, primary: false, type: :disk) } + let(:disk_config_small) do + double("disk", + name: "disk-0", + size: 41824.0, + primary: false, + type: :disk + ) + end + let(:disk_config_large) do + double("disk", + name: "disk-0", + size: 123568719476736.0, + primary: false, + type: :disk + ) + end - let(:disk_large) { [{"UUID"=>"12345", - "Path"=>"C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", - "ControllerLocation"=>0, - "ControllerNumber"=>0}] } + let(:disk_large) do + [ + { + "UUID" => "12345", + "Path" => "C:/Users/vagrant/disks/ubuntu-18.04-amd64-disk001.vhdx", + "ControllerLocation" => 0, + "ControllerNumber" => 0 + } + ] + end - let(:disk_small) { {"UUID"=>"67890", - "Path"=>"C:/Users/vagrant/disks/small_disk.vhd", - "Size"=>1073741824.0, - "ControllerLocation"=>1, - "ControllerNumber"=>0} } + let(:disk_small) do + { + "UUID" => "67890", + "Path" => "C:/Users/vagrant/disks/small_disk.vhd", + "Size" => 1073741824.0, + "ControllerLocation" => 1, + "ControllerNumber" => 0 + } + end it "shows a warning if user attempts to shrink size of a vhd disk" do expect(machine.ui).to receive(:warn) @@ -194,19 +381,30 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do context "#create_disk" do let(:disk_provider_config) { {} } - let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0, - primary: false, type: :disk, disk_ext: "vhdx", - provider_config: disk_provider_config, - file: nil) } + let(:disk_config) do + double("disk", + name: "disk-0", + size: 1073741824.0, + primary: false, + type: :disk, + disk_ext: "vhdx", + provider_config: disk_provider_config, + file: nil + ) + end let(:disk_file) { "C:/Users/vagrant/disks/Virtual Hard Disks/disk-0.vhdx" } let(:data_dir) { Pathname.new("C:/Users/vagrant/disks") } - let(:disk) { {"DiskIdentifier"=>"12345", - "Path"=>"C:/Users/vagrant/disks/Virtual Hard Disks/disk-0.vhdx", - "ControllerLocation"=>1, - "ControllerNumber"=>0} } + let(:disk) do + { + "DiskIdentifier" => "12345", + "Path" => "C:/Users/vagrant/disks/Virtual Hard Disks/disk-0.vhdx", + "ControllerLocation" => 1, + "ControllerNumber" => 0 + } + end it "creates a disk and attaches it to a guest" do expect(machine).to receive(:data_dir).and_return(data_dir) @@ -220,7 +418,14 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end context "#convert_size_vars!" do - let(:disk_provider_config) { {BlockSizeBytes: "128MB", LogicalSectorSizeBytes: 512, PhysicalSectorSizeBytes: 4096 } } + let(:disk_provider_config) do + { + BlockSizeBytes: "128MB", + LogicalSectorSizeBytes: 512, + PhysicalSectorSizeBytes: 4096 + } + end + it "converts certain powershell arguments into something usable" do updated_config = subject.convert_size_vars!(disk_provider_config) @@ -231,15 +436,26 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end context "#resize_disk" do - let(:disk_config) { double("disk", name: "disk-0", size: 1073741824.0, - primary: false, type: :disk, disk_ext: "vhdx", - provider_config: nil, - file: nil) } + let(:disk_config) do + double("disk", + name: "disk-0", + size: 1073741824.0, + primary: false, + type: :disk, + disk_ext: "vhdx", + provider_config: nil, + file: nil + ) + end - let(:disk) { {"DiskIdentifier"=>"12345", - "Path"=>"C:/Users/vagrant/disks/disk-0.vhdx", - "ControllerLocation"=>1, - "ControllerNumber"=>0} } + let(:disk) do + { + "DiskIdentifier" => "12345", + "Path" => "C:/Users/vagrant/disks/disk-0.vhdx", + "ControllerLocation" => 1, + "ControllerNumber" => 0 + } + end let(:disk_file) { "C:/Users/vagrant/disks/disk-0.vhdx" } @@ -251,3 +467,71 @@ describe VagrantPlugins::HyperV::Cap::ConfigureDisks do end end end + +val =<<-EOF +{ + "ControllerNumber": 0, + "IsTemplate": false, + "Drives": [ + { + "Path": "C:\\Users\\vagrant\\project\\.vagrant\\machines\\default\\hyperv\\Virtual Hard Disks\\ubuntu-18.04-amd64.vhdx", + "DiskNumber": null, + "MaximumIOPS": 0, + "MinimumIOPS": 0, + "QoSPolicyID": "00000000-0000-0000-0000-000000000000", + "SupportPersistentReservations": false, + "WriteHardeningMethod": 0, + "ControllerLocation": 0, + "ControllerNumber": 0, + "ControllerType": 1, + "Name": "Hard Drive on SCSI controller number 0 at location 0", + "PoolName": "Primordial", + "Id": "Microsoft:6F225311-B793-49CF-98A3-0A32108E49BB\\6AEC67E1-3135-401C-BB23-9FE1C4E34560\\0\\0\\D", + "VMId": "6f225311-b793-49cf-98a3-0a32108e49bb", + "VMName": "project_default_1744059721263_2993", + "VMSnapshotId": "00000000-0000-0000-0000-000000000000", + "VMSnapshotName": "", + "CimSession": { + "ComputerName": null, + "InstanceId": "899e8c1f-5c4f-4ba4-86a4-f72dc887885f" + }, + "ComputerName": "DESKTOP-GICAJ17", + "IsDeleted": false + }, + { + "DvdMediaType": 1, + "Path": "C:\\Users\\Vagrant\\deb2.iso", + "ControllerLocation": 2, + "ControllerNumber": 0, + "ControllerType": 1, + "Name": "DVD Drive on SCSI controller number 0 at location 2", + "PoolName": "Primordial", + "Id": "Microsoft:6F225311-B793-49CF-98A3-0A32108E49BB\\6AEC67E1-3135-401C-BB23-9FE1C4E34560\\0\\2\\D", + "VMId": "6f225311-b793-49cf-98a3-0a32108e49bb", + "VMName": "project_default_1744059721263_2993", + "VMSnapshotId": "00000000-0000-0000-0000-000000000000", + "VMSnapshotName": "", + "CimSession": { + "ComputerName": null, + "InstanceId": "899e8c1f-5c4f-4ba4-86a4-f72dc887885f" + }, + "ComputerName": "DESKTOP-GICAJ17", + "IsDeleted": false + } + ], + "Name": "SCSI Controller", + "Id": "Microsoft:6F225311-B793-49CF-98A3-0A32108E49BB\\6AEC67E1-3135-401C-BB23-9FE1C4E34560\\0", + "VMId": "6f225311-b793-49cf-98a3-0a32108e49bb", + "VMName": "project_default_1744059721263_2993", + "VMSnapshotId": "00000000-0000-0000-0000-000000000000", + "VMSnapshotName": "", + "CimSession": { + "ComputerName": null, + "InstanceId": "899e8c1f-5c4f-4ba4-86a4-f72dc887885f" + }, + "ComputerName": "DESKTOP-GICAJ17", + "IsDeleted": false, + "VMCheckpointId": "00000000-0000-0000-0000-000000000000", + "VMCheckpointName": "" +} +EOF