1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00

feature #1772:Resize VM capacity (cpu, memory and vcpu) offline

This commit is contained in:
Ruben S. Montero 2013-02-23 19:49:06 +01:00
parent 00b664dc8d
commit dcfa0a1773
17 changed files with 523 additions and 22 deletions

View File

@ -317,6 +317,19 @@ public:
} }
}; };
/**
* Updates the capacity used in a host when a VM is resized
* counters
* @param cpu increment of cpu requested by the VM
* @param mem increment of memory requested by the VM
* @param disk not used
* @return 0 on success
*/
void update_capacity(int cpu, int mem, int disk)
{
host_share.update(cpu,mem,disk);
};
/** /**
* Tests whether a new VM can be hosted by the host or not * Tests whether a new VM can be hosted by the host or not
* @param cpu needed by the VM (percentage) * @param cpu needed by the VM (percentage)

View File

@ -54,6 +54,19 @@ public:
running_vms++; running_vms++;
} }
/**
* Updates the capacity of VM in this share
* @param cpu increment
* @param mem increment
* @param disk increment
*/
void update(int cpu, int mem, int disk)
{
cpu_usage += cpu;
mem_usage += mem;
disk_usage += disk;
}
/** /**
* Delete a VM from this share * Delete a VM from this share
* @param cpu requested by the VM * @param cpu requested by the VM

View File

@ -427,6 +427,16 @@ public:
obj_template->add(name, value); obj_template->add(name, value);
} }
/**
* Adds a float attribute
* @param att_name Name for the attribute
* @param att_val integer
*/
void add_template_attribute(const string& name, float value)
{
obj_template->add(name, value);
}
/** /**
* Factory method for templates, it should be implemented * Factory method for templates, it should be implemented
* by classes that uses templates * by classes that uses templates

View File

@ -51,13 +51,26 @@ public:
*/ */
virtual bool check(Template* tmpl, Quotas& default_quotas, string& error) = 0; virtual bool check(Template* tmpl, Quotas& default_quotas, string& error) = 0;
/**
* Check if a resource update in usage counters will exceed the
* quota limits. If not the usage counters are updated for that resource
* @param tmpl with increments in MEMORY and CPU
* @param default_quotas Quotas that contain the default limits
* @param error string
* @return true if the operation can be performed
*/
virtual bool update(Template * tmpl, Quotas& default_quotas, string& error)
{
error = "Update operation for quotas not supported.";
return false;
};
/** /**
* Decrement usage counters when deallocating image * Decrement usage counters when deallocating image
* @param tmpl template for the resource * @param tmpl template for the resource
*/ */
virtual void del(Template* tmpl) = 0; virtual void del(Template* tmpl) = 0;
/** /**
* Returns the name that identifies the quota in a template * Returns the name that identifies the quota in a template
*/ */

View File

@ -57,6 +57,16 @@ public:
*/ */
bool check(Template* tmpl, Quotas& default_quotas, string& error); bool check(Template* tmpl, Quotas& default_quotas, string& error);
/**
* Check if the resource update (change in MEMORY or CPU) will exceed the
* quota limits. If not the usage counters are updated
* @param tmpl with increments in MEMORY and CPU
* @param default_quotas Quotas that contain the default limits
* @param error string
* @return true if the operation can be performed
*/
bool update(Template * tmpl, Quotas& default_quotas, string& error);
/** /**
* Decrement usage counters when deallocating image * Decrement usage counters when deallocating image
* @param tmpl template for the resource * @param tmpl template for the resource

View File

@ -141,13 +141,27 @@ public:
* @param tmpl template for the VirtualMachine * @param tmpl template for the VirtualMachine
* @param default_quotas Quotas that contain the default limits * @param default_quotas Quotas that contain the default limits
* @param error_str string describing the error * @param error_str string describing the error
* @return true if VM can be allocated, false otherwise * @return true if resource can be allocated, false otherwise
*/ */
bool quota_check(QuotaType type, bool quota_check(QuotaType type,
Template *tmpl, Template *tmpl,
Quotas& default_quotas, Quotas& default_quotas,
string& error_str); string& error_str);
/**
* Update usage of an existing quota (e.g. size of an image), it updates
* the usage counters if quotas are not exceeded.
* @param type the quota to work with
* @param tmpl template for the VirtualMachine
* @param default_quotas Quotas that contain the default limits
* @param error_str string describing the error
* @return true if resource can be updated, false otherwise
*/
bool quota_update(QuotaType type,
Template *tmpl,
Quotas& default_quotas,
string& error_str);
/** /**
* Delete usage from the given quota counters. * Delete usage from the given quota counters.
* @param type the quota to work with * @param type the quota to work with

View File

@ -217,6 +217,21 @@ public:
RequestAttributes& att); RequestAttributes& att);
}; };
/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */
class VirtualMachineResize : public RequestManagerVirtualMachine
{
public:
VirtualMachineResize():
RequestManagerVirtualMachine("VirtualMachineResize",
"Changes the capacity of the virtual machine",
"A:sidiib"){};
~VirtualMachineResize(){};
void request_execute(xmlrpc_c::paramList const& _paramList,
RequestAttributes& att);
};
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */

View File

@ -184,6 +184,20 @@ public:
set(new SingleAttribute(name, oss.str())); set(new SingleAttribute(name, oss.str()));
} }
/**
* Adds a new single attribute to the template.
* @param name of the attribute
* @param value of the attribute
*/
void add(const string& name, float value)
{
ostringstream oss;
oss << value;
set(new SingleAttribute(name, oss.str()));
}
/** /**
* Removes an attribute from the template. The attributes are returned. The * Removes an attribute from the template. The attributes are returned. The
* attributes MUST be freed by the calling funtion * attributes MUST be freed by the calling funtion

View File

@ -747,7 +747,7 @@ public:
}; };
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Timers // Timers &
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** /**
* Gets time from last information polling. * Gets time from last information polling.
@ -766,6 +766,14 @@ public:
*/ */
void get_requirements (int& cpu, int& memory, int& disk); void get_requirements (int& cpu, int& memory, int& disk);
/**
* Resize the VM capacity
* @param cpu
* @param memory
* @param vcpu
*/
void resize (float cpu, int memory, int vcpu);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Network Leases & Disk Images // Network Leases & Disk Images
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -103,6 +103,7 @@ EOT
} }
] ]
#NOTE: Other options defined using this array, add new options at the end
TEMPLATE_OPTIONS=[ TEMPLATE_OPTIONS=[
{ {
:name => 'cpu', :name => 'cpu',
@ -212,6 +213,8 @@ EOT
TEMPLATE_OPTIONS_VM=[TEMPLATE_NAME_VM]+TEMPLATE_OPTIONS+[DRY] TEMPLATE_OPTIONS_VM=[TEMPLATE_NAME_VM]+TEMPLATE_OPTIONS+[DRY]
CAPACITY_OPTIONS_VM=[TEMPLATE_OPTIONS[0],TEMPLATE_OPTIONS[1],TEMPLATE_OPTIONS[3]]
OPTIONS = XML, NUMERIC, KILOBYTES OPTIONS = XML, NUMERIC, KILOBYTES
class OneHelper class OneHelper

View File

@ -662,4 +662,23 @@ cmd=CommandParser::CmdParser.new(ARGV) do
:options=>CLIHelper::OPTIONS+OpenNebulaHelper::OPTIONS do :options=>CLIHelper::OPTIONS+OpenNebulaHelper::OPTIONS do
helper.list_pool(options, true, args[0]) helper.list_pool(options, true, args[0])
end end
resize_desc = <<-EOT.unindent
Resizes the capacity of a Virtual Machine (offline, the VM cannot be
RUNNING)
EOT
command :resize, resize_desc, :vmid, :options =>
OpenNebulaHelper::CAPACITY_OPTIONS_VM + [ENFORCE]do
cpu = options[:cpu] || 0
memory = options[:memory] || 0
vcpu = options[:vcpu] || 0
enforce = options[:enforce] || true
helper.perform_action(args[0], options, "Resizing VM") do |vm|
vm.resize(cpu, memory, vcpu, enforce)
end
end
end end

View File

@ -37,7 +37,8 @@ module OpenNebula
:attach => "vm.attach", :attach => "vm.attach",
:detach => "vm.detach", :detach => "vm.detach",
:rename => "vm.rename", :rename => "vm.rename",
:update => "vm.update" :update => "vm.update",
:resize => "vm.resize"
} }
VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED
@ -336,6 +337,17 @@ module OpenNebula
return rc return rc
end end
# Resize the VM
# @param cpu [Float] the new CPU value
# @param memory [Integer] the new MEMORY value
# @param vcpu [Integer] the new VCPU value
# @param enforce [true|false] If it is set to true, the host capacity
# will be checked
def resize(cpu, memory, vcpu, enforce)
return @client.call(VM_METHODS[:resize], @pe_id, cpu, memory,
vcpu, enforce)
end
# Changes the owner/group # Changes the owner/group
# uid:: _Integer_ the new owner id. Set to -1 to leave the current one # uid:: _Integer_ the new owner id. Set to -1 to leave the current one
# gid:: _Integer_ the new group id. Set to -1 to leave the current one # gid:: _Integer_ the new group id. Set to -1 to leave the current one

View File

@ -254,6 +254,7 @@ void RequestManager::register_xml_methods()
xmlrpc_c::methodPtr vm_monitoring(new VirtualMachineMonitoring()); xmlrpc_c::methodPtr vm_monitoring(new VirtualMachineMonitoring());
xmlrpc_c::methodPtr vm_attach(new VirtualMachineAttach()); xmlrpc_c::methodPtr vm_attach(new VirtualMachineAttach());
xmlrpc_c::methodPtr vm_detach(new VirtualMachineDetach()); xmlrpc_c::methodPtr vm_detach(new VirtualMachineDetach());
xmlrpc_c::methodPtr vm_resize(new VirtualMachineResize());
xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting()); xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting());
xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring()); xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring());
@ -396,6 +397,7 @@ void RequestManager::register_xml_methods()
RequestManagerRegistry.addMethod("one.vm.attach", vm_attach); RequestManagerRegistry.addMethod("one.vm.attach", vm_attach);
RequestManagerRegistry.addMethod("one.vm.detach", vm_detach); RequestManagerRegistry.addMethod("one.vm.detach", vm_detach);
RequestManagerRegistry.addMethod("one.vm.rename", vm_rename); RequestManagerRegistry.addMethod("one.vm.rename", vm_rename);
RequestManagerRegistry.addMethod("one.vm.resize", vm_resize);
RequestManagerRegistry.addMethod("one.vm.update", vm_update); RequestManagerRegistry.addMethod("one.vm.update", vm_update);
RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info);

View File

@ -17,6 +17,7 @@
#include "RequestManagerVirtualMachine.h" #include "RequestManagerVirtualMachine.h"
#include "PoolObjectAuth.h" #include "PoolObjectAuth.h"
#include "Nebula.h" #include "Nebula.h"
#include "Quotas.h"
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
@ -1056,3 +1057,295 @@ void VirtualMachineDetach::request_execute(xmlrpc_c::paramList const& paramList,
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
void VirtualMachineResize::request_execute(xmlrpc_c::paramList const& paramList,
RequestAttributes& att)
{
int id = xmlrpc_c::value_int(paramList.getInt(1));
float ncpu = xmlrpc_c::value_int(paramList.getDouble(2));
int nmemory = xmlrpc_c::value_int(paramList.getInt(3));
int nvcpu = xmlrpc_c::value_int(paramList.getInt(4));
bool enforce = xmlrpc_c::value_boolean(paramList.getBoolean(5));
float ocpu, dcpu;
int omemory, dmemory;
Nebula& nd = Nebula::instance();
UserPool* upool = nd.get_upool();
GroupPool* gpool = nd.get_gpool();
Quotas dquotas = nd.get_default_user_quota();
HostPool * hpool = nd.get_hpool();
Host * host;
Template deltas;
string error_str;
bool rc;
int hid = -1;
PoolObjectAuth vm_perms;
VirtualMachinePool * vmpool = static_cast<VirtualMachinePool *>(pool);
VirtualMachine * vm;
vm = vmpool->get(id, true);
if (vm == 0)
{
failure_response(NO_EXISTS,
get_error(object_name(PoolObjectSQL::VM),id),
att);
return;
}
vm->get_permissions(vm_perms);
vm->get_template_attribute("MEMORY", omemory);
vm->get_template_attribute("CPU", ocpu);
dcpu = ncpu - ocpu;
dmemory = nmemory - omemory;
deltas.add("MEMORY", dmemory);
deltas.add("CPU", dcpu);
switch (vm->get_state())
{
case VirtualMachine::POWEROFF: //Only check host capacity in POWEROFF
if (vm->hasHistory() == true)
{
hid = vm->get_hid();
}
case VirtualMachine::INIT:
case VirtualMachine::PENDING:
case VirtualMachine::HOLD:
case VirtualMachine::FAILED:
break;
case VirtualMachine::STOPPED:
case VirtualMachine::DONE:
case VirtualMachine::SUSPENDED:
case VirtualMachine::ACTIVE:
failure_response(ACTION,
request_error("Wrong state to perform action",""),
att);
vm->unlock();
return;
}
vm->unlock();
/* ---------------------------------------------------------------------- */
/* Authorize the operation */
/* ---------------------------------------------------------------------- */
if ( att.uid != UserPool::ONEADMIN_ID )
{
AuthRequest ar(att.uid, att.gid);
ar.add_auth(AuthRequest::MANAGE, vm_perms);
if (enforce == false) //Admin rights to overcommit
{
ar.add_auth(AuthRequest::ADMIN, vm_perms);
}
if (UserPool::authorize(ar) == -1)
{
failure_response(AUTHORIZATION,
authorization_error(ar.message, att),
att);
vm->unlock();
return;
}
}
/* ---------------------------------------------------------------------- */
/* Check quotas */
/* ---------------------------------------------------------------------- */
if (att.uid != UserPool::ONEADMIN_ID)
{
User * user = upool->get(att.uid, true);
if ( user == 0 )
{
failure_response(NO_EXISTS,
get_error(object_name(PoolObjectSQL::USER),att.uid),
att);
return;
}
rc = user->quota.quota_update(Quotas::VM, &deltas, dquotas, error_str);
if (rc == false)
{
ostringstream oss;
oss << object_name(PoolObjectSQL::USER) << " [" << att.uid << "] "
<< error_str;
failure_response(AUTHORIZATION,
request_error(oss.str(), ""),
att);
user->unlock();
return;
}
user->unlock();
}
if (att.gid != GroupPool::ONEADMIN_ID)
{
Group * group = gpool->get(att.gid, true);
if ( group == 0 )
{
failure_response(NO_EXISTS,
get_error(object_name(PoolObjectSQL::GROUP),att.gid),
att);
return;
}
rc = group->quota.quota_update(Quotas::VM, &deltas, dquotas, error_str);
if (rc == false)
{
ostringstream oss;
RequestAttributes att_tmp(att.uid, -1, att);
oss << object_name(PoolObjectSQL::GROUP) << " [" << att.gid << "] "
<< error_str;
failure_response(AUTHORIZATION,
request_error(oss.str(), ""),
att);
group->unlock();
quota_rollback(&deltas, Quotas::VM, att_tmp);
return;
}
group->unlock();
}
/* ---------------------------------------------------------------------- */
/* Check & update host capacity */
/* ---------------------------------------------------------------------- */
if (hid != -1)
{
host = hpool->get(hid, true);
if (host == 0)
{
failure_response(NO_EXISTS,
get_error(object_name(PoolObjectSQL::HOST),hid),
att);
quota_rollback(&deltas, Quotas::VM, att);
return ;
}
if ( enforce && host->test_capacity(dcpu, dmemory, 0) == false )
{
ostringstream oss;
oss << object_name(PoolObjectSQL::HOST)
<< " " << hid << " does not have enough capacity.";
failure_response(ACTION, request_error(oss.str(),""), att);
host->unlock();
quota_rollback(&deltas, Quotas::VM, att);
return;
}
host->update_capacity(dcpu, dmemory, 0);
hpool->update(host);
host->unlock();
}
/* ---------------------------------------------------------------------- */
/* Resize the VM */
/* ---------------------------------------------------------------------- */
vm = vmpool->get(id, true);
if (vm == 0)
{
failure_response(NO_EXISTS,
get_error(object_name(PoolObjectSQL::VM),id),
att);
quota_rollback(&deltas, Quotas::VM, att);
if (hid != -1)
{
host = hpool->get(hid, true);
if (host != 0)
{
host->update_capacity(-dcpu, -dmemory, 0);
hpool->update(host);
host->unlock();
}
}
return;
}
//Check again state as the VM may transit to active (e.g. scheduled)
switch (vm->get_state())
{
case VirtualMachine::INIT:
case VirtualMachine::PENDING:
case VirtualMachine::HOLD:
case VirtualMachine::FAILED:
case VirtualMachine::POWEROFF:
vm->resize(ncpu, nmemory, nvcpu);
vmpool->update(vm);
break;
case VirtualMachine::STOPPED:
case VirtualMachine::DONE:
case VirtualMachine::SUSPENDED:
case VirtualMachine::ACTIVE:
failure_response(ACTION,
request_error("Wrong state to perform action",""),
att);
vm->unlock();
quota_rollback(&deltas, Quotas::VM, att);
if (hid != -1)
{
host = hpool->get(hid, true);
if (host != 0)
{
host->update_capacity(ocpu - ncpu, omemory - nmemory, 0);
hpool->update(host);
host->unlock();
}
}
return;
}
vm->unlock();
success_response(id, att);
}

View File

@ -117,3 +117,25 @@ int QuotaVirtualMachine::get_default_quota(
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
bool QuotaVirtualMachine::update(Template * tmpl,
Quotas& default_quotas,
string& error)
{
map<string, float> vm_request;
int delta_memory;
float delta_cpu;
if ( tmpl->get("MEMORY", delta_memory) == true )
{
vm_request.insert(make_pair("MEMORY", delta_memory));
}
if ( tmpl->get("CPU", delta_cpu) == true )
{
vm_request.insert(make_pair("CPU", delta_cpu));
}
return check_quota("", vm_request, default_quotas, error);
}

View File

@ -219,6 +219,31 @@ bool Quotas::quota_check(QuotaType type,
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
bool Quotas::quota_update(QuotaType type,
Template *tmpl,
Quotas &default_quotas,
string &error_str)
{
switch (type)
{
// This is an internal check, should never get in here.
case DATASTORE:
case NETWORK:
case IMAGE:
case VIRTUALMACHINE:
error_str = "Cannot update quota. Not implemented";
return false;
case VM:
return vm_quota.update(tmpl, default_quotas, error_str);
}
return false;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void Quotas::quota_del(QuotaType type, int uid, int gid, Template * tmpl) void Quotas::quota_del(QuotaType type, int uid, int gid, Template * tmpl)
{ {
Nebula& nd = Nebula::instance(); Nebula& nd = Nebula::instance();

View File

@ -1258,14 +1258,11 @@ void VirtualMachine::cp_previous_history()
void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk) void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk)
{ {
string scpu;
istringstream iss; istringstream iss;
float fcpu; float fcpu;
get_template_attribute("MEMORY",memory); if ((get_template_attribute("MEMORY",memory) == false) ||
get_template_attribute("CPU",scpu); (get_template_attribute("CPU",fcpu) == false))
if ((memory == 0) || (scpu==""))
{ {
cpu = 0; cpu = 0;
memory = 0; memory = 0;
@ -1274,9 +1271,6 @@ void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk)
return; return;
} }
iss.str(scpu);
iss >> fcpu;
cpu = (int) (fcpu * 100);//now in 100% cpu = (int) (fcpu * 100);//now in 100%
memory = memory * 1024; //now in Kilobytes memory = memory * 1024; //now in Kilobytes
disk = 0; disk = 0;
@ -1287,6 +1281,27 @@ void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk)
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
void VirtualMachine::resize(float cpu, int memory, int vcpu)
{
ostringstream oss;
oss << cpu;
replace_template_attribute("CPU", oss.str());
oss.str("");
oss << memory;
replace_template_attribute("MEMORY", oss.str());
oss.str("");
oss << vcpu;
replace_template_attribute("VCPU", oss.str());
return;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
static void assign_disk_targets(queue<pair <string, VectorAttribute *> >& _queue, static void assign_disk_targets(queue<pair <string, VectorAttribute *> >& _queue,
set<string>& used_targets) set<string>& used_targets)
{ {