diff --git a/include/ImageManager.h b/include/ImageManager.h index cde114b587..57d4c27060 100644 --- a/include/ImageManager.h +++ b/include/ImageManager.h @@ -163,12 +163,13 @@ public: string& error); /** - * Deletes an image from the repository and the DB + * Deletes an image from the repository and the DB. The Datastore image list + * is also updated * @param iid id of image * @param error_str Error reason, if any * @return 0 on success */ - int delete_image(int iid, const string& ds_data, string& error_str); + int delete_image(int iid, string& error_str); /** * Gets the size of an image by calling the STAT action of the associated diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 0010fbeb7c..941a297106 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -187,6 +187,23 @@ public: /* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ +class VirtualMachineSaveDiskCancel : public RequestManagerVirtualMachine +{ +public: + VirtualMachineSaveDiskCancel(): + RequestManagerVirtualMachine("VirtualMachineSaveDiskCancel", + "Cancels a disk snapshot set by VirtualMachineSaveDisk", + "A:sii"){}; + + ~VirtualMachineSaveDiskCancel(){}; + + void request_execute(xmlrpc_c::paramList const& _paramList, + RequestAttributes& att); +}; + +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + class VirtualMachineMonitoring : public RequestManagerVirtualMachine { public: diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 09f4297a90..80cd212f9b 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -1218,6 +1218,20 @@ public: const string& source, int img_id); + /** + * Clears the SAVE_AS attribute for the "disk_id"th disk. + * @param disk_id Index of the disk to save + * @return 0 on success, -1 if the disk does not exist + */ + int clear_save_disk(int disk_id); + + /** + * Returns the image ID to be saved-as. + * @param disk_id Index of the disk to save + * @return The image ID, or -1 if the disk is not going to be saved-as + */ + int get_save_disk_image(int disk_id); + /** * Set the SAVE_AS attribute for the "disk_id"th disk. * @param disk_id Index of the disk to save diff --git a/share/doc/states/states-complete.dot b/share/doc/states/states-complete.dot index 3f1f6ac561..bfc136861d 100644 --- a/share/doc/states/states-complete.dot +++ b/share/doc/states/states-complete.dot @@ -177,6 +177,11 @@ digraph OpenNebula { suspended -> hotplug_saveas_suspended [label="disk-snapshot"] hotplug_saveas_suspended -> suspended [style="dashed", color="blue"]; +# disk-snapshot-cancel + running -> running [label="disk-snapshot-cancel"] + poweroff -> poweroff [label="disk-snapshot-cancel"] + suspended -> suspended [label="disk-snapshot-cancel"] + # failures and misc. epilog_stop -> epilog_stop_failure [label=" ", style="dotted", color="red"]; epilog_stop_failure -> epilog_stop [label="recover"]; diff --git a/src/cli/onevm b/src/cli/onevm index 62ddd14010..eb51b7c37a 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -330,6 +330,19 @@ cmd=CommandParser::CmdParser.new(ARGV) do end end + disk_snapshot_cancel_desc = <<-EOT.unindent + Cancels a deferred disk snapshot that has been set by disk-snapshot. + The target image is also deleted. + + States: ANY + EOT + + command :"disk-snapshot-cancel", disk_snapshot_cancel_desc, :vmid, :diskid do + helper.perform_action(args[0],options,"disk snapshot canceled") do |vm| + vm.disk_snapshot_cancel(args[1].to_i) + end + end + shutdown_desc = <<-EOT.unindent Shuts down the given VM. The VM life cycle will end. diff --git a/src/image/ImageManagerActions.cc b/src/image/ImageManagerActions.cc index c020ffb372..a4651ab0af 100644 --- a/src/image/ImageManagerActions.cc +++ b/src/image/ImageManagerActions.cc @@ -390,13 +390,15 @@ int ImageManager::enable_image(int iid, bool to_enable, string& error_str) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -int ImageManager::delete_image(int iid, const string& ds_data, string& error_str) +int ImageManager::delete_image(int iid, string& error_str) { - Image * img; + Image * img; + Datastore * ds; string source; string img_tmpl; string * drv_msg; + string ds_data; long long size; int ds_id; @@ -411,6 +413,31 @@ int ImageManager::delete_image(int iid, const string& ds_data, string& error_str if ( img == 0 ) { + error_str = "Image does not exist"; + return -1; + } + + ds_id = img->get_ds_id(); + + img->unlock(); + + ds = dspool->get(ds_id, true); + + if ( ds == 0 ) + { + error_str = "Datastore no longer exists cannot remove image"; + return -1; + } + + ds->to_xml(ds_data); + + ds->unlock(); + + img = ipool->get(iid,true); + + if ( img == 0 ) + { + error_str = "Image does not exist"; return -1; } @@ -518,6 +545,16 @@ int ImageManager::delete_image(int iid, const string& ds_data, string& error_str release_cloning_image(cloning_id, iid); } + ds = dspool->get(ds_id, true); + + if ( ds != 0 ) + { + ds->del_image(iid); + dspool->update(ds); + + ds->unlock(); + } + return 0; } diff --git a/src/oca/ruby/opennebula/virtual_machine.rb b/src/oca/ruby/opennebula/virtual_machine.rb index 1c513b41dd..890c580335 100644 --- a/src/oca/ruby/opennebula/virtual_machine.rb +++ b/src/oca/ruby/opennebula/virtual_machine.rb @@ -24,26 +24,27 @@ module OpenNebula ####################################################################### VM_METHODS = { - :info => "vm.info", - :allocate => "vm.allocate", - :action => "vm.action", - :migrate => "vm.migrate", - :deploy => "vm.deploy", - :savedisk => "vm.savedisk", - :chown => "vm.chown", - :chmod => "vm.chmod", - :monitoring => "vm.monitoring", - :attach => "vm.attach", - :detach => "vm.detach", - :rename => "vm.rename", - :update => "vm.update", - :resize => "vm.resize", + :info => "vm.info", + :allocate => "vm.allocate", + :action => "vm.action", + :migrate => "vm.migrate", + :deploy => "vm.deploy", + :savedisk => "vm.savedisk", + :savediskcancel => "vm.savediskcancel", + :chown => "vm.chown", + :chmod => "vm.chmod", + :monitoring => "vm.monitoring", + :attach => "vm.attach", + :detach => "vm.detach", + :rename => "vm.rename", + :update => "vm.update", + :resize => "vm.resize", :snapshotcreate => "vm.snapshotcreate", :snapshotrevert => "vm.snapshotrevert", :snapshotdelete => "vm.snapshotdelete", - :attachnic => "vm.attachnic", - :detachnic => "vm.detachnic", - :recover => "vm.recover" + :attachnic => "vm.attachnic", + :detachnic => "vm.detachnic", + :recover => "vm.recover" } VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED @@ -481,6 +482,14 @@ module OpenNebula return disk_snapshot(disk_id, image_name, image_type, hot) end + # Cancels a deferred snapshot that has been set by disk_snapshot. + # The target image is also deleted. + def disk_snapshot_cancel(disk_id) + return call(VM_METHODS[:savediskcancel], + @pe_id, + disk_id) + end + # Resize the VM # # @param capacity_template [String] Template containing the new capacity diff --git a/src/rm/RequestManager.cc b/src/rm/RequestManager.cc index 826543d4da..1addfe9842 100644 --- a/src/rm/RequestManager.cc +++ b/src/rm/RequestManager.cc @@ -293,6 +293,7 @@ void RequestManager::register_xml_methods() xmlrpc_c::methodPtr vm_migrate(new VirtualMachineMigrate()); xmlrpc_c::methodPtr vm_action(new VirtualMachineAction()); xmlrpc_c::methodPtr vm_savedisk(new VirtualMachineSaveDisk()); + xmlrpc_c::methodPtr vm_savedisk_cancel(new VirtualMachineSaveDiskCancel()); xmlrpc_c::methodPtr vm_monitoring(new VirtualMachineMonitoring()); xmlrpc_c::methodPtr vm_attach(new VirtualMachineAttach()); xmlrpc_c::methodPtr vm_detach(new VirtualMachineDetach()); @@ -438,6 +439,7 @@ void RequestManager::register_xml_methods() RequestManagerRegistry.addMethod("one.vm.action", vm_action); RequestManagerRegistry.addMethod("one.vm.migrate", vm_migrate); RequestManagerRegistry.addMethod("one.vm.savedisk", vm_savedisk); + RequestManagerRegistry.addMethod("one.vm.savediskcancel", vm_savedisk_cancel); RequestManagerRegistry.addMethod("one.vm.allocate", vm_allocate); RequestManagerRegistry.addMethod("one.vm.info", vm_info); RequestManagerRegistry.addMethod("one.vm.chown", vm_chown); diff --git a/src/rm/RequestManagerDelete.cc b/src/rm/RequestManagerDelete.cc index e717bb8708..a485ac0b70 100644 --- a/src/rm/RequestManagerDelete.cc +++ b/src/rm/RequestManagerDelete.cc @@ -180,49 +180,11 @@ int HostDelete::drop(int oid, PoolObjectSQL * object, string& error_msg) int ImageDelete::drop(int oid, PoolObjectSQL * object, string& error_msg) { Nebula& nd = Nebula::instance(); - ImageManager * imagem = nd.get_imagem(); - DatastorePool * dspool = nd.get_dspool(); - Datastore * ds; - Image * img; + object->unlock(); - int ds_id, rc; - string ds_data; - - img = static_cast(object); - ds_id = img->get_ds_id(); - - img->unlock(); - - ds = dspool->get(ds_id, true); - - if ( ds == 0 ) - { - error_msg = "Datastore no longer exists cannot remove image"; - return -1; - } - - ds->to_xml(ds_data); - - ds->unlock(); - - rc = imagem->delete_image(oid, ds_data, error_msg); - - if ( rc == 0 ) - { - ds = dspool->get(ds_id, true); - - if ( ds != 0 ) - { - ds->del_image(oid); - dspool->update(ds); - - ds->unlock(); - } - } - - return rc; + return imagem->delete_image(oid, error_msg); } /* ------------------------------------------------------------------------- */ diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 0efa1d4def..dee5806e0b 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -1494,6 +1494,157 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void VirtualMachineSaveDiskCancel::request_execute( + xmlrpc_c::paramList const& paramList, + RequestAttributes& att) +{ + Nebula& nd = Nebula::instance(); + + AclManager * aclm = nd.get_aclm(); + ImageManager * imagem = nd.get_imagem(); + ImagePool * ipool = nd.get_ipool(); + Image * img; + int img_id; + + VirtualMachinePool * vmpool = static_cast(pool); + VirtualMachine * vm; + + string error_str; + + int id = xmlrpc_c::value_int(paramList.getInt(1)); + int disk_id = xmlrpc_c::value_int(paramList.getInt(2)); + + // ------------------------------------------------------------------------- + // Authorize the VM operation + // ------------------------------------------------------------------------- + + if (att.uid != UserPool::ONEADMIN_ID) + { + PoolObjectAuth vm_perms; + PoolObjectAuth img_perms; + + if ((vm = get_vm(id, att)) == 0) + { + return; + } + + vm->get_permissions(vm_perms); + + img_id = vm->get_save_disk_image(disk_id); + + vm->unlock(); + + AuthRequest ar(att.uid, att.group_ids); + + ar.add_auth(auth_op, vm_perms); // MANAGE VM + + img = ipool->get(img_id, true); + + if ( img != 0 ) + { + img->get_permissions(img_perms); + + img->unlock(); + + ar.add_auth(AuthRequest::MANAGE, img_perms); // MANAGE IMAGE + } + + if (UserPool::authorize(ar) == -1) + { + failure_response(AUTHORIZATION, + authorization_error(ar.message, att), + att); + + return; + } + } + + // ------------------------------------------------------------------------- + // Check the VM state + // ------------------------------------------------------------------------- + + if ((vm = get_vm(id, att)) == 0) + { + return; + } + + if ((vm->get_state() != VirtualMachine::ACTIVE || + (vm->get_lcm_state() != VirtualMachine::RUNNING && + vm->get_lcm_state() != VirtualMachine::UNKNOWN) ) && + vm->get_state() != VirtualMachine::POWEROFF && + vm->get_state() != VirtualMachine::SUSPENDED) + { + failure_response(ACTION, + request_error("Wrong state to perform action",""), + att); + + vm->unlock(); + return; + } + + // ------------------------------------------------------------------------- + // Cancel the disk snapshot + // ------------------------------------------------------------------------- + + img_id = vm->get_save_disk_image(disk_id); + + if ( img_id == -1 ) + { + ostringstream oss; + oss << "Disk with ID [" << disk_id << "] is not going to be saved"; + + failure_response(ACTION, + request_error(oss.str(), ""), + att); + + vm->unlock(); + + return; + } + + vm->clear_save_disk(disk_id); + + vmpool->update(vm); + + vm->unlock(); + + // ------------------------------------------------------------------------- + // Delete the target Image + // ------------------------------------------------------------------------- + + img = ipool->get(img_id, true); + + if ( img != 0 ) + { + img->unlock(); + + int rc = imagem->delete_image(img_id, error_str); + + if (rc != 0) + { + ostringstream oss; + oss << "The snapshot was canceled, but " + << object_name(PoolObjectSQL::IMAGE) << " [" << img_id + << "] could not be deleted: " << error_str; + + failure_response(INTERNAL, + request_error(oss.str(), ""), + att); + + return; + } + + aclm->del_resource_rules(img_id, PoolObjectSQL::IMAGE); + } + + // TODO: Delete the cloned template + + success_response(id, att); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void VirtualMachineMonitoring::request_execute( xmlrpc_c::paramList const& paramList, RequestAttributes& att) diff --git a/src/sunstone/etc/sunstone-views/admin.yaml b/src/sunstone/etc/sunstone-views/admin.yaml index 5f23bab64c..77d95c9a1b 100644 --- a/src/sunstone/etc/sunstone-views/admin.yaml +++ b/src/sunstone/etc/sunstone-views/admin.yaml @@ -192,6 +192,7 @@ tabs: VM.attachdisk: true VM.detachdisk: true VM.saveas: true + VM.disk_snapshot_cancel: true VM.attachnic: true VM.detachnic: true VM.snapshot_create: true diff --git a/src/sunstone/etc/sunstone-views/admin_vcenter.yaml b/src/sunstone/etc/sunstone-views/admin_vcenter.yaml index e952afae73..b714287f8e 100644 --- a/src/sunstone/etc/sunstone-views/admin_vcenter.yaml +++ b/src/sunstone/etc/sunstone-views/admin_vcenter.yaml @@ -192,6 +192,7 @@ tabs: VM.attachdisk: true VM.detachdisk: true VM.saveas: false + VM.disk_snapshot_cancel: false VM.attachnic: true VM.detachnic: true VM.snapshot_create: true diff --git a/src/sunstone/etc/sunstone-views/user.yaml b/src/sunstone/etc/sunstone-views/user.yaml index 696af99a2b..d094c2cd65 100644 --- a/src/sunstone/etc/sunstone-views/user.yaml +++ b/src/sunstone/etc/sunstone-views/user.yaml @@ -193,6 +193,7 @@ tabs: VM.attachdisk: true VM.detachdisk: true VM.saveas: true + VM.disk_snapshot_cancel: true VM.attachnic: true VM.detachnic: true VM.snapshot_create: true diff --git a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb index 873333255a..9dc865b307 100644 --- a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb @@ -56,6 +56,7 @@ module OpenNebulaJSON when "restart" then self.restart when "reset" then self.reset when "saveas" then self.save_as(action_hash['params']) + when "disk_snapshot_cancel" then self.disk_snapshot_cancel(action_hash['params']) when "snapshot_create" then self.snapshot_create(action_hash['params']) when "snapshot_revert" then self.snapshot_revert(action_hash['params']) when "snapshot_delete" then self.snapshot_delete(action_hash['params']) @@ -110,6 +111,10 @@ module OpenNebulaJSON disk_snapshot(params['disk_id'].to_i, params['image_name'], params['type'], params['hot'], clone) end + def disk_snapshot_cancel(params=Hash.new) + super(params['disk_id'].to_i) + end + def snapshot_create(params=Hash.new) super(params['snapshot_name']) end diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 27ad91ffd0..785f5d0634 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -1067,6 +1067,11 @@ var OpenNebula = { OpenNebula.Action.simple_action(params,OpenNebula.VM.resource, "saveas",action_obj); }, + "disk_snapshot_cancel": function(params){ + var action_obj = {"disk_id": params.data.extra_param}; + OpenNebula.Action.simple_action(params,OpenNebula.VM.resource, + "disk_snapshot_cancel",action_obj); + }, "snapshot_create": function(params){ var action_obj = params.data.extra_param; OpenNebula.Action.simple_action(params,OpenNebula.VM.resource, diff --git a/src/sunstone/public/js/plugins/vms-tab.js b/src/sunstone/public/js/plugins/vms-tab.js index fc624f29d6..a69671278b 100644 --- a/src/sunstone/public/js/plugins/vms-tab.js +++ b/src/sunstone/public/js/plugins/vms-tab.js @@ -63,7 +63,7 @@ var state_actions = { ["VM.delete", "VM.delete_recreate", "VM.resume", "VM.deploy"], 5: //OpenNebula.VM.state.SUSPENDED: - ["VM.delete", "VM.resume"], + ["VM.delete", "VM.resume", "VM.saveas", "VM.disk_snapshot_cancel"], 6: //OpenNebula.VM.state.DONE: [], @@ -72,7 +72,7 @@ var state_actions = { ["VM.delete", "VM.delete_recreate", "VM.resize"], 8: //OpenNebula.VM.state.POWEROFF: - ["VM.delete", "VM.resume", "VM.resize", "VM.attachdisk", "VM.detachdisk", "VM.attachnic", "VM.detachnic", "VM.migrate"], + ["VM.delete", "VM.resume", "VM.resize", "VM.attachdisk", "VM.detachdisk", "VM.attachnic", "VM.detachnic", "VM.saveas", "VM.disk_snapshot_cancel", "VM.migrate"], 9: //OpenNebula.VM.state.UNDEPLOYED: ["VM.delete", "VM.delete_recreate", "VM.resume", "VM.resize", "VM.deploy"], @@ -86,7 +86,7 @@ var lcm_state_actions = { 2: //OpenNebula.VM.lcm_state.BOOT: ["VM.boot"], 3: //OpenNebula.VM.lcm_state.RUNNING: - ["VM.shutdown", "VM.shutdown_hard", "VM.stop", "VM.suspend", "VM.reboot", "VM.reboot_hard", "VM.resched", "VM.unresched", "VM.poweroff", "VM.poweroff_hard", "VM.undeploy", "VM.undeploy_hard", "VM.migrate", "VM.migrate_live", "VM.attachdisk", "VM.detachdisk", "VM.attachnic", "VM.detachnic"], + ["VM.shutdown", "VM.shutdown_hard", "VM.stop", "VM.suspend", "VM.reboot", "VM.reboot_hard", "VM.resched", "VM.unresched", "VM.poweroff", "VM.poweroff_hard", "VM.undeploy", "VM.undeploy_hard", "VM.migrate", "VM.migrate_live", "VM.attachdisk", "VM.detachdisk", "VM.attachnic", "VM.detachnic", "VM.saveas", "VM.disk_snapshot_cancel"], 4: //OpenNebula.VM.lcm_state.MIGRATE: [], 5: //OpenNebula.VM.lcm_state.SAVE_STOP: @@ -112,7 +112,7 @@ var lcm_state_actions = { 15: //OpenNebula.VM.lcm_state.CLEANUP_RESUBMIT: [], 16: //OpenNebula.VM.lcm_state.UNKNOWN: - ["VM.shutdown", "VM.shutdown_hard", "VM.boot", "VM.resched", "VM.unresched", "VM.poweroff", "VM.poweroff_hard", "VM.undeploy", "VM.undeploy_hard", "VM.migrate", "VM.migrate_live"], + ["VM.shutdown", "VM.shutdown_hard", "VM.boot", "VM.resched", "VM.unresched", "VM.poweroff", "VM.poweroff_hard", "VM.undeploy", "VM.undeploy_hard", "VM.migrate", "VM.migrate_live", "VM.disk_snapshot_cancel"], 17: //OpenNebula.VM.lcm_state.HOTPLUG: [], 18: //OpenNebula.VM.lcm_state.SHUTDOWN_POWEROFF: @@ -555,6 +555,17 @@ var vm_actions = { notify: true }, + "VM.disk_snapshot_cancel" : { + type: "single", + call: OpenNebula.VM.disk_snapshot_cancel, + callback: function(request) { + Sunstone.runAction("VM.show", request.request.data[0]); + OpenNebula.Helper.clear_cache("IMAGE"); + }, + error:onError, + notify: true + }, + "VM.snapshot_create" : { type: "single", call: OpenNebula.VM.snapshot_create, @@ -2030,10 +2041,18 @@ function printDisks(vm_info){ actions = ''; - if (Config.isTabActionEnabled("vms-tab", "VM.saveas")) { - // Check if its volatie - if (disk.IMAGE_ID) { - if ((vm_info.STATE == "3" && vm_info.LCM_STATE == "3") || vm_info.STATE == "5" || vm_info.STATE == "8") { + if (disk.SAVE == "YES") { + if (Config.isTabActionEnabled("vms-tab", "VM.disk_snapshot_cancel")) { + if ( enabledStateAction("VM.disk_snapshot_cancel", vm_info.STATE, vm_info.LCM_STATE)) { + actions += '\ + '+tr("Cancel Snapshot")+'  ' + } + } + } else { + if (Config.isTabActionEnabled("vms-tab", "VM.saveas")) { + // Check if it's volatile + if ( disk.IMAGE_ID && + enabledStateAction("VM.saveas", vm_info.STATE, vm_info.LCM_STATE)) { actions += ''+tr("Snapshot")+'  ' } } @@ -2225,6 +2244,18 @@ function hotpluggingOps(){ }); } + if (Config.isTabActionEnabled("vms-tab", "VM.disk_snapshot_cancel")) { + $('a.disk_snapshot_cancel').live('click', function(){ + var b = $(this); + var vm_id = b.parents('form').attr('vmid'); + var disk_id = b.parents('tr').attr('disk_id'); + + Sunstone.runAction('VM.disk_snapshot_cancel', vm_id, disk_id); + + return false; + }); + } + if (Config.isTabActionEnabled("vms-tab", "VM.attachdisk")) { setupAttachDiskDialog(); diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index a3da1187f2..533f89c1d6 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -3353,6 +3353,51 @@ int VirtualMachine::save_disk(int disk_id, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int VirtualMachine::clear_save_disk(int disk_id) +{ + VectorAttribute * disk; + + disk = get_disk(disk_id); + + if ( disk != 0 ) + { + disk->remove("SAVE_AS_SOURCE"); + disk->remove("SAVE_AS"); + disk->replace("SAVE", "NO"); + + return 0; + } + + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::get_save_disk_image(int disk_id) +{ + VectorAttribute * disk; + bool save; + int img_id = -1; + + disk = get_disk(disk_id); + + if ( disk != 0 ) + { + disk->vector_value("SAVE", save); + + if (save) + { + disk->vector_value("SAVE_AS", img_id); + } + } + + return img_id; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int VirtualMachine::save_disk_hot(int disk_id, const string& source, int img_id)