opennebula 5.2.1 → 5.3.80.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,9 +14,10 @@
14
14
  # limitations under the License. #
15
15
  # ---------------------------------------------------------------------------- #
16
16
 
17
- # -------------------------------------------------------------------------#
18
- # Set up the environment for the driver #
19
- # -------------------------------------------------------------------------#
17
+ # ---------------------------------------------------------------------------- #
18
+ # Set up the environment for the driver #
19
+ # ---------------------------------------------------------------------------- #
20
+
20
21
  ONE_LOCATION = ENV["ONE_LOCATION"] if !defined?(ONE_LOCATION)
21
22
 
22
23
  if !ONE_LOCATION
@@ -25,3131 +26,54 @@ if !ONE_LOCATION
25
26
  ETC_LOCATION = "/etc/one/" if !defined?(ETC_LOCATION)
26
27
  VAR_LOCATION = "/var/lib/one" if !defined?(VAR_LOCATION)
27
28
  else
28
- BIN_LOCATION = ONE_LOCATION + "/bin" if !defined?(BIN_LOCATION)
29
- LIB_LOCATION = ONE_LOCATION + "/lib" if !defined?(LIB_LOCATION)
30
- ETC_LOCATION = ONE_LOCATION + "/etc/" if !defined?(ETC_LOCATION)
29
+ BIN_LOCATION = ONE_LOCATION + "/bin" if !defined?(BIN_LOCATION)
30
+ LIB_LOCATION = ONE_LOCATION + "/lib" if !defined?(LIB_LOCATION)
31
+ ETC_LOCATION = ONE_LOCATION + "/etc/" if !defined?(ETC_LOCATION)
31
32
  VAR_LOCATION = ONE_LOCATION + "/var/" if !defined?(VAR_LOCATION)
32
33
  end
33
34
 
34
35
  ENV['LANG'] = 'C'
35
36
 
36
- $: << LIB_LOCATION+'/ruby/vendors/rbvmomi/lib'
37
- $: << LIB_LOCATION+'/ruby'
37
+ $: << LIB_LOCATION + '/ruby/vendors/rbvmomi/lib'
38
+ $: << LIB_LOCATION + '/ruby'
39
+ $: << LIB_LOCATION + '/ruby/vcenter_driver'
38
40
 
39
- require 'ostruct'
40
41
  require 'rbvmomi'
41
42
  require 'yaml'
42
43
  require 'opennebula'
43
44
  require 'base64'
44
45
  require 'openssl'
45
46
 
46
- ################################################################################
47
- # Monkey patch rbvmomi library with some extra functions
48
- ################################################################################
49
-
50
- class RbVmomi::VIM::Datastore
51
-
52
- # Download a file from this datastore.
53
- # @param remote_path [String] Source path on the datastore.
54
- # @param local_path [String] Destination path on the local machine.
55
- # @return [void]
56
- def download_to_stdout remote_path
57
- url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
58
-
59
- pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
60
- "-b", _connection.cookie,
61
- url
62
-
63
-
64
- Process.waitpid(pid, 0)
65
- fail "download failed" unless $?.success?
66
- end
67
-
68
- def is_descriptor? remote_path
69
- url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
70
-
71
- rout, wout = IO.pipe
72
-
73
- pid = spawn CURLBIN, "-I", "-k", '--noproxy', '*', '-f',
74
- "-b", _connection.cookie,
75
- url,
76
- :out => wout,
77
- :err => '/dev/null'
78
-
79
- Process.waitpid(pid, 0)
80
- fail "read image header failed" unless $?.success?
81
-
82
- wout.close
83
- size = rout.readlines.select{|l| l.start_with?("Content-Length")}[0].sub("Content-Length: ","")
84
- rout.close
85
- size.chomp.to_i < 4096 # If <4k, then is a descriptor
86
- end
87
-
88
- def get_text_file remote_path
89
- url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}"
90
-
91
- rout, wout = IO.pipe
92
- pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
93
- "-b", _connection.cookie,
94
- url,
95
- :out => wout,
96
- :err => '/dev/null'
97
-
98
- Process.waitpid(pid, 0)
99
- fail "get text file failed" unless $?.success?
100
-
101
- wout.close
102
- output = rout.readlines
103
- rout.close
104
- return output
105
- end
106
-
107
- end
108
-
109
- module VCenterDriver
110
-
111
- ################################################################################
112
- # This class represents a VCenter connection and an associated OpenNebula client
113
- # The connection is associated to the VCenter backing a given OpenNebula host.
114
- # For the VCenter driver each OpenNebula host represents a VCenter cluster
115
- ################################################################################
116
- class VIClient
117
- attr_reader :vim, :one, :root, :cluster, :user, :pass, :host, :dc
118
-
119
- def self.get_entities(folder, type, entities=[])
120
- return nil if folder == []
121
-
122
- folder.childEntity.each do |child|
123
- name, junk = child.to_s.split('(')
124
-
125
- case name
126
- when "Folder"
127
- VIClient.get_entities(child, type, entities)
128
- when type
129
- entities.push(child)
130
- end
131
- end
132
-
133
- return entities
134
- end
135
-
136
- # Only retrieve properties with faster search
137
- def get_entities_to_import(folder, type)
138
- res = folder.inventory_flat(type => :all)
139
- objects = []
140
-
141
- res.each {|k,v|
142
- if k.to_s.split('(').first == type
143
- obj = {}
144
- v.propSet.each{ |dynprop|
145
- obj[dynprop.name] = dynprop.val
146
- }
147
- obj[:ref] = k._ref
148
- objects << OpenStruct.new(obj)
149
- end
150
- }
151
- return objects
152
- end
153
-
154
- ############################################################################
155
- # Initialize the VIClient, and creates an OpenNebula client. The parameters
156
- # are obtained from the associated OpenNebula host
157
- # @param hid [Integer] The OpenNebula host id with VCenter attributes
158
- ############################################################################
159
- def initialize(hid)
160
-
161
- initialize_one
162
-
163
- @one_host = ::OpenNebula::Host.new_with_id(hid, @one)
164
- rc = @one_host.info
165
-
166
- if ::OpenNebula.is_error?(rc)
167
- raise "Error getting host information: #{rc.message}"
168
- end
169
-
170
- password = @one_host["TEMPLATE/VCENTER_PASSWORD"]
171
-
172
- if !@token.nil?
173
- begin
174
- cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
175
-
176
- cipher.decrypt
177
- cipher.key = @token
178
-
179
- password = cipher.update(Base64::decode64(password))
180
- password << cipher.final
181
- rescue
182
- raise "Error decrypting vCenter password"
183
- end
184
- end
185
-
186
- connection = {
187
- :host => @one_host["TEMPLATE/VCENTER_HOST"],
188
- :user => @one_host["TEMPLATE/VCENTER_USER"],
189
- :password => password
190
- }
191
-
192
- initialize_vim(connection)
193
-
194
- datacenters = VIClient.get_entities(@root, 'Datacenter')
195
-
196
- datacenters.each {|dc|
197
- ccrs = VIClient.get_entities(dc.hostFolder, 'ClusterComputeResource')
198
-
199
- next if ccrs.nil?
200
-
201
- @cluster = ccrs.find{ |ccr| @one_host.name == ccr.name }
202
-
203
- (@dc = dc; break) if @cluster
204
- }
205
-
206
- if @dc.nil? || @cluster.nil?
207
- raise "Cannot find DataCenter or ClusterComputeResource for host."
208
- end
209
- end
210
-
211
- ########################################################################
212
- # Initialize a VIConnection based just on the VIM parameters. The
213
- # OpenNebula client is also initialized
214
- ########################################################################
215
- def self.new_connection(user_opts, one_client=nil)
216
-
217
- conn = allocate
218
-
219
- conn.initialize_one(one_client)
220
-
221
- conn.initialize_vim(user_opts)
222
-
223
- return conn
224
- end
225
-
226
- ########################################################################
227
- # The associated cluster for this connection
228
- ########################################################################
229
- def cluster
230
- @cluster
231
- end
232
-
233
- ########################################################################
234
- # Is this Cluster confined in a resource pool?
235
- ########################################################################
236
- def rp_confined?
237
- !@one_host["TEMPLATE/VCENTER_RESOURCE_POOL"].nil?
238
- end
239
-
240
- ########################################################################
241
- # The associated resource pool for this connection
242
- # @return [ResourcePool] an array of resource pools including the default
243
- #  resource pool. If the connection is confined to a particular
244
- #  resource pool, then return just that one
245
- ########################################################################
246
- def resource_pool
247
- rp_name = @one_host["TEMPLATE/VCENTER_RESOURCE_POOL"]
248
-
249
- if rp_name.nil?
250
- rp_array = @cluster.resourcePool.resourcePool
251
- rp_array << @cluster.resourcePool
252
- rp_array
253
- else
254
- [find_resource_pool(rp_name)]
255
- end
256
- end
257
-
258
- ########################################################################
259
- # Get the default resource pool of the connection. Only valid if
260
- # the connection is not confined in a resource pool
261
- # @return ResourcePool the default resource pool
262
- ########################################################################
263
- def default_resource_pool
264
- @cluster.resourcePool
265
- end
266
-
267
- ########################################################################
268
- # Searches the desired ResourcePool of the DataCenter for the current
269
- # connection. Returns a RbVmomi::VIM::ResourcePool or the default pool
270
- # if not found
271
- # @param rpool [String] the ResourcePool name
272
- ########################################################################
273
- def find_resource_pool(poolName)
274
- baseEntity = @cluster
275
-
276
- entityArray = poolName.split('/')
277
- entityArray.each do |entityArrItem|
278
- if entityArrItem != ''
279
- if baseEntity.is_a? RbVmomi::VIM::Folder
280
- baseEntity = baseEntity.childEntity.find { |f|
281
- f.name == entityArrItem
282
- } or return @cluster.resourcePool
283
- elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource
284
- baseEntity = baseEntity.resourcePool.resourcePool.find { |f|
285
- f.name == entityArrItem
286
- } or return @cluster.resourcePool
287
- elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
288
- baseEntity = baseEntity.resourcePool.find { |f|
289
- f.name == entityArrItem
290
- } or return @cluster.resourcePool
291
- else
292
- return @cluster.resourcePool
293
- end
294
- end
295
- end
296
-
297
- if !baseEntity.is_a?(RbVmomi::VIM::ResourcePool) and
298
- baseEntity.respond_to?(:resourcePool)
299
- baseEntity = baseEntity.resourcePool
300
- end
301
-
302
- baseEntity
303
- end
304
-
305
- ########################################################################
306
- # Searches the associated vmFolder of the DataCenter for the current
307
- # connection. Returns a RbVmomi::VIM::VirtualMachine or nil if not found
308
- #
309
- # Searches by moref, name, uuid and then iterates over all VMs
310
- #
311
- # @param uuid [String] the UUID of the VM or VM Template
312
- # @param ref [String] VMware moref
313
- # @param name [String] VM name in vCenter
314
- ########################################################################
315
- def find_vm_fast(uuid, ref = nil, name = nil)
316
- if ref
317
- # It can raise ManagedObjectNotFound
318
- begin
319
- vm = RbVmomi::VIM::VirtualMachine.new(@dc._connection, ref)
320
- return vm if vm.config && vm.config.uuid == uuid
321
- rescue => e
322
- end
323
- end
324
-
325
- if name
326
- begin
327
- vm = @dc.vmFolder.find(name)
328
- return vm if vm.config && vm.config.uuid == uuid
329
- rescue
330
- end
331
- end
332
-
333
- return find_vm_template(uuid)
334
- end
335
-
336
- ########################################################################
337
- # Searches the associated vmFolder of the DataCenter for the current
338
- # connection. Returns a RbVmomi::VIM::VirtualMachine or nil if not found
339
- # @param uuid [String] the UUID of the VM or VM Template
340
- ########################################################################
341
- def find_vm_template(uuid)
342
- version = @vim.serviceContent.about.version
343
-
344
- found_vm = nil
345
- found_vm = @dc.vmFolder.findByUuid(uuid, RbVmomi::VIM::VirtualMachine, @dc)
346
- return found_vm if found_vm
347
-
348
- vms = VIClient.get_entities(@dc.vmFolder, 'VirtualMachine')
349
-
350
- return vms.find do |v|
351
- begin
352
- v.config && v.config.uuid == uuid
353
- rescue RbVmomi::VIM::ManagedObjectNotFound
354
- false
355
- end
356
- end
357
- end
358
-
359
- ########################################################################
360
- # Searches the associated vmFolder of the DataCenter for the current
361
- # connection. Returns a RbVmomi::VIM::VirtualMachine or nil if not found
362
- # @param vm_name [String] the UUID of the VM or VM Template
363
- ########################################################################
364
- def find_vm(vm_name)
365
- vms = VIClient.get_entities(@dc.vmFolder, 'VirtualMachine')
366
-
367
- return vms.find do |v|
368
- begin
369
- v.name == vm_name
370
- rescue RbVmomi::VIM::ManagedObjectNotFound
371
- false
372
- end
373
- end
374
- end
375
-
376
- ########################################################################
377
- # Searches the associated datacenter for a particular datastore
378
- # @param ds_name [String] name of the datastore
379
- # @returns a RbVmomi::VIM::VirtualMachine or nil if not found
380
- ########################################################################
381
- def get_datastore(ds_name)
382
- datastores = VIClient.get_entities(@dc.datastoreFolder, 'Datastore')
383
-
384
- storage_pods = VIClient.get_entities(@dc.datastoreFolder, 'StoragePod')
385
- storage_pods.each { |sp|
386
- datastores << sp #Add StoragePod
387
-
388
- # Add individual datastores under StoragePod
389
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
390
- if not storage_pod_datastores.empty?
391
- datastores.concat(storage_pod_datastores)
392
- end
393
- }
394
-
395
- ds = datastores.select{|ds| ds.name == ds_name}[0]
396
- end
397
-
398
- ########################################################################
399
- # Builds a hash with the DataCenter / ClusterComputeResource hierarchy
400
- # for this VCenter.
401
- # @return [Hash] in the form
402
- # {dc_name [String] => ClusterComputeResources Names [Array - String]}
403
- ########################################################################
404
- def hierarchy(one_client=nil)
405
- vc_hosts = {}
406
-
407
- datacenters = VIClient.get_entities(@root, 'Datacenter')
408
-
409
- hpool = OpenNebula::HostPool.new((one_client||@one))
410
- rc = hpool.info
411
-
412
- datacenters.each { |dc|
413
- ccrs = VIClient.get_entities(dc.hostFolder, 'ClusterComputeResource')
414
- vc_hosts[dc.name] = []
415
- ccrs.each { |c|
416
- if !hpool["HOST[NAME=\"#{c.name}\"]"]
417
- vc_hosts[dc.name] << c.name
418
- end
419
- }
420
- }
421
-
422
- return vc_hosts
423
- end
424
-
425
- ########################################################################
426
- # Builds a hash with the Datacenter / VM Templates for this VCenter
427
- # @param one_client [OpenNebula::Client] Use this client instead of @one
428
- # @return [Hash] in the form
429
- # { dc_name [String] => Templates [Array] }
430
- ########################################################################
431
- def vm_templates(one_client=nil)
432
- vm_templates = {}
433
-
434
- tpool = OpenNebula::TemplatePool.new(
435
- (one_client||@one), OpenNebula::Pool::INFO_ALL)
436
- rc = tpool.info
437
- if OpenNebula.is_error?(rc)
438
- raise "Error contacting OpenNebula #{rc.message}"
439
- end
440
-
441
- datacenters = VIClient.get_entities(@root, 'Datacenter')
442
-
443
- datacenters.each { |dc|
444
- vms = get_entities_to_import(dc.vmFolder, 'VirtualMachine')
445
-
446
- tmp = vms.select { |v| v.config && (v.config.template == true) }
447
-
448
- one_tmp = []
449
- host_cache = {}
450
- ds_cache = {}
451
-
452
- tmp.each { |t|
453
- vi_tmp = VCenterVm.new(self, t)
454
-
455
- if !tpool["VMTEMPLATE/TEMPLATE/PUBLIC_CLOUD[\
456
- TYPE=\"vcenter\" \
457
- and VM_TEMPLATE=\"#{vi_tmp.vm.config.uuid}\"]"]
458
- # Check cached objects
459
- if !host_cache[vi_tmp.vm.runtime.host.to_s]
460
- host_cache[vi_tmp.vm.runtime.host.to_s] =
461
- VCenterCachedHost.new vi_tmp.vm.runtime.host
462
- end
463
-
464
- if !ds_cache[t.datastore[0].to_s]
465
- ds_cache[t.datastore[0].to_s] =
466
- VCenterCachedDatastore.new t.datastore[0]
467
- end
468
-
469
- host = host_cache[vi_tmp.vm.runtime.host.to_s]
470
- ds = ds_cache[t.datastore[0].to_s]
471
-
472
- one_tmp << {
473
- :name => "#{vi_tmp.vm.name} - #{host.cluster_name}",
474
- :uuid => vi_tmp.vm.config.uuid,
475
- :host => host.cluster_name,
476
- :one => vi_tmp.to_one(host),
477
- :ds => vi_tmp.to_one_ds(host, ds.name),
478
- :default_ds => ds.name,
479
- :rp => vi_tmp.to_one_rp(host),
480
- :vcenter_ref => vi_tmp.vm._ref,
481
- :vcenter_name => vi_tmp.vm.name
482
- }
483
- end
484
- }
485
-
486
- vm_templates[dc.name] = one_tmp
487
- }
488
-
489
- return vm_templates
490
- end
491
-
492
- ########################################################################
493
- # Builds a hash with the Datacenter / CCR (Distributed)Networks
494
- # for this VCenter
495
- # @param one_client [OpenNebula::Client] Use this client instead of @one
496
- # @return [Hash] in the form
497
- # { dc_name [String] => Networks [Array] }
498
- ########################################################################
499
- def vcenter_networks(one_client=nil)
500
- vcenter_networks = {}
501
-
502
- vnpool = OpenNebula::VirtualNetworkPool.new(
503
- (one_client||@one), OpenNebula::Pool::INFO_ALL)
504
- rc = vnpool.info
505
- if OpenNebula.is_error?(rc)
506
- raise "Error contacting OpenNebula #{rc.message}"
507
- end
508
-
509
- datacenters = VIClient.get_entities(@root, 'Datacenter')
510
-
511
- datacenters.each { |dc|
512
- networks = VIClient.get_entities(dc.networkFolder, 'Network' )
513
- one_nets = []
514
-
515
- networks.each { |n|
516
- # Skip those not in cluster
517
- next if !n[:host][0]
518
-
519
- # Networks can be in several cluster, create one per cluster
520
- net_names = []
521
- Array(n[:host]).each{ |host_system|
522
- net_name = "#{n.name} - #{host_system.parent.name}"
523
- if !net_names.include?(net_name)
524
- if !vnpool["VNET[BRIDGE=\"#{n[:name]}\"]/\
525
- TEMPLATE[VCENTER_TYPE=\"Port Group\"]"]
526
- one_nets << {
527
- :name => net_name,
528
- :bridge => n.name,
529
- :cluster => host_system.parent.name,
530
- :type => "Port Group",
531
- :one => "NAME = \"#{net_name}\"\n" \
532
- "BRIDGE = \"#{n[:name]}\"\n" \
533
- "VN_MAD = \"dummy\"\n" \
534
- "VCENTER_TYPE = \"Port Group\""
535
- }
536
- net_names << net_name
537
- end
538
- end
539
- }
540
- }
541
-
542
- networks = VIClient.get_entities(dc.networkFolder,
543
- 'DistributedVirtualPortgroup' )
544
-
545
- networks.each { |n|
546
- # Skip those not in cluster
547
- next if !n[:host][0]
548
-
549
- # DistributedVirtualPortgroup can be in several cluster,
550
- # create one per cluster
551
- Array(n[:host][0]).each{ |host_system|
552
- net_name = "#{n.name} - #{n[:host][0].parent.name}"
553
-
554
- if !vnpool["VNET[BRIDGE=\"#{n[:name]}\"]/\
555
- TEMPLATE[VCENTER_TYPE=\"Distributed Port Group\"]"]
556
- vnet_template = "NAME = \"#{net_name}\"\n" \
557
- "BRIDGE = \"#{n[:name]}\"\n" \
558
- "VN_MAD = \"dummy\"\n" \
559
- "VCENTER_TYPE = \"Distributed Port Group\""
560
-
561
- default_pc = n.config.defaultPortConfig
562
-
563
- has_vlan = false
564
- vlan_str = ""
565
-
566
- if default_pc.methods.include? :vlan
567
- has_vlan = default_pc.vlan.methods.include? :vlanId
568
- end
569
-
570
- if has_vlan
571
- vlan = n.config.defaultPortConfig.vlan.vlanId
572
-
573
- if vlan != 0
574
- if vlan.is_a? Array
575
- vlan.each{|v|
576
- vlan_str += v.start.to_s + ".." +
577
- v.end.to_s + ","
578
- }
579
- vlan_str.chop!
580
- else
581
- vlan_str = vlan.to_s
582
- end
583
- end
584
- end
585
-
586
- if !vlan_str.empty?
587
- vnet_template << "VLAN_TAGGED_ID=#{vlan_str}\n"
588
- end
589
-
590
- one_net = {:name => net_name,
591
- :bridge => n.name,
592
- :cluster => host_system.parent.name,
593
- :type => "Distributed Port Group",
594
- :one => vnet_template}
595
-
596
- one_net[:vlan] = vlan_str if !vlan_str.empty?
597
-
598
- one_nets << one_net
599
- end
600
- }
601
- }
602
-
603
- vcenter_networks[dc.name] = one_nets
604
- }
605
-
606
- return vcenter_networks
607
- end
608
-
609
-
610
- ########################################################################
611
- # Builds a hash with the Datacenter / Datastores for this VCenter
612
- # @param one_client [OpenNebula::Client] Use this client instead of @one
613
- # @return [Hash] in the form
614
- # { dc_name [String] => Datastore [Array] of DS templates}
615
- ########################################################################
616
- def vcenter_datastores(one_client=nil)
617
- ds_templates = {}
618
-
619
- dspool = OpenNebula::DatastorePool.new(
620
- (one_client||@one))
621
- rc = dspool.info
622
- if OpenNebula.is_error?(rc)
623
- raise "Error contacting OpenNebula #{rc.message}"
624
- end
625
-
626
- hpool = OpenNebula::HostPool.new(
627
- (one_client||@one))
628
- rc = hpool.info
629
- if OpenNebula.is_error?(rc)
630
- raise "Error contacting OpenNebula #{rc.message}"
631
- end
632
-
633
- datacenters = VIClient.get_entities(@root, 'Datacenter')
634
-
635
- datacenters.each { |dc|
636
- one_tmp = []
637
- datastores = VIClient.get_entities(dc.datastoreFolder, 'Datastore')
638
-
639
- storage_pods = VIClient.get_entities(dc.datastoreFolder, 'StoragePod')
640
- storage_pods.each { |sp|
641
- datastores << sp # Add StoragePod
642
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
643
- if not storage_pod_datastores.empty?
644
- datastores.concat(storage_pod_datastores)
645
- end
646
- }
647
-
648
- datastores.each { |ds|
649
- next if !ds.is_a? RbVmomi::VIM::Datastore and !ds.is_a? RbVmomi::VIM::StoragePod
650
- # Find the Cluster from which to access this ds
651
-
652
- cluster_name = ""
653
- if ds.is_a? RbVmomi::VIM::StoragePod
654
- storage_pod_datastores = VIClient.get_entities(ds, 'Datastore')
655
- storage_pod_datastores.each { |sp|
656
- next if !sp.is_a? RbVmomi::VIM::Datastore
657
- # Find the Cluster from which to access this ds
658
- next if !sp.host[0]
659
- cluster_name = sp.host[0].key.parent.name
660
- break
661
- }
662
- else
663
- next if !ds.host[0]
664
- cluster_name = ds.host[0].key.parent.name
665
- end
666
-
667
- if !dspool["DATASTORE[NAME=\"#{ds.name}\"]"] and
668
- hpool["HOST[NAME=\"#{cluster_name}\"]"]
669
- if ds.is_a? RbVmomi::VIM::StoragePod
670
- one_tmp << {
671
- :name => "#{ds.name}",
672
- :total_mb => ((ds.summary.capacity.to_i / 1024) / 1024),
673
- :free_mb => ((ds.summary.freeSpace.to_i / 1024) / 1024),
674
- :cluster => cluster_name,
675
- :one => "NAME=#{ds.name}\n"\
676
- "TM_MAD=vcenter\n"\
677
- "VCENTER_CLUSTER=#{cluster_name}\n"\
678
- "TYPE=SYSTEM_DS\n" # StoragePods must be set as SYSTEM_DS
679
- }
680
- else
681
- one_tmp << {
682
- :name => "#{ds.name}",
683
- :total_mb => ((ds.summary.capacity.to_i / 1024) / 1024),
684
- :free_mb => ((ds.summary.freeSpace.to_i / 1024) / 1024),
685
- :cluster => cluster_name,
686
- :one => "NAME=#{ds.name}\n"\
687
- "DS_MAD=vcenter\n"\
688
- "TM_MAD=vcenter\n"\
689
- "VCENTER_CLUSTER=#{cluster_name}\n"
690
- }
691
- end
692
- end
693
- }
694
- ds_templates[dc.name] = one_tmp
695
- }
696
-
697
- return ds_templates
698
- end
699
-
700
- #############################################################################
701
- # Builds a hash with the Images for a particular datastore
702
- # @param one_client [OpenNebula::Client] Use this client instead of @one
703
- # @return [Array] of image templates
704
- ############################################################################
705
- def vcenter_images(ds_name, one_client=nil)
706
- img_types = ["FloppyImageFileInfo",
707
- "IsoImageFileInfo",
708
- "VmDiskFileInfo"]
709
-
710
- img_templates = []
711
-
712
- ipool = OpenNebula::ImagePool.new((one_client||@one))
713
- rc = ipool.info
714
- if OpenNebula.is_error?(rc)
715
- raise "Error contacting OpenNebula #{rc.message}"
716
- end
717
-
718
- dspool = OpenNebula::DatastorePool.new((one_client||@one))
719
- rc = dspool.info
720
- if OpenNebula.is_error?(rc)
721
- raise "Error contacting OpenNebula #{rc.message}"
722
- end
723
-
724
- ds_id = dspool["DATASTORE[NAME=\"#{ds_name}\"]/ID"]
725
-
726
- if !ds_id
727
- raise "Datastore not found in OpenNebula. Please import"\
728
- " it first and try again"
729
- end
730
-
731
- datacenters = VIClient.get_entities(@root, 'Datacenter')
732
-
733
- datacenters.each { |dc|
734
-
735
- # Find datastore within datacenter
736
- datastores = VIClient.get_entities(dc.datastoreFolder, 'Datastore')
737
-
738
- storage_pods = VIClient.get_entities(dc.datastoreFolder, 'StoragePod')
739
- storage_pods.each { |sp|
740
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
741
- if not storage_pod_datastores.empty?
742
- datastores.concat(storage_pod_datastores)
743
- end
744
- }
745
-
746
- ds = datastores.select{|ds| ds.name == ds_name}[0]
747
- next if !ds
748
-
749
- # Cannot import from StoragePod directly
750
- if ds.is_a? RbVmomi::VIM::StoragePod
751
- raise "OpenNebula cannot import images from a StoragePod. Please import"\
752
- " it from the datastore which is a member of the StorageDRS cluster"
753
- end
754
-
755
- # Create Search Spec
756
- spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new
757
- spec.query = [RbVmomi::VIM::VmDiskFileQuery.new,
758
- RbVmomi::VIM::IsoImageFileQuery.new]
759
- spec.details = RbVmomi::VIM::FileQueryFlags(:fileOwner => true,
760
- :fileSize => true,
761
- :fileType => true,
762
- :modification => true)
763
- spec.matchPattern=[]
764
-
765
- search_params = {'datastorePath' => "[#{ds.name}]",
766
- 'searchSpec' => spec}
767
-
768
- # Perform search task and return results
769
- search_task=ds.browser.SearchDatastoreSubFolders_Task(search_params)
770
- search_task.wait_for_completion
771
-
772
- search_task.info.result.each { |image|
773
- folderpath = ""
774
- if image.folderPath[-1] != "]"
775
- folderpath = image.folderPath.sub(/^\[#{ds_name}\] /, "")
776
- end
777
-
778
- image = image.file[0]
779
-
780
- # Skip not relevant files
781
- next if !img_types.include? image.class.to_s
782
-
783
- image_path = folderpath + image.path
784
-
785
- image_name = File.basename(image.path).reverse.sub("kdmv.","").reverse
786
-
787
- if !ipool["IMAGE[NAME=\"#{image_name} - #{ds_name}\"]"]
788
- img_templates << {
789
- :name => "#{image_name} - #{ds_name}",
790
- :path => image_path,
791
- :size => (image.fileSize / 1024).to_s,
792
- :type => image.class.to_s,
793
- :dsid => ds_id,
794
- :one => "NAME=\"#{image_name} - #{ds_name}\"\n"\
795
- "PATH=\"vcenter://#{image_path}\"\n"\
796
- "PERSISTENT=\"YES\"\n"\
797
- }
798
-
799
- if image.class.to_s == "VmDiskFileInfo"
800
- img_templates[-1][:one] += "TYPE=\"OS\"\n"
801
- else
802
- img_templates[-1][:one] += "TYPE=\"CDROM\"\n"
803
- end
804
-
805
- if image.class.to_s == "VmDiskFileInfo" &&
806
- !image.diskType.nil?
807
- img_templates[-1][:one] += "DISK_TYPE=#{image.diskType}\n"
808
- end
809
- end
810
- }
811
- }
812
-
813
- return img_templates
814
- end
815
-
816
- def self.translate_hostname(hostname)
817
- host_pool = OpenNebula::HostPool.new(::OpenNebula::Client.new())
818
- rc = host_pool.info
819
- raise "Could not find host #{hostname}" if OpenNebula.is_error?(rc)
820
-
821
- host = host_pool.select {|host_element| host_element.name==hostname }
822
- return host.first.id
823
- end
824
-
825
- def self.find_ds_name(ds_id)
826
- ds = OpenNebula::Datastore.new_with_id(ds_id)
827
- rc = ds.info
828
- raise "Could not find datastore #{ds_id}" if OpenNebula.is_error?(rc)
829
-
830
- return ds.name
831
- end
832
-
833
- ############################################################################
834
- # Initialize an OpenNebula connection with the default ONE_AUTH
835
- ############################################################################
836
- def initialize_one(one_client=nil)
837
- begin
838
- if one_client
839
- @one = one_client
840
- else
841
- @one = ::OpenNebula::Client.new()
842
- end
843
-
844
- system = ::OpenNebula::System.new(@one)
845
-
846
- config = system.get_configuration()
847
-
848
- if ::OpenNebula.is_error?(config)
849
- raise "Error getting oned configuration : #{config.message}"
850
- end
851
-
852
- @token = config["ONE_KEY"]
853
- rescue Exception => e
854
- raise "Error initializing OpenNebula client: #{e.message}"
855
- end
856
- end
857
-
858
- ############################################################################
859
- # Initialize a connection with vCenter. Options
860
- # @param options[Hash] with:
861
- # :user => The vcenter user
862
- # :password => Password for the user
863
- # :host => vCenter hostname or IP
864
- # :insecure => SSL (optional, defaults to true)
865
- ############################################################################
866
- def initialize_vim(user_opts={})
867
- opts = {
868
- :insecure => true
869
- }.merge(user_opts)
870
-
871
- @user = opts[:user]
872
- @pass = opts[:password]
873
- @host = opts[:host]
874
-
875
- begin
876
- @vim = RbVmomi::VIM.connect(opts)
877
- @root = @vim.root
878
- @vdm = @vim.serviceContent.virtualDiskManager
879
- @file_manager = @vim.serviceContent.fileManager
880
- rescue Exception => e
881
- raise "Error connecting to #{@host}: #{e.message}"
882
- end
883
- end
884
-
885
- ######################### Datastore Operations #############################
886
-
887
- ############################################################################
888
- # Retrieve size for a VirtualDisk in a particular datastore
889
- # @param ds_name [String] name of the datastore
890
- # @param img_str [String] path to the VirtualDisk
891
- # @return size of the file in Kb
892
- ############################################################################
893
- def stat(ds_name, img_str)
894
- img_path = File.dirname img_str
895
- img_name = File.basename img_str
896
-
897
- # Find datastore within datacenter
898
- ds = get_datastore(ds_name)
899
-
900
- # Create Search Spec
901
- spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new
902
- spec.query = [RbVmomi::VIM::VmDiskFileQuery.new,
903
- RbVmomi::VIM::IsoImageFileQuery.new]
904
- spec.details = RbVmomi::VIM::FileQueryFlags(:fileOwner => true,
905
- :fileSize => true,
906
- :fileType => true,
907
- :modification => true)
908
- spec.matchPattern=[img_name]
909
-
910
- search_params = {'datastorePath' => "[#{ds_name}] #{img_path}",
911
- 'searchSpec' => spec}
912
-
913
- # Perform search task and return results
914
- search_task=ds.browser.SearchDatastoreSubFolders_Task(search_params)
915
- search_task.wait_for_completion
916
- (search_task.info.result[0].file[0].fileSize / 1024) / 1024
917
- end
918
-
919
- ############################################################################
920
- # Returns Datastore information
921
- # @param ds_name [String] name of the datastore
922
- # @return [String] monitor information of the DS
923
- ############################################################################
924
- def monitor_ds(ds_name)
925
- # Find datastore within datacenter
926
- ds = get_datastore(ds_name)
927
-
928
- total_mb = (ds.summary.capacity.to_i / 1024) / 1024
929
- free_mb = (ds.summary.freeSpace.to_i / 1024) / 1024
930
- used_mb = total_mb - free_mb
931
-
932
- if ds.is_a? RbVmomi::VIM::Datastore
933
- ds_type = ds.summary.type
934
- end
935
-
936
- "USED_MB=#{used_mb}\nFREE_MB=#{free_mb} \nTOTAL_MB=#{total_mb}"
937
- end
938
-
939
- ############################################################################
940
- # Copy a VirtualDisk
941
- # @param ds_name [String] name of the datastore
942
- # @param img_str [String] path to the VirtualDisk
943
- ############################################################################
944
- def copy_virtual_disk(source_path, source_ds, target_path, target_ds=nil)
945
- target_ds = source_ds if target_ds.nil?
946
-
947
- copy_params= {:sourceName => "[#{source_ds}] #{source_path}",
948
- :sourceDatacenter => @dc,
949
- :destName => "[#{target_ds}] #{target_path}"}
950
-
951
- @vdm.CopyVirtualDisk_Task(copy_params).wait_for_completion
952
-
953
- target_path
954
- end
955
-
956
- ############################################################################
957
- # Create a VirtualDisk
958
- # @param img_name [String] name of the image
959
- # @param ds_name [String] name of the datastore on which the VD will be
960
- # created
961
- # @param size [String] size of the new image in MB
962
- # @param adapter_type [String] as described in
963
- #  http://pubs.vmware.com/vsphere-60/index.jsp#com.vmware.wssdk.apiref.doc/vim.VirtualDiskManager.VirtualDiskAdapterType.html
964
- # @param disk_type [String] as described in
965
- #  http://pubs.vmware.com/vsphere-60/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvim.VirtualDiskManager.VirtualDiskType.html
966
- # @return name of the final image
967
- ############################################################################
968
- def create_virtual_disk(img_name, ds_name, size, adapter_type, disk_type)
969
- vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
970
- :adapterType => adapter_type,
971
- :capacityKb => size.to_i*1024,
972
- :diskType => disk_type
973
- )
974
-
975
- @vdm.CreateVirtualDisk_Task(
976
- :datacenter => @dc,
977
- :name => "[#{ds_name}] #{img_name}.vmdk",
978
- :spec => vmdk_spec
979
- ).wait_for_completion
980
-
981
- "#{img_name}.vmdk"
982
- end
983
-
984
- ############################################################################
985
- # Delete a VirtualDisk
986
- # @param img_name [String] name of the image
987
- # @param ds_name [String] name of the datastore where the VD resides
988
- ############################################################################
989
- def delete_virtual_disk(img_name, ds_name)
990
- @vdm.DeleteVirtualDisk_Task(
991
- name: "[#{ds_name}] #{img_name}",
992
- datacenter: @dc
993
- ).wait_for_completion
994
- end
995
-
996
- ############################################################################
997
- # Delete a VirtualDisk
998
- # @param directory [String] name of the new directory
999
- # @param ds_name [String] name of the datastore where to create the dir
1000
- ############################################################################
1001
- def create_directory(directory, ds_name)
1002
- begin
1003
- path = "[#{ds_name}] #{directory}"
1004
- @file_manager.MakeDirectory(:name => path,
1005
- :datacenter => @dc,
1006
- :createParentDirectories => true)
1007
- rescue RbVmomi::VIM::FileAlreadyExists => e
1008
- end
1009
- end
1010
-
1011
- ############################################################################
1012
- # Silences standard output and error
1013
- ############################################################################
1014
- def self.in_silence
1015
- begin
1016
- orig_stderr = $stderr.clone
1017
- orig_stdout = $stdout.clone
1018
- $stderr.reopen File.new('/dev/null', 'w')
1019
- $stdout.reopen File.new('/dev/null', 'w')
1020
- retval = yield
1021
- rescue Exception => e
1022
- $stdout.reopen orig_stdout
1023
- $stderr.reopen orig_stderr
1024
- raise e
1025
- ensure
1026
- $stdout.reopen orig_stdout
1027
- $stderr.reopen orig_stderr
1028
- end
1029
- retval
1030
- end
1031
-
1032
- ############################################################################
1033
- # Silences standard output and error
1034
- ############################################################################
1035
- def self.in_stderr_silence
1036
- begin
1037
- orig_stderr = $stderr.clone
1038
- $stderr.reopen File.new('/dev/null', 'w')
1039
- retval = yield
1040
- rescue Exception => e
1041
- $stderr.reopen orig_stderr
1042
- raise e
1043
- ensure
1044
- $stderr.reopen orig_stderr
1045
- end
1046
- retval
1047
- end
1048
- end
1049
-
1050
- ################################################################################
1051
- # Cached Classes to speed up import and monitoring
1052
- ################################################################################
1053
- class VCenterCachedHost
1054
-
1055
- def initialize(rbVmomiHost)
1056
- @host = rbVmomiHost
1057
- @attributes = Hash.new
1058
- end
1059
-
1060
- def name
1061
- if !@attributes['name']
1062
- @attributes['name']=@host.parent.name
1063
- end
1064
- @attributes['name']
1065
- end
1066
-
1067
- def cluster_name
1068
- if !@attributes['cluster_name']
1069
- @attributes['cluster_name']=@host.parent.name
1070
- end
1071
- @attributes['cluster_name']
1072
- end
1073
-
1074
- def ds_list
1075
- if !@attributes['ds_list']
1076
- @attributes['ds_list']=""
1077
-
1078
- datacenter = @host.parent
1079
- while !datacenter.is_a? RbVmomi::VIM::Datacenter
1080
- datacenter = datacenter.parent
1081
- end
1082
-
1083
- datastores=VIClient.get_entities(
1084
- datacenter.datastoreFolder,
1085
- 'Datastore')
1086
-
1087
- storage_pods = VIClient.get_entities(datacenter.datastoreFolder,
1088
- 'StoragePod')
1089
- storage_pods.each { |sp|
1090
- datastores << sp # Add Storage Pod
1091
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
1092
- if not storage_pod_datastores.empty?
1093
- datastores.concat(storage_pod_datastores)
1094
- end
1095
- }
1096
-
1097
- datastores.each { |ds|
1098
- @attributes['ds_list'] += ds.name + ","
1099
- }
1100
- @attributes['ds_list']=@attributes['ds_list'][0..-2]
1101
- end
1102
- @attributes['ds_list']
1103
- end
1104
-
1105
- def rp_list
1106
- if !@attributes['rp_list']
1107
- @attributes['rp_list']=""
1108
- @host.parent.resourcePool.resourcePool.each{|rp|
1109
- @attributes['rp_list'] += get_child_rp_names(rp, "")
1110
- }
1111
- @attributes['rp_list']=@attributes['rp_list'][0..-2]
1112
- end
1113
- @attributes['rp_list']
1114
- end
1115
-
1116
- def get_child_rp_names(rp, parent_prefix)
1117
- rp_str = ""
1118
-
1119
- current_rp = (parent_prefix.empty? ? "" : parent_prefix + "/")
1120
- current_rp += rp.name
1121
-
1122
- if rp.resourcePool.size != 0
1123
- rp.resourcePool.each{|child_rp|
1124
- rp_str += get_child_rp_names(child_rp, current_rp)
1125
- }
1126
- end
1127
-
1128
- rp_str += current_rp + ","
1129
-
1130
- return rp_str
1131
- end
1132
-
1133
- def cpumhz
1134
- if !@attributes['cpumhz']
1135
- @attributes['cpumhz']=@host.summary.hardware.cpuMhz.to_f
1136
- end
1137
- @attributes['cpumhz']
1138
- end
1139
-
1140
- end
1141
-
1142
- class VCenterCachedDatastore
47
+ # ---------------------------------------------------------------------------- #
48
+ # vCenter Library #
49
+ # ---------------------------------------------------------------------------- #
1143
50
 
1144
- def initialize(rbVmomiDatastore)
1145
- @ds = rbVmomiDatastore
1146
- @attributes = Hash.new
1147
- end
51
+ require 'memoize'
52
+ require 'vi_client'
53
+ require 'vi_helper'
54
+ require 'datacenter'
55
+ require 'host'
56
+ require 'datastore'
57
+ require 'virtual_machine'
58
+ require 'network'
59
+ require 'file_helper'
60
+ require 'importer'
1148
61
 
1149
- def name
1150
- if !@attributes['name']
1151
- @attributes['name']=@ds.name
1152
- end
1153
- @attributes['name']
1154
- end
62
+ # ---------------------------------------------------------------------------- #
63
+ # Helper functions #
64
+ # ---------------------------------------------------------------------------- #
1155
65
 
66
+ def error_message(message)
67
+ error_str = "ERROR MESSAGE --8<------\n"
68
+ error_str << message
69
+ error_str << "\nERROR MESSAGE ------>8--"
1156
70
 
71
+ return error_str
1157
72
  end
1158
73
 
1159
- ################################################################################
1160
- # This class is an OpenNebula hosts that abstracts a vCenter cluster. It
1161
- # includes the functionality needed to monitor the cluster and report the ESX
1162
- # hosts and VM status of the cluster.
1163
- ################################################################################
1164
- class VCenterHost < ::OpenNebula::Host
1165
- attr_reader :vc_client, :vc_root, :cluster, :host, :client
1166
-
1167
- ############################################################################
1168
- # Initialize the VCenterHost by looking for the associated objects of the
1169
- # VIM hierarchy
1170
- # client [VIClient] to interact with the associated vCenter
1171
- ############################################################################
1172
- def initialize(client)
1173
- @client = client
1174
- @cluster = client.cluster
1175
-
1176
- @resource_pools = client.resource_pool
1177
- end
1178
-
1179
- ########################################################################
1180
- # Creates an OpenNebula host representing a cluster in this VCenter
1181
- # @param cluster_name[String] the name of the cluster in the vcenter
1182
- # @param client [VIClient] to create the host
1183
- # @return In case of success [0, host_id] or [-1, error_msg]
1184
- ########################################################################
1185
- def self.to_one(cluster_name, client)
1186
- one_host = ::OpenNebula::Host.new(::OpenNebula::Host.build_xml,
1187
- client.one)
1188
-
1189
- rc = one_host.allocate(cluster_name, 'vcenter', 'vcenter',
1190
- ::OpenNebula::ClusterPool::NONE_CLUSTER_ID)
1191
-
1192
- return -1, rc.message if ::OpenNebula.is_error?(rc)
1193
-
1194
- template = "VCENTER_HOST=\"#{client.host}\"\n"\
1195
- "VCENTER_PASSWORD=\"#{client.pass}\"\n"\
1196
- "VCENTER_USER=\"#{client.user}\"\n"
1197
-
1198
- rc = one_host.update(template, false)
1199
-
1200
- if ::OpenNebula.is_error?(rc)
1201
- error = rc.message
1202
-
1203
- rc = one_host.delete
1204
-
1205
- if ::OpenNebula.is_error?(rc)
1206
- error << ". Host #{cluster_name} could not be"\
1207
- " deleted: #{rc.message}."
1208
- end
1209
-
1210
- return -1, error
1211
- end
1212
-
1213
- return 0, one_host.id
74
+ def check_valid(parameter, label)
75
+ if parameter.nil? || parameter.empty?
76
+ STDERR.puts error_message("The parameter '#{label}' is required for this action.")
77
+ exit -1
1214
78
  end
1215
-
1216
- ############################################################################
1217
- # Generate an OpenNebula monitor string for this host. Reference:
1218
- # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/Reference
1219
- # Guide/vim.ComputeResource.Summary.html
1220
- # - effectiveCpu: Effective CPU resources (in MHz) available to run
1221
- # VMs. This is the aggregated from all running hosts excluding hosts in
1222
- # maintenance mode or unresponsive are not counted.
1223
- # - effectiveMemory: Effective memory resources (in MB) available to run
1224
- # VMs. Equivalente to effectiveCpu.
1225
- # - numCpuCores: Number of physical CPU cores.
1226
- # - numEffectiveHosts: Total number of effective hosts.
1227
- # - numHosts:Total number of hosts.
1228
- # - totalCpu: Aggregated CPU resources of all hosts, in MHz.
1229
- # - totalMemory: Aggregated memory resources of all hosts, in bytes.
1230
- ############################################################################
1231
- def monitor_cluster
1232
- #Load the host systems
1233
- summary = @cluster.summary
1234
-
1235
- mhz_core = summary.totalCpu.to_f / summary.numCpuCores.to_f
1236
- eff_core = summary.effectiveCpu.to_f / mhz_core
1237
-
1238
- free_cpu = sprintf('%.2f', eff_core * 100).to_f
1239
- total_cpu = summary.numCpuCores.to_f * 100
1240
- used_cpu = sprintf('%.2f', total_cpu - free_cpu).to_f
1241
-
1242
- total_mem = summary.totalMemory.to_i / 1024
1243
- free_mem = summary.effectiveMemory.to_i * 1024
1244
-
1245
- str_info = ""
1246
-
1247
- # System
1248
- str_info << "HYPERVISOR=vcenter\n"
1249
- str_info << "PUBLIC_CLOUD=YES\n"
1250
- str_info << "TOTALHOST=" << summary.numHosts.to_s << "\n"
1251
- str_info << "AVAILHOST=" << summary.numEffectiveHosts.to_s << "\n"
1252
-
1253
- # CPU
1254
- str_info << "CPUSPEED=" << mhz_core.to_s << "\n"
1255
- str_info << "TOTALCPU=" << total_cpu.to_s << "\n"
1256
- str_info << "USEDCPU=" << used_cpu.to_s << "\n"
1257
- str_info << "FREECPU=" << free_cpu.to_s << "\n"
1258
-
1259
- # Memory
1260
- str_info << "TOTALMEMORY=" << total_mem.to_s << "\n"
1261
- str_info << "FREEMEMORY=" << free_mem.to_s << "\n"
1262
- str_info << "USEDMEMORY=" << (total_mem - free_mem).to_s
1263
-
1264
- str_info << monitor_resource_pools(@cluster.resourcePool, "", mhz_core)
1265
- end
1266
-
1267
- ############################################################################
1268
- # Generate an OpenNebula monitor string for all resource pools of a cluster
1269
- # Reference:
1270
- # http://pubs.vmware.com/vsphere-60/index.jsp#com.vmware.wssdk.apiref.doc
1271
- # /vim.ResourcePool.html
1272
- ############################################################################
1273
- def monitor_resource_pools(parent_rp, parent_prefix, mhz_core)
1274
- return "" if parent_rp.resourcePool.size == 0
1275
-
1276
- rp_info = ""
1277
-
1278
- parent_rp.resourcePool.each{|rp|
1279
- rpcpu = rp.config.cpuAllocation
1280
- rpmem = rp.config.memoryAllocation
1281
- # CPU
1282
- cpu_expandable = rpcpu.expandableReservation ? "YES" : "NO"
1283
- cpu_limit = rpcpu.limit == "-1" ? "UNLIMITED" : rpcpu.limit
1284
- cpu_reservation = rpcpu.reservation
1285
- cpu_num = rpcpu.reservation.to_f / mhz_core
1286
- cpu_shares_level = rpcpu.shares.level
1287
- cpu_shares = rpcpu.shares.shares
1288
-
1289
- # MEMORY
1290
- mem_expandable = rpmem.expandableReservation ? "YES" : "NO"
1291
- mem_limit = rpmem.limit == "-1" ? "UNLIMITED" : rpmem.limit
1292
- mem_reservation = rpmem.reservation.to_f
1293
- mem_shares_level = rpmem.shares.level
1294
- mem_shares = rpmem.shares.shares
1295
-
1296
- rp_name = (parent_prefix.empty? ? "" : parent_prefix + "/")
1297
- rp_name += rp.name
1298
-
1299
- rp_info << "\nRESOURCE_POOL = ["
1300
- rp_info << "NAME=\"#{rp_name}\","
1301
- rp_info << "CPU_EXPANDABLE=#{cpu_expandable},"
1302
- rp_info << "CPU_LIMIT=#{cpu_limit},"
1303
- rp_info << "CPU_RESERVATION=#{cpu_reservation},"
1304
- rp_info << "CPU_RESERVATION_NUM_CORES=#{cpu_num},"
1305
- rp_info << "CPU_SHARES=#{cpu_shares},"
1306
- rp_info << "CPU_SHARES_LEVEL=#{cpu_shares_level},"
1307
- rp_info << "MEM_EXPANDABLE=#{mem_expandable},"
1308
- rp_info << "MEM_LIMIT=#{mem_limit},"
1309
- rp_info << "MEM_RESERVATION=#{mem_reservation},"
1310
- rp_info << "MEM_SHARES=#{mem_shares},"
1311
- rp_info << "MEM_SHARES_LEVEL=#{mem_shares_level}"
1312
- rp_info << "]"
1313
-
1314
- if rp.resourcePool.size != 0
1315
- rp_info << monitor_resource_pools(rp, rp_name, mhz_core)
1316
- end
1317
- }
1318
-
1319
- return rp_info
1320
- end
1321
-
1322
- ############################################################################
1323
- # Generate a template with information for each ESX Host. Reference:
1324
- # http://pubs.vmware.com/vi-sdk/visdk250/ReferenceGuide/vim.HostSystem.html
1325
- # - Summary: Basic information about the host, including connection state
1326
- # - hardware: Hardware configuration of the host. This might not be
1327
- # available for a disconnected host.
1328
- # - quickStats: Basic host statistics.
1329
- ############################################################################
1330
- def monitor_host_systems
1331
- host_info = ""
1332
-
1333
- @cluster.host.each{|h|
1334
- next if h.runtime.connectionState != "connected"
1335
-
1336
- summary = h.summary
1337
- hw = summary.hardware
1338
- stats = summary.quickStats
1339
-
1340
- total_cpu = hw.numCpuCores * 100
1341
- used_cpu = (stats.overallCpuUsage.to_f / hw.cpuMhz.to_f) * 100
1342
- used_cpu = sprintf('%.2f', used_cpu).to_f # Trim precission
1343
- free_cpu = total_cpu - used_cpu
1344
-
1345
- total_memory = hw.memorySize/1024
1346
- used_memory = stats.overallMemoryUsage*1024
1347
- free_memory = total_memory - used_memory
1348
-
1349
- host_info << "\nHOST=["
1350
- host_info << "STATE=on,"
1351
- host_info << "HOSTNAME=\"" << h.name.to_s << "\","
1352
- host_info << "MODELNAME=\"" << hw.cpuModel.to_s << "\","
1353
- host_info << "CPUSPEED=" << hw.cpuMhz.to_s << ","
1354
- host_info << "MAX_CPU=" << total_cpu.to_s << ","
1355
- host_info << "USED_CPU=" << used_cpu.to_s << ","
1356
- host_info << "FREE_CPU=" << free_cpu.to_s << ","
1357
- host_info << "MAX_MEM=" << total_memory.to_s << ","
1358
- host_info << "USED_MEM=" << used_memory.to_s << ","
1359
- host_info << "FREE_MEM=" << free_memory.to_s
1360
- host_info << "]"
1361
- }
1362
-
1363
- return host_info
1364
- end
1365
-
1366
- def monitor_vms
1367
- # Only monitor from top level (Resource) Resource Pool
1368
- monitor_vms_in_rp(@resource_pools[-1])
1369
- end
1370
-
1371
-
1372
- def monitor_vms_in_rp(rp)
1373
- str_info = ""
1374
-
1375
- if rp.resourcePool.size != 0
1376
- rp.resourcePool.each{|child_rp|
1377
- str_info += monitor_vms_in_rp(child_rp)
1378
- }
1379
- end
1380
-
1381
- host_cache = {}
1382
-
1383
- rp.vm.each { |v|
1384
- begin
1385
- # Check cached objects
1386
- if !host_cache[v.runtime.host.to_s]
1387
- host_cache[v.runtime.host.to_s] =
1388
- VCenterCachedHost.new v.runtime.host
1389
- end
1390
-
1391
- host = host_cache[v.runtime.host.to_s]
1392
-
1393
- name = v.name
1394
- number = -1
1395
- vm_extra_config = v.config.extraConfig
1396
-
1397
- # Check the running flag
1398
- running_flag = v.config.extraConfig.select{|val|
1399
- val[:key]=="opennebula.vm.running"}
1400
- if running_flag.size > 0 and running_flag[0]
1401
- running_flag = running_flag[0][:value]
1402
- end
1403
-
1404
- next if running_flag == "no"
1405
-
1406
- # Extract vmid if possible
1407
- matches = name.match(/^one-(\d*)(-(.*))?$/)
1408
- number = matches[1] if matches
1409
- extraconfig_vmid = v.config.extraConfig.select{|val|
1410
- val[:key]=="opennebula.vm.id"}
1411
- if extraconfig_vmid.size > 0 and extraconfig_vmid[0]
1412
- number = extraconfig_vmid[0][:value]
1413
- end
1414
- vm = VCenterVm.new(@client, v)
1415
- vm.monitor(host)
1416
- next if !vm.vm.config
1417
- str_info << "\nVM = ["
1418
- str_info << "ID=#{number},"
1419
- str_info << "DEPLOY_ID=\"#{vm.vm.config.uuid}\","
1420
- str_info << "VM_NAME=\"#{name} - "\
1421
- "#{host.cluster_name}\","
1422
- if number == -1
1423
- vm_template_to_one =
1424
- Base64.encode64(vm.vm_to_one(host)).gsub("\n","")
1425
- str_info << "IMPORT_TEMPLATE=\"#{vm_template_to_one}\","
1426
- end
1427
- str_info << "POLL=\"#{vm.info}\"]"
1428
- rescue Exception => e
1429
- STDERR.puts e.inspect
1430
- STDERR.puts e.backtrace
1431
- end
1432
- }
1433
- return str_info
1434
- end
1435
-
1436
- def monitor_customizations
1437
- customizations = client.vim.serviceContent.customizationSpecManager.info
1438
-
1439
- text = ''
1440
-
1441
- customizations.each do |c|
1442
- t = "CUSTOMIZATION = [ "
1443
- t << %Q<NAME = "#{c.name}", >
1444
- t << %Q<TYPE = "#{c.type}" ]\n>
1445
-
1446
- text << t
1447
- end
1448
-
1449
- text
1450
- end
1451
-
1452
- def get_available_ds
1453
- str_info = ""
1454
-
1455
- datastores = VIClient.get_entities(client.dc.datastoreFolder,
1456
- 'Datastore')
1457
-
1458
- storage_pods = VIClient.get_entities(client.dc.datastoreFolder,
1459
- 'StoragePod')
1460
-
1461
- storage_pods.each { |sp|
1462
- datastores << sp
1463
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
1464
- if not storage_pod_datastores.empty?
1465
- datastores.concat(storage_pod_datastores)
1466
- end
1467
- }
1468
-
1469
- datastores.each { |ds|
1470
- str_info += "VCENTER_DATASTORE=\"#{ds.name}\"\n"
1471
- }
1472
- str_info.chomp
1473
- end
1474
- end
1475
-
1476
- ################################################################################
1477
- # This class is a high level abstraction of a VI VirtualMachine class with
1478
- # OpenNebula semantics.
1479
- ################################################################################
1480
-
1481
- class VCenterVm
1482
- attr_reader :vm
1483
-
1484
- POLL_ATTRIBUTE = OpenNebula::VirtualMachine::Driver::POLL_ATTRIBUTE
1485
- VM_STATE = OpenNebula::VirtualMachine::Driver::VM_STATE
1486
-
1487
- ############################################################################
1488
- # Creates a new VIVm using a RbVmomi::VirtualMachine object
1489
- # @param client [VCenterClient] client to connect to vCenter
1490
- # @param vm_vi [RbVmomi::VirtualMachine] it will be used if not nil
1491
- ########################################################################
1492
- def initialize(client, vm_vi )
1493
- @vm = vm_vi
1494
- @client = client
1495
-
1496
- @used_cpu = 0
1497
- @used_memory = 0
1498
-
1499
- @netrx = 0
1500
- @nettx = 0
1501
- end
1502
-
1503
- ############################################################################
1504
- # Deploys a VM
1505
- # @xml_text XML representation of the VM
1506
- ############################################################################
1507
- def self.deploy(xml_text, lcm_state, deploy_id, hostname, datastore = nil,
1508
- ops = {})
1509
- if lcm_state == "BOOT" || lcm_state == "BOOT_FAILURE"
1510
- return clone_vm(xml_text, hostname, datastore, ops)
1511
- else
1512
- hid = VIClient::translate_hostname(hostname)
1513
- connection = VIClient.new(hid)
1514
- vm = connection.find_vm_fast(deploy_id,
1515
- ops[:ref],
1516
- ops[:name])
1517
- xml = REXML::Document.new xml_text
1518
-
1519
- reconfigure_vm(vm, xml, false, hostname)
1520
-
1521
- vm.PowerOnVM_Task.wait_for_completion
1522
- return vm.config.uuid
1523
- end
1524
- end
1525
-
1526
- ############################################################################
1527
- # Cancels a VM
1528
- # @param deploy_id vcenter identifier of the VM
1529
- # @param hostname name of the host (equals the vCenter cluster)
1530
- # @param lcm_state state of the VM
1531
- # @param keep_disks keep or not VM disks in datastore
1532
- # @param disks VM attached disks
1533
- ############################################################################
1534
- def self.cancel(deploy_id, hostname, lcm_state, keep_disks, disks, to_template)
1535
- case lcm_state
1536
- when "SHUTDOWN_POWEROFF", "SHUTDOWN_UNDEPLOY"
1537
- shutdown(deploy_id, hostname, lcm_state, keep_disks, disks, to_template)
1538
- when "CANCEL", "LCM_INIT", "CLEANUP_RESUBMIT", "SHUTDOWN", "CLEANUP_DELETE"
1539
- hid = VIClient::translate_hostname(hostname)
1540
- connection = VIClient.new(hid)
1541
- vm = connection.find_vm_template(deploy_id)
1542
-
1543
- begin
1544
- if vm.summary.runtime.powerState == "poweredOn"
1545
- vm.PowerOffVM_Task.wait_for_completion
1546
- end
1547
- rescue
1548
- end
1549
- if keep_disks
1550
- detach_all_disks(vm)
1551
- else
1552
- detach_attached_disks(vm, disks, hostname) if disks
1553
- end
1554
-
1555
- # If the VM was instantiated to persistent, convert the VM to
1556
- # vCenter VM Template and update the OpenNebula new
1557
- # VM Template to point to the new vCenter VM Template
1558
- if !to_template.nil?
1559
- vm.MarkAsTemplate
1560
-
1561
- new_template = OpenNebula::Template.new_with_id(to_template,
1562
- OpenNebula::Client.new)
1563
- new_template.info
1564
-
1565
- public_cloud_str = "PUBLIC_CLOUD=["
1566
-
1567
- new_template.to_hash["VMTEMPLATE"]["TEMPLATE"]["PUBLIC_CLOUD"].each{|k,v|
1568
- if k == "VM_TEMPLATE"
1569
- public_cloud_str += "VM_TEMPLATE=\"#{deploy_id}\",\n"
1570
- else
1571
- public_cloud_str += "#{k}=\"#{v}\",\n"
1572
- end
1573
- }
1574
-
1575
- public_cloud_str = public_cloud_str + "]"
1576
-
1577
- new_template.update(public_cloud_str, true)
1578
- else
1579
- vm.Destroy_Task.wait_for_completion
1580
- end
1581
- else
1582
- raise "LCM_STATE #{lcm_state} not supported for cancel"
1583
- end
1584
- end
1585
-
1586
-
1587
- ############################################################################
1588
- # Saves a VM
1589
- # @param deploy_id vcenter identifier of the VM
1590
- # @param hostname name of the host (equals the vCenter cluster)
1591
- ############################################################################
1592
- def self.save(deploy_id, hostname, lcm_state)
1593
- case lcm_state
1594
- when "SAVE_MIGRATE"
1595
- raise "Migration between vCenters cluster not supported"
1596
- when "SAVE_SUSPEND", "SAVE_STOP"
1597
- hid = VIClient::translate_hostname(hostname)
1598
- connection = VIClient.new(hid)
1599
- vm = connection.find_vm_template(deploy_id)
1600
-
1601
- vm.SuspendVM_Task.wait_for_completion
1602
- end
1603
- end
1604
-
1605
- ############################################################################
1606
- # Resumes a VM
1607
- # @param deploy_id vcenter identifier of the VM
1608
- # @param hostname name of the host (equals the vCenter cluster)
1609
- ############################################################################
1610
- def self.resume(deploy_id, hostname)
1611
- hid = VIClient::translate_hostname(hostname)
1612
- connection = VIClient.new(hid)
1613
- vm = connection.find_vm_template(deploy_id)
1614
-
1615
- vm.PowerOnVM_Task.wait_for_completion
1616
- end
1617
-
1618
- ############################################################################
1619
- # Reboots a VM
1620
- # @param deploy_id vcenter identifier of the VM
1621
- # @param hostname name of the host (equals the vCenter cluster)
1622
- ############################################################################
1623
- def self.reboot(deploy_id, hostname)
1624
- hid = VIClient::translate_hostname(hostname)
1625
- connection = VIClient.new(hid)
1626
-
1627
- vm = connection.find_vm_template(deploy_id)
1628
-
1629
- vm.RebootGuest.wait_for_completion
1630
- end
1631
-
1632
- ############################################################################
1633
- # Resets a VM
1634
- # @param deploy_id vcetranslate_hostnamnter identifier of the VM
1635
- # @param hostname name of the host (equals the vCenter cluster)
1636
- ############################################################################
1637
- def self.reset(deploy_id, hostname)
1638
- hid = VIClient::translate_hostname(hostname)
1639
- connection = VIClient.new(hid)
1640
-
1641
- vm = connection.find_vm_template(deploy_id)
1642
-
1643
- vm.ResetVM_Task.wait_for_completion
1644
- end
1645
-
1646
- ############################################################################
1647
- # Shutdown a VM
1648
- # @param deploy_id vcenter identifier of the VM
1649
- # @param hostname name of the host (equals the vCenter cluster)
1650
- # @param lcm_state state of the VM
1651
- # @param keep_disks keep or not VM disks in datastore
1652
- # @param disks VM attached disks
1653
- # @param to_template whether this VM has been instantiated as persistent
1654
- ############################################################################
1655
- def self.shutdown(deploy_id, hostname, lcm_state, keep_disks, disks, to_template)
1656
- hid = VIClient::translate_hostname(hostname)
1657
- connection = VIClient.new(hid)
1658
-
1659
- vm = connection.find_vm_template(deploy_id)
1660
-
1661
- case lcm_state
1662
- when "SHUTDOWN"
1663
- begin
1664
- vm.ShutdownGuest
1665
- counter = 60*10 # 10 minutes
1666
- while counter > 0
1667
- break if vm.runtime.powerState == "poweredOff"
1668
- counter -= 1
1669
- sleep 1
1670
- end
1671
- rescue
1672
- end
1673
-
1674
- if vm.runtime.powerState != "poweredOff"
1675
- vm.PowerOffVM_Task.wait_for_completion
1676
- end
1677
-
1678
- if keep_disks
1679
- detach_all_disks(vm)
1680
- else
1681
- detach_attached_disks(vm, disks, hostname) if disks
1682
- end
1683
-
1684
- # If the VM was instantiated to persistent, convert the VM to
1685
- # vCenter VM Template and update the OpenNebula new
1686
- # VM Template to point to the new vCenter VM Template
1687
- if !to_template.nil?
1688
- vm.MarkAsTemplate
1689
-
1690
- new_template = OpenNebula::Template.new_with_id(to_template,
1691
- OpenNebula::Client.new)
1692
- new_template.info
1693
-
1694
- public_cloud_str = "PUBLIC_CLOUD=["
1695
-
1696
- new_template.to_hash["VMTEMPLATE"]["TEMPLATE"]["PUBLIC_CLOUD"].each{|k,v|
1697
- if k == "VM_TEMPLATE"
1698
- public_cloud_str += "VM_TEMPLATE=\"#{deploy_id}\"\n"
1699
- else
1700
- public_cloud_str += "#{k}=\"#{v}\",\n"
1701
- end
1702
- }
1703
-
1704
- public_cloud_str = public_cloud_str + "]"
1705
-
1706
- new_template.update(public_cloud_str, true)
1707
- else
1708
- vm.Destroy_Task.wait_for_completion
1709
- end
1710
-
1711
- when "SHUTDOWN_POWEROFF", "SHUTDOWN_UNDEPLOY"
1712
- begin
1713
- vm.ShutdownGuest
1714
- counter = 60*10 # 10 minutes
1715
- while counter > 0
1716
- break if vm.runtime.powerState == "poweredOff"
1717
- counter -= 1
1718
- sleep 1
1719
- end
1720
- rescue
1721
- end
1722
-
1723
- if vm.runtime.powerState != "poweredOff"
1724
- vm.PowerOffVM_Task.wait_for_completion
1725
- end
1726
- end
1727
- end
1728
-
1729
- ############################################################################
1730
- # Create VM snapshot
1731
- # @param deploy_id vcenter identifier of the VM
1732
- # @param hostname name of the host (equals the vCenter cluster)
1733
- # @param snaphot_name name of the snapshot
1734
- ############################################################################
1735
- def self.create_snapshot(deploy_id, hostname, snapshot_name)
1736
- hid = VIClient::translate_hostname(hostname)
1737
- connection = VIClient.new(hid)
1738
-
1739
- snapshot_hash = {
1740
- :name => snapshot_name,
1741
- :description => "OpenNebula Snapshot of VM #{deploy_id}",
1742
- :memory => true,
1743
- :quiesce => true
1744
- }
1745
-
1746
- vm = connection.find_vm_template(deploy_id)
1747
-
1748
- vm.CreateSnapshot_Task(snapshot_hash).wait_for_completion
1749
-
1750
- return snapshot_name
1751
- end
1752
-
1753
- ############################################################################
1754
- # Find VM snapshot
1755
- # @param list root list of VM snapshots
1756
- # @param snaphot_name name of the snapshot
1757
- ############################################################################
1758
- def self.find_snapshot(list, snapshot_name)
1759
- list.each do |i|
1760
- if i.name == snapshot_name
1761
- return i.snapshot
1762
- elsif !i.childSnapshotList.empty?
1763
- snap = find_snapshot(i.childSnapshotList, snapshot_name)
1764
- return snap if snap
1765
- end
1766
- end
1767
-
1768
- nil
1769
- end
1770
-
1771
- ############################################################################
1772
- # Delete VM snapshot
1773
- # @param deploy_id vcenter identifier of the VM
1774
- # @param hostname name of the host (equals the vCenter cluster)
1775
- # @param snaphot_name name of the snapshot
1776
- ############################################################################
1777
- def self.delete_snapshot(deploy_id, hostname, snapshot_name)
1778
- hid = VIClient::translate_hostname(hostname)
1779
- connection = VIClient.new(hid)
1780
-
1781
- vm = connection.find_vm_template(deploy_id)
1782
-
1783
- list = vm.snapshot.rootSnapshotList
1784
-
1785
- snapshot = find_snapshot(list, snapshot_name)
1786
- return nil if !snapshot
1787
-
1788
- delete_snapshot_hash = {
1789
- :_this => snapshot,
1790
- :removeChildren => false
1791
- }
1792
-
1793
- snapshot.RemoveSnapshot_Task(delete_snapshot_hash).wait_for_completion
1794
- end
1795
-
1796
- ############################################################################
1797
- # Revert VM snapshot
1798
- # @param deploy_id vcenter identifier of the VM
1799
- # @param hostname name of the host (equals the vCenter cluster)
1800
- # @param snaphot_name name of the snapshot
1801
- ############################################################################
1802
- def self.revert_snapshot(deploy_id, hostname, snapshot_name)
1803
- hid = VIClient::translate_hostname(hostname)
1804
- connection = VIClient.new(hid)
1805
-
1806
- vm = connection.find_vm_template(deploy_id)
1807
-
1808
- list = vm.snapshot.rootSnapshotList
1809
-
1810
- snapshot = find_snapshot(list, snapshot_name)
1811
- return nil if !snapshot
1812
-
1813
- revert_snapshot_hash = {
1814
- :_this => snapshot
1815
- }
1816
-
1817
- snapshot.RevertToSnapshot_Task(revert_snapshot_hash).wait_for_completion
1818
- end
1819
-
1820
- ############################################################################
1821
- # Attach NIC to a VM
1822
- # @param deploy_id vcenter identifier of the VM
1823
- # @param mac MAC address of the NIC to be attached
1824
- # @param bridge name of the Network in vCenter
1825
- # @param model model of the NIC to be attached
1826
- # @param host hostname of the ESX where the VM is running
1827
- ############################################################################
1828
- def self.attach_nic(deploy_id, mac, bridge, model, host)
1829
- hid = VIClient::translate_hostname(host)
1830
- connection = VIClient.new(hid)
1831
-
1832
- vm = connection.find_vm_template(deploy_id)
1833
-
1834
- spec_nics = calculate_addnic_spec(vm, mac, bridge, model)
1835
-
1836
- spec_hash = {:deviceChange => [spec_nics]}
1837
-
1838
- #B4897 track hot plugged nics
1839
- hotplugged_nics = vm.config.extraConfig.select do |val|
1840
- val[:key] == "opennebula.hotplugged_nics"
1841
- end
1842
-
1843
- if hotplugged_nics && !hotplugged_nics.empty?
1844
- hotplugged_nics = hotplugged_nics[0][:value].to_s.split(";")
1845
- hotplugged_nics << mac.to_s if !hotplugged_nics.include?(mac)
1846
- else
1847
- hotplugged_nics = []
1848
- hotplugged_nics << mac.to_s
1849
- end
1850
-
1851
- config_array = [{:key=>"opennebula.hotplugged_nics",
1852
- :value=>hotplugged_nics.join(";")}]
1853
- extra_config_spec = {:extraConfig =>config_array}
1854
-
1855
- spec_hash.merge!(extra_config_spec)
1856
-
1857
- spec = RbVmomi::VIM.VirtualMachineConfigSpec(spec_hash)
1858
-
1859
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
1860
- end
1861
-
1862
- ############################################################################
1863
- # Detach NIC from a VM
1864
- ############################################################################
1865
- def self.detach_nic(deploy_id, mac, host)
1866
- hid = VIClient::translate_hostname(host)
1867
- connection = VIClient.new(hid)
1868
-
1869
- vm = connection.find_vm_template(deploy_id)
1870
-
1871
- nic = vm.config.hardware.device.find { |d|
1872
- is_nic?(d) && (d.macAddress == mac)
1873
- }
1874
-
1875
- raise "Could not find NIC with mac address #{mac}" if nic.nil?
1876
-
1877
- #B4897 track hot plugged nics
1878
- hotplugged_nics = vm.config.extraConfig.select do |val|
1879
- val[:key] == "opennebula.hotplugged_nics"
1880
- end
1881
-
1882
- config_array = []
1883
- if hotplugged_nics && !hotplugged_nics.empty?
1884
- hotplugged_nics = hotplugged_nics[0][:value].to_s.split(";")
1885
- hotplugged_nics.delete(mac) # remove hotplugged nic
1886
- config_array = [{:key=>"opennebula.hotplugged_nics",
1887
- :value=>hotplugged_nics.join(";")}]
1888
- end
1889
-
1890
- spec = {
1891
- :deviceChange => [
1892
- :operation => :remove,
1893
- :device => nic
1894
- ],
1895
- :extraConfig => config_array
1896
- }
1897
-
1898
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
1899
- end
1900
-
1901
- ############################################################################
1902
- # Reconfigures a VM (context data)
1903
- # @param deploy_id vcenter identifier of the VM
1904
- # @param hostname name of the host (equals the vCenter cluster)
1905
- # @param xml_text XML repsentation of the VM
1906
- ############################################################################
1907
- def self.reconfigure(deploy_id, hostname, xml_text)
1908
- hid = VIClient::translate_hostname(hostname)
1909
- connection = VIClient.new(hid)
1910
- vm = connection.find_vm_template(deploy_id)
1911
-
1912
- xml = REXML::Document.new xml_text
1913
- context = xml.root.elements["//TEMPLATE/CONTEXT"]
1914
-
1915
- if context
1916
- context_text = create_context(context)
1917
- context_spec = {
1918
- :extraConfig => [
1919
- { :key=>"guestinfo.opennebula.context",
1920
- :value=> Base64.encode64(context_text) }
1921
- ]
1922
- }
1923
-
1924
- spec = RbVmomi::VIM.VirtualMachineConfigSpec(context_spec)
1925
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
1926
- end
1927
- end
1928
-
1929
- ########################################################################
1930
- # Initialize the vm monitor information
1931
- ########################################################################
1932
- def monitor(host)
1933
- @summary = @vm.summary
1934
- @state = state_to_c(@summary.runtime.powerState)
1935
-
1936
- if @state != VM_STATE[:active]
1937
- @used_cpu = 0
1938
- @used_memory = 0
1939
-
1940
- @netrx = 0
1941
- @nettx = 0
1942
-
1943
- return
1944
- end
1945
-
1946
- @used_memory = @summary.quickStats.hostMemoryUsage * 1024
1947
- cpuMhz = @vm.runtime.host.summary.hardware.cpuMhz.to_f
1948
-
1949
- @used_cpu =
1950
- ((@summary.quickStats.overallCpuUsage.to_f / cpuMhz) * 100).to_s
1951
- @used_cpu = sprintf('%.2f',@used_cpu).to_s
1952
-
1953
- # Check for negative values
1954
- @used_memory = 0 if @used_memory.to_i < 0
1955
- @used_cpu = 0 if @used_cpu.to_i < 0
1956
-
1957
- @esx_host = @vm.summary.runtime.host.name
1958
- @guest_ip = @vm.guest.ipAddress
1959
- @guest_state = @vm.guest.guestState
1960
- @vmware_tools = @vm.guest.toolsRunningStatus
1961
- @vmtools_ver = @vm.guest.toolsVersion
1962
- @vmtools_verst = @vm.guest.toolsVersionStatus
1963
-
1964
- guest_ip_addresses = []
1965
-
1966
- @vm.guest.net.each do |net|
1967
- net.ipConfig.ipAddress.each do |ip|
1968
- guest_ip_addresses << ip.ipAddress
1969
- end if net.ipConfig && net.ipConfig.ipAddress
1970
- end if @vm.guest.net
1971
-
1972
- @guest_ip_addresses = guest_ip_addresses.join(',')
1973
-
1974
- # Network metrics - Realtime retrieved by perfManager
1975
- pm = @client.vim.serviceInstance.content.perfManager
1976
-
1977
- provider = pm.provider_summary [@vm].first
1978
- refresh_rate = provider.refreshRate
1979
- max_samples = 0
1980
-
1981
- vmid = -1
1982
- extraconfig_vmid = @vm.config.extraConfig.select{|val|
1983
- val[:key]=="opennebula.vm.id"}
1984
- if extraconfig_vmid.size > 0 and extraconfig_vmid[0]
1985
- vmid = extraconfig_vmid[0][:value].to_i
1986
- end
1987
-
1988
- if vmid < 0
1989
- @nettx = 0
1990
- @netrx = 0
1991
- else
1992
- one_vm = OpenNebula::VirtualMachine.new_with_id(vmid, OpenNebula::Client.new)
1993
- one_vm.info
1994
- stats = []
1995
- previous_nettx = 0
1996
- previous_netrx = 0
1997
- if one_vm["MONITORING/NETTX"]
1998
- previous_nettx = one_vm["MONITORING/NETTX"].to_i
1999
- end
2000
- if one_vm["MONITORING/NETRX"]
2001
- previous_netrx = one_vm["MONITORING/NETRX"].to_i
2002
- end
2003
-
2004
- if(one_vm["MONITORING/LAST_MON"] && one_vm["MONITORING/LAST_MON"].to_i != 0 )
2005
- #Real time data stores max 1 hour. 1 minute has 3 samples
2006
- interval = (Time.now.to_i - one_vm["MONITORING/LAST_MON"].to_i)
2007
- interval = 3601 if interval < 0 #Safety check
2008
- #If last poll was more than hour ago get 3 minutes,
2009
- #else calculate how many samples since last poll
2010
- max_samples = interval > 3600 ? 9 : (interval / refresh_rate) + 1
2011
- else
2012
- # First poll, get at least latest 3 minutes = 9 samples
2013
- max_samples = 9
2014
- end
2015
-
2016
- stats = pm.retrieve_stats(
2017
- [@vm],
2018
- ['net.transmitted','net.bytesRx','net.bytesTx','net.received'],
2019
- {interval:refresh_rate, max_samples: max_samples}
2020
- )
2021
-
2022
- if stats.empty? || stats.first[1][:metrics].empty?
2023
- @nettx = 0 + previous_nettx
2024
- @netrx = 0 + previous_netrx
2025
- else
2026
- metrics = stats.first[1][:metrics]
2027
- nettx_kbpersec = 0
2028
- netrx_kbpersec = 0
2029
- (0..max_samples-1).each { |index|
2030
- tx = [0]
2031
- rx = [0]
2032
- tx << metrics['net.transmitted'][index] if metrics['net.transmitted']
2033
- tx << metrics['net.bytesTx'][index] if metrics['net.bytesTx']
2034
- rx << metrics['net.received'][index] if metrics['net.received']
2035
- rx << metrics['net.bytesRx'][index] if metrics['net.bytesRx']
2036
- nettx_kbpersec += tx.max
2037
- netrx_kbpersec += rx.max
2038
- }
2039
- @nettx = (nettx_kbpersec * 1000 * refresh_rate).to_i + previous_nettx
2040
- @netrx = (netrx_kbpersec * 1000 * refresh_rate).to_i + previous_netrx
2041
- end
2042
- end
2043
- end
2044
-
2045
- ########################################################################
2046
- # Generates a OpenNebula IM Driver valid string with the monitor info
2047
- ########################################################################
2048
- def info
2049
- return 'STATE=d' if @state == 'd'
2050
-
2051
- str_info = ""
2052
-
2053
- str_info << "GUEST_IP=" << @guest_ip.to_s << " " if @guest_ip
2054
- if @guest_ip_addresses && !@guest_ip_addresses.empty?
2055
- str_info << "GUEST_IP_ADDRESSES=\\\"" <<
2056
- @guest_ip_addresses.to_s << "\\\" "
2057
- end
2058
- str_info << "LAST_MON=" << Time.now.to_i.to_s << " "
2059
- str_info << "#{POLL_ATTRIBUTE[:state]}=" << @state << " "
2060
- str_info << "#{POLL_ATTRIBUTE[:cpu]}=" << @used_cpu.to_s << " "
2061
- str_info << "#{POLL_ATTRIBUTE[:memory]}=" << @used_memory.to_s << " "
2062
- str_info << "#{POLL_ATTRIBUTE[:netrx]}=" << @netrx.to_s << " "
2063
- str_info << "#{POLL_ATTRIBUTE[:nettx]}=" << @nettx.to_s << " "
2064
- str_info << "ESX_HOST=\\\"" << @esx_host.to_s << "\\\" "
2065
- str_info << "GUEST_STATE=" << @guest_state.to_s << " "
2066
- str_info << "VMWARETOOLS_RUNNING_STATUS=" << @vmware_tools.to_s << " "
2067
- str_info << "VMWARETOOLS_VERSION=" << @vmtools_ver.to_s << " "
2068
- str_info << "VMWARETOOLS_VERSION_STATUS=" << @vmtools_verst.to_s << " "
2069
- str_info << "RESOURCE_POOL=\\\"" << @vm.resourcePool.name << "\\\" "
2070
- end
2071
-
2072
- ########################################################################
2073
- # Generates an OpenNebula Template for this VCenterVm
2074
- ########################################################################
2075
- def to_one(host)
2076
- cluster_name = host.cluster_name
2077
-
2078
- str = "NAME = \"#{@vm.name} - #{cluster_name}\"\n"\
2079
- "CPU = \"#{@vm.config.hardware.numCPU}\"\n"\
2080
- "vCPU = \"#{@vm.config.hardware.numCPU}\"\n"\
2081
- "MEMORY = \"#{@vm.config.hardware.memoryMB}\"\n"\
2082
- "HYPERVISOR = \"vcenter\"\n"\
2083
- "PUBLIC_CLOUD = [\n"\
2084
- " TYPE =\"vcenter\",\n"\
2085
- " VM_TEMPLATE =\"#{@vm.config.uuid}\",\n"\
2086
- " VCENTER_REF =\"#{@vm.ref}\",\n"\
2087
- " VCENTER_NAME=\"#{@vm.name}\",\n"\
2088
- " HOST =\"#{cluster_name}\"\n"\
2089
- "]\n"\
2090
- "GRAPHICS = [\n"\
2091
- " TYPE =\"vnc\",\n"\
2092
- " LISTEN =\"0.0.0.0\"\n"\
2093
- "]\n"\
2094
- "SCHED_REQUIREMENTS=\"NAME=\\\"#{cluster_name}\\\"\"\n"\
2095
- "CONTEXT = ["\
2096
- " NETWORK = \"YES\","\
2097
- " SSH_PUBLIC_KEY = \"$USER[SSH_PUBLIC_KEY]\" ]"
2098
-
2099
- if @vm.config.annotation.nil? || @vm.config.annotation.empty?
2100
- str << "DESCRIPTION = \"vCenter Template imported by OpenNebula"\
2101
- " from Cluster #{@vm.runtime.host.parent.name}\"\n"
2102
- else
2103
- notes = @vm.config.annotation.gsub("\\", "\\\\").gsub("\"", "\\\"")
2104
- str << "DESCRIPTION = \"#{notes}\"\n"
2105
- end
2106
-
2107
- case @vm.guest.guestFullName
2108
- when /CentOS/i
2109
- str << "LOGO=images/logos/centos.png"
2110
- when /Debian/i
2111
- str << "LOGO=images/logos/debian.png"
2112
- when /Red Hat/i
2113
- str << "LOGO=images/logos/redhat.png"
2114
- when /Ubuntu/i
2115
- str << "LOGO=images/logos/ubuntu.png"
2116
- when /Windows XP/i
2117
- str << "LOGO=images/logos/windowsxp.png"
2118
- when /Windows/i
2119
- str << "LOGO=images/logos/windows8.png"
2120
- when /Linux/i
2121
- str << "LOGO=images/logos/linux.png"
2122
- end
2123
- return str
2124
- end
2125
-
2126
- ########################################################################
2127
- # Generates a Datastore user input
2128
- ########################################################################
2129
- def to_one_ds(host, default_ds)
2130
- # Datastores User Input
2131
- str = ""
2132
-
2133
- if host.ds_list != ""
2134
- str = "M|list|Which datastore you want this VM to run on?|"\
2135
- << "#{host.ds_list}|#{default_ds}"
2136
- end
2137
-
2138
- return str
2139
- end
2140
-
2141
- ########################################################################
2142
- # Generates a Resource Pool user input
2143
- ########################################################################
2144
- def to_one_rp(host)
2145
- # Resource Pool User Input
2146
- str = ""
2147
-
2148
- if host.rp_list != ""
2149
- str = "M|list|Which resource pool you want this VM to run"\
2150
- " in?|#{host.rp_list}|#{host.rp_list.split(",")[0]}"
2151
- end
2152
-
2153
- return str
2154
- end
2155
-
2156
- ########################################################################
2157
- # Generates an OpenNebula VirtualMachine for this VCenterVm
2158
- #
2159
- #
2160
- ########################################################################
2161
- def vm_to_one(host)
2162
- cluster_name = host.cluster_name
2163
-
2164
- state = case state_to_c(@summary.runtime.powerState)
2165
- when 'a'
2166
- "RUNNING"
2167
- when 'd'
2168
- "POWEROFF"
2169
- end
2170
-
2171
- str = "NAME = \"#{@vm.name} - #{cluster_name}\"\n"\
2172
- "CPU = \"#{@vm.config.hardware.numCPU}\"\n"\
2173
- "vCPU = \"#{@vm.config.hardware.numCPU}\"\n"\
2174
- "MEMORY = \"#{@vm.config.hardware.memoryMB}\"\n"\
2175
- "HYPERVISOR = \"vcenter\"\n"\
2176
- "PUBLIC_CLOUD = [\n"\
2177
- " TYPE =\"vcenter\",\n"\
2178
- " VM_TEMPLATE =\"#{@vm.config.uuid}\",\n"\
2179
- " HOST =\"#{cluster_name}\"\n"\
2180
- "]\n"\
2181
- "IMPORT_VM_ID = \"#{@vm.config.uuid}\"\n"\
2182
- "IMPORT_STATE = \"#{state}\"\n"\
2183
- "SCHED_REQUIREMENTS=\"NAME=\\\"#{cluster_name}\\\"\"\n"
2184
-
2185
- vp = @vm.config.extraConfig.select{|v|
2186
- v[:key].downcase=="remotedisplay.vnc.port"}
2187
- keymap = @vm.config.extraConfig.select{|v|
2188
- v[:key].downcase=="remotedisplay.vnc.keymap"}
2189
-
2190
- if vp.size > 0
2191
- str << "GRAPHICS = [\n"\
2192
- " TYPE =\"vnc\",\n"\
2193
- " LISTEN =\"0.0.0.0\",\n"\
2194
- " PORT =\"#{vp[0][:value]}\"\n"
2195
- str << " ,KEYMAP =\"#{keymap[0][:value]}\"\n" if keymap[0]
2196
- str << "]\n"
2197
- end
2198
-
2199
- if @vm.config.annotation.nil? || @vm.config.annotation.empty?
2200
- str << "DESCRIPTION = \"vCenter Virtual Machine imported by"\
2201
- " OpenNebula from Cluster #{cluster_name}\"\n"
2202
- else
2203
- notes = @vm.config.annotation.gsub("\\", "\\\\").gsub("\"", "\\\"")
2204
- str << "DESCRIPTION = \"#{notes}\"\n"
2205
- end
2206
-
2207
- case @vm.guest.guestFullName
2208
- when /CentOS/i
2209
- str << "LOGO=images/logos/centos.png"
2210
- when /Debian/i
2211
- str << "LOGO=images/logos/debian.png"
2212
- when /Red Hat/i
2213
- str << "LOGO=images/logos/redhat.png"
2214
- when /Ubuntu/i
2215
- str << "LOGO=images/logos/ubuntu.png"
2216
- when /Windows XP/i
2217
- str << "LOGO=images/logos/windowsxp.png"
2218
- when /Windows/i
2219
- str << "LOGO=images/logos/windows8.png"
2220
- when /Linux/i
2221
- str << "LOGO=images/logos/linux.png"
2222
- end
2223
-
2224
- return str
2225
- end
2226
-
2227
- private
2228
-
2229
- ########################################################################
2230
- # Converts the VI string state to OpenNebula state convention
2231
- # Guest states are:
2232
- # - poweredOff The virtual machine is currently powered off.
2233
- # - poweredOn The virtual machine is currently powered on.
2234
- # - suspended The virtual machine is currently suspended.
2235
- ########################################################################
2236
- def state_to_c(state)
2237
- case state
2238
- when 'poweredOn'
2239
- VM_STATE[:active]
2240
- when 'suspended'
2241
- VM_STATE[:paused]
2242
- when 'poweredOff'
2243
- VM_STATE[:deleted]
2244
- else
2245
- VM_STATE[:unknown]
2246
- end
2247
- end
2248
-
2249
- ########################################################################
2250
- # Checks if a RbVmomi::VIM::VirtualDevice is a network interface
2251
- ########################################################################
2252
- def self.is_nic?(device)
2253
- !device.class.ancestors.index(RbVmomi::VIM::VirtualEthernetCard).nil?
2254
- end
2255
-
2256
- ########################################################################
2257
- # Checks if a RbVmomi::VIM::VirtualDevice is a disk
2258
- ########################################################################
2259
- def self.is_disk?(device)
2260
- is_disk = !(device.class.ancestors.index(RbVmomi::VIM::VirtualDisk)).nil?
2261
- is_cdrom = !(device.class.ancestors.index(RbVmomi::VIM::VirtualCdrom)).nil?
2262
- is_disk or is_cdrom
2263
- end
2264
-
2265
- ########################################################################
2266
- # Returns the spec to reconfig a VM and add a NIC
2267
- ########################################################################
2268
- def self.calculate_addnic_spec(vm, mac, bridge, model, limit=nil, rsrv=nil)
2269
- model = model.nil? ? nil : model.downcase
2270
- network = vm.runtime.host.network.select{|n| n.name==bridge}
2271
- backing = nil
2272
-
2273
- if network.empty?
2274
- raise "Network #{bridge} not found in host #{vm.runtime.host.name}"
2275
- else
2276
- network = network[0]
2277
- end
2278
-
2279
- card_num = 1 # start in one, we want the next avaliable id
2280
-
2281
- vm.config.hardware.device.each{ |dv|
2282
- card_num = card_num + 1 if is_nic?(dv)
2283
- }
2284
-
2285
- nic_card = case model
2286
- when "virtuale1000", "e1000"
2287
- RbVmomi::VIM::VirtualE1000
2288
- when "virtuale1000e", "e1000e"
2289
- RbVmomi::VIM::VirtualE1000e
2290
- when "virtualpcnet32", "pcnet32"
2291
- RbVmomi::VIM::VirtualPCNet32
2292
- when "virtualsriovethernetcard", "sriovethernetcard"
2293
- RbVmomi::VIM::VirtualSriovEthernetCard
2294
- when "virtualvmxnetm", "vmxnetm"
2295
- RbVmomi::VIM::VirtualVmxnetm
2296
- when "virtualvmxnet2", "vmnet2"
2297
- RbVmomi::VIM::VirtualVmxnet2
2298
- when "virtualvmxnet3", "vmxnet3"
2299
- RbVmomi::VIM::VirtualVmxnet3
2300
- else # If none matches, use VirtualE1000
2301
- RbVmomi::VIM::VirtualE1000
2302
- end
2303
-
2304
- if network.class == RbVmomi::VIM::Network
2305
- backing = RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
2306
- :deviceName => bridge,
2307
- :network => network)
2308
- else
2309
- port = RbVmomi::VIM::DistributedVirtualSwitchPortConnection(
2310
- :switchUuid =>
2311
- network.config.distributedVirtualSwitch.uuid,
2312
- :portgroupKey => network.key)
2313
- backing =
2314
- RbVmomi::VIM.VirtualEthernetCardDistributedVirtualPortBackingInfo(
2315
- :port => port)
2316
- end
2317
-
2318
- card_spec = {
2319
- :key => 0,
2320
- :deviceInfo => {
2321
- :label => "net" + card_num.to_s,
2322
- :summary => bridge
2323
- },
2324
- :backing => backing,
2325
- :addressType => mac ? 'manual' : 'generated',
2326
- :macAddress => mac
2327
- }
2328
-
2329
- if (limit or rsrv) and (limit > 0)
2330
- ra_spec = Hash.new
2331
- rsrv = limit if rsrv > limit
2332
- ra_spec[:limit] = limit if limit
2333
- ra_spec[:reservation] = rsrv if rsrv
2334
- ra_spec[:share] = RbVmomi::VIM.SharesInfo({
2335
- :level => RbVmomi::VIM.SharesLevel("normal"),
2336
- :shares => 0
2337
- })
2338
- card_spec[:resourceAllocation] =
2339
- RbVmomi::VIM.VirtualEthernetCardResourceAllocation(ra_spec)
2340
- end
2341
-
2342
- return {
2343
- :operation => :add,
2344
- :device => nic_card.new(card_spec)
2345
- }
2346
- end
2347
-
2348
- ########################################################################
2349
- # Clone a vCenter VM Template and leaves it powered on
2350
- ########################################################################
2351
- def self.clone_vm(xml_text, hostname, datastore, ops = {})
2352
-
2353
- host_id = VCenterDriver::VIClient.translate_hostname(hostname)
2354
-
2355
- # Retrieve hostname
2356
-
2357
- host = OpenNebula::Host.new_with_id(host_id, OpenNebula::Client.new())
2358
- host.info # Not failing if host retrieval fails
2359
-
2360
- # Get VM prefix name
2361
-
2362
- if host["/HOST/TEMPLATE/VM_PREFIX"] and !host["/HOST/TEMPLATE/VM_PREFIX"].empty?
2363
- vmname_prefix = host["/HOST/TEMPLATE/VM_PREFIX"]
2364
- else # fall back to default value
2365
- vmname_prefix = "one-$i-"
2366
- end
2367
-
2368
- xml = REXML::Document.new xml_text
2369
- pcs = xml.root.get_elements("/VM/USER_TEMPLATE/PUBLIC_CLOUD")
2370
-
2371
- raise "Cannot find VCenter element in VM template." if pcs.nil?
2372
-
2373
- template = pcs.select { |t|
2374
- type = t.elements["TYPE"]
2375
- !type.nil? && type.text.downcase == "vcenter"
2376
- }
2377
-
2378
- # If there are multiple vCenter templates, find the right one
2379
-
2380
- if template.is_a? Array
2381
- all_vcenter_templates = template.clone
2382
- # If there is more than one coincidence, pick the first one
2383
- template = template.select {|t|
2384
- cluster_name = t.elements["HOST"]
2385
- !cluster_name.nil? && cluster_name.text == hostname
2386
- }[0]
2387
- # The template may not reference any specific CLUSTER
2388
- # (referenced to as HOST in the OpenNebula template)
2389
- # Therefore, here take the first one that does not
2390
- # specify a CLUSTER to see if we are lucky
2391
- if template.nil?
2392
- template = all_vcenter_templates.select {|t|
2393
- t.elements["HOST"].nil?
2394
- }[0]
2395
- end
2396
- end
2397
-
2398
- raise "Cannot find vCenter element in VM template." if template.nil?
2399
-
2400
- uuid = template.elements["VM_TEMPLATE"]
2401
-
2402
- raise "Cannot find VM_TEMPLATE in vCenter element." if uuid.nil?
2403
-
2404
- uuid = uuid.text
2405
- vmid = xml.root.elements["/VM/ID"].text
2406
- vmname_prefix.gsub!("$i", vmid)
2407
- vcenter_name = "#{vmname_prefix}#{xml.root.elements["/VM/NAME"].text}"
2408
- hid = xml.root.elements["/VM/HISTORY_RECORDS/HISTORY/HID"]
2409
-
2410
- raise "Cannot find host id in deployment file history." if hid.nil?
2411
-
2412
- connection = VIClient.new(hid)
2413
- vc_template = connection.find_vm_fast(uuid, ops[:ref], ops[:name])
2414
-
2415
- # Find out requested and available resource pool
2416
-
2417
- req_rp = nil
2418
- if !xml.root.elements["/VM/USER_TEMPLATE/RESOURCE_POOL"].nil?
2419
- req_rp = xml.root.elements["/VM/USER_TEMPLATE/RESOURCE_POOL"].text
2420
- end
2421
-
2422
- if connection.rp_confined?
2423
- rp = connection.resource_pool.first
2424
- if req_rp && rp.name != req_rp
2425
- raise "Available resource pool in host [#{rp.name}]"\
2426
- " does not match requested resource pool"\
2427
- " [#{req_rp}]"
2428
- end
2429
- else
2430
- if req_rp # if there is requested resource pool, retrieve it
2431
- rp = connection.find_resource_pool(req_rp)
2432
- raise "Cannot find resource pool "\
2433
- "#{template.elements["RESOURCE_POOL"].text}" if !rp
2434
- else # otherwise, get the default resource pool
2435
- rp = connection.default_resource_pool
2436
- end
2437
- end
2438
-
2439
- # Find out requested and available datastore
2440
-
2441
- if !xml.root.elements["/VM/USER_TEMPLATE/VCENTER_DATASTORE"].nil?
2442
- datastore = xml.root.elements["/VM/USER_TEMPLATE/VCENTER_DATASTORE"].text
2443
- end
2444
-
2445
- if datastore
2446
- datastores = VIClient.get_entities(connection.dc.datastoreFolder,
2447
- 'Datastore')
2448
-
2449
- storage_pods = VIClient.get_entities(connection.dc.datastoreFolder,
2450
- 'StoragePod')
2451
-
2452
- storpod = storage_pods.select{|sp| sp.name == datastore}
2453
-
2454
- storage_pods.each { |sp|
2455
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
2456
- if not storage_pod_datastores.empty?
2457
- datastores.concat(storage_pod_datastores)
2458
- end
2459
- }
2460
-
2461
- ds = datastores.select{|ds| ds.name == datastore}[0]
2462
-
2463
- raise "Cannot find datastore #{datastore}" if !ds && !storpod
2464
-
2465
- end
2466
-
2467
- relocate_spec_params = {
2468
- :pool => rp
2469
- }
2470
-
2471
- relocate_spec_params[:datastore] = ds if datastore
2472
-
2473
- relocate_spec_params[:diskMoveType] = :moveChildMostDiskBacking if ds
2474
-
2475
- relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
2476
- relocate_spec_params)
2477
-
2478
- # This running flag will prevent spurious poweroff states in the VM
2479
-
2480
- running_flag = [{:key=>"opennebula.vm.running",:value=>"no"}]
2481
-
2482
- running_flag_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
2483
- {:extraConfig =>running_flag})
2484
-
2485
- clone_parameters = {
2486
- :location => relocate_spec,
2487
- :powerOn => false,
2488
- :template => false,
2489
- :config => running_flag_spec
2490
- }
2491
-
2492
- customization = template.elements["CUSTOMIZATION_SPEC"]
2493
-
2494
- vim = connection.vim
2495
-
2496
- if !customization.nil?
2497
- begin
2498
- custom_spec = vim.serviceContent.customizationSpecManager.
2499
- GetCustomizationSpec(:name => customization.text)
2500
-
2501
- if custom_spec && spec=custom_spec.spec
2502
- clone_parameters[:customization] = spec
2503
- else
2504
- raise "Error getting customization spec"
2505
- end
2506
-
2507
- rescue
2508
- raise "Customization spec '#{customization.text}' not found"
2509
- end
2510
- end
2511
-
2512
- clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(clone_parameters)
2513
-
2514
- if storpod && !storpod.empty? && storpod[0].is_a?(RbVmomi::VIM::StoragePod)
2515
-
2516
- storage_manager = vim.serviceContent.storageResourceManager
2517
-
2518
- pod_spec = RbVmomi::VIM.StorageDrsPodSelectionSpec(storagePod: storpod[0])
2519
-
2520
- storage_spec = RbVmomi::VIM.StoragePlacementSpec(
2521
- type: 'clone',
2522
- cloneName: vcenter_name,
2523
- folder: vc_template.parent,
2524
- podSelectionSpec: pod_spec,
2525
- vm: vc_template,
2526
- cloneSpec: clone_spec
2527
- )
2528
-
2529
- result = storage_manager.RecommendDatastores(storageSpec: storage_spec)
2530
-
2531
- recommendation = result.recommendations[0]
2532
-
2533
- key = recommendation.key ||= ''
2534
-
2535
- if key == ''
2536
- raise "Missing Datastore recommendation for StoragePod (Storage DRS)"
2537
- end
2538
-
2539
- begin
2540
- apply_sr = storage_manager.ApplyStorageDrsRecommendation_Task(key: [key]).wait_for_completion
2541
- vm = apply_sr.vm
2542
- rescue Exception => e
2543
- raise "Cannot clone VM Template to StoragePod: #{e.message}"
2544
- end
2545
- else
2546
-
2547
- begin
2548
- vm = vc_template.CloneVM_Task(
2549
- :folder => vc_template.parent,
2550
- :name => vcenter_name,
2551
- :spec => clone_spec).wait_for_completion
2552
- rescue Exception => e
2553
-
2554
- if !e.message.start_with?('DuplicateName')
2555
- raise "Cannot clone VM Template: #{e.message}"
2556
- end
2557
-
2558
- vm = connection.find_vm(vcenter_name)
2559
-
2560
- raise "Cannot clone VM Template" if vm.nil?
2561
-
2562
- vm.Destroy_Task.wait_for_completion
2563
- vm = vc_template.CloneVM_Task(
2564
- :folder => vc_template.parent,
2565
- :name => vcenter_name,
2566
- :spec => clone_spec).wait_for_completion
2567
- end
2568
-
2569
- end
2570
-
2571
-
2572
-
2573
- reconfigure_vm(vm, xml, true, hostname)
2574
-
2575
- # Power on the VM
2576
- vm.PowerOnVM_Task.wait_for_completion
2577
-
2578
- # Set to yes the running flag
2579
-
2580
- config_array = [{:key=>"opennebula.vm.running",:value=>"yes"}]
2581
- spec = RbVmomi::VIM.VirtualMachineConfigSpec(
2582
- {:extraConfig =>config_array})
2583
-
2584
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
2585
-
2586
- return vm.config.uuid
2587
- end
2588
-
2589
- ########################################################################
2590
- # Reconfigures a VM with new deployment description
2591
- ########################################################################
2592
- def self.reconfigure_vm(vm, xml, newvm, hostname)
2593
- vm_uuid = vm.config.uuid
2594
- vmid = xml.root.elements["/VM/ID"].text
2595
- context = xml.root.elements["/VM/TEMPLATE/CONTEXT"]
2596
-
2597
- token = vm.config.extraConfig.select do |val|
2598
- val[:key] == "opennebula.token"
2599
- end
2600
-
2601
- if token && !token.empty?
2602
- token = token.first[:value]
2603
- else
2604
- token = nil
2605
- end
2606
-
2607
- # Add VMID to VM's extraConfig
2608
-
2609
- config_array = [{:key=>"opennebula.vm.id",:value=>vmid}]
2610
-
2611
- # VNC Section
2612
-
2613
- vnc_port = xml.root.elements["/VM/TEMPLATE/GRAPHICS/PORT"]
2614
- vnc_listen = xml.root.elements["/VM/TEMPLATE/GRAPHICS/LISTEN"]
2615
- vnc_keymap = xml.root.elements["/VM/TEMPLATE/GRAPHICS/KEYMAP"]
2616
-
2617
- if !vnc_listen
2618
- vnc_listen = "0.0.0.0"
2619
- else
2620
- vnc_listen = vnc_listen.text
2621
- end
2622
-
2623
- context_vnc_spec = {}
2624
-
2625
- if vnc_port
2626
- config_array +=
2627
- [{:key=>"remotedisplay.vnc.enabled",:value=>"TRUE"},
2628
- {:key=>"remotedisplay.vnc.port", :value=>vnc_port.text},
2629
- {:key=>"remotedisplay.vnc.ip", :value=>vnc_listen}]
2630
- end
2631
-
2632
- config_array += [{:key=>"remotedisplay.vnc.keymap",
2633
- :value=>vnc_keymap.text}] if vnc_keymap
2634
-
2635
- # Context section
2636
-
2637
- if context
2638
- context_text = create_context(context)
2639
-
2640
- # OneGate
2641
- onegate_token_flag = xml.root.elements["/VM/TEMPLATE/CONTEXT/TOKEN"]
2642
-
2643
- if onegate_token_flag and onegate_token_flag.text == "YES"
2644
- if token
2645
- onegate_token_64 = token
2646
- else
2647
- # Create the OneGate token string
2648
- vmid_str = xml.root.elements["/VM/ID"].text
2649
- stime_str = xml.root.elements["/VM/STIME"].text
2650
- str_to_encrypt = "#{vmid_str}:#{stime_str}"
2651
-
2652
- user_id = xml.root.elements['//CREATED_BY'].text
2653
-
2654
- if user_id.nil?
2655
- STDERR.puts {"VMID:#{vmid} CREATED_BY not present" \
2656
- " in the VM TEMPLATE"}
2657
- return nil
2658
- end
2659
-
2660
- user = OpenNebula::User.new_with_id(user_id,
2661
- OpenNebula::Client.new)
2662
- rc = user.info
2663
-
2664
- if OpenNebula.is_error?(rc)
2665
- STDERR.puts {"VMID:#{vmid} user.info" \
2666
- " error: #{rc.message}"}
2667
- return nil
2668
- end
2669
-
2670
- token_password = user['TEMPLATE/TOKEN_PASSWORD']
2671
-
2672
- if token_password.nil?
2673
- STDERR.puts {"VMID:#{vmid} TOKEN_PASSWORD not present"\
2674
- " in the USER:#{user_id} TEMPLATE"}
2675
- return nil
2676
- end
2677
-
2678
- cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
2679
- cipher.encrypt
2680
- cipher.key = token_password
2681
- onegate_token = cipher.update(str_to_encrypt)
2682
- onegate_token << cipher.final
2683
-
2684
- onegate_token_64 = Base64.encode64(onegate_token).chop
2685
- config_array << {
2686
- :key => 'opennebula.token',
2687
- :value => onegate_token_64
2688
- }
2689
- end
2690
-
2691
- context_text += "ONEGATE_TOKEN='#{onegate_token_64}'\n"
2692
- end
2693
-
2694
- context_text = Base64.encode64(context_text.chop)
2695
-
2696
- config_array +=
2697
- [{:key=>"guestinfo.opennebula.context",
2698
- :value=>context_text}]
2699
- end
2700
-
2701
- device_change = []
2702
-
2703
- # NIC section, build the reconfig hash
2704
-
2705
- nics = xml.root.get_elements("/VM/TEMPLATE/NIC")
2706
-
2707
- # If the VM is not new, avoid readding NiCs
2708
- if !newvm
2709
-
2710
- nic_array = []
2711
-
2712
- # B4897 - Get mac of NICs that were hot-plugged from vCenter extraConfig
2713
- hotplugged_nics = []
2714
- extraconfig_nics = vm.config.extraConfig.select do |val|
2715
- val[:key] == "opennebula.hotplugged_nics"
2716
- end
2717
-
2718
- if extraconfig_nics && !extraconfig_nics.empty?
2719
- hotplugged_nics = extraconfig_nics[0][:value].to_s.split(";")
2720
- end
2721
-
2722
- # Get MACs from NICs inside VM template
2723
- one_mac_addresses = Array.new
2724
- nics.each{|nic|
2725
- mac = nic.elements["MAC"].text
2726
- one_mac_addresses << mac
2727
- # B4897 - Add NICs that were attached in POWEROFF
2728
- if !hotplugged_nics.include?(mac)
2729
- hotplugged_nics << mac.to_s
2730
- end
2731
- }
2732
-
2733
- vm.config.hardware.device.each{ |dv|
2734
- if is_nic?(dv)
2735
- nics.each{|nic|
2736
- if nic.elements["MAC"].text == dv.macAddress
2737
- nics.delete(nic)
2738
- end
2739
- }
2740
- # B4897 - Remove detached NICs from vCenter that were unplugged in POWEROFF
2741
- if !one_mac_addresses.include?(dv.macAddress) && hotplugged_nics.include?(dv.macAddress)
2742
- nic_array << { :operation => :remove, :device => dv}
2743
- hotplugged_nics.delete(dv.macAddress)
2744
- end
2745
- end
2746
- }
2747
-
2748
- # B4897 - Save what NICs have been attached by OpenNebula in vCenter VM extraconfig
2749
- if !hotplugged_nics.empty?
2750
- config_array << {
2751
- :key => 'opennebula.hotplugged_nics',
2752
- :value => hotplugged_nics.join(";")
2753
- }
2754
- else
2755
- config_array << {
2756
- :key => 'opennebula.hotplugged_nics',
2757
- :value => ""
2758
- }
2759
- end
2760
-
2761
- device_change += nic_array
2762
- else
2763
- # B4897 - Add NICs that have been added to the VM template
2764
- # to the hotplugged_nics extraconfig so we can track what must be removed
2765
-
2766
- # Get MACs from NICs inside VM template to track NICs added by OpenNebula
2767
- one_mac_addresses = []
2768
- nics.each{|nic|
2769
- one_mac_addresses << nic.elements["MAC"].text
2770
- }
2771
-
2772
- if !one_mac_addresses.empty?
2773
- config_array << {
2774
- :key => 'opennebula.hotplugged_nics',
2775
- :value => one_mac_addresses.join(";")
2776
- }
2777
- end
2778
-
2779
- end
2780
-
2781
- if !nics.nil?
2782
- nic_array = []
2783
- nics.each{|nic|
2784
- mac = nic.elements["MAC"].text
2785
- bridge = nic.elements["BRIDGE"].text
2786
- model = nic.elements["MODEL"] ? nic.elements["MODEL"].text : nil
2787
- limit_in = nic.elements["INBOUND_PEAK_BW"] ? nic.elements["INBOUND_PEAK_BW"].text : ""
2788
- limit_out = nic.elements["OUTBOUND_PEAK_BW"] ? nic.elements["OUTBOUND_PEAK_BW"].text : ""
2789
- limit = nil
2790
- if !limit_in.empty? or !limit_out.empty?
2791
- limit=([limit_in.to_i, limit_out.to_i].min / 1024) * 8
2792
- end
2793
- rsrv_in = nic.elements["INBOUND_AVG_BW"] ? nic.elements["INBOUND_AVG_BW"].text : ""
2794
- rsrv_out = nic.elements["OUTBOUND_AVG_BW"] ? nic.elements["OUTBOUND_AVG_BW"].text : ""
2795
- rsrv = nil
2796
- if !rsrv_in.empty? or !rsrv_out.empty?
2797
- rsrv=([rsrv_in.to_i, rsrv_out.to_i].min / 1024) * 8
2798
- end
2799
- nic_array << calculate_addnic_spec(vm,
2800
- mac,
2801
- bridge,
2802
- model,
2803
- limit,
2804
- rsrv)
2805
- }
2806
-
2807
- device_change += nic_array
2808
- end
2809
-
2810
- # DISK section, build the reconfig hash
2811
-
2812
- disks = xml.root.get_elements("/VM/TEMPLATE/DISK")
2813
- disk_spec = {}
2814
-
2815
- # If the VM is not new, avoid reading DISKS
2816
- if !newvm
2817
- vm.config.hardware.device.select { |d|
2818
- if is_disk?(d)
2819
- disks.each{|disk|
2820
- if d.backing.respond_to?(:fileName) &&
2821
- disk.elements["SOURCE"].text == d.backing.fileName &&
2822
- disks.delete(disk)
2823
- end
2824
- }
2825
- end
2826
- }
2827
- end
2828
-
2829
- if !disks.nil?
2830
- disk_array = []
2831
- hid = VIClient::translate_hostname(hostname)
2832
- connection = VIClient.new(hid)
2833
- disks.each{|disk|
2834
- ds_name = disk.elements["DATASTORE"].text
2835
- img_name = disk.elements["SOURCE"].text
2836
- type_str = disk.elements["TYPE"].text
2837
-
2838
- disk_array += attach_disk("", "", ds_name, img_name, type_str, 0, vm, connection)[:deviceChange]
2839
- }
2840
-
2841
- device_change += disk_array
2842
- end
2843
-
2844
- # Capacity section
2845
-
2846
- cpu = xml.root.elements["/VM/TEMPLATE/VCPU"] ? xml.root.elements["/VM/TEMPLATE/VCPU"].text : 1
2847
- memory = xml.root.elements["/VM/TEMPLATE/MEMORY"].text
2848
- capacity_spec = {:numCPUs => cpu.to_i,
2849
- :memoryMB => memory }
2850
-
2851
- # Perform the VM reconfiguration
2852
- if config_array != []
2853
- context_vnc_spec = {:extraConfig =>config_array}
2854
- end
2855
-
2856
- spec_hash = context_vnc_spec.merge(capacity_spec)
2857
- if device_change.length > 0
2858
- spec_hash.merge!({ :deviceChange => device_change })
2859
- end
2860
-
2861
- spec = RbVmomi::VIM.VirtualMachineConfigSpec(spec_hash)
2862
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
2863
- end
2864
-
2865
- ############################################################################
2866
- # Attach disk to a VM
2867
- # @params hostname[String] vcenter cluster name in opennebula as host
2868
- # @params deploy_id[String] deploy id of the vm
2869
- # @params ds_name[String] name of the datastore
2870
- # @params img_name[String] path of the image
2871
- # @params size_kb[String] size in kb of the disk
2872
- # @params vm[RbVmomi::VIM::VirtualMachine] VM if called from instance
2873
- # @params connection[ViClient::connectoon] connection if called from instance
2874
- ############################################################################
2875
- def self.attach_disk(hostname, deploy_id, ds_name, img_name, type, size_kb, vm=nil, connection=nil)
2876
- only_return = true
2877
- if !vm
2878
- hid = VIClient::translate_hostname(hostname)
2879
- connection = VIClient.new(hid)
2880
-
2881
- vm = connection.find_vm_template(deploy_id)
2882
- only_return = false
2883
- end
2884
-
2885
- # Find datastore within datacenter
2886
- datastores = VIClient.get_entities(connection.dc.datastoreFolder,
2887
- 'Datastore')
2888
-
2889
- storage_pods = VIClient.get_entities(connection.dc.datastoreFolder,
2890
- 'StoragePod')
2891
- storage_pods.each { |sp|
2892
- storage_pod_datastores = VIClient.get_entities(sp, 'Datastore')
2893
- if not storage_pod_datastores.empty?
2894
- datastores.concat(storage_pod_datastores)
2895
- end
2896
- }
2897
-
2898
- ds = datastores.select{|ds| ds.name == ds_name}[0]
2899
-
2900
- controller, new_number = find_free_controller(vm)
2901
-
2902
- if type == "CDROM"
2903
- vmdk_backing = RbVmomi::VIM::VirtualCdromIsoBackingInfo(
2904
- :datastore => ds,
2905
- :fileName => "[#{ds_name}] #{img_name}"
2906
- )
2907
-
2908
- cd = vm.config.hardware.device.select {|hw|
2909
- hw.class == RbVmomi::VIM::VirtualCdrom}.first
2910
-
2911
- # If no CDROM drive present, we need to add it
2912
- if !cd
2913
- controller, new_unit_number = find_free_controller(vm)
2914
- cdrom_drive_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
2915
- :deviceChange => [{
2916
- :operation => :add,
2917
- :device => RbVmomi::VIM::VirtualCdrom(
2918
- :backing => vmdk_backing,
2919
- :key => -1,
2920
- :controllerKey => 15000,
2921
- :unitNumber => 0,
2922
- :connectable => RbVmomi::VIM::VirtualDeviceConnectInfo(
2923
- :startConnected => true,
2924
- :connected => true,
2925
- :allowGuestControl => true
2926
- )
2927
- )}]
2928
- )
2929
-
2930
- vm.ReconfigVM_Task(:spec =>
2931
- cdrom_drive_spec).wait_for_completion
2932
-
2933
- return
2934
- else
2935
- device = RbVmomi::VIM::VirtualCdrom(
2936
- backing: vmdk_backing,
2937
- key: cd.key,
2938
- controllerKey: cd.controllerKey,
2939
- connectable: RbVmomi::VIM::VirtualDeviceConnectInfo(
2940
- startConnected: true,
2941
- connected: true,
2942
- allowGuestControl: true
2943
- )
2944
- )
2945
- device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
2946
- :device => device,
2947
- :operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
2948
- )
2949
- end
2950
- else
2951
- vmdk_backing = RbVmomi::VIM::VirtualDiskFlatVer2BackingInfo(
2952
- :datastore => ds,
2953
- :diskMode => 'persistent',
2954
- :fileName => "[#{ds_name}] #{img_name}"
2955
- )
2956
-
2957
- device = RbVmomi::VIM::VirtualDisk(
2958
- :backing => vmdk_backing,
2959
- :capacityInKB => size_kb,
2960
- :controllerKey => controller.key,
2961
- :key => -1,
2962
- :unitNumber => new_number
2963
- )
2964
- device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
2965
- :device => device,
2966
- :operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
2967
- )
2968
- end
2969
-
2970
- vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
2971
- :deviceChange => [device_config_spec]
2972
- )
2973
-
2974
- return vm_config_spec if only_return
2975
-
2976
- vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
2977
- end
2978
-
2979
- def self.find_free_controller(vm)
2980
- free_scsi_controllers = Array.new
2981
- available_controller = nil
2982
- scsi_schema = Hash.new
2983
-
2984
- used_numbers = Array.new
2985
- available_numbers = Array.new
2986
-
2987
- vm.config.hardware.device.each{ |dev|
2988
- if dev.is_a? RbVmomi::VIM::VirtualSCSIController
2989
- if scsi_schema[dev.controllerKey].nil?
2990
- scsi_schema[dev.key] = Hash.new
2991
- scsi_schema[dev.key][:lower] = Array.new
2992
- end
2993
- used_numbers << dev.scsiCtlrUnitNumber
2994
- scsi_schema[dev.key][:device] = dev
2995
- end
2996
-
2997
- next if dev.class != RbVmomi::VIM::VirtualDisk
2998
- used_numbers << dev.unitNumber
2999
- }
3000
-
3001
- 15.times{ |scsi_id|
3002
- available_numbers << scsi_id if used_numbers.grep(scsi_id).length <= 0
3003
- }
3004
-
3005
- scsi_schema.keys.each{|controller|
3006
- if scsi_schema[controller][:lower].length < 15
3007
- free_scsi_controllers << scsi_schema[controller][:device].deviceInfo.label
3008
- end
3009
- }
3010
-
3011
- if free_scsi_controllers.length > 0
3012
- available_controller_label = free_scsi_controllers[0]
3013
- else
3014
- add_new_scsi(vm, scsi_schema)
3015
- return find_free_controller(vm)
3016
- end
3017
-
3018
- controller = nil
3019
-
3020
- vm.config.hardware.device.each { |device|
3021
- (controller = device ; break) if device.deviceInfo.label == available_controller_label
3022
- }
3023
-
3024
- new_unit_number = available_numbers.sort[0]
3025
-
3026
- return controller, new_unit_number
3027
- end
3028
-
3029
- def self.add_new_scsi(vm, scsi_schema)
3030
- controller = nil
3031
-
3032
- if scsi_schema.keys.length >= 4
3033
- raise "Cannot add a new controller, maximum is 4."
3034
- end
3035
-
3036
- if scsi_schema.keys.length == 0
3037
- scsi_key = 0
3038
- scsi_number = 0
3039
- else scsi_schema.keys.length < 4
3040
- scsi_key = scsi_schema.keys.sort[-1] + 1
3041
- scsi_number = scsi_schema[scsi_schema.keys.sort[-1]][:device].busNumber + 1
3042
- end
3043
-
3044
- controller_device = RbVmomi::VIM::VirtualLsiLogicController(
3045
- :key => scsi_key,
3046
- :busNumber => scsi_number,
3047
- :sharedBus => :noSharing
3048
- )
3049
-
3050
- device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
3051
- :device => controller_device,
3052
- :operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
3053
- )
3054
-
3055
- vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
3056
- :deviceChange => [device_config_spec]
3057
- )
3058
-
3059
- vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
3060
-
3061
- vm.config.hardware.device.each { |device|
3062
- if device.class == RbVmomi::VIM::VirtualLsiLogicController &&
3063
- device.key == scsi_key
3064
- controller = device.deviceInfo.label
3065
- end
3066
- }
3067
-
3068
- return controller
3069
- end
3070
-
3071
- ############################################################################
3072
- # Detach a specific disk from a VM
3073
- # @params hostname[String] vcenter cluster name in opennebula as host
3074
- # @params deploy_id[String] deploy id of the vm
3075
- # @params ds_name[String] name of the datastore
3076
- # @params img_path[String] path of the image
3077
- ############################################################################
3078
- def self.detach_disk(hostname, deploy_id, ds_name, img_path)
3079
- hid = VIClient::translate_hostname(hostname)
3080
- connection = VIClient.new(hid)
3081
-
3082
- vm = connection.find_vm_template(deploy_id)
3083
-
3084
- ds_and_img_name = "[#{ds_name}] #{img_path}"
3085
-
3086
- disk = vm.config.hardware.device.select { |d| is_disk?(d) &&
3087
- d.backing.respond_to?(:fileName) &&
3088
- d.backing.fileName == ds_and_img_name }
3089
-
3090
- raise "Disk #{img_path} not found." if disk.nil?
3091
-
3092
- spec = { :deviceChange => [{
3093
- :operation => :remove,
3094
- :device => disk[0]
3095
- }]}
3096
-
3097
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
3098
- end
3099
-
3100
- ############################################################################
3101
- # Detach all disks from a VM
3102
- # @params vm[VCenterVm] vCenter VM
3103
- ############################################################################
3104
- def self.detach_all_disks(vm)
3105
- disks = vm.config.hardware.device.select { |d| is_disk?(d) }
3106
-
3107
- return if disks.nil?
3108
-
3109
- spec = { :deviceChange => [] }
3110
-
3111
- disks.each{|disk|
3112
- spec[:deviceChange] << {
3113
- :operation => :remove,
3114
- :device => disk
3115
- }
3116
- }
3117
-
3118
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
3119
- end
3120
-
3121
- def self.create_context(context)
3122
- # Remove <CONTEXT> (9) and </CONTEXT>\n (11)
3123
- context_text = "# Context variables generated by OpenNebula\n"
3124
- context.elements.each{|context_element|
3125
- next if !context_element.text
3126
- context_text += context_element.name + "='" +
3127
- context_element.text.gsub("'", "\\'") + "'\n"
3128
- }
3129
- context_text
3130
- end
3131
-
3132
- ############################################################################
3133
- # Detach attached disks from a VM
3134
- ############################################################################
3135
- def self.detach_attached_disks(vm, disks, hostname)
3136
- hid = VIClient::translate_hostname(hostname)
3137
- connection = VIClient.new(hid)
3138
-
3139
- spec = { :deviceChange => [] }
3140
-
3141
- disks.each{ |disk|
3142
- ds_and_img_name = "[#{disk['DATASTORE']}] #{disk['SOURCE']}"
3143
- vcenter_disk = vm.config.hardware.device.select { |d| is_disk?(d) &&
3144
- d.backing.respond_to?(:fileName) &&
3145
- d.backing.fileName == ds_and_img_name }[0]
3146
- spec[:deviceChange] << {
3147
- :operation => :remove,
3148
- :device => vcenter_disk
3149
- }
3150
- }
3151
-
3152
- vm.ReconfigVM_Task(:spec => spec).wait_for_completion
3153
- end
3154
- end
3155
79
  end