opennebula 5.12.9 → 6.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  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 +1257 -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.rb +32 -0
  17. data/lib/models/role.rb +1126 -0
  18. data/lib/models/service.rb +709 -0
  19. data/lib/network.rb +699 -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.rb +5 -2
  38. data/lib/opennebula/acl.rb +1 -1
  39. data/lib/opennebula/acl_pool.rb +1 -1
  40. data/lib/opennebula/client.rb +10 -2
  41. data/lib/opennebula/cluster.rb +1 -1
  42. data/lib/opennebula/cluster_pool.rb +1 -1
  43. data/lib/opennebula/datastore.rb +1 -1
  44. data/lib/opennebula/datastore_pool.rb +1 -1
  45. data/lib/opennebula/document.rb +8 -29
  46. data/lib/opennebula/document_json.rb +42 -12
  47. data/lib/opennebula/document_pool.rb +1 -1
  48. data/lib/opennebula/document_pool_json.rb +1 -1
  49. data/lib/opennebula/error.rb +4 -1
  50. data/lib/opennebula/flow.rb +23 -0
  51. data/lib/opennebula/flow/grammar.rb +1195 -0
  52. data/lib/opennebula/flow/service_pool.rb +190 -0
  53. data/lib/opennebula/flow/service_template.rb +608 -0
  54. data/lib/opennebula/flow/service_template_ext.rb +84 -0
  55. data/lib/opennebula/flow/service_template_pool.rb +32 -0
  56. data/lib/opennebula/flow/validator.rb +499 -0
  57. data/lib/opennebula/group.rb +1 -1
  58. data/lib/opennebula/group_pool.rb +1 -1
  59. data/lib/opennebula/hook.rb +5 -12
  60. data/lib/opennebula/hook_log.rb +1 -1
  61. data/lib/opennebula/hook_pool.rb +1 -1
  62. data/lib/opennebula/host.rb +1 -1
  63. data/lib/opennebula/host_pool.rb +1 -1
  64. data/lib/opennebula/image.rb +19 -14
  65. data/lib/opennebula/image_pool.rb +1 -1
  66. data/lib/opennebula/ldap_auth.rb +1 -1
  67. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  68. data/lib/opennebula/lockable_ext.rb +163 -0
  69. data/lib/opennebula/marketplace.rb +1 -1
  70. data/lib/opennebula/marketplace_pool.rb +1 -1
  71. data/lib/opennebula/marketplaceapp.rb +9 -119
  72. data/lib/opennebula/marketplaceapp_ext.rb +586 -0
  73. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  74. data/lib/opennebula/oneflow_client.rb +4 -3
  75. data/lib/opennebula/pool.rb +6 -3
  76. data/lib/opennebula/pool_element.rb +1 -1
  77. data/lib/opennebula/security_group.rb +1 -1
  78. data/lib/opennebula/security_group_pool.rb +1 -1
  79. data/lib/opennebula/server_cipher_auth.rb +1 -1
  80. data/lib/opennebula/server_x509_auth.rb +1 -1
  81. data/lib/opennebula/ssh_auth.rb +1 -1
  82. data/lib/opennebula/system.rb +1 -1
  83. data/lib/opennebula/template.rb +4 -13
  84. data/lib/opennebula/template_ext.rb +342 -0
  85. data/lib/opennebula/template_pool.rb +1 -1
  86. data/lib/opennebula/user.rb +26 -2
  87. data/lib/opennebula/user_pool.rb +1 -1
  88. data/lib/opennebula/utils.rb +1 -1
  89. data/lib/opennebula/vdc.rb +1 -1
  90. data/lib/opennebula/vdc_pool.rb +1 -1
  91. data/lib/opennebula/virtual_machine.rb +26 -206
  92. data/lib/opennebula/virtual_machine_ext.rb +469 -0
  93. data/lib/opennebula/virtual_machine_pool.rb +1 -1
  94. data/lib/opennebula/virtual_network.rb +4 -10
  95. data/lib/opennebula/virtual_network_pool.rb +1 -1
  96. data/lib/opennebula/virtual_router.rb +4 -12
  97. data/lib/opennebula/virtual_router_pool.rb +1 -1
  98. data/lib/opennebula/vm_group.rb +4 -11
  99. data/lib/opennebula/vm_group_pool.rb +1 -1
  100. data/lib/opennebula/vntemplate.rb +4 -13
  101. data/lib/opennebula/vntemplate_pool.rb +1 -1
  102. data/lib/opennebula/wait_ext.rb +257 -0
  103. data/lib/opennebula/x509_auth.rb +1 -1
  104. data/lib/opennebula/xml_element.rb +1 -1
  105. data/lib/opennebula/xml_pool.rb +1 -1
  106. data/lib/opennebula/xml_utils.rb +1 -1
  107. data/lib/opennebula/zone.rb +1 -1
  108. data/lib/opennebula/zone_pool.rb +1 -1
  109. data/lib/rest_client.rb +201 -0
  110. data/lib/scripts_common.rb +183 -0
  111. data/lib/transport_zone.rb +43 -0
  112. data/lib/vcenter_driver.rb +13 -12
  113. data/lib/vcenter_importer.rb +616 -0
  114. data/lib/vi_client.rb +281 -0
  115. data/lib/vi_helper.rb +313 -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 +1963 -0
  125. data/lib/vmm_importer.rb +121 -0
  126. metadata +140 -27
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.downcase.start_with?('content-length')
657
+ end[0].downcase.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