vagrant-cloudstack 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +13 -9
  4. data/CHANGELOG.md +30 -0
  5. data/Docker/Dockerfile +5 -8
  6. data/Docker/Dockerfile.chefdk_0_17 +2 -2
  7. data/Docker/Dockerfile.latest_dependencies +2 -2
  8. data/Docker/README.md +63 -35
  9. data/Gemfile +2 -2
  10. data/Gemfile.lock +307 -0
  11. data/README.md +3 -3
  12. data/Rakefile +2 -2
  13. data/build_rpm.sh +1 -1
  14. data/functional-tests/rsync/Vagrantfile.advanced_networking +1 -0
  15. data/functional-tests/vmlifecycle/Vagrantfile.advanced_networking +5 -7
  16. data/functional-tests/vmlifecycle/vmlifecycle_spec.rb +14 -2
  17. data/lib/vagrant-cloudstack/action/read_rdp_info.rb +9 -43
  18. data/lib/vagrant-cloudstack/action/read_ssh_info.rb +10 -44
  19. data/lib/vagrant-cloudstack/action/read_transport_info.rb +59 -0
  20. data/lib/vagrant-cloudstack/action/read_winrm_info.rb +10 -44
  21. data/lib/vagrant-cloudstack/action/run_instance.rb +607 -498
  22. data/lib/vagrant-cloudstack/action/terminate_instance.rb +17 -41
  23. data/lib/vagrant-cloudstack/config.rb +41 -166
  24. data/lib/vagrant-cloudstack/exceptions/exceptions.rb +7 -2
  25. data/lib/vagrant-cloudstack/service/cloudstack_resource_service.rb +17 -5
  26. data/lib/vagrant-cloudstack/version.rb +1 -1
  27. data/spec/spec_helper.rb +45 -0
  28. data/spec/vagrant-cloudstack/action/retrieve_public_ip_port_spec.rb +94 -0
  29. data/spec/vagrant-cloudstack/action/run_instance_spec.rb +609 -0
  30. data/spec/vagrant-cloudstack/action/terminate_instance_spec.rb +248 -0
  31. data/spec/vagrant-cloudstack/config_spec.rb +7 -7
  32. data/vagrant-cloudstack.gemspec +2 -1
  33. metadata +22 -10
  34. data/bootstrap.key +0 -27
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'vagrant-cloudstack/action/read_transport_info'
3
+ require 'vagrant-cloudstack/config'
4
+ require 'fog'
5
+
6
+ describe VagrantPlugins::Cloudstack::Action::ReadTransportInfo do
7
+ let(:action) {VagrantPlugins::Cloudstack::Action::ReadTransportInfo.new }
8
+
9
+ describe '#retrieve_public_ip_port' do
10
+ subject { action.retrieve_public_ip_port(cloudstack_compute, domain_config, machine) }
11
+
12
+ let(:cloudstack_compute) { double('Fog::Compute::Cloudstack') }
13
+ let(:machine) { double('Vagrant::Machine')}
14
+
15
+ let(:data_dir) { double('Pathname') }
16
+ let(:pf_public_port_file) { double('Pathname') }
17
+
18
+ let(:pf_ip_address) { 'ip_address_in_config' }
19
+ let(:pf_ip_address_from_server) { 'ip_address_from_server' }
20
+ let(:pf_ip_address_id) { 'ID of ip_address_in_config' }
21
+ let(:pf_public_port) { 'public_port_in_config' }
22
+ let(:pf_public_port_from_file) { 'public_port_from_file' }
23
+
24
+ let(:domain_config) do
25
+ config = VagrantPlugins::Cloudstack::Config.new
26
+ config.domain_config :cloudstack do |cfg|
27
+ cfg.pf_ip_address = pf_ip_address
28
+ cfg.pf_public_port = pf_public_port
29
+ cfg.pf_ip_address_id = pf_ip_address_id
30
+ end
31
+ config.finalize!
32
+ config.get_domain_config(:cloudstack)
33
+ end
34
+
35
+ context 'without both ip address and port in config' do
36
+ it 'retrieves those configured values' do
37
+ should eq [pf_ip_address, pf_public_port]
38
+ end
39
+ end
40
+
41
+ context 'port not configured' do
42
+ let(:pf_public_port) { nil }
43
+
44
+ it 'retrieves the active port stored on filesystem' do
45
+ expect(machine).to receive(:data_dir).and_return(data_dir)
46
+ expect(data_dir).to receive(:join).and_return(pf_public_port_file)
47
+ expect(pf_public_port_file).to receive(:file?).and_return(true)
48
+ expect(File).to receive(:read).and_return(pf_public_port_from_file)
49
+
50
+ expect(subject).to eq [pf_ip_address, pf_public_port_from_file]
51
+ end
52
+ end
53
+
54
+ context 'only ID of ip address specified (and public port)' do
55
+ let(:pf_ip_address) { nil }
56
+
57
+ it 'resolves, and returns, the ip address from the ID' do
58
+ response = {
59
+ 'listpublicipaddressesresponse' => {
60
+ 'count' =>1,
61
+ 'publicipaddress' =>[{
62
+ 'id' => pf_ip_address_id,
63
+ 'ipaddress' => pf_ip_address_from_server,
64
+ 'allocated' => '2016-05-06T13:58:04+0200',
65
+ 'zoneid' => 'UUID',
66
+ 'zonename' => 'Name',
67
+ 'issourcenat' =>false,
68
+ 'account' => 'Name',
69
+ 'domainid' => 'UUID',
70
+ 'domain' => 'Name',
71
+ 'forvirtualnetwork' =>true,
72
+ 'isstaticnat' =>false,
73
+ 'issystem' =>false,
74
+ 'associatednetworkid' => 'UUID',
75
+ 'associatednetworkname' => 'Name',
76
+ 'networkid' => 'UUID',
77
+ 'aclid' => 'UUID',
78
+ 'state' => 'Allocated',
79
+ 'physicalnetworkid' => 'UUID',
80
+ 'vpcid' => 'UUID',
81
+ 'tags' =>[],
82
+ 'isportable' =>false
83
+ }]
84
+ }
85
+ }
86
+ expect(cloudstack_compute).to receive(:list_public_ip_addresses)
87
+ .with(:id => pf_ip_address_id)
88
+ .and_return(response)
89
+
90
+ expect(subject).to eq [pf_ip_address_from_server, pf_public_port]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,609 @@
1
+ require 'spec_helper'
2
+ require 'vagrant-cloudstack/action/run_instance'
3
+ require 'vagrant-cloudstack/config'
4
+
5
+ require 'vagrant'
6
+ require 'fog'
7
+
8
+ describe VagrantPlugins::Cloudstack::Action::RunInstance do
9
+ let(:action) { VagrantPlugins::Cloudstack::Action::RunInstance.new(app, env) }
10
+
11
+ let(:create_servers_parameters) do
12
+ {
13
+ :display_name => DISPLAY_NAME,
14
+ :group => nil,
15
+ :zone_id => ZONE_ID,
16
+ :flavor_id => SERVICE_OFFERING_ID,
17
+ :image_id => TEMPLATE_ID,
18
+ 'network_ids' => NETWORK_ID
19
+ }
20
+ end
21
+
22
+ let(:fake_job_result) do
23
+ {
24
+ 'queryasyncjobresultresponse' => {
25
+ 'jobstatus' => 1,
26
+ 'jobresult' => {
27
+ 'portforwardingrule' => {
28
+ 'id' => PORT_FORWARDING_RULE_ID
29
+ },
30
+ 'networkacl' => {
31
+ 'id' => ACL_ID
32
+ },
33
+ 'virtualmachine' => {
34
+ 'password' => GENERATED_PASSWORD
35
+ }
36
+ }
37
+ }
38
+ }
39
+ end
40
+
41
+ let(:list_public_ip_addresses_response) do
42
+ {
43
+ 'listpublicipaddressesresponse' => {
44
+ 'count' => 1,
45
+ 'publicipaddress' =>
46
+ [
47
+ {
48
+ 'id' => PF_IP_ADDRESS_ID,
49
+ 'ipaddress' => PF_IP_ADDRESS,
50
+ 'associatednetworkid' => NETWORK_ID,
51
+ 'associatednetworkname' => NETWORK_NAME,
52
+ 'vpcid' => VPC_ID
53
+ }
54
+ ]
55
+ }
56
+ }
57
+ end
58
+
59
+ let(:network_type) { NETWORK_TYPE_ADVANCED }
60
+ let(:security_groups_enabled) { SECURITY_GROUPS_DISABLED }
61
+ let(:list_zones_response) do
62
+ {
63
+ 'listzonesresponse' => {
64
+ 'count' => 1,
65
+ 'zone' =>
66
+ [
67
+ {
68
+ 'tags' => [],
69
+ 'id' => ZONE_ID,
70
+ 'name' => ZONE_NAME,
71
+ 'networktype' => network_type,
72
+ 'securitygroupsenabled' => security_groups_enabled
73
+ }
74
+ ]
75
+ }
76
+ }
77
+ end
78
+
79
+ let(:list_service_offerings_response) do
80
+ {
81
+ 'listserviceofferingsresponse' => {
82
+ 'count' => 1,
83
+ 'serviceoffering' => [
84
+ {
85
+ 'id' => SERVICE_OFFERING_ID,
86
+ 'name' => SERVICE_OFFERING_NAME,
87
+ 'displaytext' => "Display version of #{SERVICE_OFFERING_NAME}"
88
+ }
89
+ ]
90
+ }
91
+ }
92
+ end
93
+
94
+ let(:list_templates_response) do
95
+ {
96
+ 'listtemplatesresponse' => {
97
+ 'count' => 1,
98
+ 'template' => [
99
+ {
100
+ 'id' => TEMPLATE_ID,
101
+ 'name' => TEMPLATE_NAME
102
+ }
103
+ ]
104
+ }
105
+ }
106
+ end
107
+
108
+ let(:list_networks_response) do
109
+ {
110
+ 'listnetworksresponse' => {
111
+ 'count' => 1,
112
+ 'network' => [
113
+ {
114
+ 'id' => NETWORK_ID,
115
+ 'name' => NETWORK_NAME,
116
+ 'vpcid' => VPC_ID,
117
+ 'aclid' => ACL_ID
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ end
123
+
124
+ let(:list_network_acl_lists_response) do
125
+ {
126
+ 'listnetworkacllistsresponse' => {
127
+ 'count' => 3,
128
+ 'networkacllist' => [
129
+ {
130
+ 'id' => ACL_ID,
131
+ 'name' => NETWORK_NAME,
132
+ 'vpcid' => VPC_ID
133
+ },
134
+ {
135
+ 'id' => '13fa8945-9248-13e5-4afa-525405b8977a', 'name' => 'default_allow',
136
+ 'description' => 'Default Network ACL Allow All'
137
+ },
138
+ {
139
+ 'id' => '13fa283b-9248-13e5-4afa-525405b8977a', 'name' => 'default_deny',
140
+ 'description' => 'Default Network ACL Deny All'
141
+ }
142
+ ]
143
+ }
144
+ }
145
+ end
146
+ let(:list_disk_offerings_response) do
147
+ {
148
+ 'listdiskofferingsresponse' => {
149
+ 'count' => 1,
150
+ 'diskoffering' => [
151
+ {
152
+ 'id' => DISK_OFFERING_ID,
153
+ 'name' => DISK_OFFERING_NAME
154
+ }
155
+ ]
156
+ }
157
+ }
158
+ end
159
+
160
+ let(:create_port_forwarding_rule_parameters) do
161
+ {
162
+ networkid: NETWORK_ID,
163
+ ipaddressid: PF_IP_ADDRESS_ID,
164
+ publicport: 49_152,
165
+ privateport: GUEST_PORT_SSH,
166
+ protocol: 'tcp',
167
+ virtualmachineid: SERVER_ID
168
+ }
169
+ end
170
+
171
+ let(:create_port_forwarding_rule_respones) do
172
+ {
173
+ 'createportforwardingruleresponse' => {
174
+ 'id' => PORT_FORWARDING_RULE_ID,
175
+ 'jobid' => JOB_ID
176
+ }
177
+ }
178
+ end
179
+
180
+ let(:create_network_acl_request) do
181
+ {
182
+ command: 'createNetworkACL',
183
+ aclid: ACL_ID,
184
+ action: 'Allow',
185
+ protocol: 'tcp',
186
+ cidrlist: PF_TRUSTED_NETWORKS,
187
+ startport: GUEST_PORT_SSH,
188
+ endport: GUEST_PORT_SSH,
189
+ icmpcode: nil,
190
+ icmptype: nil,
191
+ traffictype: 'Ingress'
192
+ }
193
+ end
194
+
195
+ let(:createNetworkACL_response) do
196
+ {
197
+ 'createnetworkaclresponse' => {
198
+ 'id' => '5dcb96b5-7785-463d-9a11-d8388c98e4ee',
199
+ 'jobid' => JOB_ID
200
+ }
201
+ }
202
+ end
203
+
204
+ let(:create_ssh_key_pair_response) do
205
+ {
206
+ 'createsshkeypairresponse' => {
207
+ 'keypair' => {
208
+ 'privatekey' => "#{SSH_GENERATED_PRIVATE_KEY}\n",
209
+ 'name' => SSH_GENERATED_KEY_NAME
210
+ }
211
+ }
212
+ }
213
+ end
214
+
215
+ describe 'run_instance' do
216
+ subject { action.call(env) }
217
+ let(:app) { double('Vagrant::Action::Warden') }
218
+ let(:ssh_key) { '/some/path' }
219
+
220
+ let(:template_name) { TEMPLATE_NAME }
221
+ let(:machine) { double('Vagrant::Machine') }
222
+ let(:data_dir) { double('Pathname') }
223
+ let(:a_path) { double('Pathname') }
224
+ let(:file) { double('File') }
225
+ let(:communicator) { double('VagrantPlugins::CommunicatorSSH::Communicator') }
226
+ let(:communicator_config) { double('VagrantPlugins::...::...Config') }
227
+ let(:cloudstack_compute) { double('Fog::Compute::Cloudstack') }
228
+ let(:servers) { double('Fog::Compute::Cloudstack::Servers') }
229
+ let(:server) { double('Fog::Compute::Cloudstack::Server') }
230
+ let(:ui) { double('Vagrant::UI::Prefixed') }
231
+ let(:root_path) { double('Pathname') }
232
+ let(:env) do
233
+ {
234
+ root_path: root_path,
235
+ ui: ui,
236
+ machine: machine,
237
+ cloudstack_compute: cloudstack_compute
238
+ }
239
+ end
240
+
241
+ let(:cloudstack_zone) do
242
+ instance_double('Fog::Compute::Cloudstack::Zone',
243
+ id: ZONE_ID,
244
+ name: ZONE_NAME,
245
+ network_type: network_type,
246
+ security_groups_enabled: security_groups_enabled)
247
+ end
248
+
249
+ before(:each) do
250
+ allow(app).to receive(:call).and_return(true)
251
+ allow(ui).to receive(:info)
252
+ allow(ui).to receive(:detail)
253
+
254
+ allow(machine).to receive(:data_dir).and_return(data_dir)
255
+ allow(data_dir).to receive(:join).and_return(a_path)
256
+ allow(a_path).to receive(:open).and_yield(file)
257
+
258
+ allow(machine).to receive(:communicate).and_return(communicator)
259
+ allow(machine).to receive_message_chain(:communicate, :ready?).and_return(true)
260
+
261
+ allow(machine).to receive(:provider_config).and_return(provider_config)
262
+ expect(server).to receive(:wait_for).and_return(ready = true)
263
+ allow(server).to receive(:password_enabled).and_return(false)
264
+ expect(cloudstack_compute).to receive(:servers).and_return(servers)
265
+ allow(cloudstack_compute).to receive(:send).with(:list_zones, available: true).and_return(list_zones_response)
266
+ allow(cloudstack_compute).to receive(:send).with(:list_service_offerings, listall: true)
267
+ .and_return(list_service_offerings_response)
268
+ allow(cloudstack_compute).to receive(:send)
269
+ .with(:list_templates, zoneid: ZONE_ID, templatefilter: 'executable', listall: true)
270
+ .and_return(list_templates_response)
271
+ allow(cloudstack_compute).to receive(:zones).and_return([cloudstack_zone])
272
+ allow(servers).to receive(:create).with(create_servers_parameters).and_return(server)
273
+ expect(server).to receive(:id).and_return(SERVER_ID)
274
+ expect(machine).to receive(:id=).with(SERVER_ID)
275
+
276
+ allow(cloudstack_compute).to receive(:volumes).and_return([])
277
+ end
278
+
279
+ context 'in basic zone' do
280
+ let(:security_groups) { [] }
281
+ let(:network_name) { nil }
282
+ let(:provider_config) do
283
+ config = VagrantPlugins::Cloudstack::Config.new
284
+ config.domain_config :cloudstack do |cfg|
285
+ cfg.zone_name = ZONE_NAME
286
+ cfg.service_offering_name = SERVICE_OFFERING_NAME
287
+ cfg.template_name = template_name
288
+ cfg.display_name = DISPLAY_NAME
289
+ cfg.ssh_key = ssh_key
290
+ cfg.security_groups = security_groups
291
+ cfg.network_name = network_name
292
+ end
293
+ config.finalize!
294
+ config.get_domain_config(:cloudstack)
295
+ end
296
+ let(:network_type) { NETWORK_TYPE_BASIC }
297
+ let(:security_groups_enabled) { SECURITY_GROUPS_ENABLED }
298
+
299
+ let(:create_servers_parameters) do
300
+ {
301
+ display_name: DISPLAY_NAME,
302
+ group: nil,
303
+ zone_id: ZONE_ID,
304
+ flavor_id: SERVICE_OFFERING_ID,
305
+ image_id: TEMPLATE_ID
306
+ }
307
+ end
308
+
309
+ context 'a basic configuration' do
310
+ it 'starts a vm' do
311
+ should eq true
312
+ end
313
+ end
314
+
315
+ context 'with inline security groups' do
316
+ let(:sg_rule) { { protocol: 'TCP', startport: 23, endport: 23, cidrlist: '0.0.0.0/0' } }
317
+
318
+ let(:create_servers_parameters) { super().merge('security_group_ids' => SECURITY_GROUP_ID) }
319
+ let(:security_groups) do
320
+ [
321
+ {
322
+ name: SECURITY_GROUP_NAME, description: SECURITY_GROUP_DESC,
323
+ rules: [sg_rule.merge(type: 'ingress')]
324
+ }
325
+ ]
326
+ end
327
+
328
+ before(:each) do
329
+ allow(cloudstack_compute).to receive(:create_security_group)
330
+ .with(name: SECURITY_GROUP_NAME, description: SECURITY_GROUP_DESC)
331
+ .and_return('createsecuritygroupresponse' => { 'securitygroup' => { 'id' => SECURITY_GROUP_ID } })
332
+
333
+ allow(cloudstack_compute).to receive(:send).with(:authorize_security_group_ingress,
334
+ { securityGroupId: SECURITY_GROUP_ID }.merge(sg_rule))
335
+ .and_return(
336
+ 'authorizesecuritygroupingressresponse' => { 'jobid' => '0b6c2c41-f0c8-43b5-be1d-9d5957873cf9' }
337
+ )
338
+ expect(file).to receive(:write).with("#{SECURITY_GROUP_ID}\n")
339
+ end
340
+
341
+ it 'starts a vm' do
342
+ should eq true
343
+ end
344
+ end
345
+
346
+ context 'with advanced zone parameters give warnings' do
347
+ let(:network_name) { NETWORK_NAME }
348
+
349
+ before(:each) do
350
+ expect(ui).to receive(:warn).with("Network name or id defined but zone Zone Name is of network type 'Basic'"\
351
+ "\nNetwork name or id will be ignored")
352
+ end
353
+ it 'starts a vm' do
354
+ should eq true
355
+ end
356
+ end
357
+ end
358
+
359
+ context 'in advanced zone' do
360
+ let(:pf_ip_address) { nil }
361
+ let(:pf_trusted_networks) { nil }
362
+ let(:pf_public_port_randomrange) { { start: 49_152, end: 65_535 } }
363
+ let(:pf_open_firewall) { true }
364
+ let(:disk_offering_name) { nil }
365
+
366
+ let(:provider_config) do
367
+ config = VagrantPlugins::Cloudstack::Config.new
368
+ config.domain_config :cloudstack do |cfg|
369
+ cfg.zone_name = ZONE_NAME
370
+ cfg.network_name = NETWORK_NAME
371
+ cfg.service_offering_name = SERVICE_OFFERING_NAME
372
+ cfg.template_name = template_name
373
+ cfg.display_name = DISPLAY_NAME
374
+ cfg.pf_ip_address = pf_ip_address
375
+ cfg.pf_trusted_networks = pf_trusted_networks
376
+ cfg.pf_public_port_randomrange = pf_public_port_randomrange
377
+ cfg.pf_open_firewall = pf_open_firewall
378
+ cfg.ssh_key = ssh_key
379
+ cfg.disk_offering_name = disk_offering_name
380
+ end
381
+ config.finalize!
382
+ config.get_domain_config(:cloudstack)
383
+ end
384
+
385
+ let(:winrm_config) { double('VagrantPlugins::VagrantWinRM::WinRMConfig') }
386
+
387
+ before(:each) do
388
+ allow(cloudstack_compute).to receive(:query_async_job_result).with(jobid: JOB_ID).and_return(fake_job_result)
389
+
390
+ allow(cloudstack_compute).to receive(:send).with(:list_networks, {}).and_return(list_networks_response)
391
+ end
392
+
393
+ context 'a basic configuration' do
394
+ it 'starts a vm' do
395
+ should eq true
396
+ end
397
+ end
398
+
399
+ context 'with template specified from Vagrant(file)' do
400
+ let(:template_name) { nil }
401
+
402
+ before(:each) do
403
+ expect(machine).to receive_message_chain(:config, :vm, :box).and_return(TEMPLATE_NAME)
404
+ end
405
+ it 'starts a vm' do
406
+ should eq true
407
+ end
408
+ end
409
+
410
+ context 'with additional data disk' do
411
+ let(:disk_offering_name) { DISK_OFFERING_NAME }
412
+ let(:create_servers_parameters) { super().merge('disk_offering_id' => DISK_OFFERING_ID) }
413
+ let(:volume) { double('Fog::Compute::Cloudstack::Volume') }
414
+
415
+ before(:each) do
416
+ allow(cloudstack_compute).to receive(:send).with(:list_disk_offerings, listall: true)
417
+ .and_return(list_disk_offerings_response)
418
+ expect(cloudstack_compute).to receive(:volumes).and_return([volume])
419
+ allow(volume).to receive(:server_id).and_return(SERVER_ID)
420
+ allow(volume).to receive(:type).and_return('DATADISK')
421
+ allow(volume).to receive(:id).and_return(VOLUME_ID)
422
+ expect(file).to receive(:write).with("#{VOLUME_ID}\n")
423
+ allow(server).to receive(:id).and_return(SERVER_ID)
424
+ end
425
+ it 'starts a vm' do
426
+ should eq true
427
+ end
428
+ end
429
+
430
+ context 'with generated password' do
431
+ before(:each) do
432
+ expect(server).to receive(:password_enabled).and_return(true)
433
+ allow(server).to receive(:job_id).and_return(JOB_ID)
434
+ expect(file).to receive(:write).with(GENERATED_PASSWORD)
435
+ end
436
+
437
+ it 'starts a vm' do
438
+ should eq true
439
+ end
440
+ end
441
+
442
+ context 'with SSH key generation' do
443
+ let(:ssh_key) { nil }
444
+ let(:create_servers_parameters) { super().merge('key_name' => SSH_GENERATED_KEY_NAME) }
445
+
446
+ before(:each) do
447
+ expect(ui).to receive(:warn)
448
+ .with('No keypair or ssh_key specified to launch your instance with.' \
449
+ "\n" + 'Generating a temporary keypair for this instance...')
450
+ expect(cloudstack_compute).to receive(:create_ssh_key_pair).with(/vagacs_#{DISPLAY_NAME}/, nil, nil, nil)
451
+ .and_return(create_ssh_key_pair_response)
452
+ expect(file).to receive(:write).with("#{SSH_GENERATED_PRIVATE_KEY}\n")
453
+ expect(file).to receive(:write).with(SSH_GENERATED_KEY_NAME)
454
+ end
455
+
456
+ it 'starts a vm' do
457
+ should eq true
458
+ end
459
+ end
460
+
461
+ context 'with autogenerated firewall and port forward' do
462
+ let(:pf_ip_address) { PF_IP_ADDRESS }
463
+ let(:pf_trusted_networks) { PF_TRUSTED_NETWORKS }
464
+ let(:pf_public_port_randomrange) { { start: PF_RANDOM_START, end: PF_RANDOM_START + 1 } }
465
+ let(:pf_open_firewall) { false }
466
+
467
+ before(:each) do
468
+ allow(cloudstack_compute).to receive(:send).with(:list_public_ip_addresses, {})
469
+ .and_return(list_public_ip_addresses_response)
470
+ expect(communicator_config).to receive(:port).and_return(nil)
471
+ expect(communicator_config).to receive(:guest_port).and_return(nil)
472
+
473
+ allow(machine).to receive_message_chain(:config, :vm, :rdp, :port).and_return(3389)
474
+ allow(machine).to receive_message_chain(:config, :vm, :guest).and_return(nil)
475
+ allow(machine).to receive(:id).and_return(SERVER_ID)
476
+
477
+ allow(cloudstack_compute).to receive(:send).with(:list_public_ip_addresses, 'id' => PF_IP_ADDRESS_ID)
478
+ .and_return(list_public_ip_addresses_response)
479
+ allow(cloudstack_compute).to receive(:list_network_acl_lists).with(id: nil)
480
+ .and_return(list_network_acl_lists_response)
481
+
482
+ expect(file).to receive(:write).with(PORT_FORWARDING_RULE_ID + "\n")
483
+ expect(file).to receive(:write).with(PF_RANDOM_START.to_s)
484
+ expect(file).to receive(:write).with("#{ACL_ID},networkacl\n")
485
+ end
486
+
487
+ context 'for the SSH communicator' do
488
+ before(:each) do
489
+ allow(communicator).to receive_message_chain(:class, :name).and_return(COMMUNICATOR_SSH)
490
+ expect(machine).to receive_message_chain(:config, :ssh).and_return(communicator_config)
491
+
492
+ allow(communicator_config).to receive_message_chain(:default, :port).and_return(GUEST_PORT_SSH)
493
+ expect(cloudstack_compute).to receive(:create_port_forwarding_rule)
494
+ .with(create_port_forwarding_rule_parameters)
495
+ .and_return(create_port_forwarding_rule_respones)
496
+ expect(cloudstack_compute).to receive(:request)
497
+ .with(create_network_acl_request)
498
+ .and_return(createNetworkACL_response)
499
+ end
500
+ it 'starts a vm' do
501
+ should eq true
502
+ end
503
+
504
+ context 'with a port conflict' do
505
+ let(:pf_public_port_randomrange) { { start: PF_RANDOM_START - 1, end: PF_RANDOM_START + 1 } }
506
+
507
+ before(:each) do
508
+ allow(Kernel).to receive(:rand).with((PF_RANDOM_START - 1)...(PF_RANDOM_START + 1))
509
+ .and_return(PF_RANDOM_START - 1, PF_RANDOM_START)
510
+ expect(cloudstack_compute).to receive(:create_port_forwarding_rule)
511
+ .with(create_port_forwarding_rule_parameters.merge(publicport: PF_RANDOM_START - 1))
512
+ .and_raise(
513
+ Fog::Compute::Cloudstack::Error,
514
+ 'The range specified, CONFLICTINGRANGE, conflicts with rule SOMERULE which has THESAME'
515
+ )
516
+ end
517
+
518
+ it 'starts a vm' do
519
+ should eq true
520
+ end
521
+ end
522
+ end
523
+
524
+ context 'for the WinRM (and RDP) communicator' do
525
+ PF_RDP_RULE_ID = 'UUID RDP port forwarding rule'.freeze
526
+ PF_RDP_JOB_ID = 'UUID RDP Port Forward Job'.freeze
527
+ FW_RDP_JOB_ID = 'UUID RDP Port Firewall Job'.freeze
528
+ FW_RDP_ACL_ID = 'UUID of RDP ACL'.freeze
529
+
530
+ let(:pf_public_port_randomrange) { { start: PF_RANDOM_START, end: PF_RANDOM_START + 2 } }
531
+
532
+ let(:create_port_forwarding_rule_parameters) { super().merge(privateport: GUEST_PORT_WINRM) }
533
+ let(:create_port_forwarding_rule_rdp_parameters) do
534
+ create_port_forwarding_rule_parameters.merge(privateport: GUEST_PORT_RDP, publicport: PF_RANDOM_START + 1)
535
+ end
536
+
537
+ let(:create_network_acl_winrm_request) do
538
+ create_network_acl_request.merge(startport: GUEST_PORT_WINRM, endport: GUEST_PORT_WINRM)
539
+ end
540
+ let(:create_network_acl_rdp_request) do
541
+ create_network_acl_request.merge(startport: GUEST_PORT_RDP, endport: GUEST_PORT_RDP)
542
+ end
543
+
544
+ before(:each) do
545
+ allow(Kernel).to receive(:rand).with(PF_RANDOM_START...(PF_RANDOM_START + 2))
546
+ .and_return(PF_RANDOM_START, PF_RANDOM_START + 1)
547
+
548
+ allow(communicator).to receive_message_chain(:class, :name).and_return(COMMUNICATOR_WINRM)
549
+ expect(machine).to receive_message_chain(:config, :winrm).and_return(communicator_config)
550
+
551
+ allow(communicator_config).to receive_message_chain(:default, :port).and_return(GUEST_PORT_WINRM)
552
+
553
+ expect(cloudstack_compute).to receive(:create_port_forwarding_rule)
554
+ .with(create_port_forwarding_rule_parameters)
555
+ .and_return(create_port_forwarding_rule_respones)
556
+
557
+ expect(cloudstack_compute).to receive(:create_port_forwarding_rule)
558
+ .with(create_port_forwarding_rule_rdp_parameters).and_return(
559
+ 'createportforwardingruleresponse' =>
560
+ {
561
+ 'id' => PF_RDP_RULE_ID,
562
+ 'jobid' => PF_RDP_JOB_ID
563
+ }
564
+ )
565
+ allow(cloudstack_compute).to receive(:query_async_job_result).with(jobid: PF_RDP_JOB_ID)
566
+ .and_return(fake_job_result.merge(
567
+ 'queryasyncjobresultresponse' => {
568
+ 'jobresult' => {
569
+ 'portforwardingrule' => {
570
+ 'id' => PF_RDP_RULE_ID
571
+ }
572
+ }
573
+ }
574
+ ))
575
+ expect(file).to receive(:write).with(PF_RDP_RULE_ID + "\n")
576
+ expect(file).to receive(:write).with((PF_RANDOM_START + 1).to_s)
577
+ expect(cloudstack_compute).to receive(:request).with(create_network_acl_winrm_request)
578
+ .and_return(createNetworkACL_response)
579
+
580
+ expect(cloudstack_compute).to receive(:request).with(create_network_acl_rdp_request)
581
+ .and_return(
582
+ 'createnetworkaclresponse' => {
583
+ 'id' => '5dcb96b5-7785-463d-9a11-d8388c98e4ee',
584
+ 'jobid' => FW_RDP_JOB_ID
585
+ }
586
+ )
587
+ allow(cloudstack_compute).to receive(:query_async_job_result).with(jobid: FW_RDP_JOB_ID)
588
+ .and_return(
589
+ fake_job_result.merge(
590
+ 'queryasyncjobresultresponse' => {
591
+ 'jobstatus' => 1,
592
+ 'jobresult' => {
593
+ 'networkacl' => {
594
+ 'id' => FW_RDP_ACL_ID
595
+ }
596
+ }
597
+ }
598
+ )
599
+ )
600
+ expect(file).to receive(:write).with("#{FW_RDP_ACL_ID},networkacl\n")
601
+ end
602
+ it 'starts a vm' do
603
+ should eq true
604
+ end
605
+ end
606
+ end
607
+ end
608
+ end
609
+ end