ManageIQ/manageiq-content

View on GitHub
content/automate/ManageIQ/Infrastructure/VM/Provisioning/Placement.class/__methods__/vmware_best_fit_with_scope.rb

Summary

Maintainability
C
1 day
Test Coverage
#
# Description: This method is used to find all hosts, datastores that have the tag category
# prov_scope = 'all' &&|| prov_scope = <group-name>
#

# Get variables
prov = $evm.root["miq_provision"]
vm = prov.vm_template
raise "VM not specified" if vm.nil?
user = prov.miq_request.requester
raise "User not specified" if user.nil?
ems  = vm.ext_management_system
raise "EMS not found for VM:<#{vm.name}>" if ems.nil?

tags  = {}

# Get Tags that are in scope
# Default is to look for Hosts and Datastores tagged with prov_scope = All or match to Group
normalized_ldap_group = user.normalized_ldap_group.gsub(/\W/,'_')
tags["prov_scope"] = ["all", normalized_ldap_group]

$evm.log("info", "VM=<#{vm.name}>, Space Required=<#{vm.provisioned_storage}>, group=<#{user.normalized_ldap_group}>")

#############################
# STORAGE LIMITATIONS
#############################
STORAGE_MAX_VMS      = 0
storage_max_vms      = $evm.object['storage_max_vms']
storage_max_vms      = storage_max_vms.strip.to_i if storage_max_vms.kind_of?(String) && !storage_max_vms.strip.empty?
storage_max_vms      = STORAGE_MAX_VMS unless storage_max_vms.kind_of?(Numeric)
STORAGE_MAX_PCT_USED = 100
storage_max_pct_used = $evm.object['storage_max_pct_used']
storage_max_pct_used = storage_max_pct_used.strip.to_i if storage_max_pct_used.kind_of?(String) && !storage_max_pct_used.strip.empty?
storage_max_pct_used = STORAGE_MAX_PCT_USED unless storage_max_pct_used.kind_of?(Numeric)
$evm.log("info", "storage_max_vms:<#{storage_max_vms}> storage_max_pct_used:<#{storage_max_pct_used}>")

#############################
# Set host sort order here
# options: :active_provioning_memory, :active_provioning_cpu, :current_memory_usage,
#          :current_memory_headroom, :current_cpu_usage, :random
#############################
HOST_SORT_ORDER = [:active_provioning_memory, :current_memory_headroom, :random]

#############################
# Sort hosts
#############################
active_prov_data = prov.check_quota(:active_provisions)
sort_data = []
ems.hosts.each do |h|
  sort_data << sd = [[], h.name, h]
  host_id = h.attributes['id'].to_i
  HOST_SORT_ORDER.each do |type|
    sd[0] << case type
             # Multiply values by (-1) to cause larger values to sort first
             when :active_provioning_memory
               active_prov_data[:active][:memory_by_host_id][host_id]
             when :active_provioning_cpu
               active_prov_data[:active][:cpu_by_host_id][host_id]
             when :current_memory_headroom
               h.current_memory_headroom * -1
             when :current_memory_usage
               h.current_memory_usage
             when :current_cpu_usage
               h.current_cpu_usage
             when :random
               rand(1000)
             else 0
             end
  end
end

sort_data.sort! { |a, b| a[0] <=> b[0] }
hosts = sort_data.collect { |sd| sd.pop }
$evm.log("info", "Sorted host Order:<#{HOST_SORT_ORDER.inspect}> Results:<#{sort_data.inspect}>")

#############################
# Set storage sort order here
# options: :active_provisioning_vms, :free_space, :free_space_percentage, :random
#############################
STORAGE_SORT_ORDER = [:active_provisioning_vms, :random]

host = storage = nil
min_registered_vms = nil
hosts.each do |h|
  next if h.maintenance
  next unless h.power_state == "on"

  #############################
  # Only consider hosts that have the required tags
  #############################
  next unless tags.all? do |key, value|
    if value.kind_of?(Array)
      value.any? { |v| h.tagged_with?(key, v) }
    else
      h.tagged_with?(key, value)
    end
  end

  nvms = h.vms.length

  #############################
  # Only consider storages that have the tag category group=all
  #############################
  storages = h.writable_storages.select do |s|
    tags.all? do |key, value|
      if value.kind_of?(Array)
        value.any? { |v| s.tagged_with?(key, v) }
      else
        s.tagged_with?(key, value)
      end
    end
  end

  $evm.log("info", "Evaluating storages:<#{storages.collect { |s| s.name }.join(", ")}>")

  #############################
  # Filter out storages that do not have enough free space for the VM
  #############################
  active_prov_data = prov.check_quota(:active_provisions)
  storages = storages.select do |s|
    storage_id = s.attributes['id'].to_i
    actively_provisioned_space = active_prov_data[:active][:storage_by_id][storage_id]
    if s.free_space > vm.provisioned_storage + actively_provisioned_space
      #        $evm.log("info", "Active Provision Data inspect: [#{active_prov_data.inspect}]")
      #        $evm.log("info", "Active provision space requirement: [#{actively_provisioned_space}]")
      #        $evm.log("info", "Valid Datastore: [#{s.name}], enough free space for VM -- Available: [#{s.free_space}], Needs: [#{vm.provisioned_storage}]")
      true
    else
      $evm.log("info", "Skipping Datastore:<#{s.name}>, not enough free space for VM:<#{vm.name}>. Available:<#{s.free_space}>, Needs:<#{vm.provisioned_storage}>")
      false
    end
  end

  #############################
  # Filter out storages number of VMs is greater than the max number of VMs allowed per Datastore
  #############################
  storages = storages.select do |s|
    storage_id = s.attributes['id'].to_i
    active_num_vms_for_storage = active_prov_data[:active][:vms_by_storage_id][storage_id].length
    if (storage_max_vms == 0) || ((s.vms.size + active_num_vms_for_storage) < storage_max_vms)
      true
    else
      $evm.log("info", "Skipping Datastore:<#{s.name}>, max number of VMs:<#{s.vms.size + active_num_vms_for_storage}> exceeded")
      false
    end
  end

  #############################
  # Filter out storages where percent used will be greater than the max % allowed per Datastore
  #############################
  storages = storages.select do |s|
    storage_id = s.attributes['id'].to_i
    active_pct_of_storage  = ((active_prov_data[:active][:storage_by_id][storage_id]) / s.total_space.to_f) * 100
    request_pct_of_storage = (vm.provisioned_storage / s.total_space.to_f) * 100

    if (storage_max_pct_used == 100) || ((s.v_used_space_percent_of_total + active_pct_of_storage + request_pct_of_storage) < storage_max_pct_used)
      true
    else
      $evm.log("info", "Skipping Datastore:<#{s.name}> percent of used space #{s.v_used_space_percent_of_total + active_pct_of_storage + request_pct_of_storage} exceeded")
      false
    end
  end

  if min_registered_vms.nil? || nvms < min_registered_vms
    #############################
    # Sort storage to determine target datastore
    #############################
    sort_data = []
    storages.each_with_index do |s, idx|
      sort_data << sd = [[], s.name, idx]
      storage_id = s.attributes['id'].to_i
      STORAGE_SORT_ORDER.each do |type|
        sd[0] << case type
                 when :free_space
                   # Multiply values by (-1) to cause larger values to sort first
                   (s.free_space - active_prov_data[:active][:storage_by_id][storage_id]) * -1
                 when :free_space_percentage
                   active_pct_of_storage  = ((active_prov_data[:active][:storage_by_id][storage_id]) / s.total_space.to_f) * 100
                   s.v_used_space_percent_of_total + active_pct_of_storage
                 when :active_provioning_vms
                   active_prov_data[:active][:vms_by_storage_id][storage_id].length
                 when :random
                   rand(1000)
                 else 0
                 end
      end
    end

    sort_data.sort! { |a, b| a[0] <=> b[0] }
    $evm.log("info", "Sorted storage Order:<#{STORAGE_SORT_ORDER.inspect}>  Results:<#{sort_data.inspect}>")
    selected_storage = sort_data.first
    unless selected_storage.nil?
      selected_idx = selected_storage.last
      storage = storages[selected_idx]
      host    = h
    end

    # Stop checking if we have found both host and storage
    break if host && storage
  end

end # END - hosts.each

obj = $evm.object
$evm.log("info", "Selected Host:<#{host.nil? ? "nil" : host.name}>")
if host
  obj["host"] = host
  prov.set_host(host)
end

$evm.log("info", "Selected Datastore:<#{storage.nil? ? "nil" : storage.name}>")
if storage
  obj["storage"] = storage
  prov.set_storage(storage)
end

$evm.log("info", "vm=<#{vm.name}> host=<#{host}> storage=<#{storage}>")