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.
- checksums.yaml +4 -4
- data/lib/cloud/CloudClient.rb +3 -3
- data/lib/models/role.rb +349 -823
- data/lib/models/service.rb +156 -80
- data/lib/models/vmrole.rb +703 -0
- data/lib/models/vrrole.rb +284 -0
- data/lib/models.rb +3 -1
- data/lib/opennebula/acl.rb +1 -1
- data/lib/opennebula/acl_pool.rb +1 -1
- data/lib/opennebula/backupjob.rb +1 -1
- data/lib/opennebula/backupjob_pool.rb +1 -1
- data/lib/opennebula/client.rb +1 -1
- data/lib/opennebula/cluster.rb +45 -2
- data/lib/opennebula/cluster_pool.rb +1 -1
- data/lib/opennebula/datastore.rb +1 -1
- data/lib/opennebula/datastore_pool.rb +1 -1
- data/lib/opennebula/document.rb +1 -1
- data/lib/opennebula/document_json.rb +1 -1
- data/lib/opennebula/document_pool.rb +1 -1
- data/lib/opennebula/document_pool_json.rb +1 -1
- data/lib/opennebula/error.rb +1 -1
- data/lib/opennebula/flow/grammar.rb +1 -1
- data/lib/opennebula/flow/service_pool.rb +1 -1
- data/lib/opennebula/flow/service_template.rb +354 -99
- data/lib/opennebula/flow/service_template_ext.rb +3 -3
- data/lib/opennebula/flow/service_template_pool.rb +1 -1
- data/lib/opennebula/flow/validator.rb +458 -410
- data/lib/opennebula/flow.rb +1 -1
- data/lib/opennebula/group.rb +1 -1
- data/lib/opennebula/group_pool.rb +1 -1
- data/lib/opennebula/hook.rb +1 -1
- data/lib/opennebula/hook_log.rb +1 -1
- data/lib/opennebula/hook_pool.rb +1 -1
- data/lib/opennebula/host.rb +1 -60
- data/lib/opennebula/host_pool.rb +1 -1
- data/lib/opennebula/image.rb +1 -1
- data/lib/opennebula/image_pool.rb +1 -1
- data/lib/opennebula/ldap_auth.rb +1 -1
- data/lib/opennebula/ldap_auth_spec.rb +1 -1
- data/lib/opennebula/lockable_ext.rb +1 -1
- data/lib/opennebula/marketplace.rb +1 -1
- data/lib/opennebula/marketplace_pool.rb +1 -1
- data/lib/opennebula/marketplaceapp.rb +1 -1
- data/lib/opennebula/marketplaceapp_ext.rb +14 -211
- data/lib/opennebula/marketplaceapp_pool.rb +1 -1
- data/lib/opennebula/oneflow_client.rb +11 -9
- data/lib/opennebula/pool.rb +1 -1
- data/lib/opennebula/pool_element.rb +1 -1
- data/lib/opennebula/security_group.rb +1 -1
- data/lib/opennebula/security_group_pool.rb +1 -1
- data/lib/opennebula/server_cipher_auth.rb +1 -1
- data/lib/opennebula/server_x509_auth.rb +1 -1
- data/lib/opennebula/ssh_auth.rb +1 -1
- data/lib/opennebula/system.rb +1 -1
- data/lib/opennebula/template.rb +1 -1
- data/lib/opennebula/template_ext.rb +1 -1
- data/lib/opennebula/template_pool.rb +1 -1
- data/lib/opennebula/user.rb +1 -1
- data/lib/opennebula/user_pool.rb +1 -1
- data/lib/opennebula/utils.rb +2 -2
- data/lib/opennebula/vdc.rb +1 -1
- data/lib/opennebula/vdc_pool.rb +1 -1
- data/lib/opennebula/virtual_machine.rb +3 -12
- data/lib/opennebula/virtual_machine_ext.rb +2 -31
- data/lib/opennebula/virtual_machine_pool.rb +1 -1
- data/lib/opennebula/virtual_network.rb +1 -1
- data/lib/opennebula/virtual_network_pool.rb +1 -1
- data/lib/opennebula/virtual_router.rb +1 -1
- data/lib/opennebula/virtual_router_pool.rb +1 -1
- data/lib/opennebula/vm_group.rb +1 -1
- data/lib/opennebula/vm_group_pool.rb +1 -1
- data/lib/opennebula/vntemplate.rb +1 -1
- data/lib/opennebula/vntemplate_pool.rb +1 -1
- data/lib/opennebula/wait_ext.rb +1 -1
- data/lib/opennebula/x509_auth.rb +1 -1
- data/lib/opennebula/xml_element.rb +2 -2
- data/lib/opennebula/xml_pool.rb +1 -1
- data/lib/opennebula/xml_utils.rb +1 -1
- data/lib/opennebula/zone.rb +1 -1
- data/lib/opennebula/zone_pool.rb +1 -1
- data/lib/opennebula.rb +2 -2
- metadata +6 -67
- data/lib/ActionManager.rb +0 -280
- data/lib/CommandManager.rb +0 -328
- data/lib/DriverExecHelper.rb +0 -213
- data/lib/HostSyncManager.rb +0 -111
- data/lib/OpenNebulaDriver.rb +0 -223
- data/lib/VirtualMachineDriver.rb +0 -404
- data/lib/datacenter.rb +0 -1319
- data/lib/datastore.rb +0 -1049
- data/lib/distributed_firewall.rb +0 -293
- data/lib/file_helper.rb +0 -374
- data/lib/host.rb +0 -1518
- data/lib/logical_port.rb +0 -50
- data/lib/logical_switch.rb +0 -77
- data/lib/memoize.rb +0 -74
- data/lib/network.rb +0 -705
- data/lib/nsx_client.rb +0 -157
- data/lib/nsx_component.rb +0 -28
- data/lib/nsx_constants.rb +0 -162
- data/lib/nsx_driver.rb +0 -91
- data/lib/nsx_error.rb +0 -77
- data/lib/nsx_rule.rb +0 -206
- data/lib/nsxt_client.rb +0 -189
- data/lib/nsxt_dfw.rb +0 -196
- data/lib/nsxt_logical_port.rb +0 -94
- data/lib/nsxt_rule.rb +0 -188
- data/lib/nsxt_tz.rb +0 -38
- data/lib/nsxv_client.rb +0 -189
- data/lib/nsxv_dfw.rb +0 -202
- data/lib/nsxv_logical_port.rb +0 -107
- data/lib/nsxv_rule.rb +0 -172
- data/lib/nsxv_tz.rb +0 -41
- data/lib/opaque_network.rb +0 -134
- data/lib/rest_client.rb +0 -191
- data/lib/scripts_common.rb +0 -176
- data/lib/transport_zone.rb +0 -43
- data/lib/vcenter_driver.rb +0 -152
- data/lib/vcenter_importer.rb +0 -626
- data/lib/vi_client.rb +0 -273
- data/lib/vi_helper.rb +0 -328
- data/lib/virtual_machine.rb +0 -3573
- data/lib/virtual_wire.rb +0 -158
- data/lib/vm_device.rb +0 -80
- data/lib/vm_disk.rb +0 -202
- data/lib/vm_folder.rb +0 -69
- data/lib/vm_helper.rb +0 -30
- data/lib/vm_monitor.rb +0 -305
- data/lib/vm_nic.rb +0 -70
- data/lib/vm_template.rb +0 -2112
- data/lib/vmm_importer.rb +0 -165
@@ -1,5 +1,5 @@
|
|
1
1
|
# -------------------------------------------------------------------------- #
|
2
|
-
# Copyright 2002-
|
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
|
-
|
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
|
-
'
|
44
|
+
'template_id' => {
|
38
45
|
:type => :integer,
|
39
46
|
:required => true
|
40
47
|
},
|
41
|
-
'
|
42
|
-
:type => :
|
48
|
+
'template_contents' => {
|
49
|
+
:type => :object,
|
50
|
+
:properties => {},
|
43
51
|
:required => false
|
44
52
|
},
|
45
|
-
'
|
53
|
+
'user_inputs' => {
|
46
54
|
:type => :object,
|
47
55
|
:properties => {},
|
48
56
|
:required => false
|
49
57
|
},
|
50
|
-
'
|
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 =>
|
264
|
+
:items => [],
|
205
265
|
:required => true
|
206
266
|
},
|
207
|
-
'
|
267
|
+
'user_inputs' => {
|
208
268
|
:type => :object,
|
209
269
|
:properties => {},
|
210
270
|
:required => false
|
211
271
|
},
|
212
|
-
'
|
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['
|
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['
|
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['
|
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
|
-
|
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
|
-
|
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
|
-
|
534
|
-
|
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
|
-
"
|
538
|
-
"greater than or equal to 'min_vms'"
|
621
|
+
"Unsupported role type \"#{template['type']}\""
|
539
622
|
end
|
623
|
+
end
|
624
|
+
end
|
540
625
|
|
541
|
-
|
542
|
-
|
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
|
-
|
545
|
-
|
546
|
-
|
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 '#{
|
556
|
-
" '
|
557
|
-
"
|
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
|
-
|
562
|
-
role['elasticity_policies'].each_with_index do |policy, index|
|
563
|
-
exp = policy['expression']
|
714
|
+
return unless vmrole['scheduled_policies']
|
564
715
|
|
565
|
-
|
566
|
-
|
567
|
-
|
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
|
-
|
572
|
-
|
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 '#{
|
576
|
-
"##{index} '
|
577
|
-
|
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
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
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
|
-
|
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
|
-
|
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
|
-
# @
|
629
|
-
def
|
630
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
637
|
-
|
638
|
-
|
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
|
-
|
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-
|
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['
|
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('
|
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-
|
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 #
|