diff --git a/include/Document.h b/include/Document.h index d78592b1de..ba1b74891f 100644 --- a/include/Document.h +++ b/include/Document.h @@ -69,6 +69,31 @@ public: return type; }; + /** + * Tries to get the DB lock. This is a mutex requested by external + * applications, not related to the internal mutex lock. The object + * must be locked (internal memory mutex) before this method is called + * + * @param owner String to identify who requested the lock + * + * @return 0 if the lock was granted, -1 if the object is already locked + */ + int lock_db(const string& owner) + { + return PoolObjectSQL::lock_db(owner); + }; + + /** + * Unlocks the DB lock for external applications. The object must be locked + * (internal memory mutex) before this method is called + * + * @param owner String to identify who requested the lock + */ + void unlock_db(const string& owner) + { + return PoolObjectSQL::unlock_db(owner); + }; + private: // ------------------------------------------------------------------------- // Friends diff --git a/include/PoolObjectSQL.h b/include/PoolObjectSQL.h index 41bd7c7d6e..cc2e5f8af0 100644 --- a/include/PoolObjectSQL.h +++ b/include/PoolObjectSQL.h @@ -117,6 +117,9 @@ public: other_m(0), other_a(0), obj_template(0), + locked(false), + lock_owner(""), + lock_expires(0), table(_table) { pthread_mutex_init(&mutex,0); @@ -707,6 +710,40 @@ protected: return 0; }; + /** + * Tries to get the DB lock. This is a mutex requested by external + * applications, not related to the internal mutex lock. The object + * must be locked (internal memory mutex) before this method is called + * + * @param owner String to identify who requested the lock + * + * @return 0 if the lock was granted, -1 if the object is already locked + */ + int lock_db(const string& owner); + + /** + * Unlocks the DB lock for external applications. The object must be locked + * (internal memory mutex) before this method is called + * + * @param owner String to identify who requested the lock + */ + void unlock_db(const string& owner); + + /** + * Prints the lock info into a string in XML format + * @param xml the resulting XML string + * @return a reference to the generated string + */ + string& lock_db_to_xml(string& xml) const; + + /** + * Rebuilds the lock info from the xml. ObjectXML::update_from_str + * must be called before this method + * + * @return 0 on success, -1 otherwise + */ + int lock_db_from_xml(); + /** * The object's unique ID */ @@ -773,12 +810,32 @@ protected: */ Template * obj_template; + /** + * Flag for the DB lock + */ + bool locked; + + /** + * Owner of the DB lock + */ + string lock_owner; + + /** + * Expiration time for the DB lock + */ + time_t lock_expires; + private: /** * Characters that can not be in a name */ static const string INVALID_NAME_CHARS; + /** + * Expiration time for the lock stored in the DB + */ + static const int LOCK_DB_EXPIRATION; + /** * The PoolSQL, friend to easily manipulate its Objects */ diff --git a/include/Request.h b/include/Request.h index f6ea6a5482..dde6e37701 100644 --- a/include/Request.h +++ b/include/Request.h @@ -268,6 +268,14 @@ protected: */ void success_response(const string& val, RequestAttributes& att); + /** + * Builds an XML-RPC response updating retval. After calling this function + * the xml-rpc execute method should return + * @param val to be returned to the client + * @param att the specific request attributes + */ + void success_response(bool val, RequestAttributes& att); + /** * Builds an XML-RPC response updating retval. After calling this function * the xml-rpc excute method should return diff --git a/include/RequestManagerLock.h b/include/RequestManagerLock.h new file mode 100644 index 0000000000..0aaace5f1d --- /dev/null +++ b/include/RequestManagerLock.h @@ -0,0 +1,122 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs */ +/* */ +/* 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_LOCK_H_ +#define REQUEST_MANAGER_LOCK_H_ + +#include "Request.h" +#include "Nebula.h" + +using namespace std; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class RequestManagerLock: public Request +{ +protected: + RequestManagerLock(const string& method_name, + const string& help) + :Request(method_name, "A:sis", help) + { + auth_op = AuthRequest::MANAGE; + }; + + ~RequestManagerLock(){}; + + /* -------------------------------------------------------------------- */ + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); + + virtual int lock_db(PoolObjectSQL * object, const string& owner) = 0; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class RequestManagerUnlock: public Request +{ +protected: + RequestManagerUnlock(const string& method_name, + const string& help) + :Request(method_name, "A:sis", help) + { + auth_op = AuthRequest::MANAGE; + }; + + ~RequestManagerUnlock(){}; + + /* -------------------------------------------------------------------- */ + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); + + virtual void unlock_db(PoolObjectSQL * object, const string& owner) = 0; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class DocumentLock : public RequestManagerLock +{ +public: + DocumentLock(): + RequestManagerLock("DocumentLock", + "Tries to acquire the object's lock") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_docpool(); + auth_object = PoolObjectSQL::DOCUMENT; + }; + + ~DocumentLock(){}; + + int lock_db(PoolObjectSQL * object, const string& owner) + { + return static_cast(object)->lock_db(owner); + }; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class DocumentUnlock : public RequestManagerUnlock +{ +public: + DocumentUnlock(): + RequestManagerUnlock("DocumentUnlock", + "Unlocks the object") + { + Nebula& nd = Nebula::instance(); + pool = nd.get_docpool(); + auth_object = PoolObjectSQL::DOCUMENT; + }; + + ~DocumentUnlock(){}; + + void unlock_db(PoolObjectSQL * object, const string& owner) + { + return static_cast(object)->unlock_db(owner); + }; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +#endif diff --git a/src/document/Document.cc b/src/document/Document.cc index 674083e63d..d06618236e 100644 --- a/src/document/Document.cc +++ b/src/document/Document.cc @@ -191,6 +191,7 @@ string& Document::to_xml(string& xml) const ostringstream oss; string template_xml; string perm_str; + string lock_str; oss << "" << "" << oid << "" @@ -201,6 +202,7 @@ string& Document::to_xml(string& xml) const << "" << name << "" << "" << type << "" << perms_to_xml(perm_str) + << lock_db_to_xml(lock_str) << obj_template->to_xml(template_xml) << ""; @@ -232,6 +234,9 @@ int Document::from_xml(const string& xml) // Permissions rc += perms_from_xml(); + // Lock info + rc += lock_db_from_xml(); + // Get associated classes ObjectXML::get_nodes("/DOCUMENT/TEMPLATE", content); diff --git a/src/pool/PoolObjectSQL.cc b/src/pool/PoolObjectSQL.cc index f7c6d5715a..cd2a898e72 100644 --- a/src/pool/PoolObjectSQL.cc +++ b/src/pool/PoolObjectSQL.cc @@ -22,6 +22,8 @@ const string PoolObjectSQL::INVALID_NAME_CHARS = "&|:\\\";/'#{}$<>"; +const int PoolObjectSQL::LOCK_DB_EXPIRATION = 120; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -481,3 +483,67 @@ bool PoolObjectSQL::name_is_valid(const string& obj_name, return true; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int PoolObjectSQL::lock_db(const string& owner) +{ + if (locked && time(0) < lock_expires) + { + return -1; + } + + locked = true; + lock_expires = time(0) + LOCK_DB_EXPIRATION; + lock_owner = owner; + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void PoolObjectSQL::unlock_db(const string& owner) +{ + // Check if owner == lock_owner? + + locked = false; + lock_expires = 0; + lock_owner = ""; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +string& PoolObjectSQL::lock_db_to_xml(string& xml) const +{ + ostringstream oss; + int locked_int = locked ? 1 : 0; + + oss << "" + << "" << locked_int << "" + << "" + << "" << lock_expires << "" + << ""; + + xml = oss.str(); + return xml; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int PoolObjectSQL::lock_db_from_xml() +{ + int rc = 0; + int locked_int; + + rc += xpath(locked_int, "/*/LOCK/LOCKED", 0); + rc += xpath(lock_owner, "/*/LOCK/OWNER", ""); + rc += xpath(lock_expires, "/*/LOCK/EXPIRES", 0); + + locked = locked_int; + + return rc; +} diff --git a/src/rm/Request.cc b/src/rm/Request.cc index ccc0b7d8b1..fc547cb68a 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -573,6 +573,23 @@ void Request::success_response(const string& val, RequestAttributes& att) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void Request::success_response(bool val, RequestAttributes& att) +{ + vector arrayData; + + arrayData.push_back(xmlrpc_c::value_boolean(true)); + arrayData.push_back(xmlrpc_c::value_boolean(val)); + arrayData.push_back(xmlrpc_c::value_int(SUCCESS)); + + + xmlrpc_c::value_array arrayresult(arrayData); + + *(att.retval) = arrayresult; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + string Request::object_name(PoolObjectSQL::ObjectType ob) { switch (ob) diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 1addfe9842..c2fb8892a2 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -27,6 +27,7 @@ #include "RequestManagerChmod.h" #include "RequestManagerClone.h" #include "RequestManagerRename.h" +#include "RequestManagerLock.h" #include "RequestManagerVirtualNetwork.h" #include "RequestManagerVirtualMachine.h" @@ -368,6 +369,10 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr doc_info(new DocumentInfo()); xmlrpc_c::methodPtr secg_info(new SecurityGroupInfo()); + // Lock Methods + xmlrpc_c::methodPtr doc_lock(new DocumentLock()); + xmlrpc_c::methodPtr doc_unlock(new DocumentUnlock()); + // PoolInfo Methods xmlrpc_c::methodPtr hostpool_info(new HostPoolInfo()); xmlrpc_c::methodPtr datastorepool_info(new DatastorePoolInfo()); @@ -706,7 +711,9 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.document.chown", doc_chown); RequestManagerRegistry.addMethod("one.document.chmod", doc_chmod); RequestManagerRegistry.addMethod("one.document.clone", doc_clone); - RequestManagerRegistry.addMethod("one.document.rename", doc_rename); + RequestManagerRegistry.addMethod("one.document.rename", doc_rename); + RequestManagerRegistry.addMethod("one.document.lock", doc_lock); + RequestManagerRegistry.addMethod("one.document.unlock", doc_unlock); RequestManagerRegistry.addMethod("one.documentpool.info",docpool_info); diff --git a/src/rm/RequestManagerLock.cc b/src/rm/RequestManagerLock.cc new file mode 100644 index 0000000000..2763aad0bb --- /dev/null +++ b/src/rm/RequestManagerLock.cc @@ -0,0 +1,96 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs */ +/* */ +/* 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 "RequestManagerLock.h" + +using namespace std; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void RequestManagerLock::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int oid = xmlrpc_c::value_int(paramList.getInt(1)); + string owner = xmlrpc_c::value_string(paramList.getString(2)); + + PoolObjectSQL * object; + string error_str; + int rc; + + if ( basic_authorization(oid, att) == false ) + { + return; + } + + object = pool->get(oid,true); + + if ( object == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),oid), + att); + return; + } + + rc = lock_db(object, owner); + + pool->update(object); + + object->unlock(); + + success_response((rc == 0), att); + + return; +} + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void RequestManagerUnlock::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int oid = xmlrpc_c::value_int(paramList.getInt(1)); + string owner = xmlrpc_c::value_string(paramList.getString(2)); + + PoolObjectSQL * object; + string error_str; + + if ( basic_authorization(oid, att) == false ) + { + return; + } + + object = pool->get(oid,true); + + if ( object == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),oid), + att); + return; + } + + unlock_db(object, owner); + + pool->update(object); + + object->unlock(); + + success_response(oid, att); + + return; +} diff --git a/src/rm/SConstruct b/src/rm/SConstruct index 4948a4c7b7..71a1dce1f0 100644 --- a/src/rm/SConstruct +++ b/src/rm/SConstruct @@ -46,6 +46,7 @@ source_files=[ 'RequestManagerProxy.cc', 'RequestManagerVdc.cc', 'RequestManagerDatastore.cc', + 'RequestManagerLock.cc', ] # Build library