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

Reply via email to