vagrant-proxmox 0.0.3 → 0.0.5

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sanity_checks.rb +1 -1
  3. data/lib/vagrant-proxmox.rb +1 -2
  4. data/lib/vagrant-proxmox/action.rb +133 -86
  5. data/lib/vagrant-proxmox/action/connect_proxmox.rb +10 -10
  6. data/lib/vagrant-proxmox/action/create_vm.rb +33 -28
  7. data/lib/vagrant-proxmox/action/destroy_vm.rb +10 -6
  8. data/lib/vagrant-proxmox/action/get_node_list.rb +8 -6
  9. data/lib/vagrant-proxmox/action/is_created.rb +1 -0
  10. data/lib/vagrant-proxmox/action/is_stopped.rb +1 -0
  11. data/lib/vagrant-proxmox/action/message_file_not_found.rb +21 -0
  12. data/lib/vagrant-proxmox/action/message_not_running.rb +20 -0
  13. data/lib/vagrant-proxmox/action/message_upload_server_error.rb +20 -0
  14. data/lib/vagrant-proxmox/action/proxmox_action.rb +6 -22
  15. data/lib/vagrant-proxmox/action/read_ssh_info.rb +1 -0
  16. data/lib/vagrant-proxmox/action/read_state.rb +11 -14
  17. data/lib/vagrant-proxmox/action/select_node.rb +23 -0
  18. data/lib/vagrant-proxmox/action/shutdown_vm.rb +8 -8
  19. data/lib/vagrant-proxmox/action/start_vm.rb +20 -12
  20. data/lib/vagrant-proxmox/action/stop_vm.rb +9 -8
  21. data/lib/vagrant-proxmox/action/sync_folders.rb +1 -1
  22. data/lib/vagrant-proxmox/action/upload_iso_file.rb +38 -0
  23. data/lib/vagrant-proxmox/action/upload_template_file.rb +38 -0
  24. data/lib/vagrant-proxmox/config.rb +85 -5
  25. data/lib/vagrant-proxmox/errors.rb +22 -2
  26. data/lib/vagrant-proxmox/plugin.rb +0 -14
  27. data/lib/vagrant-proxmox/proxmox/connection.rb +217 -0
  28. data/lib/vagrant-proxmox/proxmox/errors.rb +23 -0
  29. data/lib/vagrant-proxmox/version.rb +1 -1
  30. data/locales/en.yml +29 -2
  31. data/spec/actions/cleanup_after_destroy_action_spec.rb +2 -2
  32. data/spec/actions/connect_proxmox_action_spec.rb +32 -15
  33. data/spec/actions/create_vm_action_spec.rb +155 -130
  34. data/spec/actions/destroy_vm_action_spec.rb +50 -23
  35. data/spec/actions/get_node_list_action_spec.rb +25 -12
  36. data/spec/actions/is_created_action_spec.rb +8 -8
  37. data/spec/actions/is_stopped_action_spec.rb +8 -8
  38. data/spec/actions/message_already_running_action_spec.rb +2 -2
  39. data/spec/actions/message_already_stopped_action_spec.rb +2 -2
  40. data/spec/actions/message_file_not_found_spec.rb +23 -0
  41. data/spec/actions/message_not_created_action_spec.rb +2 -2
  42. data/spec/actions/message_not_running_action_spec.rb +23 -0
  43. data/spec/actions/message_upload_server_error_spec.rb +23 -0
  44. data/spec/actions/proxmox_action_shared.rb +0 -64
  45. data/spec/actions/proxmox_action_spec.rb +57 -0
  46. data/spec/actions/read_ssh_info_action_spec.rb +6 -6
  47. data/spec/actions/read_state_action_spec.rb +36 -34
  48. data/spec/actions/select_node_spec.rb +33 -0
  49. data/spec/actions/shutdown_vm_action_spec.rb +50 -22
  50. data/spec/actions/start_vm_action_spec.rb +87 -33
  51. data/spec/actions/stop_vm_action_spec.rb +50 -23
  52. data/spec/actions/sync_folders_action_spec.rb +9 -9
  53. data/spec/actions/upload_iso_file_action_spec.rb +70 -0
  54. data/spec/actions/upload_template_file_action_spec.rb +70 -0
  55. data/spec/commands/destroy_command_spec.rb +25 -25
  56. data/spec/commands/halt_command_spec.rb +17 -17
  57. data/spec/commands/provision_command_spec.rb +22 -9
  58. data/spec/commands/ssh_command_spec.rb +18 -5
  59. data/spec/commands/ssh_run_command_spec.rb +19 -6
  60. data/spec/commands/status_command_spec.rb +2 -2
  61. data/spec/commands/up_command_spec.rb +174 -34
  62. data/spec/config_spec.rb +325 -46
  63. data/spec/plugin_spec.rb +1 -8
  64. data/spec/provider_spec.rb +4 -4
  65. data/spec/proxmox/connection_spec.rb +662 -0
  66. data/spec/proxmox/rest_call_shared.rb +41 -0
  67. data/spec/sanity_checks_spec.rb +1 -1
  68. data/spec/spec_helper.rb +15 -97
  69. data/spec/spec_helpers/common_helpers.rb +111 -0
  70. data/spec/spec_helpers/time_helpers.rb +90 -0
  71. metadata +161 -45
data/spec/plugin_spec.rb CHANGED
@@ -2,14 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe 'Vagrant Proxmox plugin' do
4
4
 
5
- specify { Vagrant.should have_plugin 'Proxmox' }
6
-
7
- describe 'contains a fix for RestClient header unescaping bug' do
8
- let(:request) { RestClient::Request.new method: :get, url: 'url', cookies: { key: '+%20%21' }}
9
- it 'should not unescape the cookies' do
10
- request.make_headers({})['Cookie'].should == 'key=+%20%21'
11
- end
12
- end
5
+ specify { expect(Vagrant).to have_plugin 'Proxmox' }
13
6
 
14
7
  describe 'when vagrant log level is set in ENV[VAGRANT_LOG]' do
15
8
  before { ENV['VAGRANT_LOG'] = 'DEBUG' }
@@ -13,10 +13,10 @@ module VagrantPlugins::Proxmox
13
13
 
14
14
  describe '#ssh_info', :need_box do
15
15
  it 'should call the appropriate actions and return the ssh info' do
16
- Action::ConfigValidate.should be_called
17
- Action::ConnectProxmox.should be_called { |env| env[:proxmox_ticket] = 'ticket' }
18
- Action::ReadSSHInfo.should be_called { |env| env[:machine_ssh_info] = 'ssh_info'}
19
- subject.ssh_info.should == 'ssh_info'
16
+ expect(Action::ConfigValidate).to be_called
17
+ expect(Action::ConnectProxmox).to be_called
18
+ expect(Action::ReadSSHInfo).to be_called { |env| env[:machine_ssh_info] = 'ssh_info'}
19
+ expect(subject.ssh_info).to eq('ssh_info')
20
20
  end
21
21
  end
22
22
 
@@ -0,0 +1,662 @@
1
+ require 'spec_helper'
2
+ require 'proxmox/rest_call_shared'
3
+ require 'tempfile'
4
+
5
+ module VagrantPlugins::Proxmox
6
+
7
+ describe Connection do
8
+
9
+ let(:api_url) { 'https://proxmox.example.com/api' }
10
+ let(:username) { 'user' }
11
+ let(:password) { 'password' }
12
+ let(:ticket) { 'valid ticket' }
13
+ let(:csrf_token) { 'csrf prevention token' }
14
+ let(:task_upid) { 'UPID:localhost:0000F6ED:00F8E25F:5268CD3B:task_type:100:vagrant@pve:' }
15
+ let(:task_response) { {data: task_upid} }
16
+ let(:params) { {} }
17
+ let(:connection_opts) { {} }
18
+ let(:task_timeout) { 40 }
19
+ let(:task_status_check_interval) { 5 }
20
+ let(:imgcopy_timeout) { 85 }
21
+ let(:vm_type) { '' }
22
+ let(:node) { 'localhost' }
23
+ let(:machine_id) { '100' }
24
+ let(:vm_info) { {type: vm_type, node: node, id: machine_id} }
25
+
26
+ subject(:connection) { Connection.new api_url, connection_opts }
27
+
28
+ describe 'contains a fix for RestClient header unescaping bug' do
29
+ let(:request) { RestClient::Request.new method: :get, url: 'url', cookies: {key: '+%20%21'} }
30
+ it 'should not unescape the cookies' do
31
+ expect(request.make_headers({})['Cookie']).to eq('key=+%20%21')
32
+ end
33
+ end
34
+
35
+ describe '#initialize' do
36
+
37
+ describe '#api_url' do
38
+ subject { super().api_url }
39
+ it { is_expected.to eq(api_url) }
40
+ end
41
+
42
+ context 'with default values' do
43
+ describe '#vm_id_range' do
44
+ subject { super().vm_id_range }
45
+ it { is_expected.to eq(900..999) }
46
+ end
47
+
48
+ describe '#task_timeout' do
49
+ subject { super().task_timeout }
50
+ it { is_expected.to eq(60) }
51
+ end
52
+
53
+ describe '#task_status_check_interval' do
54
+ subject { super().task_status_check_interval }
55
+ it { is_expected.to eq(2) }
56
+ end
57
+
58
+ describe '#imgcopy_timeout' do
59
+ subject { super().imgcopy_timeout }
60
+ it { is_expected.to eq(120) }
61
+ end
62
+ end
63
+
64
+ context 'with custom values' do
65
+ let(:connection_opts) { {vm_id_range: (500..599), task_timeout: 90, task_status_check_interval: 3, imgcopy_timeout: 83} }
66
+
67
+ describe '#vm_id_range' do
68
+ subject { super().vm_id_range }
69
+ it { is_expected.to eq(500..599) }
70
+ end
71
+
72
+ describe '#task_timeout' do
73
+ subject { super().task_timeout }
74
+ it { is_expected.to eq(90) }
75
+ end
76
+
77
+ describe '#task_status_check_interval' do
78
+ subject { super().task_status_check_interval }
79
+ it { is_expected.to eq(3) }
80
+ end
81
+
82
+ describe '#imgcopy_timeout' do
83
+ subject { super().imgcopy_timeout }
84
+ it { is_expected.to eq(83) }
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#login' do
90
+
91
+ before do
92
+ allow(RestClient).to receive_messages :post => {data: {ticket: ticket, CSRFPreventionToken: csrf_token}}.to_json
93
+ end
94
+
95
+ it 'should call the REST API access/ticket' do
96
+ expect(RestClient).to receive(:post).with("#{api_url}/access/ticket", {username: username, password: password}, anything)
97
+ connection.login username: username, password: password
98
+ end
99
+
100
+ context 'with valid credentials' do
101
+ it 'should store the access ticket token' do
102
+ connection.login username: username, password: password
103
+ expect(connection.ticket).to eq(ticket)
104
+ end
105
+ it 'should store the CSRF prevention token' do
106
+ connection.login username: username, password: password
107
+ expect(connection.csrf_token).to eq(csrf_token)
108
+ end
109
+ end
110
+
111
+ context 'with invalid credentials' do
112
+ it 'should raise an invalid credentials error' do
113
+ allow(RestClient).to receive(:post).and_raise RestClient::InternalServerError
114
+ expect do
115
+ connection.login username: username, password: password
116
+ end.to raise_error ApiError::InvalidCredentials
117
+ end
118
+ end
119
+
120
+ context 'when a network error occurs' do
121
+ it 'should raise a connection error' do
122
+ allow(RestClient).to receive(:post).and_raise SocketError
123
+ expect do
124
+ connection.login username: username, password: password
125
+ end.to raise_error ApiError::ConnectionError
126
+ end
127
+ end
128
+ end
129
+
130
+ describe '#get_node_list' do
131
+
132
+ before { allow(RestClient).to receive_messages :get => {data: [{node: 'node1'}, {node: 'node2'}]}.to_json }
133
+
134
+ it 'should request the node list' do
135
+ expect(RestClient).to receive(:get).with("#{api_url}/nodes", anything)
136
+ connection.get_node_list
137
+ end
138
+
139
+ it 'should return an array of nodes' do
140
+ expect(connection.get_node_list).to eq(['node1', 'node2'])
141
+ end
142
+ end
143
+
144
+ describe '#get_vm_state' do
145
+
146
+ let(:status) { '' }
147
+
148
+ before do
149
+ allow(connection).to receive_messages :get_vm_info => vm_info
150
+ allow(RestClient).to receive(:get).with("#{api_url}/nodes/localhost/#{vm_type}/#{machine_id}/status/current", anything).
151
+ and_return({data: {status: status}}.to_json)
152
+ end
153
+
154
+ context 'when the machine is an openvz container' do
155
+
156
+ let(:vm_type) { 'openvz' }
157
+
158
+ it 'should request a machine state' do
159
+ expect(RestClient).to receive(:get).with("#{api_url}/nodes/localhost/openvz/100/status/current", anything)
160
+ connection.get_vm_state(100)
161
+ end
162
+
163
+ context 'when the machine is stopped' do
164
+ let(:status) { :stopped }
165
+ it 'should return :stopped' do
166
+ expect(connection.get_vm_state(100)).to eq(:stopped)
167
+ end
168
+ end
169
+
170
+ context 'when the machine is running' do
171
+ let(:status) { :running }
172
+ it 'should return :running' do
173
+ expect(connection.get_vm_state(100)).to eq(:running)
174
+ end
175
+ end
176
+ end
177
+
178
+ context 'when the machine is a qemu emulation' do
179
+
180
+ let(:vm_type) { 'qemu' }
181
+
182
+ it 'should request a machine state' do
183
+ expect(RestClient).to receive(:get).with("#{api_url}/nodes/localhost/qemu/100/status/current", anything)
184
+ connection.get_vm_state(100)
185
+ end
186
+
187
+ context 'when the machine is stopped' do
188
+ let(:status) { :stopped }
189
+ it 'should return :stopped' do
190
+ expect(connection.get_vm_state(100)).to eq(:stopped)
191
+ end
192
+ end
193
+
194
+ context 'when the machine is running' do
195
+ let(:status) { :running }
196
+ it 'should return :running' do
197
+ expect(connection.get_vm_state(100)).to eq(:running)
198
+ end
199
+ end
200
+ end
201
+
202
+ context 'when the machine is not created' do
203
+
204
+ before { allow(RestClient).to receive(:get).and_raise RestClient::InternalServerError }
205
+
206
+ it 'should return :not_created' do
207
+ expect(connection.get_vm_state(100)).to eq(:not_created)
208
+ end
209
+ end
210
+ end
211
+
212
+ describe '#get' do
213
+
214
+ it_should_behave_like 'a rest api call', :get
215
+
216
+ before { allow(RestClient).to receive_messages get: {data: {}}.to_json }
217
+
218
+ context 'with valid parameters' do
219
+ it 'should call the REST API with the correct parameters' do
220
+ expect(RestClient).to receive(:get).with("#{api_url}/resource", anything)
221
+ connection.send :get, '/resource'
222
+ end
223
+ it 'should send the authentication authorization ticket as a cookie' do
224
+ allow_any_instance_of(Connection).to receive_messages ticket: ticket
225
+ expect(RestClient).to receive(:get).with(anything, hash_including(cookies: {PVEAuthCookie: ticket}))
226
+ connection.send :get, '/resource'
227
+ end
228
+ it 'should return the JSON parsed response data' do
229
+ expect(RestClient).to receive_messages :get => ({data: 'some_response'}.to_json)
230
+ response = connection.send :get, '/resource'
231
+ expect(response).to eq({data: 'some_response'})
232
+ end
233
+ end
234
+ end
235
+
236
+
237
+ describe '#wait_for_completion' do
238
+
239
+ it 'should get the task exit status' do
240
+ expect(connection).to receive(:get_task_exitstatus).with(task_upid).and_return('OK')
241
+ connection.send(:wait_for_completion, task_response: task_response, timeout_message: '')
242
+ end
243
+
244
+ context 'when the task is completed' do
245
+ before { allow(connection).to receive_messages get_task_exitstatus: 'OK' }
246
+ it 'should return the task exit status' do
247
+ expect(connection.send(:wait_for_completion, task_response: task_response, timeout_message: '')).to eq('OK')
248
+ end
249
+ end
250
+
251
+ context 'when the task times out' do
252
+
253
+ before do
254
+ Timecop.freeze
255
+ allow(connection).to receive_messages :get_task_exitstatus => nil
256
+ allow(connection).to receive_messages :task_timeout => task_timeout
257
+ allow(connection).to receive_messages :imgcopy_timeout => imgcopy_timeout
258
+ allow(connection).to receive_messages :task_status_check_interval => task_status_check_interval
259
+ allow(connection).to receive(:sleep) { |duration| Timecop.travel(Time.now + duration) }
260
+ end
261
+
262
+ after do
263
+ Timecop.return
264
+ end
265
+
266
+ it 'should raise an timeout error' do
267
+ expect { connection.send(:wait_for_completion, task_response: task_response, timeout_message: '') }.to raise_error VagrantPlugins::Proxmox::Errors::Timeout
268
+ end
269
+
270
+ it 'should check the task status a given number of times' do
271
+ task_iterations = task_timeout / task_status_check_interval + 1
272
+ expect(connection).to receive(:get_task_exitstatus).exactly(task_iterations).times
273
+ connection.send(:wait_for_completion, task_response: task_response, timeout_message: '') rescue nil
274
+ end
275
+
276
+ it 'should check the task status a given number of times' do
277
+ task_iterations = task_timeout / task_status_check_interval + 1
278
+ expect(connection).to receive(:get_task_exitstatus).exactly(task_iterations).times
279
+ connection.send(:wait_for_completion, task_response: task_response, timeout_message: '') rescue nil
280
+ end
281
+
282
+ context 'when it is a regular task' do
283
+
284
+ it 'should wait out the task_timeout' do
285
+ connection.send(:wait_for_completion, task_response: task_response, timeout_message: '') rescue nil
286
+ expect(Time).to have_elapsed task_timeout.seconds
287
+ end
288
+ end
289
+
290
+ context 'when it is an upload task' do
291
+
292
+ let(:task_upid) { 'UPID:localhost:0000F6EF:00F8E35F:E268CD3B:imgcopy:100:vagrant@pve:' }
293
+
294
+ it 'should wait out the imgcopy_timeout' do
295
+ connection.send(:wait_for_completion, task_response: task_response, timeout_message: '') rescue nil
296
+ expect(Time).to have_elapsed imgcopy_timeout.seconds
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ describe '#get_task_exitstatus' do
303
+
304
+ it 'should request the task state from the proxmox server given in the task UPID' do
305
+ expect(RestClient).to receive(:get).with("#{api_url}/nodes/localhost/tasks/#{task_upid}/status", anything).
306
+ and_return({data: {}}.to_json)
307
+ connection.send(:get_task_exitstatus, task_upid)
308
+ end
309
+
310
+ context 'the task has exited' do
311
+ it 'should return the exit status' do
312
+ allow(RestClient).to receive_messages get: {data: {upid: task_response, status: 'stopped', exitstatus: 'OK'}}.to_json
313
+ expect(connection.send(:get_task_exitstatus, task_upid)).to eq('OK')
314
+ end
315
+ end
316
+
317
+ context 'the task is still running' do
318
+ it 'should return nil' do
319
+ allow(RestClient).to receive_messages get: {data: {upid: task_response, status: 'running'}}.to_json
320
+ expect(connection.send(:get_task_exitstatus, task_upid)).to eq(nil)
321
+ end
322
+ end
323
+ end
324
+
325
+ describe '#delete_vm' do
326
+
327
+ before do
328
+ allow(connection).to receive_messages :delete => {data: 'task_id'}.to_json
329
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
330
+ allow(connection).to receive_messages :get_vm_info => vm_info
331
+ end
332
+
333
+ context 'when the machine is an openvz container' do
334
+
335
+ let(:vm_type) { 'openvz' }
336
+
337
+ it 'should call delete with the node and vm as parameter' do
338
+ expect(connection).to receive(:delete).with("/nodes/localhost/openvz/100")
339
+ connection.delete_vm 100
340
+ end
341
+
342
+ it 'waits for completion of the server task' do
343
+ expect(connection).to receive(:wait_for_completion)
344
+ connection.delete_vm 100
345
+ end
346
+
347
+ it 'should return the task exit status' do
348
+ expect(connection.delete_vm(100)).to eq('OK')
349
+ end
350
+ end
351
+
352
+ context 'when the machine is a qemu emulation' do
353
+
354
+ let(:vm_type) { 'qemu' }
355
+
356
+ it 'should call delete with the node and vm as parameter' do
357
+ expect(connection).to receive(:delete).with("/nodes/localhost/qemu/100")
358
+ connection.delete_vm 100
359
+ end
360
+
361
+ it 'waits for completion of the server task' do
362
+ expect(connection).to receive(:wait_for_completion)
363
+ connection.delete_vm 100
364
+ end
365
+
366
+ it 'should return the task exit status' do
367
+ expect(connection.delete_vm(100)).to eq('OK')
368
+ end
369
+ end
370
+ end
371
+
372
+ describe '#get_vm_type' do
373
+
374
+ before do
375
+ allow(RestClient).to receive(:get).with("#{api_url}/cluster/resources?type=vm", anything).
376
+ and_return({data: vm_list}.to_json)
377
+ end
378
+ let(:vm_list) { [{node: 'node', id: 'openvz/100'}, {node: 'anothernode', id: 'qemu/101'}] }
379
+
380
+ it 'should return the correct vm_information' do
381
+ expect(connection.send(:get_vm_info, 101)).to eq({id: 101, node: 'anothernode', type: 'qemu'})
382
+ end
383
+
384
+ context 'when the vm is not found' do
385
+ it 'should return nil' do
386
+ expect(connection.send(:get_vm_info, 105)).to be_nil
387
+ end
388
+ end
389
+ end
390
+
391
+ describe '#delete' do
392
+
393
+ it_should_behave_like 'a rest api call', :delete
394
+
395
+ before { allow(RestClient).to receive_messages delete: {data: {}}.to_json }
396
+
397
+ it 'should send a post request that deletes the openvz container' do
398
+ expect(RestClient).to receive(:delete).with("#{api_url}/nodes/localhost/openvz/100", anything)
399
+ connection.send :delete, "/nodes/localhost/openvz/100"
400
+ end
401
+ end
402
+
403
+ describe '#get_free_vm_id' do
404
+
405
+ it 'should query the proxmox server for all qemu and openvz machines' do
406
+ expect(RestClient).to receive(:get).with("#{api_url}/cluster/resources?type=vm", anything).and_return({data: []}.to_json)
407
+ connection.get_free_vm_id
408
+ end
409
+
410
+ describe 'find the smallest unused vm_id in the configured range' do
411
+
412
+ before { connection.vm_id_range = 3..4 }
413
+
414
+ context 'when free vm_ids are available' do
415
+ [
416
+ {used_vm_ids: {data: []}, smallest_free_in_range: 3},
417
+ {used_vm_ids: {data: [{vmid: 3}]}, smallest_free_in_range: 4},
418
+ {used_vm_ids: {data: [{vmid: 1}]}, smallest_free_in_range: 3},
419
+ {used_vm_ids: {data: [{vmid: 1}, {vmid: 3}]}, smallest_free_in_range: 4},
420
+ ].each do |example|
421
+ it 'should return the smallest unused vm_id in the configured vm_id_range' do
422
+ allow(RestClient).to receive_messages :get => example[:used_vm_ids].to_json
423
+ expect(subject.send(:get_free_vm_id)).to eq(example[:smallest_free_in_range])
424
+ end
425
+ end
426
+ end
427
+
428
+ context 'when no vm_ids are available' do
429
+ it 'should throw a no_vm_id_available error' do
430
+ allow(RestClient).to receive_messages :get => {data: [{vmid: 1}, {vmid: 2}, {vmid: 3}, {vmid: 4}]}.to_json
431
+ expect { subject.send :get_free_vm_id }.to raise_error Errors::NoVmIdAvailable
432
+ end
433
+ end
434
+ end
435
+ end
436
+
437
+ describe '#create_vm' do
438
+
439
+ before do
440
+ allow(connection).to receive_messages :post => {data: 'task_id'}.to_json
441
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
442
+ end
443
+
444
+ it 'should call post with the correct parameters' do
445
+ expect(connection).to receive(:post).with('/nodes/localhost/openvz', 'params')
446
+ connection.create_vm node: 'localhost', vm_type: 'openvz', params: 'params'
447
+ end
448
+
449
+ it 'waits for completion of the server task' do
450
+ expect(connection).to receive(:wait_for_completion)
451
+ connection.create_vm node: 'localhost', vm_type: 'openvz', params: params
452
+ end
453
+
454
+ it 'should return the task exit status' do
455
+ expect(connection.create_vm(node: 'localhost', vm_type: 'openvz', params: 'params')).to eq('OK')
456
+ end
457
+ end
458
+
459
+ describe '#post' do
460
+
461
+ it_should_behave_like 'a rest api call', :post
462
+
463
+ before { allow(RestClient).to receive_messages post: {data: {}}.to_json }
464
+
465
+ it 'should call the REST API with the correct parameters' do
466
+ expect(RestClient).to receive(:post).with("#{api_url}/resource", params, anything)
467
+ connection.send :post, '/resource', params
468
+ end
469
+
470
+ it 'should return the JSON parsed response data' do
471
+ expect(RestClient).to receive(:post).and_return({data: 'some_response'}.to_json)
472
+ response = connection.send :post, '/resource', params
473
+ expect(response).to eq({data: 'some_response'})
474
+ end
475
+
476
+ describe 'sending ticket and token as part of the post request' do
477
+
478
+ context 'when authorized' do
479
+ it 'should send the authentication authorization ticket as a cookie and the csrf token' do
480
+ allow_any_instance_of(Connection).to receive_messages ticket: ticket
481
+ allow_any_instance_of(Connection).to receive_messages csrf_token: csrf_token
482
+ expect(RestClient).to receive(:post).with(anything, anything, hash_including({CSRFPreventionToken: csrf_token, cookies: {PVEAuthCookie: ticket}}))
483
+ connection.send :post, '/resource', params
484
+ end
485
+ end
486
+
487
+ context 'when not authorized' do
488
+ it 'it should send the request without ticket and token' do
489
+ allow_any_instance_of(Connection).to receive_messages ticket: nil
490
+ allow_any_instance_of(Connection).to receive_messages csrf_token: nil
491
+ expect(RestClient).to receive(:post).with anything, anything, hash_not_including(:CSRFPreventionToken, :cookies)
492
+ connection.send :post, '/resource', params
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ describe '#start_vm' do
499
+
500
+ before do
501
+ allow(connection).to receive_messages :post => {data: 'task_id'}.to_json
502
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
503
+ allow(connection).to receive_messages :get_vm_info => vm_info
504
+ end
505
+
506
+ it 'waits for completion of the server task' do
507
+ expect(connection).to receive(:wait_for_completion)
508
+ connection.start_vm '100'
509
+ end
510
+
511
+ it 'should return the task exit status' do
512
+ expect(connection.start_vm('100')).to eq('OK')
513
+ end
514
+
515
+ context 'when the machine is an openvz container' do
516
+
517
+ let(:vm_type) { 'openvz' }
518
+
519
+ it 'should call post with the correct parameters' do
520
+ expect(connection).to receive(:post).with("/nodes/localhost/openvz/100/status/start", nil)
521
+ connection.start_vm '100'
522
+ end
523
+ end
524
+
525
+ context 'when the machine is a qemu emulation' do
526
+
527
+ let(:vm_type) { 'qemu' }
528
+
529
+ it 'should call post with the correct parameters' do
530
+ expect(connection).to receive(:post).with("/nodes/localhost/qemu/100/status/start", nil)
531
+ connection.start_vm '100'
532
+ end
533
+ end
534
+ end
535
+
536
+ describe '#stop_vm' do
537
+
538
+ before do
539
+ allow(connection).to receive_messages :post => {data: 'task_id'}.to_json
540
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
541
+ allow(connection).to receive_messages :get_vm_info => vm_info
542
+ end
543
+
544
+ it 'waits for completion of the server task' do
545
+ expect(connection).to receive(:wait_for_completion)
546
+ connection.stop_vm '100'
547
+ end
548
+
549
+ it 'should return the task exit status' do
550
+ expect(connection.stop_vm('100')).to eq('OK')
551
+ end
552
+
553
+ context 'when the machine is an openvz container' do
554
+
555
+ let(:vm_type) { 'openvz' }
556
+
557
+ it 'should call post with the correct parameters' do
558
+ expect(connection).to receive(:post).with("/nodes/localhost/openvz/100/status/stop", nil)
559
+ connection.stop_vm '100'
560
+ end
561
+ end
562
+
563
+ context 'when the machine is a qemu emulation' do
564
+
565
+ let(:vm_type) { 'qemu' }
566
+
567
+ it 'should call post with the correct parameters' do
568
+ expect(connection).to receive(:post).with("/nodes/localhost/qemu/100/status/stop", nil)
569
+ connection.stop_vm '100'
570
+ end
571
+ end
572
+ end
573
+
574
+ describe '#shutdown_vm' do
575
+
576
+ before do
577
+ allow(connection).to receive_messages :post => {data: 'task_id'}.to_json
578
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
579
+ allow(connection).to receive_messages :get_vm_info => vm_info
580
+ end
581
+
582
+ it 'waits for completion of the server task' do
583
+ expect(connection).to receive(:wait_for_completion)
584
+ connection.shutdown_vm '100'
585
+ end
586
+
587
+ it 'should return the task exit status' do
588
+ expect(connection.shutdown_vm('100')).to eq('OK')
589
+ end
590
+
591
+ context 'when the machine is an openvz container' do
592
+
593
+ let(:vm_type) { 'openvz' }
594
+
595
+ it 'should call post with the correct parameters' do
596
+ expect(connection).to receive(:post).with("/nodes/localhost/openvz/100/status/shutdown", nil)
597
+ connection.shutdown_vm '100'
598
+ end
599
+ end
600
+
601
+ context 'when the machine is a qemu emulation' do
602
+
603
+ let(:vm_type) { 'qemu' }
604
+ it 'should call post with the correct parameters' do
605
+ expect(connection).to receive(:post).with("/nodes/localhost/qemu/100/status/shutdown", nil)
606
+ connection.shutdown_vm '100'
607
+ end
608
+ end
609
+ end
610
+
611
+ describe '#upload_file' do
612
+
613
+ let (:file) { '/my/dir/template.tar.gz' }
614
+ let (:storage_file_list) { [] }
615
+
616
+ before do
617
+ allow(connection).to receive(:post)
618
+ allow(File).to receive(:new).with(file, anything).and_return file
619
+ allow(RestClient).to receive(:get).with("#{api_url}/nodes/localhost/storage/local/content", anything()).
620
+ and_return(({data: storage_file_list}).to_json)
621
+ allow(connection).to receive_messages :wait_for_completion => 'OK'
622
+ end
623
+
624
+ it 'should call post with the correct parameters' do
625
+ expect(connection).to receive(:post).with('/nodes/localhost/storage/local/upload',
626
+ {:content => 'vztmpl', :filename => file, :node => 'localhost', :storage => 'local'})
627
+ connection.upload_file file, content_type: 'vztmpl', node: 'localhost', storage: 'local'
628
+ end
629
+
630
+ it 'waits for completion of the server task' do
631
+ expect(connection).to receive(:wait_for_completion)
632
+ connection.upload_file file, content_type: 'vztmpl', node: 'localhost', storage: 'local'
633
+ end
634
+
635
+ it 'should return the task exit status' do
636
+ expect(connection.upload_file file, content_type: 'vztmpl', node: 'localhost', storage: 'local').to eq('OK')
637
+ end
638
+
639
+ context 'when the file already exists in storage of the proxmox node' do
640
+
641
+ let (:storage_file_list) { [{volid: 'local:vztmpl/template.tar.gz'}] }
642
+
643
+ it 'should not upload the file' do
644
+ expect(connection).not_to receive(:post).with('/nodes/localhost/storage/local/upload', anything())
645
+ connection.upload_file file, content_type: 'vztmpl', node: 'localhost', storage: 'local'
646
+ end
647
+ end
648
+ end
649
+
650
+ describe '#list_storage_files' do
651
+ before do
652
+ expect(RestClient).to receive(:get).with("#{api_url}/nodes/node1/storage/local/content", anything()).
653
+ and_return(({data: [{volid: 'local:vztmpl/mytemplate.tar.gz'}]}).to_json)
654
+ end
655
+
656
+ it 'should return a list of the content of a storage' do
657
+ res = connection.list_storage_files node: 'node1', storage: 'local'
658
+ expect(res).to eq(['local:vztmpl/mytemplate.tar.gz'])
659
+ end
660
+ end
661
+ end
662
+ end