opennebula 6.10.4 → 6.99.90.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 +354 -99
  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 -3573
  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
@@ -259,7 +319,6 @@ module OpenNebula
259
319
 
260
320
  def allocate(template_json)
261
321
  template = JSON.parse(template_json)
262
-
263
322
  ServiceTemplate.validate(template)
264
323
 
265
324
  template['registration_time'] = Integer(Time.now)
@@ -376,11 +435,11 @@ module OpenNebula
376
435
 
377
436
  # iterate over roles to clone templates
378
437
  rc = body['roles'].each do |role|
379
- t_id = role['vm_template']
438
+ t_id = role['template_id']
380
439
 
381
440
  # if the template has already been cloned, just update the value
382
441
  if cloned_templates.keys.include?(t_id)
383
- role['vm_template'] = cloned_templates[t_id]
442
+ role['template_id'] = cloned_templates[t_id]
384
443
  next
385
444
  end
386
445
 
@@ -404,7 +463,7 @@ module OpenNebula
404
463
  # add new ID to the hash
405
464
  cloned_templates[t_id] = rc
406
465
 
407
- role['vm_template'] = rc
466
+ role['template_id'] = rc
408
467
  end
409
468
 
410
469
  # if any error, rollback and delete the left templates
@@ -469,6 +528,12 @@ module OpenNebula
469
528
  end
470
529
 
471
530
  def self.validate(template)
531
+ # Compability mode v<7.0
532
+ rc = ServiceTemplate.convert_template(template) \
533
+ if ServiceTemplate.old_format?(template)
534
+
535
+ raise Validator::ParseException(rc.message) if OpenNebula.is_error?(rc)
536
+
472
537
  validator = Validator::Validator.new(
473
538
  :default_values => true,
474
539
  :delete_extra_properties => false,
@@ -477,6 +542,10 @@ module OpenNebula
477
542
 
478
543
  validator.validate!(template, SCHEMA)
479
544
 
545
+ template['roles'].each do |role|
546
+ validate_role(role)
547
+ end
548
+
480
549
  validate_values(template)
481
550
  end
482
551
 
@@ -487,7 +556,16 @@ module OpenNebula
487
556
  :allow_extra_properties => true
488
557
  )
489
558
 
490
- validator.validate!(template, ROLE_SCHEMA)
559
+ tmplt_type = template.fetch('type', 'vm')
560
+
561
+ case tmplt_type
562
+ when 'vm'
563
+ validator.validate!(template, VM_ROLE_SCHEMA)
564
+ when 'vr'
565
+ validator.validate!(template, VR_ROLE_SCHEMA)
566
+ else
567
+ raise Validator::ParseException, "Unsupported role type \"#{template['type']}\""
568
+ end
491
569
  end
492
570
 
493
571
  def instantiate(merge_template)
@@ -496,6 +574,8 @@ module OpenNebula
496
574
  if merge_template.nil?
497
575
  instantiate_template = JSON.parse(@body.to_json)
498
576
  else
577
+ @body = handle_nested_values(@body, merge_template)
578
+
499
579
  instantiate_template = JSON.parse(@body.to_json)
500
580
  .deep_merge(merge_template)
501
581
  end
@@ -518,127 +598,302 @@ module OpenNebula
518
598
  end
519
599
 
520
600
  def self.validate_values(template)
521
- parser = ElasticityGrammarParser.new
522
-
523
601
  roles = template['roles']
524
602
 
525
603
  roles.each_with_index do |role, role_index|
526
- roles[role_index+1..-1].each do |other_role|
604
+ # General verification (applies to all roles)
605
+ roles[(role_index+1)..-1].each do |other_role|
527
606
  if role['name'] == other_role['name']
528
607
  raise Validator::ParseException,
529
608
  "Role name '#{role['name']}' is repeated"
530
609
  end
531
610
  end
532
611
 
533
- if !role['min_vms'].nil? &&
534
- role['min_vms'].to_i > role['cardinality'].to_i
535
-
612
+ # Specific values verification per role type
613
+ case role['type']
614
+ when 'vm'
615
+ parser = ElasticityGrammarParser.new
616
+ validate_vmvalues(role, parser)
617
+ when 'vr'
618
+ validate_vrvalues(role)
619
+ else
536
620
  raise Validator::ParseException,
537
- "Role '#{role['name']}' 'cardinality' must be " \
538
- "greater than or equal to 'min_vms'"
621
+ "Unsupported role type \"#{template['type']}\""
539
622
  end
623
+ end
624
+ end
540
625
 
541
- if !role['max_vms'].nil? &&
542
- role['max_vms'].to_i < role['cardinality'].to_i
626
+ # Retreives all associated VM templates IDs
627
+ #
628
+ # @return [Array] VM templates IDs
629
+ def vm_template_ids
630
+ rc = info
543
631
 
544
- raise Validator::ParseException,
545
- "Role '#{role['name']}' 'cardinality' must be " \
546
- "lower than or equal to 'max_vms'"
632
+ return rc if OpenNebula.is_error?(rc)
633
+
634
+ ret = []
635
+
636
+ @body['roles'].each do |role|
637
+ t_id = Integer(role['template_id'])
638
+ ret << t_id unless ret.include?(t_id)
639
+ end
640
+
641
+ ret
642
+ end
643
+
644
+ def self.validate_vrvalues(vrrole)
645
+ nic_array = vrrole.dig('template_contents', 'NIC')
646
+ cardinality = vrrole['cardinality']
647
+
648
+ return if nic_array.nil? || !nic_array.is_a?(Array)
649
+
650
+ contains_floating_key = nic_array.any? do |nic|
651
+ nic.keys.any? do |key|
652
+ key.to_s.start_with?('FLOATING')
547
653
  end
654
+ end
655
+
656
+ return unless cardinality > 1 && !contains_floating_key
657
+
658
+ raise(
659
+ Validator::ParseException,
660
+ "Role '#{vrrole['name']}' with 'cardinality' greather " \
661
+ 'than one must define a floating IP'
662
+ )
663
+ end
664
+
665
+ def self.validate_vmvalues(vmrole, parser)
666
+ if !vmrole['min_vms'].nil? &&
667
+ vmrole['min_vms'].to_i > vmrole['cardinality'].to_i
668
+
669
+ raise Validator::ParseException,
670
+ "Role '#{vmrole['name']}' 'cardinality' must be " \
671
+ "greater than or equal to 'min_vms'"
672
+ end
673
+
674
+ if !vmrole['max_vms'].nil? &&
675
+ vmrole['max_vms'].to_i < vmrole['cardinality'].to_i
676
+
677
+ raise Validator::ParseException,
678
+ "Role '#{vmrole['name']}' 'cardinality' must be " \
679
+ "lower than or equal to 'max_vms'"
680
+ end
681
+
682
+ if ((vmrole['elasticity_policies'] &&
683
+ !vmrole['elasticity_policies'].empty?) ||
684
+ (vmrole['scheduled_policies'] &&
685
+ !vmrole['scheduled_policies'].empty?)) &&
686
+ (vmrole['min_vms'].nil? || vmrole['max_vms'].nil?)
687
+ raise Validator::ParseException,
688
+ "Role '#{vmrole['name']}' with " \
689
+ " 'elasticity_policies' or " \
690
+ "'scheduled_policies'must define both 'min_vms'" \
691
+ " and 'max_vms'"
692
+ end
693
+
694
+ if vmrole['elasticity_policies']
695
+ vmrole['elasticity_policies'].each_with_index do |policy, index|
696
+ exp = policy['expression']
697
+
698
+ if exp.empty?
699
+ raise Validator::ParseException,
700
+ "Role '#{vmrole['name']}', elasticity policy " \
701
+ "##{index} 'expression' cannot be empty"
702
+ end
703
+
704
+ treetop = parser.parse(exp)
705
+ next unless treetop.nil?
548
706
 
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
707
  raise Validator::ParseException,
555
- "Role '#{role['name']}' with " \
556
- " 'elasticity_policies' or " \
557
- "'scheduled_policies'must define both 'min_vms'" \
558
- " and 'max_vms'"
708
+ "Role '#{vmrole['name']}', elasticity policy " \
709
+ "##{index} 'expression' parse error: " \
710
+ "#{parser.failure_reason}"
559
711
  end
712
+ end
560
713
 
561
- if role['elasticity_policies']
562
- role['elasticity_policies'].each_with_index do |policy, index|
563
- exp = policy['expression']
714
+ return unless vmrole['scheduled_policies']
564
715
 
565
- if exp.empty?
566
- raise Validator::ParseException,
567
- "Role '#{role['name']}', elasticity policy " \
568
- "##{index} 'expression' cannot be empty"
569
- end
716
+ vmrole['scheduled_policies'].each_with_index do |policy, index|
717
+ start_time = policy['start_time']
718
+ recurrence = policy['recurrence']
570
719
 
571
- treetop = parser.parse(exp)
572
- next unless treetop.nil?
720
+ if !start_time.nil?
721
+ if !policy['recurrence'].nil?
722
+ raise Validator::ParseException,
723
+ "Role '#{vmrole['name']}', scheduled policy "\
724
+ "##{index} must define "\
725
+ "'start_time' or 'recurrence', but not both"
726
+ end
727
+
728
+ begin
729
+ next if start_time.match(/^\d+$/)
573
730
 
731
+ Time.parse(start_time)
732
+ rescue ArgumentError
733
+ raise Validator::ParseException,
734
+ "Role '#{vmrole['name']}', scheduled policy " \
735
+ "##{index} 'start_time' is not a valid " \
736
+ 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \
737
+ '0YYY-MM-DDThh:mm:ssZ'
738
+ end
739
+ elsif !recurrence.nil?
740
+ begin
741
+ cron_parser = CronParser.new(recurrence)
742
+ cron_parser.next
743
+ rescue StandardError
574
744
  raise Validator::ParseException,
575
- "Role '#{role['name']}', elasticity policy " \
576
- "##{index} 'expression' parse error: " \
577
- "#{parser.failure_reason}"
745
+ "Role '#{vmrole['name']}', scheduled policy " \
746
+ "##{index} 'recurrence' is not a valid " \
747
+ 'cron expression'
578
748
  end
749
+ else
750
+ raise Validator::ParseException,
751
+ "Role '#{vmrole['name']}', scheduled policy #" \
752
+ "#{index} needs to define either " \
753
+ "'start_time' or 'recurrence'"
579
754
  end
755
+ end
756
+ end
757
+
758
+ def handle_nested_values(template, extra_template)
759
+ roles = template['roles']
760
+ extra_roles = extra_template.fetch('roles', [])
580
761
 
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
762
+ # Check if nics key already exist
763
+ template_nets = template['networks_values'] || []
764
+ extra_nets = extra_template['networks_values'] || []
765
+
766
+ unless extra_nets.empty?
767
+ extra_nets.each do |extra_net|
768
+ next unless extra_net.is_a?(Hash) && !extra_net.empty?
769
+
770
+ net_name = extra_net.keys.first
771
+ net_index = template_nets.index {|net| net.key?(net_name) }
772
+
773
+ if net_index
774
+ template_nets[net_index] = extra_net
616
775
  else
617
- raise Validator::ParseException,
618
- "Role '#{role['name']}', scheduled policy #" \
619
- "#{index} needs to define either " \
620
- "'start_time' or 'recurrence'"
776
+ template_nets << extra_net
621
777
  end
622
778
  end
779
+
780
+ template['networks_values'] = template_nets
781
+ extra_template['networks_values'] = []
623
782
  end
783
+
784
+ return template if extra_roles.empty?
785
+
786
+ roles.each_with_index do |role, index|
787
+ extra_role = extra_roles.find {|item| item['name'] == role['name'] }
788
+ next unless extra_role
789
+
790
+ roles[index] = role.deep_merge(extra_role)
791
+ end
792
+
793
+ extra_template.delete('roles')
794
+
795
+ template
624
796
  end
625
797
 
626
- # Retreives all associated VM templates IDs
798
+ def self.old_format?(body)
799
+ body.key?('roles') && body['roles'].all? do |role|
800
+ role.key?('vm_template') || role.key?('vm_template_contents')
801
+ end
802
+ end
803
+
804
+ # Compatibility function to translate a service template from the old format
805
+ # to the new one with vRouters with other data model changes
627
806
  #
628
- # @return [Array] VM templates IDs
629
- def vm_template_ids
630
- rc = info
807
+ # @param template [String] Hash body of the service template to translate
808
+ def self.convert_template(body)
809
+ body['roles'].each do |role|
810
+ # Mandatory
811
+ role['template_id'] = role['vm_template'].to_i
812
+ role['type'] = 'vm'
813
+
814
+ # Optional attributes
815
+ role['user_inputs'] = role['custom_attrs'] if role['custom_attrs']
816
+ body['user_inputs_values'] = role['custom_attrs_values'] \
817
+ if body['custom_attrs_values']
818
+
819
+ # Change name and type (string -> object)
820
+ role['template_contents'] = ServiceTemplate.upgrade_template_contents(
821
+ role['vm_template_contents']
822
+ ) if role['vm_template_contents']
823
+
824
+ # Remove old attributes
825
+ role.delete('vm_template')
826
+ role.delete('vm_template_contents') if role['vm_template_contents']
827
+ role.delete('custom_attrs') if role['custom_attrs']
828
+ role.delete('custom_attrs_values') if role['custom_attrs_values']
829
+ end
631
830
 
632
- return rc if OpenNebula.is_error?(rc)
831
+ body['user_inputs'] = body['custom_attrs'] if body['custom_attrs']
832
+ body['user_inputs_values'] = body['custom_attrs_values'] if body['custom_attrs_values']
633
833
 
634
- ret = []
834
+ body.delete('custom_attrs') if body['custom_attrs']
835
+ body.delete('custom_attrs_values') if body['custom_attrs_values']
836
+ rescue StandardError => e
837
+ return OpenNebula::Error.new(
838
+ 'An old service template format (OpenNebula v6.x) was detected and could not be ' \
839
+ 'automatically converted. Update it manually to the new format. Error: ' + e.message
840
+ )
841
+ end
635
842
 
636
- @body['roles'].each do |role|
637
- t_id = Integer(role['vm_template'])
638
- ret << t_id unless ret.include?(t_id)
843
+ # Converts a RAW string in the form KEY = VAL to a hash
844
+ #
845
+ # @param template [String] Raw string content in the form KEY = VAL,
846
+ # representing vm_template_contents
847
+ # @return [Hash, OpenNebula::Error] Hash representation of the raw content,
848
+ # or an OpenNebula Error if the conversion fails
849
+ def self.upgrade_template_contents(vm_template_contents)
850
+ return {} if vm_template_contents.nil? || vm_template_contents.empty?
851
+
852
+ result = {}
853
+
854
+ vm_template_contents.split(/\n(?![^\[]*\])/).each do |line|
855
+ next unless line.include?('=')
856
+
857
+ key, value = line.split('=', 2).map(&:strip)
858
+ value = value.tr('"', '')
859
+
860
+ # If the value is an array (e.g., NIC = [ ... ]),
861
+ # process the content inside the brackets
862
+ if value.start_with?('[') && value.end_with?(']')
863
+ # Split the elements inside the brackets by commas
864
+ array_elements = value[1..-2].split(/\s*,\s*/)
865
+
866
+ # Create a hash combining all key-value pairs in the array
867
+ array_result = {}
868
+ array_elements.each do |element|
869
+ sub_key, sub_value = element.split('=', 2).map(&:strip)
870
+ array_result[sub_key] = sub_value
871
+ end
872
+
873
+ value = [array_result]
874
+ else
875
+ value = [value]
876
+ end
877
+
878
+ # If the key already exists in the result hash, add the new value
879
+ if result[key]
880
+ if result[key].is_a?(Array)
881
+ result[key] << value.first
882
+ else
883
+ result[key] = [result[key], value.first]
884
+ end
885
+ else
886
+ result[key] = value.first
887
+ end
639
888
  end
640
889
 
641
- ret
890
+ result.each do |key, val|
891
+ if val.is_a?(Hash)
892
+ result[key] = [val]
893
+ end
894
+ end
895
+
896
+ result
642
897
  end
643
898
 
644
899
  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 #