vagrant-proxmox 0.0.2

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/lib/sanity_checks.rb +11 -0
  3. data/lib/vagrant-proxmox/action/cleanup_after_destroy.rb +20 -0
  4. data/lib/vagrant-proxmox/action/connect_proxmox.rb +33 -0
  5. data/lib/vagrant-proxmox/action/create_vm.rb +63 -0
  6. data/lib/vagrant-proxmox/action/destroy_vm.rb +29 -0
  7. data/lib/vagrant-proxmox/action/get_node_list.rb +24 -0
  8. data/lib/vagrant-proxmox/action/is_created.rb +20 -0
  9. data/lib/vagrant-proxmox/action/is_stopped.rb +20 -0
  10. data/lib/vagrant-proxmox/action/message_already_running.rb +20 -0
  11. data/lib/vagrant-proxmox/action/message_already_stopped.rb +20 -0
  12. data/lib/vagrant-proxmox/action/message_not_created.rb +20 -0
  13. data/lib/vagrant-proxmox/action/proxmox_action.rb +47 -0
  14. data/lib/vagrant-proxmox/action/read_ssh_info.rb +22 -0
  15. data/lib/vagrant-proxmox/action/read_state.rb +37 -0
  16. data/lib/vagrant-proxmox/action/shutdown_vm.rb +31 -0
  17. data/lib/vagrant-proxmox/action/start_vm.rb +40 -0
  18. data/lib/vagrant-proxmox/action/stop_vm.rb +31 -0
  19. data/lib/vagrant-proxmox/action/sync_folders.rb +52 -0
  20. data/lib/vagrant-proxmox/action.rb +159 -0
  21. data/lib/vagrant-proxmox/config.rb +81 -0
  22. data/lib/vagrant-proxmox/errors.rb +39 -0
  23. data/lib/vagrant-proxmox/plugin.rb +75 -0
  24. data/lib/vagrant-proxmox/provider.rb +51 -0
  25. data/lib/vagrant-proxmox/version.rb +7 -0
  26. data/lib/vagrant-proxmox.rb +22 -0
  27. data/locales/en.yml +66 -0
  28. data/spec/actions/cleanup_after_destroy_action_spec.rb +22 -0
  29. data/spec/actions/connect_proxmox_action_spec.rb +49 -0
  30. data/spec/actions/create_vm_action_spec.rb +167 -0
  31. data/spec/actions/destroy_vm_action_spec.rb +37 -0
  32. data/spec/actions/get_node_list_action_spec.rb +25 -0
  33. data/spec/actions/is_created_action_spec.rb +35 -0
  34. data/spec/actions/is_stopped_action_spec.rb +34 -0
  35. data/spec/actions/message_already_running_action_spec.rb +23 -0
  36. data/spec/actions/message_already_stopped_action_spec.rb +23 -0
  37. data/spec/actions/message_not_created_action_spec.rb +23 -0
  38. data/spec/actions/proxmox_action_shared.rb +74 -0
  39. data/spec/actions/read_ssh_info_action_spec.rb +32 -0
  40. data/spec/actions/read_state_action_spec.rb +54 -0
  41. data/spec/actions/shutdown_vm_action_spec.rb +37 -0
  42. data/spec/actions/start_vm_action_spec.rb +47 -0
  43. data/spec/actions/stop_vm_action_spec.rb +37 -0
  44. data/spec/actions/sync_folders_action_spec.rb +51 -0
  45. data/spec/commands/destroy_command_spec.rb +68 -0
  46. data/spec/commands/halt_command_spec.rb +46 -0
  47. data/spec/commands/provision_command_spec.rb +33 -0
  48. data/spec/commands/ssh_command_spec.rb +31 -0
  49. data/spec/commands/ssh_run_command_spec.rb +32 -0
  50. data/spec/commands/status_command_spec.rb +19 -0
  51. data/spec/commands/up_command_spec.rb +55 -0
  52. data/spec/config_spec.rb +71 -0
  53. data/spec/plugin_spec.rb +22 -0
  54. data/spec/provider_spec.rb +25 -0
  55. data/spec/sanity_checks_spec.rb +19 -0
  56. data/spec/spec_helper.rb +128 -0
  57. metadata +242 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 57fd9b0a41cefd93d184deb4f2156c278b7ea0f6
4
+ data.tar.gz: 6aff0afae6d0cd851a8780060c25b20877d0f599
5
+ SHA512:
6
+ metadata.gz: 460cd1faf092d13e2790d37ef1a7c40d6d7f289e5e15ffd2f6ddd42f44531f89b298e7462232133406f6529b04223a9913ecc1e751e17c8ea2d9fffa1cc89512
7
+ data.tar.gz: c63ab8bd9b932b52a06d50fb2181d0076e6251834955c60ccedabb6824459903be6e7a69140fca1faaeb91349f9f2a759603ccb198359af2a7f0663895451ea2
@@ -0,0 +1,11 @@
1
+ begin
2
+ require 'vagrant'
3
+ rescue LoadError
4
+ raise 'The Vagrant Proxmox plugin must be run within Vagrant.'
5
+ end
6
+
7
+ # This is a sanity check to make sure no one is attempting to install
8
+ # this into an early Vagrant version.
9
+ if Vagrant::VERSION < '1.2.0'
10
+ raise 'The Vagrant Proxmox plugin is only compatible with Vagrant 1.2+'
11
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class CleanupAfterDestroy < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ FileUtils.rm_rf ".vagrant/machines/#{env[:machine].name}/proxmox"
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action connects to the Proxmox server and stores the access ticket
6
+ # and csrf prevention token in env[:proxmox_ticket] and
7
+ # env[:proxmox_csrf_prevention_token].
8
+ class ConnectProxmox < ProxmoxAction
9
+
10
+ def initialize app, env
11
+ @app = app
12
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::connect_proxmox'
13
+ end
14
+
15
+ def call env
16
+ config = env[:machine].provider_config
17
+ response = RestClient.post "#{config.endpoint}/access/ticket",
18
+ username: config.user_name, password: config.password
19
+ begin
20
+ json_response = JSON.parse response.to_s, symbolize_names: true
21
+ env[:proxmox_ticket] = json_response[:data][:ticket]
22
+ env[:proxmox_csrf_prevention_token] = json_response[:data][:CSRFPreventionToken]
23
+ rescue => e
24
+ raise Errors::CommunicationError, error_msg: e.message
25
+ end
26
+ next_action env
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action creates a new virtual machine on the Proxmox server and
6
+ # stores its node and vm_id env[:machine].id
7
+ class CreateVm < ProxmoxAction
8
+
9
+ def initialize app, env
10
+ @app = app
11
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::create_vm'
12
+ end
13
+
14
+ def call env
15
+ env[:ui].info I18n.t('vagrant_proxmox.creating_vm')
16
+ config = env[:machine].provider_config
17
+ node = env[:proxmox_nodes].sample[:node]
18
+ vm_id = nil
19
+
20
+ begin
21
+ retryable(tries: config.task_timeout,
22
+ sleep: config.task_status_check_interval) do
23
+ vm_id = get_free_vm_id env
24
+ params = {vmid: vm_id,
25
+ ostemplate: config.os_template,
26
+ hostname: env[:machine].config.vm.hostname || env[:machine].name.to_s,
27
+ password: 'vagrant',
28
+ memory: config.vm_memory,
29
+ description: "#{config.vm_name_prefix}#{env[:machine].name}"}
30
+ get_machine_ip_address(env).try { |ip_address| params[:ip_address] = ip_address }
31
+
32
+ response = RestClient.post "#{config.endpoint}/nodes/#{node}/openvz", params,
33
+ {CSRFPreventionToken: env[:proxmox_csrf_prevention_token],
34
+ cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
35
+ exit_status = wait_for_completion parse_task_id(response), node, env, 'vagrant_proxmox.errors.create_vm_timeout'
36
+ exit_status == 'OK' ? exit_status : raise(VagrantPlugins::Proxmox::Errors::ProxmoxTaskFailed, proxmox_exit_status: exit_status)
37
+ end
38
+ rescue StandardError => e
39
+ raise VagrantPlugins::Proxmox::Errors::VMCreationError, proxmox_exit_status: e.message
40
+ end
41
+
42
+ env[:machine].id = "#{node}/#{vm_id}"
43
+
44
+ env[:ui].info I18n.t('vagrant_proxmox.done')
45
+ next_action env
46
+ end
47
+
48
+ private
49
+ def get_free_vm_id env
50
+ response = RestClient.get "#{env[:machine].provider_config.endpoint}/cluster/resources?type=vm", {cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
51
+ json_response = JSON.parse response.to_s, symbolize_names: true
52
+
53
+ allowed_vm_ids = env[:machine].provider_config.vm_id_range.to_set
54
+ used_vm_ids = json_response[:data].map { |vm| vm[:vmid] }
55
+ free_vm_ids = (allowed_vm_ids - used_vm_ids).sort
56
+ free_vm_ids.empty? ? raise(VagrantPlugins::Proxmox::Errors::NoVmIdAvailable) : free_vm_ids.first
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,29 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class DestroyVm < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:ui].info I18n.t('vagrant_proxmox.destroying_vm')
13
+ node, vm_id = env[:machine].id.split '/'
14
+ endpoint = env[:machine].provider_config.endpoint
15
+ response = RestClient.delete "#{endpoint}/nodes/#{node}/openvz/#{vm_id}",
16
+ {CSRFPreventionToken: env[:proxmox_csrf_prevention_token],
17
+ cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
18
+
19
+ wait_for_completion parse_task_id(response), node, env, 'vagrant_proxmox.errors.destroy_vm_timeout'
20
+ env[:ui].info I18n.t('vagrant_proxmox.done')
21
+
22
+ next_action env
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action gets a list of all the nodes of a Proxmox server cluster
6
+ # and stores it under env[:proxmox_nodes]
7
+ class GetNodeList < ProxmoxAction
8
+
9
+ def initialize app, env
10
+ @app = app
11
+ end
12
+
13
+ def call env
14
+ endpoint = env[:machine].provider_config.endpoint
15
+ response = RestClient.get "#{endpoint}/nodes", {cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
16
+ env[:proxmox_nodes] = JSON.parse(response.to_s, symbolize_names: true)[:data]
17
+ next_action env
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class IsCreated < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:result] = env[:machine].state.id != :not_created
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class IsStopped < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:result] = env[:machine].state.id == :stopped
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class MessageAlreadyRunning < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:ui].info I18n.t('vagrant_proxmox.already_running')
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class MessageAlreadyStopped < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:ui].info I18n.t('vagrant_proxmox.already_stopped')
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class MessageNotCreated < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:ui].info I18n.t('vagrant_proxmox.not_created')
13
+ next_action env
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class ProxmoxAction
6
+
7
+ protected
8
+ def next_action env
9
+ @app.call env
10
+ end
11
+
12
+ protected
13
+ def parse_task_id proxmox_response
14
+ JSON.parse(proxmox_response.to_s, symbolize_names: true)[:data]
15
+ end
16
+
17
+ # Wait for the completion of the proxmox task with the given upid.
18
+ protected
19
+ def wait_for_completion task_upid, node, env, timeout_message
20
+ begin
21
+ retryable(on: VagrantPlugins::Proxmox::ProxmoxTaskNotFinished,
22
+ tries: env[:machine].provider_config.task_timeout,
23
+ sleep: env[:machine].provider_config.task_status_check_interval) do
24
+ exit_status = get_task_exitstatus task_upid, node, env
25
+ exit_status.nil? ? raise(VagrantPlugins::Proxmox::ProxmoxTaskNotFinished) : exit_status
26
+ end
27
+ rescue VagrantPlugins::Proxmox::ProxmoxTaskNotFinished
28
+ raise Errors::Timeout.new timeout_message
29
+ end
30
+ end
31
+
32
+ protected
33
+ def get_machine_ip_address env
34
+ env[:machine].config.vm.networks.select { |type, _| type == :public_network }.first[1][:ip] rescue nil
35
+ end
36
+
37
+ private
38
+ def get_task_exitstatus task_upid, node, env
39
+ response = RestClient.get "#{env[:machine].provider_config.endpoint}/nodes/#{node}/tasks/#{task_upid}/status", {cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
40
+ parse_task_id(response)[:exitstatus]
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ class ReadSSHInfo < ProxmoxAction
6
+
7
+ def initialize app, env
8
+ @app = app
9
+ end
10
+
11
+ def call env
12
+ env[:machine_ssh_info] = get_machine_ip_address(env).try do |ip_address|
13
+ {host: ip_address, port: env[:machine].config.ssh.guest_port}
14
+ end
15
+ next_action env
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action reads the state of a Proxmox virtual machine and stores it
6
+ # in env[:machine_state_id].
7
+ class ReadState < ProxmoxAction
8
+
9
+ def initialize app, env
10
+ @app = app
11
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::read_state'
12
+ end
13
+
14
+ def call env
15
+ begin
16
+ if env[:machine].id
17
+ node, vm_id = env[:machine].id.split '/'
18
+ endpoint = env[:machine].provider_config.endpoint
19
+ response = RestClient.get "#{endpoint}/nodes/#{node}/openvz/#{vm_id}/status/current",
20
+ {cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
21
+ states = {'running' => :running,
22
+ 'stopped' => :stopped}
23
+ env[:machine_state_id] = states[JSON.parse(response.to_s)['data']['status']]
24
+ else
25
+ env[:machine_state_id] = :not_created
26
+ end
27
+ rescue RestClient::InternalServerError
28
+ env[:machine_state_id] = :not_created
29
+ end
30
+ next_action env
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action shuts down a Proxmox virtual machine.
6
+ class ShutdownVm < ProxmoxAction
7
+
8
+ def initialize app, env
9
+ @app = app
10
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::shutdown_vm'
11
+ end
12
+
13
+ def call env
14
+ endpoint = env[:machine].provider_config.endpoint
15
+ node, vm_id = env[:machine].id.split '/'
16
+ env[:ui].info I18n.t('vagrant_proxmox.shut_down_vm')
17
+ response = RestClient.post "#{endpoint}/nodes/#{node}/openvz/#{vm_id}/status/shutdown", nil,
18
+ {CSRFPreventionToken: env[:proxmox_csrf_prevention_token],
19
+ cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
20
+
21
+ wait_for_completion parse_task_id(response), node, env, 'vagrant_proxmox.errors.shutdown_vm_timeout'
22
+ env[:ui].info I18n.t('vagrant_proxmox.done')
23
+
24
+ next_action env
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action starts a Proxmox virtual machine.
6
+ class StartVm < ProxmoxAction
7
+
8
+ def initialize app, env
9
+ @app = app
10
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::start_vm'
11
+ end
12
+
13
+ def call env
14
+ endpoint = env[:machine].provider_config.endpoint
15
+ node, vm_id = env[:machine].id.split '/'
16
+ env[:ui].info I18n.t('vagrant_proxmox.starting_vm')
17
+ response = RestClient.post "#{endpoint}/nodes/#{node}/openvz/#{vm_id}/status/start", nil,
18
+ {CSRFPreventionToken: env[:proxmox_csrf_prevention_token],
19
+ cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
20
+
21
+ wait_for_completion parse_task_id(response), node, env, 'vagrant_proxmox.errors.start_vm_timeout'
22
+ env[:ui].info I18n.t('vagrant_proxmox.done')
23
+
24
+ env[:ui].info I18n.t('vagrant_proxmox.waiting_for_ssh_connection')
25
+ loop do
26
+ # If we're interrupted then just back out
27
+ break if env[:interrupted]
28
+ break if env[:machine].communicate.ready?
29
+ sleep env[:machine].provider_config.task_status_check_interval
30
+ end
31
+ env[:ui].info I18n.t('vagrant_proxmox.done')
32
+
33
+ next_action env
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ module Action
4
+
5
+ # This action stops a Proxmox virtual machine.
6
+ class StopVm < ProxmoxAction
7
+
8
+ def initialize app, env
9
+ @app = app
10
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::stop_vm'
11
+ end
12
+
13
+ def call env
14
+ endpoint = env[:machine].provider_config.endpoint
15
+ node, vm_id = env[:machine].id.split '/'
16
+ env[:ui].info I18n.t('vagrant_proxmox.stopping_vm')
17
+ response = RestClient.post "#{endpoint}/nodes/#{node}/openvz/#{vm_id}/status/stop", nil,
18
+ {CSRFPreventionToken: env[:proxmox_csrf_prevention_token],
19
+ cookies: {PVEAuthCookie: env[:proxmox_ticket]}}
20
+
21
+ wait_for_completion parse_task_id(response), node, env, 'vagrant_proxmox.errors.stop_vm_timeout'
22
+ env[:ui].info I18n.t('vagrant_proxmox.done')
23
+
24
+ next_action env
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'vagrant/util/subprocess'
2
+
3
+ module VagrantPlugins
4
+ module Proxmox
5
+ module Action
6
+
7
+ # This action uses 'rsync' to sync the folders over to the virtual machine.
8
+ class SyncFolders < ProxmoxAction
9
+
10
+ def initialize app, env
11
+ @app = app
12
+ @logger = Log4r::Logger.new 'vagrant_proxmox::action::sync_folders'
13
+ end
14
+
15
+ def call env
16
+ ssh_info = env[:machine].ssh_info
17
+
18
+ env[:machine].config.vm.synced_folders.each do |_, data|
19
+ hostpath = File.expand_path data[:hostpath], env[:root_path]
20
+ guestpath = data[:guestpath]
21
+ next if data[:disabled]
22
+
23
+ # Make sure there is a trailing slash on the host path to
24
+ # avoid creating an additional directory with rsync
25
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
26
+
27
+ env[:ui].info I18n.t('vagrant_proxmox.rsync_folder', hostpath: hostpath, guestpath: guestpath)
28
+
29
+ # Create the guest path
30
+ env[:machine].communicate.sudo "mkdir -p '#{guestpath}'"
31
+ env[:machine].communicate.sudo "chown #{ssh_info[:username]} '#{guestpath}'"
32
+
33
+ # rsync over to the guest path using the SSH info
34
+ command = [
35
+ 'rsync', '--verbose', '--archive', '--compress', '--delete',
36
+ '-e', "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path]}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
37
+ hostpath, "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
38
+
39
+ rsync_process = Vagrant::Util::Subprocess.execute *command
40
+ if rsync_process.exit_code != 0
41
+ raise Errors::RsyncError, guestpath: guestpath, hostpath: hostpath, stderr: rsync_process.stderr
42
+ end
43
+ end
44
+
45
+ next_action env
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
52
+ end