vagrant-vmm 1.0.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 (44) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +118 -0
  7. data/Rakefile +5 -0
  8. data/example/Berksfile +6 -0
  9. data/example/Vagrantfile +53 -0
  10. data/example/metadata.rb +5 -0
  11. data/lib/vagrant-vmm/action/delete_vm.rb +25 -0
  12. data/lib/vagrant-vmm/action/import.rb +35 -0
  13. data/lib/vagrant-vmm/action/message_will_not_destroy.rb +17 -0
  14. data/lib/vagrant-vmm/action/read_state.rb +39 -0
  15. data/lib/vagrant-vmm/action/resume_vm.rb +25 -0
  16. data/lib/vagrant-vmm/action/start_instance.rb +26 -0
  17. data/lib/vagrant-vmm/action/stop_instance.rb +26 -0
  18. data/lib/vagrant-vmm/action/suspend_vm.rb +25 -0
  19. data/lib/vagrant-vmm/action/wait_for_ip_address.rb +64 -0
  20. data/lib/vagrant-vmm/action.rb +225 -0
  21. data/lib/vagrant-vmm/cap/public_address.rb +15 -0
  22. data/lib/vagrant-vmm/config.rb +44 -0
  23. data/lib/vagrant-vmm/driver.rb +124 -0
  24. data/lib/vagrant-vmm/errors.rb +42 -0
  25. data/lib/vagrant-vmm/plugin.rb +46 -0
  26. data/lib/vagrant-vmm/provider.rb +103 -0
  27. data/lib/vagrant-vmm/scripts/delete_vm.ps1 +32 -0
  28. data/lib/vagrant-vmm/scripts/get_network_config.ps1 +84 -0
  29. data/lib/vagrant-vmm/scripts/get_vm_status.ps1 +40 -0
  30. data/lib/vagrant-vmm/scripts/import_vm.ps1 +107 -0
  31. data/lib/vagrant-vmm/scripts/resume_vm.ps1 +24 -0
  32. data/lib/vagrant-vmm/scripts/start_vm.ps1 +48 -0
  33. data/lib/vagrant-vmm/scripts/stop_vm.ps1 +24 -0
  34. data/lib/vagrant-vmm/scripts/suspend_vm.ps1 +27 -0
  35. data/lib/vagrant-vmm/scripts/sync_folders.ps1 +200 -0
  36. data/lib/vagrant-vmm/scripts/utils/manage_credentials.ps1 +26 -0
  37. data/lib/vagrant-vmm/scripts/utils/vmm_executor.ps1 +30 -0
  38. data/lib/vagrant-vmm/scripts/utils/write_messages.ps1 +20 -0
  39. data/lib/vagrant-vmm/synced_folder.rb +89 -0
  40. data/lib/vagrant-vmm/version.rb +5 -0
  41. data/lib/vagrant-vmm.rb +17 -0
  42. data/locales/en.yml +46 -0
  43. data/vagrant-vmm.gemspec +23 -0
  44. metadata +114 -0
@@ -0,0 +1,225 @@
1
+ require "pathname"
2
+
3
+ require "vagrant/action/builder"
4
+
5
+ module VagrantPlugins
6
+ module VMM
7
+ module Action
8
+ # Include the built-in modules so we can use them as top-level things.
9
+ include Vagrant::Action::Builtin
10
+
11
+ def self.action_reload
12
+ Vagrant::Action::Builder.new.tap do |b|
13
+ b.use ConfigValidate
14
+ b.use Call, IsState, :not_created do |env, b2|
15
+ if env[:result]
16
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
17
+ next
18
+ end
19
+
20
+ b2.use action_halt
21
+ b2.use action_start
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.action_destroy
27
+ Vagrant::Action::Builder.new.tap do |b|
28
+ b.use Call, IsState, :not_created do |env1, b1|
29
+ if env1[:result]
30
+ b1.use Message, I18n.t("vagrant_VMM.message_not_created")
31
+ next
32
+ end
33
+
34
+ b1.use Call, DestroyConfirm do |env2, b2|
35
+ if !env2[:result]
36
+ b2.use MessageWillNotDestroy
37
+ next
38
+ end
39
+
40
+ b2.use ConfigValidate
41
+ b2.use StopInstance
42
+ b2.use DeleteVM
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.action_halt
49
+ Vagrant::Action::Builder.new.tap do |b|
50
+ b.use ConfigValidate
51
+ b.use Call, IsState, :not_created do |env, b2|
52
+ if env[:result]
53
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
54
+ next
55
+ end
56
+
57
+ b2.use Call, GracefulHalt, :off, :running do |env2, b3|
58
+ if !env2[:result]
59
+ b3.use StopInstance
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def self.action_provision
67
+ Vagrant::Action::Builder.new.tap do |b|
68
+ b.use ConfigValidate
69
+ b.use Call, IsState, :not_created do |env, b2|
70
+ if env[:result]
71
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
72
+ next
73
+ end
74
+
75
+ b2.use Call, IsState, :running do |env1, b3|
76
+ if !env1[:result]
77
+ b3.use Message, I18n.t("vagrant_VMM.message_not_running")
78
+ next
79
+ end
80
+
81
+
82
+ b3.use Provision
83
+ b3.use SyncedFolders
84
+ b3.use WaitForIPAddress
85
+ b3.use WaitForCommunicator, [:running]
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def self.action_resume
92
+ Vagrant::Action::Builder.new.tap do |b|
93
+ b.use HandleBox
94
+ b.use ConfigValidate
95
+ b.use Call, IsState, :not_created do |env, b1|
96
+ if env[:result]
97
+ b1.use Message, I18n.t("vagrant_VMM.message_not_created")
98
+ next
99
+ end
100
+
101
+ b1.use ResumeVM
102
+ b1.use WaitForIPAddress
103
+ b1.use WaitForCommunicator, [:running]
104
+ end
105
+ end
106
+ end
107
+
108
+ def self.action_start
109
+ Vagrant::Action::Builder.new.tap do |b|
110
+ #
111
+ b.use Call, IsState, :running do |env1, b1|
112
+ if env1[:result]
113
+ b1.use Message, I18n.t("vagrant_VMM.message_already_running")
114
+ next
115
+ end
116
+
117
+ b1.use Call, IsState, :poweroff do |env2, b2|
118
+ # if not started
119
+ if env2[:result]
120
+ b2.use StartInstance
121
+ next
122
+ end
123
+
124
+ b2.use Call, IsState, :paused do |env3, b3|
125
+ # if machine was paused
126
+ if env3[:result]
127
+ b3.use action_resume
128
+ next
129
+ end
130
+ end
131
+ end
132
+ end
133
+ #
134
+ end
135
+ end
136
+
137
+ def self.action_up
138
+ Vagrant::Action::Builder.new.tap do |b|
139
+ b.use ConfigValidate
140
+ b.use Call, IsState, :not_created do |env1, b1|
141
+ if env1[:result]
142
+ b1.use Import
143
+ end
144
+ end
145
+ b.use action_start
146
+ end
147
+ end
148
+
149
+ def self.action_read_state
150
+ Vagrant::Action::Builder.new.tap do |b|
151
+ b.use ConfigValidate
152
+ b.use ReadState
153
+ end
154
+ end
155
+
156
+ def self.action_ssh
157
+ Vagrant::Action::Builder.new.tap do |b|
158
+ b.use ConfigValidate
159
+ b.use Call, IsState, :not_created do |env, b2|
160
+ if env[:result]
161
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
162
+ next
163
+ end
164
+
165
+ b2.use Call, IsState, :running do |env1, b3|
166
+ if !env1[:result]
167
+ b3.use Message, I18n.t("vagrant_VMM.message_not_running")
168
+ next
169
+ end
170
+
171
+ b3.use SSHExec
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ def self.action_ssh_run
178
+ Vagrant::Action::Builder.new.tap do |b|
179
+ b.use ConfigValidate
180
+ b.use Call, IsState, :not_created do |env, b2|
181
+ if env[:result]
182
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
183
+ next
184
+ end
185
+
186
+ b2.use Call, IsState, :running do |env1, b3|
187
+ if !env1[:result]
188
+ b3.use Message, I18n.t("vagrant_VMM.message_not_running")
189
+ next
190
+ end
191
+
192
+ b3.use SSHRun
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def self.action_suspend
199
+ Vagrant::Action::Builder.new.tap do |b|
200
+ b.use ConfigValidate
201
+ b.use Call, IsState, :not_created do |env, b2|
202
+ if env[:result]
203
+ b2.use Message, I18n.t("vagrant_VMM.message_not_created")
204
+ next
205
+ end
206
+
207
+ b2.use SuspendVM
208
+ end
209
+ end
210
+ end
211
+
212
+ # The autoload farm
213
+ action_root = Pathname.new(File.expand_path("../action", __FILE__))
214
+ autoload :DeleteVM, action_root.join("delete_vm")
215
+ autoload :Import, action_root.join("import")
216
+ autoload :ReadState, action_root.join("read_state")
217
+ autoload :ResumeVM, action_root.join("resume_vm")
218
+ autoload :StartInstance, action_root.join('start_instance')
219
+ autoload :StopInstance, action_root.join('stop_instance')
220
+ autoload :SuspendVM, action_root.join("suspend_vm")
221
+ autoload :WaitForIPAddress, action_root.join("wait_for_ip_address")
222
+ autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy")
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,15 @@
1
+ module VagrantPlugins
2
+ module VMM
3
+ module Cap
4
+ module PublicAddress
5
+ def self.public_address(machine)
6
+ return nil if machine.state.id != :running
7
+
8
+ ssh_info = machine.ssh_info
9
+ return nil if !ssh_info
10
+ ssh_info[:host]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module VMM
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The timeout to wait for an IP address when booting the machine,
7
+ # in seconds.
8
+ #
9
+ # @return [Integer]
10
+ attr_accessor :ip_address_timeout
11
+ # @return [String]
12
+ attr_accessor :proxy_server_address
13
+ # @return [String]
14
+ attr_accessor :vmm_server_address
15
+ # @return [String]
16
+ attr_accessor :vm_template_name
17
+ # @return [String]
18
+ attr_accessor :vm_host_group_name
19
+ # @return [String]
20
+ attr_accessor :vm_address
21
+
22
+ def initialize
23
+ @ip_address_timeout = UNSET_VALUE
24
+ @proxy_server_address = UNSET_VALUE
25
+ @vmm_server_address = UNSET_VALUE
26
+ @vm_template_name = UNSET_VALUE
27
+ @vm_host_group_name = UNSET_VALUE
28
+ @vm_address = UNSET_VALUE
29
+ end
30
+
31
+ def finalize!
32
+ if @ip_address_timeout == UNSET_VALUE
33
+ @ip_address_timeout = 60
34
+ end
35
+ end
36
+
37
+ def validate(machine)
38
+ errors = _detected_errors
39
+
40
+ { "VMM" => errors }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,124 @@
1
+ require "json"
2
+
3
+ require "vagrant/util/powershell"
4
+
5
+ require_relative "plugin"
6
+
7
+ module VagrantPlugins
8
+ module VMM
9
+ class Driver
10
+ ERROR_REGEXP = /===Begin-Error===(.+?)===End-Error===/m
11
+ OUTPUT_REGEXP = /===Begin-Output===(.+?)===End-Output===/m
12
+
13
+ attr_reader :vm_id
14
+ attr_reader :machine
15
+
16
+ def initialize(machine)
17
+ @machine = machine
18
+ @vm_id = machine.id
19
+ @logger = Log4r::Logger.new("vagrant::provider::vmm")
20
+ end
21
+
22
+ def execute(path, options, print_output = false)
23
+ r = execute_powershell(path, options) { |io_name, data|
24
+ if print_output && ( io_name == :stderr || io_name == :stdout )
25
+ if !OUTPUT_REGEXP.match(data) && !ERROR_REGEXP.match(data)
26
+ if io_name == :stdout
27
+ machine.ui.output(data)
28
+ end
29
+ if io_name == :stderr
30
+ machine.ui.error(data)
31
+ end
32
+ end
33
+ end
34
+ }
35
+ if r.exit_code != 0
36
+ raise Errors::PowerShellError,
37
+ script: path,
38
+ stderr: r.stderr
39
+ end
40
+
41
+ # We only want unix-style line endings within Vagrant
42
+ r.stdout.gsub!("\r\n", "\n")
43
+ r.stderr.gsub!("\r\n", "\n")
44
+
45
+ error_match = ERROR_REGEXP.match(r.stdout)
46
+ output_match = OUTPUT_REGEXP.match(r.stdout)
47
+
48
+ if error_match
49
+ data = JSON.parse(error_match[1])
50
+
51
+ # We have some error data.
52
+ raise Errors::PowerShellError,
53
+ script: path,
54
+ stderr: data["error"]
55
+ end
56
+
57
+ # Nothing
58
+ return nil if !output_match
59
+ return JSON.parse(output_match[1])
60
+ end
61
+
62
+ def get_current_state(options)
63
+ options['vm_id'] = vm_id
64
+ execute('get_vm_status.ps1', options)
65
+ end
66
+
67
+ def delete_vm(options)
68
+ options['vm_id'] = vm_id
69
+ execute('delete_vm.ps1', options)
70
+ end
71
+
72
+ def read_guest_ip(options)
73
+ options['vm_id'] = vm_id
74
+ execute('get_network_config.ps1', options, true)
75
+ end
76
+
77
+ def resume(options)
78
+ options['vm_id'] = vm_id
79
+ execute('resume_vm.ps1', options)
80
+ end
81
+
82
+ def start(options)
83
+ options['vm_id'] = vm_id
84
+ execute('start_vm.ps1', options)
85
+ end
86
+
87
+ def stop(options)
88
+ options['vm_id'] = vm_id
89
+ execute('stop_vm.ps1', options)
90
+ end
91
+
92
+ def suspend(options)
93
+ options['vm_id'] = vm_id
94
+ execute("suspend_vm.ps1", options)
95
+ end
96
+
97
+ def import(options)
98
+ execute('import_vm.ps1', options)
99
+ end
100
+
101
+ def sync_folders(options)
102
+ execute('sync_folders.ps1', options, true)
103
+ end
104
+
105
+ protected
106
+
107
+ def execute_powershell(path, options, &block)
108
+ lib_path = Pathname.new(File.expand_path("../scripts", __FILE__))
109
+ path = lib_path.join(path).to_s.gsub("/", "\\")
110
+ options = options || {}
111
+ ps_options = []
112
+ options.each do |key, value|
113
+ ps_options << "-#{key}"
114
+ ps_options << "'#{value}'"
115
+ end
116
+
117
+ # Always have a stop error action for failures
118
+ ps_options << "-ErrorAction" << "Stop"
119
+ opts = { notify: [:stdout, :stderr, :stdin] }
120
+ Vagrant::Util::PowerShell.execute(path, *ps_options, **opts, &block)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,42 @@
1
+ module VagrantPlugins
2
+ module VMM
3
+ module Errors
4
+ # A convenient superclass for all our errors.
5
+ class VMMError < Vagrant::Errors::VagrantError
6
+ error_namespace("vagrant_VMM.errors")
7
+ end
8
+
9
+ class AdminRequired < VMMError
10
+ error_key(:admin_required)
11
+ end
12
+
13
+ class BoxInvalid < VMMError
14
+ error_key(:box_invalid)
15
+ end
16
+
17
+ class IPAddrTimeout < VMMError
18
+ error_key(:ip_addr_timeout)
19
+ end
20
+
21
+ class NoSwitches < VMMError
22
+ error_key(:no_switches)
23
+ end
24
+
25
+ class PowerShellFeaturesDisabled < VMMError
26
+ error_key(:powershell_features_disabled)
27
+ end
28
+
29
+ class PowerShellError < VMMError
30
+ error_key(:powershell_error)
31
+ end
32
+
33
+ class PowerShellRequired < VMMError
34
+ error_key(:powershell_required)
35
+ end
36
+
37
+ class WindowsRequired < VMMError
38
+ error_key(:windows_required)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,46 @@
1
+ module VagrantPlugins
2
+ module VMM
3
+ autoload :Action, File.expand_path("../action", __FILE__)
4
+ autoload :Errors, File.expand_path("../errors", __FILE__)
5
+
6
+ class Plugin < Vagrant.plugin("2")
7
+ name "VMM provider"
8
+ description <<-DESC
9
+ This plugin installs a provider that allows Vagrant to manage
10
+ machines in Virtual Machine Manager.
11
+ DESC
12
+
13
+ provider(:vmm, box_optional: true, priority: 4) do
14
+ require_relative "provider"
15
+ init!
16
+ Provider
17
+ end
18
+
19
+ config(:vmm, :provider) do
20
+ require_relative "config"
21
+ init!
22
+ Config
23
+ end
24
+
25
+ provider_capability("VMM", "public_address") do
26
+ require_relative "cap/public_address"
27
+ Cap::PublicAddress
28
+ end
29
+
30
+ synced_folder(:vmm) do
31
+ require_relative 'synced_folder'
32
+ SyncedFolder
33
+ end
34
+
35
+ protected
36
+
37
+ def self.init!
38
+ return if defined?(@_init)
39
+ I18n.load_path << File.expand_path(
40
+ "locales/en.yml", VMM.source_root)
41
+ I18n.reload!
42
+ @_init = true
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,103 @@
1
+ require "log4r"
2
+
3
+ require_relative "driver"
4
+ require_relative "plugin"
5
+
6
+ require "vagrant/util/platform"
7
+ require "vagrant/util/powershell"
8
+
9
+ module VagrantPlugins
10
+ module VMM
11
+ class Provider < Vagrant.plugin("2", :provider)
12
+ attr_reader :driver
13
+
14
+ def self.usable?(raise_error=false)
15
+ if !Vagrant::Util::Platform.windows?
16
+ raise Errors::WindowsRequired
17
+ return false
18
+ end
19
+
20
+ if !Vagrant::Util::Platform.windows_admin?
21
+ raise Errors::AdminRequired
22
+ end
23
+
24
+ if !Vagrant::Util::PowerShell.available?
25
+ raise Errors::PowerShellRequired
26
+ return false
27
+ end
28
+
29
+ true
30
+ end
31
+
32
+ def initialize(machine)
33
+ @machine = machine
34
+ @state_id = nil
35
+ # This method will load in our driver, so we call it now to
36
+ # initialize it.
37
+ machine_id_changed
38
+ end
39
+
40
+ def action(name)
41
+ # Attempt to get the action method from the Action class if it
42
+ # exists, otherwise return nil to show that we don't support the
43
+ # given action.
44
+ action_method = "action_#{name}"
45
+ return Action.send(action_method) if Action.respond_to?(action_method)
46
+ nil
47
+ end
48
+
49
+ def machine_id_changed
50
+ @driver = Driver.new(@machine)
51
+ end
52
+
53
+ def current_state
54
+ @state_id
55
+ end
56
+
57
+ def reset_state
58
+ @state_id = nil
59
+ end
60
+
61
+ def state
62
+ @state_id = :not_created if !@machine.id
63
+
64
+ if !@state_id || @state_id == :not_created
65
+ env = @machine.action(:read_state)
66
+ @state_id = env[:machine_state_id]
67
+ end
68
+
69
+ # Get the short and long description
70
+ short = @state_id.to_s
71
+ long = ""
72
+
73
+ # If we're not created, then specify the special ID flag
74
+ if @state_id == :not_created
75
+ @state_id = Vagrant::MachineState::NOT_CREATED_ID
76
+ end
77
+
78
+ # Return the MachineState object
79
+ Vagrant::MachineState.new(@state_id, short, long)
80
+ end
81
+
82
+ def to_s
83
+ id = @machine.id.nil? ? "new" : @machine.id
84
+ "VMM (#{id})"
85
+ end
86
+
87
+ def ssh_info
88
+ # We can only SSH into a running machine
89
+ return nil if state.id != :running
90
+ address = @machine.provider_config.vm_address || @machine.config.winrm.host
91
+ if !address
92
+ #TODO: call wait_for_ip_address
93
+ return nil
94
+ end
95
+
96
+ {
97
+ host: address,
98
+ port: 22,
99
+ }
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,32 @@
1
+ Param(
2
+ [Parameter(Mandatory=$true)]
3
+ [string]$vm_id,
4
+ [Parameter(Mandatory=$true)]
5
+ [string]$vmm_server_address,
6
+ [string]$proxy_server_address=$null
7
+ )
8
+
9
+
10
+ # Include the following modules
11
+ $Dir = Split-Path $script:MyInvocation.MyCommand.Path
12
+ . ([System.IO.Path]::Combine($Dir, "utils\write_messages.ps1"))
13
+ . ([System.IO.Path]::Combine($Dir, "utils\vmm_executor.ps1"))
14
+
15
+
16
+ $script_block = {
17
+ # external vars
18
+ $vm_id = $using:vm_id
19
+
20
+ # Get VM
21
+ $vm = Get-SCVirtualMachine -ID $vm_id -ErrorAction Ignore
22
+ if ( $vm )
23
+ {
24
+ if ( $vm.status -eq 'Running' )
25
+ {
26
+ Stop-VM $vm
27
+ }
28
+ Remove-VM $vm
29
+ }
30
+ }
31
+
32
+ execute $script_block $vmm_server_address $proxy_server_address