class Kitchen::Driver::Hyperv

Driver for Hyper-V

Public Instance Methods

create(state) click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 75
def create(state)
  @state = state
  validate_vm_settings
  create_new_differencing_disk
  create_additional_disks
  create_virtual_machine
  set_virtual_machine_note
  update_state
  mount_virtual_machine_iso
  instance.transport.connection(@state).wait_until_ready
  copy_vm_files
  info("Hyper-V instance #{instance.to_str} created.")
end
destroy(state) click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 89
def destroy(state)
  @state = state
  if differencing_disk_exists && !vm_exists_silent
    remove_differencing_disk
  end
  return unless vm_exists

  instance.transport.connection(state).close
  remove_virtual_machine
  remove_differencing_disk
  remove_additional_disks
  info("The Hyper-V instance #{instance.to_str} has been removed.")
  state.delete(:id)
end

Private Instance Methods

additional_disk_path(disk_name, disk_type) click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 300
def additional_disk_path(disk_name, disk_type)
  File.join(kitchen_vm_path, disk_name + disk_type)
end
base_script_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 340
def base_script_path
  return remote_script_path if remote_hyperv

  local_script_path
end
boot_iso_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 290
def boot_iso_path
  @boot_iso_path ||= config[:boot_iso_path]
end
connection() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 320
def connection
  return @connection if @connection

  backend = remote_hyperv ? "winrm" : "local"

  train = Train.create(backend, {
                        host:        config[:hyperv_server],
                        user:        config[:hyperv_username],
                        password:    config[:hyperv_password],
                        ssl:         config[:hyperv_ssl],
                        self_signed: config[:hyperv_insecure],
                      })
  @connection = train.connection

  # Copy support PS1
  @connection.upload(local_script_path, remote_script_path)

  @connection
end
copy_vm_files() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 214
def copy_vm_files
  return if config[:copy_vm_files].nil?

  info("Copying files to virtual machine")
  config[:copy_vm_files].each do |file_info|
    run_ps copy_vm_file_ps(file_info[:source], file_info[:dest])
  end
  info("Copied files to virtual machine")
end
create_additional_disks() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 138
def create_additional_disks
  return if config[:additional_disks].nil?

  @additional_disk_objects = []
  config[:additional_disks].each do |additional_disk|
    raise "Missing name for additional disk" unless additional_disk[:name]

    disk_type = additional_disk[:type] || config[:disk_type]
    disk_path = additional_disk_path(additional_disk[:name], disk_type)
    raise "Additional disk file already exists: #{disk_path}" if File.exist?(disk_path)

    disk_size = additional_disk[:size_gb] || 5
    info("Creating additional disk #{additional_disk[:name]} for #{instance.name}.")
    run_ps new_additional_disk_ps(disk_path, disk_size)
    info("Created additional disk #{additional_disk[:name]} for #{instance.name}.")
    @additional_disk_objects.push(disk_path)
  end
end
create_new_differencing_disk() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 131
def create_new_differencing_disk
  info("Creating differencing disk for #{instance.name}.")
  run_ps new_differencing_disk_ps
  info("Created differencing disk for #{instance.name}.")
  set_new_vhd_size
end
create_virtual_machine() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 168
def create_virtual_machine
  return if vm_exists

  info("Creating virtual machine for #{instance.name}.")
  new_vm_object = run_ps new_vm_ps
  raise "Unable to create virtual machine for #{instance.name}." if new_vm_object.nil?

  @state[:id] = new_vm_object["Id"]
  info("Created virtual machine for #{instance.name}.")
end
differencing_disk_exists() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 246
def differencing_disk_exists
  return unless File.exist? differencing_disk_path

  true
end
differencing_disk_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 294
def differencing_disk_path
  kitchen_vm_base = remote_hyperv ? remote_kitchen_vm_path : kitchen_vm_path

  @differencing_disk_path ||= File.join(kitchen_vm_base, "diff" + "#{config[:disk_type]}")
end
kitchen_vm_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 282
def kitchen_vm_path
  @kitchen_vm_path ||= File.join(config[:kitchen_root], ".kitchen/#{instance.name}")
end
local_script_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 346
def local_script_path
  File.join(File.dirname(__FILE__), "/../../../support/hyperv.ps1")
end
mount_virtual_machine_iso() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 191
def mount_virtual_machine_iso
  return unless config[:iso_path]

  info("Mounting #{config[:iso_path]}")
  run_ps mount_vm_iso
  info("Done mounting #{config[:iso_path]}")
end
parent_vhd_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 304
def parent_vhd_path
  @parent_vhd_path ||= File.join(config[:parent_vhd_folder], config[:parent_vhd_name])
end
remote_hyperv() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 316
def remote_hyperv
  !!config[:hyperv_server]
end
remote_kitchen_vm_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 286
def remote_kitchen_vm_path
  config[:remote_vm_path]
end
remote_script_path() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 350
def remote_script_path
  File.join(config[:kitchen_root], "kitchen-hyperv", "hyperv.ps1")
end
remove_additional_disks() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 266
def remove_additional_disks
  return if config[:additional_disks].nil?

  config[:additional_disks].each do |additional_disk|
    raise "Missing name for additional disk" unless additional_disk[:name]

    disk_type = additional_disk[:type] || config[:disk_type]
    disk_path = additional_disk_path(additional_disk[:name], disk_type)
    if File.exist?(disk_path)
      info("Removing additional disk #{additional_disk[:name]} for #{instance.name}.")
      FileUtils.rm(disk_path)
      info("Removed additional disk #{additional_disk[:name]} for #{instance.name}.")
    end
  end
end
remove_differencing_disk() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 258
def remove_differencing_disk
  return unless differencing_disk_exists

  info("Removing the differencing disk for #{instance.name}.")
  FileUtils.rm(differencing_disk_path)
  info("Removed the differencing disk for #{instance.name}.")
end
remove_virtual_machine() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 252
def remove_virtual_machine
  info("Deleting virtual machine for #{instance.name}")
  run_ps delete_vm_ps
  info("Deleted virtual machine for #{instance.name}")
end
set_new_vhd_size() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 199
def set_new_vhd_size
  return unless config[:resize_vhd]

  info("Resizing differencing disk for #{instance.name}.")
  run_ps resize_vhd
  info("Resized differencing disk for #{instance.name}.")
end
set_virtual_machine_note() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 207
def set_virtual_machine_note
  return unless config[:vm_note]

  info("Adding note to VM: '#{config[:vm_note]}'")
  run_ps set_vm_note
end
update_state() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 179
def update_state
  vm_details
  @state[:id] = @vm["Id"]
  @state[:hostname] = @vm["IpAddress"]
  @state[:vm_name] = @vm["Name"]
end
validate_vm_settings() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 106
def validate_vm_settings
  raise "Missing parent_vhd_folder" unless vhd_folder? || remote_hyperv
  raise "Missing parent_vhd_name" unless vhd? || remote_hyperv

  if config[:dynamic_memory]
    startup_bytes = config[:memory_startup_bytes]
    min = config[:dynamic_memory_min_bytes]
    max = config[:dynamic_memory_max_bytes]
    memory_valid = startup_bytes.between?(min, max)
    warning = "memory_startup_bytes (#{startup_bytes}) must" \
              " fall within dynamic memory range (#{min}-#{max})"
    raise warning unless memory_valid
  end
  config[:vm_switch] = vm_switch
  if config[:vm_vlan_id]
    vm_vlan_id = config[:vm_vlan_id]
    vm_vlan_id_min = 1
    vm_vlan_id_max = 4094
    vm_vlan_id_valid = vm_vlan_id.between?(vm_vlan_id_min, vm_vlan_id_max)
    vm_vlan_id_warning = "vm_vlan_id (#{vm_vlan_id}) must be a valid 802.1Q" \
                         " VLAN ID between (#{vm_vlan_id_min}-#{vm_vlan_id_max})"
    raise vm_vlan_id_warning unless vm_vlan_id_valid
  end
end
vhd?() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 312
def vhd?
  config[:parent_vhd_name] && File.exist?(parent_vhd_path)
end
vhd_folder?() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 308
def vhd_folder?
  config[:parent_vhd_folder] && Dir.exist?(config[:parent_vhd_folder])
end
vm_details() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 186
def vm_details
  run_ps set_vm_ipaddress_ps if config[:ip_address]
  @vm = run_ps vm_details_ps
end
vm_exists() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 224
def vm_exists
  info("Checking for existing virtual machine.")
  return false unless @state.key?(:id) && !@state[:id].nil?

  existing_vm = run_ps ensure_vm_running_ps
  return false if existing_vm.nil? || existing_vm["Id"].nil?

  info("Found an exising VM with an ID: #{existing_vm["Id"]}")
  true
end
vm_exists_silent() click to toggle source

Used in testing if a stale diff disk exists. Silent so the output doesn’t appear twice on the kitchen destroy command for the second check for vm_exists

# File lib/kitchen/driver/hyperv.rb, line 237
def vm_exists_silent
  return false unless @state.key?(:id) && !@state[:id].nil?

  existing_vm = run_ps ensure_vm_running_ps
  return false if existing_vm.nil? || existing_vm["Id"].nil?

  true
end
vm_switch() click to toggle source
# File lib/kitchen/driver/hyperv.rb, line 157
def vm_switch
  default_switch_object = run_ps vm_default_switch_ps
  if default_switch_object.nil? ||
      !default_switch_object.key?("Name") ||
      default_switch_object["Name"].empty?
    raise "Failed to find a default VM Switch."
  end

  default_switch_object["Name"]
end