bosh_vcloud_cpi 0.4.8

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