diff --git a/include/Image.h b/include/Image.h index 9804de2a57..5f5defe5ea 100644 --- a/include/Image.h +++ b/include/Image.h @@ -90,14 +90,21 @@ public: } /** - * Returns the source path of the image - * @return source of image + * Sets the source path of the image */ void set_source(const string& _source) { source = _source; } + /** + * Sets the size for the image + */ + void set_size(unsigned int _size_mb) + { + size_mb = _size_mb; + } + /** * Returns the type of the image * @return type @@ -285,27 +292,32 @@ private: /** * Type of the Image */ - ImageType type; + ImageType type; /** * Persistency of the Image */ - int persistent_img; + int persistent_img; /** * Registration time */ - time_t regtime; + time_t regtime; /** * Path to the image */ - string source; + string source; + + /** + * Size of the image in MB + */ + unsigned int size_mb; /** * Image state */ - ImageState state; + ImageState state; /** * Number of VMs using the image diff --git a/install.sh b/install.sh index fe2bb69832..8110badf98 100755 --- a/install.sh +++ b/install.sh @@ -229,6 +229,7 @@ VAR_DIRS="$VAR_LOCATION/remotes \ $VAR_LOCATION/remotes/auth/ssh \ $VAR_LOCATION/remotes/auth/x509 \ $VAR_LOCATION/remotes/auth/server \ + $VAR_LOCATION/remotes/auth/quota \ $VAR_LOCATION/remotes/auth/dummy" SUNSTONE_DIRS="$SUNSTONE_LOCATION/models \ @@ -326,6 +327,7 @@ INSTALL_FILES=( AUTH_SERVER_FILES:$VAR_LOCATION/remotes/auth/server AUTH_DUMMY_FILES:$VAR_LOCATION/remotes/auth/dummy AUTH_PLAIN_FILES:$VAR_LOCATION/remotes/auth/plain + AUTH_QUOTA_FILES:$VAR_LOCATION/remotes/auth/quota VMM_EXEC_KVM_SCRIPTS:$VAR_LOCATION/remotes/vmm/kvm VMM_EXEC_XEN_SCRIPTS:$VAR_LOCATION/remotes/vmm/xen VMM_EXEC_XEN_KVM_POLL:$VAR_LOCATION/remotes/vmm/kvm/poll @@ -467,6 +469,7 @@ BIN_FILES="src/nebula/oned \ src/cli/onetemplate \ src/cli/oneacl \ src/onedb/onedb \ + src/authm_mad/remotes/quota/onequota \ share/scripts/one" #------------------------------------------------------------------------------- @@ -490,6 +493,7 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \ src/oca/ruby/OpenNebula.rb \ src/tm_mad/TMScript.rb \ src/authm_mad/remotes/ssh/ssh_auth.rb \ + src/authm_mad/remotes/quota/quota.rb \ src/authm_mad/remotes/server/server_auth.rb \ src/authm_mad/remotes/x509/x509_auth.rb" @@ -596,6 +600,8 @@ AUTH_DUMMY_FILES="src/authm_mad/remotes/dummy/authenticate" AUTH_PLAIN_FILES="src/authm_mad/remotes/plain/authenticate" +AUTH_QUOTA_FILES="src/authm_mad/remotes/quota/authorize" + #------------------------------------------------------------------------------- # Transfer Manager commands, to be installed under $LIB_LOCATION/tm_commands # - NFS TM, $LIB_LOCATION/tm_commands/nfs @@ -708,6 +714,7 @@ HM_ETC_FILES="src/hm_mad/hmrc" #------------------------------------------------------------------------------- AUTH_ETC_FILES="src/authm_mad/remotes/server/server_auth.conf \ + src/authm_mad/remotes/quota/quota.conf \ src/authm_mad/remotes/x509/x509_auth.conf" #------------------------------------------------------------------------------- diff --git a/src/acct/watch_client.rb b/src/acct/watch_client.rb index 6f65d07b48..1b1feb6a5c 100644 --- a/src/acct/watch_client.rb +++ b/src/acct/watch_client.rb @@ -98,14 +98,14 @@ module OneWatchClient max_per_vm = rsql. group(:id, :last_poll). - select(:last_poll, :MAX[mr.to_sym].as(:max_mr)) + select{[:last_poll, max(mr.to_sym).as(:max_mr)]} # SUM the monitoring resource for each last_poll value last_poll_and_sum = max_per_vm. from_self. group(:last_poll). - select(:last_poll, :SUM[:max_mr].as(:sum_mr)) + select{[:last_poll, sum(:max_mr).as(:sum_mr)]} # Retrieve the information in an Array a = Array.new @@ -113,7 +113,7 @@ module OneWatchClient if row[:last_poll] && row[:last_poll] != 0 a << [row[:last_poll], row[:sum_mr].to_i] end - end + end a end @@ -229,4 +229,4 @@ module OneWatchClient end end end -end \ No newline at end of file +end diff --git a/src/authm_mad/test/spec_common.rb b/src/authm_mad/remotes/quota/authorize old mode 100644 new mode 100755 similarity index 59% rename from src/authm_mad/test/spec_common.rb rename to src/authm_mad/remotes/quota/authorize index 80850db4fd..fe8644c176 --- a/src/authm_mad/test/spec_common.rb +++ b/src/authm_mad/remotes/quota/authorize @@ -1,3 +1,5 @@ +#!/usr/bin/env ruby + # -------------------------------------------------------------------------- # # Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # # # @@ -14,14 +16,48 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'pp' +ONE_LOCATION=ENV["ONE_LOCATION"] -$: << '../../oca/ruby' -$: << '..' +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" + ETC_LOCATION="/etc/one/" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + ETC_LOCATION=ONE_LOCATION+"/etc/" +end -require 'db_helpers' +$: << RUBY_LIB_LOCATION +require 'scripts_common' +require 'opennebula' +require 'quota' +user_id = ARGV.shift +overall_evalutation = ARGV.pop +exit -1 if overall_evalutation == 0 +quota = Quota.new +#q = { +# :cpu => 10, +# :memory => 2048, +# :storage => 100000, +# :num_vms => 5 +#} +# +#quota.set(1, q) +#OpenNebula.log_debug("quotas: #{quota.get(1)}") + +ARGV.each {|request| + rc = quota.authorize(user_id, request) + + if rc + OpenNebula.error_message rc + exit -1 + end +} + +#OpenNebula.log_debug("AUTHORIZE ARGS: #{ARGV.join(' ')}") + +exit 0 \ No newline at end of file diff --git a/src/authm_mad/remotes/quota/onequota b/src/authm_mad/remotes/quota/onequota new file mode 100755 index 0000000000..a6fd24e669 --- /dev/null +++ b/src/authm_mad/remotes/quota/onequota @@ -0,0 +1,166 @@ +#!/usr/bin/env ruby + +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/cli" + +require 'command_parser' +require 'cli_helper' +require 'one_helper' +require 'quota' + +require 'pp' + +QUOTA_KEYS = Quota::DB_QUOTA_SCHEMA.keys + +def show_table(values, usage=nil) + size = usage ? 12 : 8 + + values.sort!{|v1, v2| v1[:uid]<=>v2[:uid] } + + table=CLIHelper::ShowTable.new(nil, self) do + column :uid, "ONE identifier for the User", :size=>4 do |d| + d[:uid] + end + + QUOTA_KEYS.each { |key| + column key, "#{key} quota", :size=>size do |d| + if usage + "#{usage[key].to_i}/#{d[key].to_i}" + else + "#{d[key].to_i}" + end + end + } + + default :uid, *QUOTA_KEYS + end + + table.show(values) +end + +cmd=CommandParser::CmdParser.new(ARGV) do + usage "`onequota` [] []" + version OpenNebulaHelper::ONE_VERSION + + quota = Quota.new + + ######################################################################## + # Global Options + ######################################################################## + set :option, CommandParser::OPTIONS + + ######################################################################## + # Argument Formats + ######################################################################## + quota_list_desc = <<-EOT.unindent + List of quota keys, comma separated. + Available quotas: #{QUOTA_KEYS.join(', ')} + EOT + + set :format, :quota_list, quota_list_desc do |arg| + arg_list = arg.split(',') + + rc = nil + arg_list.each do |elem| + if !QUOTA_KEYS.include?(elem.to_sym) + rc = -1, "#{elem} is not a valid quota" + end + end + + rc ? rc : [0, arg_list] + end + + set :format, :value_list, "List of quota values, comma separated." do |arg| + arg_list = arg.split(',') + arg_list.map! {|a| a.to_i } + [0, arg_list] + end + + set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg| + OpenNebulaHelper.rname_to_id(arg, "USER") + end + + ######################################################################## + # Commands + ######################################################################## + set_desc = <<-EOT.unindent + Set a quota for a given user. + Examples: + onequota set 3 cpu 12 + onequota set 4 cpu,memory,storage 8,4096,10000 + EOT + + command :set, set_desc, :userid, :quota_list, :value_list do + user_id, keys, values = args + + if keys.length != values.length + exit_with_code -1, "The number of keys and values does not match" + end + + values_hash = Hash.new + keys.each_with_index { |k,i| + values_hash[k.to_sym] = values[i] + } + + quota.set_quota(user_id, values_hash) + exit_with_code 0 + end + + ######################################################################## + delete_desc = "Delete the defined quotas for the given user" + + command :delete, delete_desc, :userid do + quota.delete_quota(args[0]) + exit_with_code 0 + end + + ######################################################################## + show_desc = "Show the user's quota and usage. (usage/quota)" + + FORCE={ + :name => "force", + :short => "-f", + :large => "--force", + :description => "Force the usage calculation instead of using the cache" + } + + command :show, show_desc, :userid, :options=>[FORCE] do + user_usage = quota.get_usage(args[0],nil,options[:force]) + user_quota = quota.get_quota(args[0]) + + show_table([user_quota], user_usage) + exit_with_code 0 + end + + ######################################################################## + list_desc = "List the defined quotas for all the users" + + command :list, list_desc do + show_table(quota.get_quota) + exit_with_code 0 + end +end \ No newline at end of file diff --git a/src/authm_mad/test/db_helpers.rb b/src/authm_mad/remotes/quota/quota.conf similarity index 76% rename from src/authm_mad/test/db_helpers.rb rename to src/authm_mad/remotes/quota/quota.conf index 1abdf1ae8d..acf02399d7 100644 --- a/src/authm_mad/test/db_helpers.rb +++ b/src/authm_mad/remotes/quota/quota.conf @@ -14,12 +14,16 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -require 'rubygems' -require 'sequel' - -def check_column(schema, column) - schema.find {|c| c[0] == column }.should_not == nil -end - +# Database URI +#:db: sqlite:///var/one/onequota.db +#------------------------------------------------------------------------------- +# Default quotas, these apply to all users. Leave empty to disable quota for +# a given metric. +#------------------------------------------------------------------------------- +:defaults: + :cpu: + :memory: + :num_vms: + :storage: diff --git a/src/authm_mad/remotes/quota/quota.rb b/src/authm_mad/remotes/quota/quota.rb new file mode 100644 index 0000000000..a32232ef83 --- /dev/null +++ b/src/authm_mad/remotes/quota/quota.rb @@ -0,0 +1,289 @@ +# -------------------------------------------------------------------------- # +# 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 'sequel' +require 'base64' +require 'yaml' + +class Quota + ########################################################################### + # Constants with paths to relevant files and defaults + ########################################################################### + ONE_LOCATION=ENV["ONE_LOCATION"] + + if !ONE_LOCATION + VAR_LOCATION = "/var/lib/one" + ETC_LOCATION = "/etc/one" + else + VAR_LOCATION = ONE_LOCATION + "/var" + ETC_LOCATION = ONE_LOCATION + "/etc" + end + + CONF_FILE = ETC_LOCATION + "/auth/quota.conf" + + CONF = { + :db => "sqlite://#{VAR_LOCATION}/onequota.db", + :defaults => { + :cpu => nil, + :memory => nil, + :num_vms => nil, + :storage => nil + } + } + + ########################################################################### + # Schema for the USAGE and QUOTA tables + ########################################################################### + DB_QUOTA_SCHEMA = { + :cpu => Float, + :memory => Integer, + :num_vms => Integer, + :storage => Integer + } + + QUOTA_TABLE = :quotas + USAGE_TABLE = :usage + + ########################################################################### + # 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 + } + } + + IMAGE_USAGE = { + :storage => { + :proc_info => lambda {|template| File.size(template['PATH']) }, + :xpath => 'SIZE' + } + } + + RESOURCES = ["VM", "IMAGE"] + + ########################################################################### + # DB handling + ########################################################################### + def initialize + conf = YAML.load_file(CONF_FILE) + @conf=CONF.merge(conf) {|key,h1,h2| + h1.merge(h2) if h1.instance_of?(Hash) && h2.instance_of?(Hash) + } + + @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(table) + @db.create_table?(table) do + Integer :uid + + DB_QUOTA_SCHEMA.each { |key,value| + column key, value + } + + primary_key :uid + index :uid + end + end + + # Adds new user limits + def set(table, uid, quota={}) + data=quota.delete_if{|key,value| !DB_QUOTA_SCHEMA.keys.include?(key)} + + quotas=@db[table].filter(:uid => uid) + + if quotas.first + quotas.update(data) + else + @db[table].insert(data.merge!(:uid => uid)) + end + end + + # Gets user limits + def get(table, uid=nil) + if uid + @db[table].filter(:uid => uid).first + else + @db[table].all + end + end + + # Delete user limits + def delete(table, uid) + quotas=@db[table].filter(:uid => uid) + + if quotas.first + quotas.delete + end + end + + ########################################################################### + # Quota Client + ########################################################################### + def set_quota(uid, quota={}) + set(QUOTA_TABLE, uid, quota) + end + + def get_quota(uid=nil) + limit = get(QUOTA_TABLE, uid) + limit ? limit : @conf[:defaults].merge!(:uid => uid) + end + + def delete_quota(uid) + delete(QUOTA_TABLE, uid) + end + + ########################################################################### + # Authorization + ########################################################################### + def authorize(user_id, request) + obj, template_or_id, op, owner, pub, acl_eval = request.split(':') + + if acl_eval == 0 + return "ACL evaluation denied" + end + + # Check if this op needs to check the quota + return false unless with_quota?(obj, op) + + # If the object is a template the info should be retrived from the + # VM pool. + obj = "VM" if obj == "TEMPLATE" + template = Base64::decode64(template_or_id) + + check_quotas(user_id.to_i, obj, template) + end + + def check_quotas(user_id, obj, template) + info = get_resources(obj, template) + total = get_usage(user_id, obj, true) + quota = get_quota(user_id) + + msg = "" + info.each { |qname, quota_requested| + unless quota[qname] + next + end + + used = send(DB_QUOTA_SCHEMA[qname].name.to_sym, total[qname]) + request = send(DB_QUOTA_SCHEMA[qname].name.to_sym, quota_requested) + limit = send(DB_QUOTA_SCHEMA[qname].name.to_sym, quota[qname]) + spent = used + request + + if spent > limit + msg << " #{qname.to_s.upcase} quota exceeded " + msg << "(Quota: #{limit}, " + msg << "Used: #{used}, " + msg << "Asked: #{request})." + end + } + + if msg=="" + return false + else + return msg.strip + end + end + + def with_quota?(obj, op) + return (obj == "VM" && op == "CREATE") || + (obj == "IMAGE" && op == "CREATE") || + (obj == "TEMPLATE" && op == "INSTANTIATE") + end + + ########################################################################### + # Usage + ########################################################################### + def get_usage(user_id, resource=nil, force=false) + if force + if RESOURCES.include?(resource) + resources = [resource] + else + resources = RESOURCES + end + + usage = Hash.new + + resources.each{ |res| + pool = get_pool(res, user_id) + base_xpath = "/#{res}_POOL/#{resource}" + Quota.const_get("#{res}_USAGE".to_sym).each { |key, params| + pool.each_xpath("#{base_xpath}/#{params[:xpath]}") { |elem| + if elem + usage[key] ||= 0 + if params[:count] + usage[key] += 1 + else + usage[key] += send(DB_QUOTA_SCHEMA[key].name.to_sym, elem) + end + end + } + } + + set(USAGE_TABLE, user_id, usage) unless usage.empty? + usage.merge!(:uid => user_id) + } + else + usage = get(USAGE_TABLE, user_id) + usage ||= {:uid => 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 diff --git a/src/authm_mad/remotes/quota/test/fixtures/imagepool.xml b/src/authm_mad/remotes/quota/test/fixtures/imagepool.xml new file mode 100644 index 0000000000..f71c914557 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/fixtures/imagepool.xml @@ -0,0 +1,26 @@ + + <% images.each do |id,image| %> + + <%= id %> + <%= image[:uid] ? image[:uid] : 0 %> + <%= image[:gid] ? image[:gid] : 0 %> + <%= image[:uname] ? image[:uname] : 'oneadmin' %> + <%= image[:gname] ? image[:gname] : 'oneadmin' %> + <%= image[:name] ? image[:name] : 'ttylinux' %> + <%= image[:type] ? image[:type] : 0 %> + <%= image[:pub] ? image[:pub] : 0 %> + <%= image[:persistent] ? image[:persistent] : 0 %> + 1314875019 + <%= image[:source] ? image[:source] : '/etc/hosts' %> + <%= image[:state] ? image[:state] : 1 %> + <%= image[:size] ? image[:size] : 100 %> + 0 + + + <% end %> + \ No newline at end of file diff --git a/src/authm_mad/remotes/quota/test/fixtures/vm.xml b/src/authm_mad/remotes/quota/test/fixtures/vm.xml new file mode 100644 index 0000000000..1560a16029 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/fixtures/vm.xml @@ -0,0 +1,46 @@ + + <%= id %> + <%= vm[:uid] ? vm[:uid] : 0 %> + <%= vm[:gid] ? vm[:gid] : 0 %> + <%= vm[:name] ? vm[:uid] : 'pepe' %> + <%= vm[:last_poll] ? vm[:last_poll] : '1309275256' %> + <%= vm[:state] ? vm[:state] : 3 %> + 3 + 1309275252 + 0 + dummy + <%= vm[:memory] ? vm[:memory] : 128 %> + <%= vm[:cpu] ? vm[:cpu] : 1 %> + <%= vm[:net_tx] ? vm[:net_tx] : 0 %> + <%= vm[:net_rx] ? vm[:net_rx] : 0 %> + + <% if history = vm[:history] %> + + <% history.each do |h| %> + + <%= h[:seq] ? h[:seq] : 0 %> + <%= h[:hostname] ? h[:hostname] : "kvxen" %> + /Users/dmolina/trabajo/acctmoni/install/var/ + <%= h[:hid] ? h[:hid] : 0 %> + 1309275256 + 0 + vmm_dummy + tm_dummy + <%= h[:pstime] ? h[:pstime] : 0 %> + <%= h[:petime] ? h[:petime] : 0 %> + <%= h[:rstime] ? h[:rstime] : 0 %> + <%= h[:retime] ? h[:retime] : 0 %> + <%= h[:estime] ? h[:estime] : 0 %> + <%= h[:eetime] ? h[:eetime] : 0 %> + <%= h[:reason] ? h[:reason] : 0 %> + + <% end %> + + <% end %> + \ No newline at end of file diff --git a/src/authm_mad/remotes/quota/test/fixtures/vmpool.xml b/src/authm_mad/remotes/quota/test/fixtures/vmpool.xml new file mode 100644 index 0000000000..1ab1fa3112 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/fixtures/vmpool.xml @@ -0,0 +1,49 @@ + + <% vms.each do |id,vm| %> + + <%= id %> + <%= vm[:uid] ? vm[:uid] : 0 %> + <%= vm[:gid] ? vm[:gid] : 0 %> + <%= vm[:name] ? vm[:uid] : 'pepe' %> + <%= vm[:last_poll] ? vm[:last_poll] : '1309275256' %> + <%= vm[:state] ? vm[:state] : 3 %> + 3 + 1309275252 + 0 + dummy + <%= vm[:memory] ? vm[:memory] : 128 %> + <%= vm[:cpu] ? vm[:cpu] : 1 %> + <%= vm[:net_tx] ? vm[:net_tx] : 0 %> + <%= vm[:net_rx] ? vm[:net_rx] : 0 %> + + <% if history = vm[:history] %> + + <% h = history.last %> + + <%= h[:seq] ? h[:seq] : 0 %> + <%= h[:hostname] ? h[:hostname] : "kvxen" %> + /Users/dmolina/trabajo/acctmoni/install/var/ + <%= h[:hid] ? h[:hid] : 0 %> + 1309275256 + 0 + vmm_dummy + tm_dummy + <%= h[:pstime] ? h[:pstime] : 0 %> + <%= h[:petime] ? h[:petime] : 0 %> + <%= h[:rstime] ? h[:rstime] : 0 %> + <%= h[:retime] ? h[:retime] : 0 %> + <%= h[:estime] ? h[:estime] : 0 %> + <%= h[:eetime] ? h[:eetime] : 0 %> + <%= h[:reason] ? h[:reason] : 0 %> + + + <% end %> + + <% end %> + diff --git a/src/authm_mad/remotes/quota/test/helper/mock_client.rb b/src/authm_mad/remotes/quota/test/helper/mock_client.rb new file mode 100644 index 0000000000..e92264dea2 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/helper/mock_client.rb @@ -0,0 +1,53 @@ +require 'erb' + +FPATH = "./fixtures/" + +class MockClient + def initialize + @vms = Hash.new + @done_vms = Hash.new + + @images = Hash.new + end + + + def call(action, *args) + xmlrpc_action = "one."+action + + case xmlrpc_action + when "one.vm.info" + id = args[0] + vm = @vms[id] + return ERB.new(File.read(FPATH+'vm.xml')).result(binding) + when "one.vmpool.info" + case args[3] + when -1 + vms = @vms + return ERB.new(File.read(FPATH+'vmpool.xml')).result(binding) + when 6 then + vms = @done_vms + return ERB.new(File.read(FPATH+'vmpool.xml')).result(binding) + end + when "one.imagepool.info" + images = @images + return ERB.new(File.read(FPATH+'imagepool.xml')).result(binding) + end + end + + def add_vm(id, values) + if values[:state] == 6 + @done_vms[id] = values.clone + else + @vms[id] = values.clone + end + end + + def delete_vm(id) + @vms.delete(id) + @vms_done.delete(id) + end + + def add_image(id, values) + @images[id] = values + end +end \ No newline at end of file diff --git a/src/authm_mad/remotes/quota/test/helper/test_helper.rb b/src/authm_mad/remotes/quota/test/helper/test_helper.rb new file mode 100644 index 0000000000..0735253de4 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/helper/test_helper.rb @@ -0,0 +1,31 @@ + +ONE_LOCATION = ENV['ONE_LOCATION'] +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" +end + +$: << RUBY_LIB_LOCATION + +$: << './helper' +$: << '.' +$: << '..' + +require 'mock_client' +require 'opennebula' +require 'quota' + +class Quota + def set_client(client) + @client = client + end + + def rm_and_set_testdb + `rm -f /tmp/onequota_test.db` + @db=Sequel.connect("sqlite:///tmp/onequota_test.db") + + create_table(QUOTA_TABLE) + create_table(USAGE_TABLE) + end +end diff --git a/src/authm_mad/remotes/quota/test/quota_spec.rb b/src/authm_mad/remotes/quota/test/quota_spec.rb new file mode 100644 index 0000000000..ff80f0c6c8 --- /dev/null +++ b/src/authm_mad/remotes/quota/test/quota_spec.rb @@ -0,0 +1,343 @@ +$: << '.' + +require 'helper/test_helper' + +describe "Quota testing" do + before(:all) do + @mock_client = MockClient.new + + @quota = Quota.new + @quota.set_client(@mock_client) + @quota.rm_and_set_testdb + + @uid1 = 0 + @quota1 = { + :cpu => 2.4, + :memory => 1024, + :num_vms => 4, + :storage => 10000 + } + + @uid2 = 1 + @quota2 = { + :cpu => 1.2, + :memory => 512, + :num_vms => 2, + :storage => 5000 + } + + # Generate VM ACL request + vm_template = <<-EOT +