ManageIQ/manageiq-content

View on GitHub
content/automate/ManageIQ/System/CommonMethods/QuotaMethods.class/__methods__/requested.rb

Summary

Maintainability
A
15 mins
Test Coverage
#
# Description: Calculate requested quota items.
#

def request_info
  @service = ($evm.root['vmdb_object_type'] == 'service_template_provision_request')
  @miq_request = $evm.root['miq_request']
  $evm.log(:info, "Request: #{@miq_request.description} id: #{@miq_request.id} ")
end

def cloud?(prov_type)
  %w(amazon openstack google azure).include?(prov_type)
end

def calculate_requested(options_hash = {})
  total_vms = get_total_requested(options_hash, :number_of_vms)
  total_vms = 1 if total_vms.zero?
  {:vms     => total_vms,
   :storage => get_total_requested(options_hash, :storage) * total_vms,
   :memory  => get_total_requested(options_hash, :vm_memory) * total_vms,
   :cpu     => get_total_requested(options_hash, :number_of_cpus) * total_vms}
end

def get_total_requested(options_hash, prov_option)
  total_requested = collect_template_totals(prov_option)
  total_requested = request_totals(total_requested.to_i,
                                   collect_dialog_totals(prov_option, options_hash).to_i) if options_hash
  total_requested
end

def request_totals(template_totals, dialog_totals)
  dialog_totals.positive? ? dialog_totals : template_totals
end

def collect_template_totals(prov_option)
  @service ? collect_totals(service_prov_option(prov_option)) : collect_totals(vm_prov_option_value(prov_option))
end

def get_option_value(request, option)
  request.get_option(option).to_i
end

def service_prov_option(prov_option, options_array = [])
  @service_template.service_resources.each do |child_service_resource|
    if @service_template.service_type == 'composite'
      composite_service_options_value(child_service_resource, prov_option, options_array)
    else
      next if @service_template.prov_type.starts_with?("generic")
      service_prov_option_value(prov_option, child_service_resource.resource, options_array)
    end
  end
  options_array
end

def vendor
  @reconfigure_request ? @vm.vendor : @miq_request.source.vendor
end

def service_prov_option_value(prov_option, resource, options_array = [])
  args_hash = {:prov_option   => prov_option,
               :options_array => options_array,
               :resource      => resource,
               :flavor        => flavor_obj(resource.get_option(:instance_type)),
               :number_of_vms => get_option_value(resource, :number_of_vms),
               :cloud         => cloud?(resource.get_option(:st_prov_type))}

  case prov_option
  when :vm_memory
    requested_memory(args_hash, get_option_value(resource, :prov_type))
  when :number_of_cpus
    requested_number_of_cpus(args_hash)
  when :storage
    requested_storage(args_hash)
  else
    options_value(args_hash)
  end
  options_array
end

def vm_provision_cloud?
  @miq_request.source.try(:cloud) || false
end

def flavor_obj(id)
  @flavor_id ||= id
  vmdb_object('flavor', @flavor_id)
end

def vm_prov_option_value(prov_option, options_array = [])
  args_hash = {:prov_option   => prov_option,
               :options_array => options_array,
               :resource      => @miq_request,
               :flavor        => flavor_obj(@miq_request.get_option(:instance_type)),
               :number_of_vms => get_option_value(@miq_request, :number_of_vms),
               :cloud         => vm_provision_cloud?}
  # number_of_vms doesn't exist for VmReconfigureRequest

  case prov_option
  when :vm_memory
    requested_memory(args_hash, vendor)
  when :number_of_cpus
    requested_number_of_cpus(args_hash)
  when :storage
    requested_storage(args_hash)
  else
    options_value(args_hash)
  end
  options_array
end

def requested_memory(args_hash, vendor)
  memory = get_option_value(args_hash[:resource], :vm_memory)
  memory = memory.megabytes if %w(amazon openstack google).exclude?(vendor)
  args_hash[:prov_value] = memory
  if @reconfigure_request && args_hash[:resource].options[:vm_memory]
    # Account for the VM's existing memory
    args_hash[:prov_value] = args_hash[:prov_value].to_i - @vm.hardware.memory_mb.to_i.megabytes

    $evm.log(:info, "vm_memory:         #{@vm.hardware.memory_mb.to_i.megabytes}")
    $evm.log(:info, "requested_memory:  #{args_hash[:prov_value].to_i}")
    @check_quota = true if args_hash[:prov_value].to_i > 0
  end
  request_hash_value(args_hash)
end

def requested_number_of_cpus(args_hash)
  cpu_in_request = get_option_value(args_hash[:resource], :number_of_sockets) *
                   get_option_value(args_hash[:resource], :cores_per_socket)
  cpu_in_request = get_option_value(args_hash[:resource], args_hash[:number_of_cpus]) if cpu_in_request.zero?
  args_hash[:prov_value] = cpu_in_request

  if @reconfigure_request && args_hash[:resource].options[:number_of_sockets]
    # Account for the VM's existing CPUs
    args_hash[:prov_value] = args_hash[:prov_value].to_i - @vm.hardware.cpu_total_cores.to_i \
      * @vm.hardware.cpu_cores_per_socket.to_i

    $evm.log(:info, "vm_number_of_cpus:         #{@vm.hardware.cpu_total_cores.to_i \
      * @vm.hardware.cpu_cores_per_socket.to_i}")
    $evm.log(:info, "requested_number_of_cpus:  #{args_hash[:prov_value].to_i}")
    @check_quota = true if args_hash[:prov_value].to_i > 0
  end
  request_hash_value(args_hash)
end

def vmdb_object(model, id)
  $evm.vmdb(model, id.to_i) if model && id
end

def get_disk_size(disk_name)
  mydisk = $evm.vmdb('disk').find_by(:filename => disk_name)
  raise "ERROR - #{disk_name} not found for Reconfiguration" if mydisk.nil?
  mydisk.size.to_i
end

def requested_storage(args_hash)
  if @reconfigure_request
    args_hash[:prov_value] = 0
    # Adding/removing disks only supported for VMware
    if args_hash[:resource].options[:disk_add]
      args_hash[:resource].options[:disk_add].each do |disk|
        $evm.log(:info, "Adding a disk:  #{disk.inspect}")
        args_hash[:prov_value] += disk['disk_size_in_mb'].to_i.megabytes
      end
    end
    if args_hash[:resource].options[:disk_remove]
      args_hash[:resource].options[:disk_remove].each do |disk|
        $evm.log(:info, "Reconfigure Disk Removal: #{disk.inspect}")
        disk = HashWithIndifferentAccess.new(disk)
        current_size = get_disk_size(disk[:disk_name])
        $evm.log(:info, "Reconfigure Disk Removal Disk: <#{disk[:disk_name]}> Disk Size: <#{current_size.to_s(:human_size)}>")
        args_hash[:prov_value] -= current_size
      end
    end
    args_hash[:resource].options[:disk_resize]&.each do |disk|
      # Disk_resize only supports increasing the size.
      $evm.log(:info, "Reconfigure Disk Resize:  #{disk.inspect}")
      current_size = get_disk_size(disk['disk_name'])
      $evm.log(:info, "Current disk size: #{current_size.to_s(:human_size)}")
      args_hash[:prov_value] += disk['disk_size_in_mb'].to_i.megabytes - current_size
    end
  else
    vm_size = args_hash[:resource].vm_template.provisioned_storage
    args_hash[:prov_value] = vm_size
  end

  if @reconfigure_request
    $evm.log(:info, "VM Reconfigure storage change: #{args_hash[:prov_value].to_s(:human_size)}")
    @check_quota = true if args_hash[:prov_value].to_i > 0
  end
  request_hash_value(args_hash)
end

def request_object?(object)
  object.respond_to?('get_option')
end

def options_value(args_hash)
  return unless request_object?(args_hash[:resource])
  args_hash[:prov_value] = args_hash[:resource].get_option(args_hash[:prov_option])
  request_hash_value(args_hash)
end

def collect_totals(array)
  array.collect(&:to_i).inject(&:+).to_i
end

def collect_dialog_totals(prov_option, options_hash)
  dialog_values(prov_option, options_hash, dialog_array = [])
  total = collect_totals(dialog_array)
  if prov_option == :number_of_cpus
    options_hash.each_value do |options|
      if options.keys.include?(:number_of_sockets) || options.keys.include?(:cores_per_socket)
        $evm.log(:info, "Recalculating number of cpus based on dialog overrides")
        total = recalculate_number_of_cpus(service_resource, options[:number_of_sockets].to_i, options[:cores_per_socket].to_i)
      end
    end
  end
  total
end

def service_resource
  resource = nil
  @service_template.service_resources.each do |child_service_resource|
    next if @service_template.service_type == 'composite'
    next if @service_template.prov_type.starts_with?("generic")
    resource = child_service_resource.resource
  end
  resource
end

def recalculate_number_of_cpus(resource, override_number_of_sockets, override_cores_per_socket)
  $evm.log(:info, "Recalculating the number_of_cpus resource: #{resource} override_number_of_sockets: #{override_number_of_sockets} override_cores_per_socket: #{override_cores_per_socket}")
  if override_number_of_sockets.positive? && override_cores_per_socket.positive?
    cpu_in_request = override_number_of_sockets * override_cores_per_socket
  elsif override_number_of_sockets.positive?
    cpu_in_request = get_option_value(resource, :cores_per_socket) * override_number_of_sockets
  elsif override_cores_per_socket.positive?
    cpu_in_request = get_option_value(resource, :number_of_sockets) * override_cores_per_socket
  end
  cpu_in_request if cpu_in_request.to_i.positive?
end

def dialog_values(prov_option, options_hash, dialog_array)
  args_hash = {:prov_option   => prov_option,
               :options_array => dialog_array,
               :cloud         => false}

  options_hash.each do |_sequence_id, options|
    args_hash[:prov_value] = options[prov_option]
    args_hash[:flavor] = flavor_obj(options[:instance_type])
    request_hash_value(args_hash)
  end
end

def request_hash_value(args_hash)
  return if cloud_value(args_hash)

  default_option(args_hash[:prov_value], args_hash[:options_array])
end

def provision_type(resource)
  if @service
    resource.get_option(:st_prov_type)
  else
    resource.source.try(:vendor)
  end
end

def cloud_storage(args_hash)
  flavor = args_hash[:flavor]
  storage = if provision_type(args_hash[:resource]) == 'google'
              get_option_value(args_hash[:resource], :boot_disk_size).gigabytes
            else
              flavor.root_disk_size.to_i + flavor.ephemeral_disk_size.to_i + flavor.swap_disk_size.to_i
            end

  storage += cloud_volume_storage(args_hash)
  $evm.log(:info, "Retrieving cloud storage #{storage}")
  default_option(storage, args_hash[:options_array])
end

def cloud_volume_storage(args_hash)
  storage = 0
  if args_hash[:resource].options[:volumes]
    args_hash[:resource].options[:volumes].each do |v|
      $evm.log(:info, "Adding volume to storage:  #{v.inspect} ")
      storage += v[:size].to_i.gigabytes
    end
  end
  storage
end

def cloud_number_of_cpus(args_hash)
  flavor = args_hash[:flavor]
  $evm.log(:info, "Retrieving cloud flavor #{flavor.name} cpus => #{flavor.cpus}")
  default_option(flavor.cpus, args_hash[:options_array])
end

def cloud_vm_memory(args_hash)
  flavor = args_hash[:flavor]
  $evm.log(:info, "Retrieving flavor #{flavor.name} memory => #{flavor.memory}")
  default_option(flavor.memory, args_hash[:options_array])
end

def cloud_value(args_hash)
  return false unless args_hash[:cloud]
  return false unless args_hash[:flavor]

  case args_hash[:prov_option]
  when :number_of_cpus
    cloud_number_of_cpus(args_hash)
  when :vm_memory
    cloud_vm_memory(args_hash)
  when :storage
    cloud_storage(args_hash)
  end
end

def default_option(option_value, options_array)
  return if option_value.blank?
  options_array << option_value.to_i
end

def service_options
  options_hash = get_dialog_options_hash(@miq_request.options[:dialog])
  @service_template = $evm.vmdb(@miq_request.source_type, @miq_request.source_id)
  $evm.log(:info, "service_template id: #{@service_template.id} service_type: #{@service_template.service_type}")
  options_hash
end

def composite_service_options_value(child_service_resource, prov_option, options_array)
  return if child_service_resource.resource.prov_type.starts_with?('generic')
  child_service_resource.resource.service_resources.each do |grandchild_service_template_service_resource|
    service_prov_option_value(prov_option, grandchild_service_template_service_resource.resource, options_array)
  end
end

# get_dialog_options_hash - Look for dialog variables in the dialog options hash that start with "dialog_option_[0-9]"
def get_dialog_options_hash(dialog_options)
  options_hash = Hash.new { |h, k| h[k] = {} }
  # Loop through all of the options and build an options_hash from them
  dialog_options.each do |k, v|
    if /^dialog_option_(?<sequence_id>\d*)_(?<option_key>.*)/i =~ k
      set_hash_value(sequence_id, option_key.downcase.to_sym, v, options_hash)
      if $2 == 'instance_type'
        @flavor_id = v
        $evm.log(:info, "Recalculating cloud flavor based on dialog overrides")
      end
    else
      next unless /^dialog_(?<option_key>.*)/i =~ k

      set_hash_value(0, option_key.downcase.to_sym, v, options_hash)
      set_hash_value(0, k.downcase.to_sym, v, options_hash)
    end
  end
  $evm.log(:info, "Inspecting options_hash: #{options_hash.inspect}")
  options_hash
end

def set_hash_value(sequence_id, option_key, value, options_hash)
  return if value.blank?

  value = value.to_i.megabytes if option_key.to_s.downcase.include?('vm_memory')
  $evm.log(:info, "Adding seq_id: #{sequence_id} key: #{option_key.inspect} value: #{value.inspect} to options_hash")
  options_hash[sequence_id][option_key] = value
end

def error(type)
  msg = "Unable to calculate requested #{type}, due to an error getting the #{type}"
  $evm.log(:error, " #{msg}")
  $evm.root['ae_result'] = 'error'
  raise msg
end

request_info
error("request") if @miq_request.nil?

options_hash = service_options if @service

if @service && !@service_template.prov_type.nil? && @service_template.prov_type.starts_with?("generic")
  $evm.log(:info, "Generic Service Item.  No quota check being done.")
  $evm.root['ae_result'] = 'ok'
  $evm.root['ae_next_state'] = 'finished'
  exit MIQ_OK
end

@reconfigure_request = @miq_request.type == "VmReconfigureRequest"
if @reconfigure_request
  @check_quota = false # default, unless additional quota is requested
  vm_id = @miq_request.options[:src_ids]
  @vm = $evm.vmdb(:vm).find_by(:id => vm_id)
  raise "VM not found" if @vm.nil?
end

$evm.root['quota_requested'] = calculate_requested(options_hash)

$evm.root['check_quota'] = @check_quota unless @check_quota.nil?