1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-21 18:03:38 +03:00

feature #1288: Support for Group quotas

This commit is contained in:
Ruben S. Montero 2012-06-08 22:14:40 +02:00
parent 819f1b6a41
commit 981db30338
18 changed files with 571 additions and 83 deletions

View File

@ -20,6 +20,7 @@
#include "PoolSQL.h"
#include "ObjectCollection.h"
#include "User.h"
#include "Quotas.h"
using namespace std;
@ -65,6 +66,11 @@ public:
return del_collection_id(id);
}
/**
* Object quotas, provides set and check interface
*/
Quotas quota;
private:
// -------------------------------------------------------------------------
@ -79,7 +85,11 @@ private:
Group(int id, const string& name):
PoolObjectSQL(id,GROUP,name,-1,-1,"","",table),
ObjectCollection("USERS")
ObjectCollection("USERS"),
quota("/GROUP/DATASTORE_QUOTA",
"/GROUP/NETWORK_QUOTA",
"/GROUP/IMAGE_QUOTA",
"/GROUP/VM_QUOTA")
{
// Allow users in this group to see it
group_u = 1;

View File

@ -312,6 +312,27 @@ protected:
RequestAttributes& att,
PoolObjectAuth& perms,
string& name);
private:
/* ------------- Functions to manage user and group quotas -------------- */
bool user_quota_authorization(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att,
string& error_str);
bool group_quota_authorization(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att,
string& error_str);
void user_quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att);
void group_quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att);
};
/* -------------------------------------------------------------------------- */

View File

@ -0,0 +1,72 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, 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. */
/* -------------------------------------------------------------------------- */
#ifndef REQUEST_MANAGER_GROUP_H
#define REQUEST_MANAGER_GROUP_H
#include "Request.h"
#include "Nebula.h"
using namespace std;
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class RequestManagerGroup: public Request
{
protected:
RequestManagerGroup(const string& method_name,
const string& help,
const string& params)
:Request(method_name,params,help)
{
Nebula& nd = Nebula::instance();
pool = nd.get_gpool();
auth_object = PoolObjectSQL::GROUP;
};
virtual ~RequestManagerGroup(){};
virtual void request_execute(xmlrpc_c::paramList const& _paramList,
RequestAttributes& att) = 0;
};
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class GroupSetQuota : public RequestManagerGroup
{
public:
GroupSetQuota():
RequestManagerGroup("GroupSetQuota",
"Sets group quota limits",
"A:sis")
{
auth_op = AuthRequest::ADMIN;
};
~GroupSetQuota(){};
void request_execute(xmlrpc_c::paramList const& _paramList,
RequestAttributes& att);
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
#endif

View File

@ -8,7 +8,21 @@
:size: 15
:left: true
:VMS:
:desc: Number of VMS
:size: 8
:MEMORY:
:desc: Total memory allocated to user VMs
:size: 8
:CPU:
:desc: Total CPU allocated to user VMs
:size: 8
:default:
- :ID
- :NAME
- :VMS
- :MEMORY
- :CPU

View File

@ -15,6 +15,7 @@
#--------------------------------------------------------------------------- #
require 'one_helper'
require 'one_helper/onequota_helper'
class OneGroupHelper < OpenNebulaHelper::OneHelper
def self.rname
@ -56,7 +57,31 @@ class OneGroupHelper < OpenNebulaHelper::OneHelper
d["NAME"]
end
default :ID, :NAME
column :VMS, "Total number of VMS", :size=>8 do |d|
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['VMS']
else
"-"
end
end
column :MEMORY, "Total memory allocated to group VMs", :size=>8 do |d|
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['MEMORY_USED']
else
"-"
end
end
column :CPU, "Total CPU allocated to group VMs", :size=>8 do |d|
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['CPU_USED']
else
"-"
end
end
default :ID, :NAME, :VMS, :MEMORY, :CPU
end
table
@ -92,5 +117,9 @@ class OneGroupHelper < OpenNebulaHelper::OneHelper
group.user_ids.each do |uid|
puts "%-15s" % [uid]
end
group_hash = group.to_hash
OneQuotaHelper.format_quota(group_hash['GROUP'])
end
end

View File

@ -168,15 +168,15 @@ class OneUserHelper < OpenNebulaHelper::OneHelper
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['VMS']
else
"0"
end
"-"
end
end
column :MEMORY, "Total memory allocated to user VMs", :size=>8 do |d|
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['MEMORY_USED']
else
"0"
"-"
end
end
@ -184,7 +184,7 @@ class OneUserHelper < OpenNebulaHelper::OneHelper
if d.has_key?('VM_QUOTA') and d['VM_QUOTA'].has_key?('VM')
d['VM_QUOTA']['VM']['CPU_USED']
else
"0"
"-"
end
end

View File

@ -97,4 +97,20 @@ cmd=CommandParser::CmdParser.new(ARGV) do
helper.show_resource(group,options)
end
quota_desc = <<-EOT.unindent
Set the quota limits for the group. If a path is not provided the editor
will be launched to modify the current quotas.
EOT
command :quota, quota_desc, :groupid, [:file, nil] do
helper.perform_action(args[0], options, "modified") do |group|
str = OneQuotaHelper.set_quota(group, args[1])
rc = group.set_quota(str)
if OpenNebula.is_error?(rc)
puts rc.message
exit -1
end
end
end
end

View File

@ -683,11 +683,14 @@ int DispatchManager::finalize(
int vid)
{
VirtualMachine * vm;
ostringstream oss;
Template * tmpl;
User * user;
ostringstream oss;
Template * tmpl;
User * user;
Group * group;
int uid;
int gid;
VirtualMachine::VmState state;
@ -707,6 +710,7 @@ int DispatchManager::finalize(
TransferManager * tm = nd.get_tm();
LifeCycleManager * lcm = nd.get_lcm();
UserPool * upool = nd.get_upool();
GroupPool * gpool = nd.get_gpool();
switch (state)
{
@ -730,19 +734,38 @@ int DispatchManager::finalize(
vm->log("DiM", Log::INFO, "New VM state is DONE.");
uid = vm->get_uid();
gid = vm->get_gid();
tmpl = vm->clone_template();
vm->unlock();
user = upool->get(uid, true);
if ( user != 0 )
if ( uid != UserPool::ONEADMIN_ID )
{
user->quota.vm_del(tmpl);
upool->update(user);
user = upool->get(uid, true);
user->unlock();
if ( user != 0 )
{
user->quota.vm_del(tmpl);
upool->update(user);
user->unlock();
}
}
if ( gid != GroupPool::ONEADMIN_ID )
{
group = gpool->get(gid, true);
if ( group != 0 )
{
group->quota.vm_del(tmpl);
gpool->update(group);
group->unlock();
}
}
delete tmpl;

View File

@ -103,14 +103,17 @@ void DispatchManager::done_action(int vid)
Template * tmpl;
int uid;
int gid;
VirtualMachine::LcmState lcm_state;
VirtualMachine::VmState dm_state;
Nebula& nd = Nebula::instance();
UserPool * upool = nd.get_upool();
GroupPool* gpool = nd.get_gpool();
User * user;
User * user;
Group * group;
vm = vmpool->get(vid,true);
@ -142,21 +145,41 @@ void DispatchManager::done_action(int vid)
vm->release_disk_images();
uid = vm->get_uid();
gid = vm->get_gid();
tmpl = vm->clone_template();
vm->unlock();
user = upool->get(uid, true);
/* ---------------- Update Group & User quota counters -------------- */
if ( user != 0 )
if ( uid != UserPool::ONEADMIN_ID )
{
user->quota.vm_del(tmpl);
upool->update(user);
user->unlock();
user = upool->get(uid, true);
if ( user != 0 )
{
user->quota.vm_del(tmpl);
upool->update(user);
user->unlock();
}
}
if ( gid != GroupPool::ONEADMIN_ID )
{
group = gpool->get(gid, true);
if ( group != 0 )
{
group->quota.vm_del(tmpl);
gpool->update(group);
group->unlock();
}
}
delete tmpl;
}
else

View File

@ -130,14 +130,18 @@ string& Group::to_xml(string& xml) const
{
ostringstream oss;
string collection_xml;
string quota_xml;
ObjectCollection::to_xml(collection_xml);
quota.to_xml(quota_xml);
oss <<
"<GROUP>" <<
"<ID>" << oid << "</ID>" <<
"<NAME>" << name << "</NAME>" <<
collection_xml <<
quota_xml <<
"</GROUP>";
xml = oss.str();
@ -179,6 +183,8 @@ int Group::from_xml(const string& xml)
ObjectXML::free_nodes(content);
rc += quota.from_xml(this);
if (rc != 0)
{
return -1;

View File

@ -273,10 +273,13 @@ int ImageManager::delete_image(int iid, const string& ds_data)
int ds_id;
int uid;
int gid;
Group* group;
User * user;
Nebula& nd = Nebula::instance();
UserPool * upool = nd.get_upool();
GroupPool* gpool = nd.get_gpool();
img = ipool->get(iid,true);
@ -320,6 +323,7 @@ int ImageManager::delete_image(int iid, const string& ds_data)
size = img->get_size();
ds_id = img->get_ds_id();
uid = img->get_uid();
gid = img->get_gid();
if (source.empty())
{
@ -343,20 +347,39 @@ int ImageManager::delete_image(int iid, const string& ds_data)
delete drv_msg;
user = upool->get(uid, true);
/* -------------------- Update Group & User quota counters -------------- */
Template img_usage;
if ( user != 0 )
img_usage.add("DATASTORE", ds_id);
img_usage.add("SIZE", size);
if ( uid != UserPool::ONEADMIN_ID )
{
Template img_usage;
user = upool->get(uid, true);
img_usage.add("DATASTORE", ds_id);
img_usage.add("SIZE", size);
if ( user != 0 )
{
user->quota.ds_del(&img_usage);
user->quota.ds_del(&img_usage);
upool->update(user);
upool->update(user);
user->unlock();
}
}
user->unlock();
if ( gid != GroupPool::ONEADMIN_ID )
{
group = gpool->get(gid, true);
if ( group != 0 )
{
group->quota.ds_del(&img_usage);
gpool->update(group);
group->unlock();
}
}
return 0;

View File

@ -23,11 +23,11 @@ module OpenNebula
# Constants and Class Methods
#######################################################################
GROUP_METHODS = {
:info => "group.info",
:allocate => "group.allocate",
:delete => "group.delete"
:info => "group.info",
:allocate => "group.allocate",
:delete => "group.delete",
:quota => "group.quota"
}
# Flag for requesting connected user's group info
@ -120,6 +120,20 @@ module OpenNebula
super(GROUP_METHODS[:delete])
end
# Sets the group quota limits
# @param quota [String] a template (XML or txt) with the new quota limits
#
# @return [nil, OpenNebula::Error] nil in case of success, Error
# otherwise
def set_quota(quota)
return Error.new('ID not defined') if !@pe_id
rc = @client.call(GROUP_METHODS[:quota],@pe_id, quota)
rc = nil if !OpenNebula.is_error?(rc)
return rc
end
# ---------------------------------------------------------------------
# Helpers to get information
# ---------------------------------------------------------------------

View File

@ -106,25 +106,22 @@ bool Request::basic_authorization(int oid,
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool Request::quota_authorization(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
bool Request::user_quota_authorization (Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att,
string& error_str)
{
Nebula& nd = Nebula::instance();
UserPool * upool = nd.get_upool();
UserPool * upool = nd.get_upool();
User * user;
User * user;
bool rc = false;
string error_str;
user = upool->get(att.uid, true);
if ( user == 0 )
{
failure_response(AUTHORIZATION,
authorization_error("User not found", att),
att);
error_str = "User not found";
return false;
}
@ -135,12 +132,12 @@ bool Request::quota_authorization(Template * tmpl,
break;
case PoolObjectSQL::VM:
case PoolObjectSQL::TEMPLATE:
rc = user->quota.vm_check(tmpl, error_str);
break;
default:
user->unlock();
return true;
break;
}
if (rc == true)
@ -150,22 +147,60 @@ bool Request::quota_authorization(Template * tmpl,
user->unlock();
if ( rc == false )
return rc;
}
/* -------------------------------------------------------------------------- */
bool Request::group_quota_authorization (Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att,
string& error_str)
{
Nebula& nd = Nebula::instance();
GroupPool * gpool = nd.get_gpool();
Group * group;
bool rc = false;
group = gpool->get(att.gid, true);
if ( group == 0 )
{
failure_response(AUTHORIZATION,
authorization_error(error_str, att),
att);
error_str = "Group not found";
return false;
}
switch (object)
{
case PoolObjectSQL::IMAGE:
rc = group->quota.ds_check(tmpl, error_str);
break;
case PoolObjectSQL::VM:
case PoolObjectSQL::TEMPLATE:
rc = group->quota.vm_check(tmpl, error_str);
break;
default:
break;
}
if (rc == true)
{
gpool->update(group);
}
group->unlock();
return rc;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void Request::quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
void Request::user_quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
{
Nebula& nd = Nebula::instance();
UserPool * upool = nd.get_upool();
@ -199,6 +234,115 @@ void Request::quota_rollback(Template * tmpl,
user->unlock();
}
/* -------------------------------------------------------------------------- */
void Request::group_quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
{
Nebula& nd = Nebula::instance();
GroupPool * gpool = nd.get_gpool();
Group * group;
group = gpool->get(att.gid, true);
if ( group == 0 )
{
return;
}
switch (object)
{
case PoolObjectSQL::IMAGE:
group->quota.ds_del(tmpl);
break;
case PoolObjectSQL::VM:
case PoolObjectSQL::TEMPLATE:
group->quota.vm_del(tmpl);
break;
default:
break;
}
gpool->update(group);
group->unlock();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
bool Request::quota_authorization(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
{
string error_str;
if (object != PoolObjectSQL::IMAGE &&
object != PoolObjectSQL::VM &&
object != PoolObjectSQL::TEMPLATE)
{
return true;
}
// uid/gid == -1 means do not update user/group
if ( att.uid != UserPool::ONEADMIN_ID && att.uid != -1)
{
if ( user_quota_authorization(tmpl, object, att, error_str) == false )
{
failure_response(AUTHORIZATION,
authorization_error(error_str, att),
att);
return false;
}
}
if ( att.gid != GroupPool::ONEADMIN_ID && att.gid != -1)
{
if ( group_quota_authorization(tmpl, object, att, error_str) == false )
{
user_quota_rollback(tmpl, object, att);
failure_response(AUTHORIZATION,
authorization_error(error_str, att),
att);
return false;
}
}
return true;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void Request::quota_rollback(Template * tmpl,
PoolObjectSQL::ObjectType object,
RequestAttributes& att)
{
if (object != PoolObjectSQL::IMAGE &&
object != PoolObjectSQL::VM &&
object != PoolObjectSQL::TEMPLATE)
{
return;
}
// uid/gid == -1 means do not update user/group
if ( att.uid != UserPool::ONEADMIN_ID && att.uid != -1 )
{
user_quota_rollback(tmpl, object, att);
}
if ( att.gid != GroupPool::ONEADMIN_ID && att.gid != -1 )
{
group_quota_rollback(tmpl, object, att);;
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -34,6 +34,7 @@
#include "RequestManagerUser.h"
#include "RequestManagerAcl.h"
#include "RequestManagerCluster.h"
#include "RequestManagerGroup.h"
#include <sys/signal.h>
#include <sys/socket.h>
@ -235,6 +236,9 @@ void RequestManager::register_xml_methods()
xmlrpc_c::methodPtr user_change_auth(new UserChangeAuth());
xmlrpc_c::methodPtr user_set_quota(new UserSetQuota());
// Group Methods
xmlrpc_c::methodPtr group_set_quota(new GroupSetQuota());
// VMTemplate Methods
xmlrpc_c::methodPtr template_instantiate(new VMTemplateInstantiate());
xmlrpc_c::methodPtr template_clone(new VMTemplateClone());
@ -377,6 +381,7 @@ void RequestManager::register_xml_methods()
RequestManagerRegistry.addMethod("one.group.allocate", group_allocate);
RequestManagerRegistry.addMethod("one.group.delete", group_delete);
RequestManagerRegistry.addMethod("one.group.info", group_info);
RequestManagerRegistry.addMethod("one.group.quota", group_set_quota);
RequestManagerRegistry.addMethod("one.grouppool.info", grouppool_info);

View File

@ -46,11 +46,6 @@ PoolObjectSQL * RequestManagerChown::get_and_quota(
return 0;
}
if ( new_uid < 0 )
{
return object;
}
if ( auth_object == PoolObjectSQL::VM )
{
tmpl = (static_cast<VirtualMachine*>(object))->clone_template();
@ -64,41 +59,44 @@ PoolObjectSQL * RequestManagerChown::get_and_quota(
tmpl->add("SIZE", img->get_size());
}
old_uid = object->get_uid();
old_gid = object->get_gid();
if ( new_uid == -1 )
{
old_uid = -1;
}
else
{
old_uid = object->get_uid();
}
if ( new_gid == -1 )
{
old_gid = -1;
}
else
{
old_gid = object->get_gid();
}
object->unlock();
RequestAttributes att_new(new_uid, new_gid, att);
RequestAttributes att_old(old_uid, old_gid, att);
if ( new_uid != 0 )
if ( quota_authorization(tmpl, att_new) == false )
{
if ( quota_authorization(tmpl, att_new) == false )
{
delete tmpl;
return 0;
}
delete tmpl;
return 0;
}
if ( old_uid != 0 )
{
quota_rollback(tmpl, att_old);
}
quota_rollback(tmpl, att_old);
object = pool->get(oid,true);
if ( object == 0 )
{
if ( new_uid != 0 )
{
quota_rollback(tmpl, att_new);
}
if ( old_uid != 0 )
{
quota_authorization(tmpl, att_old);
}
quota_rollback(tmpl, att_new);
quota_authorization(tmpl, att_old);
failure_response(NO_EXISTS,
get_error(object_name(auth_object), oid),

View File

@ -0,0 +1,83 @@
/* -------------------------------------------------------------------------- */
/* Copyright 2002-2012, 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. */
/* -------------------------------------------------------------------------- */
#include "RequestManagerGroup.h"
using namespace std;
void GroupSetQuota::
request_execute(xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int id = xmlrpc_c::value_int(paramList.getInt(1));
string quota_str = xmlrpc_c::value_string(paramList.getString(2));
Group * group;
string error_str;
Template quota_tmpl;
int rc;
if ( id == GroupPool::ONEADMIN_ID )
{
failure_response(ACTION,
request_error("Cannot set quotas for oneadmin group",""),
att);
return;
}
if ( basic_authorization(id, att) == false )
{
return;
}
rc = quota_tmpl.parse_str_or_xml(quota_str, error_str);
if ( rc != 0 )
{
failure_response(ACTION, request_error(error_str,""), att);
return;
}
group = static_cast<Group *>(pool->get(id,true));
if ( group == 0 )
{
failure_response(NO_EXISTS,
get_error(object_name(auth_object),id),
att);
return;
}
group->quota.set(&quota_tmpl, error_str);
pool->update(group);
group->unlock();
if ( rc != 0 )
{
failure_response(ACTION, request_error(error_str,""), att);
}
else
{
success_response(id, att);
}
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -44,7 +44,7 @@ void RequestManagerUser::
if ( user_action(id,paramList,error_str) < 0 )
{
failure_response(INTERNAL, request_error(error_str,""), att);
failure_response(ACTION, request_error(error_str,""), att);
return;
}
@ -147,6 +147,12 @@ int UserSetQuota::user_action(int user_id,
int rc;
User * user;
if ( user_id == UserPool::ONEADMIN_ID )
{
error_str = "Cannot set quotas for oneadmin user";
return -1;
}
rc = quota_tmpl.parse_str_or_xml(quota_str, error_str);
if ( rc != 0 )

View File

@ -33,6 +33,7 @@ source_files=[
'RequestManagerVMTemplate.cc',
'RequestManagerUpdateTemplate.cc',
'RequestManagerUser.cc',
'RequestManagerGroup.cc',
'RequestManagerHost.cc',
'RequestManagerImage.cc',
'RequestManagerChown.cc',