From a328fd796a4b58e1bd9517eddfcfdb3ed25103d1 Mon Sep 17 00:00:00 2001 From: Daniel Molina Date: Wed, 31 Aug 2011 17:44:03 +0200 Subject: [PATCH] feature #788: Refactor Quota class --- src/authm_mad/remotes/quota/authorize | 3 +- src/authm_mad/remotes/quota/one_usage.rb | 124 ----------------- src/authm_mad/remotes/quota/quota.rb | 162 +++++++++++++++++------ 3 files changed, 127 insertions(+), 162 deletions(-) delete mode 100644 src/authm_mad/remotes/quota/one_usage.rb diff --git a/src/authm_mad/remotes/quota/authorize b/src/authm_mad/remotes/quota/authorize index 19c0ca02f4..fe8644c176 100755 --- a/src/authm_mad/remotes/quota/authorize +++ b/src/authm_mad/remotes/quota/authorize @@ -29,6 +29,7 @@ end $: << RUBY_LIB_LOCATION require 'scripts_common' +require 'opennebula' require 'quota' user_id = ARGV.shift @@ -49,7 +50,7 @@ quota = Quota.new #OpenNebula.log_debug("quotas: #{quota.get(1)}") ARGV.each {|request| - rc = quota.check_request(user_id, request) + rc = quota.authorize(user_id, request) if rc OpenNebula.error_message rc diff --git a/src/authm_mad/remotes/quota/one_usage.rb b/src/authm_mad/remotes/quota/one_usage.rb deleted file mode 100644 index 0d83e684d2..0000000000 --- a/src/authm_mad/remotes/quota/one_usage.rb +++ /dev/null @@ -1,124 +0,0 @@ -# -------------------------------------------------------------------------- # -# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # -# # -# Licensed under the Apache License, Version 2.0 (the "License"); you may # -# not use this file except in compliance with the License. You may obtain # -# a copy of the License at # -# # -# http://www.apache.org/licenses/LICENSE-2.0 # -# # -# Unless required by applicable law or agreed to in writing, software # -# distributed under the License is distributed on an "AS IS" BASIS, # -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # -# See the License for the specific language governing permissions and # -# limitations under the License. # -#--------------------------------------------------------------------------- # - -require 'OpenNebula' - -# This class retrieves and caches vms and its consuption grouped -# by users. 'update_user' method should be called to fill data for -# a user before any calculation is made -class OneUsage - VM_USAGE = { - :cpu => { - :proc_info => lambda {|template| template['CPU']}, - :xpath => 'TEMPLATE/CPU' - }, - :memory => { - :proc_info => lambda {|template| template['MEMORY']}, - :xpath => 'TEMPLATE/MEMORY' - }, - :num_vms => { - :proc_info => lambda {|template| 1 }, - :xpath => 'ID', - :count => true - } - } - - IMAGE_USAGE = { - :storage => { - :proc_info => lambda {|template| File.size(template['PATH']) }, - :proc_total => 'TEMPLATE/SIZE' - } - } - - RESOURCES = ["VM", "IMAGE"] - - def initialize() - @client = OpenNebula::Client.new - @usage = Hash.new - end - - def total(user_id, resource=nil, force=false) - usage = Hash.new - - if force - resources = [resource] if RESOURCES.include?(resource) - - resources.each{ |res| - pool = get_pool(res, user_id) - - base_xpath = "/#{res}_POOL/#{resource}" - OneUsage.const_get("#{res}_USAGE".to_sym).each { |key, params| - usage[key] ||= 0 - pool.each_xpath("#{base_xpath}/#{params[:xpath]}") { |elem| - usage[key] += params[:count] ? 1 : elem.to_i - } - } - - @usage[:user_id] ||= Hash.new - @usage[:user_id].merge!(usage) - } - else - usage = get_usage(user_id) - end - - usage - end - - # Retrieve the useful information of the template for the specified - # kind of resource - def get_resources(resource, xml_template) - template = OpenNebula::XMLElement.new - template.initialize_xml(xml_template, 'TEMPLATE') - - info = Hash.new - - self.class.const_get("#{resource}_USAGE").each { |key, params| - info[key] = params[:proc_info].call(template).to_i - } - - info - end - - private - - def get_usage(user_id) - usage = @usage[:user_id] - - unless usage - usage = Hash.new - - keys = VM_USAGE.keys + IMAGE_USAGE.keys - keys.each { |key| - usage[key] = 0 - } - - @usage[:user_id] = usage - end - - usage - end - - # Returns a an Array than contains the elements of the resource Pool - def get_pool(resource, user_id) - pool = case resource - when "VM" then OpenNebula::VirtualMachinePool.new(@client, user_id) - when "IMAGE" then OpenNebula::ImagePool.new(@client, user_id) - end - - rc = pool.info - return pool - end -end diff --git a/src/authm_mad/remotes/quota/quota.rb b/src/authm_mad/remotes/quota/quota.rb index 0a0027b88d..93014848c1 100644 --- a/src/authm_mad/remotes/quota/quota.rb +++ b/src/authm_mad/remotes/quota/quota.rb @@ -14,22 +14,17 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'one_usage' require 'sequel' require 'base64' -# Quota functionality for auth driver. Stores in database limits for each -# user and using OneUsage is able to retrieve resource usage from -# OpenNebula daemon and check if it is below limits class Quota - attr_accessor :defaults ########################################################################### - #Constants with paths to relevant files and defaults + # Constants with paths to relevant files and defaults ########################################################################### if !ENV["ONE_LOCATION"] - VAR_LOCATION = "/var/lib/one" + VAR_LOCATION = "/var/lib/one" else - VAR_LOCATION = ENV["ONE_LOCATION"] + "/var" + VAR_LOCATION = ENV["ONE_LOCATION"] + "/var" end CONF = { @@ -41,9 +36,10 @@ class Quota :storage => nil } } - - TABLE_NAME = :quotas + ########################################################################### + # Schema for the USAGE and QUOTA tables + ########################################################################### DB_QUOTA_SCHEMA = { :cpu => Float, :memory => Integer, @@ -51,30 +47,55 @@ class Quota :storage => Integer } + QUOTA_TABLE = :quotas + USAGE_TABLE = :usage - # 'db' is a Sequel database where to store user limits and client - # is OpenNebula::Client used to connect to OpenNebula daemon - def initialize(conf={}) - # TBD merge with the conf file - @conf=CONF + ########################################################################### + # Usage params to calculate each quota + ########################################################################### + VM_USAGE = { + :cpu => { + :proc_info => lambda {|template| template['CPU']}, + :xpath => 'TEMPLATE/CPU' + }, + :memory => { + :proc_info => lambda {|template| template['MEMORY']}, + :xpath => 'TEMPLATE/MEMORY' + }, + :num_vms => { + :proc_info => lambda {|template| 1 }, + :xpath => 'ID', + :count => true + } + } - @defaults=@conf[:defaults] + IMAGE_USAGE = { + :storage => { + :proc_info => lambda {|template| File.size(template['PATH']) }, + :xpath => 'SIZE' + } + } - @db=Sequel.connect(@conf[:db]) - - create_table - @table=@db[TABLE_NAME] - - @one_usage=OneUsage.new - end + RESOURCES = ["VM", "IMAGE"] ########################################################################### # DB handling ########################################################################### + def initialize(conf={}) + # TBD merge with the conf file + @conf=CONF + + @client = OpenNebula::Client.new + + @db=Sequel.connect(@conf[:db]) + + create_table(QUOTA_TABLE) + create_table(USAGE_TABLE) + end # Creates database quota table if it does not exist - def create_table - @db.create_table?(TABLE_NAME) do + def create_table(table) + @db.create_table?(table) do Integer :uid DB_QUOTA_SCHEMA.each { |key,value| @@ -87,38 +108,47 @@ class Quota end # Adds new user limits - def set(uid, quota={}) + def set(table, uid, quota={}) data=quota.delete_if{|key,value| !DB_QUOTA_SCHEMA.keys.include?(key)} - quotas=@table.filter(:uid => uid) + quotas=@db[table].filter(:uid => uid) if quotas.first quotas.update(data) else - @table.insert(data.merge!(:uid => uid)) + @db[table].insert(data.merge!(:uid => uid)) end end # Gets user limits - def get(uid=nil) + def get(table, uid=nil) if uid - limit=@table.filter(:uid => uid).first + limit=@db[table].filter(:uid => uid).first if limit limit else @conf[:defaults] end else - @table.all + @db[table].all end end + ########################################################################### + # Quota Client + ########################################################################### + def set_quota(uid, quota={}) + set(QUOTA_TABLE, uid, quota) + end + + def get_quota(uid=nil) + get(QUOTA_TABLE, uid) + end ########################################################################### # Authorization ########################################################################### - - def check_request(user_id, request) + def authorize(user_id, request) obj, template_or_id, op, owner, pub, acl_eval = request.split(':') if acl_eval == 0 @@ -137,9 +167,9 @@ class Quota end def check_quotas(user_id, obj, template) - info = @one_usage.get_resources(obj, template) - total = @one_usage.total(obj, user_id) - quota = get(user_id) + info = get_resources(obj, template) + total = get_usage(obj, user_id) + quota = get_quota(user_id) msg = "" info.each { |quota_name, quota_requested| @@ -164,5 +194,63 @@ class Quota (obj == "IMAGE" && op == "CREATE") || (obj == "TEMPLATE" && op == "INSTANTIATE") end -end + ########################################################################### + # Usage + ########################################################################### + def get_usage(user_id, resource=nil, force=false) + usage = Hash.new + + if force + if RESOURCES.include?(resource) + resources = [resource] + else + resources = RESOURCES + end + + resources.each{ |res| + pool = get_pool(res, user_id) + + base_xpath = "/#{res}_POOL/#{resource}" + Quota.const_get("#{res}_USAGE".to_sym).each { |key, params| + usage[key] ||= 0 + pool.each_xpath("#{base_xpath}/#{params[:xpath]}") { |elem| + usage[key] += params[:count] ? 1 : elem.to_i + } + } + + set(USAGE_TABLE, user_id, usage) + } + else + usage = get(USAGE_TABLE, user_id) + end + + usage + end + + # Retrieve the useful information of the template for the specified + # kind of resource + def get_resources(resource, xml_template) + template = OpenNebula::XMLElement.new + template.initialize_xml(xml_template, 'TEMPLATE') + + info = Hash.new + + self.class.const_get("#{resource}_USAGE").each { |key, params| + info[key] = params[:proc_info].call(template).to_i + } + + info + end + + # Returns a an Array than contains the elements of the resource Pool + def get_pool(resource, user_id) + pool = case resource + when "VM" then OpenNebula::VirtualMachinePool.new(@client, user_id) + when "IMAGE" then OpenNebula::ImagePool.new(@client, user_id) + end + + rc = pool.info + return pool + end +end