1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-25 02:50:08 +03:00

Feature #1291: New one.image.clone method

The core functionality is in place, but it is using the CP command.
To make it work, the ds_map cp scripts need to ignore the
check_restricted call. We will have to add another script to
the ds_mad drivers, probably clone
This commit is contained in:
Carlos Martín 2012-05-25 12:56:51 +02:00
parent 6f28628042
commit b377e4db99
16 changed files with 428 additions and 30 deletions

View File

@ -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
*/

View File

@ -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);

View File

@ -29,6 +29,8 @@ class ImageTemplate : public Template
public:
ImageTemplate() : Template(true,'=',"TEMPLATE"){};
ImageTemplate(ImageTemplate& tmpl):Template(tmpl){};
~ImageTemplate(){};
/**

View File

@ -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);
}
/**

View File

@ -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;
};
/* ------------------------------------------------------------------------- */

View File

@ -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;
};
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -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

View File

@ -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>" << size_mb << "</SIZE>" <<
"<STATE>" << state << "</STATE>" <<
"<RUNNING_VMS>" << running_vms << "</RUNNING_VMS>" <<
"<CLONING_OPS>" << cloning_ops << "</CLONING_OPS>" <<
"<SOURCE_IMG>" << source_img_id << "</SOURCE_IMG>" <<
"<DATASTORE_ID>" << ds_id << "</DATASTORE_ID>"<<
"<DATASTORE>" << ds_name << "</DATASTORE>" <<
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<ImageTemplate *>(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;
}
/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */

View File

@ -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 )

View File

@ -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);

View File

@ -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
// ---------------------------------------------------------------------

View File

@ -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);

View File

@ -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<ImagePool *>(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 )

View File

@ -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<ImagePool *>(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);
}

View File

@ -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)

View File

@ -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<Attribute *>& values)
{
multimap<string, Attribute *>::iterator i;