opennebula 5.12.7 → 6.0.0

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 +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 +572 -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 +19 -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 +5 -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 +129 -16
@@ -0,0 +1,586 @@
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
+ # Module to decorate MarketApp class with additional helpers not directly
18
+ # exposed through the OpenNebula XMLRPC API. The extensions include
19
+ # - export helper that creates OpenNebula related objects from a given app.
20
+ #
21
+ # rubocop:disable Style/ClassAndModuleChildren
22
+ module OpenNebula::MarketPlaceAppExt
23
+
24
+ include OpenNebula
25
+
26
+ def self.extend_object(obj)
27
+ if !obj.is_a?(OpenNebula::MarketPlaceApp)
28
+ raise StandardError, "Cannot extended #{obj.class} " \
29
+ 'with MarketPlaceAppExt'
30
+ end
31
+
32
+ class << obj
33
+
34
+ ####################################################################
35
+ # Public extended interface
36
+ ####################################################################
37
+ # Exports this app to a suitable OpenNebula object
38
+ # @param appid [Integer] id of the marketplace app
39
+ # @param options [Hash] to control the export behavior
40
+ # dsid [Integer] datastore to save images
41
+ # f_dsid [Integer] file datastore to save kernels
42
+ # name [String] of the new object
43
+ # vmtemplate_name [String] name for the VM Template, if the App
44
+ # has one complex [Boolean] true to only create image
45
+ #
46
+ # @return [Hash, OpenNebula::Error] with the ID and type of
47
+ # the created objects. Instead of an ID, the array may
48
+ # contain OpenNebula::Error with specific object creation errors
49
+ # {
50
+ # :vm => [ vm ids/OpenNebula::Error ],
51
+ # :vmtemplate => [ vmtemplates ids/OpenNebula::Error ],
52
+ # :image => [ vm ids/OpenNebula::Error ]
53
+ # }
54
+ # rc_info = {
55
+ # :image => [],
56
+ # :image_type => nil,
57
+ # :vmtemplate => [],
58
+ # :service_template => []
59
+ # }
60
+ def export(options = {})
61
+ # Get App information and check for errors
62
+ rc = info
63
+ return rc if OpenNebula.is_error?(rc)
64
+ return Error.new('App is not READY') if state_str != 'READY'
65
+
66
+ if options[:dsid].nil?
67
+ return Error.new('Missing datastore id')
68
+ end
69
+
70
+ if options[:name].nil?
71
+ return Error.new('Missing name to export app')
72
+ end
73
+
74
+ case type_str
75
+ when 'IMAGE'
76
+ export_image(options)
77
+ when 'VMTEMPLATE'
78
+ options[:notemplate] = true
79
+
80
+ export_vm_template(options)
81
+ when 'SERVICE_TEMPLATE'
82
+ export_service_template(options)
83
+ else
84
+ Error.new("App type #{type_str} not supported")
85
+ end
86
+ end
87
+
88
+ ####################################################################
89
+ # Private methods
90
+ ####################################################################
91
+ private
92
+
93
+ # Exports an OpenNebula Image from this marketplace app
94
+ # @param options to export the image
95
+ # :vmtemplate_name [String] name of new image and template
96
+ # :url_args [String] optional URL arguments
97
+ # :dsid [String] Datastore id to create the image
98
+ # :f_dsid [String] Files Datastore id
99
+ # :notemplate [Bool] if true do not create vm_template (if any)
100
+ # :template [Integer] Template id to use with image
101
+ # :default_template [String] Default template id for vCenter
102
+ # @return [Hash]
103
+ # :image [Array] of the new image
104
+ # :image_type [String] of the new image (CONTEXT, KERNEL, CDROM)
105
+ # :vmtemplate [Array] of the associated vm_template (if any)
106
+ # :service_template [Array] empty []
107
+ def export_image(options)
108
+ rc_info = {
109
+ :image => [],
110
+ :image_type => nil,
111
+ :vmtemplate => [],
112
+ :service_template => []
113
+ }
114
+
115
+ #---------------------------------------------------------------
116
+ # Create the image template
117
+ #---------------------------------------------------------------
118
+ tmpl = ''
119
+
120
+ if self['APPTEMPLATE64']
121
+ tmpl = Base64.decode64(self['APPTEMPLATE64'])
122
+ end
123
+
124
+ tmpl << <<-EOT
125
+ NAME = "#{options[:name]}"
126
+ FROM_APP = "#{self['ID']}"
127
+ EOT
128
+
129
+ if options[:url_args]
130
+ tmpl << "URL_ARGS=\"#{options[:url_args]}\"\n"
131
+ end
132
+
133
+ #---------------------------------------------------------------
134
+ # Kernel or context images stored in a files datastore
135
+ #---------------------------------------------------------------
136
+ dsid = options[:dsid]
137
+
138
+ if tmpl.match(/^\s*TYPE\s*=\s*KERNEL|CONTEXT\s/i)
139
+ if options[:f_dsid]
140
+ dsid = options[:f_dsid]
141
+ else
142
+ dspool = DatastorePool.new(@client)
143
+
144
+ rc = dspool.info
145
+ return { :image => [rc] } if OpenNebula.is_error?(rc)
146
+
147
+ file_ds = dspool.select {|d| d['TYPE'].to_i == 2 }
148
+ dsid = file_ds.first['ID'].to_i
149
+ end
150
+ end
151
+
152
+ ds = OpenNebula::Datastore.new_with_id(dsid, @client)
153
+ rc = ds.info
154
+
155
+ is_vcenter = !OpenNebula.is_error?(rc) &&
156
+ (ds['TEMPLATE/DRIVER'] == 'vcenter')
157
+
158
+ if is_vcenter && options[:template].nil?
159
+ options = update_options_with_template(options)
160
+ end
161
+
162
+ #---------------------------------------------------------------
163
+ # Allocate the image in OpenNebula
164
+ #---------------------------------------------------------------
165
+ image = Image.new(Image.build_xml, @client)
166
+ rc = image.allocate(tmpl, dsid)
167
+
168
+ if OpenNebula.is_error?(rc)
169
+ rc_info[:image] = [rc]
170
+
171
+ return rc_info
172
+ end
173
+
174
+ image.info
175
+
176
+ image.delete_element('TEMPLATE/FORMAT')
177
+ image.delete_element('TEMPLATE/DRIVER')
178
+ image.delete_element('TEMPLATE/DEV_PREFIX') if is_vcenter
179
+
180
+ image.update(image.template_xml)
181
+
182
+ rc_info[:image] = [image.id]
183
+ rc_info[:image_type] = image.type_str
184
+
185
+ #---------------------------------------------------------------
186
+ # Created an associated VMTemplate if needed
187
+ #---------------------------------------------------------------
188
+ if (self['TEMPLATE/VMTEMPLATE64'].nil? && !is_vcenter) ||
189
+ options[:notemplate] ||
190
+ options[:template] == -1
191
+ return rc_info
192
+ end
193
+
194
+ if !options[:template].nil?
195
+ template_id = options[:template]
196
+
197
+ if template_id < 0
198
+ raise 'Invalid option, template_id must be a valid ID'
199
+ end
200
+
201
+ template = Template.new_with_id(template_id, @client)
202
+
203
+ vmtpl_id = template.clone(
204
+ options[:vmtemplate_name] || options[:name]
205
+ )
206
+
207
+ tmpl << <<-EOT
208
+
209
+ NAME = "#{options[:vmtemplate_name] || options[:name]}"
210
+ DISK = [ IMAGE_ID = "#{image.id}" ]
211
+ EOT
212
+
213
+ template = Template.new_with_id(vmtpl_id, @client)
214
+
215
+ template.update(tmpl, true)
216
+
217
+ rc_info[:vmtemplate] = [vmtpl_id]
218
+ else
219
+ tmpl = Base64.decode64(self['TEMPLATE/VMTEMPLATE64'])
220
+ tmpl << <<-EOT
221
+
222
+ NAME = "#{options[:vmtemplate_name] || options[:name]}"
223
+ DISK = [ IMAGE_ID = "#{image.id}" ]
224
+ EOT
225
+
226
+ vmtpl = Template.new(Template.build_xml, @client)
227
+
228
+ rc = vmtpl.allocate(tmpl)
229
+ rc = vmtpl.id unless OpenNebula.is_error?(rc)
230
+
231
+ rc_info[:vmtemplate] = [rc]
232
+ end
233
+
234
+ rc_info
235
+ end
236
+
237
+ # Export complex template from marketplace
238
+ #
239
+ # @param name [Hash] Export options
240
+ # @param ds_id [Integer] Datastore ID to export child apps
241
+ def export_vm_template(options)
242
+ rc = {
243
+ :image => [],
244
+ :image_type => nil,
245
+ :vmtemplate => [],
246
+ :service_template => []
247
+ }
248
+
249
+ vmtmpl = export_recursive('//DISK', options) do |disks|
250
+ create_vm_template(options, disks)
251
+ end
252
+
253
+ rc[:vmtemplate] = [vmtmpl[0]]
254
+
255
+ unless OpenNebula.is_error?(vmtmpl[0])
256
+ rc[:image] += vmtmpl[1]
257
+ rc[:vmtemplate] += vmtmpl[2] unless options[:notemplate]
258
+ end
259
+
260
+ rc
261
+ end
262
+
263
+ # Export service template from marketplace
264
+ #
265
+ # @param name [Hash] Export options
266
+ # @param ds_id [Integer] Datastore ID to export child apps
267
+ def export_service_template(options)
268
+ rc = {
269
+ :image => [],
270
+ :image_type => nil,
271
+ :vmtemplate => [],
272
+ :service_template => []
273
+ }
274
+
275
+ stmpl = export_recursive('//ROLE', options) do |roles|
276
+ create_service_template(options, roles)
277
+ end
278
+
279
+ rc[:service_template] = [stmpl[0]]
280
+
281
+ unless OpenNebula.is_error?(stmpl[0])
282
+ rc[:image] += stmpl[1]
283
+ rc[:vmtemplate] += stmpl[2]
284
+ end
285
+
286
+ rc
287
+ end
288
+
289
+ def update_options_with_template(options, _validate = false)
290
+ vcenterrc_path =
291
+ "#{VAR_LOCATION}/remotes/etc/vmm/vcenter/vcenterrc"
292
+
293
+ if File.file?(vcenterrc_path)
294
+ config_vcenter = YAML.load_file(vcenterrc_path)
295
+
296
+ if config_vcenter.key?(:default_template)
297
+ options[:template] =
298
+ config_vcenter[:default_template]
299
+
300
+ options
301
+ else
302
+ raise "Couldn't find default_template " \
303
+ 'configuration in vcenterrc conf ' \
304
+ 'file. Please use the --template ' \
305
+ 'file to define a VM Template ID if ' \
306
+ 'needed or add default_template to' \
307
+ ' vcenterrc conf file'
308
+ end
309
+ else
310
+ raise "Couldn't find vcenterrc conf file. " \
311
+ ' Please use the --template file to define' \
312
+ ' a VM Template ID if needed.'
313
+ end
314
+ end
315
+
316
+ # Creates a VM template based on the APPTEMPLATE64 attribute
317
+ # @param [Hash] options
318
+ # :export_name [String] name of the vm template
319
+ # @param [Hash] disks exported disks from related apps.
320
+ # As returned by the export_image method
321
+ #
322
+ # @return [Integer, OpenNebula::Error] template id or error
323
+ def create_vm_template(options, disks)
324
+ dsid = options[:dsid]
325
+ ds = OpenNebula::Datastore.new_with_id(dsid, @client)
326
+ rc = ds.info
327
+
328
+ is_vcenter =
329
+ !OpenNebula.is_error?(rc) &&
330
+ (ds['TEMPLATE/DRIVER'] == 'vcenter')
331
+
332
+ if is_vcenter
333
+ if options[:template].nil?
334
+ options = update_options_with_template(options)
335
+ end
336
+
337
+ template_id = options[:template]
338
+
339
+ if template_id < 0
340
+ return
341
+ end
342
+
343
+ template = Template.new_with_id(template_id, @client)
344
+
345
+ vmtpl_id = template.clone(
346
+ options[:vmtemplate_name] || options[:name]
347
+ )
348
+
349
+ vmtpl = Template.new_with_id(vmtpl_id, @client)
350
+ rc = vmtpl.info
351
+ else
352
+ # ----------------------------------------------------------
353
+ # Allocate Template
354
+ # ----------------------------------------------------------
355
+ if self['TEMPLATE/APPTEMPLATE64'].nil?
356
+ return Error.new(
357
+ "Missing APPTEMPLATE64 for App #{id}"
358
+ )
359
+ end
360
+
361
+ tmpl = Base64.decode64(self['TEMPLATE/APPTEMPLATE64'])
362
+
363
+ tmpl << "\nNAME=\"#{options[:name]}\"\n"
364
+
365
+ vmtpl = Template.new(Template.build_xml, @client)
366
+ rc = vmtpl.allocate(tmpl)
367
+ end
368
+
369
+ return rc if OpenNebula.is_error?(rc)
370
+
371
+ # --------------------------------------------------------------
372
+ # Update disk information in template
373
+ # --------------------------------------------------------------
374
+ vmtpl.info
375
+
376
+ context = []
377
+
378
+ disks.each do |_app, disk|
379
+ id = disk[:image]
380
+
381
+ case disk[:image_type]
382
+ when 'IMAGE', 'OS', 'DATABLOCK', 'CDROM'
383
+ vmtpl.add_element('TEMPLATE',
384
+ 'DISK' => { 'IMAGE_ID' => id.first })
385
+ when 'CONTEXT'
386
+ context << "$FILE[IMAGE_ID=\"#{id}\"]"
387
+ when 'KERNEL'
388
+ if !vmtpl.has_elements?('TEMPLATE/OS')
389
+ vmtpl.add_element('TEMPLATE', 'OS'=>{})
390
+ end
391
+
392
+ vmtpl.add_element(
393
+ 'TEMPLATE/OS',
394
+ 'KERNEL_DS' => "$FILE[IMAGE_ID=#{id}]"
395
+ )
396
+ end
397
+ end
398
+
399
+ if !context.empty?
400
+ if !vmtpl.has_elements?('TEMPLATE/CONTEXT')
401
+ vmtpl.add_element('TEMPLATE', 'CONTEXT' => {})
402
+ end
403
+
404
+ vmtpl.add_element('TEMPLATE/CONTEXT',
405
+ 'FILES_DS' => context.join(' '))
406
+ end
407
+
408
+ # --------------------------------------------------------------
409
+ # Update template information in OpenNebula
410
+ # --------------------------------------------------------------
411
+ vmtpl.update(vmtpl.template_xml)
412
+
413
+ vmtpl.id
414
+ end
415
+
416
+ # Creates a Service template based on the VMTEMPLATE64 attribute
417
+ # @return [Integer, OpenNebula::Error] template id or error
418
+ def create_service_template(options, roles)
419
+ # --------------------------------------------------------------
420
+ # Allocate Template
421
+ # --------------------------------------------------------------
422
+ if self['TEMPLATE/APPTEMPLATE64'].nil?
423
+ return Error.new("Missing APPTEMPLATE64 for App #{id}")
424
+ end
425
+
426
+ tmpl = Base64.decode64(self['TEMPLATE/APPTEMPLATE64'])
427
+ tmpl = JSON.parse(tmpl)
428
+
429
+ tmpl['name'] = options[:name]
430
+
431
+ # --------------------------------------------------------------
432
+ # Append template IDs to each role information
433
+ # --------------------------------------------------------------
434
+ tmpl['roles'].each do |role|
435
+ t_id = roles.find {|_, v| v[:names].include?(role['name']) }
436
+
437
+ if t_id.nil? || t_id[1].nil? || t_id[1][:vmtemplate].nil?
438
+ next
439
+ end
440
+
441
+ role['vm_template'] = nil
442
+ role['vm_template'] = t_id[1][:vmtemplate][0]
443
+ end
444
+
445
+ # --------------------------------------------------------------
446
+ # Allocate Service template in OpenNebula
447
+ # --------------------------------------------------------------
448
+ stmpl = ServiceTemplate.new(ServiceTemplate.build_xml, @client)
449
+
450
+ rc = stmpl.allocate(tmpl.to_json)
451
+ rc = stmpl.id unless OpenNebula.is_error?(rc)
452
+
453
+ rc
454
+ end
455
+
456
+ # Export complex template from marketplace
457
+ #
458
+ # @param xpath [String] Xpath to search childs
459
+ # @param options [Hash] Export options
460
+ # @param ds_id [Integer] Datastore ID to export child apps
461
+ def export_recursive(xpath, options)
462
+ # Get marketplace apps pool to find roles apps
463
+ pool = OpenNebula::MarketPlaceAppPool.new(@client)
464
+ rc = pool.info
465
+
466
+ return rc if OpenNebula.is_error?(rc)
467
+
468
+ # Apps that have been already exported
469
+ #
470
+ # app_name =>
471
+ # :vmtempalte = exported template ID
472
+ # :image = exported image ID
473
+ # :image_type = type (KERNEL, CONTEXT...) of image
474
+ # :names = [name_a, name_b, ...]
475
+ exported = {}
476
+ idx = 0
477
+
478
+ # Store IDs of created resources
479
+ images = []
480
+ templates = []
481
+
482
+ # Iterate over all childs
483
+ rc = retrieve_xmlelements(xpath).each do |obj|
484
+ # Get name and app information
485
+ obj_name = obj['NAME']
486
+ app = obj['APP']
487
+
488
+ # If the app was already exported, do not export it again
489
+ if exported[app]
490
+ exported[app][:names] << obj_name
491
+ next
492
+ end
493
+
494
+ # Find app in pool
495
+ obj = pool.find {|p| p['NAME'] == app }
496
+
497
+ break Error.new("App `#{app}` not found") unless obj
498
+
499
+ obj.extend(MarketPlaceAppExt)
500
+
501
+ rc = obj.export(
502
+ :dsid => options[:dsid],
503
+ :name => "#{options[:name]}-#{idx}",
504
+ :notemplate => options[:notemplate]
505
+ )
506
+
507
+ image = rc[:image].first if rc[:image]
508
+ vmtemplate = rc[:vmtemplate].first if rc[:vmtemplate]
509
+
510
+ break image if OpenNebula.is_error?(image)
511
+ break vmtemplate if OpenNebula.is_error?(vmtemplate)
512
+
513
+ idx += 1
514
+
515
+ # Update exported hash with information
516
+ exported[app] = rc
517
+ exported[app][:names] = [obj_name]
518
+
519
+ # Add IDs to return object
520
+ images << image
521
+ templates << vmtemplate
522
+ end
523
+
524
+ if block_given? && !OpenNebula.is_error?(rc)
525
+ rc = yield(exported)
526
+ end
527
+
528
+ if OpenNebula.is_error?(rc)
529
+ rc_rbck = rollback_export(exported, xpath != '//DISK')
530
+
531
+ return rc_rbck if OpenNebula.is_error?(rc_rbck)
532
+ end
533
+
534
+ [rc, images, templates]
535
+ end
536
+
537
+ # Delete templates/images in case something went wrong
538
+ #
539
+ # @param exported [Hash] Exported apps information
540
+ # @param template [Boolean] True to delete VM Template
541
+ # False to delete images
542
+ #
543
+ # @return [nil | OpenNebula::Error]
544
+ def rollback_export(exported, is_template)
545
+ ret = ''
546
+
547
+ if is_template
548
+ obj_factory = lambda {|v|
549
+ id = v[:vmtemplate].first
550
+ [Template.new_with_id(id, @client),
551
+ "Error deleting template #{id}"]
552
+ }
553
+
554
+ delete_method = 'delete(true)'
555
+ else
556
+ obj_factory = lambda {|v|
557
+ id = v[:image].first
558
+ [Image.new_with_id(id, @client),
559
+ "Error deleting image #{id}"]
560
+ }
561
+
562
+ delete_method = 'delete'
563
+ end
564
+
565
+ exported.each do |_, v|
566
+ obj, err_msg = obj_factory.call(v)
567
+
568
+ next unless OpenNebula.is_error?(obj.send(delete_method))
569
+
570
+ ret << err_msg
571
+ end
572
+
573
+ if ret.empty?
574
+ nil
575
+ else
576
+ Error.new(ret) unless ret.empty?
577
+ end
578
+ end
579
+
580
+ end
581
+
582
+ super
583
+ end
584
+
585
+ end
586
+ # rubocop:enable Style/ClassAndModuleChildren