diff --git a/plugins/providers/hyperv/action/import.rb b/plugins/providers/hyperv/action/import.rb index afec6f39a..ec16cd072 100644 --- a/plugins/providers/hyperv/action/import.rb +++ b/plugins/providers/hyperv/action/import.rb @@ -1,5 +1,6 @@ require "fileutils" require "log4r" +require "json" module VagrantPlugins module HyperV @@ -60,6 +61,38 @@ module VagrantPlugins env[:ui].output("Importing a Hyper-V instance") dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join(image_path.basename).to_s + if defined? env[:machine].provider_config.controllers + controllers = env[:machine].provider_config.controllers + disks_to_create = [] + data_dir = env[:machine].data_dir + # This can happen when creating new on up. + controllers.delete_if &:empty? + + controllers.each { |controller| + next_is_size = false + disk_name = '' + controller[:disks].each { |i| + if !next_is_size + if File.file?(i) + create_disk = false + filename_for_disk = i + next_is_size = false + + @logger.error("Attaching disks is not implemented yet") + else + create_disk = true + disk_name = i + disk_name = data_dir.join("#{disk_name}.vhdx").to_s.gsub("/", "\\") + next_is_size = true + end + else + disks_to_create << { name: disk_name, size: i} + end + } + } + disks_to_create_json = disks_to_create.to_json.to_s.gsub('"', '"""') + end + options = { "VMConfigFile" => Vagrant::Util::Platform.wsl_to_windows_path(config_path).gsub("/", "\\"), "DestinationPath" => Vagrant::Util::Platform.wsl_to_windows_path(dest_path).gsub("/", "\\"), @@ -67,6 +100,7 @@ module VagrantPlugins "LinkedClone" => !!env[:machine].provider_config.linked_clone, "SourcePath" => Vagrant::Util::Platform.wsl_to_windows_path(image_path).gsub("/", "\\"), "VMName" => env[:machine].provider_config.vmname, + "DisksToCreate" => disks_to_create_json, } diff --git a/plugins/providers/hyperv/config.rb b/plugins/providers/hyperv/config.rb index 96014258c..a1fd869af 100644 --- a/plugins/providers/hyperv/config.rb +++ b/plugins/providers/hyperv/config.rb @@ -48,6 +48,8 @@ module VagrantPlugins attr_accessor :enable_virtualization_extensions # @return [Hash] Options for VMServiceIntegration attr_accessor :vm_integration_services + # @return [Array] Config of disks and controllers + attr_accessor :controllers def initialize @ip_address_timeout = UNSET_VALUE @@ -65,6 +67,11 @@ module VagrantPlugins @enable_automatic_checkpoints = UNSET_VALUE @enable_checkpoints = UNSET_VALUE @vm_integration_services = {} + @controllers = [] + end + + def controller(controller={}) + @controllers << controller end def finalize! @@ -133,6 +140,52 @@ module VagrantPlugins allowed_actions: ALLOWED_AUTO_STOP_ACTIONS.join(", ")) end + # This can happen when creating new on up. + controllers.delete_if &:empty? + + controllers.each { |controller| + + if ![:ide, :scsi].include?(controller[:type]) + errors << I18n.t("vagrant_hyperv.config.invalid_controller_type", + type: controller[:type]) + end + + if [:ide].include?(controller[:type]) + errors << I18n.t("vagrant_hyperv.config.invalid_controller_type_ide_not_implemeented_yet") + end + + if !controller[:disks].is_a?(Array) + errors << I18n.t("vagrant_hyperv.config.invalid_controller_disks_is_not_an_array", + received: controller[:disks].class) + next + end + + next_is_size = false + controller[:disks].each { |i| + if !next_is_size + if i.is_a?(String) + if File.file?(i) + next_is_size = false + + # TODO: This part not implemented yet. + errors << I18n.t("vagrant_hyperv.config.invalid_controller_disks_attaching_disks_not_implemented_yet", + element: i) + else + next_is_size = true + end + else + errors << I18n.t("vagrant_hyperv.config.invalid_controller_disks_element_is_not_a_string", + element: i, element_class: i.class) + end + else + if !i.is_a?(Integer) + errors << I18n.t("vagrant_hyperv.config.invalid_controller_disks_element_is_not_an_integer", + element: i, element_class: i.class) + end + end + } + } + {"Hyper-V" => errors} end end diff --git a/plugins/providers/hyperv/scripts/import_vm.ps1 b/plugins/providers/hyperv/scripts/import_vm.ps1 index e339a754e..814539687 100644 --- a/plugins/providers/hyperv/scripts/import_vm.ps1 +++ b/plugins/providers/hyperv/scripts/import_vm.ps1 @@ -12,7 +12,9 @@ param( [parameter (Mandatory=$false)] [switch] $LinkedClone, [parameter (Mandatory=$false)] - [string] $VMName=$null + [string] $VMName=$null, + [parameter (Mandatory=$false)] + [string] $DisksToCreate=$null ) $ErrorActionPreference = "Stop" @@ -35,3 +37,45 @@ try { Write-ErrorMessage "${PSItem}" exit 1 } + + +#controller - path (for existent) +# path, +# sizeMB, name (for new) +function AddDisks($vm, $controller) { + #get controller + + $contNumber = ($vm | Add-VMScsiController -PassThru).ControllerNumber + foreach($disk in $controller) { + #get vhd + $vhd = $null + if($disk.Path) { + if (Test-Path -Path $disk.Path) { + $vhd = Resolve-Path -Path $disk.Path + } + } + else { + $vhd = $disk.Name + if (!(Test-Path -Path $vhd)) { + New-VHD -Path $vhd -SizeBytes ([UInt64]$disk.Size * 1MB) -Dynamic + } + } + if (!(Test-Path -Path $vhd)) { + Write-Error "There is error in virtual disk (VHD) configuration" + break + } + + $driveParam = @{ + ControllerNumber = $contNumber + Path = $vhd + ControllerType = "SCSI" + } + $vm | Add-VMHardDiskDrive @driveParam + } +} + +if ($DisksToCreate) { + $ParsedDisksToCreate = $DisksToCreate | ConvertFrom-Json + $ParsedDisksToCreate | ForEach-Object { AddDisks -vm $VMName -controller $_ } +} + diff --git a/templates/locales/providers_hyperv.yml b/templates/locales/providers_hyperv.yml index dd2a13c44..d993308f5 100644 --- a/templates/locales/providers_hyperv.yml +++ b/templates/locales/providers_hyperv.yml @@ -39,6 +39,25 @@ en: The `differencing_disk` configuration option is deprecated and should no longer be used. The `linked_clone` configuration option should be used instead. + invalid_controller_type: |- + Invalid controller type provided for :type. Type receiver is + `%{type}` but `:ide` or `:scsi` was expected. + + Received: %{type} + Allowed: :ide, :scsi + invalid_controller_type_ide_not_implemeented_yet: |- + Controller type IDE is not implemented yet. + invalid_controller_disks_is_not_an_array: |- + Invalid type provided for `disks` in controller. Type received + is `%{received}` but `Array` was expected. + invalid_controller_disks_attaching_disks_not_implemented_yet: |- + Attaching existing disks is not implemented yet. + invalid_controller_disks_element_is_not_a_string: |- + Invalid type provided for element `%{element}`. Type received is + `%{element_class}` but `String` was expected. + invalid_controller_disks_element_is_not_an_integer: |- + Invalid type provided for element `%{element}`. Type received is + `%{element_class}` but `Integer` was expected. errors: admin_required: |- The Hyper-V provider requires that Vagrant be run with diff --git a/website/source/docs/hyperv/configuration.html.md b/website/source/docs/hyperv/configuration.html.md index d3f5416b0..823b5ae44 100644 --- a/website/source/docs/hyperv/configuration.html.md +++ b/website/source/docs/hyperv/configuration.html.md @@ -33,6 +33,9 @@ you may set. A complete reference is shown below: * `shutdown` (boolean) * `time_synchronization` (boolean) * `vss` (boolean) +* `controller` (hash) - Config of disks and controllers. + * `type` (:scsi) - Determines type of controller. Only supports SCSI right now. IDE is to be implemented. + * `disks` (array) - Disks to be created on the controller. First element is a string specifying name of disk, example d1. Second is size of disk in MB, example 10 * 1024 is 10 GB. Name of disk comes first and size after on every disk. ## VM Integration Services @@ -55,3 +58,21 @@ end This example would enable the `GuestServiceInterface` (which Vagrant is aware) and `CustomVMSRV` (which Vagrant is _not_ aware) VM integration services. + +## Attaching extra controllers and disks + +The `controller` configuration option is a simple hash that describes what kind of controller it is. +Such as `:scsi` or `:ide`. Right now only `:scsi` is supported. After this the disks that will be +created is specified in the `disks` array. This is an array where several disks to create can be +specified. First comes the name of the disk, for example d1, and after that followes the size of the +disk specified in MB, for example 10 * 1024 for a 10 GB disk. Repeat this for the number of disks you +want to attach on the controller. To attach existing vhd- or vhdx-files is not implemented yet. + +```ruby +config.vm.provider "hyperv" do |h| + h.controller type: :scsi, disks: [ "d1", 10 * 1024 ] +end +``` + +This example would create a SCSI controller and also create a d1.vhdx file and attach it as a dynamic +disk that is 10 * 1024 MB big, in other words 10 GB.