vagrant-proxmox 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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