Module: Kitchen::Driver::HypervHelpers
- Included in:
- Vagrant
- Defined in:
- lib/kitchen/driver/helpers.rb
Overview
Helpers for talking to Hyper-V from the Vagrant driver.
Hyper-V has no command line interface of its own, so everything here
funnels through PowerShell: a script is wrapped so that it dot-sources
the bundled support/hyperv.ps1, base64-encoded for -EncodedCommand
(which sidesteps all shell quoting problems), executed, and its JSON
output parsed back into Ruby.
The module expects to be mixed into a Configurable that also provides Logging, which the Vagrant driver does.
Instance Method Summary collapse
-
#encode_command(script) ⇒ String
private
Encodes a PowerShell script for
powershell.exe -EncodedCommand. -
#execute_command(cmd, options = {}) ⇒ Hash, ...
private
Runs a command locally and parses its output as JSON.
-
#hyperv_default_switch_ps ⇒ String
private
The PowerShell that #hyperv_switch runs.
-
#hyperv_switch ⇒ String
private
Asks Hyper-V for the virtual switch new VMs should be attached to.
-
#is_32bit? ⇒ true, false
private
Whether both the OS and the running Ruby are 32-bit.
-
#is_64bit? ⇒ true, false
private
Whether both the OS and the running Ruby are 64-bit.
-
#os_architecture ⇒ String?
private
The processor architecture Windows reports for this process.
-
#powershell_64_bit ⇒ String
private
Path to a PowerShell that matches the machine's architecture.
-
#ruby_64bit? ⇒ true, false
private
Whether the running Ruby is a 64-bit build, determined from the size of a packed pointer.
-
#run_ps(cmd, options = {}) ⇒ Hash?
private
Convenience method to run a powershell command locally.
-
#sanitize_stdout(stdout) ⇒ String
private
Strips the interactive prompt lines PowerShell interleaves with real output, which would otherwise make the result unparseable as JSON.
-
#wrap_command(script) ⇒ String
private
Builds a full
powershell.execommand line that dot-sources the gem'ssupport/hyperv.ps1helper functions and then runsscript.
Instance Method Details
#encode_command(script) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Encodes a PowerShell script for powershell.exe -EncodedCommand.
PowerShell requires UTF-16LE, not UTF-8, and strict (unwrapped) base64.
46 47 48 49 |
# File 'lib/kitchen/driver/helpers.rb', line 46 def encode_command(script) encoded_script = script.encode("UTF-16LE", "UTF-8") Base64.strict_encode64(encoded_script) end |
#execute_command(cmd, options = {}) ⇒ Hash, ...
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Runs a command locally and parses its output as JSON.
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/kitchen/driver/helpers.rb', line 147 def execute_command(cmd, = {}) debug("#Local Command BEGIN (#{cmd})") sh = Mixlib::ShellOut.new(cmd, ) sh.run_command debug("Local Command END #{Util.duration(sh.execution_time)}") raise "Failed: #{sh.stderr}" if sh.error? stdout = sanitize_stdout(sh.stdout) JSON.parse(stdout) if stdout.length > 2 end |
#hyperv_default_switch_ps ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The PowerShell that #hyperv_switch runs. Honours
KITCHEN_HYPERV_SWITCH; without it, Get-DefaultVMSwitch picks one.
189 190 191 192 193 |
# File 'lib/kitchen/driver/helpers.rb', line 189 def hyperv_default_switch_ps <<-VMSWITCH Get-DefaultVMSwitch #{ENV["KITCHEN_HYPERV_SWITCH"]} | ConvertTo-Json VMSWITCH end |
#hyperv_switch ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Asks Hyper-V for the virtual switch new VMs should be attached to.
173 174 175 176 177 178 179 180 181 182 |
# File 'lib/kitchen/driver/helpers.rb', line 173 def hyperv_switch default_switch_object = run_ps hyperv_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 |
#is_32bit? ⇒ true, false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Whether both the OS and the running Ruby are 32-bit.
Note this is deliberately not the negation of #is_64bit?: a 32-bit Ruby on a 64-bit Windows (the WOW64 case) is neither.
88 89 90 |
# File 'lib/kitchen/driver/helpers.rb', line 88 def is_32bit? os_architecture != "AMD64" && !ruby_64bit? end |
#is_64bit? ⇒ true, false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Whether both the OS and the running Ruby are 64-bit.
77 78 79 |
# File 'lib/kitchen/driver/helpers.rb', line 77 def is_64bit? os_architecture == "AMD64" && ruby_64bit? end |
#os_architecture ⇒ String?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The processor architecture Windows reports for this process.
PROCESSOR_ARCHITEW6432 is only set for a 32-bit process running under
WOW64, where it holds the machine's architecture while
PROCESSOR_ARCHITECTURE holds the emulated one -- so it wins when
present.
60 61 62 |
# File 'lib/kitchen/driver/helpers.rb', line 60 def os_architecture ENV["PROCESSOR_ARCHITEW6432"] || ENV["PROCESSOR_ARCHITECTURE"] end |
#powershell_64_bit ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Path to a PowerShell that matches the machine's architecture.
When Ruby and Windows agree on bitness, the real System32 PowerShell
is correct. When they disagree -- a 32-bit Ruby on 64-bit Windows --
System32 would be silently redirected by WOW64 to the 32-bit
PowerShell, which cannot see the Hyper-V cmdlets; Sysnative is the
alias that reaches the 64-bit one.
102 103 104 105 106 107 108 |
# File 'lib/kitchen/driver/helpers.rb', line 102 def powershell_64_bit if is_64bit? || is_32bit? 'c:\windows\system32\windowspowershell\v1.0\powershell.exe' else 'c:\windows\sysnative\windowspowershell\v1.0\powershell.exe' end end |
#ruby_64bit? ⇒ true, false
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Whether the running Ruby is a 64-bit build, determined from the size of a packed pointer.
69 70 71 |
# File 'lib/kitchen/driver/helpers.rb', line 69 def ruby_64bit? ["foo"].pack("p").size != 4 end |
#run_ps(cmd, options = {}) ⇒ Hash?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Convenience method to run a powershell command locally.
132 133 134 135 136 137 138 |
# File 'lib/kitchen/driver/helpers.rb', line 132 def run_ps(cmd, = {}) cmd = "echo #{cmd}" if config[:dry_run] debug("Preparing to run: ") debug(" #{cmd}") wrapped_command = wrap_command cmd execute_command wrapped_command, end |
#sanitize_stdout(stdout) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Strips the interactive prompt lines PowerShell interleaves with real output, which would otherwise make the result unparseable as JSON.
164 165 166 |
# File 'lib/kitchen/driver/helpers.rb', line 164 def sanitize_stdout(stdout) stdout.split("\n").select { |s| !s.start_with?("PS") }.join("\n") end |
#wrap_command(script) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Builds a full powershell.exe command line that dot-sources the gem's
support/hyperv.ps1 helper functions and then runs script.
116 117 118 119 120 121 122 123 |
# File 'lib/kitchen/driver/helpers.rb', line 116 def wrap_command(script) base_script_path = File.join(File.dirname(__FILE__), "/../../../support/hyperv.ps1") debug("Loading functions from #{base_script_path}") new_script = [ ". #{base_script_path}", "#{script}" ].join(";\n") debug("Wrapped script: #{new_script}") "#{powershell_64_bit} -noprofile -executionpolicy bypass" \ " -encodedcommand #{encode_command new_script} -outputformat Text" end |