Statistics
| Branch: | Tag: | Revision:

root / lib / ovf.py @ 72bb6b4e

History | View | Annotate | Download (61 kB)

1 ced78a66 Agata Murawska
#!/usr/bin/python
2 ced78a66 Agata Murawska
#
3 ced78a66 Agata Murawska
4 ced78a66 Agata Murawska
# Copyright (C) 2011 Google Inc.
5 ced78a66 Agata Murawska
#
6 ced78a66 Agata Murawska
# This program is free software; you can redistribute it and/or modify
7 ced78a66 Agata Murawska
# it under the terms of the GNU General Public License as published by
8 ced78a66 Agata Murawska
# the Free Software Foundation; either version 2 of the License, or
9 ced78a66 Agata Murawska
# (at your option) any later version.
10 ced78a66 Agata Murawska
#
11 ced78a66 Agata Murawska
# This program is distributed in the hope that it will be useful, but
12 ced78a66 Agata Murawska
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 ced78a66 Agata Murawska
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ced78a66 Agata Murawska
# General Public License for more details.
15 ced78a66 Agata Murawska
#
16 ced78a66 Agata Murawska
# You should have received a copy of the GNU General Public License
17 ced78a66 Agata Murawska
# along with this program; if not, write to the Free Software
18 ced78a66 Agata Murawska
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 ced78a66 Agata Murawska
# 02110-1301, USA.
20 ced78a66 Agata Murawska
21 ced78a66 Agata Murawska
22 ced78a66 Agata Murawska
"""Converter tools between ovf and ganeti config file
23 ced78a66 Agata Murawska

24 ced78a66 Agata Murawska
"""
25 ced78a66 Agata Murawska
26 864cf6bf Agata Murawska
# pylint: disable=F0401, E1101
27 864cf6bf Agata Murawska
28 864cf6bf Agata Murawska
# F0401 because ElementTree is not default for python 2.4
29 864cf6bf Agata Murawska
# E1101 makes no sense - pylint assumes that ElementTree object is a tuple
30 864cf6bf Agata Murawska
31 864cf6bf Agata Murawska
32 0963b26a Agata Murawska
import ConfigParser
33 99381e3b Agata Murawska
import errno
34 864cf6bf Agata Murawska
import logging
35 99381e3b Agata Murawska
import os
36 ced78a66 Agata Murawska
import os.path
37 864cf6bf Agata Murawska
import re
38 ced78a66 Agata Murawska
import shutil
39 864cf6bf Agata Murawska
import tarfile
40 864cf6bf Agata Murawska
import tempfile
41 0963b26a Agata Murawska
import xml.dom.minidom
42 864cf6bf Agata Murawska
import xml.parsers.expat
43 864cf6bf Agata Murawska
try:
44 864cf6bf Agata Murawska
  import xml.etree.ElementTree as ET
45 864cf6bf Agata Murawska
except ImportError:
46 864cf6bf Agata Murawska
  import elementtree.ElementTree as ET
47 ced78a66 Agata Murawska
48 864cf6bf Agata Murawska
from ganeti import constants
49 ced78a66 Agata Murawska
from ganeti import errors
50 ced78a66 Agata Murawska
from ganeti import utils
51 ced78a66 Agata Murawska
52 ced78a66 Agata Murawska
53 864cf6bf Agata Murawska
# Schemas used in OVF format
54 864cf6bf Agata Murawska
GANETI_SCHEMA = "http://ganeti"
55 864cf6bf Agata Murawska
OVF_SCHEMA = "http://schemas.dmtf.org/ovf/envelope/1"
56 864cf6bf Agata Murawska
RASD_SCHEMA = ("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/"
57 864cf6bf Agata Murawska
               "CIM_ResourceAllocationSettingData")
58 0963b26a Agata Murawska
VSSD_SCHEMA = ("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/"
59 0963b26a Agata Murawska
               "CIM_VirtualSystemSettingData")
60 0963b26a Agata Murawska
XML_SCHEMA = "http://www.w3.org/2001/XMLSchema-instance"
61 864cf6bf Agata Murawska
62 864cf6bf Agata Murawska
# File extensions in OVF package
63 864cf6bf Agata Murawska
OVA_EXT = ".ova"
64 864cf6bf Agata Murawska
OVF_EXT = ".ovf"
65 864cf6bf Agata Murawska
MF_EXT = ".mf"
66 864cf6bf Agata Murawska
CERT_EXT = ".cert"
67 99381e3b Agata Murawska
COMPRESSION_EXT = ".gz"
68 864cf6bf Agata Murawska
FILE_EXTENSIONS = [
69 864cf6bf Agata Murawska
  OVF_EXT,
70 864cf6bf Agata Murawska
  MF_EXT,
71 864cf6bf Agata Murawska
  CERT_EXT,
72 864cf6bf Agata Murawska
]
73 864cf6bf Agata Murawska
74 99381e3b Agata Murawska
COMPRESSION_TYPE = "gzip"
75 99381e3b Agata Murawska
COMPRESS = "compression"
76 99381e3b Agata Murawska
DECOMPRESS = "decompression"
77 99381e3b Agata Murawska
ALLOWED_ACTIONS = [COMPRESS, DECOMPRESS]
78 99381e3b Agata Murawska
79 7bde29b5 Agata Murawska
# ResourceType values
80 7bde29b5 Agata Murawska
RASD_TYPE = {
81 7bde29b5 Agata Murawska
  "vcpus": "3",
82 7bde29b5 Agata Murawska
  "memory": "4",
83 7432d332 Agata Murawska
  "scsi-controller": "6",
84 7432d332 Agata Murawska
  "ethernet-adapter": "10",
85 7432d332 Agata Murawska
  "disk": "17",
86 7bde29b5 Agata Murawska
}
87 7bde29b5 Agata Murawska
88 7bde29b5 Agata Murawska
# AllocationUnits values and conversion
89 7bde29b5 Agata Murawska
ALLOCATION_UNITS = {
90 7bde29b5 Agata Murawska
  'b': ["bytes", "b"],
91 7bde29b5 Agata Murawska
  'kb': ["kilobytes", "kb", "byte * 2^10", "kibibytes", "kib"],
92 7bde29b5 Agata Murawska
  'mb': ["megabytes", "mb", "byte * 2^20", "mebibytes", "mib"],
93 7bde29b5 Agata Murawska
  'gb': ["gigabytes", "gb", "byte * 2^30", "gibibytes", "gib"],
94 7bde29b5 Agata Murawska
}
95 7bde29b5 Agata Murawska
CONVERT_UNITS_TO_MB = {
96 7bde29b5 Agata Murawska
  'b': lambda x: x / (1024 * 1024),
97 7bde29b5 Agata Murawska
  'kb': lambda x: x / 1024,
98 7bde29b5 Agata Murawska
  'mb': lambda x: x,
99 7bde29b5 Agata Murawska
  'gb': lambda x: x * 1024,
100 7bde29b5 Agata Murawska
}
101 7bde29b5 Agata Murawska
102 0963b26a Agata Murawska
# Names of the config fields
103 0963b26a Agata Murawska
NAME = "name"
104 b179ce72 Agata Murawska
OS = "os"
105 b179ce72 Agata Murawska
HYPERV = "hypervisor"
106 b179ce72 Agata Murawska
VCPUS = "vcpus"
107 b179ce72 Agata Murawska
MEMORY = "memory"
108 b179ce72 Agata Murawska
AUTO_BALANCE = "auto_balance"
109 b179ce72 Agata Murawska
DISK_TEMPLATE = "disk_template"
110 b179ce72 Agata Murawska
TAGS = "tags"
111 b179ce72 Agata Murawska
VERSION = "version"
112 0963b26a Agata Murawska
113 7432d332 Agata Murawska
# Instance IDs of System and SCSI controller
114 7432d332 Agata Murawska
SYSTEM_ID = 0
115 7432d332 Agata Murawska
SCSI_ID = 3
116 7432d332 Agata Murawska
117 7432d332 Agata Murawska
# Disk format descriptions
118 7432d332 Agata Murawska
DISK_FORMAT = {
119 7432d332 Agata Murawska
  "raw": "http://en.wikipedia.org/wiki/Byte",
120 7432d332 Agata Murawska
  "vmdk": "http://www.vmware.com/interfaces/specifications/vmdk.html"
121 7432d332 Agata Murawska
          "#monolithicSparse",
122 7432d332 Agata Murawska
  "cow": "http://www.gnome.org/~markmc/qcow-image-format.html",
123 7432d332 Agata Murawska
}
124 7432d332 Agata Murawska
125 99381e3b Agata Murawska
126 99381e3b Agata Murawska
def LinkFile(old_path, prefix=None, suffix=None, directory=None):
127 99381e3b Agata Murawska
  """Create link with a given prefix and suffix.
128 99381e3b Agata Murawska

129 99381e3b Agata Murawska
  This is a wrapper over os.link. It tries to create a hard link for given file,
130 99381e3b Agata Murawska
  but instead of rising error when file exists, the function changes the name
131 99381e3b Agata Murawska
  a little bit.
132 99381e3b Agata Murawska

133 99381e3b Agata Murawska
  @type old_path:string
134 99381e3b Agata Murawska
  @param old_path: path to the file that is to be linked
135 99381e3b Agata Murawska
  @type prefix: string
136 99381e3b Agata Murawska
  @param prefix: prefix of filename for the link
137 99381e3b Agata Murawska
  @type suffix: string
138 99381e3b Agata Murawska
  @param suffix: suffix of the filename for the link
139 99381e3b Agata Murawska
  @type directory: string
140 99381e3b Agata Murawska
  @param directory: directory of the link
141 99381e3b Agata Murawska

142 99381e3b Agata Murawska
  @raise errors.OpPrereqError: when error on linking is different than
143 99381e3b Agata Murawska
    "File exists"
144 99381e3b Agata Murawska

145 99381e3b Agata Murawska
  """
146 99381e3b Agata Murawska
  assert(prefix is not None or suffix is not None)
147 99381e3b Agata Murawska
  if directory is None:
148 99381e3b Agata Murawska
    directory = os.getcwd()
149 99381e3b Agata Murawska
  new_path = utils.PathJoin(directory, "%s%s" % (prefix, suffix))
150 99381e3b Agata Murawska
  counter = 1
151 99381e3b Agata Murawska
  while True:
152 99381e3b Agata Murawska
    try:
153 99381e3b Agata Murawska
      os.link(old_path, new_path)
154 99381e3b Agata Murawska
      break
155 99381e3b Agata Murawska
    except OSError, err:
156 99381e3b Agata Murawska
      if err.errno == errno.EEXIST:
157 99381e3b Agata Murawska
        new_path = utils.PathJoin(directory,
158 99381e3b Agata Murawska
          "%s_%s%s" % (prefix, counter, suffix))
159 99381e3b Agata Murawska
        counter += 1
160 99381e3b Agata Murawska
      else:
161 99381e3b Agata Murawska
        raise errors.OpPrereqError("Error moving the file %s to %s location:"
162 99381e3b Agata Murawska
                                   " %s" % (old_path, new_path, err))
163 99381e3b Agata Murawska
  return new_path
164 99381e3b Agata Murawska
165 864cf6bf Agata Murawska
166 864cf6bf Agata Murawska
class OVFReader(object):
167 864cf6bf Agata Murawska
  """Reader class for OVF files.
168 864cf6bf Agata Murawska

169 864cf6bf Agata Murawska
  @type files_list: list
170 864cf6bf Agata Murawska
  @ivar files_list: list of files in the OVF package
171 864cf6bf Agata Murawska
  @type tree: ET.ElementTree
172 864cf6bf Agata Murawska
  @ivar tree: XML tree of the .ovf file
173 864cf6bf Agata Murawska
  @type schema_name: string
174 864cf6bf Agata Murawska
  @ivar schema_name: name of the .ovf file
175 864cf6bf Agata Murawska
  @type input_dir: string
176 864cf6bf Agata Murawska
  @ivar input_dir: directory in which the .ovf file resides
177 864cf6bf Agata Murawska

178 864cf6bf Agata Murawska
  """
179 864cf6bf Agata Murawska
  def __init__(self, input_path):
180 864cf6bf Agata Murawska
    """Initialiaze the reader - load the .ovf file to XML parser.
181 864cf6bf Agata Murawska

182 864cf6bf Agata Murawska
    It is assumed that names of manifesto (.mf), certificate (.cert) and ovf
183 864cf6bf Agata Murawska
    files are the same. In order to account any other files as part of the ovf
184 864cf6bf Agata Murawska
    package, they have to be explicitly mentioned in the Resources section
185 864cf6bf Agata Murawska
    of the .ovf file.
186 864cf6bf Agata Murawska

187 864cf6bf Agata Murawska
    @type input_path: string
188 864cf6bf Agata Murawska
    @param input_path: absolute path to the .ovf file
189 864cf6bf Agata Murawska

190 864cf6bf Agata Murawska
    @raise errors.OpPrereqError: when .ovf file is not a proper XML file or some
191 864cf6bf Agata Murawska
      of the files mentioned in Resources section do not exist
192 864cf6bf Agata Murawska

193 864cf6bf Agata Murawska
    """
194 864cf6bf Agata Murawska
    self.tree = ET.ElementTree()
195 864cf6bf Agata Murawska
    try:
196 864cf6bf Agata Murawska
      self.tree.parse(input_path)
197 864cf6bf Agata Murawska
    except xml.parsers.expat.ExpatError, err:
198 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Error while reading %s file: %s" %
199 864cf6bf Agata Murawska
                                 (OVF_EXT, err))
200 864cf6bf Agata Murawska
201 864cf6bf Agata Murawska
    # Create a list of all files in the OVF package
202 864cf6bf Agata Murawska
    (input_dir, input_file) = os.path.split(input_path)
203 864cf6bf Agata Murawska
    (input_name, _) = os.path.splitext(input_file)
204 864cf6bf Agata Murawska
    files_directory = utils.ListVisibleFiles(input_dir)
205 864cf6bf Agata Murawska
    files_list = []
206 864cf6bf Agata Murawska
    for file_name in files_directory:
207 864cf6bf Agata Murawska
      (name, extension) = os.path.splitext(file_name)
208 864cf6bf Agata Murawska
      if extension in FILE_EXTENSIONS and name == input_name:
209 864cf6bf Agata Murawska
        files_list.append(file_name)
210 864cf6bf Agata Murawska
    files_list += self._GetAttributes("{%s}References/{%s}File" %
211 864cf6bf Agata Murawska
                                      (OVF_SCHEMA, OVF_SCHEMA),
212 864cf6bf Agata Murawska
                                      "{%s}href" % OVF_SCHEMA)
213 864cf6bf Agata Murawska
    for file_name in files_list:
214 864cf6bf Agata Murawska
      file_path = utils.PathJoin(input_dir, file_name)
215 864cf6bf Agata Murawska
      if not os.path.exists(file_path):
216 864cf6bf Agata Murawska
        raise errors.OpPrereqError("File does not exist: %s" % file_path)
217 864cf6bf Agata Murawska
    logging.info("Files in the OVF package: %s", " ".join(files_list))
218 864cf6bf Agata Murawska
    self.files_list = files_list
219 864cf6bf Agata Murawska
    self.input_dir = input_dir
220 864cf6bf Agata Murawska
    self.schema_name = input_name
221 864cf6bf Agata Murawska
222 864cf6bf Agata Murawska
  def _GetAttributes(self, path, attribute):
223 864cf6bf Agata Murawska
    """Get specified attribute from all nodes accessible using given path.
224 864cf6bf Agata Murawska

225 864cf6bf Agata Murawska
    Function follows the path from root node to the desired tags using path,
226 864cf6bf Agata Murawska
    then reads the apropriate attribute values.
227 864cf6bf Agata Murawska

228 864cf6bf Agata Murawska
    @type path: string
229 864cf6bf Agata Murawska
    @param path: path of nodes to visit
230 864cf6bf Agata Murawska
    @type attribute: string
231 864cf6bf Agata Murawska
    @param attribute: attribute for which we gather the information
232 864cf6bf Agata Murawska
    @rtype: list
233 864cf6bf Agata Murawska
    @return: for each accessible tag with the attribute value set, value of the
234 864cf6bf Agata Murawska
      attribute
235 864cf6bf Agata Murawska

236 864cf6bf Agata Murawska
    """
237 864cf6bf Agata Murawska
    current_list = self.tree.findall(path)
238 864cf6bf Agata Murawska
    results = [x.get(attribute) for x in current_list]
239 864cf6bf Agata Murawska
    return filter(None, results)
240 864cf6bf Agata Murawska
241 99381e3b Agata Murawska
  def _GetElementMatchingAttr(self, path, match_attr):
242 99381e3b Agata Murawska
    """Searches for element on a path that matches certain attribute value.
243 99381e3b Agata Murawska

244 99381e3b Agata Murawska
    Function follows the path from root node to the desired tags using path,
245 99381e3b Agata Murawska
    then searches for the first one matching the attribute value.
246 99381e3b Agata Murawska

247 99381e3b Agata Murawska
    @type path: string
248 99381e3b Agata Murawska
    @param path: path of nodes to visit
249 99381e3b Agata Murawska
    @type match_attr: tuple
250 99381e3b Agata Murawska
    @param match_attr: pair (attribute, value) for which we search
251 99381e3b Agata Murawska
    @rtype: ET.ElementTree or None
252 99381e3b Agata Murawska
    @return: first element matching match_attr or None if nothing matches
253 99381e3b Agata Murawska

254 99381e3b Agata Murawska
    """
255 99381e3b Agata Murawska
    potential_elements = self.tree.findall(path)
256 99381e3b Agata Murawska
    (attr, val) = match_attr
257 99381e3b Agata Murawska
    for elem in potential_elements:
258 99381e3b Agata Murawska
      if elem.get(attr) == val:
259 99381e3b Agata Murawska
        return elem
260 99381e3b Agata Murawska
    return None
261 99381e3b Agata Murawska
262 24b9469d Agata Murawska
  def _GetElementMatchingText(self, path, match_text):
263 24b9469d Agata Murawska
    """Searches for element on a path that matches certain text value.
264 24b9469d Agata Murawska

265 24b9469d Agata Murawska
    Function follows the path from root node to the desired tags using path,
266 24b9469d Agata Murawska
    then searches for the first one matching the text value.
267 24b9469d Agata Murawska

268 24b9469d Agata Murawska
    @type path: string
269 24b9469d Agata Murawska
    @param path: path of nodes to visit
270 24b9469d Agata Murawska
    @type match_text: tuple
271 24b9469d Agata Murawska
    @param match_text: pair (node, text) for which we search
272 24b9469d Agata Murawska
    @rtype: ET.ElementTree or None
273 24b9469d Agata Murawska
    @return: first element matching match_text or None if nothing matches
274 24b9469d Agata Murawska

275 24b9469d Agata Murawska
    """
276 24b9469d Agata Murawska
    potential_elements = self.tree.findall(path)
277 24b9469d Agata Murawska
    (node, text) = match_text
278 24b9469d Agata Murawska
    for elem in potential_elements:
279 24b9469d Agata Murawska
      if elem.findtext(node) == text:
280 24b9469d Agata Murawska
        return elem
281 24b9469d Agata Murawska
    return None
282 24b9469d Agata Murawska
283 99381e3b Agata Murawska
  @staticmethod
284 99381e3b Agata Murawska
  def _GetDictParameters(root, schema):
285 99381e3b Agata Murawska
    """Reads text in all children and creates the dictionary from the contents.
286 99381e3b Agata Murawska

287 99381e3b Agata Murawska
    @type root: ET.ElementTree or None
288 99381e3b Agata Murawska
    @param root: father of the nodes we want to collect data about
289 99381e3b Agata Murawska
    @type schema: string
290 99381e3b Agata Murawska
    @param schema: schema name to be removed from the tag
291 99381e3b Agata Murawska
    @rtype: dict
292 99381e3b Agata Murawska
    @return: dictionary containing tags and their text contents, tags have their
293 99381e3b Agata Murawska
      schema fragment removed or empty dictionary, when root is None
294 99381e3b Agata Murawska

295 99381e3b Agata Murawska
    """
296 99381e3b Agata Murawska
    if not root:
297 99381e3b Agata Murawska
      return {}
298 99381e3b Agata Murawska
    results = {}
299 99381e3b Agata Murawska
    for element in list(root):
300 99381e3b Agata Murawska
      pref_len = len("{%s}" % schema)
301 99381e3b Agata Murawska
      assert(schema in element.tag)
302 99381e3b Agata Murawska
      tag = element.tag[pref_len:]
303 99381e3b Agata Murawska
      results[tag] = element.text
304 99381e3b Agata Murawska
    return results
305 99381e3b Agata Murawska
306 864cf6bf Agata Murawska
  def VerifyManifest(self):
307 864cf6bf Agata Murawska
    """Verifies manifest for the OVF package, if one is given.
308 864cf6bf Agata Murawska

309 864cf6bf Agata Murawska
    @raise errors.OpPrereqError: if SHA1 checksums do not match
310 864cf6bf Agata Murawska

311 864cf6bf Agata Murawska
    """
312 864cf6bf Agata Murawska
    if "%s%s" % (self.schema_name, MF_EXT) in self.files_list:
313 864cf6bf Agata Murawska
      logging.warning("Verifying SHA1 checksums, this may take a while")
314 864cf6bf Agata Murawska
      manifest_filename = "%s%s" % (self.schema_name, MF_EXT)
315 864cf6bf Agata Murawska
      manifest_path = utils.PathJoin(self.input_dir, manifest_filename)
316 864cf6bf Agata Murawska
      manifest_content = utils.ReadFile(manifest_path).splitlines()
317 864cf6bf Agata Murawska
      manifest_files = {}
318 864cf6bf Agata Murawska
      regexp = r"SHA1\((\S+)\)= (\S+)"
319 864cf6bf Agata Murawska
      for line in manifest_content:
320 864cf6bf Agata Murawska
        match = re.match(regexp, line)
321 864cf6bf Agata Murawska
        if match:
322 864cf6bf Agata Murawska
          file_name = match.group(1)
323 864cf6bf Agata Murawska
          sha1_sum = match.group(2)
324 864cf6bf Agata Murawska
          manifest_files[file_name] = sha1_sum
325 864cf6bf Agata Murawska
      files_with_paths = [utils.PathJoin(self.input_dir, file_name)
326 864cf6bf Agata Murawska
        for file_name in self.files_list]
327 864cf6bf Agata Murawska
      sha1_sums = utils.FingerprintFiles(files_with_paths)
328 864cf6bf Agata Murawska
      for file_name, value in manifest_files.iteritems():
329 864cf6bf Agata Murawska
        if sha1_sums.get(utils.PathJoin(self.input_dir, file_name)) != value:
330 864cf6bf Agata Murawska
          raise errors.OpPrereqError("SHA1 checksum of %s does not match the"
331 864cf6bf Agata Murawska
                                     " value in manifest file" % file_name)
332 864cf6bf Agata Murawska
      logging.info("SHA1 checksums verified")
333 864cf6bf Agata Murawska
334 99381e3b Agata Murawska
  def GetInstanceName(self):
335 99381e3b Agata Murawska
    """Provides information about instance name.
336 99381e3b Agata Murawska

337 99381e3b Agata Murawska
    @rtype: string
338 99381e3b Agata Murawska
    @return: instance name string
339 99381e3b Agata Murawska

340 99381e3b Agata Murawska
    """
341 99381e3b Agata Murawska
    find_name = "{%s}VirtualSystem/{%s}Name" % (OVF_SCHEMA, OVF_SCHEMA)
342 99381e3b Agata Murawska
    return self.tree.findtext(find_name)
343 99381e3b Agata Murawska
344 99381e3b Agata Murawska
  def GetDiskTemplate(self):
345 99381e3b Agata Murawska
    """Returns disk template from .ovf file
346 99381e3b Agata Murawska

347 99381e3b Agata Murawska
    @rtype: string or None
348 99381e3b Agata Murawska
    @return: name of the template
349 99381e3b Agata Murawska
    """
350 99381e3b Agata Murawska
    find_template = ("{%s}GanetiSection/{%s}DiskTemplate" %
351 99381e3b Agata Murawska
                     (GANETI_SCHEMA, GANETI_SCHEMA))
352 99381e3b Agata Murawska
    return self.tree.findtext(find_template)
353 99381e3b Agata Murawska
354 7bde29b5 Agata Murawska
  def GetHypervisorData(self):
355 7bde29b5 Agata Murawska
    """Provides hypervisor information - hypervisor name and options.
356 7bde29b5 Agata Murawska

357 7bde29b5 Agata Murawska
    @rtype: dict
358 7bde29b5 Agata Murawska
    @return: dictionary containing name of the used hypervisor and all the
359 7bde29b5 Agata Murawska
      specified options
360 7bde29b5 Agata Murawska

361 7bde29b5 Agata Murawska
    """
362 7bde29b5 Agata Murawska
    hypervisor_search = ("{%s}GanetiSection/{%s}Hypervisor" %
363 7bde29b5 Agata Murawska
                         (GANETI_SCHEMA, GANETI_SCHEMA))
364 7bde29b5 Agata Murawska
    hypervisor_data = self.tree.find(hypervisor_search)
365 7bde29b5 Agata Murawska
    if not hypervisor_data:
366 7bde29b5 Agata Murawska
      return {"hypervisor_name": constants.VALUE_AUTO}
367 7bde29b5 Agata Murawska
    results = {
368 7bde29b5 Agata Murawska
      "hypervisor_name": hypervisor_data.findtext("{%s}Name" % GANETI_SCHEMA,
369 7bde29b5 Agata Murawska
                           default=constants.VALUE_AUTO),
370 7bde29b5 Agata Murawska
    }
371 7bde29b5 Agata Murawska
    parameters = hypervisor_data.find("{%s}Parameters" % GANETI_SCHEMA)
372 7bde29b5 Agata Murawska
    results.update(self._GetDictParameters(parameters, GANETI_SCHEMA))
373 7bde29b5 Agata Murawska
    return results
374 7bde29b5 Agata Murawska
375 7bde29b5 Agata Murawska
  def GetOSData(self):
376 7bde29b5 Agata Murawska
    """ Provides operating system information - os name and options.
377 7bde29b5 Agata Murawska

378 7bde29b5 Agata Murawska
    @rtype: dict
379 7bde29b5 Agata Murawska
    @return: dictionary containing name and options for the chosen OS
380 7bde29b5 Agata Murawska

381 7bde29b5 Agata Murawska
    """
382 7bde29b5 Agata Murawska
    results = {}
383 7bde29b5 Agata Murawska
    os_search = ("{%s}GanetiSection/{%s}OperatingSystem" %
384 7bde29b5 Agata Murawska
                 (GANETI_SCHEMA, GANETI_SCHEMA))
385 7bde29b5 Agata Murawska
    os_data = self.tree.find(os_search)
386 7bde29b5 Agata Murawska
    if os_data:
387 7bde29b5 Agata Murawska
      results["os_name"] = os_data.findtext("{%s}Name" % GANETI_SCHEMA)
388 7bde29b5 Agata Murawska
      parameters = os_data.find("{%s}Parameters" % GANETI_SCHEMA)
389 7bde29b5 Agata Murawska
      results.update(self._GetDictParameters(parameters, GANETI_SCHEMA))
390 7bde29b5 Agata Murawska
    return results
391 7bde29b5 Agata Murawska
392 7bde29b5 Agata Murawska
  def GetBackendData(self):
393 7bde29b5 Agata Murawska
    """ Provides backend information - vcpus, memory, auto balancing options.
394 7bde29b5 Agata Murawska

395 7bde29b5 Agata Murawska
    @rtype: dict
396 7bde29b5 Agata Murawska
    @return: dictionary containing options for vcpus, memory and auto balance
397 7bde29b5 Agata Murawska
      settings
398 7bde29b5 Agata Murawska

399 7bde29b5 Agata Murawska
    """
400 7bde29b5 Agata Murawska
    results = {}
401 7bde29b5 Agata Murawska
402 7bde29b5 Agata Murawska
    find_vcpus = ("{%s}VirtualSystem/{%s}VirtualHardwareSection/{%s}Item" %
403 7bde29b5 Agata Murawska
                   (OVF_SCHEMA, OVF_SCHEMA, OVF_SCHEMA))
404 7bde29b5 Agata Murawska
    match_vcpus = ("{%s}ResourceType" % RASD_SCHEMA, RASD_TYPE["vcpus"])
405 7bde29b5 Agata Murawska
    vcpus = self._GetElementMatchingText(find_vcpus, match_vcpus)
406 7bde29b5 Agata Murawska
    if vcpus:
407 7bde29b5 Agata Murawska
      vcpus_count = vcpus.findtext("{%s}VirtualQuantity" % RASD_SCHEMA,
408 7bde29b5 Agata Murawska
        default=constants.VALUE_AUTO)
409 7bde29b5 Agata Murawska
    else:
410 7bde29b5 Agata Murawska
      vcpus_count = constants.VALUE_AUTO
411 7bde29b5 Agata Murawska
    results["vcpus"] = str(vcpus_count)
412 7bde29b5 Agata Murawska
413 7bde29b5 Agata Murawska
    find_memory = find_vcpus
414 7bde29b5 Agata Murawska
    match_memory = ("{%s}ResourceType" % RASD_SCHEMA, RASD_TYPE["memory"])
415 7bde29b5 Agata Murawska
    memory = self._GetElementMatchingText(find_memory, match_memory)
416 7bde29b5 Agata Murawska
    memory_raw = None
417 7bde29b5 Agata Murawska
    if memory:
418 7bde29b5 Agata Murawska
      alloc_units = memory.findtext("{%s}AllocationUnits" % RASD_SCHEMA)
419 7bde29b5 Agata Murawska
      matching_units = [units for units, variants in
420 7bde29b5 Agata Murawska
        ALLOCATION_UNITS.iteritems() if alloc_units.lower() in variants]
421 7bde29b5 Agata Murawska
      if matching_units == []:
422 7bde29b5 Agata Murawska
        raise errors.OpPrereqError("Unit %s for RAM memory unknown",
423 7bde29b5 Agata Murawska
          alloc_units)
424 7bde29b5 Agata Murawska
      units = matching_units[0]
425 7bde29b5 Agata Murawska
      memory_raw = int(memory.findtext("{%s}VirtualQuantity" % RASD_SCHEMA,
426 7bde29b5 Agata Murawska
            default=constants.VALUE_AUTO))
427 7bde29b5 Agata Murawska
      memory_count = CONVERT_UNITS_TO_MB[units](memory_raw)
428 7bde29b5 Agata Murawska
    else:
429 7bde29b5 Agata Murawska
      memory_count = constants.VALUE_AUTO
430 7bde29b5 Agata Murawska
    results["memory"] = str(memory_count)
431 7bde29b5 Agata Murawska
432 7bde29b5 Agata Murawska
    find_balance = ("{%s}GanetiSection/{%s}AutoBalance" %
433 7bde29b5 Agata Murawska
                   (GANETI_SCHEMA, GANETI_SCHEMA))
434 7bde29b5 Agata Murawska
    balance = self.tree.findtext(find_balance, default=constants.VALUE_AUTO)
435 7bde29b5 Agata Murawska
    results["auto_balance"] = balance
436 7bde29b5 Agata Murawska
437 7bde29b5 Agata Murawska
    return results
438 7bde29b5 Agata Murawska
439 7bde29b5 Agata Murawska
  def GetTagsData(self):
440 7bde29b5 Agata Murawska
    """Provides tags information for instance.
441 7bde29b5 Agata Murawska

442 7bde29b5 Agata Murawska
    @rtype: string or None
443 7bde29b5 Agata Murawska
    @return: string of comma-separated tags for the instance
444 7bde29b5 Agata Murawska

445 7bde29b5 Agata Murawska
    """
446 7bde29b5 Agata Murawska
    find_tags = "{%s}GanetiSection/{%s}Tags" % (GANETI_SCHEMA, GANETI_SCHEMA)
447 7bde29b5 Agata Murawska
    results = self.tree.findtext(find_tags)
448 7bde29b5 Agata Murawska
    if results:
449 7bde29b5 Agata Murawska
      return results
450 7bde29b5 Agata Murawska
    else:
451 7bde29b5 Agata Murawska
      return None
452 7bde29b5 Agata Murawska
453 7bde29b5 Agata Murawska
  def GetVersionData(self):
454 7bde29b5 Agata Murawska
    """Provides version number read from .ovf file
455 7bde29b5 Agata Murawska

456 7bde29b5 Agata Murawska
    @rtype: string
457 7bde29b5 Agata Murawska
    @return: string containing the version number
458 7bde29b5 Agata Murawska

459 7bde29b5 Agata Murawska
    """
460 7bde29b5 Agata Murawska
    find_version = ("{%s}GanetiSection/{%s}Version" %
461 7bde29b5 Agata Murawska
                    (GANETI_SCHEMA, GANETI_SCHEMA))
462 7bde29b5 Agata Murawska
    return self.tree.findtext(find_version)
463 7bde29b5 Agata Murawska
464 24b9469d Agata Murawska
  def GetNetworkData(self):
465 24b9469d Agata Murawska
    """Provides data about the network in the OVF instance.
466 24b9469d Agata Murawska

467 24b9469d Agata Murawska
    The method gathers the data about networks used by OVF instance. It assumes
468 24b9469d Agata Murawska
    that 'name' tag means something - in essence, if it contains one of the
469 24b9469d Agata Murawska
    words 'bridged' or 'routed' then that will be the mode of this network in
470 24b9469d Agata Murawska
    Ganeti. The information about the network can be either in GanetiSection or
471 24b9469d Agata Murawska
    VirtualHardwareSection.
472 24b9469d Agata Murawska

473 24b9469d Agata Murawska
    @rtype: dict
474 24b9469d Agata Murawska
    @return: dictionary containing all the network information
475 24b9469d Agata Murawska

476 24b9469d Agata Murawska
    """
477 24b9469d Agata Murawska
    results = {}
478 24b9469d Agata Murawska
    networks_search = ("{%s}NetworkSection/{%s}Network" %
479 24b9469d Agata Murawska
                       (OVF_SCHEMA, OVF_SCHEMA))
480 24b9469d Agata Murawska
    network_names = self._GetAttributes(networks_search,
481 24b9469d Agata Murawska
      "{%s}name" % OVF_SCHEMA)
482 24b9469d Agata Murawska
    required = ["ip", "mac", "link", "mode"]
483 24b9469d Agata Murawska
    for (counter, network_name) in enumerate(network_names):
484 24b9469d Agata Murawska
      network_search = ("{%s}VirtualSystem/{%s}VirtualHardwareSection/{%s}Item"
485 24b9469d Agata Murawska
                        % (OVF_SCHEMA, OVF_SCHEMA, OVF_SCHEMA))
486 24b9469d Agata Murawska
      ganeti_search = ("{%s}GanetiSection/{%s}Network/{%s}Nic" %
487 24b9469d Agata Murawska
                       (GANETI_SCHEMA, GANETI_SCHEMA, GANETI_SCHEMA))
488 24b9469d Agata Murawska
      network_match = ("{%s}Connection" % RASD_SCHEMA, network_name)
489 24b9469d Agata Murawska
      ganeti_match = ("{%s}name" % OVF_SCHEMA, network_name)
490 24b9469d Agata Murawska
      network_data = self._GetElementMatchingText(network_search, network_match)
491 24b9469d Agata Murawska
      network_ganeti_data = self._GetElementMatchingAttr(ganeti_search,
492 24b9469d Agata Murawska
        ganeti_match)
493 24b9469d Agata Murawska
494 24b9469d Agata Murawska
      ganeti_data = {}
495 24b9469d Agata Murawska
      if network_ganeti_data:
496 24b9469d Agata Murawska
        ganeti_data["mode"] = network_ganeti_data.findtext("{%s}Mode" %
497 24b9469d Agata Murawska
                                                           GANETI_SCHEMA)
498 24b9469d Agata Murawska
        ganeti_data["mac"] = network_ganeti_data.findtext("{%s}MACAddress" %
499 24b9469d Agata Murawska
                                                          GANETI_SCHEMA)
500 24b9469d Agata Murawska
        ganeti_data["ip"] = network_ganeti_data.findtext("{%s}IPAddress" %
501 24b9469d Agata Murawska
                                                         GANETI_SCHEMA)
502 24b9469d Agata Murawska
        ganeti_data["link"] = network_ganeti_data.findtext("{%s}Link" %
503 24b9469d Agata Murawska
                                                           GANETI_SCHEMA)
504 24b9469d Agata Murawska
      mac_data = None
505 24b9469d Agata Murawska
      if network_data:
506 24b9469d Agata Murawska
        mac_data = network_data.findtext("{%s}Address" % RASD_SCHEMA)
507 24b9469d Agata Murawska
508 24b9469d Agata Murawska
      network_name = network_name.lower()
509 24b9469d Agata Murawska
510 24b9469d Agata Murawska
      # First, some not Ganeti-specific information is collected
511 24b9469d Agata Murawska
      if constants.NIC_MODE_BRIDGED in network_name:
512 24b9469d Agata Murawska
        results["nic%s_mode" % counter] = "bridged"
513 24b9469d Agata Murawska
      elif constants.NIC_MODE_ROUTED in network_name:
514 24b9469d Agata Murawska
        results["nic%s_mode" % counter] = "routed"
515 24b9469d Agata Murawska
      results["nic%s_mac" % counter] = mac_data
516 24b9469d Agata Murawska
517 24b9469d Agata Murawska
      # GanetiSection data overrides 'manually' collected data
518 24b9469d Agata Murawska
      for name, value in ganeti_data.iteritems():
519 24b9469d Agata Murawska
        results["nic%s_%s" % (counter, name)] = value
520 24b9469d Agata Murawska
521 24b9469d Agata Murawska
      # Bridged network has no IP - unless specifically stated otherwise
522 24b9469d Agata Murawska
      if (results.get("nic%s_mode" % counter) == "bridged" and
523 24b9469d Agata Murawska
          not results.get("nic%s_ip" % counter)):
524 24b9469d Agata Murawska
        results["nic%s_ip" % counter] = constants.VALUE_NONE
525 24b9469d Agata Murawska
526 24b9469d Agata Murawska
      for option in required:
527 24b9469d Agata Murawska
        if not results.get("nic%s_%s" % (counter, option)):
528 24b9469d Agata Murawska
          results["nic%s_%s" % (counter, option)] = constants.VALUE_AUTO
529 24b9469d Agata Murawska
530 24b9469d Agata Murawska
    if network_names:
531 24b9469d Agata Murawska
      results["nic_count"] = str(len(network_names))
532 24b9469d Agata Murawska
    return results
533 24b9469d Agata Murawska
534 99381e3b Agata Murawska
  def GetDisksNames(self):
535 99381e3b Agata Murawska
    """Provides list of file names for the disks used by the instance.
536 99381e3b Agata Murawska

537 99381e3b Agata Murawska
    @rtype: list
538 99381e3b Agata Murawska
    @return: list of file names, as referenced in .ovf file
539 99381e3b Agata Murawska

540 99381e3b Agata Murawska
    """
541 99381e3b Agata Murawska
    results = []
542 99381e3b Agata Murawska
    disks_search = "{%s}DiskSection/{%s}Disk" % (OVF_SCHEMA, OVF_SCHEMA)
543 99381e3b Agata Murawska
    disk_ids = self._GetAttributes(disks_search, "{%s}fileRef" % OVF_SCHEMA)
544 99381e3b Agata Murawska
    for disk in disk_ids:
545 99381e3b Agata Murawska
      disk_search = "{%s}References/{%s}File" % (OVF_SCHEMA, OVF_SCHEMA)
546 99381e3b Agata Murawska
      disk_match = ("{%s}id" % OVF_SCHEMA, disk)
547 99381e3b Agata Murawska
      disk_elem = self._GetElementMatchingAttr(disk_search, disk_match)
548 99381e3b Agata Murawska
      if disk_elem is None:
549 99381e3b Agata Murawska
        raise errors.OpPrereqError("%s file corrupted - disk %s not found in"
550 99381e3b Agata Murawska
                                   " references" % (OVF_EXT, disk))
551 99381e3b Agata Murawska
      disk_name = disk_elem.get("{%s}href" % OVF_SCHEMA)
552 99381e3b Agata Murawska
      disk_compression = disk_elem.get("{%s}compression" % OVF_SCHEMA)
553 99381e3b Agata Murawska
      results.append((disk_name, disk_compression))
554 99381e3b Agata Murawska
    return results
555 99381e3b Agata Murawska
556 864cf6bf Agata Murawska
557 7432d332 Agata Murawska
def SubElementText(parent, tag, text, attrib={}, **extra):
558 7432d332 Agata Murawska
# pylint: disable=W0102
559 7432d332 Agata Murawska
  """This is just a wrapper on ET.SubElement that always has text content.
560 7432d332 Agata Murawska

561 7432d332 Agata Murawska
  """
562 7432d332 Agata Murawska
  if text is None:
563 7432d332 Agata Murawska
    return None
564 7432d332 Agata Murawska
  elem = ET.SubElement(parent, tag, attrib=attrib, **extra)
565 7432d332 Agata Murawska
  elem.text = str(text)
566 7432d332 Agata Murawska
  return elem
567 7432d332 Agata Murawska
568 7432d332 Agata Murawska
569 0963b26a Agata Murawska
class OVFWriter(object):
570 0963b26a Agata Murawska
  """Writer class for OVF files.
571 0963b26a Agata Murawska

572 0963b26a Agata Murawska
  @type tree: ET.ElementTree
573 0963b26a Agata Murawska
  @ivar tree: XML tree that we are constructing
574 7432d332 Agata Murawska
  @type hardware_list: list
575 7432d332 Agata Murawska
  @ivar hardware_list: list of items prepared for VirtualHardwareSection
576 0963b26a Agata Murawska

577 0963b26a Agata Murawska
  """
578 0963b26a Agata Murawska
  def __init__(self, has_gnt_section):
579 0963b26a Agata Murawska
    """Initialize the writer - set the top element.
580 0963b26a Agata Murawska

581 0963b26a Agata Murawska
    @type has_gnt_section: bool
582 0963b26a Agata Murawska
    @param has_gnt_section: if the Ganeti schema should be added - i.e. this
583 0963b26a Agata Murawska
      means that Ganeti section will be present
584 0963b26a Agata Murawska

585 0963b26a Agata Murawska
    """
586 0963b26a Agata Murawska
    env_attribs = {
587 0963b26a Agata Murawska
      "xmlns:xsi": XML_SCHEMA,
588 0963b26a Agata Murawska
      "xmlns:vssd": VSSD_SCHEMA,
589 0963b26a Agata Murawska
      "xmlns:rasd": RASD_SCHEMA,
590 0963b26a Agata Murawska
      "xmlns:ovf": OVF_SCHEMA,
591 0963b26a Agata Murawska
      "xmlns": OVF_SCHEMA,
592 0963b26a Agata Murawska
      "xml:lang": "en-US",
593 0963b26a Agata Murawska
    }
594 0963b26a Agata Murawska
    if has_gnt_section:
595 0963b26a Agata Murawska
      env_attribs["xmlns:gnt"] = GANETI_SCHEMA
596 0963b26a Agata Murawska
    self.tree = ET.Element("Envelope", attrib=env_attribs)
597 7432d332 Agata Murawska
    self.hardware_list = []
598 7432d332 Agata Murawska
599 7432d332 Agata Murawska
  def SaveDisksData(self, disks):
600 7432d332 Agata Murawska
    """Convert disk information to certain OVF sections.
601 7432d332 Agata Murawska

602 7432d332 Agata Murawska
    @type disks: list
603 7432d332 Agata Murawska
    @param disks: list of dictionaries of disk options from config.ini
604 7432d332 Agata Murawska

605 7432d332 Agata Murawska
    """
606 7432d332 Agata Murawska
    references = ET.SubElement(self.tree, "References")
607 7432d332 Agata Murawska
    disk_section = ET.SubElement(self.tree, "DiskSection")
608 7432d332 Agata Murawska
    for counter, disk in enumerate(disks):
609 7432d332 Agata Murawska
      file_id = "file%s" % counter
610 7432d332 Agata Murawska
      disk_id = "disk%s" % counter
611 7432d332 Agata Murawska
      file_attribs = {
612 7432d332 Agata Murawska
        "ovf:href": disk["path"],
613 7432d332 Agata Murawska
        "ovf:size": str(disk["real-size"]),
614 7432d332 Agata Murawska
        "ovf:id": file_id,
615 7432d332 Agata Murawska
      }
616 7432d332 Agata Murawska
      disk_attribs = {
617 7432d332 Agata Murawska
        "ovf:capacity": str(disk["virt-size"]),
618 7432d332 Agata Murawska
        "ovf:diskId": disk_id,
619 7432d332 Agata Murawska
        "ovf:fileRef": file_id,
620 7432d332 Agata Murawska
        "ovf:format": DISK_FORMAT.get(disk["format"], disk["format"]),
621 7432d332 Agata Murawska
      }
622 7432d332 Agata Murawska
      if "compression" in disk:
623 7432d332 Agata Murawska
        file_attribs["ovf:compression"] = disk["compression"]
624 7432d332 Agata Murawska
      ET.SubElement(references, "File", attrib=file_attribs)
625 7432d332 Agata Murawska
      ET.SubElement(disk_section, "Disk", attrib=disk_attribs)
626 7432d332 Agata Murawska
627 7432d332 Agata Murawska
      # Item in VirtualHardwareSection creation
628 7432d332 Agata Murawska
      disk_item = ET.Element("Item")
629 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:ElementName", disk_id)
630 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:ResourceType", RASD_TYPE["disk"])
631 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:HostResource", "ovf:/disk/%s" % disk_id)
632 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:Parent", SCSI_ID)
633 7432d332 Agata Murawska
      self.hardware_list.append(disk_item)
634 7432d332 Agata Murawska
635 7432d332 Agata Murawska
  def SaveNetworksData(self, networks):
636 7432d332 Agata Murawska
    """Convert network information to NetworkSection.
637 7432d332 Agata Murawska

638 7432d332 Agata Murawska
    @type networks: list
639 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
640 7432d332 Agata Murawska

641 7432d332 Agata Murawska
    """
642 7432d332 Agata Murawska
    network_section = ET.SubElement(self.tree, "NetworkSection")
643 7432d332 Agata Murawska
    for counter, network in enumerate(networks):
644 7432d332 Agata Murawska
      network_name = "%s%s" % (network["mode"], counter)
645 7432d332 Agata Murawska
      network_attrib = {"ovf:name": network_name}
646 7432d332 Agata Murawska
      ET.SubElement(network_section, "Network", attrib=network_attrib)
647 7432d332 Agata Murawska
648 7432d332 Agata Murawska
      # Item in VirtualHardwareSection creation
649 7432d332 Agata Murawska
      network_item = ET.Element("Item")
650 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:ElementName", network_name)
651 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:ResourceType",
652 7432d332 Agata Murawska
        RASD_TYPE["ethernet-adapter"])
653 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:Connection", network_name)
654 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:Address", network["mac"])
655 7432d332 Agata Murawska
      self.hardware_list.append(network_item)
656 7432d332 Agata Murawska
657 7432d332 Agata Murawska
  @staticmethod
658 7432d332 Agata Murawska
  def _SaveNameAndParams(root, data):
659 7432d332 Agata Murawska
    """Save name and parameters information under root using data.
660 7432d332 Agata Murawska

661 7432d332 Agata Murawska
    @type root: ET.Element
662 7432d332 Agata Murawska
    @param root: root element for the Name and Parameters
663 7432d332 Agata Murawska
    @type data: dict
664 7432d332 Agata Murawska
    @param data: data from which we gather the values
665 7432d332 Agata Murawska

666 7432d332 Agata Murawska
    """
667 7432d332 Agata Murawska
    assert(data.get("name"))
668 7432d332 Agata Murawska
    name = SubElementText(root, "gnt:Name", data["name"])
669 7432d332 Agata Murawska
    params = ET.SubElement(root, "gnt:Parameters")
670 7432d332 Agata Murawska
    for name, value in data.iteritems():
671 7432d332 Agata Murawska
      if name != "name":
672 7432d332 Agata Murawska
        SubElementText(params, "gnt:%s" % name, value)
673 7432d332 Agata Murawska
674 7432d332 Agata Murawska
  def SaveGanetiData(self, ganeti, networks):
675 7432d332 Agata Murawska
    """Convert Ganeti-specific information to GanetiSection.
676 7432d332 Agata Murawska

677 7432d332 Agata Murawska
    @type ganeti: dict
678 7432d332 Agata Murawska
    @param ganeti: dictionary of Ganeti-specific options from config.ini
679 7432d332 Agata Murawska
    @type networks: list
680 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
681 7432d332 Agata Murawska

682 7432d332 Agata Murawska
    """
683 7432d332 Agata Murawska
    ganeti_section = ET.SubElement(self.tree, "gnt:GanetiSection")
684 7432d332 Agata Murawska
685 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:Version", ganeti.get("version"))
686 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:DiskTemplate",
687 7432d332 Agata Murawska
      ganeti.get("disk_template"))
688 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:AutoBalance",
689 7432d332 Agata Murawska
      ganeti.get("auto_balance"))
690 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:Tags", ganeti.get("tags"))
691 7432d332 Agata Murawska
692 7432d332 Agata Murawska
    osys = ET.SubElement(ganeti_section, "gnt:OperatingSystem")
693 7432d332 Agata Murawska
    self._SaveNameAndParams(osys, ganeti["os"])
694 7432d332 Agata Murawska
695 7432d332 Agata Murawska
    hypervisor = ET.SubElement(ganeti_section, "gnt:Hypervisor")
696 7432d332 Agata Murawska
    self._SaveNameAndParams(hypervisor, ganeti["hypervisor"])
697 7432d332 Agata Murawska
698 7432d332 Agata Murawska
    network_section = ET.SubElement(ganeti_section, "gnt:Network")
699 7432d332 Agata Murawska
    for counter, network in enumerate(networks):
700 7432d332 Agata Murawska
      network_name = "%s%s" % (network["mode"], counter)
701 7432d332 Agata Murawska
      nic_attrib = {"ovf:name": network_name}
702 7432d332 Agata Murawska
      nic = ET.SubElement(network_section, "gnt:Nic", attrib=nic_attrib)
703 7432d332 Agata Murawska
      SubElementText(nic, "gnt:Mode", network["mode"])
704 7432d332 Agata Murawska
      SubElementText(nic, "gnt:MACAddress", network["mac"])
705 7432d332 Agata Murawska
      SubElementText(nic, "gnt:IPAddress", network["ip"])
706 7432d332 Agata Murawska
      SubElementText(nic, "gnt:Link", network["link"])
707 7432d332 Agata Murawska
708 7432d332 Agata Murawska
  def SaveVirtualSystemData(self, name, vcpus, memory):
709 7432d332 Agata Murawska
    """Convert virtual system information to OVF sections.
710 7432d332 Agata Murawska

711 7432d332 Agata Murawska
    @type name: string
712 7432d332 Agata Murawska
    @param name: name of the instance
713 7432d332 Agata Murawska
    @type vcpus: int
714 7432d332 Agata Murawska
    @param vcpus: number of VCPUs
715 7432d332 Agata Murawska
    @type memory: int
716 7432d332 Agata Murawska
    @param memory: RAM memory in MB
717 7432d332 Agata Murawska

718 7432d332 Agata Murawska
    """
719 7432d332 Agata Murawska
    assert(vcpus > 0)
720 7432d332 Agata Murawska
    assert(memory > 0)
721 7432d332 Agata Murawska
    vs_attrib = {"ovf:id": name}
722 7432d332 Agata Murawska
    virtual_system = ET.SubElement(self.tree, "VirtualSystem", attrib=vs_attrib)
723 7432d332 Agata Murawska
724 7432d332 Agata Murawska
    name_section = ET.SubElement(virtual_system, "Name")
725 7432d332 Agata Murawska
    name_section.text = name
726 7432d332 Agata Murawska
    os_attrib = {"ovf:id": "0"}
727 7432d332 Agata Murawska
    ET.SubElement(virtual_system, "OperatingSystemSection",
728 7432d332 Agata Murawska
      attrib=os_attrib)
729 7432d332 Agata Murawska
    hardware_section = ET.SubElement(virtual_system, "VirtualHardwareSection")
730 7432d332 Agata Murawska
731 7432d332 Agata Murawska
    # System description
732 7432d332 Agata Murawska
    system = ET.SubElement(hardware_section, "System")
733 7432d332 Agata Murawska
    SubElementText(system, "vssd:ElementName", "Virtual Hardware Family")
734 7432d332 Agata Murawska
    SubElementText(system, "vssd:InstanceId", SYSTEM_ID)
735 7432d332 Agata Murawska
    SubElementText(system, "vssd:VirtualSystemIdentifier", name)
736 7432d332 Agata Murawska
    SubElementText(system, "vssd:VirtualSystemType", "ganeti-ovf")
737 7432d332 Agata Murawska
738 7432d332 Agata Murawska
    # Item for vcpus
739 7432d332 Agata Murawska
    vcpus_item = ET.SubElement(hardware_section, "Item")
740 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:ElementName",
741 7432d332 Agata Murawska
      "%s virtual CPU(s)" % vcpus)
742 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:InstanceID", "1")
743 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:ResourceType", RASD_TYPE["vcpus"])
744 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:VirtualQuantity", vcpus)
745 7432d332 Agata Murawska
746 7432d332 Agata Murawska
    # Item for memory
747 7432d332 Agata Murawska
    memory_item = ET.SubElement(hardware_section, "Item")
748 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:AllocationUnits", "byte * 2^20")
749 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:ElementName", "%sMB of memory" % memory)
750 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:InstanceID", "2")
751 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:ResourceType", RASD_TYPE["memory"])
752 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:VirtualQuantity", memory)
753 7432d332 Agata Murawska
754 7432d332 Agata Murawska
    # Item for scsi controller
755 7432d332 Agata Murawska
    scsi_item = ET.SubElement(hardware_section, "Item")
756 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:Address", SYSTEM_ID)
757 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:ElementName", "scsi_controller0")
758 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:ResourceType", RASD_TYPE["scsi-controller"])
759 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:InstanceId", "3")
760 7432d332 Agata Murawska
761 7432d332 Agata Murawska
    # Other items - from self.hardware_list
762 7432d332 Agata Murawska
    for counter, item in enumerate(self.hardware_list):
763 7432d332 Agata Murawska
      SubElementText(item, "rasd:InstanceID", counter + 4)
764 7432d332 Agata Murawska
      hardware_section.append(item)
765 0963b26a Agata Murawska
766 0963b26a Agata Murawska
  def PrettyXmlDump(self):
767 0963b26a Agata Murawska
    """Formatter of the XML file.
768 0963b26a Agata Murawska

769 0963b26a Agata Murawska
    @rtype: string
770 0963b26a Agata Murawska
    @return: XML tree in the form of nicely-formatted string
771 0963b26a Agata Murawska

772 0963b26a Agata Murawska
    """
773 0963b26a Agata Murawska
    raw_string = ET.tostring(self.tree)
774 0963b26a Agata Murawska
    parsed_xml = xml.dom.minidom.parseString(raw_string)
775 0963b26a Agata Murawska
    xml_string = parsed_xml.toprettyxml(indent="  ")
776 0963b26a Agata Murawska
    text_re = re.compile(">\n\s+([^<>\s].*?)\n\s+</", re.DOTALL)
777 0963b26a Agata Murawska
    return text_re.sub(">\g<1></", xml_string)
778 0963b26a Agata Murawska
779 0963b26a Agata Murawska
780 ced78a66 Agata Murawska
class Converter(object):
781 ced78a66 Agata Murawska
  """Converter class for OVF packages.
782 ced78a66 Agata Murawska

783 ced78a66 Agata Murawska
  Converter is a class above both ImporterOVF and ExporterOVF. It's purpose is
784 ced78a66 Agata Murawska
  to provide a common interface for the two.
785 ced78a66 Agata Murawska

786 ced78a66 Agata Murawska
  @type options: optparse.Values
787 ced78a66 Agata Murawska
  @ivar options: options parsed from the command line
788 ced78a66 Agata Murawska
  @type output_dir: string
789 ced78a66 Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
790 ced78a66 Agata Murawska
    written
791 ced78a66 Agata Murawska
  @type temp_file_manager: L{utils.TemporaryFileManager}
792 ced78a66 Agata Murawska
  @ivar temp_file_manager: container for temporary files created during
793 ced78a66 Agata Murawska
    conversion
794 ced78a66 Agata Murawska
  @type temp_dir: string
795 ced78a66 Agata Murawska
  @ivar temp_dir: temporary directory created then we deal with OVA
796 ced78a66 Agata Murawska

797 ced78a66 Agata Murawska
  """
798 ced78a66 Agata Murawska
  def __init__(self, input_path, options):
799 ced78a66 Agata Murawska
    """Initialize the converter.
800 ced78a66 Agata Murawska

801 ced78a66 Agata Murawska
    @type input_path: string
802 ced78a66 Agata Murawska
    @param input_path: path to the Converter input file
803 ced78a66 Agata Murawska
    @type options: optparse.Values
804 ced78a66 Agata Murawska
    @param options: command line options
805 ced78a66 Agata Murawska

806 ced78a66 Agata Murawska
    @raise errors.OpPrereqError: if file does not exist
807 ced78a66 Agata Murawska

808 ced78a66 Agata Murawska
    """
809 ced78a66 Agata Murawska
    input_path = os.path.abspath(input_path)
810 ced78a66 Agata Murawska
    if not os.path.isfile(input_path):
811 ced78a66 Agata Murawska
      raise errors.OpPrereqError("File does not exist: %s" % input_path)
812 ced78a66 Agata Murawska
    self.options = options
813 ced78a66 Agata Murawska
    self.temp_file_manager = utils.TemporaryFileManager()
814 ced78a66 Agata Murawska
    self.temp_dir = None
815 ced78a66 Agata Murawska
    self.output_dir = None
816 ced78a66 Agata Murawska
    self._ReadInputData(input_path)
817 ced78a66 Agata Murawska
818 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
819 ced78a66 Agata Murawska
    """Reads the data on which the conversion will take place.
820 ced78a66 Agata Murawska

821 ced78a66 Agata Murawska
    @type input_path: string
822 ced78a66 Agata Murawska
    @param input_path: absolute path to the Converter input file
823 ced78a66 Agata Murawska

824 ced78a66 Agata Murawska
    """
825 ced78a66 Agata Murawska
    raise NotImplementedError()
826 ced78a66 Agata Murawska
827 99381e3b Agata Murawska
  def _CompressDisk(self, disk_path, compression, action):
828 99381e3b Agata Murawska
    """Performs (de)compression on the disk and returns the new path
829 99381e3b Agata Murawska

830 99381e3b Agata Murawska
    @type disk_path: string
831 99381e3b Agata Murawska
    @param disk_path: path to the disk
832 99381e3b Agata Murawska
    @type compression: string
833 99381e3b Agata Murawska
    @param compression: compression type
834 99381e3b Agata Murawska
    @type action: string
835 99381e3b Agata Murawska
    @param action: whether the action is compression or decompression
836 99381e3b Agata Murawska
    @rtype: string
837 99381e3b Agata Murawska
    @return: new disk path after (de)compression
838 99381e3b Agata Murawska

839 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk (de)compression failed or "compression"
840 99381e3b Agata Murawska
      is not supported
841 99381e3b Agata Murawska

842 99381e3b Agata Murawska
    """
843 99381e3b Agata Murawska
    assert(action in ALLOWED_ACTIONS)
844 99381e3b Agata Murawska
    # For now we only support gzip, as it is used in ovftool
845 99381e3b Agata Murawska
    if compression != COMPRESSION_TYPE:
846 99381e3b Agata Murawska
      raise errors.OpPrereqError("Unsupported compression type: %s"
847 99381e3b Agata Murawska
                                 % compression)
848 99381e3b Agata Murawska
    disk_file = os.path.basename(disk_path)
849 99381e3b Agata Murawska
    if action == DECOMPRESS:
850 99381e3b Agata Murawska
      (disk_name, _) = os.path.splitext(disk_file)
851 99381e3b Agata Murawska
      prefix = disk_name
852 99381e3b Agata Murawska
    elif action == COMPRESS:
853 99381e3b Agata Murawska
      prefix = disk_file
854 99381e3b Agata Murawska
    new_path = utils.GetClosedTempfile(suffix=COMPRESSION_EXT, prefix=prefix,
855 99381e3b Agata Murawska
      dir=self.output_dir)
856 99381e3b Agata Murawska
    self.temp_file_manager.Add(new_path)
857 99381e3b Agata Murawska
    args = ["gzip", "-c", disk_path]
858 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, output=new_path)
859 99381e3b Agata Murawska
    if run_result.failed:
860 99381e3b Agata Murawska
      raise errors.OpPrereqError("Disk %s failed with output: %s"
861 99381e3b Agata Murawska
                                 % (action, run_result.stderr))
862 99381e3b Agata Murawska
    logging.info("The %s of the disk is completed", action)
863 99381e3b Agata Murawska
    return (COMPRESSION_EXT, new_path)
864 99381e3b Agata Murawska
865 99381e3b Agata Murawska
  def _ConvertDisk(self, disk_format, disk_path):
866 99381e3b Agata Murawska
    """Performes conversion to specified format.
867 99381e3b Agata Murawska

868 99381e3b Agata Murawska
    @type disk_format: string
869 99381e3b Agata Murawska
    @param disk_format: format to which the disk should be converted
870 99381e3b Agata Murawska
    @type disk_path: string
871 99381e3b Agata Murawska
    @param disk_path: path to the disk that should be converted
872 99381e3b Agata Murawska
    @rtype: string
873 99381e3b Agata Murawska
    @return path to the output disk
874 99381e3b Agata Murawska

875 99381e3b Agata Murawska
    @raise errors.OpPrereqError: convertion of the disk failed
876 99381e3b Agata Murawska

877 99381e3b Agata Murawska
    """
878 99381e3b Agata Murawska
    disk_file = os.path.basename(disk_path)
879 99381e3b Agata Murawska
    (disk_name, disk_extension) = os.path.splitext(disk_file)
880 99381e3b Agata Murawska
    if disk_extension != disk_format:
881 99381e3b Agata Murawska
      logging.warning("Conversion of disk image to %s format, this may take"
882 99381e3b Agata Murawska
                      " a while", disk_format)
883 99381e3b Agata Murawska
884 99381e3b Agata Murawska
    new_disk_path = utils.GetClosedTempfile(suffix=".%s" % disk_format,
885 99381e3b Agata Murawska
      prefix=disk_name, dir=self.output_dir)
886 99381e3b Agata Murawska
    self.temp_file_manager.Add(new_disk_path)
887 99381e3b Agata Murawska
    args = [
888 99381e3b Agata Murawska
      "qemu-img",
889 99381e3b Agata Murawska
      "convert",
890 99381e3b Agata Murawska
      "-O",
891 99381e3b Agata Murawska
      disk_format,
892 99381e3b Agata Murawska
      disk_path,
893 99381e3b Agata Murawska
      new_disk_path,
894 99381e3b Agata Murawska
    ]
895 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, cwd=os.getcwd())
896 99381e3b Agata Murawska
    if run_result.failed:
897 99381e3b Agata Murawska
      raise errors.OpPrereqError("Convertion to %s failed, qemu-img output was"
898 99381e3b Agata Murawska
                                 ": %s" % (disk_format, run_result.stderr))
899 99381e3b Agata Murawska
    return (".%s" % disk_format, new_disk_path)
900 99381e3b Agata Murawska
901 99381e3b Agata Murawska
  @staticmethod
902 99381e3b Agata Murawska
  def _GetDiskQemuInfo(disk_path, regexp):
903 99381e3b Agata Murawska
    """Figures out some information of the disk using qemu-img.
904 99381e3b Agata Murawska

905 99381e3b Agata Murawska
    @type disk_path: string
906 99381e3b Agata Murawska
    @param disk_path: path to the disk we want to know the format of
907 99381e3b Agata Murawska
    @type regexp: string
908 99381e3b Agata Murawska
    @param regexp: string that has to be matched, it has to contain one group
909 99381e3b Agata Murawska
    @rtype: string
910 99381e3b Agata Murawska
    @return: disk format
911 99381e3b Agata Murawska

912 99381e3b Agata Murawska
    @raise errors.OpPrereqError: format information cannot be retrieved
913 99381e3b Agata Murawska

914 99381e3b Agata Murawska
    """
915 99381e3b Agata Murawska
    args = ["qemu-img", "info", disk_path]
916 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, cwd=os.getcwd())
917 99381e3b Agata Murawska
    if run_result.failed:
918 99381e3b Agata Murawska
      raise errors.OpPrereqError("Gathering info about the disk using qemu-img"
919 99381e3b Agata Murawska
                                 " failed, output was: %s" % run_result.stderr)
920 99381e3b Agata Murawska
    result = run_result.output
921 99381e3b Agata Murawska
    regexp = r"%s" % regexp
922 99381e3b Agata Murawska
    match = re.search(regexp, result)
923 99381e3b Agata Murawska
    if match:
924 99381e3b Agata Murawska
      disk_format = match.group(1)
925 99381e3b Agata Murawska
    else:
926 99381e3b Agata Murawska
      raise errors.OpPrereqError("No file information matching %s found in:"
927 99381e3b Agata Murawska
                                 " %s" % (regexp, result))
928 99381e3b Agata Murawska
    return disk_format
929 99381e3b Agata Murawska
930 ced78a66 Agata Murawska
  def Parse(self):
931 ced78a66 Agata Murawska
    """Parses the data and creates a structure containing all required info.
932 ced78a66 Agata Murawska

933 ced78a66 Agata Murawska
    """
934 ced78a66 Agata Murawska
    raise NotImplementedError()
935 ced78a66 Agata Murawska
936 ced78a66 Agata Murawska
  def Save(self):
937 ced78a66 Agata Murawska
    """Saves the gathered configuration in an apropriate format.
938 ced78a66 Agata Murawska

939 ced78a66 Agata Murawska
    """
940 ced78a66 Agata Murawska
    raise NotImplementedError()
941 ced78a66 Agata Murawska
942 ced78a66 Agata Murawska
  def Cleanup(self):
943 ced78a66 Agata Murawska
    """Cleans the temporary directory, if one was created.
944 ced78a66 Agata Murawska

945 ced78a66 Agata Murawska
    """
946 ced78a66 Agata Murawska
    self.temp_file_manager.Cleanup()
947 ced78a66 Agata Murawska
    if self.temp_dir:
948 ced78a66 Agata Murawska
      shutil.rmtree(self.temp_dir)
949 ced78a66 Agata Murawska
      self.temp_dir = None
950 ced78a66 Agata Murawska
951 ced78a66 Agata Murawska
952 ced78a66 Agata Murawska
class OVFImporter(Converter):
953 864cf6bf Agata Murawska
  """Converter from OVF to Ganeti config file.
954 864cf6bf Agata Murawska

955 864cf6bf Agata Murawska
  @type input_dir: string
956 864cf6bf Agata Murawska
  @ivar input_dir: directory in which the .ovf file resides
957 864cf6bf Agata Murawska
  @type output_dir: string
958 864cf6bf Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
959 864cf6bf Agata Murawska
    written
960 864cf6bf Agata Murawska
  @type input_path: string
961 864cf6bf Agata Murawska
  @ivar input_path: complete path to the .ovf file
962 864cf6bf Agata Murawska
  @type ovf_reader: L{OVFReader}
963 864cf6bf Agata Murawska
  @ivar ovf_reader: OVF reader instance collects data from .ovf file
964 99381e3b Agata Murawska
  @type results_name: string
965 99381e3b Agata Murawska
  @ivar results_name: name of imported instance
966 99381e3b Agata Murawska
  @type results_template: string
967 99381e3b Agata Murawska
  @ivar results_template: disk template read from .ovf file or command line
968 99381e3b Agata Murawska
    arguments
969 7bde29b5 Agata Murawska
  @type results_hypervisor: dict
970 7bde29b5 Agata Murawska
  @ivar results_hypervisor: hypervisor information gathered from .ovf file or
971 7bde29b5 Agata Murawska
    command line arguments
972 7bde29b5 Agata Murawska
  @type results_os: dict
973 7bde29b5 Agata Murawska
  @ivar results_os: operating system information gathered from .ovf file or
974 7bde29b5 Agata Murawska
    command line arguments
975 7bde29b5 Agata Murawska
  @type results_backend: dict
976 7bde29b5 Agata Murawska
  @ivar results_backend: backend information gathered from .ovf file or
977 7bde29b5 Agata Murawska
    command line arguments
978 7bde29b5 Agata Murawska
  @type results_tags: string
979 7bde29b5 Agata Murawska
  @ivar results_tags: string containing instance-specific tags
980 7bde29b5 Agata Murawska
  @type results_version: string
981 7bde29b5 Agata Murawska
  @ivar results_version: version as required by Ganeti import
982 24b9469d Agata Murawska
  @type results_network: dict
983 24b9469d Agata Murawska
  @ivar results_network: network information gathered from .ovf file or command
984 24b9469d Agata Murawska
    line arguments
985 99381e3b Agata Murawska
  @type results_disk: dict
986 99381e3b Agata Murawska
  @ivar results_disk: disk information gathered from .ovf file or command line
987 99381e3b Agata Murawska
    arguments
988 864cf6bf Agata Murawska

989 864cf6bf Agata Murawska
  """
990 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
991 864cf6bf Agata Murawska
    """Reads the data on which the conversion will take place.
992 864cf6bf Agata Murawska

993 864cf6bf Agata Murawska
    @type input_path: string
994 864cf6bf Agata Murawska
    @param input_path: absolute path to the .ovf or .ova input file
995 864cf6bf Agata Murawska

996 864cf6bf Agata Murawska
    @raise errors.OpPrereqError: if input file is neither .ovf nor .ova
997 864cf6bf Agata Murawska

998 864cf6bf Agata Murawska
    """
999 864cf6bf Agata Murawska
    (input_dir, input_file) = os.path.split(input_path)
1000 864cf6bf Agata Murawska
    (_, input_extension) = os.path.splitext(input_file)
1001 864cf6bf Agata Murawska
1002 864cf6bf Agata Murawska
    if input_extension == OVF_EXT:
1003 864cf6bf Agata Murawska
      logging.info("%s file extension found, no unpacking necessary", OVF_EXT)
1004 864cf6bf Agata Murawska
      self.input_dir = input_dir
1005 864cf6bf Agata Murawska
      self.input_path = input_path
1006 864cf6bf Agata Murawska
      self.temp_dir = None
1007 864cf6bf Agata Murawska
    elif input_extension == OVA_EXT:
1008 864cf6bf Agata Murawska
      logging.info("%s file extension found, proceeding to unpacking", OVA_EXT)
1009 864cf6bf Agata Murawska
      self._UnpackOVA(input_path)
1010 864cf6bf Agata Murawska
    else:
1011 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Unknown file extension; expected %s or %s"
1012 864cf6bf Agata Murawska
                                 " file" % (OVA_EXT, OVF_EXT))
1013 864cf6bf Agata Murawska
    assert ((input_extension == OVA_EXT and self.temp_dir) or
1014 864cf6bf Agata Murawska
            (input_extension == OVF_EXT and not self.temp_dir))
1015 864cf6bf Agata Murawska
    assert self.input_dir in self.input_path
1016 864cf6bf Agata Murawska
1017 864cf6bf Agata Murawska
    if self.options.output_dir:
1018 864cf6bf Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1019 864cf6bf Agata Murawska
      if (os.path.commonprefix([constants.EXPORT_DIR, self.output_dir]) !=
1020 864cf6bf Agata Murawska
          constants.EXPORT_DIR):
1021 864cf6bf Agata Murawska
        logging.warning("Export path is not under %s directory, import to"
1022 864cf6bf Agata Murawska
                        " Ganeti using gnt-backup may fail",
1023 864cf6bf Agata Murawska
                        constants.EXPORT_DIR)
1024 864cf6bf Agata Murawska
    else:
1025 864cf6bf Agata Murawska
      self.output_dir = constants.EXPORT_DIR
1026 864cf6bf Agata Murawska
1027 864cf6bf Agata Murawska
    self.ovf_reader = OVFReader(self.input_path)
1028 864cf6bf Agata Murawska
    self.ovf_reader.VerifyManifest()
1029 864cf6bf Agata Murawska
1030 864cf6bf Agata Murawska
  def _UnpackOVA(self, input_path):
1031 864cf6bf Agata Murawska
    """Unpacks the .ova package into temporary directory.
1032 864cf6bf Agata Murawska

1033 864cf6bf Agata Murawska
    @type input_path: string
1034 864cf6bf Agata Murawska
    @param input_path: path to the .ova package file
1035 864cf6bf Agata Murawska

1036 864cf6bf Agata Murawska
    @raise errors.OpPrereqError: if file is not a proper tarball, one of the
1037 864cf6bf Agata Murawska
        files in the archive seem malicious (e.g. path starts with '../') or
1038 864cf6bf Agata Murawska
        .ova package does not contain .ovf file
1039 864cf6bf Agata Murawska

1040 864cf6bf Agata Murawska
    """
1041 864cf6bf Agata Murawska
    input_name = None
1042 864cf6bf Agata Murawska
    if not tarfile.is_tarfile(input_path):
1043 864cf6bf Agata Murawska
      raise errors.OpPrereqError("The provided %s file is not a proper tar"
1044 864cf6bf Agata Murawska
                                 " archive", OVA_EXT)
1045 864cf6bf Agata Murawska
    ova_content = tarfile.open(input_path)
1046 864cf6bf Agata Murawska
    temp_dir = tempfile.mkdtemp()
1047 864cf6bf Agata Murawska
    self.temp_dir = temp_dir
1048 864cf6bf Agata Murawska
    for file_name in ova_content.getnames():
1049 864cf6bf Agata Murawska
      file_normname = os.path.normpath(file_name)
1050 864cf6bf Agata Murawska
      try:
1051 864cf6bf Agata Murawska
        utils.PathJoin(temp_dir, file_normname)
1052 864cf6bf Agata Murawska
      except ValueError, err:
1053 864cf6bf Agata Murawska
        raise errors.OpPrereqError("File %s inside %s package is not safe" %
1054 864cf6bf Agata Murawska
                                   (file_name, OVA_EXT))
1055 864cf6bf Agata Murawska
      if file_name.endswith(OVF_EXT):
1056 864cf6bf Agata Murawska
        input_name = file_name
1057 864cf6bf Agata Murawska
    if not input_name:
1058 864cf6bf Agata Murawska
      raise errors.OpPrereqError("No %s file in %s package found" %
1059 864cf6bf Agata Murawska
                                 (OVF_EXT, OVA_EXT))
1060 864cf6bf Agata Murawska
    logging.warning("Unpacking the %s archive, this may take a while",
1061 864cf6bf Agata Murawska
      input_path)
1062 864cf6bf Agata Murawska
    self.input_dir = temp_dir
1063 864cf6bf Agata Murawska
    self.input_path = utils.PathJoin(self.temp_dir, input_name)
1064 864cf6bf Agata Murawska
    try:
1065 864cf6bf Agata Murawska
      try:
1066 864cf6bf Agata Murawska
        extract = ova_content.extractall
1067 864cf6bf Agata Murawska
      except AttributeError:
1068 864cf6bf Agata Murawska
        # This is a prehistorical case of using python < 2.5
1069 864cf6bf Agata Murawska
        for member in ova_content.getmembers():
1070 864cf6bf Agata Murawska
          ova_content.extract(member, path=self.temp_dir)
1071 864cf6bf Agata Murawska
      else:
1072 864cf6bf Agata Murawska
        extract(self.temp_dir)
1073 864cf6bf Agata Murawska
    except tarfile.TarError, err:
1074 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Error while extracting %s archive: %s" %
1075 864cf6bf Agata Murawska
                                 (OVA_EXT, err))
1076 864cf6bf Agata Murawska
    logging.info("OVA package extracted to %s directory", self.temp_dir)
1077 ced78a66 Agata Murawska
1078 ced78a66 Agata Murawska
  def Parse(self):
1079 99381e3b Agata Murawska
    """Parses the data and creates a structure containing all required info.
1080 99381e3b Agata Murawska

1081 99381e3b Agata Murawska
    The method reads the information given either as a command line option or as
1082 99381e3b Agata Murawska
    a part of the OVF description.
1083 99381e3b Agata Murawska

1084 99381e3b Agata Murawska
    @raise errors.OpPrereqError: if some required part of the description of
1085 99381e3b Agata Murawska
      virtual instance is missing or unable to create output directory
1086 99381e3b Agata Murawska

1087 99381e3b Agata Murawska
    """
1088 99381e3b Agata Murawska
    self.results_name = self._GetInfo("instance name", self.options.name,
1089 99381e3b Agata Murawska
      self._ParseNameOptions, self.ovf_reader.GetInstanceName)
1090 99381e3b Agata Murawska
    if not self.results_name:
1091 99381e3b Agata Murawska
      raise errors.OpPrereqError("Name of instance not provided")
1092 99381e3b Agata Murawska
1093 99381e3b Agata Murawska
    self.output_dir = utils.PathJoin(self.output_dir, self.results_name)
1094 99381e3b Agata Murawska
    try:
1095 99381e3b Agata Murawska
      utils.Makedirs(self.output_dir)
1096 99381e3b Agata Murawska
    except OSError, err:
1097 99381e3b Agata Murawska
      raise errors.OpPrereqError("Failed to create directory %s: %s" %
1098 99381e3b Agata Murawska
                                 (self.output_dir, err))
1099 99381e3b Agata Murawska
1100 99381e3b Agata Murawska
    self.results_template = self._GetInfo("disk template",
1101 99381e3b Agata Murawska
      self.options.disk_template, self._ParseTemplateOptions,
1102 99381e3b Agata Murawska
      self.ovf_reader.GetDiskTemplate)
1103 99381e3b Agata Murawska
    if not self.results_template:
1104 99381e3b Agata Murawska
      logging.info("Disk template not given")
1105 99381e3b Agata Murawska
1106 7bde29b5 Agata Murawska
    self.results_hypervisor = self._GetInfo("hypervisor",
1107 7bde29b5 Agata Murawska
      self.options.hypervisor, self._ParseHypervisorOptions,
1108 7bde29b5 Agata Murawska
      self.ovf_reader.GetHypervisorData)
1109 7bde29b5 Agata Murawska
    assert self.results_hypervisor["hypervisor_name"]
1110 7bde29b5 Agata Murawska
    if self.results_hypervisor["hypervisor_name"] == constants.VALUE_AUTO:
1111 7bde29b5 Agata Murawska
      logging.debug("Default hypervisor settings from the cluster will be used")
1112 7bde29b5 Agata Murawska
1113 7bde29b5 Agata Murawska
    self.results_os = self._GetInfo("OS", self.options.os,
1114 7bde29b5 Agata Murawska
      self._ParseOSOptions, self.ovf_reader.GetOSData)
1115 7bde29b5 Agata Murawska
    if not self.results_os.get("os_name"):
1116 7bde29b5 Agata Murawska
      raise errors.OpPrereqError("OS name must be provided")
1117 7bde29b5 Agata Murawska
1118 7bde29b5 Agata Murawska
    self.results_backend = self._GetInfo("backend", self.options.beparams,
1119 7bde29b5 Agata Murawska
      self._ParseBackendOptions, self.ovf_reader.GetBackendData)
1120 7bde29b5 Agata Murawska
    assert self.results_backend.get("vcpus")
1121 7bde29b5 Agata Murawska
    assert self.results_backend.get("memory")
1122 7bde29b5 Agata Murawska
    assert self.results_backend.get("auto_balance") is not None
1123 7bde29b5 Agata Murawska
1124 7bde29b5 Agata Murawska
    self.results_tags = self._GetInfo("tags", self.options.tags,
1125 7bde29b5 Agata Murawska
      self._ParseTags, self.ovf_reader.GetTagsData)
1126 7bde29b5 Agata Murawska
1127 7bde29b5 Agata Murawska
    ovf_version = self.ovf_reader.GetVersionData()
1128 7bde29b5 Agata Murawska
    if ovf_version:
1129 7bde29b5 Agata Murawska
      self.results_version = ovf_version
1130 7bde29b5 Agata Murawska
    else:
1131 7bde29b5 Agata Murawska
      self.results_version = constants.EXPORT_VERSION
1132 7bde29b5 Agata Murawska
1133 24b9469d Agata Murawska
    self.results_network = self._GetInfo("network", self.options.nics,
1134 24b9469d Agata Murawska
      self._ParseNicOptions, self.ovf_reader.GetNetworkData,
1135 24b9469d Agata Murawska
      ignore_test=self.options.no_nics)
1136 24b9469d Agata Murawska
1137 99381e3b Agata Murawska
    self.results_disk = self._GetInfo("disk", self.options.disks,
1138 99381e3b Agata Murawska
      self._ParseDiskOptions, self._GetDiskInfo,
1139 99381e3b Agata Murawska
      ignore_test=self.results_template == constants.DT_DISKLESS)
1140 99381e3b Agata Murawska
1141 24b9469d Agata Murawska
    if not self.results_disk and not self.results_network:
1142 24b9469d Agata Murawska
      raise errors.OpPrereqError("Either disk specification or network"
1143 24b9469d Agata Murawska
                                 " description must be present")
1144 24b9469d Agata Murawska
1145 99381e3b Agata Murawska
  @staticmethod
1146 99381e3b Agata Murawska
  def _GetInfo(name, cmd_arg, cmd_function, nocmd_function,
1147 99381e3b Agata Murawska
    ignore_test=False):
1148 99381e3b Agata Murawska
    """Get information about some section - e.g. disk, network, hypervisor.
1149 99381e3b Agata Murawska

1150 99381e3b Agata Murawska
    @type name: string
1151 99381e3b Agata Murawska
    @param name: name of the section
1152 99381e3b Agata Murawska
    @type cmd_arg: dict
1153 99381e3b Agata Murawska
    @param cmd_arg: command line argument specific for section 'name'
1154 99381e3b Agata Murawska
    @type cmd_function: callable
1155 99381e3b Agata Murawska
    @param cmd_function: function to call if 'cmd_args' exists
1156 99381e3b Agata Murawska
    @type nocmd_function: callable
1157 99381e3b Agata Murawska
    @param nocmd_function: function to call if 'cmd_args' is not there
1158 99381e3b Agata Murawska

1159 99381e3b Agata Murawska
    """
1160 99381e3b Agata Murawska
    if ignore_test:
1161 99381e3b Agata Murawska
      logging.info("Information for %s will be ignored", name)
1162 99381e3b Agata Murawska
      return {}
1163 99381e3b Agata Murawska
    if cmd_arg:
1164 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from command line", name)
1165 99381e3b Agata Murawska
      results = cmd_function()
1166 99381e3b Agata Murawska
    else:
1167 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from %s file",
1168 99381e3b Agata Murawska
        name, OVF_EXT)
1169 99381e3b Agata Murawska
      results = nocmd_function()
1170 99381e3b Agata Murawska
    logging.info("Options for %s were succesfully read", name)
1171 99381e3b Agata Murawska
    return results
1172 99381e3b Agata Murawska
1173 99381e3b Agata Murawska
  def _ParseNameOptions(self):
1174 99381e3b Agata Murawska
    """Returns name if one was given in command line.
1175 99381e3b Agata Murawska

1176 99381e3b Agata Murawska
    @rtype: string
1177 99381e3b Agata Murawska
    @return: name of an instance
1178 99381e3b Agata Murawska

1179 99381e3b Agata Murawska
    """
1180 99381e3b Agata Murawska
    return self.options.name
1181 99381e3b Agata Murawska
1182 99381e3b Agata Murawska
  def _ParseTemplateOptions(self):
1183 99381e3b Agata Murawska
    """Returns disk template if one was given in command line.
1184 99381e3b Agata Murawska

1185 99381e3b Agata Murawska
    @rtype: string
1186 99381e3b Agata Murawska
    @return: disk template name
1187 99381e3b Agata Murawska

1188 99381e3b Agata Murawska
    """
1189 99381e3b Agata Murawska
    return self.options.disk_template
1190 99381e3b Agata Murawska
1191 7bde29b5 Agata Murawska
  def _ParseHypervisorOptions(self):
1192 7bde29b5 Agata Murawska
    """Parses hypervisor options given in a command line.
1193 7bde29b5 Agata Murawska

1194 7bde29b5 Agata Murawska
    @rtype: dict
1195 7bde29b5 Agata Murawska
    @return: dictionary containing name of the chosen hypervisor and all the
1196 7bde29b5 Agata Murawska
      options
1197 7bde29b5 Agata Murawska

1198 7bde29b5 Agata Murawska
    """
1199 7bde29b5 Agata Murawska
    assert type(self.options.hypervisor) is tuple
1200 7bde29b5 Agata Murawska
    assert len(self.options.hypervisor) == 2
1201 7bde29b5 Agata Murawska
    results = {}
1202 7bde29b5 Agata Murawska
    if self.options.hypervisor[0]:
1203 7bde29b5 Agata Murawska
      results["hypervisor_name"] = self.options.hypervisor[0]
1204 7bde29b5 Agata Murawska
    else:
1205 7bde29b5 Agata Murawska
      results["hypervisor_name"] = constants.VALUE_AUTO
1206 7bde29b5 Agata Murawska
    results.update(self.options.hypervisor[1])
1207 7bde29b5 Agata Murawska
    return results
1208 7bde29b5 Agata Murawska
1209 7bde29b5 Agata Murawska
  def _ParseOSOptions(self):
1210 7bde29b5 Agata Murawska
    """Parses OS options given in command line.
1211 7bde29b5 Agata Murawska

1212 7bde29b5 Agata Murawska
    @rtype: dict
1213 7bde29b5 Agata Murawska
    @return: dictionary containing name of chosen OS and all its options
1214 7bde29b5 Agata Murawska

1215 7bde29b5 Agata Murawska
    """
1216 7bde29b5 Agata Murawska
    assert self.options.os
1217 7bde29b5 Agata Murawska
    results = {}
1218 7bde29b5 Agata Murawska
    results["os_name"] = self.options.os
1219 7bde29b5 Agata Murawska
    results.update(self.options.osparams)
1220 7bde29b5 Agata Murawska
    return results
1221 7bde29b5 Agata Murawska
1222 7bde29b5 Agata Murawska
  def _ParseBackendOptions(self):
1223 7bde29b5 Agata Murawska
    """Parses backend options given in command line.
1224 7bde29b5 Agata Murawska

1225 7bde29b5 Agata Murawska
    @rtype: dict
1226 7bde29b5 Agata Murawska
    @return: dictionary containing vcpus, memory and auto-balance options
1227 7bde29b5 Agata Murawska

1228 7bde29b5 Agata Murawska
    """
1229 7bde29b5 Agata Murawska
    assert self.options.beparams
1230 7bde29b5 Agata Murawska
    backend = {}
1231 7bde29b5 Agata Murawska
    backend.update(self.options.beparams)
1232 7bde29b5 Agata Murawska
    must_contain = ["vcpus", "memory", "auto_balance"]
1233 7bde29b5 Agata Murawska
    for element in must_contain:
1234 7bde29b5 Agata Murawska
      if backend.get(element) is None:
1235 7bde29b5 Agata Murawska
        backend[element] = constants.VALUE_AUTO
1236 7bde29b5 Agata Murawska
    return backend
1237 7bde29b5 Agata Murawska
1238 7bde29b5 Agata Murawska
  def _ParseTags(self):
1239 7bde29b5 Agata Murawska
    """Returns tags list given in command line.
1240 7bde29b5 Agata Murawska

1241 7bde29b5 Agata Murawska
    @rtype: string
1242 7bde29b5 Agata Murawska
    @return: string containing comma-separated tags
1243 7bde29b5 Agata Murawska

1244 7bde29b5 Agata Murawska
    """
1245 7bde29b5 Agata Murawska
    return self.options.tags
1246 7bde29b5 Agata Murawska
1247 24b9469d Agata Murawska
  def _ParseNicOptions(self):
1248 24b9469d Agata Murawska
    """Parses network options given in a command line or as a dictionary.
1249 24b9469d Agata Murawska

1250 24b9469d Agata Murawska
    @rtype: dict
1251 24b9469d Agata Murawska
    @return: dictionary of network-related options
1252 24b9469d Agata Murawska

1253 24b9469d Agata Murawska
    """
1254 24b9469d Agata Murawska
    assert self.options.nics
1255 24b9469d Agata Murawska
    results = {}
1256 24b9469d Agata Murawska
    for (nic_id, nic_desc) in self.options.nics:
1257 24b9469d Agata Murawska
      results["nic%s_mode" % nic_id] = \
1258 24b9469d Agata Murawska
        nic_desc.get("mode", constants.VALUE_AUTO)
1259 24b9469d Agata Murawska
      results["nic%s_mac" % nic_id] = nic_desc.get("mac", constants.VALUE_AUTO)
1260 24b9469d Agata Murawska
      results["nic%s_link" % nic_id] = \
1261 24b9469d Agata Murawska
        nic_desc.get("link", constants.VALUE_AUTO)
1262 24b9469d Agata Murawska
      if nic_desc.get("mode") == "bridged":
1263 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_NONE
1264 24b9469d Agata Murawska
      else:
1265 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_AUTO
1266 24b9469d Agata Murawska
    results["nic_count"] = str(len(self.options.nics))
1267 24b9469d Agata Murawska
    return results
1268 24b9469d Agata Murawska
1269 99381e3b Agata Murawska
  def _ParseDiskOptions(self):
1270 99381e3b Agata Murawska
    """Parses disk options given in a command line.
1271 99381e3b Agata Murawska

1272 99381e3b Agata Murawska
    @rtype: dict
1273 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1274 99381e3b Agata Murawska

1275 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk description does not contain size
1276 99381e3b Agata Murawska
      information or size information is invalid or creation failed
1277 99381e3b Agata Murawska

1278 99381e3b Agata Murawska
    """
1279 99381e3b Agata Murawska
    assert self.options.disks
1280 99381e3b Agata Murawska
    results = {}
1281 99381e3b Agata Murawska
    for (disk_id, disk_desc) in self.options.disks:
1282 99381e3b Agata Murawska
      results["disk%s_ivname" % disk_id] = "disk/%s" % disk_id
1283 99381e3b Agata Murawska
      if disk_desc.get("size"):
1284 99381e3b Agata Murawska
        try:
1285 99381e3b Agata Murawska
          disk_size = utils.ParseUnit(disk_desc["size"])
1286 99381e3b Agata Murawska
        except ValueError:
1287 99381e3b Agata Murawska
          raise errors.OpPrereqError("Invalid disk size for disk %s: %s" %
1288 99381e3b Agata Murawska
                                     (disk_id, disk_desc["size"]))
1289 99381e3b Agata Murawska
        new_path = utils.PathJoin(self.output_dir, str(disk_id))
1290 99381e3b Agata Murawska
        args = [
1291 99381e3b Agata Murawska
          "qemu-img",
1292 99381e3b Agata Murawska
          "create",
1293 99381e3b Agata Murawska
          "-f",
1294 99381e3b Agata Murawska
          "raw",
1295 99381e3b Agata Murawska
          new_path,
1296 99381e3b Agata Murawska
          disk_size,
1297 99381e3b Agata Murawska
        ]
1298 99381e3b Agata Murawska
        run_result = utils.RunCmd(args)
1299 99381e3b Agata Murawska
        if run_result.failed:
1300 99381e3b Agata Murawska
          raise errors.OpPrereqError("Creation of disk %s failed, output was:"
1301 99381e3b Agata Murawska
                                     " %s" % (new_path, run_result.stderr))
1302 99381e3b Agata Murawska
        results["disk%s_size" % disk_id] = str(disk_size)
1303 99381e3b Agata Murawska
        results["disk%s_dump" % disk_id] = "disk%s.raw" % disk_id
1304 99381e3b Agata Murawska
      else:
1305 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks created for import must have their"
1306 99381e3b Agata Murawska
                                   " size specified")
1307 99381e3b Agata Murawska
    results["disk_count"] = str(len(self.options.disks))
1308 99381e3b Agata Murawska
    return results
1309 99381e3b Agata Murawska
1310 99381e3b Agata Murawska
  def _GetDiskInfo(self):
1311 99381e3b Agata Murawska
    """Gathers information about disks used by instance, perfomes conversion.
1312 99381e3b Agata Murawska

1313 99381e3b Agata Murawska
    @rtype: dict
1314 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1315 99381e3b Agata Murawska

1316 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk is not in the same directory as .ovf file
1317 99381e3b Agata Murawska

1318 99381e3b Agata Murawska
    """
1319 99381e3b Agata Murawska
    results = {}
1320 99381e3b Agata Murawska
    disks_list = self.ovf_reader.GetDisksNames()
1321 99381e3b Agata Murawska
    for (counter, (disk_name, disk_compression)) in enumerate(disks_list):
1322 99381e3b Agata Murawska
      if os.path.dirname(disk_name):
1323 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks are not allowed to have absolute"
1324 99381e3b Agata Murawska
                                   " paths or paths outside main OVF directory")
1325 99381e3b Agata Murawska
      disk, _ = os.path.splitext(disk_name)
1326 99381e3b Agata Murawska
      disk_path = utils.PathJoin(self.input_dir, disk_name)
1327 99381e3b Agata Murawska
      if disk_compression:
1328 99381e3b Agata Murawska
        _, disk_path = self._CompressDisk(disk_path, disk_compression,
1329 99381e3b Agata Murawska
          DECOMPRESS)
1330 99381e3b Agata Murawska
        disk, _ = os.path.splitext(disk)
1331 99381e3b Agata Murawska
      if self._GetDiskQemuInfo(disk_path, "file format: (\S+)") != "raw":
1332 99381e3b Agata Murawska
        logging.info("Conversion to raw format is required")
1333 99381e3b Agata Murawska
      ext, new_disk_path = self._ConvertDisk("raw", disk_path)
1334 99381e3b Agata Murawska
1335 99381e3b Agata Murawska
      final_disk_path = LinkFile(new_disk_path, prefix=disk, suffix=ext,
1336 99381e3b Agata Murawska
        directory=self.output_dir)
1337 99381e3b Agata Murawska
      final_name = os.path.basename(final_disk_path)
1338 99381e3b Agata Murawska
      disk_size = os.path.getsize(final_disk_path) / (1024 * 1024)
1339 99381e3b Agata Murawska
      results["disk%s_dump" % counter] = final_name
1340 99381e3b Agata Murawska
      results["disk%s_size" % counter] = str(disk_size)
1341 99381e3b Agata Murawska
      results["disk%s_ivname" % counter] = "disk/%s" % str(counter)
1342 99381e3b Agata Murawska
    if disks_list:
1343 99381e3b Agata Murawska
      results["disk_count"] = str(len(disks_list))
1344 99381e3b Agata Murawska
    return results
1345 ced78a66 Agata Murawska
1346 ced78a66 Agata Murawska
  def Save(self):
1347 864cf6bf Agata Murawska
    """Saves all the gathered information in a constant.EXPORT_CONF_FILE file.
1348 864cf6bf Agata Murawska

1349 864cf6bf Agata Murawska
    @raise errors.OpPrereqError: when saving to config file failed
1350 864cf6bf Agata Murawska

1351 864cf6bf Agata Murawska
    """
1352 864cf6bf Agata Murawska
    logging.info("Conversion was succesfull, saving %s in %s directory",
1353 864cf6bf Agata Murawska
                 constants.EXPORT_CONF_FILE, self.output_dir)
1354 864cf6bf Agata Murawska
    results = {
1355 864cf6bf Agata Murawska
      constants.INISECT_INS: {},
1356 864cf6bf Agata Murawska
      constants.INISECT_BEP: {},
1357 864cf6bf Agata Murawska
      constants.INISECT_EXP: {},
1358 864cf6bf Agata Murawska
      constants.INISECT_OSP: {},
1359 864cf6bf Agata Murawska
      constants.INISECT_HYP: {},
1360 864cf6bf Agata Murawska
    }
1361 864cf6bf Agata Murawska
1362 864cf6bf Agata Murawska
    results[constants.INISECT_INS].update(self.results_disk)
1363 24b9469d Agata Murawska
    results[constants.INISECT_INS].update(self.results_network)
1364 7bde29b5 Agata Murawska
    results[constants.INISECT_INS]["hypervisor"] = \
1365 7bde29b5 Agata Murawska
      self.results_hypervisor["hypervisor_name"]
1366 864cf6bf Agata Murawska
    results[constants.INISECT_INS]["name"] = self.results_name
1367 864cf6bf Agata Murawska
    if self.results_template:
1368 864cf6bf Agata Murawska
      results[constants.INISECT_INS]["disk_template"] = self.results_template
1369 7bde29b5 Agata Murawska
    if self.results_tags:
1370 7bde29b5 Agata Murawska
      results[constants.INISECT_INS]["tags"] = self.results_tags
1371 7bde29b5 Agata Murawska
1372 7bde29b5 Agata Murawska
    results[constants.INISECT_BEP].update(self.results_backend)
1373 7bde29b5 Agata Murawska
1374 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["os"] = self.results_os["os_name"]
1375 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["version"] = self.results_version
1376 7bde29b5 Agata Murawska
1377 7bde29b5 Agata Murawska
    del self.results_os["os_name"]
1378 7bde29b5 Agata Murawska
    results[constants.INISECT_OSP].update(self.results_os)
1379 7bde29b5 Agata Murawska
1380 7bde29b5 Agata Murawska
    del self.results_hypervisor["hypervisor_name"]
1381 7bde29b5 Agata Murawska
    results[constants.INISECT_HYP].update(self.results_hypervisor)
1382 864cf6bf Agata Murawska
1383 864cf6bf Agata Murawska
    output_file_name = utils.PathJoin(self.output_dir,
1384 864cf6bf Agata Murawska
      constants.EXPORT_CONF_FILE)
1385 864cf6bf Agata Murawska
1386 864cf6bf Agata Murawska
    output = []
1387 864cf6bf Agata Murawska
    for section, options in results.iteritems():
1388 864cf6bf Agata Murawska
      output.append("[%s]" % section)
1389 864cf6bf Agata Murawska
      for name, value in options.iteritems():
1390 99381e3b Agata Murawska
        if value is None:
1391 99381e3b Agata Murawska
          value = ""
1392 864cf6bf Agata Murawska
        output.append("%s = %s" % (name, value))
1393 864cf6bf Agata Murawska
      output.append("")
1394 864cf6bf Agata Murawska
    output_contents = "\n".join(output)
1395 864cf6bf Agata Murawska
1396 864cf6bf Agata Murawska
    try:
1397 864cf6bf Agata Murawska
      utils.WriteFile(output_file_name, data=output_contents)
1398 864cf6bf Agata Murawska
    except errors.ProgrammerError, err:
1399 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Saving the config file failed: %s" % err)
1400 864cf6bf Agata Murawska
1401 864cf6bf Agata Murawska
    self.Cleanup()
1402 ced78a66 Agata Murawska
1403 ced78a66 Agata Murawska
1404 0963b26a Agata Murawska
class ConfigParserWithDefaults(ConfigParser.SafeConfigParser):
1405 0963b26a Agata Murawska
  """This is just a wrapper on SafeConfigParser, that uses default values
1406 0963b26a Agata Murawska

1407 0963b26a Agata Murawska
  """
1408 0963b26a Agata Murawska
  def get(self, section, options, raw=None, vars=None): # pylint: disable=W0622
1409 0963b26a Agata Murawska
    try:
1410 0963b26a Agata Murawska
      result = ConfigParser.SafeConfigParser.get(self, section, options, \
1411 0963b26a Agata Murawska
        raw=raw, vars=vars)
1412 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1413 0963b26a Agata Murawska
      result = None
1414 0963b26a Agata Murawska
    return result
1415 0963b26a Agata Murawska
1416 0963b26a Agata Murawska
  def getint(self, section, options):
1417 0963b26a Agata Murawska
    try:
1418 0963b26a Agata Murawska
      result = ConfigParser.SafeConfigParser.get(self, section, options)
1419 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1420 0963b26a Agata Murawska
      result = 0
1421 0963b26a Agata Murawska
    return int(result)
1422 0963b26a Agata Murawska
1423 0963b26a Agata Murawska
1424 ced78a66 Agata Murawska
class OVFExporter(Converter):
1425 0963b26a Agata Murawska
  """Converter from Ganeti config file to OVF
1426 0963b26a Agata Murawska

1427 0963b26a Agata Murawska
  @type input_dir: string
1428 0963b26a Agata Murawska
  @ivar input_dir: directory in which the config.ini file resides
1429 0963b26a Agata Murawska
  @type output_dir: string
1430 0963b26a Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
1431 0963b26a Agata Murawska
    written
1432 0963b26a Agata Murawska
  @type packed_dir: string
1433 0963b26a Agata Murawska
  @ivar packed_dir: if we want OVA package, this points to the real (i.e. not
1434 0963b26a Agata Murawska
    temp) output directory
1435 0963b26a Agata Murawska
  @type input_path: string
1436 0963b26a Agata Murawska
  @ivar input_path: complete path to the config.ini file
1437 0963b26a Agata Murawska
  @type output_path: string
1438 0963b26a Agata Murawska
  @ivar output_path: complete path to .ovf file
1439 0963b26a Agata Murawska
  @type config_parser: L{ConfigParserWithDefaults}
1440 0963b26a Agata Murawska
  @ivar config_parser: parser for the config.ini file
1441 7432d332 Agata Murawska
  @type reference_files: list
1442 7432d332 Agata Murawska
  @ivar reference_files: files referenced in the ovf file
1443 b179ce72 Agata Murawska
  @type results_disk: list
1444 b179ce72 Agata Murawska
  @ivar results_disk: list of dictionaries of disk options from config.ini
1445 b179ce72 Agata Murawska
  @type results_network: list
1446 b179ce72 Agata Murawska
  @ivar results_network: list of dictionaries of network options form config.ini
1447 0963b26a Agata Murawska
  @type results_name: string
1448 0963b26a Agata Murawska
  @ivar results_name: name of the instance
1449 b179ce72 Agata Murawska
  @type results_vcpus: string
1450 b179ce72 Agata Murawska
  @ivar results_vcpus: number of VCPUs
1451 b179ce72 Agata Murawska
  @type results_memory: string
1452 b179ce72 Agata Murawska
  @ivar results_memory: RAM memory in MB
1453 b179ce72 Agata Murawska
  @type results_ganeti: dict
1454 b179ce72 Agata Murawska
  @ivar results_ganeti: dictionary of Ganeti-specific options from config.ini
1455 0963b26a Agata Murawska

1456 0963b26a Agata Murawska
  """
1457 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
1458 0963b26a Agata Murawska
    """Reads the data on which the conversion will take place.
1459 0963b26a Agata Murawska

1460 0963b26a Agata Murawska
    @type input_path: string
1461 0963b26a Agata Murawska
    @param input_path: absolute path to the config.ini input file
1462 0963b26a Agata Murawska

1463 0963b26a Agata Murawska
    @raise errors.OpPrereqError: error when reading the config file
1464 0963b26a Agata Murawska

1465 0963b26a Agata Murawska
    """
1466 0963b26a Agata Murawska
    input_dir = os.path.dirname(input_path)
1467 0963b26a Agata Murawska
    self.input_path = input_path
1468 0963b26a Agata Murawska
    self.input_dir = input_dir
1469 0963b26a Agata Murawska
    if self.options.output_dir:
1470 0963b26a Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1471 0963b26a Agata Murawska
    else:
1472 0963b26a Agata Murawska
      self.output_dir = input_dir
1473 0963b26a Agata Murawska
    self.config_parser = ConfigParserWithDefaults()
1474 0963b26a Agata Murawska
    logging.info("Reading configuration from %s file", input_path)
1475 0963b26a Agata Murawska
    try:
1476 0963b26a Agata Murawska
      self.config_parser.read(input_path)
1477 0963b26a Agata Murawska
    except ConfigParser.MissingSectionHeaderError, err:
1478 0963b26a Agata Murawska
      raise errors.OpPrereqError("Error when trying to read %s: %s" %
1479 0963b26a Agata Murawska
                                 (input_path, err))
1480 0963b26a Agata Murawska
    if self.options.ova_package:
1481 0963b26a Agata Murawska
      self.temp_dir = tempfile.mkdtemp()
1482 0963b26a Agata Murawska
      self.packed_dir = self.output_dir
1483 0963b26a Agata Murawska
      self.output_dir = self.temp_dir
1484 0963b26a Agata Murawska
1485 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1486 0963b26a Agata Murawska
1487 0963b26a Agata Murawska
  def _ParseName(self):
1488 0963b26a Agata Murawska
    """Parses name from command line options or config file.
1489 0963b26a Agata Murawska

1490 0963b26a Agata Murawska
    @rtype: string
1491 0963b26a Agata Murawska
    @return: name of Ganeti instance
1492 0963b26a Agata Murawska

1493 0963b26a Agata Murawska
    @raise errors.OpPrereqError: if name of the instance is not provided
1494 0963b26a Agata Murawska

1495 0963b26a Agata Murawska
    """
1496 0963b26a Agata Murawska
    if self.options.name:
1497 0963b26a Agata Murawska
      name = self.options.name
1498 0963b26a Agata Murawska
    else:
1499 0963b26a Agata Murawska
      name = self.config_parser.get(constants.INISECT_INS, NAME)
1500 0963b26a Agata Murawska
    if name is None:
1501 0963b26a Agata Murawska
      raise errors.OpPrereqError("No instance name found")
1502 0963b26a Agata Murawska
    return name
1503 ced78a66 Agata Murawska
1504 b179ce72 Agata Murawska
  def _ParseVCPUs(self):
1505 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1506 b179ce72 Agata Murawska

1507 b179ce72 Agata Murawska
    @rtype: int
1508 b179ce72 Agata Murawska
    @return: number of virtual CPUs
1509 b179ce72 Agata Murawska

1510 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if number of VCPUs equals 0
1511 b179ce72 Agata Murawska

1512 b179ce72 Agata Murawska
    """
1513 b179ce72 Agata Murawska
    vcpus = self.config_parser.getint(constants.INISECT_BEP, VCPUS)
1514 b179ce72 Agata Murawska
    if vcpus == 0:
1515 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No CPU information found")
1516 b179ce72 Agata Murawska
    return vcpus
1517 b179ce72 Agata Murawska
1518 b179ce72 Agata Murawska
  def _ParseMemory(self):
1519 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1520 b179ce72 Agata Murawska

1521 b179ce72 Agata Murawska
    @rtype: int
1522 b179ce72 Agata Murawska
    @return: amount of memory in MB
1523 b179ce72 Agata Murawska

1524 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if amount of memory equals 0
1525 b179ce72 Agata Murawska

1526 b179ce72 Agata Murawska
    """
1527 b179ce72 Agata Murawska
    memory = self.config_parser.getint(constants.INISECT_BEP, MEMORY)
1528 b179ce72 Agata Murawska
    if memory == 0:
1529 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No memory information found")
1530 b179ce72 Agata Murawska
    return memory
1531 b179ce72 Agata Murawska
1532 b179ce72 Agata Murawska
  def _ParseGaneti(self):
1533 b179ce72 Agata Murawska
    """Parses Ganeti data from config file.
1534 b179ce72 Agata Murawska

1535 b179ce72 Agata Murawska
    @rtype: dictionary
1536 b179ce72 Agata Murawska
    @return: dictionary of Ganeti-specific options
1537 b179ce72 Agata Murawska

1538 b179ce72 Agata Murawska
    """
1539 b179ce72 Agata Murawska
    results = {}
1540 b179ce72 Agata Murawska
    # hypervisor
1541 b179ce72 Agata Murawska
    results["hypervisor"] = {}
1542 b179ce72 Agata Murawska
    hyp_name = self.config_parser.get(constants.INISECT_INS, HYPERV)
1543 b179ce72 Agata Murawska
    if hyp_name is None:
1544 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No hypervisor information found")
1545 b179ce72 Agata Murawska
    results["hypervisor"]["name"] = hyp_name
1546 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_HYP)
1547 b179ce72 Agata Murawska
    for (name, value) in pairs:
1548 b179ce72 Agata Murawska
      results["hypervisor"][name] = value
1549 b179ce72 Agata Murawska
    # os
1550 b179ce72 Agata Murawska
    results["os"] = {}
1551 b179ce72 Agata Murawska
    os_name = self.config_parser.get(constants.INISECT_EXP, OS)
1552 b179ce72 Agata Murawska
    if os_name is None:
1553 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No operating system information found")
1554 b179ce72 Agata Murawska
    results["os"]["name"] = os_name
1555 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_OSP)
1556 b179ce72 Agata Murawska
    for (name, value) in pairs:
1557 b179ce72 Agata Murawska
      results["os"][name] = value
1558 b179ce72 Agata Murawska
    # other
1559 b179ce72 Agata Murawska
    others = [
1560 b179ce72 Agata Murawska
      (constants.INISECT_INS, DISK_TEMPLATE, "disk_template"),
1561 b179ce72 Agata Murawska
      (constants.INISECT_BEP, AUTO_BALANCE, "auto_balance"),
1562 b179ce72 Agata Murawska
      (constants.INISECT_INS, TAGS, "tags"),
1563 b179ce72 Agata Murawska
      (constants.INISECT_EXP, VERSION, "version"),
1564 b179ce72 Agata Murawska
    ]
1565 b179ce72 Agata Murawska
    for (section, element, name) in others:
1566 b179ce72 Agata Murawska
      results[name] = self.config_parser.get(section, element)
1567 b179ce72 Agata Murawska
    return results
1568 b179ce72 Agata Murawska
1569 b179ce72 Agata Murawska
  def _ParseNetworks(self):
1570 b179ce72 Agata Murawska
    """Parses network data from config file.
1571 b179ce72 Agata Murawska

1572 b179ce72 Agata Murawska
    @rtype: list
1573 b179ce72 Agata Murawska
    @return: list of dictionaries of network options
1574 b179ce72 Agata Murawska

1575 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: then network mode is not recognized
1576 b179ce72 Agata Murawska

1577 b179ce72 Agata Murawska
    """
1578 b179ce72 Agata Murawska
    results = []
1579 b179ce72 Agata Murawska
    counter = 0
1580 b179ce72 Agata Murawska
    while True:
1581 b179ce72 Agata Murawska
      data_link = \
1582 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "nic%s_link" % counter)
1583 b179ce72 Agata Murawska
      if data_link is None:
1584 b179ce72 Agata Murawska
        break
1585 b179ce72 Agata Murawska
      results.append({
1586 b179ce72 Agata Murawska
        "mode": self.config_parser.get(constants.INISECT_INS,
1587 b179ce72 Agata Murawska
           "nic%s_mode" % counter),
1588 b179ce72 Agata Murawska
        "mac": self.config_parser.get(constants.INISECT_INS,
1589 b179ce72 Agata Murawska
           "nic%s_mac" % counter),
1590 b179ce72 Agata Murawska
        "ip": self.config_parser.get(constants.INISECT_INS,
1591 b179ce72 Agata Murawska
           "nic%s_ip" % counter),
1592 b179ce72 Agata Murawska
        "link": data_link,
1593 b179ce72 Agata Murawska
      })
1594 b179ce72 Agata Murawska
      if results[counter]["mode"] not in constants.NIC_VALID_MODES:
1595 b179ce72 Agata Murawska
        raise errors.OpPrereqError("Network mode %s not recognized"
1596 b179ce72 Agata Murawska
                                   % results[counter]["mode"])
1597 b179ce72 Agata Murawska
      counter += 1
1598 b179ce72 Agata Murawska
    return results
1599 b179ce72 Agata Murawska
1600 b179ce72 Agata Murawska
  def _GetDiskOptions(self, disk_file, compression):
1601 b179ce72 Agata Murawska
    """Convert the disk and gather disk info for .ovf file.
1602 b179ce72 Agata Murawska

1603 b179ce72 Agata Murawska
    @type disk_file: string
1604 b179ce72 Agata Murawska
    @param disk_file: name of the disk (without the full path)
1605 b179ce72 Agata Murawska
    @type compression: bool
1606 b179ce72 Agata Murawska
    @param compression: whether the disk should be compressed or not
1607 b179ce72 Agata Murawska

1608 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: when disk image does not exist
1609 b179ce72 Agata Murawska

1610 b179ce72 Agata Murawska
    """
1611 b179ce72 Agata Murawska
    disk_path = utils.PathJoin(self.input_dir, disk_file)
1612 b179ce72 Agata Murawska
    results = {}
1613 b179ce72 Agata Murawska
    if not os.path.isfile(disk_path):
1614 b179ce72 Agata Murawska
      raise errors.OpPrereqError("Disk image does not exist: %s" % disk_path)
1615 b179ce72 Agata Murawska
    if os.path.dirname(disk_file):
1616 b179ce72 Agata Murawska
      raise errors.OpPrereqError("Path for the disk: %s contains a directory"
1617 b179ce72 Agata Murawska
                                 " name" % disk_path)
1618 b179ce72 Agata Murawska
    disk_name, _ = os.path.splitext(disk_file)
1619 b179ce72 Agata Murawska
    ext, new_disk_path = self._ConvertDisk(self.options.disk_format, disk_path)
1620 b179ce72 Agata Murawska
    results["format"] = self.options.disk_format
1621 b179ce72 Agata Murawska
    results["virt-size"] = self._GetDiskQemuInfo(new_disk_path,
1622 b179ce72 Agata Murawska
      "virtual size: \S+ \((\d+) bytes\)")
1623 b179ce72 Agata Murawska
    if compression:
1624 b179ce72 Agata Murawska
      ext2, new_disk_path = self._CompressDisk(new_disk_path, "gzip",
1625 b179ce72 Agata Murawska
        COMPRESS)
1626 b179ce72 Agata Murawska
      disk_name, _ = os.path.splitext(disk_name)
1627 b179ce72 Agata Murawska
      results["compression"] = "gzip"
1628 b179ce72 Agata Murawska
      ext += ext2
1629 b179ce72 Agata Murawska
    final_disk_path = LinkFile(new_disk_path, prefix=disk_name, suffix=ext,
1630 b179ce72 Agata Murawska
      directory=self.output_dir)
1631 b179ce72 Agata Murawska
    final_disk_name = os.path.basename(final_disk_path)
1632 b179ce72 Agata Murawska
    results["real-size"] = os.path.getsize(final_disk_path)
1633 b179ce72 Agata Murawska
    results["path"] = final_disk_name
1634 b179ce72 Agata Murawska
    self.references_files.append(final_disk_path)
1635 b179ce72 Agata Murawska
    return results
1636 b179ce72 Agata Murawska
1637 b179ce72 Agata Murawska
  def _ParseDisks(self):
1638 b179ce72 Agata Murawska
    """Parses disk data from config file.
1639 b179ce72 Agata Murawska

1640 b179ce72 Agata Murawska
    @rtype: list
1641 b179ce72 Agata Murawska
    @return: list of dictionaries of disk options
1642 b179ce72 Agata Murawska

1643 b179ce72 Agata Murawska
    """
1644 b179ce72 Agata Murawska
    results = []
1645 b179ce72 Agata Murawska
    counter = 0
1646 b179ce72 Agata Murawska
    while True:
1647 b179ce72 Agata Murawska
      disk_file = \
1648 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "disk%s_dump" % counter)
1649 b179ce72 Agata Murawska
      if disk_file is None:
1650 b179ce72 Agata Murawska
        break
1651 b179ce72 Agata Murawska
      results.append(self._GetDiskOptions(disk_file, self.options.compression))
1652 b179ce72 Agata Murawska
      counter += 1
1653 b179ce72 Agata Murawska
    return results
1654 b179ce72 Agata Murawska
1655 ced78a66 Agata Murawska
  def Parse(self):
1656 0963b26a Agata Murawska
    """Parses the data and creates a structure containing all required info.
1657 0963b26a Agata Murawska

1658 0963b26a Agata Murawska
    """
1659 0963b26a Agata Murawska
    try:
1660 0963b26a Agata Murawska
      utils.Makedirs(self.output_dir)
1661 0963b26a Agata Murawska
    except OSError, err:
1662 0963b26a Agata Murawska
      raise errors.OpPrereqError("Failed to create directory %s: %s" %
1663 0963b26a Agata Murawska
                                 (self.output_dir, err))
1664 0963b26a Agata Murawska
1665 b179ce72 Agata Murawska
    self.references_files = []
1666 0963b26a Agata Murawska
    self.results_name = self._ParseName()
1667 b179ce72 Agata Murawska
    self.results_vcpus = self._ParseVCPUs()
1668 b179ce72 Agata Murawska
    self.results_memory = self._ParseMemory()
1669 b179ce72 Agata Murawska
    if not self.options.ext_usage:
1670 b179ce72 Agata Murawska
      self.results_ganeti = self._ParseGaneti()
1671 b179ce72 Agata Murawska
    self.results_network = self._ParseNetworks()
1672 b179ce72 Agata Murawska
    self.results_disk = self._ParseDisks()
1673 0963b26a Agata Murawska
1674 0963b26a Agata Murawska
  def _PrepareManifest(self, path):
1675 0963b26a Agata Murawska
    """Creates manifest for all the files in OVF package.
1676 0963b26a Agata Murawska

1677 0963b26a Agata Murawska
    @type path: string
1678 0963b26a Agata Murawska
    @param path: path to manifesto file
1679 0963b26a Agata Murawska

1680 0963b26a Agata Murawska
    @raise errors.OpPrereqError: if error occurs when writing file
1681 0963b26a Agata Murawska

1682 0963b26a Agata Murawska
    """
1683 0963b26a Agata Murawska
    logging.info("Preparing manifest for the OVF package")
1684 0963b26a Agata Murawska
    lines = []
1685 0963b26a Agata Murawska
    files_list = [self.output_path]
1686 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1687 0963b26a Agata Murawska
    logging.warning("Calculating SHA1 checksums, this may take a while")
1688 0963b26a Agata Murawska
    sha1_sums = utils.FingerprintFiles(files_list)
1689 0963b26a Agata Murawska
    for file_path, value in sha1_sums.iteritems():
1690 0963b26a Agata Murawska
      file_name = os.path.basename(file_path)
1691 0963b26a Agata Murawska
      lines.append("SHA1(%s)= %s" % (file_name, value))
1692 0963b26a Agata Murawska
    lines.append("")
1693 0963b26a Agata Murawska
    data = "\n".join(lines)
1694 0963b26a Agata Murawska
    try:
1695 0963b26a Agata Murawska
      utils.WriteFile(path, data=data)
1696 0963b26a Agata Murawska
    except errors.ProgrammerError, err:
1697 0963b26a Agata Murawska
      raise errors.OpPrereqError("Saving the manifest file failed: %s" % err)
1698 0963b26a Agata Murawska
1699 0963b26a Agata Murawska
  @staticmethod
1700 0963b26a Agata Murawska
  def _PrepareTarFile(tar_path, files_list):
1701 0963b26a Agata Murawska
    """Creates tarfile from the files in OVF package.
1702 0963b26a Agata Murawska

1703 0963b26a Agata Murawska
    @type tar_path: string
1704 0963b26a Agata Murawska
    @param tar_path: path to the resulting file
1705 0963b26a Agata Murawska
    @type files_list: list
1706 0963b26a Agata Murawska
    @param files_list: list of files in the OVF package
1707 0963b26a Agata Murawska

1708 0963b26a Agata Murawska
    """
1709 0963b26a Agata Murawska
    logging.info("Preparing tarball for the OVF package")
1710 0963b26a Agata Murawska
    open(tar_path, mode="w").close()
1711 0963b26a Agata Murawska
    ova_package = tarfile.open(name=tar_path, mode="w")
1712 0963b26a Agata Murawska
    for file_name in files_list:
1713 0963b26a Agata Murawska
      ova_package.add(file_name)
1714 0963b26a Agata Murawska
    ova_package.close()
1715 ced78a66 Agata Murawska
1716 ced78a66 Agata Murawska
  def Save(self):
1717 0963b26a Agata Murawska
    """Saves the gathered configuration in an apropriate format.
1718 0963b26a Agata Murawska

1719 0963b26a Agata Murawska
    @raise errors.OpPrereqError: if unable to create output directory
1720 0963b26a Agata Murawska

1721 0963b26a Agata Murawska
    """
1722 0963b26a Agata Murawska
    output_file = "%s%s" % (self.results_name, OVF_EXT)
1723 0963b26a Agata Murawska
    output_path = utils.PathJoin(self.output_dir, output_file)
1724 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1725 0963b26a Agata Murawska
    logging.info("Saving read data to %s", output_path)
1726 0963b26a Agata Murawska
1727 0963b26a Agata Murawska
    self.output_path = utils.PathJoin(self.output_dir, output_file)
1728 0963b26a Agata Murawska
    files_list = [self.output_path]
1729 0963b26a Agata Murawska
1730 7432d332 Agata Murawska
    self.ovf_writer.SaveDisksData(self.results_disk)
1731 7432d332 Agata Murawska
    self.ovf_writer.SaveNetworksData(self.results_network)
1732 7432d332 Agata Murawska
    if not self.options.ext_usage:
1733 7432d332 Agata Murawska
      self.ovf_writer.SaveGanetiData(self.results_ganeti, self.results_network)
1734 7432d332 Agata Murawska
1735 7432d332 Agata Murawska
    self.ovf_writer.SaveVirtualSystemData(self.results_name, self.results_vcpus,
1736 7432d332 Agata Murawska
      self.results_memory)
1737 7432d332 Agata Murawska
1738 0963b26a Agata Murawska
    data = self.ovf_writer.PrettyXmlDump()
1739 0963b26a Agata Murawska
    utils.WriteFile(self.output_path, data=data)
1740 0963b26a Agata Murawska
1741 0963b26a Agata Murawska
    manifest_file = "%s%s" % (self.results_name, MF_EXT)
1742 0963b26a Agata Murawska
    manifest_path = utils.PathJoin(self.output_dir, manifest_file)
1743 0963b26a Agata Murawska
    self._PrepareManifest(manifest_path)
1744 0963b26a Agata Murawska
    files_list.append(manifest_path)
1745 0963b26a Agata Murawska
1746 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1747 0963b26a Agata Murawska
1748 0963b26a Agata Murawska
    if self.options.ova_package:
1749 0963b26a Agata Murawska
      ova_file = "%s%s" % (self.results_name, OVA_EXT)
1750 0963b26a Agata Murawska
      packed_path = utils.PathJoin(self.packed_dir, ova_file)
1751 0963b26a Agata Murawska
      try:
1752 0963b26a Agata Murawska
        utils.Makedirs(self.packed_dir)
1753 0963b26a Agata Murawska
      except OSError, err:
1754 0963b26a Agata Murawska
        raise errors.OpPrereqError("Failed to create directory %s: %s" %
1755 0963b26a Agata Murawska
                                   (self.packed_dir, err))
1756 0963b26a Agata Murawska
      self._PrepareTarFile(packed_path, files_list)
1757 0963b26a Agata Murawska
    logging.info("Creation of the OVF package was successfull")
1758 0963b26a Agata Murawska
    self.Cleanup()