class OpenNebula::ServiceTemplate
Constants
- DOCUMENT_TYPE
- IMMUTABLE_ATTRS
-
List of attributes that can’t be changed in update operation
registration_time: this is internal info managed by OneFlow server
- SCHEMA
- VM_ROLE_SCHEMA
- VR_ROLE_SCHEMA
Public Class Methods
Source
# File lib/opennebula/flow/service_template.rb, line 808 def self.convert_template(body) body['roles'].each do |role| # Mandatory role['template_id'] = role['vm_template'].to_i role['type'] = 'vm' # Optional attributes role['user_inputs'] = role['custom_attrs'] if role['custom_attrs'] body['user_inputs_values'] = role['custom_attrs_values'] \ if body['custom_attrs_values'] # Change name and type (string -> object) role['template_contents'] = ServiceTemplate.upgrade_template_contents( role['vm_template_contents'] ) if role['vm_template_contents'] # Remove old attributes role.delete('vm_template') role.delete('vm_template_contents') if role['vm_template_contents'] role.delete('custom_attrs') if role['custom_attrs'] role.delete('custom_attrs_values') if role['custom_attrs_values'] end body['user_inputs'] = body['custom_attrs'] if body['custom_attrs'] body['user_inputs_values'] = body['custom_attrs_values'] if body['custom_attrs_values'] body.delete('custom_attrs') if body['custom_attrs'] body.delete('custom_attrs_values') if body['custom_attrs_values'] rescue StandardError => e return OpenNebula::Error.new( 'An old service template format (OpenNebula v6.x) was detected and could not be ' \ 'automatically converted. Update it manually to the new format. Error: ' + e.message ) end
Compatibility function to translate a service template from the old format to the new one with vRouters with other data model changes
@param template [String] Hash
body of the service template to translate
Source
# File lib/opennebula/flow/service_template.rb, line 312 def self.init_default_vn_name_template(vn_name_template) # rubocop:disable Style/ClassVars @@vn_name_template = vn_name_template # rubocop:enable Style/ClassVars end
Source
# File lib/opennebula/flow/service_template.rb, line 798 def self.old_format?(body) body.key?('roles') && body['roles'].all? do |role| role.key?('vm_template') || role.key?('vm_template_contents') end end
Source
# File lib/opennebula/flow/service_template.rb, line 849 def self.upgrade_template_contents(vm_template_contents) return {} if vm_template_contents.nil? || vm_template_contents.empty? result = {} vm_template_contents.split(/\n(?![^\[]*\])/).each do |line| next unless line.include?('=') key, value = line.split('=', 2).map(&:strip) value = value.tr('"', '') # If the value is an array (e.g., NIC = [ ... ]), # process the content inside the brackets if value.start_with?('[') && value.end_with?(']') # Split the elements inside the brackets by commas array_elements = value[1..-2].split(/\s*,\s*/) # Create a hash combining all key-value pairs in the array array_result = {} array_elements.each do |element| sub_key, sub_value = element.split('=', 2).map(&:strip) array_result[sub_key] = sub_value end value = [array_result] else value = [value] end # If the key already exists in the result hash, add the new value if result[key] if result[key].is_a?(Array) result[key] << value.first else result[key] = [result[key], value.first] end else result[key] = value.first end end result.each do |key, val| if val.is_a?(Hash) result[key] = [val] end end result end
Converts a RAW string in the form KEY = VAL to a hash
@param template [String] Raw string content in the form KEY = VAL,
representing vm_template_contents
@return [Hash, OpenNebula::Error
] Hash
representation of the raw content,
or an OpenNebula Error if the conversion fails
Source
# File lib/opennebula/flow/service_template.rb, line 530 def self.validate(template) # Compability mode v<7.0 rc = ServiceTemplate.convert_template(template) \ if ServiceTemplate.old_format?(template) raise Validator::ParseException(rc.message) if OpenNebula.is_error?(rc) validator = Validator::Validator.new( :default_values => true, :delete_extra_properties => false, :allow_extra_properties => true ) validator.validate!(template, SCHEMA) template['roles'].each do |role| validate_role(role) end validate_values(template) end
Source
# File lib/opennebula/flow/service_template.rb, line 552 def self.validate_role(template) validator = Validator::Validator.new( :default_values => true, :delete_extra_properties => false, :allow_extra_properties => true ) tmplt_type = template.fetch('type', 'vm') case tmplt_type when 'vm' validator.validate!(template, VM_ROLE_SCHEMA) when 'vr' validator.validate!(template, VR_ROLE_SCHEMA) else raise Validator::ParseException, "Unsupported role type \"#{template['type']}\"" end end
Source
# File lib/opennebula/flow/service_template.rb, line 600 def self.validate_values(template) roles = template['roles'] roles.each_with_index do |role, role_index| # General verification (applies to all roles) roles[(role_index+1)..-1].each do |other_role| if role['name'] == other_role['name'] raise Validator::ParseException, "Role name '#{role['name']}' is repeated" end end # Specific values verification per role type case role['type'] when 'vm' parser = ElasticityGrammarParser.new validate_vmvalues(role, parser) when 'vr' validate_vrvalues(role) else raise Validator::ParseException, "Unsupported role type \"#{template['type']}\"" end end end
Source
# File lib/opennebula/flow/service_template.rb, line 665 def self.validate_vmvalues(vmrole, parser) if !vmrole['min_vms'].nil? && vmrole['min_vms'].to_i > vmrole['cardinality'].to_i raise Validator::ParseException, "Role '#{vmrole['name']}' 'cardinality' must be " \ "greater than or equal to 'min_vms'" end if !vmrole['max_vms'].nil? && vmrole['max_vms'].to_i < vmrole['cardinality'].to_i raise Validator::ParseException, "Role '#{vmrole['name']}' 'cardinality' must be " \ "lower than or equal to 'max_vms'" end if ((vmrole['elasticity_policies'] && !vmrole['elasticity_policies'].empty?) || (vmrole['scheduled_policies'] && !vmrole['scheduled_policies'].empty?)) && (vmrole['min_vms'].nil? || vmrole['max_vms'].nil?) raise Validator::ParseException, "Role '#{vmrole['name']}' with " \ " 'elasticity_policies' or " \ "'scheduled_policies'must define both 'min_vms'" \ " and 'max_vms'" end if vmrole['elasticity_policies'] vmrole['elasticity_policies'].each_with_index do |policy, index| exp = policy['expression'] if exp.empty? raise Validator::ParseException, "Role '#{vmrole['name']}', elasticity policy " \ "##{index} 'expression' cannot be empty" end treetop = parser.parse(exp) next unless treetop.nil? raise Validator::ParseException, "Role '#{vmrole['name']}', elasticity policy " \ "##{index} 'expression' parse error: " \ "#{parser.failure_reason}" end end return unless vmrole['scheduled_policies'] vmrole['scheduled_policies'].each_with_index do |policy, index| start_time = policy['start_time'] recurrence = policy['recurrence'] if !start_time.nil? if !policy['recurrence'].nil? raise Validator::ParseException, "Role '#{vmrole['name']}', scheduled policy "\ "##{index} must define "\ "'start_time' or 'recurrence', but not both" end begin next if start_time.match(/^\d+$/) Time.parse(start_time) rescue ArgumentError raise Validator::ParseException, "Role '#{vmrole['name']}', scheduled policy " \ "##{index} 'start_time' is not a valid " \ 'Time. Try with YYYY-MM-DD hh:mm:ss or ' \ '0YYY-MM-DDThh:mm:ssZ' end elsif !recurrence.nil? begin cron_parser = CronParser.new(recurrence) cron_parser.next rescue StandardError raise Validator::ParseException, "Role '#{vmrole['name']}', scheduled policy " \ "##{index} 'recurrence' is not a valid " \ 'cron expression' end else raise Validator::ParseException, "Role '#{vmrole['name']}', scheduled policy #" \ "#{index} needs to define either " \ "'start_time' or 'recurrence'" end end end
Source
# File lib/opennebula/flow/service_template.rb, line 644 def self.validate_vrvalues(vrrole) nic_array = vrrole.dig('template_contents', 'NIC') cardinality = vrrole['cardinality'] return if nic_array.nil? || !nic_array.is_a?(Array) contains_floating_key = nic_array.any? do |nic| nic.keys.any? do |key| key.to_s.start_with?('FLOATING') end end return unless cardinality > 1 && !contains_floating_key raise( Validator::ParseException, "Role '#{vrrole['name']}' with 'cardinality' greather " \ 'than one must define a floating IP' ) end
Public Instance Methods
Source
# File lib/opennebula/flow/service_template.rb, line 320 def allocate(template_json) template = JSON.parse(template_json) ServiceTemplate.validate(template) template['registration_time'] = Integer(Time.now) super(template.to_json, template['name']) end
OpenNebula::DocumentJSON#allocate
Source
# File lib/opennebula/flow/service_template.rb, line 498 def clone(name) new_id = super doc = OpenNebula::ServiceTemplate.new_with_id(new_id, @client) rc = doc.info return rc if OpenNebula.is_error?(rc) body = JSON.parse(doc["TEMPLATE/#{TEMPLATE_TAG}"]) # add registration time, as the template is new body['registration_time'] = Integer(Time.now) # update the template with the new body DocumentJSON.instance_method(:update).bind(doc).call(body.to_json) # return the new document ID new_id end
Clones a service template
@param name [Stirng] New name
@return [Integer] New template ID
OpenNebula::Document#clone
Source
# File lib/opennebula/flow/service_template.rb, line 420 def clone_recursively(name, mode) recursive = mode == 'all' # clone the document to get new ID new_id = clone(name) return new_id if OpenNebula.is_error?(new_id) doc = OpenNebula::ServiceTemplate.new_with_id(new_id, @client) rc = doc.info return rc if OpenNebula.is_error?(rc) body = JSON.parse(doc["TEMPLATE/#{TEMPLATE_TAG}"]) cloned_templates = {} # iterate over roles to clone templates rc = body['roles'].each do |role| t_id = role['template_id'] # if the template has already been cloned, just update the value if cloned_templates.keys.include?(t_id) role['template_id'] = cloned_templates[t_id] next end template = OpenNebula::Template.new_with_id(t_id, @client) rc = template.info break rc if OpenNebula.is_error?(rc) # The maximum size is 128, so crop the template name if it # exceeds the limit new_name = "#{template.name}-#{name}" if new_name.size > 119 new_name = "#{template.name[0..(119 - name.size)]}-#{name}" end rc = template.clone(new_name, recursive) break rc if OpenNebula.is_error?(rc) # add new ID to the hash cloned_templates[t_id] = rc role['template_id'] = rc end # if any error, rollback and delete the left templates if OpenNebula.is_error?(rc) cloned_templates.each do |_, value| template = OpenNebula::Template.new_with_id(value, @client) rc = template.info break rc if OpenNebula.is_error?(rc) rc = template.delete(recursive) break rc if OpenNebula.is_error?(rc) end return rc end # update the template with the new body doc.update(body.to_json) # return the new document ID new_id end
Clone service template and the VM templates asssociated to it
@param name [String] New template name @param mode [Symbol] Cloning mode (:all, :templates)
@return [Integer] New document ID
Source
# File lib/opennebula/flow/service_template.rb, line 335 def delete(type = nil) case type when 'all' recursive = true when 'templates' recursive = false end if type && type != 'none' rc = vm_template_ids return rc if OpenNebula.is_error?(rc) rc = rc.each do |t_id| t = OpenNebula::Template.new_with_id(t_id, @client) rc = t.info break rc if OpenNebula.is_error?(rc) rc = t.delete(recursive) break rc if OpenNebula.is_error?(rc) end end return rc if OpenNebula.is_error?(rc) super() end
Delete service template
@param type [String] Delete type
- none: just the service template - all: delete VM templates, images and service template - templates: delete VM templates and service template
OpenNebula::Document#delete
Source
# File lib/opennebula/flow/service_template.rb, line 758 def handle_nested_values(template, extra_template) roles = template['roles'] extra_roles = extra_template.fetch('roles', []) # Check if nics key already exist template_nets = template['networks_values'] || [] extra_nets = extra_template['networks_values'] || [] unless extra_nets.empty? extra_nets.each do |extra_net| next unless extra_net.is_a?(Hash) && !extra_net.empty? net_name = extra_net.keys.first net_index = template_nets.index {|net| net.key?(net_name) } if net_index template_nets[net_index] = extra_net else template_nets << extra_net end end template['networks_values'] = template_nets extra_template['networks_values'] = [] end return template if extra_roles.empty? roles.each_with_index do |role, index| extra_role = extra_roles.find {|item| item['name'] == role['name'] } next unless extra_role roles[index] = role.deep_merge(extra_role) end extra_template.delete('roles') template end
Source
# File lib/opennebula/flow/service_template.rb, line 571 def instantiate(merge_template) rc = nil if merge_template.nil? instantiate_template = JSON.parse(@body.to_json) else @body = handle_nested_values(@body, merge_template) instantiate_template = JSON.parse(@body.to_json) .deep_merge(merge_template) end begin ServiceTemplate.validate(instantiate_template) xml = OpenNebula::Service.build_xml service = OpenNebula::Service.new(xml, @client) rc = service.allocate(instantiate_template.to_json) rescue Validator::ParseException, JSON::ParserError => e return e end return rc if OpenNebula.is_error?(rc) service.info service end
Source
# File lib/opennebula/flow/service_template.rb, line 368 def template @body.to_json end
Retrieves the template
@return [String] json template
Source
# File lib/opennebula/flow/service_template.rb, line 380 def update(template_json, append = false) rc = info return rc if OpenNebula.is_error?(rc) template = JSON.parse(template_json) if append IMMUTABLE_ATTRS.each do |attr| unless template[attr].nil? return [false, "service_template/#{attr}"] end end else IMMUTABLE_ATTRS.each do |attr| # Allows updating the template without # specifying the immutable attributes if template[attr].nil? template[attr] = @body[attr] end next if template[attr] == @body[attr] return [false, "service_template/#{attr}"] end end template = @body.merge(template) if append ServiceTemplate.validate(template) super(template.to_json) end
Replaces the template contents
@param template_json [String] New template contents @param append [true, false] True to append new attributes instead of
replace the whole template
@return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
OpenNebula::DocumentJSON#update
Source
# File lib/opennebula/flow/service_template.rb, line 526 def update_raw(template_raw, append = false) super(template_raw, append) end
Replaces the raw template contents
@param template [String] New template contents, in the form KEY = VAL @param append [true, false] True to append new attributes instead of
replace the whole template
@return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
OpenNebula::DocumentJSON#update_raw
Source
# File lib/opennebula/flow/service_template.rb, line 629 def vm_template_ids rc = info return rc if OpenNebula.is_error?(rc) ret = [] @body['roles'].each do |role| t_id = Integer(role['template_id']) ret << t_id unless ret.include?(t_id) end ret end
Retreives all associated VM templates IDs
@return [Array] VM templates IDs