vagrant-proxmox 0.0.3 → 0.0.5

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