diff --git a/include/Datastore.h b/include/Datastore.h index fe2cc717f7..092ca656ed 100644 --- a/include/Datastore.h +++ b/include/Datastore.h @@ -114,6 +114,15 @@ public: return tm_mad; }; + /** + * Retrieves DS mad name + * @return string ds mad name + */ + const string& get_ds_mad() const + { + return ds_mad; + }; + /** * Retrieves the base path * @return base path string diff --git a/src/cli/oneimage b/src/cli/oneimage index e033932539..d8e97f15eb 100755 --- a/src/cli/oneimage +++ b/src/cli/oneimage @@ -161,9 +161,11 @@ cmd=CommandParser::CmdParser.new(ARGV) do Creates a new Image from an existing one EOT - command :clone, clone_desc, :imageid, :name do + command :clone, clone_desc, :imageid, :name, + :options=>[OneDatastoreHelper::DATASTORE] do helper.perform_action(args[0],options,"cloned") do |image| - res = image.clone(args[1]) + ds_id = options[:datastore] || -1 # -1 clones to self + res = image.clone(args[1], ds_id) if !OpenNebula.is_error?(res) puts "ID: #{res}" diff --git a/src/oca/ruby/opennebula/image.rb b/src/oca/ruby/opennebula/image.rb index de4e679587..cc89dc1668 100644 --- a/src/oca/ruby/opennebula/image.rb +++ b/src/oca/ruby/opennebula/image.rb @@ -206,10 +206,10 @@ module OpenNebula # # @return [Integer, OpenNebula::Error] The new Image ID in case # of success, Error otherwise - def clone(name) + def clone(name, target_ds=-1) return Error.new('ID not defined') if !@pe_id - rc = @client.call(IMAGE_METHODS[:clone], @pe_id, name) + rc = @client.call(IMAGE_METHODS[:clone], @pe_id, name, target_ds) return rc end diff --git a/src/rm/RequestManagerImage.cc b/src/rm/RequestManagerImage.cc index 5cd8076015..fb19ba9814 100644 --- a/src/rm/RequestManagerImage.cc +++ b/src/rm/RequestManagerImage.cc @@ -230,12 +230,12 @@ void ImageClone::request_execute( string name = xmlrpc_c::value_string(paramList.getString(2)); long long avail, size; - int rc, new_id, ds_id; - string error_str, ds_name, ds_data; + int rc, new_id, ds_id_orig, ds_id = -1; + string error_str, ds_name, ds_data, ds_mad; bool ds_check; Image::DiskType disk_type; - PoolObjectAuth perms, ds_perms; + PoolObjectAuth perms, ds_perms, ds_perms_orig; ImageTemplate * tmpl; Template img_usage; @@ -247,6 +247,11 @@ void ImageClone::request_execute( DatastorePool * dspool = nd.get_dspool(); ImagePool * ipool = static_cast(pool); + if (paramList.size() > 3) + { + ds_id = xmlrpc_c::value_int(paramList.getInt(3)); + } + // ------------------------- Get source Image info ------------------------- img = ipool->get(clone_id, true); @@ -281,13 +286,18 @@ void ImageClone::request_execute( img->get_permissions(perms); - ds_id = img->get_ds_id(); - ds_name = img->get_ds_name(); - size = img->get_size(); + if (ds_id == -1) //Target Datastore not set, use the current one + { + ds_id = img->get_ds_id(); + } + + ds_id_orig = img->get_ds_id(); + + size = img->get_size(); img->unlock(); - // ------------------------- Get Datastore info ---------------------------- + // ----------------------- Get target Datastore info ----------------------- ds = dspool->get(ds_id, true); @@ -301,15 +311,14 @@ void ImageClone::request_execute( return; } - if ( ds->get_type() == Datastore::FILE_DS ) + if ( ds->get_type() != Datastore::IMAGE_DS ) { - failure_response(ACTION, "Clone not supported for FILE_DS Datastores", - att); - - delete tmpl; + failure_response(ACTION, + request_error("Clone only supported for IMAGE_DS Datastores",""),att); ds->unlock(); + delete tmpl; return; } @@ -320,9 +329,51 @@ void ImageClone::request_execute( ds->to_xml(ds_data); ds_check = ds->get_avail_mb(avail); + ds_name = ds->get_name(); + ds_mad = ds->get_ds_mad(); ds->unlock(); + if (ds_id != ds_id_orig) //check same DS_MAD + { + ds = dspool->get(ds_id_orig, true); + + if (ds == 0) + { + failure_response(NO_EXISTS, + get_error(object_name(PoolObjectSQL::DATASTORE),ds_id_orig),att); + + delete tmpl; + return; + } + + if (ds->get_type() != Datastore::IMAGE_DS) + { + failure_response(ACTION, request_error( + "Clone only supported for IMAGE_DS Datastores",""), att); + + ds->unlock(); + + delete tmpl; + return; + } + + if (ds->get_ds_mad() != ds_mad) + { + failure_response(ACTION, request_error( + "Clone only supported to same DS_MAD Datastores",""), att); + + ds->unlock(); + + delete tmpl; + return; + } + + ds->get_permissions(ds_perms_orig); + + ds->unlock(); + } + // ------------- Set authorization request --------------------------------- img_usage.add("DATASTORE", ds_id); @@ -330,7 +381,8 @@ void ImageClone::request_execute( if (ds_check && (size > avail)) { - failure_response(ACTION, "Not enough space in datastore", att); + failure_response(ACTION, + request_error("Not enough space in datastore",""), att); delete tmpl; return; @@ -349,6 +401,11 @@ void ImageClone::request_execute( ar.add_auth(AuthRequest::USE, ds_perms); // USE DATASTORE + if (ds_id != ds_id_orig) // USE (original) DATASTORE + { + ar.add_auth(AuthRequest::USE, ds_perms_orig); // USE DATASTORE + } + if (UserPool::authorize(ar) == -1) { failure_response(AUTHORIZATION,