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/Attribute.h b/include/Attribute.h index d302419987..2e3ae4ef07 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; @@ -251,7 +254,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 @@ -290,7 +304,25 @@ 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()); + } + + /** + * Removes given the vector attribute + * @param name of the vector attribute + */ + void remove(const string& name); + /** * Returns the attribute type */ diff --git a/include/AuthManager.h b/include/AuthManager.h index 0d325495eb..ea4da3ccb4 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; @@ -43,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(){}; @@ -99,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 * @@ -146,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) */ @@ -230,287 +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); -}; - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -/** - * 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..2ae3e04471 --- /dev/null +++ b/include/AuthRequest.h @@ -0,0 +1,211 @@ +/* -------------------------------------------------------------------------- */ +/* 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" + +#include "SyncRequest.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 SyncRequest +{ +public: + AuthRequest(int _uid, int _gid): uid(_uid),gid(_gid),self_authorize(true){}; + + ~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; + + /** + * 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/DispatchManager.h b/include/DispatchManager.h index 1f9aa56ecb..e22beea804 100644 --- a/include/DispatchManager.h +++ b/include/DispatchManager.h @@ -239,6 +239,37 @@ public: int vid, bool do_resched); + /** + * Starts the attach disk action. + * + * @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( + VirtualMachine * vm, + 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/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/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 2d3d8789ca..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 { @@ -125,6 +126,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(Template* 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/LifeCycleManager.h b/include/LifeCycleManager.h index bad3e951af..55664b63ca 100644 --- a/include/LifeCycleManager.h +++ b/include/LifeCycleManager.h @@ -58,6 +58,10 @@ 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 */ + 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 */ @@ -172,6 +176,14 @@ private: void epilog_failure_action(int vid); + void attach_success_action(int vid); + + 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/MadManager.h b/include/MadManager.h index 2cd552f665..43cfd434f5 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,28 @@ 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 + */ + void 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); + private: /** * Function to lock the Manager @@ -155,11 +183,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/include/Quota.h b/include/Quota.h new file mode 100644 index 0000000000..1c8de9bf98 --- /dev/null +++ b/include/Quota.h @@ -0,0 +1,175 @@ +/* -------------------------------------------------------------------------- */ +/* 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(vector * quotas, 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(Template* tmpl, string& error) = 0; + + /** + * Decrement usage counters when deallocating image + * @param tmpl template for the resource + */ + 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, + 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(){}; + + /** + * 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 + */ + + /** + * Name of the quota used in the templates + */ + const char * template_name; + + /** + * The name of the quota metrics + */ + const char ** metrics; + + /** + * Length + */ + 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); + + /** + * 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 + */ + 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 + */ + void add(VectorAttribute * nq) + { + attributes.insert(make_pair(nq->name(), nq)); + } + + /** + * 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); + + /** + * 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 for the defined quota metrics from a given attribute + * @param va the attribute with the limits + * @param limits stores the known limits + * @return 0 on success + */ + int get_limits(const VectorAttribute * va, map& limits); +}; + +#endif /*QUOTA_H_*/ diff --git a/include/QuotaDatastore.h b/include/QuotaDatastore.h new file mode 100644 index 0000000000..02964e141c --- /dev/null +++ b/include/QuotaDatastore.h @@ -0,0 +1,68 @@ +/* -------------------------------------------------------------------------- */ +/* 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_DATASTORE_H_ +#define QUOTA_DATASTORE_H_ + +#include "Quota.h" + +/** + * DataStore Quotas, defined as: + * DATASTORE = [ + * ID = + * 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 QuotaDatastore : public Quota +{ +public: + + QuotaDatastore():Quota("DATASTORE_QUOTA", + "DATASTORE", + DS_METRICS, + NUM_DS_METRICS) + {}; + + ~QuotaDatastore(){}; + + /** + * 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 * DS_METRICS[]; + + static const int NUM_DS_METRICS; +}; + +#endif /*QUOTA_DATASTORE_H_*/ \ No newline at end of file diff --git a/include/QuotaImage.h b/include/QuotaImage.h new file mode 100644 index 0000000000..6d75c62648 --- /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 = + * RVMS = + * RVMS _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/QuotaNetwork.h b/include/QuotaNetwork.h new file mode 100644 index 0000000000..defb73e482 --- /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..8b430e10ee --- /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, overrides base to not to use 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/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/Request.h b/include/Request.h index 9c479d97b6..2faeef213f 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; @@ -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) -------- */ @@ -130,7 +156,54 @@ protected: */ bool basic_authorization(int oid, AuthRequest::Operation op, RequestAttributes& att); - + + /** + * 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) + { + 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 + * for the request. + * @param tmpl describing the object + * @param att the specific request attributes + */ + void quota_rollback(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att); + /** * Actual Execution method for the request. Must be implemented by the * XML-RPC requests @@ -239,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/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/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/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/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 00326f47fa..029932581d 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -166,6 +166,40 @@ 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); +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +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/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/include/Template.h b/include/Template.h index ab4ab441fd..0b4f061f58 100644 --- a/include/Template.h +++ b/include/Template.h @@ -134,6 +134,30 @@ 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) + { + 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())); + } + /** * Removes an attribute from the template. The attributes are returned. The * attributes MUST be freed by the calling funtion @@ -152,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/TransferManager.h b/include/TransferManager.h index 6977ad76fb..1de34727c1 100644 --- a/include/TransferManager.h +++ b/include/TransferManager.h @@ -94,6 +94,46 @@ 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, + 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/User.h b/include/User.h index 770b5943bc..f8aa724fcc 100644 --- a/include/User.h +++ b/include/User.h @@ -19,6 +19,7 @@ #include "PoolSQL.h" #include "UserTemplate.h" +#include "Quotas.h" using namespace std; @@ -166,7 +167,12 @@ public: { return new UserTemplate; } - + + /** + * Object quotas, provides set and check interface + */ + Quotas quota; + private: // ------------------------------------------------------------------------- // Friends @@ -289,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/include/UserPool.h b/include/UserPool.h index afe2e54e36..046224bbd5 100644 --- a/include/UserPool.h +++ b/include/UserPool.h @@ -131,7 +131,8 @@ 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 */ diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 12739eefb7..a2bcef425a 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 }; // ------------------------------------------------------------------------- @@ -116,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 @@ -569,6 +585,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 // ------------------------------------------------------------------------ @@ -726,6 +753,67 @@ 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); + + /** + * + * @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 + * + * @return the disk waiting for an attachment action, or 0 + */ + VectorAttribute* get_attach_disk(); + + /** + * Cleans the ATTACH = YES attribute from the disks + * + * @return 0 on success, -1 otherwise + */ + 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(); + + /** + * 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 5691425563..b4bdeef93e 100644 --- a/include/VirtualMachineManager.h +++ b/include/VirtualMachineManager.h @@ -56,7 +56,9 @@ public: POLL, TIMER, DRIVER_CANCEL, - FINALIZE + FINALIZE, + ATTACH, + DETACH }; /** @@ -199,6 +201,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 +215,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 +298,22 @@ 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); + + /** + * 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 f7dce5d7ef..5fe1331969 100644 --- a/include/VirtualMachineManagerDriver.h +++ b/include/VirtualMachineManagerDriver.h @@ -232,6 +232,30 @@ 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); + } + + /** + * 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/include/VirtualMachinePool.h b/include/VirtualMachinePool.h index 71f3843ee8..e3b75236b8 100644 --- a/include/VirtualMachinePool.h +++ b/include/VirtualMachinePool.h @@ -76,6 +76,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 diff --git a/install.sh b/install.sh index e5a607317b..121f6bc754 100755 --- a/install.sh +++ b/install.sh @@ -245,7 +245,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 \ @@ -392,7 +391,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 @@ -589,7 +587,6 @@ BIN_FILES="src/nebula/oned \ src/cli/onecluster \ src/onedb/onedb \ src/onedb/onezonedb/onezonedb \ - src/authm_mad/remotes/quota/onequota \ src/mad/utils/tty_expect \ share/scripts/one" @@ -616,7 +613,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 \ @@ -747,8 +743,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 #------------------------------------------------------------------------------- @@ -869,23 +863,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" @@ -952,7 +951,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" @@ -1124,6 +1122,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 \ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/etc/oned.conf b/share/etc/oned.conf index 2edc71c8f4..669307e776 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -419,22 +419,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/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..c7ba3b694b 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" @@ -23,8 +24,6 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -time_t AuthManager::_time_out; - const char * AuthManager::auth_driver_name = "auth_exe"; /* -------------------------------------------------------------------------- */ @@ -125,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."); @@ -203,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) { @@ -242,13 +241,12 @@ void AuthManager::authenticate_action(AuthRequest * ar) // Queue the request // ------------------------------------------------------------------------ - ar->id = add_request(ar); + add_request(ar); // ------------------------------------------------------------------------ // Make the request to the driver // ---- -------------------------------------------------------------------- - authm_md->authenticate(ar->id, ar->uid, ar->driver, @@ -287,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 @@ -312,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/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 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.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/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/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/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/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 2b1a532919..7653ec9c86 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 @@ -163,11 +164,35 @@ 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 + "-" + 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 + "-" + 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 + "-" + 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 @@ -207,5 +232,9 @@ class OneUserHelper < OpenNebulaHelper::OneHelper CLIHelper.print_header(str_h1 % "USER TEMPLATE",false) puts user.template_str + + user_hash = user.to_hash + + OneQuotaHelper.format_quota(user_hash['USER']) 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/cli/oneuser b/src/cli/oneuser index 860078664b..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' @@ -196,6 +197,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 = OneQuotaHelper.set_quota(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 +258,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 bce9e58eb8..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; @@ -225,3 +240,39 @@ 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; + + if (iss.fail() || !iss.eof()) + { + value = -1; + return ""; + } + + return it->second; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/datastore_mad/one_datastore.rb b/src/datastore_mad/one_datastore.rb index 2ed7afc99b..c77d937225 100755 --- a/src/datastore_mad/one_datastore.rb +++ b/src/datastore_mad/one_datastore.rb @@ -44,11 +44,11 @@ class DatastoreDriver < OpenNebulaDriver # Image Driver Protocol constants ACTION = { - :mv => "MV", :cp => "CP", :rm => "RM", :mkfs => "MKFS", - :log => "LOG" + :log => "LOG", + :stat => "STAT" } # Register default actions for the protocol @@ -58,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 @@ -77,19 +77,15 @@ 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[: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 ############################################################################ # 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) @@ -106,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) @@ -130,8 +131,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 diff --git a/src/authm_mad/remotes/quota/authorize b/src/datastore_mad/remotes/common/stat similarity index 54% rename from src/authm_mad/remotes/quota/authorize rename to src/datastore_mad/remotes/common/stat index ae5916b4c0..855ecae00b 100755 --- a/src/authm_mad/remotes/quota/authorize +++ b/src/datastore_mad/remotes/common/stat @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +#!/bin/bash # -------------------------------------------------------------------------- # # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # @@ -16,54 +16,44 @@ # limitations under the License. # #--------------------------------------------------------------------------- # -ONE_LOCATION=ENV["ONE_LOCATION"] +############################################################################### +# This script is used to copy a VM image (SRC) to the image repository as DST +# Several SRC types are supported +############################################################################### -if !ONE_LOCATION - RUBY_LIB_LOCATION="/usr/lib/one/ruby" - ETC_LOCATION="/etc/one/" +# -------- Set up the environment to source common tools & conf ------------ + +if [ -z "${ONE_LOCATION}" ]; then + LIB_LOCATION=/usr/lib/one else - RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" - ETC_LOCATION=ONE_LOCATION+"/etc/" -end + LIB_LOCATION=$ONE_LOCATION/lib +fi -$: << RUBY_LIB_LOCATION +. $LIB_LOCATION/sh/scripts_common.sh -require 'scripts_common' -require 'OpenNebula' -require 'quota' +DRIVER_PATH=$(dirname $0) +source ${DRIVER_PATH}/../libfs.sh -user_id = ARGV.shift +# -------- Get cp and datastore arguments from OpenNebula core ------------ -overall_evalutation = ARGV.pop -exit -1 if overall_evalutation.to_i == 0 +DRV_ACTION=$1 +ID=$2 -quota = Quota.new +XPATH="${DRIVER_PATH}/../xpath.rb -b $DRV_ACTION" -#q = { -# :cpu => 10, -# :memory => 2048, -# :storage => 100000, -# :num_vms => 5 -#} -# -#quota.set(1, q) -#OpenNebula.log_debug("quotas: #{quota.get(1)}") +unset i XPATH_ELEMENTS -ARGV.each {|request| - obj, template_or_id, op, owner, acl_eval = request.split(':') +while IFS= read -r -d '' element; do + XPATH_ELEMENTS[i++]="$element" +done < <($XPATH /DS_DRIVER_ACTION_DATA/IMAGE/PATH) - 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 -} +SRC="${XPATH_ELEMENTS[0]}" -#OpenNebula.log_debug("AUTHORIZE ARGS: #{ARGV.join(' ')}") +SIZE=`fs_size $SRC` -exit 0 \ No newline at end of file +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/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/authm_mad/remotes/quota/quota.conf b/src/datastore_mad/remotes/dummy/stat old mode 100644 new mode 100755 similarity index 76% rename from src/authm_mad/remotes/quota/quota.conf rename to src/datastore_mad/remotes/dummy/stat index 4d66cfb48b..390fb72944 --- a/src/authm_mad/remotes/quota/quota.conf +++ b/src/datastore_mad/remotes/dummy/stat @@ -1,3 +1,5 @@ +#!/bin/sh + # -------------------------------------------------------------------------- # # Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # # # @@ -14,16 +16,4 @@ # 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: - +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..65583e827b --- /dev/null +++ b/src/datastore_mad/remotes/fs/stat @@ -0,0 +1 @@ +../common/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..65583e827b --- /dev/null +++ b/src/datastore_mad/remotes/iscsi/stat @@ -0,0 +1 @@ +../common/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..370858e417 100644 --- a/src/datastore_mad/remotes/libfs.sh +++ b/src/datastore_mad/remotes/libfs.sh @@ -88,14 +88,23 @@ 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 +115,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..65583e827b --- /dev/null +++ b/src/datastore_mad/remotes/lvm/stat @@ -0,0 +1 @@ +../common/stat \ 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..65583e827b --- /dev/null +++ b/src/datastore_mad/remotes/vmware/stat @@ -0,0 +1 @@ +../common/stat \ No newline at end of file diff --git a/src/dm/DispatchManagerActions.cc b/src/dm/DispatchManagerActions.cc index a245389409..75df37e554 100644 --- a/src/dm/DispatchManagerActions.cc +++ b/src/dm/DispatchManagerActions.cc @@ -676,6 +676,7 @@ error: return -2; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -683,7 +684,15 @@ int DispatchManager::finalize( int vid) { VirtualMachine * vm; - ostringstream oss; + ostringstream oss; + Template * tmpl; + + User * user; + Group * group; + + int uid; + int gid; + VirtualMachine::VmState state; vm = vmpool->get(vid,true); @@ -701,6 +710,8 @@ int DispatchManager::finalize( Nebula& nd = Nebula::instance(); TransferManager * tm = nd.get_tm(); LifeCycleManager * lcm = nd.get_lcm(); + UserPool * upool = nd.get_upool(); + GroupPool * gpool = nd.get_gpool(); switch (state) { @@ -722,17 +733,55 @@ int DispatchManager::finalize( vmpool->update(vm); vm->log("DiM", Log::INFO, "New VM state is DONE."); + + uid = vm->get_uid(); + gid = vm->get_gid(); + tmpl = vm->clone_template(); + + vm->unlock(); + + if ( uid != UserPool::ONEADMIN_ID ) + { + + 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; break; case VirtualMachine::ACTIVE: lcm->trigger(LifeCycleManager::DELETE,vid); + vm->unlock(); break; + case VirtualMachine::DONE: + vm->unlock(); break; } - vm->unlock(); - return 0; } @@ -793,3 +842,126 @@ int DispatchManager::resubmit(int vid) return rc; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int DispatchManager::attach( + VirtualMachine * vm, + VirtualMachineTemplate * tmpl, + string & error_str) +{ + ostringstream oss; + int rc; + int vid = vm->get_oid(); + + 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 ) + { + goto error_state; + } + + rc = vm->attach_disk(tmpl, error_str); + + if ( rc != 0 ) + { + goto error; + } + + // TODO: Cancel resched? + // vm->set_resched(false); + + 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."; + error_str = oss.str(); + + NebulaLog::log("DiM", Log::ERROR, error_str); + + vm->unlock(); + delete tmpl; + + 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/dm/DispatchManagerStates.cc b/src/dm/DispatchManagerStates.cc index a26a965bcf..b2305cb026 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,22 @@ void DispatchManager::stop_success_action(int vid) void DispatchManager::done_action(int vid) { - VirtualMachine * vm; + VirtualMachine * vm; + 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; + Group * group; + vm = vmpool->get(vid,true); if ( vm == 0 ) @@ -130,6 +143,44 @@ void DispatchManager::done_action(int vid) vm->release_network_leases(); vm->release_disk_images(); + + uid = vm->get_uid(); + gid = vm->get_gid(); + tmpl = vm->clone_template(); + + vm->unlock(); + + /* ---------------- Update Group & User quota counters -------------- */ + + if ( uid != UserPool::ONEADMIN_ID ) + { + 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 { @@ -137,10 +188,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/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/Image.cc b/src/image/Image.cc index b0eb1b53a9..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; @@ -464,7 +443,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(); } @@ -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/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..0258c70885 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -18,6 +18,10 @@ #include "NebulaLog.h" #include "ImagePool.h" #include "SSLTools.h" +#include "SyncRequest.h" +#include "Template.h" +#include "Nebula.h" + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -259,10 +263,23 @@ 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; + 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); @@ -303,7 +320,11 @@ 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(); + gid = img->get_gid(); + if (source.empty()) { string err_str; @@ -326,6 +347,41 @@ int ImageManager::delete_image(int iid, const string& ds_data) delete drv_msg; + /* -------------------- Update Group & User quota counters -------------- */ + + Template img_usage; + + img_usage.add("DATASTORE", ds_id); + img_usage.add("SIZE", size); + + if ( uid != UserPool::ONEADMIN_ID ) + { + user = upool->get(uid, true); + + if ( user != 0 ) + { + user->quota.ds_del(&img_usage); + + upool->update(user); + + 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; } @@ -395,6 +451,80 @@ int ImageManager::register_image(int iid, const string& ds_data) return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int ImageManager::stat_image(Template* img_tmpl, + const string& ds_data, + string& res) +{ + const ImageManagerDriver* imd = get(); + + string* drv_msg; + string type_att; + + ostringstream img_data; + + SyncRequest sr; + + 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_data.str(), ds_data); + + 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..06e77496fd 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,329 @@ 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(); + + rc = ipool->drop(image, tmp_error); + + image->unlock(); + + if ( result == "FAILURE" ) + { + goto error; + } + else 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. Manually remove image source " + << source << " to completely delete the image"; + + 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 +419,6 @@ void ImageManagerDriver::protocol( { if ( action == "LOG" ) { - string info; - is.clear(); getline(is,info); @@ -131,145 +431,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 +453,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; } 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/lcm/LifeCycleManager.cc b/src/lcm/LifeCycleManager.cc index b3a72dda42..c9603a54a5 100644 --- a/src/lcm/LifeCycleManager.cc +++ b/src/lcm/LifeCycleManager.cc @@ -129,6 +129,22 @@ 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 DETACH_SUCCESS: + aname = "DETACH_SUCCESS"; + break; + + case DETACH_FAILURE: + aname = "DETACH_FAILURE"; + break; + case DEPLOY: aname = "DEPLOY"; break; @@ -262,6 +278,22 @@ void LifeCycleManager::do_action(const string &action, void * arg) { epilog_failure_action(vid); } + else if (action == "ATTACH_SUCCESS") + { + attach_success_action(vid); + } + else if (action == "ATTACH_FAILURE") + { + 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 0165d3ec8d..a8aad281f3 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -834,3 +834,102 @@ void LifeCycleManager::failure_action(VirtualMachine * vm) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void LifeCycleManager::attach_success_action(int vid) +{ + VirtualMachine * vm; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + vm->attach_success(); + + vm->set_state(VirtualMachine::RUNNING); + + vmpool->update(vm); + + vm->unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void LifeCycleManager::attach_failure_action(int 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 +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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/mad/MadManager.cc b/src/mad/MadManager.cc index 1a72a647f4..287b61c0ba 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,110 @@ 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(); + +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void MadManager::add_request(SyncRequest *ar) +{ + static int request_id = 0; + + lock(); + + ar->id = request_id++; + + sync_requests.insert(sync_requests.end(),make_pair(ar->id,ar)); + + unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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 811c8fce9a..79195359fe 100644 --- a/src/nebula/Nebula.cc +++ b/src/nebula/Nebula.cc @@ -512,8 +512,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 { 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/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/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" 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, diff --git a/src/rm/Request.cc b/src/rm/Request.cc index d3ffafdf9a..c0e9af5284 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -112,6 +112,246 @@ bool Request::basic_authorization(int oid, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +bool Request::user_quota_authorization (Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att, + string& error_str) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + User * user; + + bool rc = false; + + user = upool->get(att.uid, true); + + if ( user == 0 ) + { + error_str = "User not found"; + return false; + } + + switch (object) + { + case PoolObjectSQL::IMAGE: + rc = user->quota.ds_check(tmpl, error_str); + break; + + case PoolObjectSQL::VM: + case PoolObjectSQL::TEMPLATE: + rc = user->quota.vm_check(tmpl, error_str); + break; + + default: + break; + } + + if (rc == true) + { + upool->update(user); + } + + user->unlock(); + + 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 ) + { + 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::user_quota_rollback(Template * tmpl, + PoolObjectSQL::ObjectType object, + RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + UserPool * upool = nd.get_upool(); + + User * user; + + user = upool->get(att.uid, true); + + if ( user == 0 ) + { + return; + } + + switch (object) + { + case PoolObjectSQL::IMAGE: + user->quota.ds_del(tmpl); + break; + + case PoolObjectSQL::VM: + case PoolObjectSQL::TEMPLATE: + user->quota.vm_del(tmpl); + break; + + default: + break; + } + + upool->update(user); + + 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);; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void Request::failure_response(ErrorCode ec, const string& str_val, RequestAttributes& att) { diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 8ce9deaec9..8e2fdbbc76 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 @@ -233,6 +234,10 @@ 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()); + + // Group Methods + xmlrpc_c::methodPtr group_set_quota(new GroupSetQuota()); // VMTemplate Methods xmlrpc_c::methodPtr template_instantiate(new VMTemplateInstantiate()); @@ -244,6 +249,8 @@ 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_detach(new VirtualMachineDetach()); xmlrpc_c::methodPtr vm_pool_acct(new VirtualMachinePoolAccounting()); xmlrpc_c::methodPtr vm_pool_monitoring(new VirtualMachinePoolMonitoring()); @@ -352,6 +359,8 @@ 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.vm.detach", vm_detach); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); RequestManagerRegistry.addMethod("one.vmpool.accounting", vm_pool_acct); @@ -384,6 +393,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); @@ -409,6 +419,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/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 8d7221c1cd..a290e3936e 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -15,7 +15,6 @@ /* -------------------------------------------------------------------------- */ #include "RequestManagerAllocate.h" -#include "NebulaLog.h" #include "Nebula.h" #include "PoolObjectSQL.h" @@ -80,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 ) { @@ -98,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); @@ -111,6 +112,13 @@ bool VirtualMachineAllocate::allocate_authorization( return false; } + // -------------------------- Check Quotas ---------------------------- + + if ( quota_authorization(tmpl, att) == false ) + { + return false; + } + return true; } @@ -266,8 +274,17 @@ 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); + Template tmpl_back(*tmpl); + + int rc = vmpool->allocate(att.uid, att.gid, att.uname, att.gname, ttmpl, &id, + error_str, false); + + if ( rc < 0 ) + { + quota_rollback(&tmpl_back, att); + } + + return rc; } @@ -297,8 +314,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,9 +332,12 @@ 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); + ImageManager * imagem = nd.get_imagem(); + ImageTemplate * tmpl = new ImageTemplate; + Template img_usage; + Datastore * ds; Image::DiskType ds_disk_type; @@ -359,13 +385,45 @@ 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 -------------- + + img_usage.add("DATASTORE", ds_id); + img_usage.add("SIZE", size_str); if ( att.uid != 0 ) { AuthRequest ar(att.uid, att.gid); - string tmpl_str = ""; + string tmpl_str; + // ------------------ Check permissions and ACLs ---------------------- tmpl->to_xml(tmpl_str); ar.add_create_auth(auth_object, tmpl_str); // CREATE IMAGE @@ -381,6 +439,14 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, delete tmpl; return; } + + // -------------------------- Check Quotas ---------------------------- + + if ( quota_authorization(&img_usage, att) == false ) + { + delete tmpl; + return; + } } rc = ipool->allocate(att.uid, @@ -396,6 +462,8 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, error_str); if ( rc < 0 ) { + quota_rollback(&img_usage, att); + failure_response(INTERNAL, allocate_error(error_str), att); return; } @@ -455,9 +523,6 @@ int HostAllocate::pool_allocate( } -/* -------------------------------------------------------------------------- */ - - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerChown.cc b/src/rm/RequestManagerChown.cc index 768a87c5e8..6651a3d9d7 100644 --- a/src/rm/RequestManagerChown.cc +++ b/src/rm/RequestManagerChown.cc @@ -23,6 +23,94 @@ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +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 ( 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()); + } + + 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 ( quota_authorization(tmpl, att_new) == false ) + { + delete tmpl; + return 0; + } + + quota_rollback(tmpl, att_old); + + object = pool->get(oid,true); + + if ( object == 0 ) + { + quota_rollback(tmpl, att_new); + + 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 +190,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; } @@ -118,7 +220,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/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 00c2cb883a..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; } @@ -133,6 +133,45 @@ 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; + + 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 ) + { + return -1; + } + + user = static_cast(pool->get(user_id,true)); + + if ( user == 0 ) + { + return -1; + } + + rc = user->quota.set("a_tmpl, error_str); + + pool->update(user); + + user->unlock(); + + return rc; +} 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; } diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index ab2908045c..d39e3da8c7 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -518,33 +518,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 ) @@ -553,6 +552,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 // ------------------------------------------------------------------------- @@ -570,6 +575,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; @@ -613,3 +620,107 @@ 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)); + + 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 + + vm = get_vm(id, att); + + if ( vm == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),id), + att); + delete tmpl; + + return; + } + + rc = dm->attach(vm, tmpl, error_str); + + if ( rc != 0 ) + { + failure_response(ACTION, + request_error(error_str, ""), + att); + + return; + } + + success_response(id, att); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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/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', 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/tm/TransferManager.cc b/src/tm/TransferManager.cc index 9e8448d980..10b62a337e 100644 --- a/src/tm/TransferManager.cc +++ b/src/tm/TransferManager.cc @@ -201,6 +201,145 @@ void TransferManager::do_action(const string &action, void * arg) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int TransferManager::prolog_transfer_command( + VirtualMachine * vm, + const VectorAttribute * disk, + 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; + 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); + + 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 +347,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 +398,8 @@ void TransferManager::prolog_action(int vid) goto error_file; } + opennebula_hostname = nd.get_nebula_hostname(); + // ------------------------------------------------------------------------- // Image Transfer Commands // ------------------------------------------------------------------------- @@ -275,106 +414,15 @@ void TransferManager::prolog_action(int vid) continue; } - type = disk->vector_value("TYPE"); + rc = prolog_transfer_command(vm, disk, 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 +471,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; @@ -458,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; @@ -518,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() ) { @@ -528,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; } @@ -583,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; @@ -642,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() ) { @@ -652,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; } @@ -695,17 +745,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(); @@ -763,55 +881,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 @@ -857,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(); @@ -917,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()) { @@ -927,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; } @@ -980,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(); @@ -1040,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() ) { @@ -1050,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; } @@ -1100,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(); @@ -1160,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()) { @@ -1170,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/um/Quota.cc b/src/um/Quota.cc new file mode 100644 index 0000000000..8fd83d5655 --- /dev/null +++ b/src/um/Quota.cc @@ -0,0 +1,361 @@ +/* -------------------------------------------------------------------------- */ +/* 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::get_quota(const string& id, VectorAttribute ** va) +{ + map::iterator it; + VectorAttribute * q; + + istringstream iss(id); + int id_i; + + *va = 0; + + if ( id.empty() ) + { + 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); + + if (q->vector_value("ID") == id) + { + *va = q; + return 0; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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()); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int Quota::set(vector * new_quotas, string& error) +{ + vector::iterator it; + + VectorAttribute * iq; + VectorAttribute * tq; + string id; + + for ( it = new_quotas->begin(); it != new_quotas->end(); it++) + { + iq = dynamic_cast(*it); + + if ( iq == 0 ) + { + goto error_limits; + } + + id = iq->vector_value("ID"); + + if ( get_quota(id, &tq) == -1 ) + { + goto error_limits; + } + + if ( tq == 0 ) + { + VectorAttribute * nq; + + if ((nq = new_quota(iq)) == 0) + { + goto error_limits; + } + + add(nq); + } + else + { + if (update_limits(tq, iq) != 0) + { + goto error_limits; + } + } + } + + return 0; + +error_limits: + ostringstream oss; + + 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; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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 of " << limit << " reached for " << metrics[i] + << " quota in " << 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_str(metrics[i], limit_i); + + if ( limit_i < 0 ) //No quota, NaN or negative + { + return -1; + } + else + { + quota->replace(metrics[i], limit); + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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_str(metrics[i], limit_i); + + if ( limit_i < 0 ) //No quota, NaN or negative + { + limit = "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 new file mode 100644 index 0000000000..833919f9fa --- /dev/null +++ b/src/um/QuotaDatastore.cc @@ -0,0 +1,85 @@ +/* -------------------------------------------------------------------------- */ +/* 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 "QuotaDatastore.h" + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +const char * QuotaDatastore::DS_METRICS[] = {"SIZE", "IMAGES"}; + +const int QuotaDatastore::NUM_DS_METRICS = 2; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +bool QuotaDatastore::check(Template * tmpl, string& error) +{ + map ds_request; + + string ds_id; + int size; + + tmpl->get("DATASTORE", ds_id); + + if ( ds_id.empty() ) + { + error = "Datastore not defined for image"; + return false; + } + + if ( tmpl->get("SIZE", size) == false ) + { + error = "Size not defined for image"; + return false; + } + + ds_request.insert(make_pair("IMAGES",1)); + ds_request.insert(make_pair("SIZE", size)); + + return check_quota(ds_id, ds_request, error); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void QuotaDatastore::del(Template * tmpl) +{ + map ds_request; + + string ds_id; + int size; + + tmpl->get("DATASTORE", ds_id); + + if ( ds_id.empty() ) + { + return; + } + + if ( tmpl->get("SIZE", size) == false ) + { + return; + } + + ds_request.insert(make_pair("IMAGES",1)); + ds_request.insert(make_pair("SIZE", size)); + + del_quota(ds_id, ds_request); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/um/QuotaImage.cc b/src/um/QuotaImage.cc new file mode 100644 index 0000000000..84d7d859f3 --- /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[] = {"RVMS"}; + +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("RVMS",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("RVMS",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/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..deb07ea196 --- /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 = 3; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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 175d2c58ee..893811e548 100644 --- a/src/um/SConstruct +++ b/src/um/SConstruct @@ -23,7 +23,12 @@ lib_name='nebula_um' # Sources to generate the library source_files=[ 'User.cc', - 'UserPool.cc' + 'UserPool.cc', + 'Quota.cc', + 'QuotaDatastore.cc', + 'QuotaNetwork.cc', + 'QuotaVirtualMachine.cc', + 'QuotaImage.cc' ] # Build library diff --git a/src/um/User.cc b/src/um/User.cc index 7e6d78855b..0dfc86cc8f 100644 --- a/src/um/User.cc +++ b/src/um/User.cc @@ -136,8 +136,10 @@ error_common: string& User::to_xml(string& xml) const { - ostringstream oss; - string template_xml; + ostringstream oss; + + string template_xml; + string quota_xml; int enabled_int = enabled?1:0; @@ -151,6 +153,7 @@ string& User::to_xml(string& xml) const "" << auth_driver <<""<< "" << enabled_int <<"" << obj_template->to_xml(template_xml) << + quota.to_xml(quota_xml) << ""; xml = oss.str(); @@ -194,6 +197,8 @@ int User::from_xml(const string& xml) rc += obj_template->from_xml_node(content[0]); ObjectXML::free_nodes(content); + + rc += quota.from_xml(this); if (rc != 0) { diff --git a/src/um/UserPool.cc b/src/um/UserPool.cc index 5f1b89813e..90afc539a8 100644 --- a/src/um/UserPool.cc +++ b/src/um/UserPool.cc @@ -762,4 +762,3 @@ int UserPool::authorize(AuthRequest& ar) return rc; } - diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index 3612f15e84..ce21c3666e 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -212,8 +212,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 @@ -241,6 +241,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 // ------------------------------------------------------------------------ @@ -321,9 +339,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: @@ -897,6 +922,7 @@ void VirtualMachine::get_requirements (int& cpu, int& memory, int& disk) return; } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -1083,6 +1109,358 @@ 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 = tmpl->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; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +// 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; + 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::attach_success() +{ + int num_disks; + vector disks; + VectorAttribute * disk; + bool removed; + + num_disks = obj_template->get("DISK", disks); + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + 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; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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/vm/VirtualMachinePool.cc b/src/vm/VirtualMachinePool.cc index e4afafca8a..8b0ac72538 100644 --- a/src/vm/VirtualMachinePool.cc +++ b/src/vm/VirtualMachinePool.cc @@ -235,7 +235,7 @@ int VirtualMachinePool::allocate ( // Insert the Object in the pool // ------------------------------------------------------------------------ - *oid = PoolSQL::allocate(vm,error_str); + *oid = PoolSQL::allocate(vm, error_str); return *oid; } diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index 4952f733f6..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; @@ -131,11 +132,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; @@ -311,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()) { @@ -340,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 5161011caf..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; @@ -119,11 +120,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; @@ -204,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()) { @@ -231,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\t"; } + if ( !tm_command.empty() ) + { + oss << "" + << "" << disk_id << "" + << "" << disk_target_path << ""; + } + else + { + oss << "" + << "" + << ""; + } + oss << tmpl << ""; @@ -380,6 +412,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 +512,9 @@ void VirtualMachineManager::save_action( "", "", vm->get_checkpoint_file(), + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->save(vid, *drv_msg); @@ -556,6 +594,9 @@ void VirtualMachineManager::shutdown_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->shutdown(vid, *drv_msg); @@ -630,6 +671,9 @@ void VirtualMachineManager::reboot_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->reboot(vid, *drv_msg); @@ -699,6 +743,9 @@ void VirtualMachineManager::reset_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->reset(vid, *drv_msg); @@ -769,6 +816,9 @@ void VirtualMachineManager::cancel_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->cancel(vid, *drv_msg); @@ -847,6 +897,9 @@ void VirtualMachineManager::cancel_previous_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->cancel(vid, *drv_msg); @@ -921,6 +974,9 @@ void VirtualMachineManager::migrate_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->migrate(vid, *drv_msg); @@ -1001,6 +1057,9 @@ void VirtualMachineManager::restore_action( "", "", vm->get_checkpoint_file(), + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->restore(vid, *drv_msg); @@ -1076,6 +1135,9 @@ void VirtualMachineManager::poll_action( "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->poll(vid, *drv_msg); @@ -1236,6 +1298,9 @@ void VirtualMachineManager::timer_action() "", "", "", + 0, + "", + "", vm->to_xml(vm_tmpl)); vmd->poll(*it, *drv_msg); @@ -1248,6 +1313,244 @@ 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 ) + { + goto error_disk; + } + + system_tm_mad = nd.get_system_ds_tm_mad(); + opennebula_hostname = nd.get_nebula_hostname(); + + disk->vector_value("DISK_ID", disk_id); + + Nebula::instance().get_tm()->prolog_transfer_command( + vm, + disk, + 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_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"; + 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; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +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->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 651d9bbefe..440326660e 100644 --- a/src/vmm/VirtualMachineManagerDriver.cc +++ b/src/vmm/VirtualMachineManagerDriver.cc @@ -335,6 +335,44 @@ void VirtualMachineManagerDriver::protocol( vmpool->update(vm); } } + else if ( action == "ATTACH" ) + { + Nebula &ne = Nebula::instance(); + LifeCycleManager *lcm = ne.get_lcm(); + + 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 == "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") diff --git a/src/vmm/XenDriver.cc b/src/vmm/XenDriver.cc index 0add29a1d9..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; @@ -109,11 +110,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; @@ -242,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() ) { @@ -281,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; 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 { 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:" ] 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()) {