class Chef::Provisioning::OpenNebulaDriver::FlowLib

Implementation.

Constants

ROLE_NOT_EXIST

ROLE:

ROLE_NO_VMS
ROLE_PENDING
ROLE_POWEROFF
ROLE_RUNNING
ROLE_STOPPED
ROLE_SUSPENDED
ROLE_UNDEPLOYED
SERVICE_COOLDOWN
SERVICE_DEPLOYING
SERVICE_DONE
SERVICE_FAILED_DEPLOYING
SERVICE_FAILED_SCALING
SERVICE_PENDING

SERVICE:

SERVICE_RUNNING
SERVICE_SCALING
SERVICE_UNDEPLOYING

Attributes

flow_url[RW]
password[RW]
username[RW]

Public Class Methods

new(url, one_auth) click to toggle source
# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 66
def initialize(url, one_auth)
  @flow_url = url
  @username, _, @password = one_auth.rpartition(':')
end

Public Instance Methods

chmod_service(sid, octet) click to toggle source

Modifies the permissions of a service

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 456
def chmod_service(sid, octet)
  url = "/service/#{sid}/action"
  perform_action(url, 'chmod', :octet => octet)
end
chmod_template(template_id, octet) click to toggle source

Modifies the permissions of a template

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 413
def chmod_template(template_id, octet)
  url = "/service_template/#{template_id}/action"
  perform_action(url, 'chmod', :octet => octet)
end
create_template(payload) click to toggle source

Creates a template in ONE

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 382
def create_template(payload)
  request(:post, '/service_template', payload)
  true
end
delete_service(service_id) click to toggle source

Deletes a service in ONE

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 423
def delete_service(service_id)
  request(:delete, "/service/#{service_id}")
end
delete_template(template_id) click to toggle source

Deletes a template in ONE

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 393
def delete_template(template_id)
  request(:delete, "/service_template/#{template_id}")
end
exists?(type, name) click to toggle source

Check if a service or template exists

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 252
def exists?(type, name)
  !get_ids(type, name).empty?
end
get_ids(type, name) click to toggle source

Returns all of the IDs of a service or template that matches a name

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 229
def get_ids(type, name)
  response = request(:get, type == :template ? '/service_template' : '/service')
  ids = []
  data = JSON.parse(response, :symbolize_names => true)
  return [] if data[:DOCUMENT_POOL][:DOCUMENT].nil?
  data[:DOCUMENT_POOL][:DOCUMENT].each { |e| ids.push(e[:ID].to_i) if e[:NAME] == name }
  ids
end
get_permissions(type, id) click to toggle source

Gets permission of service or template

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 257
def get_permissions(type, id)
  id = id.to_s
  response = request(:get, type == :template ? '/service_template' : '/service')
  data = JSON.parse(response, :symbolize_names => true)[:DOCUMENT_POOL][:DOCUMENT]
  data.each do |tpl|
    next unless tpl[:ID] == id
    perms = tpl[:PERMISSIONS]
    mode = ''
    [:OWNER_U, :OWNER_M, :OWNER_A, :GROUP_U, :GROUP_M, :GROUP_A, :OTHER_U, :OTHER_M, :OTHER_A].each { |m| mode += perms[m] }
    return mode.to_i(2).to_s(8)
  end
  fail "#{type} with id=#{id} does not exist."
end
get_reduced_service_template(id) click to toggle source

Returns the template of a service with runtime content removed

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 318
def get_reduced_service_template(id)
  response = request(:get, "/service/#{id}")
  template_from_one = JSON.parse(response, :symbolize_names => true)[:DOCUMENT][:TEMPLATE][:BODY]
  service_name = template_from_one[:name]
  [:log, :name, :state, :custom_attrs_values].each { |key| template_from_one.delete(key) }
  template_from_one[:roles].each do |role|
    role[:nodes].each do |node|
      unless node[:running]
        Chef::Log.warn("A node in role '#{node[:vm_info][:VM][:USER_TEMPLATE][:ROLE_NAME]}' of service '#{service_name}' is not normal!")
      end
    end
    [:cardinality, :nodes, :state, :disposed_nodes, :cooldown_end, :last_vmname, :user_inputs_values, :vm_template_contents].each { |key| role.delete(key) }
  end
  template_from_one
end
get_role(service_id, role_name) click to toggle source

Returns a role of a service

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 343
def get_role(service_id, role_name)
  response = request(:get, "/service/#{service_id}")
  JSON.parse(response, :symbolize_names => true)[:DOCUMENT][:TEMPLATE][:BODY][:roles].each { |role| return role if role[:name] == role_name }
  fail "#{role_name} doesn't seem to exist!"
end
get_role_cardinality(id, role_name) click to toggle source

Gets the cardinality of a role

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 368
def get_role_cardinality(id, role_name)
  get_role(id, role_name)[:cardinality].to_i
end
get_role_state(id, role_name) click to toggle source

Returns the state of a role It seems that regardless of the state of the VMs, the state of a role will be RUNNING So I will be doing a workaround where I will return that it's SUSPENDED if all of the VMs are SUSPENDED nil => role doesn't exist, -1 => there are no VMs, 1 => PENDING, 3 => RUNNING, 4 => STOPPED, 5 => SUSPENDED, 8 => POWEROFF, 9 => UNDEPLOYED

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 354
def get_role_state(id, role_name)
  role = get_role(id, role_name)
  return -1 if role[:cardinality].to_i == 0
  state_counter = { 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 8 => 0, 9 => 0 }
  role[:nodes].each do |node|
    state = node[:vm_info][:VM][:STATE].to_i
    fail "UNSUPPORTED STATE #{state}" if state_counter[state].nil?
    state_counter[state] += 1
    return state if state_counter[state] == role[:cardinality].to_i
  end
  state_counter # Return the hash of counters if states are staggered
end
get_service_ids(name) click to toggle source

Wrapper for get_ids

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 298
def get_service_ids(name)
  get_ids(:service, name)
end
get_service_permissions(id) click to toggle source

Wrapper for get_permissions

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 313
def get_service_permissions(id)
  get_permissions(:service, id)
end
get_service_state(id) click to toggle source

Gets the state of a service 0 => PENDING, 1 => DEPLOYING, 2 => RUNNING, 3 => UNDEPLOYING, 5 => DONE, 7 => FAILED_DEPLOYING, 8 => SCALING, 9 => FAILED_SCALING, 10 => COOLDOWN

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 337
def get_service_state(id)
  response = request(:get, "/service/#{id}")
  JSON.parse(response, :symbolize_names => true)[:DOCUMENT][:TEMPLATE][:BODY][:state].to_i
end
get_template(id) click to toggle source

Gets a template given a template ID

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 287
def get_template(id)
  response = request(:get, "/service_template/#{id}")
  JSON.parse(response, :symbolize_names => true)[:DOCUMENT][:TEMPLATE][:BODY]
end
get_template_ids(name) click to toggle source

Wrapper for get_ids

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 272
def get_template_ids(name)
  get_ids(:template, name)
end
get_template_permissions(id) click to toggle source

Wrapper for get_permissions

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 293
def get_template_permissions(id)
  get_permissions(:template, id)
end
get_unique_id(type, name, nil_if_none = false) click to toggle source

Gets a single ID of a service or template, fails if there's not exactly 1, or returns nil if there 0 and nil_if_none

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 239
def get_unique_id(type, name, nil_if_none = false)
  matches = get_ids(type, name)
  if matches.empty?
    return nil if nil_if_none
    fail "There are no OneFlow #{type}s with the name '#{name}'"
  elsif matches.length > 1
    fail "There are multiple OneFlow #{type}s with the name '#{name}'"
  else
    matches[0]
  end
end
get_unique_service_id(name, nil_if_none = false) click to toggle source

Wrapper for get_unique_id

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 303
def get_unique_service_id(name, nil_if_none = false)
  get_unique_id(:service, name, nil_if_none)
end
get_unique_template_id(name, nil_if_none = false) click to toggle source

Wrapper for get_unique_id

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 277
def get_unique_template_id(name, nil_if_none = false)
  get_unique_id(:template, name, nil_if_none)
end
hash_eq?(hash1, hash2) click to toggle source

Checks if two hashes are equal, ignore array order

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 224
def hash_eq?(hash1, hash2)
  recursive_array_to_set(Marshal.load(Marshal.dump(hash1))) == recursive_array_to_set(Marshal.load(Marshal.dump(hash2)))
end
instantiate_template(tid, template, service_name) click to toggle source

Spawns a service from a template

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 398
def instantiate_template(tid, template, service_name)
  url = "/service_template/#{tid}/action"
  perform_action(url, 'instantiate', :merge_template => template)

  service_id = get_unique_service_id(service_name)
  state = nil
  while state != SERVICE_RUNNING
    Chef::Log.info("Waiting for RUNNING for '#{service_name}'")
    sleep(15)
    state = get_service_state(service_id)
    fail "Service failed to deploy  ...\nThere's probably something wrong with your template." if state == SERVICE_FAILED_DEPLOYING
  end
end
merge_template(base_tpl, new_tpl, overwrite_name = false, delete_roles = true) click to toggle source

Performs a overwrite-merge of two OneFlow templates, any key with nil value will be deleted

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 158
def merge_template(base_tpl, new_tpl, overwrite_name = false, delete_roles = true)
  fail 'Service template name changing is not supported.' if new_tpl.key?(:name) && !overwrite_name && base_tpl[:name] != new_tpl[:name]
  special_merge_hash(base_tpl, new_tpl, delete_roles)
end
normalize_template(name, driver, template, allow_no_roles = false, allow_no_vm_template = false) click to toggle source

Performs some basic verifications on a template Also adds in default values

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 77
def normalize_template(name, driver, template, allow_no_roles = false, allow_no_vm_template = false)
  template = {
    :deployment => 'straight',
    :name => name,
    :ready_status_gate => false,
    :description => '',
    :roles => [],
    :custom_attrs => {}
  }.merge(template)

  fail "You must specify at least 1 role for template '#{name}'" if template[:roles].empty? && !allow_no_roles
  id_cache = {}
  template[:roles].map! do |role|
    fail "Some roles in template '#{name}' are missing a name." if role[:name].nil?
    fail "Role '#{role[:name]}' in template '#{name}' is missing a vm_template." if role[:vm_template].nil? && !allow_no_vm_template
    new_role = {
      :cardinality => role[:min_vms] || 1,
      :elasticity_policies => [],
      :scheduled_policies => []
    }.merge(role)
    if role[:vm_template].is_a?(String)
      if id_cache[role[:vm_template]].nil?
        template_from_one = driver.one.get_resource(:template, :name => role[:vm_template])
        fail "Could not find a template with the name '#{role[:vm_template]}'" if template_from_one.nil?
        id = template_from_one.to_hash['VMTEMPLATE']['ID'].to_i
        id_cache[role[:vm_template]] = id
        new_role[:vm_template] = id
      else
        new_role[:vm_template] = id_cache[role[:vm_template]]
      end
    end
    new_role
  end
  template
end
override_failsafe_warn() click to toggle source

Issues warnings for a failsafe override

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 164
def override_failsafe_warn
  Chef::Log.warn('You have chose to use an action that is untested / partially implemented.')
  Chef::Log.warn('Specifically, the driver will send the appropriate POST request to the Flow API')
  Chef::Log.warn('But the driver will not verify that the action ran successfully, or ran at all.')
  Chef::Log.warn('Moreover, the driver will not wait for the action complete, as in, the action will')
  Chef::Log.warn('run asynchronously, meaning dependent actions after this one may fail.')
  Chef::Log.warn('Use at your own risk. Please report any issues.')
end
perform_action(url, action, params) click to toggle source

Creates a post request for an action

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 373
def perform_action(url, action, params)
  request(:post, url, "{\"action\":{\"perform\":\"#{action}\",\"params\":#{params.to_json}}}")
end
recover_service(service_id, service_name) click to toggle source

Performs the recover action on a service

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 442
def recover_service(service_id, service_name)
  url = "/service/#{service_id}/action"
  perform_action(url, 'recover', {})

  state = nil
  while state != SERVICE_RUNNING
    Chef::Log.info("Waiting for RUNNING for '#{service_name}'")
    sleep(15)
    state = get_service_state(service_id)
    fail 'Service failed to recover ...' if state == SERVICE_FAILED_DEPLOYING
  end
end
recursive_array_to_set(object) click to toggle source

Converts all arrays to sets

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 209
def recursive_array_to_set(object)
  case object
  when Array
    return object.map { |e| recursive_array_to_set(e) }.to_set
  when Hash
    object.each do |key, value|
      object[key] = recursive_array_to_set(value)
    end
    return object
  else
    return object
  end
end
request(method, url, payload = '{}') click to toggle source

REST call to flow api

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 180
def request(method, url, payload = '{}')
  case payload
  when Hash
    RestClient::Request.execute(
      method: method,
      url: @flow_url + url,
      user: @username,
      password: @password,
      payload: payload.to_json
    )
  when String
    JSON.parse(payload)
    RestClient::Request.execute(
      method: method,
      url: @flow_url + url,
      user: @username,
      password: @password,
      payload: payload
    )
  else
    fail 'Payload must be hash or json string.'
  end
rescue JSON::ParserError
  fail 'Malformed json string.'
rescue RestClient::ResourceNotFound, RestClient::BadRequest, RestClient::InternalServerError => e
  raise OpenNebulaException, "#{e}\nThere's a problem. Here's a hint:\n#{e.response}"
end
role_action(sid, role_name, action, period, number, desired_state = nil) click to toggle source

Performs an action on a role

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 462
def role_action(sid, role_name, action, period, number, desired_state = nil)
  validate_role_action(period, number)
  url = "/service/#{sid}/role/#{role_name}/action"
  perform_action(url, action, :period => period, :number => number)

  state = nil
  while state != desired_state
    Chef::Log.info("Waiting for #{action} to complete")
    sleep(15)
    state = get_role_state(sid, role_name)
    fail "#{action} failed. Got unsupported state #{state}" unless Set[ROLE_NO_VMS, ROLE_PENDING, ROLE_RUNNING, ROLE_STOPPED, ROLE_SUSPENDED, ROLE_POWEROFF, ROLE_UNDEPLOYED].include?(state)
  end
end
role_scale(service_id, service_name, role_name, card, force) click to toggle source

Scales a role to a new cardinality

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 477
def role_scale(service_id, service_name, role_name, card, force)
  request(:put, "/service/#{service_id}/role/#{role_name}", :cardinality => card, :force => force)

  state = nil
  while state != SERVICE_RUNNING
    Chef::Log.info("Waiting for RUNNING for '#{service_name}'")
    sleep(15)
    state = get_service_state(service_id)
    fail 'Service failed to scale ...' if state == SERVICE_FAILED_SCALING
  end
end
service_exists?(name) click to toggle source

Wrapper for exists?

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 308
def service_exists?(name)
  exists?(:service, name)
end
shutdown_service(service_name, sid) click to toggle source

Performs shutdown on an entire service

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 428
def shutdown_service(service_name, sid)
  url = "/service/#{sid}/action"
  perform_action(url, 'shutdown', {})

  state = nil
  while state != SERVICE_DONE
    Chef::Log.info("Waiting for SHUTDOWN COMPLETE for '#{service_name}'")
    sleep(15)
    state = get_service_state(sid)
    fail 'Service failed to shutdown ...' unless Set[SERVICE_RUNNING, SERVICE_UNDEPLOYING, SERVICE_DONE].include?(state)
  end
end
special_merge_hash(base_hash, new_hash, delete_roles = true) click to toggle source

Helper for merge_template

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 114
def special_merge_hash(base_hash, new_hash, delete_roles = true)
  keys_to_delete = []
  merged = base_hash.merge(new_hash) do |key, oldval, newval|
    # This is probably a redundant fail ...
    fail 'Class of values in the templates must remain the same. If you want to delete an entry, set it to nil.' unless
      oldval.is_a?(newval.class) || newval.is_a?(NilClass) || key == :vm_template
    case newval
    when NilClass
      keys_to_delete.push(key)
      nil
    when Array
      if key == :roles
        new_array = []
        old_as_hash = Hash[oldval.collect { |role| [role[:name], role] }]

        newval.each do |role|
          if role.key?(:delete_role) && delete_roles
            old_as_hash.delete(role[:name])
            next
          end
          fail 'All roles must have a name.' if role[:name].nil?
          if old_as_hash.key?(role[:name])
            new_array.push(special_merge_hash(old_as_hash[role[:name]], role))
            old_as_hash.delete(role[:name])
          else
            new_array.push(role)
          end
        end

        new_array + old_as_hash.values
      else
        newval
      end
    when Hash
      special_merge_hash(oldval, newval)
    else
      newval
    end
  end
  keys_to_delete.each { |key| merged.delete(key) }
  merged
end
template_exists?(name) click to toggle source

Wrapper for exists?

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 282
def template_exists?(name)
  exists?(:template, name)
end
update_template(template_name, payload) click to toggle source

Updates a template in ONE

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 388
def update_template(template_name, payload)
  request(:put, "/service_template/#{get_unique_template_id(template_name)}", payload)
end
validate_role_action(period, number) click to toggle source

Validate the attributes period and number

# File lib/chef/provisioning/opennebula_driver/flow_lib.rb, line 174
def validate_role_action(period, number)
  fail "Make sure 'period' >= 0" if !period.empty? && period.to_i < 0
  fail "Make sure 'number' >= 0" if !number.empty? && number.to_i < 0
end