opennebula 5.12.11 → 5.12.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +5 -5
  2. data/lib/ActionManager.rb +1 -1
  3. data/lib/CommandManager.rb +1 -1
  4. data/lib/DriverExecHelper.rb +1 -1
  5. data/lib/OpenNebulaDriver.rb +1 -1
  6. data/lib/VirtualMachineDriver.rb +1 -1
  7. data/lib/cloud/CloudClient.rb +3 -3
  8. data/lib/models/role.rb +1095 -0
  9. data/lib/models/service.rb +648 -0
  10. data/lib/models/service_pool.rb +166 -0
  11. data/lib/models/service_template.rb +503 -0
  12. data/lib/models/service_template_pool.rb +32 -0
  13. data/lib/models.rb +32 -0
  14. data/lib/opennebula/acl.rb +1 -1
  15. data/lib/opennebula/acl_pool.rb +1 -1
  16. data/lib/opennebula/client.rb +1 -1
  17. data/lib/opennebula/cluster.rb +1 -1
  18. data/lib/opennebula/cluster_pool.rb +1 -1
  19. data/lib/opennebula/datastore.rb +1 -1
  20. data/lib/opennebula/datastore_pool.rb +1 -1
  21. data/lib/opennebula/document.rb +1 -1
  22. data/lib/opennebula/document_json.rb +1 -1
  23. data/lib/opennebula/document_pool.rb +1 -1
  24. data/lib/opennebula/document_pool_json.rb +1 -1
  25. data/lib/opennebula/error.rb +1 -1
  26. data/lib/opennebula/group.rb +1 -1
  27. data/lib/opennebula/group_pool.rb +1 -1
  28. data/lib/opennebula/hook.rb +1 -1
  29. data/lib/opennebula/hook_log.rb +1 -1
  30. data/lib/opennebula/hook_pool.rb +1 -1
  31. data/lib/opennebula/host.rb +1 -1
  32. data/lib/opennebula/host_pool.rb +1 -1
  33. data/lib/opennebula/image.rb +1 -1
  34. data/lib/opennebula/image_pool.rb +1 -1
  35. data/lib/opennebula/ldap_auth.rb +1 -1
  36. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  37. data/lib/opennebula/marketplace.rb +1 -1
  38. data/lib/opennebula/marketplace_pool.rb +1 -1
  39. data/lib/opennebula/marketplaceapp.rb +1 -1
  40. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  41. data/lib/opennebula/oneflow_client.rb +1 -1
  42. data/lib/opennebula/pool.rb +1 -1
  43. data/lib/opennebula/pool_element.rb +1 -1
  44. data/lib/opennebula/security_group.rb +1 -1
  45. data/lib/opennebula/security_group_pool.rb +1 -1
  46. data/lib/opennebula/server_cipher_auth.rb +1 -1
  47. data/lib/opennebula/server_x509_auth.rb +1 -1
  48. data/lib/opennebula/ssh_auth.rb +1 -1
  49. data/lib/opennebula/system.rb +1 -1
  50. data/lib/opennebula/template.rb +1 -1
  51. data/lib/opennebula/template_pool.rb +1 -1
  52. data/lib/opennebula/user.rb +1 -1
  53. data/lib/opennebula/user_pool.rb +1 -1
  54. data/lib/opennebula/utils.rb +1 -1
  55. data/lib/opennebula/vdc.rb +1 -1
  56. data/lib/opennebula/vdc_pool.rb +1 -1
  57. data/lib/opennebula/virtual_machine.rb +1 -1
  58. data/lib/opennebula/virtual_machine_pool.rb +1 -1
  59. data/lib/opennebula/virtual_network.rb +1 -1
  60. data/lib/opennebula/virtual_network_pool.rb +1 -1
  61. data/lib/opennebula/virtual_router.rb +1 -1
  62. data/lib/opennebula/virtual_router_pool.rb +1 -1
  63. data/lib/opennebula/vm_group.rb +1 -1
  64. data/lib/opennebula/vm_group_pool.rb +1 -1
  65. data/lib/opennebula/vntemplate.rb +1 -1
  66. data/lib/opennebula/vntemplate_pool.rb +1 -1
  67. data/lib/opennebula/x509_auth.rb +1 -1
  68. data/lib/opennebula/xml_element.rb +1 -1
  69. data/lib/opennebula/xml_pool.rb +1 -1
  70. data/lib/opennebula/xml_utils.rb +1 -1
  71. data/lib/opennebula/zone.rb +1 -1
  72. data/lib/opennebula/zone_pool.rb +1 -1
  73. data/lib/opennebula.rb +2 -2
  74. data/lib/vcenter_driver.rb +1 -1
  75. metadata +93 -46
@@ -0,0 +1,166 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2023, 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 OpenNebula
18
+
19
+ # ServicePool class
20
+ class OpenNebulaServicePool < DocumentPoolJSON
21
+
22
+ DOCUMENT_TYPE = 100
23
+
24
+ def initialize(client, user_id = -2)
25
+ super(client, user_id)
26
+ end
27
+
28
+ def factory(element_xml)
29
+ service = OpenNebula::Service.new(element_xml, @client)
30
+ service.load_body
31
+ service
32
+ end
33
+
34
+ end
35
+
36
+ # ServicePool class
37
+ class ServicePool
38
+
39
+ # rubocop:disable Style/ClassVars
40
+ @@mutex = Mutex.new
41
+ @@mutex_hash = {}
42
+ # rubocop:enable Style/ClassVars
43
+
44
+ # Class constructor
45
+ #
46
+ # @param [OpenNebula::Client] client the xml-rpc client
47
+ # @param [Integer] user_id the filter flag, see
48
+ # http://docs.opennebula.io/stable/integration/system_interfaces/api.html
49
+ #
50
+ # @return [DocumentPool] the new object
51
+ def initialize(cloud_auth, client)
52
+ # TODO, what if cloud_auth is nil?
53
+ @cloud_auth = cloud_auth
54
+ @client = client
55
+ @one_pool = nil
56
+ end
57
+
58
+ def client
59
+ # If there's a client defined use it
60
+ return @client unless @client.nil?
61
+
62
+ # If not, get one via cloud_auth
63
+ @cloud_auth.client
64
+ end
65
+
66
+ def info
67
+ osp = OpenNebulaServicePool.new(client)
68
+ rc = osp.info
69
+
70
+ @one_pool = osp
71
+
72
+ rc
73
+ end
74
+
75
+ def info_all
76
+ osp = OpenNebulaServicePool.new(client)
77
+ rc = osp.info_all
78
+
79
+ @one_pool = osp
80
+
81
+ rc
82
+ end
83
+
84
+ # rubocop:disable Lint/ToJSON
85
+ def to_json
86
+ # rubocop:enable Lint/ToJSON
87
+ @one_pool.to_json
88
+ end
89
+
90
+ def each(&block)
91
+ return if @one_pool.nil?
92
+
93
+ @one_pool.each(&block)
94
+ end
95
+
96
+ # Retrieves a Service element from OpenNebula. The Service::info()
97
+ # method is called
98
+ #
99
+ # @param [Integer] service_id Numerical Id of the service to retrieve
100
+ # @yieldparam [Service] this block will have the service's mutex locked.
101
+ # The mutex will be unlocked after the block execution.
102
+ #
103
+ # @return [Service, OpenNebula::Error] The Service in case of success
104
+ def get(service_id, external_client = nil, &block)
105
+ service_id = service_id.to_i if service_id
106
+ aux_client = nil
107
+
108
+ if external_client.nil?
109
+ aux_client = client
110
+ else
111
+ aux_client = external_client
112
+ end
113
+
114
+ service = Service.new_with_id(service_id, aux_client)
115
+
116
+ if block_given?
117
+ obj_mutex = nil
118
+ entry = nil
119
+
120
+ @@mutex.synchronize do
121
+ # entry is an array of [Mutex, waiting]
122
+ # waiting is the number of threads waiting on this mutex
123
+ entry = @@mutex_hash[service_id]
124
+
125
+ if entry.nil?
126
+ entry = [Mutex.new, 0]
127
+ @@mutex_hash[service_id] = entry
128
+ end
129
+
130
+ obj_mutex = entry[0]
131
+ entry[1] = entry[1] + 1
132
+
133
+ if @@mutex_hash.size > 10000
134
+ @@mutex_hash.delete_if do |_s_id, entry_loop|
135
+ entry_loop[1] == 0
136
+ end
137
+ end
138
+ end
139
+
140
+ rc = obj_mutex.synchronize do
141
+ rc = service.info
142
+
143
+ if OpenNebula.is_error?(rc)
144
+ return rc
145
+ end
146
+
147
+ block.call(service)
148
+ end
149
+
150
+ @@mutex.synchronize do
151
+ entry[1] = entry[1] - 1
152
+ end
153
+
154
+ if OpenNebula.is_error?(rc)
155
+ return rc
156
+ end
157
+ else
158
+ service.info
159
+ end
160
+
161
+ service
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,503 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2023, 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
+ require 'parse-cron'
18
+
19
+ module OpenNebula
20
+
21
+ # Service Tempalte
22
+ class ServiceTemplate < DocumentJSON
23
+
24
+ ROLE_SCHEMA = {
25
+ :type => :object,
26
+ :properties => {
27
+ 'name' => {
28
+ :type => :string,
29
+ :required => true,
30
+ :regex => /^\w+$/
31
+ },
32
+ 'cardinality' => {
33
+ :type => :integer,
34
+ :default => 1,
35
+ :minimum => 0
36
+ },
37
+ 'vm_template' => {
38
+ :type => :integer,
39
+ :required => true
40
+ },
41
+ 'vm_template_contents' => {
42
+ :type => :string,
43
+ :required => false
44
+ },
45
+ 'parents' => {
46
+ :type => :array,
47
+ :items => {
48
+ :type => :string
49
+ }
50
+ },
51
+ 'shutdown_action' => {
52
+ :type => :string,
53
+ :enum => %w[
54
+ terminate
55
+ terminate-hard
56
+ shutdown
57
+ shutdown-hard
58
+ ],
59
+ :required => false
60
+ },
61
+ 'min_vms' => {
62
+ :type => :integer,
63
+ :required => false,
64
+ :minimum => 0
65
+ },
66
+ 'max_vms' => {
67
+ :type => :integer,
68
+ :required => false,
69
+ :minimum => 0
70
+ },
71
+ 'cooldown' => {
72
+ :type => :integer,
73
+ :required => false,
74
+ :minimum => 0
75
+ },
76
+ 'elasticity_policies' => {
77
+ :type => :array,
78
+ :items => {
79
+ :type => :object,
80
+ :properties => {
81
+ 'type' => {
82
+ :type => :string,
83
+ :enum => %w[
84
+ CHANGE
85
+ CARDINALITY
86
+ PERCENTAGE_CHANGE
87
+ ],
88
+ :required => true
89
+ },
90
+ 'adjust' => {
91
+ :type => :integer,
92
+ :required => true
93
+ },
94
+ 'min_adjust_step' => {
95
+ :type => :integer,
96
+ :required => false,
97
+ :minimum => 1
98
+ },
99
+ 'period_number' => {
100
+ :type => :integer,
101
+ :required => false,
102
+ :minimum => 0
103
+ },
104
+ 'period' => {
105
+ :type => :integer,
106
+ :required => false,
107
+ :minimum => 0
108
+ },
109
+ 'expression' => {
110
+ :type => :string,
111
+ :required => true
112
+ },
113
+ 'cooldown' => {
114
+ :type => :integer,
115
+ :required => false,
116
+ :minimum => 0
117
+ }
118
+ # 'statistic' => {
119
+ # # SampleCount | Average | Sum | Minimum | Maximum
120
+ # :type => :string
121
+ # }
122
+ }
123
+ }
124
+ },
125
+ 'scheduled_policies' => {
126
+ :type => :array,
127
+ :items => {
128
+ :type => :object,
129
+ :properties => {
130
+ 'type' => {
131
+ :type => :string,
132
+ :enum => %w[
133
+ CHANGE
134
+ CARDINALITY
135
+ PERCENTAGE_CHANGE
136
+ ],
137
+ :required => true
138
+ },
139
+ 'adjust' => {
140
+ :type => :integer,
141
+ :required => true
142
+ },
143
+ 'min_adjust_step' => {
144
+ :type => :integer,
145
+ :required => false,
146
+ :minimum => 1
147
+ },
148
+ 'start_time' => {
149
+ :type => :string,
150
+ :required => false
151
+ },
152
+ 'recurrence' => {
153
+ :type => :string,
154
+ :required => false
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ SCHEMA = {
163
+ :type => :object,
164
+ :properties => {
165
+ 'name' => {
166
+ :type => :string,
167
+ :required => true
168
+ },
169
+ 'deployment' => {
170
+ :type => :string,
171
+ :enum => %w[none straight],
172
+ :default => 'none'
173
+ },
174
+ 'description' => {
175
+ :type => :string,
176
+ :default => ''
177
+ },
178
+ 'shutdown_action' => {
179
+ :type => :string,
180
+ :enum => %w[
181
+ terminate
182
+ terminate-hard
183
+ shutdown
184
+ shutdown-hard
185
+ ],
186
+ :required => false
187
+ },
188
+ 'roles' => {
189
+ :type => :array,
190
+ :items => ROLE_SCHEMA,
191
+ :required => true
192
+ },
193
+ 'custom_attrs' => {
194
+ :type => :object,
195
+ :properties => {},
196
+ :required => false
197
+ },
198
+ 'custom_attrs_values' => {
199
+ :type => :object,
200
+ :properties => {},
201
+ :required => false
202
+ },
203
+ 'ready_status_gate' => {
204
+ :type => :boolean,
205
+ :required => false
206
+ },
207
+ 'networks' => {
208
+ :type => :object,
209
+ :properties => {},
210
+ :required => false
211
+ },
212
+ 'networks_values' => {
213
+ :type => :array,
214
+ :items => {
215
+ :type => :object,
216
+ :properties => {}
217
+ },
218
+ :required => false
219
+ }
220
+ }
221
+ }
222
+
223
+ def self.init_default_vn_name_template(vn_name_template)
224
+ # rubocop:disable Style/ClassVars
225
+ @@vn_name_template = vn_name_template
226
+ # rubocop:enable Style/ClassVars
227
+ end
228
+
229
+ DOCUMENT_TYPE = 101
230
+
231
+ def allocate(template_json)
232
+ template = JSON.parse(template_json)
233
+
234
+ ServiceTemplate.validate(template)
235
+
236
+ super(template.to_json, template['name'])
237
+ end
238
+
239
+ # Retrieves the template
240
+ #
241
+ # @return [String] json template
242
+ def template
243
+ @body.to_json
244
+ end
245
+
246
+ # Replaces the template contents
247
+ #
248
+ # @param template_json [String] New template contents
249
+ # @param append [true, false] True to append new attributes instead of
250
+ # replace the whole template
251
+ #
252
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
253
+ # otherwise
254
+ def update(template_json, append = false)
255
+ template = JSON.parse(template_json)
256
+
257
+ if append
258
+ rc = info
259
+
260
+ return rc if OpenNebula.is_error?(rc)
261
+
262
+ template = @body.merge(template)
263
+ end
264
+
265
+ ServiceTemplate.validate(template)
266
+
267
+ super(template.to_json)
268
+ end
269
+
270
+ # Clone service template and the VM templates asssociated to it
271
+ #
272
+ # @param name [String] New template name
273
+ # @param mode [Symbol] Cloning mode (:all, :templates)
274
+ #
275
+ # @return [Integer] New document ID
276
+ def clone_recursively(name, mode)
277
+ recursive = mode == 'all'
278
+
279
+ # clone the document to get new ID
280
+ new_id = clone(name)
281
+
282
+ return new_id if OpenNebula.is_error?(new_id)
283
+
284
+ doc = OpenNebula::ServiceTemplate.new_with_id(new_id, @client)
285
+ rc = doc.info
286
+
287
+ return rc if OpenNebula.is_error?(rc)
288
+
289
+ body = JSON.parse(doc["TEMPLATE/#{TEMPLATE_TAG}"])
290
+ cloned_templates = {}
291
+
292
+ # iterate over roles to clone templates
293
+ rc = body['roles'].each do |role|
294
+ t_id = role['vm_template']
295
+
296
+ # if the template has already been cloned, just update the value
297
+ if cloned_templates.keys.include?(t_id)
298
+ role['vm_template'] = cloned_templates[t_id]
299
+ next
300
+ end
301
+
302
+ template = OpenNebula::Template.new_with_id(t_id, @client)
303
+ rc = template.info
304
+
305
+ break rc if OpenNebula.is_error?(rc)
306
+
307
+ rc = template.clone("#{template.name}-#{name}", recursive)
308
+
309
+ break rc if OpenNebula.is_error?(rc)
310
+
311
+ # add new ID to the hash
312
+ cloned_templates[t_id] = rc
313
+
314
+ role['vm_template'] = rc
315
+ end
316
+
317
+ # if any error, rollback and delete the left templates
318
+ if OpenNebula.is_error?(rc)
319
+ cloned_templates.each do |_, value|
320
+ template = OpenNebula::Template.new_with_id(value, @client)
321
+
322
+ rc = template.info
323
+
324
+ break rc if OpenNebula.is_error?(rc)
325
+
326
+ rc = template.delete(recursive)
327
+
328
+ break rc if OpenNebula.is_error?(rc)
329
+ end
330
+
331
+ return rc
332
+ end
333
+
334
+ # update the template with the new body
335
+ doc.update(body.to_json)
336
+
337
+ # return the new document ID
338
+ new_id
339
+ end
340
+
341
+ # Replaces the raw template contents
342
+ #
343
+ # @param template [String] New template contents, in the form KEY = VAL
344
+ # @param append [true, false] True to append new attributes instead of
345
+ # replace the whole template
346
+ #
347
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
348
+ # otherwise
349
+ def update_raw(template_raw, append = false)
350
+ super(template_raw, append)
351
+ end
352
+
353
+ def self.validate(template)
354
+ validator = Validator::Validator.new(
355
+ :default_values => true,
356
+ :delete_extra_properties => false,
357
+ :allow_extra_properties => true
358
+ )
359
+
360
+ validator.validate!(template, SCHEMA)
361
+
362
+ validate_values(template)
363
+ end
364
+
365
+ def instantiate(merge_template)
366
+ rc = nil
367
+
368
+ if merge_template.nil?
369
+ instantiate_template = JSON.parse(@body.to_json)
370
+ else
371
+ instantiate_template = JSON.parse(@body.to_json)
372
+ .merge(merge_template)
373
+ end
374
+
375
+ begin
376
+ ServiceTemplate.validate(instantiate_template)
377
+
378
+ xml = OpenNebula::Service.build_xml
379
+ service = OpenNebula::Service.new(xml, @client)
380
+
381
+ rc = service.allocate(instantiate_template.to_json)
382
+ rescue Validator::ParseException, JSON::ParserError => e
383
+ return e
384
+ end
385
+
386
+ return rc if OpenNebula.is_error?(rc)
387
+
388
+ service.info
389
+ service
390
+ end
391
+
392
+ def self.validate_values(template)
393
+ parser = ElasticityGrammarParser.new
394
+
395
+ roles = template['roles']
396
+
397
+ roles.each_with_index do |role, role_index|
398
+ roles[role_index+1..-1].each do |other_role|
399
+ if role['name'] == other_role['name']
400
+ raise Validator::ParseException,
401
+ "Role name '#{role['name']}' is repeated"
402
+ end
403
+ end
404
+
405
+ if !role['min_vms'].nil? &&
406
+ role['min_vms'].to_i > role['cardinality'].to_i
407
+
408
+ raise Validator::ParseException,
409
+ "Role '#{role['name']}' 'cardinality' must be " \
410
+ "greater than or equal to 'min_vms'"
411
+ end
412
+
413
+ if !role['max_vms'].nil? &&
414
+ role['max_vms'].to_i < role['cardinality'].to_i
415
+
416
+ raise Validator::ParseException,
417
+ "Role '#{role['name']}' 'cardinality' must be " \
418
+ "lower than or equal to 'max_vms'"
419
+ end
420
+
421
+ if (role['elasticity_policies'] &&
422
+ !role['elasticity_policies'].empty?) ||
423
+ (role['scheduled_policies'] &&
424
+ !role['scheduled_policies'].empty?)
425
+
426
+ if role['min_vms'].nil? || role['max_vms'].nil?
427
+ raise Validator::ParseException,
428
+ "Role '#{role['name']}' with " \
429
+ " 'elasticity_policies' or " \
430
+ "'scheduled_policies'must define both 'min_vms'" \
431
+ " and 'max_vms'"
432
+ end
433
+ end
434
+
435
+ if role['elasticity_policies']
436
+ role['elasticity_policies'].each_with_index do |policy,
437
+ index|
438
+ exp = policy['expression']
439
+
440
+ if exp.empty?
441
+ raise Validator::ParseException,
442
+ "Role '#{role['name']}', elasticity policy " \
443
+ "##{index} 'expression' cannot be empty"
444
+ end
445
+
446
+ treetop = parser.parse(exp)
447
+ next unless treetop.nil?
448
+
449
+ raise Validator::ParseException,
450
+ "Role '#{role['name']}', elasticity policy " \
451
+ "##{index} 'expression' parse error: " \
452
+ "#{parser.failure_reason}"
453
+ end
454
+ end
455
+
456
+ next unless role['scheduled_policies']
457
+
458
+ role['scheduled_policies'].each_with_index do |policy, index|
459
+ start_time = policy['start_time']
460
+ recurrence = policy['recurrence']
461
+
462
+ if !start_time.nil?
463
+ if !policy['recurrence'].nil?
464
+ raise Validator::ParseException,
465
+ "Role '#{role['name']}', scheduled policy "\
466
+ "##{index} must define "\
467
+ "'start_time' or 'recurrence', but not both"
468
+ end
469
+
470
+ begin
471
+ next if start_time.match(/^\d+$/)
472
+
473
+ Time.parse(start_time)
474
+ rescue ArgumentError
475
+ raise Validator::ParseException,
476
+ "Role '#{role['name']}', scheduled policy " \
477
+ "##{index} 'start_time' is not a valid " \
478
+ 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \
479
+ '0YYY-MM-DDThh:mm:ssZ'
480
+ end
481
+ elsif !recurrence.nil?
482
+ begin
483
+ cron_parser = CronParser.new(recurrence)
484
+ cron_parser.next
485
+ rescue StandardError
486
+ raise Validator::ParseException,
487
+ "Role '#{role['name']}', scheduled policy " \
488
+ "##{index} 'recurrence' is not a valid " \
489
+ 'cron expression'
490
+ end
491
+ else
492
+ raise Validator::ParseException,
493
+ "Role '#{role['name']}', scheduled policy #" \
494
+ "#{index} needs to define either " \
495
+ "'start_time' or 'recurrence'"
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ end
502
+
503
+ end
@@ -0,0 +1,32 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2023, 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 OpenNebula
18
+
19
+ # Service Template pool
20
+ class ServiceTemplatePool < DocumentPoolJSON
21
+
22
+ DOCUMENT_TYPE = 101
23
+
24
+ def factory(element_xml)
25
+ s_template = OpenNebula::ServiceTemplate.new(element_xml, @client)
26
+ s_template.load_body
27
+ s_template
28
+ end
29
+
30
+ end
31
+
32
+ end