fog-hyperv 0.0.1

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 (85) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +4 -0
  5. data/README.md +53 -0
  6. data/Rakefile +10 -0
  7. data/fog-hyperv.gemspec +28 -0
  8. data/lib/fog/bin/hyperv.rb +28 -0
  9. data/lib/fog/collection.rb +114 -0
  10. data/lib/fog/hyperv.rb +123 -0
  11. data/lib/fog/hyperv/compute.rb +327 -0
  12. data/lib/fog/hyperv/fog_extensions/enum.rb +51 -0
  13. data/lib/fog/hyperv/models/compute/bios.rb +52 -0
  14. data/lib/fog/hyperv/models/compute/cluster.rb +37 -0
  15. data/lib/fog/hyperv/models/compute/clusters.rb +11 -0
  16. data/lib/fog/hyperv/models/compute/com_port.rb +22 -0
  17. data/lib/fog/hyperv/models/compute/dvd_drive.rb +62 -0
  18. data/lib/fog/hyperv/models/compute/dvd_drives.rb +11 -0
  19. data/lib/fog/hyperv/models/compute/firmware.rb +52 -0
  20. data/lib/fog/hyperv/models/compute/floppy_drive.rb +48 -0
  21. data/lib/fog/hyperv/models/compute/floppy_drives.rb +11 -0
  22. data/lib/fog/hyperv/models/compute/hard_drive.rb +110 -0
  23. data/lib/fog/hyperv/models/compute/hard_drives.rb +11 -0
  24. data/lib/fog/hyperv/models/compute/host.rb +20 -0
  25. data/lib/fog/hyperv/models/compute/hosts.rb +15 -0
  26. data/lib/fog/hyperv/models/compute/network_adapter.rb +142 -0
  27. data/lib/fog/hyperv/models/compute/network_adapters.rb +19 -0
  28. data/lib/fog/hyperv/models/compute/server.rb +192 -0
  29. data/lib/fog/hyperv/models/compute/servers.rb +30 -0
  30. data/lib/fog/hyperv/models/compute/switch.rb +58 -0
  31. data/lib/fog/hyperv/models/compute/switches.rb +15 -0
  32. data/lib/fog/hyperv/models/compute/vhd.rb +100 -0
  33. data/lib/fog/hyperv/models/compute/vhds.rb +16 -0
  34. data/lib/fog/hyperv/requests/compute/add_vm_hard_disk_drive.rb +12 -0
  35. data/lib/fog/hyperv/requests/compute/add_vm_network_adapter.rb +12 -0
  36. data/lib/fog/hyperv/requests/compute/connect_vm_network_adapter.rb +12 -0
  37. data/lib/fog/hyperv/requests/compute/disconnect_vm_network_adapter.rb +12 -0
  38. data/lib/fog/hyperv/requests/compute/get_cluster.rb +11 -0
  39. data/lib/fog/hyperv/requests/compute/get_cluster_node.rb +19 -0
  40. data/lib/fog/hyperv/requests/compute/get_vhd.rb +34 -0
  41. data/lib/fog/hyperv/requests/compute/get_vm.rb +20 -0
  42. data/lib/fog/hyperv/requests/compute/get_vm_bios.rb +21 -0
  43. data/lib/fog/hyperv/requests/compute/get_vm_dvd_drive.rb +20 -0
  44. data/lib/fog/hyperv/requests/compute/get_vm_firmware.rb +21 -0
  45. data/lib/fog/hyperv/requests/compute/get_vm_floppy_disk_drive.rb +20 -0
  46. data/lib/fog/hyperv/requests/compute/get_vm_group.rb +23 -0
  47. data/lib/fog/hyperv/requests/compute/get_vm_hard_disk_drive.rb +20 -0
  48. data/lib/fog/hyperv/requests/compute/get_vm_host.rb +12 -0
  49. data/lib/fog/hyperv/requests/compute/get_vm_host_cluster.rb +25 -0
  50. data/lib/fog/hyperv/requests/compute/get_vm_network_adapter.rb +27 -0
  51. data/lib/fog/hyperv/requests/compute/get_vm_switch.rb +27 -0
  52. data/lib/fog/hyperv/requests/compute/mock_files/get_cluster.json +1 -0
  53. data/lib/fog/hyperv/requests/compute/mock_files/get_cluster_node.json +1 -0
  54. data/lib/fog/hyperv/requests/compute/mock_files/get_vhd.json +1 -0
  55. data/lib/fog/hyperv/requests/compute/mock_files/get_vm.json +1 -0
  56. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_bios.json +1 -0
  57. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_dvd_drive.json +1 -0
  58. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_firmware.json +1 -0
  59. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_floppy_disk_drive.json +1 -0
  60. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_hard_disk_drive.json +1 -0
  61. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_host.json +1 -0
  62. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_network_adapter.json +1 -0
  63. data/lib/fog/hyperv/requests/compute/mock_files/get_vm_switch.json +1 -0
  64. data/lib/fog/hyperv/requests/compute/new_vhd.rb +12 -0
  65. data/lib/fog/hyperv/requests/compute/new_vm.rb +15 -0
  66. data/lib/fog/hyperv/requests/compute/new_vm_switch.rb +13 -0
  67. data/lib/fog/hyperv/requests/compute/remove_item.rb +13 -0
  68. data/lib/fog/hyperv/requests/compute/remove_vm.rb +15 -0
  69. data/lib/fog/hyperv/requests/compute/remove_vm_hard_disk_drive.rb +12 -0
  70. data/lib/fog/hyperv/requests/compute/remove_vm_network_adapter.rb +12 -0
  71. data/lib/fog/hyperv/requests/compute/restart_vm.rb +15 -0
  72. data/lib/fog/hyperv/requests/compute/set_vm.rb +12 -0
  73. data/lib/fog/hyperv/requests/compute/set_vm_bios.rb +13 -0
  74. data/lib/fog/hyperv/requests/compute/set_vm_dvd_drive.rb +12 -0
  75. data/lib/fog/hyperv/requests/compute/set_vm_firmware.rb +13 -0
  76. data/lib/fog/hyperv/requests/compute/set_vm_hard_disk_drive.rb +12 -0
  77. data/lib/fog/hyperv/requests/compute/set_vm_network_adapter.rb +12 -0
  78. data/lib/fog/hyperv/requests/compute/set_vm_switch.rb +13 -0
  79. data/lib/fog/hyperv/requests/compute/start_vm.rb +15 -0
  80. data/lib/fog/hyperv/requests/compute/stop_vm.rb +15 -0
  81. data/lib/fog/hyperv/version.rb +5 -0
  82. data/lib/fog/model.rb +64 -0
  83. data/test/fog/hyperv_test.rb +7 -0
  84. data/test/test_helper.rb +4 -0
  85. metadata +199 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f4fc7f6bb6e4beb182866dc8c274d15c25dbbdd
4
+ data.tar.gz: da491628457575effecb17779afa392f0d02276b
5
+ SHA512:
6
+ metadata.gz: 9bcc6fa50ef5d9d3ab0eef2fdb56937090186df326016c93e36d6a606defb257d52f3e66df9ded5512eddcebd996cf118fd84fb50437ea084dde3ec91b1270c7
7
+ data.tar.gz: 003df173d2ab645056e6eeac5feea8b6cc987fabd1b80d65867684f395f84f36799e645da1b76c8286c7065510cdaae6b5c6aba99c7d45721544e72656deceeb
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ ---
2
+ before_install: gem install bundler
3
+ language: ruby
4
+ notifications:
5
+ email: false
6
+ rvm:
7
+ - 2.0.0
8
+ - 2.1.9
9
+ - 2.2.6
10
+ - 2.4.0
11
+ sudo: false
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fog-hyperv.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Fog Hyper-V
2
+
3
+ [![Build Status](https://travis-ci.org/ace13/fog-hyperv.svg?branch=master)](https://travis-ci.org/ace13/fog-hyperv)
4
+
5
+ Manage your Hyper-V instance with the help of the Fog cloud service abstractions.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'fog-hyperv'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install fog-hyperv
22
+
23
+ ## Usage
24
+
25
+ To remotely manage your Hyper-V instance;
26
+
27
+ ```ruby
28
+ require 'fog/hyperv'
29
+
30
+ compute = Fog::Compute.new(
31
+ provider: :hyperv,
32
+ hyperv_host: 'hyperv.example.com',
33
+ hyperv_username: 'domain\\user',
34
+ hyperv_password: 'password'
35
+ )
36
+
37
+ compute.servers.all
38
+ #=> [<Fog::Compute::Hyperv::Server
39
+ #=> id='',
40
+ #=> name='example',
41
+ #=> computer_name='HYPERV',
42
+ #=> dynamic_memory_enabled=false,
43
+ #=> ...
44
+ ```
45
+
46
+ ## Development
47
+
48
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests. You can also run `bundle exec irb` for an interactive prompt that will allow you to experiment.
49
+
50
+ ## Contributing
51
+
52
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ace13/fog-hyperv.
53
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fog/hyperv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'fog-hyperv'
8
+ spec.version = Fog::Hyperv::VERSION
9
+ spec.authors = ['Alexander Olofsson']
10
+ spec.email = ['alexander.olofsson@liu.se']
11
+
12
+ spec.summary = 'Module for the `fog` gem to support Microsoft Hyper-V.'
13
+ spec.description = 'This library wraps Hyper-V in the `fog` concepts.'
14
+ spec.homepage = 'https://github.com/ace13/fog-hyperv'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^test\/})
19
+
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_runtime_dependency 'fog-core', '~> 1.44'
23
+ spec.add_runtime_dependency 'fog-json', '~> 1.0'
24
+ spec.add_runtime_dependency 'winrm', '~> 2.2'
25
+
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'minitest', '~> 5.0'
28
+ end
@@ -0,0 +1,28 @@
1
+ class Hyperv < Fog::Bin
2
+ class << self
3
+ def class_for(key)
4
+ case key
5
+ when :compute
6
+ Fog::Compute::Hyperv
7
+ else
8
+ raise ArgumentError, "Unsupported #{self} service: #{key}"
9
+ end
10
+ end
11
+
12
+ def [](service)
13
+ @@connections ||= Hash.new do |h, k|
14
+ h[k] = case key
15
+ when :compute
16
+ Fog::Compute.new(provider: 'Hyperv')
17
+ else
18
+ raise ArgumentError, "Unrecognized service: #{key.inspect}"
19
+ end
20
+ end
21
+ @@connections[service]
22
+ end
23
+
24
+ def services
25
+ Fog::Hyperv.services
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,114 @@
1
+ require 'fog/core/collection'
2
+
3
+ module Fog
4
+ module Hyperv
5
+ class Collection < Fog::Collection
6
+ def self.get_method(method = nil)
7
+ @get_method ||= method
8
+ end
9
+
10
+ def self.requires?
11
+ @requires ||= []
12
+ end
13
+
14
+ def search_attributes
15
+ attributes.dup.merge(
16
+ _return_fields: model.attributes - model.lazy_attributes,
17
+ _json_depth: 1
18
+ )
19
+ end
20
+
21
+ def all(filters = {})
22
+ requires(*self.class.requires?)
23
+ data = service.send(method, search_attributes.merge(filters))
24
+ data = [] unless data
25
+
26
+ load [data].flatten
27
+ end
28
+
29
+ def get(filters = {})
30
+ data = self.all(filters).first
31
+ data if data
32
+ rescue Fog::Hyperv::Errors::PSError => err
33
+ raise Fog::Errors::NotFound, err if err.message =~ /Hyper-V was unable to find|^No .* is found|/
34
+ raise err
35
+ end
36
+
37
+ def new(options = {})
38
+ requires(*self.class.requires?)
39
+ super(search_attributes.merge(options))
40
+ end
41
+
42
+ def create(attributes = {})
43
+ object = new(attributes)
44
+ object.save
45
+ object
46
+ end
47
+
48
+ private
49
+
50
+ def method
51
+ self.class.get_method
52
+ end
53
+ end
54
+
55
+ class ComputerCollection < Fog::Hyperv::Collection
56
+ def self.requires_computer
57
+ requires? << :computer
58
+ end
59
+
60
+ attr_accessor :computer
61
+
62
+ def search_attributes
63
+ attrs = super
64
+ attrs[:computer_name] ||= computer.name if computer
65
+ attrs
66
+ end
67
+ end
68
+
69
+ class VMCollection < Fog::Hyperv::ComputerCollection
70
+ def self.match_on(attr = nil)
71
+ @match_on ||= attr
72
+ end
73
+
74
+ def self.requires_vm
75
+ requires? << :vm
76
+ end
77
+
78
+ attr_accessor :vm
79
+
80
+ def search_attributes
81
+ attrs = super
82
+ if vm
83
+ attrs[:computer_name] ||= vm.computer_name
84
+ attrs[match] = vm.send(match)
85
+ end
86
+ attrs
87
+ end
88
+
89
+ def create(attributes = {})
90
+ object = new(attributes)
91
+ # Ensure both ID and Name are populated, regardless of `match_on`
92
+ object.vm_id ||= vm.id if vm && object.respond_to?(:vm_id)
93
+ object.vm_name ||= vm.name if vm && object.respond_to?(:vm_name)
94
+ object.save
95
+ object
96
+ end
97
+
98
+ def inspect
99
+ # To avoid recursing on VM
100
+ to_s
101
+ end
102
+
103
+ private
104
+
105
+ def logger
106
+ service.logger
107
+ end
108
+
109
+ def match
110
+ self.class.match_on || :vm_name
111
+ end
112
+ end
113
+ end
114
+ end
data/lib/fog/hyperv.rb ADDED
@@ -0,0 +1,123 @@
1
+ require 'fog/core'
2
+
3
+ module Fog
4
+ 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__)
10
+ end
11
+
12
+ module Hyperv
13
+ extend Fog::Provider
14
+
15
+ module Errors
16
+ class ServiceError < Fog::Errors::Error; end
17
+ class VersionError < ServiceError
18
+ attr_reader :version, :required_version, :function
19
+
20
+ def initialize(required_version, version, function)
21
+ @function = function
22
+ @required_version = required_version
23
+ @version = version
24
+
25
+ super "#{function} requires at least Hyper-V v#{required_version}, you have v#{version}"
26
+ end
27
+ end
28
+
29
+ class PSError < ServiceError
30
+ attr_reader :stdout, :stderr, :exitcode, :info, :message
31
+
32
+ def initialize(output, info)
33
+ @stdout = output.stdout
34
+ @stderr = output.stderr
35
+ @exitcode = output.exitcode
36
+ @info = info
37
+ @message = @stderr.split("\n").first
38
+ super @message
39
+ end
40
+
41
+ def to_s
42
+ ret = [super]
43
+ ret << info unless info.nil? || info.empty?
44
+ ret.join "\n"
45
+ end
46
+ end
47
+ end
48
+
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__)
54
+
55
+ service(:compute, 'Compute')
56
+
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
+ else
68
+ data
69
+ end
70
+ when Array
71
+ '@(' + data.map { |e| shell_quoted(e, true) }.join(', ') + ')'
72
+ when FalseClass
73
+ '$false'
74
+ when TrueClass
75
+ '$true'
76
+ else
77
+ shell_quoted data.to_s
78
+ end
79
+ end
80
+
81
+ def self.camelize(data)
82
+ case data
83
+ when Array
84
+ data.collect { |d| camelize(d) }
85
+ when Hash
86
+ data.each_with_object({}) do |(k, v), hash|
87
+ value = v
88
+ value = camelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all? { |h| h.is_a?(Hash) })
89
+ hash[camelize(k)] = value
90
+ end
91
+ when Symbol
92
+ camelize(data.to_s).to_sym
93
+ when String
94
+ data.split('_').collect(&:capitalize).join
95
+ else
96
+ data
97
+ end
98
+ end
99
+
100
+ def self.uncamelize(data)
101
+ case data
102
+ when Array
103
+ data.collect { |d| uncamelize(d) }
104
+ when Hash
105
+ data.each_with_object({}) do |(k, v), hash|
106
+ value = v
107
+ value = uncamelize(v) if v.is_a?(Hash) || (v.is_a?(Array) && v.all? { |h| h.is_a?(Hash) })
108
+ hash[uncamelize(k)] = value
109
+ end
110
+ when Symbol
111
+ uncamelize(data.to_s).to_sym
112
+ when String
113
+ data.to_s
114
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
115
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
116
+ .tr('-', '_')
117
+ .downcase.to_sym
118
+ else
119
+ data
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,327 @@
1
+ module Fog
2
+ module Compute
3
+ class Hyperv < Fog::Service
4
+ STATUS_ENUM_VALUES = [
5
+ :Unknown, # 0
6
+ :Other, # 1
7
+ :Ok, # 2
8
+ :Degraded, # 3
9
+ :Stressed, # 4
10
+ :PredictiveFailure, # 5
11
+ :Error, # 6
12
+ :NonRecoverableError, # 7
13
+ :Starting, # 8
14
+ :Stopping, # 9
15
+ :Stopped, # 10
16
+ :InService, # 11
17
+ :NoContact, # 12
18
+ :LostCommunication, # 13
19
+ :Aborted, # 14
20
+ :Dormant, # 15
21
+ :SupportingEntity, # 16
22
+ :Completed, # 17
23
+ :PowerMode # 18
24
+ # :ProtocolVersion # 32775
25
+ ].freeze
26
+
27
+ requires :hyperv_username, :hyperv_password
28
+ recognizes :hyperv_endpoint, :hyperv_host,
29
+ :hyperv_transport,
30
+ :hyperv_debug
31
+ secrets :hyperv_password, :connection
32
+
33
+ model_path 'fog/hyperv/models/compute'
34
+ model :bios
35
+ model :cluster
36
+ collection :clusters
37
+ model :com_port
38
+ model :dvd_drive
39
+ collection :dvd_drives
40
+ model :firmware
41
+ model :floppy_drive
42
+ collection :floppy_drives
43
+ model :hard_drive
44
+ collection :hard_drives
45
+ model :host
46
+ collection :hosts
47
+ model :network_adapter
48
+ collection :network_adapters
49
+ model :server
50
+ collection :servers
51
+ model :switch
52
+ collection :switches
53
+ model :vhd
54
+ collection :vhds
55
+
56
+ request_path 'fog/hyperv/requests/compute'
57
+ request :add_vm_hard_disk_drive
58
+ request :add_vm_network_adapter
59
+ request :connect_vm_network_adapter
60
+ request :disconnect_vm_network_adapter
61
+ request :get_cluster
62
+ request :get_cluster_node
63
+ request :get_vhd
64
+ request :get_vm
65
+ request :get_vm_bios
66
+ request :get_vm_dvd_drive
67
+ request :get_vm_firmware
68
+ request :get_vm_floppy_disk_drive
69
+ request :get_vm_group
70
+ request :get_vm_hard_disk_drive
71
+ request :get_vm_host
72
+ request :get_vm_host_cluster
73
+ request :get_vm_network_adapter
74
+ request :get_vm_switch
75
+ request :new_vhd
76
+ request :new_vm
77
+ request :new_vm_switch
78
+ request :remove_item
79
+ request :remove_vm
80
+ request :remove_vm_hard_disk_drive
81
+ request :remove_vm_network_adapter
82
+ request :restart_vm
83
+ request :set_vm
84
+ request :set_vm_bios
85
+ request :set_vm_dvd_drive
86
+ request :set_vm_hard_disk_drive
87
+ request :set_vm_firmware
88
+ request :set_vm_network_adapter
89
+ request :set_vm_switch
90
+ request :start_vm
91
+ request :stop_vm
92
+
93
+ class Shared
94
+ def version
95
+ '0.0'
96
+ end
97
+
98
+ protected
99
+
100
+ def requires(opts, *args)
101
+ missing = args - opts.keys
102
+ return if missing.none?
103
+
104
+ method = caller[0][/`.*'/][1..-2]
105
+ if missing.length == 1
106
+ raise(ArgumentError, "#{missing.first} is required for #{method}")
107
+ elsif missing.any?
108
+ raise(ArgumentError, "#{missing[0...-1].join(', ')}, and #{missing[-1]} are required for #{method}")
109
+ end
110
+ end
111
+
112
+ def requires_one(opts, *args)
113
+ missing = args - opts.keys
114
+ return if missing.length < args.length
115
+
116
+ method = caller[0][/`.*'/][1..-2]
117
+ raise(ArgumentError, "#{missing[0...-1].join(', ')}, or #{missing[-1]} are required for #{method}")
118
+ end
119
+
120
+ def requires_version(required_version)
121
+ method = caller[0][/`.*'/][1..-2].split('_')
122
+ method = method[0].capitalize + "-" + Fog::Hyperv.camelize(method[1..-1].join('_'))
123
+
124
+ raise Fog::Hyperv::Errors::VersionError.new(required_version, version, method) \
125
+ unless Gem::Version.new(version) >= Gem::Version.new(required_version)
126
+ end
127
+
128
+ end
129
+
130
+ class Real < Shared
131
+ attr_reader :logger
132
+
133
+ def initialize(options = {})
134
+ # require 'ostruct'
135
+ require 'fog/json'
136
+ require 'logging'
137
+
138
+ @hyperv_endpoint = options[:hyperv_endpoint]
139
+ @hyperv_endpoint = "http://#{options[:hyperv_host]}:5985/wsman" if !@hyperv_endpoint && options[:hyperv_host]
140
+ @hyperv_username = options[:hyperv_username]
141
+ @hyperv_password = options[:hyperv_password]
142
+ @hyperv_transport = options[:hyperv_transport] || :negotiate
143
+ @logger = Logging.logger['hyper-v']
144
+ if options[:hyperv_debug]
145
+ logger.level = :debug
146
+ logger.add_appenders Logging.appenders.stdout
147
+ end
148
+
149
+ connect
150
+ end
151
+
152
+ def local?
153
+ false # @hyperv_endpoint.nil?
154
+ end
155
+
156
+ def valid?
157
+ if local?
158
+ run_shell('Get-VMHost', _return_fields: :name) && true
159
+ else
160
+ run_wql('SELECT Name FROM Msvm_ComputerSystem WHERE Caption = "Hosting Computer System"') && true
161
+ end
162
+ rescue Fog::Hyperv::Errors::ServiceError
163
+ false
164
+ end
165
+
166
+ def version
167
+ @version ||= run_wql('SELECT Version FROM Win32_OperatingSystem', _namespace: 'root/cimv2/*')[:xml_fragment].first[:version] rescue \
168
+ run_shell("$VMMS = if ([environment]::Is64BitProcess) { \"$($env:SystemRoot)\\System32\\vmms.exe\" } else { \"$($env:SystemRoot)\\Sysnative\\vmms.exe\" }\n(Get-Item $VMMS).VersionInfo.ProductVersion", _skip_json: true).stdout.strip
169
+ end
170
+
171
+ private
172
+
173
+ def hash_to_optmap(options = {})
174
+ args = options.reject { |k, v| v.nil? || v.is_a?(FalseClass) || k.to_s.start_with?('_') }.map do |k, v|
175
+ "'#{k}'=#{Fog::Hyperv.shell_quoted(v, true)}"
176
+ end
177
+ "@{#{args.join ';'}}"
178
+ end
179
+
180
+ def run_shell_with_vm(command, vm_options, options = {})
181
+ # $VM = Get-VM @vm_options
182
+ # $Result = <command> @options
183
+ # $Result | select <return_fields> | ConvertTo-Json
184
+ end
185
+
186
+ def run_wql(query, options = {})
187
+ skip_camelize = options.delete :_skip_camelize
188
+ namespace = options.delete(:_namespace) || 'root/virtualization/v2/*'
189
+
190
+ options = Fog::Hyperv.camelize(options) unless skip_camelize
191
+ args = options.reject { |k, v| v.nil? || v.is_a?(FalseClass) || k.to_s.start_with?('_') }.map do |k, v|
192
+ "#{k} = #{(v.is_a?(String) || v.to_s =~ /\s/) && v.inspect || v}"
193
+ end
194
+
195
+ query = "#{query}#{" WHERE #{args.join ' AND '}" unless args.none?}"
196
+ data = \
197
+ if local?
198
+ # TODO
199
+ else
200
+ logger.debug "WQL; #{namespace} >>> #{query}"
201
+ @connection.run_wql(query, namespace)
202
+ end
203
+
204
+ logger.debug "WQL; <<< #{data}"
205
+ data
206
+ end
207
+
208
+ def run_shell(command, options = {})
209
+ return_fields = options.delete :_return_fields
210
+ return_fields = "| select #{Fog::Hyperv.camelize([return_fields].flatten).join ','}" if return_fields
211
+ suffix = options.delete :_suffix
212
+ json_depth = options.delete :_json_depth
213
+ skip_json = options.delete :_skip_json
214
+ skip_camelize = options.delete :_skip_camelize
215
+ skip_uncamelize = options.delete :_skip_uncamelize
216
+ options = Fog::Hyperv.camelize(options) unless skip_camelize
217
+
218
+ # commandline = "$Args = #{hash_to_optmap options}\n$Ret = #{command} @Args#{"\n$Ret #{return_fields} | ConvertTo-Json -Compress #{"-Depth #{json_depth}" if json_depth}" unless skip_json}"
219
+ # puts " > #{commandline.split("\n").join "\n > "}" if @hyperv_debug
220
+ args = options.reject { |k, v| v.nil? || v.is_a?(FalseClass) || k.to_s.start_with?('_') || (v.is_a?(String) && v.empty?) }.map do |k, v|
221
+ "-#{k} #{Fog::Hyperv.shell_quoted v unless v.is_a?(TrueClass)}"
222
+ end
223
+ command_args = "#{command} #{args.join ' ' unless args.empty?}"
224
+ commandline = "#{command_args} #{suffix} #{return_fields} #{"| ConvertTo-Json -Compress #{"-Depth #{json_depth}" if json_depth}" unless skip_json}"
225
+ logger.debug "PS; >>> #{commandline}"
226
+
227
+ out = nil # OpenStruct.new stdout: '',
228
+ # stderr: '',
229
+ # exitcode: -1
230
+
231
+ if local?
232
+ commanddata = [
233
+ 'powershell',
234
+ '-NoLogo',
235
+ '-NoProfile',
236
+ '-NonInteractive',
237
+ commandline
238
+ ]
239
+ begin
240
+ out.stdout, out.stderr, out.exitcode = Open3.capture3(*commanddata)
241
+ out.exitcode = out.exitcode.exitstatus
242
+ rescue StandardError => ex
243
+ out.stderr = ex.inspect
244
+ out.exitcode = -1
245
+ end
246
+ else
247
+ @connection.shell(:powershell) do |shell|
248
+ out = shell.run(commandline)
249
+ end
250
+ end
251
+
252
+ # TODO: Map error codes in some manner?
253
+ raise Fog::Hyperv::Errors::ServiceError, "Failed to execute #{commandline}" unless out
254
+ raise Fog::Hyperv::Errors::PSError.new(out, "When executing #{command_args}") unless out.exitcode.zero?
255
+
256
+ logger.debug "PS; <<< OUT=[#{out.stdout.inspect}] ERR=[#{out.stderr.inspect}] EXIT=[#{out.exitcode}]"
257
+
258
+ if skip_json
259
+ out
260
+ else
261
+ return nil if out.stdout.empty?
262
+ json = Fog::JSON.decode(out.stdout)
263
+ json = Fog::Hyperv.uncamelize(json) unless skip_uncamelize
264
+ json
265
+ end
266
+ end
267
+
268
+ def connect
269
+ # return require 'open3' if local?
270
+
271
+ require 'winrm'
272
+ @connection = WinRM::Connection.new(
273
+ endpoint: @hyperv_endpoint,
274
+ user: @hyperv_username,
275
+ password: @hyperv_password,
276
+ transport: @hyperv_transport
277
+ )
278
+ Logging.logger['WinRM::HTTP::HttpNegotiate'].level = :error
279
+ @connection.logger.level = :error
280
+ end
281
+ end
282
+
283
+ class Mock < Shared
284
+ def initialize(_options = {})
285
+ require 'fog/json'
286
+ end
287
+
288
+ def method_missing(method, *args)
289
+ if requests.find { |_, k| k == method }
290
+ handle_mock_response((args.first || {}).merge(_method: method))
291
+ else
292
+ super
293
+ end
294
+ end
295
+
296
+ def respond_to_missing?(method, include_private = false)
297
+ requests.find { |_, k| k == method } || super
298
+ end
299
+
300
+ def self.method_defined?(method)
301
+ Fog::Compute::Hyperv.requests.find { |_, k| k == method } || super
302
+ end
303
+
304
+ private
305
+
306
+ def handle_mock_response(args = {})
307
+ method = args.delete :_method
308
+ method ||= caller[0][/`.*'/][1..-2]
309
+ method ||= caller[1][/`.*'/][1..-2]
310
+
311
+ path = File.join File.dirname(__FILE__), 'requests', 'compute', 'mock_files', "#{method}.json"
312
+ Fog::Mock.not_implemented unless File.exist? path
313
+ raise Fog::Errors::MockNotImplemented, 'Not implementing skipping of json' if args[:_skip_json]
314
+ raise Fog::Errors::MockNotImplemented, 'Not implementing skipping of uncamelize' if args[:_skip_uncamelize]
315
+
316
+ ret = Fog::JSON.decode(open(path).read)
317
+ ret = Fog::Hyperv.uncamelize(ret)
318
+
319
+ ret = ret.map do |obj|
320
+ obj.select { |k, _| args[:_return_fields].include? k }
321
+ end if args[:_return_fields]
322
+ ret
323
+ end
324
+ end
325
+ end
326
+ end
327
+ end