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;