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:
parent
6f28628042
commit
b377e4db99
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -29,6 +29,8 @@ class ImageTemplate : public Template
|
||||
public:
|
||||
ImageTemplate() : Template(true,'=',"TEMPLATE"){};
|
||||
|
||||
ImageTemplate(ImageTemplate& tmpl):Template(tmpl){};
|
||||
|
||||
~ImageTemplate(){};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
@ -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 )
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
// ---------------------------------------------------------------------
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user