From 99cd9aa69d954c71a87be48c48a2523b6db4fe06 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sun, 27 May 2012 23:15:05 +0200 Subject: [PATCH 01/47] feature 1288: Add base class for Quotas. Implementation of Image Quotas --- include/Quota.h | 84 ++++++++++++++++ include/QuotaImage.h | 94 ++++++++++++++++++ src/um/Quota.cc | 106 ++++++++++++++++++++ src/um/QuotaImage.cc | 224 +++++++++++++++++++++++++++++++++++++++++++ src/um/SConstruct | 4 +- 5 files changed, 511 insertions(+), 1 deletion(-) create mode 100644 include/Quota.h create mode 100644 include/QuotaImage.h create mode 100644 src/um/Quota.cc create mode 100644 src/um/QuotaImage.cc diff --git a/include/Quota.h b/include/Quota.h new file mode 100644 index 0000000000..bfa01de451 --- /dev/null +++ b/include/Quota.h @@ -0,0 +1,84 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTA_H_ +#define QUOTA_H_ + +#include "Template.h" + +/** + * Base class for resource quotas, it provides basic storage and management of + * the quotas. Each resource MUST inherit from it to implement check and + * update methods. Quotas are stored in a template form, each class store the + * limits and usage in a resource specific format. + */ +class Quota: public Template +{ +public: + + /** + * Set the quotas. If the quota previously exists its limit is updated. + * @param quota_str the quota template in ASCII or XML formats + * @param error describe the error in case of error + * @return 0 on success -1 otherwise + */ + int set(const string& quota_str, string& error); + +protected: + + Quota(const char * quota_name): Template(true, '=', quota_name) {}; + + virtual ~Quota(){}; + + /** + * Sets new limit values for the quota + * @param quota to be updated + * @param va attribute with the new limits + * @return 0 on success or -1 if wrong limits + */ + virtual int update_limits(Attribute* quota, const Attribute* va) + { + return -1; + }; + + /** + * Creates an empty quota based on the given attribute. The attribute va + * contains the limits for the quota. + * @param va limits for the new quota if 0 limits will be 0 + * @return a new attribute representing the quota + */ + virtual Attribute * new_quota(Attribute* va) + { + return 0; + } + + /** + * Adds a given value to the current quota (single) + * @param attr the quota with a numeric value + * @param num value to add to the current quota + */ + void add_to_quota(SingleAttribute * attr, float num); + + /** + * Adds a given value to the current quota (vector) + * @param attr the quota; + * @param va_name name of the quota in the vector attribute + * @param num value to add to the current quota; + */ + void add_to_quota(VectorAttribute * attr, const string& va_name, int num); +}; + +#endif /*QUOTA_H_*/ diff --git a/include/QuotaImage.h b/include/QuotaImage.h new file mode 100644 index 0000000000..ac56908aa1 --- /dev/null +++ b/include/QuotaImage.h @@ -0,0 +1,94 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTA_IMAGE_H_ +#define QUOTA_IMAGE_H_ + +#include "Quota.h" + +/** + * Image Quotas, defined as: + * = [ + * IMAGES = + * SIZE = + * IMAGES_USED = Current number of images in the datastore + * SIZE_USED = Current storage capacity un the datastore + * ] + * + * 0 = unlimited, default if missing + */ + +class QuotaImage : public Quota +{ + QuotaImage():Quota("IMAGE_QUOTA"){}; + + ~QuotaImage(){}; + + /** + * Check if image allocation will exceed the quota limits. If not the + * usage counters are updated + * @param ds_name the datastore where the image is going to be allocated + * @param size of the image + * @param error string + * @return true if the operation can be performed + */ + bool check(const string& ds_name, int size, string& error); + + /** + * Decrement usage counters when deallocating image + * @param ds_name the datastore where the image is allocated + * @param size of the image + */ + void del_usage(const string& ds_name, int size); + + /** + * Increment size usage counters + * @param ds_name the datastore where the image is allocated + * @param size of the image + */ + void add_size_usage(const string& ds_name, int size); + +protected: + + /** + * Sets new limit values for the quota + * @param quota to be updated + * @param va attribute with the new limits + * @return 0 on success or -1 if wrong limits + */ + int update_limits(Attribute * quota, Attribute * va); + + /** + * Creates an empty quota based on the given attribute. The attribute va + * contains the limits for the quota. + * @param va limits for the new quota if 0 limits will be 0 + * @return a new attribute representing the quota + */ + Attribute * new_quota(Attribute * va); + +private: + /** + * Return the limits for image and size stored in the a given quota. + * @param va_ptr the attribute that stores the quota + * @param images the limit for the number of images + * @param size the limit for the total storage size + * + * @return -1 if the limits are wrong 0 otherwise + */ + int get_limits(Attribute * va_ptr, string& images, string& size); +}; + +#endif /*QUOTA_IMAGE_H_*/ \ No newline at end of file diff --git a/src/um/Quota.cc b/src/um/Quota.cc new file mode 100644 index 0000000000..f8c3a29306 --- /dev/null +++ b/src/um/Quota.cc @@ -0,0 +1,106 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "Quota.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int Quota::set(const string& quota_str, string& error) +{ + Quota tmp("GENERIC_QUOTA"); + + if ( tmp.parse_str_or_xml(quota_str, error) != 0 ) + { + return -1; + } + + multimap::iterator it; + + pair::iterator, + multimap::iterator> actual; + + Attribute * quota; + + for ( it = tmp.attributes.begin(); it != tmp.attributes.end(); it++) + { + actual = attributes.equal_range(it->first); + + if (actual.first == actual.second ) //Quota not set yet. + { + if ((quota = new_quota(it->second)) == 0) + { + goto error_limits; + } + + attributes.insert(make_pair(quota->name(),quota)); + } + else + { + if (update_limits(actual.first->second, it->second)) + { + goto error_limits; + } + } + } + +error_limits: + ostringstream oss; + oss << "Negative limits or bad format in quota " << it->first; + + error = oss.str(); + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void Quota::add_to_quota(SingleAttribute * attr, float num) +{ + istringstream iss; + ostringstream oss; + float total; + + iss.str(attr->value()); + + iss >> total; + + total += num; + + oss << total; + + attr->replace(oss.str()); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num) +{ + istringstream iss; + ostringstream oss; + float total; + + iss.str(attr->vector_value(va_name.c_str())); + + iss >> total; + + total += num; + + oss << total; + + attr->replace(va_name, oss.str()); +} diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc new file mode 100644 index 0000000000..a0d0d0ce0c --- /dev/null +++ b/src/um/QuotaImage.cc @@ -0,0 +1,224 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "QuotaImage.h" + +bool QuotaImage::check(const string& ds_name, int size, string& error) +{ + vector vector_ds_limit; + VectorAttribute * ds_limit; + + int img_limit = 0; + int size_limit = 0; + int img_used = 0; + int size_used = 0; + + bool img_ok; + bool size_ok; + + // There is no quotas for this datastore, create a new one + if ( get(ds_name,vector_ds_limit) == 0 ) + { + map ds_quota; + VectorAttribute * attr; + + ds_quota.insert(make_pair("IMAGES", "0")); + ds_quota.insert(make_pair("SIZE", "0")); + ds_quota.insert(make_pair("IMAGES_USED", "1")); + ds_quota.insert(make_pair("SIZE_USED", "0")); + + attr = new VectorAttribute(ds_name, ds_quota); + + attributes.insert(make_pair(ds_name, attr)); + + return true; + } + + ds_limit = dynamic_cast(vector_ds_limit[0]); + + if (ds_limit == 0) + { + error = "Internal error checking quota limits"; + return false; + } + + ds_limit->vector_value("IMAGES", img_limit); + ds_limit->vector_value("SIZE", size_limit); + + ds_limit->vector_value("IMAGES_USED", img_used); + ds_limit->vector_value("SIZE_USED", size_used); + + img_ok = (img_limit == 0) || ((img_used + 1) <= img_limit ); + size_ok = (size_limit== 0) || ((size_used + size) <= size_limit); + + if ( img_ok && size_ok ) + { + add_to_quota(ds_limit, "IMAGES_USED", +1); + add_to_quota(ds_limit, "SIZE_USED", +size); + } + else if (!img_ok) + { + ostringstream oss; + + oss << "Maximum number of images limit (" << img_limit << ")" + << " reached for datastore " << ds_name; + + error = oss.str(); + } + else if (!size_ok) + { + ostringstream oss; + + oss << "Maximum storage capacity limit (" << size_limit << ")" + << " reached for datastore " << ds_name; + + error = oss.str(); + } + + return img_ok && size_ok; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaImage::del_usage(const string& ds_name, int size) +{ + vector vector_ds_limit; + VectorAttribute * ds_limit; + + if ( get(ds_name,vector_ds_limit) == 0 ) + { + return; + } + + ds_limit = dynamic_cast(vector_ds_limit[0]); + + if (ds_limit == 0) + { + return; + } + + add_to_quota(ds_limit, "IMAGES_USED", -1); + add_to_quota(ds_limit, "SIZE_USED", -size); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaImage::add_size_usage(const string& ds_name, int size) +{ + vector vector_ds_limit; + VectorAttribute * ds_limit; + + if ( get(ds_name,vector_ds_limit) == 0 ) + { + return; + } + + ds_limit = dynamic_cast(vector_ds_limit[0]); + + if (ds_limit == 0) + { + return; + } + + add_to_quota(ds_limit, "SIZE_USED", +size); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int QuotaImage::update_limits(Attribute * quota, Attribute * va) +{ + string images_limit; + string size_limit; + + VectorAttribute * vquota = dynamic_cast(quota); + + if ( vquota == 0 || get_limits(va, images_limit, size_limit) != 0 ) + { + return -1; + } + + vquota->replace("IMAGES", images_limit); + vquota->replace("SIZE", size_limit); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +Attribute * QuotaImage::new_quota(Attribute * va) +{ + string images_limit = "0"; + string size_limit = "0"; + + if ( va != 0 ) + { + if (get_limits(va, images_limit, size_limit) != 0 ) + { + return 0; + } + } + + map limits; + + limits.insert(make_pair("IMAGES",images_limit)); + limits.insert(make_pair("SIZE",size_limit)); + limits.insert(make_pair("IMAGES_USED","0")); + limits.insert(make_pair("SIZE_USED","0")); + + return new VectorAttribute(va->name(),limits); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int QuotaImage::get_limits(Attribute * va_ptr, string& images, string& size) +{ + int images_limit = 0; + int size_limit = 0; + + ostringstream oss; + + VectorAttribute * va = dynamic_cast(va_ptr); + + if ( va == 0 ) + { + return -1; + } + + va->vector_value("IMAGES", images_limit); + va->vector_value("SIZE", size_limit); + + if ( images_limit < 0 || size_limit < 0 ) + { + return -1; + } + + oss << images_limit; + + images = oss.str(); + + oss.str(""); + + oss << size_limit; + + size = oss.str(); + + return 0; +} diff --git a/src/um/SConstruct b/src/um/SConstruct index 175d2c58ee..fe833d659e 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -23,7 +23,9 @@ lib_name='nebula_um' # Sources to generate the library source_files=[ 'User.cc', - 'UserPool.cc' + 'UserPool.cc', + 'Quota.cc', + 'QuotaImage.cc' ] # Build library From 18695f131446a5f5613922a18b9fe5e2222d44fe Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 28 May 2012 00:13:38 +0200 Subject: [PATCH 02/47] feature #1288: Image quotas in the user class --- include/QuotaImage.h | 2 ++ include/User.h | 7 +++++++ src/rm/RequestManagerAllocate.cc | 20 ++++++++++++++++++++ src/um/User.cc | 13 +++++++++++++ 4 files changed, 42 insertions(+) diff --git a/include/QuotaImage.h b/include/QuotaImage.h index ac56908aa1..e731ad0f1e 100644 --- a/include/QuotaImage.h +++ b/include/QuotaImage.h @@ -33,6 +33,8 @@ class QuotaImage : public Quota { +public: + QuotaImage():Quota("IMAGE_QUOTA"){}; ~QuotaImage(){}; diff --git a/include/User.h b/include/User.h index 770b5943bc..598c5d3ad3 100644 --- a/include/User.h +++ b/include/User.h @@ -19,6 +19,7 @@ #include "PoolSQL.h" #include "UserTemplate.h" +#include "QuotaImage.h" using namespace std; @@ -193,6 +194,12 @@ private: */ bool enabled; +public: + /** + * Usage Counters and Quotas + */ + QuotaImage image_quota; +private: // ************************************************************************* // Authentication session (Private) // ************************************************************************* diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 8d7221c1cd..f0c87d395d 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -381,6 +381,26 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, delete tmpl; return; } + + //TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST + User * user; + UserPool * upool = nd.get_upool(); + string error; + + user = upool->get(att.uid,false); + + if (!user->image_quota.check(ds_name, 0, error)) + { + failure_response(AUTHORIZATION, + authorization_error(error, att), + att); + + delete tmpl; + return; + } + + upool->update(user); + //TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST } rc = ipool->allocate(att.uid, diff --git a/src/um/User.cc b/src/um/User.cc index 7e6d78855b..2e0099a20c 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -138,6 +138,7 @@ string& User::to_xml(string& xml) const { ostringstream oss; string template_xml; + string image_quota_xml; int enabled_int = enabled?1:0; @@ -151,6 +152,7 @@ string& User::to_xml(string& xml) const "" << auth_driver <<""<< "" << enabled_int <<"" << obj_template->to_xml(template_xml) << + image_quota.to_xml(image_quota_xml) << ""; xml = oss.str(); @@ -193,6 +195,17 @@ int User::from_xml(const string& xml) rc += obj_template->from_xml_node(content[0]); + ObjectXML::free_nodes(content); + content.clear(); + + // Get associated quota for the user + ObjectXML::get_nodes("/USER/IMAGE_QUOTA", content); + + if (!content.empty()) + { + rc += image_quota.from_xml_node(content[0]); + } + ObjectXML::free_nodes(content); if (rc != 0) From e8fbb0cb6ec4d0a766c2a8496a6e00fe8f8fd6b7 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 28 May 2012 00:16:34 +0200 Subject: [PATCH 03/47] feature #1288: Remove test code --- src/rm/RequestManagerAllocate.cc | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index f0c87d395d..8d7221c1cd 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -381,26 +381,6 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, delete tmpl; return; } - - //TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST - User * user; - UserPool * upool = nd.get_upool(); - string error; - - user = upool->get(att.uid,false); - - if (!user->image_quota.check(ds_name, 0, error)) - { - failure_response(AUTHORIZATION, - authorization_error(error, att), - att); - - delete tmpl; - return; - } - - upool->update(user); - //TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST } rc = ipool->allocate(att.uid, From d6beb1fb06a2b2642ca032f30e5aa9e8e40ff151 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 29 May 2012 00:36:13 +0200 Subject: [PATCH 04/47] feature #1288: Work on Quota interface. Integration with authZ/authN system --- include/Attribute.h | 13 +++++- include/AuthManager.h | 30 +++++++------- include/Quota.h | 21 ++++++++++ include/QuotaImage.h | 21 +++------- include/User.h | 14 ++++++- include/UserPool.h | 5 +++ src/common/Attribute.cc | 30 ++++++++++++++ src/image/Image.cc | 2 +- src/um/QuotaImage.cc | 87 ++++++++++++++++++++++------------------- src/um/UserPool.cc | 60 ++++++++++++++++++++++++++++ 10 files changed, 208 insertions(+), 75 deletions(-) diff --git a/include/Attribute.h b/include/Attribute.h index d302419987..23a6f3314a 100644 --- a/include/Attribute.h +++ b/include/Attribute.h @@ -251,7 +251,18 @@ public: * * @return 0 on success, -1 otherwise */ - int vector_value(const char *name, int & value) const; + int vector_value(const char *name, int& value) const; + + /** + * Returns the integer value + * + * @param name Name of the attribute + * @param value Integer value, if an error ocurred the string returned is + * empty and value set to -1; + * + * @return the value in string form on success, "" otherwise + */ + string vector_value_str(const char *name, int& value) const; /** * Marshall the attribute in a single string. The string MUST be freed diff --git a/include/AuthManager.h b/include/AuthManager.h index 0d325495eb..f5b402ec9a 100644 --- a/include/AuthManager.h +++ b/include/AuthManager.h @@ -421,22 +421,32 @@ public: /** * The result of the request, true if authorized or authenticated */ - bool result; + bool result; /** * Error message for negative results */ - string message; + string message; /** * Time out */ - bool timeout; - + bool timeout; + /** * Identification of this request */ - int id; + int id; + + /** + * The user id for this request + */ + int uid; + + /** + * The user group ID + */ + int gid; private: @@ -447,16 +457,6 @@ private: */ ActionManager am; - /** - * The user id for this request - */ - int uid; - - /** - * The user group ID - */ - int gid; - /** * Timeout for this request */ diff --git a/include/Quota.h b/include/Quota.h index bfa01de451..7f17bd3b60 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -37,6 +37,27 @@ public: */ int set(const string& quota_str, string& error); + /** + * Check if the resource allocation will exceed the quota limits. If not + * the usage counters are updated + * @param tmpl template for the resource + * @param error string + * @return true if the operation can be performed + */ + virtual bool check_add(const Template& tmpl, string& error) + { + return false; + } + + /** + * Decrement usage counters when deallocating image + * @param tmpl template for the resource + */ + virtual void del(const Template& tmpl) + { + return; + } + protected: Quota(const char * quota_name): Template(true, '=', quota_name) {}; diff --git a/include/QuotaImage.h b/include/QuotaImage.h index e731ad0f1e..49e976e51a 100644 --- a/include/QuotaImage.h +++ b/include/QuotaImage.h @@ -40,28 +40,19 @@ public: ~QuotaImage(){}; /** - * Check if image allocation will exceed the quota limits. If not the - * usage counters are updated - * @param ds_name the datastore where the image is going to be allocated - * @param size of the image + * Check if the resource allocation will exceed the quota limits. If not + * the usage counters are updated + * @param tmpl template for the resource * @param error string * @return true if the operation can be performed */ - bool check(const string& ds_name, int size, string& error); + bool check_add(const Template& tmpl, string& error); /** * Decrement usage counters when deallocating image - * @param ds_name the datastore where the image is allocated - * @param size of the image + * @param tmpl template for the resource */ - void del_usage(const string& ds_name, int size); - - /** - * Increment size usage counters - * @param ds_name the datastore where the image is allocated - * @param size of the image - */ - void add_size_usage(const string& ds_name, int size); + void del(const Template& tmpl); protected: diff --git a/include/User.h b/include/User.h index 598c5d3ad3..cd053a1742 100644 --- a/include/User.h +++ b/include/User.h @@ -167,6 +167,17 @@ public: { return new UserTemplate; } + + /** + * Check Image quotas, it updates usage counters if quotas are not exceeded + * @param tmpl template for the image + * @param reason string describing the error + * @return true if image can be allocated, false otherwise + */ + bool image_quota_check(const Template& tmpl, string& reason) + { + return image_quota.check_add(tmpl, reason); + } private: // ------------------------------------------------------------------------- @@ -194,12 +205,11 @@ private: */ bool enabled; -public: /** * Usage Counters and Quotas */ QuotaImage image_quota; -private: + // ************************************************************************* // Authentication session (Private) // ************************************************************************* diff --git a/include/UserPool.h b/include/UserPool.h index afe2e54e36..25fc66147f 100644 --- a/include/UserPool.h +++ b/include/UserPool.h @@ -137,6 +137,11 @@ public: */ static int authorize(AuthRequest& ar); + int authorize_quota(AuthRequest& ar, + PoolObjectSQL::ObjectType type, + const Template * tmpl, + string& error); + /** * Dumps the User pool in XML format. A filter can be also added to the * query diff --git a/src/common/Attribute.cc b/src/common/Attribute.cc index bce9e58eb8..8eb41d6cb1 100644 --- a/src/common/Attribute.cc +++ b/src/common/Attribute.cc @@ -225,3 +225,33 @@ int VectorAttribute::vector_value(const char *name, int & value) const return 0; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string VectorAttribute::vector_value_str(const char *name, int& value) const +{ + map::const_iterator it; + + it = attribute_value.find(name); + + if ( it == attribute_value.end() ) + { + value = -1; + return ""; + } + + if ( it->second.empty() ) + { + value = -1; + return ""; + } + + istringstream iss(it->second); + iss >> value; + + return it->second; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/image/Image.cc b/src/image/Image.cc index b0eb1b53a9..19b6238d31 100644 --- a/src/image/Image.cc +++ b/src/image/Image.cc @@ -464,7 +464,7 @@ int Image::disk_attribute( VectorAttribute * disk, get_template_attribute("DEV_PREFIX", dev_prefix); - if (dev_prefix.empty())//Removed from image template, get it again from defaults + if (dev_prefix.empty())//Removed from image template, get it again { dev_prefix = ImagePool::default_dev_prefix(); } diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc index a0d0d0ce0c..1ace1bdbdf 100644 --- a/src/um/QuotaImage.cc +++ b/src/um/QuotaImage.cc @@ -16,7 +16,7 @@ #include "QuotaImage.h" -bool QuotaImage::check(const string& ds_name, int size, string& error) +bool QuotaImage::check_add(const Template& tmpl, string& error) { vector vector_ds_limit; VectorAttribute * ds_limit; @@ -29,7 +29,27 @@ bool QuotaImage::check(const string& ds_name, int size, string& error) bool img_ok; bool size_ok; - // There is no quotas for this datastore, create a new one + string ds_name; + int size; + + // --------------------- Get data from the Template -------------------- + + tmpl.get("DATASTORE", ds_name); + + if ( ds_name.empty() ) + { + error = "Datastore not defined for image"; + return false; + } + + if ( tmpl.get("SIZE", size) == false ) + { + error = "Size not defined for image"; + return false; + } + + // ------ There is no quotas for this datastore, create a new one ------ + if ( get(ds_name,vector_ds_limit) == 0 ) { map ds_quota; @@ -47,6 +67,8 @@ bool QuotaImage::check(const string& ds_name, int size, string& error) return true; } + // ------ Check usage limits ------ + ds_limit = dynamic_cast(vector_ds_limit[0]); if (ds_limit == 0) @@ -94,10 +116,25 @@ bool QuotaImage::check(const string& ds_name, int size, string& error) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void QuotaImage::del_usage(const string& ds_name, int size) +void QuotaImage::del(const Template& tmpl) { vector vector_ds_limit; - VectorAttribute * ds_limit; + VectorAttribute * ds_limit; + + string ds_name; + int size; + + tmpl.get("DATASTORE", ds_name); + + if ( ds_name.empty() ) + { + return; + } + + if ( tmpl.get("SIZE", size) == false ) + { + return; + } if ( get(ds_name,vector_ds_limit) == 0 ) { @@ -118,29 +155,6 @@ void QuotaImage::del_usage(const string& ds_name, int size) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void QuotaImage::add_size_usage(const string& ds_name, int size) -{ - vector vector_ds_limit; - VectorAttribute * ds_limit; - - if ( get(ds_name,vector_ds_limit) == 0 ) - { - return; - } - - ds_limit = dynamic_cast(vector_ds_limit[0]); - - if (ds_limit == 0) - { - return; - } - - add_to_quota(ds_limit, "SIZE_USED", +size); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - int QuotaImage::update_limits(Attribute * quota, Attribute * va) { string images_limit; @@ -193,8 +207,6 @@ int QuotaImage::get_limits(Attribute * va_ptr, string& images, string& size) int images_limit = 0; int size_limit = 0; - ostringstream oss; - VectorAttribute * va = dynamic_cast(va_ptr); if ( va == 0 ) @@ -202,23 +214,16 @@ int QuotaImage::get_limits(Attribute * va_ptr, string& images, string& size) return -1; } - va->vector_value("IMAGES", images_limit); - va->vector_value("SIZE", size_limit); + images = va->vector_value("IMAGES", images_limit); + size = va->vector_value("SIZE", size_limit); if ( images_limit < 0 || size_limit < 0 ) { return -1; } - oss << images_limit; - - images = oss.str(); - - oss.str(""); - - oss << size_limit; - - size = oss.str(); - return 0; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index 5f1b89813e..165c8ef626 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -763,3 +763,63 @@ int UserPool::authorize(AuthRequest& ar) return rc; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int UserPool::authorize_quota(AuthRequest& ar, + PoolObjectSQL::ObjectType type, + const Template * tmpl, + string& error) +{ + Nebula& nd = Nebula::instance(); + AuthManager * authm = nd.get_authm(); + int rc = -1; + + if (authm == 0 || !authm->is_authz_enabled()) + { + if (ar.core_authorize()) //ACL and Permissions OK + { + User * user; + bool quota_check; + + user = get(ar.uid, true); + + if ( user == 0 ) + { + error = "Internal error authorizing request"; + } + else + { + //TODO use type to check the right quota + quota_check = user-> image_quota_check(tmpl, error); + + if (quota_check) + { + rc = 0; + } + } + } + } + else + { + authm->trigger(AuthManager::AUTHORIZE,&ar); + ar.wait(); + + if (ar.result==true) + { + rc = 0; + } + else + { + ostringstream oss; + oss << "Auth Error: " << ar.message; + + NebulaLog::log("AuM",Log::ERROR,oss); + + error = oss.str(); + } + } + + return rc; +} + From 552d370593a4315d4daa89d2f254d10591d72272 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 30 May 2012 02:20:16 +0200 Subject: [PATCH 05/47] feature #1288: Better integration for Quotas with RequestManager --- include/AuthManager.h | 18 +++++----- include/Quota.h | 4 +-- include/QuotaImage.h | 4 +-- include/Request.h | 12 ++++++- include/User.h | 2 +- include/UserPool.h | 8 ++--- src/rm/Request.cc | 53 +++++++++++++++++++++++++++ src/rm/RequestManagerAllocate.cc | 34 ++++++++++++++---- src/um/QuotaImage.cc | 12 +++---- src/um/UserPool.cc | 61 -------------------------------- 10 files changed, 113 insertions(+), 95 deletions(-) diff --git a/include/AuthManager.h b/include/AuthManager.h index f5b402ec9a..53ba90a23c 100644 --- a/include/AuthManager.h +++ b/include/AuthManager.h @@ -438,6 +438,15 @@ public: */ int id; +private: + + friend class AuthManager; + + /** + * The ActionManager that will be notify when the request is ready. + */ + ActionManager am; + /** * The user id for this request */ @@ -448,15 +457,6 @@ public: */ int gid; -private: - - friend class AuthManager; - - /** - * The ActionManager that will be notify when the request is ready. - */ - ActionManager am; - /** * Timeout for this request */ diff --git a/include/Quota.h b/include/Quota.h index 7f17bd3b60..888525e8b8 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -44,7 +44,7 @@ public: * @param error string * @return true if the operation can be performed */ - virtual bool check_add(const Template& tmpl, string& error) + virtual bool check_add(Template* tmpl, string& error) { return false; } @@ -53,7 +53,7 @@ public: * Decrement usage counters when deallocating image * @param tmpl template for the resource */ - virtual void del(const Template& tmpl) + virtual void del(Template* tmpl) { return; } diff --git a/include/QuotaImage.h b/include/QuotaImage.h index 49e976e51a..91be5c4963 100644 --- a/include/QuotaImage.h +++ b/include/QuotaImage.h @@ -46,13 +46,13 @@ public: * @param error string * @return true if the operation can be performed */ - bool check_add(const Template& tmpl, string& error); + bool check_add(Template* tmpl, string& error); /** * Decrement usage counters when deallocating image * @param tmpl template for the resource */ - void del(const Template& tmpl); + void del(Template* tmpl); protected: diff --git a/include/Request.h b/include/Request.h index 9c479d97b6..45f07eae15 100644 --- a/include/Request.h +++ b/include/Request.h @@ -130,7 +130,17 @@ protected: */ bool basic_authorization(int oid, AuthRequest::Operation op, RequestAttributes& att); - + + /** + * Performs a basic quota check for this request using the uid/gid + * from the request. Usage counters are updated for the user/group. + * @param tmpl describing the object + * @param att the specific request attributes + * + * @return true if the user is authorized. + */ + bool quota_authorization(Template * tmpl, RequestAttributes& att); + /** * Actual Execution method for the request. Must be implemented by the * XML-RPC requests diff --git a/include/User.h b/include/User.h index cd053a1742..aca9f88347 100644 --- a/include/User.h +++ b/include/User.h @@ -174,7 +174,7 @@ public: * @param reason string describing the error * @return true if image can be allocated, false otherwise */ - bool image_quota_check(const Template& tmpl, string& reason) + bool image_quota_check(Template * tmpl, string& reason) { return image_quota.check_add(tmpl, reason); } diff --git a/include/UserPool.h b/include/UserPool.h index 25fc66147f..046224bbd5 100644 --- a/include/UserPool.h +++ b/include/UserPool.h @@ -131,17 +131,13 @@ public: string& uname, string& gname); /** - * Returns whether there is a user with given username/password or not + * Returns whether the operations described in a authorization request are + * authorized ot not. * @param ar, an Authorization Request * @return -1 if authz failed, 0 otherwise */ static int authorize(AuthRequest& ar); - int authorize_quota(AuthRequest& ar, - PoolObjectSQL::ObjectType type, - const Template * tmpl, - string& error); - /** * Dumps the User pool in XML format. A filter can be also added to the * query diff --git a/src/rm/Request.cc b/src/rm/Request.cc index bd5b9a9f82..c9ee721a79 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -102,6 +102,59 @@ bool Request::basic_authorization(int oid, return true; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + + 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); + + return false; + } + + switch (auth_object) + { + case PoolObjectSQL::IMAGE: + rc = user->image_quota_check(tmpl, error_str); + break; + + case PoolObjectSQL::VM: + break; + + default: + user->unlock(); + return true; + } + + if (rc == true) + { + upool->update(user); + } + + user->unlock(); + + if ( rc == false ) + { + failure_response(AUTHORIZATION, + authorization_error(error_str, att), + att); + } + + return rc; +} /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 8d7221c1cd..e008edc7f6 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -15,11 +15,12 @@ /* -------------------------------------------------------------------------- */ #include "RequestManagerAllocate.h" -#include "NebulaLog.h" #include "Nebula.h" #include "PoolObjectSQL.h" +#define TO_UPPER(S) transform(S.begin(),S.end(),S.begin(),(int(*)(int))toupper) + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -309,9 +310,10 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, Nebula& nd = Nebula::instance(); DatastorePool * dspool = nd.get_dspool(); - ImagePool * ipool = static_cast(pool); - + ImagePool * ipool = static_cast(pool); + ImageTemplate * tmpl = new ImageTemplate; + Datastore * ds; Image::DiskType ds_disk_type; @@ -364,7 +366,19 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, if ( att.uid != 0 ) { AuthRequest ar(att.uid, att.gid); - string tmpl_str = ""; + string tmpl_str; + string tmpl_ds = ds_name; + + SingleAttribute * attr; + + // ------------------ Check permissions and ACLs ---------------------- + + TO_UPPER(tmpl_ds); + attr = new SingleAttribute("DATASTORE", tmpl_ds); + tmpl->set(attr); + + attr = new SingleAttribute("SIZE", "0"); + tmpl->set(attr); tmpl->to_xml(tmpl_str); @@ -381,6 +395,14 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, delete tmpl; return; } + + // -------------------------- Check Quotas ---------------------------- + + if ( quota_authorization(tmpl, att) == false ) + { + delete tmpl; + return; + } } rc = ipool->allocate(att.uid, @@ -396,6 +418,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, error_str); if ( rc < 0 ) { + //TODO: rollback quotas failure_response(INTERNAL, allocate_error(error_str), att); return; } @@ -455,9 +478,6 @@ int HostAllocate::pool_allocate( } -/* -------------------------------------------------------------------------- */ - - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc index 1ace1bdbdf..d0433acb0a 100644 --- a/src/um/QuotaImage.cc +++ b/src/um/QuotaImage.cc @@ -16,7 +16,7 @@ #include "QuotaImage.h" -bool QuotaImage::check_add(const Template& tmpl, string& error) +bool QuotaImage::check_add(Template * tmpl, string& error) { vector vector_ds_limit; VectorAttribute * ds_limit; @@ -34,7 +34,7 @@ bool QuotaImage::check_add(const Template& tmpl, string& error) // --------------------- Get data from the Template -------------------- - tmpl.get("DATASTORE", ds_name); + tmpl->get("DATASTORE", ds_name); if ( ds_name.empty() ) { @@ -42,7 +42,7 @@ bool QuotaImage::check_add(const Template& tmpl, string& error) return false; } - if ( tmpl.get("SIZE", size) == false ) + if ( tmpl->get("SIZE", size) == false ) { error = "Size not defined for image"; return false; @@ -116,7 +116,7 @@ bool QuotaImage::check_add(const Template& tmpl, string& error) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void QuotaImage::del(const Template& tmpl) +void QuotaImage::del(Template * tmpl) { vector vector_ds_limit; VectorAttribute * ds_limit; @@ -124,14 +124,14 @@ void QuotaImage::del(const Template& tmpl) string ds_name; int size; - tmpl.get("DATASTORE", ds_name); + tmpl->get("DATASTORE", ds_name); if ( ds_name.empty() ) { return; } - if ( tmpl.get("SIZE", size) == false ) + if ( tmpl->get("SIZE", size) == false ) { return; } diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index 165c8ef626..90afc539a8 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -762,64 +762,3 @@ int UserPool::authorize(AuthRequest& ar) return rc; } - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int UserPool::authorize_quota(AuthRequest& ar, - PoolObjectSQL::ObjectType type, - const Template * tmpl, - string& error) -{ - Nebula& nd = Nebula::instance(); - AuthManager * authm = nd.get_authm(); - int rc = -1; - - if (authm == 0 || !authm->is_authz_enabled()) - { - if (ar.core_authorize()) //ACL and Permissions OK - { - User * user; - bool quota_check; - - user = get(ar.uid, true); - - if ( user == 0 ) - { - error = "Internal error authorizing request"; - } - else - { - //TODO use type to check the right quota - quota_check = user-> image_quota_check(tmpl, error); - - if (quota_check) - { - rc = 0; - } - } - } - } - else - { - authm->trigger(AuthManager::AUTHORIZE,&ar); - ar.wait(); - - if (ar.result==true) - { - rc = 0; - } - else - { - ostringstream oss; - oss << "Auth Error: " << ar.message; - - NebulaLog::log("AuM",Log::ERROR,oss); - - error = oss.str(); - } - } - - return rc; -} - From 6819043e4efec975c428fd601d1baf44b4238d42 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 30 May 2012 12:53:51 +0200 Subject: [PATCH 06/47] feature #1288: Add rollback to image creation --- include/Request.h | 8 +++++++ include/Template.h | 11 ++++++++++ include/User.h | 10 ++++++++- src/rm/Request.cc | 36 ++++++++++++++++++++++++++++++++ src/rm/RequestManagerAllocate.cc | 24 ++++++++++----------- 5 files changed, 75 insertions(+), 14 deletions(-) diff --git a/include/Request.h b/include/Request.h index 45f07eae15..18b4ac58ec 100644 --- a/include/Request.h +++ b/include/Request.h @@ -141,6 +141,14 @@ protected: */ bool quota_authorization(Template * tmpl, RequestAttributes& att); + /** + * Performs rollback on usage counters for a previous quota check operation + * for the request. + * @param tmpl describing the object + * @param att the specific request attributes + */ + void quota_rollback(Template * tmpl, RequestAttributes& att); + /** * Actual Execution method for the request. Must be implemented by the * XML-RPC requests diff --git a/include/Template.h b/include/Template.h index ab4ab441fd..671945c9ec 100644 --- a/include/Template.h +++ b/include/Template.h @@ -134,6 +134,17 @@ public: */ virtual void set(Attribute * attr); + /** + * Adds a new single attribute to the template. + * @param name of the attribute + * @param value of the attribute + */ + void add(const string& name, const string& value) + { + SingleAttribute * at = new SingleAttribute(name, value); + set(at); + } + /** * Removes an attribute from the template. The attributes are returned. The * attributes MUST be freed by the calling funtion diff --git a/include/User.h b/include/User.h index aca9f88347..cd88f51a93 100644 --- a/include/User.h +++ b/include/User.h @@ -178,7 +178,15 @@ public: { return image_quota.check_add(tmpl, reason); } - + + /** + * Delete usage from quota counters. + * @param tmpl template for the image, with usage + */ + void image_quota_del(Template * tmpl) + { + return image_quota.del(tmpl); + } private: // ------------------------------------------------------------------------- // Friends diff --git a/src/rm/Request.cc b/src/rm/Request.cc index c9ee721a79..858fbdccf8 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -102,6 +102,7 @@ bool Request::basic_authorization(int oid, return true; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -159,6 +160,41 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void Request::quota_rollback(Template * tmpl, RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + + User * user; + + user = upool->get(att.uid, true); + + if ( user == 0 ) + { + return; + } + + switch (auth_object) + { + case PoolObjectSQL::IMAGE: + user->image_quota_del(tmpl); + break; + + case PoolObjectSQL::VM: + break; + + default: + break; + } + + upool->update(user); + + user->unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void Request::failure_response(ErrorCode ec, const string& str_val, RequestAttributes& att) { diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index e008edc7f6..2cee0997d2 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -19,8 +19,6 @@ #include "Nebula.h" #include "PoolObjectSQL.h" -#define TO_UPPER(S) transform(S.begin(),S.end(),S.begin(),(int(*)(int))toupper) - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -367,18 +365,10 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, { AuthRequest ar(att.uid, att.gid); string tmpl_str; - string tmpl_ds = ds_name; - - SingleAttribute * attr; // ------------------ Check permissions and ACLs ---------------------- - - TO_UPPER(tmpl_ds); - attr = new SingleAttribute("DATASTORE", tmpl_ds); - tmpl->set(attr); - - attr = new SingleAttribute("SIZE", "0"); - tmpl->set(attr); + tmpl->add("DATASTORE", ds_name); + tmpl->add("SIZE", "0"); tmpl->to_xml(tmpl_str); @@ -403,6 +393,8 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, delete tmpl; return; } + + tmpl->erase("DATASTORE"); } rc = ipool->allocate(att.uid, @@ -418,7 +410,13 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, error_str); if ( rc < 0 ) { - //TODO: rollback quotas + Template img_usage; + + img_usage.add("DATASTORE", ds_name); + img_usage.add("SIZE", "0"); + + quota_rollback(&img_usage, att); + failure_response(INTERNAL, allocate_error(error_str), att); return; } From e192b6348b9bc6ae1139e74ee0a31ca82bc59777 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 31 May 2012 16:51:07 +0200 Subject: [PATCH 07/47] feature #1288: Moved AuthRequest to its own file to prepare a generic synchronous request interface --- include/AclManager.h | 1 + include/AuthManager.h | 249 ---------------------------------- include/AuthRequest.h | 271 +++++++++++++++++++++++++++++++++++++ include/Request.h | 2 +- src/acl/AclRule.cc | 2 +- src/authm/AuthManager.cc | 1 + src/pool/PoolObjectAuth.cc | 2 +- 7 files changed, 276 insertions(+), 252 deletions(-) create mode 100644 include/AuthRequest.h diff --git a/include/AclManager.h b/include/AclManager.h index 1107b7917c..d4401cbff1 100644 --- a/include/AclManager.h +++ b/include/AclManager.h @@ -18,6 +18,7 @@ #define ACL_MANAGER_H_ #include "AuthManager.h" +#include "AuthRequest.h" #include "PoolObjectSQL.h" #include "AclRule.h" diff --git a/include/AuthManager.h b/include/AuthManager.h index 53ba90a23c..e01332b65d 100644 --- a/include/AuthManager.h +++ b/include/AuthManager.h @@ -21,11 +21,8 @@ #include "MadManager.h" #include "ActionManager.h" -#include "SSLTools.h" - #include "AuthManagerDriver.h" #include "PoolObjectSQL.h" -#include "PoolObjectAuth.h" using namespace std; @@ -267,251 +264,5 @@ private: AuthRequest * get_request(int id); }; -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/** - * The AuthRequest class is used to pass an Authorization or Authentication - * request to the AuthManager. The result of the request will be stored - * in the result and message attributes of this class. - */ -class AuthRequest : public ActionListener -{ -public: - AuthRequest(int _uid, int _gid): - result(false), - timeout(false), - uid(_uid), - gid(_gid), - time_out(0), - self_authorize(true) - { - am.addListener(this); - }; - - ~AuthRequest(){}; - - /** - * Authorization Request Type - */ - enum Operation - { - USE = 0x1LL, /**< Auth. to use an object */ - MANAGE = 0x2LL, /**< Auth. to perform management actions */ - ADMIN = 0x4LL, /**< Auth. to perform administrative actions */ - CREATE = 0x8LL /**< Auth. to create an object */ - }; - - static string operation_to_str(Operation op) - { - switch (op) - { - case USE: return "USE"; - case MANAGE: return "MANAGE"; - case ADMIN: return "ADMIN"; - case CREATE: return "CREATE"; - default: return ""; - } - }; - - - /** - * Sets the challenge to authenticate an user - * @param challenge a driver specific authentication challenge - */ - void add_authenticate(const string &_driver, - const string &_username, - const string &_password, - const string &_session) - { - username = _username; - password = _password; - session = _session; - - driver = _driver; - } - - /** - * Adds a CREATE authorization request. - * - * OBJECT:<-1|OBJECT_TMPL_XML64>:CREATE:UID:AUTH - * - * @param type of the object to be created - * @param template (base64 encoded) of the new object - */ - void add_create_auth(PoolObjectSQL::ObjectType type, const string& txml_64) - { - PoolObjectAuth perms; //oid & gid set to -1 - - perms.uid = uid; - perms.obj_type = type; - - add_auth(AuthRequest::CREATE, perms, txml_64); - } - - /** - * Adds a new authorization item to this request - * - * OBJECT:OBJECT_ID:ACTION:OWNER:AUTH - * - * @param op the operation to be authorized - * @param ob_perms object's permission attributes - */ - void add_auth(Operation op, - const PoolObjectAuth& ob_perms) - { - add_auth(op, ob_perms, ""); - } - - /** - * Gets the authorization requests in a single string - * @return a space separated list of auth requests, or an empty string if - * no auth requests were added - */ - string get_auths() - { - ostringstream oss; - unsigned int i; - - if ( auths.empty() ) - { - return string(); - } - - for (i=0; i auths; - - /** - * Plain authorization for the request - */ - bool self_authorize; - - /** - * No actions defined for the Auth request, just FINALIZE when done - */ - void do_action(const string &name, void *args){}; - - /** - * Adds a new authorization item to this request, with a template for - * a new object - * - * OBJECT::ACTION:OWNER:AUTH - * - * @param op the operation to be authorized - * @param ob_perms object's permission attributes - * @param ob_template new object's template. If it is empty, - * it will be ignored - */ - void add_auth(Operation op, - const PoolObjectAuth& ob_perms, - string ob_template); -}; - #endif /*AUTH_MANAGER_H*/ diff --git a/include/AuthRequest.h b/include/AuthRequest.h new file mode 100644 index 0000000000..6fe9a59e31 --- /dev/null +++ b/include/AuthRequest.h @@ -0,0 +1,271 @@ +/* -------------------------------------------------------------------------- */ +/* 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 AUTH_REQUEST_H_ +#define AUTH_REQUEST_H_ + +#include + +#include "ActionManager.h" +#include "PoolObjectAuth.h" +#include "SSLTools.h" +#include "AuthManager.h" + +using namespace std; + +/** + * The AuthRequest class is used to pass an Authorization or Authentication + * request to the AuthManager. The result of the request will be stored + * in the result and message attributes of this class. + */ +class AuthRequest : public ActionListener +{ +public: + AuthRequest(int _uid, int _gid): + result(false), + timeout(false), + uid(_uid), + gid(_gid), + time_out(0), + self_authorize(true) + { + am.addListener(this); + }; + + ~AuthRequest(){}; + + /** + * Authorization Request Type + */ + enum Operation + { + USE = 0x1LL, /**< Auth. to use an object */ + MANAGE = 0x2LL, /**< Auth. to perform management actions */ + ADMIN = 0x4LL, /**< Auth. to perform administrative actions */ + CREATE = 0x8LL /**< Auth. to create an object */ + }; + + static string operation_to_str(Operation op) + { + switch (op) + { + case USE: return "USE"; + case MANAGE: return "MANAGE"; + case ADMIN: return "ADMIN"; + case CREATE: return "CREATE"; + default: return ""; + } + }; + + /** + * Sets the challenge to authenticate an user + * @param challenge a driver specific authentication challenge + */ + void add_authenticate(const string &_driver, + const string &_username, + const string &_password, + const string &_session) + { + username = _username; + password = _password; + session = _session; + + driver = _driver; + } + + /** + * Adds a CREATE authorization request. + * + * OBJECT:<-1|OBJECT_TMPL_XML64>:CREATE:UID:AUTH + * + * @param type of the object to be created + * @param template (base64 encoded) of the new object + */ + void add_create_auth(PoolObjectSQL::ObjectType type, const string& txml_64) + { + PoolObjectAuth perms; //oid & gid set to -1 + + perms.uid = uid; + perms.obj_type = type; + + add_auth(AuthRequest::CREATE, perms, txml_64); + } + + /** + * Adds a new authorization item to this request + * + * OBJECT:OBJECT_ID:ACTION:OWNER:AUTH + * + * @param op the operation to be authorized + * @param ob_perms object's permission attributes + */ + void add_auth(Operation op, + const PoolObjectAuth& ob_perms) + { + add_auth(op, ob_perms, ""); + } + + /** + * Gets the authorization requests in a single string + * @return a space separated list of auth requests, or an empty string if + * no auth requests were added + */ + string get_auths() + { + ostringstream oss; + unsigned int i; + + if ( auths.empty() ) + { + return string(); + } + + for (i=0; i auths; + + /** + * Plain authorization for the request + */ + bool self_authorize; + + /** + * No actions defined for the Auth request, just FINALIZE when done + */ + void do_action(const string &name, void *args){}; + + /** + * Adds a new authorization item to this request, with a template for + * a new object + * + * OBJECT::ACTION:OWNER:AUTH + * + * @param op the operation to be authorized + * @param ob_perms object's permission attributes + * @param ob_template new object's template. If it is empty, + * it will be ignored + */ + void add_auth(Operation op, + const PoolObjectAuth& ob_perms, + string ob_template); +}; + +#endif diff --git a/include/Request.h b/include/Request.h index 18b4ac58ec..3ee051e7c1 100644 --- a/include/Request.h +++ b/include/Request.h @@ -21,7 +21,7 @@ #include #include "RequestManager.h" -#include "AuthManager.h" +#include "AuthRequest.h" #include "PoolObjectSQL.h" using namespace std; diff --git a/src/acl/AclRule.cc b/src/acl/AclRule.cc index e65a1071e2..8376d5b92e 100644 --- a/src/acl/AclRule.cc +++ b/src/acl/AclRule.cc @@ -15,7 +15,7 @@ /* -------------------------------------------------------------------------- */ #include "AclRule.h" -#include "AuthManager.h" +#include "AuthRequest.h" #include "PoolObjectSQL.h" /* -------------------------------------------------------------------------- */ diff --git a/src/authm/AuthManager.cc b/src/authm/AuthManager.cc index a772dca1dc..a45b64aa7f 100644 --- a/src/authm/AuthManager.cc +++ b/src/authm/AuthManager.cc @@ -15,6 +15,7 @@ /* -------------------------------------------------------------------------- */ #include "AuthManager.h" +#include "AuthRequest.h" #include "NebulaLog.h" #include "SSLTools.h" #include "PoolObjectAuth.h" diff --git a/src/pool/PoolObjectAuth.cc b/src/pool/PoolObjectAuth.cc index ec1ffe3d00..7c4ccbcedf 100644 --- a/src/pool/PoolObjectAuth.cc +++ b/src/pool/PoolObjectAuth.cc @@ -15,7 +15,7 @@ /* -------------------------------------------------------------------------- */ #include "PoolObjectAuth.h" -#include "AuthManager.h" +#include "AuthRequest.h" #include "AclRule.h" void PoolObjectAuth::get_acl_rules(AclRule& owner_rule, From 9361376e90612801952768f9ff9287d73313210b Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 31 May 2012 18:38:14 +0200 Subject: [PATCH 08/47] feature #1288: Managers can now execute synchronous actions --- include/AuthManager.h | 85 +--------------------------- include/AuthRequest.h | 70 ++--------------------- include/MadManager.h | 52 ++++++++++++++++- src/authm/AuthManager.cc | 117 +-------------------------------------- src/mad/MadManager.cc | 111 +++++++++++++++++++++++++++++++++++++ src/nebula/Nebula.cc | 3 +- 6 files changed, 170 insertions(+), 268 deletions(-) diff --git a/include/AuthManager.h b/include/AuthManager.h index e01332b65d..ea4da3ccb4 100644 --- a/include/AuthManager.h +++ b/include/AuthManager.h @@ -40,16 +40,11 @@ class AuthManager : public MadManager, public ActionListener public: AuthManager( - time_t timer, - time_t __time_out, - vector& _mads): + time_t timer, + vector& _mads): MadManager(_mads), timer_period(timer) { - _time_out = __time_out; - am.addListener(this); - - pthread_mutex_init(&mutex,0); }; ~AuthManager(){}; @@ -96,32 +91,6 @@ public: return authm_thread; }; - /** - * Notify the result of an auth request - */ - void notify_request(int auth_id, bool result, const string& message); - - /** - * Discards a pending request. Call this before freeing not notified or - * timeout requests. - */ - void discard_request(int auth_id) - { - lock(); - - auth_requests.erase(auth_id); - - unlock(); - } - - /** - * Gets default timeout for Auth requests - */ - static time_t time_out() - { - return _time_out; - } - /** * Returns true if there is an authorization driver enabled * @@ -143,21 +112,6 @@ private: */ ActionManager am; - /** - * List of pending requests - */ - map auth_requests; - - /** - * Mutex to access the auth_requests - */ - pthread_mutex_t mutex; - - /** - * Default timeout for Auth requests - */ - static time_t _time_out; - /** * Timer for the Manager (periocally triggers timer action) */ @@ -227,41 +181,6 @@ private: * This function authorizes a user request */ void authorize_action(AuthRequest * ar); - - /** - * This function is periodically executed to check time_outs on requests - */ - void timer_action(); - - /** - * Function to lock the pool - */ - void lock() - { - pthread_mutex_lock(&mutex); - }; - - /** - * Function to unlock the pool - */ - void unlock() - { - pthread_mutex_unlock(&mutex); - }; - - /** - * Add a new request to the Request map - * @param ar pointer to the AuthRequest - * @return the id for the request - */ - int add_request(AuthRequest *ar); - - /** - * Gets request from the Request map - * @param id for the request - * @return pointer to the AuthRequest - */ - AuthRequest * get_request(int id); }; #endif /*AUTH_MANAGER_H*/ diff --git a/include/AuthRequest.h b/include/AuthRequest.h index 6fe9a59e31..2ae3e04471 100644 --- a/include/AuthRequest.h +++ b/include/AuthRequest.h @@ -24,6 +24,8 @@ #include "SSLTools.h" #include "AuthManager.h" +#include "SyncRequest.h" + using namespace std; /** @@ -31,19 +33,10 @@ using namespace std; * request to the AuthManager. The result of the request will be stored * in the result and message attributes of this class. */ -class AuthRequest : public ActionListener +class AuthRequest : public SyncRequest { public: - AuthRequest(int _uid, int _gid): - result(false), - timeout(false), - uid(_uid), - gid(_gid), - time_out(0), - self_authorize(true) - { - am.addListener(this); - }; + AuthRequest(int _uid, int _gid): uid(_uid),gid(_gid),self_authorize(true){}; ~AuthRequest(){}; @@ -143,24 +136,6 @@ public: return oss.str(); }; - /** - * Notify client that we have an answer for the request - */ - void notify() - { - am.trigger(ActionListener::ACTION_FINALIZE,0); - }; - - /** - * Wait for the AuthRequest to be completed - */ - void wait() - { - time_out = time(0) + AuthManager::time_out(); - - am.loop(0,0); - }; - bool core_authorize() { return ( uid == 0 || self_authorize ); @@ -172,35 +147,10 @@ public: return (password == sha1_session); } - - /** - * The result of the request, true if authorized or authenticated - */ - bool result; - - /** - * Error message for negative results - */ - string message; - - /** - * Time out - */ - bool timeout; - /** - * Identification of this request - */ - int id; +private: -private: - friend class AuthManager; - - /** - * The ActionManager that will be notify when the request is ready. - */ - ActionManager am; /** * The user id for this request @@ -212,11 +162,6 @@ private: */ int gid; - /** - * Timeout for this request - */ - time_t time_out; - /** * Username to authenticate the user */ @@ -247,11 +192,6 @@ private: */ bool self_authorize; - /** - * No actions defined for the Auth request, just FINALIZE when done - */ - void do_action(const string &name, void *args){}; - /** * Adds a new authorization item to this request, with a template for * a new object diff --git a/include/MadManager.h b/include/MadManager.h index 2cd552f665..98ead506c8 100644 --- a/include/MadManager.h +++ b/include/MadManager.h @@ -30,6 +30,8 @@ using namespace std; +class SyncRequest; + extern "C" void * mad_manager_listener(void * _mm); /** @@ -54,7 +56,12 @@ public: * sudo application. */ virtual void load_mads(int uid) = 0; - + + /** + * Notify the result of an auth request + */ + void notify_request(int id, bool result, const string& message); + protected: MadManager(vector& _mads); @@ -93,7 +100,41 @@ protected: * @return 0 on success. */ int add(Mad *mad); - + + /** + * This function can be periodically executed to check time_outs on + * request. It will fail requests with an expired timeout and will notify + * the clients. + */ + void check_time_outs_action(); + + /** + * Add a new request to the Request map + * @param ar pointer to the request + * @return the id for the request + */ + int add_request(SyncRequest *ar); + + /** + * Gets request from the Request map + * @param id for the request + * @return pointer to the Request + */ + SyncRequest * get_request(int id); + + /** + * Discards a pending request. Call this before freeing not notified or + * timeout requests. + */ + void discard_request(int id) + { + lock(); + + sync_requests.erase(id); + + unlock(); + } + private: /** * Function to lock the Manager @@ -155,11 +196,16 @@ private: * can be free upon listener thread cancellation. */ ostringstream buffer; + + /** + * List of pending requests + */ + map sync_requests; /** * Listener thread implementation. */ - void listener(); + void listener(); }; #endif /*MAD_MANAGER_H_*/ diff --git a/src/authm/AuthManager.cc b/src/authm/AuthManager.cc index a45b64aa7f..2c6aad22b1 100644 --- a/src/authm/AuthManager.cc +++ b/src/authm/AuthManager.cc @@ -24,8 +24,6 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -time_t AuthManager::_time_out; - const char * AuthManager::auth_driver_name = "auth_exe"; /* -------------------------------------------------------------------------- */ @@ -126,7 +124,7 @@ extern "C" void * authm_action_loop(void *arg) NebulaLog::log("AuM",Log::INFO,"Authorization Manager started."); - authm->am.loop(authm->timer_period,0); + authm->am.loop(authm->timer_period, 0); NebulaLog::log("AuM",Log::INFO,"Authorization Manager stopped."); @@ -204,7 +202,7 @@ void AuthManager::do_action(const string &action, void * arg) } else if (action == ACTION_TIMER) { - timer_action(); + check_time_outs_action(); } else if (action == ACTION_FINALIZE) { @@ -249,7 +247,6 @@ void AuthManager::authenticate_action(AuthRequest * ar) // Make the request to the driver // ---- -------------------------------------------------------------------- - authm_md->authenticate(ar->id, ar->uid, ar->driver, @@ -313,116 +310,6 @@ error: return; } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -void AuthManager::timer_action() -{ - map::iterator it; - - time_t the_time = time(0); - - lock(); - - it = auth_requests.begin(); - - while ( it !=auth_requests.end()) - { - if ((it->second->time_out != 0) && (the_time > it->second->time_out)) - { - AuthRequest * ar = it->second; - auth_requests.erase(it++); - - ar->result = false; - ar->timeout = true; - ar->message = "Auth request timeout"; - - ar->notify(); - } - else - { - ++it; - } - } - - unlock(); - -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int AuthManager::add_request(AuthRequest *ar) -{ - static int auth_id = 0; - int id; - - lock(); - - id = auth_id++; - - auth_requests.insert(auth_requests.end(),make_pair(id,ar)); - - unlock(); - - return id; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -AuthRequest * AuthManager::get_request(int id) -{ - AuthRequest * ar = 0; - map::iterator it; - ostringstream oss; - - lock(); - - it=auth_requests.find(id); - - if ( it != auth_requests.end()) - { - ar = it->second; - - auth_requests.erase(it); - } - - unlock(); - - return ar; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -void AuthManager::notify_request(int auth_id,bool result,const string& message) -{ - - AuthRequest * ar; - - ar = get_request(auth_id); - - if ( ar == 0 ) - { - return; - } - - ar->result = result; - - if ( message != "-" ) - { - if ( !ar->message.empty() ) - { - ar->message.append("; "); - } - - ar->message.append(message); - } - - ar->notify(); -} - /* ************************************************************************** */ /* MAD Loading */ /* ************************************************************************** */ diff --git a/src/mad/MadManager.cc b/src/mad/MadManager.cc index 1a72a647f4..4fd2514be9 100644 --- a/src/mad/MadManager.cc +++ b/src/mad/MadManager.cc @@ -22,6 +22,7 @@ #include #include "MadManager.h" +#include "SyncRequest.h" /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -336,3 +337,113 @@ void MadManager::listener() } } } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void MadManager::check_time_outs_action() +{ + map::iterator it; + + time_t the_time = time(0); + + lock(); + + it = sync_requests.begin(); + + while ( it != sync_requests.end()) + { + if ((it->second->time_out != 0) && (the_time > it->second->time_out)) + { + SyncRequest * ar = it->second; + sync_requests.erase(it++); + + ar->result = false; + ar->timeout = true; + ar->message = "Request timeout"; + + ar->notify(); + } + else + { + ++it; + } + } + + unlock(); + +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int MadManager::add_request(SyncRequest *ar) +{ + static int request_id = 0; + int id; + + lock(); + + id = request_id++; + + sync_requests.insert(sync_requests.end(),make_pair(id,ar)); + + unlock(); + + return id; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +SyncRequest * MadManager::get_request(int id) +{ + SyncRequest * ar = 0; + map::iterator it; + ostringstream oss; + + lock(); + + it = sync_requests.find(id); + + if ( it != sync_requests.end()) + { + ar = it->second; + + sync_requests.erase(it); + } + + unlock(); + + return ar; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void MadManager::notify_request(int id, bool result, const string& message) +{ + + SyncRequest * ar; + + ar = get_request(id); + + if ( ar == 0 ) + { + return; + } + + ar->result = result; + + if ( message != "-" ) + { + if ( !ar->message.empty() ) + { + ar->message.append("; "); + } + + ar->message.append(message); + } + + ar->notify(); +} diff --git a/src/nebula/Nebula.cc b/src/nebula/Nebula.cc index 7ccb90c74b..ceba7f2609 100644 --- a/src/nebula/Nebula.cc +++ b/src/nebula/Nebula.cc @@ -500,8 +500,7 @@ void Nebula::start() if (!auth_mads.empty()) { - //Defaults 60s to timeout auth requests - authm = new AuthManager(timer_period,60,auth_mads); + authm = new AuthManager(timer_period, auth_mads); } else { From 20969b33758b903f3e7a7d8d4838bd4a39c8823b Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 11:58:24 +0200 Subject: [PATCH 09/47] feature #1288: Update AuthManager tests --- src/authm/test/AuthManagerTest.cc | 6 ++---- src/authm/test/SConstruct | 4 +++- src/authm/test/dummy | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/authm/test/AuthManagerTest.cc b/src/authm/test/AuthManagerTest.cc index 38635216ff..d5c709aa2f 100644 --- a/src/authm/test/AuthManagerTest.cc +++ b/src/authm/test/AuthManagerTest.cc @@ -65,7 +65,7 @@ public: t->get("AUTH_MAD", am_mads); - return new AuthManager(1,3,am_mads); + return new AuthManager(1,am_mads); }; }; @@ -144,7 +144,7 @@ public: /* ********************************************************************* */ /* ********************************************************************* */ - //This test needs a driver that takes more than 3 secs to AUTHENTICATE + //This test needs a driver that takes more than 90 secs to AUTHENTICATE void timeout() { AuthRequest ar(2, 2); @@ -157,8 +157,6 @@ public: CPPUNIT_ASSERT(ar.result==false); CPPUNIT_ASSERT(ar.timeout==true); - - am->discard_request(ar.id); } void authenticate() diff --git a/src/authm/test/SConstruct b/src/authm/test/SConstruct index 0e5538fcc1..3d7c89cba9 100644 --- a/src/authm/test/SConstruct +++ b/src/authm/test/SConstruct @@ -45,4 +45,6 @@ env.Prepend(LIBS=[ 'crypto' ]) -env.Program('test','AuthManagerTest.cc') +nt = env.Object('NebulaTemplate.o','../../nebula/NebulaTemplate.cc') + +env.Program('test',[nt,'AuthManagerTest.cc']) diff --git a/src/authm/test/dummy b/src/authm/test/dummy index 403ecc81a8..c3fa9f79ee 100755 --- a/src/authm/test/dummy +++ b/src/authm/test/dummy @@ -36,7 +36,7 @@ do "AUTHENTICATE") date 1>&2 >> mad.log if [ "$ARG4" = "timeout" ] ; then - sleep 4 + sleep 95 fi date 1>&2 >> mad.log From 0d9a4c998aeaaed17b36ba018363440cb16ae691 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 11:58:47 +0200 Subject: [PATCH 10/47] feature #1288: get, string version, for VirtualMachinePool --- include/VirtualMachinePool.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/VirtualMachinePool.h b/include/VirtualMachinePool.h index 664da7fdb6..b82087205b 100644 --- a/include/VirtualMachinePool.h +++ b/include/VirtualMachinePool.h @@ -75,6 +75,26 @@ public: return static_cast(PoolSQL::get(oid,lock)); }; + /** + * Function to get a VM from the pool, string version for VM ID + */ + VirtualMachine * get( + const string& oid_s, + bool lock) + { + istringstream iss(oid_s); + int oid; + + iss >> oid; + + if ( iss.fail() ) + { + return 0; + } + + return static_cast(PoolSQL::get(oid,lock)); + }; + /** * Function to get the IDs of running VMs * @param oids a vector that contains the IDs From f72e5f3a7da28241b53fa14053db54956dcb220f Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 11:59:19 +0200 Subject: [PATCH 11/47] feature #1288: Removed unneeded actions in datastore driver --- src/datastore_mad/one_datastore.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/datastore_mad/one_datastore.rb b/src/datastore_mad/one_datastore.rb index 2ed7afc99b..f06270b6b7 100755 --- a/src/datastore_mad/one_datastore.rb +++ b/src/datastore_mad/one_datastore.rb @@ -44,7 +44,6 @@ class DatastoreDriver < OpenNebulaDriver # Image Driver Protocol constants ACTION = { - :mv => "MV", :cp => "CP", :rm => "RM", :mkfs => "MKFS", @@ -77,7 +76,6 @@ class DatastoreDriver < OpenNebulaDriver @types = ds_type end -# register_action(ACTION[:mv].to_sym, method("mv")) register_action(ACTION[:cp].to_sym, method("cp")) register_action(ACTION[:rm].to_sym, method("rm")) register_action(ACTION[:mkfs].to_sym, method("mkfs")) @@ -86,10 +84,6 @@ class DatastoreDriver < OpenNebulaDriver ############################################################################ # Image Manager Protocol Actions (generic implementation) ############################################################################ -# TODO: Integrate this with TM -# def mv(id, ds, src, dst) -# do_image_action(id, ds, :mv, "'#{src}' '#{dst}' '#{id}'") -# end def cp(id, drv_message) ds = get_ds_type(drv_message) @@ -130,8 +124,6 @@ class DatastoreDriver < OpenNebulaDriver result, info = get_info_from_execution(rc) - - PP.pp([ACTION[action], result, id, info],STDERR) send_message(ACTION[action], result, id, info) end From 293ee180a40f4b18f52099d069b7130687df6e8b Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 12:01:23 +0200 Subject: [PATCH 12/47] feature #1288: Update request id in add method for sync requests. AuthManager updated. --- include/MadManager.h | 15 +-------------- src/authm/AuthManager.cc | 4 ++-- src/mad/MadManager.cc | 8 +++----- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/include/MadManager.h b/include/MadManager.h index 98ead506c8..43cfd434f5 100644 --- a/include/MadManager.h +++ b/include/MadManager.h @@ -113,7 +113,7 @@ protected: * @param ar pointer to the request * @return the id for the request */ - int add_request(SyncRequest *ar); + void add_request(SyncRequest *ar); /** * Gets request from the Request map @@ -122,19 +122,6 @@ protected: */ SyncRequest * get_request(int id); - /** - * Discards a pending request. Call this before freeing not notified or - * timeout requests. - */ - void discard_request(int id) - { - lock(); - - sync_requests.erase(id); - - unlock(); - } - private: /** * Function to lock the Manager diff --git a/src/authm/AuthManager.cc b/src/authm/AuthManager.cc index 2c6aad22b1..c7ba3b694b 100644 --- a/src/authm/AuthManager.cc +++ b/src/authm/AuthManager.cc @@ -241,7 +241,7 @@ void AuthManager::authenticate_action(AuthRequest * ar) // Queue the request // ------------------------------------------------------------------------ - ar->id = add_request(ar); + add_request(ar); // ------------------------------------------------------------------------ // Make the request to the driver @@ -285,7 +285,7 @@ void AuthManager::authorize_action(AuthRequest * ar) // Queue the request // ------------------------------------------------------------------------ - ar->id = add_request(ar); + add_request(ar); // ------------------------------------------------------------------------ // Make the request to the driver diff --git a/src/mad/MadManager.cc b/src/mad/MadManager.cc index 4fd2514be9..099ca18131 100644 --- a/src/mad/MadManager.cc +++ b/src/mad/MadManager.cc @@ -377,20 +377,18 @@ void MadManager::check_time_outs_action() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int MadManager::add_request(SyncRequest *ar) +void MadManager::add_request(SyncRequest *ar) { static int request_id = 0; int id; lock(); - id = request_id++; + ar->id = request_id++; - sync_requests.insert(sync_requests.end(),make_pair(id,ar)); + sync_requests.insert(sync_requests.end(),make_pair(ar->id,ar)); unlock(); - - return id; } /* -------------------------------------------------------------------------- */ From b52cfad4986c78bfcbb96dd638fd05e2784ac845 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 12:02:15 +0200 Subject: [PATCH 13/47] feature #1288: Stat action for core. Re-write of image driver protocol --- include/ImageManager.h | 12 + include/ImageManagerDriver.h | 15 +- include/SyncRequest.h | 101 ++++++ src/image/ImageManager.cc | 1 - src/image/ImageManagerActions.cc | 34 ++ src/image/ImageManagerDriver.cc | 538 +++++++++++++++++++------------ 6 files changed, 477 insertions(+), 224 deletions(-) create mode 100644 include/SyncRequest.h diff --git a/include/ImageManager.h b/include/ImageManager.h index 2d3d8789ca..66ffec84a7 100644 --- a/include/ImageManager.h +++ b/include/ImageManager.h @@ -125,6 +125,18 @@ public: */ int delete_image(int iid, const string& ds_data); + /** + * Gets the size of an image by calling the STAT action of the associated + * datastore driver. + * + * @param img_tmpl the template for the image + * @param ds_tmpl the template for the datastore + * @oaram result with a string representation of the size or if an error + * occurred describing the error. + * @result 0 on success + */ + int stat_image(const string& img_tmpl, const string& ds_tmpl, string& res); + private: /** * Generic name for the Image driver diff --git a/include/ImageManagerDriver.h b/include/ImageManagerDriver.h index 3319ebf717..8ac43a2878 100644 --- a/include/ImageManagerDriver.h +++ b/include/ImageManagerDriver.h @@ -71,27 +71,26 @@ private: //Template driver_conf; /** - * Sends a copy request to the MAD + * Sends a copy request to the MAD. * @param oid the image id. * @param drv_msg xml data for the mad operation. */ void cp(int oid, const string& drv_msg) const; /** - * Sends a move request to the MAD: "MV IMAGE_ID SRC_PATH DST_PATH" - * @param oid the image id. - * @param destination is a driver specific location or "-" if not - * initialized - * @param size_mb of the image to be created + * Sends a stat request to the MAD. + * @param oid the id of the stat request + * @param drv_msg xml data for the mad operation. */ - void mv(int oid, const string& source, const string& destination) const; + void stat(int oid, const string& drv_msg) const; /** - * Sends a make filesystem request to the MAD: "MKFS IMAGE_ID PATH SIZE_MB" + * Sends a make filesystem request to the MAD. * @param oid the image id. * @param drv_msg xml data for the mad operation. */ void mkfs(int oid, const string& drv_msg) const; + /** * Sends a delete request to the MAD: "DELETE IMAGE_ID PATH" * @param oid the image id. diff --git a/include/SyncRequest.h b/include/SyncRequest.h new file mode 100644 index 0000000000..21f941d809 --- /dev/null +++ b/include/SyncRequest.h @@ -0,0 +1,101 @@ +/* -------------------------------------------------------------------------- */ +/* 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 SYNC_REQUEST_H_ +#define SYNC_REQUEST_H_ + +#include + +#include "ActionManager.h" + +/** + * Base class to implement synchronous operation in the MadManagers. This class + * cannot be directly instantiated. + */ +class SyncRequest: public ActionListener +{ +public: + SyncRequest(): + result(false), + message(""), + timeout(false), + id(-1), + time_out(0) + { + am.addListener(this); + }; + + virtual ~SyncRequest(){}; + + /** + * The result of the request, true if the operation succeeded + */ + bool result; + + /** + * Error message for negative results + */ + string message; + + /** + * Time out, true if the request ended because of a time out + */ + bool timeout; + + /** + * Identification of this request + */ + int id; + + /** + * Notify client that we have an answer for the request + */ + void notify() + { + am.trigger(ActionListener::ACTION_FINALIZE,0); + }; + + /** + * Wait for the AuthRequest to be completed + */ + void wait() + { + time_out = time(0) + 90;//Requests will expire in 1.5 minutes + + am.loop(0,0); + }; + +protected: + + friend class MadManager; + + /** + * Time in seconds when this request will expire + */ + time_t time_out; + + /** + * The ActionManager that will be notify when the request is ready. + */ + ActionManager am; + + /** + * No actions defined for the request, just FINALIZE when done + */ + void do_action(const string &name, void *args){}; +}; + +#endif /*SYNC_REQUEST_H_*/ diff --git a/src/image/ImageManager.cc b/src/image/ImageManager.cc index 52c3ab9df8..d3a9744cd2 100644 --- a/src/image/ImageManager.cc +++ b/src/image/ImageManager.cc @@ -126,4 +126,3 @@ void ImageManager::do_action(const string &action, void * arg) NebulaLog::log("ImM", Log::ERROR, oss); } } - diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 17f63c3f62..76e39637ed 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -18,6 +18,7 @@ #include "NebulaLog.h" #include "ImagePool.h" #include "SSLTools.h" +#include "SyncRequest.h" /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -395,6 +396,39 @@ int ImageManager::register_image(int iid, const string& ds_data) return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int ImageManager::stat_image(const string& img_tmpl, + const string& ds_tmpl, + string& res) +{ + const ImageManagerDriver* imd = get(); + + string * drv_msg; + SyncRequest sr; + + int rc; + + add_request(&sr); + + drv_msg = format_message(img_tmpl, ds_tmpl); + + imd->stat(sr.id, *drv_msg); + + sr.wait(); + + delete drv_msg; + + res = sr.message; + + if ( sr.result != true ) + { + rc = -1; + } + + return rc; +} /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index 43eae93c5a..a5ad5d9f96 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -38,13 +38,12 @@ void ImageManagerDriver::cp(int oid, /* -------------------------------------------------------------------------- */ -void ImageManagerDriver::mv(int oid, - const string& source, - const string& destination) const +void ImageManagerDriver::stat(int oid, + const string& drv_msg) const { ostringstream os; - os << "MV " << oid << " " << source << " " << destination << endl; + os << "STAT " << oid << " " << drv_msg << endl; write(os); } @@ -79,26 +78,327 @@ void ImageManagerDriver::rm(int oid, const string& drv_msg) const /* MAD Interface */ /* ************************************************************************** */ +static void stat_action(istringstream& is, int id, const string& result) +{ + string size_mb; + string info; + + Nebula& nd = Nebula::instance(); + ImageManager * im = nd.get_imagem(); + + if ( result == "SUCCESS" ) + { + if ( is.good() ) + { + is >> size_mb >> ws; + } + + if ( is.fail() ) + { + im->notify_request(id, false, "Cannot get size from STAT"); + } + + im->notify_request(id, true, size_mb); + } + else + { + getline(is,info); + + im->notify_request(id, false, info); + } +} + +/* -------------------------------------------------------------------------- */ + +static void cp_action(istringstream& is, + ImagePool* ipool, + int id, + const string& result) +{ + string source; + string info; + + Image * image; + + ostringstream oss; + + image = ipool->get(id,true); + + if ( image == 0 ) + { + return; + } + + if ( result == "FAILURE" ) + { + goto error; + } + + if ( is.good() ) + { + is >> source >> ws; + } + + if ( is.fail() ) + { + goto error; + } + + image->set_source(source); + + image->set_state(Image::READY); + + ipool->update(image); + + image->unlock(); + + NebulaLog::log("ImM", Log::INFO, "Image copied and ready to use."); + + return; + +error: + oss << "Error copying image in the repository"; + + getline(is, info); + + if (!info.empty() && (info[0] != '-')) + { + oss << ": " << info; + } + + NebulaLog::log("ImM", Log::ERROR, oss); + + image->set_template_error_message(oss.str()); + image->set_state(Image::ERROR); + + ipool->update(image); + + image->unlock(); + + return; +} + +/* -------------------------------------------------------------------------- */ + +static void mkfs_action(istringstream& is, + ImagePool* ipool, + int id, + const string& result) +{ + string source; + Image * image; + bool is_saving; + + string disk_id; + string vm_id; + string info; + int rc; + + VirtualMachine * vm; + ostringstream oss; + + Nebula& nd = Nebula::instance(); + VirtualMachinePool * vmpool = nd.get_vmpool(); + + image = ipool->get(id, true); + + if ( image == 0 ) + { + return; + } + + if ( result == "FAILURE" ) + { + goto error_img; + } + + if ( is.good() ) + { + is >> source >> ws; + } + + if ( is.fail() ) + { + goto error_img; + } + + is_saving = image->isSaving(); + + image->set_source(source); + + if (is_saving) + { + image->get_template_attribute("SAVED_DISK_ID", disk_id); + image->get_template_attribute("SAVED_VM_ID", vm_id); + } + else + { + image->set_state(Image::READY); + + NebulaLog::log("ImM", Log::INFO, "Image created and ready to use"); + } + + ipool->update(image); + + image->unlock(); + + if ( ! is_saving ) + { + return; + } + + /* ---------------- Set up information for the Saved Image -------------- */ + + vm = vmpool->get(vm_id, true); + + if ( vm == 0 ) + { + goto error_save_get; + } + + rc = vm->save_disk(disk_id, source, id); + + if ( rc == -1 ) + { + goto error_save_state; + } + + vmpool->update(vm); + + vm->unlock(); + + return; + +error_img: + oss << "Error creating datablock"; + goto error; + +error_save_get: + oss << "Image created for SAVE_AS, but the associated VM does not exist."; + goto error_save; + +error_save_state: + vm->unlock(); + oss << "Image created for SAVE_AS, but VM is no longer running"; + +error_save: + image = ipool->get(id, true); + + if ( image == 0 ) + { + NebulaLog::log("ImM", Log::ERROR, oss); + return; + } + +error: + getline(is,info); + + if (!info.empty() && (info[0] != '-')) + { + oss << ": " << info; + } + + NebulaLog::log("ImM", Log::ERROR, oss); + + image->set_template_error_message(oss.str()); + image->set_state(Image::ERROR); + + ipool->update(image); + + image->unlock(); + + return ; +} + +/* -------------------------------------------------------------------------- */ + +static void rm_action(istringstream& is, + ImagePool* ipool, + int id, + const string& result) +{ + int rc; + string tmp_error; + string source; + string info; + Image * image; + + ostringstream oss; + + image = ipool->get(id, true); + + if ( image == 0 ) + { + return; + } + + source = image->get_source(); + + if ( result == "FAILURE" ) + { + goto error; + } + + rc = ipool->drop(image, tmp_error); + + if ( rc < 0 ) + { + goto error_drop; + } + + NebulaLog::log("ImM", Log::INFO, "Image successfully removed."); + + return; + +error_drop: + oss << "Error removing image from DB: " << tmp_error + << ". Remove image source " << source << " to completely delete image."; + + NebulaLog::log("ImM", Log::ERROR, oss); + return; + +error: + oss << "Error removing image from datastore"; + + getline(is,info); + + if (!info.empty() && (info[0] != '-')) + { + oss << ": " << info; + } + + NebulaLog::log("ImM", Log::ERROR, oss); + + image->set_template_error_message(oss.str()); + image->set_state(Image::ERROR); + + ipool->update(image); + + image->unlock(); + + return; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void ImageManagerDriver::protocol( string& message) { istringstream is(message); - ostringstream os; + ostringstream oss; - string action; - string result; + string action; + string result; + string source; + string info; + int id; + + oss << "Message received: " << message; + NebulaLog::log("ImG", Log::DEBUG, oss); - int id; - Image * image; - string source; - unsigned int size_mb; + // --------------------- Parse the driver message -------------------------- - string info; - - os << "Message received: " << message; - NebulaLog::log("ImG", Log::DEBUG, os); - - // Parse the driver message if ( is.good() ) is >> action >> ws; else @@ -117,8 +417,6 @@ void ImageManagerDriver::protocol( { if ( action == "LOG" ) { - string info; - is.clear(); getline(is,info); @@ -131,145 +429,21 @@ void ImageManagerDriver::protocol( else return; - // Parse driver message for CP, MV and MKFS - // SUCESS IMAGE_ID SOURCE SIZE - if ( (result == "SUCCESS") && (action != "RM") ) + if ( action == "STAT" ) { - if ( is.good() ) - { - is >> source >> ws; - } - - if ( is.good() ) - { - is >> size_mb >> ws; - } - - if ( is.fail() ) - { - result = "FAILURE"; - } + stat_action(is, id, result); } - - - // Get the image from the pool - image = ipool->get(id,true); - - if ( image == 0 ) + else if ( action == "CP" ) { - return; - } - - // Driver Actions - if ( action == "CP" ) - { - if ( result == "SUCCESS" ) - { - image->set_source(source); - image->set_size(size_mb); - - image->set_state(Image::READY); - - ipool->update(image); - - NebulaLog::log("ImM", Log::INFO, "Image copied and ready to use."); - } - else - { - goto error_cp; - } + cp_action(is, ipool, id, result); } else if ( action == "MKFS" ) { - if ( result == "SUCCESS" ) - { - bool is_saving = image->isSaving(); - - string disk_id; - string vm_id; - int rc; - - image->set_source(source); - - if (is_saving) - { - image->get_template_attribute("SAVED_DISK_ID",disk_id); - image->get_template_attribute("SAVED_VM_ID", vm_id); - } - else - { - image->set_size(size_mb); - - image->set_state(Image::READY); - - NebulaLog::log("ImM", Log::INFO, - "Image created and ready to use"); - } - - ipool->update(image); - - image->unlock(); - - if (is_saving) - { - Nebula& nd = Nebula::instance(); - - VirtualMachinePool * vmpool = nd.get_vmpool(); - - VirtualMachine * vm; - istringstream iss(vm_id); - - int vm_id_i; - - iss >> vm_id_i; - - vm = vmpool->get(vm_id_i, true); - - if ( vm == 0 ) - { - goto error_save_no_vm; - } - - rc = vm->save_disk(disk_id, source, id); - - if ( rc == -1 ) - { - vm->unlock(); - goto error_save_state_vm; - } - - vmpool->update(vm); - - vm->unlock(); - } - - return; - } - else - { - goto error_mkfs; - } + mkfs_action(is, ipool, id, result); } else if ( action == "RM" ) { - int rc; - string tmp_error; - - rc = ipool->drop(image, tmp_error); - - if ( rc < 0 ) - { - NebulaLog::log("ImM",Log::ERROR,"Image could not be removed from DB"); - } - - if ( result == "SUCCESS" ) - { - NebulaLog::log("ImM",Log::INFO,"Image successfully removed."); - } - else - { - goto error_rm; - } + rm_action(is, ipool, id, result); } else if (action == "LOG") { @@ -277,72 +451,6 @@ void ImageManagerDriver::protocol( NebulaLog::log("ImM", log_type(result[0]), info.c_str()); } - image->unlock(); - - return; - -error_cp: - os.str(""); - os << "Error copying image in the repository"; - goto error_common; - -error_mkfs: - os.str(""); - os << "Error creating datablock"; - goto error_common; - -error_rm: - os.str(""); - os << "Error removing image from repository. Remove file " << image->get_source() - << " to completely delete image."; - - image->unlock(); - - getline(is,info); - - if (!info.empty() && (info[0] != '-')) - { - os << ": " << info; - } - - NebulaLog::log("ImM", Log::ERROR, os); - return; - -error_save_no_vm: - os.str(""); - os << "Image created for SAVE_AS, but the associated VM does not exist."; - - goto error_save_common; - -error_save_state_vm: - os.str(""); - os << "Image created for SAVE_AS, but VM is no longer running"; - - goto error_save_common; - -error_save_common: - image = ipool->get(id, true); - - if (image == 0 ) - { - return; - } - -error_common: - getline(is,info); - - if (!info.empty() && (info[0] != '-')) - { - os << ": " << info; - image->set_template_error_message(os.str()); - } - - NebulaLog::log("ImM", Log::ERROR, os); - - image->set_state(Image::ERROR); - ipool->update(image); - - image->unlock(); return; } From ea898cdf7d5fd7829b3ae61d57b2d142d6f0a9ec Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 13:55:01 +0200 Subject: [PATCH 14/47] feature #1288: Add stat operation to DS drivers. Updated drivers to get image size from the template --- install.sh | 5 +++ src/datastore_mad/one_datastore.rb | 15 +++++-- src/datastore_mad/remotes/dummy/cp | 2 +- src/datastore_mad/remotes/dummy/mkfs | 2 +- src/datastore_mad/remotes/dummy/stat | 19 +++++++++ src/datastore_mad/remotes/fs/cp | 6 +-- src/datastore_mad/remotes/fs/mkfs | 4 +- src/datastore_mad/remotes/fs/stat | 1 + src/datastore_mad/remotes/iscsi/cp | 8 ++-- src/datastore_mad/remotes/iscsi/mkfs | 4 +- src/datastore_mad/remotes/iscsi/stat | 1 + src/datastore_mad/remotes/libfs.sh | 38 +++++++---------- src/datastore_mad/remotes/lvm/cp | 8 ++-- src/datastore_mad/remotes/lvm/mkfs | 6 +-- src/datastore_mad/remotes/lvm/stat | 1 + src/datastore_mad/remotes/stat | 59 +++++++++++++++++++++++++++ src/datastore_mad/remotes/vmware/cp | 6 +-- src/datastore_mad/remotes/vmware/mkfs | 8 +--- src/datastore_mad/remotes/vmware/stat | 1 + 19 files changed, 134 insertions(+), 60 deletions(-) create mode 100755 src/datastore_mad/remotes/dummy/stat create mode 120000 src/datastore_mad/remotes/fs/stat create mode 120000 src/datastore_mad/remotes/iscsi/stat create mode 120000 src/datastore_mad/remotes/lvm/stat create mode 100755 src/datastore_mad/remotes/stat create mode 120000 src/datastore_mad/remotes/vmware/stat diff --git a/install.sh b/install.sh index 504353be95..09351fb2c6 100755 --- a/install.sh +++ b/install.sh @@ -854,23 +854,28 @@ DATASTORE_DRIVER_COMMON_SCRIPTS="src/datastore_mad/remotes/xpath.rb \ DATASTORE_DRIVER_DUMMY_SCRIPTS="src/datastore_mad/remotes/dummy/cp \ src/datastore_mad/remotes/dummy/mkfs \ + src/datastore_mad/remotes/dummy/stat \ src/datastore_mad/remotes/dummy/rm" DATASTORE_DRIVER_FS_SCRIPTS="src/datastore_mad/remotes/fs/cp \ src/datastore_mad/remotes/fs/mkfs \ + src/datastore_mad/remotes/fs/stat \ src/datastore_mad/remotes/fs/rm" DATASTORE_DRIVER_VMWARE_SCRIPTS="src/datastore_mad/remotes/vmware/cp \ src/datastore_mad/remotes/vmware/mkfs \ + src/datastore_mad/remotes/vmware/stat \ src/datastore_mad/remotes/vmware/rm" DATASTORE_DRIVER_ISCSI_SCRIPTS="src/datastore_mad/remotes/iscsi/cp \ src/datastore_mad/remotes/iscsi/mkfs \ + src/datastore_mad/remotes/iscsi/stat \ src/datastore_mad/remotes/iscsi/rm \ src/datastore_mad/remotes/iscsi/iscsi.conf" DATASTORE_DRIVER_LVM_SCRIPTS="src/datastore_mad/remotes/lvm/cp \ src/datastore_mad/remotes/lvm/mkfs \ + src/datastore_mad/remotes/lvm/stat \ src/datastore_mad/remotes/lvm/rm \ src/datastore_mad/remotes/lvm/lvm.conf" diff --git a/src/datastore_mad/one_datastore.rb b/src/datastore_mad/one_datastore.rb index f06270b6b7..c77d937225 100755 --- a/src/datastore_mad/one_datastore.rb +++ b/src/datastore_mad/one_datastore.rb @@ -47,7 +47,8 @@ class DatastoreDriver < OpenNebulaDriver :cp => "CP", :rm => "RM", :mkfs => "MKFS", - :log => "LOG" + :log => "LOG", + :stat => "STAT" } # Register default actions for the protocol @@ -57,7 +58,7 @@ class DatastoreDriver < OpenNebulaDriver :threaded => true, :retries => 0, :local_actions => { - ACTION[:mv] => nil, + ACTION[:stat] => nil, ACTION[:cp] => nil, ACTION[:rm] => nil, ACTION[:mkfs] => nil @@ -76,9 +77,10 @@ class DatastoreDriver < OpenNebulaDriver @types = ds_type end - register_action(ACTION[:cp].to_sym, method("cp")) - register_action(ACTION[:rm].to_sym, method("rm")) + register_action(ACTION[:cp].to_sym, method("cp")) + register_action(ACTION[:rm].to_sym, method("rm")) register_action(ACTION[:mkfs].to_sym, method("mkfs")) + register_action(ACTION[:stat].to_sym, method("stat")) end ############################################################################ @@ -100,6 +102,11 @@ class DatastoreDriver < OpenNebulaDriver do_image_action(id, ds, :mkfs, "#{drv_message} #{id}") end + def stat(id, drv_message) + ds = get_ds_type(drv_message) + do_image_action(id, ds, :stat, "#{drv_message} #{id}") + end + private def is_available?(ds, id, action) diff --git a/src/datastore_mad/remotes/dummy/cp b/src/datastore_mad/remotes/dummy/cp index d915146f42..9cf7239279 100755 --- a/src/datastore_mad/remotes/dummy/cp +++ b/src/datastore_mad/remotes/dummy/cp @@ -16,4 +16,4 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -echo "dummy_path 1024" +echo "dummy_path" diff --git a/src/datastore_mad/remotes/dummy/mkfs b/src/datastore_mad/remotes/dummy/mkfs index d915146f42..9cf7239279 100755 --- a/src/datastore_mad/remotes/dummy/mkfs +++ b/src/datastore_mad/remotes/dummy/mkfs @@ -16,4 +16,4 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -echo "dummy_path 1024" +echo "dummy_path" diff --git a/src/datastore_mad/remotes/dummy/stat b/src/datastore_mad/remotes/dummy/stat new file mode 100755 index 0000000000..390fb72944 --- /dev/null +++ b/src/datastore_mad/remotes/dummy/stat @@ -0,0 +1,19 @@ +#!/bin/sh + +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + +echo "1024" diff --git a/src/datastore_mad/remotes/fs/cp b/src/datastore_mad/remotes/fs/cp index 81a6b07679..fbb80dce23 100755 --- a/src/datastore_mad/remotes/fs/cp +++ b/src/datastore_mad/remotes/fs/cp @@ -84,8 +84,4 @@ http://*) ;; esac -# ---------------- Get the size of the image ------------ - -SIZE=`fs_du $DST` - -echo "$DST $SIZE" +echo "$DST" diff --git a/src/datastore_mad/remotes/fs/mkfs b/src/datastore_mad/remotes/fs/mkfs index d6a3d8d484..381a7a461b 100755 --- a/src/datastore_mad/remotes/fs/mkfs +++ b/src/datastore_mad/remotes/fs/mkfs @@ -68,7 +68,7 @@ DST=`generate_image_path` # ------------ Image to save_as disk, no need to create a FS ------------ if [ "$FSTYPE" = "save_as" ]; then - echo "$DST $SIZE" + echo "$DST" exit 0 fi @@ -81,4 +81,4 @@ exec_and_log "$DD if=/dev/zero of=$DST bs=1 count=1 seek=${SIZE}M" \ exec_and_log "$MKFS_CMD" \ "Unable to create filesystem $FSTYPE in $DST" -echo "$DST $SIZE" +echo "$DST" diff --git a/src/datastore_mad/remotes/fs/stat b/src/datastore_mad/remotes/fs/stat new file mode 120000 index 0000000000..10665a59d6 --- /dev/null +++ b/src/datastore_mad/remotes/fs/stat @@ -0,0 +1 @@ +../stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/iscsi/cp b/src/datastore_mad/remotes/iscsi/cp index 7eb8c4bd83..7399f0acc1 100755 --- a/src/datastore_mad/remotes/iscsi/cp +++ b/src/datastore_mad/remotes/iscsi/cp @@ -54,8 +54,8 @@ done < <($XPATH /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/VG_NAME \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BASE_IQN \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BASE_TID \ - /DS_DRIVER_ACTION_DATA/IMAGE/PATH) - + /DS_DRIVER_ACTION_DATA/IMAGE/PATH \ + /DS_DRIVER_ACTION_DATA/IMAGE/SIZE) BASE_PATH="${XPATH_ELEMENTS[0]}" RESTRICTED_DIRS="${XPATH_ELEMENTS[1]}" @@ -66,10 +66,10 @@ VG_NAME="${XPATH_ELEMENTS[5]:-$VG_NAME}" BASE_IQN="${XPATH_ELEMENTS[6]:-$BASE_IQN}" BASE_TID="${XPATH_ELEMENTS[7]:-$BASE_TID}" SRC="${XPATH_ELEMENTS[8]}" +SIZE="${XPATH_ELEMENTS[9]}" set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS" "$UMASK" -SIZE=`fs_du $SRC` LV_NAME="lv-one-${ID}" IQN="$BASE_IQN:$DST_HOST.$VG_NAME.$LV_NAME" DEV="/dev/$VG_NAME/$LV_NAME" @@ -110,4 +110,4 @@ ssh_exec_and_log "$DST_HOST" "$REGISTER_CMD" "Error registering $DST_HOST:$DEV" exec_and_log "eval $DUMP | $SSH $DST_HOST $SUDO $DD of=$DEV bs=64k" \ "Error dumping $SRC to $DST_HOST:$DEV" -echo "$IQN $SIZE" +echo "$IQN" diff --git a/src/datastore_mad/remotes/iscsi/mkfs b/src/datastore_mad/remotes/iscsi/mkfs index c06d64ce6b..8756d69cf7 100755 --- a/src/datastore_mad/remotes/iscsi/mkfs +++ b/src/datastore_mad/remotes/iscsi/mkfs @@ -90,7 +90,7 @@ EOF # ------------ Image to save_as disk, no need to create a FS ------------ if [ "$FSTYPE" = "save_as" ]; then - echo "$DST $SIZE" + echo "$DST" exit 0 fi @@ -99,4 +99,4 @@ fi ssh_exec_and_log "$DST_HOST" "$REGISTER_CMD" \ "Error registering $DST_HOST:$DEV" -echo "$IQN $SIZE" +echo "$IQN" diff --git a/src/datastore_mad/remotes/iscsi/stat b/src/datastore_mad/remotes/iscsi/stat new file mode 120000 index 0000000000..10665a59d6 --- /dev/null +++ b/src/datastore_mad/remotes/iscsi/stat @@ -0,0 +1 @@ +../stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/libfs.sh b/src/datastore_mad/remotes/libfs.sh index 199ba2244f..50eae059fa 100644 --- a/src/datastore_mad/remotes/libfs.sh +++ b/src/datastore_mad/remotes/libfs.sh @@ -88,14 +88,22 @@ EOF # @param $1 - Path to the image # @return size of the image in Mb #------------------------------------------------------------------------------- -function fs_du { - if [ -d "$1" ]; then - SIZE=`du -sb "$1" | cut -f1` +function fs_size{ + + case $1 in + http://*) + SIZE=`curl --head $1 2>/dev/null | grep Length | cut -d: -f` error=$? - else - SIZE=`stat -c %s "$1"` - error=$? - fi + ;; + *) + if [ -d "$1" ]; then + SIZE=`du -sb "$1" | cut -f1` + error=$? + else + SIZE=`stat -c %s "$1"` + error=$? + fi + esac if [ $error -ne 0 ]; then SIZE=0 @@ -106,22 +114,6 @@ function fs_du { echo "$SIZE" } -#------------------------------------------------------------------------------- -# Computes the size of an image -# @param $1 - Path to the image -# @return size of the image in Mb -#------------------------------------------------------------------------------- -function qemu_size { - DISK="$1" - - SIZE=`$QEMU_IMG info $DISK|grep "^virtual size:"|\ - sed 's/^.*(\([0-9]\+\) bytes.*$/\1/g'` - - SIZE=$((($SIZE+1048575)/1048576)) - - echo "$SIZE" -} - #------------------------------------------------------------------------------- # Checks if a path is safe for copying the image from # @param $1 - Path to the image diff --git a/src/datastore_mad/remotes/lvm/cp b/src/datastore_mad/remotes/lvm/cp index 6c95b0c814..66137931d0 100755 --- a/src/datastore_mad/remotes/lvm/cp +++ b/src/datastore_mad/remotes/lvm/cp @@ -52,7 +52,8 @@ done < <($XPATH /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/UMASK \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/HOST \ /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/VG_NAME \ - /DS_DRIVER_ACTION_DATA/IMAGE/PATH) + /DS_DRIVER_ACTION_DATA/IMAGE/PATH \ + /DS_DRIVER_ACTION_DATA/IMAGE/SIZE) BASE_PATH="${XPATH_ELEMENTS[0]}" RESTRICTED_DIRS="${XPATH_ELEMENTS[1]}" @@ -61,11 +62,10 @@ UMASK="${XPATH_ELEMENTS[3]}" DST_HOST="${XPATH_ELEMENTS[4]:-$HOST}" VG_NAME="${XPATH_ELEMENTS[5]:-$VG_NAME}" SRC="${XPATH_ELEMENTS[6]}" +SIZE="${XPATH_ELEMENTS[7]}" set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS" "$UMASK" -SIZE=`fs_du $SRC` - LV_NAME="lv-one-${ID}" LVM_SOURCE="$DST_HOST:$VG_NAME.$LV_NAME" DEV="/dev/$VG_NAME/$LV_NAME" @@ -101,4 +101,4 @@ ssh_exec_and_log "$DST_HOST" "$REGISTER_CMD" "Error registering $DST_HOST:$DEV" exec_and_log "eval $DUMP | $SSH $DST_HOST $SUDO $DD of=$DEV bs=64k" \ "Error dumping $SRC to $DST_HOST:$DEV" -echo "$LVM_SOURCE $SIZE" +echo "$LVM_SOURCE" diff --git a/src/datastore_mad/remotes/lvm/mkfs b/src/datastore_mad/remotes/lvm/mkfs index 71bb3f9638..5eb699d9ce 100755 --- a/src/datastore_mad/remotes/lvm/mkfs +++ b/src/datastore_mad/remotes/lvm/mkfs @@ -62,7 +62,7 @@ UMASK="${XPATH_ELEMENTS[3]}" DST_HOST="${XPATH_ELEMENTS[4]:-$HOST}" VG_NAME="${XPATH_ELEMENTS[5]:-$VG_NAME}" FSTYPE="${XPATH_ELEMENTS[6]}" -SIZE="${XPATH_ELEMENTS[7]:-0}" +SIZE="${XPATH_ELEMENTS[7]}" set_up_datastore "$BASE_PATH" "$RESTRICTED_DIRS" "$SAFE_DIRS" "$UMASK" @@ -80,7 +80,7 @@ EOF # ------------ Image to save_as disk, no need to create a FS ------------ if [ "$FSTYPE" = "save_as" ]; then - echo "$LVM_SOURCE $SIZE" + echo "$LVM_SOURCE" exit 0 fi @@ -89,4 +89,4 @@ fi ssh_exec_and_log "$DST_HOST" "$REGISTER_CMD" \ "Error registering $DST_HOST:$DEV" -echo "$LVM_SOURCE $SIZE" +echo "$LVM_SOURCE" diff --git a/src/datastore_mad/remotes/lvm/stat b/src/datastore_mad/remotes/lvm/stat new file mode 120000 index 0000000000..10665a59d6 --- /dev/null +++ b/src/datastore_mad/remotes/lvm/stat @@ -0,0 +1 @@ +../stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/stat b/src/datastore_mad/remotes/stat new file mode 100755 index 0000000000..6f2340071d --- /dev/null +++ b/src/datastore_mad/remotes/stat @@ -0,0 +1,59 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + +############################################################################### +# This script is used to copy a VM image (SRC) to the image repository as DST +# Several SRC types are supported +############################################################################### + +# -------- Set up the environment to source common tools & conf ------------ + +if [ -z "${ONE_LOCATION}" ]; then + LIB_LOCATION=/usr/lib/one +else + LIB_LOCATION=$ONE_LOCATION/lib +fi + +. $LIB_LOCATION/sh/scripts_common.sh + +DRIVER_PATH=$(dirname $0) +source ${DRIVER_PATH}/../libfs.sh + +# -------- Get cp and datastore arguments from OpenNebula core ------------ + +DRV_ACTION=$1 +ID=$2 + +XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION" + +unset i XPATH_ELEMENTS + +while IFS= read -r -d '' element; do + XPATH_ELEMENTS[i++]="$element" +done < <($XPATH /DS_DRIVER_ACTION_DATA/IMAGE/PATH) + +SRC="${XPATH_ELEMENTS[0]}" + +SIZE=`fs_size $SRC` + +if [ SIZE = "0"]; then + log_error "Cannot determine size for $SRC" + exit -1 +fi + +echo "SIZE" \ No newline at end of file diff --git a/src/datastore_mad/remotes/vmware/cp b/src/datastore_mad/remotes/vmware/cp index a0e892d153..0bfb247c50 100755 --- a/src/datastore_mad/remotes/vmware/cp +++ b/src/datastore_mad/remotes/vmware/cp @@ -87,8 +87,4 @@ case $SRC in ;; esac -# ---------------- Get the size of the image ------------ - -SIZE=`fs_du $DST` - -echo "$DST $SIZE" +echo "$DST" diff --git a/src/datastore_mad/remotes/vmware/mkfs b/src/datastore_mad/remotes/vmware/mkfs index a4d38bccea..13cd67afdf 100755 --- a/src/datastore_mad/remotes/vmware/mkfs +++ b/src/datastore_mad/remotes/vmware/mkfs @@ -67,7 +67,7 @@ DST=`generate_image_path` # ------------ Image to save_as disk, no need to create a FS ------------ if [ "$FSTYPE" = "save_as" ]; then - echo "$DST $SIZE" + echo "$DST" exit 0 fi @@ -91,8 +91,4 @@ exec_and_log "$QEMU_IMG convert -O $IMAGE_FORMAT $DISK_TMP $DISK" \ exec_and_log "rm -f $DISK_TMP" \ "Unable to remove temporary disk $DISK_TMP" -# ---------------- Get the size of the image ------------ - -SIZE=`qemu_size $DISK` - -echo "$DST $SIZE" +echo "$DST" diff --git a/src/datastore_mad/remotes/vmware/stat b/src/datastore_mad/remotes/vmware/stat new file mode 120000 index 0000000000..10665a59d6 --- /dev/null +++ b/src/datastore_mad/remotes/vmware/stat @@ -0,0 +1 @@ +../stat \ No newline at end of file From 7b934d673ca7ac9c97a6c2e4f511bac7eba4f40b Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 1 Jun 2012 19:18:49 +0200 Subject: [PATCH 15/47] feature #1288: Integrate stat driver function with image allocation process --- include/Image.h | 7 +++ include/ImageManager.h | 3 +- src/datastore_mad/remotes/libfs.sh | 3 +- src/datastore_mad/remotes/stat | 4 +- src/image/Image.cc | 70 ++++++++++++++++-------------- src/image/ImageManagerActions.cc | 56 +++++++++++++++++++++--- src/image/ImageManagerDriver.cc | 2 + src/rm/RequestManagerAllocate.cc | 38 +++++++++++++++- 8 files changed, 138 insertions(+), 45 deletions(-) diff --git a/include/Image.h b/include/Image.h index 3b4027b9bf..4560cfeaf6 100644 --- a/include/Image.h +++ b/include/Image.h @@ -55,6 +55,13 @@ public: } }; + /** + * Return the string representation of an ImageType + * @param ob the type + * @return the string + */ + static ImageType str_to_type(string& str_type); + /** * Type of Disks (used by the VMM_MAD). Values: BLOCK, CDROM or * FILE diff --git a/include/ImageManager.h b/include/ImageManager.h index 66ffec84a7..e5400a82d7 100644 --- a/include/ImageManager.h +++ b/include/ImageManager.h @@ -26,6 +26,7 @@ using namespace std; extern "C" void * image_action_loop(void *arg); class Image; +class Template; class ImageManager : public MadManager, public ActionListener { @@ -135,7 +136,7 @@ public: * occurred describing the error. * @result 0 on success */ - int stat_image(const string& img_tmpl, const string& ds_tmpl, string& res); + int stat_image(Template* img_tmpl, const string& ds_tmpl, string& res); private: /** diff --git a/src/datastore_mad/remotes/libfs.sh b/src/datastore_mad/remotes/libfs.sh index 50eae059fa..370858e417 100644 --- a/src/datastore_mad/remotes/libfs.sh +++ b/src/datastore_mad/remotes/libfs.sh @@ -88,7 +88,7 @@ EOF # @param $1 - Path to the image # @return size of the image in Mb #------------------------------------------------------------------------------- -function fs_size{ +function fs_size { case $1 in http://*) @@ -103,6 +103,7 @@ function fs_size{ SIZE=`stat -c %s "$1"` error=$? fi + ;; esac if [ $error -ne 0 ]; then diff --git a/src/datastore_mad/remotes/stat b/src/datastore_mad/remotes/stat index 6f2340071d..855ecae00b 100755 --- a/src/datastore_mad/remotes/stat +++ b/src/datastore_mad/remotes/stat @@ -51,9 +51,9 @@ SRC="${XPATH_ELEMENTS[0]}" SIZE=`fs_size $SRC` -if [ SIZE = "0"]; then +if [ "$SIZE" = "0" ]; then log_error "Cannot determine size for $SRC" exit -1 fi -echo "SIZE" \ No newline at end of file +echo "$SIZE" \ No newline at end of file diff --git a/src/image/Image.cc b/src/image/Image.cc index 19b6238d31..2cba997fe6 100644 --- a/src/image/Image.cc +++ b/src/image/Image.cc @@ -97,7 +97,9 @@ int Image::insert(SqlDB *db, string& error_str) string dev_prefix; string source_attr; string saved_id; + string size_attr; + istringstream iss; ostringstream oss; // --------------------------------------------------------------------- @@ -141,6 +143,13 @@ int Image::insert(SqlDB *db, string& error_str) obj_template->set(dev_att); } + // ------------ SIZE -------------------- + + erase_template_attribute("SIZE", size_attr); + + iss.str(size_attr); + iss >> size_mb; + // ------------ PATH & SOURCE -------------------- erase_template_attribute("PATH", path); @@ -150,26 +159,13 @@ int Image::insert(SqlDB *db, string& error_str) { if ( source.empty() && path.empty() ) { - string size_attr; - istringstream iss; - - erase_template_attribute("SIZE", size_attr); erase_template_attribute("FSTYPE", fs_type); - // DATABLOCK image needs SIZE and FSTYPE - if (type != DATABLOCK || size_attr.empty() || fs_type.empty()) + // DATABLOCK image needs FSTYPE + if (type != DATABLOCK || fs_type.empty()) { goto error_no_path; } - - iss.str(size_attr); - - iss >> size_mb; - - if (iss.fail() == true) - { - goto error_size_format; - } } else if ( !source.empty() && !path.empty() ) { @@ -178,20 +174,7 @@ int Image::insert(SqlDB *db, string& error_str) } else { - string size_attr; - istringstream iss; - fs_type = "save_as"; - erase_template_attribute("SIZE", size_attr); - - iss.str(size_attr); - - iss >> size_mb; - - if (iss.fail() == true) - { - goto error_size_format; - } } state = LOCKED; //LOCKED till the ImageManager copies it to the Repository @@ -220,10 +203,6 @@ error_no_path: } goto error_common; -error_size_format: - error_str = "Wrong number in SIZE."; - goto error_common; - error_path_and_source: error_str = "Template malformed, PATH and SOURCE are mutually exclusive."; goto error_common; @@ -561,3 +540,30 @@ int Image::set_type(string& _type) /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ + +Image::ImageType Image::str_to_type(string& str_type) +{ + Image::ImageType it = OS; + + if (str_type.empty()) + { + str_type = ImagePool::default_type(); + } + + TO_UPPER(str_type); + + if ( str_type == "OS" ) + { + it = OS; + } + else if ( str_type == "CDROM" ) + { + it = CDROM; + } + else if ( str_type == "DATABLOCK" ) + { + it = DATABLOCK; + } + + return it; +} \ No newline at end of file diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 76e39637ed..a33e04cafc 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -19,6 +19,7 @@ #include "ImagePool.h" #include "SSLTools.h" #include "SyncRequest.h" +#include "Template.h" /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -399,20 +400,61 @@ int ImageManager::register_image(int iid, const string& ds_data) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int ImageManager::stat_image(const string& img_tmpl, - const string& ds_tmpl, - string& res) +int ImageManager::stat_image(Template* img_tmpl, + const string& ds_data, + string& res) { const ImageManagerDriver* imd = get(); - string * drv_msg; + string* drv_msg; + string type_att; + + ostringstream img_data; + SyncRequest sr; - int rc; - + int rc = 0; + + img_tmpl->get("TYPE", type_att); + + switch (Image::str_to_type(type_att)) + { + case Image::OS: + case Image::CDROM: + img_tmpl->get("SOURCE", res); + + if (!res.empty()) + { + res = "0"; + return 0; + } + + img_tmpl->get("PATH", res); + + if (res.empty()) + { + res = "Either PATH or SOURCE are required for " + type_att; + return -1; + } + + img_data << "" << res << ""; + break; + + case Image::DATABLOCK: + img_tmpl->get("SIZE", res); + + if (res.empty()) + { + res = "SIZE attribute is mandatory for DATABLOCK."; + return -1; + } + + return 0; + } + add_request(&sr); - drv_msg = format_message(img_tmpl, ds_tmpl); + drv_msg = format_message(img_data.str(), ds_data); imd->stat(sr.id, *drv_msg); diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index a5ad5d9f96..7a7176aff4 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -341,6 +341,8 @@ static void rm_action(istringstream& is, rc = ipool->drop(image, tmp_error); + image->unlock(); + if ( rc < 0 ) { goto error_drop; diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 2cee0997d2..33441bbe3e 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -296,8 +296,14 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, RequestAttributes& att) { string error_str; + string size_str; + + int size_mb; + istringstream iss; + string ds_name; string ds_data; + int rc, id; PoolObjectAuth ds_perms; @@ -309,6 +315,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, DatastorePool * dspool = nd.get_dspool(); ImagePool * ipool = static_cast(pool); + ImageManager * imagem = nd.get_imagem(); ImageTemplate * tmpl = new ImageTemplate; @@ -359,6 +366,34 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, ds->unlock(); + // --------------- Get the SIZE for the Image, (DS driver) ----------------- + + rc = imagem->stat_image(tmpl, ds_data, size_str); + + if ( rc == -1 ) + { + failure_response(INTERNAL, + request_error("Cannot determine Image SIZE", size_str), + att); + delete tmpl; + return; + } + + iss.str(size_str); + iss >> size_mb; + + if ( iss.fail() ) + { + failure_response(INTERNAL, + request_error("Cannot parse SIZE", size_str), + att); + delete tmpl; + return; + } + + tmpl->erase("SIZE"); + tmpl->add("SIZE", size_str); + // ------------- Set authorization request for non-oneadmin's -------------- if ( att.uid != 0 ) @@ -368,7 +403,6 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, // ------------------ Check permissions and ACLs ---------------------- tmpl->add("DATASTORE", ds_name); - tmpl->add("SIZE", "0"); tmpl->to_xml(tmpl_str); @@ -413,7 +447,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, Template img_usage; img_usage.add("DATASTORE", ds_name); - img_usage.add("SIZE", "0"); + img_usage.add("SIZE", size_str); quota_rollback(&img_usage, att); From ce3609c28c0b4be06f2666f075971039b9717f76 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sat, 2 Jun 2012 02:58:46 +0200 Subject: [PATCH 16/47] feature #1288: Better syntax to define DATASTORE quotas --- include/Attribute.h | 3 + include/Quota.h | 14 +- include/{QuotaImage.h => QuotaDatastore.h} | 35 +++-- include/Template.h | 17 ++- include/User.h | 15 +- src/mad/MadManager.cc | 1 - src/rm/Request.cc | 4 +- src/um/Quota.cc | 17 ++- src/um/{QuotaImage.cc => QuotaDatastore.cc} | 157 +++++++++++++------- src/um/SConstruct | 2 +- src/um/User.cc | 8 +- 11 files changed, 186 insertions(+), 87 deletions(-) rename include/{QuotaImage.h => QuotaDatastore.h} (76%) rename src/um/{QuotaImage.cc => QuotaDatastore.cc} (57%) diff --git a/include/Attribute.h b/include/Attribute.h index 23a6f3314a..abf0ca3777 100644 --- a/include/Attribute.h +++ b/include/Attribute.h @@ -239,7 +239,10 @@ public: }; /** + * Returns the string value + * @param name of the attribute * + * @return the value of the attribute if found, empty otherwise */ string vector_value(const char *name) const; diff --git a/include/Quota.h b/include/Quota.h index 888525e8b8..c6a8682427 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -60,7 +60,7 @@ public: protected: - Quota(const char * quota_name): Template(true, '=', quota_name) {}; + Quota(const char * quota_name): Template(false, '=', quota_name) {}; virtual ~Quota(){}; @@ -86,6 +86,18 @@ protected: return 0; } + /** + * Gets a quota for a given resource. The resource is specified as a + * Template attribute. Derived class should return a quota that matches the + * resource. + * @param base_attribute describing the quota with the new limits + * @return pointer to the quota on success or 0 otherwise + */ + virtual Attribute * get_quota(const Attribute* base_attribute) + { + return 0; + }; + /** * Adds a given value to the current quota (single) * @param attr the quota with a numeric value diff --git a/include/QuotaImage.h b/include/QuotaDatastore.h similarity index 76% rename from include/QuotaImage.h rename to include/QuotaDatastore.h index 91be5c4963..3c21b3519a 100644 --- a/include/QuotaImage.h +++ b/include/QuotaDatastore.h @@ -14,14 +14,15 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -#ifndef QUOTA_IMAGE_H_ -#define QUOTA_IMAGE_H_ +#ifndef QUOTA_DATASTORE_H_ +#define QUOTA_DATASTORE_H_ #include "Quota.h" /** - * Image Quotas, defined as: - * = [ + * DataStore Quotas, defined as: + * DATASTORE = [ + * NAME = * IMAGES = * SIZE = * IMAGES_USED = Current number of images in the datastore @@ -31,13 +32,13 @@ * 0 = unlimited, default if missing */ -class QuotaImage : public Quota +class QuotaDatastore : public Quota { public: - QuotaImage():Quota("IMAGE_QUOTA"){}; + QuotaDatastore():Quota("DATASTORE_QUOTA"){}; - ~QuotaImage(){}; + ~QuotaDatastore(){}; /** * Check if the resource allocation will exceed the quota limits. If not @@ -73,15 +74,31 @@ protected: Attribute * new_quota(Attribute * va); private: + /** * Return the limits for image and size stored in the a given quota. * @param va_ptr the attribute that stores the quota + * @param ds the name of the DATASTORE quota * @param images the limit for the number of images * @param size the limit for the total storage size * * @return -1 if the limits are wrong 0 otherwise */ - int get_limits(Attribute * va_ptr, string& images, string& size); + int get_limits(Attribute* va_ptr, string& ds, string& images, string& size); + + /** + * Return the attribute with the datastore quotas + * @param ds_name name of the datastore + * @return pointer to the datastore quota or 0 if not found + */ + VectorAttribute * get_datastore_quota(const string& ds_name); + + /** + * Return the attribute with the datastore quotas + * @param resource attribute describing a datastore quota + * @return pointer to the datastore quota or 0 if not found + */ + Attribute * get_quota(Attribute * resource); }; -#endif /*QUOTA_IMAGE_H_*/ \ No newline at end of file +#endif /*QUOTA_DATASTORE_H_*/ \ No newline at end of file diff --git a/include/Template.h b/include/Template.h index 671945c9ec..3759b08859 100644 --- a/include/Template.h +++ b/include/Template.h @@ -141,8 +141,21 @@ public: */ void add(const string& name, const string& value) { - SingleAttribute * at = new SingleAttribute(name, value); - set(at); + set(new SingleAttribute(name, value)); + } + + /** + * Adds a new single attribute to the template. + * @param name of the attribute + * @param value of the attribute + */ + void add(const string& name, int value) + { + ostringstream oss; + + oss << value; + + set(new SingleAttribute(name, oss.str())); } /** diff --git a/include/User.h b/include/User.h index cd88f51a93..391c0f0522 100644 --- a/include/User.h +++ b/include/User.h @@ -19,7 +19,7 @@ #include "PoolSQL.h" #include "UserTemplate.h" -#include "QuotaImage.h" +#include "QuotaDatastore.h" using namespace std; @@ -169,23 +169,24 @@ public: } /** - * Check Image quotas, it updates usage counters if quotas are not exceeded + * Check Datastore quotas, it updates usage counters if quotas are not + * exceeded. * @param tmpl template for the image * @param reason string describing the error * @return true if image can be allocated, false otherwise */ - bool image_quota_check(Template * tmpl, string& reason) + bool datastore_quota_check(Template * tmpl, string& reason) { - return image_quota.check_add(tmpl, reason); + return datastore_quota.check_add(tmpl, reason); } /** * Delete usage from quota counters. * @param tmpl template for the image, with usage */ - void image_quota_del(Template * tmpl) + void datastore_quota_del(Template * tmpl) { - return image_quota.del(tmpl); + return datastore_quota.del(tmpl); } private: // ------------------------------------------------------------------------- @@ -216,7 +217,7 @@ private: /** * Usage Counters and Quotas */ - QuotaImage image_quota; + QuotaDatastore datastore_quota; // ************************************************************************* // Authentication session (Private) diff --git a/src/mad/MadManager.cc b/src/mad/MadManager.cc index 099ca18131..287b61c0ba 100644 --- a/src/mad/MadManager.cc +++ b/src/mad/MadManager.cc @@ -380,7 +380,6 @@ void MadManager::check_time_outs_action() void MadManager::add_request(SyncRequest *ar) { static int request_id = 0; - int id; lock(); diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 858fbdccf8..76adb6a107 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -129,7 +129,7 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) switch (auth_object) { case PoolObjectSQL::IMAGE: - rc = user->image_quota_check(tmpl, error_str); + rc = user->datastore_quota_check(tmpl, error_str); break; case PoolObjectSQL::VM: @@ -177,7 +177,7 @@ void Request::quota_rollback(Template * tmpl, RequestAttributes& att) switch (auth_object) { case PoolObjectSQL::IMAGE: - user->image_quota_del(tmpl); + user->datastore_quota_del(tmpl); break; case PoolObjectSQL::VM: diff --git a/src/um/Quota.cc b/src/um/Quota.cc index f8c3a29306..af6dca32a9 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -37,29 +37,32 @@ int Quota::set(const string& quota_str, string& error) for ( it = tmp.attributes.begin(); it != tmp.attributes.end(); it++) { - actual = attributes.equal_range(it->first); + quota = get_quota(it->second); - if (actual.first == actual.second ) //Quota not set yet. + if ( quota == 0 ) //Quota not set yet. { - if ((quota = new_quota(it->second)) == 0) + Attribute * nq; + + if ((nq = new_quota(it->second)) == 0) { goto error_limits; } - attributes.insert(make_pair(quota->name(),quota)); + attributes.insert(make_pair(nq->name(),nq)); } else { - if (update_limits(actual.first->second, it->second)) + if (update_limits(quota, it->second)) { goto error_limits; - } + } } } error_limits: ostringstream oss; - oss << "Negative limits or bad format in quota " << it->first; + oss << "Negative limits or bad format in quota " << it->first + << " = " << it->second->marshall(); error = oss.str(); return -1; diff --git a/src/um/QuotaImage.cc b/src/um/QuotaDatastore.cc similarity index 57% rename from src/um/QuotaImage.cc rename to src/um/QuotaDatastore.cc index d0433acb0a..77973ac7c9 100644 --- a/src/um/QuotaImage.cc +++ b/src/um/QuotaDatastore.cc @@ -14,12 +14,62 @@ /* limitations under the License. */ /* -------------------------------------------------------------------------- */ -#include "QuotaImage.h" +#include "QuotaDatastore.h" -bool QuotaImage::check_add(Template * tmpl, string& error) +VectorAttribute * QuotaDatastore::get_datastore_quota(const string& ds_name) { - vector vector_ds_limit; - VectorAttribute * ds_limit; + vector vquota; + VectorAttribute * ds_quota = 0; + + int num = get("DATASTORE", vquota); + + for (int i = 0; i< num ; i++) + { + ds_quota = dynamic_cast(vquota[i]); + + if (ds_quota == 0) + { + continue; + } + + if ( ds_quota->vector_value("NAME") == ds_name ) + { + return ds_quota; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +Attribute * QuotaDatastore::get_quota(Attribute * resource) +{ + VectorAttribute * vresource = dynamic_cast(resource); + string ds_name; + + if (vresource == 0) + { + return 0; + } + + ds_name = vresource->vector_value("NAME"); + + if (ds_name.empty()) + { + return 0; + } + + return get_datastore_quota(ds_name); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool QuotaDatastore::check_add(Template * tmpl, string& error) +{ + VectorAttribute * ds_quota; int img_limit = 0; int size_limit = 0; @@ -48,48 +98,46 @@ bool QuotaImage::check_add(Template * tmpl, string& error) return false; } - // ------ There is no quotas for this datastore, create a new one ------ + // ------ There are no quotas for this datastore, create a new one ------ - if ( get(ds_name,vector_ds_limit) == 0 ) + ds_quota = get_datastore_quota(ds_name); + + if ( ds_quota == 0 ) { map ds_quota; VectorAttribute * attr; + ostringstream size_str; + size_str << size; + + ds_quota.insert(make_pair("NAME", ds_name)); ds_quota.insert(make_pair("IMAGES", "0")); ds_quota.insert(make_pair("SIZE", "0")); ds_quota.insert(make_pair("IMAGES_USED", "1")); - ds_quota.insert(make_pair("SIZE_USED", "0")); + ds_quota.insert(make_pair("SIZE_USED", size_str.str())); - attr = new VectorAttribute(ds_name, ds_quota); + attr = new VectorAttribute("DATASTORE", ds_quota); - attributes.insert(make_pair(ds_name, attr)); + attributes.insert(make_pair("DATASTORE", attr)); return true; } // ------ Check usage limits ------ - ds_limit = dynamic_cast(vector_ds_limit[0]); + ds_quota->vector_value("IMAGES", img_limit); + ds_quota->vector_value("SIZE", size_limit); - if (ds_limit == 0) - { - error = "Internal error checking quota limits"; - return false; - } - - ds_limit->vector_value("IMAGES", img_limit); - ds_limit->vector_value("SIZE", size_limit); - - ds_limit->vector_value("IMAGES_USED", img_used); - ds_limit->vector_value("SIZE_USED", size_used); + ds_quota->vector_value("IMAGES_USED", img_used); + ds_quota->vector_value("SIZE_USED", size_used); img_ok = (img_limit == 0) || ((img_used + 1) <= img_limit ); size_ok = (size_limit== 0) || ((size_used + size) <= size_limit); if ( img_ok && size_ok ) { - add_to_quota(ds_limit, "IMAGES_USED", +1); - add_to_quota(ds_limit, "SIZE_USED", +size); + add_to_quota(ds_quota, "IMAGES_USED", +1); + add_to_quota(ds_quota, "SIZE_USED", +size); } else if (!img_ok) { @@ -116,10 +164,9 @@ bool QuotaImage::check_add(Template * tmpl, string& error) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void QuotaImage::del(Template * tmpl) +void QuotaDatastore::del(Template * tmpl) { - vector vector_ds_limit; - VectorAttribute * ds_limit; + VectorAttribute * ds_quota; string ds_name; int size; @@ -136,33 +183,30 @@ void QuotaImage::del(Template * tmpl) return; } - if ( get(ds_name,vector_ds_limit) == 0 ) + ds_quota = get_datastore_quota(ds_name); + + if ( ds_quota == 0 ) { return; } - ds_limit = dynamic_cast(vector_ds_limit[0]); - - if (ds_limit == 0) - { - return; - } - - add_to_quota(ds_limit, "IMAGES_USED", -1); - add_to_quota(ds_limit, "SIZE_USED", -size); + add_to_quota(ds_quota, "IMAGES_USED", -1); + add_to_quota(ds_quota, "SIZE_USED", -size); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int QuotaImage::update_limits(Attribute * quota, Attribute * va) +int QuotaDatastore::update_limits(Attribute * quota, Attribute * va) { string images_limit; string size_limit; + string ds; VectorAttribute * vquota = dynamic_cast(quota); + int rc = get_limits(va, ds, images_limit, size_limit); - if ( vquota == 0 || get_limits(va, images_limit, size_limit) != 0 ) + if ( vquota == 0 || rc != 0 ) { return -1; } @@ -176,33 +220,39 @@ int QuotaImage::update_limits(Attribute * quota, Attribute * va) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -Attribute * QuotaImage::new_quota(Attribute * va) +Attribute * QuotaDatastore::new_quota(Attribute * va) { string images_limit = "0"; string size_limit = "0"; + string ds_name; - if ( va != 0 ) + int rc = get_limits(va, ds_name, images_limit, size_limit); + + if ( rc != 0 || ds_name.empty()) { - if (get_limits(va, images_limit, size_limit) != 0 ) - { - return 0; - } - } + return 0; + } map limits; + + limits.insert(make_pair("NAME", ds_name)); - limits.insert(make_pair("IMAGES",images_limit)); - limits.insert(make_pair("SIZE",size_limit)); - limits.insert(make_pair("IMAGES_USED","0")); - limits.insert(make_pair("SIZE_USED","0")); + limits.insert(make_pair("IMAGES", images_limit)); + limits.insert(make_pair("SIZE", size_limit)); - return new VectorAttribute(va->name(),limits); + limits.insert(make_pair("IMAGES_USED", "0")); + limits.insert(make_pair("SIZE_USED", "0")); + + return new VectorAttribute("DATASTORE",limits); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int QuotaImage::get_limits(Attribute * va_ptr, string& images, string& size) +int QuotaDatastore::get_limits(Attribute * va_ptr, + string& ds_name, + string& images, + string& size) { int images_limit = 0; int size_limit = 0; @@ -214,8 +264,9 @@ int QuotaImage::get_limits(Attribute * va_ptr, string& images, string& size) return -1; } - images = va->vector_value("IMAGES", images_limit); - size = va->vector_value("SIZE", size_limit); + images = va->vector_value("IMAGES", images_limit); + size = va->vector_value("SIZE", size_limit); + ds_name = va->vector_value("NAME"); if ( images_limit < 0 || size_limit < 0 ) { diff --git a/src/um/SConstruct b/src/um/SConstruct index fe833d659e..ebee5078b7 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -25,7 +25,7 @@ source_files=[ 'User.cc', 'UserPool.cc', 'Quota.cc', - 'QuotaImage.cc' + 'QuotaDatastore.cc' ] # Build library diff --git a/src/um/User.cc b/src/um/User.cc index 2e0099a20c..7c93094678 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -138,7 +138,7 @@ string& User::to_xml(string& xml) const { ostringstream oss; string template_xml; - string image_quota_xml; + string ds_quota_xml; int enabled_int = enabled?1:0; @@ -152,7 +152,7 @@ string& User::to_xml(string& xml) const "" << auth_driver <<""<< "" << enabled_int <<"" << obj_template->to_xml(template_xml) << - image_quota.to_xml(image_quota_xml) << + datastore_quota.to_xml(ds_quota_xml) << ""; xml = oss.str(); @@ -199,11 +199,11 @@ int User::from_xml(const string& xml) content.clear(); // Get associated quota for the user - ObjectXML::get_nodes("/USER/IMAGE_QUOTA", content); + ObjectXML::get_nodes("/USER/DATASTORE_QUOTA", content); if (!content.empty()) { - rc += image_quota.from_xml_node(content[0]); + rc += datastore_quota.from_xml_node(content[0]); } ObjectXML::free_nodes(content); From e73c78869b03af5a1565ca076a9eee4c519524f9 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 4 Jun 2012 00:45:56 +0200 Subject: [PATCH 17/47] feature #1288: Better interface for Quotas --- include/Quota.h | 51 +++++-------- include/QuotaDatastore.h | 32 +++----- include/User.h | 2 +- src/rm/RequestManagerAllocate.cc | 4 +- src/um/Quota.cc | 114 ++++++++++++++++------------ src/um/QuotaDatastore.cc | 124 ++++++++----------------------- 6 files changed, 130 insertions(+), 197 deletions(-) diff --git a/include/Quota.h b/include/Quota.h index c6a8682427..846a3c47ff 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -35,7 +35,7 @@ public: * @param error describe the error in case of error * @return 0 on success -1 otherwise */ - int set(const string& quota_str, string& error); + int set(vector * quotas, string& error); /** * Check if the resource allocation will exceed the quota limits. If not @@ -44,19 +44,14 @@ public: * @param error string * @return true if the operation can be performed */ - virtual bool check_add(Template* tmpl, string& error) - { - return false; - } + virtual bool check(Template* tmpl, string& error) = 0; /** * Decrement usage counters when deallocating image * @param tmpl template for the resource */ - virtual void del(Template* tmpl) - { - return; - } + virtual void del(Template* tmpl) = 0; + protected: @@ -70,10 +65,8 @@ protected: * @param va attribute with the new limits * @return 0 on success or -1 if wrong limits */ - virtual int update_limits(Attribute* quota, const Attribute* va) - { - return -1; - }; + virtual int update_limits(VectorAttribute* quota, + const VectorAttribute* va) = 0; /** * Creates an empty quota based on the given attribute. The attribute va @@ -81,29 +74,21 @@ protected: * @param va limits for the new quota if 0 limits will be 0 * @return a new attribute representing the quota */ - virtual Attribute * new_quota(Attribute* va) - { - return 0; - } - - /** - * Gets a quota for a given resource. The resource is specified as a - * Template attribute. Derived class should return a quota that matches the - * resource. - * @param base_attribute describing the quota with the new limits - * @return pointer to the quota on success or 0 otherwise - */ - virtual Attribute * get_quota(const Attribute* base_attribute) - { - return 0; - }; + virtual VectorAttribute * new_quota(VectorAttribute* va) = 0; /** - * Adds a given value to the current quota (single) - * @param attr the quota with a numeric value - * @param num value to add to the current quota + * Gets a quota identified by its ID. + * @param id of the quota + * @return a pointer to the quota or 0 if not found */ - void add_to_quota(SingleAttribute * attr, float num); + virtual VectorAttribute * get_quota(const string& id); + + /** + * Adds a new quota, it also updates an internal index for fast accessing + * the quotas + * @param quota is the new quota, allocated in the HEAP + */ + virtual void add(VectorAttribute * quota); /** * Adds a given value to the current quota (vector) diff --git a/include/QuotaDatastore.h b/include/QuotaDatastore.h index 3c21b3519a..0876a857d5 100644 --- a/include/QuotaDatastore.h +++ b/include/QuotaDatastore.h @@ -22,7 +22,7 @@ /** * DataStore Quotas, defined as: * DATASTORE = [ - * NAME = + * ID = * IMAGES = * SIZE = * IMAGES_USED = Current number of images in the datastore @@ -47,7 +47,7 @@ public: * @param error string * @return true if the operation can be performed */ - bool check_add(Template* tmpl, string& error); + bool check(Template* tmpl, string& error); /** * Decrement usage counters when deallocating image @@ -63,7 +63,8 @@ protected: * @param va attribute with the new limits * @return 0 on success or -1 if wrong limits */ - int update_limits(Attribute * quota, Attribute * va); + int update_limits(VectorAttribute* quota, + const VectorAttribute* va); /** * Creates an empty quota based on the given attribute. The attribute va @@ -71,34 +72,23 @@ protected: * @param va limits for the new quota if 0 limits will be 0 * @return a new attribute representing the quota */ - Attribute * new_quota(Attribute * va); + VectorAttribute * new_quota(VectorAttribute * va); private: /** * Return the limits for image and size stored in the a given quota. * @param va_ptr the attribute that stores the quota - * @param ds the name of the DATASTORE quota - * @param images the limit for the number of images + * @param ds the id of the DATASTORE quota + * @param imgs the limit for the number of images * @param size the limit for the total storage size * * @return -1 if the limits are wrong 0 otherwise */ - int get_limits(Attribute* va_ptr, string& ds, string& images, string& size); - - /** - * Return the attribute with the datastore quotas - * @param ds_name name of the datastore - * @return pointer to the datastore quota or 0 if not found - */ - VectorAttribute * get_datastore_quota(const string& ds_name); - - /** - * Return the attribute with the datastore quotas - * @param resource attribute describing a datastore quota - * @return pointer to the datastore quota or 0 if not found - */ - Attribute * get_quota(Attribute * resource); + int get_limits(const VectorAttribute* va, + string& ds, + string& imgs, + string& size); }; #endif /*QUOTA_DATASTORE_H_*/ \ No newline at end of file diff --git a/include/User.h b/include/User.h index 391c0f0522..ee369b30ce 100644 --- a/include/User.h +++ b/include/User.h @@ -177,7 +177,7 @@ public: */ bool datastore_quota_check(Template * tmpl, string& reason) { - return datastore_quota.check_add(tmpl, reason); + return datastore_quota.check(tmpl, reason); } /** diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 33441bbe3e..c06b82b114 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -402,7 +402,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, string tmpl_str; // ------------------ Check permissions and ACLs ---------------------- - tmpl->add("DATASTORE", ds_name); + tmpl->add("DATASTORE", ds_id); tmpl->to_xml(tmpl_str); @@ -446,7 +446,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, { Template img_usage; - img_usage.add("DATASTORE", ds_name); + img_usage.add("DATASTORE", ds_id); img_usage.add("SIZE", size_str); quota_rollback(&img_usage, att); diff --git a/src/um/Quota.cc b/src/um/Quota.cc index af6dca32a9..124bc0639f 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -19,73 +19,44 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int Quota::set(const string& quota_str, string& error) +VectorAttribute * Quota::get_quota(const string& id) { - Quota tmp("GENERIC_QUOTA"); + map::iterator it; + VectorAttribute * q; - if ( tmp.parse_str_or_xml(quota_str, error) != 0 ) + if ( id.empty() ) { - return -1; + return 0; } - multimap::iterator it; - - pair::iterator, - multimap::iterator> actual; - - Attribute * quota; - - for ( it = tmp.attributes.begin(); it != tmp.attributes.end(); it++) + for ( it = attributes.begin(); it != attributes.end(); it++) { - quota = get_quota(it->second); + q = static_cast(it->second); - if ( quota == 0 ) //Quota not set yet. + if (q->vector_value("ID") == id) { - Attribute * nq; - - if ((nq = new_quota(it->second)) == 0) - { - goto error_limits; - } - - attributes.insert(make_pair(nq->name(),nq)); - } - else - { - if (update_limits(quota, it->second)) - { - goto error_limits; - } + return q; } } -error_limits: - ostringstream oss; - oss << "Negative limits or bad format in quota " << it->first - << " = " << it->second->marshall(); - - error = oss.str(); - return -1; + return 0; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void Quota::add_to_quota(SingleAttribute * attr, float num) +void Quota::add(VectorAttribute * nq) { - istringstream iss; - ostringstream oss; - float total; + string id; - iss.str(attr->value()); + id = nq->vector_value("ID"); - iss >> total; + if ( id.empty() ) + { + return; + } - total += num; - - oss << total; - - attr->replace(oss.str()); + attributes.insert(make_pair(nq->name(), nq)); } /* -------------------------------------------------------------------------- */ @@ -107,3 +78,52 @@ void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num) attr->replace(va_name, oss.str()); } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int Quota::set(vector * new_quotas, string& error) +{ + vector::iterator it; + + VectorAttribute * tq; + string id; + + for ( it = new_quotas->begin(); it != new_quotas->end(); it++) + { + id = (*it)->vector_value("ID"); + + tq = get_quota(id); + + if ( tq == 0 ) + { + VectorAttribute * nq; + + if ((nq = new_quota(*it)) == 0) + { + goto error_limits; + } + + add(nq); + } + else + { + if (update_limits(tq, *it)) + { + goto error_limits; + } + } + } + + return 0; + +error_limits: + ostringstream oss; + oss << "Negative limits or bad format in quota " << (*it)->marshall(); + + error = oss.str(); + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaDatastore.cc b/src/um/QuotaDatastore.cc index 77973ac7c9..64c39895ae 100644 --- a/src/um/QuotaDatastore.cc +++ b/src/um/QuotaDatastore.cc @@ -16,58 +16,10 @@ #include "QuotaDatastore.h" -VectorAttribute * QuotaDatastore::get_datastore_quota(const string& ds_name) -{ - vector vquota; - VectorAttribute * ds_quota = 0; - - int num = get("DATASTORE", vquota); - - for (int i = 0; i< num ; i++) - { - ds_quota = dynamic_cast(vquota[i]); - - if (ds_quota == 0) - { - continue; - } - - if ( ds_quota->vector_value("NAME") == ds_name ) - { - return ds_quota; - } - } - - return 0; -} - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -Attribute * QuotaDatastore::get_quota(Attribute * resource) -{ - VectorAttribute * vresource = dynamic_cast(resource); - string ds_name; - - if (vresource == 0) - { - return 0; - } - - ds_name = vresource->vector_value("NAME"); - - if (ds_name.empty()) - { - return 0; - } - - return get_datastore_quota(ds_name); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -bool QuotaDatastore::check_add(Template * tmpl, string& error) +bool QuotaDatastore::check(Template * tmpl, string& error) { VectorAttribute * ds_quota; @@ -79,14 +31,14 @@ bool QuotaDatastore::check_add(Template * tmpl, string& error) bool img_ok; bool size_ok; - string ds_name; + string ds_id; int size; // --------------------- Get data from the Template -------------------- - tmpl->get("DATASTORE", ds_name); + tmpl->get("DATASTORE", ds_id); - if ( ds_name.empty() ) + if ( ds_id.empty() ) { error = "Datastore not defined for image"; return false; @@ -100,25 +52,22 @@ bool QuotaDatastore::check_add(Template * tmpl, string& error) // ------ There are no quotas for this datastore, create a new one ------ - ds_quota = get_datastore_quota(ds_name); + ds_quota = get_quota(ds_id); if ( ds_quota == 0 ) { map ds_quota; - VectorAttribute * attr; ostringstream size_str; size_str << size; - ds_quota.insert(make_pair("NAME", ds_name)); + ds_quota.insert(make_pair("ID", ds_id)); ds_quota.insert(make_pair("IMAGES", "0")); ds_quota.insert(make_pair("SIZE", "0")); ds_quota.insert(make_pair("IMAGES_USED", "1")); ds_quota.insert(make_pair("SIZE_USED", size_str.str())); - attr = new VectorAttribute("DATASTORE", ds_quota); - - attributes.insert(make_pair("DATASTORE", attr)); + add(new VectorAttribute("DATASTORE", ds_quota)); return true; } @@ -144,7 +93,7 @@ bool QuotaDatastore::check_add(Template * tmpl, string& error) ostringstream oss; oss << "Maximum number of images limit (" << img_limit << ")" - << " reached for datastore " << ds_name; + << " reached for datastore " << ds_id; error = oss.str(); } @@ -153,7 +102,7 @@ bool QuotaDatastore::check_add(Template * tmpl, string& error) ostringstream oss; oss << "Maximum storage capacity limit (" << size_limit << ")" - << " reached for datastore " << ds_name; + << " reached for datastore " << ds_id; error = oss.str(); } @@ -168,12 +117,12 @@ void QuotaDatastore::del(Template * tmpl) { VectorAttribute * ds_quota; - string ds_name; + string ds_id; int size; - tmpl->get("DATASTORE", ds_name); + tmpl->get("DATASTORE", ds_id); - if ( ds_name.empty() ) + if ( ds_id.empty() ) { return; } @@ -183,7 +132,7 @@ void QuotaDatastore::del(Template * tmpl) return; } - ds_quota = get_datastore_quota(ds_name); + ds_quota = get_quota(ds_id); if ( ds_quota == 0 ) { @@ -197,22 +146,20 @@ void QuotaDatastore::del(Template * tmpl) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int QuotaDatastore::update_limits(Attribute * quota, Attribute * va) +int QuotaDatastore::update_limits(VectorAttribute * quota, + const VectorAttribute * va) { string images_limit; string size_limit; - string ds; + string ds_id; - VectorAttribute * vquota = dynamic_cast(quota); - int rc = get_limits(va, ds, images_limit, size_limit); - - if ( vquota == 0 || rc != 0 ) + if ( get_limits(va, ds_id, images_limit, size_limit) != 0 ) { return -1; } - vquota->replace("IMAGES", images_limit); - vquota->replace("SIZE", size_limit); + quota->replace("IMAGES", images_limit); + quota->replace("SIZE", size_limit); return 0; } @@ -220,22 +167,20 @@ int QuotaDatastore::update_limits(Attribute * quota, Attribute * va) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -Attribute * QuotaDatastore::new_quota(Attribute * va) +VectorAttribute * QuotaDatastore::new_quota(VectorAttribute * va) { string images_limit = "0"; string size_limit = "0"; - string ds_name; + string ds_id; - int rc = get_limits(va, ds_name, images_limit, size_limit); - - if ( rc != 0 || ds_name.empty()) + if ( get_limits(va, ds_id, images_limit, size_limit) != 0 ) { return 0; } map limits; - limits.insert(make_pair("NAME", ds_name)); + limits.insert(make_pair("ID", ds_id)); limits.insert(make_pair("IMAGES", images_limit)); limits.insert(make_pair("SIZE", size_limit)); @@ -249,26 +194,19 @@ Attribute * QuotaDatastore::new_quota(Attribute * va) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int QuotaDatastore::get_limits(Attribute * va_ptr, - string& ds_name, - string& images, - string& size) +int QuotaDatastore::get_limits(const VectorAttribute * va, + string& ds_id, + string& images, + string& size) { int images_limit = 0; int size_limit = 0; - VectorAttribute * va = dynamic_cast(va_ptr); + images = va->vector_value("IMAGES", images_limit); + size = va->vector_value("SIZE", size_limit); + ds_id = va->vector_value("ID"); - if ( va == 0 ) - { - return -1; - } - - images = va->vector_value("IMAGES", images_limit); - size = va->vector_value("SIZE", size_limit); - ds_name = va->vector_value("NAME"); - - if ( images_limit < 0 || size_limit < 0 ) + if ( images_limit < 0 || size_limit < 0 || ds_id.empty()) { return -1; } From e60efb14804b16fdad8aea4defa8f932d689fef5 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 5 Jun 2012 01:58:37 +0200 Subject: [PATCH 18/47] feature #1288: Generic Quota Management --- include/Quota.h | 68 +++++++++++-- include/QuotaDatastore.h | 4 +- src/um/Quota.cc | 205 ++++++++++++++++++++++++++++++++++++--- src/um/SConstruct | 2 +- 4 files changed, 254 insertions(+), 25 deletions(-) diff --git a/include/Quota.h b/include/Quota.h index 846a3c47ff..9dc8d9a00c 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -59,14 +59,51 @@ protected: virtual ~Quota(){}; - /** - * Sets new limit values for the quota - * @param quota to be updated - * @param va attribute with the new limits - * @return 0 on success or -1 if wrong limits + /** + * Generic Quota Names + * + * template_name = [ + * ID = "ID to identify the resource", + * metrics[0] = "Limit for the first metric" + * metrics[0]_USED = "Usage for metric" + * ] + * + * ID & counter fields are optional */ - virtual int update_limits(VectorAttribute* quota, - const VectorAttribute* va) = 0; + + /** + * Name of the quota used in the templates + */ + char * template_name; + + /** + * The name of the quota metrics + */ + char ** metrics; + + /** + * Length o + */ + int num_metrics; + + /** + * Check a given quota for an usage request and update counters if the + * request does not exceed quota limits + * @param qid id that identifies the quota, to be used by get_quota + * @param usage_req usage for each metric + * @return true if the request does not exceed current limits + */ + bool check_quota(const string& qid, + map& usage_req, + string& error); + + /** + * Reduce usage from a given quota based on the current consumption + * @param qid id that identifies the quota, to be used by get_quota + * @param usage_req usage for each metric + */ + void del_quota(const string& qid, + map& usage_req); /** * Creates an empty quota based on the given attribute. The attribute va @@ -81,7 +118,7 @@ protected: * @param id of the quota * @return a pointer to the quota or 0 if not found */ - virtual VectorAttribute * get_quota(const string& id); + virtual int get_quota(const string& id, VectorAttribute **va); /** * Adds a new quota, it also updates an internal index for fast accessing @@ -97,6 +134,21 @@ protected: * @param num value to add to the current quota; */ void add_to_quota(VectorAttribute * attr, const string& va_name, int num); + + /** + * Sets new limit values for the quota + * @param quota to be updated + * @param va attribute with the new limits + * @return 0 on success or -1 if wrong limits + */ + int update_limits(VectorAttribute* quota, const VectorAttribute* va); + + /** + * Extract the limits from a given attribute + * @param va the attribute with the limits + * @param + */ + int get_limits(const VectorAttribute * va, map& limits); }; #endif /*QUOTA_H_*/ diff --git a/include/QuotaDatastore.h b/include/QuotaDatastore.h index 0876a857d5..e40cf75cf9 100644 --- a/include/QuotaDatastore.h +++ b/include/QuotaDatastore.h @@ -63,8 +63,8 @@ protected: * @param va attribute with the new limits * @return 0 on success or -1 if wrong limits */ - int update_limits(VectorAttribute* quota, - const VectorAttribute* va); + /*int update_limits(VectorAttribute* quota, + const VectorAttribute* va);*/ /** * Creates an empty quota based on the given attribute. The attribute va diff --git a/src/um/Quota.cc b/src/um/Quota.cc index 124bc0639f..5d0e5ba166 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -19,14 +19,16 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -VectorAttribute * Quota::get_quota(const string& id) +int Quota::get_quota(const string& id, VectorAttribute ** va) { map::iterator it; VectorAttribute * q; + *va = 0; + if ( id.empty() ) { - return 0; + return -1; } for ( it = attributes.begin(); it != attributes.end(); it++) @@ -35,7 +37,8 @@ VectorAttribute * Quota::get_quota(const string& id) if (q->vector_value("ID") == id) { - return q; + *va = q; + return 0; } } @@ -47,15 +50,6 @@ VectorAttribute * Quota::get_quota(const string& id) void Quota::add(VectorAttribute * nq) { - string id; - - id = nq->vector_value("ID"); - - if ( id.empty() ) - { - return; - } - attributes.insert(make_pair(nq->name(), nq)); } @@ -93,7 +87,10 @@ int Quota::set(vector * new_quotas, string& error) { id = (*it)->vector_value("ID"); - tq = get_quota(id); + if ( get_quota(id, &tq) == -1 ) + { + goto error_limits; + } if ( tq == 0 ) { @@ -111,7 +108,7 @@ int Quota::set(vector * new_quotas, string& error) if (update_limits(tq, *it)) { goto error_limits; - } + } } } @@ -127,3 +124,183 @@ error_limits: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +bool Quota::check_quota(const string& qid, + map& usage_req, + string& error) +{ + VectorAttribute * q; + map::iterator it; + + bool check; + int limit; + int usage; + + if ( get_quota(qid, &q) == -1 ) + { + return false; + } + + // ------------------------------------------------------------------------- + // Quota does not exist, create a new one + // ------------------------------------------------------------------------- + if ( q == 0 ) + { + map values; + + for (int i=0; i < num_metrics; i++) + { + ostringstream usage_req_str; + string metrics_used = metrics[i]; + + metrics_used += "_USED"; + + it = usage_req.find(metrics[i]); + + if (it == usage_req.end()) + { + usage_req_str << "0"; + } + else + { + usage_req_str << it->second; + } + + values.insert(make_pair(metrics[i], "0")); + values.insert(make_pair(metrics_used, usage_req_str.str())); + } + + if (!qid.empty()) + { + values.insert(make_pair("ID", qid)); + } + + add(new VectorAttribute(template_name, values)); + + return true; + } + + // ------------------------------------------------------------------------- + // Check the quotas for each usage request + // ------------------------------------------------------------------------- + for (int i=0; i < num_metrics; i++) + { + string metrics_used = metrics[i]; + + metrics_used += "_USED"; + + it = usage_req.find(metrics[i]); + + if (it == usage_req.end()) + { + continue; + } + + q->vector_value(metrics[i], limit); + q->vector_value(metrics_used.c_str(), usage); + + check = ( limit == 0 ) || ( ( usage + it->second ) <= limit ); + + if ( !check ) + { + ostringstream oss; + + oss << "Limit (" << limit << ") reached for " << metrics[i] + << " in quota " << template_name; + + if ( !qid.empty() ) + { + oss << "with ID: " << qid; + } + + error = oss.str(); + + return false; + } + } + + // ------------------------------------------------------------------------- + // Add resource usage to quotas + // ------------------------------------------------------------------------- + for (int i=0; i < num_metrics; i++) + { + string metrics_used = metrics[i]; + + metrics_used += "_USED"; + + it = usage_req.find(metrics[i]); + + if (it == usage_req.end()) + { + continue; + } + + add_to_quota(q, metrics_used, it->second); + } + + return true; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void Quota::del_quota(const string& qid, map& usage_req) +{ + VectorAttribute * q; + map::iterator it; + + if ( get_quota(qid, &q) == -1) + { + return; + } + + if ( q == 0 ) + { + return; + } + + for (int i=0; i < num_metrics; i++) + { + string metrics_used = metrics[i]; + + metrics_used += "_USED"; + + it = usage_req.find(metrics[i]); + + if (it == usage_req.end()) + { + continue; + } + + add_to_quota(q, metrics_used, -it->second); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va) +{ + string limit; + int limit_i; + + for (int i=0; i < num_metrics; i++) + { + limit = va->vector_value(metrics[i], limit_i); + + if ( !limit.empty() ) + { + if ( limit_i < 0 ) + { + return -1; + } + + quota->replace(metrics[i], limit); + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/SConstruct b/src/um/SConstruct index ebee5078b7..af1fe543bc 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -25,7 +25,7 @@ source_files=[ 'User.cc', 'UserPool.cc', 'Quota.cc', - 'QuotaDatastore.cc' +# 'QuotaDatastore.cc' ] # Build library From 2a33492ad81e22bf404020f1d921f062bf4f45ab Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 5 Jun 2012 13:34:28 +0200 Subject: [PATCH 19/47] feature #1288: Generic quota implementation --- include/Quota.h | 42 ++++++---- include/QuotaDatastore.h | 40 ++-------- src/um/Quota.cc | 47 +++++++++-- src/um/QuotaDatastore.cc | 165 ++++----------------------------------- src/um/SConstruct | 2 +- 5 files changed, 90 insertions(+), 206 deletions(-) diff --git a/include/Quota.h b/include/Quota.h index 9dc8d9a00c..1db7af781e 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -55,7 +55,14 @@ public: protected: - Quota(const char * quota_name): Template(false, '=', quota_name) {}; + Quota(const char * quota_name, + const char * _template_name, + const char ** _metrics, + int _num_metrics) + : Template(false, '=', quota_name), + template_name(_template_name), + metrics(_metrics), + num_metrics(_num_metrics){}; virtual ~Quota(){}; @@ -74,15 +81,15 @@ protected: /** * Name of the quota used in the templates */ - char * template_name; + const char * template_name; /** * The name of the quota metrics */ - char ** metrics; + const char ** metrics; /** - * Length o + * Length */ int num_metrics; @@ -105,27 +112,31 @@ protected: void del_quota(const string& qid, map& usage_req); + /** + * Gets a quota identified by its ID. + * @param id of the quota + * @return a pointer to the quota or 0 if not found + */ + virtual int get_quota(const string& id, VectorAttribute **va); + +private: /** * Creates an empty quota based on the given attribute. The attribute va * contains the limits for the quota. * @param va limits for the new quota if 0 limits will be 0 * @return a new attribute representing the quota */ - virtual VectorAttribute * new_quota(VectorAttribute* va) = 0; - - /** - * Gets a quota identified by its ID. - * @param id of the quota - * @return a pointer to the quota or 0 if not found - */ - virtual int get_quota(const string& id, VectorAttribute **va); + VectorAttribute * new_quota(VectorAttribute* va); /** * Adds a new quota, it also updates an internal index for fast accessing * the quotas * @param quota is the new quota, allocated in the HEAP */ - virtual void add(VectorAttribute * quota); + void add(VectorAttribute * nq) + { + attributes.insert(make_pair(nq->name(), nq)); + } /** * Adds a given value to the current quota (vector) @@ -144,9 +155,10 @@ protected: int update_limits(VectorAttribute* quota, const VectorAttribute* va); /** - * Extract the limits from a given attribute + * Extract the limits for the defined quota metrics from a given attribute * @param va the attribute with the limits - * @param + * @param limits stores the known limits + * @return 0 on success */ int get_limits(const VectorAttribute * va, map& limits); }; diff --git a/include/QuotaDatastore.h b/include/QuotaDatastore.h index e40cf75cf9..02964e141c 100644 --- a/include/QuotaDatastore.h +++ b/include/QuotaDatastore.h @@ -36,7 +36,11 @@ class QuotaDatastore : public Quota { public: - QuotaDatastore():Quota("DATASTORE_QUOTA"){}; + QuotaDatastore():Quota("DATASTORE_QUOTA", + "DATASTORE", + DS_METRICS, + NUM_DS_METRICS) + {}; ~QuotaDatastore(){}; @@ -56,39 +60,9 @@ public: void del(Template* tmpl); protected: + static const char * DS_METRICS[]; - /** - * Sets new limit values for the quota - * @param quota to be updated - * @param va attribute with the new limits - * @return 0 on success or -1 if wrong limits - */ - /*int update_limits(VectorAttribute* quota, - const VectorAttribute* va);*/ - - /** - * Creates an empty quota based on the given attribute. The attribute va - * contains the limits for the quota. - * @param va limits for the new quota if 0 limits will be 0 - * @return a new attribute representing the quota - */ - VectorAttribute * new_quota(VectorAttribute * va); - -private: - - /** - * Return the limits for image and size stored in the a given quota. - * @param va_ptr the attribute that stores the quota - * @param ds the id of the DATASTORE quota - * @param imgs the limit for the number of images - * @param size the limit for the total storage size - * - * @return -1 if the limits are wrong 0 otherwise - */ - int get_limits(const VectorAttribute* va, - string& ds, - string& imgs, - string& size); + static const int NUM_DS_METRICS; }; #endif /*QUOTA_DATASTORE_H_*/ \ No newline at end of file diff --git a/src/um/Quota.cc b/src/um/Quota.cc index 5d0e5ba166..63b70f1d4d 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -48,14 +48,6 @@ int Quota::get_quota(const string& id, VectorAttribute ** va) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void Quota::add(VectorAttribute * nq) -{ - attributes.insert(make_pair(nq->name(), nq)); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num) { istringstream iss; @@ -304,3 +296,42 @@ int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +VectorAttribute * Quota::new_quota(VectorAttribute * va) +{ + map limits; + + string limit; + int limit_i; + + for (int i=0; i < num_metrics; i++) + { + string metrics_used = metrics[i]; + + metrics_used += "_USED"; + + limit = va->vector_value(metrics[i], limit_i); + + if ( limit.empty() ) + { + limit = "0"; + } + else if ( limit_i < 0 ) + { + return 0; + } + + limits.insert(make_pair(metrics[i], limit)); + limits.insert(make_pair(metrics_used, "0")); + } + + string id = va->vector_value("ID"); + + if ( !id.empty() ) + { + limits.insert(make_pair("ID", id)); + } + + return new VectorAttribute(template_name,limits); +} + diff --git a/src/um/QuotaDatastore.cc b/src/um/QuotaDatastore.cc index 64c39895ae..833919f9fa 100644 --- a/src/um/QuotaDatastore.cc +++ b/src/um/QuotaDatastore.cc @@ -19,23 +19,20 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +const char * QuotaDatastore::DS_METRICS[] = {"SIZE", "IMAGES"}; + +const int QuotaDatastore::NUM_DS_METRICS = 2; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + bool QuotaDatastore::check(Template * tmpl, string& error) { - VectorAttribute * ds_quota; - - int img_limit = 0; - int size_limit = 0; - int img_used = 0; - int size_used = 0; - - bool img_ok; - bool size_ok; + map ds_request; string ds_id; int size; - // --------------------- Get data from the Template -------------------- - tmpl->get("DATASTORE", ds_id); if ( ds_id.empty() ) @@ -50,64 +47,10 @@ bool QuotaDatastore::check(Template * tmpl, string& error) return false; } - // ------ There are no quotas for this datastore, create a new one ------ - - ds_quota = get_quota(ds_id); - - if ( ds_quota == 0 ) - { - map ds_quota; - ostringstream size_str; - - size_str << size; - - ds_quota.insert(make_pair("ID", ds_id)); - ds_quota.insert(make_pair("IMAGES", "0")); - ds_quota.insert(make_pair("SIZE", "0")); - ds_quota.insert(make_pair("IMAGES_USED", "1")); - ds_quota.insert(make_pair("SIZE_USED", size_str.str())); - - add(new VectorAttribute("DATASTORE", ds_quota)); - - return true; - } - - // ------ Check usage limits ------ - - ds_quota->vector_value("IMAGES", img_limit); - ds_quota->vector_value("SIZE", size_limit); - - ds_quota->vector_value("IMAGES_USED", img_used); - ds_quota->vector_value("SIZE_USED", size_used); - - img_ok = (img_limit == 0) || ((img_used + 1) <= img_limit ); - size_ok = (size_limit== 0) || ((size_used + size) <= size_limit); - - if ( img_ok && size_ok ) - { - add_to_quota(ds_quota, "IMAGES_USED", +1); - add_to_quota(ds_quota, "SIZE_USED", +size); - } - else if (!img_ok) - { - ostringstream oss; - - oss << "Maximum number of images limit (" << img_limit << ")" - << " reached for datastore " << ds_id; - - error = oss.str(); - } - else if (!size_ok) - { - ostringstream oss; - - oss << "Maximum storage capacity limit (" << size_limit << ")" - << " reached for datastore " << ds_id; - - error = oss.str(); - } - - return img_ok && size_ok; + ds_request.insert(make_pair("IMAGES",1)); + ds_request.insert(make_pair("SIZE", size)); + + return check_quota(ds_id, ds_request, error); } /* -------------------------------------------------------------------------- */ @@ -115,7 +58,7 @@ bool QuotaDatastore::check(Template * tmpl, string& error) void QuotaDatastore::del(Template * tmpl) { - VectorAttribute * ds_quota; + map ds_request; string ds_id; int size; @@ -132,86 +75,10 @@ void QuotaDatastore::del(Template * tmpl) return; } - ds_quota = get_quota(ds_id); + ds_request.insert(make_pair("IMAGES",1)); + ds_request.insert(make_pair("SIZE", size)); - if ( ds_quota == 0 ) - { - return; - } - - add_to_quota(ds_quota, "IMAGES_USED", -1); - add_to_quota(ds_quota, "SIZE_USED", -size); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int QuotaDatastore::update_limits(VectorAttribute * quota, - const VectorAttribute * va) -{ - string images_limit; - string size_limit; - string ds_id; - - if ( get_limits(va, ds_id, images_limit, size_limit) != 0 ) - { - return -1; - } - - quota->replace("IMAGES", images_limit); - quota->replace("SIZE", size_limit); - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -VectorAttribute * QuotaDatastore::new_quota(VectorAttribute * va) -{ - string images_limit = "0"; - string size_limit = "0"; - string ds_id; - - if ( get_limits(va, ds_id, images_limit, size_limit) != 0 ) - { - return 0; - } - - map limits; - - limits.insert(make_pair("ID", ds_id)); - - limits.insert(make_pair("IMAGES", images_limit)); - limits.insert(make_pair("SIZE", size_limit)); - - limits.insert(make_pair("IMAGES_USED", "0")); - limits.insert(make_pair("SIZE_USED", "0")); - - return new VectorAttribute("DATASTORE",limits); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int QuotaDatastore::get_limits(const VectorAttribute * va, - string& ds_id, - string& images, - string& size) -{ - int images_limit = 0; - int size_limit = 0; - - images = va->vector_value("IMAGES", images_limit); - size = va->vector_value("SIZE", size_limit); - ds_id = va->vector_value("ID"); - - if ( images_limit < 0 || size_limit < 0 || ds_id.empty()) - { - return -1; - } - - return 0; + del_quota(ds_id, ds_request); } /* -------------------------------------------------------------------------- */ diff --git a/src/um/SConstruct b/src/um/SConstruct index af1fe543bc..ebee5078b7 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -25,7 +25,7 @@ source_files=[ 'User.cc', 'UserPool.cc', 'Quota.cc', -# 'QuotaDatastore.cc' + 'QuotaDatastore.cc' ] # Build library From 4420b1d81ba64fc4ffad89625831e7fcfde04c38 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 5 Jun 2012 19:04:02 +0200 Subject: [PATCH 20/47] feature #1288: New VM and Network quotas. Integration qith VM create operation --- include/Attribute.h | 14 ++++- include/QuotaNetwork.h | 66 +++++++++++++++++++ include/QuotaVirtualMachine.h | 76 ++++++++++++++++++++++ include/User.h | 51 ++++++++++++++- src/image/ImagePool.cc | 5 ++ src/rm/Request.cc | 13 ++++ src/rm/RequestManagerAllocate.cc | 22 ++++++- src/um/QuotaNetwork.cc | 97 ++++++++++++++++++++++++++++ src/um/QuotaVirtualMachine.cc | 105 +++++++++++++++++++++++++++++++ src/um/SConstruct | 4 +- src/um/User.cc | 25 ++++++++ src/vnm/VirtualNetworkPool.cc | 5 ++ 12 files changed, 476 insertions(+), 7 deletions(-) create mode 100644 include/QuotaNetwork.h create mode 100644 include/QuotaVirtualMachine.h create mode 100644 src/um/QuotaNetwork.cc create mode 100644 src/um/QuotaVirtualMachine.cc diff --git a/include/Attribute.h b/include/Attribute.h index abf0ca3777..86049cfbdb 100644 --- a/include/Attribute.h +++ b/include/Attribute.h @@ -304,7 +304,19 @@ public: * Replace the value of the given vector attribute */ void replace(const string& name, const string& value); - + + /** + * Replace the value of the given vector attribute + */ + void replace(const string& name, int value) + { + ostringstream oss; + + oss << value; + + replace(name, oss.str()); + } + /** * Returns the attribute type */ diff --git a/include/QuotaNetwork.h b/include/QuotaNetwork.h new file mode 100644 index 0000000000..3ff0123cce --- /dev/null +++ b/include/QuotaNetwork.h @@ -0,0 +1,66 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTA_NETWORK_H_ +#define QUOTA_NETWORK_H_ + +#include "Quota.h" + +/** + * DataStore Quotas, defined as: + * NETWORK = [ + * ID = + * LEASES = + * LEASES_USED = Current number of IPs + * ] + * + * 0 = unlimited, default if missing + */ + +class QuotaNetwork : public Quota +{ +public: + + QuotaNetwork():Quota("NETWORK_QUOTA", + "NETWORK", + NET_METRICS, + NUM_NET_METRICS) + {}; + + ~QuotaNetwork(){}; + + /** + * Check if the resource allocation will exceed the quota limits. If not + * the usage counters are updated + * @param tmpl template for the resource + * @param error string + * @return true if the operation can be performed + */ + bool check(Template* tmpl, string& error); + + /** + * Decrement usage counters when deallocating image + * @param tmpl template for the resource + */ + void del(Template* tmpl); + +protected: + static const char * NET_METRICS[]; + + static const int NUM_NET_METRICS; +}; + +#endif /*QUOTA_NETWORK_H_*/ diff --git a/include/QuotaVirtualMachine.h b/include/QuotaVirtualMachine.h new file mode 100644 index 0000000000..75593db3d6 --- /dev/null +++ b/include/QuotaVirtualMachine.h @@ -0,0 +1,76 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTA_VIRTUALMACHINE_H_ +#define QUOTA_VIRTUALMACHINE_H_ + +#include "Quota.h" + +/** + * VM Quotas, defined as: + * VM = [ + * VMS = + * MEMORY = + * CPU = + * VMS_USED = Current number of VMs + * MEMORY_USED = Overall Memory requested + * CPU_USED = Overall CPU requested + * ] + * + * 0 = unlimited, default if missing + */ + +class QuotaVirtualMachine : public Quota +{ +public: + + QuotaVirtualMachine():Quota("VM_QUOTA", + "VM", + VM_METRICS, + NUM_VM_METRICS) + {}; + + ~QuotaVirtualMachine(){}; + + /** + * Check if the resource allocation will exceed the quota limits. If not + * the usage counters are updated + * @param tmpl template for the resource + * @param error string + * @return true if the operation can be performed + */ + bool check(Template* tmpl, string& error); + + /** + * Decrement usage counters when deallocating image + * @param tmpl template for the resource + */ + void del(Template* tmpl); + + /** + * Gets a quota identified by its ID. + * @param id of the quota + * @return a pointer to the quota or 0 if not found + */ + int get_quota(const string& id, VectorAttribute **va); + +protected: + static const char * VM_METRICS[]; + + static const int NUM_VM_METRICS; +}; + +#endif /*QUOTA_VIRTUALMACHINE_H_*/ \ No newline at end of file diff --git a/include/User.h b/include/User.h index ee369b30ce..a23cd3be7d 100644 --- a/include/User.h +++ b/include/User.h @@ -20,6 +20,8 @@ #include "PoolSQL.h" #include "UserTemplate.h" #include "QuotaDatastore.h" +#include "QuotaNetwork.h" +#include "QuotaVirtualMachine.h" using namespace std; @@ -187,7 +189,50 @@ public: void datastore_quota_del(Template * tmpl) { return datastore_quota.del(tmpl); - } + } + + /** + * Check Network quotas, it updates usage counters if quotas are not + * exceeded. + * @param tmpl template for the VirtualMachine + * @param reason string describing the error + * @return true if image can be allocated, false otherwise + */ + bool network_quota_check(Template * tmpl, string& reason) + { + return network_quota.check(tmpl, reason); + } + + /** + * Delete usage from quota counters. + * @param tmpl template for the image, with usage + */ + void network_quota_del(Template * tmpl) + { + return network_quota.del(tmpl); + } + + /** + * Check VM quotas, it updates usage counters if quotas are not + * exceeded. + * @param tmpl template for the VirtualMachine + * @param reason string describing the error + * @return true if image can be allocated, false otherwise + */ + bool vm_quota_check(Template * tmpl, string& reason) + { + return vm_quota.check(tmpl, reason); + } + + /** + * Delete usage from quota counters. + * @param tmpl template for the image, with usage + */ + void vm_quota_del(Template * tmpl) + { + return vm_quota.del(tmpl); + } + private: // ------------------------------------------------------------------------- // Friends @@ -217,7 +262,9 @@ private: /** * Usage Counters and Quotas */ - QuotaDatastore datastore_quota; + QuotaDatastore datastore_quota; + QuotaNetwork network_quota; + QuotaVirtualMachine vm_quota; // ************************************************************************* // Authentication session (Private) diff --git a/src/image/ImagePool.cc b/src/image/ImagePool.cc index 251c037a0e..1c9ba404f8 100644 --- a/src/image/ImagePool.cc +++ b/src/image/ImagePool.cc @@ -349,6 +349,11 @@ void ImagePool::authorize_disk(VectorAttribute * disk,int uid, AuthRequest * ar) } img = get(source , uiid, true); + + if ( img != 0 ) + { + disk->replace("IMAGE_ID", img->get_oid()); + } } else if (!(source = disk->vector_value("IMAGE_ID")).empty()) { diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 76adb6a107..14c625108d 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -133,6 +133,17 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) break; case PoolObjectSQL::VM: + rc = user->network_quota_check(tmpl, error_str); + + if ( rc == true ) + { + rc = user->vm_quota_check(tmpl, error_str); + + if ( rc == false ) + { + user->network_quota_del(tmpl); + } + } break; default: @@ -181,6 +192,8 @@ void Request::quota_rollback(Template * tmpl, RequestAttributes& att) break; case PoolObjectSQL::VM: + user->network_quota_del(tmpl); + user->vm_quota_del(tmpl); break; default: diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index c06b82b114..818ac476f8 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -79,7 +79,7 @@ bool VirtualMachineAllocate::allocate_authorization( VirtualMachineTemplate * ttmpl = static_cast(tmpl); - // Check template for restricted attributes + // ------------ Check template for restricted attributes ------------------- if ( att.uid != 0 && att.gid != GroupPool::ONEADMIN_ID ) { @@ -97,6 +97,8 @@ bool VirtualMachineAllocate::allocate_authorization( } } + // ------------------ Authorize VM create operation ------------------------ + ar.add_create_auth(auth_object, tmpl->to_xml(t64)); VirtualMachine::set_auth_request(att.uid, ar, ttmpl); @@ -110,6 +112,13 @@ bool VirtualMachineAllocate::allocate_authorization( return false; } + // -------------------------- Check Quotas ---------------------------- + + if ( quota_authorization(tmpl, att) == false ) + { + return false; + } + return true; } @@ -265,8 +274,15 @@ int VirtualMachineAllocate::pool_allocate(xmlrpc_c::paramList const& paramList, VirtualMachineTemplate * ttmpl= static_cast(tmpl); VirtualMachinePool * vmpool = static_cast(pool); - return vmpool->allocate(att.uid, att.gid, att.uname, att.gname, ttmpl, &id, - error_str, false); + int rc = vmpool->allocate(att.uid, att.gid, att.uname, att.gname, ttmpl, &id, + error_str, false); + + if ( rc != 0 ) + { + quota_rollback(tmpl, att); + } + + return rc; } diff --git a/src/um/QuotaNetwork.cc b/src/um/QuotaNetwork.cc new file mode 100644 index 0000000000..97433d952c --- /dev/null +++ b/src/um/QuotaNetwork.cc @@ -0,0 +1,97 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "QuotaNetwork.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * QuotaNetwork::NET_METRICS[] = {"LEASES"}; + +const int QuotaNetwork::NUM_NET_METRICS = 1; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool QuotaNetwork::check(Template * tmpl, string& error) +{ + vector nics; + VectorAttribute * nic; + + string net_id; + int num; + + map net_request; + + net_request.insert(make_pair("LEASES",1)); + + num = tmpl->get("NIC", nics); + + for (int i = 0 ; i < num ; i++) + { + nic = dynamic_cast(nics[i]); + + if ( nic == 0 ) + { + continue; + } + + net_id = nic->vector_value("NETWORK_ID"); + + if ( !check_quota(net_id, net_request, error) ) + { + return false; + } + } + + return true; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaNetwork::del(Template * tmpl) +{ + + vector nics; + VectorAttribute * nic; + + string net_id; + int num; + + map net_request; + + net_request.insert(make_pair("LEASES",1)); + + num = tmpl->get("NIC", nics); + + for (int i = 0 ; i < num ; i++) + { + nic = dynamic_cast(nics[i]); + + if ( nic == 0 ) + { + continue; + } + + net_id = nic->vector_value("NETWORK_ID"); + + del_quota(net_id, net_request); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaVirtualMachine.cc b/src/um/QuotaVirtualMachine.cc new file mode 100644 index 0000000000..8f125a19eb --- /dev/null +++ b/src/um/QuotaVirtualMachine.cc @@ -0,0 +1,105 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "QuotaVirtualMachine.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * QuotaVirtualMachine::VM_METRICS[] = {"VMS", "CPU", "MEMORY"}; + +const int QuotaVirtualMachine::NUM_VM_METRICS = 2; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int QuotaVirtualMachine::get_quota(const string& id, VectorAttribute **va) +{ + vector values; + int num; + + *va = 0; + + num = get(template_name, values); + + if ( num == 0 ) + { + return 0; + } + + *va = dynamic_cast(values[0]); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool QuotaVirtualMachine::check(Template * tmpl, string& error) +{ + map vm_request; + + int memory; + int cpu; + + if ( tmpl->get("MEMORY", memory) == false ) + { + error = "MEMORY not defined for VM"; + return false; + } + + if ( tmpl->get("CPU", cpu) == false ) + { + error = "CPU not defined for VM"; + return false; + } + + vm_request.insert(make_pair("VMS",1)); + vm_request.insert(make_pair("MEMORY", memory)); + vm_request.insert(make_pair("CPU", cpu)); + + return check_quota("", vm_request, error); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaVirtualMachine::del(Template * tmpl) +{ + map vm_request; + + int memory; + int cpu; + + if ( tmpl->get("MEMORY", memory) == false ) + { + return; + } + + if ( tmpl->get("CPU", cpu) == false ) + { + return; + } + + vm_request.insert(make_pair("VMS",1)); + vm_request.insert(make_pair("MEMORY", memory)); + vm_request.insert(make_pair("CPU", cpu)); + + del_quota("", vm_request); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/SConstruct b/src/um/SConstruct index ebee5078b7..f31d3068b8 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -25,7 +25,9 @@ source_files=[ 'User.cc', 'UserPool.cc', 'Quota.cc', - 'QuotaDatastore.cc' + 'QuotaDatastore.cc', + 'QuotaNetwork.cc', + 'QuotaVirtualMachine.cc', ] # Build library diff --git a/src/um/User.cc b/src/um/User.cc index 7c93094678..3ca5abad5b 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -139,6 +139,8 @@ string& User::to_xml(string& xml) const ostringstream oss; string template_xml; string ds_quota_xml; + string net_quota_xml; + string vm_quota_xml; int enabled_int = enabled?1:0; @@ -153,6 +155,8 @@ string& User::to_xml(string& xml) const "" << enabled_int <<"" << obj_template->to_xml(template_xml) << datastore_quota.to_xml(ds_quota_xml) << + network_quota.to_xml(net_quota_xml) << + vm_quota.to_xml(vm_quota_xml) << ""; xml = oss.str(); @@ -207,6 +211,27 @@ int User::from_xml(const string& xml) } ObjectXML::free_nodes(content); + content.clear(); + + ObjectXML::get_nodes("/USER/NETWORK_QUOTA", content); + + if (!content.empty()) + { + rc += network_quota.from_xml_node(content[0]); + } + + ObjectXML::free_nodes(content); + content.clear(); + + ObjectXML::get_nodes("/USER/VM_QUOTA", content); + + if (!content.empty()) + { + rc += network_quota.from_xml_node(content[0]); + } + + ObjectXML::free_nodes(content); + content.clear(); if (rc != 0) { diff --git a/src/vnm/VirtualNetworkPool.cc b/src/vnm/VirtualNetworkPool.cc index 4a1ca02857..3f0efef8f6 100644 --- a/src/vnm/VirtualNetworkPool.cc +++ b/src/vnm/VirtualNetworkPool.cc @@ -286,6 +286,11 @@ void VirtualNetworkPool::authorize_nic(VectorAttribute * nic, if (!(network = nic->vector_value("NETWORK")).empty()) { vnet = get_nic_by_name (nic, network, uid, error); + + if ( vnet != 0 ) + { + nic->replace("NETWORK_ID", vnet->get_oid()); + } } else if (!(network = nic->vector_value("NETWORK_ID")).empty()) { From 7e7dcf6eac21d30a52771bc4454a670b0c8a57ca Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 5 Jun 2012 23:06:14 +0200 Subject: [PATCH 21/47] feature #1288: Fix some bugs in quota rollback --- include/QuotaNetwork.h | 6 +++--- include/QuotaVirtualMachine.h | 8 ++++---- src/rm/RequestManagerAllocate.cc | 6 ++++-- src/um/QuotaVirtualMachine.cc | 2 +- src/um/User.cc | 2 +- src/vm/VirtualMachinePool.cc | 2 +- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/QuotaNetwork.h b/include/QuotaNetwork.h index 3ff0123cce..defb73e482 100644 --- a/include/QuotaNetwork.h +++ b/include/QuotaNetwork.h @@ -35,9 +35,9 @@ class QuotaNetwork : public Quota public: QuotaNetwork():Quota("NETWORK_QUOTA", - "NETWORK", - NET_METRICS, - NUM_NET_METRICS) + "NETWORK", + NET_METRICS, + NUM_NET_METRICS) {}; ~QuotaNetwork(){}; diff --git a/include/QuotaVirtualMachine.h b/include/QuotaVirtualMachine.h index 75593db3d6..8b430e10ee 100644 --- a/include/QuotaVirtualMachine.h +++ b/include/QuotaVirtualMachine.h @@ -38,9 +38,9 @@ class QuotaVirtualMachine : public Quota public: QuotaVirtualMachine():Quota("VM_QUOTA", - "VM", - VM_METRICS, - NUM_VM_METRICS) + "VM", + VM_METRICS, + NUM_VM_METRICS) {}; ~QuotaVirtualMachine(){}; @@ -61,7 +61,7 @@ public: void del(Template* tmpl); /** - * Gets a quota identified by its ID. + * Gets a quota, overrides base to not to use ID. * @param id of the quota * @return a pointer to the quota or 0 if not found */ diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 818ac476f8..009f4b5c81 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -274,12 +274,14 @@ int VirtualMachineAllocate::pool_allocate(xmlrpc_c::paramList const& paramList, VirtualMachineTemplate * ttmpl= static_cast(tmpl); VirtualMachinePool * vmpool = static_cast(pool); + Template tmpl_back(*tmpl); + int rc = vmpool->allocate(att.uid, att.gid, att.uname, att.gname, ttmpl, &id, error_str, false); - if ( rc != 0 ) + if ( rc < 0 ) { - quota_rollback(tmpl, att); + quota_rollback(&tmpl_back, att); } return rc; diff --git a/src/um/QuotaVirtualMachine.cc b/src/um/QuotaVirtualMachine.cc index 8f125a19eb..deb07ea196 100644 --- a/src/um/QuotaVirtualMachine.cc +++ b/src/um/QuotaVirtualMachine.cc @@ -21,7 +21,7 @@ const char * QuotaVirtualMachine::VM_METRICS[] = {"VMS", "CPU", "MEMORY"}; -const int QuotaVirtualMachine::NUM_VM_METRICS = 2; +const int QuotaVirtualMachine::NUM_VM_METRICS = 3; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/um/User.cc b/src/um/User.cc index 3ca5abad5b..36d78f451e 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -227,7 +227,7 @@ int User::from_xml(const string& xml) if (!content.empty()) { - rc += network_quota.from_xml_node(content[0]); + rc += vm_quota.from_xml_node(content[0]); } ObjectXML::free_nodes(content); diff --git a/src/vm/VirtualMachinePool.cc b/src/vm/VirtualMachinePool.cc index 7f7fa7d207..a36f153a24 100644 --- a/src/vm/VirtualMachinePool.cc +++ b/src/vm/VirtualMachinePool.cc @@ -221,7 +221,7 @@ int VirtualMachinePool::allocate ( // Insert the Object in the pool // ------------------------------------------------------------------------ - *oid = PoolSQL::allocate(vm,error_str); + *oid = PoolSQL::allocate(vm, error_str); return *oid; } From 5c7dc58c662881c879154b9bc183b6c30c057a49 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 5 Jun 2012 23:32:05 +0200 Subject: [PATCH 22/47] feature #1288: Image Quotas --- include/QuotaImage.h | 66 ++++++++++++++++++++++++++++++ include/User.h | 23 +++++++++++ src/rm/Request.cc | 11 +++++ src/um/QuotaImage.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/um/SConstruct | 1 + src/um/User.cc | 23 ++++++++--- 6 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 include/QuotaImage.h create mode 100644 src/um/QuotaImage.cc diff --git a/include/QuotaImage.h b/include/QuotaImage.h new file mode 100644 index 0000000000..0e0e0b4738 --- /dev/null +++ b/include/QuotaImage.h @@ -0,0 +1,66 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTA_IMAGE_H_ +#define QUOTA_IMAGE_H_ + +#include "Quota.h" + +/** + * Image Quotas, defined as: + * IMAGE = [ + * ID = + * INSTANCES = + * INSTANCES_USED = Current number of VMs using the image + * ] + * + * 0 = unlimited, default if missing + */ + +class QuotaImage : public Quota +{ +public: + + QuotaImage():Quota("IMAGE_QUOTA", + "IMAGE", + IMAGE_METRICS, + NUM_IMAGE_METRICS) + {}; + + ~QuotaImage(){}; + + /** + * Check if the resource allocation will exceed the quota limits. If not + * the usage counters are updated + * @param tmpl template for the resource + * @param error string + * @return true if the operation can be performed + */ + bool check(Template* tmpl, string& error); + + /** + * Decrement usage counters when deallocating image + * @param tmpl template for the resource + */ + void del(Template* tmpl); + +protected: + static const char * IMAGE_METRICS[]; + + static const int NUM_IMAGE_METRICS; +}; + +#endif /*QUOTA_IMAGE_H_*/ diff --git a/include/User.h b/include/User.h index a23cd3be7d..81801fbcba 100644 --- a/include/User.h +++ b/include/User.h @@ -22,6 +22,7 @@ #include "QuotaDatastore.h" #include "QuotaNetwork.h" #include "QuotaVirtualMachine.h" +#include "QuotaImage.h" using namespace std; @@ -233,6 +234,27 @@ public: return vm_quota.del(tmpl); } + /** + * Check IMAGE quotas, it updates usage counters if quotas are not + * exceeded. + * @param tmpl template for the VirtualMachine + * @param reason string describing the error + * @return true if image can be allocated, false otherwise + */ + bool image_quota_check(Template * tmpl, string& reason) + { + return image_quota.check(tmpl, reason); + } + + /** + * Delete usage from quota counters. + * @param tmpl template for the image, with usage + */ + void image_quota_del(Template * tmpl) + { + return image_quota.del(tmpl); + } + private: // ------------------------------------------------------------------------- // Friends @@ -265,6 +287,7 @@ private: QuotaDatastore datastore_quota; QuotaNetwork network_quota; QuotaVirtualMachine vm_quota; + QuotaImage image_quota; // ************************************************************************* // Authentication session (Private) diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 14c625108d..5ac442d97c 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -143,6 +143,16 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) { user->network_quota_del(tmpl); } + else + { + rc = user->image_quota_check(tmpl, error_str); + + if ( rc == false ) + { + user->network_quota_del(tmpl); + user->vm_quota_del(tmpl); + } + } } break; @@ -194,6 +204,7 @@ void Request::quota_rollback(Template * tmpl, RequestAttributes& att) case PoolObjectSQL::VM: user->network_quota_del(tmpl); user->vm_quota_del(tmpl); + user->image_quota_del(tmpl); break; default: diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc new file mode 100644 index 0000000000..1c6b2f0fbe --- /dev/null +++ b/src/um/QuotaImage.cc @@ -0,0 +1,97 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "QuotaImage.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * QuotaImage::IMAGE_METRICS[] = {"INSTANCES"}; + +const int QuotaImage::NUM_IMAGE_METRICS = 1; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool QuotaImage::check(Template * tmpl, string& error) +{ + vector disks; + VectorAttribute * disk; + + string image_id; + int num; + + map image_request; + + image_request.insert(make_pair("INSTANCES",1)); + + num = tmpl->get("DISK", disks); + + for (int i = 0 ; i < num ; i++) + { + disk = dynamic_cast(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + image_id = disk->vector_value("IMAGE_ID"); + + if ( !check_quota(image_id, image_request, error) ) + { + return false; + } + } + + return true; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaImage::del(Template * tmpl) +{ + + vector disks; + VectorAttribute * disk; + + string image_id; + int num; + + map image_request; + + image_request.insert(make_pair("INSTANCES",1)); + + num = tmpl->get("DISK", disks); + + for (int i = 0 ; i < num ; i++) + { + disk = dynamic_cast(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + image_id = disk->vector_value("IMAGE_ID"); + + del_quota(image_id, image_request); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/SConstruct b/src/um/SConstruct index f31d3068b8..893811e548 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -28,6 +28,7 @@ source_files=[ 'QuotaDatastore.cc', 'QuotaNetwork.cc', 'QuotaVirtualMachine.cc', + 'QuotaImage.cc' ] # Build library diff --git a/src/um/User.cc b/src/um/User.cc index 36d78f451e..eb5a196a17 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -136,11 +136,13 @@ error_common: string& User::to_xml(string& xml) const { - ostringstream oss; - string template_xml; - string ds_quota_xml; - string net_quota_xml; - string vm_quota_xml; + ostringstream oss; + + string template_xml; + string ds_quota_xml; + string net_quota_xml; + string vm_quota_xml; + string image_quota_xml; int enabled_int = enabled?1:0; @@ -157,6 +159,7 @@ string& User::to_xml(string& xml) const datastore_quota.to_xml(ds_quota_xml) << network_quota.to_xml(net_quota_xml) << vm_quota.to_xml(vm_quota_xml) << + image_quota.to_xml(image_quota_xml) << ""; xml = oss.str(); @@ -233,6 +236,16 @@ int User::from_xml(const string& xml) ObjectXML::free_nodes(content); content.clear(); + ObjectXML::get_nodes("/USER/IMAGE_QUOTA", content); + + if (!content.empty()) + { + rc += image_quota.from_xml_node(content[0]); + } + + ObjectXML::free_nodes(content); + content.clear(); + if (rc != 0) { return -1; From cbcda494d79a33e27e0916cc552621a7a37ebe91 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 6 Jun 2012 17:05:11 +0200 Subject: [PATCH 23/47] feature #1288: Update usage counters on resource removal --- include/VirtualMachine.h | 11 +++++++++ src/dm/DispatchManagerActions.cc | 29 ++++++++++++++++++++-- src/dm/DispatchManagerStates.cc | 36 ++++++++++++++++++++++++--- src/image/ImageManagerActions.cc | 39 ++++++++++++++++++++++++++---- src/image/ImageManagerDriver.cc | 14 +++++------ src/rm/Request.cc | 1 + src/rm/RequestManagerVMTemplate.cc | 10 ++++++++ 7 files changed, 122 insertions(+), 18 deletions(-) diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 494b2a342a..a169429578 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -569,6 +569,17 @@ public: return new VirtualMachineTemplate; } + /** + * Returns a copy of the VirtualMachineTemplate + * @return A copy of the VirtualMachineTemplate + */ + VirtualMachineTemplate * clone_template() const + { + return new VirtualMachineTemplate( + *(static_cast(obj_template))); + }; + + // ------------------------------------------------------------------------ // States // ------------------------------------------------------------------------ diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index a245389409..51c33573f6 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -684,6 +684,11 @@ int DispatchManager::finalize( { VirtualMachine * vm; ostringstream oss; + Template * tmpl; + User * user; + + int uid; + VirtualMachine::VmState state; vm = vmpool->get(vid,true); @@ -701,6 +706,7 @@ int DispatchManager::finalize( Nebula& nd = Nebula::instance(); TransferManager * tm = nd.get_tm(); LifeCycleManager * lcm = nd.get_lcm(); + UserPool * upool = nd.get_upool(); switch (state) { @@ -722,17 +728,36 @@ int DispatchManager::finalize( vmpool->update(vm); vm->log("DiM", Log::INFO, "New VM state is DONE."); + + uid = vm->get_uid(); + tmpl = vm->clone_template(); + + vm->unlock(); + + user = upool->get(uid, true); + + if ( user != 0 ) + { + user->network_quota_del(tmpl); + user->vm_quota_del(tmpl); + user->image_quota_del(tmpl); + + user->unlock(); + } + + delete tmpl; break; case VirtualMachine::ACTIVE: lcm->trigger(LifeCycleManager::DELETE,vid); + vm->unlock(); break; + case VirtualMachine::DONE: + vm->unlock(); break; } - vm->unlock(); - return 0; } diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index a26a965bcf..2fa98f0973 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -17,6 +17,8 @@ #include "DispatchManager.h" #include "NebulaLog.h" +#include "Nebula.h" + void DispatchManager::suspend_success_action(int vid) { VirtualMachine * vm; @@ -97,11 +99,19 @@ void DispatchManager::stop_success_action(int vid) void DispatchManager::done_action(int vid) { - VirtualMachine * vm; + VirtualMachine * vm; + Template * tmpl; + + int uid; VirtualMachine::LcmState lcm_state; VirtualMachine::VmState dm_state; + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + + User * user; + vm = vmpool->get(vid,true); if ( vm == 0 ) @@ -130,6 +140,24 @@ void DispatchManager::done_action(int vid) vm->release_network_leases(); vm->release_disk_images(); + + uid = vm->get_uid(); + tmpl = vm->clone_template(); + + vm->unlock(); + + user = upool->get(uid, true); + + if ( user != 0 ) + { + user->network_quota_del(tmpl); + user->vm_quota_del(tmpl); + user->image_quota_del(tmpl); + + user->unlock(); + } + + delete tmpl; } else { @@ -137,10 +165,10 @@ void DispatchManager::done_action(int vid) oss << "done action received but VM " << vid << " not in ACTIVE state"; NebulaLog::log("DiM",Log::ERROR,oss); + + vm->unlock(); } - - vm->unlock(); - + return; } diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index a33e04cafc..41fe526c84 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -20,6 +20,8 @@ #include "SSLTools.h" #include "SyncRequest.h" #include "Template.h" +#include "Nebula.h" + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -261,10 +263,20 @@ int ImageManager::enable_image(int iid, bool to_enable) int ImageManager::delete_image(int iid, const string& ds_data) { - Image * img; - string source; - string img_tmpl; - string * drv_msg; + Image * img; + + string source; + string img_tmpl; + string * drv_msg; + + int size; + int ds_id; + + int uid; + User * user; + + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); img = ipool->get(iid,true); @@ -305,7 +317,10 @@ int ImageManager::delete_image(int iid, const string& ds_data) drv_msg = format_message(img->to_xml(img_tmpl), ds_data); source = img->get_source(); - + size = img->get_size(); + ds_id = img->get_ds_id(); + uid = img->get_uid(); + if (source.empty()) { string err_str; @@ -328,6 +343,20 @@ int ImageManager::delete_image(int iid, const string& ds_data) delete drv_msg; + user = upool->get(uid, true); + + if ( user != 0 ) + { + Template img_usage; + + img_usage.add("DATASTORE", ds_id); + img_usage.add("SIZE", size); + + user->datastore_quota_del(&img_usage); + + user->unlock(); + } + return 0; } diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index 7a7176aff4..06e77496fd 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -334,16 +334,15 @@ static void rm_action(istringstream& is, source = image->get_source(); - if ( result == "FAILURE" ) - { - goto error; - } - rc = ipool->drop(image, tmp_error); image->unlock(); - if ( rc < 0 ) + if ( result == "FAILURE" ) + { + goto error; + } + else if ( rc < 0 ) { goto error_drop; } @@ -360,7 +359,8 @@ error_drop: return; error: - oss << "Error removing image from datastore"; + oss << "Error removing image from datastore. Manually remove image source " + << source << " to completely delete the image"; getline(is,info); diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 5ac442d97c..1add2881aa 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -202,6 +202,7 @@ void Request::quota_rollback(Template * tmpl, RequestAttributes& att) break; case PoolObjectSQL::VM: + case PoolObjectSQL::TEMPLATE: user->network_quota_del(tmpl); user->vm_quota_del(tmpl); user->image_quota_del(tmpl); diff --git a/src/rm/RequestManagerVMTemplate.cc b/src/rm/RequestManagerVMTemplate.cc index 360ac80434..107defc82a 100644 --- a/src/rm/RequestManagerVMTemplate.cc +++ b/src/rm/RequestManagerVMTemplate.cc @@ -98,8 +98,16 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList delete tmpl; return; } + + if ( quota_authorization(tmpl, att) == false ) + { + delete tmpl; + return; + } } + Template tmpl_back(*tmpl); + rc = vmpool->allocate(att.uid, att.gid, att.uname, att.gname, tmpl, &vid, error_str, false); @@ -109,6 +117,8 @@ void VMTemplateInstantiate::request_execute(xmlrpc_c::paramList const& paramList allocate_error(PoolObjectSQL::VM,error_str), att); + quota_rollback(&tmpl_back, att); + return; } From dc85b9ba1a7acee47080b44c9ec8665e2eb6c53e Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Wed, 6 Jun 2012 17:52:44 +0200 Subject: [PATCH 24/47] feature #1288: Quotas for the SAVE_AS operation --- include/Request.h | 37 +++++++++++++++++++++++--- src/rm/Request.cc | 12 ++++++--- src/rm/RequestManagerAllocate.cc | 15 ++++------- src/rm/RequestManagerChown.cc | 1 - src/rm/RequestManagerVirtualMachine.cc | 35 ++++++++++++++---------- 5 files changed, 67 insertions(+), 33 deletions(-) diff --git a/include/Request.h b/include/Request.h index 3ee051e7c1..132bd7a2d2 100644 --- a/include/Request.h +++ b/include/Request.h @@ -132,14 +132,41 @@ protected: RequestAttributes& att); /** - * Performs a basic quota check for this request using the uid/gid - * from the request. Usage counters are updated for the user/group. + * Performs a basic quota check for this request using the uid/gid and + * object type from the request. Usage counters are updated for the + * user/group. * @param tmpl describing the object * @param att the specific request attributes * * @return true if the user is authorized. */ - bool quota_authorization(Template * tmpl, RequestAttributes& att); + bool quota_authorization(Template * tmpl, RequestAttributes& att) + { + return quota_authorization(tmpl, auth_object, att); + } + + /** + * Performs a basic quota check for this request using the uid/gid + * from the request. Usage counters are updated for the user/group. + * @param tmpl describing the object + * @param object type of the object + * @param att the specific request attributes + * + * @return true if the user is authorized. + */ + bool quota_authorization(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att); + /** + * Performs rollback on usage counters for a previous quota check operation + * for the request. + * @param tmpl describing the object + * @param att the specific request attributes + */ + void quota_rollback(Template * tmpl, RequestAttributes& att) + { + quota_rollback(tmpl, auth_object, att); + } /** * Performs rollback on usage counters for a previous quota check operation @@ -147,7 +174,9 @@ protected: * @param tmpl describing the object * @param att the specific request attributes */ - void quota_rollback(Template * tmpl, RequestAttributes& att); + void quota_rollback(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att); /** * Actual Execution method for the request. Must be implemented by the diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 1add2881aa..ab2c0efc61 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -106,7 +106,9 @@ bool Request::basic_authorization(int oid, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) +bool Request::quota_authorization(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att) { Nebula& nd = Nebula::instance(); UserPool * upool = nd.get_upool(); @@ -126,7 +128,7 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) return false; } - switch (auth_object) + switch (object) { case PoolObjectSQL::IMAGE: rc = user->datastore_quota_check(tmpl, error_str); @@ -181,7 +183,9 @@ bool Request::quota_authorization(Template * tmpl, RequestAttributes& att) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void Request::quota_rollback(Template * tmpl, RequestAttributes& att) +void Request::quota_rollback(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att) { Nebula& nd = Nebula::instance(); UserPool * upool = nd.get_upool(); @@ -195,7 +199,7 @@ void Request::quota_rollback(Template * tmpl, RequestAttributes& att) return; } - switch (auth_object) + switch (object) { case PoolObjectSQL::IMAGE: user->datastore_quota_del(tmpl); diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 009f4b5c81..a290e3936e 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -336,6 +336,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, ImageManager * imagem = nd.get_imagem(); ImageTemplate * tmpl = new ImageTemplate; + Template img_usage; Datastore * ds; Image::DiskType ds_disk_type; @@ -413,6 +414,9 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, tmpl->add("SIZE", size_str); // ------------- Set authorization request for non-oneadmin's -------------- + + img_usage.add("DATASTORE", ds_id); + img_usage.add("SIZE", size_str); if ( att.uid != 0 ) { @@ -420,8 +424,6 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, string tmpl_str; // ------------------ Check permissions and ACLs ---------------------- - tmpl->add("DATASTORE", ds_id); - tmpl->to_xml(tmpl_str); ar.add_create_auth(auth_object, tmpl_str); // CREATE IMAGE @@ -440,13 +442,11 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, // -------------------------- Check Quotas ---------------------------- - if ( quota_authorization(tmpl, att) == false ) + if ( quota_authorization(&img_usage, att) == false ) { delete tmpl; return; } - - tmpl->erase("DATASTORE"); } rc = ipool->allocate(att.uid, @@ -462,11 +462,6 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, error_str); if ( rc < 0 ) { - Template img_usage; - - img_usage.add("DATASTORE", ds_id); - img_usage.add("SIZE", size_str); - quota_rollback(&img_usage, att); failure_response(INTERNAL, allocate_error(error_str), att); diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index 768a87c5e8..f6de0585b1 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -118,7 +118,6 @@ void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList, old_uid = object->get_uid(); object->set_user(noid,nuname); - } if ( ngid != -1 ) diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index fbfdcc102d..78e86fda86 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -514,33 +514,32 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis // ------------------------------------------------------------------------- // Create a template for the new Image // ------------------------------------------------------------------------- - ImageTemplate * itemplate; - ostringstream oss; + ImageTemplate * itemplate = new ImageTemplate; + Template img_usage; - oss << "NAME = \"" << img_name << "\"" << endl; - oss << "SIZE = " << size << endl; + itemplate->add("NAME", img_name); + itemplate->add("SIZE", size); - oss << "SAVED_IMAGE_ID = " << iid_orig << endl; - oss << "SAVED_DISK_ID = " << disk_id << endl; - oss << "SAVED_VM_ID = " << id << endl; + itemplate->add("SAVED_IMAGE_ID",iid_orig); + itemplate->add("SAVED_DISK_ID",disk_id); + itemplate->add("SAVED_VM_ID", id); if ( img_type.empty() ) { - oss << "TYPE = " << Image::type_to_str(type) << endl; + itemplate->add("TYPE", Image::type_to_str(type)); } else { - oss << "TYPE = " << img_type << endl; + itemplate->add("TYPE", img_type); } - itemplate = new ImageTemplate; - - itemplate->parse_str_or_xml(oss.str(), error_str); - itemplate->set_saving(); + img_usage.add("SIZE", size); + img_usage.add("DATASTORE", ds_id); + // ------------------------------------------------------------------------- - // Authorize the operation + // Authorize the operation & check quotas // ------------------------------------------------------------------------- if ( vm_authorization(id, itemplate, att, 0, &ds_perms, auth_op) == false ) @@ -549,6 +548,12 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis return; } + if ( quota_authorization(&img_usage, PoolObjectSQL::IMAGE, att) == false ) + { + delete itemplate; + return; + } + // ------------------------------------------------------------------------- // Create the image // ------------------------------------------------------------------------- @@ -566,6 +571,8 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis error_str); if (rc < 0) { + quota_rollback(&img_usage, PoolObjectSQL::IMAGE, att); + failure_response(INTERNAL, allocate_error(PoolObjectSQL::IMAGE, error_str), att); return; From 82e3f2e0529fe45de06ef51a073d7848e5e66987 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 7 Jun 2012 00:04:08 +0200 Subject: [PATCH 25/47] feature 1288: Add missing updates after modifying user quotas. XML-RPC method to set user quotas --- include/Quota.h | 11 +++++- include/RequestManagerUser.h | 21 ++++++++++++ include/User.h | 58 ++++++++++++++++++++++++++++++++ src/dm/DispatchManagerActions.cc | 2 ++ src/dm/DispatchManagerStates.cc | 2 ++ src/image/ImageManagerActions.cc | 2 ++ src/rm/RequestManager.cc | 2 ++ src/rm/RequestManagerUser.cc | 37 ++++++++++++++++++-- src/um/Quota.cc | 20 +++++++---- 9 files changed, 146 insertions(+), 9 deletions(-) diff --git a/include/Quota.h b/include/Quota.h index 1db7af781e..1c8de9bf98 100644 --- a/include/Quota.h +++ b/include/Quota.h @@ -35,7 +35,7 @@ public: * @param error describe the error in case of error * @return 0 on success -1 otherwise */ - int set(vector * quotas, string& error); + int set(vector * quotas, string& error); /** * Check if the resource allocation will exceed the quota limits. If not @@ -53,6 +53,15 @@ public: virtual void del(Template* tmpl) = 0; + /** + * Returns the name that identifies the quota in a template + */ + const char * get_quota_name() + { + return template_name; + } + + protected: Quota(const char * quota_name, diff --git a/include/RequestManagerUser.h b/include/RequestManagerUser.h index 9201d22b63..e65d914b67 100644 --- a/include/RequestManagerUser.h +++ b/include/RequestManagerUser.h @@ -97,6 +97,27 @@ public: string& err); }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class UserSetQuota : public RequestManagerUser +{ +public: + UserSetQuota(): + RequestManagerUser("UserSetQuota", + "Sets user quota limits", + "A:sis") + { + auth_op = AuthRequest::ADMIN; + }; + + ~UserSetQuota(){}; + + int user_action(int user_id, + xmlrpc_c::paramList const& _paramList, + string& err); +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/User.h b/include/User.h index 81801fbcba..3fe5acc05e 100644 --- a/include/User.h +++ b/include/User.h @@ -171,6 +171,64 @@ public: return new UserTemplate; } + // ------------------------------------------------------------------------- + // Quota Interface + // ------------------------------------------------------------------------- + + /** + * Set the user quotas + * @param tmpl contains the user quota limits + * @param error describes error when setting the quotas + * + * @return 0 on success, -1 otherwise + */ + int set_quota(Template *tmpl, string& error) + { + vector vquotas; + + if ( tmpl->get(datastore_quota.get_quota_name(), vquotas) > 0 ) + { + if ( datastore_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(network_quota.get_quota_name(), vquotas) > 0 ) + { + if ( network_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(image_quota.get_quota_name(), vquotas) > 0 ) + { + if ( image_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(vm_quota.get_quota_name(), vquotas) > 0 ) + { + if ( vm_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + return 0; + } + /** * Check Datastore quotas, it updates usage counters if quotas are not * exceeded. diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 51c33573f6..8dc0679469 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -742,6 +742,8 @@ int DispatchManager::finalize( user->vm_quota_del(tmpl); user->image_quota_del(tmpl); + upool->update(user); + user->unlock(); } diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index 2fa98f0973..50183ffeed 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -154,6 +154,8 @@ void DispatchManager::done_action(int vid) user->vm_quota_del(tmpl); user->image_quota_del(tmpl); + upool->update(user); + user->unlock(); } diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 41fe526c84..472891b4f2 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -354,6 +354,8 @@ int ImageManager::delete_image(int iid, const string& ds_data) user->datastore_quota_del(&img_usage); + upool->update(user); + user->unlock(); } diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index e2413c867c..fcfe07ba47 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -233,6 +233,7 @@ void RequestManager::register_xml_methods() // User Methods xmlrpc_c::methodPtr user_change_password(new UserChangePassword()); xmlrpc_c::methodPtr user_change_auth(new UserChangeAuth()); + xmlrpc_c::methodPtr user_set_quota(new UserSetQuota()); // VMTemplate Methods xmlrpc_c::methodPtr template_instantiate(new VMTemplateInstantiate()); @@ -401,6 +402,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.user.passwd", user_change_password); RequestManagerRegistry.addMethod("one.user.chgrp", user_chown); RequestManagerRegistry.addMethod("one.user.chauth", user_change_auth); + RequestManagerRegistry.addMethod("one.user.quota", user_set_quota); RequestManagerRegistry.addMethod("one.userpool.info", userpool_info); diff --git a/src/rm/RequestManagerUser.cc b/src/rm/RequestManagerUser.cc index 00c2cb883a..c154408780 100644 --- a/src/rm/RequestManagerUser.cc +++ b/src/rm/RequestManagerUser.cc @@ -133,6 +133,39 @@ int UserChangeAuth::user_action(int user_id, return rc; } -/* ------------------------------------------------------------------------- */ -/* ------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +int UserSetQuota::user_action(int user_id, + xmlrpc_c::paramList const& paramList, + string& error_str) +{ + + string quota_str = xmlrpc_c::value_string(paramList.getString(2)); + Template quota_tmpl; + + int rc; + User * user; + + rc = quota_tmpl.parse_str_or_xml(quota_str, error_str); + + if ( rc != 0 ) + { + return -1; + } + + user = static_cast(pool->get(user_id,true)); + + if ( user == 0 ) + { + return -1; + } + + rc = user->set_quota("a_tmpl, error_str); + + pool->update(user); + + user->unlock(); + + return rc; +} diff --git a/src/um/Quota.cc b/src/um/Quota.cc index 63b70f1d4d..a2ed250262 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -68,16 +68,24 @@ void Quota::add_to_quota(VectorAttribute * attr, const string& va_name, int num) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int Quota::set(vector * new_quotas, string& error) +int Quota::set(vector * new_quotas, string& error) { - vector::iterator it; + vector::iterator it; + VectorAttribute * iq; VectorAttribute * tq; string id; for ( it = new_quotas->begin(); it != new_quotas->end(); it++) { - id = (*it)->vector_value("ID"); + iq = dynamic_cast(*it); + + if ( iq == 0 ) + { + goto error_limits; + } + + id = iq->vector_value("ID"); if ( get_quota(id, &tq) == -1 ) { @@ -88,7 +96,7 @@ int Quota::set(vector * new_quotas, string& error) { VectorAttribute * nq; - if ((nq = new_quota(*it)) == 0) + if ((nq = new_quota(iq)) == 0) { goto error_limits; } @@ -97,7 +105,7 @@ int Quota::set(vector * new_quotas, string& error) } else { - if (update_limits(tq, *it)) + if (update_limits(tq, iq)) { goto error_limits; } @@ -108,7 +116,7 @@ int Quota::set(vector * new_quotas, string& error) error_limits: ostringstream oss; - oss << "Negative limits or bad format in quota " << (*it)->marshall(); + oss << "Negative limits or bad format in quota " << iq->marshall(); error = oss.str(); return -1; From a60cf1ad7092d17cbb5191ee47cbf6c1a9a41af3 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 7 Jun 2012 02:18:55 +0200 Subject: [PATCH 26/47] feature #1288: Fixes bugs when setting quotas. OCA and CLI commands for setting user quotas --- src/cli/one_helper/oneuser_helper.rb | 68 ++++++++++++++++++++++++++++ src/cli/oneuser | 22 ++++++++- src/common/Attribute.cc | 6 +++ src/oca/ruby/OpenNebula/User.rb | 17 ++++++- src/um/Quota.cc | 52 ++++++++++++++------- 5 files changed, 147 insertions(+), 18 deletions(-) diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 2b1a532919..97d0dca0ad 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -173,6 +173,74 @@ class OneUserHelper < OpenNebulaHelper::OneHelper table end + HELP_QUOTA = <<-EOT.unindent + #----------------------------------------------------------------------- + # Supported quota limits for users + #----------------------------------------------------------------------- + # DATASTORE = [ + # ID = + # IMAGES = + # SIZE = + # ] + # + # VM = [ + # VMS = + # MEMORY = + # CPU = + # ] + # + # NETWORK = [ + # ID = + # LEASES = + # ] + # + # IMAGE = [ + # ID = + # INSTANCES = + # ] + # + # In any quota 0 means unlimited. The usage counters "*_USED" are + # shown for information purposes and will NOT be modified. + #----------------------------------------------------------------------- + EOT + + def self.set_quota(id, resource, path) + unless path + require 'tempfile' + + tmp = Tempfile.new(id.to_s) + path = tmp.path + + rc = resource.info + + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + tmp << HELP_QUOTA + tmp << resource.template_like_str("DATASTORE_QUOTA") << "\n" + tmp << resource.template_like_str("VM_QUOTA") << "\n" + tmp << resource.template_like_str("NETWORK_QUOTA") << "\n" + tmp << resource.template_like_str("IMAGE_QUOTA") << "\n" + + tmp.flush + + editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH + system("#{editor_path} #{path}") + + unless $?.exitstatus == 0 + puts "Editor not defined" + exit -1 + end + + tmp.close + end + + str = File.read(path) + str + end + private def factory(id=nil) diff --git a/src/cli/oneuser b/src/cli/oneuser index 860078664b..85a36c8d3d 100755 --- a/src/cli/oneuser +++ b/src/cli/oneuser @@ -196,6 +196,26 @@ cmd=CommandParser::CmdParser.new(ARGV) do end end + quota_desc = <<-EOT.unindent + Set the quota limits for the user. If a path is not provided the editor + will be launched to modify the current quotas. + EOT + + command :quota, quota_desc, :userid, [:file, nil] do + helper = OneUserHelper.new + + helper.perform_action(args[0], options, "modified") do |user| + str = OneUserHelper.set_quota(args[0], user, args[1]) + rc = user.set_quota(str) + + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + end + end + login_desc = <<-EOT.unindent Creates the Login token for authentication Examples: @@ -237,7 +257,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do command :delete, delete_desc, [:range, :userid_list] do helper = OneUserHelper.new - helper.perform_actions(args[0],options,"deleted") do |user| + helper.perform_actions(args[0], options, "deleted") do |user| user.delete end end diff --git a/src/common/Attribute.cc b/src/common/Attribute.cc index 8eb41d6cb1..389681ea1b 100644 --- a/src/common/Attribute.cc +++ b/src/common/Attribute.cc @@ -250,6 +250,12 @@ string VectorAttribute::vector_value_str(const char *name, int& value) const istringstream iss(it->second); iss >> value; + if (iss.fail() || !iss.eof()) + { + value = -1; + return ""; + } + return it->second; } diff --git a/src/oca/ruby/OpenNebula/User.rb b/src/oca/ruby/OpenNebula/User.rb index d32cbe5651..a4c381ccb6 100644 --- a/src/oca/ruby/OpenNebula/User.rb +++ b/src/oca/ruby/OpenNebula/User.rb @@ -30,7 +30,8 @@ module OpenNebula :passwd => "user.passwd", :chgrp => "user.chgrp", :update => "user.update", - :chauth => "user.chauth" + :chauth => "user.chauth", + :quota => "user.quota" } SELF = -1 @@ -144,6 +145,20 @@ module OpenNebula return rc end + # Sets the user 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(USER_METHODS[:quota],@pe_id, quota) + rc = nil if !OpenNebula.is_error?(rc) + + return rc + end + ####################################################################### # Helpers to get User information ####################################################################### diff --git a/src/um/Quota.cc b/src/um/Quota.cc index a2ed250262..07ed305954 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -24,6 +24,9 @@ int Quota::get_quota(const string& id, VectorAttribute ** va) map::iterator it; VectorAttribute * q; + istringstream iss(id); + int id_i; + *va = 0; if ( id.empty() ) @@ -31,6 +34,13 @@ int Quota::get_quota(const string& id, VectorAttribute ** va) return -1; } + iss >> id_i; + + if (iss.fail() || !iss.eof()) + { + return -1; + } + for ( it = attributes.begin(); it != attributes.end(); it++) { q = static_cast(it->second); @@ -105,7 +115,7 @@ int Quota::set(vector * new_quotas, string& error) } else { - if (update_limits(tq, iq)) + if (update_limits(tq, iq) != 0) { goto error_limits; } @@ -116,9 +126,20 @@ int Quota::set(vector * new_quotas, string& error) error_limits: ostringstream oss; - oss << "Negative limits or bad format in quota " << iq->marshall(); + oss << "Negative limits or bad format in quota " << template_name; + + if ( iq != 0 ) + { + string * quota_str = iq->marshall(","); + + oss << " = [ " << *quota_str << " ]"; + + delete quota_str; + } + error = oss.str(); + return -1; } @@ -286,15 +307,14 @@ int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va) for (int i=0; i < num_metrics; i++) { - limit = va->vector_value(metrics[i], limit_i); + limit = va->vector_value_str(metrics[i], limit_i); - if ( !limit.empty() ) + if ( limit_i < 0 ) + { + return -1; + } + else if ( !limit.empty() ) { - if ( limit_i < 0 ) - { - return -1; - } - quota->replace(metrics[i], limit); } } @@ -318,16 +338,16 @@ VectorAttribute * Quota::new_quota(VectorAttribute * va) metrics_used += "_USED"; - limit = va->vector_value(metrics[i], limit_i); - - if ( limit.empty() ) - { - limit = "0"; - } - else if ( limit_i < 0 ) + limit = va->vector_value_str(metrics[i], limit_i); + + if ( limit_i < 0 ) { return 0; } + else if ( limit.empty() ) + { + limit = "0"; + } limits.insert(make_pair(metrics[i], limit)); limits.insert(make_pair(metrics_used, "0")); From 3ff91211df3cff6be26b7dc38ded33456e122b19 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 7 Jun 2012 02:33:02 +0200 Subject: [PATCH 27/47] feature #1288: Fix setting quotas with missing limits. --- src/um/Quota.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/um/Quota.cc b/src/um/Quota.cc index 07ed305954..8fd83d5655 100644 --- a/src/um/Quota.cc +++ b/src/um/Quota.cc @@ -226,12 +226,12 @@ bool Quota::check_quota(const string& qid, { ostringstream oss; - oss << "Limit (" << limit << ") reached for " << metrics[i] - << " in quota " << template_name; + oss << "Limit of " << limit << " reached for " << metrics[i] + << " quota in " << template_name; if ( !qid.empty() ) { - oss << "with ID: " << qid; + oss << " with ID: " << qid; } error = oss.str(); @@ -309,11 +309,11 @@ int Quota::update_limits(VectorAttribute * quota, const VectorAttribute * va) { limit = va->vector_value_str(metrics[i], limit_i); - if ( limit_i < 0 ) + if ( limit_i < 0 ) //No quota, NaN or negative { return -1; } - else if ( !limit.empty() ) + else { quota->replace(metrics[i], limit); } @@ -340,11 +340,7 @@ VectorAttribute * Quota::new_quota(VectorAttribute * va) limit = va->vector_value_str(metrics[i], limit_i); - if ( limit_i < 0 ) - { - return 0; - } - else if ( limit.empty() ) + if ( limit_i < 0 ) //No quota, NaN or negative { limit = "0"; } From 79a040ce17efe60202f24a7eab00eff63b459bff Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Thu, 7 Jun 2012 19:31:16 +0200 Subject: [PATCH 28/47] feature #1288: Change metric for IMAGE quota. Show/List methods for user --- include/QuotaImage.h | 6 +- src/cli/etc/oneuser.yaml | 17 ++- src/cli/one_helper/oneuser_helper.rb | 152 ++++++++++++++++++++++++++- src/um/QuotaImage.cc | 6 +- 4 files changed, 172 insertions(+), 9 deletions(-) diff --git a/include/QuotaImage.h b/include/QuotaImage.h index 0e0e0b4738..6d75c62648 100644 --- a/include/QuotaImage.h +++ b/include/QuotaImage.h @@ -22,9 +22,9 @@ /** * Image Quotas, defined as: * IMAGE = [ - * ID = - * INSTANCES = - * INSTANCES_USED = Current number of VMs using the image + * ID = + * RVMS = + * RVMS _USED = Current number of VMs using the image * ] * * 0 = unlimited, default if missing diff --git a/src/cli/etc/oneuser.yaml b/src/cli/etc/oneuser.yaml index 377dd40d87..4795f35d9d 100644 --- a/src/cli/etc/oneuser.yaml +++ b/src/cli/etc/oneuser.yaml @@ -18,6 +18,18 @@ :size: 8 :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 + :PASSWORD: :desc: Password of the User :size: 50 @@ -27,4 +39,7 @@ - :GROUP - :NAME - :AUTH -- :PASSWORD +- :VMS +- :MEMORY +- :CPU + diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 97d0dca0ad..79cbc9fee3 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -163,11 +163,123 @@ class OneUserHelper < OpenNebulaHelper::OneHelper d["AUTH_DRIVER"] end + column :VMS, "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 + "0" + 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 + + column :CPU, "Total CPU 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']['CPU_USED'] + else + "0" + end + end + column :PASSWORD, "Password of the User", :size=>50 do |d| d['PASSWORD'] end - default :ID, :GROUP, :NAME, :AUTH, :PASSWORD + default :ID, :GROUP, :NAME, :AUTH, :VMS, :MEMORY, :CPU + end + + table + end + + def format_ds_quota() + table = CLIHelper::ShowTable.new(nil, self) do + column :"DATASTORE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"IMAGES (used)", "", :right, :size=>14 do |d| + d["IMAGES_USED"] if !d.nil? + end + + column :"IMAGES (limit)", "", :right, :size=>14 do |d| + d["IMAGES"] if !d.nil? + end + + column :"SIZE (used)", "", :right, :size=>14 do |d| + d["SIZE_USED"] if !d.nil? + end + + column :"SIZE (limit)", "", :right, :size=>14 do |d| + d["SIZE"] if !d.nil? + end + end + + table + end + + def format_net_quota() + table = CLIHelper::ShowTable.new(nil, self) do + column :"NETWORK ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"LEASES (used)", "", :right, :size=>14 do |d| + d["LEASES_USED"] if !d.nil? + end + + column :"LEASES (limit)", "", :right, :size=>14 do |d| + d["LEASES"] if !d.nil? + end + end + + table + end + + def format_vm_quota() + table = CLIHelper::ShowTable.new(nil, self) do + column :"VMS", "", :left, :size=>12 do |d| + d["VMS"] if !d.nil? + end + + column :"MEMORY (used)", "", :right, :size=>14 do |d| + d["MEMORY_USED"] if !d.nil? + end + + column :"MEMORY (limit)", "", :right, :size=>14 do |d| + d["MEMORY"] if !d.nil? + end + + column :"CPU (used)", "", :right, :size=>14 do |d| + d["CPU_USED"] if !d.nil? + end + + column :"CPU (limit)", "", :right, :size=>14 do |d| + d["CPU"] if !d.nil? + end + end + + table + end + + def format_image_quota() + table = CLIHelper::ShowTable.new(nil, self) do + column :"IMAGE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"RVMS (used)", "", :right, :size=>14 do |d| + d["INSTANCES_USED"] if !d.nil? + end + + column :"RVMS (limit)", "", :right, :size=>14 do |d| + d["INSTANCES"] if !d.nil? + end end table @@ -196,7 +308,7 @@ class OneUserHelper < OpenNebulaHelper::OneHelper # # IMAGE = [ # ID = - # INSTANCES = + # RVMS = # ] # # In any quota 0 means unlimited. The usage counters "*_USED" are @@ -275,5 +387,41 @@ class OneUserHelper < OpenNebulaHelper::OneHelper CLIHelper.print_header(str_h1 % "USER TEMPLATE",false) puts user.template_str + + user_hash = user.to_hash + + puts + + CLIHelper.print_header(str_h1 % "RESOURCE USAGE & QUOTAS",false) + + puts + + ds_quotas = [user_hash['USER']['DATASTORE_QUOTA']['DATASTORE']].flatten + if !ds_quotas[0].nil? + table_ds = format_ds_quota + table_ds.show(ds_quotas, {}) + puts + end + + vm_quotas = [user_hash['USER']['VM_QUOTA']['VM']].flatten + if !vm_quotas[0].nil? + table_net = format_vm_quota + table_net.show(vm_quotas, {}) + puts + end + + net_quotas = [user_hash['USER']['NETWORK_QUOTA']['NETWORK']].flatten + if !net_quotas[0].nil? + table_net = format_net_quota + table_net.show(net_quotas, {}) + puts + end + + image_quotas = [user_hash['USER']['IMAGE_QUOTA']['IMAGE']].flatten + if !image_quotas[0].nil? + table_image = format_image_quota + table_image.show(image_quotas, {}) + end + end end diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc index 1c6b2f0fbe..84d7d859f3 100644 --- a/src/um/QuotaImage.cc +++ b/src/um/QuotaImage.cc @@ -19,7 +19,7 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -const char * QuotaImage::IMAGE_METRICS[] = {"INSTANCES"}; +const char * QuotaImage::IMAGE_METRICS[] = {"RVMS"}; const int QuotaImage::NUM_IMAGE_METRICS = 1; @@ -36,7 +36,7 @@ bool QuotaImage::check(Template * tmpl, string& error) map image_request; - image_request.insert(make_pair("INSTANCES",1)); + image_request.insert(make_pair("RVMS",1)); num = tmpl->get("DISK", disks); @@ -74,7 +74,7 @@ void QuotaImage::del(Template * tmpl) map image_request; - image_request.insert(make_pair("INSTANCES",1)); + image_request.insert(make_pair("RVMS",1)); num = tmpl->get("DISK", disks); From 654e84087105f9d4b2d91c76cfbd28e2c56299de Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 8 Jun 2012 01:50:15 +0200 Subject: [PATCH 29/47] feature #1288: Update quotas for chown operations --- include/Request.h | 26 ++++++ include/RequestManagerChown.h | 5 ++ src/cli/one_helper/oneuser_helper.rb | 3 + src/rm/RequestManagerChown.cc | 114 +++++++++++++++++++++++++-- 4 files changed, 143 insertions(+), 5 deletions(-) diff --git a/include/Request.h b/include/Request.h index 132bd7a2d2..1cc24244dc 100644 --- a/include/Request.h +++ b/include/Request.h @@ -75,6 +75,32 @@ protected: string session; /**< Session from ONE XML-RPC API */ xmlrpc_c::value * retval; /**< Return value from libxmlrpc-c */ + + RequestAttributes(){}; + + RequestAttributes(const RequestAttributes& ra) + { + uid = ra.uid; + gid = ra.gid; + + uname = ra.uname; + gname = ra.gname; + + session = ra.session; + retval = ra.retval; + }; + + RequestAttributes(int _uid, int _gid, const RequestAttributes& ra) + { + uid = _uid; + gid = _gid; + + uname = ""; + gname = ""; + + session = ra.session; + retval = ra.retval; + }; }; /* -------- Static (shared among request of the same method) -------- */ diff --git a/include/RequestManagerChown.h b/include/RequestManagerChown.h index 9ccedc49a0..37778dbc14 100644 --- a/include/RequestManagerChown.h +++ b/include/RequestManagerChown.h @@ -52,6 +52,11 @@ protected: virtual void request_execute(xmlrpc_c::paramList const& _paramList, RequestAttributes& att); + + PoolObjectSQL * get_and_quota(int oid, + int new_uid, + int new_gid, + RequestAttributes& att); }; /* ------------------------------------------------------------------------- */ diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 79cbc9fee3..070ff8d5e8 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -197,6 +197,9 @@ class OneUserHelper < OpenNebulaHelper::OneHelper table end + #--------------------------------------------------------------------------- + # Tables to format user quotas + #--------------------------------------------------------------------------- def format_ds_quota() table = CLIHelper::ShowTable.new(nil, self) do column :"DATASTORE ID", "", :left, :size=>12 do |d| diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index f6de0585b1..fdbd2d6837 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -23,6 +23,96 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +PoolObjectSQL * RequestManagerChown::get_and_quota( + int oid, + int new_uid, + int new_gid, + RequestAttributes& att) +{ + Template * tmpl; + + int old_uid; + int old_gid; + + PoolObjectSQL * object; + + object = pool->get(oid,true); + + if ( object == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object), oid), + att); + return 0; + } + + if ( new_uid < 0 ) + { + return object; + } + + if ( auth_object == PoolObjectSQL::VM ) + { + tmpl = (static_cast(object))->clone_template(); + } + else + { + Image * img = static_cast(object); + tmpl = new Template; + + tmpl->add("DATASTORE", img->get_ds_id()); + tmpl->add("SIZE", img->get_size()); + } + + old_uid = object->get_uid(); + 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 ) + { + delete tmpl; + return 0; + } + } + + if ( old_uid != 0 ) + { + 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); + } + + failure_response(NO_EXISTS, + get_error(object_name(auth_object), oid), + att); + } + + delete tmpl; + + return object; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList, RequestAttributes& att) { @@ -102,13 +192,27 @@ void RequestManagerChown::request_execute(xmlrpc_c::paramList const& paramList, } } - // ------------- Update the object --------------------- + // --------------- Update the object and check quotas ---------------------- - object = pool->get(oid,true); + if ( auth_object == PoolObjectSQL::VM || + auth_object == PoolObjectSQL::IMAGE ) + { + object = get_and_quota(oid, noid, ngid, att); + } + else + { + object = pool->get(oid,true); - if ( object == 0 ) - { - failure_response(NO_EXISTS,get_error(object_name(auth_object),oid),att); + if ( object == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object), oid), + att); + } + } + + if ( object == 0 ) + { return; } From 819f1b6a41562703dbf0f9f0f53f033d4e147b4d Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 8 Jun 2012 13:45:15 +0200 Subject: [PATCH 30/47] feature #1288: Abstract quota functionality to be reused by Groups --- include/Quotas.h | 284 ++++++++++++++++++++++++++ include/User.h | 158 +------------- install.sh | 1 + src/cli/one_helper/onequota_helper.rb | 209 +++++++++++++++++++ src/cli/one_helper/oneuser_helper.rb | 194 +----------------- src/cli/oneuser | 3 +- src/dm/DispatchManagerActions.cc | 4 +- src/dm/DispatchManagerStates.cc | 6 +- src/image/ImageManagerActions.cc | 2 +- src/rm/Request.cc | 30 +-- src/rm/RequestManagerUser.cc | 2 +- src/um/User.cc | 54 +---- 12 files changed, 518 insertions(+), 429 deletions(-) create mode 100644 include/Quotas.h create mode 100644 src/cli/one_helper/onequota_helper.rb diff --git a/include/Quotas.h b/include/Quotas.h new file mode 100644 index 0000000000..d6767668d1 --- /dev/null +++ b/include/Quotas.h @@ -0,0 +1,284 @@ +/* -------------------------------------------------------------------------- */ +/* 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 QUOTAS_H_ +#define QUOTAS_H_ + +#include "QuotaDatastore.h" +#include "QuotaNetwork.h" +#include "QuotaVirtualMachine.h" +#include "QuotaImage.h" + +class Quotas +{ +public: + Quotas(const char * _ds_xpath, + const char * _net_xpath, + const char * _img_xpath, + const char * _vm_xpath): + ds_xpath(_ds_xpath), + net_xpath(_net_xpath), + img_xpath(_img_xpath), + vm_xpath(_vm_xpath) + {}; + + virtual ~Quotas(){}; + + /** + * Set the quotas + * @param tmpl contains the user quota limits + * @param error describes error when setting the quotas + * + * @return 0 on success, -1 otherwise + */ + int set(Template *tmpl, string& error) + { + vector vquotas; + + if ( tmpl->get(datastore_quota.get_quota_name(), vquotas) > 0 ) + { + if ( datastore_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(network_quota.get_quota_name(), vquotas) > 0 ) + { + if ( network_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(image_quota.get_quota_name(), vquotas) > 0 ) + { + if ( image_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + if ( tmpl->get(vm_quota.get_quota_name(), vquotas) > 0 ) + { + if ( vm_quota.set(&vquotas, error) != 0 ) + { + return -1; + } + + vquotas.clear(); + } + + return 0; + } + + /** + * Check Datastore quotas, it updates usage counters if quotas are not + * exceeded. + * @param tmpl template for the image + * @param reason string describing the error + * @return true if image can be allocated, false otherwise + */ + bool ds_check(Template * tmpl, string& reason) + { + return datastore_quota.check(tmpl, reason); + } + + /** + * Delete usage from quota counters. + * @param tmpl template for the image, with usage + */ + void ds_del(Template * tmpl) + { + return datastore_quota.del(tmpl); + } + + /** + * Check Virtual Machine quotas (network, image and compute), it updates + * usage counters if quotas are not exceeded. + * @param tmpl template for the VirtualMachine + * @param error_str string describing the error + * @return true if VM can be allocated, false otherwise + */ + bool vm_check(Template * tmpl, string& error_str) + { + + if ( network_quota.check(tmpl, error_str) == false ) + { + return false; + } + + if ( vm_quota.check(tmpl, error_str) == false ) + { + network_quota.del(tmpl); + return false; + } + + if ( image_quota.check(tmpl, error_str) == false ) + { + network_quota.del(tmpl); + vm_quota.del(tmpl); + return false; + } + + return true; + } + + /** + * Delete VM related usage (network, image and compute) from quota counters. + * @param tmpl template for the image, with usage + */ + void vm_del(Template * tmpl) + { + network_quota.del(tmpl); + vm_quota.del(tmpl); + image_quota.del(tmpl); + } + + /** + * Generates a string representation of the quotas in XML format + * @param xml the string to store the XML + * @return the same xml string to use it in << compounds + */ + string& to_xml(string& xml) const + { + ostringstream oss; + + string ds_quota_xml; + string net_quota_xml; + string vm_quota_xml; + string image_quota_xml; + + oss << datastore_quota.to_xml(ds_quota_xml) + << network_quota.to_xml(net_quota_xml) + << vm_quota.to_xml(vm_quota_xml) + << image_quota.to_xml(image_quota_xml); + + xml = oss.str(); + + return xml; + } + + /** + * Builds quota object from an ObjectXML + * @param object_xml pointer to the ObjectXML + * @return 0 if success + */ + int from_xml(ObjectXML * object_xml) + { + vector content; + int rc = 0; + + object_xml->get_nodes(ds_xpath, content); + + if (!content.empty()) + { + rc += datastore_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(net_xpath, content); + + if (!content.empty()) + { + rc += network_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(vm_xpath, content); + + if (!content.empty()) + { + rc += vm_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + content.clear(); + + object_xml->get_nodes(img_xpath, content); + + if (!content.empty()) + { + rc += image_quota.from_xml_node(content[0]); + } + + object_xml->free_nodes(content); + + return rc; + } + +private: + //-------------------------------------------------------------------------- + // Usage Counters and Quotas + //-------------------------------------------------------------------------- + + /** + * Datastore Quotas + */ + QuotaDatastore datastore_quota; + + /** + * Network Quotas + */ + QuotaNetwork network_quota; + + /** + * Image Quotas + */ + QuotaImage image_quota; + + /** + * Virtual Machine Quotas + */ + QuotaVirtualMachine vm_quota; + + //-------------------------------------------------------------------------- + // XPaths + //-------------------------------------------------------------------------- + + /** + * Path for the datastore quota object + */ + const char * ds_xpath; + + /** + * Path for the network quota object + */ + const char * net_xpath; + + /** + * Path for the image quota object + */ + const char * img_xpath; + + /** + * Path for the vm quota object + */ + const char * vm_xpath; + +}; + +#endif /*QUOTABLE_H_*/ diff --git a/include/User.h b/include/User.h index 3fe5acc05e..f8aa724fcc 100644 --- a/include/User.h +++ b/include/User.h @@ -19,10 +19,7 @@ #include "PoolSQL.h" #include "UserTemplate.h" -#include "QuotaDatastore.h" -#include "QuotaNetwork.h" -#include "QuotaVirtualMachine.h" -#include "QuotaImage.h" +#include "Quotas.h" using namespace std; @@ -171,147 +168,10 @@ public: return new UserTemplate; } - // ------------------------------------------------------------------------- - // Quota Interface - // ------------------------------------------------------------------------- - /** - * Set the user quotas - * @param tmpl contains the user quota limits - * @param error describes error when setting the quotas - * - * @return 0 on success, -1 otherwise + * Object quotas, provides set and check interface */ - int set_quota(Template *tmpl, string& error) - { - vector vquotas; - - if ( tmpl->get(datastore_quota.get_quota_name(), vquotas) > 0 ) - { - if ( datastore_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(network_quota.get_quota_name(), vquotas) > 0 ) - { - if ( network_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(image_quota.get_quota_name(), vquotas) > 0 ) - { - if ( image_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - if ( tmpl->get(vm_quota.get_quota_name(), vquotas) > 0 ) - { - if ( vm_quota.set(&vquotas, error) != 0 ) - { - return -1; - } - - vquotas.clear(); - } - - return 0; - } - - /** - * Check Datastore quotas, it updates usage counters if quotas are not - * exceeded. - * @param tmpl template for the image - * @param reason string describing the error - * @return true if image can be allocated, false otherwise - */ - bool datastore_quota_check(Template * tmpl, string& reason) - { - return datastore_quota.check(tmpl, reason); - } - - /** - * Delete usage from quota counters. - * @param tmpl template for the image, with usage - */ - void datastore_quota_del(Template * tmpl) - { - return datastore_quota.del(tmpl); - } - - /** - * Check Network quotas, it updates usage counters if quotas are not - * exceeded. - * @param tmpl template for the VirtualMachine - * @param reason string describing the error - * @return true if image can be allocated, false otherwise - */ - bool network_quota_check(Template * tmpl, string& reason) - { - return network_quota.check(tmpl, reason); - } - - /** - * Delete usage from quota counters. - * @param tmpl template for the image, with usage - */ - void network_quota_del(Template * tmpl) - { - return network_quota.del(tmpl); - } - - /** - * Check VM quotas, it updates usage counters if quotas are not - * exceeded. - * @param tmpl template for the VirtualMachine - * @param reason string describing the error - * @return true if image can be allocated, false otherwise - */ - bool vm_quota_check(Template * tmpl, string& reason) - { - return vm_quota.check(tmpl, reason); - } - - /** - * Delete usage from quota counters. - * @param tmpl template for the image, with usage - */ - void vm_quota_del(Template * tmpl) - { - return vm_quota.del(tmpl); - } - - /** - * Check IMAGE quotas, it updates usage counters if quotas are not - * exceeded. - * @param tmpl template for the VirtualMachine - * @param reason string describing the error - * @return true if image can be allocated, false otherwise - */ - bool image_quota_check(Template * tmpl, string& reason) - { - return image_quota.check(tmpl, reason); - } - - /** - * Delete usage from quota counters. - * @param tmpl template for the image, with usage - */ - void image_quota_del(Template * tmpl) - { - return image_quota.del(tmpl); - } + Quotas quota; private: // ------------------------------------------------------------------------- @@ -339,14 +199,6 @@ private: */ bool enabled; - /** - * Usage Counters and Quotas - */ - QuotaDatastore datastore_quota; - QuotaNetwork network_quota; - QuotaVirtualMachine vm_quota; - QuotaImage image_quota; - // ************************************************************************* // Authentication session (Private) // ************************************************************************* @@ -443,6 +295,10 @@ protected: const string& _auth_driver, bool _enabled): PoolObjectSQL(id,USER,_uname,-1,_gid,"",_gname,table), + quota("/USER/DATASTORE_QUOTA", + "/USER/NETWORK_QUOTA", + "/USER/IMAGE_QUOTA", + "/USER/VM_QUOTA"), password(_password), auth_driver(_auth_driver), enabled(_enabled), diff --git a/install.sh b/install.sh index 09351fb2c6..0b4ef57645 100755 --- a/install.sh +++ b/install.sh @@ -1115,6 +1115,7 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/oneimage_helper.rb \ src/cli/one_helper/onetemplate_helper.rb \ src/cli/one_helper/oneuser_helper.rb \ + src/cli/one_helper/onequota_helper.rb \ src/cli/one_helper/onevm_helper.rb \ src/cli/one_helper/onevnet_helper.rb \ src/cli/one_helper/oneacl_helper.rb \ diff --git a/src/cli/one_helper/onequota_helper.rb b/src/cli/one_helper/onequota_helper.rb new file mode 100644 index 0000000000..3e2e0a57ed --- /dev/null +++ b/src/cli/one_helper/onequota_helper.rb @@ -0,0 +1,209 @@ +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + +require 'cli_helper' + +class OneQuotaHelper + + #--------------------------------------------------------------------------- + # Tables to format user quotas + #--------------------------------------------------------------------------- + TABLE_DS = CLIHelper::ShowTable.new(nil, self) do + column :"DATASTORE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"IMAGES (used)", "", :right, :size=>14 do |d| + d["IMAGES_USED"] if !d.nil? + end + + column :"IMAGES (limit)", "", :right, :size=>14 do |d| + d["IMAGES"] if !d.nil? + end + + column :"SIZE (used)", "", :right, :size=>14 do |d| + d["SIZE_USED"] if !d.nil? + end + + column :"SIZE (limit)", "", :right, :size=>14 do |d| + d["SIZE"] if !d.nil? + end + end + + TABLE_NET = CLIHelper::ShowTable.new(nil, self) do + column :"NETWORK ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"LEASES (used)", "", :right, :size=>14 do |d| + d["LEASES_USED"] if !d.nil? + end + + column :"LEASES (limit)", "", :right, :size=>14 do |d| + d["LEASES"] if !d.nil? + end + end + + TABLE_VM = CLIHelper::ShowTable.new(nil, self) do + column :"VMS", "", :left, :size=>12 do |d| + d["VMS"] if !d.nil? + end + + column :"MEMORY (used)", "", :right, :size=>14 do |d| + d["MEMORY_USED"] if !d.nil? + end + + column :"MEMORY (limit)", "", :right, :size=>14 do |d| + d["MEMORY"] if !d.nil? + end + + column :"CPU (used)", "", :right, :size=>14 do |d| + d["CPU_USED"] if !d.nil? + end + + column :"CPU (limit)", "", :right, :size=>14 do |d| + d["CPU"] if !d.nil? + end + end + + TABLE_IMG = CLIHelper::ShowTable.new(nil, self) do + column :"IMAGE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"RVMS (used)", "", :right, :size=>14 do |d| + d["RVMS_USED"] if !d.nil? + end + + column :"RVMS (limit)", "", :right, :size=>14 do |d| + d["RVMS"] if !d.nil? + end + end + + HELP_QUOTA = <<-EOT.unindent + #----------------------------------------------------------------------- + # Supported quota limits: + # + # DATASTORE = [ + # ID = + # IMAGES = + # SIZE = + # ] + # + # VM = [ + # VMS = + # MEMORY = + # CPU = + # ] + # + # NETWORK = [ + # ID = + # LEASES = + # ] + # + # IMAGE = [ + # ID = + # RVMS = + # ] + # + # In any quota 0 means unlimited. The usage counters "*_USED" are + # shown for information purposes and will NOT be modified. + #----------------------------------------------------------------------- + EOT + + # Edits the quota template of a resource + # @param resource [PoolElement] to get the current info from + # @param path [String] path to the new contents. If nil a editor will be + # used + # @return [String] contents of the new quotas + def self.set_quota(resource, path) + str = "" + + if path.nil? + require 'tempfile' + + tmp = Tempfile.new('one-cli') + path = tmp.path + + rc = resource.info + + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + tmp << HELP_QUOTA + tmp << resource.template_like_str("DATASTORE_QUOTA") << "\n" + tmp << resource.template_like_str("VM_QUOTA") << "\n" + tmp << resource.template_like_str("NETWORK_QUOTA") << "\n" + tmp << resource.template_like_str("IMAGE_QUOTA") << "\n" + + tmp.close + + editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH + system("#{editor_path} #{path}") + + unless $?.exitstatus == 0 + puts "Editor not defined" + exit -1 + end + + str = File.read(path) + + File.unlink(path) + else + str = File.read(path) + end + + str + end + + # Outputs formated quota information to stdout + # @param qh [Hash] with the quotas for a given resource + # + def self.format_quota(qh) + str_h1="%-80s" + + puts + + CLIHelper.print_header(str_h1 % "RESOURCE USAGE & QUOTAS",false) + + puts + + ds_quotas = [qh['DATASTORE_QUOTA']['DATASTORE']].flatten + if !ds_quotas[0].nil? + TABLE_DS.show(ds_quotas, {}) + puts + end + + vm_quotas = [qh['VM_QUOTA']['VM']].flatten + if !vm_quotas[0].nil? + TABLE_VM.show(vm_quotas, {}) + puts + end + + net_quotas = [qh['NETWORK_QUOTA']['NETWORK']].flatten + if !net_quotas[0].nil? + TABLE_NET.show(net_quotas, {}) + puts + end + + image_quotas = [qh['IMAGE_QUOTA']['IMAGE']].flatten + if !image_quotas[0].nil? + TABLE_IMG.show(image_quotas, {}) + end + end +end diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 070ff8d5e8..0af47befa1 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -15,6 +15,7 @@ #--------------------------------------------------------------------------- # require 'one_helper' +require 'one_helper/onequota_helper' class OneUserHelper < OpenNebulaHelper::OneHelper def self.rname @@ -197,165 +198,6 @@ class OneUserHelper < OpenNebulaHelper::OneHelper table end - #--------------------------------------------------------------------------- - # Tables to format user quotas - #--------------------------------------------------------------------------- - def format_ds_quota() - table = CLIHelper::ShowTable.new(nil, self) do - column :"DATASTORE ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"IMAGES (used)", "", :right, :size=>14 do |d| - d["IMAGES_USED"] if !d.nil? - end - - column :"IMAGES (limit)", "", :right, :size=>14 do |d| - d["IMAGES"] if !d.nil? - end - - column :"SIZE (used)", "", :right, :size=>14 do |d| - d["SIZE_USED"] if !d.nil? - end - - column :"SIZE (limit)", "", :right, :size=>14 do |d| - d["SIZE"] if !d.nil? - end - end - - table - end - - def format_net_quota() - table = CLIHelper::ShowTable.new(nil, self) do - column :"NETWORK ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"LEASES (used)", "", :right, :size=>14 do |d| - d["LEASES_USED"] if !d.nil? - end - - column :"LEASES (limit)", "", :right, :size=>14 do |d| - d["LEASES"] if !d.nil? - end - end - - table - end - - def format_vm_quota() - table = CLIHelper::ShowTable.new(nil, self) do - column :"VMS", "", :left, :size=>12 do |d| - d["VMS"] if !d.nil? - end - - column :"MEMORY (used)", "", :right, :size=>14 do |d| - d["MEMORY_USED"] if !d.nil? - end - - column :"MEMORY (limit)", "", :right, :size=>14 do |d| - d["MEMORY"] if !d.nil? - end - - column :"CPU (used)", "", :right, :size=>14 do |d| - d["CPU_USED"] if !d.nil? - end - - column :"CPU (limit)", "", :right, :size=>14 do |d| - d["CPU"] if !d.nil? - end - end - - table - end - - def format_image_quota() - table = CLIHelper::ShowTable.new(nil, self) do - column :"IMAGE ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"RVMS (used)", "", :right, :size=>14 do |d| - d["INSTANCES_USED"] if !d.nil? - end - - column :"RVMS (limit)", "", :right, :size=>14 do |d| - d["INSTANCES"] if !d.nil? - end - end - - table - end - - HELP_QUOTA = <<-EOT.unindent - #----------------------------------------------------------------------- - # Supported quota limits for users - #----------------------------------------------------------------------- - # DATASTORE = [ - # ID = - # IMAGES = - # SIZE = - # ] - # - # VM = [ - # VMS = - # MEMORY = - # CPU = - # ] - # - # NETWORK = [ - # ID = - # LEASES = - # ] - # - # IMAGE = [ - # ID = - # RVMS = - # ] - # - # In any quota 0 means unlimited. The usage counters "*_USED" are - # shown for information purposes and will NOT be modified. - #----------------------------------------------------------------------- - EOT - - def self.set_quota(id, resource, path) - unless path - require 'tempfile' - - tmp = Tempfile.new(id.to_s) - path = tmp.path - - rc = resource.info - - if OpenNebula.is_error?(rc) - puts rc.message - exit -1 - end - - tmp << HELP_QUOTA - tmp << resource.template_like_str("DATASTORE_QUOTA") << "\n" - tmp << resource.template_like_str("VM_QUOTA") << "\n" - tmp << resource.template_like_str("NETWORK_QUOTA") << "\n" - tmp << resource.template_like_str("IMAGE_QUOTA") << "\n" - - tmp.flush - - editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH - system("#{editor_path} #{path}") - - unless $?.exitstatus == 0 - puts "Editor not defined" - exit -1 - end - - tmp.close - end - - str = File.read(path) - str - end - private def factory(id=nil) @@ -393,38 +235,6 @@ class OneUserHelper < OpenNebulaHelper::OneHelper user_hash = user.to_hash - puts - - CLIHelper.print_header(str_h1 % "RESOURCE USAGE & QUOTAS",false) - - puts - - ds_quotas = [user_hash['USER']['DATASTORE_QUOTA']['DATASTORE']].flatten - if !ds_quotas[0].nil? - table_ds = format_ds_quota - table_ds.show(ds_quotas, {}) - puts - end - - vm_quotas = [user_hash['USER']['VM_QUOTA']['VM']].flatten - if !vm_quotas[0].nil? - table_net = format_vm_quota - table_net.show(vm_quotas, {}) - puts - end - - net_quotas = [user_hash['USER']['NETWORK_QUOTA']['NETWORK']].flatten - if !net_quotas[0].nil? - table_net = format_net_quota - table_net.show(net_quotas, {}) - puts - end - - image_quotas = [user_hash['USER']['IMAGE_QUOTA']['IMAGE']].flatten - if !image_quotas[0].nil? - table_image = format_image_quota - table_image.show(image_quotas, {}) - end - + OneQuotaHelper.format_quota(user_hash['USER']) end end diff --git a/src/cli/oneuser b/src/cli/oneuser index 85a36c8d3d..74d1e98895 100755 --- a/src/cli/oneuser +++ b/src/cli/oneuser @@ -29,6 +29,7 @@ $: << RUBY_LIB_LOCATION+"/cli" require 'command_parser' require 'one_helper/oneuser_helper' +require 'one_helper/onequota_helper' require 'uri' @@ -205,7 +206,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper = OneUserHelper.new helper.perform_action(args[0], options, "modified") do |user| - str = OneUserHelper.set_quota(args[0], user, args[1]) + str = OneQuotaHelper.set_quota(user, args[1]) rc = user.set_quota(str) if OpenNebula.is_error?(rc) diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 8dc0679469..112fd215a2 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -738,9 +738,7 @@ int DispatchManager::finalize( if ( user != 0 ) { - user->network_quota_del(tmpl); - user->vm_quota_del(tmpl); - user->image_quota_del(tmpl); + user->quota.vm_del(tmpl); upool->update(user); diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index 50183ffeed..80f8c7061e 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -150,10 +150,8 @@ void DispatchManager::done_action(int vid) if ( user != 0 ) { - user->network_quota_del(tmpl); - user->vm_quota_del(tmpl); - user->image_quota_del(tmpl); - + user->quota.vm_del(tmpl); + upool->update(user); user->unlock(); diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 472891b4f2..d63c84e9bb 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -352,7 +352,7 @@ int ImageManager::delete_image(int iid, const string& ds_data) img_usage.add("DATASTORE", ds_id); img_usage.add("SIZE", size); - user->datastore_quota_del(&img_usage); + user->quota.ds_del(&img_usage); upool->update(user); diff --git a/src/rm/Request.cc b/src/rm/Request.cc index ab2c0efc61..2a9098dcbe 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -131,31 +131,11 @@ bool Request::quota_authorization(Template * tmpl, switch (object) { case PoolObjectSQL::IMAGE: - rc = user->datastore_quota_check(tmpl, error_str); + rc = user->quota.ds_check(tmpl, error_str); break; case PoolObjectSQL::VM: - rc = user->network_quota_check(tmpl, error_str); - - if ( rc == true ) - { - rc = user->vm_quota_check(tmpl, error_str); - - if ( rc == false ) - { - user->network_quota_del(tmpl); - } - else - { - rc = user->image_quota_check(tmpl, error_str); - - if ( rc == false ) - { - user->network_quota_del(tmpl); - user->vm_quota_del(tmpl); - } - } - } + rc = user->quota.vm_check(tmpl, error_str); break; default: @@ -202,14 +182,12 @@ void Request::quota_rollback(Template * tmpl, switch (object) { case PoolObjectSQL::IMAGE: - user->datastore_quota_del(tmpl); + user->quota.ds_del(tmpl); break; case PoolObjectSQL::VM: case PoolObjectSQL::TEMPLATE: - user->network_quota_del(tmpl); - user->vm_quota_del(tmpl); - user->image_quota_del(tmpl); + user->quota.vm_del(tmpl); break; default: diff --git a/src/rm/RequestManagerUser.cc b/src/rm/RequestManagerUser.cc index c154408780..f1e3683dc1 100644 --- a/src/rm/RequestManagerUser.cc +++ b/src/rm/RequestManagerUser.cc @@ -161,7 +161,7 @@ int UserSetQuota::user_action(int user_id, return -1; } - rc = user->set_quota("a_tmpl, error_str); + rc = user->quota.set("a_tmpl, error_str); pool->update(user); diff --git a/src/um/User.cc b/src/um/User.cc index eb5a196a17..0dfc86cc8f 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -139,10 +139,7 @@ string& User::to_xml(string& xml) const ostringstream oss; string template_xml; - string ds_quota_xml; - string net_quota_xml; - string vm_quota_xml; - string image_quota_xml; + string quota_xml; int enabled_int = enabled?1:0; @@ -156,10 +153,7 @@ string& User::to_xml(string& xml) const "" << auth_driver <<""<< "" << enabled_int <<"" << obj_template->to_xml(template_xml) << - datastore_quota.to_xml(ds_quota_xml) << - network_quota.to_xml(net_quota_xml) << - vm_quota.to_xml(vm_quota_xml) << - image_quota.to_xml(image_quota_xml) << + quota.to_xml(quota_xml) << ""; xml = oss.str(); @@ -203,48 +197,8 @@ int User::from_xml(const string& xml) rc += obj_template->from_xml_node(content[0]); ObjectXML::free_nodes(content); - content.clear(); - - // Get associated quota for the user - ObjectXML::get_nodes("/USER/DATASTORE_QUOTA", content); - - if (!content.empty()) - { - rc += datastore_quota.from_xml_node(content[0]); - } - - ObjectXML::free_nodes(content); - content.clear(); - - ObjectXML::get_nodes("/USER/NETWORK_QUOTA", content); - - if (!content.empty()) - { - rc += network_quota.from_xml_node(content[0]); - } - - ObjectXML::free_nodes(content); - content.clear(); - - ObjectXML::get_nodes("/USER/VM_QUOTA", content); - - if (!content.empty()) - { - rc += vm_quota.from_xml_node(content[0]); - } - - ObjectXML::free_nodes(content); - content.clear(); - - ObjectXML::get_nodes("/USER/IMAGE_QUOTA", content); - - if (!content.empty()) - { - rc += image_quota.from_xml_node(content[0]); - } - - ObjectXML::free_nodes(content); - content.clear(); + + rc += quota.from_xml(this); if (rc != 0) { From 981db30338f09ac406b418801ced1a5deede784e Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Fri, 8 Jun 2012 22:14:40 +0200 Subject: [PATCH 31/47] feature #1288: Support for Group quotas --- include/Group.h | 12 +- include/Request.h | 21 +++ include/RequestManagerGroup.h | 72 ++++++++++ src/cli/etc/onegroup.yaml | 16 ++- src/cli/one_helper/onegroup_helper.rb | 31 ++++- src/cli/one_helper/oneuser_helper.rb | 8 +- src/cli/onegroup | 16 +++ src/dm/DispatchManagerActions.cc | 41 ++++-- src/dm/DispatchManagerStates.cc | 39 ++++-- src/group/Group.cc | 6 + src/image/ImageManagerActions.cc | 39 ++++-- src/oca/ruby/OpenNebula/Group.rb | 22 ++- src/rm/Request.cc | 184 +++++++++++++++++++++++--- src/rm/RequestManager.cc | 5 + src/rm/RequestManagerChown.cc | 50 ++++--- src/rm/RequestManagerGroup.cc | 83 ++++++++++++ src/rm/RequestManagerUser.cc | 8 +- src/rm/SConstruct | 1 + 18 files changed, 571 insertions(+), 83 deletions(-) create mode 100644 include/RequestManagerGroup.h create mode 100644 src/rm/RequestManagerGroup.cc diff --git a/include/Group.h b/include/Group.h index ace3058d36..3a080e0566 100644 --- a/include/Group.h +++ b/include/Group.h @@ -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; diff --git a/include/Request.h b/include/Request.h index 1cc24244dc..2faeef213f 100644 --- a/include/Request.h +++ b/include/Request.h @@ -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); }; /* -------------------------------------------------------------------------- */ diff --git a/include/RequestManagerGroup.h b/include/RequestManagerGroup.h new file mode 100644 index 0000000000..968654af05 --- /dev/null +++ b/include/RequestManagerGroup.h @@ -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 diff --git a/src/cli/etc/onegroup.yaml b/src/cli/etc/onegroup.yaml index 96fc7a2e73..662d3fe297 100644 --- a/src/cli/etc/onegroup.yaml +++ b/src/cli/etc/onegroup.yaml @@ -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 diff --git a/src/cli/one_helper/onegroup_helper.rb b/src/cli/one_helper/onegroup_helper.rb index a949d29978..d2923647dd 100644 --- a/src/cli/one_helper/onegroup_helper.rb +++ b/src/cli/one_helper/onegroup_helper.rb @@ -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 diff --git a/src/cli/one_helper/oneuser_helper.rb b/src/cli/one_helper/oneuser_helper.rb index 0af47befa1..7653ec9c86 100644 --- a/src/cli/one_helper/oneuser_helper.rb +++ b/src/cli/one_helper/oneuser_helper.rb @@ -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 diff --git a/src/cli/onegroup b/src/cli/onegroup index f520300c79..ccb0a1cae7 100755 --- a/src/cli/onegroup +++ b/src/cli/onegroup @@ -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 diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 112fd215a2..df0505cc8e 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -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; diff --git a/src/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index 80f8c7061e..b2305cb026 100644 --- a/src/dm/DispatchManagerStates.cc +++ b/src/dm/DispatchManagerStates.cc @@ -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 diff --git a/src/group/Group.cc b/src/group/Group.cc index 28e1582af0..02588502b2 100644 --- a/src/group/Group.cc +++ b/src/group/Group.cc @@ -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 << "" << "" << oid << "" << "" << name << "" << collection_xml << + quota_xml << ""; 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; diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index d63c84e9bb..0258c70885 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -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; diff --git a/src/oca/ruby/OpenNebula/Group.rb b/src/oca/ruby/OpenNebula/Group.rb index 3fa5a9a41c..fcfefe8e38 100644 --- a/src/oca/ruby/OpenNebula/Group.rb +++ b/src/oca/ruby/OpenNebula/Group.rb @@ -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 # --------------------------------------------------------------------- diff --git a/src/rm/Request.cc b/src/rm/Request.cc index 2a9098dcbe..97c803211b 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -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);; + } +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index fcfe07ba47..e82b503d28 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -34,6 +34,7 @@ #include "RequestManagerUser.h" #include "RequestManagerAcl.h" #include "RequestManagerCluster.h" +#include "RequestManagerGroup.h" #include #include @@ -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); diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index fdbd2d6837..6651a3d9d7 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -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(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), diff --git a/src/rm/RequestManagerGroup.cc b/src/rm/RequestManagerGroup.cc new file mode 100644 index 0000000000..84c62612ea --- /dev/null +++ b/src/rm/RequestManagerGroup.cc @@ -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(pool->get(id,true)); + + if ( group == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),id), + att); + + return; + } + + group->quota.set("a_tmpl, error_str); + + pool->update(group); + + group->unlock(); + + if ( rc != 0 ) + { + failure_response(ACTION, request_error(error_str,""), att); + } + else + { + success_response(id, att); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerUser.cc b/src/rm/RequestManagerUser.cc index f1e3683dc1..2af6c7d61d 100644 --- a/src/rm/RequestManagerUser.cc +++ b/src/rm/RequestManagerUser.cc @@ -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 ) diff --git a/src/rm/SConstruct b/src/rm/SConstruct index 5c565c5b73..7f9b1b33e1 100644 --- a/src/rm/SConstruct +++ b/src/rm/SConstruct @@ -33,6 +33,7 @@ source_files=[ 'RequestManagerVMTemplate.cc', 'RequestManagerUpdateTemplate.cc', 'RequestManagerUser.cc', + 'RequestManagerGroup.cc', 'RequestManagerHost.cc', 'RequestManagerImage.cc', 'RequestManagerChown.cc', From e793ca89e1e880ccda20b5763073cfa22436bfc2 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sun, 10 Jun 2012 20:40:18 +0200 Subject: [PATCH 32/47] feature #1288: Remove previous quota drivers --- install.sh | 8 - share/etc/oned.conf | 8 +- src/authm_mad/remotes/quota/authorize | 69 ---- src/authm_mad/remotes/quota/onequota | 189 --------- src/authm_mad/remotes/quota/quota.conf | 29 -- src/authm_mad/remotes/quota/quota.rb | 374 ------------------ .../remotes/quota/test/fixtures/imagepool.xml | 26 -- .../remotes/quota/test/fixtures/vm.xml | 46 --- .../remotes/quota/test/fixtures/vmpool.xml | 49 --- .../remotes/quota/test/helper/mock_client.rb | 53 --- .../remotes/quota/test/helper/test_helper.rb | 31 -- .../remotes/quota/test/quota_spec.rb | 343 ---------------- src/cli/one_helper/onequota_helper.rb | 209 ---------- 13 files changed, 3 insertions(+), 1431 deletions(-) delete mode 100755 src/authm_mad/remotes/quota/authorize delete mode 100755 src/authm_mad/remotes/quota/onequota delete mode 100644 src/authm_mad/remotes/quota/quota.conf delete mode 100644 src/authm_mad/remotes/quota/quota.rb delete mode 100644 src/authm_mad/remotes/quota/test/fixtures/imagepool.xml delete mode 100644 src/authm_mad/remotes/quota/test/fixtures/vm.xml delete mode 100644 src/authm_mad/remotes/quota/test/fixtures/vmpool.xml delete mode 100644 src/authm_mad/remotes/quota/test/helper/mock_client.rb delete mode 100644 src/authm_mad/remotes/quota/test/helper/test_helper.rb delete mode 100644 src/authm_mad/remotes/quota/test/quota_spec.rb delete mode 100644 src/cli/one_helper/onequota_helper.rb diff --git a/install.sh b/install.sh index 0b4ef57645..5f4898a0ab 100755 --- a/install.sh +++ b/install.sh @@ -246,7 +246,6 @@ VAR_DIRS="$VAR_LOCATION/remotes \ $VAR_LOCATION/remotes/auth/ldap \ $VAR_LOCATION/remotes/auth/server_x509 \ $VAR_LOCATION/remotes/auth/server_cipher \ - $VAR_LOCATION/remotes/auth/quota \ $VAR_LOCATION/remotes/auth/dummy" SUNSTONE_DIRS="$SUNSTONE_LOCATION/models \ @@ -384,7 +383,6 @@ INSTALL_FILES=( AUTH_SERVER_CIPHER_FILES:$VAR_LOCATION/remotes/auth/server_cipher 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_VMWARE_SCRIPTS:$VAR_LOCATION/remotes/vmm/vmware @@ -574,7 +572,6 @@ BIN_FILES="src/nebula/oned \ src/cli/onedatastore \ src/cli/onecluster \ src/onedb/onedb \ - src/authm_mad/remotes/quota/onequota \ src/mad/utils/tty_expect \ share/scripts/one" @@ -601,7 +598,6 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \ src/mad/ruby/Ganglia.rb \ src/oca/ruby/OpenNebula.rb \ src/authm_mad/remotes/ssh/ssh_auth.rb \ - src/authm_mad/remotes/quota/quota.rb \ src/authm_mad/remotes/server_x509/server_x509_auth.rb \ src/authm_mad/remotes/server_cipher/server_cipher_auth.rb \ src/authm_mad/remotes/ldap/ldap_auth.rb \ @@ -732,8 +728,6 @@ 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" - #------------------------------------------------------------------------------- # Virtual Network Manager drivers to be installed under $REMOTES_LOCATION/vnm #------------------------------------------------------------------------------- @@ -942,7 +936,6 @@ HM_ETC_FILES="src/hm_mad/hmrc" #------------------------------------------------------------------------------- AUTH_ETC_FILES="src/authm_mad/remotes/server_x509/server_x509_auth.conf \ - src/authm_mad/remotes/quota/quota.conf \ src/authm_mad/remotes/ldap/ldap_auth.conf \ src/authm_mad/remotes/x509/x509_auth.conf" @@ -1115,7 +1108,6 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/oneimage_helper.rb \ src/cli/one_helper/onetemplate_helper.rb \ src/cli/one_helper/oneuser_helper.rb \ - src/cli/one_helper/onequota_helper.rb \ src/cli/one_helper/onevm_helper.rb \ src/cli/one_helper/onevnet_helper.rb \ src/cli/one_helper/oneacl_helper.rb \ diff --git a/share/etc/oned.conf b/share/etc/oned.conf index 2f4c5d807a..bb30e5af51 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -414,22 +414,20 @@ HM_MAD = [ # valid. During this time, the driver is not used. Use 0 to disable session # caching # -# ENABLE_OTHER_PERMISSIONS: Whether or not to enable the permissions for -# 'other'. Users in the oneadmin group will still be able to change -# these permissions. Values: YES or NO +# ENABLE_OTHER_PERMISSIONS: Whether or not users can set the permissions for +# 'other', so publishing or sharing resources with others. Users in the oneadmin +# group will still be able to change these permissions. Values: YES or NO. #******************************************************************************* AUTH_MAD = [ executable = "one_auth_mad", authn = "ssh,x509,ldap,server_cipher,server_x509" -# , authz = "quota" ] SESSION_EXPIRATION_TIME = 900 #ENABLE_OTHER_PERMISSIONS = "YES" - #******************************************************************************* # Restricted Attributes Configuration #******************************************************************************* diff --git a/src/authm_mad/remotes/quota/authorize b/src/authm_mad/remotes/quota/authorize deleted file mode 100755 index ae5916b4c0..0000000000 --- a/src/authm_mad/remotes/quota/authorize +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -# -------------------------------------------------------------------------- # -# 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. # -#--------------------------------------------------------------------------- # - -ONE_LOCATION=ENV["ONE_LOCATION"] - -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 - -$: << RUBY_LIB_LOCATION - -require 'scripts_common' -require 'OpenNebula' -require 'quota' - -user_id = ARGV.shift - -overall_evalutation = ARGV.pop -exit -1 if overall_evalutation.to_i == 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| - obj, template_or_id, op, owner, acl_eval = request.split(':') - - if ( obj == "TEMPLATE" && op == "USE" && ARGV.size == 1 ) - rc = false - else - rc = quota.authorize(user_id, request) - end - - 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 deleted file mode 100755 index 5d616f29c0..0000000000 --- a/src/authm_mad/remotes/quota/onequota +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env ruby - -# -------------------------------------------------------------------------- # -# 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. # -#--------------------------------------------------------------------------- # - -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.collect! do |elem| - sym = elem.upcase.to_sym - if !QUOTA_KEYS.include?(sym) - rc = -1, "#{elem} is not a valid quota" - break - end - sym - 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] = values[i] - } - - quota.set_quota(user_id, values_hash) - exit_with_code 0 - end - - ######################################################################## - unset_desc = <<-EOT.unindent - Unset a quota for a given user. - Examples: - onequota unset 3 cpu - onequota unset 4 cpu,memory,storage - EOT - - command :unset, unset_desc, :userid, :quota_list do - user_id, keys = args - - values_hash = Hash.new - keys.each_with_index { |k,i| - values_hash[k] = 0 - } - - 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/remotes/quota/quota.conf b/src/authm_mad/remotes/quota/quota.conf deleted file mode 100644 index 4d66cfb48b..0000000000 --- a/src/authm_mad/remotes/quota/quota.conf +++ /dev/null @@ -1,29 +0,0 @@ -# -------------------------------------------------------------------------- # -# 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. # -#--------------------------------------------------------------------------- # - -# 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 deleted file mode 100644 index e7a94b166e..0000000000 --- a/src/authm_mad/remotes/quota/quota.rb +++ /dev/null @@ -1,374 +0,0 @@ -# -------------------------------------------------------------------------- # -# 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. # -#--------------------------------------------------------------------------- # - -require 'sequel' -require 'base64' -require 'yaml' -require 'uri' -require 'net/http' - -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| - if template['TYPE'] == 'DATABLOCK' - template['SIZE'].to_i - elsif template['PATH'] - uri = URI.parse(template['PATH']) - size = if uri.scheme.nil? - File.size(template['PATH']) - else - Net::HTTP.start(uri.host,uri.port) { |http| - http.head(uri.path) - }.content_length - end - (size.to_f / 2**20).round - elsif template['SAVED_VM_ID'] - vm_id = template['SAVED_VM_ID'].to_i - disk_id = template['SAVED_DISK_ID'].to_i - - client = OpenNebula::Client.new - vm = OpenNebula::VirtualMachine.new_with_id(vm_id, client) - vm.info - - im_id = vm["DISK[DISK_ID=#{disk_id}]/IMAGE_ID"].to_i - - im = OpenNebula::Image.new_with_id(im_id, client) - im.info - - im['SIZE'].to_i - else - 0 - end - }, - :xpath => 'SIZE' - } - } - - RESOURCES = ["VM", "IMAGE"] - - ########################################################################### - # DB handling - ########################################################################### - def initialize - conf = YAML.load_file(CONF_FILE) - @conf=CONF.merge(conf) {|key,h1,h2| - if h1.instance_of?(Hash) && h2.instance_of?(Hash) - h1.merge(h2) - else - if h2 - h2 - else - h1 - end - end - } - - @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 - - # Retrieves quota information for a given user - # - # @param [Integer, nil] uid the user id from which get the quota - # information, if nil will retrieve the quotas for all users. - # @return [Hash] Hash containing the quota information and the user id - # - # { - # :uid => 4, - # :cpu => 8, - # :memory => 8064, - # :num_vms => 4, - # :storage => 1240019 - # } - 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, acl_eval = request.split(':') - - if acl_eval.to_i == 0 - return "ACL evaluation denied" - end - - # Check if this op needs to check the quota - return false unless with_quota?(obj, op) - - template = "" - - if ( obj == "TEMPLATE" ) - obj = "VM" - - vm_template = OpenNebula::Template.new_with_id(template_or_id, @client) - vm_template.info - - vm_template.each("TEMPLATE") { |xml_elem| - template = xml_elem.to_xml - } - else - template = Base64::decode64(template_or_id) - end - - 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 = "" - separator = "" - info.each { |qname, quota_requested| - unless quota[qname] || quota[qname]==0 - next - end - - type = DB_QUOTA_SCHEMA[qname].name.to_sym - - used = send(type, total[qname]) - request = send(type, quota_requested) - limit = send(type, quota[qname]) - spent = used + request - - if spent > limit - msg << separator - msg << " #{qname.to_s.upcase} quota exceeded " - msg << "(Quota: #{limit}, " - msg << "Used: #{used}, " - msg << "Requested: #{request})" - - separator = ";" - 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 == "USE") - end - - ########################################################################### - # Usage - ########################################################################### - # Retrieves usage information for a given user - # - # @param [Integer] uid the user id from which get the usage information. - # @param ["VM", "IMAGE"] resource kind of resource. If nil will return - # the usage for all kinds of resources - # @param [true, false] force If true will force the usage calculation - # instead of retrieving it from the cache - # @return [Hash] Hash containing the usage information and the user id - # - # { - # :uid => 4, - # :cpu => 8, - # :memory => 8064, - # :num_vms => 4, - # :storage => 1240019 - # } - 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| - usage[key] ||= 0 - pool.each_xpath("#{base_xpath}/#{params[:xpath]}") { |elem| - if elem - 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 deleted file mode 100644 index f71c914557..0000000000 --- a/src/authm_mad/remotes/quota/test/fixtures/imagepool.xml +++ /dev/null @@ -1,26 +0,0 @@ - - <% 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 deleted file mode 100644 index 1560a16029..0000000000 --- a/src/authm_mad/remotes/quota/test/fixtures/vm.xml +++ /dev/null @@ -1,46 +0,0 @@ - - <%= 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 deleted file mode 100644 index 1ab1fa3112..0000000000 --- a/src/authm_mad/remotes/quota/test/fixtures/vmpool.xml +++ /dev/null @@ -1,49 +0,0 @@ - - <% 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 deleted file mode 100644 index e92264dea2..0000000000 --- a/src/authm_mad/remotes/quota/test/helper/mock_client.rb +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 3f40bb8d58..0000000000 --- a/src/authm_mad/remotes/quota/test/helper/test_helper.rb +++ /dev/null @@ -1,31 +0,0 @@ - -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 deleted file mode 100644 index 560b55ad1a..0000000000 --- a/src/authm_mad/remotes/quota/test/quota_spec.rb +++ /dev/null @@ -1,343 +0,0 @@ -$: << '.' - -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 - - EOT - - vm_base64 = Base64::encode64(vm_template) - @acl_vm_create = "VM:#{vm_base64}:CREATE:0:1:1" - - # Generate IMAGE ACL request - image_template = <<-EOT - - EOT - - image_base64 = Base64::encode64(image_template) - @acl_image_create = "IMAGE:#{image_base64}:CREATE:0:1:1" - - # Generate TEMPLATE ACL request - temp_template = <<-EOT - - EOT - - temp_base64 = Base64::encode64(temp_template) - @acl_template_instantiate = "TEMPLATE:#{temp_base64}:INSTANTIATE:0:1:1" - end - - it "should check default quotas" do - quota1 = @quota.get_quota(@uid1) - quota1[:UID].should eql(0) - quota1[:NUM_VMS].should eql(nil) - quota1[:CPU].should eql(nil) - quota1[:MEMORY].should eql(nil) - quota1[:STORAGE].should eql(nil) - end - - it "should check default usage cache" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(nil) - usage1cache[:CPU].should eql(nil) - usage1cache[:MEMORY].should eql(nil) - usage1cache[:STORAGE].should eql(nil) - end - - it "should check default cache (force)" do - usage1force = @quota.get_usage(@uid1, nil, true) - usage1force[:UID].should eql(0) - usage1force[:NUM_VMS].should eql(0) - usage1force[:CPU].should eql(0) - usage1force[:MEMORY].should eql(0) - usage1force[:STORAGE].should eql(0) - end - - it "should authorize the user because there is no quota defined" do - @quota.authorize(@uid1, @acl_vm_create).should eql(false) - @quota.authorize(@uid1, @acl_image_create).should eql(false) - @quota.authorize(@uid1, @acl_template_instantiate).should eql(false) - end - - it "should add a new VM" do - values = { - :CPU => 2, - :MEMORY => 128, - :UID => 2, - :GID => 4, - } - - @mock_client.add_vm(0, values) - end - - it "should check the usage cache is not updated" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(0) - usage1cache[:CPU].should eql(0.0) - usage1cache[:MEMORY].should eql(0) - usage1cache[:STORAGE].should eql(0) - end - - it "should check the cache (force)" do - usage1force = @quota.get_usage(@uid1, nil, true) - usage1force[:UID].should eql(0) - usage1force[:NUM_VMS].should eql(1) - usage1force[:CPU].should eql(2.0) - usage1force[:MEMORY].should eql(128) - usage1force[:STORAGE].should eql(0) - end - - it "should check the usage cache is updated and contains the last usage" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1) - usage1cache[:CPU].should eql(2.0) - usage1cache[:MEMORY].should eql(128) - usage1cache[:STORAGE].should eql(0) - end - - it "should add a new Image" do - values = { - :UID => 2, - :GID => 4, - :SIZE => 1000 - } - - @mock_client.add_image(0, values) - end - - it "should check the usage cache is not updated" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1) - usage1cache[:CPU].should eql(2.0) - usage1cache[:MEMORY].should eql(128) - usage1cache[:STORAGE].should eql(0) - end - - it "should check the cache (force)" do - usage1force = @quota.get_usage(@uid1, nil, true) - usage1force[:UID].should eql(0) - usage1force[:NUM_VMS].should eql(1) - usage1force[:CPU].should eql(2.0) - usage1force[:MEMORY].should eql(128) - usage1force[:STORAGE].should eql(1000) - end - - it "should check the usage cache is updated and contains the last usage" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1) - usage1cache[:CPU].should eql(2.0) - usage1cache[:MEMORY].should eql(128) - usage1cache[:STORAGE].should eql(1000) - end - - it "should add a second VM" do - values = { - :CPU => 2, - :MEMORY => 128, - :UID => 2, - :GID => 4, - } - - @mock_client.add_vm(1, values) - end - - it "should check the usage cache is not updated" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1) - usage1cache[:CPU].should eql(2.0) - usage1cache[:MEMORY].should eql(128) - usage1cache[:STORAGE].should eql(1000) - end - - it "should check the cache (force)" do - usage1force = @quota.get_usage(@uid1, nil, true) - usage1force[:UID].should eql(0) - usage1force[:NUM_VMS].should eql(1*2) - usage1force[:CPU].should eql(2.0*2) - usage1force[:MEMORY].should eql(128*2) - usage1force[:STORAGE].should eql(1000) - end - - it "should check the usage cache is updated and contains the last usage" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1*2) - usage1cache[:CPU].should eql(2.0*2) - usage1cache[:MEMORY].should eql(128*2) - usage1cache[:STORAGE].should eql(1000) - end - - it "should add a second Image" do - values = { - :UID => 2, - :GID => 4, - :SIZE => 1000 - } - - @mock_client.add_image(1, values) - end - - it "should check the usage cache is not updated" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1*2) - usage1cache[:CPU].should eql(2.0*2) - usage1cache[:MEMORY].should eql(128*2) - usage1cache[:STORAGE].should eql(1000) - end - - it "should check the cache (force)" do - usage1force = @quota.get_usage(@uid1, nil, true) - usage1force[:UID].should eql(0) - usage1force[:NUM_VMS].should eql(1*2) - usage1force[:CPU].should eql(2.0*2) - usage1force[:MEMORY].should eql(128*2) - usage1force[:STORAGE].should eql(1000*2) - end - - it "should check the usage cache is updated and contains the last usage" do - usage1cache = @quota.get_usage(@uid1) - usage1cache[:UID].should eql(0) - usage1cache[:NUM_VMS].should eql(1*2) - usage1cache[:CPU].should eql(2.0*2) - usage1cache[:MEMORY].should eql(128*2) - usage1cache[:STORAGE].should eql(1000*2) - end - - it "should add a new quota and check it" do - @quota.set_quota(@uid1, @quota1) - - quota = @quota.get_quota(@uid1) - @quota1.each{ |key,value| - quota[key].should eql(value) - } - end - - it "should not authorize the user because the vm quota is spent" do - err_msg = "CPU quota exceeded (Quota: 2.4, Used: 4.0, Requested: 2.0)" - @quota.authorize(@uid1, @acl_vm_create).should eql(err_msg) - @quota.authorize(@uid1, @acl_template_instantiate).should eql(err_msg) - - @quota.authorize(@uid1, @acl_image_create).should eql(false) - end - - it "should add a new quota for another user and check it" do - @quota.set_quota(@uid2, @quota2) - - quota = @quota.get_quota(@uid2) - @quota2.each{ |key,value| - quota[key].should eql(value) - } - end - - it "should list all the defined quotas" do - quotas = @quota.get_quota - quotas.each { |quota| - if quota[:UID] == @uid1 - @quota1.each{ |key,value| - quota[key].should eql(value) - } - elsif quota[:UID] == @uid2 - @quota2.each{ |key,value| - quota[key].should eql(value) - } - end - } - - end - - it "should update the first user quota and check it" do - new_quota = Hash.new - @quota1.each { |key,value| - new_quota[key] = value*3 - } - - @quota.set_quota(@uid1, new_quota) - - quota = @quota.get_quota(@uid1) - new_quota.each{ |key,value| - quota[key] == value - } - end - - it "should authorize the user because the quota is not spent" do - @quota.authorize(@uid1, @acl_vm_create).should eql(false) - @quota.authorize(@uid1, @acl_image_create).should eql(false) - @quota.authorize(@uid1, @acl_template_instantiate).should eql(false) - end - - it "should update the first user quota and check it" do - new_quota = { - :STORAGE => 0 - } - - @quota.set_quota(@uid1, new_quota) - - quota = @quota.get_quota(@uid1) - - quota[:STORAGE].should eql(new_quota[:STORAGE]) - end - - it "should not authorize the user because the image quota is spent" do - @quota.authorize(@uid1, @acl_vm_create).should eql(false) - @quota.authorize(@uid1, @acl_template_instantiate).should eql(false) - - err_msg = "STORAGE quota exceeded (Quota: 0, Used: 2000, Requested: 271)" - @quota.authorize(@uid1, @acl_image_create).should eql(err_msg) - end - - it "should delete the quota and check it" do - @quota.delete_quota(@uid1) - - quota1 = @quota.get_quota(@uid1) - quota1[:UID].should eql(0) - quota1[:NUM_VMS].should eql(nil) - quota1[:CPU].should eql(nil) - quota1[:MEMORY].should eql(nil) - quota1[:STORAGE].should eql(nil) - end - - it "should authorize the user because the quota was deleted" do - @quota.authorize(@uid1, @acl_vm_create).should eql(false) - @quota.authorize(@uid1, @acl_image_create).should eql(false) - @quota.authorize(@uid1, @acl_template_instantiate).should eql(false) - end -end \ No newline at end of file diff --git a/src/cli/one_helper/onequota_helper.rb b/src/cli/one_helper/onequota_helper.rb deleted file mode 100644 index 3e2e0a57ed..0000000000 --- a/src/cli/one_helper/onequota_helper.rb +++ /dev/null @@ -1,209 +0,0 @@ -# -------------------------------------------------------------------------- # -# 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. # -#--------------------------------------------------------------------------- # - -require 'cli_helper' - -class OneQuotaHelper - - #--------------------------------------------------------------------------- - # Tables to format user quotas - #--------------------------------------------------------------------------- - TABLE_DS = CLIHelper::ShowTable.new(nil, self) do - column :"DATASTORE ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"IMAGES (used)", "", :right, :size=>14 do |d| - d["IMAGES_USED"] if !d.nil? - end - - column :"IMAGES (limit)", "", :right, :size=>14 do |d| - d["IMAGES"] if !d.nil? - end - - column :"SIZE (used)", "", :right, :size=>14 do |d| - d["SIZE_USED"] if !d.nil? - end - - column :"SIZE (limit)", "", :right, :size=>14 do |d| - d["SIZE"] if !d.nil? - end - end - - TABLE_NET = CLIHelper::ShowTable.new(nil, self) do - column :"NETWORK ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"LEASES (used)", "", :right, :size=>14 do |d| - d["LEASES_USED"] if !d.nil? - end - - column :"LEASES (limit)", "", :right, :size=>14 do |d| - d["LEASES"] if !d.nil? - end - end - - TABLE_VM = CLIHelper::ShowTable.new(nil, self) do - column :"VMS", "", :left, :size=>12 do |d| - d["VMS"] if !d.nil? - end - - column :"MEMORY (used)", "", :right, :size=>14 do |d| - d["MEMORY_USED"] if !d.nil? - end - - column :"MEMORY (limit)", "", :right, :size=>14 do |d| - d["MEMORY"] if !d.nil? - end - - column :"CPU (used)", "", :right, :size=>14 do |d| - d["CPU_USED"] if !d.nil? - end - - column :"CPU (limit)", "", :right, :size=>14 do |d| - d["CPU"] if !d.nil? - end - end - - TABLE_IMG = CLIHelper::ShowTable.new(nil, self) do - column :"IMAGE ID", "", :left, :size=>12 do |d| - d["ID"] if !d.nil? - end - - column :"RVMS (used)", "", :right, :size=>14 do |d| - d["RVMS_USED"] if !d.nil? - end - - column :"RVMS (limit)", "", :right, :size=>14 do |d| - d["RVMS"] if !d.nil? - end - end - - HELP_QUOTA = <<-EOT.unindent - #----------------------------------------------------------------------- - # Supported quota limits: - # - # DATASTORE = [ - # ID = - # IMAGES = - # SIZE = - # ] - # - # VM = [ - # VMS = - # MEMORY = - # CPU = - # ] - # - # NETWORK = [ - # ID = - # LEASES = - # ] - # - # IMAGE = [ - # ID = - # RVMS = - # ] - # - # In any quota 0 means unlimited. The usage counters "*_USED" are - # shown for information purposes and will NOT be modified. - #----------------------------------------------------------------------- - EOT - - # Edits the quota template of a resource - # @param resource [PoolElement] to get the current info from - # @param path [String] path to the new contents. If nil a editor will be - # used - # @return [String] contents of the new quotas - def self.set_quota(resource, path) - str = "" - - if path.nil? - require 'tempfile' - - tmp = Tempfile.new('one-cli') - path = tmp.path - - rc = resource.info - - if OpenNebula.is_error?(rc) - puts rc.message - exit -1 - end - - tmp << HELP_QUOTA - tmp << resource.template_like_str("DATASTORE_QUOTA") << "\n" - tmp << resource.template_like_str("VM_QUOTA") << "\n" - tmp << resource.template_like_str("NETWORK_QUOTA") << "\n" - tmp << resource.template_like_str("IMAGE_QUOTA") << "\n" - - tmp.close - - editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH - system("#{editor_path} #{path}") - - unless $?.exitstatus == 0 - puts "Editor not defined" - exit -1 - end - - str = File.read(path) - - File.unlink(path) - else - str = File.read(path) - end - - str - end - - # Outputs formated quota information to stdout - # @param qh [Hash] with the quotas for a given resource - # - def self.format_quota(qh) - str_h1="%-80s" - - puts - - CLIHelper.print_header(str_h1 % "RESOURCE USAGE & QUOTAS",false) - - puts - - ds_quotas = [qh['DATASTORE_QUOTA']['DATASTORE']].flatten - if !ds_quotas[0].nil? - TABLE_DS.show(ds_quotas, {}) - puts - end - - vm_quotas = [qh['VM_QUOTA']['VM']].flatten - if !vm_quotas[0].nil? - TABLE_VM.show(vm_quotas, {}) - puts - end - - net_quotas = [qh['NETWORK_QUOTA']['NETWORK']].flatten - if !net_quotas[0].nil? - TABLE_NET.show(net_quotas, {}) - puts - end - - image_quotas = [qh['IMAGE_QUOTA']['IMAGE']].flatten - if !image_quotas[0].nil? - TABLE_IMG.show(image_quotas, {}) - end - end -end From 32f72f56e768aa218688377c935525b37420a320 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Sun, 10 Jun 2012 22:14:52 +0200 Subject: [PATCH 33/47] feature #1288: XSD schemas for user/group quotas --- share/doc/xsd/group.xsd | 57 +++++++++++++++++++++++++++++++++++++++++ share/doc/xsd/user.xsd | 57 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/share/doc/xsd/group.xsd b/share/doc/xsd/group.xsd index eb36432fff..81772e34c7 100644 --- a/share/doc/xsd/group.xsd +++ b/share/doc/xsd/group.xsd @@ -13,6 +13,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/doc/xsd/user.xsd b/share/doc/xsd/user.xsd index 5add6f1685..bf9c88b1cd 100644 --- a/share/doc/xsd/user.xsd +++ b/share/doc/xsd/user.xsd @@ -12,6 +12,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 0875bef8338a7deaa098a78c6bd3eb2fc56628ae Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 11 Jun 2012 12:16:14 +0200 Subject: [PATCH 34/47] feature #1288 #1284: Memory and CPU are now mandatory for VMs. Default values have been removed from the driver configuration files --- src/vm/VirtualMachine.cc | 33 +++++++++++++++++++++++---- src/vmm/LibVirtDriverKVM.cc | 5 ---- src/vmm/LibVirtDriverVMware.cc | 5 ---- src/vmm/XenDriver.cc | 5 ---- src/vmm_mad/exec/vmm_exec_kvm.conf | 3 --- src/vmm_mad/exec/vmm_exec_vmware.conf | 7 +++--- src/vmm_mad/exec/vmm_exec_xen.conf | 7 +++--- 7 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index f3db0feb78..b384a27adc 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -203,8 +203,8 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) int rc; string name; - string value; - ostringstream oss; + string value; + ostringstream oss; // ------------------------------------------------------------------------ // Set a name if the VM has not got one and VM_ID @@ -232,6 +232,24 @@ int VirtualMachine::insert(SqlDB * db, string& error_str) this->name = name; + // ------------------------------------------------------------------------ + // Check for CPU and MEMORY attributes + // ------------------------------------------------------------------------ + + get_template_attribute("MEMORY", value); + + if ( value.empty()) + { + goto error_no_memory; + } + + get_template_attribute("CPU", value); + + if ( value.empty()) + { + goto error_no_cpu; + } + // ------------------------------------------------------------------------ // Get network leases // ------------------------------------------------------------------------ @@ -312,9 +330,16 @@ error_leases_rollback: release_network_leases(); goto error_common; +error_no_cpu: + error_str = "CPU attribute missing."; + goto error_common; + +error_no_memory: + error_str = "MEMORY attribute missing."; + goto error_common; + error_name_length: - oss << "NAME is too long; max length is 128 chars."; - error_str = oss.str(); + error_str = "NAME is too long; max length is 128 chars."; goto error_common; error_common: diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index 4952f733f6..b307f96103 100644 --- a/src/vmm/LibVirtDriverKVM.cc +++ b/src/vmm/LibVirtDriverKVM.cc @@ -131,11 +131,6 @@ int LibVirtDriver::deployment_description_kvm( vm->get_template_attribute("MEMORY",memory); - if (memory.empty()) - { - get_default("MEMORY",memory); - } - if (!memory.empty()) { memory_in_kb = atoi(memory.c_str()) * 1024; diff --git a/src/vmm/LibVirtDriverVMware.cc b/src/vmm/LibVirtDriverVMware.cc index 5161011caf..05b4cff863 100644 --- a/src/vmm/LibVirtDriverVMware.cc +++ b/src/vmm/LibVirtDriverVMware.cc @@ -119,11 +119,6 @@ int LibVirtDriver::deployment_description_vmware( vm->get_template_attribute("MEMORY",memory); - if (memory.empty()) - { - get_default("MEMORY",memory); - } - if (!memory.empty()) { memory_in_kb = atoi(memory.c_str()) * 1024; diff --git a/src/vmm/XenDriver.cc b/src/vmm/XenDriver.cc index 0add29a1d9..7ffa4fa944 100644 --- a/src/vmm/XenDriver.cc +++ b/src/vmm/XenDriver.cc @@ -109,11 +109,6 @@ int XenDriver::deployment_description( vm->get_template_attribute("MEMORY",memory); - if (memory.empty()) - { - get_default("MEMORY",memory); - } - if (!memory.empty()) { file << "memory = '" << memory << "'" << endl; diff --git a/src/vmm_mad/exec/vmm_exec_kvm.conf b/src/vmm_mad/exec/vmm_exec_kvm.conf index e3984f7971..5f3ef0d752 100644 --- a/src/vmm_mad/exec/vmm_exec_kvm.conf +++ b/src/vmm_mad/exec/vmm_exec_kvm.conf @@ -19,8 +19,6 @@ # be overridden in each VM template. Valid atributes are: # - emulator # - os [kernel,initrd,boot,root,kernel_cmd,arch] -# - memory -# - cpu # - vcpu # - features [acpi, pae] # - disk [driver, cache] @@ -29,7 +27,6 @@ #EMULATOR = /usr/libexec/qemu-kvm #VCPU = 1 -#MEMORY = 128 OS = [ boot = "hd", arch = "i686" ] FEATURES = [ PAE = "no", ACPI = "yes" ] diff --git a/src/vmm_mad/exec/vmm_exec_vmware.conf b/src/vmm_mad/exec/vmm_exec_vmware.conf index 5d15d9d114..f0e3e1040e 100644 --- a/src/vmm_mad/exec/vmm_exec_vmware.conf +++ b/src/vmm_mad/exec/vmm_exec_vmware.conf @@ -17,14 +17,13 @@ # Default configuration attributes for the VMware driver # (all domains will use these values as defaults) # Valid atributes: -# - memory -# - cpu +# - vcpu # - os[arch] # - disk[dirver] # - datastore -CPU = 1 -MEMORY = 256 +#VCPU = 1 + OS = [ ARCH = i686 ] DISK = [ DRIVER = file ] diff --git a/src/vmm_mad/exec/vmm_exec_xen.conf b/src/vmm_mad/exec/vmm_exec_xen.conf index d818abbe23..3d4ac94fba 100644 --- a/src/vmm_mad/exec/vmm_exec_xen.conf +++ b/src/vmm_mad/exec/vmm_exec_xen.conf @@ -19,12 +19,11 @@ # be overridden in each VM template. Valid atributes are: # - credit # - os [kernel,initrd,root,kernel_cmd] -# - memory # - vcpu # - disk[driver] -#VCPU = 1 -#MEMORY = 128 -#OS = [ kernel="/vmlinuz", initrd="/initrd.img", root="sda1", kernel_cmd="ro" ] +#VCPU = 1 +#OS = [ kernel="/vmlinuz", initrd="/initrd.img", root="sda1", kernel_cmd="ro" ] + CREDIT = 256 DISK = [ driver = "tap:aio:" ] From e80c6a1e23b8be413c7d7a8c2a7b2ab07759fdfe Mon Sep 17 00:00:00 2001 From: Tino Vazquez Date: Mon, 11 Jun 2012 14:39:28 +0200 Subject: [PATCH 35/47] Changed migrator to accept getopts --- src/onedb/onezonedb/onezonedb | 79 ++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/src/onedb/onezonedb/onezonedb b/src/onedb/onezonedb/onezonedb index 1e316e4f48..bfa55ff7a4 100755 --- a/src/onedb/onezonedb/onezonedb +++ b/src/onedb/onezonedb/onezonedb @@ -1,9 +1,52 @@ #!/bin/bash -if [ "$1" != "sqlite" -a "$1" != "mysql" -a "$1" != "postgres" ]; then - echo "$0: The first command needs to be one of {sqlite, mysql}" - exit 1 -fi + +usage() +{ +cat << EOF +usage: $0 options + +This script upgrades oZones DB from 3.4 to 3.6 + +OPTIONS: + -h Show this message + -t DB type, can be "mysql" or "sqlite". Compulsory + -s Server address (for mysql only, "localhost" if not defined) + -u User name (for mysql only, compulsory) + -p User password (for mysql only, compulsory) + -l DB path (for sqlite only, compulsory) +EOF +} + + +while getopts “t:u:p:s:l:h” OPTION +do + case $OPTION in + h) + usage + exit 1 + ;; + t) + TYPE=$OPTARG + ;; + s) + SERVER=$OPTARG + ;; + u) + USERNAME=$OPTARG + ;; + p) + PASSWD=$OPTARG + ;; + l) + DB_PATH=$OPTARG + ;; + ?) + usage + exit + ;; + esac +done MIGRATE_CMDS=$(cat <" - echo " where is optional and defaults to localhost" +if [ $TYPE == "mysql" ]; then + if [ -z $USERNAME -a -z $PASSWD ]; then + usage exit 1 fi - if [ -z $4 ]; then - MYSQL_SERVER="localhost" - else - MYSQL_SERVER=$4 + if [ -z $SERVER ]; then + SERVER="localhost" fi - mysql -u $2 --password=$3 -h $MYSQL_SERVER ozones < <(echo $MIGRATE_CMDS) + mysql -u $USERNAME --password=$PASSWD -h $SERVER ozones < <(echo $MIGRATE_CMDS) + RC=$? fi -if [ $? -ne 0 ]; then +if [ $RC -ne 0 ]; then echo "There was an error during migration" else echo "Migration successful" From 39d1a4aa66876de23d8ac3d9017fe3e4e6a90a11 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 11 Jun 2012 17:24:46 +0200 Subject: [PATCH 36/47] feature #1288: restore needed file --- src/cli/one_helper/onequota_helper.rb | 209 ++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/cli/one_helper/onequota_helper.rb diff --git a/src/cli/one_helper/onequota_helper.rb b/src/cli/one_helper/onequota_helper.rb new file mode 100644 index 0000000000..3e2e0a57ed --- /dev/null +++ b/src/cli/one_helper/onequota_helper.rb @@ -0,0 +1,209 @@ +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + +require 'cli_helper' + +class OneQuotaHelper + + #--------------------------------------------------------------------------- + # Tables to format user quotas + #--------------------------------------------------------------------------- + TABLE_DS = CLIHelper::ShowTable.new(nil, self) do + column :"DATASTORE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"IMAGES (used)", "", :right, :size=>14 do |d| + d["IMAGES_USED"] if !d.nil? + end + + column :"IMAGES (limit)", "", :right, :size=>14 do |d| + d["IMAGES"] if !d.nil? + end + + column :"SIZE (used)", "", :right, :size=>14 do |d| + d["SIZE_USED"] if !d.nil? + end + + column :"SIZE (limit)", "", :right, :size=>14 do |d| + d["SIZE"] if !d.nil? + end + end + + TABLE_NET = CLIHelper::ShowTable.new(nil, self) do + column :"NETWORK ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"LEASES (used)", "", :right, :size=>14 do |d| + d["LEASES_USED"] if !d.nil? + end + + column :"LEASES (limit)", "", :right, :size=>14 do |d| + d["LEASES"] if !d.nil? + end + end + + TABLE_VM = CLIHelper::ShowTable.new(nil, self) do + column :"VMS", "", :left, :size=>12 do |d| + d["VMS"] if !d.nil? + end + + column :"MEMORY (used)", "", :right, :size=>14 do |d| + d["MEMORY_USED"] if !d.nil? + end + + column :"MEMORY (limit)", "", :right, :size=>14 do |d| + d["MEMORY"] if !d.nil? + end + + column :"CPU (used)", "", :right, :size=>14 do |d| + d["CPU_USED"] if !d.nil? + end + + column :"CPU (limit)", "", :right, :size=>14 do |d| + d["CPU"] if !d.nil? + end + end + + TABLE_IMG = CLIHelper::ShowTable.new(nil, self) do + column :"IMAGE ID", "", :left, :size=>12 do |d| + d["ID"] if !d.nil? + end + + column :"RVMS (used)", "", :right, :size=>14 do |d| + d["RVMS_USED"] if !d.nil? + end + + column :"RVMS (limit)", "", :right, :size=>14 do |d| + d["RVMS"] if !d.nil? + end + end + + HELP_QUOTA = <<-EOT.unindent + #----------------------------------------------------------------------- + # Supported quota limits: + # + # DATASTORE = [ + # ID = + # IMAGES = + # SIZE = + # ] + # + # VM = [ + # VMS = + # MEMORY = + # CPU = + # ] + # + # NETWORK = [ + # ID = + # LEASES = + # ] + # + # IMAGE = [ + # ID = + # RVMS = + # ] + # + # In any quota 0 means unlimited. The usage counters "*_USED" are + # shown for information purposes and will NOT be modified. + #----------------------------------------------------------------------- + EOT + + # Edits the quota template of a resource + # @param resource [PoolElement] to get the current info from + # @param path [String] path to the new contents. If nil a editor will be + # used + # @return [String] contents of the new quotas + def self.set_quota(resource, path) + str = "" + + if path.nil? + require 'tempfile' + + tmp = Tempfile.new('one-cli') + path = tmp.path + + rc = resource.info + + if OpenNebula.is_error?(rc) + puts rc.message + exit -1 + end + + tmp << HELP_QUOTA + tmp << resource.template_like_str("DATASTORE_QUOTA") << "\n" + tmp << resource.template_like_str("VM_QUOTA") << "\n" + tmp << resource.template_like_str("NETWORK_QUOTA") << "\n" + tmp << resource.template_like_str("IMAGE_QUOTA") << "\n" + + tmp.close + + editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH + system("#{editor_path} #{path}") + + unless $?.exitstatus == 0 + puts "Editor not defined" + exit -1 + end + + str = File.read(path) + + File.unlink(path) + else + str = File.read(path) + end + + str + end + + # Outputs formated quota information to stdout + # @param qh [Hash] with the quotas for a given resource + # + def self.format_quota(qh) + str_h1="%-80s" + + puts + + CLIHelper.print_header(str_h1 % "RESOURCE USAGE & QUOTAS",false) + + puts + + ds_quotas = [qh['DATASTORE_QUOTA']['DATASTORE']].flatten + if !ds_quotas[0].nil? + TABLE_DS.show(ds_quotas, {}) + puts + end + + vm_quotas = [qh['VM_QUOTA']['VM']].flatten + if !vm_quotas[0].nil? + TABLE_VM.show(vm_quotas, {}) + puts + end + + net_quotas = [qh['NETWORK_QUOTA']['NETWORK']].flatten + if !net_quotas[0].nil? + TABLE_NET.show(net_quotas, {}) + puts + end + + image_quotas = [qh['IMAGE_QUOTA']['IMAGE']].flatten + if !image_quotas[0].nil? + TABLE_IMG.show(image_quotas, {}) + end + end +end From 62b03af3f1641e7fe195cfd24e858711ecce8917 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 11 Jun 2012 17:26:01 +0200 Subject: [PATCH 37/47] feature #1288: Install missing quota file --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index 5f4898a0ab..6ea95ff1f9 100755 --- a/install.sh +++ b/install.sh @@ -1107,6 +1107,7 @@ ONE_CLI_LIB_FILES="src/cli/one_helper/onegroup_helper.rb \ src/cli/one_helper/onehost_helper.rb \ src/cli/one_helper/oneimage_helper.rb \ src/cli/one_helper/onetemplate_helper.rb \ + src/cli/one_helper/onequota_helper.rb \ src/cli/one_helper/oneuser_helper.rb \ src/cli/one_helper/onevm_helper.rb \ src/cli/one_helper/onevnet_helper.rb \ From 7a9d749f0f1732520692ec7801e9db1b349321c7 Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Mon, 11 Jun 2012 23:04:29 +0200 Subject: [PATCH 38/47] feature #1288: Moved stat script to common src dir --- src/datastore_mad/remotes/{ => common}/stat | 0 src/datastore_mad/remotes/fs/stat | 2 +- src/datastore_mad/remotes/iscsi/stat | 2 +- src/datastore_mad/remotes/lvm/stat | 2 +- src/datastore_mad/remotes/vmware/stat | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/datastore_mad/remotes/{ => common}/stat (100%) diff --git a/src/datastore_mad/remotes/stat b/src/datastore_mad/remotes/common/stat similarity index 100% rename from src/datastore_mad/remotes/stat rename to src/datastore_mad/remotes/common/stat diff --git a/src/datastore_mad/remotes/fs/stat b/src/datastore_mad/remotes/fs/stat index 10665a59d6..65583e827b 120000 --- a/src/datastore_mad/remotes/fs/stat +++ b/src/datastore_mad/remotes/fs/stat @@ -1 +1 @@ -../stat \ No newline at end of file +../common/stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/iscsi/stat b/src/datastore_mad/remotes/iscsi/stat index 10665a59d6..65583e827b 120000 --- a/src/datastore_mad/remotes/iscsi/stat +++ b/src/datastore_mad/remotes/iscsi/stat @@ -1 +1 @@ -../stat \ No newline at end of file +../common/stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/lvm/stat b/src/datastore_mad/remotes/lvm/stat index 10665a59d6..65583e827b 120000 --- a/src/datastore_mad/remotes/lvm/stat +++ b/src/datastore_mad/remotes/lvm/stat @@ -1 +1 @@ -../stat \ No newline at end of file +../common/stat \ No newline at end of file diff --git a/src/datastore_mad/remotes/vmware/stat b/src/datastore_mad/remotes/vmware/stat index 10665a59d6..65583e827b 120000 --- a/src/datastore_mad/remotes/vmware/stat +++ b/src/datastore_mad/remotes/vmware/stat @@ -1 +1 @@ -../stat \ No newline at end of file +../common/stat \ No newline at end of file From 6063129165d3e1459c9e1e781b6500f6ef00a80d Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Wed, 13 Jun 2012 13:10:55 +0200 Subject: [PATCH 39/47] feature #1223: Prepare driver to read attach parameters from XML driver action --- src/vmm_mad/exec/one_vmm_exec.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vmm_mad/exec/one_vmm_exec.rb b/src/vmm_mad/exec/one_vmm_exec.rb index 50b34a4543..2acc36e948 100755 --- a/src/vmm_mad/exec/one_vmm_exec.rb +++ b/src/vmm_mad/exec/one_vmm_exec.rb @@ -72,7 +72,7 @@ class VmmAction get_data(:dest_driver, :MIGR_NET_DRV) # For disk hotplugging - get_data(:target) + get_data(:disk_target_path) get_data(:tm_command) # Initialize streams and vnm @@ -222,7 +222,9 @@ class VmmAction path=name.to_s.upcase end - @data[name]=@xml_data.elements[path].text + if (elem = @xml_data.elements[path]) + @data[name]=elem.text + end end end @@ -503,6 +505,11 @@ class ExecDriver < VirtualMachineDriver # ATTACHDISK action, attaches a disk to a running VM # def attach_disk(id, drv_message) + xml_data = decode(drv_message) + disk_id = xml_data.elements['DISK_ID'].text + disk = xml_data.elements["VM/TEMPLATE/DISK[DISK_ID='#{disk_id}']"] + target = disk.elements['TARGET'].text + action = VmmAction.new(self, id, :attach_disk, drv_message) steps = [ @@ -514,7 +521,7 @@ class ExecDriver < VirtualMachineDriver { :driver => :vmm, :action => :attach_disk, - :parameters => [:deploy_id, :source, :target] + :parameters => [:deploy_id, :disk_target_path, target] } ] @@ -525,14 +532,19 @@ class ExecDriver < VirtualMachineDriver # DETACHDISK action, attaches a disk to a running VM # def detach_disk(id, drv_message) - action = VmmAction.new(self, id, :attach_disk, drv_message) + xml_data = decode(drv_message) + disk_id = xml_data.elements['DISK_ID'].text + disk = xml_data.elements["VM/TEMPLATE/DISK[DISK_ID='#{disk_id}']"] + target = disk.elements['TARGET'].text + + action = VmmAction.new(self, id, :detach_disk, drv_message) steps = [ # Run the detach vmm script { :driver => :vmm, :action => :attach_disk, - :parameters => [:deploy_id, :target] + :parameters => [:deploy_id, target] }, # Perform a PROLOG on the disk { From 73b6457d0e3c18c30b6c9d1088631cbc7888b2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Wed, 13 Jun 2012 18:19:22 +0200 Subject: [PATCH 40/47] Feature #1223: Refactor TransferManager::prolog_action to create the prolog commands in a separate method --- include/TransferManager.h | 22 ++++ src/tm/TransferManager.cc | 258 ++++++++++++++++++++++---------------- 2 files changed, 173 insertions(+), 107 deletions(-) diff --git a/include/TransferManager.h b/include/TransferManager.h index 6977ad76fb..0aacfaeb30 100644 --- a/include/TransferManager.h +++ b/include/TransferManager.h @@ -94,6 +94,28 @@ public: return tm_thread; }; + /** + * Inserts a transfer command in the xfs stream + * + * @param vm The VM + * @param disk Disk to transfer + * @param disk_index Disk index + * @param system_tm_mad The Transfer Manager for the system datastore + * @param opennebula_hostname The front-end hostname + * @param xfr Stream where the transfer command will be written + * @param error_str Error reason, if any + * + * @return 0 on success + */ + int prolog_transfer_command( + VirtualMachine * vm, + const VectorAttribute * disk, + int disk_index, + string& system_tm_mad, + string& opennebula_hostname, + ostream& xfr, + string& error_str); + private: /** * Thread id for the Transfer Manager diff --git a/src/tm/TransferManager.cc b/src/tm/TransferManager.cc index 9e8448d980..62c69fcc1d 100644 --- a/src/tm/TransferManager.cc +++ b/src/tm/TransferManager.cc @@ -201,6 +201,143 @@ void TransferManager::do_action(const string &action, void * arg) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int TransferManager::prolog_transfer_command( + VirtualMachine * vm, + const VectorAttribute * disk, + int disk_index, + string& system_tm_mad, + string& opennebula_hostname, + ostream& xfr, + string& error_str) +{ + string source; + string type; + string clon; + string size; + string format; + string tm_mad; + string ds_id; + + ostringstream os; + + type = disk->vector_value("TYPE"); + + transform(type.begin(),type.end(),type.begin(),(int(*)(int))toupper); + + if ( type == "SWAP" ) + { + // ----------------------------------------------------------------- + // Generate a swap disk image + // ----------------------------------------------------------------- + size = disk->vector_value("SIZE"); + + if ( size.empty() ) + { + vm->log("TM",Log::WARNING,"No size in swap image, skipping"); + goto skip; + } + + //MKSWAP tm_mad size host:remote_system_dir/disk.i vmid dsid(=0) + xfr << "MKSWAP " + << system_tm_mad << " " + << size << " " + << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_index << " " + << vm->get_oid() << " " + << "0"; + } + else if ( type == "FS" ) + { + // ----------------------------------------------------------------- + // Create a clean file system disk image + // ----------------------------------------------------------------- + size = disk->vector_value("SIZE"); + format = disk->vector_value("FORMAT"); + + if ( size.empty() || format.empty() ) + { + vm->log("TM",Log::WARNING, "No size or format in FS, skipping"); + goto skip; + } + + //MKIMAGE tm_mad size format host:remote_system_dir/disk.i vmid dsid(=0) + xfr << "MKIMAGE " + << system_tm_mad << " " + << size << " " + << format << " " + << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_index << " " + << vm->get_oid() << " " + << "0"; + } + else + { + // ----------------------------------------------------------------- + // Get transfer attributes & check errors + // ----------------------------------------------------------------- + tm_mad = disk->vector_value("TM_MAD"); + ds_id = disk->vector_value("DATASTORE_ID"); + source = disk->vector_value("SOURCE"); + clon = disk->vector_value("CLONE"); + + if ( source.empty() || + tm_mad.empty() || + ds_id.empty() || + clon.empty() ) + { + goto error_attributes; + } + + transform(clon.begin(),clon.end(),clon.begin(),(int(*)(int))toupper); + + // ----------------------------------------------------------------- + // CLONE or LINK disk images + // ----------------------------------------------------------------- + + // tm_mad fe:SOURCE host:remote_system_ds/disk.i vmid dsid + if (clon == "YES") + { + xfr << "CLONE "; + } + else + { + xfr << "LN "; + } + + xfr << tm_mad << " "; + + if ( source.find(":") == string::npos ) //Regular file + { + xfr << opennebula_hostname << ":" << source << " "; + } + else //TM Plugin specific protocol + { + xfr << source << " "; + } + + xfr << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_index << " " + << vm->get_oid() << " " + << ds_id; + } + + return 0; + +error_attributes: + os << "prolog, missing DISK mandatory attributes " + << "(SOURCE, TM_MAD, CLONE, DATASTORE_ID) for VM " << vm->get_oid() + << ", DISK " << disk->vector_value("DISK_ID"); + + error_str = os.str(); + return -1; + +skip: + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void TransferManager::prolog_action(int vid) { ofstream xfr; @@ -208,14 +345,12 @@ void TransferManager::prolog_action(int vid) string xfr_name; const VectorAttribute * disk; - string source; - string type; - string clon; - string files; - string size; - string format; - string tm_mad, system_tm_mad; - string ds_id; + + string files; + string system_tm_mad; + string opennebula_hostname; + int rc; + string error_str; VirtualMachine * vm; Nebula& nd = Nebula::instance(); @@ -261,6 +396,8 @@ void TransferManager::prolog_action(int vid) goto error_file; } + opennebula_hostname = nd.get_nebula_hostname(); + // ------------------------------------------------------------------------- // Image Transfer Commands // ------------------------------------------------------------------------- @@ -275,106 +412,15 @@ void TransferManager::prolog_action(int vid) continue; } - type = disk->vector_value("TYPE"); + rc = prolog_transfer_command(vm, disk, i, system_tm_mad, + opennebula_hostname, xfr, error_str); - transform(type.begin(),type.end(),type.begin(),(int(*)(int))toupper); - - if ( type == "SWAP" ) + if ( rc != 0 ) { - // ----------------------------------------------------------------- - // Generate a swap disk image - // ----------------------------------------------------------------- - size = disk->vector_value("SIZE"); - - if ( size.empty() ) - { - vm->log("TM",Log::WARNING,"No size in swap image, skipping"); - continue; - } - - //MKSWAP tm_mad size host:remote_system_dir/disk.i vmid dsid(=0) - xfr << "MKSWAP " - << system_tm_mad << " " - << size << " " - << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " - << vm->get_oid() << " " - << "0" << endl; + goto error_attributes; } - else if ( type == "FS" ) - { - // ----------------------------------------------------------------- - // Create a clean file system disk image - // ----------------------------------------------------------------- - size = disk->vector_value("SIZE"); - format = disk->vector_value("FORMAT"); - if ( size.empty() || format.empty() ) - { - vm->log("TM",Log::WARNING, "No size or format in FS, skipping"); - continue; - } - - //MKIMAGE tm_mad size format host:remote_system_dir/disk.i vmid dsid(=0) - xfr << "MKIMAGE " - << system_tm_mad << " " - << size << " " - << format << " " - << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " - << vm->get_oid() << " " - << "0" << endl; - } - else - { - // ----------------------------------------------------------------- - // Get transfer attributes & check errors - // ----------------------------------------------------------------- - tm_mad = disk->vector_value("TM_MAD"); - ds_id = disk->vector_value("DATASTORE_ID"); - source = disk->vector_value("SOURCE"); - clon = disk->vector_value("CLONE"); - - if ( source.empty() || - tm_mad.empty() || - ds_id.empty() || - clon.empty() ) - { - goto error_attributes; - } - - transform(clon.begin(),clon.end(),clon.begin(),(int(*)(int))toupper); - - // ----------------------------------------------------------------- - // CLONE or LINK disk images - // ----------------------------------------------------------------- - - // tm_mad fe:SOURCE host:remote_system_ds/disk.i vmid dsid - if (clon == "YES") - { - xfr << "CLONE "; - } - else - { - xfr << "LN "; - } - - xfr << tm_mad << " "; - - if ( source.find(":") == string::npos ) //Regular file - { - xfr << nd.get_nebula_hostname() << ":" << source << " "; - } - else //TM Plugin specific protocol - { - xfr << source << " "; - } - - xfr << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " - << vm->get_oid() << " " - << ds_id << endl; - } + xfr << endl; } // ------------------------------------------------------------------------- @@ -423,9 +469,7 @@ error_file: goto error_common; error_attributes: - os.str(""); - os << "prolog, missing DISK mandatory attributes " - << "(SOURCE, TM_MAD, CLONE, DATASTORE_ID) for VM " << vid; + os.str(error_str); xfr.close(); goto error_common; From cc13c07317df6bef2e128afab66f3efce65aad71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Wed, 13 Jun 2012 18:42:42 +0200 Subject: [PATCH 41/47] Feature #1223: New VMM attach action --- include/Attribute.h | 6 + include/LifeCycleManager.h | 2 + include/VirtualMachine.h | 31 ++++ include/VirtualMachineManager.h | 17 +- include/VirtualMachineManagerDriver.h | 12 ++ src/common/Attribute.cc | 15 ++ src/lcm/LifeCycleManager.cc | 18 ++ src/vm/VirtualMachine.cc | 236 +++++++++++++++++++++++++ src/vmm/VirtualMachineManager.cc | 176 ++++++++++++++++++ src/vmm/VirtualMachineManagerDriver.cc | 31 ++++ 10 files changed, 543 insertions(+), 1 deletion(-) diff --git a/include/Attribute.h b/include/Attribute.h index 86049cfbdb..2e3ae4ef07 100644 --- a/include/Attribute.h +++ b/include/Attribute.h @@ -317,6 +317,12 @@ public: replace(name, oss.str()); } + /** + * Removes given the vector attribute + * @param name of the vector attribute + */ + void remove(const string& name); + /** * Returns the attribute type */ diff --git a/include/LifeCycleManager.h b/include/LifeCycleManager.h index bad3e951af..1e77b9eecf 100644 --- a/include/LifeCycleManager.h +++ b/include/LifeCycleManager.h @@ -58,6 +58,8 @@ public: PROLOG_FAILURE, /**< Sent by the TM when the prolog phase fails */ EPILOG_SUCCESS, /**< Sent by the TM when the epilog phase succeeds */ EPILOG_FAILURE, /**< Sent by the TM when the epilog phase fails */ + ATTACH_SUCCESS, /**< Sent by the VMM when an attach action succeeds */ + ATTACH_FAILURE, /**< Sent by the VMM when an attach action fails */ DEPLOY, /**< Sent by the DM to deploy a VM on a host */ SUSPEND, /**< Sent by the DM to suspend an running VM */ RESTORE, /**< Sent by the DM to restore a suspended VM */ diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 12cd0a9e66..6c105b4e32 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -737,6 +737,37 @@ public: static void set_auth_request(int uid, AuthRequest& ar, VirtualMachineTemplate *tmpl); + + // ------------------------------------------------------------------------ + // Hotplug related functions + // ------------------------------------------------------------------------ + + /** + * Attaches a new disk. It will acquire the Image used, if any, and add the + * disk to the VM template. The VM must be updated in the DB afterwards. + * + * @param tmpl Template containing a single DISK vector attribute. The + * caller must delete this template + * @param error_str Returns the error reason, if any + * + * @return 0 on success + */ + int attach_disk(VirtualMachineTemplate * tmpl, string& error_str); + + /** + * Returns the disk that is waiting for an attachment action + * + * @return the disk waiting for an attachment action, or 0 + */ + VectorAttribute* get_attach_disk(); + + /** + * Cleans the ATTACH = YES attribute from the disk + * + * @return 0 on success + */ + int end_attach_operation(int disk_id); + private: // ------------------------------------------------------------------------- diff --git a/include/VirtualMachineManager.h b/include/VirtualMachineManager.h index 5691425563..eccdc638e6 100644 --- a/include/VirtualMachineManager.h +++ b/include/VirtualMachineManager.h @@ -56,7 +56,8 @@ public: POLL, TIMER, DRIVER_CANCEL, - FINALIZE + FINALIZE, + ATTACH }; /** @@ -199,6 +200,9 @@ private: * @param domain domain id as returned by the hypervisor * @param dfile deployment file to boot the VM * @param cfile checkpoint file to save the VM + * @param disk_id Disk to attach/detach, if any + * @param tm_command Transfer Manager command to attach/detach, if any + * @param disk_target_path Path of the disk to attach, if any * @param tmpl the VM information in XML */ string * format_message( @@ -210,6 +214,9 @@ private: const string& ldfile, const string& rdfile, const string& cfile, + int disk_id, + const string& tm_command, + const string& disk_target_path, const string& tmpl); /** @@ -290,6 +297,14 @@ private: */ void timer_action(); + /** + * Attaches a new disk to a VM. The VM must have a disk with the + * attribute ATTACH = YES + * @param vid the id of the VM. + */ + void attach_action( + int vid); + /** * This function cancels the current driver operation */ diff --git a/include/VirtualMachineManagerDriver.h b/include/VirtualMachineManagerDriver.h index f7dce5d7ef..9a3a9bb9fb 100644 --- a/include/VirtualMachineManagerDriver.h +++ b/include/VirtualMachineManagerDriver.h @@ -232,6 +232,18 @@ private: } + /** + * Sends an attach request to the MAD: "ATTACH ID XML_DRV_MSG" + * @param oid the virtual machine id. + * @param drv_msg xml data for the mad operation + */ + void attach ( + const int oid, + const string& drv_msg) const + { + write_drv("ATTACH", oid, drv_msg); + } + private: void write_drv(const char * aname, const int oid, const string& msg) const diff --git a/src/common/Attribute.cc b/src/common/Attribute.cc index 389681ea1b..3afcb326c3 100644 --- a/src/common/Attribute.cc +++ b/src/common/Attribute.cc @@ -185,6 +185,21 @@ void VectorAttribute::replace(const string& name, const string& value) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void VectorAttribute::remove(const string& name) +{ + map::iterator it; + + it = attribute_value.find(name); + + if ( it != attribute_value.end() ) + { + attribute_value.erase(it); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + string VectorAttribute::vector_value(const char *name) const { map::const_iterator it; diff --git a/src/lcm/LifeCycleManager.cc b/src/lcm/LifeCycleManager.cc index b3a72dda42..60acd5b84b 100644 --- a/src/lcm/LifeCycleManager.cc +++ b/src/lcm/LifeCycleManager.cc @@ -129,6 +129,14 @@ void LifeCycleManager::trigger(Actions action, int _vid) aname = "EPILOG_FAILURE"; break; + case ATTACH_SUCCESS: + aname = "ATTACH_SUCCESS"; + break; + + case ATTACH_FAILURE: + aname = "ATTACH_FAILURE"; + break; + case DEPLOY: aname = "DEPLOY"; break; @@ -262,6 +270,16 @@ void LifeCycleManager::do_action(const string &action, void * arg) { epilog_failure_action(vid); } + else if (action == "ATTACH_SUCCESS") + { + // TODO +// attach_success_action(vid); + } + else if (action == "ATTACH_FAILURE") + { + // TODO +// attach_failure_action(vid); + } else if (action == "DEPLOY") { deploy_action(vid); diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index c06f552293..7948e70822 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -922,6 +922,7 @@ void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk) return; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -1108,6 +1109,241 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +// TODO: this method requires the VM to be locked, and then it locks the Image +// to acquire. Check if this can be troublesome + +int VirtualMachine::attach_disk(VirtualMachineTemplate * tmpl, string& error_str) +{ + int num_disks, rc; + vector disks; + ImagePool * ipool; + VectorAttribute * disk; + VectorAttribute * new_disk; + vector acquired_images; + + int new_disk_id; + int image_id; + string dev_prefix; + string target; + + queue > disks_queue; + + set used_targets; + + ostringstream oss; + Image::ImageType img_type; + + Nebula& nd = Nebula::instance(); + ipool = nd.get_ipool(); + + // ------------------------------------------------------------------------- + // Get the DISK attribute from the template + // ------------------------------------------------------------------------- + + num_disks = obj_template->get("DISK", disks); + + if ( num_disks != 1 ) + { + goto error_no_disk; + } + + new_disk = new VectorAttribute( *(dynamic_cast(disks[0])) ); + + // ------------------------------------------------------------------------- + // See if there is a CONTEXT cdrom, and get the target it is using + // ------------------------------------------------------------------------- + num_disks = obj_template->get("CONTEXT", disks); + + if ( num_disks > 0 ) + { + disk = dynamic_cast(disks[0]); + + if ( disk != 0 ) + { + target = disk->vector_value("TARGET"); + + if ( !target.empty() ) + { + used_targets.insert(target); + } + } + } + + // ------------------------------------------------------------------------- + // Check the used targets + // ------------------------------------------------------------------------- + disks.clear(); + num_disks = obj_template->get("DISK", disks); + + if ( num_disks >= 20 ) + { + goto error_max_disks; + } + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + target = disk->vector_value("TARGET"); + + if ( !target.empty() ) + { + used_targets.insert(target); + } + } + + // ------------------------------------------------------------------------- + // Acquire the new disk image + // ------------------------------------------------------------------------- + + // num_disks +1 because the context is not a DISK, but it takes the + // ds//disk.num_disks file + new_disk_id = num_disks + 1; + + rc = ipool->disk_attribute(new_disk, + new_disk_id, + img_type, + dev_prefix, + uid, + image_id, + error_str); + if ( rc == 0 ) + { + acquired_images.push_back(image_id); + + target = new_disk->vector_value("TARGET"); + + if ( !target.empty() ) + { + if ( used_targets.insert(target).second == false ) + { + goto error_duplicated_target; + } + } + else + { + disks_queue.push( make_pair(dev_prefix, new_disk) ); + } + } + else + { + goto error_common; + } + + assign_disk_targets(disks_queue, used_targets); + + // ------------------------------------------------------------------------- + // Add the disk to the VM template + // ------------------------------------------------------------------------- + + new_disk->replace("ATTACH", "YES"); + + obj_template->set(new_disk); + + return 0; + +error_no_disk: + error_str = "The template must contain one DISK attribute"; + return -1; + +error_max_disks: + error_str = "Exceeded the maximum number of disks (20)"; + return -1; + +error_duplicated_target: + oss << "Two disks have defined the same target " << target; + error_str = oss.str(); + +error_common: + ImageManager * imagem = nd.get_imagem(); + + vector::iterator it; + + for ( it=acquired_images.begin() ; it < acquired_images.end(); it++ ) + { + imagem->release_image(*it, false); + } + + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VectorAttribute* VirtualMachine::get_attach_disk() +{ + int num_disks; + vector disks; + VectorAttribute * disk; + + ostringstream oss; + + // ------------------------------------------------------------------------- + // Set DISK attributes & Targets + // ------------------------------------------------------------------------- + num_disks = obj_template->get("DISK", disks); + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + if ( disk->vector_value("ATTACH") == "YES" ) + { + return disk; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::end_attach_operation(int disk_id) +{ + int num_disks; + vector disks; + VectorAttribute * disk; + int value; + + ostringstream oss; + + num_disks = obj_template->get("DISK", disks); + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + disk->vector_value("DISK_ID", value); + + if ( value == disk_id ) + { + disk->remove("ATTACH"); + return 0; + } + } + + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void VirtualMachine::release_disk_images() { int iid; diff --git a/src/vmm/VirtualMachineManager.cc b/src/vmm/VirtualMachineManager.cc index e676f3524f..52f63d226b 100644 --- a/src/vmm/VirtualMachineManager.cc +++ b/src/vmm/VirtualMachineManager.cc @@ -162,6 +162,10 @@ void VirtualMachineManager::trigger(Actions action, int _vid) aname = ACTION_FINALIZE; break; + case ATTACH: + aname = "ATTACH"; + break; + default: delete vid; return; @@ -238,6 +242,10 @@ void VirtualMachineManager::do_action(const string &action, void * arg) { driver_cancel_action(vid); } + else if (action == "ATTACH") + { + attach_action(vid); + } else if (action == ACTION_TIMER) { timer_action(); @@ -270,6 +278,9 @@ string * VirtualMachineManager::format_message( const string& ldfile, const string& rdfile, const string& cfile, + int disk_id, + const string& tm_command, + const string& disk_target_path, const string& tmpl) { ostringstream oss; @@ -317,6 +328,19 @@ string * VirtualMachineManager::format_message( oss << ""; } + if ( !tm_command.empty() ) + { + oss << "" << tm_command << "" + << "" << disk_id << "" + << "" << disk_target_path << ""; + } + else + { + oss << "" + << "" + << ""; + } + oss << tmpl << ""; @@ -380,6 +404,9 @@ void VirtualMachineManager::deploy_action(int vid) vm->get_deployment_file(), vm->get_remote_deployment_file(), "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->deploy(vid, *drv_msg); @@ -477,6 +504,9 @@ void VirtualMachineManager::save_action( "", "", vm->get_checkpoint_file(), + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->save(vid, *drv_msg); @@ -556,6 +586,9 @@ void VirtualMachineManager::shutdown_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->shutdown(vid, *drv_msg); @@ -630,6 +663,9 @@ void VirtualMachineManager::reboot_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->reboot(vid, *drv_msg); @@ -699,6 +735,9 @@ void VirtualMachineManager::reset_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->reset(vid, *drv_msg); @@ -769,6 +808,9 @@ void VirtualMachineManager::cancel_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->cancel(vid, *drv_msg); @@ -847,6 +889,9 @@ void VirtualMachineManager::cancel_previous_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->cancel(vid, *drv_msg); @@ -921,6 +966,9 @@ void VirtualMachineManager::migrate_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->migrate(vid, *drv_msg); @@ -1001,6 +1049,9 @@ void VirtualMachineManager::restore_action( "", "", vm->get_checkpoint_file(), + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->restore(vid, *drv_msg); @@ -1076,6 +1127,9 @@ void VirtualMachineManager::poll_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->poll(vid, *drv_msg); @@ -1236,6 +1290,9 @@ void VirtualMachineManager::timer_action() "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->poll(*it, *drv_msg); @@ -1248,6 +1305,125 @@ void VirtualMachineManager::timer_action() } } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachineManager::attach_action( + int vid) +{ + VirtualMachine * vm; + const VirtualMachineManagerDriver * vmd; + + ostringstream os; + string vm_tmpl; + string * drv_msg; + string tm_command; + string system_tm_mad; + string opennebula_hostname; + string prolog_cmd; + string disk_path; + string error_str; + + const VectorAttribute * disk; + int disk_id; + + Nebula& nd = Nebula::instance(); + + // Get the VM from the pool + vm = vmpool->get(vid,true); + + if (vm == 0) + { + return; + } + + if (!vm->hasHistory()) + { + goto error_history; + } + + // Get the driver for this VM + vmd = get(vm->get_vmm_mad()); + + if ( vmd == 0 ) + { + goto error_driver; + } + + disk = vm->get_attach_disk(); + + if ( disk == 0 ) + { + return; + } + + system_tm_mad = nd.get_system_ds_tm_mad(); + opennebula_hostname = nd.get_nebula_hostname(); + + disk_id = disk->vector_value("DISK_ID", disk_id); + + Nebula::instance().get_tm()->prolog_transfer_command( + vm, + disk, + disk_id, + system_tm_mad, + opennebula_hostname, + os, + error_str); + + prolog_cmd = os.str(); + + os.str(""); + os << vm->get_remote_system_dir() << "/disk." << disk_id; + + disk_path = os.str(); + + // Invoke driver method + drv_msg = format_message( + vm->get_hostname(), + vm->get_vnm_mad(), + "", + "", + vm->get_deploy_id(), + "", + "", + "", + disk_id, + prolog_cmd, + disk_path, + vm->to_xml(vm_tmpl)); + + + vmd->attach(vid, *drv_msg); + + delete drv_msg; + + vm->unlock(); + + return; + +error_history: + os.str(""); + os << "attach_action, VM has no history"; + goto error_common; + +error_driver: + os.str(""); + os << "attach_action, error getting driver " << vm->get_vmm_mad(); + goto error_common; + +error_common: + Nebula &ne = Nebula::instance(); + LifeCycleManager * lcm = ne.get_lcm(); + + lcm->trigger(LifeCycleManager::ATTACH_FAILURE, vid); + + vm->log("VMM", Log::ERROR, os); + vm->unlock(); + return; +} + + /* ************************************************************************** */ /* MAD Loading */ /* ************************************************************************** */ diff --git a/src/vmm/VirtualMachineManagerDriver.cc b/src/vmm/VirtualMachineManagerDriver.cc index 651d9bbefe..f8e40b4143 100644 --- a/src/vmm/VirtualMachineManagerDriver.cc +++ b/src/vmm/VirtualMachineManagerDriver.cc @@ -335,6 +335,37 @@ void VirtualMachineManagerDriver::protocol( vmpool->update(vm); } } + else if ( action == "ATTACH" ) + { + Nebula &ne = Nebula::instance(); + LifeCycleManager *lcm = ne.get_lcm(); + + int disk_id; + istringstream tiss; + + string disk_id_str = is.str(); + + tiss.clear(); + tiss.str(disk_id_str); + + tiss >> disk_id; + + // TODO: check disk_id is correctly returned by the driver + + if ( result == "SUCCESS" ) + { + vm->log("VMM",Log::ERROR,"VM Disk Successfully attached."); + + lcm->trigger(LifeCycleManager::ATTACH_SUCCESS, id); + } + else + { + log_error(vm,os,is,"Error attaching new VM Disk"); + vmpool->update(vm); + + lcm->trigger(LifeCycleManager::ATTACH_FAILURE, id); + } + } else if ( action == "POLL" ) { if (result == "SUCCESS") From 8071e5ca0d155d76454a7e0f7c3f5145a42f747c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Wed, 13 Jun 2012 18:47:04 +0200 Subject: [PATCH 42/47] Feature #1223: implement DispatchManager::attach --- include/DispatchManager.h | 11 ++++++++ src/dm/DispatchManagerActions.cc | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/DispatchManager.h b/include/DispatchManager.h index 1f9aa56ecb..0c994ed0c2 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -239,6 +239,17 @@ public: int vid, bool do_resched); + /** + * Starts the attach disk action. The VM must be prepared before, calling + * VirtualMachine::attach_disk + * + * @param vid VirtualMachine identification + * @return 0 on success, -1 if the VM does not exits or -2 if the VM is + * in a wrong a state + */ + int attach( + int vid); + private: /** * Thread id for the Dispatch Manager diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index df0505cc8e..327acf1f33 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -841,3 +841,48 @@ int DispatchManager::resubmit(int vid) return rc; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int DispatchManager::attach(int vid) +{ + VirtualMachine * vm; + ostringstream oss; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return -1; + } + + oss << "Attaching a new disk to VM " << vid; + NebulaLog::log("DiM",Log::DEBUG,oss); + + if (vm->get_state() == VirtualMachine::ACTIVE && + vm->get_lcm_state() == VirtualMachine::RUNNING ) + { + Nebula& nd = Nebula::instance(); + VirtualMachineManager * vmm = nd.get_vmm(); + + vmm->trigger(VirtualMachineManager::ATTACH,vid); + } + else + { + goto error; + } + + vm->unlock(); + + return 0; + +error: + oss.str(""); + oss << "Could not attach a new disk to VM " << vid << ", wrong state."; + NebulaLog::log("DiM",Log::ERROR,oss); + + vm->unlock(); + + return -2; +} From e5b2f84f4cb157a71058162356c5ebe61284ae8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Wed, 13 Jun 2012 18:57:12 +0200 Subject: [PATCH 43/47] Feature #1223: Basic xmlrpc method one.vm.attach, needs to be finished --- include/RequestManagerVirtualMachine.h | 17 ++++++ src/rm/RequestManager.cc | 2 + src/rm/RequestManagerVirtualMachine.cc | 72 ++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 00326f47fa..05ac3caccb 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -166,6 +166,23 @@ public: xmlrpc_c::paramList const& paramList, RequestAttributes& att); }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class VirtualMachineAttach : public RequestManagerVirtualMachine +{ +public: + VirtualMachineAttach(): + RequestManagerVirtualMachine("VirtualMachineAttach", + "Attaches a new disk to the virtual machine", + "A:sis"){}; + + ~VirtualMachineAttach(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 611c3db474..0319e5fc00 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -249,6 +249,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr vm_action(new VirtualMachineAction()); xmlrpc_c::methodPtr vm_savedisk(new VirtualMachineSaveDisk()); xmlrpc_c::methodPtr vm_monitoring(new VirtualMachineMonitoring()); + xmlrpc_c::methodPtr vm_attach(new VirtualMachineAttach()); xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting()); xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring()); @@ -357,6 +358,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vm.chown", vm_chown); RequestManagerRegistry.addMethod("one.vm.chmod", vm_chmod); RequestManagerRegistry.addMethod("one.vm.monitoring", vm_monitoring); + RequestManagerRegistry.addMethod("one.vm.attach", vm_attach); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.accounting", vm_pool_acct); diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 07f5055ba6..46ccb6da9a 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -620,3 +620,75 @@ void VirtualMachineMonitoring::request_execute( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + DispatchManager * dm = nd.get_dm(); + + VirtualMachine * vm; + VirtualMachineTemplate * tmpl; + PoolObjectAuth host_perms; + + int rc; + string error_str; + + int id = xmlrpc_c::value_int(paramList.getInt(1)); + string str_tmpl = xmlrpc_c::value_string(paramList.getString(2)); + + VirtualMachinePool * vmpool = static_cast(pool); + + tmpl = new VirtualMachineTemplate(); + + rc = tmpl->parse_str_or_xml(str_tmpl, error_str); + + if ( rc != 0 ) + { + failure_response(INTERNAL, "", att); // TODO: error message + delete tmpl; + + return; + } + + // TODO: auth & quotas + + // TODO: set vm state HOTPLUG & vm->set_resched(false); // Cancel re-scheduling actions + + vm = get_vm(id, att); + + if ( vm == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),id), + att); + delete tmpl; + + return; + } + + rc = vm->attach_disk(tmpl, error_str); + + if ( rc != 0 ) + { + failure_response(INTERNAL, "", att); // TODO: error message + + vm->unlock(); + delete tmpl; + + return; + } + + vmpool->update(vm); + + vm->unlock(); + + dm->attach(id); + + + delete tmpl; + success_response(id, att); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ From 492ba46570a31c841f567ea15760098502dd701a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Wed, 13 Jun 2012 19:15:33 +0200 Subject: [PATCH 44/47] Feature #1223: Implement LCM::attach success/failure. --- include/LifeCycleManager.h | 4 +++ include/VirtualMachine.h | 4 +-- src/lcm/LifeCycleManager.cc | 6 ++--- src/lcm/LifeCycleStates.cc | 35 ++++++++++++++++++++++++++ src/vm/VirtualMachine.cc | 10 +++----- src/vmm/VirtualMachineManagerDriver.cc | 12 --------- 6 files changed, 46 insertions(+), 25 deletions(-) diff --git a/include/LifeCycleManager.h b/include/LifeCycleManager.h index 1e77b9eecf..dc4bd99f61 100644 --- a/include/LifeCycleManager.h +++ b/include/LifeCycleManager.h @@ -174,6 +174,10 @@ private: void epilog_failure_action(int vid); + void attach_success_action(int vid); + + void attach_failure_action(int vid); + void deploy_action(int vid); void suspend_action(int vid); diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 6c105b4e32..77c009c835 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -762,11 +762,11 @@ public: VectorAttribute* get_attach_disk(); /** - * Cleans the ATTACH = YES attribute from the disk + * Cleans the ATTACH = YES attribute from the disks * * @return 0 on success */ - int end_attach_operation(int disk_id); + int end_attach_operation(); private: diff --git a/src/lcm/LifeCycleManager.cc b/src/lcm/LifeCycleManager.cc index 60acd5b84b..42d8875555 100644 --- a/src/lcm/LifeCycleManager.cc +++ b/src/lcm/LifeCycleManager.cc @@ -272,13 +272,11 @@ void LifeCycleManager::do_action(const string &action, void * arg) } else if (action == "ATTACH_SUCCESS") { - // TODO -// attach_success_action(vid); + attach_success_action(vid); } else if (action == "ATTACH_FAILURE") { - // TODO -// attach_failure_action(vid); + attach_failure_action(vid); } else if (action == "DEPLOY") { diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 0165d3ec8d..7daf21f7b6 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -834,3 +834,38 @@ void LifeCycleManager::failure_action(VirtualMachine * vm) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void LifeCycleManager::attach_success_action(int vid) +{ + VirtualMachine * vm; + VectorAttribute * disk; + int disk_id; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + vm->end_attach_operation(); + + vmpool->update(vm); + + vm->unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void LifeCycleManager::attach_failure_action(int vid) +{ + // TODO: For now, on success or failure the LCM just cleans the ATTACH + // attribute of the VM disk. + attach_success_action(vid); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + + diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 7948e70822..9e364c6382 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -1309,12 +1309,11 @@ VectorAttribute* VirtualMachine::get_attach_disk() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int VirtualMachine::end_attach_operation(int disk_id) +int VirtualMachine::end_attach_operation() { int num_disks; vector disks; VectorAttribute * disk; - int value; ostringstream oss; @@ -1329,16 +1328,13 @@ int VirtualMachine::end_attach_operation(int disk_id) continue; } - disk->vector_value("DISK_ID", value); - - if ( value == disk_id ) + if ( disk->vector_value("ATTACH") == "YES" ) { disk->remove("ATTACH"); - return 0; } } - return -1; + return 0; } /* -------------------------------------------------------------------------- */ diff --git a/src/vmm/VirtualMachineManagerDriver.cc b/src/vmm/VirtualMachineManagerDriver.cc index f8e40b4143..3955d052a1 100644 --- a/src/vmm/VirtualMachineManagerDriver.cc +++ b/src/vmm/VirtualMachineManagerDriver.cc @@ -340,18 +340,6 @@ void VirtualMachineManagerDriver::protocol( Nebula &ne = Nebula::instance(); LifeCycleManager *lcm = ne.get_lcm(); - int disk_id; - istringstream tiss; - - string disk_id_str = is.str(); - - tiss.clear(); - tiss.str(disk_id_str); - - tiss >> disk_id; - - // TODO: check disk_id is correctly returned by the driver - if ( result == "SUCCESS" ) { vm->log("VMM",Log::ERROR,"VM Disk Successfully attached."); From 554321c73bc90b858057f157d383e33b0fea112d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 14 Jun 2012 13:02:18 +0200 Subject: [PATCH 45/47] Feature #1223: New VM state HOTPLUG, continue work on the attach operation --- include/DispatchManager.h | 16 +++--- include/Template.h | 7 +++ include/VirtualMachine.h | 15 ++++-- src/dm/DispatchManagerActions.cc | 49 ++++++++++++------- src/lcm/LifeCycleStates.cc | 27 +++++++--- src/rm/RequestManagerVirtualMachine.cc | 19 ++----- src/template/Template.cc | 28 +++++++++++ src/vm/VirtualMachine.cc | 68 ++++++++++++++++++++++++-- src/vmm/VirtualMachineManager.cc | 7 ++- 9 files changed, 184 insertions(+), 52 deletions(-) diff --git a/include/DispatchManager.h b/include/DispatchManager.h index 0c994ed0c2..48a9c1e1a3 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -240,15 +240,19 @@ public: bool do_resched); /** - * Starts the attach disk action. The VM must be prepared before, calling - * VirtualMachine::attach_disk + * Starts the attach disk action. * - * @param vid VirtualMachine identification - * @return 0 on success, -1 if the VM does not exits or -2 if the VM is - * in a wrong a state + * @param vm pointer to a VirtualMachine with its mutex locked. It will be + * unlocked + * @param tmpl Template containing the new DISK attribute. + * It will be deleted + * @param error_str Error reason, if any + * @return 0 on success, -1 action error, -2 if the VM is in a wrong a state */ int attach( - int vid); + VirtualMachine * vm, + VirtualMachineTemplate * tmpl, + string & error_str); private: /** diff --git a/include/Template.h b/include/Template.h index 3759b08859..0b4f061f58 100644 --- a/include/Template.h +++ b/include/Template.h @@ -176,6 +176,13 @@ public: */ virtual int erase(const string& name); + /** + * Removes an attribute from the template, and frees the attributes. + * @param att Attribute to remove. It will be deleted + * @return the number of attributes removed + */ + virtual int erase(Attribute * att); + /** * Gets all the attributes with the given name. * @param name the attribute name. diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 77c009c835..07a77a5405 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -79,7 +79,8 @@ public: CANCEL = 13, FAILURE = 14, CLEANUP = 15, - UNKNOWN = 16 + UNKNOWN = 16, + HOTPLUG = 17 }; // ------------------------------------------------------------------------- @@ -764,9 +765,17 @@ public: /** * Cleans the ATTACH = YES attribute from the disks * - * @return 0 on success + * @return 0 on success, -1 otherwise */ - int end_attach_operation(); + int attach_success(); + + /** + * Deletes the DISK that was in the process of being attached, and releases + * the image, if any + * + * @return 0 on success, -1 otherwise + */ + int attach_failure(); private: diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 327acf1f33..30dac49742 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -845,44 +845,59 @@ int DispatchManager::resubmit(int vid) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int DispatchManager::attach(int vid) +int DispatchManager::attach( + VirtualMachine * vm, + VirtualMachineTemplate * tmpl, + string & error_str) { - VirtualMachine * vm; - ostringstream oss; + ostringstream oss; + int rc; + int vid = vm->get_oid(); - vm = vmpool->get(vid,true); - - if ( vm == 0 ) - { - return -1; - } + Nebula& nd = Nebula::instance(); + VirtualMachineManager * vmm = nd.get_vmm(); oss << "Attaching a new disk to VM " << vid; NebulaLog::log("DiM",Log::DEBUG,oss); - if (vm->get_state() == VirtualMachine::ACTIVE && - vm->get_lcm_state() == VirtualMachine::RUNNING ) + if ( vm->get_state() != VirtualMachine::ACTIVE || + vm->get_lcm_state() != VirtualMachine::RUNNING ) { - Nebula& nd = Nebula::instance(); - VirtualMachineManager * vmm = nd.get_vmm(); - - vmm->trigger(VirtualMachineManager::ATTACH,vid); + goto error_state; } - else + + rc = vm->attach_disk(tmpl, error_str); + + if ( rc != 0 ) { goto error; } + vm->set_state(VirtualMachine::HOTPLUG); + vmpool->update(vm); + vm->unlock(); + delete tmpl; + + vmm->trigger(VirtualMachineManager::ATTACH,vid); return 0; error: + vm->unlock(); + delete tmpl; + + return -1; + +error_state: oss.str(""); oss << "Could not attach a new disk to VM " << vid << ", wrong state."; - NebulaLog::log("DiM",Log::ERROR,oss); + error_str = oss.str(); + + NebulaLog::log("DiM", Log::ERROR, error_str); vm->unlock(); + delete tmpl; return -2; } diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 7daf21f7b6..0e53f4864e 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -838,8 +838,6 @@ void LifeCycleManager::failure_action(VirtualMachine * vm) void LifeCycleManager::attach_success_action(int vid) { VirtualMachine * vm; - VectorAttribute * disk; - int disk_id; vm = vmpool->get(vid,true); @@ -848,7 +846,9 @@ void LifeCycleManager::attach_success_action(int vid) return; } - vm->end_attach_operation(); + vm->attach_success(); + + vm->set_state(VirtualMachine::RUNNING); vmpool->update(vm); @@ -860,9 +860,24 @@ void LifeCycleManager::attach_success_action(int vid) void LifeCycleManager::attach_failure_action(int vid) { - // TODO: For now, on success or failure the LCM just cleans the ATTACH - // attribute of the VM disk. - attach_success_action(vid); + VirtualMachine * vm; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + vm->attach_failure(); + + vm->set_state(VirtualMachine::RUNNING); + + vmpool->update(vm); + + vm->unlock(); + + // TODO: update quotas, here or in VirtualMachine::attach_failure } /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 46ccb6da9a..d862c5697b 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -637,8 +637,6 @@ void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, int id = xmlrpc_c::value_int(paramList.getInt(1)); string str_tmpl = xmlrpc_c::value_string(paramList.getString(2)); - VirtualMachinePool * vmpool = static_cast(pool); - tmpl = new VirtualMachineTemplate(); rc = tmpl->parse_str_or_xml(str_tmpl, error_str); @@ -667,26 +665,17 @@ void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, return; } - rc = vm->attach_disk(tmpl, error_str); + rc = dm->attach(vm, tmpl, error_str); if ( rc != 0 ) { - failure_response(INTERNAL, "", att); // TODO: error message - - vm->unlock(); - delete tmpl; + failure_response(ACTION, + request_error(error_str, ""), + att); return; } - vmpool->update(vm); - - vm->unlock(); - - dm->attach(id); - - - delete tmpl; success_response(id, att); } diff --git a/src/template/Template.cc b/src/template/Template.cc index 18227d4af6..962546b097 100644 --- a/src/template/Template.cc +++ b/src/template/Template.cc @@ -281,6 +281,34 @@ int Template::erase(const string& name) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int Template::erase(Attribute * att) +{ + multimap::iterator i; + + pair< + multimap::iterator, + multimap::iterator + > index; + + index = attributes.equal_range( att->name() ); + + for ( i = index.first; i != index.second; i++ ) + { + if ( i->second == att ) + { + delete att; + attributes.erase(i); + + return 1; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int Template::get( const string& name, vector& values) const diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 9e364c6382..75aa159d23 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -1140,7 +1140,7 @@ int VirtualMachine::attach_disk(VirtualMachineTemplate * tmpl, string& error_str // Get the DISK attribute from the template // ------------------------------------------------------------------------- - num_disks = obj_template->get("DISK", disks); + num_disks = tmpl->get("DISK", disks); if ( num_disks != 1 ) { @@ -1309,13 +1309,12 @@ VectorAttribute* VirtualMachine::get_attach_disk() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int VirtualMachine::end_attach_operation() +int VirtualMachine::attach_success() { int num_disks; vector disks; VectorAttribute * disk; - - ostringstream oss; + bool removed; num_disks = obj_template->get("DISK", disks); @@ -1331,9 +1330,70 @@ int VirtualMachine::end_attach_operation() if ( disk->vector_value("ATTACH") == "YES" ) { disk->remove("ATTACH"); + removed = true; } } + if ( removed ) + { + return 0; + } + + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +// TODO: this method requires the VM to be locked, and then it locks the Image +// to release. Check if this can be troublesome + +int VirtualMachine::attach_failure() +{ + int num_disks; + vector disks; + VectorAttribute * disk; + bool found = false; + bool uses_image = false; + int iid; + + Nebula& nd = Nebula::instance(); + ImageManager * imagem = nd.get_imagem(); + + num_disks = obj_template->get("DISK", disks); + + int i = 0; + + while( !found && i(disks[i]); + + i++; + + if ( disk == 0 ) + { + continue; + } + + if ( disk->vector_value("ATTACH") == "YES" ) + { + uses_image = ( disk->vector_value("IMAGE_ID", iid) != -1 ); + + obj_template->erase(disk); + found = true; + } + } + + if ( !found ) + { + return -1; + } + + if ( uses_image ) + { + imagem->release_image(iid, false); + } + return 0; } diff --git a/src/vmm/VirtualMachineManager.cc b/src/vmm/VirtualMachineManager.cc index 52f63d226b..906112c6ae 100644 --- a/src/vmm/VirtualMachineManager.cc +++ b/src/vmm/VirtualMachineManager.cc @@ -1354,7 +1354,7 @@ void VirtualMachineManager::attach_action( if ( disk == 0 ) { - return; + goto error_disk; } system_tm_mad = nd.get_system_ds_tm_mad(); @@ -1402,6 +1402,11 @@ void VirtualMachineManager::attach_action( return; +error_disk: + os.str(""); + os << "attach_action, could not find disk to attach"; + goto error_common; + error_history: os.str(""); os << "attach_action, VM has no history"; From 1eebdbc07a443415f4b6dbcb5bf6a5a33c300d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 14 Jun 2012 17:45:41 +0200 Subject: [PATCH 46/47] Feature #1223: Detach operation --- include/DispatchManager.h | 16 ++++ include/LifeCycleManager.h | 6 ++ include/Log.h | 8 ++ include/RequestManagerVirtualMachine.h | 17 ++++ include/TransferManager.h | 20 +++- include/VirtualMachine.h | 37 +++++++ include/VirtualMachineManager.h | 11 ++- include/VirtualMachineManagerDriver.h | 12 +++ src/dm/DispatchManagerActions.cc | 64 +++++++++++++ src/lcm/LifeCycleManager.cc | 16 ++++ src/lcm/LifeCycleStates.cc | 49 ++++++++++ src/rm/RequestManager.cc | 2 + src/rm/RequestManagerVirtualMachine.cc | 47 ++++++++- src/tm/TransferManager.cc | 128 ++++++++++++++++--------- src/vm/VirtualMachine.cc | 61 ++++++++++++ src/vmm/VirtualMachineManager.cc | 126 +++++++++++++++++++++++- src/vmm/VirtualMachineManagerDriver.cc | 19 ++++ 17 files changed, 586 insertions(+), 53 deletions(-) diff --git a/include/DispatchManager.h b/include/DispatchManager.h index 48a9c1e1a3..e22beea804 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -247,6 +247,7 @@ public: * @param tmpl Template containing the new DISK attribute. * It will be deleted * @param error_str Error reason, if any + * * @return 0 on success, -1 action error, -2 if the VM is in a wrong a state */ int attach( @@ -254,6 +255,21 @@ public: VirtualMachineTemplate * tmpl, string & error_str); + /** + * Starts the detach disk action. + * + * @param vm pointer to a VirtualMachine with its mutex locked. It will be + * unlocked + * @param disk_id Disk to detach + * @param error_str Error reason, if any + * + * @return 0 on success, -1 action error, -2 if the VM is in a wrong a state + */ + int detach( + VirtualMachine* vm, + int disk_id, + string & error_str); + private: /** * Thread id for the Dispatch Manager diff --git a/include/LifeCycleManager.h b/include/LifeCycleManager.h index dc4bd99f61..55664b63ca 100644 --- a/include/LifeCycleManager.h +++ b/include/LifeCycleManager.h @@ -60,6 +60,8 @@ public: EPILOG_FAILURE, /**< Sent by the TM when the epilog phase fails */ ATTACH_SUCCESS, /**< Sent by the VMM when an attach action succeeds */ ATTACH_FAILURE, /**< Sent by the VMM when an attach action fails */ + DETACH_SUCCESS, /**< Sent by the VMM when a detach action succeeds */ + DETACH_FAILURE, /**< Sent by the VMM when a detach action fails */ DEPLOY, /**< Sent by the DM to deploy a VM on a host */ SUSPEND, /**< Sent by the DM to suspend an running VM */ RESTORE, /**< Sent by the DM to restore a suspended VM */ @@ -178,6 +180,10 @@ private: void attach_failure_action(int vid); + void detach_success_action(int vid); + + void detach_failure_action(int vid); + void deploy_action(int vid); void suspend_action(int vid); diff --git a/include/Log.h b/include/Log.h index 968106ccf3..35cf433b1b 100644 --- a/include/Log.h +++ b/include/Log.h @@ -80,6 +80,14 @@ public: const MessageType type, const char * message); + virtual void log( + const char * module, + const Log::MessageType type, + const string& message) + { + log(module,type,message.c_str()); + }; + private: char * log_file; }; diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 05ac3caccb..029932581d 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -183,6 +183,23 @@ public: RequestAttributes& att); }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class VirtualMachineDetach : public RequestManagerVirtualMachine +{ +public: + VirtualMachineDetach(): + RequestManagerVirtualMachine("VirtualMachineDetach", + "Detaches a disk from a virtual machine", + "A:sii"){}; + + ~VirtualMachineDetach(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/TransferManager.h b/include/TransferManager.h index 0aacfaeb30..1de34727c1 100644 --- a/include/TransferManager.h +++ b/include/TransferManager.h @@ -110,12 +110,30 @@ public: int prolog_transfer_command( VirtualMachine * vm, const VectorAttribute * disk, - int disk_index, string& system_tm_mad, string& opennebula_hostname, ostream& xfr, string& error_str); + /** + * Inserts a transfer command in the xfs stream + * + * @param vm The VM + * @param disk Disk to transfer + * @param disk_index Disk index + * @param xfr Stream where the transfer command will be written + * @param error_str Error reason, if any + * + * @return 0 if the command was written to xfr, -1 if there is an + * error (and error reason in error_str), -2 if the disk didn't need + * a transfer command + */ + int epilog_transfer_command( + VirtualMachine * vm, + const VectorAttribute * disk, + ostream& xfr, + string& error_str); + private: /** * Thread id for the Transfer Manager diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 07a77a5405..a2bcef425a 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -117,6 +117,21 @@ public: } }; + /** + * writes a log message in vm.log. The class lock should be locked and + * the VM MUST BE obtained through the VirtualMachinePool get() method. + */ + void log( + const char * module, + const Log::MessageType type, + const string& message) const + { + if (_log != 0) + { + _log->log(module,type,message); + } + }; + /** * Function to print the VirtualMachine object into a string in * XML format @@ -755,6 +770,14 @@ public: */ int attach_disk(VirtualMachineTemplate * tmpl, string& error_str); + /** + * + * @param disk_id + * @param error_str + * @return + */ + int detach_disk(int disk_id, string& error_str); + /** * Returns the disk that is waiting for an attachment action * @@ -777,6 +800,20 @@ public: */ int attach_failure(); + /** + * Cleans the ATTACH = YES attribute from the disks + * + * @return 0 on success, -1 otherwise + */ + int detach_success(); + + /** + * Cleans the ATTACH = YES attribute from the disks + * + * @return 0 on success, -1 otherwise + */ + int detach_failure(); + private: // ------------------------------------------------------------------------- diff --git a/include/VirtualMachineManager.h b/include/VirtualMachineManager.h index eccdc638e6..b4bdeef93e 100644 --- a/include/VirtualMachineManager.h +++ b/include/VirtualMachineManager.h @@ -57,7 +57,8 @@ public: TIMER, DRIVER_CANCEL, FINALIZE, - ATTACH + ATTACH, + DETACH }; /** @@ -305,6 +306,14 @@ private: void attach_action( int vid); + /** + * Detaches a disk from a VM. The VM must have a disk with the + * attribute ATTACH = YES + * @param vid the id of the VM. + */ + void detach_action( + int vid); + /** * This function cancels the current driver operation */ diff --git a/include/VirtualMachineManagerDriver.h b/include/VirtualMachineManagerDriver.h index 9a3a9bb9fb..5fe1331969 100644 --- a/include/VirtualMachineManagerDriver.h +++ b/include/VirtualMachineManagerDriver.h @@ -244,6 +244,18 @@ private: write_drv("ATTACH", oid, drv_msg); } + /** + * Sends a detach request to the MAD: "DETACH ID XML_DRV_MSG" + * @param oid the virtual machine id. + * @param drv_msg xml data for the mad operation + */ + void detach ( + const int oid, + const string& drv_msg) const + { + write_drv("DETACH", oid, drv_msg); + } + private: void write_drv(const char * aname, const int oid, const string& msg) const diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index 30dac49742..75df37e554 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -676,6 +676,7 @@ error: return -2; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -873,6 +874,9 @@ int DispatchManager::attach( goto error; } + // TODO: Cancel resched? + // vm->set_resched(false); + vm->set_state(VirtualMachine::HOTPLUG); vmpool->update(vm); @@ -901,3 +905,63 @@ error_state: return -2; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int DispatchManager::detach( + VirtualMachine* vm, + int disk_id, + string & error_str) +{ + ostringstream oss; + int rc; + int vid = vm->get_oid(); + + Nebula& nd = Nebula::instance(); + VirtualMachineManager * vmm = nd.get_vmm(); + + oss << "Detaching disk " << disk_id << " from VM " << vid; + NebulaLog::log("DiM",Log::DEBUG,oss); + + if ( vm->get_state() != VirtualMachine::ACTIVE || + vm->get_lcm_state() != VirtualMachine::RUNNING ) + { + goto error_state; + } + + rc = vm->detach_disk(disk_id, error_str); + + if ( rc != 0 ) + { + goto error; + } + + // TODO: Cancel resched? + // vm->set_resched(false); + + vm->set_state(VirtualMachine::HOTPLUG); + vmpool->update(vm); + + vm->unlock(); + + vmm->trigger(VirtualMachineManager::DETACH,vid); + + return 0; + +error: + vm->unlock(); + + return -1; + +error_state: + oss.str(""); + oss << "Could not attach a new disk to VM " << vid << ", wrong state."; + error_str = oss.str(); + + NebulaLog::log("DiM", Log::ERROR, error_str); + + vm->unlock(); + + return -2; +} diff --git a/src/lcm/LifeCycleManager.cc b/src/lcm/LifeCycleManager.cc index 42d8875555..c9603a54a5 100644 --- a/src/lcm/LifeCycleManager.cc +++ b/src/lcm/LifeCycleManager.cc @@ -137,6 +137,14 @@ void LifeCycleManager::trigger(Actions action, int _vid) aname = "ATTACH_FAILURE"; break; + case DETACH_SUCCESS: + aname = "DETACH_SUCCESS"; + break; + + case DETACH_FAILURE: + aname = "DETACH_FAILURE"; + break; + case DEPLOY: aname = "DEPLOY"; break; @@ -278,6 +286,14 @@ void LifeCycleManager::do_action(const string &action, void * arg) { attach_failure_action(vid); } + else if (action == "DETACH_SUCCESS") + { + detach_success_action(vid); + } + else if (action == "DETACH_FAILURE") + { + detach_failure_action(vid); + } else if (action == "DEPLOY") { deploy_action(vid); diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 0e53f4864e..a8aad281f3 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -883,4 +883,53 @@ void LifeCycleManager::attach_failure_action(int vid) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void LifeCycleManager::detach_success_action(int vid) +{ + VirtualMachine * vm; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + vm->detach_success(); + + vm->set_state(VirtualMachine::RUNNING); + + vmpool->update(vm); + + vm->unlock(); + + // TODO: update quotas +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void LifeCycleManager::detach_failure_action(int vid) +{ + VirtualMachine * vm; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + vm->detach_failure(); + + vm->set_state(VirtualMachine::RUNNING); + + vmpool->update(vm); + + vm->unlock(); + +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 0319e5fc00..8e2fdbbc76 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -250,6 +250,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr vm_savedisk(new VirtualMachineSaveDisk()); xmlrpc_c::methodPtr vm_monitoring(new VirtualMachineMonitoring()); xmlrpc_c::methodPtr vm_attach(new VirtualMachineAttach()); + xmlrpc_c::methodPtr vm_detach(new VirtualMachineDetach()); xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting()); xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring()); @@ -359,6 +360,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vm.chmod", vm_chmod); RequestManagerRegistry.addMethod("one.vm.monitoring", vm_monitoring); RequestManagerRegistry.addMethod("one.vm.attach", vm_attach); + RequestManagerRegistry.addMethod("one.vm.detach", vm_detach); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.accounting", vm_pool_acct); diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index d862c5697b..d39e3da8c7 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -651,8 +651,6 @@ void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, // TODO: auth & quotas - // TODO: set vm state HOTPLUG & vm->set_resched(false); // Cancel re-scheduling actions - vm = get_vm(id, att); if ( vm == 0 ) @@ -681,3 +679,48 @@ void VirtualMachineAttach::request_execute(xmlrpc_c::paramList const& paramList, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void VirtualMachineDetach::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + DispatchManager * dm = nd.get_dm(); + + VirtualMachine * vm; + PoolObjectAuth host_perms; + + int rc; + string error_str; + + int id = xmlrpc_c::value_int(paramList.getInt(1)); + int disk_id = xmlrpc_c::value_int(paramList.getInt(2)); + + // TODO: auth & quotas + + vm = get_vm(id, att); + + if ( vm == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),id), + att); + + return; + } + + rc = dm->detach(vm, disk_id, error_str); + + if ( rc != 0 ) + { + failure_response(ACTION, + request_error(error_str, ""), + att); + + return; + } + + success_response(id, att); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/tm/TransferManager.cc b/src/tm/TransferManager.cc index 62c69fcc1d..7ca03c6517 100644 --- a/src/tm/TransferManager.cc +++ b/src/tm/TransferManager.cc @@ -204,7 +204,6 @@ void TransferManager::do_action(const string &action, void * arg) int TransferManager::prolog_transfer_command( VirtualMachine * vm, const VectorAttribute * disk, - int disk_index, string& system_tm_mad, string& opennebula_hostname, ostream& xfr, @@ -217,9 +216,12 @@ int TransferManager::prolog_transfer_command( string format; string tm_mad; string ds_id; + int disk_index; ostringstream os; + disk->vector_value("DISK_ID", disk_index); + type = disk->vector_value("TYPE"); transform(type.begin(),type.end(),type.begin(),(int(*)(int))toupper); @@ -412,7 +414,7 @@ void TransferManager::prolog_action(int vid) continue; } - rc = prolog_transfer_command(vm, disk, i, system_tm_mad, + rc = prolog_transfer_command(vm, disk, system_tm_mad, opennebula_hostname, xfr, error_str); if ( rc != 0 ) @@ -739,17 +741,85 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int TransferManager::epilog_transfer_command( + VirtualMachine * vm, + const VectorAttribute * disk, + ostream& xfr, + string& error_str) +{ + string save; + string tm_mad; + string ds_id; + int disk_index; + + save = disk->vector_value("SAVE"); + ds_id = disk->vector_value("DATASTORE_ID"); + tm_mad = disk->vector_value("TM_MAD"); + + if ( save.empty() || ds_id.empty() || tm_mad.empty() ) + { + return -2; + } + + disk->vector_value("DISK_ID", disk_index); + + transform(save.begin(),save.end(),save.begin(),(int(*)(int))toupper); + + if ( save == "YES" ) + { + string source; + string save_source; + + source = disk->vector_value("SOURCE"); + save_source = disk->vector_value("SAVE_AS_SOURCE"); + + if (source.empty() && save_source.empty()) + { + error_str = "No SOURCE to save disk image"; + return -1; + } + + if (!save_source.empty())//Use the save_as_source instead + { + source = save_source; + } + + //MVDS tm_mad hostname:remote_system_dir/disk.0 vmid dsid + xfr << "MVDS " + << tm_mad << " " + << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_index << " " + << source << " " + << vm->get_oid() << " " + << ds_id; + } + else //No saving disk + { + //DELETE tm_mad hostname:remote_system_dir/disk.i vmid ds_id + xfr << "DELETE " + << tm_mad << " " + << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_index << " " + << vm->get_oid() << " " + << ds_id; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void TransferManager::epilog_action(int vid) { ofstream xfr; ostringstream os; string xfr_name; string system_tm_mad; - string tm_mad; - string ds_id; + string error_str; + int rc; const VectorAttribute * disk; - string save; VirtualMachine * vm; Nebula& nd = Nebula::instance(); @@ -807,55 +877,19 @@ void TransferManager::epilog_action(int vid) continue; } - save = disk->vector_value("SAVE"); - ds_id = disk->vector_value("DATASTORE_ID"); - tm_mad = disk->vector_value("TM_MAD"); + rc = epilog_transfer_command(vm, disk, xfr, error_str); - if ( save.empty() || ds_id.empty() || tm_mad.empty() ) + if ( rc == -2 ) { continue; } - - transform(save.begin(),save.end(),save.begin(),(int(*)(int))toupper); - if ( save == "YES" ) + if ( rc == -1 ) { - string source; - string save_source; - - source = disk->vector_value("SOURCE"); - save_source = disk->vector_value("SAVE_AS_SOURCE"); - - if (source.empty() && save_source.empty()) - { - vm->log("TM", Log::ERROR, "No SOURCE to save disk image"); - continue; - } - - if (!save_source.empty())//Use the save_as_source instead - { - source = save_source; - } - - //MVDS tm_mad hostname:remote_system_dir/disk.0 vmid dsid - xfr << "MVDS " - << tm_mad << " " - << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " - << source << " " - << vm->get_oid() << " " - << ds_id << endl; - } - else //No saving disk - { - //DELETE tm_mad hostname:remote_system_dir/disk.i vmid ds_id - xfr << "DELETE " - << tm_mad << " " - << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " - << vm->get_oid() << " " - << ds_id << endl; + vm->log("TM", Log::ERROR, error_str); } + + xfr << endl; } //DELETE system_tm_mad hostname:remote_system_dir vmid ds_id diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 75aa159d23..ce21c3666e 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -1275,6 +1275,51 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +// TODO: this method requires the VM to be locked, and then it locks the Image +// to acquire. Check if this can be troublesome + +int VirtualMachine::detach_disk(int disk_id, string& error_str) +{ + int num_disks; + vector disks; + VectorAttribute * disk; + bool found = false; + + num_disks = obj_template->get("DISK", disks); + + int i = 0; + int d_id; + + while( !found && i(disks[i]); + + i++; + + if ( disk == 0 ) + { + continue; + } + + disk->vector_value("DISK_ID", d_id); + if ( d_id == disk_id ) + { + disk->replace("ATTACH", "YES"); + found = true; + } + } + + if ( !found ) + { + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + VectorAttribute* VirtualMachine::get_attach_disk() { int num_disks; @@ -1400,6 +1445,22 @@ int VirtualMachine::attach_failure() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int VirtualMachine::detach_success() +{ + return attach_failure(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::detach_failure() +{ + return attach_success(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void VirtualMachine::release_disk_images() { int iid; diff --git a/src/vmm/VirtualMachineManager.cc b/src/vmm/VirtualMachineManager.cc index 906112c6ae..cda33b27d5 100644 --- a/src/vmm/VirtualMachineManager.cc +++ b/src/vmm/VirtualMachineManager.cc @@ -166,6 +166,10 @@ void VirtualMachineManager::trigger(Actions action, int _vid) aname = "ATTACH"; break; + case DETACH: + aname = "DETACH"; + break; + default: delete vid; return; @@ -246,6 +250,10 @@ void VirtualMachineManager::do_action(const string &action, void * arg) { attach_action(vid); } + else if (action == "DETACH") + { + detach_action(vid); + } else if (action == ACTION_TIMER) { timer_action(); @@ -330,7 +338,7 @@ string * VirtualMachineManager::format_message( if ( !tm_command.empty() ) { - oss << "" << tm_command << "" + oss << "" << "" << disk_id << "" << "" << disk_target_path << ""; } @@ -1365,7 +1373,6 @@ void VirtualMachineManager::attach_action( Nebula::instance().get_tm()->prolog_transfer_command( vm, disk, - disk_id, system_tm_mad, opennebula_hostname, os, @@ -1428,6 +1435,121 @@ error_common: return; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachineManager::detach_action( + int vid) +{ + VirtualMachine * vm; + const VirtualMachineManagerDriver * vmd; + + ostringstream os; + string vm_tmpl; + string * drv_msg; + string tm_command; + string system_tm_mad; + string opennebula_hostname; + string epilog_cmd; + string disk_path; + string error_str; + + const VectorAttribute * disk; + int disk_id; + + Nebula& nd = Nebula::instance(); + + // Get the VM from the pool + vm = vmpool->get(vid,true); + + if (vm == 0) + { + return; + } + + if (!vm->hasHistory()) + { + goto error_history; + } + + // Get the driver for this VM + vmd = get(vm->get_vmm_mad()); + + if ( vmd == 0 ) + { + goto error_driver; + } + + disk = vm->get_attach_disk(); + + if ( disk == 0 ) + { + goto error_disk; + } + + system_tm_mad = nd.get_system_ds_tm_mad(); + opennebula_hostname = nd.get_nebula_hostname(); + + disk_id = disk->vector_value("DISK_ID", disk_id); + + Nebula::instance().get_tm()->epilog_transfer_command(vm,disk,os,error_str); + + epilog_cmd = os.str(); + + os.str(""); + os << vm->get_remote_system_dir() << "/disk." << disk_id; + + disk_path = os.str(); + + // Invoke driver method + drv_msg = format_message( + vm->get_hostname(), + vm->get_vnm_mad(), + "", + "", + vm->get_deploy_id(), + "", + "", + "", + disk_id, + epilog_cmd, + disk_path, + vm->to_xml(vm_tmpl)); + + + vmd->detach(vid, *drv_msg); + + delete drv_msg; + + vm->unlock(); + + return; + +error_disk: + os.str(""); + os << "detach_action, could not find disk to detach"; + goto error_common; + +error_history: + os.str(""); + os << "detach_action, VM has no history"; + goto error_common; + +error_driver: + os.str(""); + os << "detach_action, error getting driver " << vm->get_vmm_mad(); + goto error_common; + +error_common: + Nebula &ne = Nebula::instance(); + LifeCycleManager * lcm = ne.get_lcm(); + + lcm->trigger(LifeCycleManager::DETACH_FAILURE, vid); + + vm->log("VMM", Log::ERROR, os); + vm->unlock(); + return; +} /* ************************************************************************** */ /* MAD Loading */ diff --git a/src/vmm/VirtualMachineManagerDriver.cc b/src/vmm/VirtualMachineManagerDriver.cc index 3955d052a1..440326660e 100644 --- a/src/vmm/VirtualMachineManagerDriver.cc +++ b/src/vmm/VirtualMachineManagerDriver.cc @@ -354,6 +354,25 @@ void VirtualMachineManagerDriver::protocol( lcm->trigger(LifeCycleManager::ATTACH_FAILURE, id); } } + else if ( action == "DETACH" ) + { + Nebula &ne = Nebula::instance(); + LifeCycleManager *lcm = ne.get_lcm(); + + if ( result == "SUCCESS" ) + { + vm->log("VMM",Log::ERROR,"VM Disk Successfully detached."); + + lcm->trigger(LifeCycleManager::DETACH_SUCCESS, id); + } + else + { + log_error(vm,os,is,"Error detaching VM Disk"); + vmpool->update(vm); + + lcm->trigger(LifeCycleManager::DETACH_FAILURE, id); + } + } else if ( action == "POLL" ) { if (result == "SUCCESS") From 0f59a29e483793b60ecfd54cd87cf6abac880c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 14 Jun 2012 18:03:58 +0200 Subject: [PATCH 47/47] Feature #1223: Use the disk_id to generate the disk.i paths everywhere --- src/tm/TransferManager.cc | 26 ++++++++++++++++++-------- src/vmm/LibVirtDriverKVM.cc | 8 +++++--- src/vmm/LibVirtDriverVMware.cc | 8 +++++--- src/vmm/VirtualMachineManager.cc | 4 ++-- src/vmm/XenDriver.cc | 4 +++- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/tm/TransferManager.cc b/src/tm/TransferManager.cc index 7ca03c6517..10b62a337e 100644 --- a/src/tm/TransferManager.cc +++ b/src/tm/TransferManager.cc @@ -504,6 +504,7 @@ void TransferManager::prolog_migr_action(int vid) string tm_mad; string system_tm_mad; string ds_id; + int disk_id; vector attrs; int num; @@ -564,6 +565,7 @@ void TransferManager::prolog_migr_action(int vid) tm_mad = disk->vector_value("TM_MAD"); ds_id = disk->vector_value("DATASTORE_ID"); + disk->vector_value_str("DISK_ID", disk_id); if ( tm_mad.empty() || ds_id.empty() ) { @@ -574,9 +576,9 @@ void TransferManager::prolog_migr_action(int vid) xfr << "MV " << tm_mad << " " << vm->get_previous_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << vm->get_oid() << " " << ds_id << endl; } @@ -629,6 +631,7 @@ void TransferManager::prolog_resume_action(int vid) string tm_mad; string system_tm_mad; string ds_id; + int disk_id; vector attrs; int num; @@ -688,6 +691,7 @@ void TransferManager::prolog_resume_action(int vid) tm_mad = disk->vector_value("TM_MAD"); ds_id = disk->vector_value("DATASTORE_ID"); + disk->vector_value_str("DISK_ID", disk_id); if ( tm_mad.empty() || ds_id.empty() ) { @@ -698,9 +702,9 @@ void TransferManager::prolog_resume_action(int vid) xfr << "MV " << tm_mad << " " << nd.get_nebula_hostname() << ":" - << vm->get_system_dir() << "/disk." << i << " " + << vm->get_system_dir() << "/disk." << disk_id << " " << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << vm->get_oid() << " " << ds_id << endl; } @@ -935,6 +939,7 @@ void TransferManager::epilog_stop_action(int vid) string tm_mad; string system_tm_mad; string ds_id; + int disk_id; VirtualMachine * vm; Nebula& nd = Nebula::instance(); @@ -995,6 +1000,7 @@ void TransferManager::epilog_stop_action(int vid) tm_mad = disk->vector_value("TM_MAD"); ds_id = disk->vector_value("DATASTORE_ID"); + disk->vector_value_str("DISK_ID", disk_id); if (tm_mad.empty() || ds_id.empty()) { @@ -1005,9 +1011,9 @@ void TransferManager::epilog_stop_action(int vid) xfr << "MV " << tm_mad << " " << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << nd.get_nebula_hostname() << ":" - << vm->get_system_dir() << "/disk." << i << " " + << vm->get_system_dir() << "/disk." << disk_id << " " << vm->get_oid() << " " << ds_id << endl; } @@ -1058,6 +1064,7 @@ void TransferManager::epilog_delete_action(int vid) string system_tm_mad; string tm_mad; string ds_id; + int disk_id; VirtualMachine * vm; Nebula& nd = Nebula::instance(); @@ -1118,6 +1125,7 @@ void TransferManager::epilog_delete_action(int vid) tm_mad = disk->vector_value("TM_MAD"); ds_id = disk->vector_value("DATASTORE_ID"); + disk->vector_value_str("DISK_ID", disk_id); if ( tm_mad.empty() || ds_id.empty() ) { @@ -1128,7 +1136,7 @@ void TransferManager::epilog_delete_action(int vid) xfr << "DELETE " << tm_mad << " " << vm->get_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << vm->get_oid() << " " << ds_id << endl; } @@ -1178,6 +1186,7 @@ void TransferManager::epilog_delete_previous_action(int vid) string system_tm_mad; string tm_mad; string ds_id; + int disk_id; VirtualMachine * vm; Nebula& nd = Nebula::instance(); @@ -1238,6 +1247,7 @@ void TransferManager::epilog_delete_previous_action(int vid) tm_mad = disk->vector_value("TM_MAD"); ds_id = disk->vector_value("DATASTORE_ID"); + disk->vector_value_str("DISK_ID", disk_id); if (tm_mad.empty() || ds_id.empty()) { @@ -1248,7 +1258,7 @@ void TransferManager::epilog_delete_previous_action(int vid) xfr << "DELETE " << tm_mad << " " << vm->get_previous_hostname() << ":" - << vm->get_remote_system_dir() << "/disk." << i << " " + << vm->get_remote_system_dir() << "/disk." << disk_id << " " << vm->get_oid() << " " << ds_id << endl; } diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index b307f96103..1859b5862c 100644 --- a/src/vmm/LibVirtDriverKVM.cc +++ b/src/vmm/LibVirtDriverKVM.cc @@ -54,6 +54,7 @@ int LibVirtDriver::deployment_description_kvm( string ro = ""; string driver = ""; string cache = ""; + int disk_id; string default_driver = ""; string default_driver_cache = ""; bool readonly; @@ -306,6 +307,7 @@ int LibVirtDriver::deployment_description_kvm( bus = disk->vector_value("BUS"); driver = disk->vector_value("DRIVER"); cache = disk->vector_value("CACHE"); + disk->vector_value_str("DISK_ID", disk_id); if (target.empty()) { @@ -335,19 +337,19 @@ int LibVirtDriver::deployment_description_kvm( { file << "\t\t" << endl << "\t\t\t" << endl; + << "/disk." << disk_id << "'/>" << endl; } else if ( type == "CDROM" ) { file << "\t\t" << endl << "\t\t\t" << endl; + << "/disk." << disk_id << "'/>" << endl; } else { file << "\t\t" << endl << "\t\t\t" << endl; + << "/disk." << disk_id << "'/>" << endl; } // ---- target device to map the disk ---- diff --git a/src/vmm/LibVirtDriverVMware.cc b/src/vmm/LibVirtDriverVMware.cc index 05b4cff863..61fd36b7da 100644 --- a/src/vmm/LibVirtDriverVMware.cc +++ b/src/vmm/LibVirtDriverVMware.cc @@ -55,6 +55,7 @@ int LibVirtDriver::deployment_description_vmware( string source = ""; string datastore = ""; string driver = ""; + int disk_id; string default_driver = ""; bool readonly; @@ -199,6 +200,7 @@ int LibVirtDriver::deployment_description_vmware( bus = disk->vector_value("BUS"); source = disk->vector_value("SOURCE"); driver = disk->vector_value("DRIVER"); + disk->vector_value_str("DISK_ID", disk_id); if (target.empty()) { @@ -226,19 +228,19 @@ int LibVirtDriver::deployment_description_vmware( { file << "\t\t" << endl; file << "\t\t\tget_oid() - << "/disk." << i << "'/>" << endl; + << "/disk." << disk_id << "'/>" << endl; } else if ( type == "CDROM" ) { file << "\t\t" << endl; file << "\t\t\tget_oid() - << "/disk." << i << ".iso'/>" << endl; + << "/disk." << disk_id << ".iso'/>" << endl; } else { file << "\t\t" << endl << "\t\t\t" << endl; + << "/disk." << disk_id << "/disk.vmdk'/>" << endl; } file << "\t\t\tvector_value("DISK_ID", disk_id); + disk->vector_value("DISK_ID", disk_id); Nebula::instance().get_tm()->prolog_transfer_command( vm, @@ -1490,7 +1490,7 @@ void VirtualMachineManager::detach_action( system_tm_mad = nd.get_system_ds_tm_mad(); opennebula_hostname = nd.get_nebula_hostname(); - disk_id = disk->vector_value("DISK_ID", disk_id); + disk->vector_value("DISK_ID", disk_id); Nebula::instance().get_tm()->epilog_transfer_command(vm,disk,os,error_str); diff --git a/src/vmm/XenDriver.cc b/src/vmm/XenDriver.cc index 7ffa4fa944..a9ad8a16f6 100644 --- a/src/vmm/XenDriver.cc +++ b/src/vmm/XenDriver.cc @@ -50,6 +50,7 @@ int XenDriver::deployment_description( string ro = ""; string type = ""; string driver = ""; + int disk_id; string default_driver = ""; string mode; @@ -237,6 +238,7 @@ int XenDriver::deployment_description( type = disk->vector_value("TYPE"); ro = disk->vector_value("READONLY"); driver = disk->vector_value("DRIVER"); + disk->vector_value_str("DISK_ID", disk_id); if ( target.empty() ) { @@ -276,7 +278,7 @@ int XenDriver::deployment_description( } } - file << vm->get_remote_system_dir() << "/disk." << i << "," + file << vm->get_remote_system_dir() << "/disk." << disk_id << "," << target << "," << mode << "'," << endl;