diff --git a/include/Image.h b/include/Image.h index 08949f6c72..500e26eb76 100644 --- a/include/Image.h +++ b/include/Image.h @@ -348,6 +348,17 @@ public: return it->is_saving(); } + /** + * Check if the image is a hot snapshot + * @return true if image is a hot snapshot + */ + bool isHot() + { + ImageTemplate * it = static_cast(obj_template); + + return it->is_saving_hot(); + } + /** * Set permissions for the Image. Extends the PoolSQLObject method * by checking the persistent state of the image. diff --git a/include/ImageTemplate.h b/include/ImageTemplate.h index 7f7b0b2a43..21f2b5aca1 100644 --- a/include/ImageTemplate.h +++ b/include/ImageTemplate.h @@ -52,6 +52,15 @@ public: return (saving.empty() == false); } + bool is_saving_hot() + { + string save_as_hot; + + get(saving_hot_attribute, save_as_hot); + + return (save_as_hot.empty() == false); + } + void set_saving() { SingleAttribute * attr= new SingleAttribute(saving_attribute, "YES"); @@ -61,6 +70,15 @@ public: set(attr); } + void set_saving_hot() + { + SingleAttribute * attr = new SingleAttribute(saving_hot_attribute,"YES"); + + erase(saving_hot_attribute); + + set(attr); + } + void unset_saving() { erase(saving_attribute); @@ -72,6 +90,7 @@ private: static vector restricted_attributes; static string saving_attribute; + static string saving_hot_attribute; /** * Stores the attributes as restricted, these attributes will be used in @@ -83,7 +102,7 @@ private: Template::set_restricted_attributes(rattrs, restricted_attributes); }; }; - + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/include/LifeCycleManager.h b/include/LifeCycleManager.h index 7c755ef1a4..291605c2ea 100644 --- a/include/LifeCycleManager.h +++ b/include/LifeCycleManager.h @@ -68,6 +68,8 @@ public: DETACH_NIC_FAILURE,/**< Sent by the VMM when a detach nic action fails */ CLEANUP_SUCCESS, /**< Sent by the VMM when a cleanup action succeeds */ CLEANUP_FAILURE, /**< Sent by the VMM when a cleanup action fails */ + SAVEAS_HOT_SUCCESS,/**< Sent by the VMM when hot saveas succeeds */ + SAVEAS_HOT_FAILURE,/**< Sent by the VMM when hot saveas fails */ SNAPSHOT_CREATE_SUCCESS, /**< Sent by the VMM on snap. create success */ SNAPSHOT_CREATE_FAILURE, /**< Sent by the VMM on snap. create failure */ SNAPSHOT_REVERT_SUCCESS, /**< Sent by the VMM on snap. revert success */ @@ -197,6 +199,10 @@ private: void detach_failure_action(int vid); + void saveas_hot_success_action(int vid); + + void saveas_hot_failure_action(int vid); + void attach_nic_success_action(int vid); void attach_nic_failure_action(int vid); diff --git a/include/RequestManagerVirtualMachine.h b/include/RequestManagerVirtualMachine.h index 20fb202dc4..610a3ac334 100644 --- a/include/RequestManagerVirtualMachine.h +++ b/include/RequestManagerVirtualMachine.h @@ -152,7 +152,7 @@ public: VirtualMachineSaveDisk(): RequestManagerVirtualMachine("VirtualMachineSaveDisk", "Saves a disk from virtual machine as a new image", - "A:siiss"){}; + "A:siissb"){}; ~VirtualMachineSaveDisk(){}; diff --git a/include/TransferManager.h b/include/TransferManager.h index a31a8484bb..7ec8945e86 100644 --- a/include/TransferManager.h +++ b/include/TransferManager.h @@ -57,6 +57,7 @@ public: EPILOG_DELETE_BOTH, CHECKPOINT, DRIVER_CANCEL, + SAVEAS_HOT, FINALIZE }; @@ -307,6 +308,11 @@ private: * This function cancels the operation being performed by the driver */ void driver_cancel_action(int vid); + + /** + * This function starts the saveas of the given disk + */ + void saveas_hot_action(int vid); }; #endif /*TRANSFER_MANAGER_H*/ diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index 62ec2b93a4..3f87e05837 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -91,7 +91,10 @@ public: BOOT_STOPPED = 22, CLEANUP_DELETE = 23, HOTPLUG_SNAPSHOT = 24, - HOTPLUG_NIC = 25 + HOTPLUG_NIC = 25, + HOTPLUG_SAVEAS = 26, + HOTPLUG_SAVEAS_POWEROFF = 27, + HOTPLUG_SAVEAS_SUSPENDED = 28 }; // ------------------------------------------------------------------------- @@ -845,20 +848,49 @@ public: */ int generate_context(string &files, int &disk_id); - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- // Datastore related functions - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------- + /** + * Gest the associated image to the given disk_id + * @param disk_id of the VM + * @param hot is this a save_as hot operation + * @param err_str describing the error + * @return -1 if the image cannot saveas + */ + int get_image_from_disk(int disk_id, bool hot, string& err_str); + + /** + * Sets the corresponding SAVE_AS state. + * @return 0 if the VM can be saved as + */ + int set_saveas_state(); + + /** + * Clears the SAVE_AS state, moving the VM to the original state. + * @return 0 if the VM was in a SAVE_AS state + */ + int clear_saveas_state(); + /** * Set the SAVE_AS attribute for the "disk_id"th disk. * @param disk_id Index of the disk to save * @param source to save the disk (SAVE_AS_SOURCE) * @param img_id ID of the image this disk will be saved to (SAVE_AS). - * @return 0 if success */ int save_disk(const string& disk_id, const string& source, int img_id); + /** + * Set the SAVE_AS attribute for the "disk_id"th disk. + * @param disk_id Index of the disk to save + * @param source to save the disk (SAVE_AS_SOURCE) + * @param img_id ID of the image this disk will be saved to (SAVE_AS). + */ + int save_disk_hot(const string& disk_id, + const string& source, + int img_id); /** * Get the original image id of the disk. It also checks that the disk can * be saved_as. @@ -866,7 +898,12 @@ public: * @param error_str describes the error * @return -1 if failure */ - int get_image_from_disk(int disk_id, string& error_str); + int get_saveas_disk_hot(int& disk_id, string& source, int& image_id); + + /** + * Cleans the HOTPLUG_SAVEAS = YES attribute from the disks + */ + void clear_saveas_disk_hot(); // ------------------------------------------------------------------------ // Authorization related functions @@ -882,10 +919,9 @@ public: AuthRequest& ar, VirtualMachineTemplate *tmpl); - // ------------------------------------------------------------------------ - // Disk Hotplug related functions - // ------------------------------------------------------------------------ - + // ------------------------------------------------------------------------- + // Hotplug related functions + // ------------------------------------------------------------------------- /** * Collects information about VM DISKS * @param max_disk_id of the VM @@ -893,6 +929,14 @@ public: */ void get_disk_info(int& max_disk_id, set& used_targets); + /** + * Get the IMAGE_ID of the image that's being saved as hot + * @param disk_id of the DISK + * @param image_id id of the image being saved + * @return IMAGE_ID on success, -1 otherwise + */ + int get_disk_hot_info(int& image_id, int& disk_id, string& source); + /** * Generate a DISK attribute to be attached to the VM. * @param tmpl Template containing a single DISK vector attribute. diff --git a/install.sh b/install.sh index cfb3ab6e81..aa40a89f07 100755 --- a/install.sh +++ b/install.sh @@ -935,7 +935,8 @@ TM_SHARED_FILES="src/tm_mad/shared/clone \ src/tm_mad/shared/context \ src/tm_mad/shared/premigrate \ src/tm_mad/shared/postmigrate \ - src/tm_mad/shared/mvds" + src/tm_mad/shared/mvds \ + src/tm_mad/shared/cpds" TM_QCOW2_FILES="src/tm_mad/qcow2/clone \ src/tm_mad/qcow2/delete \ @@ -946,7 +947,8 @@ TM_QCOW2_FILES="src/tm_mad/qcow2/clone \ src/tm_mad/qcow2/context \ src/tm_mad/qcow2/premigrate \ src/tm_mad/qcow2/postmigrate \ - src/tm_mad/qcow2/mvds" + src/tm_mad/qcow2/mvds \ + src/tm_mad/qcow2/cpds" TM_SSH_FILES="src/tm_mad/ssh/clone \ src/tm_mad/ssh/delete \ @@ -957,7 +959,8 @@ TM_SSH_FILES="src/tm_mad/ssh/clone \ src/tm_mad/ssh/context \ src/tm_mad/ssh/premigrate \ src/tm_mad/ssh/postmigrate \ - src/tm_mad/ssh/mvds" + src/tm_mad/ssh/mvds \ + src/tm_mad/ssh/cpds" TM_DUMMY_FILES="src/tm_mad/dummy/clone \ src/tm_mad/dummy/delete \ @@ -968,7 +971,8 @@ TM_DUMMY_FILES="src/tm_mad/dummy/clone \ src/tm_mad/dummy/context \ src/tm_mad/dummy/premigrate \ src/tm_mad/dummy/postmigrate \ - src/tm_mad/dummy/mvds" + src/tm_mad/dummy/mvds \ + src/tm_mad/dummy/cpds" TM_VMFS_FILES="src/tm_mad/vmfs/clone \ src/tm_mad/vmfs/delete @@ -978,6 +982,7 @@ TM_VMFS_FILES="src/tm_mad/vmfs/clone \ src/tm_mad/vmfs/mv \ src/tm_mad/vmfs/context \ src/tm_mad/vmfs/mvds \ + src/tm_mad/vmfs/cpds \ src/tm_mad/vmfs/postmigrate \ src/tm_mad/vmfs/premigrate" @@ -985,6 +990,7 @@ TM_ISCSI_FILES="src/tm_mad/iscsi/clone \ src/tm_mad/iscsi/ln \ src/tm_mad/iscsi/mv \ src/tm_mad/iscsi/mvds \ + src/tm_mad/iscsi/cpds \ src/tm_mad/iscsi/premigrate \ src/tm_mad/iscsi/postmigrate \ src/tm_mad/iscsi/delete" @@ -993,6 +999,7 @@ TM_LVM_FILES="src/tm_mad/lvm/clone \ src/tm_mad/lvm/ln \ src/tm_mad/lvm/mv \ src/tm_mad/lvm/mvds \ + src/tm_mad/lvm/cpds \ src/tm_mad/lvm/premigrate \ src/tm_mad/lvm/postmigrate \ src/tm_mad/lvm/delete" @@ -1001,6 +1008,7 @@ TM_CEPH_FILES="src/tm_mad/ceph/clone \ src/tm_mad/ceph/ln \ src/tm_mad/ceph/mv \ src/tm_mad/ceph/mvds \ + src/tm_mad/ceph/cpds \ src/tm_mad/ceph/premigrate \ src/tm_mad/ceph/postmigrate \ src/tm_mad/ceph/delete" diff --git a/share/doc/xsd/acct.xsd b/share/doc/xsd/acct.xsd index 7234640862..f7c7d29381 100644 --- a/share/doc/xsd/acct.xsd +++ b/share/doc/xsd/acct.xsd @@ -108,7 +108,10 @@ BOOT_STOPPED = 22, CLEANUP_DELETE = 23, HOTPLUG_SNAPSHOT = 24, - HOTPLUG_NIC = 25 + HOTPLUG_NIC = 25, + HOTPLUG_SAVEAS = 26, + HOTPLUG_SAVEAS_POWEROFF = 27, + HOTPLUG_SAVEAS_SUSPENDED = 28 --> diff --git a/share/doc/xsd/vm.xsd b/share/doc/xsd/vm.xsd index 08a73a8f44..fa5babf322 100644 --- a/share/doc/xsd/vm.xsd +++ b/share/doc/xsd/vm.xsd @@ -70,7 +70,10 @@ BOOT_STOPPED = 22, CLEANUP_DELETE = 23, HOTPLUG_SNAPSHOT = 24, - HOTPLUG_NIC = 25 + HOTPLUG_NIC = 25, + HOTPLUG_SAVEAS = 26, + HOTPLUG_SAVEAS_POWEROFF = 27, + HOTPLUG_SAVEAS_SUSPENDED = 28 --> diff --git a/src/cli/one_helper/onevm_helper.rb b/src/cli/one_helper/onevm_helper.rb index b04380d99e..332ce83a57 100644 --- a/src/cli/one_helper/onevm_helper.rb +++ b/src/cli/one_helper/onevm_helper.rb @@ -82,6 +82,12 @@ class OneVMHelper < OpenNebulaHelper::OneHelper :description => "Show all template data" } + HOT = { + :name =>"hot", + :large => "--hot", + :description => "The Image will be saved immediately" + } + def self.rname "VM" end diff --git a/src/cli/onevm b/src/cli/onevm index e8792192ab..9684bb6619 100755 --- a/src/cli/onevm +++ b/src/cli/onevm @@ -261,12 +261,19 @@ cmd=CommandParser::CmdParser.new(ARGV) do created immediately, but the contents are saved only if the VM is shut down gracefully (i.e., using 'onevm shutdown' and not 'onevm delete') + Sets the specified VM disk to be saved in a new Image. + + The Image is created immediately, but the contents are saved only if the + VM is shut down gracefully (i.e., using 'onevm shutdown' and not 'onevm + delete') + + If '--hot' is specified, the Image will be saved immediately. States: ANY EOT command :saveas, saveas_desc, :vmid, :diskid, :img_name, - :options=>[TYPE] do + :options=>[TYPE, OneVMHelper::HOT] do disk_id = args[1].to_i image_name = args[2] image_type = options[:type] || "" @@ -275,7 +282,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do "the image #{image_name}" helper.perform_action(args[0],options,verbose) do |vm| - res = vm.save_as(disk_id, image_name, image_type) + res = vm.save_as(disk_id, image_name, image_type, options[:hot]) if !OpenNebula.is_error?(res) puts "Image ID: #{res}" diff --git a/src/document/Document.cc b/src/document/Document.cc index b6481e9010..4343d694e5 100644 --- a/src/document/Document.cc +++ b/src/document/Document.cc @@ -79,7 +79,7 @@ int Document::insert(SqlDB *db, string& error_str) // Check default attributes // --------------------------------------------------------------------- - get_template_attribute("NAME", name); + erase_template_attribute("NAME", name); if ( name.empty() == true ) { @@ -199,8 +199,8 @@ string& Document::to_xml(string& xml) const << "" << oid << "" << "" << uid << "" << "" << gid << "" - << "" << uname << "" - << "" << gname << "" + << "" << uname << "" + << "" << gname << "" << "" << name << "" << "" << type << "" << perms_to_xml(perm_str) diff --git a/src/image/ImageManagerDriver.cc b/src/image/ImageManagerDriver.cc index a252e219ae..6e9b38ec0e 100644 --- a/src/image/ImageManagerDriver.cc +++ b/src/image/ImageManagerDriver.cc @@ -26,7 +26,7 @@ /* Driver ASCII Protocol Implementation */ /* ************************************************************************** */ -void ImageManagerDriver::cp(int oid, +void ImageManagerDriver::cp(int oid, const string& drv_msg) const { ostringstream os; @@ -38,7 +38,7 @@ void ImageManagerDriver::cp(int oid, /* -------------------------------------------------------------------------- */ -void ImageManagerDriver::clone(int oid, +void ImageManagerDriver::clone(int oid, const string& drv_msg) const { ostringstream os; @@ -49,7 +49,7 @@ void ImageManagerDriver::clone(int oid, } /* -------------------------------------------------------------------------- */ -void ImageManagerDriver::stat(int oid, +void ImageManagerDriver::stat(int oid, const string& drv_msg) const { ostringstream os; @@ -61,13 +61,13 @@ void ImageManagerDriver::stat(int oid, /* -------------------------------------------------------------------------- */ -void ImageManagerDriver::mkfs(int oid, +void ImageManagerDriver::mkfs(int oid, const string& drv_msg) const { ostringstream os; os << "MKFS " << oid << " " << drv_msg << endl; - + write(os); } @@ -121,9 +121,9 @@ static void stat_action(istringstream& is, int id, const string& result) /* -------------------------------------------------------------------------- */ -static void cp_action(istringstream& is, - ImagePool* ipool, - int id, +static void cp_action(istringstream& is, + ImagePool* ipool, + int id, const string& result) { string source; @@ -143,7 +143,7 @@ static void cp_action(istringstream& is, if ( is.good()) { - is >> source >> ws; + is >> source >> ws; } if (!source.empty()) @@ -151,7 +151,7 @@ static void cp_action(istringstream& is, oss << "CP operation succeeded but image no longer exists." << " Source image: " << source << ", may be left in datastore"; - NebulaLog::log("ImM", Log::ERROR, oss); + NebulaLog::log("ImM", Log::ERROR, oss); } } @@ -160,7 +160,7 @@ static void cp_action(istringstream& is, if ( result == "FAILURE" ) { - goto error; + goto error; } if ( is.good() ) @@ -172,7 +172,7 @@ static void cp_action(istringstream& is, { goto error; } - + image->set_source(source); image->set_state(Image::READY); @@ -194,7 +194,7 @@ error: { oss << ": " << info; } - + NebulaLog::log("ImM", Log::ERROR, oss); image->set_template_error_message(oss.str()); @@ -209,9 +209,9 @@ error: /* -------------------------------------------------------------------------- */ -static void clone_action(istringstream& is, - ImagePool* ipool, - int id, +static void clone_action(istringstream& is, + ImagePool* ipool, + int id, const string& result) { int cloning_id; @@ -235,7 +235,7 @@ static void clone_action(istringstream& is, if ( is.good()) { - is >> source >> ws; + is >> source >> ws; } if (!source.empty()) @@ -243,7 +243,7 @@ static void clone_action(istringstream& is, oss << "CLONE operation succeeded but image no longer exists." << " Source image: " << source << ", may be left in datastore"; - NebulaLog::log("ImM", Log::ERROR, oss); + NebulaLog::log("ImM", Log::ERROR, oss); } } @@ -254,7 +254,7 @@ static void clone_action(istringstream& is, if ( result == "FAILURE" ) { - goto error; + goto error; } if ( is.good() ) @@ -266,7 +266,7 @@ static void clone_action(istringstream& is, { goto error; } - + image->set_source(source); image->set_state(Image::READY); @@ -292,7 +292,7 @@ error: { oss << ": " << info; } - + NebulaLog::log("ImM", Log::ERROR, oss); image->set_template_error_message(oss.str()); @@ -311,25 +311,28 @@ error: /* -------------------------------------------------------------------------- */ -static void mkfs_action(istringstream& is, - ImagePool* ipool, - int id, +static void mkfs_action(istringstream& is, + ImagePool* ipool, + int id, const string& result) { string source; Image * image; - bool is_saving; + bool is_saving = false; + bool is_hot = false; - string disk_id; - string vm_id; string info; int rc; + int vm_id = -1; + string disk_id; + VirtualMachine * vm; ostringstream oss; Nebula& nd = Nebula::instance(); VirtualMachinePool * vmpool = nd.get_vmpool(); + TransferManager * tm = nd.get_tm(); image = ipool->get(id, true); @@ -341,7 +344,7 @@ static void mkfs_action(istringstream& is, if ( is.good()) { - is >> source >> ws; + is >> source >> ws; } if (!source.empty()) @@ -349,55 +352,54 @@ static void mkfs_action(istringstream& is, oss << "MkFS operation succeeded but image no longer exists." << " Source image: " << source << ", may be left in datastore"; - NebulaLog::log("ImM", Log::ERROR, oss); + NebulaLog::log("ImM", Log::ERROR, oss); } } - + return; } + is_saving = image->isSaving(); + is_hot = image->isHot(); + + if ( is_saving ) + { + image->get_template_attribute("SAVED_VM_ID", vm_id); + image->get_template_attribute("SAVED_DISK_ID", disk_id); + } + if ( result == "FAILURE" ) { - goto error_img; + goto error_img; } - if ( is.good() ) + if ( !is.fail() ) { is >> source >> ws; } - - if ( is.fail() ) + else { goto error_img; } - is_saving = image->isSaving(); - image->set_source(source); - if (is_saving) - { - image->get_template_attribute("SAVED_DISK_ID", disk_id); - image->get_template_attribute("SAVED_VM_ID", vm_id); - } - else + if (!is_saving) { image->set_state(Image::READY); - - NebulaLog::log("ImM", Log::INFO, "Image created and ready to use"); } + NebulaLog::log("ImM", Log::INFO, "Image created and ready to use"); + ipool->update(image); image->unlock(); - if ( ! is_saving ) + if ( !is_saving ) { return; } - /* ---------------- Set up information for the Saved Image -------------- */ - vm = vmpool->get(vm_id, true); if ( vm == 0 ) @@ -405,11 +407,27 @@ static void mkfs_action(istringstream& is, goto error_save_get; } - rc = vm->save_disk(disk_id, source, id); - - if ( rc == -1 ) + if ( is_hot ) //Saveas hot, trigger disk copy { - goto error_save_state; + rc = vm->save_disk_hot(disk_id, source, id); + + if ( rc == -1 ) + { + goto error_save_state; + } + + tm->trigger(TransferManager::SAVEAS_HOT, vm_id); + } + else //setup disk information + { + rc = vm->save_disk(disk_id, source, id); + + if ( rc == -1 ) + { + goto error_save_state; + } + + vm->clear_saveas_state(); } vmpool->update(vm); @@ -446,7 +464,7 @@ error: { oss << ": " << info; } - + NebulaLog::log("ImM", Log::ERROR, oss); image->set_template_error_message(oss.str()); @@ -456,14 +474,25 @@ error: image->unlock(); + if (is_saving && vm_id != -1) + { + if ((vm = vmpool->get(vm_id, true)) != 0) + { + vm->clear_saveas_state(); + vmpool->update(vm); + + vm->unlock(); + } + } + return ; } /* -------------------------------------------------------------------------- */ -static void rm_action(istringstream& is, - ImagePool* ipool, - int id, +static void rm_action(istringstream& is, + ImagePool* ipool, + int id, const string& result) { int rc; @@ -489,7 +518,7 @@ static void rm_action(istringstream& is, if ( result == "FAILURE" ) { - goto error; + goto error; } else if ( rc < 0 ) { @@ -501,7 +530,7 @@ static void rm_action(istringstream& is, return; error_drop: - oss << "Error removing image from DB: " << tmp_error + oss << "Error removing image from DB: " << tmp_error << ". Remove image source " << source << " to completely delete image."; NebulaLog::log("ImM", Log::ERROR, oss); @@ -517,7 +546,7 @@ error: { oss << ": " << info; } - + NebulaLog::log("ImM", Log::ERROR, oss); return; @@ -535,9 +564,9 @@ void ImageManagerDriver::protocol( string action; string result; string source; - string info; + string info; int id; - + oss << "Message received: " << message; NebulaLog::log("ImG", Log::DEBUG, oss); @@ -584,7 +613,7 @@ void ImageManagerDriver::protocol( else if ( action == "CLONE" ) { clone_action(is, ipool, id, result); - } + } else if ( action == "MKFS" ) { mkfs_action(is, ipool, id, result); diff --git a/src/image/ImageTemplate.cc b/src/image/ImageTemplate.cc index 802569bfc0..c10713fb02 100644 --- a/src/image/ImageTemplate.cc +++ b/src/image/ImageTemplate.cc @@ -21,7 +21,8 @@ vector ImageTemplate::restricted_attributes; -string ImageTemplate::saving_attribute = "SAVE_AS"; +string ImageTemplate::saving_attribute = "SAVE_AS"; +string ImageTemplate::saving_hot_attribute = "SAVE_AS_HOT"; /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/lcm/LifeCycleActions.cc b/src/lcm/LifeCycleActions.cc index 41d0f69d55..aa81b3d168 100644 --- a/src/lcm/LifeCycleActions.cc +++ b/src/lcm/LifeCycleActions.cc @@ -550,8 +550,6 @@ void LifeCycleManager::restart_action(int vid) vm->log("LCM", Log::INFO, "New VM state is BOOT_POWEROFF"); } - //---------------------------------------------------- - vmm->trigger(VirtualMachineManager::DEPLOY,vid); } else @@ -738,6 +736,9 @@ void LifeCycleManager::clean_up_vm(VirtualMachine * vm, bool dispose) case VirtualMachine::SHUTDOWN_POWEROFF: case VirtualMachine::CANCEL: case VirtualMachine::HOTPLUG: + case VirtualMachine::HOTPLUG_SAVEAS: + case VirtualMachine::HOTPLUG_SAVEAS_POWEROFF: + case VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED: case VirtualMachine::HOTPLUG_SNAPSHOT: case VirtualMachine::HOTPLUG_NIC: vm->set_running_etime(the_time); diff --git a/src/lcm/LifeCycleManager.cc b/src/lcm/LifeCycleManager.cc index dfe3f1e4ed..1e89d9c0fe 100644 --- a/src/lcm/LifeCycleManager.cc +++ b/src/lcm/LifeCycleManager.cc @@ -145,6 +145,14 @@ void LifeCycleManager::trigger(Actions action, int _vid) aname = "DETACH_FAILURE"; break; + case SAVEAS_HOT_SUCCESS: + aname = "SAVEAS_HOT_SUCCESS"; + break; + + case SAVEAS_HOT_FAILURE: + aname = "SAVEAS_HOT_FAILURE"; + break; + case ATTACH_NIC_SUCCESS: aname = "ATTACH_NIC_SUCCESS"; break; @@ -345,6 +353,14 @@ void LifeCycleManager::do_action(const string &action, void * arg) { detach_failure_action(vid); } + else if (action == "SAVEAS_HOT_SUCCESS") + { + saveas_hot_success_action(vid); + } + else if (action == "SAVEAS_HOT_FAILURE") + { + saveas_hot_failure_action(vid); + } else if (action == "ATTACH_NIC_SUCCESS") { attach_nic_success_action(vid); diff --git a/src/lcm/LifeCycleStates.cc b/src/lcm/LifeCycleStates.cc index 41297a7a25..e7d2a126d4 100644 --- a/src/lcm/LifeCycleStates.cc +++ b/src/lcm/LifeCycleStates.cc @@ -1478,3 +1478,117 @@ void LifeCycleManager::detach_nic_failure_action(int vid) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ + +void LifeCycleManager::saveas_hot_success_action(int vid) +{ + Nebula& nd = Nebula::instance(); + ImagePool * ipool = nd.get_ipool(); + + VirtualMachine * vm; + Image * image; + + int image_id; + int disk_id; + string source; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + if (vm->clear_saveas_state() == -1) + { + vm->log("LCM", Log::ERROR, "saveas_hot_success_action, VM in a wrong state"); + vm->unlock(); + + return; + } + + int rc = vm->get_saveas_disk_hot(disk_id, source, image_id); + + vm->clear_saveas_disk_hot(); + + vmpool->update(vm); + + vm->unlock(); + + if ( rc != 0 ) + { + return; + } + + image = ipool->get(image_id, true); + + if ( image == 0 ) + { + return; + } + + image->set_state(Image::READY); + + ipool->update(image); + + image->unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void LifeCycleManager::saveas_hot_failure_action(int vid) +{ + Nebula& nd = Nebula::instance(); + ImagePool * ipool = nd.get_ipool(); + + VirtualMachine * vm; + Image * image; + + int image_id; + int disk_id; + string source; + + vm = vmpool->get(vid,true); + + if ( vm == 0 ) + { + return; + } + + if (vm->clear_saveas_state() == -1) + { + vm->log("LCM", Log::ERROR, "saveas_hot_success_action, VM in a wrong state"); + vm->unlock(); + + return; + } + + int rc = vm->get_saveas_disk_hot(disk_id, source, image_id); + + vm->clear_saveas_disk_hot(); + + vmpool->update(vm); + + vm->unlock(); + + if ( rc != 0 ) + { + return; + } + + image = ipool->get(image_id, true); + + if ( image == 0 ) + { + return; + } + + image->set_state(Image::ERROR); + + ipool->update(image); + + image->unlock(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ diff --git a/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java b/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java index 7a9949e525..c8322af13b 100644 --- a/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java +++ b/src/oca/java/src/org/opennebula/client/vm/VirtualMachine.java @@ -96,7 +96,10 @@ public class VirtualMachine extends PoolElement{ "BOOT_STOPPED", "CLEANUP_DELETE", "HOTPLUG_SNAPSHOT", - "HOTPLUG_NIC" }; + "HOTPLUG_NIC", + "HOTPLUG_SAVEAS", + "HOTPLUG_SAVEAS_POWEROFF", + "HOTPLUG_SAVEAS_SUSPENDED" }; private static final String[] SHORT_LCM_STATES = { @@ -124,7 +127,11 @@ public class VirtualMachine extends PoolElement{ "boot", "boot", "clea", - "snap" }; + "snap", + "hotp", + "hotp", + "hotp", + "hotp" }; /** * Creates a new VM representation. @@ -468,9 +475,9 @@ public class VirtualMachine extends PoolElement{ * @param imageName Name of the new Image that will be created. * @return If an error occurs the error message contains the reason. */ - public OneResponse savedisk(int diskId, String imageName) + public OneResponse savedisk(int diskId, String imageName, boolean hot = false) { - return savedisk(diskId, imageName, ""); + return savedisk(diskId, imageName, "", hot); } /** @@ -481,11 +488,13 @@ public class VirtualMachine extends PoolElement{ * @param imageName Name of the new Image that will be created. * @param imageType Type of the new image. Set to empty string to use * the default type + * @param hot do not defer saveas operation * @return If an error occurs the error message contains the reason. */ - public OneResponse savedisk(int diskId, String imageName, String imageType) + public OneResponse savedisk(int diskId, String imageName, String imageType, + boolean hot) { - return client.call(SAVEDISK, id ,diskId, imageName, imageType); + return client.call(SAVEDISK, id ,diskId, imageName, imageType, hot); } /** diff --git a/src/oca/ruby/opennebula/virtual_machine.rb b/src/oca/ruby/opennebula/virtual_machine.rb index a338e97ecb..2c10a9b0b2 100644 --- a/src/oca/ruby/opennebula/virtual_machine.rb +++ b/src/oca/ruby/opennebula/virtual_machine.rb @@ -52,7 +52,8 @@ module OpenNebula SAVE_MIGRATE PROLOG_MIGRATE PROLOG_RESUME EPILOG_STOP EPILOG SHUTDOWN CANCEL FAILURE CLEANUP_RESUBMIT UNKNOWN HOTPLUG SHUTDOWN_POWEROFF BOOT_UNKNOWN BOOT_POWEROFF BOOT_SUSPENDED BOOT_STOPPED CLEANUP_DELETE - HOTPLUG_SNAPSHOT HOTPLUG_NIC} + HOTPLUG_SNAPSHOT HOTPLUG_NIC HOTPLUG_SAVEAS HOTPLUG_SAVEAS_POWEROFF + HOTPLUG_SAVEAS_SUSPENDED} SHORT_VM_STATES={ "INIT" => "init", @@ -91,7 +92,10 @@ module OpenNebula "BOOT_STOPPED" => "boot", "CLEANUP_DELETE" => "clea", "HOTPLUG_SNAPSHOT" => "snap", - "HOTPLUG_NIC" => "hotp" + "HOTPLUG_NIC" => "hotp", + "HOTPLUG_SAVEAS" => "hotp", + "HOTPLUG_SAVEAS_POWEROFF" => "hotp", + "HOTPLUG_SAVEAS_SUSPENDED" => "hotp" } MIGRATE_REASON=%w{NONE ERROR STOP_RESUME USER CANCEL} @@ -350,14 +354,15 @@ module OpenNebula # # @return [Integer, OpenNebula::Error] the new Image ID in case of # success, error otherwise - def save_as(disk_id, image_name, image_type="") + def save_as(disk_id, image_name, image_type="", hot=false) return Error.new('ID not defined') if !@pe_id rc = @client.call(VM_METHODS[:savedisk], @pe_id, disk_id, image_name, - image_type) + image_type, + hot) return rc end diff --git a/src/ozones/Server/public/js/ozones.js b/src/ozones/Server/public/js/ozones.js index ab47b88fc4..e8661caccd 100644 --- a/src/ozones/Server/public/js/ozones.js +++ b/src/ozones/Server/public/js/ozones.js @@ -104,7 +104,10 @@ var oZones = { "BOOT_STOPPED", "CLEANUP_DELETE", "HOTPLUG_SNAPSHOT", - "HOTPLUG_NIC"][value]); + "HOTPLUG_NIC", + "HOTPLUG_SAVEAS", + "HOTPLUG_SAVEAS_POWEROFF", + "HOTPLUG_SAVEAS_SUSPENDED"][value]); break; case "IMAGE": case "image": diff --git a/src/rm/RequestManagerVirtualMachine.cc b/src/rm/RequestManagerVirtualMachine.cc index 26bc4cc592..20ce4a98ca 100644 --- a/src/rm/RequestManagerVirtualMachine.cc +++ b/src/rm/RequestManagerVirtualMachine.cc @@ -682,47 +682,62 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis { Nebula& nd = Nebula::instance(); - ImagePool * ipool = nd.get_ipool(); - DatastorePool * dspool = nd.get_dspool(); - UserPool * upool = nd.get_upool(); + ImagePool * ipool = nd.get_ipool(); + DatastorePool * dspool = nd.get_dspool(); + UserPool * upool = nd.get_upool(); 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 + if ( paramList.size() > 5 ) + { + is_hot = xmlrpc_c::value_boolean(paramList.getBoolean(5)); + } + + VirtualMachinePool * vmpool = static_cast(pool); VirtualMachine * vm; + Datastore * ds; int iid; - int iid_orig; - Image * img; - Datastore * ds; - User * user; - Image::DiskType ds_disk_type; - - int umask; - int rc; - string error_str; + string error_str; string driver; string target; string dev_prefix; // ------------------------------------------------------------------------- - // Prepare and check the VM/DISK to be saved_as + // Get user's umask // ------------------------------------------------------------------------- + User * user = upool->get(att.uid, true); - if ( (vm = get_vm(id, att)) == 0 ) + if ( user == 0 ) { failure_response(NO_EXISTS, - get_error(object_name(PoolObjectSQL::VM), id), - att); + get_error(object_name(PoolObjectSQL::USER), att.uid), + att); + return; } - iid_orig = vm->get_image_from_disk(disk_id, error_str); + int umask = user->get_umask(); - pool->update(vm); + user->unlock(); + + // ------------------------------------------------------------------------- + // Prepare and check the VM/DISK to be saved_as + // ------------------------------------------------------------------------- + if ((vm = get_vm(id, att)) == 0) + { + return; + } + + int rc = vm->set_saveas_state(); + int iid_orig = vm->get_image_from_disk(disk_id, is_hot, error_str); + + vmpool->update(vm); vm->unlock(); @@ -734,36 +749,32 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis return; } - // ------------------------------------------------------------------------- - // Get user's umask - // ------------------------------------------------------------------------- - - user = upool->get(att.uid, true); - - if ( user == 0 ) + if ( rc != 0 ) { - failure_response(NO_EXISTS, - get_error(object_name(PoolObjectSQL::USER), att.uid), - att); - + failure_response(INTERNAL, + request_error("VM has to be RUNNING, POWEROFF or" + " SUSPENDED to saveas disks.",""), att); return; } - umask = user->get_umask(); - - user->unlock(); - // ------------------------------------------------------------------------- // Get the data of the Image to be saved // ------------------------------------------------------------------------- - - img = ipool->get(iid_orig, true); + Image * img = ipool->get(iid_orig, true); if ( img == 0 ) { failure_response(NO_EXISTS, get_error(object_name(PoolObjectSQL::IMAGE), iid_orig), att); + + if ((vm = vmpool->get(id, true)) != 0) + { + vm->clear_saveas_state(); + vmpool->update(vm); + vm->unlock(); + } + return; } @@ -779,11 +790,22 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis img->unlock(); + // ------------------------------------------------------------------------- + // Get the data of the DataStore for the new image + // ------------------------------------------------------------------------- if ((ds = dspool->get(ds_id, true)) == 0 ) { failure_response(NO_EXISTS, get_error(object_name(PoolObjectSQL::DATASTORE), ds_id), att); + + if ((vm = vmpool->get(id, true)) != 0) + { + vm->clear_saveas_state(); + vmpool->update(vm); + vm->unlock(); + } + return; } @@ -798,21 +820,18 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis case Image::RAMDISK: case Image::CONTEXT: failure_response(INTERNAL, - request_error("Cannot save_as image of type " + Image::type_to_str(type), ""), - att); + request_error("Cannot save_as image of type " + + Image::type_to_str(type), ""), att); return; } - // ------------------------------------------------------------------------- - // Get the data of the DataStore for the new image - // ------------------------------------------------------------------------- string ds_data; PoolObjectAuth ds_perms; ds->get_permissions(ds_perms); ds->to_xml(ds_data); - ds_disk_type = ds->get_disk_type(); + Image::DiskType ds_disk_type = ds->get_disk_type(); ds->unlock(); @@ -829,6 +848,13 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis itemplate->add("SAVED_DISK_ID",disk_id); itemplate->add("SAVED_VM_ID", id); + itemplate->set_saving(); + + if ( is_hot ) + { + itemplate->set_saving_hot(); + } + if ( img_type.empty() ) { itemplate->add("TYPE", Image::type_to_str(type)); @@ -853,31 +879,36 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis itemplate->add("DEV_PREFIX", dev_prefix); } - itemplate->set_saving(); - img_usage.add("SIZE", size); img_usage.add("DATASTORE", ds_id); // ------------------------------------------------------------------------- // Authorize the operation & check quotas // ------------------------------------------------------------------------- + bool rc_auth = vm_authorization(id, itemplate, 0, att, 0,&ds_perms,auth_op); - if ( vm_authorization(id, itemplate, 0, att, 0, &ds_perms, auth_op) == false ) + if ( rc_auth == true ) { - delete itemplate; - return; + rc_auth = quota_authorization(&img_usage, Quotas::DATASTORE, att); } - if ( quota_authorization(&img_usage, Quotas::DATASTORE, att) == false ) + if ( rc_auth == false) { delete itemplate; + + if ((vm = vmpool->get(id, true)) != 0) + { + vm->clear_saveas_state(); + vmpool->update(vm); + vm->unlock(); + } + return; } // ------------------------------------------------------------------------- // Create the image // ------------------------------------------------------------------------- - rc = ipool->allocate(att.uid, att.gid, att.uname, @@ -896,6 +927,13 @@ void VirtualMachineSaveDisk::request_execute(xmlrpc_c::paramList const& paramLis { quota_rollback(&img_usage, Quotas::DATASTORE, att); + if ((vm = vmpool->get(id, true)) != 0) + { + vm->clear_saveas_state(); + vmpool->update(vm); + vm->unlock(); + } + failure_response(INTERNAL, allocate_error(PoolObjectSQL::IMAGE, error_str), att); return; diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 4008cee7a1..a512a2bf32 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -112,7 +112,10 @@ var OpenNebula = { "BOOT_STOPPED", "CLEANUP_DELETE", "HOTPLUG_SNAPSHOT", - "HOTPLUG_NIC"][value]); + "HOTPLUG_NIC", + "HOTPLUG_SAVEAS", + "HOTPLUG_SAVEAS_POWEROFF", + "HOTPLUG_SAVEAS_SUSPENDED"][value]); break; case "IMAGE": case "image": diff --git a/src/sunstone/public/js/plugins/vms-tab.js b/src/sunstone/public/js/plugins/vms-tab.js index a2654070cc..0aa73874ab 100644 --- a/src/sunstone/public/js/plugins/vms-tab.js +++ b/src/sunstone/public/js/plugins/vms-tab.js @@ -49,8 +49,7 @@ var vm_graphs = [ } ]; - -var VNCstates=["RUNNING","SHUTDOWN","SHUTDOWN_POWEROFF","UNKNOWN","HOTPLUG","CANCEL","MIGRATE", "HOTPLUG_SNAPSHOT", "HOTPLUG_NIC"]; +var VNCstates=["RUNNING","SHUTDOWN","SHUTDOWN_POWEROFF","UNKNOWN","HOTPLUG","CANCEL","MIGRATE", "HOTPLUG_SNAPSHOT", "HOTPLUG_NIC", "HOTPLUG_SAVEAS", "HOTPLUG_SAVEAS_POWEROFF", "HOTPLUG_SAVEAS_SUSPENDED"]; //Permanent storage for last value of aggregated network usage //Used to calculate bandwidth diff --git a/src/tm/TransferManager.cc b/src/tm/TransferManager.cc index 73393a8456..2dcff9158d 100644 --- a/src/tm/TransferManager.cc +++ b/src/tm/TransferManager.cc @@ -123,6 +123,10 @@ void TransferManager::trigger(Actions action, int _vid) aname = "CHECKPOINT"; break; + case SAVEAS_HOT: + aname = "SAVEAS_HOT"; + break; + case DRIVER_CANCEL: aname = "DRIVER_CANCEL"; break; @@ -195,6 +199,10 @@ void TransferManager::do_action(const string &action, void * arg) { checkpoint_action(vid); } + else if (action == "SAVEAS_HOT") + { + saveas_hot_action(vid); + } else if (action == "DRIVER_CANCEL") { driver_cancel_action(vid); @@ -1625,6 +1633,145 @@ void TransferManager::checkpoint_action(int vid) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +void TransferManager::saveas_hot_action(int vid) +{ + int disk_id; + int image_id; + string save_source; + + string save; + string tm_mad; + string ds_id; + + int num; + int disk_id_iter; + + ostringstream os; + + ofstream xfr; + string xfr_name; + + string source; + + const VectorAttribute * disk; + vector attrs; + + VirtualMachine * vm; + Nebula& nd = Nebula::instance(); + + const TransferManagerDriver * tm_md; + + // ------------------------------------------------------------------------ + // Setup & Transfer script + // ------------------------------------------------------------------------ + vm = vmpool->get(vid,true); + + if (vm == 0) + { + vm->log("TM", Log::ERROR, "Could not obtain the VM"); + goto error_common; + } + + if (!vm->hasHistory()) + { + vm->log("TM", Log::ERROR, "The VM has no history"); + goto error_common; + } + + if (vm->get_saveas_disk_hot(disk_id, save_source, image_id) == -1) + { + vm->log("TM", Log::ERROR, "Could not get disk information to saveas it"); + goto error_common; + } + + tm_md = get(); + + if (tm_md == 0) + { + goto error_driver; + } + + num = vm->get_template_attribute("DISK",attrs); + + for (int i=0 ; i < num ; i++) + { + disk = dynamic_cast(attrs[i]); + + if ( disk == 0 ) + { + continue; + } + + disk->vector_value("DISK_ID", disk_id_iter); + + if (disk_id == disk_id_iter) + { + tm_mad = disk->vector_value("TM_MAD"); + ds_id = disk->vector_value("DATASTORE_ID"); + + break; + } + } + + if ( ds_id.empty() || tm_mad.empty() ) + { + vm->log("TM", Log::ERROR, "No DS_ID or TM_MAD to save disk image"); + goto error_common; + } + + if (save_source.empty()) + { + vm->log("TM", Log::ERROR, "No SOURCE to save disk image"); + goto error_common; + } + + xfr_name = vm->get_transfer_file() + ".saveas_hot"; + xfr.open(xfr_name.c_str(),ios::out | ios::trunc); + + if (xfr.fail() == true) + { + goto error_file; + } + + //MVDS tm_mad hostname:remote_system_dir/disk.0 vmid dsid + xfr << "CPDS " + << tm_mad << " " + << vm->get_hostname() << ":" + << vm->get_remote_system_dir() << "/disk." << disk_id << " " + << save_source << " " + << vm->get_oid() << " " + << ds_id + << endl; + + xfr.close(); + + tm_md->transfer(vid, xfr_name); + + vm->unlock(); + + return; + +error_driver: + os << "saveas_hot_transfer, error getting TM driver."; + goto error_common; + +error_file: + os << "saveas_hot_transfer, could not open file: " << xfr_name; + os << ". You may need to manually clean hosts (previous & current)"; + goto error_common; + +error_common: + vm->log("TM", Log::ERROR, os); + + (nd.get_lcm())->trigger(LifeCycleManager::SAVEAS_HOT_FAILURE, vid); + + vm->unlock(); + return; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void TransferManager::migrate_transfer_command( VirtualMachine * vm, ostream& xfr) diff --git a/src/tm/TransferManagerDriver.cc b/src/tm/TransferManagerDriver.cc index 7f5e85cc79..44027fb044 100644 --- a/src/tm/TransferManagerDriver.cc +++ b/src/tm/TransferManagerDriver.cc @@ -131,6 +131,12 @@ void TransferManagerDriver::protocol( lcm_action = LifeCycleManager::EPILOG_SUCCESS; break; + case VirtualMachine::HOTPLUG_SAVEAS: + case VirtualMachine::HOTPLUG_SAVEAS_POWEROFF: + case VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED: + lcm_action = LifeCycleManager::SAVEAS_HOT_SUCCESS; + break; + default: goto error_state; } @@ -168,6 +174,12 @@ void TransferManagerDriver::protocol( lcm_action = LifeCycleManager::EPILOG_FAILURE; break; + case VirtualMachine::HOTPLUG_SAVEAS: + case VirtualMachine::HOTPLUG_SAVEAS_POWEROFF: + case VirtualMachine::HOTPLUG_SAVEAS_SUSPENDED: + lcm_action = LifeCycleManager::SAVEAS_HOT_FAILURE; + break; + default: goto error_state; } diff --git a/src/tm_mad/ceph/cpds b/src/tm_mad/ceph/cpds new file mode 100755 index 0000000000..bd729f2e7a --- /dev/null +++ b/src/tm_mad/ceph/cpds @@ -0,0 +1,76 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +# mvds host:remote_system_ds/disk.i fe:SOURCE +# - fe is the front-end hostname +# - SOURCE is the path of the disk image in the form DS_BASE_PATH/disk +# - host is the target host to deploy the VM +# - remote_system_ds is the path for the system datastore in the host + +SRC=$1 +DST=$2 +VM_ID=$3 +DS_ID=$4 + +if [ -z "${ONE_LOCATION}" ]; then + TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh +else + TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh +fi + +DRIVER_PATH=$(dirname $0) + +source $TMCOMMON +source ${DRIVER_PATH}/../../datastore/ceph/ceph.conf + +#------------------------------------------------------------------------------- +# Set dst path and dir +#------------------------------------------------------------------------------- + +SRC_HOST=`arg_host $SRC` +SRC_PATH=`arg_path $SRC` + +#------------------------------------------------------------------------------- +# Get Image information +#------------------------------------------------------------------------------- + +DISK_ID=$(echo "$SRC_PATH" | $AWK -F. '{print $NF}') + +XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin" + +unset i j XPATH_ELEMENTS + +while IFS= read -r -d '' element; do + XPATH_ELEMENTS[i++]="$element" +done < <(onevm show -x $VM_ID| $XPATH \ + /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \ + /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/PERSISTENT) + +RBD_SRC="${XPATH_ELEMENTS[j++]}" +PERSISTENT="${XPATH_ELEMENTS[j++]}" + +if [ "$PERSISTENT" = "YES" ]; then + RBD_DST="${RBD_SRC}" +else + RBD_DST="${RBD_SRC}-${VM_ID}-${DISK_ID}" +fi + +ssh_exec_and_log "$SRC_HOST" "$RBD copy $RBD_DST $DST" \ + "Error cloning $RBD_DST to $DST in $DST_HOST" + +exit 0 diff --git a/src/tm_mad/dummy/cpds b/src/tm_mad/dummy/cpds new file mode 120000 index 0000000000..300563f2ad --- /dev/null +++ b/src/tm_mad/dummy/cpds @@ -0,0 +1 @@ +../common/dummy.sh \ No newline at end of file diff --git a/src/tm_mad/iscsi/cpds b/src/tm_mad/iscsi/cpds new file mode 100755 index 0000000000..15706507a1 --- /dev/null +++ b/src/tm_mad/iscsi/cpds @@ -0,0 +1,92 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +# mvds host:remote_system_ds/disk.i fe:SOURCE +# - fe is the front-end hostname +# - SOURCE is the path of the disk image in the form DS_BASE_PATH/disk +# - host is the target host to deploy the VM +# - remote_system_ds is the path for the system datastore in the host +# - vmid is the id of the VM +# - dsid is the target datastore (0 is the system datastore) + +SRC=$1 +DST=$2 + +VMID=$3 +DSID=$4 + +if [ -z "${ONE_LOCATION}" ]; then + TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh +else + TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh +fi + +DRIVER_PATH=$(dirname $0) + +. $TMCOMMON + +source ${DRIVER_PATH}/../../datastore/iscsi/iscsi.conf + +SRC_HOST=`arg_host $SRC` +NEW_IQN="$DST" + +DISK_ID=$(echo $SRC|awk -F. '{print $NF}') + +#------------------------------------------------------------------------------- +# Get image information +#------------------------------------------------------------------------------- + +XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin" + +unset i XPATH_ELEMENTS + +while IFS= read -r -d '' element; do + XPATH_ELEMENTS[i++]="$element" +done < <(onevm show -x $VMID| $XPATH \ + /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SOURCE \ + /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SAVE_AS \ + /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/PERSISTENT) + +IQN="${XPATH_ELEMENTS[0]}" +SAVE_AS="${XPATH_ELEMENTS[1]}" +PERSISTENT="${XPATH_ELEMENTS[2]}" + +if [ -z "$PERSISTENT" ]; then + IQN=$IQN-$VMID +fi + +#------------------------------------------------------------------------------- +# IQN and TARGETs +#------------------------------------------------------------------------------- + +LV_NAME=`echo $IQN|$AWK -F. '{print $(NF)}'` +VG_NAME=`echo $IQN|$AWK -F. '{print $(NF-1)}'` +SOURCE_DEV="/dev/$VG_NAME/$LV_NAME" + +TARGET=`arg_path $IQN` +TARGET_LV_NAME=`echo $NEW_IQN|$AWK -F. '{print $(NF)}'` +TARGET_DEV="/dev/$VG_NAME/$TARGET_LV_NAME" +TARGET_HOST="${TARGET%.$VG_NAME.$LV_NAME}" + +CLONE_CMD=$(cat <vector_value("PERSISTENT")).empty())) + if(!(disk->vector_value("PERSISTENT").empty()) && !hot) { goto error_persistent; } @@ -2511,7 +2511,7 @@ error_not_found: oss << "The DISK " << disk_id << " does not exist for VM " << oid << "."; error_common: - error_str = oss.str(); + err_str = oss.str(); return -1; } @@ -2519,6 +2519,65 @@ error_common: /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int VirtualMachine::set_saveas_state() +{ + switch (state) + { + case ACTIVE: + switch (lcm_state) + { + case RUNNING: + lcm_state = HOTPLUG_SAVEAS; + return 0; + default: + return -1; + } + break; + + case POWEROFF: + state = ACTIVE; + lcm_state = HOTPLUG_SAVEAS_POWEROFF; + return 0; + + case SUSPENDED: + state = ACTIVE; + lcm_state = HOTPLUG_SAVEAS_SUSPENDED; + return 0; + + default: + return -1; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::clear_saveas_state() + { + switch (lcm_state) + { + case HOTPLUG_SAVEAS: + lcm_state = RUNNING; + return 0; + + case HOTPLUG_SAVEAS_POWEROFF: + state = POWEROFF; + lcm_state = LCM_INIT; + return 0; + + case HOTPLUG_SAVEAS_SUSPENDED: + state = SUSPENDED; + lcm_state = LCM_INIT; + return 0; + + default: + return -1; + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + int VirtualMachine::save_disk(const string& disk_id, const string& source, int img_id) @@ -2526,18 +2585,16 @@ int VirtualMachine::save_disk(const string& disk_id, vector disks; VectorAttribute * disk; - int num_disks; string tdisk_id; - ostringstream oss; + int num_disks = obj_template->get("DISK",disks); - if ( state == DONE || state == FAILED ) + if (lcm_state != HOTPLUG_SAVEAS && lcm_state != HOTPLUG_SAVEAS_SUSPENDED + && lcm_state != HOTPLUG_SAVEAS_POWEROFF ) { return -1; } - num_disks = obj_template->get("DISK",disks); - for(int i=0; i(disks[i]); @@ -2553,8 +2610,7 @@ int VirtualMachine::save_disk(const string& disk_id, { disk->replace("SAVE_AS_SOURCE", source); - oss << (img_id); - disk->replace("SAVE_AS", oss.str()); + disk->replace("SAVE_AS", img_id); disk->replace("SAVE", "YES"); @@ -2568,6 +2624,123 @@ int VirtualMachine::save_disk(const string& disk_id, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +int VirtualMachine::save_disk_hot(const string& disk_id, + const string& source, + int img_id) +{ + int num_disks; + string tdisk_id; + + vector disks; + VectorAttribute * disk; + + num_disks = obj_template->get("DISK", disks); + + if (lcm_state != HOTPLUG_SAVEAS && lcm_state != HOTPLUG_SAVEAS_SUSPENDED + && lcm_state != HOTPLUG_SAVEAS_POWEROFF ) + { + return -1; + } + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + tdisk_id = disk->vector_value("DISK_ID"); + + if ( tdisk_id == disk_id ) + { + disk->replace("HOTPLUG_SAVEAS", "YES"); + disk->replace("HOTPLUG_SAVEAS_IMAGE_ID", img_id); + disk->replace("HOTPLUG_SAVEAS_SOURCE", source); + break; + } + } + + return 0; +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::get_saveas_disk_hot(int& disk_id, string& source, int& image_id) +{ + vector disks; + VectorAttribute * disk; + + int rc; + int num_disks; + + num_disks = obj_template->get("DISK", disks); + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + if ( disk->vector_value("HOTPLUG_SAVEAS") == "YES" ) + { + source = disk->vector_value("HOTPLUG_SAVEAS_SOURCE"); + + rc = disk->vector_value("HOTPLUG_SAVEAS_IMAGE_ID", image_id); + rc += disk->vector_value("DISK_ID", disk_id); + + if ( rc != 0 || source.empty() ) + { + return -1; + } + + return 0; + } + } + + return -1; +} + + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +void VirtualMachine::clear_saveas_disk_hot() +{ + int num_disks; + vector disks; + VectorAttribute * disk; + + num_disks = obj_template->get("DISK", disks); + + for(int i=0; i(disks[i]); + + if ( disk == 0 ) + { + continue; + } + + if ( disk->vector_value("HOTPLUG_SAVEAS") == "YES" ) + { + disk->remove("HOTPLUG_SAVEAS"); + disk->remove("HOTPLUG_SAVEAS_IMAGE_ID"); + disk->remove("HOTPLUG_SAVEAS_SOURCE"); + return; + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + void VirtualMachine::set_auth_request(int uid, AuthRequest& ar, VirtualMachineTemplate *tmpl) diff --git a/src/vm_template/VMTemplate.cc b/src/vm_template/VMTemplate.cc index af638437fd..822885c0ad 100644 --- a/src/vm_template/VMTemplate.cc +++ b/src/vm_template/VMTemplate.cc @@ -81,7 +81,7 @@ int VMTemplate::insert(SqlDB *db, string& error_str) // Check default attributes // --------------------------------------------------------------------- - get_template_attribute("NAME", name); + erase_template_attribute("NAME", name); if ( name.empty() == true ) {