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:
parent
4b66f92b08
commit
cdc7961e92
@ -176,7 +176,7 @@ public:
|
||||
VirtualMachineSaveDisk():
|
||||
RequestManagerVirtualMachine("VirtualMachineSaveDisk",
|
||||
"Saves a disk from virtual machine as a new image",
|
||||
"A:siissbb"){};
|
||||
"A:siissb"){};
|
||||
|
||||
~VirtualMachineSaveDisk(){};
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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": {
|
||||
|
@ -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");
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user