class OpenNebula::Service
Service
class as wrapper of DocumentJSON
Constants
- DOCUMENT_TYPE
- FAILED_STATES
- IMMUTABLE_ATTRS
List of attributes that can’t be changed in update operation
custom_attrs: it only has sense when deploying, not in running custom_attrs_values: it only has sense when deploying, not in running deployment: changing this, changes the undeploy operation log: this is just internal information, no sense to change it name: this has to be changed using rename operation networks: it only has sense when deploying, not in running networks_values: it only has sense when deploying, not in running ready_status_gate: it only has sense when deploying, not in running state: this is internal information managed by OneFlow server start_time: this is internal information managed by OneFlow server
- LOG_COMP
- MAX_LOG
Maximum number of log entries per service TODO: Make this value configurable
- RECOVER_DEPLOY_NETS_STATES
- RECOVER_DEPLOY_STATES
- RECOVER_SCALE_STATES
- RECOVER_UNDEPLOY_NETS_STATES
- RECOVER_UNDEPLOY_STATES
- STATE
- STATE_STR
- TRANSIENT_STATES
Attributes
Public Instance Methods
Adds a role to the service
@param template [Hash] Role
information
@return [OpenNebula::Role] New role
# File lib/models/service.rb, line 409 def add_role(template) template['state'] ||= Role::STATE['PENDING'] role = Role.new(template, self) if @roles[role.name] return OpenNebula::Error.new("Role #{role.name} already exists") end @roles[role.name] = role @body['roles'] << template if @body && @body['roles'] role end
Returns true if all the nodes are in done state @return [true, false] true if all the nodes are correctly deployed
# File lib/models/service.rb, line 270 def all_roles_done? @roles.each do |_name, role| if role.state != Role::STATE['DONE'] return false end end true end
Returns true if all the nodes are in hold state @return [true, false] true if all the nodes are in hold state
# File lib/models/service.rb, line 282 def all_roles_hold? @roles.each do |_name, role| if role.state != Role::STATE['HOLD'] return false end end true end
Returns true if all the nodes are correctly deployed @return [true, false] true if all the nodes are correctly deployed
# File lib/models/service.rb, line 258 def all_roles_running? @roles.each do |_name, role| if role.state != Role::STATE['RUNNING'] return false end end true end
Create a new service based on the template provided @param [String] template_json @return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 314 def allocate(template_json) template = JSON.parse(template_json) template['state'] = STATE['PENDING'] if template['roles'] template['roles'].each do |elem| elem['state'] ||= Role::STATE['PENDING'] end end template['start_time'] = Integer(Time.now) super(template.to_json, template['name']) end
# File lib/models/service.rb, line 181 def can_recover_deploy? RECOVER_DEPLOY_STATES.include? STATE_STR[state] end
# File lib/models/service.rb, line 193 def can_recover_deploy_nets? RECOVER_DEPLOY_NETS_STATES.include?(STATE_STR[state]) end
# File lib/models/service.rb, line 189 def can_recover_scale? RECOVER_SCALE_STATES.include? STATE_STR[state] end
# File lib/models/service.rb, line 185 def can_recover_undeploy? RECOVER_UNDEPLOY_STATES.include? STATE_STR[state] end
# File lib/models/service.rb, line 197 def can_recover_undeploy_nets? RECOVER_UNDEPLOY_NETS_STATES.include?(STATE_STR[state]) end
# File lib/models/service.rb, line 696 def can_scale? state == Service::STATE['RUNNING'] end
Return true if the service can be undeployed @return true if the service can be undeployed, false otherwise
# File lib/models/service.rb, line 164 def can_undeploy? # rubocop:disable Style/IfWithBooleanLiteralBranches if (transient_state? && state != Service::STATE['UNDEPLOYING']) || state == Service::STATE['DONE'] || failed_state? false else true end # rubocop:enable Style/IfWithBooleanLiteralBranches end
Return true if the service can be updated @return true if the service can be updated, false otherwise
# File lib/models/service.rb, line 177 def can_update? !transient_state? && !failed_state? end
Check that changes values are correct
@param template_json [String] New template @param append [Boolean] True to append template to the current
@return [Boolean, String] True, nil if everything is correct
False, attr if attr was changed
# File lib/models/service.rb, line 600 def check_new_template(template_json, append) template = JSON.parse(template_json) if append IMMUTABLE_ATTRS.each do |attr| next if template[attr].nil? return [false, "service/#{attr}"] end else if template['roles'].size != @roles.size return [false, 'service/roles size'] end IMMUTABLE_ATTRS.each do |attr| next if template[attr] == @body[attr] return [false, "service/#{attr}"] end template['roles'].each do |role| # Role name can't be changed, if it is changed some problems # may appear, as name is used to reference roles return [false, 'name'] unless @roles[role['name']] rc = @roles[role['name']].check_new_template(role) return rc unless rc[0] end end [true, nil] end
Check if role is terminated or not
@param role [OpenNebula::Role] Role
information
@return [Boolean]
True if the service should be undeployed False otherwise
# File lib/models/service.rb, line 707 def check_role(role) return unless @body['automatic_deletion'] return unless role.nodes.empty? ret = true @body['roles'].each {|r| ret &= r['nodes'].empty? } ret end
Changes the owner/group
@param [Integer] uid the new owner id. Use -1 to leave the current one @param [Integer] gid the new group id. Use -1 to leave the current one
@return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 473 def chown(uid, gid) old_uid = self['UID'].to_i old_gid = self['GID'].to_i rc = super(uid, gid) if OpenNebula.is_error?(rc) return rc end @roles.each do |_name, role| rc = role.chown(uid, gid) break if rc[0] == false end if rc[0] == false log_error('Chown operation failed, will try to rollback ' \ 'all VMs to the old user and group') update super(old_uid, old_gid) @roles.each do |_name, role| role.chown(old_uid, old_gid) end return OpenNebula::Error.new(rc[1]) end nil end
# File lib/models/service.rb, line 670 def delete_networks vnets = @body['networks_values'] vnets_failed = [] return if vnets.nil? vnets.each do |vnet| vnet.each do |_, net| key = net.keys.first next unless %w[template_id reserve_from].include?(key) rc = OpenNebula::VirtualNetwork.new_with_id( net['id'], @client ).delete next unless OpenNebula.is_error?(rc) vnets_failed << net['id'] end end vnets_failed end
# File lib/models/service.rb, line 634 def deploy_networks(deploy = true) if deploy body = JSON.parse(self['TEMPLATE/BODY']) else body = @body end return if body['networks_values'].nil? body['networks_values'].each do |vnet| vnet.each do |name, net| key = net.keys.first case key when 'id' next when 'template_id' rc = create_vnet(name, net) when 'reserve_from' rc = reserve(name, net) end return rc if OpenNebula.is_error?(rc) net['id'] = rc end end if deploy # Replace $attibute by the corresponding value resolve_attributes(body) # @body = template.to_hash update_body(body) end
Return true if the service is in failed state @return true if the service is in failed state, false otherwise
# File lib/models/service.rb, line 158 def failed_state? FAILED_STATES.include? STATE_STR[state] end
# File lib/models/service.rb, line 217 def gid self['GID'].to_i end
# File lib/models/service.rb, line 227 def hold? state_str == 'HOLD' end
Retrieves the information of the Service
and all its Nodes.
@return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 385 def info rc = super if OpenNebula.is_error?(rc) return rc end @roles = {} if @body['roles'] @body['roles'].each do |elem| elem['state'] ||= Role::STATE['PENDING'] role = Role.new(elem, self) @roles[role.name] = role end end nil end
Retrieves the information of the Service
and all its Nodes.
@return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 438 def info_roles @roles = {} if @body['roles'] @body['roles'].each do |elem| elem['state'] ||= Role::STATE['PENDING'] role = Role.new(elem, self) @roles[role.name] = role end end nil end
Add an error message in the service information that will be stored
in OpenNebula
@param [String] message
# File lib/models/service.rb, line 462 def log_error(message) add_log(Logger::ERROR, message) end
Add an info message in the service information that will be stored
in OpenNebula
@param [String] message
# File lib/models/service.rb, line 455 def log_info(message) add_log(Logger::INFO, message) end
Returns virtual networks IDs @return [Array] Array of integers containing the IDs
# File lib/models/service.rb, line 294 def networks(deploy) ret = [] return ret unless @body['networks_values'] @body['networks_values'].each do |vnet| vnet.each do |_, net| next if net.keys.first == 'id' && !deploy ret << net['id'].to_i end end ret end
Returns the on_hold service option @return [true, false] true if the on_hold option is enabled
# File lib/models/service.rb, line 223 def on_hold? @body['on_hold'] end
Recover a failed service. @return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 332 def recover if [Service::STATE['FAILED_DEPLOYING']].include?(state) @roles.each do |_name, role| if role.state == Role::STATE['FAILED_DEPLOYING'] role.set_state(Role::STATE['PENDING']) end end set_state(Service::STATE['DEPLOYING']) elsif state == Service::STATE['FAILED_SCALING'] @roles.each do |_name, role| if role.state == Role::STATE['FAILED_SCALING'] role.set_state(Role::STATE['SCALING']) end end set_state(Service::STATE['SCALING']) elsif state == Service::STATE['FAILED_UNDEPLOYING'] @roles.each do |_name, role| if role.state == Role::STATE['FAILED_UNDEPLOYING'] role.set_state(Role::STATE['RUNNING']) end end set_state(Service::STATE['UNDEPLOYING']) elsif state == Service::STATE['COOLDOWN'] @roles.each do |_name, role| if role.state == Role::STATE['COOLDOWN'] role.set_state(Role::STATE['RUNNING']) end end set_state(Service::STATE['RUNNING']) elsif state == Service::STATE['WARNING'] @roles.each do |_name, role| if role.state == Role::STATE['WARNING'] role.recover_warning end end else OpenNebula::Error.new('Action recover: Wrong state' \ " #{state_str}") end end
Removes a role from the service
@param name [String] Role
name to delete
# File lib/models/service.rb, line 426 def remove_role(name) @roles.delete(name) @body['roles'].delete_if do |role| role['name'] == name end end
Replaces this object’s client with a new one @param [OpenNebula::Client] owner_client the new client
# File lib/models/service.rb, line 233 def replace_client(owner_client) @client = owner_client end
Returns the running_status_vm option @return [true, false] true if the running_status_vm option is enabled
# File lib/models/service.rb, line 209 def report_ready? @body['ready_status_gate'] end
Return true if the service is running @return true if the service is runnning, false otherwise
# File lib/models/service.rb, line 203 def running? state_str == 'RUNNING' end
Sets a new state @param [Integer] the new state @return [true, false] true if the value was changed rubocop:disable Naming/AccessorMethodName
# File lib/models/service.rb, line 241 def set_state(state) # rubocop:enable Naming/AccessorMethodName if state < 0 || state > STATE_STR.size return false end @body['state'] = state.to_i msg = "New state: #{STATE_STR[state]}" Log.info LOG_COMP, msg, id log_info(msg) true end
# File lib/models/service.rb, line 549 def shutdown_action @body['shutdown_action'] end
Returns the service state @return [Integer] the service state
# File lib/models/service.rb, line 134 def state @body['state'].to_i end
Returns the string representation of the service state @return the state string
# File lib/models/service.rb, line 146 def state_str STATE_STR[state] end
Returns the service strategy @return [String] the service strategy
# File lib/models/service.rb, line 140 def strategy @body['deployment'] end
Returns true if the service is in transient state @return true if the service is in transient state, false otherwise
# File lib/models/service.rb, line 152 def transient_state? TRANSIENT_STATES.include? STATE_STR[state] end
# File lib/models/service.rb, line 213 def uname self['UNAME'] 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
# File lib/models/service.rb, line 561 def update(template_json = nil, append = false) if template_json template = JSON.parse(template_json) if append rc = info if OpenNebula.is_error? rc return rc end template = @body.merge(template) end template_json = template.to_json end super(template_json, 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
# File lib/models/service.rb, line 589 def update_raw(template_raw, append = false) super(template_raw, append) end
Updates a role @param [String] role_name @param [String] template_json @return [nil, OpenNebula::Error
] nil in case of success, Error
otherwise
# File lib/models/service.rb, line 512 def update_role(role_name, template_json) if ![Service::STATE['RUNNING'], Service::STATE['WARNING']] .include?(state) return OpenNebula::Error.new('Update role: Wrong state' \ " #{state_str}") end template = JSON.parse(template_json) # TODO: Validate template? role = @roles[role_name] if role.nil? return OpenNebula::Error.new("ROLE \"#{role_name}\" " \ 'does not exist') end rc = role.update(template) if OpenNebula.is_error?(rc) return rc end # TODO: The update may not change the cardinality, only # the max and min vms... role.set_state(Role::STATE['SCALING']) role.set_default_cooldown_duration set_state(Service::STATE['SCALING']) update end
Private Instance Methods
@param [Logger::Severity] severity @param [String] message
# File lib/models/service.rb, line 744 def add_log(severity, message) severity_str = Logger::SEV_LABEL[severity][0..0] @body['log'] ||= [] @body['log'] << { :timestamp => Time.now.to_i, :severity => severity_str, :message => message } # Truncate the number of log entries @body['log'] = @body['log'].last(MAX_LOG) end
# File lib/models/service.rb, line 758 def create_vnet(name, net) extra = '' extra = net['extra'] if net.key? 'extra' OpenNebula::VNTemplate.new_with_id( net['template_id'].to_i, @client ).instantiate(get_vnet_name(name), extra) end
# File lib/models/service.rb, line 782 def get_vnet_name(net) "#{net}-#{id}" end
# File lib/models/service.rb, line 768 def reserve(name, net) extra = '' extra = net['extra'] if net.key? 'extra' return false if !extra || extra.empty? extra.concat("\nNAME=\"#{get_vnet_name(name)}\"\n") OpenNebula::VirtualNetwork.new_with_id( net['reserve_from'].to_i, @client ).reserve_with_extra(extra) end
# File lib/models/service.rb, line 786 def resolve_attributes(template) template['roles'].each do |role| if role['vm_template_contents'] # $CUSTOM1_VAR Any word character # (letter, number, underscore) role['vm_template_contents'].scan(/\$(\w+)/).each do |key| # Check if $ var value is in custom_attrs_values if !template['custom_attrs_values'].nil? && template['custom_attrs_values'].key?(key[0]) role['vm_template_contents'].gsub!( '$'+key[0], template['custom_attrs_values'][key[0]] ) next end # Check if $ var value is in networks net = template['networks_values'] .find {|att| att.key? key[0] } next if net.nil? role['vm_template_contents'].gsub!( '$'+key[0], net[net.keys[0]]['id'].to_s ) end end next unless role['user_inputs_values'] role['vm_template_contents'] ||= '' role['user_inputs_values'].each do |key, value| role['vm_template_contents'] += "\n#{key}=\"#{value}\"" end end end
# File lib/models/service.rb, line 725 def update_body(body) @body = body # Update @roles attribute with the new @body content @roles = {} if @body['roles'] @body['roles'].each do |elem| elem['state'] ||= Role::STATE['PENDING'] role = Role.new(elem, self) @roles[role.name] = role end end # Update @xml attribute with the new body content @xml.at_xpath('/DOCUMENT/TEMPLATE/BODY').children[0].content = @body end