diff --git a/include/MarketPlaceApp.h b/include/MarketPlaceApp.h index f8c3dc892d..351dac2e81 100644 --- a/include/MarketPlaceApp.h +++ b/include/MarketPlaceApp.h @@ -37,6 +37,7 @@ public: READY = 1, /** < Ready to use */ LOCKED = 2, /** < Operation in process */ ERROR = 3, /** < Error state the operation failed*/ + DISABLED = 4 }; /** @@ -48,10 +49,11 @@ public: { switch(state) { - case INIT: return "INIT"; break; - case READY: return "READY"; break; - case LOCKED: return "LOCKED"; break; - case ERROR: return "ERROR"; break; + case INIT: return "INIT"; break; + case READY: return "READY"; break; + case LOCKED: return "LOCKED"; break; + case ERROR: return "ERROR"; break; + case DISABLED: return "DISABLED"; break; default: return ""; } }; @@ -106,6 +108,15 @@ public: */ int from_xml(const std::string &xml_str); + /** + * Enable or disable the app. A disabled app cannot be exported + * @param enable true to enable + * @param error_str Returns the error reason, if any + * + * @return 0 on success + */ + int enable(bool enable, string& error_str); + /** * Returns the marketplace ID */ diff --git a/include/MarketPlaceManagerDriver.h b/include/MarketPlaceManagerDriver.h index 79c493ec17..6c1108da16 100644 --- a/include/MarketPlaceManagerDriver.h +++ b/include/MarketPlaceManagerDriver.h @@ -70,29 +70,22 @@ private: MarketPlaceManager * marketm; /** - * Sends a stat request to the MAD. - * @param oid the id of the stat request + * Imports a new object into the marketplace + * @param oid of the app * @param drv_msg xml data for the mad operation. */ void importapp(int oid, const std::string& drv_msg) const; /** - * Sends a make filesystem request to the MAD. - * @param oid the image id. - * @param drv_msg xml data for the mad operation. - */ - void exportapp(int oid, const std::string& drv_msg) const; - - /** - * Sends a delete request to the MAD: "DELETE IMAGE_ID PATH" - * @param oid the image id. + * Deletes an app from the marketplace + * @param oid of the app * @param drv_msg xml data for the mad operation. */ void deleteapp(int oid, const std::string& drv_msg) const; /** - * Sends a monitor request to the MAD: "MONITOR DS_ID DS_XML" - * @param oid the datastore id. + * Monitors the marketplace + * @param oid of the operation * @param drv_msg xml data for the mad operation. */ void monitor(int oid, const std::string& drv_msg) const; diff --git a/include/RequestManagerMarketPlaceApp.h b/include/RequestManagerMarketPlaceApp.h new file mode 100644 index 0000000000..d0b8cb4015 --- /dev/null +++ b/include/RequestManagerMarketPlaceApp.h @@ -0,0 +1,66 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2015, OpenNebula Project, OpenNebula Systems */ +/* */ +/* 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_MARKETPLACEAPP_H +#define REQUEST_MANAGER_MARKETPLACEAPP_H + +#include "Request.h" +#include "Nebula.h" + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class RequestManagerMarketPlaceApp: public Request +{ +protected: + RequestManagerMarketPlaceApp(const std::string& method_name, + const std::string& help, const std::string& params) : + Request(method_name, params, help) + { + Nebula& nd = Nebula::instance(); + pool = nd.get_apppool(); + + auth_object = PoolObjectSQL::MARKETPLACEAPP; + auth_op = AuthRequest::MANAGE; + }; + + ~RequestManagerMarketPlaceApp(){}; + + /* --------------------------------------------------------------------- */ + + virtual void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att) = 0; +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class MarketPlaceAppEnable : public RequestManagerMarketPlaceApp +{ +public: + MarketPlaceAppEnable(): RequestManagerMarketPlaceApp("MarketPlaceAppEnable", + "Enables or disables a marketplace app", "A:sib"){}; + + ~MarketPlaceAppEnable(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); +}; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +#endif diff --git a/src/cli/onemarketapp b/src/cli/onemarketapp index c624f8f9fe..d803e3417e 100755 --- a/src/cli/onemarketapp +++ b/src/cli/onemarketapp @@ -30,6 +30,7 @@ $: << RUBY_LIB_LOCATION+"/cli" require 'command_parser' require 'one_helper/onemarketapp_helper' require 'one_helper/onemarket_helper' +require 'one_helper/onedatastore_helper' CommandParser::CmdParser.new(ARGV) do usage "`onemarket` [] []" @@ -52,6 +53,7 @@ CommandParser::CmdParser.new(ARGV) do list_options << OpenNebulaHelper::DESCRIBE CREATE_OPTIONS = [OneMarketPlaceHelper::MARKETPLACE] + EXPORT_OPTIONS = [OneDatastoreHelper::DATASTORE] ######################################################################## # Formatters for arguments @@ -103,12 +105,32 @@ CommandParser::CmdParser.new(ARGV) do end end + export_desc = <<-EOT.unindent + Exports the marketplace app to the OpenNebula cloud + EOT + + command :export, export_desc, :appid, :name, :options=>EXPORT_OPTIONS do + helper.perform_action(args[0], options, "exported") do |obj| + + rc = obj.export(:dsid=>options[:datastore], :name=>args[1]) + + next rc if OpenNebula.is_error?(rc) + + rc.each { |key, value| + puts "#{key.to_s.upcase}" + value.each{ |id| + puts " ID: #{id}" + } + } + end + end + delete_desc = <<-EOT.unindent Deletes the given marketplace app EOT command :delete, delete_desc, [:range, :appid_list] do - helper.perform_actions(args[0],options,"deleted") do |app| + helper.perform_actions(args[0], options, "deleted") do |app| app.delete end end @@ -119,7 +141,8 @@ CommandParser::CmdParser.new(ARGV) do EOT command :update, update_desc, :appid, [:file, nil], - :options=>OpenNebulaHelper::APPEND do + :options=>OpenNebulaHelper::APPEND do + helper.perform_action(args[0],options,"modified") do |obj| if options[:append] str = OpenNebulaHelper.append_template(args[0], obj, args[1]) @@ -136,7 +159,7 @@ CommandParser::CmdParser.new(ARGV) do EOT command :chgrp, chgrp_desc,[:range, :appid_list], :groupid do - helper.perform_actions(args[0],options,"Group changed") do |app| + helper.perform_actions(args[0], options, "Group changed") do |app| app.chown(-1, args[1].to_i) end end @@ -146,10 +169,10 @@ CommandParser::CmdParser.new(ARGV) do EOT command :chown, chown_desc, [:range, :appid_list], :userid, - [:groupid,nil] do + [:groupid, nil] do gid = args[2].nil? ? -1 : args[2].to_i - helper.perform_actions(args[0],options, - "Owner/Group changed") do |app| + + helper.perform_actions(args[0], options, "Owner/Group changed") do |app| app.chown(args[1].to_i, gid) end end @@ -159,8 +182,7 @@ CommandParser::CmdParser.new(ARGV) do EOT command :chmod, chmod_desc, [:range, :appid_list], :octet do - helper.perform_actions(args[0],options, - "Permissions changed") do |app| + helper.perform_actions(args[0], options, "Permissions changed") do |app| app.chmod_octet(args[1]) end end @@ -170,7 +192,7 @@ CommandParser::CmdParser.new(ARGV) do EOT command :rename, rename_desc, :appid, :name do - helper.perform_action(args[0],options,"renamed") do |o| + helper.perform_action(args[0], options, "renamed") do |o| o.rename(args[1]) end end @@ -188,6 +210,27 @@ CommandParser::CmdParser.new(ARGV) do EOT command :show, show_desc, :appid, :options=>OpenNebulaHelper::XML do - helper.show_resource(args[0],options) + helper.show_resource(args[0], options) + end + + enable_desc = <<-EOT.unindent + Enables the marketplace app + EOT + + command :enable, enable_desc, [:range, :appid_list] do + helper.perform_actions(args[0], options, "enabled") do |obj| + obj.enable + end + end + + disable_desc = <<-EOT.unindent + Disables the marketplace app. A disabled marketplace app cannot be + exported to a cloud + EOT + + command :disable, disable_desc, [:range, :appid_list] do + helper.perform_actions(args[0], options, "disabled") do |obj| + obj.disable + end end end diff --git a/src/market/MarketPlaceApp.cc b/src/market/MarketPlaceApp.cc index 1a404164a7..5276751995 100644 --- a/src/market/MarketPlaceApp.cc +++ b/src/market/MarketPlaceApp.cc @@ -374,3 +374,31 @@ MarketPlaceApp::MarketPlaceAppType MarketPlaceApp::str_to_type(string& str_type) return UNKNOWN; } +/* --------------------------------------------------------------------------- */ +/* --------------------------------------------------------------------------- */ + +int MarketPlaceApp::enable(bool enable, string& error_str) +{ + switch(state) + { + case INIT: + case LOCKED: + case ERROR: + break; + + case READY: + case DISABLED: + if (enable) + { + state = READY; + } + else + { + state = DISABLED; + } + break; + } + + return 0; +} + diff --git a/src/market/MarketPlaceManagerActions.cc b/src/market/MarketPlaceManagerActions.cc index 1e8bad1936..be9093b570 100644 --- a/src/market/MarketPlaceManagerActions.cc +++ b/src/market/MarketPlaceManagerActions.cc @@ -198,6 +198,7 @@ int MarketPlaceManager::delete_app(int appid, const std::string& market_data, case MarketPlaceApp::INIT: case MarketPlaceApp::READY : case MarketPlaceApp::ERROR: + case MarketPlaceApp::DISABLED: break; } break; diff --git a/src/market/MarketPlaceManagerDriver.cc b/src/market/MarketPlaceManagerDriver.cc index 83fad70214..1bdd557e61 100644 --- a/src/market/MarketPlaceManagerDriver.cc +++ b/src/market/MarketPlaceManagerDriver.cc @@ -33,17 +33,6 @@ void MarketPlaceManagerDriver::importapp(int oid, const std::string& msg) const /* -------------------------------------------------------------------------- */ -void MarketPlaceManagerDriver::exportapp(int oid, const std::string& msg) const -{ - std::ostringstream os; - - os << "EXPORT " << oid << " " << msg << endl; - - write(os); -} - -/* -------------------------------------------------------------------------- */ - void MarketPlaceManagerDriver::deleteapp(int oid, const std::string& msg) const { std::ostringstream os; @@ -451,10 +440,6 @@ void MarketPlaceManagerDriver::protocol(const string& message) const { import_action(is, apppool, marketm, id, result); } - else if (action == "EXPORT") - { - return; - } else if (action == "DELETE") { delete_action(is, apppool, marketm, id, result); diff --git a/src/market_mad/one_market.rb b/src/market_mad/one_market.rb index 80363676a8..ad49089359 100755 --- a/src/market_mad/one_market.rb +++ b/src/market_mad/one_market.rb @@ -148,6 +148,8 @@ class MarketPlaceDriver < OpenNebulaDriver info_doc.initialize_xml(info, 'IMPORT_INFO') #----------------------------------------------------------------------- # Only IMAGE type is supported + # when "VMTEMPLATE" + # when "FLOW" #----------------------------------------------------------------------- else failure(:import, id, "Type #{apptype} not supported") @@ -169,12 +171,8 @@ class MarketPlaceDriver < OpenNebulaDriver send_message(ACTION[:import], rc, id, info) end - def export(id, drv_message) - send_message(ACTION[:export], RESULT[:failure], id, "Not implemented") - end - ############################################################################ - # Deletes an app from the marketplace by freeing the underlying resources + # Deletes an app from the marketplace ############################################################################ def delete(id, drv_message) xml = decode(drv_message) @@ -196,6 +194,9 @@ class MarketPlaceDriver < OpenNebulaDriver send_message(ACTION[:delete], rc, id, info) end + ############################################################################ + # Monitor the marketplace. It gathers information about usage and app status + ############################################################################ def monitor(id, drv_message) send_message(ACTION[:export], RESULT[:failure], id, "Not implemented") end diff --git a/src/oca/ruby/opennebula/marketplaceapp.rb b/src/oca/ruby/opennebula/marketplaceapp.rb index 94c44c068c..a0e6b6c1fd 100644 --- a/src/oca/ruby/opennebula/marketplaceapp.rb +++ b/src/oca/ruby/opennebula/marketplaceapp.rb @@ -29,16 +29,18 @@ module OpenNebula :update => "marketapp.update", :chown => "marketapp.chown", :chmod => "marketapp.chmod", - :rename => "marketapp.rename" + :rename => "marketapp.rename", + :enable => "marketapp.enable" } - MARKETPLACEAPP_STATES=%w{INIT READY LOCKED ERROR} + MARKETPLACEAPP_STATES=%w{INIT READY LOCKED ERROR DISABLED} SHORT_MARKETPLACEAPP_STATES={ - "INIT" => "init", + "INIT" => "ini", "READY" => "rdy", - "LOCKED" => "lock", + "LOCKED" => "lck", "ERROR" => "err", + "DISABLED" => "dis" } MARKETPLACEAPP_TYPES=%w{UNKNOWN IMAGE VMTEMPLATE FLOW} @@ -137,8 +139,8 @@ module OpenNebula # otherwise def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, other_m, other_a) - super(MARKETPLACEAPP_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, - group_m, group_a, other_u, other_m, other_a) + super(MARKETPLACEAPP_METHODS[:chmod], owner_u, owner_m, owner_a, + group_u, group_m, group_a, other_u, other_m, other_a) end # Renames this marketplace app @@ -151,6 +153,78 @@ module OpenNebula return call(MARKETPLACEAPP_METHODS[:rename], @pe_id, name) end + # Exports this app to a suitable OpenNebula object + # @param appid [Integer] id of the marketplace app + # @param options [Hash] to control the export behavior + # dsid [Integer] datastore to save images + # name [String] of the new object + # + # @return [Hash, OpenNebula::Error] with the ID and type of the created + # objects + # { :vm => [ vm ids ], + # :vmtemplate => [vmtemplates ids], + # :image => [ vm ids] } + def export(options={}) + return Error.new("Missing datastore id") if options[:dsid].nil? + return Error.new("Missing name to export app") if options[:name].nil? + + one = Client.new() + + rc = info + return rc if OpenNebula.is_error?(rc) + return Error.new("App is not in READY state") if state_str!="READY" + + case type_str + when "IMAGE" + if !self['APPTEMPLATE64'].nil? + tmpl=Base64::decode64(self['APPTEMPLATE64']) + else + tmpl="" + end + + name = options[:name] || "marketapp-#{self.id}" + + tmpl << "\n" + tmpl << "NAME=\"" << name << "\"\n" + tmpl << "PATH=" << self['SOURCE'] << "\n" + tmpl << "FORMAT=" << self['FORMAT'] << "\n" + tmpl << "MD5=" << self['MD5'] << "\n" + tmpl << "FROM_APP=\"" << self['ID'] << "\"\n" + tmpl << "FROM_APP_NAME=\"" << self['NAME'] << "\"\n" + + image = Image.new(Image.build_xml, one) + rc = image.allocate(tmpl, options[:dsid]) + + return rc if OpenNebula.is_error?(rc) + + if !self['TEMPLATE/VMTEMPLATE64'].nil? + tmpl=Base64::decode64(self['TEMPLATE/VMTEMPLATE64']) + + tmpl << "\nNAME=#{name}\n" + tmpl << "DISK=[ IMAGE_ID = #{image.id} ]\n" + + vmtpl = Template.new(Template.build_xml, one) + rc = vmtpl.allocate(tmpl) + + return rc if OpenNebula.is_error?(rc) + end + + return { :image => [image.id], :vmtemplate => [vmtpl.id] } + else + return Error.new("App type #{app.type_str} not supported") + end + end + + # Enables this app + def enable + return call(MARKETPLACEAPP_METHODS[:enable], @pe_id, true) + end + + # Enables this app + def disable + return call(MARKETPLACEAPP_METHODS[:enable], @pe_id, false) + end + # --------------------------------------------------------------------- # Helpers to get information # --------------------------------------------------------------------- diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 14417a70e6..34d13d603d 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -40,6 +40,7 @@ #include "RequestManagerGroup.h" #include "RequestManagerVdc.h" #include "RequestManagerDatastore.h" +#include "RequestManagerMarketPlaceApp.h" #include "RequestManagerSystem.h" #include "RequestManagerProxy.h" @@ -956,6 +957,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::method * marketapp_delete_pt; xmlrpc_c::method * marketapp_chmod_pt; xmlrpc_c::method * marketapp_chown_pt; + xmlrpc_c::method * marketapp_enable_pt; if (nebula.is_federation_slave()) { @@ -964,6 +966,7 @@ void RequestManager::register_xml_methods() marketapp_delete_pt = new RequestManagerProxy("one.marketapp.delete"); marketapp_chmod_pt = new RequestManagerProxy("one.marketapp.chmod"); marketapp_chown_pt = new RequestManagerProxy("one.marketapp.chown"); + marketapp_enable_pt = new RequestManagerProxy("one.marketapp.enable"); } else { @@ -972,6 +975,7 @@ void RequestManager::register_xml_methods() marketapp_delete_pt = new MarketPlaceAppDelete(); marketapp_chmod_pt = new MarketPlaceAppChmod(); marketapp_chown_pt = new MarketPlaceAppChown(); + marketapp_enable_pt = new MarketPlaceAppEnable(); } xmlrpc_c::methodPtr marketapp_allocate(marketapp_allocate_pt); @@ -979,6 +983,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr marketapp_delete(marketapp_delete_pt); xmlrpc_c::methodPtr marketapp_chmod(marketapp_chmod_pt); xmlrpc_c::methodPtr marketapp_chown(marketapp_chown_pt); + xmlrpc_c::methodPtr marketapp_enable(marketapp_enable_pt); xmlrpc_c::methodPtr marketapp_info(new MarketPlaceAppInfo()); xmlrpc_c::methodPtr marketapp_rename(new MarketPlaceAppRename()); @@ -989,6 +994,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.marketapp.delete", marketapp_delete); 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.info", marketapp_info); RequestManagerRegistry.addMethod("one.marketapp.rename", marketapp_rename); diff --git a/src/rm/RequestManagerMarketPlaceApp.cc b/src/rm/RequestManagerMarketPlaceApp.cc new file mode 100644 index 0000000000..e9532103ab --- /dev/null +++ b/src/rm/RequestManagerMarketPlaceApp.cc @@ -0,0 +1,60 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2015, OpenNebula Project, OpenNebula Systems */ +/* */ +/* 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 "RequestManagerMarketPlaceApp.h" + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +void MarketPlaceAppEnable::request_execute(xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int id = xmlrpc_c::value_int(paramList.getInt(1)); + bool enable_flag = xmlrpc_c::value_boolean(paramList.getBoolean(2)); + int rc; + + MarketPlaceApp * app; + std::string err_msg; + + if ( basic_authorization(id, att) == false ) + { + return; + } + + app = static_cast(pool->get(id, true)); + + if ( app == 0 ) + { + failure_response(NO_EXISTS,get_error(object_name(auth_object),id),att); + return; + } + + rc = app->enable(enable_flag, err_msg); + + if ( rc != 0 ) + { + failure_response(INTERNAL, request_error(err_msg,""), att); + + app->unlock(); + return; + } + + pool->update(app); + + app->unlock(); + + success_response(id, att); +} diff --git a/src/rm/SConstruct b/src/rm/SConstruct index 09e3d43eea..cdb68f7ae4 100644 --- a/src/rm/SConstruct +++ b/src/rm/SConstruct @@ -47,6 +47,7 @@ source_files=[ 'RequestManagerVdc.cc', 'RequestManagerDatastore.cc', 'RequestManagerLock.cc', + 'RequestManagerMarketPlaceApp.cc' ] # Build library