bosh_vcloud_cpi 0.4.8

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.
@@ -0,0 +1,633 @@
1
+ $: << File.expand_path(File.dirname(__FILE__))
2
+
3
+ require "ruby_vcloud_sdk"
4
+ require "cloud/vcloud/const"
5
+ require "cloud/vcloud/util"
6
+
7
+ require "digest/sha1"
8
+ require "fileutils"
9
+ require "logger"
10
+ require "uuidtools"
11
+ require "yajl"
12
+ require "const"
13
+ require "thread"
14
+
15
+ module VCloudCloud
16
+
17
+ class Cloud
18
+
19
+ def initialize(options)
20
+ @logger = Bosh::Clouds::Config.logger
21
+ VCloudSdk::Config.configure({ "logger" => @logger })
22
+ @logger.debug("Input cloud options: #{options.inspect}")
23
+
24
+ @agent_properties = options["agent"]
25
+ vcds = options["vcds"]
26
+ raise ArgumentError, "Invalid number of VCDs" unless vcds.size == 1
27
+ @vcd = vcds[0]
28
+
29
+ finalize_options
30
+ @control = @vcd["control"]
31
+ @retries = @control["retries"]
32
+ @logger.info("VCD cloud options: #{options.inspect}")
33
+
34
+ @client_lock = Mutex.new
35
+
36
+ at_exit { destroy_client }
37
+ end
38
+
39
+ def client()
40
+ @client_lock.synchronize {
41
+ if @client.nil?
42
+ create_client
43
+ else
44
+ begin
45
+ @client.get_ovdc
46
+ @client
47
+ rescue VCloudSdk::CloudError => e
48
+ log_exception("validate, creating new session.", e)
49
+ create_client
50
+ end
51
+ end
52
+ }
53
+ end
54
+
55
+ def create_stemcell(image, _)
56
+ @client = client
57
+
58
+ with_thread_name("create_stemcell(#{image}, _)") do
59
+ @logger.debug("create_stemcell #{image} #{_}")
60
+ result = nil
61
+ Dir.mktmpdir do |temp_dir|
62
+ @logger.info("Extracting stemcell to: #{temp_dir}")
63
+ output = `tar -C #{temp_dir} -xzf #{image} 2>&1`
64
+ raise "Corrupt image, tar exit status: #{$?.exitstatus} output:" +
65
+ "#{output}" if $?.exitstatus != 0
66
+
67
+ ovf_file = Dir.entries(temp_dir).find {
68
+ |entry| File.extname(entry) == ".ovf" }
69
+ raise "Missing OVF" unless ovf_file
70
+ ovf_file = File.join(temp_dir, ovf_file)
71
+
72
+ name = "sc-#{generate_unique_name}"
73
+ @logger.info("Generated name: #{name}")
74
+
75
+ @logger.info("Uploading #{ovf_file}")
76
+ result = @client.upload_vapp_template(name, temp_dir).urn
77
+ end
78
+
79
+ @logger.info("Stemcell created as catalog vApp with ID #{result}.")
80
+ result
81
+ end
82
+ end
83
+
84
+ def delete_stemcell(catalog_vapp_id)
85
+ @client = client
86
+
87
+ with_thread_name("delete_stemcell(#{catalog_vapp_id})") do
88
+ @logger.debug("delete_stemcell #{catalog_vapp_id}")
89
+
90
+ # shadow VMs (stemcell replicas) get deleted serially,
91
+ # and upon failure to delete they must be deleted manually
92
+ # from VCD "stranded items"
93
+ @client.delete_catalog_vapp(catalog_vapp_id)
94
+ end
95
+ end
96
+
97
+ def reconfigure_vm(vapp, resource_pool, networks, environment)
98
+ vm = get_vm(vapp)
99
+ ram_mb = Integer(resource_pool["ram"])
100
+ cpu = Integer(resource_pool["cpu"])
101
+ disk_mb = Integer(resource_pool["disk"])
102
+
103
+ disks = vm.hardware_section.hard_disks
104
+ @logger.debug("disks = #{disks.inspect}")
105
+ raise IndexError, "Invalid number of VM hard disks" unless disks.size == 1
106
+ system_disk = disks[0]
107
+ disks_previous = Array.new(disks)
108
+
109
+ add_vapp_networks(vapp, networks)
110
+
111
+ @logger.info("Reconfiguring VM hardware: #{ram_mb} MB RAM, #{cpu} CPU, " +
112
+ "#{disk_mb} MB disk, #{networks}.")
113
+ @client.reconfigure_vm(vm) do |v|
114
+ v.name = vapp.name
115
+ v.description = @vcd["entities"]["description"]
116
+ v.change_cpu_count(cpu)
117
+ v.change_memory(ram_mb)
118
+ v.add_hard_disk(disk_mb)
119
+ v.delete_nic(*vm.hardware_section.nics)
120
+ add_vm_nics(v, networks)
121
+ end
122
+
123
+ delete_vapp_networks(vapp, networks)
124
+
125
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp.urn)
126
+ ephemeral_disk = get_newly_added_disk(vm, disks_previous)
127
+
128
+ # prepare guest customization settings
129
+ network_env = generate_network_env(vm.hardware_section.nics, networks)
130
+ disk_env = generate_disk_env(system_disk, ephemeral_disk)
131
+ env = generate_agent_env(vapp.name, vm, vapp.name, network_env, disk_env)
132
+ env["env"] = environment
133
+ @logger.info("Setting VM env: #{vapp.urn} #{env.inspect}")
134
+ set_agent_env(vm, env)
135
+
136
+ @logger.info("Powering on vApp: #{vapp.urn}")
137
+ @client.power_on_vapp(vapp)
138
+ rescue VCloudSdk::CloudError
139
+ delete_vm(vapp.urn)
140
+ raise
141
+ end
142
+
143
+ def create_vm(agent_id, catalog_vapp_id, resource_pool, networks,
144
+ disk_locality = nil, environment = {})
145
+ @client = client
146
+
147
+ with_thread_name("create_vm(#{agent_id}, ...)") do
148
+ Util.retry_operation("create_vm(#{agent_id}, ...)", @retries['cpi'],
149
+ @control['backoff']) do
150
+ @logger.info("Creating VM: #{agent_id}")
151
+ @logger.debug("networks: #{networks.inspect}")
152
+
153
+ locality = independent_disks(disk_locality)
154
+
155
+ vapp = @client.instantiate_vapp_template(
156
+ catalog_vapp_id, agent_id, # vapp name
157
+ @vcd["entities"]["description"], locality)
158
+ @logger.debug("Instantiated vApp: id=#{vapp.urn} name=#{vapp.name}")
159
+
160
+ reconfigure_vm(vapp, resource_pool, networks, environment)
161
+
162
+ @logger.info("Created VM: #{agent_id} as #{vapp.urn}")
163
+ vapp.urn
164
+ end
165
+ end
166
+ rescue VCloudSdk::CloudError => e
167
+ log_exception("create vApp", e)
168
+ raise e
169
+ end
170
+
171
+ def delete_vm(vapp_id)
172
+ @client = client
173
+
174
+ with_thread_name("delete_vm(#{vapp_id}, ...)") do
175
+ Util.retry_operation("delete_vm(#{vapp_id}, ...)", @retries['cpi'],
176
+ @control['backoff']) do
177
+ @logger.info("Deleting vApp: #{vapp_id}")
178
+ vapp = @client.get_vapp(vapp_id)
179
+ vm = get_vm(vapp)
180
+ vm_name = vm.name
181
+
182
+ begin
183
+ @client.power_off_vapp(vapp)
184
+ rescue VCloudSdk::VappSuspendedError => e
185
+ @client.discard_suspended_state_vapp(vapp)
186
+ @client.power_off_vapp(vapp)
187
+ end
188
+ del_vapp = @vcd['debug']['delete_vapp']
189
+ @client.delete_vapp(vapp) if del_vapp
190
+ @logger.info("Deleting ISO #{vm_name}")
191
+ @client.delete_catalog_media(vm_name) if del_vapp
192
+ @logger.info("Deleted vApp: #{vapp_id}")
193
+ end
194
+ end
195
+ rescue VCloudSdk::CloudError => e
196
+ log_exception("delete vApp #{vapp_id}", e)
197
+ raise e
198
+ end
199
+
200
+ def reboot_vm(vapp_id)
201
+ @client = client
202
+
203
+ with_thread_name("reboot_vm(#{vapp_id}, ...)") do
204
+ Util.retry_operation("reboot_vm(#{vapp_id}, ...)", @retries['cpi'],
205
+ @control['backoff']) do
206
+ @logger.info("Rebooting vApp: #{vapp_id}")
207
+ vapp = @client.get_vapp(vapp_id)
208
+ begin
209
+ @client.reboot_vapp(vapp)
210
+ rescue VCloudSdk::VappPoweredOffError => e
211
+ @client.power_on_vapp(vapp)
212
+ rescue VCloudSdk::VappSuspendedError => e
213
+ @client.discard_suspended_state_vapp(vapp)
214
+ @client.power_on_vapp(vapp)
215
+ end
216
+ @logger.info("Rebooted vApp: #{vapp_id}")
217
+ end
218
+ end
219
+ rescue VCloudSdk::CloudError => e
220
+ log_exception("reboot vApp #{vapp_id}", e)
221
+ raise e
222
+ end
223
+
224
+ def configure_networks(vapp_id, networks)
225
+ @client = client
226
+
227
+ with_thread_name("configure_networks(#{vapp_id}, ...)") do
228
+ Util.retry_operation("configure_networks(#{vapp_id}, ...)",
229
+ @retries['cpi'], @control['backoff']) do
230
+ @logger.info("Reconfiguring vApp networks: #{vapp_id}")
231
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp_id)
232
+ @logger.debug("Powering off #{vapp.name}.")
233
+ begin
234
+ @client.power_off_vapp(vapp)
235
+ rescue VCloudSdk::VappSuspendedError => e
236
+ @client.discard_suspended_state_vapp(vapp)
237
+ @client.power_off_vapp(vapp)
238
+ end
239
+
240
+ add_vapp_networks(vapp, networks)
241
+ @client.reconfigure_vm(vm) do |v|
242
+ v.delete_nic(*vm.hardware_section.nics)
243
+ add_vm_nics(v, networks)
244
+ end
245
+ delete_vapp_networks(vapp, networks)
246
+
247
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp_id)
248
+ env = get_current_agent_env(vm)
249
+ env["networks"] = generate_network_env(vm.hardware_section.nics,
250
+ networks)
251
+ @logger.debug("Updating agent env to: #{env.inspect}")
252
+ set_agent_env(vm, env)
253
+
254
+ @logger.debug("Powering #{vapp.name} back on.")
255
+ @client.power_on_vapp(vapp)
256
+ @logger.info("Configured vApp networks: #{vapp}")
257
+ end
258
+ end
259
+ rescue VCloudSdk::CloudError => e
260
+ log_exception("configure vApp networks: #{vapp}", e)
261
+ raise e
262
+ end
263
+
264
+ def attach_disk(vapp_id, disk_id)
265
+ @client = client
266
+
267
+ with_thread_name("attach_disk(#{vapp_id} #{disk_id})") do
268
+ Util.retry_operation("attach_disk(#{vapp_id}, #{disk_id})",
269
+ @retries['cpi'], @control['backoff']) do
270
+ @logger.info("Attaching disk: #{disk_id} on vm: #{vapp_id}")
271
+
272
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp_id)
273
+ # vm.hardware_section will change, save current state of disks
274
+ disks_previous = Array.new(vm.hardware_section.hard_disks)
275
+
276
+ disk = @client.get_disk(disk_id)
277
+ @client.attach_disk(disk, vm)
278
+
279
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp_id)
280
+ persistent_disk = get_newly_added_disk(vm, disks_previous)
281
+
282
+ env = get_current_agent_env(vm)
283
+ env["disks"]["persistent"][disk_id] = persistent_disk.disk_id
284
+ @logger.info("Updating agent env to: #{env.inspect}")
285
+ set_agent_env(vm, env)
286
+
287
+ @logger.info("Attached disk:#{disk_id} to VM:#{vapp_id}")
288
+ end
289
+ end
290
+ rescue VCloudSdk::CloudError => e
291
+ log_exception("attach disk", e)
292
+ raise e
293
+ end
294
+
295
+ def detach_disk(vapp_id, disk_id)
296
+ @client = client
297
+
298
+ with_thread_name("detach_disk(#{vapp_id} #{disk_id})") do
299
+ Util.retry_operation("detach_disk(#{vapp_id}, #{disk_id})",
300
+ @retries['cpi'], @control['backoff']) do
301
+ @logger.info("Detaching disk: #{disk_id} from vm: #{vapp_id}")
302
+
303
+ vapp, vm = get_vapp_vm_by_vapp_id(vapp_id)
304
+
305
+ disk = @client.get_disk(disk_id)
306
+ begin
307
+ @client.detach_disk(disk, vm)
308
+ rescue VCloudSdk::VmSuspendedError => e
309
+ @client.discard_suspended_state_vapp(vapp)
310
+ @client.detach_disk(disk, vm)
311
+ end
312
+
313
+ env = get_current_agent_env(vm)
314
+ env["disks"]["persistent"].delete(disk_id)
315
+ @logger.info("Updating agent env to: #{env.inspect}")
316
+ set_agent_env(vm, env)
317
+
318
+ @logger.info("Detached disk: #{disk_id} on vm: #{vapp_id}")
319
+ end
320
+ end
321
+ rescue VCloudSdk::CloudError => e
322
+ log_exception("detach disk", e)
323
+ raise e
324
+ end
325
+
326
+ def create_disk(size_mb, vm_locality = nil)
327
+ @client = client
328
+
329
+ with_thread_name("create_disk(#{size_mb}, vm_locality)") do
330
+ Util.retry_operation("create_disk(#{size_mb}, vm_locality)",
331
+ @retries['cpi'], @control['backoff']) do
332
+ @logger.info("Create disk: #{size_mb}, #{vm_locality}")
333
+ disk_name = "#{generate_unique_name}"
334
+ disk = nil
335
+ if vm_locality.nil?
336
+ @logger.info("Creating disk: #{disk_name} #{size_mb}")
337
+ disk = @client.create_disk(disk_name, size_mb)
338
+ else
339
+ # vm_locality => vapp_id
340
+ vapp, vm = get_vapp_vm_by_vapp_id(vm_locality)
341
+ @logger.info("Creating disk: #{disk_name} #{size_mb} #{vm.name}")
342
+ disk = @client.create_disk(disk_name, size_mb, vm)
343
+ end
344
+ @logger.info("Created disk: #{disk_name} #{disk.urn} #{size_mb} " +
345
+ "#{vm_locality}")
346
+ disk.urn
347
+ end
348
+ end
349
+ rescue VCloudSdk::CloudError => e
350
+ log_exception("create disk", e)
351
+ raise e
352
+ end
353
+
354
+ def delete_disk(disk_id)
355
+ @client = client
356
+
357
+ with_thread_name("delete_disk(#{disk_id})") do
358
+ Util.retry_operation("delete_disk(#{disk_id})", @retries['cpi'],
359
+ @control['backoff']) do
360
+ @logger.info("Deleting disk: #{disk_id}")
361
+ disk = @client.get_disk(disk_id)
362
+ @client.delete_disk(disk)
363
+ @logger.info("Deleted disk: #{disk_id}")
364
+ end
365
+ end
366
+ rescue VCloudSdk::CloudError => e
367
+ log_exception("delete disk", e)
368
+ raise e
369
+ end
370
+
371
+ def get_disk_size_mb(disk_id)
372
+ @client = client
373
+
374
+ with_thread_name("get_disk_size(#{disk_id})") do
375
+ Util.retry_operation("get_disk_size(#{disk_id})", @retries['cpi'],
376
+ @control['backoff']) do
377
+ @logger.info("Getting disk size: #{disk_id}")
378
+ disk = @client.get_disk(disk_id)
379
+ @logger.info("Disk #{disk_id} size: #{disk.size_mb} MB")
380
+ disk.size_mb
381
+ end
382
+ end
383
+ rescue VCloudSdk::CloudError => e
384
+ log_exception("get_disk_size", e)
385
+ raise e
386
+ end
387
+
388
+ def validate_deployment(old_manifest, new_manifest)
389
+ # There is TODO in vSphere CPI that questions the necessity of this method
390
+ raise NotImplementedError, "validate_deployment"
391
+ end
392
+
393
+ private
394
+
395
+ def finalize_options
396
+ @vcd['control'] = {} unless @vcd['control']
397
+ @vcd['control']['retries'] = {} unless @vcd['control']['retries']
398
+ @vcd['control']['retries']['default'] ||= RETRIES_DEFAULT
399
+ @vcd['control']['retries']['upload_vapp_files'] ||=
400
+ RETRIES_UPLOAD_VAPP_FILES
401
+ @vcd['control']['retries']['cpi'] ||= RETRIES_CPI
402
+ @vcd['control']['delay'] ||= DELAY
403
+ @vcd['control']['time_limit_sec'] = {} unless
404
+ @vcd['control']['time_limit_sec']
405
+ @vcd['control']['time_limit_sec']['default'] ||= TIMELIMIT_DEFAULT
406
+ @vcd['control']['time_limit_sec']['delete_vapp_template'] ||=
407
+ TIMELIMIT_DELETE_VAPP_TEMPLATE
408
+ @vcd['control']['time_limit_sec']['delete_vapp'] ||= TIMELIMIT_DELETE_VAPP
409
+ @vcd['control']['time_limit_sec']['delete_media'] ||=
410
+ TIMELIMIT_DELETE_MEDIA
411
+ @vcd['control']['time_limit_sec']['instantiate_vapp_template'] ||=
412
+ TIMELIMIT_INSTANTIATE_VAPP_TEMPLATE
413
+ @vcd['control']['time_limit_sec']['power_on'] ||= TIMELIMIT_POWER_ON
414
+ @vcd['control']['time_limit_sec']['power_off'] ||= TIMELIMIT_POWER_OFF
415
+ @vcd['control']['time_limit_sec']['undeploy'] ||= TIMELIMIT_UNDEPLOY
416
+ @vcd['control']['time_limit_sec']['process_descriptor_vapp_template'] ||=
417
+ TIMELIMIT_PROCESS_DESCRIPTOR_VAPP_TEMPLATE
418
+ @vcd['control']['time_limit_sec']['http_request'] ||=
419
+ TIMELIMIT_HTTP_REQUEST
420
+ @vcd['control']['backoff'] ||= BACKOFF
421
+ @vcd['control']['rest_throttle'] = {} unless
422
+ @vcd['control']['rest_throttle']
423
+ @vcd['control']['rest_throttle']['min'] ||= REST_THROTTLE_MIN
424
+ @vcd['control']['rest_throttle']['max'] ||= REST_THROTTLE_MAX
425
+ @vcd['debug'] = {} unless @vcd['debug']
426
+ @vcd['debug']['delete_vapp'] = DEBUG_DELETE_VAPP unless
427
+ @vcd['debug']['delete_vapp']
428
+ end
429
+
430
+ def create_client()
431
+ url = @vcd["url"]
432
+ @logger.debug("Create session to VCD cloud: #{url}")
433
+ #@rest_logger.debug("Session to VCD cloud: #{url}")
434
+
435
+ @client = VCloudSdk::Client.new(url, @vcd["user"],
436
+ @vcd["password"], @vcd["entities"], @vcd["control"])
437
+
438
+ @logger.info("Created session to VCD cloud: #{url}")
439
+
440
+ @client
441
+ rescue VCloudSdk::ApiError => e
442
+ log_exception(e, "Failed to connect and establish session.")
443
+ raise e
444
+ end
445
+
446
+ def destroy_client()
447
+ url = @vcd["url"]
448
+ @logger.debug("Destroy session to VCD cloud: #{url}")
449
+ # TODO VCloudSdk::Client should permit logout.
450
+ @logger.info("Destroyed session to VCD cloud: #{url}")
451
+ end
452
+
453
+ def generate_unique_name
454
+ UUIDTools::UUID.random_create.to_s
455
+ end
456
+
457
+ def log_exception(op, e)
458
+ @logger.error("Failed to #{op}.")
459
+ @logger.error(e)
460
+ end
461
+
462
+ def generate_network_env(nics, networks)
463
+ nic_net = {}
464
+ nics.each do |nic|
465
+ nic_net[nic.network] = nic
466
+ end
467
+ @logger.debug("nic_net #{nic_net.inspect}")
468
+
469
+ network_env = {}
470
+ networks.each do |network_name, network|
471
+ network_entry = network.dup
472
+ v_network_name = network["cloud_properties"]["name"]
473
+ nic = nic_net[v_network_name]
474
+ if nic.nil? then
475
+ @logger.warn("Not generating network env for #{v_network_name}")
476
+ next
477
+ end
478
+ network_entry["mac"] = nic.mac_address
479
+ network_env[network_name] = network_entry
480
+ end
481
+ network_env
482
+ end
483
+
484
+ def generate_disk_env(system_disk, ephemeral_disk)
485
+ {
486
+ "system" => system_disk.disk_id,
487
+ "ephemeral" => ephemeral_disk.disk_id,
488
+ "persistent" => {}
489
+ }
490
+ end
491
+
492
+ def generate_agent_env(name, vm, agent_id, networking_env, disk_env)
493
+ vm_env = {
494
+ "name" => name,
495
+ "id" => vm.urn
496
+ }
497
+
498
+ env = {}
499
+ env["vm"] = vm_env
500
+ env["agent_id"] = agent_id
501
+ env["networks"] = networking_env
502
+ env["disks"] = disk_env
503
+ env.merge!(@agent_properties)
504
+ end
505
+
506
+ def get_current_agent_env(vm)
507
+ env = @client.get_metadata(vm, @vcd["entities"]["vm_metadata_key"])
508
+ @logger.info("Current agent env: #{env.inspect}")
509
+ Yajl::Parser.parse(env)
510
+ end
511
+
512
+ def set_agent_env(vm, env)
513
+ env_json = Yajl::Encoder.encode(env)
514
+ @logger.debug("env.iso content #{env_json}")
515
+
516
+ begin
517
+ # Clear existing ISO if one exists.
518
+ @logger.info("Ejecting ISO #{vm.name}")
519
+ @client.eject_catalog_media(vm, vm.name)
520
+ @logger.info("Deleting ISO #{vm.name}")
521
+ @client.delete_catalog_media(vm.name)
522
+ rescue VCloudSdk::ObjectNotFoundError
523
+ @logger.debug("No ISO to eject/delete before setting new agent env.")
524
+ # Continue setting agent env...
525
+ end
526
+
527
+ # generate env iso, and insert into VM
528
+ Dir.mktmpdir do |path|
529
+ env_path = File.join(path, "env")
530
+ iso_path = File.join(path, "env.iso")
531
+ File.open(env_path, "w") { |f| f.write(env_json) }
532
+ output = `genisoimage -o #{iso_path} #{env_path} 2>&1`
533
+ raise "#{$?.exitstatus} -#{output}" if $?.exitstatus != 0
534
+
535
+ @client.set_metadata(vm, @vcd["entities"]["vm_metadata_key"], env_json)
536
+
537
+ storage_profiles = @client.get_ovdc.storage_profiles || []
538
+ media_storage_profile = storage_profiles.find { |sp| sp['name'] ==
539
+ @vcd["entities"]["media_storage_profile"] }
540
+ @logger.info("Uploading and inserting ISO #{iso_path} as #{vm.name} " +
541
+ "to #{media_storage_profile.inspect}")
542
+ @client.upload_catalog_media(vm.name, iso_path, media_storage_profile)
543
+ @client.insert_catalog_media(vm, vm.name)
544
+ @logger.info("Uploaded and inserted ISO #{iso_path} as #{vm.name}")
545
+ end
546
+ end
547
+
548
+ def delete_vapp_networks(vapp, exclude_nets)
549
+ exclude = exclude_nets.map {|k,v| v["cloud_properties"]["name"]}.uniq
550
+ @client.delete_networks(vapp, exclude)
551
+ @logger.debug("Deleted vApp #{vapp.name} networks excluding " +
552
+ "#{exclude.inspect}.")
553
+ end
554
+
555
+ def add_vapp_networks(vapp, networks)
556
+ @logger.debug("Networks to add: #{networks.inspect}")
557
+ ovdc = @client.get_ovdc
558
+ accessible_org_networks = ovdc.available_networks
559
+ @logger.debug("Accessible Org nets: #{accessible_org_networks.inspect}")
560
+
561
+ cloud_networks = networks.map { |k,v| v["cloud_properties"]["name"] }.uniq
562
+ cloud_networks.each do |configured_network|
563
+ @logger.debug("Adding configured network: #{configured_network}")
564
+ org_net = accessible_org_networks.find {
565
+ |n| n['name'] == configured_network }
566
+ unless org_net
567
+ raise VCloudSdk::CloudError, "Configured network: " +
568
+ "#{configured_network}, is not accessible to VDC:#{ovdc.name}."
569
+ end
570
+ @logger.debug("Adding configured network: #{configured_network}, => " +
571
+ "Org net:#{org_net.inspect} to vApp:#{vapp.name}.")
572
+ @client.add_network(vapp, org_net)
573
+ @logger.debug("Added vApp network: #{configured_network}.")
574
+ end
575
+ @logger.debug("Accessible configured networks added:#{networks.inspect}.")
576
+ end
577
+
578
+ def add_vm_nics(v, networks)
579
+ networks.values.each_with_index do |network, nic_index|
580
+ if nic_index + 1 >= VM_NIC_LIMIT then
581
+ @logger.warn("Max number of NICs reached")
582
+ break
583
+ end
584
+ configured_network = network["cloud_properties"]["name"]
585
+ @logger.info("Adding NIC with IP address #{network["ip"]}.")
586
+ v.add_nic(nic_index, configured_network,
587
+ VCloudSdk::Xml::IP_ADDRESSING_MODE[:MANUAL], network["ip"])
588
+ v.connect_nic(nic_index, configured_network,
589
+ VCloudSdk::Xml::IP_ADDRESSING_MODE[:MANUAL], network["ip"])
590
+ end
591
+ @logger.info("NICs added to #{v.name} and connected to network:" +
592
+ " #{networks.inspect}")
593
+ end
594
+
595
+ def get_vm(vapp)
596
+ vms = vapp.vms
597
+ raise IndexError, "Invalid number of vApp VMs" unless vms.size == 1
598
+ vms[0]
599
+ end
600
+
601
+ def get_vapp_vm_by_vapp_id(id)
602
+ vapp = @client.get_vapp(id)
603
+ [vapp, get_vm(vapp)]
604
+ end
605
+
606
+ def get_newly_added_disk(vm, disks_previous)
607
+ disks_current = vm.hardware_section.hard_disks
608
+ newly_added = disks_current - disks_previous
609
+
610
+ if newly_added.size != 1
611
+ @logger.debug("Previous disks in #{vapp_id}: #{disks_previous.inspect}")
612
+ @logger.debug("Current disks in #{vapp_id}: #{disks_current.inspect}")
613
+ raise IndexError, "Expecting #{disks_previous.size + 1} disks, found " +
614
+ "#{disks_current.size}"
615
+ end
616
+
617
+ @logger.info("Newly added disk: #{newly_added[0]}")
618
+ newly_added[0]
619
+ end
620
+
621
+ def independent_disks(disk_locality)
622
+ disk_locality ||= []
623
+ @logger.info("Instantiate vApp accessible to disks: " +
624
+ "#{disk_locality.join(',')}")
625
+ disks = []
626
+ disk_locality.each do |disk_id|
627
+ disks << @client.get_disk(disk_id)
628
+ end
629
+ disks
630
+ end
631
+
632
+ end
633
+ end
@@ -0,0 +1,27 @@
1
+ module VCloudCloud
2
+
3
+ VM_NIC_LIMIT = 10
4
+
5
+ # control options
6
+ RETRIES_DEFAULT = 5
7
+ RETRIES_UPLOAD_VAPP_FILES = 7
8
+ RETRIES_CPI = 3
9
+ DELAY = 1
10
+ TIMELIMIT_DEFAULT = 1200
11
+ TIMELIMIT_DELETE_VAPP_TEMPLATE = 1200
12
+ TIMELIMIT_DELETE_VAPP = 1200
13
+ TIMELIMIT_DELETE_MEDIA = 1200
14
+ TIMELIMIT_INSTANTIATE_VAPP_TEMPLATE = 3000
15
+ TIMELIMIT_POWER_ON = 6000
16
+ TIMELIMIT_POWER_OFF = 6000
17
+ TIMELIMIT_UNDEPLOY = 7200
18
+ TIMELIMIT_PROCESS_DESCRIPTOR_VAPP_TEMPLATE = 3000
19
+ TIMELIMIT_HTTP_REQUEST = 240
20
+ BACKOFF = 2
21
+ REST_THROTTLE_MIN = 0
22
+ REST_THROTTLE_MAX = 1
23
+
24
+ # debug option
25
+ DEBUG_DELETE_VAPP = true
26
+
27
+ end
@@ -0,0 +1,21 @@
1
+ module VCloudCloud
2
+ class Util
3
+ class << self
4
+
5
+ def retry_operation(op, attempts, backoff, &b)
6
+ attempts.times do |attempt|
7
+ begin
8
+ return b.call
9
+ rescue VCloudSdk::ApiError => e
10
+ raise e if attempt >= attempts-1
11
+ delay = backoff ** attempt
12
+ Config.logger.error("Retry-attempt #{attempt+1}/#{attempts} " +
13
+ "failed to #{op}, retrying in #{delay} seconds.\t#{e}")
14
+ sleep (delay)
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Bosh
2
+ module Clouds
3
+ class VCloud
4
+ VERSION = "0.4.8"
5
+ end
6
+ end
7
+ end