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

client[R]
roles[R]

Public Instance Methods

add_role(template) click to toggle source

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
all_roles_done?() click to toggle source

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
all_roles_hold?() click to toggle source

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
all_roles_running?() click to toggle source

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
allocate(template_json) click to toggle source

Create a new service based on the template provided @param [String] template_json @return [nil, OpenNebula::Error] nil in case of success, Error

otherwise
Calls superclass method
# 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
can_recover_deploy?() click to toggle source
# File lib/models/service.rb, line 181
def can_recover_deploy?
    RECOVER_DEPLOY_STATES.include? STATE_STR[state]
end
can_recover_deploy_nets?() click to toggle source
# File lib/models/service.rb, line 193
def can_recover_deploy_nets?
    RECOVER_DEPLOY_NETS_STATES.include?(STATE_STR[state])
end
can_recover_scale?() click to toggle source
# File lib/models/service.rb, line 189
def can_recover_scale?
    RECOVER_SCALE_STATES.include? STATE_STR[state]
end
can_recover_undeploy?() click to toggle source
# File lib/models/service.rb, line 185
def can_recover_undeploy?
    RECOVER_UNDEPLOY_STATES.include? STATE_STR[state]
end
can_recover_undeploy_nets?() click to toggle source
# File lib/models/service.rb, line 197
def can_recover_undeploy_nets?
    RECOVER_UNDEPLOY_NETS_STATES.include?(STATE_STR[state])
end
can_scale?() click to toggle source
# File lib/models/service.rb, line 696
def can_scale?
    state == Service::STATE['RUNNING']
end
can_undeploy?() click to toggle source

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
can_update?() click to toggle source

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_new_template(template_json, append) click to toggle source

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_role(role) click to toggle source

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
chown(uid, gid) click to toggle source

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
Calls superclass method
# 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
delete_networks() click to toggle source
# 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
deploy_networks(deploy = true) click to toggle source
# 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
failed_state?() click to toggle source

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
gid() click to toggle source
# File lib/models/service.rb, line 217
def gid
    self['GID'].to_i
end
hold?() click to toggle source
# File lib/models/service.rb, line 227
def hold?
    state_str == 'HOLD'
end
info() click to toggle source

Retrieves the information of the Service and all its Nodes.

@return [nil, OpenNebula::Error] nil in case of success, Error

otherwise
Calls superclass method
# 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
info_roles() click to toggle source

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
log_error(message) click to toggle source

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
log_info(message) click to toggle source

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
networks(deploy) click to toggle source

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
on_hold?() click to toggle source

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() click to toggle source

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
remove_role(name) click to toggle source

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
replace_client(owner_client) click to toggle source

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
report_ready?() click to toggle source

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
running?() click to toggle source

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
set_state(state) click to toggle source

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
shutdown_action() click to toggle source
# File lib/models/service.rb, line 549
def shutdown_action
    @body['shutdown_action']
end
state() click to toggle source

Returns the service state @return [Integer] the service state

# File lib/models/service.rb, line 134
def state
    @body['state'].to_i
end
state_str() click to toggle source

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
strategy() click to toggle source

Returns the service strategy @return [String] the service strategy

# File lib/models/service.rb, line 140
def strategy
    @body['deployment']
end
transient_state?() click to toggle source

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
uname() click to toggle source
# File lib/models/service.rb, line 213
def uname
    self['UNAME']
end
update(template_json = nil, append = false) click to toggle source

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
Calls superclass method
# 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
update_raw(template_raw, append = false) click to toggle source

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
Calls superclass method
# File lib/models/service.rb, line 589
def update_raw(template_raw, append = false)
    super(template_raw, append)
end
update_role(role_name, template_json) click to toggle source

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

add_log(severity, message) click to toggle source

@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
create_vnet(name, net) click to toggle source
# 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
get_vnet_name(net) click to toggle source
# File lib/models/service.rb, line 782
def get_vnet_name(net)
    "#{net}-#{id}"
end
reserve(name, net) click to toggle source
# 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
resolve_attributes(template) click to toggle source
# 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
update_body(body) click to toggle source
# 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