diff --git a/include/Image.h b/include/Image.h index 3b4027b9bf..44052e7a0f 100644 --- a/include/Image.h +++ b/include/Image.h @@ -95,6 +95,25 @@ public: ERROR = 5 /** < Error state the operation FAILED*/ }; + /** + * Returns the string representation of an ImageState + * @param state The state + * @return the string representation + */ + static string state_to_str(ImageState state) + { + switch(state) + { + case INIT: return "INIT"; break; + case READY: return "READY"; break; + case USED: return "USED"; break; + case DISABLED: return "DISABLED"; break; + case LOCKED: return "LOCKED"; break; + case ERROR: return "ERROR"; break; + default: return ""; + } + }; + // ************************************************************************* // Image Public Methods // ************************************************************************* @@ -187,7 +206,7 @@ public: * Returns the image state * @return state of image */ - ImageState get_state() + ImageState get_state() const { return state; } @@ -225,6 +244,36 @@ public: return running_vms; } + int get_cloning() + { + return cloning_ops; + } + + int dec_cloning() + { + return --cloning_ops; + } + + int inc_cloning() + { + return ++cloning_ops; + } + + int get_source_img() + { + return source_img_id; + } + + void set_source_img(int id) + { + source_img_id = id; + } + + void unset_source_img() + { + source_img_id = -1; + } + /** * Sets the Image type. * @@ -365,7 +414,18 @@ public: { return ds_name; }; - + + /** + * Prepares a template to allocate a clone of this Image + * + * @param new_name Value for the NAME attribute + * @param tmpl Will contain the resulting template, if any + * @param error_str Returns the error reason, if any + * @return 0 in case of success, -1 otherwise + */ + int clone_template(string& new_name, + ImageTemplate * &tmpl, string& error_str) const; + private: // ------------------------------------------------------------------------- @@ -428,6 +488,17 @@ private: */ int running_vms; + /** + * Number of pending cloning operations + */ + int cloning_ops; + + /** + * Indicates if this Image is a clone of another one. + * Once the clone process is complete, it should be set to -1 + */ + int source_img_id; + /** * Datastore ID */ diff --git a/include/ImagePool.h b/include/ImagePool.h index 1bc30383cd..36713732a9 100644 --- a/include/ImagePool.h +++ b/include/ImagePool.h @@ -56,6 +56,9 @@ public: * @param ds_name the name of the datastore * @param ds_type disk type for the image * @param ds_data the datastore data + * @param source_img_id If the new Image is a clone, this must be the + * source Image ID. Otherwise, it must be set to -1 + * * @param oid the id assigned to the Image * @param error_str Returns the error reason, if any * @return the oid assigned to the object, @@ -72,6 +75,7 @@ public: const string& ds_name, Image::DiskType ds_type, const string& ds_data, + int source_img_id, int * oid, string& error_str); diff --git a/include/ImageTemplate.h b/include/ImageTemplate.h index 36c2727bb6..d6340432f0 100644 --- a/include/ImageTemplate.h +++ b/include/ImageTemplate.h @@ -29,6 +29,8 @@ class ImageTemplate : public Template public: ImageTemplate() : Template(true,'=',"TEMPLATE"){}; + ImageTemplate(ImageTemplate& tmpl):Template(tmpl){}; + ~ImageTemplate(){}; /** diff --git a/include/PoolObjectSQL.h b/include/PoolObjectSQL.h index bd4e72b449..7cba12669c 100644 --- a/include/PoolObjectSQL.h +++ b/include/PoolObjectSQL.h @@ -324,13 +324,7 @@ public: const string& name, const string& value) { - SingleAttribute * sattr = new SingleAttribute(name,value); - - obj_template->erase(sattr->name()); - - obj_template->set(sattr); - - return 0; + return obj_template->replace(name, value); } /** diff --git a/include/RequestManagerAllocate.h b/include/RequestManagerAllocate.h index e13bdbc42c..01161b6b3d 100644 --- a/include/RequestManagerAllocate.h +++ b/include/RequestManagerAllocate.h @@ -196,6 +196,7 @@ public: { Nebula& nd = Nebula::instance(); pool = nd.get_ipool(); + dspool = nd.get_dspool(); auth_object = PoolObjectSQL::IMAGE; }; @@ -209,6 +210,9 @@ public: bool allocate_authorization(Template * obj_template, RequestAttributes& att, PoolObjectAuth * cluster_perms); + +private: + DatastorePool * dspool; }; /* ------------------------------------------------------------------------- */ diff --git a/include/RequestManagerImage.h b/include/RequestManagerImage.h index a6f6298bbd..a8e9bc4344 100644 --- a/include/RequestManagerImage.h +++ b/include/RequestManagerImage.h @@ -99,6 +99,32 @@ public: RequestAttributes& att); }; +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +class ImageClone : public RequestManagerImage +{ +public: + ImageClone(): + RequestManagerImage("ImageClone", + "Clones an existing image", + "A:sis") + { + auth_op = AuthRequest::USE; + + Nebula& nd = Nebula::instance(); + dspool = nd.get_dspool(); + }; + + ~ImageClone(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); + +private: + DatastorePool * dspool; +}; + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/Template.h b/include/Template.h index ab4ab441fd..74203ad000 100644 --- a/include/Template.h +++ b/include/Template.h @@ -134,6 +134,15 @@ public: */ virtual void set(Attribute * attr); + /** + * Adds a new attribute to the template (replacing it if + * already defined) + * @param name of the new attribute + * @param value of the new attribute + * @return 0 on success + */ + int replace(const string& name, const string& value); + /** * Removes an attribute from the template. The attributes are returned. The * attributes MUST be freed by the calling funtion diff --git a/src/image/Image.cc b/src/image/Image.cc index b0eb1b53a9..7f647b2285 100644 --- a/src/image/Image.cc +++ b/src/image/Image.cc @@ -49,6 +49,8 @@ Image::Image(int _uid, size_mb(0), state(INIT), running_vms(0), + cloning_ops(0), + source_img_id(-1), ds_id(-1), ds_name("") { @@ -355,6 +357,8 @@ string& Image::to_xml(string& xml) const "" << size_mb << "" << "" << state << "" << "" << running_vms << "" << + "" << cloning_ops << "" << + "" << source_img_id << "" << "" << ds_id << ""<< "" << ds_name << "" << obj_template->to_xml(template_xml) << @@ -381,27 +385,29 @@ int Image::from_xml(const string& xml) update_from_str(xml); // Get class base attributes - rc += xpath(oid, "/IMAGE/ID", -1); - rc += xpath(uid, "/IMAGE/UID", -1); - rc += xpath(gid, "/IMAGE/GID", -1); + rc += xpath(oid, "/IMAGE/ID", -1); + rc += xpath(uid, "/IMAGE/UID", -1); + rc += xpath(gid, "/IMAGE/GID", -1); - rc += xpath(uname, "/IMAGE/UNAME", "not_found"); - rc += xpath(gname, "/IMAGE/GNAME", "not_found"); + rc += xpath(uname, "/IMAGE/UNAME", "not_found"); + rc += xpath(gname, "/IMAGE/GNAME", "not_found"); - rc += xpath(name, "/IMAGE/NAME", "not_found"); + rc += xpath(name, "/IMAGE/NAME", "not_found"); - rc += xpath(int_type, "/IMAGE/TYPE", 0); - rc += xpath(int_disk_type, "/IMAGE/DISK_TYPE", 0); - rc += xpath(persistent_img, "/IMAGE/PERSISTENT", 0); - rc += xpath(regtime, "/IMAGE/REGTIME", 0); + rc += xpath(int_type, "/IMAGE/TYPE", 0); + rc += xpath(int_disk_type, "/IMAGE/DISK_TYPE", 0); + rc += xpath(persistent_img, "/IMAGE/PERSISTENT",0); + rc += xpath(regtime, "/IMAGE/REGTIME", 0); - rc += xpath(source, "/IMAGE/SOURCE", "not_found"); - rc += xpath(size_mb, "/IMAGE/SIZE", 0); - rc += xpath(int_state, "/IMAGE/STATE", 0); - rc += xpath(running_vms, "/IMAGE/RUNNING_VMS", -1); + rc += xpath(source, "/IMAGE/SOURCE", "not_found"); + rc += xpath(size_mb, "/IMAGE/SIZE", 0); + rc += xpath(int_state, "/IMAGE/STATE", 0); + rc += xpath(running_vms, "/IMAGE/RUNNING_VMS", -1); + rc += xpath(cloning_ops, "/IMAGE/CLONING_OPS", -1); + rc += xpath(source_img_id, "/IMAGE/SOURCE_IMG", -1); - rc += xpath(ds_id, "/IMAGE/DATASTORE_ID", -1); - rc += xpath(ds_name,"/IMAGE/DATASTORE", "not_found"); + rc += xpath(ds_id, "/IMAGE/DATASTORE_ID", -1); + rc += xpath(ds_name, "/IMAGE/DATASTORE", "not_found"); // Permissions rc += perms_from_xml(); @@ -561,3 +567,63 @@ int Image::set_type(string& _type) /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ + +int Image::clone_template(string& new_name, + ImageTemplate * &tmpl, string& error_str) const +{ + if ( get_state() != READY ) + { + if ( isPersistent() ) + { + ostringstream oss; + + oss << "Image [" << oid << "] is in state " + << state_to_str( get_state() ) + << "; persistent Images can only be cloned in the " + << state_to_str( READY ) << " state."; + + error_str = oss.str(); + return -1; + } + else if ( get_state() != USED ) + { + ostringstream oss; + + oss << "Image [" << oid << "] is in state " + << state_to_str( get_state() ) + << "; non-persistent Images can only be cloned in the " + << state_to_str( READY ) << " or " + << state_to_str(USED) << " states."; + + error_str = oss.str(); + return -1; + } + } + + tmpl = new ImageTemplate( + *(static_cast(obj_template))); + + tmpl->replace("NAME", new_name); + tmpl->replace("TYPE", type_to_str(type)); + tmpl->replace("PATH", source); + tmpl->replace("FSTYPE", fs_type); + + ostringstream oss; + oss << size_mb; + + tmpl->replace("SIZE", oss.str()); + + if ( isPersistent() ) + { + tmpl->replace("PERSISTENT", "YES"); + } + else + { + tmpl->replace("PERSISTENT", "NO"); + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index 17f63c3f62..f370266cf4 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -159,7 +159,7 @@ void ImageManager::release_image(int iid, bool failed) img->set_state(Image::READY); } } - else if ( rvms == 0 ) + else if ( rvms == 0 && img->get_cloning() == 0 ) { img->set_state(Image::READY); } @@ -263,6 +263,7 @@ int ImageManager::delete_image(int iid, const string& ds_data) string source; string img_tmpl; string * drv_msg; + int source_id; img = ipool->get(iid,true); @@ -274,7 +275,7 @@ int ImageManager::delete_image(int iid, const string& ds_data) switch(img->get_state()) { case Image::READY: - if ( img->get_running() != 0 ) + if ( img->get_running() != 0 || img->get_cloning() > 0 ) { img->unlock(); return -1; //Cannot remove images in use @@ -293,6 +294,45 @@ int ImageManager::delete_image(int iid, const string& ds_data) break; } + source_id = img->get_source_img(); + + if ( source_id != -1 ) // An Image clone was in progress + { + Image * source_image; + + // Unlock to avoid death-locks with source_image + img->unlock(); + + source_image = ipool->get(source_id, true); + + if ( source_image != 0 ) + { + source_image->dec_cloning(); + + if ( source_image->get_state() == Image::USED && + source_image->get_running() == 0 && + source_image->get_cloning() == 0 ) + { + source_image->set_state(Image::READY); + } + + ipool->update(source_image); + source_image->unlock(); + } + + // Lock the image again + img = ipool->get(iid,true); + + if ( img == 0 ) + { + return -1; + } + + img->unset_source_img(); + + ipool->update(img); + } + const ImageManagerDriver* imd = get(); if ( imd == 0 ) diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index 43eae93c5a..7aaa002567 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -163,6 +163,43 @@ void ImageManagerDriver::protocol( // Driver Actions if ( action == "CP" ) { + int source_id = image->get_source_img(); + + if ( source_id != -1 ) // An Image clone operation finished + { + Image * source_image; + + // Unlock to avoid death-locks with source_image + image->unlock(); + + source_image = ipool->get(source_id, true); + + if ( source_image != 0 ) + { + source_image->dec_cloning(); + + if ( source_image->get_state() == Image::USED && + source_image->get_running() == 0 && + source_image->get_cloning() == 0 ) + { + source_image->set_state(Image::READY); + } + + ipool->update(source_image); + source_image->unlock(); + } + + // Lock the image again + image = ipool->get(id,true); + + if ( image == 0 ) + { + return; + } + + image->unset_source_img(); + } + if ( result == "SUCCESS" ) { image->set_source(source); diff --git a/src/image/ImagePool.cc b/src/image/ImagePool.cc index 251c037a0e..560c786e5b 100644 --- a/src/image/ImagePool.cc +++ b/src/image/ImagePool.cc @@ -69,6 +69,7 @@ int ImagePool::allocate ( const string& ds_name, Image::DiskType disk_type, const string& ds_data, + int source_img_id, int * oid, string& error_str) { @@ -106,6 +107,11 @@ int ImagePool::allocate ( img->disk_type = disk_type; + if ( source_img_id != -1 ) + { + img->set_source_img(source_img_id); + } + // --------------------------------------------------------------------- // Insert the Object in the pool & Register the image in the repository // --------------------------------------------------------------------- diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index e2413c867c..f69edcc164 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -309,6 +309,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr image_persistent(new ImagePersistent()); xmlrpc_c::methodPtr image_enable(new ImageEnable()); xmlrpc_c::methodPtr image_chtype(new ImageChangeType()); + xmlrpc_c::methodPtr image_clone(new ImageClone()); // Chown Methods xmlrpc_c::methodPtr vm_chown(new VirtualMachineChown()); @@ -414,6 +415,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.image.chown", image_chown); RequestManagerRegistry.addMethod("one.image.chmod", image_chmod); RequestManagerRegistry.addMethod("one.image.chtype", image_chtype); + RequestManagerRegistry.addMethod("one.image.clone", image_clone); RequestManagerRegistry.addMethod("one.imagepool.info", imagepool_info); diff --git a/src/rm/RequestManagerAllocate.cc b/src/rm/RequestManagerAllocate.cc index 8d7221c1cd..23faf9c0a7 100644 --- a/src/rm/RequestManagerAllocate.cc +++ b/src/rm/RequestManagerAllocate.cc @@ -306,9 +306,6 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, string str_tmpl = xmlrpc_c::value_string(params.getString(1)); int ds_id = xmlrpc_c::value_int(params.getInt(2)); - Nebula& nd = Nebula::instance(); - - DatastorePool * dspool = nd.get_dspool(); ImagePool * ipool = static_cast(pool); ImageTemplate * tmpl = new ImageTemplate; @@ -392,6 +389,7 @@ void ImageAllocate::request_execute(xmlrpc_c::paramList const& params, ds_name, ds_disk_type, ds_data, + -1, &id, error_str); if ( rc < 0 ) diff --git a/src/rm/RequestManagerImage.cc b/src/rm/RequestManagerImage.cc index 206e0aec34..bf25072254 100644 --- a/src/rm/RequestManagerImage.cc +++ b/src/rm/RequestManagerImage.cc @@ -160,3 +160,127 @@ void ImageChangeType::request_execute(xmlrpc_c::paramList const& paramList, success_response(id, att); } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void ImageClone::request_execute( + xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + int source_id = xmlrpc_c::value_int(paramList.getInt(1)); + string name = xmlrpc_c::value_string(paramList.getString(2)); + + int rc, new_id, ds_id; + string error_str, ds_name, ds_data; + + Image::DiskType disk_type; + PoolObjectAuth perms, ds_perms; + ImageTemplate * tmpl = 0; + Image * source_img; + Datastore * ds; + + ImagePool * ipool = static_cast(pool); + + // ------------------------- Get source Image info ------------------------- + + source_img = ipool->get(source_id, true); + + if ( source_img == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(auth_object),source_id), + att); + + return; + } + + rc = source_img->clone_template(name, tmpl, error_str); + + if ( rc != 0 ) + { + source_img->unlock(); + + failure_response(INTERNAL, + request_error("Could not clone Image",error_str), + att); + + return; + } + + source_img->get_permissions(perms); + + ds_id = source_img->get_ds_id(); + ds_name = source_img->get_ds_name(); + + source_img->unlock(); + + // ------------------------- Get Datastore info ---------------------------- + + ds = dspool->get(ds_id, true); + if ( ds == 0 ) + { + failure_response(NO_EXISTS, + get_error(object_name(PoolObjectSQL::DATASTORE), ds_id), + att); + + delete tmpl; + return; + } + + ds->get_permissions(ds_perms); + disk_type = ds->get_disk_type(); + ds->to_xml(ds_data); + + ds->unlock(); + + // ------------- Set authorization request --------------------------------- + + if ( att.uid != 0 ) + { + AuthRequest ar(att.uid, att.gid); + string tmpl_str = ""; + + tmpl->to_xml(tmpl_str); + + ar.add_create_auth(auth_object, tmpl_str); // CREATE IMAGE + + ar.add_auth(AuthRequest::USE, ds_perms); // USE DATASTORE + + if (UserPool::authorize(ar) == -1) + { + failure_response(AUTHORIZATION, + authorization_error(ar.message, att), + att); + + delete tmpl; + return; + } + } + + rc = ipool->allocate(att.uid, att.gid, att.uname, att.gname, + tmpl, ds_id, ds_name, disk_type, + ds_data, source_id, &new_id, error_str); + + if ( rc < 0 ) + { + failure_response(INTERNAL, allocate_error(error_str), att); + return; + } + + source_img = ipool->get(source_id, true); + + if ( source_img != 0 ) + { + source_img->inc_cloning(); + source_img->set_state(Image::USED); + + ipool->update(source_img); + + source_img->unlock(); + } + + success_response(new_id, att); +} + + diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index fbfdcc102d..061ddb45c5 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -562,6 +562,7 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis ds_name, ds_disk_type, ds_data, + -1, &iid, error_str); if (rc < 0) diff --git a/src/template/Template.cc b/src/template/Template.cc index 18227d4af6..acc0487a39 100644 --- a/src/template/Template.cc +++ b/src/template/Template.cc @@ -200,6 +200,7 @@ void Template::marshall(string &str, const char delim) delete attr; } } + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -227,6 +228,19 @@ void Template::set(Attribute * attr) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int Template::replace(const string& name, const string& value) +{ + SingleAttribute * sattr = new SingleAttribute(name,value); + + erase(name); + set(sattr); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int Template::remove(const string& name, vector& values) { multimap::iterator i;