opennebula 6.10.3 → 6.99.85.pre

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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cloud/CloudClient.rb +3 -3
  3. data/lib/models/role.rb +349 -823
  4. data/lib/models/service.rb +156 -80
  5. data/lib/models/vmrole.rb +703 -0
  6. data/lib/models/vrrole.rb +284 -0
  7. data/lib/models.rb +3 -1
  8. data/lib/opennebula/acl.rb +1 -1
  9. data/lib/opennebula/acl_pool.rb +1 -1
  10. data/lib/opennebula/backupjob.rb +1 -1
  11. data/lib/opennebula/backupjob_pool.rb +1 -1
  12. data/lib/opennebula/client.rb +1 -1
  13. data/lib/opennebula/cluster.rb +45 -2
  14. data/lib/opennebula/cluster_pool.rb +1 -1
  15. data/lib/opennebula/datastore.rb +1 -1
  16. data/lib/opennebula/datastore_pool.rb +1 -1
  17. data/lib/opennebula/document.rb +1 -1
  18. data/lib/opennebula/document_json.rb +1 -1
  19. data/lib/opennebula/document_pool.rb +1 -1
  20. data/lib/opennebula/document_pool_json.rb +1 -1
  21. data/lib/opennebula/error.rb +1 -1
  22. data/lib/opennebula/flow/grammar.rb +1 -1
  23. data/lib/opennebula/flow/service_pool.rb +1 -1
  24. data/lib/opennebula/flow/service_template.rb +353 -97
  25. data/lib/opennebula/flow/service_template_ext.rb +3 -3
  26. data/lib/opennebula/flow/service_template_pool.rb +1 -1
  27. data/lib/opennebula/flow/validator.rb +458 -410
  28. data/lib/opennebula/flow.rb +1 -1
  29. data/lib/opennebula/group.rb +1 -1
  30. data/lib/opennebula/group_pool.rb +1 -1
  31. data/lib/opennebula/hook.rb +1 -1
  32. data/lib/opennebula/hook_log.rb +1 -1
  33. data/lib/opennebula/hook_pool.rb +1 -1
  34. data/lib/opennebula/host.rb +1 -60
  35. data/lib/opennebula/host_pool.rb +1 -1
  36. data/lib/opennebula/image.rb +1 -1
  37. data/lib/opennebula/image_pool.rb +1 -1
  38. data/lib/opennebula/ldap_auth.rb +1 -1
  39. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  40. data/lib/opennebula/lockable_ext.rb +1 -1
  41. data/lib/opennebula/marketplace.rb +1 -1
  42. data/lib/opennebula/marketplace_pool.rb +1 -1
  43. data/lib/opennebula/marketplaceapp.rb +1 -1
  44. data/lib/opennebula/marketplaceapp_ext.rb +14 -211
  45. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  46. data/lib/opennebula/oneflow_client.rb +11 -9
  47. data/lib/opennebula/pool.rb +1 -1
  48. data/lib/opennebula/pool_element.rb +1 -1
  49. data/lib/opennebula/security_group.rb +1 -1
  50. data/lib/opennebula/security_group_pool.rb +1 -1
  51. data/lib/opennebula/server_cipher_auth.rb +1 -1
  52. data/lib/opennebula/server_x509_auth.rb +1 -1
  53. data/lib/opennebula/ssh_auth.rb +1 -1
  54. data/lib/opennebula/system.rb +1 -1
  55. data/lib/opennebula/template.rb +1 -1
  56. data/lib/opennebula/template_ext.rb +1 -1
  57. data/lib/opennebula/template_pool.rb +1 -1
  58. data/lib/opennebula/user.rb +1 -1
  59. data/lib/opennebula/user_pool.rb +1 -1
  60. data/lib/opennebula/utils.rb +2 -2
  61. data/lib/opennebula/vdc.rb +1 -1
  62. data/lib/opennebula/vdc_pool.rb +1 -1
  63. data/lib/opennebula/virtual_machine.rb +3 -12
  64. data/lib/opennebula/virtual_machine_ext.rb +2 -31
  65. data/lib/opennebula/virtual_machine_pool.rb +1 -1
  66. data/lib/opennebula/virtual_network.rb +1 -1
  67. data/lib/opennebula/virtual_network_pool.rb +1 -1
  68. data/lib/opennebula/virtual_router.rb +1 -1
  69. data/lib/opennebula/virtual_router_pool.rb +1 -1
  70. data/lib/opennebula/vm_group.rb +1 -1
  71. data/lib/opennebula/vm_group_pool.rb +1 -1
  72. data/lib/opennebula/vntemplate.rb +1 -1
  73. data/lib/opennebula/vntemplate_pool.rb +1 -1
  74. data/lib/opennebula/wait_ext.rb +1 -1
  75. data/lib/opennebula/x509_auth.rb +1 -1
  76. data/lib/opennebula/xml_element.rb +2 -2
  77. data/lib/opennebula/xml_pool.rb +1 -1
  78. data/lib/opennebula/xml_utils.rb +1 -1
  79. data/lib/opennebula/zone.rb +1 -1
  80. data/lib/opennebula/zone_pool.rb +1 -1
  81. data/lib/opennebula.rb +2 -2
  82. metadata +6 -67
  83. data/lib/ActionManager.rb +0 -280
  84. data/lib/CommandManager.rb +0 -328
  85. data/lib/DriverExecHelper.rb +0 -213
  86. data/lib/HostSyncManager.rb +0 -111
  87. data/lib/OpenNebulaDriver.rb +0 -223
  88. data/lib/VirtualMachineDriver.rb +0 -404
  89. data/lib/datacenter.rb +0 -1319
  90. data/lib/datastore.rb +0 -1049
  91. data/lib/distributed_firewall.rb +0 -293
  92. data/lib/file_helper.rb +0 -374
  93. data/lib/host.rb +0 -1518
  94. data/lib/logical_port.rb +0 -50
  95. data/lib/logical_switch.rb +0 -77
  96. data/lib/memoize.rb +0 -74
  97. data/lib/network.rb +0 -705
  98. data/lib/nsx_client.rb +0 -157
  99. data/lib/nsx_component.rb +0 -28
  100. data/lib/nsx_constants.rb +0 -162
  101. data/lib/nsx_driver.rb +0 -91
  102. data/lib/nsx_error.rb +0 -77
  103. data/lib/nsx_rule.rb +0 -206
  104. data/lib/nsxt_client.rb +0 -189
  105. data/lib/nsxt_dfw.rb +0 -196
  106. data/lib/nsxt_logical_port.rb +0 -94
  107. data/lib/nsxt_rule.rb +0 -188
  108. data/lib/nsxt_tz.rb +0 -38
  109. data/lib/nsxv_client.rb +0 -189
  110. data/lib/nsxv_dfw.rb +0 -202
  111. data/lib/nsxv_logical_port.rb +0 -107
  112. data/lib/nsxv_rule.rb +0 -172
  113. data/lib/nsxv_tz.rb +0 -41
  114. data/lib/opaque_network.rb +0 -134
  115. data/lib/rest_client.rb +0 -191
  116. data/lib/scripts_common.rb +0 -176
  117. data/lib/transport_zone.rb +0 -43
  118. data/lib/vcenter_driver.rb +0 -152
  119. data/lib/vcenter_importer.rb +0 -626
  120. data/lib/vi_client.rb +0 -273
  121. data/lib/vi_helper.rb +0 -328
  122. data/lib/virtual_machine.rb +0 -3574
  123. data/lib/virtual_wire.rb +0 -158
  124. data/lib/vm_device.rb +0 -80
  125. data/lib/vm_disk.rb +0 -202
  126. data/lib/vm_folder.rb +0 -69
  127. data/lib/vm_helper.rb +0 -30
  128. data/lib/vm_monitor.rb +0 -305
  129. data/lib/vm_nic.rb +0 -70
  130. data/lib/vm_template.rb +0 -2112
  131. data/lib/vmm_importer.rb +0 -165
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2024, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2025, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -21,7 +21,7 @@ module OpenNebula
21
21
  # Service Template
22
22
  class ServiceTemplate < DocumentJSON
23
23
 
24
- ROLE_SCHEMA = {
24
+ VM_ROLE_SCHEMA = {
25
25
  :type => :object,
26
26
  :properties => {
27
27
  'name' => {
@@ -29,25 +29,33 @@ module OpenNebula
29
29
  :required => true,
30
30
  :regex => /^\w+$/
31
31
  },
32
+ 'type' => {
33
+ :type => :string,
34
+ :enum => [
35
+ 'vm'
36
+ ],
37
+ :required => true
38
+ },
32
39
  'cardinality' => {
33
40
  :type => :integer,
34
41
  :default => 0,
35
42
  :minimum => 0
36
43
  },
37
- 'vm_template' => {
44
+ 'template_id' => {
38
45
  :type => :integer,
39
46
  :required => true
40
47
  },
41
- 'vm_template_contents' => {
42
- :type => :string,
48
+ 'template_contents' => {
49
+ :type => :object,
50
+ :properties => {},
43
51
  :required => false
44
52
  },
45
- 'custom_attrs' => {
53
+ 'user_inputs' => {
46
54
  :type => :object,
47
55
  :properties => {},
48
56
  :required => false
49
57
  },
50
- 'custom_attrs_values' => {
58
+ 'user_inputs_values' => {
51
59
  :type => :object,
52
60
  :properties => {},
53
61
  :required => false
@@ -173,6 +181,58 @@ module OpenNebula
173
181
  }
174
182
  }
175
183
 
184
+ VR_ROLE_SCHEMA = {
185
+ :type => :object,
186
+ :properties => {
187
+ 'name' => {
188
+ :type => :string,
189
+ :required => true,
190
+ :regex => /^\w+$/
191
+ },
192
+ 'type' => {
193
+ :type => :string,
194
+ :enum => [
195
+ 'vr'
196
+ ],
197
+ :required => true
198
+ },
199
+ 'template_id' => {
200
+ :type => :integer,
201
+ :required => true
202
+ },
203
+ 'cardinality' => {
204
+ :type => :integer,
205
+ :default => 0,
206
+ :minimum => 0
207
+ },
208
+ 'template_contents' => {
209
+ :type => :object,
210
+ :properties => {},
211
+ :required => false
212
+ },
213
+ 'user_inputs' => {
214
+ :type => :object,
215
+ :properties => {},
216
+ :required => false
217
+ },
218
+ 'user_inputs_values' => {
219
+ :type => :object,
220
+ :properties => {},
221
+ :required => false
222
+ },
223
+ 'on_hold' => {
224
+ :type => :boolean,
225
+ :required => false
226
+ },
227
+ 'parents' => {
228
+ :type => :array,
229
+ :items => {
230
+ :type => :string
231
+ }
232
+ }
233
+ }
234
+ }
235
+
176
236
  SCHEMA = {
177
237
  :type => :object,
178
238
  :properties => {
@@ -201,15 +261,15 @@ module OpenNebula
201
261
  },
202
262
  'roles' => {
203
263
  :type => :array,
204
- :items => ROLE_SCHEMA,
264
+ :items => [],
205
265
  :required => true
206
266
  },
207
- 'custom_attrs' => {
267
+ 'user_inputs' => {
208
268
  :type => :object,
209
269
  :properties => {},
210
270
  :required => false
211
271
  },
212
- 'custom_attrs_values' => {
272
+ 'user_inputs_values' => {
213
273
  :type => :object,
214
274
  :properties => {},
215
275
  :required => false
@@ -260,6 +320,12 @@ module OpenNebula
260
320
  def allocate(template_json)
261
321
  template = JSON.parse(template_json)
262
322
 
323
+ # Compability mode v<7.0
324
+ rc = ServiceTemplate.convert_template(template) \
325
+ if ServiceTemplate.old_format?(template)
326
+
327
+ return rc if OpenNebula.is_error?(rc)
328
+
263
329
  ServiceTemplate.validate(template)
264
330
 
265
331
  template['registration_time'] = Integer(Time.now)
@@ -376,11 +442,11 @@ module OpenNebula
376
442
 
377
443
  # iterate over roles to clone templates
378
444
  rc = body['roles'].each do |role|
379
- t_id = role['vm_template']
445
+ t_id = role['template_id']
380
446
 
381
447
  # if the template has already been cloned, just update the value
382
448
  if cloned_templates.keys.include?(t_id)
383
- role['vm_template'] = cloned_templates[t_id]
449
+ role['template_id'] = cloned_templates[t_id]
384
450
  next
385
451
  end
386
452
 
@@ -404,7 +470,7 @@ module OpenNebula
404
470
  # add new ID to the hash
405
471
  cloned_templates[t_id] = rc
406
472
 
407
- role['vm_template'] = rc
473
+ role['template_id'] = rc
408
474
  end
409
475
 
410
476
  # if any error, rollback and delete the left templates
@@ -477,6 +543,10 @@ module OpenNebula
477
543
 
478
544
  validator.validate!(template, SCHEMA)
479
545
 
546
+ template['roles'].each do |role|
547
+ validate_role(role)
548
+ end
549
+
480
550
  validate_values(template)
481
551
  end
482
552
 
@@ -487,7 +557,16 @@ module OpenNebula
487
557
  :allow_extra_properties => true
488
558
  )
489
559
 
490
- validator.validate!(template, ROLE_SCHEMA)
560
+ tmplt_type = template.fetch('type', 'vm')
561
+
562
+ case tmplt_type
563
+ when 'vm'
564
+ validator.validate!(template, VM_ROLE_SCHEMA)
565
+ when 'vr'
566
+ validator.validate!(template, VR_ROLE_SCHEMA)
567
+ else
568
+ raise Validator::ParseException, "Unsupported role type \"#{template['type']}\""
569
+ end
491
570
  end
492
571
 
493
572
  def instantiate(merge_template)
@@ -496,6 +575,8 @@ module OpenNebula
496
575
  if merge_template.nil?
497
576
  instantiate_template = JSON.parse(@body.to_json)
498
577
  else
578
+ @body = handle_nested_values(@body, merge_template)
579
+
499
580
  instantiate_template = JSON.parse(@body.to_json)
500
581
  .deep_merge(merge_template)
501
582
  end
@@ -518,11 +599,10 @@ module OpenNebula
518
599
  end
519
600
 
520
601
  def self.validate_values(template)
521
- parser = ElasticityGrammarParser.new
522
-
523
602
  roles = template['roles']
524
603
 
525
604
  roles.each_with_index do |role, role_index|
605
+ # General verification (applies to all roles)
526
606
  roles[role_index+1..-1].each do |other_role|
527
607
  if role['name'] == other_role['name']
528
608
  raise Validator::ParseException,
@@ -530,115 +610,291 @@ module OpenNebula
530
610
  end
531
611
  end
532
612
 
533
- if !role['min_vms'].nil? &&
534
- role['min_vms'].to_i > role['cardinality'].to_i
535
-
613
+ # Specific values verification per role type
614
+ case role['type']
615
+ when 'vm'
616
+ parser = ElasticityGrammarParser.new
617
+ validate_vmvalues(role, parser)
618
+ when 'vr'
619
+ validate_vrvalues(role)
620
+ else
536
621
  raise Validator::ParseException,
537
- "Role '#{role['name']}' 'cardinality' must be " \
538
- "greater than or equal to 'min_vms'"
622
+ "Unsupported role type \"#{template['type']}\""
539
623
  end
624
+ end
625
+ end
540
626
 
541
- if !role['max_vms'].nil? &&
542
- role['max_vms'].to_i < role['cardinality'].to_i
627
+ # Retreives all associated VM templates IDs
628
+ #
629
+ # @return [Array] VM templates IDs
630
+ def vm_template_ids
631
+ rc = info
543
632
 
544
- raise Validator::ParseException,
545
- "Role '#{role['name']}' 'cardinality' must be " \
546
- "lower than or equal to 'max_vms'"
633
+ return rc if OpenNebula.is_error?(rc)
634
+
635
+ ret = []
636
+
637
+ @body['roles'].each do |role|
638
+ t_id = Integer(role['template_id'])
639
+ ret << t_id unless ret.include?(t_id)
640
+ end
641
+
642
+ ret
643
+ end
644
+
645
+ def self.validate_vrvalues(vrrole)
646
+ nic_array = vrrole.dig('template_contents', 'NIC')
647
+ cardinality = vrrole['cardinality']
648
+
649
+ return if nic_array.nil? || !nic_array.is_a?(Array)
650
+
651
+ contains_floating_key = nic_array.any? do |nic|
652
+ nic.keys.any? do |key|
653
+ key.to_s.start_with?('FLOATING')
547
654
  end
655
+ end
656
+
657
+ return unless cardinality > 1 && !contains_floating_key
658
+
659
+ raise(
660
+ Validator::ParseException,
661
+ "Role '#{vrrole['name']}' with 'cardinality' greather " \
662
+ 'than one must define a floating IP'
663
+ )
664
+ end
665
+
666
+ def self.validate_vmvalues(vmrole, parser)
667
+ if !vmrole['min_vms'].nil? &&
668
+ vmrole['min_vms'].to_i > vmrole['cardinality'].to_i
669
+
670
+ raise Validator::ParseException,
671
+ "Role '#{vmrole['name']}' 'cardinality' must be " \
672
+ "greater than or equal to 'min_vms'"
673
+ end
674
+
675
+ if !vmrole['max_vms'].nil? &&
676
+ vmrole['max_vms'].to_i < vmrole['cardinality'].to_i
677
+
678
+ raise Validator::ParseException,
679
+ "Role '#{vmrole['name']}' 'cardinality' must be " \
680
+ "lower than or equal to 'max_vms'"
681
+ end
682
+
683
+ if ((vmrole['elasticity_policies'] &&
684
+ !vmrole['elasticity_policies'].empty?) ||
685
+ (vmrole['scheduled_policies'] &&
686
+ !vmrole['scheduled_policies'].empty?)) &&
687
+ (vmrole['min_vms'].nil? || vmrole['max_vms'].nil?)
688
+ raise Validator::ParseException,
689
+ "Role '#{vmrole['name']}' with " \
690
+ " 'elasticity_policies' or " \
691
+ "'scheduled_policies'must define both 'min_vms'" \
692
+ " and 'max_vms'"
693
+ end
694
+
695
+ if vmrole['elasticity_policies']
696
+ vmrole['elasticity_policies'].each_with_index do |policy, index|
697
+ exp = policy['expression']
698
+
699
+ if exp.empty?
700
+ raise Validator::ParseException,
701
+ "Role '#{vmrole['name']}', elasticity policy " \
702
+ "##{index} 'expression' cannot be empty"
703
+ end
704
+
705
+ treetop = parser.parse(exp)
706
+ next unless treetop.nil?
548
707
 
549
- if ((role['elasticity_policies'] &&
550
- !role['elasticity_policies'].empty?) ||
551
- (role['scheduled_policies'] &&
552
- !role['scheduled_policies'].empty?)) &&
553
- (role['min_vms'].nil? || role['max_vms'].nil?)
554
708
  raise Validator::ParseException,
555
- "Role '#{role['name']}' with " \
556
- " 'elasticity_policies' or " \
557
- "'scheduled_policies'must define both 'min_vms'" \
558
- " and 'max_vms'"
709
+ "Role '#{vmrole['name']}', elasticity policy " \
710
+ "##{index} 'expression' parse error: " \
711
+ "#{parser.failure_reason}"
559
712
  end
713
+ end
560
714
 
561
- if role['elasticity_policies']
562
- role['elasticity_policies'].each_with_index do |policy, index|
563
- exp = policy['expression']
715
+ return unless vmrole['scheduled_policies']
564
716
 
565
- if exp.empty?
566
- raise Validator::ParseException,
567
- "Role '#{role['name']}', elasticity policy " \
568
- "##{index} 'expression' cannot be empty"
569
- end
717
+ vmrole['scheduled_policies'].each_with_index do |policy, index|
718
+ start_time = policy['start_time']
719
+ recurrence = policy['recurrence']
570
720
 
571
- treetop = parser.parse(exp)
572
- next unless treetop.nil?
721
+ if !start_time.nil?
722
+ if !policy['recurrence'].nil?
723
+ raise Validator::ParseException,
724
+ "Role '#{vmrole['name']}', scheduled policy "\
725
+ "##{index} must define "\
726
+ "'start_time' or 'recurrence', but not both"
727
+ end
728
+
729
+ begin
730
+ next if start_time.match(/^\d+$/)
573
731
 
732
+ Time.parse(start_time)
733
+ rescue ArgumentError
734
+ raise Validator::ParseException,
735
+ "Role '#{vmrole['name']}', scheduled policy " \
736
+ "##{index} 'start_time' is not a valid " \
737
+ 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \
738
+ '0YYY-MM-DDThh:mm:ssZ'
739
+ end
740
+ elsif !recurrence.nil?
741
+ begin
742
+ cron_parser = CronParser.new(recurrence)
743
+ cron_parser.next
744
+ rescue StandardError
574
745
  raise Validator::ParseException,
575
- "Role '#{role['name']}', elasticity policy " \
576
- "##{index} 'expression' parse error: " \
577
- "#{parser.failure_reason}"
746
+ "Role '#{vmrole['name']}', scheduled policy " \
747
+ "##{index} 'recurrence' is not a valid " \
748
+ 'cron expression'
578
749
  end
750
+ else
751
+ raise Validator::ParseException,
752
+ "Role '#{vmrole['name']}', scheduled policy #" \
753
+ "#{index} needs to define either " \
754
+ "'start_time' or 'recurrence'"
579
755
  end
756
+ end
757
+ end
758
+
759
+ def handle_nested_values(template, extra_template)
760
+ roles = template['roles']
761
+ extra_roles = extra_template.fetch('roles', [])
580
762
 
581
- next unless role['scheduled_policies']
582
-
583
- role['scheduled_policies'].each_with_index do |policy, index|
584
- start_time = policy['start_time']
585
- recurrence = policy['recurrence']
586
-
587
- if !start_time.nil?
588
- if !policy['recurrence'].nil?
589
- raise Validator::ParseException,
590
- "Role '#{role['name']}', scheduled policy "\
591
- "##{index} must define "\
592
- "'start_time' or 'recurrence', but not both"
593
- end
594
-
595
- begin
596
- next if start_time.match(/^\d+$/)
597
-
598
- Time.parse(start_time)
599
- rescue ArgumentError
600
- raise Validator::ParseException,
601
- "Role '#{role['name']}', scheduled policy " \
602
- "##{index} 'start_time' is not a valid " \
603
- 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \
604
- '0YYY-MM-DDThh:mm:ssZ'
605
- end
606
- elsif !recurrence.nil?
607
- begin
608
- cron_parser = CronParser.new(recurrence)
609
- cron_parser.next
610
- rescue StandardError
611
- raise Validator::ParseException,
612
- "Role '#{role['name']}', scheduled policy " \
613
- "##{index} 'recurrence' is not a valid " \
614
- 'cron expression'
615
- end
763
+ # Check if nics key already exist
764
+ template_nets = template['networks_values'] || []
765
+ extra_nets = extra_template['networks_values'] || []
766
+
767
+ unless extra_nets.empty?
768
+ extra_nets.each do |extra_net|
769
+ next unless extra_net.is_a?(Hash) && !extra_net.empty?
770
+
771
+ net_name = extra_net.keys.first
772
+ net_index = template_nets.index {|net| net.key?(net_name) }
773
+
774
+ if net_index
775
+ template_nets[net_index] = extra_net
616
776
  else
617
- raise Validator::ParseException,
618
- "Role '#{role['name']}', scheduled policy #" \
619
- "#{index} needs to define either " \
620
- "'start_time' or 'recurrence'"
777
+ template_nets << extra_net
621
778
  end
622
779
  end
780
+
781
+ template['networks_values'] = template_nets
782
+ extra_template['networks_values'] = []
623
783
  end
784
+
785
+ return template if extra_roles.empty?
786
+
787
+ roles.each_with_index do |role, index|
788
+ extra_role = extra_roles.find {|item| item['name'] == role['name'] }
789
+ next unless extra_role
790
+
791
+ roles[index] = role.deep_merge(extra_role)
792
+ end
793
+
794
+ extra_template.delete('roles')
795
+
796
+ template
624
797
  end
625
798
 
626
- # Retreives all associated VM templates IDs
799
+ def self.old_format?(body)
800
+ body.key?('roles') && body['roles'].all? do |role|
801
+ role.key?('vm_template') || role.key?('vm_template_contents')
802
+ end
803
+ end
804
+
805
+ # Compatibility function to translate a service template from the old format
806
+ # to the new one with vRouters with other data model changes
627
807
  #
628
- # @return [Array] VM templates IDs
629
- def vm_template_ids
630
- rc = info
808
+ # @param template [String] Hash body of the service template to translate
809
+ def self.convert_template(body)
810
+ body['roles'].each do |role|
811
+ # Mandatory
812
+ role['template_id'] = role['vm_template']
813
+ role['type'] = 'vm'
814
+
815
+ # Optional attributes
816
+ role['user_inputs'] = role['custom_attrs'] if role['custom_attrs']
817
+ body['user_inputs_values'] = role['custom_attrs_values'] \
818
+ if body['custom_attrs_values']
819
+
820
+ # Change name and type (string -> object)
821
+ role['template_contents'] = ServiceTemplate.upgrade_template_contents(
822
+ role['vm_template_contents']
823
+ ) if role['vm_template_contents']
824
+
825
+ # Remove old attributes
826
+ role.delete('vm_template')
827
+ role.delete('vm_template_contents') if role['vm_template_contents']
828
+ role.delete('custom_attrs') if role['custom_attrs']
829
+ role.delete('custom_attrs_values') if role['custom_attrs_values']
830
+ end
631
831
 
632
- return rc if OpenNebula.is_error?(rc)
832
+ body['user_inputs'] = body['custom_attrs'] if body['custom_attrs']
833
+ body['user_inputs_values'] = body['custom_attrs_values'] if body['custom_attrs_values']
633
834
 
634
- ret = []
835
+ body.delete('custom_attrs') if body['custom_attrs']
836
+ body.delete('custom_attrs_values') if body['custom_attrs_values']
837
+ rescue StandardError => e
838
+ return OpenNebula::Error.new(
839
+ 'An old service template format (OpenNebula v6.x) was detected and could not be ' \
840
+ 'automatically converted. Update it manually to the new format. Error: ' + e.message
841
+ )
842
+ end
635
843
 
636
- @body['roles'].each do |role|
637
- t_id = Integer(role['vm_template'])
638
- ret << t_id unless ret.include?(t_id)
844
+ # Converts a RAW string in the form KEY = VAL to a hash
845
+ #
846
+ # @param template [String] Raw string content in the form KEY = VAL,
847
+ # representing vm_template_contents
848
+ # @return [Hash, OpenNebula::Error] Hash representation of the raw content,
849
+ # or an OpenNebula Error if the conversion fails
850
+ def self.upgrade_template_contents(vm_template_contents)
851
+ return {} if vm_template_contents.nil? || vm_template_contents.empty?
852
+
853
+ result = {}
854
+
855
+ vm_template_contents.split(/\n(?![^\[]*\])/).each do |line|
856
+ next unless line.include?('=')
857
+
858
+ key, value = line.split('=', 2).map(&:strip)
859
+ value = value.tr('"', '')
860
+
861
+ # If the value is an array (e.g., NIC = [ ... ]),
862
+ # process the content inside the brackets
863
+ if value.start_with?('[') && value.end_with?(']')
864
+ # Split the elements inside the brackets by commas
865
+ array_elements = value[1..-2].split(/\s*,\s*/)
866
+
867
+ # Create a hash combining all key-value pairs in the array
868
+ array_result = {}
869
+ array_elements.each do |element|
870
+ sub_key, sub_value = element.split('=', 2).map(&:strip)
871
+ array_result[sub_key] = sub_value
872
+ end
873
+
874
+ value = [array_result]
875
+ else
876
+ value = [value]
877
+ end
878
+
879
+ # If the key already exists in the result hash, add the new value
880
+ if result[key]
881
+ if result[key].is_a?(Array)
882
+ result[key] << value.first
883
+ else
884
+ result[key] = [result[key], value.first]
885
+ end
886
+ else
887
+ result[key] = value.first
888
+ end
639
889
  end
640
890
 
641
- ret
891
+ result.each do |key, val|
892
+ if val.is_a?(Hash)
893
+ result[key] = [val]
894
+ end
895
+ end
896
+
897
+ result
642
898
  end
643
899
 
644
900
  end
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2024, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2025, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #
@@ -51,7 +51,7 @@ module OpenNebula::ServiceTemplateExt
51
51
  @body['roles'].each do |role|
52
52
  # Find role template into templates to get the name to use
53
53
  t = templates.find do |_, v|
54
- v[:template]['ID'].to_i == role['vm_template']
54
+ v[:template]['ID'].to_i == role['template_id']
55
55
  end
56
56
 
57
57
  next if t.nil? || t[1].nil? || t[1][:name].nil?
@@ -62,7 +62,7 @@ module OpenNebula::ServiceTemplateExt
62
62
  ROLE = [ NAME="#{role['name']}", APP="#{app_name}"]
63
63
  EOT
64
64
 
65
- role.delete('vm_template')
65
+ role.delete('template_id')
66
66
  end
67
67
 
68
68
  xml = MarketPlaceApp.build_xml
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------- #
2
- # Copyright 2002-2024, OpenNebula Project, OpenNebula Systems #
2
+ # Copyright 2002-2025, OpenNebula Project, OpenNebula Systems #
3
3
  # #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
5
  # not use this file except in compliance with the License. You may obtain #