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
@@ -0,0 +1,159 @@
1
+ require 'vagrant/action/builder'
2
+ require 'pathname'
3
+
4
+ module VagrantPlugins
5
+ module Proxmox
6
+ module Action
7
+
8
+ include Vagrant::Action::Builtin
9
+
10
+ def self.action_read_state
11
+ Vagrant::Action::Builder.new.tap do |b|
12
+ b.use ConfigValidate
13
+ b.use ConnectProxmox
14
+ b.use ReadState
15
+ end
16
+ end
17
+
18
+ def self.action_up
19
+ Vagrant::Action::Builder.new.tap do |b|
20
+ b.use ConfigValidate
21
+ b.use ConnectProxmox
22
+ b.use Call, IsCreated do |env1, b1|
23
+ if env1[:result]
24
+ b1.use Call, IsStopped do |env2, b2|
25
+ if env2[:result]
26
+ b2.use Provision
27
+ b2.use StartVm
28
+ b2.use SyncFolders
29
+ else
30
+ b2.use MessageAlreadyRunning
31
+ end
32
+ end
33
+ else
34
+ b1.use GetNodeList
35
+ b1.use Provision
36
+ b1.use CreateVm
37
+ b1.use StartVm
38
+ b1.use SyncFolders
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def self.action_provision
45
+ Vagrant::Action::Builder.new.tap do |b|
46
+ b.use ConfigValidate
47
+ b.use Call, IsCreated do |env, b2|
48
+ if env[:result]
49
+ b2.use Provision
50
+ b2.use SyncFolders
51
+ else
52
+ b2.use MessageNotCreated
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def self.action_halt
59
+ Vagrant::Action::Builder.new.tap do |b|
60
+ b.use ConfigValidate
61
+ b.use Call, IsCreated do |env1, b1|
62
+ if env1[:result]
63
+ b1.use Call, IsStopped do |env2, b2|
64
+ if env2[:result]
65
+ b2.use MessageAlreadyStopped
66
+ else
67
+ b2.use ConnectProxmox
68
+ b2.use ShutdownVm
69
+ end
70
+ end
71
+ else
72
+ b1.use MessageNotCreated
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ # This action is called to destroy the remote machine.
79
+ def self.action_destroy
80
+ Vagrant::Action::Builder.new.tap do |b|
81
+ b.use ConfigValidate
82
+ b.use ConnectProxmox
83
+ b.use Call, IsCreated do |env1, b1|
84
+ if env1[:result]
85
+ b1.use Call, ::Vagrant::Action::Builtin::DestroyConfirm do |env2, b2|
86
+ if env2[:result]
87
+ b2.use Call, IsStopped do |env3, b3|
88
+ b3.use ShutdownVm unless env3[:result]
89
+ b3.use DestroyVm
90
+ b3.use ::Vagrant::Action::Builtin::ProvisionerCleanup
91
+ b3.use CleanupAfterDestroy
92
+ end
93
+ else
94
+ b2.use ::VagrantPlugins::ProviderVirtualBox::Action::MessageWillNotDestroy
95
+ end
96
+ end
97
+ else
98
+ b1.use MessageNotCreated
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def self.action_read_ssh_info
105
+ Vagrant::Action::Builder.new.tap do |b|
106
+ b.use ConfigValidate
107
+ b.use ConnectProxmox
108
+ b.use ReadSSHInfo
109
+ end
110
+ end
111
+
112
+ def self.action_ssh
113
+ Vagrant::Action::Builder.new.tap do |b|
114
+ b.use ConfigValidate
115
+ b.use Call, IsCreated do |env, b2|
116
+ if env[:result]
117
+ b2.use SSHExec
118
+ else
119
+ b2.use MessageNotCreated
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def self.action_ssh_run
126
+ Vagrant::Action::Builder.new.tap do |b|
127
+ b.use ConfigValidate
128
+ b.use Call, IsCreated do |env, b2|
129
+ if env[:result]
130
+ b2.use SSHRun
131
+ else
132
+ b2.use MessageNotCreated
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ action_root = Pathname.new(File.expand_path '../action', __FILE__)
139
+ autoload :ProxmoxAction, action_root.join('proxmox_action')
140
+ autoload :ConnectProxmox, action_root.join('connect_proxmox')
141
+ autoload :GetNodeList, action_root.join('get_node_list')
142
+ autoload :ReadState, action_root.join('read_state')
143
+ autoload :IsCreated, action_root.join('is_created')
144
+ autoload :IsStopped, action_root.join('is_stopped')
145
+ autoload :MessageAlreadyRunning, action_root.join('message_already_running')
146
+ autoload :MessageAlreadyStopped, action_root.join('message_already_stopped')
147
+ autoload :MessageNotCreated, action_root.join('message_not_created')
148
+ autoload :CreateVm, action_root.join('create_vm')
149
+ autoload :StartVm, action_root.join('start_vm')
150
+ autoload :StopVm, action_root.join('stop_vm')
151
+ autoload :ShutdownVm, action_root.join('shutdown_vm')
152
+ autoload :DestroyVm, action_root.join('destroy_vm')
153
+ autoload :CleanupAfterDestroy, action_root.join('cleanup_after_destroy')
154
+ autoload :ReadSSHInfo, action_root.join('read_ssh_info')
155
+ autoload :SyncFolders, action_root.join('sync_folders')
156
+
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,81 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+ class Config < Vagrant.plugin('2', :config)
4
+
5
+ # The Proxmox REST API endpoint
6
+ #
7
+ # @return [String]
8
+ attr_accessor :endpoint
9
+
10
+ # The Proxmox user name
11
+ #
12
+ # @return [String]
13
+ attr_accessor :user_name
14
+
15
+ # The Proxmox password
16
+ #
17
+ # @return [String]
18
+ attr_accessor :password
19
+
20
+ # The openvz os template to use for the virtual machines
21
+ #
22
+ # @return [String]
23
+ attr_accessor :os_template
24
+
25
+ # The id range to use for the virtual machines
26
+ #
27
+ # @return [Range]
28
+ attr_accessor :vm_id_range
29
+
30
+ # The prefix for the virtual machine name
31
+ #
32
+ # @return [String]
33
+ attr_accessor :vm_name_prefix
34
+
35
+ # Amount of RAM for the virtual machine in MB
36
+ #
37
+ # @return [Integer]
38
+ attr_accessor :vm_memory
39
+
40
+ # The maximum timeout for a proxmox server task (in seconds)
41
+ #
42
+ # @return [Integer]
43
+ attr_accessor :task_timeout
44
+
45
+ # The interval between two proxmox task status retrievals (in seconds)
46
+ #
47
+ # @return [Integer, Proc]
48
+ attr_accessor :task_status_check_interval
49
+
50
+ def initialize
51
+ @endpoint = UNSET_VALUE
52
+ @user_name = UNSET_VALUE
53
+ @password = UNSET_VALUE
54
+ @os_template = UNSET_VALUE
55
+ @vm_id_range = 900..999
56
+ @vm_name_prefix = 'vagrant_'
57
+ @vm_memory = 512
58
+ @task_timeout = 60
59
+ @task_status_check_interval = 2
60
+ end
61
+
62
+ # This is the hook that is called to finalize the object before it is put into use.
63
+ def finalize!
64
+ @endpoint = nil if @endpoint == UNSET_VALUE
65
+ @user_name = nil if @user_name == UNSET_VALUE
66
+ @password = nil if @password == UNSET_VALUE
67
+ @os_template = nil if @os_template == UNSET_VALUE
68
+ end
69
+
70
+ def validate machine
71
+ errors = []
72
+ errors << I18n.t('vagrant_proxmox.errors.no_endpoint_specified') unless @endpoint
73
+ errors << I18n.t('vagrant_proxmox.errors.no_user_name_specified') unless @user_name
74
+ errors << I18n.t('vagrant_proxmox.errors.no_password_specified') unless @password
75
+ errors << I18n.t('vagrant_proxmox.errors.no_os_template_specified') unless @os_template
76
+ {'Proxmox Provider' => errors}
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,39 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+
4
+ class ProxmoxTaskNotFinished < Exception
5
+ end
6
+
7
+ module Errors
8
+
9
+ class VagrantProxmoxError < Vagrant::Errors::VagrantError
10
+ error_namespace 'vagrant_proxmox.errors'
11
+ end
12
+
13
+ class ProxmoxTaskFailed < VagrantProxmoxError
14
+ error_key :proxmox_task_failed
15
+ end
16
+
17
+ class CommunicationError < VagrantProxmoxError
18
+ error_key :communication_error
19
+ end
20
+
21
+ class Timeout < VagrantProxmoxError
22
+ error_key :timeout
23
+ end
24
+
25
+ class NoVmIdAvailable < VagrantProxmoxError
26
+ error_key :no_vm_id_available
27
+ end
28
+
29
+ class VMCreationError < VagrantProxmoxError
30
+ error_key :vm_creation_error
31
+ end
32
+
33
+ class RsyncError < VagrantProxmoxError
34
+ error_key :rsync_error
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,75 @@
1
+ # Fix wrong header unescaping in RestClient library.
2
+ module RestClient
3
+ class Request
4
+ def make_headers user_headers
5
+ unless @cookies.empty?
6
+ user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{val}" }.sort.join('; ')
7
+ end
8
+ headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
9
+ headers.merge!(@payload.headers) if @payload
10
+ headers
11
+ end
12
+ end
13
+ end
14
+
15
+ module VagrantPlugins
16
+ module Proxmox
17
+ class Plugin < Vagrant.plugin ('2')
18
+
19
+ name 'Proxmox'
20
+ description <<-DESC
21
+ This plugin installs a provider that allows Vagrant to manage
22
+ machines on Proxmox.
23
+ DESC
24
+
25
+ config(:proxmox, :provider) do
26
+ require_relative 'config'
27
+ Config
28
+ end
29
+
30
+ provider(:proxmox, parallel: true) do
31
+ # Setup logging and i18n
32
+ setup_logging
33
+ setup_i18n
34
+
35
+ # Return the provider
36
+ require_relative 'provider'
37
+ Provider
38
+ end
39
+
40
+ # This initializes the internationalization strings.
41
+ def self.setup_i18n
42
+ I18n.load_path << File.expand_path('locales/en.yml', Proxmox.source_root)
43
+ I18n.reload!
44
+ end
45
+
46
+ # This sets up our log level to be whatever VAGRANT_LOG is.
47
+ def self.setup_logging
48
+ require 'log4r'
49
+
50
+ level = nil
51
+ begin
52
+ level = Log4r.const_get ENV['VAGRANT_LOG'].upcase
53
+ rescue NameError
54
+ # This means that the logging constant wasn't found,
55
+ # which is fine. We just keep `level` as `nil`. But
56
+ # we tell the user.
57
+ level = nil
58
+ end
59
+
60
+ # Some constants, such as "true" resolve to booleans, so the
61
+ # above error checking doesn't catch it. This will check to make
62
+ # sure that the log level is an integer, as Log4r requires.
63
+ level = nil if !level.is_a?(Integer)
64
+
65
+ # Set the logging level on all "vagrant" namespaced
66
+ # logs as long as we have a valid level.
67
+ if level
68
+ logger = Log4r::Logger.new 'vagrant_proxmox'
69
+ logger.outputters = Log4r::Outputter.stderr
70
+ logger.level = level
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+
4
+ class Provider < Vagrant.plugin('2', :provider)
5
+
6
+ def initialize machine
7
+ @machine = machine
8
+ end
9
+
10
+ def action name
11
+ # Attempt to get the action method from the Action class if it
12
+ # exists, otherwise return nil to show that we don't support the
13
+ # given action.
14
+ action_method = "action_#{name}"
15
+ return Action.send(action_method) if Action.respond_to?(action_method)
16
+ nil
17
+ end
18
+
19
+ def state
20
+ # Run a custom action we define called "read_state" which does
21
+ # what it says. It puts the state in the `:machine_state_id`
22
+ # key in the environment.
23
+ env = @machine.action 'read_state'
24
+
25
+ state_id = env[:machine_state_id]
26
+
27
+ # Get the short and long description
28
+ short = I18n.t "vagrant_proxmox.states.short_#{state_id}"
29
+ long = I18n.t "vagrant_proxmox.states.long_#{state_id}"
30
+
31
+ # Return the MachineState object
32
+ Vagrant::MachineState.new state_id, short, long
33
+ end
34
+
35
+ def ssh_info
36
+ # Run a custom action called "read_ssh_info" which does what it
37
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
38
+ # key in the environment.
39
+ env = @machine.action 'read_ssh_info'
40
+ env[:machine_ssh_info]
41
+ end
42
+
43
+ def to_s
44
+ id = @machine.id.nil? ? 'new' : @machine.id
45
+ "Proxmox (#{id})"
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ module VagrantPlugins
2
+ module Proxmox
3
+
4
+ VERSION = '0.0.2'
5
+
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require 'pathname'
2
+ require 'log4r'
3
+ require 'rest-client'
4
+ require 'retryable'
5
+ require 'active_support/core_ext/object/try'
6
+
7
+ require 'sanity_checks'
8
+ require 'vagrant-proxmox/plugin'
9
+ require 'vagrant-proxmox/errors'
10
+
11
+
12
+ module VagrantPlugins
13
+ module Proxmox
14
+ lib_path = Pathname.new(File.expand_path '../vagrant-proxmox', __FILE__)
15
+ autoload :Action, lib_path.join('action')
16
+ autoload :Errors, lib_path.join('errors')
17
+
18
+ def self.source_root
19
+ @source_root ||= Pathname.new(File.expand_path '../../', __FILE__)
20
+ end
21
+ end
22
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,66 @@
1
+ en:
2
+ vagrant_proxmox:
3
+ already_running: 'The virtual machine is already up and running'
4
+ already_stopped: "The virtual machine is already stopped"
5
+ creating_vm: 'Creating the virtual machine...'
6
+ destroying_vm: 'Destroying the virtual machine...'
7
+ done: "Done!"
8
+ errors:
9
+ create_vm_timeout: |-
10
+ Creating the vm on the proxmox server timed out.
11
+ communication_error: |-
12
+ Unable to communicate with proxmox server:
13
+
14
+ %{error_msg}
15
+ destroy_vm_timeout: |-
16
+ Destroying the vm on the proxmox server timed out.
17
+ no_endpoint_specified: 'No endpoint specified.'
18
+ no_os_template_specified: 'No os_template specified.'
19
+ no_password_specified: 'No password specified.'
20
+ no_user_name_specified: 'No user_name specified.'
21
+ no_vm_id_available: |-
22
+ Unable to create a new virtual machine on the proxmox server.
23
+ None of the configured vm_ids is available.
24
+ proxmox_task_failed: |-
25
+ A task on the proxmox server failed.
26
+ It returned the following exit status:
27
+
28
+ %{proxmox_exit_status}
29
+ rsync_error: |-
30
+ There was an error when attemping to rsync a share folder.
31
+ Please inspect the error message below for more info.
32
+
33
+ Host path: %{hostpath}
34
+ Guest path: %{guestpath}
35
+ Error: %{stderr}
36
+ shutdown_vm_timeout: |-
37
+ Stopping the vm on the proxmox server timed out.
38
+ start_vm_timeout: |-
39
+ Starting the vm on the proxmox server timed out.
40
+ stop_vm_timeout: |-
41
+ Stopping the vm on the proxmox server timed out.
42
+ timeout: 'Timeout error'
43
+ vm_creation_error: |-
44
+ Unable to create the virtual machine!
45
+
46
+ Cause: %{proxmox_exit_status}
47
+ not_created: 'The virtual machine is not created on the server!'
48
+ rsync_folder: |-
49
+ Rsyncing folder: %{hostpath} => %{guestpath}
50
+ shut_down_vm: 'Shutting down the virtual machine...'
51
+ starting_vm: 'Starting the virtual machine...'
52
+ states:
53
+ long_not_created: |-
54
+ The server is not created. Run `vagrant up` to create it.
55
+ long_running: |-
56
+ The server is up and running. Run `vagrant ssh` to access it.
57
+ long_stopped: |-
58
+ The server is stopped.
59
+ short_not_created: |-
60
+ not created
61
+ short_running: |-
62
+ running
63
+ short_stopped: |-
64
+ stopped
65
+ stopping_vm: 'Stopping the virtual machine...'
66
+ waiting_for_ssh_connection: 'Waiting for SSH connection...'
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'actions/proxmox_action_shared'
3
+
4
+ describe VagrantPlugins::Proxmox::Action::CleanupAfterDestroy do
5
+
6
+ let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
7
+ let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
8
+ let(:ui) { double('ui').as_null_object }
9
+
10
+ subject { described_class.new(-> (_) {}, environment) }
11
+
12
+ it_behaves_like 'a proxmox action call'
13
+
14
+ describe '#call', :need_box do
15
+ it 'should delete the directory `.vagrant/[:machine].name`' do
16
+ expect do
17
+ subject.call env
18
+ end.to change{File.exists?(".vagrant/machines/#{env[:machine].name}/proxmox")}.to false
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'actions/proxmox_action_shared'
3
+
4
+ module VagrantPlugins::Proxmox
5
+
6
+ describe Action::ConnectProxmox do
7
+
8
+ let(:environment) { Vagrant::Environment.new vagrantfile_name: 'dummy_box/Vagrantfile' }
9
+ let(:env) { {machine: environment.machine(environment.primary_machine_name, :proxmox)} }
10
+
11
+ subject { described_class.new(-> (_) {}, environment) }
12
+
13
+ before { VagrantPlugins::Proxmox::Plugin.setup_i18n }
14
+
15
+ describe '#call' do
16
+
17
+ before do
18
+ allow(RestClient).to receive(:post).and_return({data: {ticket: 'valid_ticket', CSRFPreventionToken: 'valid_token'}}.to_json)
19
+ end
20
+
21
+ it_behaves_like 'a proxmox action call'
22
+
23
+ it 'should call the REST API access/ticket' do
24
+ RestClient.should_receive(:post).with('https://your.proxmox.server/api2/json/access/ticket', {username: 'vagrant', password: 'password'})
25
+ subject.call env
26
+ end
27
+
28
+ it 'should store the access ticket in env[:proxmox_ticket]' do
29
+ subject.call env
30
+ env[:proxmox_ticket].should == 'valid_ticket'
31
+ end
32
+
33
+ it 'should store the access ticket in env[:proxmox_csrf_prevention_token]' do
34
+ subject.call env
35
+ env[:proxmox_csrf_prevention_token].should == 'valid_token'
36
+ end
37
+
38
+ describe 'when the server communication fails' do
39
+ before { RestClient.stub(:post).and_return nil }
40
+ specify do
41
+ expect { subject.call env }.to raise_error Errors::CommunicationError
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
49
+ end