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

Feature #2051: Save VMs to a new template in the ruby oca instead of oned

This commit is contained in:
Carlos Martín 2015-05-07 15:50:38 +02:00
parent 4b66f92b08
commit cdc7961e92
8 changed files with 208 additions and 150 deletions

View File

@ -176,7 +176,7 @@ public:
VirtualMachineSaveDisk():
RequestManagerVirtualMachine("VirtualMachineSaveDisk",
"Saves a disk from virtual machine as a new image",
"A:siissbb"){};
"A:siissb"){};
~VirtualMachineSaveDisk(){};

View File

@ -97,13 +97,6 @@ cmd=CommandParser::CmdParser.new(ARGV) do
:description => "Recover a VM by retrying the last failed action"
}
CLONETEMPLATE={
:name => "clonetemplate",
:short => "-c",
:large => "--clonetemplate",
:description => "Clone original VM Template and replace disk with saved one"
}
########################################################################
# Global Options
########################################################################
@ -310,7 +303,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
EOT
command :"disk-snapshot", disk_snapshot_desc, :vmid, :diskid, :img_name,
:options=>[TYPE, OneVMHelper::LIVE, CLONETEMPLATE] do
:options=>[TYPE, OneVMHelper::LIVE] do
disk_id = args[1].to_i
image_name = args[2]
image_type = options[:type] || ""
@ -320,7 +313,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
helper.perform_action(args[0],options,verbose) do |vm|
res = vm.disk_snapshot(disk_id, image_name, image_type,
options[:live]==true, options[:clonetemplate]==true)
options[:live]==true)
if !OpenNebula.is_error?(res)
puts "Image ID: #{res}"
@ -843,6 +836,23 @@ cmd=CommandParser::CmdParser.new(ARGV) do
end
end
save_desc = <<-EOT.unindent
Clones the VM's source Template, replacing the disks with live snapshots
of the current disks. The VM capacity and NICs are also preserved
EOT
command :save, save_desc, :vmid, :name, :options=>[OneVMHelper::LIVE] do
helper.perform_action(args[0],options,"Saving VM") do |vm|
res = vm.save_as_template(args[1])
if !OpenNebula.is_error?(res)
puts "Template ID: #{res}"
end
res
end
end
# Deprecated commands
deprecated_command(:attachdisk, 'disk-attach')

View File

@ -399,16 +399,12 @@ public class VirtualMachine extends PoolElement{
* the default type
* @param hot True to save the disk immediately, false will perform
* the operation when the VM shuts down
* @param doTemplate True to clone also the VM originating template
* and replace the disk with the saved image
* @return If an error occurs the error message contains the reason.
*/
public static OneResponse diskSnapshot(Client client, int id,
int diskId, String imageName, String imageType,
boolean hot, boolean doTemplate)
int diskId, String imageName, String imageType, boolean hot)
{
return client.call(SAVEDISK, id ,diskId, imageName, imageType,
hot, doTemplate);
return client.call(SAVEDISK, id ,diskId, imageName, imageType, hot);
}
/**
@ -747,15 +743,12 @@ public class VirtualMachine extends PoolElement{
* the default type
* @param hot True to save the disk immediately, false will perform
* the operation when the VM shuts down
* @param doTemplate True to clone also the VM originating template
* and replace the disk with the saved image
* @return If an error occurs the error message contains the reason.
*/
public OneResponse diskSnapshot(int diskId, String imageName,
String imageType, boolean hot, boolean doTemplate)
String imageType, boolean hot)
{
return diskSnapshot(client, id, diskId, imageName, imageType,
hot, doTemplate);
return diskSnapshot(client, id, diskId, imageName, imageType, hot);
}
/**
@ -768,7 +761,7 @@ public class VirtualMachine extends PoolElement{
*/
public OneResponse diskSnapshot(int diskId, String imageName)
{
return diskSnapshot(diskId, imageName, "", false, false);
return diskSnapshot(diskId, imageName, "", false);
}
/**
@ -782,7 +775,7 @@ public class VirtualMachine extends PoolElement{
*/
public OneResponse diskSnapshot(int diskId, String imageName, boolean hot)
{
return diskSnapshot(diskId, imageName, "", hot, false);
return diskSnapshot(diskId, imageName, "", hot);
}
/**
@ -1170,11 +1163,11 @@ public class VirtualMachine extends PoolElement{
}
/**
* @deprecated Replaced by {@link #diskSnapshot(int,String,String,boolean,boolean)}
* @deprecated Replaced by {@link #diskSnapshot(int,String,String,boolean)}
*/
public OneResponse savedisk(int diskId, String imageName, String imageType)
{
return diskSnapshot(diskId, imageName, imageType, false, false);
return diskSnapshot(diskId, imageName, imageType, false);
}
/**

View File

@ -457,13 +457,10 @@ module OpenNebula
# to use the default type
# @param hot [true|false] True to save the disk immediately, false will
# perform the operation when the VM shuts down
# @param do_template [true|false] True to clone also the VM originating
# template and replace the disk with the saved image
#
# @return [Integer, OpenNebula::Error] the new Image ID in case of
# success, error otherwise
def disk_snapshot(disk_id, image_name, image_type="", hot=false,
do_template=false)
def disk_snapshot(disk_id, image_name, image_type="", hot=false)
return Error.new('ID not defined') if !@pe_id
rc = @client.call(VM_METHODS[:savedisk],
@ -471,8 +468,7 @@ module OpenNebula
disk_id,
image_name,
image_type,
hot,
do_template)
hot)
return rc
end
@ -667,6 +663,125 @@ module OpenNebula
self['DEPLOY_ID']
end
# Clones the VM's source Template, replacing the disks with live snapshots
# of the current disks. The VM capacity and NICs are also preserved
#
# @param name [String] Name for the new Template
#
# @return [Integer, OpenNebula::Error] the new Template ID in case of
# success, error otherwise
def save_as_template(name)
rc = info()
return rc if OpenNebula.is_error?(rc)
tid = self['TEMPLATE/TEMPLATE_ID']
if tid.nil? || tid.empty?
return Error.new('VM has no template to be saved')
end
if state_str() != "POWEROFF"
return Error.new("VM state must be POWEROFF, "<<
"current state is #{state_str()}, #{lcm_state_str()}")
end
# Clone the source template
new_tid = OpenNebula::Template.new_with_id(tid, @client).clone(name)
return new_tid if OpenNebula.is_error?(new_tid)
# Replace the original template's capacity with the actual VM values
replace = ""
cpu = self['TEMPLATE/CPU']
if !cpu.nil? && !cpu.empty?
replace << "CPU = #{cpu}\n"
end
vcpu = self['TEMPLATE/VCPU']
if !vcpu.nil? && !vcpu.empty?
replace << "VCPU = #{vcpu}\n"
end
mem = self['TEMPLATE/MEMORY']
if !mem.nil? && !mem.empty?
replace << "MEMORY = #{mem}\n"
end
self.each('TEMPLATE/DISK') do |disk|
# While the previous snapshot is still in progress, we wait
# indefinitely
rc = info()
return rc if OpenNebula.is_error?(rc)
steps = 0
while lcm_state_str() == "HOTPLUG_SAVEAS_POWEROFF"
if steps < 30
sleep 1
else
sleep 15
end
rc = info()
return rc if OpenNebula.is_error?(rc)
steps += 1
end
# If the VM is not busy with a previous disk snapshot, we wait
# but this time with a timeout
rc = wait_state("POWEROFF")
return rc if OpenNebula.is_error?(rc)
disk_id = disk["DISK_ID"]
if disk_id.nil? || disk_id.empty?
return Error.new('The DISK_ID is missing from the VM template')
end
image_id = disk["IMAGE_ID"]
if !image_id.nil? && !image_id.empty?
rc = disk_snapshot(disk_id.to_i, "#{name}-disk-#{disk_id}",
"", true)
return rc if OpenNebula.is_error?(rc)
replace << "DISK = [ IMAGE_ID = #{rc} ]\n"
else
# Volatile disks cannot be saved, so the definition is copied
replace << self.template_like_str(
"TEMPLATE", true, "DISK[DISK_ID=#{disk_id}]") << "\n"
end
end
self.each('TEMPLATE/NIC') do |nic|
nic_id = nic["NIC_ID"]
if nic_id.nil? || nic_id.empty?
return Error.new('The NIC_ID is missing from the VM template')
end
net_id = nic["NETWORK_ID"]
if !net_id.nil? && !net_id.empty?
replace << "NIC = [ NETWORK_ID = #{net_id} ]\n"
else
# This NIC does not use a Virtual Network
replace << self.template_like_str(
"TEMPLATE", true, "NIC[NIC_ID=#{nic_id}]") << "\n"
end
end
# Required by the Sunstone Cloud View
replace << "SAVED_TEMPLATE_ID = #{tid}\n"
new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client)
rc = new_tmpl.update(replace, true)
return rc if OpenNebula.is_error?(rc)
return new_tid
# TODO: rollback in case of error
end
private
def action(name)
return Error.new('ID not defined') if !@pe_id
@ -676,5 +791,49 @@ module OpenNebula
return rc
end
def wait_state(state, timeout=10)
vm_state = ""
lcm_state = ""
timeout.times do
rc = info()
return rc if OpenNebula.is_error?(rc)
vm_state = state_str()
lcm_state = lcm_state_str()
if vm_state == state
return true
end
sleep 1
end
return Error.new("Timeout expired for state #{state}. "<<
"VM is in state #{vm_state}, #{lcm_state}")
end
def wait_lcm_state(state, timeout=10)
vm_state = ""
lcm_state = ""
timeout.times do
rc = info()
return rc if OpenNebula.is_error?(rc)
vm_state = state_str()
lcm_state = lcm_state_str()
if lcm_state == state
return true
end
sleep 1
end
return Error.new("Timeout expired for state #{state}. "<<
"VM is in state #{vm_state}, #{lcm_state}")
end
end
end

View File

@ -1202,30 +1202,22 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis
ImagePool * ipool = nd.get_ipool();
DatastorePool * dspool = nd.get_dspool();
VMTemplatePool* tpool = nd.get_tpool();
int id = xmlrpc_c::value_int(paramList.getInt(1));
int disk_id = xmlrpc_c::value_int(paramList.getInt(2));
string img_name = xmlrpc_c::value_string(paramList.getString(3));
string img_type = xmlrpc_c::value_string(paramList.getString(4));
bool is_hot = false; //Optional XML-RPC argument
bool do_template = false; //Optional XML-RPC argument
if ( paramList.size() > 5 )
{
is_hot = xmlrpc_c::value_boolean(paramList.getBoolean(5));
}
if ( paramList.size() > 6 )
{
do_template = xmlrpc_c::value_boolean(paramList.getBoolean(6));
}
VirtualMachinePool * vmpool = static_cast<VirtualMachinePool *>(pool);
VirtualMachine * vm;
Datastore * ds;
int iid;
int tid;
string error_str;
@ -1265,18 +1257,6 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis
return;
}
if (do_template && !vm->get_template_attribute("TEMPLATE_ID",tid))
{
vm->clear_saveas_state(disk_id, is_hot);
vm->unlock();
failure_response(ACTION,
request_error("VM has no template to be saved",""),
att);
return;
}
vmpool->update(vm);
vm->unlock();
@ -1502,96 +1482,6 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis
ds->unlock();
}
// Return the new allocated Image ID
if (!do_template)
{
success_response(iid, att);
return;
}
// -------------------------------------------------------------------------
// Clone original template and replace disk with saved one
// -------------------------------------------------------------------------
int ntid;
PoolObjectAuth perms;
VMTemplate * vm_tmpl = tpool->get(tid,true);
if ( vm_tmpl == 0 ) //Failed to get original template return saved image id
{
ostringstream error;
error << get_error(object_name(PoolObjectSQL::TEMPLATE), tid)
<< "Image successfully saved with id: " << iid;
failure_response(NO_EXISTS, error.str(), att);
return;
}
VirtualMachineTemplate * tmpl = vm_tmpl->clone_template();
vm_tmpl->get_permissions(perms);
vm_tmpl->unlock();
//Setup the new template: name and replace disk
ostringstream tmpl_name;
tmpl_name << img_name << "-" << iid;
tmpl->replace("NAME", tmpl_name.str());
tmpl->replace("SAVED_TEMPLATE_ID", tid);
tmpl->replace("SAVED_TO_IMAGE_ID", iid);
tmpl->replace_disk_image(iid_orig, iname_orig, iuname_orig, img_name, att.uname);
//Authorize the template creation operation
if ( att.uid != 0 )
{
string tmpl_str = "";
AuthRequest ar(att.uid, att.group_ids);
ar.add_auth(AuthRequest::USE, perms);
tmpl->to_xml(tmpl_str);
ar.add_create_auth(att.uid, att.gid, PoolObjectSQL::TEMPLATE, tmpl_str);
if (UserPool::authorize(ar) == -1)
{
delete tmpl;
ostringstream error;
error << authorization_error(ar.message, att)
<< "Image successfully saved with id: " << iid;
failure_response(AUTHORIZATION, error.str(), att);
return;
}
}
//Allocate the template
rc = tpool->allocate(att.uid, att.gid, att.uname, att.gname, att.umask,
tmpl, &ntid, error_str);
if (rc < 0)
{
ostringstream error;
error << allocate_error(PoolObjectSQL::TEMPLATE, error_str)
<< "Image successfully saved with id: " << iid;
failure_response(INTERNAL, error.str(), att);
return;
}
success_response(iid, att);
}

View File

@ -76,6 +76,7 @@ module OpenNebulaJSON
when "resched" then self.resched
when "unresched" then self.unresched
when "recover" then self.recover(action_hash['params'])
when "save_as_template" then self.save_as_template(action_hash['params'])
else
error_msg = "#{action_hash['perform']} action not " <<
" available for this resource"
@ -171,5 +172,9 @@ module OpenNebulaJSON
def recover(params=Hash.new)
super(params['result'].to_i)
end
def save_as_template(params=Hash.new)
super(params['name'])
end
end
end

View File

@ -1226,7 +1226,12 @@ var OpenNebula = {
},
"showback": function(params){
OpenNebula.Action.showback(params,OpenNebula.VM.resource);
}
},
"save_as_template": function(params){
var action_obj = params.data.extra_param;
OpenNebula.Action.simple_action(params,OpenNebula.VM.resource,
"save_as_template",action_obj);
},
},
"Group": {

View File

@ -3887,22 +3887,18 @@ function setup_info_vm(context) {
var context = $(".provision_info_vm[vm_id]");
var vm_id = context.attr("vm_id");
var image_name = $('.provision_snapshot_name', context).val();
var template_name = $('.provision_snapshot_name', context).val();
OpenNebula.VM.saveas({
OpenNebula.VM.save_as_template({
data : {
id: vm_id,
extra_param: {
disk_id : "0",
image_name : image_name,
type: "",
clonetemplate: true,
hot: true
name : template_name
}
},
success: function(request, response){
OpenNebula.Helper.clear_cache("VMTEMPLATE");
notifyMessage(tr("Image") + ' ' + request.request.data[0][1].image_name + ' ' + tr("saved successfully"))
notifyMessage(tr("Image") + ' ' + request.request.data[0][1].name + ' ' + tr("saved successfully"))
update_provision_vm_info(vm_id, context);
button.removeAttr("disabled");
},