opennebula 5.12.13 → 5.13.80.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +5 -5
  2. data/lib/ActionManager.rb +1 -1
  3. data/lib/CommandManager.rb +1 -1
  4. data/lib/DriverExecHelper.rb +44 -28
  5. data/lib/OpenNebulaDriver.rb +8 -4
  6. data/lib/VirtualMachineDriver.rb +9 -2
  7. data/lib/cloud/CloudClient.rb +3 -3
  8. data/lib/datacenter.rb +1258 -0
  9. data/lib/datastore.rb +1025 -0
  10. data/lib/distributed_firewall.rb +280 -0
  11. data/lib/file_helper.rb +370 -0
  12. data/lib/host.rb +1517 -0
  13. data/lib/logical_port.rb +50 -0
  14. data/lib/logical_switch.rb +77 -0
  15. data/lib/memoize.rb +74 -0
  16. data/lib/models/role.rb +39 -8
  17. data/lib/models/service.rb +92 -31
  18. data/lib/models.rb +5 -5
  19. data/lib/network.rb +635 -0
  20. data/lib/nsx_client.rb +144 -0
  21. data/lib/nsx_component.rb +28 -0
  22. data/lib/nsx_constants.rb +149 -0
  23. data/lib/nsx_driver.rb +78 -0
  24. data/lib/nsx_error.rb +77 -0
  25. data/lib/nsx_rule.rb +193 -0
  26. data/lib/nsxt_client.rb +176 -0
  27. data/lib/nsxt_dfw.rb +196 -0
  28. data/lib/nsxt_logical_port.rb +94 -0
  29. data/lib/nsxt_rule.rb +188 -0
  30. data/lib/nsxt_tz.rb +38 -0
  31. data/lib/nsxv_client.rb +176 -0
  32. data/lib/nsxv_dfw.rb +202 -0
  33. data/lib/nsxv_logical_port.rb +107 -0
  34. data/lib/nsxv_rule.rb +172 -0
  35. data/lib/nsxv_tz.rb +41 -0
  36. data/lib/opaque_network.rb +134 -0
  37. data/lib/opennebula/acl.rb +1 -1
  38. data/lib/opennebula/acl_pool.rb +1 -1
  39. data/lib/opennebula/client.rb +1 -1
  40. data/lib/opennebula/cluster.rb +1 -1
  41. data/lib/opennebula/cluster_pool.rb +1 -1
  42. data/lib/opennebula/datastore.rb +1 -1
  43. data/lib/opennebula/datastore_pool.rb +1 -1
  44. data/lib/opennebula/document.rb +8 -29
  45. data/lib/opennebula/document_json.rb +42 -12
  46. data/lib/opennebula/document_pool.rb +1 -1
  47. data/lib/opennebula/document_pool_json.rb +1 -1
  48. data/lib/opennebula/error.rb +4 -1
  49. data/lib/opennebula/flow/grammar.rb +1195 -0
  50. data/lib/{models → opennebula/flow}/service_pool.rb +26 -2
  51. data/lib/{models → opennebula/flow}/service_template.rb +86 -17
  52. data/lib/opennebula/flow/service_template_ext.rb +84 -0
  53. data/lib/{models → opennebula/flow}/service_template_pool.rb +1 -1
  54. data/lib/opennebula/flow/validator.rb +499 -0
  55. data/lib/opennebula/flow.rb +23 -0
  56. data/lib/opennebula/group.rb +1 -1
  57. data/lib/opennebula/group_pool.rb +1 -1
  58. data/lib/opennebula/hook.rb +5 -12
  59. data/lib/opennebula/hook_log.rb +1 -1
  60. data/lib/opennebula/hook_pool.rb +1 -1
  61. data/lib/opennebula/host.rb +1 -1
  62. data/lib/opennebula/host_pool.rb +1 -1
  63. data/lib/opennebula/image.rb +17 -14
  64. data/lib/opennebula/image_pool.rb +1 -1
  65. data/lib/opennebula/ldap_auth.rb +1 -1
  66. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  67. data/lib/opennebula/lockable_ext.rb +163 -0
  68. data/lib/opennebula/marketplace.rb +1 -1
  69. data/lib/opennebula/marketplace_pool.rb +1 -1
  70. data/lib/opennebula/marketplaceapp.rb +9 -119
  71. data/lib/opennebula/marketplaceapp_ext.rb +522 -0
  72. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  73. data/lib/opennebula/oneflow_client.rb +4 -3
  74. data/lib/opennebula/pool.rb +4 -3
  75. data/lib/opennebula/pool_element.rb +1 -1
  76. data/lib/opennebula/security_group.rb +1 -1
  77. data/lib/opennebula/security_group_pool.rb +1 -1
  78. data/lib/opennebula/server_cipher_auth.rb +1 -1
  79. data/lib/opennebula/server_x509_auth.rb +1 -1
  80. data/lib/opennebula/ssh_auth.rb +1 -1
  81. data/lib/opennebula/system.rb +1 -1
  82. data/lib/opennebula/template.rb +4 -13
  83. data/lib/opennebula/template_ext.rb +325 -0
  84. data/lib/opennebula/template_pool.rb +1 -1
  85. data/lib/opennebula/user.rb +26 -2
  86. data/lib/opennebula/user_pool.rb +1 -1
  87. data/lib/opennebula/utils.rb +1 -1
  88. data/lib/opennebula/vdc.rb +1 -1
  89. data/lib/opennebula/vdc_pool.rb +1 -1
  90. data/lib/opennebula/virtual_machine.rb +25 -207
  91. data/lib/opennebula/virtual_machine_ext.rb +469 -0
  92. data/lib/opennebula/virtual_machine_pool.rb +1 -5
  93. data/lib/opennebula/virtual_network.rb +4 -10
  94. data/lib/opennebula/virtual_network_pool.rb +1 -1
  95. data/lib/opennebula/virtual_router.rb +4 -12
  96. data/lib/opennebula/virtual_router_pool.rb +1 -1
  97. data/lib/opennebula/vm_group.rb +4 -11
  98. data/lib/opennebula/vm_group_pool.rb +1 -1
  99. data/lib/opennebula/vntemplate.rb +4 -13
  100. data/lib/opennebula/vntemplate_pool.rb +1 -1
  101. data/lib/opennebula/wait_ext.rb +222 -0
  102. data/lib/opennebula/x509_auth.rb +1 -1
  103. data/lib/opennebula/xml_element.rb +1 -1
  104. data/lib/opennebula/xml_pool.rb +1 -1
  105. data/lib/opennebula/xml_utils.rb +1 -1
  106. data/lib/opennebula/zone.rb +1 -1
  107. data/lib/opennebula/zone_pool.rb +1 -1
  108. data/lib/opennebula.rb +5 -2
  109. data/lib/rest_client.rb +201 -0
  110. data/lib/scripts_common.rb +180 -0
  111. data/lib/transport_zone.rb +43 -0
  112. data/lib/vcenter_driver.rb +9 -22
  113. data/lib/vcenter_importer.rb +616 -0
  114. data/lib/vi_client.rb +281 -0
  115. data/lib/vi_helper.rb +312 -0
  116. data/lib/virtual_machine.rb +3477 -0
  117. data/lib/virtual_wire.rb +158 -0
  118. data/lib/vm_device.rb +80 -0
  119. data/lib/vm_disk.rb +202 -0
  120. data/lib/vm_folder.rb +69 -0
  121. data/lib/vm_helper.rb +30 -0
  122. data/lib/vm_monitor.rb +303 -0
  123. data/lib/vm_nic.rb +70 -0
  124. data/lib/vm_template.rb +1961 -0
  125. data/lib/vmm_importer.rb +121 -0
  126. metadata +101 -35
data/lib/datastore.rb ADDED
@@ -0,0 +1,1025 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ ##############################################################################
18
+ # Module VCenterDriver
19
+ ##############################################################################
20
+ module VCenterDriver
21
+
22
+ require 'digest'
23
+ require 'uri'
24
+
25
+ ##########################################################################
26
+ # Class DatastoreFolder
27
+ ##########################################################################
28
+ class DatastoreFolder
29
+
30
+ attr_accessor :item, :items
31
+
32
+ def initialize(item)
33
+ @item = item
34
+ @items = {}
35
+ end
36
+
37
+ ######################################################################
38
+ # Builds a hash with Datastore-Ref / Datastore to be used as a cache
39
+ # @return [Hash] in the form
40
+ # { ds_ref [Symbol] => Datastore object }
41
+ ######################################################################
42
+ def fetch!
43
+ VIClient.get_entities(@item, 'Datastore').each do |item|
44
+ item_name = item._ref
45
+ @items[item_name.to_sym] = Datastore.new(item)
46
+ end
47
+
48
+ VIClient.get_entities(@item, 'StoragePod').each do |sp|
49
+ @items[sp._ref.to_sym] = StoragePod.new(sp)
50
+ VIClient.get_entities(sp, 'Datastore').each do |item|
51
+ item_name = item._ref
52
+ @items[item_name.to_sym] = Datastore.new(item)
53
+ end
54
+ end
55
+ @items
56
+ end
57
+
58
+ ######################################################################
59
+ # Returns a Datastore or StoragePod. Uses the cache if available.
60
+ # @param ref [Symbol] the vcenter ref
61
+ # @return Datastore
62
+ ######################################################################
63
+ def get(ref)
64
+ if !@items[ref.to_sym]
65
+ if ref.start_with?('group-')
66
+ rbvmomi_spod = RbVmomi::VIM::StoragePod
67
+ .new(
68
+ @item._connection,
69
+ ref
70
+ ) rescue nil
71
+ @items[ref.to_sym] = StoragePod.new(rbvmomi_spod)
72
+ else
73
+ rbvmomi_ds = RbVmomi::VIM::Datastore
74
+ .new(
75
+ @item._connection,
76
+ ref
77
+ ) rescue nil
78
+ @items[ref.to_sym] = Datastore.new(rbvmomi_ds)
79
+ end
80
+ end
81
+ @items[ref.to_sym]
82
+ end
83
+
84
+ end
85
+ # class DatastoreFolder
86
+
87
+ ##########################################################################
88
+ # Class Storage
89
+ ##########################################################################
90
+ class Storage
91
+
92
+ attr_accessor :item
93
+
94
+ include Memoize
95
+
96
+ CURLBIN = 'curl'
97
+
98
+ def self.new_from_ref(ref, vi_client)
99
+ if ref.start_with?('group-')
100
+ VCenterDriver::StoragePod.new_from_ref(ref, vi_client)
101
+ else
102
+ VCenterDriver::Datastore.new_from_ref(ref, vi_client)
103
+ end
104
+ end
105
+
106
+ def self.get_image_import_template(params)
107
+ disk = params[:disk]
108
+ ipool = params[:ipool]
109
+ _type = params[:_type]
110
+ ds_id = params[:ds_id]
111
+ opts = params[:opts]
112
+ images = params[:images]
113
+
114
+ VCenterDriver::VIHelper.check_opts(opts, [:persistent])
115
+
116
+ ds_name = disk[:datastore].name
117
+ image_path = disk[:path_wo_ds]
118
+ image_type = disk[:type]
119
+ image_prefix = disk[:prefix]
120
+
121
+ image_name = nil
122
+
123
+ one_image = {}
124
+ one_image[:template] = ''
125
+
126
+ # Get image name
127
+ file_name = File.basename(image_path).gsub(/\.vmdk$/, '')
128
+
129
+ # Check if the image has already been imported
130
+ image = VIHelper
131
+ .find_image_by(
132
+ 'SOURCE',
133
+ OpenNebula::ImagePool,
134
+ image_path,
135
+ ds_id,
136
+ ipool
137
+ )
138
+
139
+ if image.nil?
140
+ key = "#{file_name}#{ds_name}#{image_path}"
141
+ byte = 0
142
+ image_name = VCenterDriver::VIHelper
143
+ .one_name(
144
+ OpenNebula::ImagePool,
145
+ file_name,
146
+ key,
147
+ ipool,
148
+ byte
149
+ )
150
+ while images.include?(image_name)
151
+ byte += 2
152
+ image_name = VCenterDriver::VIHelper
153
+ .one_name(
154
+ OpenNebula::ImagePool,
155
+ file_name,
156
+ key,
157
+ ipool,
158
+ byte
159
+ )
160
+ end
161
+
162
+ # Set template
163
+ one_image[:template] << "NAME=\"#{image_name}\"\n"
164
+ one_image[:template] << "PATH=\"vcenter://#{image_path}\"\n"
165
+ one_image[:template] << "TYPE=\"#{image_type}\"\n"
166
+ one_image[:template] << "PERSISTENT=\"#{opts[:persistent]}\"\n"
167
+ unless CONFIG[:delete_images]
168
+ one_image[:template] << "VCENTER_IMPORTED=\"YES\"\n"
169
+ end
170
+ one_image[:template] << "DEV_PREFIX=\"#{image_prefix}\"\n"
171
+ else
172
+ # Return the image XML if it already exists
173
+ one_image[:one] = image
174
+ end
175
+
176
+ [one_image, image_name]
177
+ end
178
+
179
+ def self.get_one_image_ds_by_ref_and_dc(
180
+ ref,
181
+ dc_ref,
182
+ vcenter_uuid,
183
+ pool = nil
184
+ )
185
+ if pool.nil?
186
+ pool = VCenterDriver::VIHelper
187
+ .one_pool(
188
+ OpenNebula::DatastorePool,
189
+ false
190
+ )
191
+ if pool.respond_to?(:message)
192
+ raise "Could not get \
193
+ OpenNebula DatastorePool: #{pool.message}"
194
+ end
195
+ end
196
+
197
+ pool.select do |e|
198
+ e['TEMPLATE/TYPE'] == 'IMAGE_DS' &&
199
+ e['TEMPLATE/VCENTER_DS_REF'] == ref &&
200
+ e['TEMPLATE/VCENTER_DC_REF'] == dc_ref &&
201
+ e['TEMPLATE/VCENTER_INSTANCE_ID'] == vcenter_uuid
202
+ end.first rescue nil
203
+ end
204
+
205
+ # Checks if a RbVmomi::VIM::VirtualDevice is a disk or an iso file
206
+ def self.disk_or_iso?(device)
207
+ is_disk = !device.class.ancestors.index(
208
+ RbVmomi::VIM::VirtualDisk
209
+ ).nil?
210
+ is_iso = device
211
+ .backing.is_a? RbVmomi::VIM::VirtualCdromIsoBackingInfo
212
+ is_disk || is_iso
213
+ end
214
+
215
+ def monitor
216
+ summary = self['summary']
217
+
218
+ total_mb = (summary.capacity.to_i / 1024) / 1024
219
+ free_mb = (summary.freeSpace.to_i / 1024) / 1024
220
+ used_mb = total_mb - free_mb
221
+
222
+ "USED_MB=#{used_mb}\nFREE_MB=#{free_mb} \nTOTAL_MB=#{total_mb}"
223
+ end
224
+
225
+ def self.exists_one_by_ref_dc_and_type?(
226
+ ref,
227
+ dc_ref,
228
+ vcenter_uuid,
229
+ type,
230
+ pool = nil
231
+ )
232
+ if pool.nil?
233
+ pool = VCenterDriver::VIHelper.one_pool(
234
+ OpenNebula::DatastorePool,
235
+ false
236
+ )
237
+ if pool.respond_to?(:message)
238
+ raise "Could not get OpenNebula \
239
+ DatastorePool: #{pool.message}"
240
+ end
241
+ end
242
+ elements = pool.select do |e|
243
+ e['TEMPLATE/TYPE'] == type &&
244
+ e['TEMPLATE/VCENTER_DS_REF'] == ref &&
245
+ e['TEMPLATE/VCENTER_DC_REF'] == dc_ref &&
246
+ e['TEMPLATE/VCENTER_INSTANCE_ID'] == vcenter_uuid
247
+ end
248
+
249
+ elements.size == 1
250
+ end
251
+
252
+ def to_one(ds_hash, vcenter_uuid, dc_name, dc_ref)
253
+ one = ''
254
+ one << "DRIVER=\"vcenter\"\n"
255
+ one << "NAME=\"#{ds_hash[:name]}\"\n"
256
+ one << "TM_MAD=vcenter\n"
257
+ one << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
258
+ one << "VCENTER_DC_REF=\"#{dc_ref}\"\n"
259
+ one << "VCENTER_DC_NAME=\"#{dc_name}\"\n"
260
+ one << "VCENTER_DS_NAME=\"#{ds_hash[:simple_name]}\"\n"
261
+ one << "VCENTER_DS_REF=\"#{self['_ref']}\"\n"
262
+ one
263
+ end
264
+
265
+ def to_one_template(ds_hash, vcenter_uuid, dc_name, dc_ref, type)
266
+ one_tmp = {
267
+ :one => to_one(ds_hash, vcenter_uuid, dc_name, dc_ref)
268
+ }
269
+
270
+ if type == 'SYSTEM_DS'
271
+ one_tmp[:one] << "TYPE=SYSTEM_DS\n"
272
+ else
273
+ one_tmp[:one] << "DS_MAD=vcenter\n"
274
+ one_tmp[:one] << "TYPE=IMAGE_DS\n"
275
+ end
276
+
277
+ one_tmp
278
+ end
279
+
280
+ def create_virtual_disk(img_name, size, adapter_type, disk_type)
281
+ leading_dirs = img_name.split('/')[0..-2]
282
+ if !leading_dirs.empty?
283
+ create_directory(leading_dirs.join('/'))
284
+ end
285
+
286
+ ds_name = self['name']
287
+
288
+ vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
289
+ :adapterType => adapter_type,
290
+ :capacityKb => size.to_i*1024,
291
+ :diskType => disk_type
292
+ )
293
+
294
+ obtain_vdm.CreateVirtualDisk_Task(
295
+ :datacenter => obtain_dc.item,
296
+ :name => "[#{ds_name}] #{img_name}.vmdk",
297
+ :spec => vmdk_spec
298
+ ).wait_for_completion
299
+
300
+ "#{img_name}.vmdk"
301
+ end
302
+
303
+ def create_directory(directory)
304
+ ds_name = self['name']
305
+
306
+ return if self.class == VCenterDriver::StoragePod
307
+
308
+ directory_name = "[#{ds_name}] #{directory}"
309
+
310
+ create_directory_params = {
311
+ :name => directory_name,
312
+ :datacenter => obtain_dc.item,
313
+ :createParentDirectories => true
314
+ }
315
+
316
+ obtain_fm.MakeDirectory(create_directory_params) rescue nil
317
+ end
318
+
319
+ def obtain_fm
320
+ self['_connection.serviceContent.fileManager']
321
+ end
322
+
323
+ def obtain_vdm
324
+ self['_connection.serviceContent.virtualDiskManager']
325
+ end
326
+
327
+ def obtain_dc
328
+ item = @item
329
+
330
+ until item.instance_of? RbVmomi::VIM::Datacenter
331
+ item = item.parent
332
+ if item.nil?
333
+ raise 'Could not find the parent Datacenter'
334
+ end
335
+ end
336
+
337
+ Datacenter.new(item)
338
+ end
339
+
340
+ end
341
+ # class Storage
342
+
343
+ ########################################################################
344
+ # Class StoragePod
345
+ ########################################################################
346
+ class StoragePod < Storage
347
+
348
+ def initialize(item, _vi_client = nil)
349
+ check_item(item, RbVmomi::VIM::StoragePod)
350
+ @item = item
351
+
352
+ super()
353
+ end
354
+
355
+ # This is never cached
356
+ def self.new_from_ref(ref, vi_client)
357
+ new(RbVmomi::VIM::StoragePod.new(vi_client.vim, ref), vi_client)
358
+ end
359
+
360
+ end
361
+ # class StoragePod
362
+
363
+ ##########################################################################
364
+ # Class Datastore
365
+ ##########################################################################
366
+ class Datastore < Storage
367
+
368
+ attr_accessor :one_item
369
+
370
+ def initialize(item, vi_client = nil)
371
+ check_item(item, RbVmomi::VIM::Datastore)
372
+ @vi_client = vi_client
373
+ @item = item
374
+ @one_item = {}
375
+
376
+ super()
377
+ end
378
+
379
+ def delete_virtual_disk(img_name)
380
+ ds_name = self['name']
381
+
382
+ begin
383
+ obtain_vdm.DeleteVirtualDisk_Task(
384
+ :name => "[#{ds_name}] #{img_name}",
385
+ :datacenter => obtain_dc.item
386
+ ).wait_for_completion
387
+ rescue StandardError => e
388
+ # Ignore if file not found
389
+ if !e.message.start_with?('ManagedObjectNotFound') &&
390
+ !e.message.start_with?('FileNotFound')
391
+ raise e
392
+ end
393
+ end
394
+ end
395
+
396
+ def delete_file(img_name)
397
+ ds_name = self['name']
398
+
399
+ begin
400
+ obtain_fm.DeleteDatastoreFile_Task(
401
+ :name => "[#{ds_name}] #{img_name}",
402
+ :datacenter => obtain_dc.item
403
+ ).wait_for_completion
404
+ rescue StandardError => e
405
+ # Ignore if file not found
406
+ if !e.message.start_with?('ManagedObjectNotFound') &&
407
+ !e.message.start_with?('FileNotFound')
408
+ raise e
409
+ end
410
+ end
411
+ end
412
+
413
+ # Copy a VirtualDisk
414
+ def copy_virtual_disk(src_path, target_ds, target_path, new_size = nil)
415
+ source_ds_name = self['name']
416
+ target_ds_name = target_ds['name']
417
+
418
+ leading_dirs = target_path.split('/')[0..-2]
419
+ if !leading_dirs.empty?
420
+ if source_ds_name == target_ds_name
421
+ create_directory(leading_dirs.join('/'))
422
+ else
423
+ target_ds.create_directory(leading_dirs.join('/'))
424
+ end
425
+ end
426
+
427
+ copy_params = {
428
+ :sourceName => "[#{source_ds_name}] #{src_path}",
429
+ :sourceDatacenter => obtain_dc.item
430
+ }
431
+
432
+ if File.extname(src_path) == '.vmdk'
433
+ copy_params[:destName] = "[#{target_ds_name}] #{target_path}"
434
+ obtain_vdm.CopyVirtualDisk_Task(copy_params).wait_for_completion
435
+
436
+ if new_size
437
+ resize_spec = {
438
+ :name => "[#{target_ds_name}] #{target_path}",
439
+ :datacenter => target_ds.obtain_dc.item,
440
+ :newCapacityKb => new_size,
441
+ :eagerZero => false
442
+ }
443
+
444
+ obtain_vdm.ExtendVirtualDisk_Task(
445
+ resize_spec
446
+ ).wait_for_completion
447
+ end
448
+ else
449
+ copy_params[:destinationName] =
450
+ "[#{target_ds_name}] #{target_path}"
451
+ obtain_fm.CopyDatastoreFile_Task(
452
+ copy_params
453
+ ).wait_for_completion
454
+ end
455
+
456
+ target_path
457
+ end
458
+
459
+ def move_virtual_disk(disk, dest_path, dest_dsid, vi_client = nil)
460
+ vi_client ||= @vi_client
461
+
462
+ target_ds = VCenterDriver::VIHelper.one_item(
463
+ OpenNebula::Datastore,
464
+ dest_dsid,
465
+ false
466
+ )
467
+ target_ds_ref = target_ds['TEMPLATE/VCENTER_DS_REF']
468
+ target_ds_vc = VCenterDriver::Datastore
469
+ .new_from_ref(
470
+ target_ds_ref,
471
+ vi_client
472
+ )
473
+ dest_name = target_ds_vc['name']
474
+
475
+ target_ds_vc.create_directory(File.dirname(dest_path))
476
+
477
+ dpath_ds = "[#{dest_name}] #{dest_path}"
478
+ orig_path = "[#{self['name']}] #{disk.path}"
479
+
480
+ move_params = {
481
+ :sourceName => orig_path,
482
+ :sourceDatacenter => obtain_dc.item,
483
+ :destName => dpath_ds,
484
+ :force => true
485
+ }
486
+
487
+ obtain_vdm.MoveVirtualDisk_Task(move_params).wait_for_completion
488
+ end
489
+
490
+ def rm_directory(directory)
491
+ ds_name = self['name']
492
+
493
+ rm_directory_params = {
494
+ :name => "[#{ds_name}] #{directory}",
495
+ :datacenter => obtain_dc.item
496
+ }
497
+
498
+ obtain_fm.DeleteDatastoreFile_Task(
499
+ rm_directory_params
500
+ ).wait_for_completion
501
+ end
502
+
503
+ def dir_empty?(path)
504
+ ds_name = self['name']
505
+
506
+ spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new
507
+
508
+ search_params = {
509
+ 'datastorePath' => "[#{ds_name}] #{path}",
510
+ 'searchSpec' => spec
511
+ }
512
+
513
+ begin
514
+ search_task = self['browser']
515
+ .SearchDatastoreSubFolders_Task(search_params)
516
+ search_task.wait_for_completion
517
+ !search_task.info.result.nil? &&
518
+ search_task.info.result.length == 1 &&
519
+ search_task.info.result.first.file.empty?
520
+ rescue StandardError
521
+ false
522
+ end
523
+ end
524
+
525
+ def upload_file(source_path, target_path)
526
+ @item.upload(target_path, source_path)
527
+ end
528
+
529
+ def download_file(source_path, target_path)
530
+ @item.download(source_path, target_path)
531
+ end
532
+
533
+ # Get file size for image handling
534
+ def stat(img_str)
535
+ ds_name = self['name']
536
+ img_path = File.dirname img_str
537
+ img_name = File.basename img_str
538
+
539
+ # Create Search Spec
540
+ search_params = get_search_params(ds_name, img_path, img_name)
541
+
542
+ # Perform search task and return results
543
+ begin
544
+ search_task = self['browser']
545
+ .SearchDatastoreSubFolders_Task(search_params)
546
+
547
+ search_task.wait_for_completion
548
+
549
+ # Try to get vmdk capacity as seen by VM
550
+ size = search_task
551
+ .info.result[0].file[0].capacityKb / 1024 rescue nil
552
+
553
+ # Try to get file size
554
+ size ||= search_task
555
+ .info
556
+ .result[0].file[0].fileSize / 1024 / 1024 rescue nil
557
+
558
+ raise 'Could not get file size or capacity' if size.nil?
559
+
560
+ size
561
+ rescue StandardError
562
+ raise 'Could not find file.'
563
+ end
564
+ end
565
+
566
+ def get_search_params(ds_name, img_path = nil, img_name = nil)
567
+ spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new
568
+
569
+ vmdisk_query = RbVmomi::VIM::VmDiskFileQuery.new
570
+ vmdisk_query.details = RbVmomi::VIM::VmDiskFileQueryFlags(
571
+ :diskType => true,
572
+ :capacityKb => true,
573
+ :hardwareVersion => true,
574
+ :controllerType => true
575
+ )
576
+
577
+ spec.query = [vmdisk_query,
578
+ RbVmomi::VIM::IsoImageFileQuery.new]
579
+ spec.details = RbVmomi::VIM::FileQueryFlags(
580
+ :fileOwner => true,
581
+ :fileSize => true,
582
+ :fileType => true,
583
+ :modification => true
584
+ )
585
+
586
+ if img_name.nil?
587
+ spec.matchPattern = []
588
+ else
589
+ spec.matchPattern = [img_name]
590
+ end
591
+
592
+ datastore_path = "[#{ds_name}]"
593
+ datastore_path << " #{img_path}" unless img_path.nil?
594
+
595
+ { 'datastorePath' => datastore_path,
596
+ 'searchSpec' => spec }
597
+ end
598
+
599
+ def dc_path
600
+ dc = obtain_dc
601
+ p = dc.item.parent
602
+ path = [dc.item.name]
603
+ while p.instance_of? RbVmomi::VIM::Folder
604
+ path.unshift(p.name)
605
+ p = p.parent
606
+ end
607
+ path.delete_at(0) # The first folder is the root "Datacenters"
608
+ path.join('/')
609
+ end
610
+
611
+ def generate_file_url(path)
612
+ if self['_connection.http.use_ssl?']
613
+ protocol = 'https://'
614
+ else
615
+ protocol = 'http://'
616
+ end
617
+ hostname = self['_connection.http.address']
618
+ port = self['_connection.http.port']
619
+ dcpath = dc_path
620
+ url_path = "folder/#{path}?dcPath=#{dcpath}&dsName=#{self['name']}"
621
+
622
+ # This creates the vcenter file URL
623
+ # for uploading or downloading files
624
+ # e.g:
625
+ url = "#{protocol}#{hostname}:#{port}/#{url_path}"
626
+ URI.escape(url) # rubocop:disable Lint/UriEscapeUnescape
627
+ end
628
+
629
+ def download_to_stdout(remote_path)
630
+ url = generate_file_url(remote_path)
631
+ pid = spawn(CURLBIN,
632
+ '-k', '--noproxy', '*', '-f',
633
+ '-b', self['_connection.cookie'],
634
+ url)
635
+
636
+ Process.waitpid(pid, 0)
637
+ raise 'download failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars
638
+ end
639
+
640
+ def descriptor?(remote_path)
641
+ url = generate_file_url(remote_path)
642
+
643
+ rout, wout = IO.pipe
644
+ pid = spawn(CURLBIN,
645
+ '-I', '-k', '--noproxy', '*', '-f',
646
+ '-b', self['_connection.cookie'],
647
+ url,
648
+ :out => wout,
649
+ :err => '/dev/null')
650
+
651
+ Process.waitpid(pid, 0)
652
+ raise 'read image header failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars
653
+
654
+ wout.close
655
+ size = rout.readlines.select do |l|
656
+ l.start_with?('Content-Length')
657
+ end[0].sub('Content-Length: ', '')
658
+ rout.close
659
+ size.chomp.to_i < 4096 # If <4k, then is a descriptor
660
+ end
661
+
662
+ def get_text_file(remote_path)
663
+ url = generate_file_url(remote_path)
664
+
665
+ rout, wout = IO.pipe
666
+ pid = spawn CURLBIN, '-k', '--noproxy', '*', '-f',
667
+ '-b', self['_connection.cookie'],
668
+ url,
669
+ :out => wout,
670
+ :err => '/dev/null'
671
+
672
+ Process.waitpid(pid, 0)
673
+ raise 'get text file failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars
674
+
675
+ wout.close
676
+ output = rout.readlines
677
+ rout.close
678
+ output
679
+ end
680
+
681
+ def all_images
682
+ images = {}
683
+ imid = -1
684
+ ds_id = nil
685
+ ds_name = self['name']
686
+
687
+ # We need OpenNebula Images and Datastores pools
688
+ ipool = VCenterDriver::VIHelper
689
+ .one_pool(OpenNebula::ImagePool, false)
690
+ if ipool.respond_to?(:message)
691
+ raise "Could not get OpenNebula ImagePool: #{pool.message}"
692
+ end
693
+
694
+ dpool = VCenterDriver::VIHelper
695
+ .one_pool(OpenNebula::DatastorePool, false)
696
+ if dpool.respond_to?(:message)
697
+ raise "Could not get OpenNebula DatastorePool: #{pool.message}"
698
+ end
699
+
700
+ ds_id = @one_item['ID']
701
+
702
+ begin
703
+ # Create Search Spec
704
+ search_params = get_search_params(ds_name)
705
+
706
+ # Perform search task and return results
707
+ search_task = self['browser']
708
+ .SearchDatastoreSubFolders_Task(search_params)
709
+ search_task.wait_for_completion
710
+
711
+ # Loop through search results
712
+ search_task.info.result.each do |result|
713
+ # Remove [datastore] from file path
714
+ folderpath = ''
715
+ size = result.folderPath.size
716
+ if result.folderPath[-1] != ']'
717
+ if result.folderPath[-1] != '/'
718
+ result.folderPath[size] = '/'
719
+ end
720
+ folderpath = result
721
+ .folderPath.sub(/^\[#{ds_name}\] /, '')
722
+ end
723
+
724
+ # Loop through images in result.file
725
+ result.file.each do |image|
726
+ image_path = ''
727
+
728
+ # Skip not relevant files
729
+ next unless %w[FloppyImageFileInfo
730
+ IsoImageFileInfo
731
+ VmDiskFileInfo].include? image.class.to_s
732
+
733
+ # Get image path and name
734
+ image_path << folderpath << image.path
735
+ image_name = File.basename(image.path)
736
+ .reverse.sub('kdmv.', '').reverse
737
+
738
+ # Get image's type
739
+ if image.class.to_s == 'VmDiskFileInfo'
740
+ image_type = 'OS'
741
+ else
742
+ image_type = 'CDROM'
743
+ end
744
+
745
+ # Get image's size
746
+ image_size = image.capacityKb / 1024 rescue nil
747
+ image_size ||= image.fileSize / 1024 / 1024 rescue nil
748
+
749
+ # Assign image prefix if known or assign default prefix
750
+ controller = image.controllerType rescue nil
751
+ if controller
752
+ if controller == 'VirtualIDEController'
753
+ disk_prefix = 'hd'
754
+ else
755
+ disk_prefix = 'sd'
756
+ end
757
+ else
758
+ # Get default value for disks that
759
+ # are not attached to any controller
760
+ disk_prefix = VCenterDriver::VIHelper
761
+ .get_default(
762
+ 'IMAGE/TEMPLATE/DEV_PREFIX'
763
+ )
764
+ end
765
+
766
+ # Generate a crypto hash
767
+ # this hash is used to avoid name collisions
768
+ key = "#{image_name}#{ds_name}#{image_path}"
769
+ import_name = VCenterDriver::VIHelper
770
+ .one_name(
771
+ OpenNebula::ImagePool,
772
+ image_name,
773
+ key,
774
+ ipool
775
+ )
776
+
777
+ # Set template
778
+ one_image = "NAME=\"#{import_name}\"\n"
779
+ one_image << "PATH=\"vcenter://#{image_path}\"\n"
780
+ one_image << "PERSISTENT=\"NO\"\n"
781
+ one_image << "TYPE=\"#{image_type}\"\n"
782
+ unless CONFIG[:delete_images]
783
+ one_image << "VCENTER_IMPORTED=\"YES\"\n"
784
+ end
785
+ one_image << "DEV_PREFIX=\"#{disk_prefix}\"\n"
786
+
787
+ # Check image hasn't already been imported
788
+ image_found = VCenterDriver::VIHelper
789
+ .find_image_by(
790
+ 'SOURCE',
791
+ OpenNebula::ImagePool,
792
+ image_path,
793
+ ds_id,
794
+ ipool
795
+ )
796
+
797
+ next if image_found
798
+
799
+ # Add template to image array
800
+ images[import_name] = {
801
+ :import_id => imid+=1,
802
+ :name => import_name,
803
+ :ref => import_name,
804
+ :path => image_path,
805
+ :size => image_size.to_s,
806
+ :type => image.class.to_s,
807
+ :dsid => ds_id,
808
+ :one => one_image
809
+ }
810
+ end
811
+ end
812
+ rescue StandardError => e
813
+ raise "Could not find images. \
814
+ Reason: #{e.message}/#{e.backtrace}"
815
+ end
816
+ vname = @vi_client.vc_name || ''
817
+
818
+ { vname => images }
819
+ end
820
+
821
+ # This is never cached
822
+ def self.new_from_ref(ref, vi_client)
823
+ new(RbVmomi::VIM::Datastore.new(vi_client.vim, ref), vi_client)
824
+ end
825
+
826
+ # detach disk from vCenter vm if possible, destroy the disk on FS
827
+ def self.detach_and_destroy(disk, vm, disk_id, prev_ds_ref, vi_client)
828
+ # it's not a CDROM (CLONE=NO)
829
+ is_cd = !(disk['CLONE'].nil? || disk['CLONE'] == 'YES')
830
+
831
+ begin
832
+ # Detach disk if possible (VM is reconfigured)
833
+ # and gather vCenter info
834
+ # Needed for poweroff machines too
835
+ ds_ref, img_path = vm.detach_disk(disk)
836
+
837
+ return if is_cd
838
+
839
+ # Disk could't be detached, use OpenNebula info
840
+ if !(ds_ref && img_path && !img_path.empty?)
841
+ img_path = vm.disk_real_path(disk, disk_id)
842
+ ds_ref = prev_ds_ref
843
+ end
844
+
845
+ # If disk was already detached we have no way to remove it
846
+ ds = VCenterDriver::Datastore.new_from_ref(ds_ref, vi_client)
847
+
848
+ search_params = ds.get_search_params(ds['name'],
849
+ File.dirname(img_path),
850
+ File.basename(img_path))
851
+
852
+ # Perform search task and return results
853
+ search_task = ds['browser']
854
+ .SearchDatastoreSubFolders_Task(search_params)
855
+ search_task.wait_for_completion
856
+
857
+ ds.delete_virtual_disk(img_path)
858
+ img_dir = File.dirname(img_path)
859
+ ds.rm_directory(img_dir) if ds.dir_empty?(img_dir)
860
+ rescue StandardError => e
861
+ if !e.message.start_with?('FileNotFound')
862
+ raise e.message # Ignore FileNotFound
863
+ end
864
+ end
865
+ end
866
+
867
+ end
868
+ # class Datastore
869
+
870
+ ##########################################################################
871
+ # Class DsImporter
872
+ ##########################################################################
873
+ class DsImporter < VCenterDriver::VcImporter
874
+
875
+ def initialize(one_client, vi_client)
876
+ super(one_client, vi_client)
877
+ @one_class = OpenNebula::Datastore
878
+ end
879
+
880
+ def get_list(_args = {})
881
+ dc_folder = VCenterDriver::DatacenterFolder.new(@vi_client)
882
+
883
+ # one pool creation
884
+ dpool = VCenterDriver::VIHelper
885
+ .one_pool(
886
+ OpenNebula::DatastorePool,
887
+ false
888
+ )
889
+ if dpool.respond_to?(:message)
890
+ raise "Could not get OpenNebula DatastorePool: #{dpool.message}"
891
+ end
892
+
893
+ # OpenNebula's HostPool
894
+ hpool = VCenterDriver::VIHelper
895
+ .one_pool(
896
+ OpenNebula::HostPool,
897
+ false
898
+ )
899
+ if hpool.respond_to?(:message)
900
+ raise "Could not get OpenNebula HostPool: #{hpool.message}"
901
+ end
902
+
903
+ rs = dc_folder
904
+ .get_unimported_datastores(
905
+ dpool,
906
+ @vi_client.vc_name,
907
+ hpool
908
+ )
909
+ @list = rs
910
+ end
911
+
912
+ def add_cluster(cid, eid)
913
+ one_cluster = @info[:clusters][cid]
914
+ raise 'no cluster defined' unless one_cluster
915
+
916
+ one_cluster.adddatastore(eid)
917
+ end
918
+
919
+ def remove_default(id)
920
+ cid = 0
921
+ @info[:clusters][cid] ||= VCenterDriver::VIHelper
922
+ .one_item(
923
+ OpenNebula::Cluster,
924
+ cid.to_s,
925
+ false
926
+ )
927
+ @info[:clusters][cid].deldatastore(id.to_i)
928
+ end
929
+
930
+ def import(selected)
931
+ inner = lambda {|object, auth|
932
+ one = ''
933
+ one << "VCENTER_HOST=\"#{auth[:host]}\"\n"
934
+
935
+ object.update(one, true)
936
+ }
937
+
938
+ opts = @info[selected[:ref]][:opts]
939
+
940
+ # Datastore info comes in a pair (SYS, IMG)
941
+ pair = selected[:ds]
942
+ clusters = selected[:cluster]
943
+ if opts && opts['selected_clusters']
944
+ clusters = opts['selected_clusters'].each.map(&:to_i)
945
+ end
946
+
947
+ res = { :id => [], :name => selected[:simple_name] }
948
+ @info[:rollback] = []
949
+ pair.each do |ds|
950
+ create(ds[:one]) do |one_object, id|
951
+ res[:id] << id
952
+
953
+ add_clusters(id, clusters)
954
+
955
+ inner.call(
956
+ one_object,
957
+ @vi_client.host_credentials(
958
+ @one_client
959
+ )
960
+ )
961
+ end
962
+ end
963
+
964
+ res
965
+ end
966
+
967
+ end
968
+
969
+ ##########################################################################
970
+ # Class ImageImporter
971
+ ##########################################################################
972
+ class ImageImporter < VCenterDriver::VcImporter
973
+
974
+ def initialize(one_client, vi_client)
975
+ super(one_client, vi_client)
976
+ @one_class = OpenNebula::Image
977
+ end
978
+
979
+ def get_list(args = {})
980
+ ds_ref = args[:datastore][:ds_ref]
981
+ one_ds = args[:datastore][:one_item]
982
+
983
+ unless ds_ref
984
+ raise "can't retrieve ref info from openNebula datastore"
985
+ end
986
+
987
+ datastore = VCenterDriver::Datastore
988
+ .new_from_ref(ds_ref, @vi_client)
989
+
990
+ ds = datastore.tap do |spawn|
991
+ spawn.one_item = one_ds
992
+ end
993
+
994
+ vc_uuid = @vi_client.vim.serviceContent.about.instanceUuid
995
+ one_ds_instance_id = one_ds['TEMPLATE/VCENTER_INSTANCE_ID']
996
+
997
+ if one_ds_instance_id != vc_uuid
998
+ raise 'Datastore is not in the same vCenter \
999
+ instance provided in credentials'
1000
+ end
1001
+
1002
+ @list = ds.all_images
1003
+ end
1004
+
1005
+ def import(selected)
1006
+ resource = VCenterDriver::VIHelper.new_one_item(@one_class)
1007
+ message = 'Error creating the OpenNebula resource'
1008
+ info = selected[:one]
1009
+ dsid = selected[:dsid].to_i
1010
+ name = selected[:name]
1011
+
1012
+ rc = resource.allocate(info, dsid, false)
1013
+ VCenterDriver::VIHelper.check_error(rc, message)
1014
+
1015
+ resource.info
1016
+ id = resource['ID']
1017
+ @rollback << Raction.new(resource, :delete)
1018
+
1019
+ { :id => [id], :name => name }
1020
+ end
1021
+
1022
+ end
1023
+
1024
+ end
1025
+ # module VCenterDriver