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

Feature #2051: Implement rollback in case of error

This commit is contained in:
Carlos Martín 2015-08-03 17:37:01 +02:00
parent 90496a0aa0
commit da8449151b

View File

@ -710,114 +710,139 @@ module OpenNebula
# @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)
img_ids = []
new_tid = nil
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
begin
rc = info()
return rc if OpenNebula.is_error?(rc)
raise if OpenNebula.is_error?(rc)
steps = 0
while lcm_state_str() == "HOTPLUG_SAVEAS_POWEROFF"
if steps < 30
sleep 1
else
sleep 15
tid = self['TEMPLATE/TEMPLATE_ID']
if tid.nil? || tid.empty?
rc = Error.new('VM has no template to be saved')
raise
end
if state_str() != "POWEROFF"
rc = Error.new("VM state must be POWEROFF, "<<
"current state is #{state_str()}, #{lcm_state_str()}")
raise
end
# Clone the source template
rc = OpenNebula::Template.new_with_id(tid, @client).clone(name)
raise if OpenNebula.is_error?(rc)
new_tid = rc
# 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()
raise 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()
raise if OpenNebula.is_error?(rc)
steps += 1
end
rc = info()
return rc if OpenNebula.is_error?(rc)
# If the VM is not busy with a previous disk snapshot, we wait
# but this time with a timeout
rc = wait_state("POWEROFF")
raise if OpenNebula.is_error?(rc)
steps += 1
disk_id = disk["DISK_ID"]
if disk_id.nil? || disk_id.empty?
rc = Error.new('The DISK_ID is missing from the VM template')
raise
end
image_id = disk["IMAGE_ID"]
if !image_id.nil? && !image_id.empty?
rc = disk_saveas(disk_id.to_i,"#{name}-disk-#{disk_id}","",-1)
raise if OpenNebula.is_error?(rc)
img_ids << rc.to_i
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
# 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)
self.each('TEMPLATE/NIC') do |nic|
nic_id = nic["NIC_ID"]
if nic_id.nil? || nic_id.empty?
rc = Error.new('The NIC_ID is missing from the VM template')
raise
end
disk_id = disk["DISK_ID"]
if disk_id.nil? || disk_id.empty?
return Error.new('The DISK_ID is missing from the VM template')
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
image_id = disk["IMAGE_ID"]
# Required by the Sunstone Cloud View
replace << "SAVED_TEMPLATE_ID = #{tid}\n"
if !image_id.nil? && !image_id.empty?
rc = disk_saveas(disk_id.to_i,"#{name}-disk-#{disk_id}","",-1)
new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client)
return rc if OpenNebula.is_error?(rc)
rc = new_tmpl.update(replace, true)
raise 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"
return new_tid
rescue
# Rollback. Delete the template and the images created
if !new_tid.nil?
new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client)
new_tmpl.delete()
end
img_ids.each do |id|
img = OpenNebula::Image.new_with_id(id, @client)
img.delete()
end
return rc
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