Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quota - Calculate quota values for active provisions. #15466

Merged
merged 7 commits into from
Aug 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 116 additions & 43 deletions app/models/mixins/miq_provision_quota_mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module MiqProvisionQuotaMixin
# Supported quota types
# vm_by_owner, vm_by_group
# provision_by_owner, provision_by_group
# requset_by_owner, request_by_group
# request_by_owner, request_by_group

def check_quota(quota_type = :vms_by_owner, options = {})
quota_method = "quota_#{quota_type}"
unless respond_to?(quota_method)
Expand Down Expand Up @@ -69,6 +70,11 @@ def quota_active_provisions_by_group(options)
quota_provision_stats(:quota_find_active_prov_request_by_group, options.merge(:nil_vm_id_only => true))
end

# Collect stats based on currently active provision requests for user tenant.
def quota_active_provisions_by_tenant(options)
quota_provision_stats(:quota_find_active_prov_request_by_tenant, options.merge(:nil_vm_id_only => true))
end

# Collect stats based on currently active provision requesets for the same owner.
def quota_active_provisions_by_owner(options)
quota_provision_stats(:quota_find_active_prov_request_by_owner, options.merge(:nil_vm_id_only => true))
Expand Down Expand Up @@ -173,7 +179,7 @@ def quota_find_provisions(options)

queued_requests = MiqQueue.where(
:class_name => 'MiqProvisionRequest',
:method_name => 'create_provision_instances',
:method_name => 'create_request_tasks',
:state => 'ready',
:deliver_on => scheduled_range,
)
Expand Down Expand Up @@ -247,42 +253,88 @@ def quota_find_prov_request_by_group(options)

def quota_find_active_prov_request_by_owner(options)
email = get_option(:owner_email).to_s.strip
quota_find_active_prov_request(options).select { |p| email.casecmp(p.get_option(:owner_email).to_s.strip) == 0 }
quota_find_active_prov_request(options).select { |p| email.casecmp(p.request_owner_email(p)).zero? }
end

def request_owner_email(request)
service_request?(request) ? request.requester.email : request.get_option(:owner_email).to_s.strip
end

def service_request?(request)
request.kind_of?(ServiceTemplateProvisionRequest)
end

def quota_find_active_prov_request_by_group(options)
prov_requests = []
prov_owner = get_owner
unless prov_owner.nil?
group = prov_owner.ldap_group
prov_requests = quota_find_active_prov_request(options).select do |p|
prov_req_owner = p.get_owner
prov_req_owner && group.casecmp(prov_req_owner.ldap_group) == 0
end
prov_request_group = miq_request.options[:requester_group]
quota_find_active_prov_request(options).select do |r|
prov_request_group == r.options[:requester_group]
end
prov_requests
end

def quota_find_active_prov_request_by_tenant(options)
quota_find_active_prov_request(options).where(:tenant => miq_request.tenant)
end

def quota_find_active_prov_request(_options)
prov_req_ids = MiqQueue.where(
:class_name => 'MiqProvisionRequest',
:method_name => 'create_provision_instances',
:class_name => %w(MiqProvisionRequest ServiceTemplateProvisionRequest),
:method_name => 'create_request_tasks',
:state => 'dequeue'
).pluck(:instance_id)

prov_ids = []
MiqQueue
.where(:method_name => 'deliver', :state => %w(ready dequeue), :class_name => 'MiqAeEngine')
.where("tracking_label like ?", '%miq_provision_%')
.where("tracking_label like ?", '%_provision_%')
.each do |q|
if q.args
args = q.args.first
prov_ids << args[:object_id] if args[:object_type] == 'MiqProvision' && !args[:object_id].blank?
prov_ids << args[:object_id] if args[:object_type].include?('Provision') && !args[:object_id].blank?
end
end
prov_req_ids += MiqProvision.where(:id => prov_ids).pluck("miq_request_id")
prov_req_ids += MiqRequestTask.where(:id => prov_ids).pluck("miq_request_id")

MiqRequest.where(:id => prov_req_ids.compact.uniq)
end

def vm_quota_values(pr, result)
num_vms_for_request = number_of_vms(pr)
return if num_vms_for_request.zero?
flavor_obj = flavor(pr)
result[:count] += num_vms_for_request
result[:memory] += memory(pr, cloud?(pr), vendor(pr), flavor_obj) * num_vms_for_request
result[:cpu] += number_of_cpus(pr, cloud?(pr), flavor_obj) * num_vms_for_request
result[:storage] += storage(pr, cloud?(pr), vendor(pr), flavor_obj) * num_vms_for_request
result[:ids] << pr.id

pr.miq_request_tasks.each do |p|
next unless p.state == 'Active'
host_id, storage_id = p.get_option(:dest_host).to_i, p.get_option(:dest_storage).to_i
active = result[:active]
active[:memory_by_host_id][host_id] += memory(p, cloud?(pr), vendor(pr), flavor_obj)
active[:cpu_by_host_id][host_id] += number_of_cpus(p, cloud?(pr), flavor_obj)
active[:storage_by_id][storage_id] += storage(p, cloud?(pr), vendor(pr), flavor_obj)
active[:vms_by_storage_id][storage_id] << p.id
active[:ids] << p.id
end
end

MiqProvisionRequest.where(:id => prov_req_ids.compact.uniq)
def service_quota_values(request, result)
request.service_template.service_resources.each do |sr|
if request.service_template.service_type == 'composite'
bundle_quota_values(sr, result)
else
next if request.service_template.prov_type.starts_with?("generic")
vm_quota_values(sr.resource, result)
end
end
end

def bundle_quota_values(service_resource, result)
return if service_resource.resource.prov_type.starts_with?('generic')
service_resource.resource.service_resources.each do |sr|
vm_quota_values(sr.resource, result)
end
end

def quota_provision_stats(prov_method, options)
Expand All @@ -295,33 +347,54 @@ def quota_provision_stats(prov_method, options)
}

send(prov_method, options).each do |pr|
num_vms_for_request = pr.get_option(:number_of_vms).to_i
if options[:nil_vm_id_only] == true && pr.miq_request_tasks.length == num_vms_for_request
no_vm = pr.miq_request_tasks.find_all { |p| p.destination_id.nil? && p.state != 'finished' }
num_vms_for_request = no_vm.length
end
service_request?(pr) ? service_quota_values(pr, result) : vm_quota_values(pr, result)
end
result
end

unless num_vms_for_request.zero?
new_disk_storage_size = pr.get_new_disks.inject(0) { |s, d| s += d[:sizeInMB].to_i } * 1.megabyte
result[:count] += num_vms_for_request
result[:memory] += pr.get_option(:vm_memory).to_i * num_vms_for_request
result[:cpu] += pr.get_option(:number_of_cpus).to_i * num_vms_for_request
result[:storage] += (pr.vm_template.allocated_disk_storage.to_i + new_disk_storage_size) * num_vms_for_request
result[:ids] << pr.id

# Include a resource breakdown for actively provisioning records
pr.miq_request_tasks.each do |p|
next unless p.state == 'active'
host_id, storage_id = p.get_option(:dest_host).to_i, p.get_option(:dest_storage).to_i
active = result[:active]
active[:memory_by_host_id][host_id] += p.get_option(:vm_memory).to_i
active[:cpu_by_host_id][host_id] += p.get_option(:number_of_cpus).to_i
active[:storage_by_id][storage_id] += p.vm_template.allocated_disk_storage.to_i + new_disk_storage_size
active[:vms_by_storage_id][storage_id] << p.id
active[:ids] << p.id
end
def number_of_vms(request)
num_vms_for_request = request.get_option(:number_of_vms).to_i
if options[:nil_vm_id_only] == true && request.miq_request_tasks.length == num_vms_for_request
num_vms_for_request = request.miq_request_tasks.where(:destination_id => nil).where.not(:state => 'finished').count
end
num_vms_for_request
end

def cloud?(request)
request.source.try(:cloud) || false
end

def vendor(request)
request.source.try(:vendor)
end

def flavor(request)
Flavor.find(request.get_option(:instance_type)) if cloud?(request)
end

def number_of_cpus(prov, cloud, flavor_obj)
return flavor_obj.try(:cpus) if cloud
request = prov.kind_of?(MiqRequest) ? prov : prov.miq_request
num_cpus = request.get_option(:number_of_sockets).to_i * request.get_option(:cores_per_socket).to_i
num_cpus.zero? ? request.get_option(:number_of_cpus).to_i : num_cpus
end

def storage(prov, cloud, vendor, flavor_obj = nil)
if cloud
if vendor == 'google'
return prov.get_option(:boot_disk_size).to_i.gigabytes
end
return nil unless flavor_obj
flavor_obj.root_disk_size.to_i + flavor_obj.ephemeral_disk_size.to_i + flavor_obj.swap_disk_size.to_i
else
prov.kind_of?(MiqRequest) ? prov.vm_template.provisioned_storage : prov.miq_request.vm_template.provisioned_storage
end
result
end

def memory(prov, cloud, vendor, flavor_obj = nil)
return flavor_obj.try(:memory) if cloud
request = prov.kind_of?(MiqRequest) ? prov : prov.miq_request
memory = request.get_option(:vm_memory).to_i
%w(amazon openstack google).include?(vendor) ? memory : memory.megabytes
end
end
1 change: 1 addition & 0 deletions app/models/service_template_provision_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ServiceTemplateProvisionRequest < MiqRequest
delegate :picture, :to => :service_template, :allow_nil => true

alias_method :user, :get_user
include MiqProvisionQuotaMixin

def process_service_order
case options[:cart_state]
Expand Down
159 changes: 157 additions & 2 deletions spec/models/miq_provision_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,173 @@
end

it "should return stats from quota methods" do
prov_options = {:number_of_vms => [2, '2'], :owner_email => 'tester@miq.com', :vm_memory => ['1024', '1024'], :number_of_cpus => [2, '2']}
prov_options = {:number_of_vms => [2, '2'], :owner_email => 'tester@miq.com', :vm_memory => [1024, '1024'], :number_of_cpus => [2, '2']}
@pr2 = FactoryGirl.create(:miq_provision_request, :requester => user, :src_vm_id => vm_template.id, :options => prov_options)

stats = @pr.check_quota(:requests_by_owner)
expect(stats).to be_kind_of(Hash)

expect(stats[:class_name]).to eq("MiqProvisionRequest")
expect(stats[:count]).to eq(2)
expect(stats[:memory]).to eq(2048)
expect(stats[:memory]).to eq(2.gigabytes)
expect(stats[:cpu]).to eq(4)
expect(stats.fetch_path(:active, :class_name)).to eq("MiqProvision")
end

context "for cloud and infra providers," do
def create_task(user, request)
FactoryGirl.create(:miq_request_task, :miq_request => request, :miq_request_id => request.id, :type => 'MiqProvision', :description => "task", :tenant => user.current_tenant)
end

def create_request(user, vm_template, prov_options)
FactoryGirl.create(:miq_provision_request, :requester => user,
:description => "request",
:tenant => user.current_tenant,
:source => vm_template,
:src_vm_id => vm_template.id,
:options => prov_options.merge(:owner_email => user.email, :requester_group => user.miq_groups.first.description))
end

def request_queue_entry(request)
FactoryGirl.create(:miq_queue,
:state => MiqQueue::STATE_DEQUEUE,
:instance_id => request.id,
:class_name => 'MiqProvisionRequest',
:method_name => 'create_request_tasks')
end

def task_queue_entry(task)
FactoryGirl.create(:miq_queue,
:state => MiqQueue::STATE_DEQUEUE,
:args => [{:object_type => "Provision", :object_id => task.id}],
:tracking_label => 'miq_provision_task',
:class_name => 'MiqAeEngine',
:method_name => 'deliver')
end

def create_test_task(user, template)
request = create_request(user, template, {})
create_task(user, request)
request
end

def queue(requests)
requests.each do |request|
request.miq_request_tasks.empty? ? request_queue_entry(request) : task_queue_entry(request.miq_request_tasks.first)
end
end

let(:vmware_tasks) do
ems = FactoryGirl.create(:ems_vmware)
vmware_tenant = FactoryGirl.create(:tenant)
group = FactoryGirl.create(:miq_group, :tenant => vmware_tenant)
@vmware_user1 = FactoryGirl.create(:user_with_email, :miq_groups => [group])
@vmware_user2 = FactoryGirl.create(:user_with_email, :miq_groups => [group])
hardware = FactoryGirl.create(:hardware, :cpu1x2, :memory_mb => 512)
@vmware_template = FactoryGirl.create(:template_vmware,
:ext_management_system => ems,
:hardware => hardware)
prov_options = {:number_of_vms => [2, '2'], :vm_memory => [1024, '1024'], :number_of_cpus => [2, '2']}
requests = []
2.times { requests << create_request(@vmware_user1, @vmware_template, prov_options) }
create_task(@vmware_user1, requests.first)

2.times { requests << create_request(@vmware_user2, @vmware_template, prov_options) }
create_task(@vmware_user2, requests.last)
requests
end

let(:google_tasks) do
ems = FactoryGirl.create(:ems_google_with_authentication,
:availability_zones => [FactoryGirl.create(:availability_zone_google)])
google_tenant = FactoryGirl.create(:tenant)
group = FactoryGirl.create(:miq_group, :tenant => google_tenant)
@google_user1 = FactoryGirl.create(:user_with_email, :miq_groups => [group])
@google_user2 = FactoryGirl.create(:user_with_email, :miq_groups => [group])
@google_template = FactoryGirl.create(:template_google, :ext_management_system => ems)
flavor = FactoryGirl.create(:flavor_google, :ems_id => ems.id,
:cpus => 4, :cpu_cores => 1, :memory => 1024)
prov_options = {:number_of_vms => 1, :src_vm_id => vm_template.id, :boot_disk_size => ["10.GB", "10 GB"],
:placement_auto => [true, 1], :instance_type => [flavor.id, flavor.name]}
requests = []
2.times { requests << create_request(@google_user1, @google_template, prov_options) }
create_task(@google_user1, requests.first)

2.times { requests << create_request(@google_user2, @google_template, prov_options) }
create_task(@google_user2, requests.last)
requests
end

shared_examples_for "check_quota" do
it "check" do
load_queue
stats = request.check_quota(quota_method)
expect(stats).to include(counts_hash)
end
end

context "active_provisions," do
let(:load_queue) { queue(vmware_tasks | google_tasks) }
let(:request) { create_test_task(@vmware_user1, @vmware_template) }
let(:quota_method) { :active_provisions }
let(:counts_hash) do
{:count => 12, :memory => 8_589_938_688, :cpu => 32, :storage => 44.gigabytes}
end
it_behaves_like "check_quota"
end

context "infra," do
let(:load_queue) { queue(vmware_tasks | google_tasks) }
let(:request) { create_test_task(@vmware_user1, @vmware_template) }
let(:counts_hash) do
{:count => 8, :memory => 8.gigabytes, :cpu => 16, :storage => 4.gigabytes}
end

context "active_provisions_by_tenant," do
let(:quota_method) { :active_provisions_by_tenant }
it_behaves_like "check_quota"
end

context "active_provisions_by_group," do
let(:quota_method) { :active_provisions_by_group }
it_behaves_like "check_quota"
end

context "active_provisions_by_owner," do
let(:quota_method) { :active_provisions_by_owner }
let(:counts_hash) do
{:count => 4, :memory => 4.gigabytes, :cpu => 8, :storage => 2.gigabytes}
end
it_behaves_like "check_quota"
end
end

context "cloud," do
let(:load_queue) { queue(vmware_tasks | google_tasks) }
let(:request) { create_test_task(@google_user1, @google_template) }
let(:counts_hash) do
{:count => 4, :memory => 4096, :cpu => 16, :storage => 40.gigabytes}
end

context "active_provisions_by_tenant," do
let(:quota_method) { :active_provisions_by_tenant }
it_behaves_like "check_quota"
end

context "active_provisions_by_group," do
let(:quota_method) { :active_provisions_by_group }
it_behaves_like "check_quota"
end

context "active_provisions_by_owner," do
let(:quota_method) { :active_provisions_by_owner }
let(:counts_hash) do
{:count => 2, :memory => 2048, :cpu => 8, :storage => 20.gigabytes}
end
it_behaves_like "check_quota"
end
end
end
end

context "when processing tags" do
Expand Down
Loading