fog-hyperv 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -2
  3. data/lib/fog/bin/hyperv.rb +6 -4
  4. data/lib/fog/hyperv/collection.rb +89 -0
  5. data/lib/fog/hyperv/compute/models/bios.rb +70 -0
  6. data/lib/fog/hyperv/compute/models/cluster.rb +60 -0
  7. data/lib/fog/hyperv/compute/models/clusters.rb +21 -0
  8. data/lib/fog/hyperv/compute/models/com_port.rb +78 -0
  9. data/lib/fog/hyperv/compute/models/com_ports.rb +29 -0
  10. data/lib/fog/hyperv/compute/models/dvd_drive.rb +126 -0
  11. data/lib/fog/hyperv/compute/models/dvd_drives.rb +29 -0
  12. data/lib/fog/hyperv/compute/models/firmware.rb +78 -0
  13. data/lib/fog/hyperv/compute/models/floppy_drive.rb +64 -0
  14. data/lib/fog/hyperv/compute/models/floppy_drives.rb +18 -0
  15. data/lib/fog/hyperv/compute/models/hard_drive.rb +187 -0
  16. data/lib/fog/hyperv/compute/models/hard_drives.rb +28 -0
  17. data/lib/fog/hyperv/compute/models/host.rb +66 -0
  18. data/lib/fog/hyperv/compute/models/hosts.rb +13 -0
  19. data/lib/fog/hyperv/compute/models/network_adapter.rb +362 -0
  20. data/lib/fog/hyperv/compute/models/network_adapter_vlan.rb +172 -0
  21. data/lib/fog/hyperv/compute/models/network_adapters.rb +32 -0
  22. data/lib/fog/hyperv/compute/models/security.rb +121 -0
  23. data/lib/fog/hyperv/compute/models/server.rb +466 -0
  24. data/lib/fog/hyperv/compute/models/servers.rb +18 -0
  25. data/lib/fog/hyperv/compute/models/switch.rb +117 -0
  26. data/lib/fog/hyperv/compute/models/switches.rb +20 -0
  27. data/lib/fog/hyperv/compute/models/vhd.rb +210 -0
  28. data/lib/fog/hyperv/compute/models/vhds.rb +28 -0
  29. data/lib/fog/hyperv/compute/requests/add_vm_dvd_drive.rb +15 -0
  30. data/lib/fog/hyperv/compute/requests/add_vm_hard_disk_drive.rb +15 -0
  31. data/lib/fog/hyperv/compute/requests/add_vm_network_adapter.rb +24 -0
  32. data/lib/fog/hyperv/compute/requests/connect_vm_network_adapter.rb +41 -0
  33. data/lib/fog/hyperv/compute/requests/disable_vm_tpm.rb +16 -0
  34. data/lib/fog/hyperv/compute/requests/disconnect_vm_network_adapter.rb +29 -0
  35. data/lib/fog/hyperv/compute/requests/enable_vm_tpm.rb +16 -0
  36. data/lib/fog/hyperv/compute/requests/get_cluster.rb +11 -0
  37. data/lib/fog/hyperv/compute/requests/get_cluster_node.rb +22 -0
  38. data/lib/fog/hyperv/compute/requests/get_vhd.rb +32 -0
  39. data/lib/fog/hyperv/compute/requests/get_vm.rb +18 -0
  40. data/lib/fog/hyperv/compute/requests/get_vm_bios.rb +21 -0
  41. data/lib/fog/hyperv/compute/requests/get_vm_com_port.rb +17 -0
  42. data/lib/fog/hyperv/compute/requests/get_vm_dvd_drive.rb +25 -0
  43. data/lib/fog/hyperv/compute/requests/get_vm_firmware.rb +21 -0
  44. data/lib/fog/hyperv/compute/requests/get_vm_floppy_disk_drive.rb +16 -0
  45. data/lib/fog/hyperv/compute/requests/get_vm_group.rb +20 -0
  46. data/lib/fog/hyperv/compute/requests/get_vm_hard_disk_drive.rb +24 -0
  47. data/lib/fog/hyperv/compute/requests/get_vm_host.rb +9 -0
  48. data/lib/fog/hyperv/compute/requests/get_vm_host_cluster.rb +21 -0
  49. data/lib/fog/hyperv/compute/requests/get_vm_host_sbt.rb +10 -0
  50. data/lib/fog/hyperv/compute/requests/get_vm_key_protector.rb +16 -0
  51. data/lib/fog/hyperv/compute/requests/get_vm_network_adapter.rb +41 -0
  52. data/lib/fog/hyperv/compute/requests/get_vm_network_adapter_vlan.rb +41 -0
  53. data/lib/fog/hyperv/compute/requests/get_vm_security.rb +15 -0
  54. data/lib/fog/hyperv/compute/requests/get_vm_switch.rb +10 -0
  55. data/lib/fog/hyperv/compute/requests/mock_files/get_vm.json +4 -0
  56. data/lib/fog/hyperv/compute/requests/new_vhd.rb +9 -0
  57. data/lib/fog/hyperv/compute/requests/new_vm.rb +12 -0
  58. data/lib/fog/hyperv/compute/requests/new_vm_switch.rb +11 -0
  59. data/lib/fog/hyperv/compute/requests/optimize_vhd.rb +9 -0
  60. data/lib/fog/hyperv/compute/requests/remove_item.rb +10 -0
  61. data/lib/fog/hyperv/compute/requests/remove_vm.rb +16 -0
  62. data/lib/fog/hyperv/compute/requests/remove_vm_dvd_drive.rb +17 -0
  63. data/lib/fog/hyperv/compute/requests/remove_vm_hard_disk_drive.rb +17 -0
  64. data/lib/fog/hyperv/compute/requests/remove_vm_network_adapter.rb +29 -0
  65. data/lib/fog/hyperv/compute/requests/remove_vm_switch.rb +9 -0
  66. data/lib/fog/hyperv/compute/requests/rename_vm.rb +16 -0
  67. data/lib/fog/hyperv/compute/requests/rename_vm_network_adapter.rb +25 -0
  68. data/lib/fog/hyperv/compute/requests/rename_vm_switch.rb +9 -0
  69. data/lib/fog/hyperv/compute/requests/resize_vhd.rb +9 -0
  70. data/lib/fog/hyperv/compute/requests/restart_vm.rb +22 -0
  71. data/lib/fog/hyperv/compute/requests/resume_vm.rb +22 -0
  72. data/lib/fog/hyperv/compute/requests/save_vm.rb +22 -0
  73. data/lib/fog/hyperv/compute/requests/set_vm.rb +15 -0
  74. data/lib/fog/hyperv/compute/requests/set_vm_bios.rb +15 -0
  75. data/lib/fog/hyperv/compute/requests/set_vm_com_port.rb +16 -0
  76. data/lib/fog/hyperv/compute/requests/set_vm_dvd_drive.rb +16 -0
  77. data/lib/fog/hyperv/compute/requests/set_vm_firmware.rb +15 -0
  78. data/lib/fog/hyperv/compute/requests/set_vm_floppy_disk_drive.rb +16 -0
  79. data/lib/fog/hyperv/compute/requests/set_vm_hard_disk_drive.rb +16 -0
  80. data/lib/fog/hyperv/compute/requests/set_vm_key_protector.rb +15 -0
  81. data/lib/fog/hyperv/compute/requests/set_vm_network_adapter.rb +25 -0
  82. data/lib/fog/hyperv/compute/requests/set_vm_network_adapter_vlan.rb +25 -0
  83. data/lib/fog/hyperv/compute/requests/set_vm_security.rb +17 -0
  84. data/lib/fog/hyperv/compute/requests/set_vm_switch.rb +9 -0
  85. data/lib/fog/hyperv/compute/requests/start_vm.rb +22 -0
  86. data/lib/fog/hyperv/compute/requests/stop_vm.rb +22 -0
  87. data/lib/fog/hyperv/compute/requests/suspend_vm.rb +22 -0
  88. data/lib/fog/hyperv/compute/requests/update_vm.rb +22 -0
  89. data/lib/fog/hyperv/compute.rb +206 -387
  90. data/lib/fog/hyperv/constants.rb +24 -0
  91. data/lib/fog/hyperv/fog_extensions/associations/collection.rb +11 -0
  92. data/lib/fog/hyperv/fog_extensions/attributes/datetime.rb +28 -0
  93. data/lib/fog/hyperv/fog_extensions/attributes/enum.rb +139 -0
  94. data/lib/fog/hyperv/fog_extensions/attributes/enumarray.rb +149 -0
  95. data/lib/fog/hyperv/fog_extensions/attributes/timespan.rb +27 -0
  96. data/lib/fog/hyperv/model.rb +142 -0
  97. data/lib/fog/hyperv/utils/powershell.rb +88 -0
  98. data/lib/fog/hyperv/utils/winrm.rb +233 -0
  99. data/lib/fog/hyperv/version.rb +4 -1
  100. data/lib/fog/hyperv.rb +51 -44
  101. metadata +187 -105
  102. data/.gitignore +0 -10
  103. data/.travis.yml +0 -11
  104. data/CHANGELOG.md +0 -52
  105. data/Gemfile +0 -4
  106. data/Rakefile +0 -10
  107. data/fog-hyperv.gemspec +0 -25
  108. data/lib/fog/collection.rb +0 -152
  109. data/lib/fog/hyperv/fog_extensions/enum.rb +0 -85
  110. data/lib/fog/hyperv/models/compute/bios.rb +0 -61
  111. data/lib/fog/hyperv/models/compute/cluster.rb +0 -64
  112. data/lib/fog/hyperv/models/compute/clusters.rb +0 -15
  113. data/lib/fog/hyperv/models/compute/com_port.rb +0 -22
  114. data/lib/fog/hyperv/models/compute/dvd_drive.rb +0 -92
  115. data/lib/fog/hyperv/models/compute/dvd_drives.rb +0 -12
  116. data/lib/fog/hyperv/models/compute/firmware.rb +0 -53
  117. data/lib/fog/hyperv/models/compute/floppy_drive.rb +0 -53
  118. data/lib/fog/hyperv/models/compute/floppy_drives.rb +0 -12
  119. data/lib/fog/hyperv/models/compute/hard_drive.rb +0 -110
  120. data/lib/fog/hyperv/models/compute/hard_drives.rb +0 -11
  121. data/lib/fog/hyperv/models/compute/host.rb +0 -45
  122. data/lib/fog/hyperv/models/compute/hosts.rb +0 -15
  123. data/lib/fog/hyperv/models/compute/network_adapter.rb +0 -145
  124. data/lib/fog/hyperv/models/compute/network_adapters.rb +0 -19
  125. data/lib/fog/hyperv/models/compute/server.rb +0 -220
  126. data/lib/fog/hyperv/models/compute/servers.rb +0 -21
  127. data/lib/fog/hyperv/models/compute/switch.rb +0 -65
  128. data/lib/fog/hyperv/models/compute/switches.rb +0 -15
  129. data/lib/fog/hyperv/models/compute/vhd.rb +0 -101
  130. data/lib/fog/hyperv/models/compute/vhds.rb +0 -16
  131. data/lib/fog/hyperv/requests/compute/add_vm_dvd_drive.rb +0 -12
  132. data/lib/fog/hyperv/requests/compute/add_vm_hard_disk_drive.rb +0 -12
  133. data/lib/fog/hyperv/requests/compute/add_vm_network_adapter.rb +0 -12
  134. data/lib/fog/hyperv/requests/compute/connect_vm_network_adapter.rb +0 -12
  135. data/lib/fog/hyperv/requests/compute/disconnect_vm_network_adapter.rb +0 -12
  136. data/lib/fog/hyperv/requests/compute/get_cluster.rb +0 -11
  137. data/lib/fog/hyperv/requests/compute/get_cluster_node.rb +0 -19
  138. data/lib/fog/hyperv/requests/compute/get_vhd.rb +0 -34
  139. data/lib/fog/hyperv/requests/compute/get_vm.rb +0 -20
  140. data/lib/fog/hyperv/requests/compute/get_vm_bios.rb +0 -21
  141. data/lib/fog/hyperv/requests/compute/get_vm_dvd_drive.rb +0 -20
  142. data/lib/fog/hyperv/requests/compute/get_vm_firmware.rb +0 -19
  143. data/lib/fog/hyperv/requests/compute/get_vm_floppy_disk_drive.rb +0 -20
  144. data/lib/fog/hyperv/requests/compute/get_vm_group.rb +0 -23
  145. data/lib/fog/hyperv/requests/compute/get_vm_hard_disk_drive.rb +0 -20
  146. data/lib/fog/hyperv/requests/compute/get_vm_host.rb +0 -12
  147. data/lib/fog/hyperv/requests/compute/get_vm_host_cluster.rb +0 -25
  148. data/lib/fog/hyperv/requests/compute/get_vm_network_adapter.rb +0 -27
  149. data/lib/fog/hyperv/requests/compute/get_vm_switch.rb +0 -27
  150. data/lib/fog/hyperv/requests/compute/mock_files/get_vm.json +0 -1
  151. data/lib/fog/hyperv/requests/compute/new_vhd.rb +0 -12
  152. data/lib/fog/hyperv/requests/compute/new_vm.rb +0 -15
  153. data/lib/fog/hyperv/requests/compute/new_vm_switch.rb +0 -13
  154. data/lib/fog/hyperv/requests/compute/remove_item.rb +0 -13
  155. data/lib/fog/hyperv/requests/compute/remove_vm.rb +0 -15
  156. data/lib/fog/hyperv/requests/compute/remove_vm_dvd_drive.rb +0 -12
  157. data/lib/fog/hyperv/requests/compute/remove_vm_hard_disk_drive.rb +0 -12
  158. data/lib/fog/hyperv/requests/compute/remove_vm_network_adapter.rb +0 -12
  159. data/lib/fog/hyperv/requests/compute/restart_vm.rb +0 -15
  160. data/lib/fog/hyperv/requests/compute/set_vm.rb +0 -12
  161. data/lib/fog/hyperv/requests/compute/set_vm_bios.rb +0 -13
  162. data/lib/fog/hyperv/requests/compute/set_vm_dvd_drive.rb +0 -12
  163. data/lib/fog/hyperv/requests/compute/set_vm_firmware.rb +0 -13
  164. data/lib/fog/hyperv/requests/compute/set_vm_hard_disk_drive.rb +0 -12
  165. data/lib/fog/hyperv/requests/compute/set_vm_network_adapter.rb +0 -12
  166. data/lib/fog/hyperv/requests/compute/set_vm_network_adapter_vlan.rb +0 -12
  167. data/lib/fog/hyperv/requests/compute/set_vm_switch.rb +0 -13
  168. data/lib/fog/hyperv/requests/compute/start_vm.rb +0 -15
  169. data/lib/fog/hyperv/requests/compute/stop_vm.rb +0 -15
  170. data/lib/fog/model.rb +0 -91
  171. data/test/fog/hyperv_test.rb +0 -7
  172. data/test/test_helper.rb +0 -4
  173. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_cluster.json +0 -0
  174. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_cluster_node.json +0 -0
  175. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vhd.json +0 -0
  176. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_bios.json +0 -0
  177. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_dvd_drive.json +0 -0
  178. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_firmware.json +0 -0
  179. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_floppy_disk_drive.json +0 -0
  180. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_hard_disk_drive.json +0 -0
  181. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_host.json +0 -0
  182. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_network_adapter.json +0 -0
  183. /data/lib/fog/hyperv/{requests/compute → compute/requests}/mock_files/get_vm_switch.json +0 -0
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/ModuleLength
4
+
5
+ # WinRM helper methods
6
+ module Fog::Hyperv::Utils::Winrm
7
+ # Struct emulating a WinRM shell return
8
+ LocalExecOutput = Struct.new(:stdout, :stderr, :exitcode)
9
+
10
+ # Check if the PowerShell version is newer than the value specified
11
+ # @param version [String] a semver version to check if powershell matches
12
+ # @returns [Boolean] is the running PowerShell newer than the given version
13
+ def ps_version?(version)
14
+ Gem::Version(version) >= Gem::Version("#{ps_version[:major]}.#{ps_version[:minor]}")
15
+ end
16
+
17
+ # Run a command on the Hyper-V system
18
+ # @param cmd [String] the PowerShell command to execute
19
+ # @param _skip_json [Boolean] should the return data be returned as-is, instead of being sent as JSON
20
+ # @param _target_computer [String,nil] the computer to execute the command on, in case of clustering
21
+ # @param options [Hash] the options to call the command with
22
+ # @option options [Integer] _json_depth (1) the depth to limit the JSON object to on return
23
+ # @return [Hash] the returned object from PowerShell
24
+ def run_cmd(cmd, _skip_json: false, _target_computer: nil, **options)
25
+ _json_depth = options.delete(:_json_depth) { 1 }
26
+
27
+ run_cmdlist([[cmd, options.dup]], skip_json: _skip_json, json_depth: _json_depth, target_computer: _target_computer)
28
+ end
29
+
30
+ # Run a list of commands on the Hyper-V system
31
+ # @param commands [Array<Array(String,Hash)>] a list of commands with their arguments to run
32
+ # @param skip_json [Boolean] should the return value be given as-is, instead of being sent as JSON
33
+ # @param target_computer [String,nil] the computer to execute the command on, in case of clustering
34
+ # @param separate_calls [Boolean] should the commands be run separately
35
+ # @param options [Hash] additional options for the call
36
+ # @option options [Integer] json_depth (1) the depth to limit the JSON object to on return
37
+ def run_cmdlist(commands, skip_json: false, target_computer: nil, separate_calls: false, **options)
38
+ target_computer = [target_computer].flatten.compact
39
+ target_computer << '.' if target_computer.empty?
40
+
41
+ json_depth = options.delete(:json_depth) { 1 }
42
+
43
+ Fog::Logger.debug "run_cmdlist given unknown meta-arguments: #{options.keys.join ', '}" if options.any?
44
+
45
+ out = nil
46
+ target_computer.each do |computer|
47
+ connection(computer).shell(:powershell) do |shell|
48
+ # Avoid confirmation questions, abort early on errors
49
+ setup = [
50
+ "$ConfirmPreference = 'None'",
51
+ "$ErrorActionPreference = 'Stop'",
52
+ '$PSNativeCommandUseErrorActionPreference = $true'
53
+ ].join('; ')
54
+ shell.run setup
55
+
56
+ pscalls = commands.map.with_index do |(command, args), idx|
57
+ last = idx == commands.size - 1
58
+ build_pscall(command, _to_json: last && !skip_json, _json_depth: json_depth, **args)
59
+ end
60
+ pscalls = [pscalls.join("\n")] unless separate_calls
61
+
62
+ pscalls.each do |cmd|
63
+ Fog::Logger.debug "PS; >>> \"#{cmd.split("\n").join("\n\t")}\""
64
+ out = shell.run cmd
65
+ Fog::Logger.debug "PS; <<< OUT=[#{out.stdout.inspect}] ERR=[#{out.stderr.inspect}] EXIT=[#{out.exitcode}]"
66
+
67
+ is_success = true
68
+ is_success = shell.run('$?').stdout.strip.downcase == 'true' if out.stderr.include? 'FullyQualifiedErrorId'
69
+
70
+ raise Fog::Hyperv::Errors::PSError.new(out, "When executing #{cmd}") if
71
+ out.exitcode != 0 || !is_success
72
+ end
73
+ end
74
+ end
75
+
76
+ return out if skip_json
77
+ return nil if out.stdout.strip.empty?
78
+
79
+ json = Fog::JSON.decode(out.stdout)
80
+ Fog::Hyperv.uncamelize(json)
81
+ end
82
+
83
+ # Perform a WQL query against the Hyper-V system
84
+ # @param query [String] the query to perform
85
+ # @param _namespace [String] the namespace to perform the call in
86
+ # @param where [Hash] the WHERE arguments to add to the query
87
+ # @return [Hash] the return hash from the WQL query
88
+ def run_wql(query, _namespace: 'root/virtualization/v2', **where)
89
+ args = Fog::Hyperv.camelize(where).reject { |k, v| v.nil? || v.is_a?(FalseClass) || k.to_s.start_with?('_') }.map do |k, v|
90
+ "#{k} = #{((v.is_a?(String) || v.to_s =~ /\s/) && v.inspect) || v}"
91
+ end
92
+
93
+ query = "#{query}#{" WHERE #{args.join ' AND '}" unless args.none?}"
94
+
95
+ Fog::Logger.debug "WQL; in #{_namespace} >>> #{query}"
96
+ data =
97
+ if local?
98
+ raise NotImplementedError, 'Not implemented for local connection'
99
+ # run_cmd('Get-WmiObject', query:, namespace:, _return_fields: options.keys)
100
+ else
101
+ @connection.run_wql(query, "#{_namespace}/*")[:xml_fragment].first
102
+ end
103
+ Fog::Logger.debug "WQL; <<< #{data}"
104
+
105
+ data
106
+ end
107
+
108
+ private
109
+
110
+ def version
111
+ @version ||= begin
112
+ run_wql('SELECT Version FROM Win32_OperatingSystem', _namespace: 'root/cimv2')[:version]
113
+ rescue StandardError
114
+ run_cmd(
115
+ <<~CMD,
116
+ $VMMS = if ([environment]::Is64BitProcess) {
117
+ "$($env:SystemRoot)\\System32\\vmms.exe"
118
+ } else {
119
+ "$($env:SystemRoot)\\Sysnative\\vmms.exe"
120
+ }
121
+ (Get-Item $VMMS).VersionInfo.ProductVersion"
122
+ CMD
123
+ _skip_json: true,
124
+ _ps_version: 1
125
+ ).stdout.strip
126
+ end
127
+ end
128
+
129
+ def ps_version
130
+ @ps_version ||= run_cmd('$PSVersionTable.PSVersion', _ps_version: 1)
131
+ end
132
+
133
+ def build_pscall(command, _by_id: nil, _return_fields: nil, _always_include: [], **options)
134
+ _ps_version = options.delete(:_ps_version) { ps_version[:major] }
135
+ _to_json = options.delete(:_to_json) { true }
136
+ _json_depth = options.delete(:_json_depth) { 1 }
137
+
138
+ pipeline = []
139
+ pipeline << "Where-Object {$_.id -eq '#{_by_id}'}" if _by_id
140
+ pipeline << "Select #{Fog::Hyperv.camelize([_return_fields].flatten).join ','}" if _return_fields
141
+ pipeline << "ConvertTo-Json -Compress#{" -Depth #{_json_depth}" if _json_depth}" if _to_json
142
+
143
+ invalid_opts = options.select { |k, _| k.to_s.start_with? '_' }
144
+ Fog::Logger.debug "build_pscall given unexpected meta-arguments: #{invalid_opts.keys.join ', '}" if invalid_opts.any?
145
+
146
+ options.reject! { |k, _| k.to_s.start_with?('_') }
147
+ options.reject! do |k, v|
148
+ !_always_include.include?(k) && (v.nil? || v.is_a?(FalseClass) || k.to_s.start_with?('_') || (v.is_a?(String) && v.empty?))
149
+ end
150
+ options = Fog::Hyperv.camelize(options.compact)
151
+
152
+ command += ' @Args' unless command.include? '@Args'
153
+ pipeline = [command] + pipeline
154
+ Fog::Hyperv::Utils::Powershell.build_call(pipeline.join(' | '), options, _ps_version:)
155
+ end
156
+
157
+ def connection(host)
158
+ existing = @connections.find do |c_host, c_connection|
159
+ c_host.downcase.start_with?(host.downcase) ||
160
+ URI(c_connection.instance_variable_get(:@connection_opts)[:endpoint]).host.downcase.start_with?(host.downcase)
161
+ end
162
+
163
+ if existing
164
+ @connections[host] = existing unless @connections.key? host
165
+ return existing.last
166
+ end
167
+
168
+ if %w[. localhost].include? host
169
+ endpoint = @hyperv_endpoint
170
+ else
171
+ # TODO: Support non-standard endpoints for additional hosts
172
+ unless host.include? '.'
173
+ host = run_cmd("[System.Net.Dns]::GetHostByName(#{host.inspect})", _return_fields: :host_name, _ps_version: 1)[:host_name]
174
+ end
175
+ endpoint = "http://#{host}:5985/wsman"
176
+ end
177
+
178
+ existing = @connections.find do |_, c_connection|
179
+ c_connection.instance_variable_get(:@connection_opts)[:endpoint] == endpoint
180
+ end
181
+
182
+ if existing
183
+ @connections[host] = existing unless @connections.key? host
184
+ return existing.last
185
+ end
186
+
187
+ connect(endpoint)
188
+ end
189
+
190
+ def connect(endpoint = nil)
191
+ endpoint ||= @hyperv_endpoint
192
+ fqdn = URI.parse(endpoint).host
193
+
194
+ require 'winrm'
195
+
196
+ opts = {
197
+ endpoint: endpoint,
198
+ transport: @hyperv_transport,
199
+ user: @hyperv_username,
200
+ password: @hyperv_password,
201
+ realm: @hyperv_realm,
202
+ no_ssl_peer_verification: true
203
+ }
204
+
205
+ Fog::Logger.debug "Creating WinRM connection with #{opts.merge password: '<REDACTED>'}"
206
+ connection = WinRM::Connection.new opts
207
+ connection.logger.level = :error
208
+ @connections[fqdn] = connection
209
+
210
+ # Add the local host's names to the connection
211
+ begin
212
+ hostname = run_cmd('$env:computerName', _target_computer: fqdn, _skip_json: true, _ps_version: 1).stdout.downcase.strip
213
+ @connections[hostname] ||= connection
214
+ fqdn = run_cmd(
215
+ '[System.Net.Dns]::GetHostByName(($env:computerName)).Hostname',
216
+ _target_computer: fqdn,
217
+ _skip_json: true,
218
+ _ps_version: 1
219
+ ).stdout.downcase.strip
220
+ @connections[fqdn] ||= connection
221
+ end
222
+
223
+ if endpoint == @hyperv_endpoint
224
+ @connection = connection
225
+ @connections['.'] = connection
226
+ @connections['localhost'] = connection
227
+ @local_hostname = hostname
228
+ end
229
+
230
+ connection
231
+ end
232
+ end
233
+ # rubocop:enable Metrics/ModuleLength
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fog
2
4
  module Hyperv
3
- VERSION = '0.0.9'.freeze
5
+ # The Fog Hyper-V version
6
+ VERSION = '0.1.0'
4
7
  end
5
8
  end
data/lib/fog/hyperv.rb CHANGED
@@ -1,19 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fog/core'
2
4
 
3
5
  module Fog
4
6
  module Attributes
5
- autoload :Enum, File.expand_path('../hyperv/fog_extensions/enum.rb', __FILE__)
6
- end
7
-
8
- module Compute
9
- autoload :Hyperv, File.expand_path('../hyperv/compute', __FILE__)
7
+ autoload :Hypervdatetime, File.expand_path('hyperv/fog_extensions/attributes/datetime.rb', __dir__)
8
+ autoload :Hypervenum, File.expand_path('hyperv/fog_extensions/attributes/enum.rb', __dir__)
9
+ autoload :Hypervenumarray, File.expand_path('hyperv/fog_extensions/attributes/enumarray.rb', __dir__)
10
+ autoload :Hypervtimespan, File.expand_path('hyperv/fog_extensions/attributes/timespan.rb', __dir__)
10
11
  end
11
12
 
12
13
  module Hyperv
14
+ require 'fog/hyperv/constants'
15
+
13
16
  extend Fog::Provider
14
17
 
18
+ autoload :Compute, File.expand_path('hyperv/compute', __dir__)
19
+
15
20
  module Errors
21
+ # A general service error occurred
16
22
  class ServiceError < Fog::Errors::Error; end
23
+
24
+ # A version constrain was not matched
17
25
  class VersionError < ServiceError
18
26
  attr_reader :version, :required_version, :function
19
27
 
@@ -22,10 +30,11 @@ module Fog
22
30
  @required_version = required_version
23
31
  @version = version
24
32
 
25
- super "#{function} requires at least Hyper-V v#{required_version}, you have v#{version}"
33
+ super("#{function} requires at least Hyper-V v#{required_version}, you have v#{version}")
26
34
  end
27
35
  end
28
36
 
37
+ # A powershell call failed
29
38
  class PSError < ServiceError
30
39
  attr_reader :stdout, :stderr, :exitcode, :info, :message
31
40
 
@@ -34,51 +43,48 @@ module Fog
34
43
  @stderr = output.stderr
35
44
  @exitcode = output.exitcode
36
45
  @info = info
37
- @message = @stderr.split("\n").first
38
- super @message
39
- end
46
+ extract_message
40
47
 
41
- def to_s
42
- ret = [super]
43
- ret << info unless info.nil? || info.empty?
44
- ret.join "\n"
48
+ super(@message)
45
49
  end
46
- end
47
- end
48
50
 
49
- autoload :Collection, File.expand_path('../collection', __FILE__)
50
- autoload :Model, File.expand_path('../model', __FILE__)
51
- autoload :ModelExtends, File.expand_path('../model', __FILE__)
52
- autoload :ModelIncludes, File.expand_path('../model', __FILE__)
53
- autoload :VMCollection, File.expand_path('../collection', __FILE__)
51
+ # def to_s
52
+ # ret = [super]
53
+ # ret << info unless info.nil? || info.empty?
54
+ # ret.join "\n"
55
+ # end
54
56
 
55
- service(:compute, 'Compute')
57
+ private
56
58
 
57
- def self.shell_quoted(data, always = false)
58
- case data
59
- when String
60
- if !data.start_with?('$') && (data =~ /(^$)|\s/ || always)
61
- data.gsub(/`/, '``')
62
- .gsub(/\0/, '`0')
63
- .gsub(/\n/, '`n')
64
- .gsub(/\r/, '`r')
65
- .inspect
66
- .gsub(/\\"/, '`"')
67
- .gsub(/\\\\/, '\\')
68
- else
69
- data
59
+ def extract_message
60
+ stderr = @stderr.split("\n").map(&:strip)
61
+
62
+ # Find a line ending in an error ID
63
+ @message = stderr.find { |line| line =~ /\(0x[0-9a-f]+\)\.?$/}
64
+ # Find the last line that isn't include error class information
65
+ @message ||= stderr.take_while { |line| !line.start_with? '+' }.last
70
66
  end
71
- when Array
72
- '@(' + data.map { |e| shell_quoted(e, true) }.join(', ') + ')'
73
- when FalseClass
74
- '$false'
75
- when TrueClass
76
- '$true'
77
- else
78
- shell_quoted data.to_s
79
67
  end
80
68
  end
81
69
 
70
+ module Associations
71
+ autoload :Collection, File.expand_path('hyperv/fog_extensions/associations/collection', __dir__)
72
+ end
73
+
74
+ module Utils
75
+ autoload :Powershell, File.expand_path('hyperv/utils/powershell', __dir__)
76
+ autoload :Winrm, File.expand_path('hyperv/utils/winrm', __dir__)
77
+ end
78
+
79
+ autoload :Collection, File.expand_path('hyperv/collection', __dir__)
80
+ autoload :Model, File.expand_path('hyperv/model', __dir__)
81
+ autoload :ModelExtends, File.expand_path('hyperv/model', __dir__)
82
+ autoload :ModelIncludes, File.expand_path('hyperv/model', __dir__)
83
+ autoload :VMCollection, File.expand_path('hyperv/collection', __dir__)
84
+
85
+ service(:compute, 'Compute')
86
+
87
+ # Convert a piece of data from being snake_case to being CamelCase
82
88
  def self.camelize(data)
83
89
  case data
84
90
  when Array
@@ -86,7 +92,7 @@ module Fog
86
92
  when Hash
87
93
  data.each_with_object({}) do |(k, v), hash|
88
94
  value = v
89
- value = camelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all? { |h| h.is_a?(Hash) })
95
+ value = camelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all?(Hash))
90
96
  hash[camelize(k)] = value
91
97
  end
92
98
  when Symbol
@@ -98,6 +104,7 @@ module Fog
98
104
  end
99
105
  end
100
106
 
107
+ # Convert a piece of data from being CamelCase to being snake_case
101
108
  def self.uncamelize(data)
102
109
  case data
103
110
  when Array
@@ -105,7 +112,7 @@ module Fog
105
112
  when Hash
106
113
  data.each_with_object({}) do |(k, v), hash|
107
114
  value = v
108
- value = uncamelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all? { |h| h.is_a?(Hash) })
115
+ value = uncamelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all?(Hash))
109
116
  hash[uncamelize(k)] = value
110
117
  end
111
118
  when Symbol