diff --git a/include/AuthRequest.h b/include/AuthRequest.h index e5a0e92cac..f30317dc1a 100644 --- a/include/AuthRequest.h +++ b/include/AuthRequest.h @@ -46,10 +46,14 @@ public: */ 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 */ + USE = 0x1LL, /**< Auth. to use an object */ + USE_NO_LCK = 0x11LL, /**< Auth. to use an object no lockable */ + MANAGE = 0x2LL, /**< Auth. to perform management actions */ + MANAGE_NO_LCK = 0x12LL, /**< Auth. to perform management actions of an object no lockable */ + ADMIN = 0x4LL, /**< Auth. to perform administrative actions */ + ADMIN_NO_LCK = 0x14LL, /**< Auth. to perform administrative actions of an object no lockable */ + CREATE = 0x8LL, /**< Auth. to create an object */ + CREATE_NO_LCK = 0x18LL /**< Auth. to create an object of an object no lockable */ }; static string operation_to_str(Operation op) diff --git a/include/PoolObjectAuth.h b/include/PoolObjectAuth.h index 79070e9dc3..2a6185f978 100644 --- a/include/PoolObjectAuth.h +++ b/include/PoolObjectAuth.h @@ -45,7 +45,8 @@ public: other_a(0), disable_all_acl(false), disable_cluster_acl(false), - disable_group_acl(false) {}; + disable_group_acl(false), + locked(0) {}; void get_acl_rules(AclRule& owner_rule, AclRule& group_rule, @@ -81,6 +82,8 @@ public: bool disable_all_acl; // All objects of this type (e.g. NET/*) bool disable_cluster_acl; // All objects in a cluster (e.g. NET/%100) bool disable_group_acl; // All objects own by this group (e.g. NET/@101) + + int locked; }; #endif /*POOL_OBJECT_AUTH_H_*/ diff --git a/include/PoolObjectSQL.h b/include/PoolObjectSQL.h index d01d175c65..cd130d8ee4 100644 --- a/include/PoolObjectSQL.h +++ b/include/PoolObjectSQL.h @@ -70,6 +70,19 @@ public: VMGROUP = 0x0020000000000000LL }; + /** + * OpenNebula objects. This definitions are used for define the level of lock + */ + enum LockStates + { + ST_NONE = 0x0LL, + ST_USE = 0x1LL, + ST_MANAGE = 0x2LL, + ST_ADMIN = 0x4LL + }; + + static const long int LockableObject; + static string type_to_str(ObjectType ob) { switch (ob) @@ -96,6 +109,18 @@ public: } }; + static string lock_state_to_str(LockStates ob) + { + switch (ob) + { + case ST_NONE: return "NONE" ; break; + case ST_USE: return "USE" ; break; + case ST_MANAGE: return "MANAGE" ; break; + case ST_ADMIN: return "ADMIN" ; break; + default: return ""; + } + }; + /* ---------------------------------------------------------------------- */ PoolObjectSQL(int id, @@ -126,9 +151,10 @@ public: other_m(0), other_a(0), obj_template(0), - locked(false), - lock_owner(""), - lock_expires(0), + locked(LockStates::ST_NONE), + lock_owner(-1), + lock_req_id(-1), + lock_time(0), table(_table) { pthread_mutex_init(&mutex,0); @@ -520,7 +546,7 @@ public: * * @return 0 if the lock was granted, -1 if the object is already locked */ - int lock_db(const string& owner); + int lock_db(const int owner,const int req_id, const int level); /** * Unlocks the DB lock for external applications. The object must be locked @@ -528,7 +554,17 @@ public: * * @param owner String to identify who requested the lock */ - void unlock_db(const string& owner); + void unlock_db(const int owner, const int req_id); + + /** + * 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 + */ + LockStates get_lock_state(){ + return locked; + } protected: @@ -730,17 +766,22 @@ protected: /** * Flag for the DB lock */ - bool locked; + LockStates locked; /** * Owner of the DB lock */ - string lock_owner; + int lock_owner; + + /** + * Owner of the DB lock + */ + int lock_req_id; /** * Expiration time for the DB lock */ - time_t lock_expires; + time_t lock_time; private: /** diff --git a/include/Request.h b/include/Request.h index ad7e671ed5..b5f6ab2e65 100644 --- a/include/Request.h +++ b/include/Request.h @@ -131,7 +131,8 @@ public: ACTION = 0x0800, XML_RPC_API = 0x1000, INTERNAL = 0x2000, - ALLOCATE = 0x4000 + ALLOCATE = 0x4000, + LOCKED = 0x8000 }; /** diff --git a/include/RequestManagerInfo.h b/include/RequestManagerInfo.h index 6c36e74af4..76ec93e6a5 100644 --- a/include/RequestManagerInfo.h +++ b/include/RequestManagerInfo.h @@ -33,7 +33,7 @@ protected: const string& help) :Request(method_name, "A:si", help) { - auth_op = AuthRequest::USE; + auth_op = AuthRequest::USE_NO_LCK; leader_only = false; }; diff --git a/include/RequestManagerLock.h b/include/RequestManagerLock.h index 55d6a91991..d690d3206a 100644 --- a/include/RequestManagerLock.h +++ b/include/RequestManagerLock.h @@ -33,7 +33,7 @@ protected: const string& help) :Request(method_name, "A:sis", help) { - auth_op = AuthRequest::MANAGE; + auth_op = AuthRequest::MANAGE_NO_LCK; }; ~RequestManagerLock(){}; @@ -43,7 +43,10 @@ protected: void request_execute(xmlrpc_c::paramList const& _paramList, RequestAttributes& att); - virtual int lock_db(PoolObjectSQL * object, const string& owner) = 0; + int lock_db(PoolObjectSQL * object, const int owner, const int req_id, const int level) + { + return object->lock_db(owner, req_id, level); + }; }; /* ------------------------------------------------------------------------- */ @@ -54,9 +57,9 @@ class RequestManagerUnlock: public Request protected: RequestManagerUnlock(const string& method_name, const string& help) - :Request(method_name, "A:sis", help) + :Request(method_name, "A:sii", help) { - auth_op = AuthRequest::MANAGE; + auth_op = AuthRequest::MANAGE_NO_LCK; }; ~RequestManagerUnlock(){}; @@ -66,7 +69,10 @@ protected: void request_execute(xmlrpc_c::paramList const& _paramList, RequestAttributes& att); - virtual void unlock_db(PoolObjectSQL * object, const string& owner) = 0; + void unlock_db(PoolObjectSQL * object, const int owner, const int req_id) + { + object->unlock_db(owner, req_id); + }; }; /* ------------------------------------------------------------------------- */ @@ -85,11 +91,6 @@ public: }; ~DocumentLock(){}; - - int lock_db(PoolObjectSQL * object, const string& owner) - { - return static_cast(object)->lock_db(owner); - }; }; /* ------------------------------------------------------------------------- */ @@ -108,15 +109,241 @@ public: }; ~DocumentUnlock(){}; - - void unlock_db(PoolObjectSQL * object, const string& owner) - { - return static_cast(object)->unlock_db(owner); - }; }; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +class VirtualMachineLock: public RequestManagerLock +{ +public: + VirtualMachineLock(): + RequestManagerLock("one.vm.lock", + "Lock a VM"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VM; + pool = nd.get_vmpool(); + }; + + ~VirtualMachineLock(){}; +}; +/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +class VirtualMachineUnlock: public RequestManagerUnlock +{ +public: + VirtualMachineUnlock(): + RequestManagerUnlock("one.vm.unlock", + "Unlock a VM"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VM; + pool = nd.get_vmpool(); + }; + + ~VirtualMachineUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VMTemplateLock: public RequestManagerLock +{ +public: + VMTemplateLock(): + RequestManagerLock("one.template.lock", + "Lock a Template"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::TEMPLATE; + pool = nd.get_tpool(); + }; + + ~VMTemplateLock(){}; +}; +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VMTemplateUnlock: public RequestManagerUnlock +{ +public: + VMTemplateUnlock(): + RequestManagerUnlock("one.template.unlock", + "Unlock a Template"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::TEMPLATE; + pool = nd.get_tpool(); + }; + + ~VMTemplateUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualNetworkLock: public RequestManagerLock +{ +public: + VirtualNetworkLock(): + RequestManagerLock("one.vn.lock", + "Lock a VNet"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::NET; + pool = nd.get_vnpool(); + }; + + ~VirtualNetworkLock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualNetworkUnlock: public RequestManagerUnlock +{ +public: + VirtualNetworkUnlock(): + RequestManagerUnlock("one.vn.unlock", + "Unlock a VNet"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::NET; + pool = nd.get_vnpool(); + }; + + ~VirtualNetworkUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class ImageLock: public RequestManagerLock +{ +public: + ImageLock(): + RequestManagerLock("one.image.lock", + "Lock a Image"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::IMAGE; + pool = nd.get_ipool(); + }; + + ~ImageLock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class ImageUnlock: public RequestManagerUnlock +{ +public: + ImageUnlock(): + RequestManagerUnlock("one.image.unlock", + "Unlock a Image"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::IMAGE; + pool = nd.get_ipool(); + }; + + ~ImageUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class MarketPlaceAppLock: public RequestManagerLock +{ +public: + MarketPlaceAppLock(): + RequestManagerLock("one.marketapp.lock", + "Lock a MarketPlaceApp"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::MARKETPLACEAPP; + pool = nd.get_apppool(); + }; + + ~MarketPlaceAppLock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class MarketPlaceAppUnlock: public RequestManagerUnlock +{ +public: + MarketPlaceAppUnlock(): + RequestManagerUnlock("one.marketapp.unlock", + "Unlock a MarketPlaceApp"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::MARKETPLACEAPP; + pool = nd.get_apppool(); + }; + + ~MarketPlaceAppUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualRouterLock: public RequestManagerLock +{ +public: + VirtualRouterLock(): + RequestManagerLock("one.vrouter.lock", + "Lock a VirtualRouter"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VROUTER; + pool = nd.get_vrouterpool(); + }; + + ~VirtualRouterLock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VirtualRouterUnlock: public RequestManagerUnlock +{ +public: + VirtualRouterUnlock(): + RequestManagerUnlock("one.vrouter.unlock", + "Unlock a VirtualRouter"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VROUTER; + pool = nd.get_vrouterpool(); + }; + + ~VirtualRouterUnlock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VMGroupLock: public RequestManagerLock +{ +public: + VMGroupLock(): + RequestManagerLock("one.vmgroup.lock", + "Lock a VMGroup"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VMGROUP; + pool = nd.get_vmgrouppool(); + }; + + ~VMGroupLock(){}; +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +class VMGroupUnlock: public RequestManagerUnlock +{ +public: + VMGroupUnlock(): + RequestManagerUnlock("one.vmgroup.unlock", + "Unlock a VMGroup"){ + Nebula& nd = Nebula::instance(); + auth_object = PoolObjectSQL::VMGROUP; + pool = nd.get_vmgrouppool(); + }; + + ~VMGroupUnlock(){}; +}; #endif diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 069df26e37..52e0ed3009 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -32,7 +32,7 @@ protected: RequestManagerVirtualMachine(const string& method_name, const string& help, const string& params) - :Request(method_name,params,help) + :Request(method_name, params, help) { Nebula& nd = Nebula::instance(); pool = nd.get_vmpool(); @@ -193,13 +193,17 @@ public: RequestManagerVirtualMachine("one.vm.monitoring", "Returns the virtual machine monitoring records", "A:si"){ - auth_op = AuthRequest::USE; + auth_op = AuthRequest::USE_NO_LCK; }; ~VirtualMachineMonitoring(){}; void request_execute( xmlrpc_c::paramList const& paramList, RequestAttributes& att); + + virtual bool is_locked(xmlrpc_c::paramList const& paramList, RequestAttributes& att){ + return false; + }; }; /* ------------------------------------------------------------------------- */ @@ -521,9 +525,6 @@ public: RequestAttributes& att); }; -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - class VirtualMachineDiskResize : public RequestManagerVirtualMachine { public: diff --git a/src/acl/AclManager.cc b/src/acl/AclManager.cc index bb47f9053d..404809d1a8 100644 --- a/src/acl/AclManager.cc +++ b/src/acl/AclManager.cc @@ -228,6 +228,15 @@ const bool AclManager::authorize( long long user_req; long long resource_oid_req; + if (static_cast(op) & 0x10LL) //No lockable object + { + op = static_cast(op & 0x0FLL); + } + else if (obj_perms.locked > 0 && obj_perms.locked <= static_cast(op)) + { + return false; + } + if ( obj_perms.oid >= 0 ) { resource_oid_req = obj_perms.obj_type | diff --git a/src/authm/AuthManager.cc b/src/authm/AuthManager.cc index 499678cee9..e91124839e 100644 --- a/src/authm/AuthManager.cc +++ b/src/authm/AuthManager.cc @@ -56,7 +56,7 @@ void AuthRequest::add_auth(Operation op, oss << ob_perms.oid << ":"; } - oss << operation_to_str(op) << ":"; + oss << operation_to_str(static_cast(op & 0x0FLL)) << ":"; oss << ob_perms.uid << ":"; diff --git a/src/cli/one_helper.rb b/src/cli/one_helper.rb index d80e24e3b9..9b786703b7 100644 --- a/src/cli/one_helper.rb +++ b/src/cli/one_helper.rb @@ -1267,4 +1267,21 @@ EOT return OpenNebula::Error.new("Remote server error: #{error_message}") end end + + def OpenNebulaHelper.level_lock_to_str(str) + level = str.to_i + if level == 0 + "None" + elsif level == 1 + "Use" + elsif level == 2 + "Manage" + elsif level == 3 + "Admin" + elsif level == 4 + "All" + else + "-" + end + end end diff --git a/src/cli/one_helper/oneimage_helper.rb b/src/cli/one_helper/oneimage_helper.rb index fdb0ee0087..c72743b84c 100644 --- a/src/cli/one_helper/oneimage_helper.rb +++ b/src/cli/one_helper/oneimage_helper.rb @@ -265,6 +265,7 @@ class OneImageHelper < OpenNebulaHelper::OneHelper puts str % ["NAME", image.name] puts str % ["USER", image['UNAME']] puts str % ["GROUP",image['GNAME']] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(image['LOCK/LOCKED'])] puts str % ["DATASTORE",image['DATASTORE']] puts str % ["TYPE", image.type_str] puts str % ["REGISTER TIME", diff --git a/src/cli/one_helper/onemarketapp_helper.rb b/src/cli/one_helper/onemarketapp_helper.rb index 934b4c15f8..e0fbacfdde 100644 --- a/src/cli/one_helper/onemarketapp_helper.rb +++ b/src/cli/one_helper/onemarketapp_helper.rb @@ -150,6 +150,7 @@ class OneMarketPlaceAppHelper < OpenNebulaHelper::OneHelper puts str % ["GROUP", app['GNAME']] puts str % ["MARKETPLACE", app['MARKETPLACE']] puts str % ["STATE", OneMarketPlaceAppHelper.state_to_str(app["STATE"])] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(app['LOCK/LOCKED'])] puts diff --git a/src/cli/one_helper/onetemplate_helper.rb b/src/cli/one_helper/onetemplate_helper.rb index eefd1fa61b..f5ceb4426c 100644 --- a/src/cli/one_helper/onetemplate_helper.rb +++ b/src/cli/one_helper/onetemplate_helper.rb @@ -321,6 +321,7 @@ EOT puts str % ["NAME", template.name] puts str % ["USER", template['UNAME']] puts str % ["GROUP", template['GNAME']] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(template['LOCK/LOCKED'])] puts str % ["REGISTER TIME", OpenNebulaHelper.time_to_str(template['REGTIME'])] puts diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index 40b7da279b..6969dd6db0 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -516,6 +516,7 @@ in the frontend machine. puts str % ["GROUP", vm['GNAME']] puts str % ["STATE", vm.state_str] puts str % ["LCM_STATE", vm.lcm_state_str] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(vm['LOCK/LOCKED'])] puts str % ["RESCHED", OpenNebulaHelper.boolean_to_str(vm['RESCHED'])] puts str % ["HOST", vm['/VM/HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']] if diff --git a/src/cli/one_helper/onevmgroup_helper.rb b/src/cli/one_helper/onevmgroup_helper.rb index 23dc54cce4..ea8c01ed11 100644 --- a/src/cli/one_helper/onevmgroup_helper.rb +++ b/src/cli/one_helper/onevmgroup_helper.rb @@ -102,6 +102,7 @@ class OneVMGroupHelper < OpenNebulaHelper::OneHelper puts str % ["NAME", vmgroup.name] puts str % ["USER", vmgroup['UNAME']] puts str % ["GROUP", vmgroup['GNAME']] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(vmgroup['LOCK/LOCKED'])] CLIHelper.print_header(str_h1 % "PERMISSIONS",false) diff --git a/src/cli/one_helper/onevnet_helper.rb b/src/cli/one_helper/onevnet_helper.rb index 2799f46eac..0647ceeb57 100644 --- a/src/cli/one_helper/onevnet_helper.rb +++ b/src/cli/one_helper/onevnet_helper.rb @@ -219,6 +219,7 @@ class OneVNetHelper < OpenNebulaHelper::OneHelper puts str % ["NAME", vn['NAME']] puts str % ["USER", vn['UNAME']] puts str % ["GROUP", vn['GNAME']] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(vn['LOCK/LOCKED'])] puts str % ["CLUSTERS", OpenNebulaHelper.clusters_str(vn.retrieve_elements("CLUSTERS/ID"))] puts str % ["BRIDGE", vn["BRIDGE"]] diff --git a/src/cli/one_helper/onevrouter_helper.rb b/src/cli/one_helper/onevrouter_helper.rb index c08d41a915..34a1f1f446 100644 --- a/src/cli/one_helper/onevrouter_helper.rb +++ b/src/cli/one_helper/onevrouter_helper.rb @@ -109,6 +109,7 @@ class OneVirtualRouterHelper < OpenNebulaHelper::OneHelper puts str % ["NAME", obj.name] puts str % ["USER", obj['UNAME']] puts str % ["GROUP", obj['GNAME']] + puts str % ["LOCK", OpenNebulaHelper.level_lock_to_str(obj['LOCK/LOCKED'])] puts CLIHelper.print_header(str_h1 % "PERMISSIONS",false) diff --git a/src/cli/oneimage b/src/cli/oneimage index fa0d6632b1..de9d2d8850 100755 --- a/src/cli/oneimage +++ b/src/cli/oneimage @@ -41,6 +41,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -357,4 +381,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do command :top, top_desc, [:filterflag, nil], :options=>list_options do helper.list_pool(options, true, args[0]) end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :vmid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"Image locked") do |i| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + i.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :vmid do + helper.perform_action(args[0],options,"Image unlocked") do |i| + i.unlock + end + end end diff --git a/src/cli/onemarketapp b/src/cli/onemarketapp index b209c4d567..7a34785782 100755 --- a/src/cli/onemarketapp +++ b/src/cli/onemarketapp @@ -44,6 +44,30 @@ CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -279,4 +303,44 @@ CommandParser::CmdParser.new(ARGV) do obj.disable end end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :appid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"MarketPlaceApp locked") do |app| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + app.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :vmid do + helper.perform_action(args[0],options,"MarketPlaceApp unlocked") do |app| + app.unlock + end + end end diff --git a/src/cli/onetemplate b/src/cli/onetemplate index b8a7b8aa5b..0e9ecb2957 100755 --- a/src/cli/onetemplate +++ b/src/cli/onetemplate @@ -41,6 +41,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -341,4 +365,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do command :top, top_desc, [:filterflag, nil], :options=>list_options do helper.list_pool(options, true, args[0]) end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :templateid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"Template locked") do |t| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + t.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :templateid do + helper.perform_action(args[0],options,"Template unlocked") do |t| + t.unlock + end + end end diff --git a/src/cli/onevm b/src/cli/onevm index dc1eedaff5..4e705eb010 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -137,6 +137,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do :description => "Make the new images persistent" } + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -996,6 +1020,42 @@ cmd=CommandParser::CmdParser.new(ARGV) do end end + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Aalid states are: All. + EOT + + command :lock, lock_desc, :vmid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"VM locked") do |vm| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + vm.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :vmid do + helper.perform_action(args[0],options,"VM unlocked") do |vm| + vm.unlock + end + end + # Deprecated commands deprecated_command(:shutdown, 'terminate') diff --git a/src/cli/onevmgroup b/src/cli/onevmgroup index a9edb33217..7e15c27e18 100755 --- a/src/cli/onevmgroup +++ b/src/cli/onevmgroup @@ -40,6 +40,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -182,4 +206,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do o.rename(args[1]) end end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :vmid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"VMGroup locked") do |vmg| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + vmg.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :vmid do + helper.perform_action(args[0],options,"VMGroup unlocked") do |vmg| + vmg.unlock + end + end end diff --git a/src/cli/onevnet b/src/cli/onevnet index d09e9ad7fc..3a5db4e529 100755 --- a/src/cli/onevnet +++ b/src/cli/onevnet @@ -41,6 +41,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -422,4 +446,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do o.rename(args[1]) end end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :vnetid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"VNet locked") do |vnet| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + vnet.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + Valid states are: All. + EOT + + command :unlock, unlock_desc, :vnetid do + helper.perform_action(args[0],options,"VNet unlocked") do |vnet| + vnet.unlock + end + end end diff --git a/src/cli/onevrouter b/src/cli/onevrouter index 5088fcd985..3a8c99d8a9 100755 --- a/src/cli/onevrouter +++ b/src/cli/onevrouter @@ -42,6 +42,30 @@ cmd=CommandParser::CmdParser.new(ARGV) do helper.set_client(options) end + USE={ + :name => "use", + :large => "--use", + :description => "lock use actions" + } + + MANAGE={ + :name => "manage", + :large => "--manage", + :description => "lock manage actions" + } + + ADMIN={ + :name => "admin", + :large => "--admin", + :description => "lock admin actions" + } + + ALL={ + :name => "all", + :large => "--all", + :description => "lock all actions" + } + ######################################################################## # Global Options ######################################################################## @@ -314,4 +338,44 @@ cmd=CommandParser::CmdParser.new(ARGV) do command :top, top_desc, [:filterflag, nil], :options=>list_options do helper.list_pool(options, true, args[0]) end + + lock_desc = <<-EOT.unindent + Locks a VM with differents levels for lock any actions with this VM, show and + monitoring never will be locked. + Valid states are: All. + Levels: + [Use]: locks Admin, Manage and Use actions. + [Manage]: locks Manage and Use actions. + [Admin]: locks only Admin actions. + EOT + + command :lock, lock_desc, :vmid, + :options => [USE, MANAGE, ADMIN, ALL] do + helper.perform_action(args[0],options,"VRouter locked") do |vr| + if !options[:use].nil? + level = 1 + elsif !options[:manage].nil? + level = 2 + elsif !options[:admin].nil? + level = 3 + elsif !options[:all].nil? + level = 4 + else + STDERR.puts "Need to level of lock." + exit -1 + end + vr.lock(level) + end + end + + unlock_desc = <<-EOT.unindent + Unlocks a VM for unlock any actions with this VM. + valid states are: All. + EOT + + command :unlock, unlock_desc, :vmid do + helper.perform_action(args[0],options,"VRouter unlocked") do |vr| + vr.unlock + end + end end diff --git a/src/image/Image.cc b/src/image/Image.cc index 60a9ba4d93..7ad3f2b945 100644 --- a/src/image/Image.cc +++ b/src/image/Image.cc @@ -201,6 +201,7 @@ int Image::insert(SqlDB *db, string& error_str) } state = LOCKED; //LOCKED till the ImageManager copies it to the Repository + lock_db(-1,-1, PoolObjectSQL::LockStates::ST_USE); //-------------------------------------------------------------------------- // Insert the Image @@ -329,6 +330,7 @@ string& Image::to_xml(string& xml) const string clone_collection_xml; string app_clone_collection_xml; string snapshots_xml; + string lock_str; oss << "" << @@ -338,6 +340,7 @@ string& Image::to_xml(string& xml) const "" << uname << "" << "" << gname << "" << "" << name << "" << + lock_db_to_xml(lock_str) << perms_to_xml(perms_xml) << "" << type << "" << "" << disk_type << "" << @@ -409,6 +412,7 @@ int Image::from_xml(const string& xml) rc += xpath(ds_id, "/IMAGE/DATASTORE_ID", -1); rc += xpath(ds_name, "/IMAGE/DATASTORE", "not_found"); + rc += lock_db_from_xml(); // Permissions rc += perms_from_xml(); @@ -804,6 +808,13 @@ void Image::set_state(ImageState _state) { lcm->trigger(LCMAction::DISK_LOCK_FAILURE, *i); } + } else if( _state == LOCKED) + { + lock_db(-1,-1, PoolObjectSQL::LockStates::ST_USE); + } + if (_state != LOCKED ) + { + unlock_db(-1,-1); } state = _state; @@ -820,6 +831,7 @@ void Image::set_state_unlock() switch (state) { case LOCKED: + unlock_db(-1,-1); set_state(READY); break; diff --git a/src/market/MarketPlaceApp.cc b/src/market/MarketPlaceApp.cc index e4dbd866c9..a08c230b27 100644 --- a/src/market/MarketPlaceApp.cc +++ b/src/market/MarketPlaceApp.cc @@ -222,6 +222,7 @@ std::string& MarketPlaceApp::to_xml(std::string& xml) const std::ostringstream oss; std::string template_xml; std::string perm_str; + string lock_str; oss << "" "" << oid << "" << @@ -229,6 +230,7 @@ std::string& MarketPlaceApp::to_xml(std::string& xml) const "" << gid << "" << "" << uname << "" << "" << gname << "" << + lock_db_to_xml(lock_str) << "" << regtime << "" << "" << name << "" << "" << one_util::escape_xml(zone_id) << "" << @@ -294,6 +296,9 @@ int MarketPlaceApp::from_xml(const std::string &xml_str) // ----- Permissions ----- rc += perms_from_xml(); + // ------ Lock ------- + rc += lock_db_from_xml(); + // ----- TEMPLATE ----- ObjectXML::get_nodes("/MARKETPLACEAPP/TEMPLATE", content); diff --git a/src/oca/java/src/org/opennebula/client/image/Image.java b/src/oca/java/src/org/opennebula/client/image/Image.java index 8690508429..087f0214cf 100644 --- a/src/oca/java/src/org/opennebula/client/image/Image.java +++ b/src/oca/java/src/org/opennebula/client/image/Image.java @@ -42,6 +42,8 @@ public class Image extends PoolElement private static final String SNAPSHOTDELETE = METHOD_PREFIX + "snapshotdelete"; private static final String SNAPSHOTREVERT = METHOD_PREFIX + "snapshotrevert"; private static final String SNAPSHOTFLATTEN = METHOD_PREFIX + "snapshotflatten"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; private static final String[] IMAGE_STATES = {"INIT", "READY", "USED", "DISABLED", "LOCKED", @@ -323,6 +325,31 @@ public class Image extends PoolElement return client.call(SNAPSHOTFLATTEN, id, snapId); } + /** + * lock this Image + * + * @param client XML-RPC Client. + * @param id The Image id. + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this Image + * + * @param client XML-RPC Client. + * @param id The Image id. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -622,6 +649,27 @@ public class Image extends PoolElement return snapshotFlatten(client, id, snapId); } + /** + * Lock this Image + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this Image + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/java/src/org/opennebula/client/marketplaceapp/MarketPlaceApp.java b/src/oca/java/src/org/opennebula/client/marketplaceapp/MarketPlaceApp.java index a7ef69698b..ee0d47d079 100644 --- a/src/oca/java/src/org/opennebula/client/marketplaceapp/MarketPlaceApp.java +++ b/src/oca/java/src/org/opennebula/client/marketplaceapp/MarketPlaceApp.java @@ -36,6 +36,8 @@ public class MarketPlaceApp extends PoolElement private static final String CHOWN = METHOD_PREFIX + "chown"; private static final String CHMOD = METHOD_PREFIX + "chmod"; private static final String RENAME = METHOD_PREFIX + "rename"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; private static final String[] MARKETPLACEAPP_STATES = {"INIT", "READY", "LOCKED", "ERROR", "DISABLED"}; @@ -222,6 +224,31 @@ public class MarketPlaceApp extends PoolElement return client.call(RENAME, id, name); } + /** + * lock this MarketPlaceApp + * + * @param client XML-RPC Client. + * @param id The MarketPlaceApp id. + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this MarketPlaceApp + * + * @param client XML-RPC Client. + * @param id The MarketPlaceApp id. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -394,6 +421,27 @@ public class MarketPlaceApp extends PoolElement return rename(client, id, name); } + /** + * Lock this MarketPlaceApp + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this MarketPlaceApp + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/java/src/org/opennebula/client/template/Template.java b/src/oca/java/src/org/opennebula/client/template/Template.java index f115abb323..1953d1b37e 100644 --- a/src/oca/java/src/org/opennebula/client/template/Template.java +++ b/src/oca/java/src/org/opennebula/client/template/Template.java @@ -37,6 +37,8 @@ public class Template extends PoolElement private static final String INSTANTIATE = METHOD_PREFIX + "instantiate"; private static final String CLONE = METHOD_PREFIX + "clone"; private static final String RENAME = METHOD_PREFIX + "rename"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; /** * Creates a new Template representation. @@ -352,6 +354,31 @@ public class Template extends PoolElement return client.call(RENAME, id, name); } + /** + * lock this template + * + * @param client XML-RPC Client. + * @param id The template id. + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this template + * + * @param client XML-RPC Client. + * @param id The template id. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -666,6 +693,27 @@ public class Template extends PoolElement return rename(client, id, name); } + /** + * Lock this template + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this template + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/java/src/org/opennebula/client/vmgroup/VMGroup.java b/src/oca/java/src/org/opennebula/client/vmgroup/VMGroup.java index d18d654b84..ef83e94d77 100644 --- a/src/oca/java/src/org/opennebula/client/vmgroup/VMGroup.java +++ b/src/oca/java/src/org/opennebula/client/vmgroup/VMGroup.java @@ -35,6 +35,8 @@ public class VMGroup extends PoolElement{ private static final String CHOWN = METHOD_PREFIX + "chown"; private static final String CHMOD = METHOD_PREFIX + "chmod"; private static final String RENAME = METHOD_PREFIX + "rename"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; /** * Creates a new vmgroup representation. @@ -197,6 +199,31 @@ public class VMGroup extends PoolElement{ return client.call(RENAME, id, name); } + /** + * lock this vmgroup + * + * @param client XML-RPC Client. + * @param id The id of the target vmgroup. + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this vmgroup + * + * @param client XML-RPC Client. + * @param id The id of the target vmgroup. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -338,6 +365,27 @@ public class VMGroup extends PoolElement{ return rename(client, id, name); } + /** + * Lock this vmgroup + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this vmgroup + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/java/src/org/opennebula/client/vnet/VirtualNetwork.java b/src/oca/java/src/org/opennebula/client/vnet/VirtualNetwork.java index e3b8617dd5..398f58d366 100644 --- a/src/oca/java/src/org/opennebula/client/vnet/VirtualNetwork.java +++ b/src/oca/java/src/org/opennebula/client/vnet/VirtualNetwork.java @@ -42,6 +42,8 @@ public class VirtualNetwork extends PoolElement{ private static final String RENAME = METHOD_PREFIX + "rename"; private static final String RESERVE = METHOD_PREFIX + "reserve"; private static final String FREEAR = METHOD_PREFIX + "free_ar"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; /** * Creates a new virtual network representation. @@ -362,6 +364,31 @@ public class VirtualNetwork extends PoolElement{ return client.call(FREEAR, id, arId); } + /** + * lock this virtual network + * + * @param client XML-RPC Client. + * @param id The virtual network id (nid) + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this virtual network + * + * @param client XML-RPC Client. + * @param id The virtual network id (nid) + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -679,6 +706,27 @@ public class VirtualNetwork extends PoolElement{ return free(client, id, arId); } + /** + * Lock this virtual network + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this virtual network + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/java/src/org/opennebula/client/vrouter/VirtualRouter.java b/src/oca/java/src/org/opennebula/client/vrouter/VirtualRouter.java index da003b1000..6c2d59bbeb 100644 --- a/src/oca/java/src/org/opennebula/client/vrouter/VirtualRouter.java +++ b/src/oca/java/src/org/opennebula/client/vrouter/VirtualRouter.java @@ -38,6 +38,8 @@ public class VirtualRouter extends PoolElement private static final String RENAME = METHOD_PREFIX + "rename"; private static final String ATTACHNIC = METHOD_PREFIX + "attachnic"; private static final String DETACHNIC = METHOD_PREFIX + "detachnic"; + private static final String LOCK = METHOD_PREFIX + "lock"; + private static final String UNLOCK = METHOD_PREFIX + "unlock"; /** * Creates a new VirtualRouter representation. @@ -259,6 +261,31 @@ public class VirtualRouter extends PoolElement return client.call(DETACHNIC, id, nicId); } + /** + * lock this virtual router + * + * @param client XML-RPC Client. + * @param id The virtual router id. + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse lock(Client client, int id, int level) + { + return client.call(LOCK, id, level); + } + + /** + * Unlock this virtual router + * + * @param client XML-RPC Client. + * @param id The virtual router id. + * @return If an error occurs the error message contains the reason. + */ + public static OneResponse unlock(Client client, int id) + { + return client.call(UNLOCK, id); + } + // ================================= // Instanced object XML-RPC methods // ================================= @@ -485,6 +512,27 @@ public class VirtualRouter extends PoolElement return nicDetach(client, id, nicId); } + /** + * Lock this virtual router + * + * @param level Lock level. + * @return If an error occurs the error message contains the reason. + */ + public OneResponse lock(int level) + { + return rename(client, id, level); + } + + /** + * Unlock this virtual router + * + * @return If an error occurs the error message contains the reason. + */ + public OneResponse unlock() + { + return rename(client, id); + } + // ================================= // Helpers // ================================= diff --git a/src/oca/ruby/opennebula/image.rb b/src/oca/ruby/opennebula/image.rb index d434633f17..fbcfd50308 100644 --- a/src/oca/ruby/opennebula/image.rb +++ b/src/oca/ruby/opennebula/image.rb @@ -38,7 +38,9 @@ module OpenNebula :rename => "image.rename", :snapshotdelete => "image.snapshotdelete", :snapshotrevert => "image.snapshotrevert", - :snapshotflatten=> "image.snapshotflatten" + :snapshotflatten=> "image.snapshotflatten", + :lock => "image.lock", + :unlock => "image.unlock" } IMAGE_STATES=%w{INIT READY USED DISABLED LOCKED ERROR CLONE DELETE @@ -296,6 +298,14 @@ module OpenNebula self['GID'].to_i end + def lock(level) + return call(IMAGE_METHODS[:lock], @pe_id, level) + end + + def unlock() + return call(IMAGE_METHODS[:unlock], @pe_id) + end + def public? if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" true diff --git a/src/oca/ruby/opennebula/marketplaceapp.rb b/src/oca/ruby/opennebula/marketplaceapp.rb index 1c58f5a210..a0b1c48121 100644 --- a/src/oca/ruby/opennebula/marketplaceapp.rb +++ b/src/oca/ruby/opennebula/marketplaceapp.rb @@ -30,7 +30,9 @@ module OpenNebula :chown => "marketapp.chown", :chmod => "marketapp.chmod", :rename => "marketapp.rename", - :enable => "marketapp.enable" + :enable => "marketapp.enable", + :lock => "marketapp.lock", + :unlock => "marketapp.unlock" } MARKETPLACEAPP_STATES=%w{INIT READY LOCKED ERROR DISABLED} @@ -263,5 +265,15 @@ module OpenNebula def short_state_str SHORT_MARKETPLACEAPP_STATES[state_str] end + + #Locked a MarketplaceApp + def lock(level) + return call(MARKETPLACEAPP_METHODS[:lock], @pe_id, level) + end + + #Unlocked a MarketplaceApp + def unlock() + return call(MARKETPLACEAPP_METHODS[:unlock], @pe_id) + end end end diff --git a/src/oca/ruby/opennebula/template.rb b/src/oca/ruby/opennebula/template.rb index e089802058..cde90349b6 100644 --- a/src/oca/ruby/opennebula/template.rb +++ b/src/oca/ruby/opennebula/template.rb @@ -33,7 +33,9 @@ module OpenNebula :chown => "template.chown", :chmod => "template.chmod", :clone => "template.clone", - :rename => "template.rename" + :rename => "template.rename", + :lock => "template.lock", + :unlock => "template.unlock" } # Creates a Template description with just its identifier @@ -241,6 +243,16 @@ module OpenNebula self['UID'].to_i end + # Lock a Template + def lock(level) + return call(TEMPLATE_METHODS[:lock], @pe_id, level) + end + + # Unlock a Template + def unlock() + return call(TEMPLATE_METHODS[:unlock], @pe_id) + end + def public? if self['PERMISSIONS/GROUP_U'] == "1" || self['PERMISSIONS/OTHER_U'] == "1" true diff --git a/src/oca/ruby/opennebula/virtual_machine.rb b/src/oca/ruby/opennebula/virtual_machine.rb index 2ae4f75c1d..4a4cfd23b9 100644 --- a/src/oca/ruby/opennebula/virtual_machine.rb +++ b/src/oca/ruby/opennebula/virtual_machine.rb @@ -47,7 +47,9 @@ module OpenNebula :disksnapshotrevert => "vm.disksnapshotrevert", :disksnapshotdelete => "vm.disksnapshotdelete", :diskresize => "vm.diskresize", - :updateconf => "vm.updateconf" + :updateconf => "vm.updateconf", + :lock => "vm.lock", + :unlock => "vm.unlock" } VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED @@ -686,7 +688,17 @@ module OpenNebula # otherwise def updateconf(new_conf) return call(VM_METHODS[:updateconf], @pe_id, new_conf) - end + end + + # Lock a VM + def lock(level) + return call(VM_METHODS[:lock], @pe_id, level) + end + + # Unlock a VM + def unlock() + return call(VM_METHODS[:unlock], @pe_id) + end ######################################################################## # Helpers to get VirtualMachine information @@ -853,7 +865,7 @@ module OpenNebula REMOVE_VNET_ATTRS.each do |attr| nic.delete_element(attr) end - + replace << "NIC = [ " << nic.template_like_str(".").tr("\n", ",\n") << " ] \n" end diff --git a/src/oca/ruby/opennebula/virtual_network.rb b/src/oca/ruby/opennebula/virtual_network.rb index af07aebc10..57aa62ca41 100644 --- a/src/oca/ruby/opennebula/virtual_network.rb +++ b/src/oca/ruby/opennebula/virtual_network.rb @@ -38,7 +38,9 @@ module OpenNebula :release => "vn.release", :rename => "vn.rename", :reserve => "vn.reserve", - :free_ar => "vn.free_ar" + :free_ar => "vn.free_ar", + :lock => "vn.lock", + :unlock => "vn.unlock" } # Creates a VirtualNetwork description with just its identifier @@ -330,6 +332,14 @@ module OpenNebula return array end + def lock(level) + return call(VN_METHODS[:lock], @pe_id, level) + end + + def unlock() + return call(VN_METHODS[:unlock], @pe_id) + end + private def set_publish(published) group_u = published ? 1 : 0 diff --git a/src/oca/ruby/opennebula/virtual_router.rb b/src/oca/ruby/opennebula/virtual_router.rb index 73cf7f5a1a..4a6dfcdf2b 100644 --- a/src/oca/ruby/opennebula/virtual_router.rb +++ b/src/oca/ruby/opennebula/virtual_router.rb @@ -34,6 +34,8 @@ module OpenNebula :rename => "vrouter.rename", :attachnic => "vrouter.attachnic", :detachnic => "vrouter.detachnic", + :lock => "vrouter.lock", + :unlock => "vrouter.unlock" } # Creates a VirtualRouter description with just its identifier @@ -175,6 +177,16 @@ module OpenNebula return call(VIRTUAL_ROUTER_METHODS[:detachnic], @pe_id, nic_id) end + # Lock a VRouter + def lock(level) + return call(VIRTUAL_ROUTER_METHODS[:lock], @pe_id, level) + end + + # Unlock a VRouter + def unlock() + return call(VIRTUAL_ROUTER_METHODS[:unlock], @pe_id) + end + ####################################################################### # Helpers to get VirtualRouter information ####################################################################### diff --git a/src/oca/ruby/opennebula/vm_group.rb b/src/oca/ruby/opennebula/vm_group.rb index fc73df6d59..59c0da3812 100644 --- a/src/oca/ruby/opennebula/vm_group.rb +++ b/src/oca/ruby/opennebula/vm_group.rb @@ -28,7 +28,9 @@ module OpenNebula :delete => "vmgroup.delete", :chown => "vmgroup.chown", :chmod => "vmgroup.chmod", - :rename => "vmgroup.rename" + :rename => "vmgroup.rename", + :lock => "vmgroup.lock", + :unlock => "vmgroup.unlock" } # Creates a VMGroup description with just its identifier @@ -129,6 +131,16 @@ module OpenNebula return call(VMGROUP_METHODS[:rename], @pe_id, name) end + # Lock a VMGroup + def lock(level) + return call(VMGROUP_METHODS[:lock], @pe_id, level) + end + + # Unlock a VMGroup + def unlock() + return call(VMGROUP_METHODS[:unlock], @pe_id) + end + ####################################################################### # Helpers to get VMGroup information ####################################################################### diff --git a/src/pool/PoolObjectSQL.cc b/src/pool/PoolObjectSQL.cc index 49a21298db..d53a099eb2 100644 --- a/src/pool/PoolObjectSQL.cc +++ b/src/pool/PoolObjectSQL.cc @@ -24,6 +24,14 @@ const string PoolObjectSQL::INVALID_NAME_CHARS = "&|:\\\";/'#{}$<>"; const int PoolObjectSQL::LOCK_DB_EXPIRATION = 120; +const long int PoolObjectSQL::LockableObject = PoolObjectSQL::ObjectType::VM + | PoolObjectSQL::ObjectType::TEMPLATE + | PoolObjectSQL::ObjectType::IMAGE + | PoolObjectSQL::ObjectType::MARKETPLACEAPP + | PoolObjectSQL::ObjectType::NET + | PoolObjectSQL::ObjectType::VROUTER + | PoolObjectSQL::ObjectType::VMGROUP; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -369,6 +377,8 @@ void PoolObjectSQL::get_permissions(PoolObjectAuth& auth) auth.other_m = other_m; auth.other_a = other_a; + auth.locked = static_cast(locked); + Clusterable* cl = dynamic_cast(this); if(cl != 0) @@ -511,16 +521,12 @@ bool PoolObjectSQL::name_is_valid(const string& obj_name, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int PoolObjectSQL::lock_db(const string& owner) +int PoolObjectSQL::lock_db(const int owner, const int req_id, const int level) { - if (locked && time(0) < lock_expires) - { - return -1; - } - - locked = true; - lock_expires = time(0) + LOCK_DB_EXPIRATION; + locked = static_cast(level); + lock_time = time(0); lock_owner = owner; + lock_req_id = req_id; return 0; } @@ -528,13 +534,15 @@ int PoolObjectSQL::lock_db(const string& owner) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -void PoolObjectSQL::unlock_db(const string& owner) +void PoolObjectSQL::unlock_db(const int owner, const int req_id) { - // Check if owner == lock_owner? - - locked = false; - lock_expires = 0; - lock_owner = ""; + if ( owner == lock_owner ) + { + locked = LockStates::ST_NONE; + lock_time = time(0); + lock_owner = -1; + lock_req_id = -1; + } } /* -------------------------------------------------------------------------- */ @@ -543,13 +551,15 @@ void PoolObjectSQL::unlock_db(const string& owner) string& PoolObjectSQL::lock_db_to_xml(string& xml) const { ostringstream oss; - int locked_int = locked ? 1 : 0; - - oss << "" - << "" << locked_int << "" - << "" << one_util::escape_xml(lock_owner) << "" - << "" << lock_expires << "" - << ""; + if (locked != LockStates::ST_NONE) + { + oss << "" + << "" << static_cast(locked) << "" + << "" << lock_owner << "" + << "" + << "" << lock_req_id << "" + << ""; + } xml = oss.str(); return xml; @@ -562,12 +572,17 @@ int PoolObjectSQL::lock_db_from_xml() { int rc = 0; int locked_int; + vector content; - rc += xpath(locked_int, "/*/LOCK/LOCKED", 0); - rc += xpath(lock_owner, "/*/LOCK/OWNER", ""); - rc += xpath(lock_expires, "/*/LOCK/EXPIRES", 0); + if (ObjectXML::get_nodes("/*/LOCK/LOCKED", content) > 0) + { + rc += xpath(locked_int, "/*/LOCK/LOCKED", 0); + rc += xpath(lock_req_id, "/*/LOCK/REQ_ID", -1); + rc += xpath(lock_owner, "/*/LOCK/OWNER", -1); + xpath(lock_time, "/*/LOCK/TIME", time(0)); - locked = locked_int; + locked = static_cast(locked_int); + } return rc; } diff --git a/src/rm/Request.cc b/src/rm/Request.cc index b7833d33a9..0685cfce17 100644 --- a/src/rm/Request.cc +++ b/src/rm/Request.cc @@ -722,6 +722,14 @@ string Request::failure_message(ErrorCode ec, RequestAttributes& att) oss << " " << att.resp_msg; } break; + case LOCKED: + oss << "The resource " << obname << " is locked."; + + if ( att.resp_id != -1 ) + { + oss << " [" << att.resp_id << "]."; + } + break; } return oss.str(); diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index a94c9e1f71..3a06d74b22 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -382,6 +382,18 @@ void RequestManager::register_xml_methods() // Lock Methods xmlrpc_c::methodPtr doc_lock(new DocumentLock()); xmlrpc_c::methodPtr doc_unlock(new DocumentUnlock()); + xmlrpc_c::methodPtr vm_lock(new VirtualMachineLock()); + xmlrpc_c::methodPtr vm_unlock(new VirtualMachineUnlock()); + xmlrpc_c::methodPtr template_lock(new VMTemplateLock()); + xmlrpc_c::methodPtr template_unlock(new VMTemplateUnlock()); + xmlrpc_c::methodPtr vn_lock(new VirtualNetworkLock()); + xmlrpc_c::methodPtr vn_unlock(new VirtualNetworkUnlock()); + xmlrpc_c::methodPtr image_lock(new ImageLock()); + xmlrpc_c::methodPtr image_unlock(new ImageUnlock()); + xmlrpc_c::methodPtr vrouter_lock(new VirtualRouterLock()); + xmlrpc_c::methodPtr vrouter_unlock(new VirtualRouterUnlock()); + xmlrpc_c::methodPtr vmg_lock(new VMGroupLock()); + xmlrpc_c::methodPtr vmg_unlock(new VMGroupUnlock()); // PoolInfo Methods xmlrpc_c::methodPtr hostpool_info(new HostPoolInfo()); @@ -495,6 +507,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vm.disksnapshotdelete", vm_dsnap_delete); RequestManagerRegistry.addMethod("one.vm.recover", vm_recover); RequestManagerRegistry.addMethod("one.vm.updateconf", vm_updateconf); + RequestManagerRegistry.addMethod("one.vm.lock", vm_lock); + RequestManagerRegistry.addMethod("one.vm.unlock", vm_unlock); RequestManagerRegistry.addMethod("one.vm.diskresize", vm_disk_resize); RequestManagerRegistry.addMethod("one.vmpool.info", vm_pool_info); @@ -513,7 +527,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.template.chmod", template_chmod); RequestManagerRegistry.addMethod("one.template.clone", template_clone); RequestManagerRegistry.addMethod("one.template.rename", template_rename); - + RequestManagerRegistry.addMethod("one.template.lock", template_lock); + RequestManagerRegistry.addMethod("one.template.unlock", template_unlock); RequestManagerRegistry.addMethod("one.templatepool.info",template_pool_info); /* Host related methods*/ @@ -593,6 +608,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vn.chown", vn_chown); RequestManagerRegistry.addMethod("one.vn.chmod", vn_chmod); RequestManagerRegistry.addMethod("one.vn.rename", vn_rename); + RequestManagerRegistry.addMethod("one.vn.lock", vn_lock); + RequestManagerRegistry.addMethod("one.vn.unlock", vn_unlock); RequestManagerRegistry.addMethod("one.vnpool.info", vnpool_info); @@ -685,6 +702,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.image.snapshotdelete", image_snap_delete); RequestManagerRegistry.addMethod("one.image.snapshotrevert", image_snap_revert); RequestManagerRegistry.addMethod("one.image.snapshotflatten", image_snap_flatten); + RequestManagerRegistry.addMethod("one.image.lock", image_lock); + RequestManagerRegistry.addMethod("one.image.unlock", image_unlock); RequestManagerRegistry.addMethod("one.imagepool.info", imagepool_info); @@ -833,6 +852,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vmgroup.chmod", vmg_chmod); RequestManagerRegistry.addMethod("one.vmgroup.rename", vmg_rename); RequestManagerRegistry.addMethod("one.vmgroup.update", vmg_update); + RequestManagerRegistry.addMethod("one.vmgroup.lock", vmg_lock); + RequestManagerRegistry.addMethod("one.vmgroup.unlock", vmg_unlock); RequestManagerRegistry.addMethod("one.vmgrouppool.info", vmgpool_info); @@ -943,6 +964,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vrouter.instantiate",vrouter_instantiate); RequestManagerRegistry.addMethod("one.vrouter.attachnic", vrouter_attachnic); RequestManagerRegistry.addMethod("one.vrouter.detachnic", vrouter_detachnic); + RequestManagerRegistry.addMethod("one.vrouter.lock", vrouter_lock); + RequestManagerRegistry.addMethod("one.vrouter.unlock", vrouter_unlock); RequestManagerRegistry.addMethod("one.vrouterpool.info",vrouter_pool_info); @@ -1008,6 +1031,8 @@ void RequestManager::register_xml_methods() xmlrpc_c::method * marketapp_chown_pt; xmlrpc_c::method * marketapp_enable_pt; xmlrpc_c::method * marketapp_rename_pt; + xmlrpc_c::method * marketapp_lock_pt; + xmlrpc_c::method * marketapp_unlock_pt; if (nebula.is_federation_slave()) { @@ -1016,6 +1041,8 @@ void RequestManager::register_xml_methods() marketapp_chown_pt = new RequestManagerProxy("one.marketapp.chown"); marketapp_enable_pt = new RequestManagerProxy("one.marketapp.enable"); marketapp_rename_pt = new RequestManagerProxy("one.marketapp.rename"); + marketapp_lock_pt = new RequestManagerProxy("one.marketapp.lock"); + marketapp_unlock_pt = new RequestManagerProxy("one.marketapp.unlock"); } else { @@ -1024,6 +1051,8 @@ void RequestManager::register_xml_methods() marketapp_chown_pt = new MarketPlaceAppChown(); marketapp_enable_pt = new MarketPlaceAppEnable(); marketapp_rename_pt = new MarketPlaceAppRename(); + marketapp_lock_pt = new MarketPlaceAppLock(); + marketapp_unlock_pt = new MarketPlaceAppUnlock(); xmlrpc_c::methodPtr marketapp_updatedb(new MarketPlaceAppUpdateDB()); xmlrpc_c::methodPtr marketapp_dropdb(new MarketPlaceAppDropDB()); @@ -1046,6 +1075,8 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr marketapp_chown(marketapp_chown_pt); xmlrpc_c::methodPtr marketapp_enable(marketapp_enable_pt); xmlrpc_c::methodPtr marketapp_rename(marketapp_rename_pt); + xmlrpc_c::methodPtr marketapp_lock(marketapp_lock_pt); + xmlrpc_c::methodPtr marketapp_unlock(marketapp_unlock_pt); xmlrpc_c::methodPtr marketapp_info(new MarketPlaceAppInfo()); xmlrpc_c::methodPtr marketapppool_info(new MarketPlaceAppPoolInfo()); @@ -1056,6 +1087,8 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.marketapp.chmod", marketapp_chmod); RequestManagerRegistry.addMethod("one.marketapp.chown", marketapp_chown); RequestManagerRegistry.addMethod("one.marketapp.enable", marketapp_enable); + RequestManagerRegistry.addMethod("one.marketapp.lock", marketapp_lock); + RequestManagerRegistry.addMethod("one.marketapp.unlock", marketapp_unlock); RequestManagerRegistry.addMethod("one.marketapp.info", marketapp_info); RequestManagerRegistry.addMethod("one.marketapp.rename", marketapp_rename); diff --git a/src/rm/RequestManagerLock.cc b/src/rm/RequestManagerLock.cc index 3e2a37c986..64e7092755 100644 --- a/src/rm/RequestManagerLock.cc +++ b/src/rm/RequestManagerLock.cc @@ -25,8 +25,8 @@ 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)); - + int level = xmlrpc_c::value_int(paramList.getInt(2)); + int owner = att.uid; PoolObjectSQL * object; string error_str; int rc; @@ -45,13 +45,22 @@ void RequestManagerLock::request_execute(xmlrpc_c::paramList const& paramList, return; } - rc = lock_db(object, owner); + if ((auth_object & PoolObjectSQL::LockableObject) != 0) + { + rc = lock_db(object, owner, att.req_id, level); - pool->update(object); + pool->update(object); - object->unlock(); + object->unlock(); - success_response((rc == 0), att); + success_response((rc == 0), att); + } + else + { + object->unlock(); + + failure_response(AUTHORIZATION, att); + } return; } @@ -63,10 +72,11 @@ 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; + int owner = att.uid; + int req_id = att.req_id; if ( basic_authorization(oid, att) == false ) { @@ -82,7 +92,7 @@ void RequestManagerUnlock::request_execute(xmlrpc_c::paramList const& paramList, return; } - unlock_db(object, owner); + unlock_db(object, owner, req_id); pool->update(object); diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 7fc800a774..1880c8d303 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -69,7 +69,7 @@ bool RequestManagerVirtualMachine::vm_authorization( { string t_xml; - ar.add_create_auth(att.uid, att.gid, PoolObjectSQL::IMAGE, + ar.add_create_auth(att.uid, att.gid, PoolObjectSQL::IMAGE, tmpl->to_xml(t_xml)); } diff --git a/src/sunstone/models/OpenNebulaJSON/ImageJSON.rb b/src/sunstone/models/OpenNebulaJSON/ImageJSON.rb index 46f94dff55..b1e2e99d80 100644 --- a/src/sunstone/models/OpenNebulaJSON/ImageJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/ImageJSON.rb @@ -63,6 +63,8 @@ module OpenNebulaJSON when "snapshot_flatten" then self.snapshot_flatten(action_hash['params']) when "snapshot_revert" then self.snapshot_revert(action_hash['params']) when "snapshot_delete" then self.snapshot_delete(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -135,5 +137,13 @@ module OpenNebulaJSON def snapshot_delete(params=Hash.new) super(params['snapshot_id'].to_i) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb b/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb index 0a24bd9e38..7584355a1c 100644 --- a/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb @@ -54,6 +54,8 @@ module OpenNebulaJSON when "rename" then self.rename(action_hash['params']) when "disable" then self.disable when "enable" then self.enable + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -122,5 +124,13 @@ module OpenNebulaJSON def rename(params=Hash.new) super(params['name']) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb b/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb index 85a1fdd733..6c2c9f660b 100644 --- a/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb @@ -51,6 +51,8 @@ module OpenNebulaJSON when "clone" then self.clone(action_hash['params']) when "rename" then self.rename(action_hash['params']) when "delete_recursive" then self.delete_recursive(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -140,5 +142,13 @@ module OpenNebulaJSON recursive = (params['recursive'] == true) self.delete(recursive) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/models/OpenNebulaJSON/VMGroupJSON.rb b/src/sunstone/models/OpenNebulaJSON/VMGroupJSON.rb index 332312f542..841e726241 100644 --- a/src/sunstone/models/OpenNebulaJSON/VMGroupJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VMGroupJSON.rb @@ -47,6 +47,8 @@ module OpenNebulaJSON when "chmod" then self.chmod_octet(action_hash['params']) when "update" then self.update(action_hash['params']) when "rename" then self.rename(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -77,5 +79,13 @@ module OpenNebulaJSON def rename(params=Hash.new) super(params['name']) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb index 13ee9a99b5..aeee8750ed 100644 --- a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb @@ -77,6 +77,8 @@ module OpenNebulaJSON when "recover" then self.recover(action_hash['params']) when "save_as_template" then self.save_as_template(action_hash['params']) when "disk_resize" then self.disk_resize(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -191,6 +193,14 @@ module OpenNebulaJSON super(params['name']) end + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end + def recover(params=Hash.new) super(params['result'].to_i) end diff --git a/src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb b/src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb index 3104c45385..fadbd6250d 100644 --- a/src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb @@ -54,6 +54,8 @@ module OpenNebulaJSON when "add_ar" then self.add_ar(action_hash['params']) when "update_ar" then self.update_ar(action_hash['params']) when "reserve" then self.reserve(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -111,5 +113,13 @@ module OpenNebulaJSON super(params['name'], params['size'], params['ar_id'], params['addr'], params['vnet']) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/models/OpenNebulaJSON/VirtualRouterJSON.rb b/src/sunstone/models/OpenNebulaJSON/VirtualRouterJSON.rb index c807b1ec98..76f32a6b63 100644 --- a/src/sunstone/models/OpenNebulaJSON/VirtualRouterJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VirtualRouterJSON.rb @@ -51,6 +51,8 @@ module OpenNebulaJSON when "rename" then self.rename(action_hash['params']) when "attachnic" then self.nic_attach(action_hash['params']) when "detachnic" then self.nic_detach(action_hash['params']) + when "lock" then self.lock(action_hash['params']) + when "unlock" then self.unlock(action_hash['params']) else error_msg = "#{action_hash['perform']} action not " << " available for this resource" @@ -113,5 +115,13 @@ module OpenNebulaJSON def nic_detach(params=Hash.new) super(params['nic_id'].to_i) end + + def lock(params=Hash.new) + super(params['level'].to_i) + end + + def unlock(params=Hash.new) + super() + end end end diff --git a/src/sunstone/public/app/opennebula/action.js b/src/sunstone/public/app/opennebula/action.js index 39decd0046..e9fa1b6464 100644 --- a/src/sunstone/public/app/opennebula/action.js +++ b/src/sunstone/public/app/opennebula/action.js @@ -291,6 +291,13 @@ define(function(require) { _simple_action(params, resource, "chown", action_obj, path); }, + "lock": function(params, resource, path) { + var level = params.data.extra_param; + var action_obj = {"level": level}; + + _simple_action(params, resource, "lock", action_obj, path); + }, + "chgrp": function(params, resource, path) { var id = params.data.extra_param; var action_obj = {"owner_id": "-1", diff --git a/src/sunstone/public/app/opennebula/image.js b/src/sunstone/public/app/opennebula/image.js index 3ea69218e6..6d31270758 100644 --- a/src/sunstone/public/app/opennebula/image.js +++ b/src/sunstone/public/app/opennebula/image.js @@ -164,6 +164,12 @@ define(function(require) { }, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); + }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); } } diff --git a/src/sunstone/public/app/opennebula/marketplaceapp.js b/src/sunstone/public/app/opennebula/marketplaceapp.js index db854ab5a6..dbdb928159 100644 --- a/src/sunstone/public/app/opennebula/marketplaceapp.js +++ b/src/sunstone/public/app/opennebula/marketplaceapp.js @@ -109,6 +109,12 @@ define(function(require) { }, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); + }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); } } diff --git a/src/sunstone/public/app/opennebula/network.js b/src/sunstone/public/app/opennebula/network.js index 9a5f7c6868..39b42ae6b0 100644 --- a/src/sunstone/public/app/opennebula/network.js +++ b/src/sunstone/public/app/opennebula/network.js @@ -90,7 +90,13 @@ define(function(require) { }, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); - } + }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); + }, } return Network; diff --git a/src/sunstone/public/app/opennebula/template.js b/src/sunstone/public/app/opennebula/template.js index e033d5e0a2..3f8fe0c5f9 100644 --- a/src/sunstone/public/app/opennebula/template.js +++ b/src/sunstone/public/app/opennebula/template.js @@ -97,6 +97,12 @@ define(function(require) { return true; } }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); + }, "cost": function(template) { var cost = 0; var capacity = template.VMTEMPLATE.TEMPLATE; diff --git a/src/sunstone/public/app/opennebula/virtualrouter.js b/src/sunstone/public/app/opennebula/virtualrouter.js index a44e37d873..5fe433e3ee 100644 --- a/src/sunstone/public/app/opennebula/virtualrouter.js +++ b/src/sunstone/public/app/opennebula/virtualrouter.js @@ -70,6 +70,12 @@ define(function(require) { }, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); + }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); } }; diff --git a/src/sunstone/public/app/opennebula/vm.js b/src/sunstone/public/app/opennebula/vm.js index ab03a098f8..5b01a885d0 100644 --- a/src/sunstone/public/app/opennebula/vm.js +++ b/src/sunstone/public/app/opennebula/vm.js @@ -563,6 +563,12 @@ define(function(require) { var action_obj = params.data.extra_param; OpenNebulaAction.simple_action(params, RESOURCE, "save_as_template", action_obj); }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); + }, "stateStr": function(stateId) { return STATES_STR[stateId]; }, diff --git a/src/sunstone/public/app/opennebula/vmgroup.js b/src/sunstone/public/app/opennebula/vmgroup.js index 3943cb00fc..c7c91378b1 100644 --- a/src/sunstone/public/app/opennebula/vmgroup.js +++ b/src/sunstone/public/app/opennebula/vmgroup.js @@ -50,6 +50,12 @@ define(function(require) { }, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); + }, + "lock" : function(params) { + OpenNebulaAction.lock(params, RESOURCE); + }, + "unlock" : function(params) { + OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); } }; diff --git a/src/sunstone/public/app/sunstone.js b/src/sunstone/public/app/sunstone.js index ee74b28760..0d7100ee70 100644 --- a/src/sunstone/public/app/sunstone.js +++ b/src/sunstone/public/app/sunstone.js @@ -269,6 +269,7 @@ define(function(require) { } var type = button.type + "_button"; + var data = ""; var strClass = [type]; switch (button.type) { case "select": @@ -292,6 +293,10 @@ define(function(require) { strClass.push(button.custom_classes); } + if (button.data) { + data = button.data; + } + var buttonContext; var text; switch (button.layout) { @@ -330,6 +335,11 @@ define(function(require) { strClass.push("button"); buttonCode = ""; break; + case "lock_buttons": + buttonContext = $("#" + customId + "lock_buttons", buttonsRow); + text = button.text; + buttonCode = "
  • " + text + "
  • "; + break; case "vmspause_buttons": buttonContext = $("#" + customId + "vmspause_buttons", buttonsRow); text = button.text; @@ -416,8 +426,8 @@ define(function(require) { $("button[data-toggle=" + customId + "vmsstop_buttons]", actionBlock).remove(); } - if ($("#" + customId + "vmspause_buttons li", actionBlock).length == 0) { - $("button[data-toggle=" + customId + "vmspause_buttons]", actionBlock).remove(); + if ($("#" + customId + "lock_buttons li", actionBlock).length == 0) { + $("button[data-toggle=" + customId + "lock_buttons]", actionBlock).remove(); } if ($("#" + customId + "vmsrepeat_buttons li", actionBlock).length == 0) { @@ -459,6 +469,7 @@ define(function(require) { $(document).on("click", ".action_button", function() { var error = 0; var value = $(this).val(); + var data = ($(this).attr("data") == "")? undefined: $(this).attr("data"); if ($.isEmptyObject(value)) { value = $(this).attr("href"); } @@ -474,10 +485,10 @@ define(function(require) { case "multiple": //find the datatable var context = $(this).parents(".tab"); var nodes = action.elements(); - error = _runAction(value, nodes); + error = _runAction(value, nodes, data); break; default: - error = _runAction(value); + error = _runAction(value,undefined, data); } return false; diff --git a/src/sunstone/public/app/sunstone/buttons.hbs b/src/sunstone/public/app/sunstone/buttons.hbs index a7852d74e5..29f55e68e3 100644 --- a/src/sunstone/public/app/sunstone/buttons.hbs +++ b/src/sunstone/public/app/sunstone/buttons.hbs @@ -39,6 +39,12 @@ + + + + diff --git a/src/sunstone/public/app/sunstone/tab.hbs b/src/sunstone/public/app/sunstone/tab.hbs index 319b8a0d8b..8ee7c1989a 100644 --- a/src/sunstone/public/app/sunstone/tab.hbs +++ b/src/sunstone/public/app/sunstone/tab.hbs @@ -22,9 +22,12 @@ diff --git a/src/sunstone/public/app/tabs/acls-tab.js b/src/sunstone/public/app/tabs/acls-tab.js index 455c3a3173..60e0d7eeff 100644 --- a/src/sunstone/public/app/tabs/acls-tab.js +++ b/src/sunstone/public/app/tabs/acls-tab.js @@ -40,6 +40,7 @@ define(function(require) { tabClass: "subTab", parentTab: "system-top-tab", listHeader: Locale.tr("Access Control Lists"), + lockable: false, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/clusters-tab.js b/src/sunstone/public/app/tabs/clusters-tab.js index 5881903b40..90965c3dd9 100644 --- a/src/sunstone/public/app/tabs/clusters-tab.js +++ b/src/sunstone/public/app/tabs/clusters-tab.js @@ -49,6 +49,7 @@ define(function(require) { parentTab: "infrastructure-top-tab", listHeader: Locale.tr("Clusters"), infoHeader: Locale.tr("Cluster"), + lockable: false, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/datastores-tab.js b/src/sunstone/public/app/tabs/datastores-tab.js index 2c00fa7b58..a1bb9c2acd 100644 --- a/src/sunstone/public/app/tabs/datastores-tab.js +++ b/src/sunstone/public/app/tabs/datastores-tab.js @@ -49,6 +49,7 @@ define(function(require) { parentTab: "storage-top-tab", listHeader: Locale.tr("Datastores"), infoHeader: Locale.tr("Datastore"), + lockable: false, subheader: ' '+Locale.tr("TOTAL")+' \ '+Locale.tr("ON")+' \ '+Locale.tr("OFF")+'', diff --git a/src/sunstone/public/app/tabs/files-tab.js b/src/sunstone/public/app/tabs/files-tab.js index 31ff1a3ffc..76eb42bc1d 100644 --- a/src/sunstone/public/app/tabs/files-tab.js +++ b/src/sunstone/public/app/tabs/files-tab.js @@ -45,6 +45,7 @@ define(function(require) { parentTab: "storage-top-tab", listHeader: Locale.tr("Files"), infoHeader: Locale.tr("File"), + lockable: false, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/groups-tab.js b/src/sunstone/public/app/tabs/groups-tab.js index 4789c22592..b31214d71e 100644 --- a/src/sunstone/public/app/tabs/groups-tab.js +++ b/src/sunstone/public/app/tabs/groups-tab.js @@ -52,6 +52,7 @@ define(function(require) { parentTab: "system-top-tab", listHeader: Locale.tr("Groups"), infoHeader: Locale.tr("Group"), + lockable: false, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/hosts-tab.js b/src/sunstone/public/app/tabs/hosts-tab.js index d8ed07a934..fecd6655b6 100644 --- a/src/sunstone/public/app/tabs/hosts-tab.js +++ b/src/sunstone/public/app/tabs/hosts-tab.js @@ -54,6 +54,7 @@ define(function(require) { parentTab: "infrastructure-top-tab", listHeader: Locale.tr("Hosts"), infoHeader: Locale.tr("Host"), + lockable: false, subheader: ' ' + Locale.tr("TOTAL") + ' \ ' + Locale.tr("ON") + ' \ ' + Locale.tr("OFF") + ' \ diff --git a/src/sunstone/public/app/tabs/images-tab.js b/src/sunstone/public/app/tabs/images-tab.js index c1a515f8e6..e751834525 100644 --- a/src/sunstone/public/app/tabs/images-tab.js +++ b/src/sunstone/public/app/tabs/images-tab.js @@ -50,6 +50,7 @@ define(function(require) { parentTab: "storage-top-tab", listHeader: Locale.tr("Images"), infoHeader: Locale.tr("Image"), + lockable: true, subheader: ' '+Locale.tr("TOTAL")+' \ '+Locale.tr("TOTAL SIZE")+'', resource: 'Image', diff --git a/src/sunstone/public/app/tabs/images-tab/actions.js b/src/sunstone/public/app/tabs/images-tab/actions.js index 67b37e2a18..40d2033cab 100644 --- a/src/sunstone/public/app/tabs/images-tab/actions.js +++ b/src/sunstone/public/app/tabs/images-tab/actions.js @@ -64,6 +64,10 @@ define(function(require) { "Image.snapshot_flatten": _commonActions.singleAction("snapshot_flatten"), "Image.snapshot_revert": _commonActions.singleAction("snapshot_revert"), "Image.snapshot_delete": _commonActions.singleAction("snapshot_delete"), + "Image.lockM": _commonActions.multipleAction('lock'), + "Image.lockU": _commonActions.multipleAction('lock'), + "Image.lockA": _commonActions.multipleAction('lock'), + "Image.unlock": _commonActions.multipleAction('unlock'), "Image.upload_marketplace_dialog" : { type: "custom", call: function(params) { diff --git a/src/sunstone/public/app/tabs/images-tab/buttons.js b/src/sunstone/public/app/tabs/images-tab/buttons.js index c4f61a5787..8f8b40a58c 100644 --- a/src/sunstone/public/app/tabs/images-tab/buttons.js +++ b/src/sunstone/public/app/tabs/images-tab/buttons.js @@ -16,6 +16,7 @@ define(function(require) { var Locale = require('utils/locale'); + var Tips = require('utils/tips'); var Buttons = { "Image.refresh" : { @@ -83,6 +84,29 @@ define(function(require) { }, "Image.edit_labels" : { layout: "labels", + }, + "Image.lockA" : { + type: "action", + text: Locale.tr("Admin") + " " + Tips.html(Locale.tr("Lock Admin actions")) + "", + layout: "lock_buttons", + data: 3 + }, + "Image.lockM" : { + type: "action", + text: Locale.tr("Manage") + " " + Tips.html(Locale.tr("Lock Manage actions")) + "", + layout: "lock_buttons", + data: 2 + }, + "Image.lockU" : { + type: "action", + text: Locale.tr("Use") + " " + Tips.html(Locale.tr("Lock Use actions")) + "", + layout: "lock_buttons", + data: 1 + }, + "Image.unlock" : { + type: "action", + text: Locale.tr("Unlock") + " " + Tips.html(Locale.tr("Unlock all actions")) + "", + layout: "lock_buttons" } } diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab.js b/src/sunstone/public/app/tabs/marketplaceapps-tab.js index 516547d8bb..35d89ddb63 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab.js +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab.js @@ -52,6 +52,7 @@ define(function(require) { parentTab: "storage-top-tab", listHeader: Locale.tr("Apps"), infoHeader: Locale.tr("App"), + lockable: true, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js b/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js index 35a4d3ff6d..d65572d5e0 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js @@ -37,6 +37,10 @@ define(function(require) { var _actions = { "MarketPlaceApp.create" : _commonActions.create(CREATE_DIALOG_ID), "MarketPlaceApp.create_dialog" : _commonActions.showCreate(CREATE_DIALOG_ID), + "MarketPlaceApp.lockM": _commonActions.multipleAction('lock'), + "MarketPlaceApp.lockU": _commonActions.multipleAction('lock'), + "MarketPlaceApp.lockA": _commonActions.multipleAction('lock'), + "MarketPlaceApp.unlock": _commonActions.multipleAction('unlock'), "MarketPlaceApp.download_opennebula_dialog" : { type: "custom", call: function() { diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/buttons.js b/src/sunstone/public/app/tabs/marketplaceapps-tab/buttons.js index f04ea48d52..5d9ba5fcf9 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab/buttons.js +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/buttons.js @@ -16,6 +16,7 @@ define(function(require) { var Locale = require('utils/locale'); + var Tips = require('utils/tips'); var MarketPlaceAppButtons = { "MarketPlaceApp.refresh" : { @@ -66,6 +67,29 @@ define(function(require) { }, "MarketPlaceApp.edit_labels" : { layout: "labels", + }, + "MarketPlaceApp.lockA" : { + type: "action", + text: Locale.tr("Admin") + " " + Tips.html(Locale.tr("Lock Admin actions")) + "", + layout: "lock_buttons", + data: 3 + }, + "MarketPlaceApp.lockM" : { + type: "action", + text: Locale.tr("Manage") + " " + Tips.html(Locale.tr("Lock Manage actions")) + "", + layout: "lock_buttons", + data: 2 + }, + "MarketPlaceApp.lockU" : { + type: "action", + text: Locale.tr("Use") + " " + Tips.html(Locale.tr("Lock Use actions")) + "", + layout: "lock_buttons", + data: 1 + }, + "MarketPlaceApp.unlock" : { + type: "action", + text: Locale.tr("Unlock") + " " + Tips.html(Locale.tr("Unlock all actions")) + "", + layout: "lock_buttons" } }; diff --git a/src/sunstone/public/app/tabs/marketplaces-tab.js b/src/sunstone/public/app/tabs/marketplaces-tab.js index 2feea51b4b..0389c36794 100644 --- a/src/sunstone/public/app/tabs/marketplaces-tab.js +++ b/src/sunstone/public/app/tabs/marketplaces-tab.js @@ -51,6 +51,7 @@ define(function(require) { parentTab: "storage-top-tab", listHeader: Locale.tr("MarketPlaces"), infoHeader: Locale.tr("MarketPlace"), + lockable: false, subheader: '\ '+Locale.tr("TOTAL")+'\ ', diff --git a/src/sunstone/public/app/tabs/oneflow-services-tab.js b/src/sunstone/public/app/tabs/oneflow-services-tab.js index 12391238ea..bb50c513d5 100644 --- a/src/sunstone/public/app/tabs/oneflow-services-tab.js +++ b/src/sunstone/public/app/tabs/oneflow-services-tab.js @@ -50,6 +50,7 @@ define(function(require) { parentTab: "instances-top-tab", listHeader: Locale.tr("Services"), infoHeader: Locale.tr("Service"), + lockable: false, subheader: '', content: '