diff --git a/include/VirtualMachine.h b/include/VirtualMachine.h index c48b58cc4f..cbf11b3c01 100644 --- a/include/VirtualMachine.h +++ b/include/VirtualMachine.h @@ -982,28 +982,6 @@ public: // ------------------------------------------------------------------------ // Template // ------------------------------------------------------------------------ - /** - * Parse a string and substitute variables (e.g. $NAME) using the VM - * template values: - * @param attribute, the string to be parsed - * @param parsed, the resulting parsed string - * @param error description in case of failure - * @return 0 on success. - */ - int parse_template_attribute(const string& attribute, - string& parsed, - string& error); - /** - * Parse a file string variable (i.e. $FILE) using the FILE_DS datastores. - * It should be used for OS/DS_KERNEL, OS/DS_INITRD, CONTEXT/DS_FILES. - * @param attribute the string to be parsed - * @param img_ids ids of the FILE images in the attribute - * @param error description in case of failure - * @return 0 on success. - */ - int parse_file_attribute(string attribute, - vector& img_ids, - string& error); /** * Updates the configuration attributes based on a template, the state of * the virtual machine is checked to assure operation consistency @@ -1378,7 +1356,11 @@ public: * @param err_str describing the error if any * @return -1 if the image cannot saveas, 0 on success */ - int set_saveas_disk(int disk_id, int snap_id, int &img_id, long long &size, string& err_str); + int set_saveas_disk(int disk_id, int snap_id, int &img_id, long long &size, + string& err_str) + { + return disks.set_saveas(disk_id, snap_id, img_id, size, err_str); + } /** * Set save attributes for the disk @@ -1386,7 +1368,16 @@ public: * @param source to save the disk * @param img_id ID of the image this disk will be saved to */ - int set_saveas_disk(int disk_id, const string& source, int img_id); + int set_saveas_disk(int disk_id, const string& source, int img_id) + { + if (lcm_state != HOTPLUG_SAVEAS && lcm_state != HOTPLUG_SAVEAS_SUSPENDED + && lcm_state != HOTPLUG_SAVEAS_POWEROFF ) + { + return -1; + } + + return disks.set_saveas(disk_id, source, img_id); + } /** * Sets the corresponding state to save the disk. @@ -1405,7 +1396,10 @@ public: * @return the ID of the image this disk will be saved to or -1 if it * is not found. */ - int clear_saveas_disk(); + int clear_saveas_disk() + { + return disks.clear_saveas(); + } /** * Get the original image id of the disk. It also checks that the disk can @@ -1418,7 +1412,11 @@ public: * @return -1 if failure */ int get_saveas_disk(int& disk_id, string& source, int& image_id, - string& snap_id, string& tm_mad, string& ds_id); + string& snap_id, string& tm_mad, string& ds_id) + { + return disks.get_saveas_info(disk_id, source, image_id, snap_id, + tm_mad, ds_id); + } // ------------------------------------------------------------------------ // Authorization related functions @@ -1473,7 +1471,10 @@ public: */ VirtualMachineDisk * delete_attach_disk() { - return disks.delete_attach(); + VirtualMachineDisk * disk = disks.delete_attach(); + obj_template->remove(disk->vector_attribute()); + + return disk; } /** @@ -1514,6 +1515,19 @@ public: */ int set_resize_disk(int disk_id); + /** + * Prepares a disk to be resized. + * @param disk_id of disk + * @param size new size for the disk (needs to be greater than current) + * @param error + * + * @return 0 on success + */ + int set_up_resize_disk(int disk_id, long size, string& error) + { + return disks.set_up_resize(disk_id, size, error); + } + // ------------------------------------------------------------------------ // NIC Hotplug related functions // ------------------------------------------------------------------------ @@ -1860,11 +1874,9 @@ private: */ Log * _log; - // ************************************************************************* // DataBase implementation (Private) // ************************************************************************* - /** * Bootstraps the database table(s) associated to the VirtualMachine * @return 0 on success @@ -1943,15 +1955,64 @@ private: */ int update_monitoring(SqlDB * db); + /** + * Function that renders the VM in XML format optinally including + * extended information (all history records) + * @param xml the resulting XML string + * @param n_history Number of history records to include: + * 0: none + * 1: the last one + * 2: all + * @return a reference to the generated string + */ + string& to_xml_extended(string& xml, int n_history) const; + // ------------------------------------------------------------------------- // Attribute Parser // ------------------------------------------------------------------------- - /** * Mutex to perform just one attribute parse at a time */ static pthread_mutex_t lex_mutex; + /** + * Attributes not allowed in NIC_DEFAULT to avoid authorization bypass and + * inconsistencies for NIC_DEFAULTS + */ + static const char* NO_NIC_DEFAULTS[]; + static const int NUM_NO_NIC_DEFAULTS; + + /** + * Known Virtual Router attributes, to be moved from the user template + * to the template + */ + static const char* VROUTER_ATTRIBUTES[]; + static const int NUM_VROUTER_ATTRIBUTES; + + /** + * Parse a string and substitute variables (e.g. $NAME) using the VM + * template values: + * @param attribute, the string to be parsed + * @param parsed, the resulting parsed string + * @param error description in case of failure + * @return 0 on success. + */ + int parse_template_attribute(const string& attribute, + string& parsed, + string& error); + + /** + * Parse a file string variable (i.e. $FILE) using the FILE_DS datastores. + * It should be used for OS/DS_KERNEL, OS/DS_INITRD, CONTEXT/DS_FILES. + * @param attribute the string to be parsed + * @param img_ids ids of the FILE images in the attribute + * @param error description in case of failure + * @return 0 on success. + */ + int parse_file_attribute(string attribute, + vector& img_ids, + string& error); + /** * Generates image attributes (DS_ID, TM_MAD, SOURCE...) for KERNEL and * INITRD files. @@ -1959,12 +2020,11 @@ private: * @param base_name of the attribute "KERNEL", or "INITRD" * @param base_type of the image attribute KERNEL, RAMDISK * @param error_str Returns the error reason, if any - * @return 0 on success + * @return 0 on succes */ - int set_os_file(VectorAttribute * os, - const string& base_name, - Image::ImageType base_type, - string& error_str); + int set_os_file(VectorAttribute* os, const string& base_name, + Image::ImageType base_type, string& error_str); + /** * Parse the "OS" attribute of the template by substituting * $FILE variables @@ -1973,49 +2033,6 @@ private: */ int parse_os(string& error_str); - /** - * Attributes not allowed in NIC_DEFAULT to avoid authorization bypass and - * inconsistencies for NIC_DEFAULTS - */ - static const char * NO_NIC_DEFAULTS[]; - - static const int NUM_NO_NIC_DEFAULTS; - - /** - * Parse and generate the ETH_ network attributed of a NIC - * @param context attribute - * @param nic attribute - * - * @return 0 on success - */ - void parse_nic_context(VectorAttribute * context, VectorAttribute * nic); - - /** - * Generate the NETWORK related CONTEXT setions, i.e. ETH_*. This function - * is invoked when ever the context is prepared for the VM to capture - * netowrking updates. - * @param context attribute of the VM - * @param error string if any - * @return 0 on success - */ - int generate_network_context(VectorAttribute * context, string& error); - - /** - * Generate the PCI related CONTEXT setions, i.e. PCI_*. This function - * is also adds basic network attributes for pass-through NICs - * @param context attribute of the VM - * @return true if the net context was generated. - */ - bool generate_pci_context(VectorAttribute * context); - - /** - * Generate the ONE_GATE token & url - * @param context attribute of the VM - * @param error_str describing the error - * @return 0 if success - */ - int generate_token_context(VectorAttribute * context, string& error_str); - /** * Parse the "NIC_DEFAULT" attribute * @param error_str Returns the error reason, if any @@ -2031,12 +2048,35 @@ private: int parse_vrouter(string& error_str); /** - * Known Virtual Router attributes, to be moved from the user template - * to the template + * Parse the "PCI" attribute of the template and checks mandatory attributes + * @param error_str Returns the error reason, if any + * @return 0 on success */ - static const char* VROUTER_ATTRIBUTES[]; - static const int NUM_VROUTER_ATTRIBUTES; + int parse_pci(string& error_str); + /** + * Parse the "SCHED_REQUIREMENTS" attribute of the template by substituting + * $VARIABLE, $VARIABLE[ATTR] and $VARIABLE[ATTR, ATTR = VALUE] + * @param error_str Returns the error reason, if any + * @return 0 on success + */ + int parse_requirements(string& error_str); + + /** + * Parse the "GRAPHICS" attribute and generates a default PORT if not + * defined + */ + int parse_graphics(string& error_str); + + /** + * Searches the meaningful attributes and moves them from the user template + * to the internal template + */ + void parse_well_known_attributes(); + + // ------------------------------------------------------------------------- + // Context related functions + // ------------------------------------------------------------------------- /** * Known attributes for network contextualization rendered as: * ETH__ = $NETWORK[context[1], vnet_name] @@ -2066,11 +2106,30 @@ private: static const int NUM_NETWORK6_CONTEXT; /** - * Parse the "PCI" attribute of the template and checks mandatory attributes - * @param error_str Returns the error reason, if any + * Generate the NETWORK related CONTEXT setions, i.e. ETH_*. This function + * is invoked when ever the context is prepared for the VM to capture + * netowrking updates. + * @param context attribute of the VM + * @param error string if any * @return 0 on success */ - int parse_pci(string& error_str); + int generate_network_context(VectorAttribute * context, string& error); + + /** + * Generate the PCI related CONTEXT setions, i.e. PCI_*. This function + * is also adds basic network attributes for pass-through NICs + * @param context attribute of the VM + * @return true if the net context was generated. + */ + bool generate_pci_context(VectorAttribute * context); + + /** + * Generate the ONE_GATE token & url + * @param context attribute of the VM + * @param error_str describing the error + * @return 0 if success + */ + int generate_token_context(VectorAttribute * context, string& error_str); /** * Parse the "CONTEXT" attribute of the template by substituting @@ -2091,44 +2150,6 @@ private: */ int parse_context_variables(VectorAttribute ** context, string& error_str); - /** - * Parse the "SCHED_REQUIREMENTS" attribute of the template by substituting - * $VARIABLE, $VARIABLE[ATTR] and $VARIABLE[ATTR, ATTR = VALUE] - * @param error_str Returns the error reason, if any - * @return 0 on success - */ - int parse_requirements(string& error_str); - - /** - * Parse the "GRAPHICS" attribute and generates a default PORT if not - * defined - */ - int parse_graphics(string& error_str); - - /** - * Searches the meaningful attributes and moves them from the user template - * to the internal template - */ - void parse_well_known_attributes(); - - /** - * Function that renders the VM in XML format optinally including - * extended information (all history records) - * @param xml the resulting XML string - * @param n_history Number of history records to include: - * 0: none - * 1: the last one - * 2: all - * @return a reference to the generated string - */ - string& to_xml_extended(string& xml, int n_history) const; - - /** - * Merges NIC_DEFAULT with the given NIC - * @param nic NIC to process - */ - void merge_nic_defaults(VectorAttribute* nic); - // ------------------------------------------------------------------------- // NIC & DISK Management Helpers // ------------------------------------------------------------------------- @@ -2155,6 +2176,12 @@ private: */ VectorAttribute* get_attach_nic(); + /** + * Merges NIC_DEFAULT with the given NIC + * @param nic NIC to process + */ + void merge_nic_defaults(VectorAttribute* nic); + // ------------------------------------------------------------------------ // Public cloud templates related functions // ------------------------------------------------------------------------ diff --git a/include/VirtualMachineAttribute.h b/include/VirtualMachineAttribute.h index 66c71e7fa5..05b160ecf4 100644 --- a/include/VirtualMachineAttribute.h +++ b/include/VirtualMachineAttribute.h @@ -169,17 +169,9 @@ class VirtualMachineAttributeSet { protected: /** - * Creates the VirtualMachineAttribute set from the Template or vector with - * all the attributes. - * @param a_name the attribute name (e.g. "DISK") - * @param id_name to search for attributes (e.g. "DISK_ID") + * Creates the VirtualMachineAttribute set + * @param dispose elements upon set destruction */ - VirtualMachineAttributeSet(const std::string a_name, - const std::string& id_name, Template * tmpl); - - VirtualMachineAttributeSet(const std::string& id_name, - std::vector& vas, bool dispose); - VirtualMachineAttributeSet(bool _dispose):dispose(_dispose){}; virtual ~VirtualMachineAttributeSet(); diff --git a/include/VirtualMachineDisk.h b/include/VirtualMachineDisk.h index e42de3e092..b16c252138 100644 --- a/include/VirtualMachineDisk.h +++ b/include/VirtualMachineDisk.h @@ -84,7 +84,7 @@ public: void clear_active_snapshot() { - set_flag("DISK_SNAPSHOT_ACTIVE"); + clear_flag("DISK_SNAPSHOT_ACTIVE"); }; bool is_active_snapshot() @@ -92,6 +92,16 @@ public: return is_flag("DISK_SNAPSHOT_ACTIVE"); } + void set_saveas() + { + set_flag("HOTPLUG_SAVE_AS_ACTIVE"); + }; + + void clear_saveas() + { + clear_flag("HOTPLUG_SAVE_AS_ACTIVE"); + }; + /* ---------------------------------------------------------------------- */ /* Disk attributes, not accesible through vector_value */ /* ---------------------------------------------------------------------- */ @@ -243,7 +253,14 @@ public: * @param tmpl template with DISK */ VirtualMachineDisks(Template * tmpl, bool has_id): - VirtualMachineAttributeSet(DISK_NAME, id_name(has_id), tmpl){}; + VirtualMachineAttributeSet(false) + { + std::vector vas; + + tmpl->get(DISK_NAME, vas); + + init(vas, has_id); + }; /** * Creates the VirtualMachineDisk set from a vector of DISK VectorAttribute @@ -251,7 +268,10 @@ public: * @param va vector of DISK Vector Attributes */ VirtualMachineDisks(vector& va, bool has_id, bool dispose): - VirtualMachineAttributeSet(id_name(has_id), va, dispose){}; + VirtualMachineAttributeSet(dispose) + { + init(va, has_id); + }; /** * Creates an empty disk set @@ -401,6 +421,22 @@ public: /* ---------------------------------------------------------------------- */ /* Attach disk Interface */ /* ---------------------------------------------------------------------- */ + /** + * Clear attach status from the attach disk (ATTACH=YES) + */ + VirtualMachineDisk * delete_attach() + { + return static_cast(remove_attribute("ATTACH")); + } + + /** + * Get the attach disk (ATTACH=YES) + */ + VirtualMachineDisk * get_attach() + { + return static_cast(get_attribute("ATTACH")); + } + /** * Sets the attach attribute to the given disk * @param disk_id of the DISK @@ -416,16 +452,6 @@ public: clear_flag("ATTACH"); } - VirtualMachineDisk * delete_attach() - { - return static_cast(remove_attribute("ATTACH")); - } - - VirtualMachineDisk * get_attach() - { - return static_cast(get_attribute("ATTACH")); - } - /** * Prepares a disk to be attached to the virtual machine and adds it to the * disk set. It checks target assigment and cluster compatibility. @@ -441,6 +467,59 @@ public: VirtualMachineDisk * set_up_attach(int vmid, int uid, int cluster_id, VectorAttribute * vdisk, VectorAttribute * vcontext, string& error); + /* ---------------------------------------------------------------------- */ + /* Save as Interface */ + /* ---------------------------------------------------------------------- */ + /** + * Get the saveas disk (HOTPLUG_SAVE_AS_ACTIVE = YES) + */ + VirtualMachineDisk * get_saveas() + { + return static_cast( + get_attribute("HOTPLUG_SAVE_AS_ACTIVE")); + } + + /** + * Mark the disk that is going to be "save as" + * @param disk_id of the VM + * @param snap_id of the disk to save, -1 to select the active snapshot + * @param iid The image id used by the disk + * @param size The disk size. This may be different to the original + * image size + * @param err_str describing the error if any + * @return -1 if the image cannot saveas, 0 on success + */ + int set_saveas(int disk_id, int snap_id, int &iid, long long &size, + string& err_str); + + /** + * Set save attributes for the disk + * @param disk_id Index of the disk to save + * @param source to save the disk + * @param img_id ID of the image this disk will be saved to + */ + int set_saveas(int disk_id, const string& source, int iid); + + /** + * Clears the SAVE_AS_* attributes of the disk being saved as + * @return the ID of the image this disk will be saved to or -1 if it + * is not found. + */ + int clear_saveas(); + + /** + * Get the original image id of the disk. It also checks that the disk can + * be saved_as. + * @param disk_id Index of the disk to save + * @param source of the image to save the disk to + * @param image_id of the image to save the disk to + * @param tm_mad in use by the disk + * @param ds_id of the datastore in use by the disk + * @return -1 if failure + */ + int get_saveas_info(int& disk_id, string& source, int& image_id, + string& snap_id, string& tm_mad, string& ds_id); + /* ---------------------------------------------------------------------- */ /* Resize disk Interface */ /* ---------------------------------------------------------------------- */ @@ -467,9 +546,25 @@ public: return static_cast(get_attribute("RESIZE")); } + /** + * Prepares a disk to be resized. + * @param disk_id of disk + * @param size new size for the disk (needs to be greater than current) + * @param error + * + * @return 0 on success + */ + int set_up_resize(int disk_id, long size, string& error); + /* ---------------------------------------------------------------------- */ /* SNAPSHOT interface */ /* ---------------------------------------------------------------------- */ + VirtualMachineDisk * get_active_snapshot() + { + return static_cast( + get_attribute("DISK_SNAPSHOT_ACTIVE")); + } + /** * Set the snapshots for a disk * @param id of disk @@ -562,18 +657,6 @@ private: void assign_disk_targets( std::queue >& dqueue, std::set& used_targets); - - static const char * id_name(bool has_id) - { - if (has_id) - { - return DISK_ID_NAME; - } - else - { - return ""; - } - } }; #endif /*VIRTUAL_MACHINE_DISK_H_*/ diff --git a/share/etc/oned.conf b/share/etc/oned.conf index 2f3a43d25f..9f65a101ae 100644 --- a/share/etc/oned.conf +++ b/share/etc/oned.conf @@ -815,6 +815,7 @@ VM_RESTRICTED_ATTR = "DISK/READ_IOPS_SEC" VM_RESTRICTED_ATTR = "DISK/WRITE_IOPS_SEC" #VM_RESTRICTED_ATTR = "DISK/SIZE" VM_RESTRICTED_ATTR = "DISK/ORIGINAL_SIZE" +VM_RESTRICTED_ATTR = "DISK/SIZE_PREV" VM_RESTRICTED_ATTR = "CPU_COST" VM_RESTRICTED_ATTR = "MEMORY_COST" VM_RESTRICTED_ATTR = "DISK_COST" diff --git a/src/onedb/fsck.rb b/src/onedb/fsck.rb index 9f52c559ab..606ad430bf 100644 --- a/src/onedb/fsck.rb +++ b/src/onedb/fsck.rb @@ -31,7 +31,7 @@ require 'nokogiri' module OneDBFsck VERSION = "5.2.0" - LOCAL_VERSION = "4.90.0" + LOCAL_VERSION = "5.2.0" def check_db_version() db_version = read_db_version() diff --git a/src/vm/SConstruct b/src/vm/SConstruct index 7e57bc934c..5bf35aa029 100644 --- a/src/vm/SConstruct +++ b/src/vm/SConstruct @@ -51,6 +51,8 @@ source_files=[ 'VirtualMachineAttribute.cc', 'VirtualMachineDisk.cc', 'VirtualMachineSystemSnapshot.cc', + 'VirtualMachineParser.cc', + 'VirtualMachineContext.cc', 'Snapshots.cc' ] diff --git a/src/vm/VirtualMachine.cc b/src/vm/VirtualMachine.cc index ac28d0284c..0b1043346c 100644 --- a/src/vm/VirtualMachine.cc +++ b/src/vm/VirtualMachine.cc @@ -126,40 +126,6 @@ const char * VirtualMachine::showback_db_bootstrap = "(vmid INTEGER, year INTEGER, month INTEGER, body MEDIUMTEXT, " "PRIMARY KEY(vmid, year, month))"; -const char * VirtualMachine::NO_NIC_DEFAULTS[] = {"NETWORK_ID", "NETWORK", - "NETWORK_UID", "NETWORK_UNAME"}; - -const int VirtualMachine::NUM_NO_NIC_DEFAULTS = 4; - -const char * VirtualMachine::NETWORK_CONTEXT[][2] = { - {"IP", "IP"}, - {"MAC", "MAC"}, - {"MASK", "NETWORK_MASK"}, - {"NETWORK", "NETWORK_ADDRESS"}, - {"GATEWAY", "GATEWAY"}, - {"DNS", "DNS"}, - {"SEARCH_DOMAIN", "SEARCH_DOMAIN"}, - {"MTU", "GUEST_MTU"}, - {"VLAN_ID", "VLAN_ID"}, - {"VROUTER_IP", "VROUTER_IP"}, - {"VROUTER_MANAGEMENT", "VROUTER_MANAGEMENT"}}; -const int VirtualMachine::NUM_NETWORK_CONTEXT = 11; - -const char* VirtualMachine::NETWORK6_CONTEXT[][2] = { - {"IP6", "IP6_GLOBAL"}, - {"IP6_ULA", "IP6_ULA"}, - {"GATEWAY6", "GATEWAY6"}, - {"CONTEXT_FORCE_IPV4", "CONTEXT_FORCE_IPV4"}, - {"VROUTER_IP6", "VROUTER_IP6_GLOBAL"}}; - -const int VirtualMachine::NUM_NETWORK6_CONTEXT = 5; - -const char* VirtualMachine::VROUTER_ATTRIBUTES[] = { - "VROUTER_ID", - "VROUTER_KEEPALIVED_ID", - "VROUTER_KEEPALIVED_PASSWORD"}; -const int VirtualMachine::NUM_VROUTER_ATTRIBUTES = 3; - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -774,665 +740,6 @@ error_common: return -1; } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::set_os_file(VectorAttribute * os, - const string& base_name, - Image::ImageType base_type, - string& error_str) -{ - vector img_ids; - Nebula& nd = Nebula::instance(); - - ImagePool * ipool = nd.get_ipool(); - Image * img = 0; - - int img_id; - - Image::ImageType type; - Image::ImageState state; - - DatastorePool * ds_pool = nd.get_dspool(); - Datastore * ds; - int ds_id; - - string attr; - string base_name_ds = base_name + "_DS"; - string base_name_id = base_name + "_DS_ID"; - string base_name_source = base_name + "_DS_SOURCE"; - string base_name_ds_id = base_name + "_DS_DSID"; - string base_name_tm = base_name + "_DS_TM"; - string base_name_cluster= base_name + "_DS_CLUSTER_ID"; - - string type_str; - - attr = os->vector_value(base_name_ds.c_str()); - - if ( attr.empty() ) - { - return 0; - } - - if ( parse_file_attribute(attr, img_ids, error_str) != 0 ) - { - return -1; - } - - if ( img_ids.size() != 1 ) - { - error_str = "Only one FILE variable can be used in: " + attr; - return -1; - } - - img_id = img_ids.back(); - - img = ipool->get(img_id, true); - - if ( img == 0 ) - { - error_str = "Image no longer exists in attribute: " + attr; - return -1; - } - - state = img->get_state(); - - ds_id = img->get_ds_id(); - type = img->get_type(); - - os->remove(base_name); - - os->replace(base_name_id, img->get_oid()); - os->replace(base_name_source, img->get_source()); - os->replace(base_name_ds_id, img->get_ds_id()); - - img->unlock(); - - type_str = Image::type_to_str(type); - - if ( type != base_type ) - { - ostringstream oss; - - oss << base_name << " needs an image of type " - << Image::type_to_str(base_type) << " and not " - << type_str; - - error_str = oss.str(); - return -1; - } - - if ( state != Image::READY ) - { - ostringstream oss; - - oss << type_str << " Image '" << img_id << " 'not in READY state."; - - error_str = oss.str(); - return -1; - } - - ds = ds_pool->get(ds_id, true); - - if ( ds == 0 ) - { - error_str = "Associated datastore for image does not exist"; - return -1; - } - - os->replace(base_name_tm, ds->get_tm_mad()); - - set cluster_ids = ds->get_cluster_ids(); - - if (!cluster_ids.empty()) - { - os->replace(base_name_cluster, one_util::join(cluster_ids, ',')); - } - - ds->unlock(); - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_os(string& error_str) -{ - int num; - int rc; - - vector os_attr; - VectorAttribute * os; - - vector::iterator it; - - num = user_obj_template->remove("OS", os_attr); - - for (it=os_attr.begin(); it != os_attr.end(); it++) - { - obj_template->set(*it); - } - - if ( num == 0 ) - { - return 0; - } - else if ( num > 1 ) - { - error_str = "Only one OS attribute can be defined."; - return -1; - } - - os = dynamic_cast(os_attr[0]); - - if ( os == 0 ) - { - error_str = "Internal error parsing OS attribute."; - return -1; - } - - rc = set_os_file(os, "KERNEL", Image::KERNEL, error_str); - - if ( rc != 0 ) - { - return -1; - } - - rc = set_os_file(os, "INITRD", Image::RAMDISK, error_str); - - if ( rc != 0 ) - { - return -1; - } - - return 0; -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_defaults(string& error_str) -{ - int num; - - vector attr; - VectorAttribute* vatt = 0; - - num = user_obj_template->remove("NIC_DEFAULT", attr); - - if ( num == 0 ) - { - return 0; - } - - if ( num > 1 ) - { - error_str = "Only one NIC_DEFAULT attribute can be defined."; - goto error_cleanup; - } - - vatt = dynamic_cast(attr[0]); - - if ( vatt == 0 ) - { - error_str = "Wrong format for NIC_DEFAULT attribute."; - goto error_cleanup; - } - - for (int i=0; i < NUM_NO_NIC_DEFAULTS; i++) - { - if(vatt->vector_value(NO_NIC_DEFAULTS[i]) != "") - { - ostringstream oss; - oss << "Attribute " << NO_NIC_DEFAULTS[i] - << " is not allowed inside NIC_DEFAULT."; - - error_str = oss.str(); - - return -1; - } - } - - obj_template->set(vatt); - - return 0; - -error_cleanup: - - for (int i = 0; i < num ; i++) - { - delete attr[i]; - } - - return -1; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_vrouter(string& error_str) -{ - string st; - - for (int i = 0; i < NUM_VROUTER_ATTRIBUTES; i++) - { - user_obj_template->get(VROUTER_ATTRIBUTES[i], st); - - if (!st.empty()) - { - obj_template->replace(VROUTER_ATTRIBUTES[i], st); - } - - user_obj_template->erase(VROUTER_ATTRIBUTES[i]); - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -static void clear_context_network(const char* vars[][2], int num_vars, - VectorAttribute * context, int nic_id) -{ - ostringstream att_name; - - for (int i=0; i < num_vars; i++) - { - att_name.str(""); - - att_name << "ETH" << nic_id << "_" << vars[i][0]; - - context->remove(att_name.str()); - } -} - - -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_context(string& error_str) -{ - VectorAttribute * context = obj_template->get("CONTEXT"); - - if ( context == 0 ) - { - return 0; - } - - string files_ds = context->vector_value("FILES_DS"); - - context->remove("FILES_DS"); - - // ------------------------------------------------------------------------- - // Add network context and parse variables - // ------------------------------------------------------------------------- - if (parse_context_variables(&context, error_str) == -1 || - generate_network_context(context, error_str) == -1 ) - { - return -1; - } - - generate_pci_context(context); - - // ------------------------------------------------------------------------- - // Parse FILE_DS variables - // ------------------------------------------------------------------------- - if (!files_ds.empty()) - { - string files_ds_parsed; - string st; - - ostringstream oss_parsed; - - vector img_ids; - - if ( parse_file_attribute(files_ds, img_ids, error_str) != 0 ) - { - return -1; - } - - if ( img_ids.size() > 0 ) - { - vector::iterator it; - - Nebula& nd = Nebula::instance(); - - ImagePool * ipool = nd.get_ipool(); - Image * img = 0; - - Image::ImageType type; - Image::ImageState state; - - for ( it=img_ids.begin() ; it < img_ids.end(); it++ ) - { - img = ipool->get(*it, true); - - if ( img != 0 ) - { - oss_parsed << img->get_source() << ":'" - << img->get_name() << "' "; - - type = img->get_type(); - state = img->get_state(); - - img->unlock(); - - if (type != Image::CONTEXT) - { - error_str = "Only images of type CONTEXT can be used in" - " FILE_DS attribute."; - return -1; - } - - if ( state != Image::READY ) - { - ostringstream oss; - - oss << Image::type_to_str(type) - << " Image '" << *it << "' not in READY state."; - - error_str = oss.str(); - - return -1; - } - - } - } - } - - files_ds_parsed = oss_parsed.str(); - - if ( !files_ds_parsed.empty() ) - { - context->replace("FILES_DS", files_ds_parsed); - } - } - - // ------------------------------------------------------------------------- - // OneGate URL - // ------------------------------------------------------------------------- - if ( generate_token_context(context, error_str) != 0 ) - { - return -1; - } - - // ------------------------------------------------------------------------- - // Virtual Router attributes - // ------------------------------------------------------------------------- - string st; - - for (int i = 0; i < NUM_VROUTER_ATTRIBUTES; i++) - { - obj_template->get(VROUTER_ATTRIBUTES[i], st); - - if (!st.empty()) - { - context->replace(VROUTER_ATTRIBUTES[i], st); - } - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_context_variables(VectorAttribute ** context, - string& error_str) -{ - int rc; - - string parsed; - string * str = (*context)->marshall(); - - if (str == 0) - { - return -1; - } - - rc = parse_template_attribute(*str, parsed, error_str); - - delete str; - - if (rc != 0) - { - return -1; - } - - *context = new VectorAttribute("CONTEXT"); - (*context)->unmarshall(parsed); - - obj_template->erase("CONTEXT"); - obj_template->set(*context); - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -static int check_pci_attributes(VectorAttribute * pci, const string& default_bus, - string& error_str) -{ - static string attrs[] = {"VENDOR", "DEVICE", "CLASS"}; - static int num_attrs = 3; - - string bus; - bool found = false; - - for (int i = 0; i < num_attrs; i++) - { - unsigned int val; - int rc = HostSharePCI::get_pci_value(attrs[i].c_str(), pci, val); - - if (rc == -1) - { - error_str = "Wrong Hex value for PCI attribute " + attrs[i]; - return -1; - } - else if ( rc != 0 ) - { - found = true; - } - } - - if (!found) - { - error_str = "DEVICE, VENDOR or CLASS must be defined for PCI."; - return -1; - } - - if ( HostSharePCI::set_pci_address(pci, default_bus) != 0 ) - { - error_str = "Wrong BUS in PCI attribute"; - return -1; - } - - return 0; -} - -int VirtualMachine::parse_pci(string& error_str) -{ - vector array_pci; - vector::iterator it; - - int pci_id = 0; - - user_obj_template->remove("PCI", array_pci); - - for (it = array_pci.begin(); it !=array_pci.end(); ++it, ++pci_id) - { - (*it)->replace("PCI_ID", pci_id); - - obj_template->set(*it); - } - - Nebula& nd = Nebula::instance(); - string default_bus; - - nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus); - - for (it = array_pci.begin(); it !=array_pci.end(); ++it) - { - if ( check_pci_attributes(*it, default_bus, error_str) != 0 ) - { - return -1; - } - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_graphics(string& error_str) -{ - VectorAttribute * user_graphics = user_obj_template->get("GRAPHICS"); - - if ( user_graphics == 0 ) - { - return 0; - } - - VectorAttribute * graphics = new VectorAttribute(user_graphics); - - user_obj_template->erase("GRAPHICS"); - - obj_template->set(graphics); - - if ( !graphics->vector_value("PORT").empty() ) - { - unsigned int port; - - int rc = graphics->vector_value("PORT", port); - - if (rc == -1 || port > 65535 ) - { - error_str = "Wrong PORT number in GRAPHICS attribute"; - return -1; - } - } - - string random_passwd = graphics->vector_value("RANDOM_PASSWD"); - - if ( !random_passwd.empty() ) - { - graphics->replace("PASSWD", one_util::random_password()); - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_requirements(string& error_str) -{ - int rc, num; - - vector array_reqs; - SingleAttribute * reqs; - - string parsed; - - num = user_obj_template->remove("SCHED_REQUIREMENTS", array_reqs); - - if ( num == 0 ) // Compatibility with old REQUIREMENTS attribute - { - num = user_obj_template->remove("REQUIREMENTS", array_reqs); - } - else - { - user_obj_template->erase("REQUIREMENTS"); - } - - if ( num == 0 ) - { - return 0; - } - else if ( num > 1 ) - { - error_str = "Only one SCHED_REQUIREMENTS attribute can be defined."; - goto error_cleanup; - } - - reqs = dynamic_cast(array_reqs[0]); - - if ( reqs == 0 ) - { - error_str = "Wrong format for SCHED_REQUIREMENTS attribute."; - goto error_cleanup; - } - - rc = parse_template_attribute(reqs->value(), parsed, error_str); - - if ( rc == 0 ) - { - SingleAttribute * reqs_parsed; - - reqs_parsed = new SingleAttribute("SCHED_REQUIREMENTS",parsed); - user_obj_template->set(reqs_parsed); - } - - /* --- Delete old requirements attribute --- */ - - delete array_reqs[0]; - - return rc; - -error_cleanup: - for (int i = 0; i < num ; i++) - { - delete array_reqs[i]; - } - - return -1; -} - -/* ------------------------------------------------------------------------ */ -/* ------------------------------------------------------------------------ */ - -void VirtualMachine::parse_well_known_attributes() -{ - /* - * List of meaningful attributes, used in other places and expected in - * obj_template: - * - * DISK - * NIC - * VCPU - * MEMORY - * CPU - * CONTEXT - * OS - * GRAPHICS - * - * INPUT - * FEATURES - * RAW - * CLONING_TEMPLATE_ID - */ - - vector v_attr; - vector::iterator it; - - string names[] = {"INPUT", "FEATURES", "RAW", "CLONING_TEMPLATE_ID"}; - - for (int i=0; i<4; i++) - { - v_attr.clear(); - - user_obj_template->remove(names[i], v_attr); - - for (it=v_attr.begin(); it != v_attr.end(); it++) - { - obj_template->set(*it); - } - } -} - /* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */ @@ -2333,6 +1640,24 @@ void VirtualMachine::attach_nic_success() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ +static void clear_context_network(const char* vars[][2], int num_vars, + VectorAttribute * context, int nic_id) +{ + ostringstream att_name; + + for (int i=0; i < num_vars; i++) + { + att_name.str(""); + + att_name << "ETH" << nic_id << "_" << vars[i][0]; + + context->remove(att_name.str()); + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + VectorAttribute * VirtualMachine::attach_nic_failure() { @@ -2691,164 +2016,6 @@ bool VirtualMachine::is_vrouter() /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ - -int VirtualMachine::generate_context(string &files, int &disk_id, - const string& token_password) -{ - ofstream file; - string files_ds, error_str; - - vector attrs; - - map::const_iterator it; - - files = ""; - bool token; - - if ( history == 0 ) - { - return -1; - } - - VectorAttribute * context = obj_template->get("CONTEXT"); - - if ( context == 0 ) - { - log("VM", Log::INFO, "Virtual Machine has no context"); - return 0; - } - - //Generate dynamic context attributes - if ( generate_network_context(context, error_str) != 0 ) - { - ostringstream oss; - - oss << "Cannot parse network context:: " << error_str; - log("VM", Log::ERROR, oss); - return -1; - } - - file.open(history->context_file.c_str(),ios::out); - - if (file.fail() == true) - { - ostringstream oss; - - oss << "Could not open context file: " << history->context_file; - log("VM", Log::ERROR, oss); - return -1; - } - - files = context->vector_value("FILES"); - files_ds = context->vector_value("FILES_DS"); - - if (!files_ds.empty()) - { - files += " "; - files += files_ds; - } - - for (size_t i=0;ivector_value("TOKEN", token); - - if (token) - { - ofstream token_file; - ostringstream oss; - - string* encrypted; - string tk_error; - - if (token_password.empty()) - { - tk_error = "Cannot generate OneGate token: TOKEN_PASSWORD not set in" - " the user template."; - file.close(); - - log("VM", Log::ERROR, tk_error.c_str()); - set_template_error_message(tk_error); - - return -1; - } - - token_file.open(history->token_file.c_str(), ios::out); - - if (token_file.fail()) - { - tk_error = "Cannot create token file"; - - file.close(); - - log("VM", Log::ERROR, tk_error.c_str()); - set_template_error_message(tk_error); - - return -1; - } - - oss << oid << ':' << stime; - - encrypted = one_util::aes256cbc_encrypt(oss.str(), token_password); - - token_file << *encrypted << endl; - - token_file.close(); - - delete encrypted; - - files += (" " + history->token_file); - } - - const map values = context->value(); - - file << "# Context variables generated by OpenNebula\n"; - - for (it=values.begin(); it != values.end(); it++ ) - { - //Replace every ' in value by '\'' - string escape_str(it->second); - size_t pos = 0; - - while ((pos = escape_str.find('\'', pos)) != string::npos) - { - escape_str.replace(pos,1,"'\\''"); - pos = pos + 4; - } - - file << it->first <<"='" << escape_str << "'" << endl; - } - - file.close(); - - context->vector_value("DISK_ID", disk_id); - - return 1; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::get_created_by_uid() const -{ - int created_by_uid; - - if (obj_template->get("CREATED_BY", created_by_uid)) - { - return created_by_uid; - } - - return get_uid(); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - const VectorAttribute* VirtualMachine::get_nic(int nic_id) const { int num_nics; @@ -2871,195 +2038,6 @@ const VectorAttribute* VirtualMachine::get_nic(int nic_id) const return 0; } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::set_saveas_disk(int disk_id, int snap_id, int &iid, - long long &size, string& err_str) -{ - iid = -1; - - VirtualMachineDisk * disk = get_disk(disk_id); - - if (disk == 0) - { - err_str = "DISK does not exist."; - return -1; - } - - if (disk->vector_value("IMAGE_ID", iid) != 0) - { - iid = -1; - err_str = "DISK does not have a valid IMAGE_ID."; - return -1; - } - - const Snapshots * snaps = disk->get_snapshots(); - - if (snap_id != -1) - { - if (snaps == 0 || !snaps->exists(snap_id)) - { - err_str = "Snapshot does not exist."; - return -1; - } - } - - disk->replace("HOTPLUG_SAVE_AS_ACTIVE", "YES"); - disk->replace("HOTPLUG_SAVE_AS_SNAPSHOT_ID", snap_id); - - size = 0; - disk->vector_value("SIZE", size); - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::set_saveas_disk(int disk_id, const string& source, int iid) -{ - if (lcm_state != HOTPLUG_SAVEAS && lcm_state != HOTPLUG_SAVEAS_SUSPENDED - && lcm_state != HOTPLUG_SAVEAS_POWEROFF ) - { - return -1; - } - - VirtualMachineDisk * disk = get_disk(disk_id); - - if ( disk == 0 ) - { - return -1; - } - - disk->replace("HOTPLUG_SAVE_AS", iid); - disk->replace("HOTPLUG_SAVE_AS_SOURCE", source); - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::set_saveas_state() -{ - switch (state) - { - case ACTIVE: - if (lcm_state != RUNNING) - { - return -1; - } - - set_state(HOTPLUG_SAVEAS); - break; - - case POWEROFF: - set_state(ACTIVE); - set_state(HOTPLUG_SAVEAS_POWEROFF); - break; - - case SUSPENDED: - set_state(ACTIVE); - set_state(HOTPLUG_SAVEAS_SUSPENDED); - break; - - default: - return -1; - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::clear_saveas_state() -{ - switch (lcm_state) - { - case HOTPLUG_SAVEAS: - set_state(RUNNING); - break; - - case HOTPLUG_SAVEAS_POWEROFF: - set_state(POWEROFF); - set_state(LCM_INIT); - break; - - case HOTPLUG_SAVEAS_SUSPENDED: - set_state(SUSPENDED); - set_state(LCM_INIT); - break; - - default: - return -1; - } - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::clear_saveas_disk() -{ - vector disks; - - int image_id; - bool active; - - int num_disks = obj_template->get("DISK", disks); - - for(int i=0; ivector_value("HOTPLUG_SAVE_AS_ACTIVE", active); - - if (active) - { - disks[i]->vector_value("HOTPLUG_SAVE_AS", image_id); - - disks[i]->remove("HOTPLUG_SAVE_AS_ACTIVE"); - disks[i]->remove("HOTPLUG_SAVE_AS"); - disks[i]->remove("HOTPLUG_SAVE_AS_SOURCE"); - disks[i]->remove("HOTPLUG_SAVE_AS_SNAPSHOT_ID"); - - return image_id; - } - } - - return -1; -} - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::get_saveas_disk(int& disk_id, string& source, - int& image_id, string& snap_id, string& tm_mad, string& ds_id) -{ - vector disks; - - int rc; - int num_disks = obj_template->get("DISK", disks); - - for(int i=0; ivector_value("HOTPLUG_SAVE_AS_ACTIVE") == "YES" ) - { - rc = disks[i]->vector_value("HOTPLUG_SAVE_AS_SOURCE", source); - rc += disks[i]->vector_value("HOTPLUG_SAVE_AS", image_id); - rc += disks[i]->vector_value("HOTPLUG_SAVE_AS_SNAPSHOT_ID", snap_id); - rc += disks[i]->vector_value("DISK_ID", disk_id); - rc += disks[i]->vector_value("DATASTORE_ID", ds_id); - rc += disks[i]->vector_value("TM_MAD", tm_mad); - - return rc; - } - } - - return -1; -} /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -3591,193 +2569,6 @@ int VirtualMachine::parse_public_clouds(const char * pname, string& error) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -static void parse_context_network(const char* vars[][2], int num_vars, - VectorAttribute * context, VectorAttribute * nic) -{ - string nic_id = nic->vector_value("NIC_ID"); - - for (int i=0; i < num_vars; i++) - { - ostringstream cvar; - string cval; - - cvar << "ETH" << nic_id << "_" << vars[i][0]; - - cval = nic->vector_value(vars[i][1]); //Check the NIC - - if (cval.empty()) //Will check the AR and VNET - { - ostringstream cval_ss; - - cval_ss << "$NETWORK["<< vars[i][1] <<", NIC_ID=\""<< nic_id <<"\"]"; - cval = cval_ss.str(); - } - - context->replace(cvar.str(), cval); - } -} - -/* -------------------------------------------------------------------------- */ - -static void parse_pci_context_network(const char* vars[][2], int num_vars, - VectorAttribute * context, VectorAttribute * nic) -{ - string pci_id = nic->vector_value("PCI_ID"); - - for (int i=0; i < num_vars; i++) - { - ostringstream cvar; - - cvar << "PCI" << pci_id << "_" << vars[i][0]; - - string cval = nic->vector_value(vars[i][1]); - - if (!cval.empty()) - { - context->replace(cvar.str(), cval); - } - } - -} - -/* -------------------------------------------------------------------------- */ - -void VirtualMachine::parse_nic_context(VectorAttribute * c, VectorAttribute * n) -{ - parse_context_network(NETWORK_CONTEXT, NUM_NETWORK_CONTEXT, c, n); - parse_context_network(NETWORK6_CONTEXT, NUM_NETWORK6_CONTEXT, c, n); -} - -/* -------------------------------------------------------------------------- */ - -bool VirtualMachine::generate_pci_context(VectorAttribute * context) -{ - bool net_context; - vector vatts; - - context->vector_value("NETWORK", net_context); - - int num_vatts = obj_template->get("PCI", vatts); - - for(int i=0; ivector_value("TYPE") == "NIC" ) - { - parse_pci_context_network(NETWORK_CONTEXT, NUM_NETWORK_CONTEXT, - context, vatts[i]); - parse_pci_context_network(NETWORK6_CONTEXT, NUM_NETWORK6_CONTEXT, - context, vatts[i]); - } - - ostringstream cvar; - - cvar << "PCI" << vatts[i]->vector_value("PCI_ID") << "_ADDRESS"; - - string cval = vatts[i]->vector_value("VM_ADDRESS"); - - if (!cval.empty()) - { - context->replace(cvar.str(), cval); - } - } - - return net_context; -} - -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::generate_network_context(VectorAttribute * context, - string& error_str) -{ - bool net_context; - - context->vector_value("NETWORK", net_context); - - if (!net_context) - { - return 0; - } - - vector vatts; - int rc; - - string parsed; - string* str; - - VectorAttribute tmp_context("TMP_CONTEXT"); - - int num_vatts = obj_template->get("NIC", vatts); - - if ( num_vatts == 0 ) - { - return 0; - } - - for(int i=0; imerge(&tmp_context, true); - - return 0; -} - -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::generate_token_context(VectorAttribute * context, string& e) -{ - bool token; - string ep; - - context->vector_value("TOKEN", token); - - if ( token == false ) - { - return 0; - } - - Nebula::instance().get_configuration_attribute("ONEGATE_ENDPOINT", ep); - - if ( ep.empty() ) - { - e = "TOKEN set, but onegate endpoint was not defined in oned.conf."; - return -1; - } - - context->replace("ONEGATE_ENDPOINT", ep); - context->replace("VMID", oid); - - // Store the original owner to compute token_password in case of a chown - replace_template_attribute("CREATED_BY", uid); - - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - /** * Replaces the values of a vector value, preserving the existing ones */ @@ -4062,186 +2853,65 @@ int VirtualMachine::set_up_attach_disk(VirtualMachineTemplate * tmpl, string& er /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -/* -int VirtualMachine::set_up_resize_disk(int disk_id, long size, string& err) +/* Disk save as interface */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::set_saveas_state() { - VectorAttribute * disk = get_disk(disk_id); - long size_prev; - - if ( disk == 0 ) + switch (state) { - err = "Disk not found"; - return -1; + case ACTIVE: + if (lcm_state != RUNNING) + { + return -1; + } + + set_state(HOTPLUG_SAVEAS); + break; + + case POWEROFF: + set_state(ACTIVE); + set_state(HOTPLUG_SAVEAS_POWEROFF); + break; + + case SUSPENDED: + set_state(ACTIVE); + set_state(HOTPLUG_SAVEAS_SUSPENDED); + break; + + default: + return -1; } - if ( disk->vector_value("SIZE", size_prev) != 0 ) - { - err = "Wrong format for disk SIZE"; - return -1; - } - - if ( size <= size_prev ) - { - err = "New size has to be bigger than current one"; - return -1; - } - - disk->replace("SIZE_PREV", size_prev); - - disk->replace("SIZE", size); - - disk->replace("RESIZE", "YES"); - return 0; } -*/ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ - - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ -/* VirtualMachine parser functions */ -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -pthread_mutex_t VirtualMachine::lex_mutex = PTHREAD_MUTEX_INITIALIZER; - -extern "C" +int VirtualMachine::clear_saveas_state() { - typedef struct yy_buffer_state * YY_BUFFER_STATE; + switch (lcm_state) + { + case HOTPLUG_SAVEAS: + set_state(RUNNING); + break; - int vm_var_parse (VirtualMachine * vm, - ostringstream * parsed, - char ** errmsg); + case HOTPLUG_SAVEAS_POWEROFF: + set_state(POWEROFF); + set_state(LCM_INIT); + break; - int vm_file_var_parse (VirtualMachine * vm, - vector * img_ids, - char ** errmsg); + case HOTPLUG_SAVEAS_SUSPENDED: + set_state(SUSPENDED); + set_state(LCM_INIT); + break; - int vm_var_lex_destroy(); + default: + return -1; + } - YY_BUFFER_STATE vm_var__scan_string(const char * str); - - void vm_var__delete_buffer(YY_BUFFER_STATE); + return 0; } -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_template_attribute(const string& attribute, - string& parsed, - string& error_str) -{ - YY_BUFFER_STATE str_buffer = 0; - const char * str; - int rc; - ostringstream oss_parsed; - char * error_msg = 0; - - pthread_mutex_lock(&lex_mutex); - - str = attribute.c_str(); - str_buffer = vm_var__scan_string(str); - - if (str_buffer == 0) - { - goto error_yy; - } - - rc = vm_var_parse(this, &oss_parsed, &error_msg); - - vm_var__delete_buffer(str_buffer); - - vm_var_lex_destroy(); - - pthread_mutex_unlock(&lex_mutex); - - if ( rc != 0 && error_msg != 0 ) - { - ostringstream oss; - - oss << "Error parsing: " << attribute << ". " << error_msg; - log("VM", Log::ERROR, oss); - - error_str = oss.str(); - - free(error_msg); - } - - parsed = oss_parsed.str(); - - return rc; - -error_yy: - log("VM",Log::ERROR,"Error setting scan buffer"); - pthread_mutex_unlock(&lex_mutex); - return -1; -} - -/* -------------------------------------------------------------------------- */ - -int VirtualMachine::parse_file_attribute(string attribute, - vector& img_ids, - string& error) -{ - YY_BUFFER_STATE str_buffer = 0; - const char * str; - int rc; - ostringstream oss_parsed; - char * error_msg = 0; - - size_t non_blank_pos; - - //Removes leading blanks from attribute, these are not managed - //by the parser as it is common to the other VM varibales - non_blank_pos = attribute.find_first_not_of(" \t\n\v\f\r"); - - if ( non_blank_pos != string::npos ) - { - attribute.erase(0, non_blank_pos); - } - - pthread_mutex_lock(&lex_mutex); - - str = attribute.c_str(); - str_buffer = vm_var__scan_string(str); - - if (str_buffer == 0) - { - goto error_yy; - } - - rc = vm_file_var_parse(this, &img_ids, &error_msg); - - vm_var__delete_buffer(str_buffer); - - vm_var_lex_destroy(); - - pthread_mutex_unlock(&lex_mutex); - - if ( rc != 0 ) - { - ostringstream oss; - - if ( error_msg != 0 ) - { - oss << "Error parsing: " << attribute << ". " << error_msg; - free(error_msg); - } - else - { - oss << "Unknown error parsing: " << attribute << "."; - } - - error = oss.str(); - } - - return rc; - -error_yy: - log("VM",Log::ERROR,"Error setting scan buffer"); - pthread_mutex_unlock(&lex_mutex); - return -1; -} diff --git a/src/vm/VirtualMachineAttribute.cc b/src/vm/VirtualMachineAttribute.cc index 11eed951e4..46f1efa383 100644 --- a/src/vm/VirtualMachineAttribute.cc +++ b/src/vm/VirtualMachineAttribute.cc @@ -16,26 +16,6 @@ #include "VirtualMachineAttribute.h" - -VirtualMachineAttributeSet::VirtualMachineAttributeSet(const std::string anam, - const std::string& idnam, Template * tmpl):dispose(false) -{ - std::vector vas; - - tmpl->get(anam, vas); - - init_attribute_map(idnam, vas); -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - -VirtualMachineAttributeSet::VirtualMachineAttributeSet(const std::string& idnam, - std::vector& vas, bool _dispose):dispose(_dispose) -{ - init_attribute_map(idnam, vas); -} - /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ diff --git a/src/vm/VirtualMachineContext.cc b/src/vm/VirtualMachineContext.cc new file mode 100644 index 0000000000..3333c720fb --- /dev/null +++ b/src/vm/VirtualMachineContext.cc @@ -0,0 +1,579 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* 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. */ +/* -------------------------------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "VirtualMachine.h" +#include "VirtualNetworkPool.h" +#include "ImagePool.h" +#include "NebulaLog.h" +#include "NebulaUtil.h" +#include "Snapshots.h" + +#include "Nebula.h" + +#include "vm_file_var_syntax.h" +#include "vm_var_syntax.h" + +/* -------------------------------------------------------------------------- */ +/* Context constants */ +/* -------------------------------------------------------------------------- */ +const char * VirtualMachine::NETWORK_CONTEXT[][2] = { + {"IP", "IP"}, + {"MAC", "MAC"}, + {"MASK", "NETWORK_MASK"}, + {"NETWORK", "NETWORK_ADDRESS"}, + {"GATEWAY", "GATEWAY"}, + {"DNS", "DNS"}, + {"SEARCH_DOMAIN", "SEARCH_DOMAIN"}, + {"MTU", "GUEST_MTU"}, + {"VLAN_ID", "VLAN_ID"}, + {"VROUTER_IP", "VROUTER_IP"}, + {"VROUTER_MANAGEMENT", "VROUTER_MANAGEMENT"}}; +const int VirtualMachine::NUM_NETWORK_CONTEXT = 11; + +const char* VirtualMachine::NETWORK6_CONTEXT[][2] = { + {"IP6", "IP6_GLOBAL"}, + {"IP6_ULA", "IP6_ULA"}, + {"GATEWAY6", "GATEWAY6"}, + {"CONTEXT_FORCE_IPV4", "CONTEXT_FORCE_IPV4"}, + {"VROUTER_IP6", "VROUTER_IP6_GLOBAL"}}; + +const int VirtualMachine::NUM_NETWORK6_CONTEXT = 5; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* CONTEXT - Public Interface */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::generate_context(string &files, int &disk_id, + const string& token_password) +{ + ofstream file; + string files_ds, error_str; + + vector attrs; + + map::const_iterator it; + + files = ""; + bool token; + + if ( history == 0 ) + { + return -1; + } + + VectorAttribute * context = obj_template->get("CONTEXT"); + + if ( context == 0 ) + { + log("VM", Log::INFO, "Virtual Machine has no context"); + return 0; + } + + //Generate dynamic context attributes + if ( generate_network_context(context, error_str) != 0 ) + { + ostringstream oss; + + oss << "Cannot parse network context:: " << error_str; + log("VM", Log::ERROR, oss); + return -1; + } + + file.open(history->context_file.c_str(),ios::out); + + if (file.fail() == true) + { + ostringstream oss; + + oss << "Could not open context file: " << history->context_file; + log("VM", Log::ERROR, oss); + return -1; + } + + files = context->vector_value("FILES"); + files_ds = context->vector_value("FILES_DS"); + + if (!files_ds.empty()) + { + files += " "; + files += files_ds; + } + + for (size_t i=0;ivector_value("TOKEN", token); + + if (token) + { + ofstream token_file; + ostringstream oss; + + string* encrypted; + string tk_error; + + if (token_password.empty()) + { + tk_error = "Cannot generate OneGate token: TOKEN_PASSWORD not set in" + " the user template."; + file.close(); + + log("VM", Log::ERROR, tk_error.c_str()); + set_template_error_message(tk_error); + + return -1; + } + + token_file.open(history->token_file.c_str(), ios::out); + + if (token_file.fail()) + { + tk_error = "Cannot create token file"; + + file.close(); + + log("VM", Log::ERROR, tk_error.c_str()); + set_template_error_message(tk_error); + + return -1; + } + + oss << oid << ':' << stime; + + encrypted = one_util::aes256cbc_encrypt(oss.str(), token_password); + + token_file << *encrypted << endl; + + token_file.close(); + + delete encrypted; + + files += (" " + history->token_file); + } + + const map values = context->value(); + + file << "# Context variables generated by OpenNebula\n"; + + for (it=values.begin(); it != values.end(); it++ ) + { + //Replace every ' in value by '\'' + string escape_str(it->second); + size_t pos = 0; + + while ((pos = escape_str.find('\'', pos)) != string::npos) + { + escape_str.replace(pos,1,"'\\''"); + pos = pos + 4; + } + + file << it->first <<"='" << escape_str << "'" << endl; + } + + file.close(); + + context->vector_value("DISK_ID", disk_id); + + return 1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::get_created_by_uid() const +{ + int created_by_uid; + + if (obj_template->get("CREATED_BY", created_by_uid)) + { + return created_by_uid; + } + + return get_uid(); +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* CONTEXT - Private Interface */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static void parse_context_network(const char* vars[][2], int num_vars, + VectorAttribute * context, VectorAttribute * nic) +{ + string nic_id = nic->vector_value("NIC_ID"); + + for (int i=0; i < num_vars; i++) + { + ostringstream cvar; + string cval; + + cvar << "ETH" << nic_id << "_" << vars[i][0]; + + cval = nic->vector_value(vars[i][1]); //Check the NIC + + if (cval.empty()) //Will check the AR and VNET + { + ostringstream cval_ss; + + cval_ss << "$NETWORK["<< vars[i][1] <<", NIC_ID=\""<< nic_id <<"\"]"; + cval = cval_ss.str(); + } + + context->replace(cvar.str(), cval); + } +} + + +int VirtualMachine::generate_network_context(VectorAttribute* context, + string& error_str) +{ + bool net_context; + + context->vector_value("NETWORK", net_context); + + if (!net_context) + { + return 0; + } + + vector vatts; + int rc; + + string parsed; + string* str; + + VectorAttribute tmp_context("TMP_CONTEXT"); + + int num_vatts = obj_template->get("NIC", vatts); + + if ( num_vatts == 0 ) + { + return 0; + } + + for(int i=0; imerge(&tmp_context, true); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static void parse_pci_context_network(const char* vars[][2], int num_vars, + VectorAttribute * context, VectorAttribute * nic) +{ + string pci_id = nic->vector_value("PCI_ID"); + + for (int i=0; i < num_vars; i++) + { + ostringstream cvar; + + cvar << "PCI" << pci_id << "_" << vars[i][0]; + + string cval = nic->vector_value(vars[i][1]); + + if (!cval.empty()) + { + context->replace(cvar.str(), cval); + } + } + +} + +/** + * Generate the PCI related CONTEXT setions, i.e. PCI_*. This function + * is also adds basic network attributes for pass-through NICs + * @param context attribute of the VM + * @return true if the net context was generated. + */ +bool VirtualMachine::generate_pci_context(VectorAttribute * context) +{ + bool net_context; + vector vatts; + + context->vector_value("NETWORK", net_context); + + int num_vatts = obj_template->get("PCI", vatts); + + for(int i=0; ivector_value("TYPE") == "NIC" ) + { + parse_pci_context_network(NETWORK_CONTEXT, NUM_NETWORK_CONTEXT, + context, vatts[i]); + parse_pci_context_network(NETWORK6_CONTEXT, NUM_NETWORK6_CONTEXT, + context, vatts[i]); + } + + ostringstream cvar; + + cvar << "PCI" << vatts[i]->vector_value("PCI_ID") << "_ADDRESS"; + + string cval = vatts[i]->vector_value("VM_ADDRESS"); + + if (!cval.empty()) + { + context->replace(cvar.str(), cval); + } + } + + return net_context; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::generate_token_context(VectorAttribute * context, string& e) +{ + bool token; + string ep; + + context->vector_value("TOKEN", token); + + if ( token == false ) + { + return 0; + } + + Nebula::instance().get_configuration_attribute("ONEGATE_ENDPOINT", ep); + + if ( ep.empty() ) + { + e = "TOKEN set, but onegate endpoint was not defined in oned.conf."; + return -1; + } + + context->replace("ONEGATE_ENDPOINT", ep); + context->replace("VMID", oid); + + // Store the original owner to compute token_password in case of a chown + replace_template_attribute("CREATED_BY", uid); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_context(string& error_str) +{ + VectorAttribute * context = obj_template->get("CONTEXT"); + + if ( context == 0 ) + { + return 0; + } + + string files_ds = context->vector_value("FILES_DS"); + + context->remove("FILES_DS"); + + // ------------------------------------------------------------------------- + // Add network context and parse variables + // ------------------------------------------------------------------------- + if (parse_context_variables(&context, error_str) == -1 || + generate_network_context(context, error_str) == -1 ) + { + return -1; + } + + generate_pci_context(context); + + // ------------------------------------------------------------------------- + // Parse FILE_DS variables + // ------------------------------------------------------------------------- + if (!files_ds.empty()) + { + string files_ds_parsed; + string st; + + ostringstream oss_parsed; + + vector img_ids; + + if ( parse_file_attribute(files_ds, img_ids, error_str) != 0 ) + { + return -1; + } + + if ( img_ids.size() > 0 ) + { + vector::iterator it; + + Nebula& nd = Nebula::instance(); + + ImagePool * ipool = nd.get_ipool(); + Image * img = 0; + + Image::ImageType type; + Image::ImageState state; + + for ( it=img_ids.begin() ; it < img_ids.end(); it++ ) + { + img = ipool->get(*it, true); + + if ( img != 0 ) + { + oss_parsed << img->get_source() << ":'" + << img->get_name() << "' "; + + type = img->get_type(); + state = img->get_state(); + + img->unlock(); + + if (type != Image::CONTEXT) + { + error_str = "Only images of type CONTEXT can be used in" + " FILE_DS attribute."; + return -1; + } + + if ( state != Image::READY ) + { + ostringstream oss; + + oss << Image::type_to_str(type) + << " Image '" << *it << "' not in READY state."; + + error_str = oss.str(); + + return -1; + } + + } + } + } + + files_ds_parsed = oss_parsed.str(); + + if ( !files_ds_parsed.empty() ) + { + context->replace("FILES_DS", files_ds_parsed); + } + } + + // ------------------------------------------------------------------------- + // OneGate URL + // ------------------------------------------------------------------------- + if ( generate_token_context(context, error_str) != 0 ) + { + return -1; + } + + // ------------------------------------------------------------------------- + // Virtual Router attributes + // ------------------------------------------------------------------------- + string st; + + for (int i = 0; i < NUM_VROUTER_ATTRIBUTES; i++) + { + obj_template->get(VROUTER_ATTRIBUTES[i], st); + + if (!st.empty()) + { + context->replace(VROUTER_ATTRIBUTES[i], st); + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_context_variables(VectorAttribute ** context, + string& error_str) +{ + int rc; + + string parsed; + string * str = (*context)->marshall(); + + if (str == 0) + { + return -1; + } + + rc = parse_template_attribute(*str, parsed, error_str); + + delete str; + + if (rc != 0) + { + return -1; + } + + *context = new VectorAttribute("CONTEXT"); + (*context)->unmarshall(parsed); + + obj_template->erase("CONTEXT"); + obj_template->set(*context); + + return 0; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + diff --git a/src/vm/VirtualMachineDisk.cc b/src/vm/VirtualMachineDisk.cc index f8d999dea7..945dd2204e 100644 --- a/src/vm/VirtualMachineDisk.cc +++ b/src/vm/VirtualMachineDisk.cc @@ -368,7 +368,7 @@ long long VirtualMachineDisks::system_ds_size() long long VirtualMachineDisks::system_ds_size(Template * ds_tmpl) { - VirtualMachineDisks disks(ds_tmpl); + VirtualMachineDisks disks(ds_tmpl, false); return disks.system_ds_size(); } @@ -386,7 +386,7 @@ void VirtualMachineDisks::extended_info(int uid) void VirtualMachineDisks::extended_info(int uid, Template * tmpl) { - VirtualMachineDisks disks(tmpl); + VirtualMachineDisks disks(tmpl, false); return disks.extended_info(uid); } @@ -415,8 +415,6 @@ bool VirtualMachineDisks::volatile_info(int ds_id) return found; } -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ @@ -477,140 +475,6 @@ void VirtualMachineDisks::assign_disk_targets( /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -VirtualMachineDisk * VirtualMachineDisks::set_up_attach(int vmid, int uid, - int cluster_id, VectorAttribute * vdisk, VectorAttribute * vcontext, - string& error) -{ - set used_targets; - int max_disk_id = -1; - - // ------------------------------------------------------------------------- - // Get the list of used targets and max_disk_id - // ------------------------------------------------------------------------- - for ( disk_iterator disk = begin() ; disk != end() ; ++disk ) - { - string target = (*disk)->vector_value("TARGET"); - - if ( !target.empty() ) - { - used_targets.insert(target); - } - - int disk_id = (*disk)->get_disk_id(); - - if ( disk_id > max_disk_id ) - { - max_disk_id = disk_id; - } - } - - if ( vcontext != 0 ) - { - string target = vcontext->vector_value("TARGET"); - - if ( !target.empty() ) - { - used_targets.insert(target); - } - - int disk_id; - - vcontext->vector_value("DISK_ID", disk_id); - - if ( disk_id > max_disk_id ) - { - max_disk_id = disk_id; - } - } - - // ------------------------------------------------------------------------- - // Acquire the new disk image - // ------------------------------------------------------------------------- - Nebula& nd = Nebula::instance(); - ImagePool * ipool = nd.get_ipool(); - ImageManager* imagem = nd.get_imagem(); - - Snapshots * snap = 0; - - string dev_prefix; - Image::ImageType img_type; - - int image_id; - - VirtualMachineDisk * disk = new VirtualMachineDisk(vdisk, max_disk_id + 1); - - int rc = ipool->acquire_disk(vmid, disk, max_disk_id + 1, img_type, - dev_prefix, uid, image_id, &snap, error); - if ( rc != 0 ) - { - return 0; - } - - disk->set_snapshots(snap); - - string target = disk->vector_value("TARGET"); - - if ( !target.empty() ) - { - if ( used_targets.insert(target).second == false ) - { - error = "Target " + target + " is already in use."; - - imagem->release_image(vmid, image_id, false); - - delete disk; - return 0; - } - } - else - { - queue > disks_queue; - - disks_queue.push(make_pair(dev_prefix, disk)); - - assign_disk_targets(disks_queue, used_targets); - } - - // ------------------------------------------------------------------------- - // Check that we don't have a cluster incompatibility. - // ------------------------------------------------------------------------- - string disk_cluster_ids = disk->vector_value("CLUSTER_ID"); - - if ( !disk_cluster_ids.empty() ) - { - set cluster_ids; - one_util::split_unique(disk_cluster_ids, ',', cluster_ids); - - if (cluster_ids.count(cluster_id) == 0) - { - ostringstream oss; - - oss << "Image [" << image_id << "] is not part of cluster [" - << cluster_id << "]"; - - error = oss.str(); - - imagem->release_image(vmid, image_id, false); - - delete disk; - return 0; - } - } - - // ------------------------------------------------------------------------- - // Add disk to the set - // ------------------------------------------------------------------------- - - disk->set_attach(); - - add_attribute(disk, disk->get_disk_id()); - - return disk; -} - -/* -------------------------------------------------------------------------- */ -/* -------------------------------------------------------------------------- */ - int VirtualMachineDisks::get_images(int vm_id, int uid, vector disks, VectorAttribute * vcontext, std::string& error_str) @@ -875,6 +739,140 @@ int VirtualMachineDisks::set_attach(int id) return 0; } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +VirtualMachineDisk * VirtualMachineDisks::set_up_attach(int vmid, int uid, + int cluster_id, VectorAttribute * vdisk, VectorAttribute * vcontext, + string& error) +{ + set used_targets; + int max_disk_id = -1; + + // ------------------------------------------------------------------------- + // Get the list of used targets and max_disk_id + // ------------------------------------------------------------------------- + for ( disk_iterator disk = begin() ; disk != end() ; ++disk ) + { + string target = (*disk)->vector_value("TARGET"); + + if ( !target.empty() ) + { + used_targets.insert(target); + } + + int disk_id = (*disk)->get_disk_id(); + + if ( disk_id > max_disk_id ) + { + max_disk_id = disk_id; + } + } + + if ( vcontext != 0 ) + { + string target = vcontext->vector_value("TARGET"); + + if ( !target.empty() ) + { + used_targets.insert(target); + } + + int disk_id; + + vcontext->vector_value("DISK_ID", disk_id); + + if ( disk_id > max_disk_id ) + { + max_disk_id = disk_id; + } + } + + // ------------------------------------------------------------------------- + // Acquire the new disk image + // ------------------------------------------------------------------------- + Nebula& nd = Nebula::instance(); + ImagePool * ipool = nd.get_ipool(); + ImageManager* imagem = nd.get_imagem(); + + Snapshots * snap = 0; + + string dev_prefix; + Image::ImageType img_type; + + int image_id; + + VirtualMachineDisk * disk = new VirtualMachineDisk(vdisk, max_disk_id + 1); + + int rc = ipool->acquire_disk(vmid, disk, max_disk_id + 1, img_type, + dev_prefix, uid, image_id, &snap, error); + if ( rc != 0 ) + { + return 0; + } + + disk->set_snapshots(snap); + + string target = disk->vector_value("TARGET"); + + if ( !target.empty() ) + { + if ( used_targets.insert(target).second == false ) + { + error = "Target " + target + " is already in use."; + + imagem->release_image(vmid, image_id, false); + + delete disk; + return 0; + } + } + else + { + queue > disks_queue; + + disks_queue.push(make_pair(dev_prefix, disk)); + + assign_disk_targets(disks_queue, used_targets); + } + + // ------------------------------------------------------------------------- + // Check that we don't have a cluster incompatibility. + // ------------------------------------------------------------------------- + string disk_cluster_ids = disk->vector_value("CLUSTER_ID"); + + if ( !disk_cluster_ids.empty() ) + { + set cluster_ids; + one_util::split_unique(disk_cluster_ids, ',', cluster_ids); + + if (cluster_ids.count(cluster_id) == 0) + { + ostringstream oss; + + oss << "Image [" << image_id << "] is not part of cluster [" + << cluster_id << "]"; + + error = oss.str(); + + imagem->release_image(vmid, image_id, false); + + delete disk; + return 0; + } + } + + // ------------------------------------------------------------------------- + // Add disk to the set + // ------------------------------------------------------------------------- + + disk->set_attach(); + + add_attribute(disk, disk->get_disk_id()); + + return disk; +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* RESIZE DISK INTERFACE */ @@ -895,6 +893,8 @@ int VirtualMachineDisks::set_resize(int id) return 0; } +/* -------------------------------------------------------------------------- */ + void VirtualMachineDisks::clear_resize(bool restore) { string size, size_prev; @@ -914,6 +914,40 @@ void VirtualMachineDisks::clear_resize(bool restore) disk->clear_resize(); } +/* -------------------------------------------------------------------------- */ + +int VirtualMachineDisks::set_up_resize(int disk_id, long size, string& err) +{ + VirtualMachineDisk * disk = get_disk(disk_id); + long size_prev; + + if ( disk == 0 ) + { + err = "Disk not found"; + return -1; + } + + if ( disk->vector_value("SIZE", size_prev) != 0 ) + { + err = "Wrong format for disk SIZE"; + return -1; + } + + if ( size <= size_prev ) + { + err = "New size has to be bigger than current one"; + return -1; + } + + disk->replace("SIZE_PREV", size_prev); + + disk->replace("SIZE", size); + + disk->set_resize(); + + return 0; +} + /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* SNAPSHOT INTERFACE */ @@ -951,15 +985,12 @@ int VirtualMachineDisks::set_active_snapshot(int id, int snap_id) void VirtualMachineDisks::clear_active_snapshot() { - for ( disk_iterator disk = begin() ; disk != end() ; ++disk ) - { - if ( (*disk)->is_active_snapshot() ) - { - (*disk)->clear_active_snapshot(); - (*disk)->remove("DISK_SNAPSHOT_ID"); + VirtualMachineDisk * disk = get_active_snapshot(); - break; - } + if ( disk != 0 ) + { + disk->clear_active_snapshot(); + disk->remove("DISK_SNAPSHOT_ID"); } } @@ -1081,7 +1112,7 @@ void VirtualMachineDisks::delete_snapshot(int disk_id, int snap_id, void VirtualMachineDisks::delete_non_persistent_snapshots(Template **vm_quotas, map& ds_quotas) { - long long system_disk; + long long system_disk = 0; for ( disk_iterator disk = begin() ; disk != end() ; ++disk ) { @@ -1131,3 +1162,115 @@ void VirtualMachineDisks::delete_non_persistent_snapshots(Template **vm_quotas, } } +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineDisks::set_saveas(int disk_id, int snap_id, int &iid, + long long &size, string& err_str) +{ + iid = -1; + + VirtualMachineDisk * disk = + static_cast(get_attribute(disk_id)); + + if (disk == 0) + { + err_str = "DISK does not exist."; + return -1; + } + + if (disk->vector_value("IMAGE_ID", iid) != 0) + { + iid = -1; + err_str = "DISK does not have a valid IMAGE_ID."; + return -1; + } + + const Snapshots * snaps = disk->get_snapshots(); + + if (snap_id != -1) + { + if (snaps == 0 || !snaps->exists(snap_id)) + { + err_str = "Snapshot does not exist."; + return -1; + } + } + + disk->set_saveas(); + disk->replace("HOTPLUG_SAVE_AS_SNAPSHOT_ID", snap_id); + + size = 0; + disk->vector_value("SIZE", size); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineDisks::set_saveas(int disk_id, const string& source, int iid) +{ + VirtualMachineDisk * disk = get_saveas(); + + if ( disk == 0 ) + { + return -1; + } + + disk->replace("HOTPLUG_SAVE_AS", iid); + disk->replace("HOTPLUG_SAVE_AS_SOURCE", source); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineDisks::clear_saveas() +{ + VirtualMachineDisk * disk = get_saveas(); + + if ( disk == 0 ) + { + return -1; + } + + int image_id; + + disk->clear_saveas(); + + disk->vector_value("HOTPLUG_SAVE_AS", image_id); + + disk->remove("HOTPLUG_SAVE_AS"); + disk->remove("HOTPLUG_SAVE_AS_SOURCE"); + disk->remove("HOTPLUG_SAVE_AS_SNAPSHOT_ID"); + + return image_id; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachineDisks::get_saveas_info(int& disk_id, string& source, + int& image_id, string& snap_id, string& tm_mad, string& ds_id) +{ + int rc; + + VirtualMachineDisk * disk = get_saveas(); + + if ( disk == 0 ) + { + return -1; + } + + rc = disk->vector_value("HOTPLUG_SAVE_AS_SOURCE", source); + rc += disk->vector_value("HOTPLUG_SAVE_AS", image_id); + rc += disk->vector_value("HOTPLUG_SAVE_AS_SNAPSHOT_ID", snap_id); + rc += disk->vector_value("DISK_ID", disk_id); + rc += disk->vector_value("DATASTORE_ID", ds_id); + rc += disk->vector_value("TM_MAD", tm_mad); + + return rc; +} + diff --git a/src/vm/VirtualMachineParser.cc b/src/vm/VirtualMachineParser.cc new file mode 100644 index 0000000000..2c5af4a528 --- /dev/null +++ b/src/vm/VirtualMachineParser.cc @@ -0,0 +1,683 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems */ +/* */ +/* 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. */ +/* -------------------------------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "VirtualMachine.h" +#include "VirtualNetworkPool.h" +#include "ImagePool.h" +#include "NebulaLog.h" +#include "NebulaUtil.h" +#include "Snapshots.h" + +#include "Nebula.h" + +#include "vm_file_var_syntax.h" +#include "vm_var_syntax.h" + +/* -------------------------------------------------------------------------- */ +/* Parser constanta */ +/* -------------------------------------------------------------------------- */ + +const char * VirtualMachine::NO_NIC_DEFAULTS[] = {"NETWORK_ID", "NETWORK", + "NETWORK_UID", "NETWORK_UNAME"}; + +const int VirtualMachine::NUM_NO_NIC_DEFAULTS = 4; + +const char* VirtualMachine::VROUTER_ATTRIBUTES[] = { + "VROUTER_ID", + "VROUTER_KEEPALIVED_ID", + "VROUTER_KEEPALIVED_PASSWORD"}; +const int VirtualMachine::NUM_VROUTER_ATTRIBUTES = 3; + +pthread_mutex_t VirtualMachine::lex_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/** + * Generates image attributes (DS_ID, TM_MAD, SOURCE...) for KERNEL and + * INITRD files. + * @param os attribute of the VM template + * @param base_name of the attribute "KERNEL", or "INITRD" + * @param base_type of the image attribute KERNEL, RAMDISK + * @param error_str Returns the error reason, if any + * @return 0 on succes + */ +int VirtualMachine::set_os_file(VectorAttribute* os, const string& base_name, + Image::ImageType base_type, string& error_str) +{ + vector img_ids; + Nebula& nd = Nebula::instance(); + + ImagePool * ipool = nd.get_ipool(); + Image * img = 0; + + int img_id; + + Image::ImageType type; + Image::ImageState state; + + DatastorePool * ds_pool = nd.get_dspool(); + Datastore * ds; + int ds_id; + + string attr; + string base_name_ds = base_name + "_DS"; + string base_name_id = base_name + "_DS_ID"; + string base_name_source = base_name + "_DS_SOURCE"; + string base_name_ds_id = base_name + "_DS_DSID"; + string base_name_tm = base_name + "_DS_TM"; + string base_name_cluster= base_name + "_DS_CLUSTER_ID"; + + string type_str; + + attr = os->vector_value(base_name_ds.c_str()); + + if ( attr.empty() ) + { + return 0; + } + + if ( parse_file_attribute(attr, img_ids, error_str) != 0 ) + { + return -1; + } + + if ( img_ids.size() != 1 ) + { + error_str = "Only one FILE variable can be used in: " + attr; + return -1; + } + + img_id = img_ids.back(); + + img = ipool->get(img_id, true); + + if ( img == 0 ) + { + error_str = "Image no longer exists in attribute: " + attr; + return -1; + } + + state = img->get_state(); + + ds_id = img->get_ds_id(); + type = img->get_type(); + + os->remove(base_name); + + os->replace(base_name_id, img->get_oid()); + os->replace(base_name_source, img->get_source()); + os->replace(base_name_ds_id, img->get_ds_id()); + + img->unlock(); + + type_str = Image::type_to_str(type); + + if ( type != base_type ) + { + ostringstream oss; + + oss << base_name << " needs an image of type " + << Image::type_to_str(base_type) << " and not " + << type_str; + + error_str = oss.str(); + return -1; + } + + if ( state != Image::READY ) + { + ostringstream oss; + + oss << type_str << " Image '" << img_id << " 'not in READY state."; + + error_str = oss.str(); + return -1; + } + + ds = ds_pool->get(ds_id, true); + + if ( ds == 0 ) + { + error_str = "Associated datastore for image does not exist"; + return -1; + } + + os->replace(base_name_tm, ds->get_tm_mad()); + + set cluster_ids = ds->get_cluster_ids(); + + if (!cluster_ids.empty()) + { + os->replace(base_name_cluster, one_util::join(cluster_ids, ',')); + } + + ds->unlock(); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_os(string& error_str) +{ + int num; + int rc; + + vector os_attr; + VectorAttribute * os; + + vector::iterator it; + + num = user_obj_template->remove("OS", os_attr); + + for (it=os_attr.begin(); it != os_attr.end(); it++) + { + obj_template->set(*it); + } + + if ( num == 0 ) + { + return 0; + } + else if ( num > 1 ) + { + error_str = "Only one OS attribute can be defined."; + return -1; + } + + os = dynamic_cast(os_attr[0]); + + if ( os == 0 ) + { + error_str = "Internal error parsing OS attribute."; + return -1; + } + + rc = set_os_file(os, "KERNEL", Image::KERNEL, error_str); + + if ( rc != 0 ) + { + return -1; + } + + rc = set_os_file(os, "INITRD", Image::RAMDISK, error_str); + + if ( rc != 0 ) + { + return -1; + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_defaults(string& error_str) +{ + int num; + + vector attr; + VectorAttribute* vatt = 0; + + num = user_obj_template->remove("NIC_DEFAULT", attr); + + if ( num == 0 ) + { + return 0; + } + + if ( num > 1 ) + { + error_str = "Only one NIC_DEFAULT attribute can be defined."; + goto error_cleanup; + } + + vatt = dynamic_cast(attr[0]); + + if ( vatt == 0 ) + { + error_str = "Wrong format for NIC_DEFAULT attribute."; + goto error_cleanup; + } + + for (int i=0; i < NUM_NO_NIC_DEFAULTS; i++) + { + if(vatt->vector_value(NO_NIC_DEFAULTS[i]) != "") + { + ostringstream oss; + oss << "Attribute " << NO_NIC_DEFAULTS[i] + << " is not allowed inside NIC_DEFAULT."; + + error_str = oss.str(); + + return -1; + } + } + + obj_template->set(vatt); + + return 0; + +error_cleanup: + + for (int i = 0; i < num ; i++) + { + delete attr[i]; + } + + return -1; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_vrouter(string& error_str) +{ + string st; + + for (int i = 0; i < NUM_VROUTER_ATTRIBUTES; i++) + { + user_obj_template->get(VROUTER_ATTRIBUTES[i], st); + + if (!st.empty()) + { + obj_template->replace(VROUTER_ATTRIBUTES[i], st); + } + + user_obj_template->erase(VROUTER_ATTRIBUTES[i]); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +static int check_pci_attributes(VectorAttribute * pci, const string& default_bus, + string& error_str) +{ + static string attrs[] = {"VENDOR", "DEVICE", "CLASS"}; + static int num_attrs = 3; + + string bus; + bool found = false; + + for (int i = 0; i < num_attrs; i++) + { + unsigned int val; + int rc = HostSharePCI::get_pci_value(attrs[i].c_str(), pci, val); + + if (rc == -1) + { + error_str = "Wrong Hex value for PCI attribute " + attrs[i]; + return -1; + } + else if ( rc != 0 ) + { + found = true; + } + } + + if (!found) + { + error_str = "DEVICE, VENDOR or CLASS must be defined for PCI."; + return -1; + } + + if ( HostSharePCI::set_pci_address(pci, default_bus) != 0 ) + { + error_str = "Wrong BUS in PCI attribute"; + return -1; + } + + return 0; +} + +int VirtualMachine::parse_pci(string& error_str) +{ + vector array_pci; + vector::iterator it; + + int pci_id = 0; + + user_obj_template->remove("PCI", array_pci); + + for (it = array_pci.begin(); it !=array_pci.end(); ++it, ++pci_id) + { + (*it)->replace("PCI_ID", pci_id); + + obj_template->set(*it); + } + + Nebula& nd = Nebula::instance(); + string default_bus; + + nd.get_configuration_attribute("PCI_PASSTHROUGH_BUS", default_bus); + + for (it = array_pci.begin(); it !=array_pci.end(); ++it) + { + if ( check_pci_attributes(*it, default_bus, error_str) != 0 ) + { + return -1; + } + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_graphics(string& error_str) +{ + VectorAttribute * user_graphics = user_obj_template->get("GRAPHICS"); + + if ( user_graphics == 0 ) + { + return 0; + } + + VectorAttribute * graphics = new VectorAttribute(user_graphics); + + user_obj_template->erase("GRAPHICS"); + + obj_template->set(graphics); + + if ( !graphics->vector_value("PORT").empty() ) + { + unsigned int port; + + int rc = graphics->vector_value("PORT", port); + + if (rc == -1 || port > 65535 ) + { + error_str = "Wrong PORT number in GRAPHICS attribute"; + return -1; + } + } + + string random_passwd = graphics->vector_value("RANDOM_PASSWD"); + + if ( !random_passwd.empty() ) + { + graphics->replace("PASSWD", one_util::random_password()); + } + + return 0; +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_requirements(string& error_str) +{ + int rc, num; + + vector array_reqs; + SingleAttribute * reqs; + + string parsed; + + num = user_obj_template->remove("SCHED_REQUIREMENTS", array_reqs); + + if ( num == 0 ) // Compatibility with old REQUIREMENTS attribute + { + num = user_obj_template->remove("REQUIREMENTS", array_reqs); + } + else + { + user_obj_template->erase("REQUIREMENTS"); + } + + if ( num == 0 ) + { + return 0; + } + else if ( num > 1 ) + { + error_str = "Only one SCHED_REQUIREMENTS attribute can be defined."; + goto error_cleanup; + } + + reqs = dynamic_cast(array_reqs[0]); + + if ( reqs == 0 ) + { + error_str = "Wrong format for SCHED_REQUIREMENTS attribute."; + goto error_cleanup; + } + + rc = parse_template_attribute(reqs->value(), parsed, error_str); + + if ( rc == 0 ) + { + SingleAttribute * reqs_parsed; + + reqs_parsed = new SingleAttribute("SCHED_REQUIREMENTS",parsed); + user_obj_template->set(reqs_parsed); + } + + /* --- Delete old requirements attribute --- */ + + delete array_reqs[0]; + + return rc; + +error_cleanup: + for (int i = 0; i < num ; i++) + { + delete array_reqs[i]; + } + + return -1; +} + +/* ------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------ */ + +void VirtualMachine::parse_well_known_attributes() +{ + /* + * List of meaningful attributes, used in other places and expected in + * obj_template: + * + * DISK + * NIC + * VCPU + * MEMORY + * CPU + * CONTEXT + * OS + * GRAPHICS + * + * INPUT + * FEATURES + * RAW + * CLONING_TEMPLATE_ID + */ + + vector v_attr; + vector::iterator it; + + string names[] = {"INPUT", "FEATURES", "RAW", "CLONING_TEMPLATE_ID"}; + + for (int i=0; i<4; i++) + { + v_attr.clear(); + + user_obj_template->remove(names[i], v_attr); + + for (it=v_attr.begin(); it != v_attr.end(); it++) + { + obj_template->set(*it); + } + } +} + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* VirtualMachine Lex & YACC parser functions */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ + +extern "C" +{ + typedef struct yy_buffer_state * YY_BUFFER_STATE; + + int vm_var_parse (VirtualMachine * vm, + ostringstream * parsed, + char ** errmsg); + + int vm_file_var_parse (VirtualMachine * vm, + vector * img_ids, + char ** errmsg); + + int vm_var_lex_destroy(); + + YY_BUFFER_STATE vm_var__scan_string(const char * str); + + void vm_var__delete_buffer(YY_BUFFER_STATE); +} + +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_template_attribute(const string& attribute, + string& parsed, + string& error_str) +{ + YY_BUFFER_STATE str_buffer = 0; + const char * str; + int rc; + ostringstream oss_parsed; + char * error_msg = 0; + + pthread_mutex_lock(&lex_mutex); + + str = attribute.c_str(); + str_buffer = vm_var__scan_string(str); + + if (str_buffer == 0) + { + goto error_yy; + } + + rc = vm_var_parse(this, &oss_parsed, &error_msg); + + vm_var__delete_buffer(str_buffer); + + vm_var_lex_destroy(); + + pthread_mutex_unlock(&lex_mutex); + + if ( rc != 0 && error_msg != 0 ) + { + ostringstream oss; + + oss << "Error parsing: " << attribute << ". " << error_msg; + log("VM", Log::ERROR, oss); + + error_str = oss.str(); + + free(error_msg); + } + + parsed = oss_parsed.str(); + + return rc; + +error_yy: + log("VM",Log::ERROR,"Error setting scan buffer"); + pthread_mutex_unlock(&lex_mutex); + return -1; +} + +/* -------------------------------------------------------------------------- */ + +int VirtualMachine::parse_file_attribute(string attribute, + vector& img_ids, + string& error) +{ + YY_BUFFER_STATE str_buffer = 0; + const char * str; + int rc; + ostringstream oss_parsed; + char * error_msg = 0; + + size_t non_blank_pos; + + //Removes leading blanks from attribute, these are not managed + //by the parser as it is common to the other VM varibales + non_blank_pos = attribute.find_first_not_of(" \t\n\v\f\r"); + + if ( non_blank_pos != string::npos ) + { + attribute.erase(0, non_blank_pos); + } + + pthread_mutex_lock(&lex_mutex); + + str = attribute.c_str(); + str_buffer = vm_var__scan_string(str); + + if (str_buffer == 0) + { + goto error_yy; + } + + rc = vm_file_var_parse(this, &img_ids, &error_msg); + + vm_var__delete_buffer(str_buffer); + + vm_var_lex_destroy(); + + pthread_mutex_unlock(&lex_mutex); + + if ( rc != 0 ) + { + ostringstream oss; + + if ( error_msg != 0 ) + { + oss << "Error parsing: " << attribute << ". " << error_msg; + free(error_msg); + } + else + { + oss << "Unknown error parsing: " << attribute << "."; + } + + error = oss.str(); + } + + return rc; + +error_yy: + log("VM",Log::ERROR,"Error setting scan buffer"); + pthread_mutex_unlock(&lex_mutex); + return -1; +}