opennebula 5.12.9 → 6.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ActionManager.rb +1 -1
  3. data/lib/CommandManager.rb +1 -1
  4. data/lib/DriverExecHelper.rb +44 -28
  5. data/lib/OpenNebulaDriver.rb +8 -4
  6. data/lib/VirtualMachineDriver.rb +9 -2
  7. data/lib/cloud/CloudClient.rb +3 -3
  8. data/lib/datacenter.rb +1257 -0
  9. data/lib/datastore.rb +1025 -0
  10. data/lib/distributed_firewall.rb +280 -0
  11. data/lib/file_helper.rb +370 -0
  12. data/lib/host.rb +1517 -0
  13. data/lib/logical_port.rb +50 -0
  14. data/lib/logical_switch.rb +77 -0
  15. data/lib/memoize.rb +74 -0
  16. data/lib/models.rb +32 -0
  17. data/lib/models/role.rb +1126 -0
  18. data/lib/models/service.rb +709 -0
  19. data/lib/network.rb +699 -0
  20. data/lib/nsx_client.rb +144 -0
  21. data/lib/nsx_component.rb +28 -0
  22. data/lib/nsx_constants.rb +149 -0
  23. data/lib/nsx_driver.rb +78 -0
  24. data/lib/nsx_error.rb +77 -0
  25. data/lib/nsx_rule.rb +193 -0
  26. data/lib/nsxt_client.rb +176 -0
  27. data/lib/nsxt_dfw.rb +196 -0
  28. data/lib/nsxt_logical_port.rb +94 -0
  29. data/lib/nsxt_rule.rb +188 -0
  30. data/lib/nsxt_tz.rb +38 -0
  31. data/lib/nsxv_client.rb +176 -0
  32. data/lib/nsxv_dfw.rb +202 -0
  33. data/lib/nsxv_logical_port.rb +107 -0
  34. data/lib/nsxv_rule.rb +172 -0
  35. data/lib/nsxv_tz.rb +41 -0
  36. data/lib/opaque_network.rb +134 -0
  37. data/lib/opennebula.rb +5 -2
  38. data/lib/opennebula/acl.rb +1 -1
  39. data/lib/opennebula/acl_pool.rb +1 -1
  40. data/lib/opennebula/client.rb +10 -2
  41. data/lib/opennebula/cluster.rb +1 -1
  42. data/lib/opennebula/cluster_pool.rb +1 -1
  43. data/lib/opennebula/datastore.rb +1 -1
  44. data/lib/opennebula/datastore_pool.rb +1 -1
  45. data/lib/opennebula/document.rb +8 -29
  46. data/lib/opennebula/document_json.rb +42 -12
  47. data/lib/opennebula/document_pool.rb +1 -1
  48. data/lib/opennebula/document_pool_json.rb +1 -1
  49. data/lib/opennebula/error.rb +4 -1
  50. data/lib/opennebula/flow.rb +23 -0
  51. data/lib/opennebula/flow/grammar.rb +1195 -0
  52. data/lib/opennebula/flow/service_pool.rb +190 -0
  53. data/lib/opennebula/flow/service_template.rb +608 -0
  54. data/lib/opennebula/flow/service_template_ext.rb +84 -0
  55. data/lib/opennebula/flow/service_template_pool.rb +32 -0
  56. data/lib/opennebula/flow/validator.rb +499 -0
  57. data/lib/opennebula/group.rb +1 -1
  58. data/lib/opennebula/group_pool.rb +1 -1
  59. data/lib/opennebula/hook.rb +5 -12
  60. data/lib/opennebula/hook_log.rb +1 -1
  61. data/lib/opennebula/hook_pool.rb +1 -1
  62. data/lib/opennebula/host.rb +1 -1
  63. data/lib/opennebula/host_pool.rb +1 -1
  64. data/lib/opennebula/image.rb +19 -14
  65. data/lib/opennebula/image_pool.rb +1 -1
  66. data/lib/opennebula/ldap_auth.rb +1 -1
  67. data/lib/opennebula/ldap_auth_spec.rb +1 -1
  68. data/lib/opennebula/lockable_ext.rb +163 -0
  69. data/lib/opennebula/marketplace.rb +1 -1
  70. data/lib/opennebula/marketplace_pool.rb +1 -1
  71. data/lib/opennebula/marketplaceapp.rb +9 -119
  72. data/lib/opennebula/marketplaceapp_ext.rb +586 -0
  73. data/lib/opennebula/marketplaceapp_pool.rb +1 -1
  74. data/lib/opennebula/oneflow_client.rb +4 -3
  75. data/lib/opennebula/pool.rb +6 -3
  76. data/lib/opennebula/pool_element.rb +1 -1
  77. data/lib/opennebula/security_group.rb +1 -1
  78. data/lib/opennebula/security_group_pool.rb +1 -1
  79. data/lib/opennebula/server_cipher_auth.rb +1 -1
  80. data/lib/opennebula/server_x509_auth.rb +1 -1
  81. data/lib/opennebula/ssh_auth.rb +1 -1
  82. data/lib/opennebula/system.rb +1 -1
  83. data/lib/opennebula/template.rb +4 -13
  84. data/lib/opennebula/template_ext.rb +342 -0
  85. data/lib/opennebula/template_pool.rb +1 -1
  86. data/lib/opennebula/user.rb +26 -2
  87. data/lib/opennebula/user_pool.rb +1 -1
  88. data/lib/opennebula/utils.rb +1 -1
  89. data/lib/opennebula/vdc.rb +1 -1
  90. data/lib/opennebula/vdc_pool.rb +1 -1
  91. data/lib/opennebula/virtual_machine.rb +26 -206
  92. data/lib/opennebula/virtual_machine_ext.rb +469 -0
  93. data/lib/opennebula/virtual_machine_pool.rb +1 -1
  94. data/lib/opennebula/virtual_network.rb +4 -10
  95. data/lib/opennebula/virtual_network_pool.rb +1 -1
  96. data/lib/opennebula/virtual_router.rb +4 -12
  97. data/lib/opennebula/virtual_router_pool.rb +1 -1
  98. data/lib/opennebula/vm_group.rb +4 -11
  99. data/lib/opennebula/vm_group_pool.rb +1 -1
  100. data/lib/opennebula/vntemplate.rb +4 -13
  101. data/lib/opennebula/vntemplate_pool.rb +1 -1
  102. data/lib/opennebula/wait_ext.rb +257 -0
  103. data/lib/opennebula/x509_auth.rb +1 -1
  104. data/lib/opennebula/xml_element.rb +1 -1
  105. data/lib/opennebula/xml_pool.rb +1 -1
  106. data/lib/opennebula/xml_utils.rb +1 -1
  107. data/lib/opennebula/zone.rb +1 -1
  108. data/lib/opennebula/zone_pool.rb +1 -1
  109. data/lib/rest_client.rb +201 -0
  110. data/lib/scripts_common.rb +183 -0
  111. data/lib/transport_zone.rb +43 -0
  112. data/lib/vcenter_driver.rb +13 -12
  113. data/lib/vcenter_importer.rb +616 -0
  114. data/lib/vi_client.rb +281 -0
  115. data/lib/vi_helper.rb +313 -0
  116. data/lib/virtual_machine.rb +3477 -0
  117. data/lib/virtual_wire.rb +158 -0
  118. data/lib/vm_device.rb +80 -0
  119. data/lib/vm_disk.rb +202 -0
  120. data/lib/vm_folder.rb +69 -0
  121. data/lib/vm_helper.rb +30 -0
  122. data/lib/vm_monitor.rb +303 -0
  123. data/lib/vm_nic.rb +70 -0
  124. data/lib/vm_template.rb +1963 -0
  125. data/lib/vmm_importer.rb +121 -0
  126. metadata +140 -27
@@ -0,0 +1,190 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ module 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 < Pool
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
+
57
+ if @client
58
+ info = Nokogiri::XML(@client.call('user.info', -1))
59
+ @user_id = Integer(info.xpath('/USER/ID').text)
60
+ end
61
+
62
+ super('DOCUMENT_POOL', 'DOCUMENT', @client)
63
+ end
64
+
65
+ def client
66
+ # If there's a client defined use it
67
+ return @client unless @client.nil?
68
+
69
+ # If not, get one via cloud_auth
70
+ @cloud_auth.client
71
+ end
72
+
73
+ def info
74
+ osp = OpenNebulaServicePool.new(client)
75
+ rc = osp.info
76
+
77
+ @one_pool = osp
78
+
79
+ rc
80
+ end
81
+
82
+ def info_all
83
+ osp = OpenNebulaServicePool.new(client)
84
+ rc = osp.info_all
85
+
86
+ @one_pool = osp
87
+
88
+ rc
89
+ end
90
+
91
+ # rubocop:disable Lint/ToJSON
92
+ def to_json
93
+ # rubocop:enable Lint/ToJSON
94
+ @one_pool.to_json
95
+ end
96
+
97
+ def each(&block)
98
+ return if @one_pool.nil?
99
+
100
+ @one_pool.each(&block)
101
+ end
102
+
103
+ # Iterates over pool pages
104
+ # size:: nil => default page size
105
+ # > 0 => page size
106
+ # state state of objects
107
+ def each_page(size)
108
+ loop_page(size, Service::DOCUMENT_TYPE, false) do |element, page|
109
+ page.each("//#{element}") do |obj|
110
+ service = Service.new_with_id(obj['ID'], @client)
111
+ service.info
112
+
113
+ yield(service)
114
+ end
115
+
116
+ size
117
+ end
118
+ end
119
+
120
+ # Retrieves a Service element from OpenNebula. The Service::info()
121
+ # method is called
122
+ #
123
+ # @param [Integer] service_id Numerical Id of the service to retrieve
124
+ # @yieldparam [Service] this block will have the service's mutex locked.
125
+ # The mutex will be unlocked after the block execution.
126
+ #
127
+ # @return [Service, OpenNebula::Error] The Service in case of success
128
+ def get(service_id, external_client = nil, &block)
129
+ service_id = service_id.to_i if service_id
130
+ aux_client = nil
131
+
132
+ if external_client.nil?
133
+ aux_client = client
134
+ else
135
+ aux_client = external_client
136
+ end
137
+
138
+ service = Service.new_with_id(service_id, aux_client)
139
+
140
+ if block_given?
141
+ obj_mutex = nil
142
+ entry = nil
143
+
144
+ @@mutex.synchronize do
145
+ # entry is an array of [Mutex, waiting]
146
+ # waiting is the number of threads waiting on this mutex
147
+ entry = @@mutex_hash[service_id]
148
+
149
+ if entry.nil?
150
+ entry = [Mutex.new, 0]
151
+ @@mutex_hash[service_id] = entry
152
+ end
153
+
154
+ obj_mutex = entry[0]
155
+ entry[1] = entry[1] + 1
156
+
157
+ if @@mutex_hash.size > 10000
158
+ @@mutex_hash.delete_if do |_s_id, entry_loop|
159
+ entry_loop[1] == 0
160
+ end
161
+ end
162
+ end
163
+
164
+ rc = obj_mutex.synchronize do
165
+ rc = service.info
166
+
167
+ if OpenNebula.is_error?(rc)
168
+ return rc
169
+ end
170
+
171
+ block.call(service)
172
+ end
173
+
174
+ @@mutex.synchronize do
175
+ entry[1] = entry[1] - 1
176
+ end
177
+
178
+ if OpenNebula.is_error?(rc)
179
+ return rc
180
+ end
181
+ else
182
+ service.info
183
+ end
184
+
185
+ service
186
+ end
187
+
188
+ end
189
+
190
+ end
@@ -0,0 +1,608 @@
1
+ # -------------------------------------------------------------------------- #
2
+ # Copyright 2002-2021, OpenNebula Project, OpenNebula Systems #
3
+ # #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may #
5
+ # not use this file except in compliance with the License. You may obtain #
6
+ # a copy of the License at #
7
+ # #
8
+ # http://www.apache.org/licenses/LICENSE-2.0 #
9
+ # #
10
+ # Unless required by applicable law or agreed to in writing, software #
11
+ # distributed under the License is distributed on an "AS IS" BASIS, #
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
13
+ # See the License for the specific language governing permissions and #
14
+ # limitations under the License. #
15
+ #--------------------------------------------------------------------------- #
16
+
17
+ require 'parse-cron'
18
+
19
+ module OpenNebula
20
+
21
+ # Service Template
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
+ 'automatic_deletion' => {
208
+ :type => :boolean,
209
+ :required => false
210
+ },
211
+ 'networks' => {
212
+ :type => :object,
213
+ :properties => {},
214
+ :required => false
215
+ },
216
+ 'networks_values' => {
217
+ :type => :array,
218
+ :items => {
219
+ :type => :object,
220
+ :properties => {}
221
+ },
222
+ :required => false
223
+ }
224
+ }
225
+ }
226
+
227
+ # List of attributes that can't be changed in update operation
228
+ #
229
+ # registration_time: this is internal info managed by OneFlow server
230
+ IMMUTABLE_ATTRS = %w[
231
+ registration_time
232
+ ]
233
+
234
+ def self.init_default_vn_name_template(vn_name_template)
235
+ # rubocop:disable Style/ClassVars
236
+ @@vn_name_template = vn_name_template
237
+ # rubocop:enable Style/ClassVars
238
+ end
239
+
240
+ DOCUMENT_TYPE = 101
241
+
242
+ def allocate(template_json)
243
+ template = JSON.parse(template_json)
244
+
245
+ ServiceTemplate.validate(template)
246
+
247
+ template['registration_time'] = Integer(Time.now)
248
+
249
+ super(template.to_json, template['name'])
250
+ end
251
+
252
+ # Delete service template
253
+ #
254
+ # @param type [String] Delete type
255
+ # - none: just the service template
256
+ # - all: delete VM templates, images and service template
257
+ # - templates: delete VM templates and service template
258
+ def delete(type = nil)
259
+ case type
260
+ when 'all'
261
+ recursive = true
262
+ when 'templates'
263
+ recursive = false
264
+ end
265
+
266
+ if type && type != 'none'
267
+ rc = vm_template_ids
268
+
269
+ return rc if OpenNebula.is_error?(rc)
270
+
271
+ rc = rc.each do |t_id|
272
+ t = OpenNebula::Template.new_with_id(t_id, @client)
273
+ rc = t.info
274
+
275
+ break rc if OpenNebula.is_error?(rc)
276
+
277
+ rc = t.delete(recursive)
278
+
279
+ break rc if OpenNebula.is_error?(rc)
280
+ end
281
+ end
282
+
283
+ return rc if OpenNebula.is_error?(rc)
284
+
285
+ super()
286
+ end
287
+
288
+ # Retrieves the template
289
+ #
290
+ # @return [String] json template
291
+ def template
292
+ @body.to_json
293
+ end
294
+
295
+ # Replaces the template contents
296
+ #
297
+ # @param template_json [String] New template contents
298
+ # @param append [true, false] True to append new attributes instead of
299
+ # replace the whole template
300
+ #
301
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
302
+ # otherwise
303
+ def update(template_json, append = false)
304
+ rc = info
305
+
306
+ return rc if OpenNebula.is_error?(rc)
307
+
308
+ template = JSON.parse(template_json)
309
+
310
+ IMMUTABLE_ATTRS.each do |attr|
311
+ next if template[attr] == @body[attr]
312
+
313
+ return [false, "service_template/#{attr}"]
314
+ end
315
+
316
+ template = @body.merge(template) if append
317
+
318
+ ServiceTemplate.validate(template)
319
+
320
+ super(template.to_json)
321
+ end
322
+
323
+ # Clone service template and the VM templates asssociated to it
324
+ #
325
+ # @param name [String] New template name
326
+ # @param mode [Symbol] Cloning mode (:all, :templates)
327
+ #
328
+ # @return [Integer] New document ID
329
+ def clone_recursively(name, mode)
330
+ recursive = mode == 'all'
331
+
332
+ # clone the document to get new ID
333
+ new_id = clone(name)
334
+
335
+ return new_id if OpenNebula.is_error?(new_id)
336
+
337
+ doc = OpenNebula::ServiceTemplate.new_with_id(new_id, @client)
338
+ rc = doc.info
339
+
340
+ return rc if OpenNebula.is_error?(rc)
341
+
342
+ body = JSON.parse(doc["TEMPLATE/#{TEMPLATE_TAG}"])
343
+ cloned_templates = {}
344
+
345
+ # iterate over roles to clone templates
346
+ rc = body['roles'].each do |role|
347
+ t_id = role['vm_template']
348
+
349
+ # if the template has already been cloned, just update the value
350
+ if cloned_templates.keys.include?(t_id)
351
+ role['vm_template'] = cloned_templates[t_id]
352
+ next
353
+ end
354
+
355
+ template = OpenNebula::Template.new_with_id(t_id, @client)
356
+ rc = template.info
357
+
358
+ break rc if OpenNebula.is_error?(rc)
359
+
360
+ # The maximum size is 128, so crop the template name if it
361
+ # exceeds the limit
362
+ new_name = "#{template.name}-#{name}"
363
+
364
+ if new_name.size > 128
365
+ new_name = "#{template.name[0..(126 - name.size)]}-#{name}"
366
+ end
367
+
368
+ rc = template.clone(new_name, recursive)
369
+
370
+ break rc if OpenNebula.is_error?(rc)
371
+
372
+ # add new ID to the hash
373
+ cloned_templates[t_id] = rc
374
+
375
+ role['vm_template'] = rc
376
+ end
377
+
378
+ # if any error, rollback and delete the left templates
379
+ if OpenNebula.is_error?(rc)
380
+ cloned_templates.each do |_, value|
381
+ template = OpenNebula::Template.new_with_id(value, @client)
382
+
383
+ rc = template.info
384
+
385
+ break rc if OpenNebula.is_error?(rc)
386
+
387
+ rc = template.delete(recursive)
388
+
389
+ break rc if OpenNebula.is_error?(rc)
390
+ end
391
+
392
+ return rc
393
+ end
394
+
395
+ # add registration time, as the template is new
396
+ body['registration_time'] = Integer(Time.now)
397
+
398
+ # update the template with the new body
399
+ doc.update(body.to_json)
400
+
401
+ # return the new document ID
402
+ new_id
403
+ end
404
+
405
+ # Clones a service template
406
+ #
407
+ # @param name [Stirng] New name
408
+ #
409
+ # @return [Integer] New template ID
410
+ def clone(name)
411
+ new_id = super
412
+
413
+ doc = OpenNebula::ServiceTemplate.new_with_id(new_id, @client)
414
+ rc = doc.info
415
+
416
+ return rc if OpenNebula.is_error?(rc)
417
+
418
+ body = JSON.parse(doc["TEMPLATE/#{TEMPLATE_TAG}"])
419
+
420
+ # add registration time, as the template is new
421
+ body['registration_time'] = Integer(Time.now)
422
+
423
+ # update the template with the new body
424
+ DocumentJSON.instance_method(:update).bind(doc).call(body.to_json)
425
+
426
+ # return the new document ID
427
+ new_id
428
+ end
429
+
430
+ # Replaces the raw template contents
431
+ #
432
+ # @param template [String] New template contents, in the form KEY = VAL
433
+ # @param append [true, false] True to append new attributes instead of
434
+ # replace the whole template
435
+ #
436
+ # @return [nil, OpenNebula::Error] nil in case of success, Error
437
+ # otherwise
438
+ def update_raw(template_raw, append = false)
439
+ super(template_raw, append)
440
+ end
441
+
442
+ def self.validate(template)
443
+ validator = Validator::Validator.new(
444
+ :default_values => true,
445
+ :delete_extra_properties => false,
446
+ :allow_extra_properties => true
447
+ )
448
+
449
+ validator.validate!(template, SCHEMA)
450
+
451
+ validate_values(template)
452
+ end
453
+
454
+ def instantiate(merge_template)
455
+ rc = nil
456
+
457
+ if merge_template.nil?
458
+ instantiate_template = JSON.parse(@body.to_json)
459
+ else
460
+ instantiate_template = JSON.parse(@body.to_json)
461
+ .merge(merge_template)
462
+ end
463
+
464
+ begin
465
+ ServiceTemplate.validate(instantiate_template)
466
+
467
+ xml = OpenNebula::Service.build_xml
468
+ service = OpenNebula::Service.new(xml, @client)
469
+
470
+ rc = service.allocate(instantiate_template.to_json)
471
+ rescue Validator::ParseException, JSON::ParserError => e
472
+ return e
473
+ end
474
+
475
+ return rc if OpenNebula.is_error?(rc)
476
+
477
+ service.info
478
+ service
479
+ end
480
+
481
+ def self.validate_values(template)
482
+ parser = ElasticityGrammarParser.new
483
+
484
+ roles = template['roles']
485
+
486
+ roles.each_with_index do |role, role_index|
487
+ roles[role_index+1..-1].each do |other_role|
488
+ if role['name'] == other_role['name']
489
+ raise Validator::ParseException,
490
+ "Role name '#{role['name']}' is repeated"
491
+ end
492
+ end
493
+
494
+ if !role['min_vms'].nil? &&
495
+ role['min_vms'].to_i > role['cardinality'].to_i
496
+
497
+ raise Validator::ParseException,
498
+ "Role '#{role['name']}' 'cardinality' must be " \
499
+ "greater than or equal to 'min_vms'"
500
+ end
501
+
502
+ if !role['max_vms'].nil? &&
503
+ role['max_vms'].to_i < role['cardinality'].to_i
504
+
505
+ raise Validator::ParseException,
506
+ "Role '#{role['name']}' 'cardinality' must be " \
507
+ "lower than or equal to 'max_vms'"
508
+ end
509
+
510
+ if ((role['elasticity_policies'] &&
511
+ !role['elasticity_policies'].empty?) ||
512
+ (role['scheduled_policies'] &&
513
+ !role['scheduled_policies'].empty?)) &&
514
+ (role['min_vms'].nil? || role['max_vms'].nil?)
515
+ raise Validator::ParseException,
516
+ "Role '#{role['name']}' with " \
517
+ " 'elasticity_policies' or " \
518
+ "'scheduled_policies'must define both 'min_vms'" \
519
+ " and 'max_vms'"
520
+ end
521
+
522
+ if role['elasticity_policies']
523
+ role['elasticity_policies'].each_with_index do |policy,
524
+ index|
525
+ exp = policy['expression']
526
+
527
+ if exp.empty?
528
+ raise Validator::ParseException,
529
+ "Role '#{role['name']}', elasticity policy " \
530
+ "##{index} 'expression' cannot be empty"
531
+ end
532
+
533
+ treetop = parser.parse(exp)
534
+ next unless treetop.nil?
535
+
536
+ raise Validator::ParseException,
537
+ "Role '#{role['name']}', elasticity policy " \
538
+ "##{index} 'expression' parse error: " \
539
+ "#{parser.failure_reason}"
540
+ end
541
+ end
542
+
543
+ next unless role['scheduled_policies']
544
+
545
+ role['scheduled_policies'].each_with_index do |policy, index|
546
+ start_time = policy['start_time']
547
+ recurrence = policy['recurrence']
548
+
549
+ if !start_time.nil?
550
+ if !policy['recurrence'].nil?
551
+ raise Validator::ParseException,
552
+ "Role '#{role['name']}', scheduled policy "\
553
+ "##{index} must define "\
554
+ "'start_time' or 'recurrence', but not both"
555
+ end
556
+
557
+ begin
558
+ next if start_time.match(/^\d+$/)
559
+
560
+ Time.parse(start_time)
561
+ rescue ArgumentError
562
+ raise Validator::ParseException,
563
+ "Role '#{role['name']}', scheduled policy " \
564
+ "##{index} 'start_time' is not a valid " \
565
+ 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \
566
+ '0YYY-MM-DDThh:mm:ssZ'
567
+ end
568
+ elsif !recurrence.nil?
569
+ begin
570
+ cron_parser = CronParser.new(recurrence)
571
+ cron_parser.next
572
+ rescue StandardError
573
+ raise Validator::ParseException,
574
+ "Role '#{role['name']}', scheduled policy " \
575
+ "##{index} 'recurrence' is not a valid " \
576
+ 'cron expression'
577
+ end
578
+ else
579
+ raise Validator::ParseException,
580
+ "Role '#{role['name']}', scheduled policy #" \
581
+ "#{index} needs to define either " \
582
+ "'start_time' or 'recurrence'"
583
+ end
584
+ end
585
+ end
586
+ end
587
+
588
+ # Retreives all associated VM templates IDs
589
+ #
590
+ # @return [Array] VM templates IDs
591
+ def vm_template_ids
592
+ rc = info
593
+
594
+ return rc if OpenNebula.is_error?(rc)
595
+
596
+ ret = []
597
+
598
+ @body['roles'].each do |role|
599
+ t_id = Integer(role['vm_template'])
600
+ ret << t_id unless ret.include?(t_id)
601
+ end
602
+
603
+ ret
604
+ end
605
+
606
+ end
607
+
608
+ end