Signed-off-by: Agata Murawska <[email protected]> --- lib/ovf.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 148 insertions(+), 1 deletions(-)
diff --git a/lib/ovf.py b/lib/ovf.py index a340b50..47521d3 100644 --- a/lib/ovf.py +++ b/lib/ovf.py @@ -53,6 +53,8 @@ from ganeti.utils import io GANETI_SCHEMA = "http://ganeti" OVF_SCHEMA = "http://schemas.dmtf.org/ovf/envelope/1" +RASD_SCHEMA = ("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/" + "CIM_ResourceAllocationSettingData") class OVFReader: @@ -151,6 +153,27 @@ class OVFReader: return elem return None + def _GetElementMatchingText(self, path, match_text): + """Searches for element on a path that matches certain text value. + + Function follows the path from root node to the desired tags using path, + then searches for the first one matching the text value. + + @type path: string + @param path: path of nodes to visit + @type match_attr: tuple + @param match_text: pair (node, text) for which we search + @rtype: L{ElementTree} or None + @return: first element matching match_text or None if nothing matches + + """ + potential_elements = self.tree.findall(path) + (node, text) = match_text + for elem in potential_elements: + if elem.findtext(node) == text: + return elem + return None + def VerifyManifest(self): """Verifies manifest for the OVF package, if one is given. @@ -205,6 +228,69 @@ class OVFReader: (OVF_SCHEMA, OVF_SCHEMA) return self.tree.findtext(find_template) + def GetNetworkData(self): + """Provides data about the network in the OVF instance. + + The method gathers the data about networks used by OVF instance. It assumes + that 'name' tag means something - in essence, if it contains one of the + words 'bridged' or 'routed' then that will be the mode of this network in + Ganeti. The information about the network can be either in GanetiSection or + VirtualHardwareSection. + + @rtype: list + @return: list of dictionaries, one for each network + + """ + results = {} + networks_search = ("{%s}NetworkSection/{%s}Network" % + (OVF_SCHEMA, OVF_SCHEMA)) + network_names = self._GetAttributes(networks_search, + "{%s}name" % OVF_SCHEMA) + required = ["ip", "mac", "link", "mode"] + for (counter, network_name) in enumerate(network_names): + network_search = ("{%s}VirtualSystem/{%s}VirtualHardwareSection/{%s}Item" + % (OVF_SCHEMA, OVF_SCHEMA, OVF_SCHEMA)) + ganeti_search = ("{%s}GanetiSection/{%s}Network/{%s}Nic" % + (GANETI_SCHEMA, GANETI_SCHEMA, GANETI_SCHEMA)) + network_match = ("{%s}Connection" % RASD_SCHEMA, network_name) + ganeti_match = ("{%s}name" % OVF_SCHEMA, network_name) + network_data = self._GetElementMatchingText(network_search, network_match) + network_ganeti_data = self._GetElementMatchingAttr(ganeti_search, + ganeti_match) + + ganeti_data = {} + if network_ganeti_data: + ganeti_data["mode"] = network_ganeti_data.findtext("{%s}Mode" % + GANETI_SCHEMA) + ganeti_data["mac"] = network_ganeti_data.findtext("{%s}MACAddress" % + GANETI_SCHEMA) + ganeti_data["ip"] = network_ganeti_data.findtext("{%s}IPAddress" % + GANETI_SCHEMA) + ganeti_data["link"] = network_ganeti_data.findtext("{%s}Link" % + GANETI_SCHEMA) + data = {} + if network_data: + data["mac"] = network_data.findtext("{%s}Address" % RASD_SCHEMA) + network_name = network_name.lower() + + if constants.NIC_MODE_BRIDGED in network_name: + results["nic%s_mode" % str(counter)] = "bridged" + elif constants.NIC_MODE_ROUTED in network_name: + results["nic%s_mode" % str(counter)] = "routed" + if data.get("mac"): + results["nic%s_mac" % str(counter)] = data["mac"] + for name, value in ganeti_data.iteritems(): + results["nic%s_%s" % (str(counter), name)] = value + if results.get("nic%s_mode" % str(counter)) == "bridged": + if not results.get("nic%s_ip" % str(counter)): + results["nic%s_ip" % str(counter)] = "None" + for option in required: + if not results.get("nic%s_%s" % (str(counter), option)): + results["nic%s_%s" % (str(counter), option)] = "auto" + if network_names: + results["nic_count"] = str(len(network_names)) + return results + def GetDisksNames(self): """Provides list of file names for the disks used by the instance. @@ -298,9 +384,14 @@ class OVFImporter(Converter): @ivar input_path: complete path to the .ovf file @type ovf_reader: L{OVFReader} @ivar ovf_reader: OVF reader instance collects data from .ovf file + @type results_name: string + @ivar results_name: name of imported instance @type results_template: string @ivar results_template: disk template read from .ovf file or command line arguments + @type results_network: dict + @ivar results_network: network information gathered from .ovf file or command + line arguments @type results_disk: dict @ivar results_disk: disk information gathered from .ovf file or command line arguments @@ -405,10 +496,19 @@ class OVFImporter(Converter): if not self.results_template: logging.info("Disk template not given") + self.results_network = self._GetInfo("network", self.options.nics, + self._ParseNicOptions, self.ovf_reader.GetNetworkData, + ignore_test = self.options.no_nics) + self.results_disk = self._GetInfo("disk", self.options.disks, self._ParseDiskOptions, self._GetDiskInfo, ignore_test = self.results_template == constants.DT_DISKLESS) + if ((not self.results_disk or self.results_disk["disk_count"] == 0) and + (not self.results_network or self.results_network["nic_count"] == 0)): + raise errors.OpPrereqError("Either disk specification or network" + " description must be present") + def _GetInfo(self, name, cmd_arg, cmd_function, nocmd_function, ignore_test = False): """Get information about some section - e.g. disk, network, hypervisor. @@ -453,6 +553,26 @@ class OVFImporter(Converter): """ return self.options.disk_template + def _ParseNicOptions(self): + """Parses network options given in a command line or as a dictionary. + + @rtype: dict + @return: dictionary of network-related options + + """ + assert self.options.nics + results = {} + for (nic_id, nic_desc) in self.options.nics: + results["nic%s_mode" % nic_id] = nic_desc.get("mode", "auto") + results["nic%s_mac" % nic_id] = nic_desc.get("mac", "auto") + results["nic%s_link" % nic_id] = nic_desc.get("link", "auto") + if nic_desc.get("mode") == "bridged": + results["nic%s_ip" % nic_id] = "None" + else: + results["nic%s_ip" % nic_id] = "auto" + results["nic_count"] = str(len(self.options.nics)) + return results + def _ParseDiskOptions(self): """Parses disk options given in a command line. @@ -564,7 +684,34 @@ class OVFImporter(Converter): return results def Save(self): - pass + """Saves all the gathered information in a constant.EXPORT_CONF_FILE file. + + """ + logging.info("Conversion from .ovf succesfull, saving %s in %s directory", + constants.EXPORT_CONF_FILE, self.output_dir) + results = {} + results[constants.INISECT_INS] = {} + results[constants.INISECT_BEP] = {} + results[constants.INISECT_EXP] = {} + results[constants.INISECT_OSP] = {} + results[constants.INISECT_HYP] = {} + + results[constants.INISECT_INS].update(self.results_disk) + results[constants.INISECT_INS].update(self.results_network) + results[constants.INISECT_INS]["name"] = self.results_name + + results[constants.INISECT_EXP]["version"] = constants.EXPORT_VERSION + + output_file_name = "%s/%s" % (self.output_dir, constants.EXPORT_CONF_FILE) + output_file = open(output_file_name, "w") + for section, options in results.iteritems(): + output_file.write("[%s]\n" % section) + for name, value in options.iteritems(): + output_file.write("%s = %s\n" % (name, value)) + output_file.write("\n") + output_file.close() + + self.Cleanup() def Cleanup(self): """Cleanes the temporary directory, if one was created. -- 1.7.3.1
