Statistics
| Branch: | Tag: | Revision:

root / lib / ovf.py @ f2f57b6e

History | View | Annotate | Download (63 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 d6f8db24 Guido Trotter
try:
49 d6f8db24 Guido Trotter
  ParseError = ET.ParseError # pylint: disable=E1103
50 d6f8db24 Guido Trotter
except AttributeError:
51 d6f8db24 Guido Trotter
  ParseError = None
52 d6f8db24 Guido Trotter
53 864cf6bf Agata Murawska
from ganeti import constants
54 ced78a66 Agata Murawska
from ganeti import errors
55 ced78a66 Agata Murawska
from ganeti import utils
56 ced78a66 Agata Murawska
57 ced78a66 Agata Murawska
58 864cf6bf Agata Murawska
# Schemas used in OVF format
59 864cf6bf Agata Murawska
GANETI_SCHEMA = "http://ganeti"
60 864cf6bf Agata Murawska
OVF_SCHEMA = "http://schemas.dmtf.org/ovf/envelope/1"
61 864cf6bf Agata Murawska
RASD_SCHEMA = ("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/"
62 864cf6bf Agata Murawska
               "CIM_ResourceAllocationSettingData")
63 0963b26a Agata Murawska
VSSD_SCHEMA = ("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/"
64 0963b26a Agata Murawska
               "CIM_VirtualSystemSettingData")
65 0963b26a Agata Murawska
XML_SCHEMA = "http://www.w3.org/2001/XMLSchema-instance"
66 864cf6bf Agata Murawska
67 864cf6bf Agata Murawska
# File extensions in OVF package
68 864cf6bf Agata Murawska
OVA_EXT = ".ova"
69 864cf6bf Agata Murawska
OVF_EXT = ".ovf"
70 864cf6bf Agata Murawska
MF_EXT = ".mf"
71 864cf6bf Agata Murawska
CERT_EXT = ".cert"
72 99381e3b Agata Murawska
COMPRESSION_EXT = ".gz"
73 864cf6bf Agata Murawska
FILE_EXTENSIONS = [
74 864cf6bf Agata Murawska
  OVF_EXT,
75 864cf6bf Agata Murawska
  MF_EXT,
76 864cf6bf Agata Murawska
  CERT_EXT,
77 864cf6bf Agata Murawska
]
78 864cf6bf Agata Murawska
79 99381e3b Agata Murawska
COMPRESSION_TYPE = "gzip"
80 fa337742 Agata Murawska
NO_COMPRESSION = [None, "identity"]
81 99381e3b Agata Murawska
COMPRESS = "compression"
82 99381e3b Agata Murawska
DECOMPRESS = "decompression"
83 99381e3b Agata Murawska
ALLOWED_ACTIONS = [COMPRESS, DECOMPRESS]
84 99381e3b Agata Murawska
85 fa337742 Agata Murawska
VMDK = "vmdk"
86 fa337742 Agata Murawska
RAW = "raw"
87 fa337742 Agata Murawska
COW = "cow"
88 fa337742 Agata Murawska
ALLOWED_FORMATS = [RAW, COW, VMDK]
89 fa337742 Agata Murawska
90 7bde29b5 Agata Murawska
# ResourceType values
91 7bde29b5 Agata Murawska
RASD_TYPE = {
92 7bde29b5 Agata Murawska
  "vcpus": "3",
93 7bde29b5 Agata Murawska
  "memory": "4",
94 7432d332 Agata Murawska
  "scsi-controller": "6",
95 7432d332 Agata Murawska
  "ethernet-adapter": "10",
96 7432d332 Agata Murawska
  "disk": "17",
97 7bde29b5 Agata Murawska
}
98 7bde29b5 Agata Murawska
99 fa337742 Agata Murawska
SCSI_SUBTYPE = "lsilogic"
100 fa337742 Agata Murawska
VS_TYPE = {
101 fa337742 Agata Murawska
  "ganeti": "ganeti-ovf",
102 fa337742 Agata Murawska
  "external": "vmx-04",
103 fa337742 Agata Murawska
}
104 fa337742 Agata Murawska
105 7bde29b5 Agata Murawska
# AllocationUnits values and conversion
106 7bde29b5 Agata Murawska
ALLOCATION_UNITS = {
107 7bde29b5 Agata Murawska
  'b': ["bytes", "b"],
108 7bde29b5 Agata Murawska
  'kb': ["kilobytes", "kb", "byte * 2^10", "kibibytes", "kib"],
109 7bde29b5 Agata Murawska
  'mb': ["megabytes", "mb", "byte * 2^20", "mebibytes", "mib"],
110 7bde29b5 Agata Murawska
  'gb': ["gigabytes", "gb", "byte * 2^30", "gibibytes", "gib"],
111 7bde29b5 Agata Murawska
}
112 7bde29b5 Agata Murawska
CONVERT_UNITS_TO_MB = {
113 7bde29b5 Agata Murawska
  'b': lambda x: x / (1024 * 1024),
114 7bde29b5 Agata Murawska
  'kb': lambda x: x / 1024,
115 7bde29b5 Agata Murawska
  'mb': lambda x: x,
116 7bde29b5 Agata Murawska
  'gb': lambda x: x * 1024,
117 7bde29b5 Agata Murawska
}
118 7bde29b5 Agata Murawska
119 0963b26a Agata Murawska
# Names of the config fields
120 0963b26a Agata Murawska
NAME = "name"
121 b179ce72 Agata Murawska
OS = "os"
122 b179ce72 Agata Murawska
HYPERV = "hypervisor"
123 b179ce72 Agata Murawska
VCPUS = "vcpus"
124 b179ce72 Agata Murawska
MEMORY = "memory"
125 b179ce72 Agata Murawska
AUTO_BALANCE = "auto_balance"
126 b179ce72 Agata Murawska
DISK_TEMPLATE = "disk_template"
127 b179ce72 Agata Murawska
TAGS = "tags"
128 b179ce72 Agata Murawska
VERSION = "version"
129 0963b26a Agata Murawska
130 7432d332 Agata Murawska
# Instance IDs of System and SCSI controller
131 fa337742 Agata Murawska
INSTANCE_ID = {
132 fa337742 Agata Murawska
  "system": 0,
133 fa337742 Agata Murawska
  "vcpus": 1,
134 fa337742 Agata Murawska
  "memory": 2,
135 fa337742 Agata Murawska
  "scsi": 3,
136 fa337742 Agata Murawska
}
137 7432d332 Agata Murawska
138 7432d332 Agata Murawska
# Disk format descriptions
139 7432d332 Agata Murawska
DISK_FORMAT = {
140 fa337742 Agata Murawska
  RAW: "http://en.wikipedia.org/wiki/Byte",
141 fa337742 Agata Murawska
  VMDK: "http://www.vmware.com/interfaces/specifications/vmdk.html"
142 7432d332 Agata Murawska
          "#monolithicSparse",
143 fa337742 Agata Murawska
  COW: "http://www.gnome.org/~markmc/qcow-image-format.html",
144 7432d332 Agata Murawska
}
145 7432d332 Agata Murawska
146 487895ed Agata Murawska
147 a002ed79 Agata Murawska
def CheckQemuImg():
148 a002ed79 Agata Murawska
  """ Make sure that qemu-img is present before performing operations.
149 a002ed79 Agata Murawska

150 a002ed79 Agata Murawska
  @raise errors.OpPrereqError: when qemu-img was not found in the system
151 a002ed79 Agata Murawska

152 a002ed79 Agata Murawska
  """
153 a002ed79 Agata Murawska
  if not constants.QEMUIMG_PATH:
154 a002ed79 Agata Murawska
    raise errors.OpPrereqError("qemu-img not found at build time, unable"
155 a002ed79 Agata Murawska
                               " to continue")
156 99381e3b Agata Murawska
157 487895ed Agata Murawska
158 99381e3b Agata Murawska
def LinkFile(old_path, prefix=None, suffix=None, directory=None):
159 99381e3b Agata Murawska
  """Create link with a given prefix and suffix.
160 99381e3b Agata Murawska

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

165 99381e3b Agata Murawska
  @type old_path:string
166 99381e3b Agata Murawska
  @param old_path: path to the file that is to be linked
167 99381e3b Agata Murawska
  @type prefix: string
168 99381e3b Agata Murawska
  @param prefix: prefix of filename for the link
169 99381e3b Agata Murawska
  @type suffix: string
170 99381e3b Agata Murawska
  @param suffix: suffix of the filename for the link
171 99381e3b Agata Murawska
  @type directory: string
172 99381e3b Agata Murawska
  @param directory: directory of the link
173 99381e3b Agata Murawska

174 99381e3b Agata Murawska
  @raise errors.OpPrereqError: when error on linking is different than
175 99381e3b Agata Murawska
    "File exists"
176 99381e3b Agata Murawska

177 99381e3b Agata Murawska
  """
178 99381e3b Agata Murawska
  assert(prefix is not None or suffix is not None)
179 99381e3b Agata Murawska
  if directory is None:
180 99381e3b Agata Murawska
    directory = os.getcwd()
181 99381e3b Agata Murawska
  new_path = utils.PathJoin(directory, "%s%s" % (prefix, suffix))
182 99381e3b Agata Murawska
  counter = 1
183 99381e3b Agata Murawska
  while True:
184 99381e3b Agata Murawska
    try:
185 99381e3b Agata Murawska
      os.link(old_path, new_path)
186 99381e3b Agata Murawska
      break
187 99381e3b Agata Murawska
    except OSError, err:
188 99381e3b Agata Murawska
      if err.errno == errno.EEXIST:
189 99381e3b Agata Murawska
        new_path = utils.PathJoin(directory,
190 99381e3b Agata Murawska
          "%s_%s%s" % (prefix, counter, suffix))
191 99381e3b Agata Murawska
        counter += 1
192 99381e3b Agata Murawska
      else:
193 99381e3b Agata Murawska
        raise errors.OpPrereqError("Error moving the file %s to %s location:"
194 99381e3b Agata Murawska
                                   " %s" % (old_path, new_path, err))
195 99381e3b Agata Murawska
  return new_path
196 99381e3b Agata Murawska
197 864cf6bf Agata Murawska
198 864cf6bf Agata Murawska
class OVFReader(object):
199 864cf6bf Agata Murawska
  """Reader class for OVF files.
200 864cf6bf Agata Murawska

201 864cf6bf Agata Murawska
  @type files_list: list
202 864cf6bf Agata Murawska
  @ivar files_list: list of files in the OVF package
203 864cf6bf Agata Murawska
  @type tree: ET.ElementTree
204 864cf6bf Agata Murawska
  @ivar tree: XML tree of the .ovf file
205 864cf6bf Agata Murawska
  @type schema_name: string
206 864cf6bf Agata Murawska
  @ivar schema_name: name of the .ovf file
207 864cf6bf Agata Murawska
  @type input_dir: string
208 864cf6bf Agata Murawska
  @ivar input_dir: directory in which the .ovf file resides
209 864cf6bf Agata Murawska

210 864cf6bf Agata Murawska
  """
211 864cf6bf Agata Murawska
  def __init__(self, input_path):
212 864cf6bf Agata Murawska
    """Initialiaze the reader - load the .ovf file to XML parser.
213 864cf6bf Agata Murawska

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

219 864cf6bf Agata Murawska
    @type input_path: string
220 864cf6bf Agata Murawska
    @param input_path: absolute path to the .ovf file
221 864cf6bf Agata Murawska

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

225 864cf6bf Agata Murawska
    """
226 864cf6bf Agata Murawska
    self.tree = ET.ElementTree()
227 864cf6bf Agata Murawska
    try:
228 864cf6bf Agata Murawska
      self.tree.parse(input_path)
229 d6f8db24 Guido Trotter
    except (ParseError, xml.parsers.expat.ExpatError), err:
230 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Error while reading %s file: %s" %
231 864cf6bf Agata Murawska
                                 (OVF_EXT, err))
232 864cf6bf Agata Murawska
233 864cf6bf Agata Murawska
    # Create a list of all files in the OVF package
234 864cf6bf Agata Murawska
    (input_dir, input_file) = os.path.split(input_path)
235 864cf6bf Agata Murawska
    (input_name, _) = os.path.splitext(input_file)
236 864cf6bf Agata Murawska
    files_directory = utils.ListVisibleFiles(input_dir)
237 864cf6bf Agata Murawska
    files_list = []
238 864cf6bf Agata Murawska
    for file_name in files_directory:
239 864cf6bf Agata Murawska
      (name, extension) = os.path.splitext(file_name)
240 864cf6bf Agata Murawska
      if extension in FILE_EXTENSIONS and name == input_name:
241 864cf6bf Agata Murawska
        files_list.append(file_name)
242 864cf6bf Agata Murawska
    files_list += self._GetAttributes("{%s}References/{%s}File" %
243 864cf6bf Agata Murawska
                                      (OVF_SCHEMA, OVF_SCHEMA),
244 864cf6bf Agata Murawska
                                      "{%s}href" % OVF_SCHEMA)
245 864cf6bf Agata Murawska
    for file_name in files_list:
246 864cf6bf Agata Murawska
      file_path = utils.PathJoin(input_dir, file_name)
247 864cf6bf Agata Murawska
      if not os.path.exists(file_path):
248 864cf6bf Agata Murawska
        raise errors.OpPrereqError("File does not exist: %s" % file_path)
249 864cf6bf Agata Murawska
    logging.info("Files in the OVF package: %s", " ".join(files_list))
250 864cf6bf Agata Murawska
    self.files_list = files_list
251 864cf6bf Agata Murawska
    self.input_dir = input_dir
252 864cf6bf Agata Murawska
    self.schema_name = input_name
253 864cf6bf Agata Murawska
254 864cf6bf Agata Murawska
  def _GetAttributes(self, path, attribute):
255 864cf6bf Agata Murawska
    """Get specified attribute from all nodes accessible using given path.
256 864cf6bf Agata Murawska

257 864cf6bf Agata Murawska
    Function follows the path from root node to the desired tags using path,
258 864cf6bf Agata Murawska
    then reads the apropriate attribute values.
259 864cf6bf Agata Murawska

260 864cf6bf Agata Murawska
    @type path: string
261 864cf6bf Agata Murawska
    @param path: path of nodes to visit
262 864cf6bf Agata Murawska
    @type attribute: string
263 864cf6bf Agata Murawska
    @param attribute: attribute for which we gather the information
264 864cf6bf Agata Murawska
    @rtype: list
265 864cf6bf Agata Murawska
    @return: for each accessible tag with the attribute value set, value of the
266 864cf6bf Agata Murawska
      attribute
267 864cf6bf Agata Murawska

268 864cf6bf Agata Murawska
    """
269 864cf6bf Agata Murawska
    current_list = self.tree.findall(path)
270 864cf6bf Agata Murawska
    results = [x.get(attribute) for x in current_list]
271 864cf6bf Agata Murawska
    return filter(None, results)
272 864cf6bf Agata Murawska
273 99381e3b Agata Murawska
  def _GetElementMatchingAttr(self, path, match_attr):
274 99381e3b Agata Murawska
    """Searches for element on a path that matches certain attribute value.
275 99381e3b Agata Murawska

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

279 99381e3b Agata Murawska
    @type path: string
280 99381e3b Agata Murawska
    @param path: path of nodes to visit
281 99381e3b Agata Murawska
    @type match_attr: tuple
282 99381e3b Agata Murawska
    @param match_attr: pair (attribute, value) for which we search
283 99381e3b Agata Murawska
    @rtype: ET.ElementTree or None
284 99381e3b Agata Murawska
    @return: first element matching match_attr or None if nothing matches
285 99381e3b Agata Murawska

286 99381e3b Agata Murawska
    """
287 99381e3b Agata Murawska
    potential_elements = self.tree.findall(path)
288 99381e3b Agata Murawska
    (attr, val) = match_attr
289 99381e3b Agata Murawska
    for elem in potential_elements:
290 99381e3b Agata Murawska
      if elem.get(attr) == val:
291 99381e3b Agata Murawska
        return elem
292 99381e3b Agata Murawska
    return None
293 99381e3b Agata Murawska
294 24b9469d Agata Murawska
  def _GetElementMatchingText(self, path, match_text):
295 24b9469d Agata Murawska
    """Searches for element on a path that matches certain text value.
296 24b9469d Agata Murawska

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

300 24b9469d Agata Murawska
    @type path: string
301 24b9469d Agata Murawska
    @param path: path of nodes to visit
302 24b9469d Agata Murawska
    @type match_text: tuple
303 24b9469d Agata Murawska
    @param match_text: pair (node, text) for which we search
304 24b9469d Agata Murawska
    @rtype: ET.ElementTree or None
305 24b9469d Agata Murawska
    @return: first element matching match_text or None if nothing matches
306 24b9469d Agata Murawska

307 24b9469d Agata Murawska
    """
308 24b9469d Agata Murawska
    potential_elements = self.tree.findall(path)
309 24b9469d Agata Murawska
    (node, text) = match_text
310 24b9469d Agata Murawska
    for elem in potential_elements:
311 24b9469d Agata Murawska
      if elem.findtext(node) == text:
312 24b9469d Agata Murawska
        return elem
313 24b9469d Agata Murawska
    return None
314 24b9469d Agata Murawska
315 99381e3b Agata Murawska
  @staticmethod
316 99381e3b Agata Murawska
  def _GetDictParameters(root, schema):
317 99381e3b Agata Murawska
    """Reads text in all children and creates the dictionary from the contents.
318 99381e3b Agata Murawska

319 99381e3b Agata Murawska
    @type root: ET.ElementTree or None
320 99381e3b Agata Murawska
    @param root: father of the nodes we want to collect data about
321 99381e3b Agata Murawska
    @type schema: string
322 99381e3b Agata Murawska
    @param schema: schema name to be removed from the tag
323 99381e3b Agata Murawska
    @rtype: dict
324 99381e3b Agata Murawska
    @return: dictionary containing tags and their text contents, tags have their
325 99381e3b Agata Murawska
      schema fragment removed or empty dictionary, when root is None
326 99381e3b Agata Murawska

327 99381e3b Agata Murawska
    """
328 99381e3b Agata Murawska
    if not root:
329 99381e3b Agata Murawska
      return {}
330 99381e3b Agata Murawska
    results = {}
331 99381e3b Agata Murawska
    for element in list(root):
332 99381e3b Agata Murawska
      pref_len = len("{%s}" % schema)
333 99381e3b Agata Murawska
      assert(schema in element.tag)
334 99381e3b Agata Murawska
      tag = element.tag[pref_len:]
335 99381e3b Agata Murawska
      results[tag] = element.text
336 99381e3b Agata Murawska
    return results
337 99381e3b Agata Murawska
338 864cf6bf Agata Murawska
  def VerifyManifest(self):
339 864cf6bf Agata Murawska
    """Verifies manifest for the OVF package, if one is given.
340 864cf6bf Agata Murawska

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

343 864cf6bf Agata Murawska
    """
344 864cf6bf Agata Murawska
    if "%s%s" % (self.schema_name, MF_EXT) in self.files_list:
345 864cf6bf Agata Murawska
      logging.warning("Verifying SHA1 checksums, this may take a while")
346 864cf6bf Agata Murawska
      manifest_filename = "%s%s" % (self.schema_name, MF_EXT)
347 864cf6bf Agata Murawska
      manifest_path = utils.PathJoin(self.input_dir, manifest_filename)
348 864cf6bf Agata Murawska
      manifest_content = utils.ReadFile(manifest_path).splitlines()
349 864cf6bf Agata Murawska
      manifest_files = {}
350 864cf6bf Agata Murawska
      regexp = r"SHA1\((\S+)\)= (\S+)"
351 864cf6bf Agata Murawska
      for line in manifest_content:
352 864cf6bf Agata Murawska
        match = re.match(regexp, line)
353 864cf6bf Agata Murawska
        if match:
354 864cf6bf Agata Murawska
          file_name = match.group(1)
355 864cf6bf Agata Murawska
          sha1_sum = match.group(2)
356 864cf6bf Agata Murawska
          manifest_files[file_name] = sha1_sum
357 864cf6bf Agata Murawska
      files_with_paths = [utils.PathJoin(self.input_dir, file_name)
358 864cf6bf Agata Murawska
        for file_name in self.files_list]
359 864cf6bf Agata Murawska
      sha1_sums = utils.FingerprintFiles(files_with_paths)
360 864cf6bf Agata Murawska
      for file_name, value in manifest_files.iteritems():
361 864cf6bf Agata Murawska
        if sha1_sums.get(utils.PathJoin(self.input_dir, file_name)) != value:
362 864cf6bf Agata Murawska
          raise errors.OpPrereqError("SHA1 checksum of %s does not match the"
363 864cf6bf Agata Murawska
                                     " value in manifest file" % file_name)
364 864cf6bf Agata Murawska
      logging.info("SHA1 checksums verified")
365 864cf6bf Agata Murawska
366 99381e3b Agata Murawska
  def GetInstanceName(self):
367 99381e3b Agata Murawska
    """Provides information about instance name.
368 99381e3b Agata Murawska

369 99381e3b Agata Murawska
    @rtype: string
370 99381e3b Agata Murawska
    @return: instance name string
371 99381e3b Agata Murawska

372 99381e3b Agata Murawska
    """
373 99381e3b Agata Murawska
    find_name = "{%s}VirtualSystem/{%s}Name" % (OVF_SCHEMA, OVF_SCHEMA)
374 99381e3b Agata Murawska
    return self.tree.findtext(find_name)
375 99381e3b Agata Murawska
376 99381e3b Agata Murawska
  def GetDiskTemplate(self):
377 99381e3b Agata Murawska
    """Returns disk template from .ovf file
378 99381e3b Agata Murawska

379 99381e3b Agata Murawska
    @rtype: string or None
380 99381e3b Agata Murawska
    @return: name of the template
381 99381e3b Agata Murawska
    """
382 99381e3b Agata Murawska
    find_template = ("{%s}GanetiSection/{%s}DiskTemplate" %
383 99381e3b Agata Murawska
                     (GANETI_SCHEMA, GANETI_SCHEMA))
384 99381e3b Agata Murawska
    return self.tree.findtext(find_template)
385 99381e3b Agata Murawska
386 7bde29b5 Agata Murawska
  def GetHypervisorData(self):
387 7bde29b5 Agata Murawska
    """Provides hypervisor information - hypervisor name and options.
388 7bde29b5 Agata Murawska

389 7bde29b5 Agata Murawska
    @rtype: dict
390 7bde29b5 Agata Murawska
    @return: dictionary containing name of the used hypervisor and all the
391 7bde29b5 Agata Murawska
      specified options
392 7bde29b5 Agata Murawska

393 7bde29b5 Agata Murawska
    """
394 7bde29b5 Agata Murawska
    hypervisor_search = ("{%s}GanetiSection/{%s}Hypervisor" %
395 7bde29b5 Agata Murawska
                         (GANETI_SCHEMA, GANETI_SCHEMA))
396 7bde29b5 Agata Murawska
    hypervisor_data = self.tree.find(hypervisor_search)
397 7bde29b5 Agata Murawska
    if not hypervisor_data:
398 7bde29b5 Agata Murawska
      return {"hypervisor_name": constants.VALUE_AUTO}
399 7bde29b5 Agata Murawska
    results = {
400 7bde29b5 Agata Murawska
      "hypervisor_name": hypervisor_data.findtext("{%s}Name" % GANETI_SCHEMA,
401 7bde29b5 Agata Murawska
                           default=constants.VALUE_AUTO),
402 7bde29b5 Agata Murawska
    }
403 7bde29b5 Agata Murawska
    parameters = hypervisor_data.find("{%s}Parameters" % GANETI_SCHEMA)
404 7bde29b5 Agata Murawska
    results.update(self._GetDictParameters(parameters, GANETI_SCHEMA))
405 7bde29b5 Agata Murawska
    return results
406 7bde29b5 Agata Murawska
407 7bde29b5 Agata Murawska
  def GetOSData(self):
408 7bde29b5 Agata Murawska
    """ Provides operating system information - os name and options.
409 7bde29b5 Agata Murawska

410 7bde29b5 Agata Murawska
    @rtype: dict
411 7bde29b5 Agata Murawska
    @return: dictionary containing name and options for the chosen OS
412 7bde29b5 Agata Murawska

413 7bde29b5 Agata Murawska
    """
414 7bde29b5 Agata Murawska
    results = {}
415 7bde29b5 Agata Murawska
    os_search = ("{%s}GanetiSection/{%s}OperatingSystem" %
416 7bde29b5 Agata Murawska
                 (GANETI_SCHEMA, GANETI_SCHEMA))
417 7bde29b5 Agata Murawska
    os_data = self.tree.find(os_search)
418 7bde29b5 Agata Murawska
    if os_data:
419 7bde29b5 Agata Murawska
      results["os_name"] = os_data.findtext("{%s}Name" % GANETI_SCHEMA)
420 7bde29b5 Agata Murawska
      parameters = os_data.find("{%s}Parameters" % GANETI_SCHEMA)
421 7bde29b5 Agata Murawska
      results.update(self._GetDictParameters(parameters, GANETI_SCHEMA))
422 7bde29b5 Agata Murawska
    return results
423 7bde29b5 Agata Murawska
424 7bde29b5 Agata Murawska
  def GetBackendData(self):
425 7bde29b5 Agata Murawska
    """ Provides backend information - vcpus, memory, auto balancing options.
426 7bde29b5 Agata Murawska

427 7bde29b5 Agata Murawska
    @rtype: dict
428 7bde29b5 Agata Murawska
    @return: dictionary containing options for vcpus, memory and auto balance
429 7bde29b5 Agata Murawska
      settings
430 7bde29b5 Agata Murawska

431 7bde29b5 Agata Murawska
    """
432 7bde29b5 Agata Murawska
    results = {}
433 7bde29b5 Agata Murawska
434 7bde29b5 Agata Murawska
    find_vcpus = ("{%s}VirtualSystem/{%s}VirtualHardwareSection/{%s}Item" %
435 7bde29b5 Agata Murawska
                   (OVF_SCHEMA, OVF_SCHEMA, OVF_SCHEMA))
436 7bde29b5 Agata Murawska
    match_vcpus = ("{%s}ResourceType" % RASD_SCHEMA, RASD_TYPE["vcpus"])
437 7bde29b5 Agata Murawska
    vcpus = self._GetElementMatchingText(find_vcpus, match_vcpus)
438 7bde29b5 Agata Murawska
    if vcpus:
439 7bde29b5 Agata Murawska
      vcpus_count = vcpus.findtext("{%s}VirtualQuantity" % RASD_SCHEMA,
440 7bde29b5 Agata Murawska
        default=constants.VALUE_AUTO)
441 7bde29b5 Agata Murawska
    else:
442 7bde29b5 Agata Murawska
      vcpus_count = constants.VALUE_AUTO
443 7bde29b5 Agata Murawska
    results["vcpus"] = str(vcpus_count)
444 7bde29b5 Agata Murawska
445 7bde29b5 Agata Murawska
    find_memory = find_vcpus
446 7bde29b5 Agata Murawska
    match_memory = ("{%s}ResourceType" % RASD_SCHEMA, RASD_TYPE["memory"])
447 7bde29b5 Agata Murawska
    memory = self._GetElementMatchingText(find_memory, match_memory)
448 7bde29b5 Agata Murawska
    memory_raw = None
449 7bde29b5 Agata Murawska
    if memory:
450 7bde29b5 Agata Murawska
      alloc_units = memory.findtext("{%s}AllocationUnits" % RASD_SCHEMA)
451 7bde29b5 Agata Murawska
      matching_units = [units for units, variants in
452 7bde29b5 Agata Murawska
        ALLOCATION_UNITS.iteritems() if alloc_units.lower() in variants]
453 7bde29b5 Agata Murawska
      if matching_units == []:
454 7bde29b5 Agata Murawska
        raise errors.OpPrereqError("Unit %s for RAM memory unknown",
455 7bde29b5 Agata Murawska
          alloc_units)
456 7bde29b5 Agata Murawska
      units = matching_units[0]
457 7bde29b5 Agata Murawska
      memory_raw = int(memory.findtext("{%s}VirtualQuantity" % RASD_SCHEMA,
458 7bde29b5 Agata Murawska
            default=constants.VALUE_AUTO))
459 7bde29b5 Agata Murawska
      memory_count = CONVERT_UNITS_TO_MB[units](memory_raw)
460 7bde29b5 Agata Murawska
    else:
461 7bde29b5 Agata Murawska
      memory_count = constants.VALUE_AUTO
462 7bde29b5 Agata Murawska
    results["memory"] = str(memory_count)
463 7bde29b5 Agata Murawska
464 7bde29b5 Agata Murawska
    find_balance = ("{%s}GanetiSection/{%s}AutoBalance" %
465 7bde29b5 Agata Murawska
                   (GANETI_SCHEMA, GANETI_SCHEMA))
466 7bde29b5 Agata Murawska
    balance = self.tree.findtext(find_balance, default=constants.VALUE_AUTO)
467 7bde29b5 Agata Murawska
    results["auto_balance"] = balance
468 7bde29b5 Agata Murawska
469 7bde29b5 Agata Murawska
    return results
470 7bde29b5 Agata Murawska
471 7bde29b5 Agata Murawska
  def GetTagsData(self):
472 7bde29b5 Agata Murawska
    """Provides tags information for instance.
473 7bde29b5 Agata Murawska

474 7bde29b5 Agata Murawska
    @rtype: string or None
475 7bde29b5 Agata Murawska
    @return: string of comma-separated tags for the instance
476 7bde29b5 Agata Murawska

477 7bde29b5 Agata Murawska
    """
478 7bde29b5 Agata Murawska
    find_tags = "{%s}GanetiSection/{%s}Tags" % (GANETI_SCHEMA, GANETI_SCHEMA)
479 7bde29b5 Agata Murawska
    results = self.tree.findtext(find_tags)
480 7bde29b5 Agata Murawska
    if results:
481 7bde29b5 Agata Murawska
      return results
482 7bde29b5 Agata Murawska
    else:
483 7bde29b5 Agata Murawska
      return None
484 7bde29b5 Agata Murawska
485 7bde29b5 Agata Murawska
  def GetVersionData(self):
486 7bde29b5 Agata Murawska
    """Provides version number read from .ovf file
487 7bde29b5 Agata Murawska

488 7bde29b5 Agata Murawska
    @rtype: string
489 7bde29b5 Agata Murawska
    @return: string containing the version number
490 7bde29b5 Agata Murawska

491 7bde29b5 Agata Murawska
    """
492 7bde29b5 Agata Murawska
    find_version = ("{%s}GanetiSection/{%s}Version" %
493 7bde29b5 Agata Murawska
                    (GANETI_SCHEMA, GANETI_SCHEMA))
494 7bde29b5 Agata Murawska
    return self.tree.findtext(find_version)
495 7bde29b5 Agata Murawska
496 24b9469d Agata Murawska
  def GetNetworkData(self):
497 24b9469d Agata Murawska
    """Provides data about the network in the OVF instance.
498 24b9469d Agata Murawska

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

505 24b9469d Agata Murawska
    @rtype: dict
506 24b9469d Agata Murawska
    @return: dictionary containing all the network information
507 24b9469d Agata Murawska

508 24b9469d Agata Murawska
    """
509 24b9469d Agata Murawska
    results = {}
510 24b9469d Agata Murawska
    networks_search = ("{%s}NetworkSection/{%s}Network" %
511 24b9469d Agata Murawska
                       (OVF_SCHEMA, OVF_SCHEMA))
512 24b9469d Agata Murawska
    network_names = self._GetAttributes(networks_search,
513 24b9469d Agata Murawska
      "{%s}name" % OVF_SCHEMA)
514 24b9469d Agata Murawska
    required = ["ip", "mac", "link", "mode"]
515 24b9469d Agata Murawska
    for (counter, network_name) in enumerate(network_names):
516 24b9469d Agata Murawska
      network_search = ("{%s}VirtualSystem/{%s}VirtualHardwareSection/{%s}Item"
517 24b9469d Agata Murawska
                        % (OVF_SCHEMA, OVF_SCHEMA, OVF_SCHEMA))
518 24b9469d Agata Murawska
      ganeti_search = ("{%s}GanetiSection/{%s}Network/{%s}Nic" %
519 24b9469d Agata Murawska
                       (GANETI_SCHEMA, GANETI_SCHEMA, GANETI_SCHEMA))
520 24b9469d Agata Murawska
      network_match = ("{%s}Connection" % RASD_SCHEMA, network_name)
521 24b9469d Agata Murawska
      ganeti_match = ("{%s}name" % OVF_SCHEMA, network_name)
522 24b9469d Agata Murawska
      network_data = self._GetElementMatchingText(network_search, network_match)
523 24b9469d Agata Murawska
      network_ganeti_data = self._GetElementMatchingAttr(ganeti_search,
524 24b9469d Agata Murawska
        ganeti_match)
525 24b9469d Agata Murawska
526 24b9469d Agata Murawska
      ganeti_data = {}
527 24b9469d Agata Murawska
      if network_ganeti_data:
528 24b9469d Agata Murawska
        ganeti_data["mode"] = network_ganeti_data.findtext("{%s}Mode" %
529 24b9469d Agata Murawska
                                                           GANETI_SCHEMA)
530 24b9469d Agata Murawska
        ganeti_data["mac"] = network_ganeti_data.findtext("{%s}MACAddress" %
531 24b9469d Agata Murawska
                                                          GANETI_SCHEMA)
532 24b9469d Agata Murawska
        ganeti_data["ip"] = network_ganeti_data.findtext("{%s}IPAddress" %
533 24b9469d Agata Murawska
                                                         GANETI_SCHEMA)
534 24b9469d Agata Murawska
        ganeti_data["link"] = network_ganeti_data.findtext("{%s}Link" %
535 24b9469d Agata Murawska
                                                           GANETI_SCHEMA)
536 24b9469d Agata Murawska
      mac_data = None
537 24b9469d Agata Murawska
      if network_data:
538 24b9469d Agata Murawska
        mac_data = network_data.findtext("{%s}Address" % RASD_SCHEMA)
539 24b9469d Agata Murawska
540 24b9469d Agata Murawska
      network_name = network_name.lower()
541 24b9469d Agata Murawska
542 24b9469d Agata Murawska
      # First, some not Ganeti-specific information is collected
543 24b9469d Agata Murawska
      if constants.NIC_MODE_BRIDGED in network_name:
544 24b9469d Agata Murawska
        results["nic%s_mode" % counter] = "bridged"
545 24b9469d Agata Murawska
      elif constants.NIC_MODE_ROUTED in network_name:
546 24b9469d Agata Murawska
        results["nic%s_mode" % counter] = "routed"
547 24b9469d Agata Murawska
      results["nic%s_mac" % counter] = mac_data
548 24b9469d Agata Murawska
549 24b9469d Agata Murawska
      # GanetiSection data overrides 'manually' collected data
550 24b9469d Agata Murawska
      for name, value in ganeti_data.iteritems():
551 24b9469d Agata Murawska
        results["nic%s_%s" % (counter, name)] = value
552 24b9469d Agata Murawska
553 24b9469d Agata Murawska
      # Bridged network has no IP - unless specifically stated otherwise
554 24b9469d Agata Murawska
      if (results.get("nic%s_mode" % counter) == "bridged" and
555 24b9469d Agata Murawska
          not results.get("nic%s_ip" % counter)):
556 24b9469d Agata Murawska
        results["nic%s_ip" % counter] = constants.VALUE_NONE
557 24b9469d Agata Murawska
558 24b9469d Agata Murawska
      for option in required:
559 24b9469d Agata Murawska
        if not results.get("nic%s_%s" % (counter, option)):
560 24b9469d Agata Murawska
          results["nic%s_%s" % (counter, option)] = constants.VALUE_AUTO
561 24b9469d Agata Murawska
562 24b9469d Agata Murawska
    if network_names:
563 24b9469d Agata Murawska
      results["nic_count"] = str(len(network_names))
564 24b9469d Agata Murawska
    return results
565 24b9469d Agata Murawska
566 99381e3b Agata Murawska
  def GetDisksNames(self):
567 99381e3b Agata Murawska
    """Provides list of file names for the disks used by the instance.
568 99381e3b Agata Murawska

569 99381e3b Agata Murawska
    @rtype: list
570 99381e3b Agata Murawska
    @return: list of file names, as referenced in .ovf file
571 99381e3b Agata Murawska

572 99381e3b Agata Murawska
    """
573 99381e3b Agata Murawska
    results = []
574 99381e3b Agata Murawska
    disks_search = "{%s}DiskSection/{%s}Disk" % (OVF_SCHEMA, OVF_SCHEMA)
575 99381e3b Agata Murawska
    disk_ids = self._GetAttributes(disks_search, "{%s}fileRef" % OVF_SCHEMA)
576 99381e3b Agata Murawska
    for disk in disk_ids:
577 99381e3b Agata Murawska
      disk_search = "{%s}References/{%s}File" % (OVF_SCHEMA, OVF_SCHEMA)
578 99381e3b Agata Murawska
      disk_match = ("{%s}id" % OVF_SCHEMA, disk)
579 99381e3b Agata Murawska
      disk_elem = self._GetElementMatchingAttr(disk_search, disk_match)
580 99381e3b Agata Murawska
      if disk_elem is None:
581 99381e3b Agata Murawska
        raise errors.OpPrereqError("%s file corrupted - disk %s not found in"
582 99381e3b Agata Murawska
                                   " references" % (OVF_EXT, disk))
583 99381e3b Agata Murawska
      disk_name = disk_elem.get("{%s}href" % OVF_SCHEMA)
584 99381e3b Agata Murawska
      disk_compression = disk_elem.get("{%s}compression" % OVF_SCHEMA)
585 99381e3b Agata Murawska
      results.append((disk_name, disk_compression))
586 99381e3b Agata Murawska
    return results
587 99381e3b Agata Murawska
588 864cf6bf Agata Murawska
589 7432d332 Agata Murawska
def SubElementText(parent, tag, text, attrib={}, **extra):
590 7432d332 Agata Murawska
# pylint: disable=W0102
591 7432d332 Agata Murawska
  """This is just a wrapper on ET.SubElement that always has text content.
592 7432d332 Agata Murawska

593 7432d332 Agata Murawska
  """
594 7432d332 Agata Murawska
  if text is None:
595 7432d332 Agata Murawska
    return None
596 7432d332 Agata Murawska
  elem = ET.SubElement(parent, tag, attrib=attrib, **extra)
597 7432d332 Agata Murawska
  elem.text = str(text)
598 7432d332 Agata Murawska
  return elem
599 7432d332 Agata Murawska
600 7432d332 Agata Murawska
601 0963b26a Agata Murawska
class OVFWriter(object):
602 0963b26a Agata Murawska
  """Writer class for OVF files.
603 0963b26a Agata Murawska

604 0963b26a Agata Murawska
  @type tree: ET.ElementTree
605 0963b26a Agata Murawska
  @ivar tree: XML tree that we are constructing
606 fa337742 Agata Murawska
  @type virtual_system_type: string
607 fa337742 Agata Murawska
  @ivar virtual_system_type: value of vssd:VirtualSystemType, for external usage
608 fa337742 Agata Murawska
    in VMWare this requires to be vmx
609 7432d332 Agata Murawska
  @type hardware_list: list
610 7432d332 Agata Murawska
  @ivar hardware_list: list of items prepared for VirtualHardwareSection
611 fa337742 Agata Murawska
  @type next_instance_id: int
612 fa337742 Agata Murawska
  @ivar next_instance_id: next instance id to be used when creating elements on
613 fa337742 Agata Murawska
    hardware_list
614 0963b26a Agata Murawska

615 0963b26a Agata Murawska
  """
616 0963b26a Agata Murawska
  def __init__(self, has_gnt_section):
617 0963b26a Agata Murawska
    """Initialize the writer - set the top element.
618 0963b26a Agata Murawska

619 0963b26a Agata Murawska
    @type has_gnt_section: bool
620 0963b26a Agata Murawska
    @param has_gnt_section: if the Ganeti schema should be added - i.e. this
621 0963b26a Agata Murawska
      means that Ganeti section will be present
622 0963b26a Agata Murawska

623 0963b26a Agata Murawska
    """
624 0963b26a Agata Murawska
    env_attribs = {
625 0963b26a Agata Murawska
      "xmlns:xsi": XML_SCHEMA,
626 0963b26a Agata Murawska
      "xmlns:vssd": VSSD_SCHEMA,
627 0963b26a Agata Murawska
      "xmlns:rasd": RASD_SCHEMA,
628 0963b26a Agata Murawska
      "xmlns:ovf": OVF_SCHEMA,
629 0963b26a Agata Murawska
      "xmlns": OVF_SCHEMA,
630 0963b26a Agata Murawska
      "xml:lang": "en-US",
631 0963b26a Agata Murawska
    }
632 0963b26a Agata Murawska
    if has_gnt_section:
633 0963b26a Agata Murawska
      env_attribs["xmlns:gnt"] = GANETI_SCHEMA
634 fa337742 Agata Murawska
      self.virtual_system_type = VS_TYPE["ganeti"]
635 fa337742 Agata Murawska
    else:
636 fa337742 Agata Murawska
      self.virtual_system_type = VS_TYPE["external"]
637 0963b26a Agata Murawska
    self.tree = ET.Element("Envelope", attrib=env_attribs)
638 7432d332 Agata Murawska
    self.hardware_list = []
639 fa337742 Agata Murawska
    # INSTANCE_ID contains statically assigned IDs, starting from 0
640 fa337742 Agata Murawska
    self.next_instance_id = len(INSTANCE_ID) # FIXME: hackish
641 7432d332 Agata Murawska
642 7432d332 Agata Murawska
  def SaveDisksData(self, disks):
643 7432d332 Agata Murawska
    """Convert disk information to certain OVF sections.
644 7432d332 Agata Murawska

645 7432d332 Agata Murawska
    @type disks: list
646 7432d332 Agata Murawska
    @param disks: list of dictionaries of disk options from config.ini
647 7432d332 Agata Murawska

648 7432d332 Agata Murawska
    """
649 7432d332 Agata Murawska
    references = ET.SubElement(self.tree, "References")
650 7432d332 Agata Murawska
    disk_section = ET.SubElement(self.tree, "DiskSection")
651 fa337742 Agata Murawska
    SubElementText(disk_section, "Info", "Virtual disk information")
652 7432d332 Agata Murawska
    for counter, disk in enumerate(disks):
653 7432d332 Agata Murawska
      file_id = "file%s" % counter
654 7432d332 Agata Murawska
      disk_id = "disk%s" % counter
655 7432d332 Agata Murawska
      file_attribs = {
656 7432d332 Agata Murawska
        "ovf:href": disk["path"],
657 7432d332 Agata Murawska
        "ovf:size": str(disk["real-size"]),
658 7432d332 Agata Murawska
        "ovf:id": file_id,
659 7432d332 Agata Murawska
      }
660 7432d332 Agata Murawska
      disk_attribs = {
661 7432d332 Agata Murawska
        "ovf:capacity": str(disk["virt-size"]),
662 7432d332 Agata Murawska
        "ovf:diskId": disk_id,
663 7432d332 Agata Murawska
        "ovf:fileRef": file_id,
664 7432d332 Agata Murawska
        "ovf:format": DISK_FORMAT.get(disk["format"], disk["format"]),
665 7432d332 Agata Murawska
      }
666 7432d332 Agata Murawska
      if "compression" in disk:
667 7432d332 Agata Murawska
        file_attribs["ovf:compression"] = disk["compression"]
668 7432d332 Agata Murawska
      ET.SubElement(references, "File", attrib=file_attribs)
669 7432d332 Agata Murawska
      ET.SubElement(disk_section, "Disk", attrib=disk_attribs)
670 7432d332 Agata Murawska
671 7432d332 Agata Murawska
      # Item in VirtualHardwareSection creation
672 7432d332 Agata Murawska
      disk_item = ET.Element("Item")
673 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:ElementName", disk_id)
674 7432d332 Agata Murawska
      SubElementText(disk_item, "rasd:HostResource", "ovf:/disk/%s" % disk_id)
675 fa337742 Agata Murawska
      SubElementText(disk_item, "rasd:InstanceID", self.next_instance_id)
676 fa337742 Agata Murawska
      SubElementText(disk_item, "rasd:Parent", INSTANCE_ID["scsi"])
677 fa337742 Agata Murawska
      SubElementText(disk_item, "rasd:ResourceType", RASD_TYPE["disk"])
678 7432d332 Agata Murawska
      self.hardware_list.append(disk_item)
679 fa337742 Agata Murawska
      self.next_instance_id += 1
680 7432d332 Agata Murawska
681 7432d332 Agata Murawska
  def SaveNetworksData(self, networks):
682 7432d332 Agata Murawska
    """Convert network information to NetworkSection.
683 7432d332 Agata Murawska

684 7432d332 Agata Murawska
    @type networks: list
685 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
686 7432d332 Agata Murawska

687 7432d332 Agata Murawska
    """
688 7432d332 Agata Murawska
    network_section = ET.SubElement(self.tree, "NetworkSection")
689 fa337742 Agata Murawska
    SubElementText(network_section, "Info", "List of logical networks")
690 7432d332 Agata Murawska
    for counter, network in enumerate(networks):
691 7432d332 Agata Murawska
      network_name = "%s%s" % (network["mode"], counter)
692 7432d332 Agata Murawska
      network_attrib = {"ovf:name": network_name}
693 7432d332 Agata Murawska
      ET.SubElement(network_section, "Network", attrib=network_attrib)
694 7432d332 Agata Murawska
695 7432d332 Agata Murawska
      # Item in VirtualHardwareSection creation
696 7432d332 Agata Murawska
      network_item = ET.Element("Item")
697 fa337742 Agata Murawska
      SubElementText(network_item, "rasd:Address", network["mac"])
698 fa337742 Agata Murawska
      SubElementText(network_item, "rasd:Connection", network_name)
699 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:ElementName", network_name)
700 fa337742 Agata Murawska
      SubElementText(network_item, "rasd:InstanceID", self.next_instance_id)
701 7432d332 Agata Murawska
      SubElementText(network_item, "rasd:ResourceType",
702 7432d332 Agata Murawska
        RASD_TYPE["ethernet-adapter"])
703 7432d332 Agata Murawska
      self.hardware_list.append(network_item)
704 fa337742 Agata Murawska
      self.next_instance_id += 1
705 7432d332 Agata Murawska
706 7432d332 Agata Murawska
  @staticmethod
707 7432d332 Agata Murawska
  def _SaveNameAndParams(root, data):
708 7432d332 Agata Murawska
    """Save name and parameters information under root using data.
709 7432d332 Agata Murawska

710 7432d332 Agata Murawska
    @type root: ET.Element
711 7432d332 Agata Murawska
    @param root: root element for the Name and Parameters
712 7432d332 Agata Murawska
    @type data: dict
713 7432d332 Agata Murawska
    @param data: data from which we gather the values
714 7432d332 Agata Murawska

715 7432d332 Agata Murawska
    """
716 7432d332 Agata Murawska
    assert(data.get("name"))
717 7432d332 Agata Murawska
    name = SubElementText(root, "gnt:Name", data["name"])
718 7432d332 Agata Murawska
    params = ET.SubElement(root, "gnt:Parameters")
719 7432d332 Agata Murawska
    for name, value in data.iteritems():
720 7432d332 Agata Murawska
      if name != "name":
721 7432d332 Agata Murawska
        SubElementText(params, "gnt:%s" % name, value)
722 7432d332 Agata Murawska
723 7432d332 Agata Murawska
  def SaveGanetiData(self, ganeti, networks):
724 7432d332 Agata Murawska
    """Convert Ganeti-specific information to GanetiSection.
725 7432d332 Agata Murawska

726 7432d332 Agata Murawska
    @type ganeti: dict
727 7432d332 Agata Murawska
    @param ganeti: dictionary of Ganeti-specific options from config.ini
728 7432d332 Agata Murawska
    @type networks: list
729 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
730 7432d332 Agata Murawska

731 7432d332 Agata Murawska
    """
732 7432d332 Agata Murawska
    ganeti_section = ET.SubElement(self.tree, "gnt:GanetiSection")
733 7432d332 Agata Murawska
734 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:Version", ganeti.get("version"))
735 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:DiskTemplate",
736 7432d332 Agata Murawska
      ganeti.get("disk_template"))
737 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:AutoBalance",
738 7432d332 Agata Murawska
      ganeti.get("auto_balance"))
739 7432d332 Agata Murawska
    SubElementText(ganeti_section, "gnt:Tags", ganeti.get("tags"))
740 7432d332 Agata Murawska
741 7432d332 Agata Murawska
    osys = ET.SubElement(ganeti_section, "gnt:OperatingSystem")
742 7432d332 Agata Murawska
    self._SaveNameAndParams(osys, ganeti["os"])
743 7432d332 Agata Murawska
744 7432d332 Agata Murawska
    hypervisor = ET.SubElement(ganeti_section, "gnt:Hypervisor")
745 7432d332 Agata Murawska
    self._SaveNameAndParams(hypervisor, ganeti["hypervisor"])
746 7432d332 Agata Murawska
747 7432d332 Agata Murawska
    network_section = ET.SubElement(ganeti_section, "gnt:Network")
748 7432d332 Agata Murawska
    for counter, network in enumerate(networks):
749 7432d332 Agata Murawska
      network_name = "%s%s" % (network["mode"], counter)
750 7432d332 Agata Murawska
      nic_attrib = {"ovf:name": network_name}
751 7432d332 Agata Murawska
      nic = ET.SubElement(network_section, "gnt:Nic", attrib=nic_attrib)
752 7432d332 Agata Murawska
      SubElementText(nic, "gnt:Mode", network["mode"])
753 7432d332 Agata Murawska
      SubElementText(nic, "gnt:MACAddress", network["mac"])
754 7432d332 Agata Murawska
      SubElementText(nic, "gnt:IPAddress", network["ip"])
755 7432d332 Agata Murawska
      SubElementText(nic, "gnt:Link", network["link"])
756 7432d332 Agata Murawska
757 7432d332 Agata Murawska
  def SaveVirtualSystemData(self, name, vcpus, memory):
758 7432d332 Agata Murawska
    """Convert virtual system information to OVF sections.
759 7432d332 Agata Murawska

760 7432d332 Agata Murawska
    @type name: string
761 7432d332 Agata Murawska
    @param name: name of the instance
762 7432d332 Agata Murawska
    @type vcpus: int
763 7432d332 Agata Murawska
    @param vcpus: number of VCPUs
764 7432d332 Agata Murawska
    @type memory: int
765 7432d332 Agata Murawska
    @param memory: RAM memory in MB
766 7432d332 Agata Murawska

767 7432d332 Agata Murawska
    """
768 7432d332 Agata Murawska
    assert(vcpus > 0)
769 7432d332 Agata Murawska
    assert(memory > 0)
770 7432d332 Agata Murawska
    vs_attrib = {"ovf:id": name}
771 7432d332 Agata Murawska
    virtual_system = ET.SubElement(self.tree, "VirtualSystem", attrib=vs_attrib)
772 fa337742 Agata Murawska
    SubElementText(virtual_system, "Info", "A virtual machine")
773 7432d332 Agata Murawska
774 7432d332 Agata Murawska
    name_section = ET.SubElement(virtual_system, "Name")
775 7432d332 Agata Murawska
    name_section.text = name
776 7432d332 Agata Murawska
    os_attrib = {"ovf:id": "0"}
777 fa337742 Agata Murawska
    os_section = ET.SubElement(virtual_system, "OperatingSystemSection",
778 7432d332 Agata Murawska
      attrib=os_attrib)
779 fa337742 Agata Murawska
    SubElementText(os_section, "Info", "Installed guest operating system")
780 7432d332 Agata Murawska
    hardware_section = ET.SubElement(virtual_system, "VirtualHardwareSection")
781 fa337742 Agata Murawska
    SubElementText(hardware_section, "Info", "Virtual hardware requirements")
782 7432d332 Agata Murawska
783 7432d332 Agata Murawska
    # System description
784 7432d332 Agata Murawska
    system = ET.SubElement(hardware_section, "System")
785 7432d332 Agata Murawska
    SubElementText(system, "vssd:ElementName", "Virtual Hardware Family")
786 fa337742 Agata Murawska
    SubElementText(system, "vssd:InstanceID", INSTANCE_ID["system"])
787 7432d332 Agata Murawska
    SubElementText(system, "vssd:VirtualSystemIdentifier", name)
788 fa337742 Agata Murawska
    SubElementText(system, "vssd:VirtualSystemType", self.virtual_system_type)
789 7432d332 Agata Murawska
790 7432d332 Agata Murawska
    # Item for vcpus
791 7432d332 Agata Murawska
    vcpus_item = ET.SubElement(hardware_section, "Item")
792 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:ElementName",
793 7432d332 Agata Murawska
      "%s virtual CPU(s)" % vcpus)
794 fa337742 Agata Murawska
    SubElementText(vcpus_item, "rasd:InstanceID", INSTANCE_ID["vcpus"])
795 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:ResourceType", RASD_TYPE["vcpus"])
796 7432d332 Agata Murawska
    SubElementText(vcpus_item, "rasd:VirtualQuantity", vcpus)
797 7432d332 Agata Murawska
798 7432d332 Agata Murawska
    # Item for memory
799 7432d332 Agata Murawska
    memory_item = ET.SubElement(hardware_section, "Item")
800 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:AllocationUnits", "byte * 2^20")
801 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:ElementName", "%sMB of memory" % memory)
802 fa337742 Agata Murawska
    SubElementText(memory_item, "rasd:InstanceID", INSTANCE_ID["memory"])
803 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:ResourceType", RASD_TYPE["memory"])
804 7432d332 Agata Murawska
    SubElementText(memory_item, "rasd:VirtualQuantity", memory)
805 7432d332 Agata Murawska
806 7432d332 Agata Murawska
    # Item for scsi controller
807 7432d332 Agata Murawska
    scsi_item = ET.SubElement(hardware_section, "Item")
808 fa337742 Agata Murawska
    SubElementText(scsi_item, "rasd:Address", INSTANCE_ID["system"])
809 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:ElementName", "scsi_controller0")
810 fa337742 Agata Murawska
    SubElementText(scsi_item, "rasd:InstanceID", INSTANCE_ID["scsi"])
811 fa337742 Agata Murawska
    SubElementText(scsi_item, "rasd:ResourceSubType", SCSI_SUBTYPE)
812 7432d332 Agata Murawska
    SubElementText(scsi_item, "rasd:ResourceType", RASD_TYPE["scsi-controller"])
813 7432d332 Agata Murawska
814 7432d332 Agata Murawska
    # Other items - from self.hardware_list
815 fa337742 Agata Murawska
    for item in self.hardware_list:
816 7432d332 Agata Murawska
      hardware_section.append(item)
817 0963b26a Agata Murawska
818 0963b26a Agata Murawska
  def PrettyXmlDump(self):
819 0963b26a Agata Murawska
    """Formatter of the XML file.
820 0963b26a Agata Murawska

821 0963b26a Agata Murawska
    @rtype: string
822 0963b26a Agata Murawska
    @return: XML tree in the form of nicely-formatted string
823 0963b26a Agata Murawska

824 0963b26a Agata Murawska
    """
825 0963b26a Agata Murawska
    raw_string = ET.tostring(self.tree)
826 0963b26a Agata Murawska
    parsed_xml = xml.dom.minidom.parseString(raw_string)
827 0963b26a Agata Murawska
    xml_string = parsed_xml.toprettyxml(indent="  ")
828 0963b26a Agata Murawska
    text_re = re.compile(">\n\s+([^<>\s].*?)\n\s+</", re.DOTALL)
829 0963b26a Agata Murawska
    return text_re.sub(">\g<1></", xml_string)
830 0963b26a Agata Murawska
831 0963b26a Agata Murawska
832 ced78a66 Agata Murawska
class Converter(object):
833 ced78a66 Agata Murawska
  """Converter class for OVF packages.
834 ced78a66 Agata Murawska

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

838 ced78a66 Agata Murawska
  @type options: optparse.Values
839 ced78a66 Agata Murawska
  @ivar options: options parsed from the command line
840 ced78a66 Agata Murawska
  @type output_dir: string
841 ced78a66 Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
842 ced78a66 Agata Murawska
    written
843 ced78a66 Agata Murawska
  @type temp_file_manager: L{utils.TemporaryFileManager}
844 ced78a66 Agata Murawska
  @ivar temp_file_manager: container for temporary files created during
845 ced78a66 Agata Murawska
    conversion
846 ced78a66 Agata Murawska
  @type temp_dir: string
847 ced78a66 Agata Murawska
  @ivar temp_dir: temporary directory created then we deal with OVA
848 ced78a66 Agata Murawska

849 ced78a66 Agata Murawska
  """
850 ced78a66 Agata Murawska
  def __init__(self, input_path, options):
851 ced78a66 Agata Murawska
    """Initialize the converter.
852 ced78a66 Agata Murawska

853 ced78a66 Agata Murawska
    @type input_path: string
854 ced78a66 Agata Murawska
    @param input_path: path to the Converter input file
855 ced78a66 Agata Murawska
    @type options: optparse.Values
856 ced78a66 Agata Murawska
    @param options: command line options
857 ced78a66 Agata Murawska

858 ced78a66 Agata Murawska
    @raise errors.OpPrereqError: if file does not exist
859 ced78a66 Agata Murawska

860 ced78a66 Agata Murawska
    """
861 ced78a66 Agata Murawska
    input_path = os.path.abspath(input_path)
862 ced78a66 Agata Murawska
    if not os.path.isfile(input_path):
863 ced78a66 Agata Murawska
      raise errors.OpPrereqError("File does not exist: %s" % input_path)
864 ced78a66 Agata Murawska
    self.options = options
865 ced78a66 Agata Murawska
    self.temp_file_manager = utils.TemporaryFileManager()
866 ced78a66 Agata Murawska
    self.temp_dir = None
867 ced78a66 Agata Murawska
    self.output_dir = None
868 ced78a66 Agata Murawska
    self._ReadInputData(input_path)
869 ced78a66 Agata Murawska
870 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
871 ced78a66 Agata Murawska
    """Reads the data on which the conversion will take place.
872 ced78a66 Agata Murawska

873 ced78a66 Agata Murawska
    @type input_path: string
874 ced78a66 Agata Murawska
    @param input_path: absolute path to the Converter input file
875 ced78a66 Agata Murawska

876 ced78a66 Agata Murawska
    """
877 ced78a66 Agata Murawska
    raise NotImplementedError()
878 ced78a66 Agata Murawska
879 99381e3b Agata Murawska
  def _CompressDisk(self, disk_path, compression, action):
880 99381e3b Agata Murawska
    """Performs (de)compression on the disk and returns the new path
881 99381e3b Agata Murawska

882 99381e3b Agata Murawska
    @type disk_path: string
883 99381e3b Agata Murawska
    @param disk_path: path to the disk
884 99381e3b Agata Murawska
    @type compression: string
885 99381e3b Agata Murawska
    @param compression: compression type
886 99381e3b Agata Murawska
    @type action: string
887 99381e3b Agata Murawska
    @param action: whether the action is compression or decompression
888 99381e3b Agata Murawska
    @rtype: string
889 99381e3b Agata Murawska
    @return: new disk path after (de)compression
890 99381e3b Agata Murawska

891 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk (de)compression failed or "compression"
892 99381e3b Agata Murawska
      is not supported
893 99381e3b Agata Murawska

894 99381e3b Agata Murawska
    """
895 99381e3b Agata Murawska
    assert(action in ALLOWED_ACTIONS)
896 99381e3b Agata Murawska
    # For now we only support gzip, as it is used in ovftool
897 99381e3b Agata Murawska
    if compression != COMPRESSION_TYPE:
898 99381e3b Agata Murawska
      raise errors.OpPrereqError("Unsupported compression type: %s"
899 99381e3b Agata Murawska
                                 % compression)
900 99381e3b Agata Murawska
    disk_file = os.path.basename(disk_path)
901 99381e3b Agata Murawska
    if action == DECOMPRESS:
902 99381e3b Agata Murawska
      (disk_name, _) = os.path.splitext(disk_file)
903 99381e3b Agata Murawska
      prefix = disk_name
904 99381e3b Agata Murawska
    elif action == COMPRESS:
905 99381e3b Agata Murawska
      prefix = disk_file
906 99381e3b Agata Murawska
    new_path = utils.GetClosedTempfile(suffix=COMPRESSION_EXT, prefix=prefix,
907 99381e3b Agata Murawska
      dir=self.output_dir)
908 99381e3b Agata Murawska
    self.temp_file_manager.Add(new_path)
909 99381e3b Agata Murawska
    args = ["gzip", "-c", disk_path]
910 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, output=new_path)
911 99381e3b Agata Murawska
    if run_result.failed:
912 99381e3b Agata Murawska
      raise errors.OpPrereqError("Disk %s failed with output: %s"
913 99381e3b Agata Murawska
                                 % (action, run_result.stderr))
914 99381e3b Agata Murawska
    logging.info("The %s of the disk is completed", action)
915 99381e3b Agata Murawska
    return (COMPRESSION_EXT, new_path)
916 99381e3b Agata Murawska
917 99381e3b Agata Murawska
  def _ConvertDisk(self, disk_format, disk_path):
918 99381e3b Agata Murawska
    """Performes conversion to specified format.
919 99381e3b Agata Murawska

920 99381e3b Agata Murawska
    @type disk_format: string
921 99381e3b Agata Murawska
    @param disk_format: format to which the disk should be converted
922 99381e3b Agata Murawska
    @type disk_path: string
923 99381e3b Agata Murawska
    @param disk_path: path to the disk that should be converted
924 99381e3b Agata Murawska
    @rtype: string
925 99381e3b Agata Murawska
    @return path to the output disk
926 99381e3b Agata Murawska

927 99381e3b Agata Murawska
    @raise errors.OpPrereqError: convertion of the disk failed
928 99381e3b Agata Murawska

929 99381e3b Agata Murawska
    """
930 a002ed79 Agata Murawska
    CheckQemuImg()
931 99381e3b Agata Murawska
    disk_file = os.path.basename(disk_path)
932 99381e3b Agata Murawska
    (disk_name, disk_extension) = os.path.splitext(disk_file)
933 99381e3b Agata Murawska
    if disk_extension != disk_format:
934 99381e3b Agata Murawska
      logging.warning("Conversion of disk image to %s format, this may take"
935 99381e3b Agata Murawska
                      " a while", disk_format)
936 99381e3b Agata Murawska
937 99381e3b Agata Murawska
    new_disk_path = utils.GetClosedTempfile(suffix=".%s" % disk_format,
938 99381e3b Agata Murawska
      prefix=disk_name, dir=self.output_dir)
939 99381e3b Agata Murawska
    self.temp_file_manager.Add(new_disk_path)
940 99381e3b Agata Murawska
    args = [
941 e68949b0 Agata Murawska
      constants.QEMUIMG_PATH,
942 99381e3b Agata Murawska
      "convert",
943 99381e3b Agata Murawska
      "-O",
944 99381e3b Agata Murawska
      disk_format,
945 99381e3b Agata Murawska
      disk_path,
946 99381e3b Agata Murawska
      new_disk_path,
947 99381e3b Agata Murawska
    ]
948 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, cwd=os.getcwd())
949 99381e3b Agata Murawska
    if run_result.failed:
950 99381e3b Agata Murawska
      raise errors.OpPrereqError("Convertion to %s failed, qemu-img output was"
951 99381e3b Agata Murawska
                                 ": %s" % (disk_format, run_result.stderr))
952 99381e3b Agata Murawska
    return (".%s" % disk_format, new_disk_path)
953 99381e3b Agata Murawska
954 99381e3b Agata Murawska
  @staticmethod
955 99381e3b Agata Murawska
  def _GetDiskQemuInfo(disk_path, regexp):
956 99381e3b Agata Murawska
    """Figures out some information of the disk using qemu-img.
957 99381e3b Agata Murawska

958 99381e3b Agata Murawska
    @type disk_path: string
959 99381e3b Agata Murawska
    @param disk_path: path to the disk we want to know the format of
960 99381e3b Agata Murawska
    @type regexp: string
961 99381e3b Agata Murawska
    @param regexp: string that has to be matched, it has to contain one group
962 99381e3b Agata Murawska
    @rtype: string
963 99381e3b Agata Murawska
    @return: disk format
964 99381e3b Agata Murawska

965 99381e3b Agata Murawska
    @raise errors.OpPrereqError: format information cannot be retrieved
966 99381e3b Agata Murawska

967 99381e3b Agata Murawska
    """
968 a002ed79 Agata Murawska
    CheckQemuImg()
969 e68949b0 Agata Murawska
    args = [constants.QEMUIMG_PATH, "info", disk_path]
970 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, cwd=os.getcwd())
971 99381e3b Agata Murawska
    if run_result.failed:
972 99381e3b Agata Murawska
      raise errors.OpPrereqError("Gathering info about the disk using qemu-img"
973 99381e3b Agata Murawska
                                 " failed, output was: %s" % run_result.stderr)
974 99381e3b Agata Murawska
    result = run_result.output
975 99381e3b Agata Murawska
    regexp = r"%s" % regexp
976 99381e3b Agata Murawska
    match = re.search(regexp, result)
977 99381e3b Agata Murawska
    if match:
978 99381e3b Agata Murawska
      disk_format = match.group(1)
979 99381e3b Agata Murawska
    else:
980 99381e3b Agata Murawska
      raise errors.OpPrereqError("No file information matching %s found in:"
981 99381e3b Agata Murawska
                                 " %s" % (regexp, result))
982 99381e3b Agata Murawska
    return disk_format
983 99381e3b Agata Murawska
984 ced78a66 Agata Murawska
  def Parse(self):
985 ced78a66 Agata Murawska
    """Parses the data and creates a structure containing all required info.
986 ced78a66 Agata Murawska

987 ced78a66 Agata Murawska
    """
988 ced78a66 Agata Murawska
    raise NotImplementedError()
989 ced78a66 Agata Murawska
990 ced78a66 Agata Murawska
  def Save(self):
991 ced78a66 Agata Murawska
    """Saves the gathered configuration in an apropriate format.
992 ced78a66 Agata Murawska

993 ced78a66 Agata Murawska
    """
994 ced78a66 Agata Murawska
    raise NotImplementedError()
995 ced78a66 Agata Murawska
996 ced78a66 Agata Murawska
  def Cleanup(self):
997 ced78a66 Agata Murawska
    """Cleans the temporary directory, if one was created.
998 ced78a66 Agata Murawska

999 ced78a66 Agata Murawska
    """
1000 ced78a66 Agata Murawska
    self.temp_file_manager.Cleanup()
1001 ced78a66 Agata Murawska
    if self.temp_dir:
1002 ced78a66 Agata Murawska
      shutil.rmtree(self.temp_dir)
1003 ced78a66 Agata Murawska
      self.temp_dir = None
1004 ced78a66 Agata Murawska
1005 ced78a66 Agata Murawska
1006 ced78a66 Agata Murawska
class OVFImporter(Converter):
1007 864cf6bf Agata Murawska
  """Converter from OVF to Ganeti config file.
1008 864cf6bf Agata Murawska

1009 864cf6bf Agata Murawska
  @type input_dir: string
1010 864cf6bf Agata Murawska
  @ivar input_dir: directory in which the .ovf file resides
1011 864cf6bf Agata Murawska
  @type output_dir: string
1012 864cf6bf Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
1013 864cf6bf Agata Murawska
    written
1014 864cf6bf Agata Murawska
  @type input_path: string
1015 864cf6bf Agata Murawska
  @ivar input_path: complete path to the .ovf file
1016 864cf6bf Agata Murawska
  @type ovf_reader: L{OVFReader}
1017 864cf6bf Agata Murawska
  @ivar ovf_reader: OVF reader instance collects data from .ovf file
1018 99381e3b Agata Murawska
  @type results_name: string
1019 99381e3b Agata Murawska
  @ivar results_name: name of imported instance
1020 99381e3b Agata Murawska
  @type results_template: string
1021 99381e3b Agata Murawska
  @ivar results_template: disk template read from .ovf file or command line
1022 99381e3b Agata Murawska
    arguments
1023 7bde29b5 Agata Murawska
  @type results_hypervisor: dict
1024 7bde29b5 Agata Murawska
  @ivar results_hypervisor: hypervisor information gathered from .ovf file or
1025 7bde29b5 Agata Murawska
    command line arguments
1026 7bde29b5 Agata Murawska
  @type results_os: dict
1027 7bde29b5 Agata Murawska
  @ivar results_os: operating system information gathered from .ovf file or
1028 7bde29b5 Agata Murawska
    command line arguments
1029 7bde29b5 Agata Murawska
  @type results_backend: dict
1030 7bde29b5 Agata Murawska
  @ivar results_backend: backend information gathered from .ovf file or
1031 7bde29b5 Agata Murawska
    command line arguments
1032 7bde29b5 Agata Murawska
  @type results_tags: string
1033 7bde29b5 Agata Murawska
  @ivar results_tags: string containing instance-specific tags
1034 7bde29b5 Agata Murawska
  @type results_version: string
1035 7bde29b5 Agata Murawska
  @ivar results_version: version as required by Ganeti import
1036 24b9469d Agata Murawska
  @type results_network: dict
1037 24b9469d Agata Murawska
  @ivar results_network: network information gathered from .ovf file or command
1038 24b9469d Agata Murawska
    line arguments
1039 99381e3b Agata Murawska
  @type results_disk: dict
1040 99381e3b Agata Murawska
  @ivar results_disk: disk information gathered from .ovf file or command line
1041 99381e3b Agata Murawska
    arguments
1042 864cf6bf Agata Murawska

1043 864cf6bf Agata Murawska
  """
1044 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
1045 864cf6bf Agata Murawska
    """Reads the data on which the conversion will take place.
1046 864cf6bf Agata Murawska

1047 864cf6bf Agata Murawska
    @type input_path: string
1048 864cf6bf Agata Murawska
    @param input_path: absolute path to the .ovf or .ova input file
1049 864cf6bf Agata Murawska

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

1052 864cf6bf Agata Murawska
    """
1053 864cf6bf Agata Murawska
    (input_dir, input_file) = os.path.split(input_path)
1054 864cf6bf Agata Murawska
    (_, input_extension) = os.path.splitext(input_file)
1055 864cf6bf Agata Murawska
1056 864cf6bf Agata Murawska
    if input_extension == OVF_EXT:
1057 864cf6bf Agata Murawska
      logging.info("%s file extension found, no unpacking necessary", OVF_EXT)
1058 864cf6bf Agata Murawska
      self.input_dir = input_dir
1059 864cf6bf Agata Murawska
      self.input_path = input_path
1060 864cf6bf Agata Murawska
      self.temp_dir = None
1061 864cf6bf Agata Murawska
    elif input_extension == OVA_EXT:
1062 864cf6bf Agata Murawska
      logging.info("%s file extension found, proceeding to unpacking", OVA_EXT)
1063 864cf6bf Agata Murawska
      self._UnpackOVA(input_path)
1064 864cf6bf Agata Murawska
    else:
1065 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Unknown file extension; expected %s or %s"
1066 864cf6bf Agata Murawska
                                 " file" % (OVA_EXT, OVF_EXT))
1067 864cf6bf Agata Murawska
    assert ((input_extension == OVA_EXT and self.temp_dir) or
1068 864cf6bf Agata Murawska
            (input_extension == OVF_EXT and not self.temp_dir))
1069 864cf6bf Agata Murawska
    assert self.input_dir in self.input_path
1070 864cf6bf Agata Murawska
1071 864cf6bf Agata Murawska
    if self.options.output_dir:
1072 864cf6bf Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1073 864cf6bf Agata Murawska
      if (os.path.commonprefix([constants.EXPORT_DIR, self.output_dir]) !=
1074 864cf6bf Agata Murawska
          constants.EXPORT_DIR):
1075 864cf6bf Agata Murawska
        logging.warning("Export path is not under %s directory, import to"
1076 864cf6bf Agata Murawska
                        " Ganeti using gnt-backup may fail",
1077 864cf6bf Agata Murawska
                        constants.EXPORT_DIR)
1078 864cf6bf Agata Murawska
    else:
1079 864cf6bf Agata Murawska
      self.output_dir = constants.EXPORT_DIR
1080 864cf6bf Agata Murawska
1081 864cf6bf Agata Murawska
    self.ovf_reader = OVFReader(self.input_path)
1082 864cf6bf Agata Murawska
    self.ovf_reader.VerifyManifest()
1083 864cf6bf Agata Murawska
1084 864cf6bf Agata Murawska
  def _UnpackOVA(self, input_path):
1085 864cf6bf Agata Murawska
    """Unpacks the .ova package into temporary directory.
1086 864cf6bf Agata Murawska

1087 864cf6bf Agata Murawska
    @type input_path: string
1088 864cf6bf Agata Murawska
    @param input_path: path to the .ova package file
1089 864cf6bf Agata Murawska

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

1094 864cf6bf Agata Murawska
    """
1095 864cf6bf Agata Murawska
    input_name = None
1096 864cf6bf Agata Murawska
    if not tarfile.is_tarfile(input_path):
1097 864cf6bf Agata Murawska
      raise errors.OpPrereqError("The provided %s file is not a proper tar"
1098 864cf6bf Agata Murawska
                                 " archive", OVA_EXT)
1099 864cf6bf Agata Murawska
    ova_content = tarfile.open(input_path)
1100 864cf6bf Agata Murawska
    temp_dir = tempfile.mkdtemp()
1101 864cf6bf Agata Murawska
    self.temp_dir = temp_dir
1102 864cf6bf Agata Murawska
    for file_name in ova_content.getnames():
1103 864cf6bf Agata Murawska
      file_normname = os.path.normpath(file_name)
1104 864cf6bf Agata Murawska
      try:
1105 864cf6bf Agata Murawska
        utils.PathJoin(temp_dir, file_normname)
1106 864cf6bf Agata Murawska
      except ValueError, err:
1107 864cf6bf Agata Murawska
        raise errors.OpPrereqError("File %s inside %s package is not safe" %
1108 864cf6bf Agata Murawska
                                   (file_name, OVA_EXT))
1109 864cf6bf Agata Murawska
      if file_name.endswith(OVF_EXT):
1110 864cf6bf Agata Murawska
        input_name = file_name
1111 864cf6bf Agata Murawska
    if not input_name:
1112 864cf6bf Agata Murawska
      raise errors.OpPrereqError("No %s file in %s package found" %
1113 864cf6bf Agata Murawska
                                 (OVF_EXT, OVA_EXT))
1114 864cf6bf Agata Murawska
    logging.warning("Unpacking the %s archive, this may take a while",
1115 864cf6bf Agata Murawska
      input_path)
1116 864cf6bf Agata Murawska
    self.input_dir = temp_dir
1117 864cf6bf Agata Murawska
    self.input_path = utils.PathJoin(self.temp_dir, input_name)
1118 864cf6bf Agata Murawska
    try:
1119 864cf6bf Agata Murawska
      try:
1120 864cf6bf Agata Murawska
        extract = ova_content.extractall
1121 864cf6bf Agata Murawska
      except AttributeError:
1122 864cf6bf Agata Murawska
        # This is a prehistorical case of using python < 2.5
1123 864cf6bf Agata Murawska
        for member in ova_content.getmembers():
1124 864cf6bf Agata Murawska
          ova_content.extract(member, path=self.temp_dir)
1125 864cf6bf Agata Murawska
      else:
1126 864cf6bf Agata Murawska
        extract(self.temp_dir)
1127 864cf6bf Agata Murawska
    except tarfile.TarError, err:
1128 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Error while extracting %s archive: %s" %
1129 864cf6bf Agata Murawska
                                 (OVA_EXT, err))
1130 864cf6bf Agata Murawska
    logging.info("OVA package extracted to %s directory", self.temp_dir)
1131 ced78a66 Agata Murawska
1132 ced78a66 Agata Murawska
  def Parse(self):
1133 99381e3b Agata Murawska
    """Parses the data and creates a structure containing all required info.
1134 99381e3b Agata Murawska

1135 99381e3b Agata Murawska
    The method reads the information given either as a command line option or as
1136 99381e3b Agata Murawska
    a part of the OVF description.
1137 99381e3b Agata Murawska

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

1141 99381e3b Agata Murawska
    """
1142 99381e3b Agata Murawska
    self.results_name = self._GetInfo("instance name", self.options.name,
1143 99381e3b Agata Murawska
      self._ParseNameOptions, self.ovf_reader.GetInstanceName)
1144 99381e3b Agata Murawska
    if not self.results_name:
1145 99381e3b Agata Murawska
      raise errors.OpPrereqError("Name of instance not provided")
1146 99381e3b Agata Murawska
1147 99381e3b Agata Murawska
    self.output_dir = utils.PathJoin(self.output_dir, self.results_name)
1148 99381e3b Agata Murawska
    try:
1149 99381e3b Agata Murawska
      utils.Makedirs(self.output_dir)
1150 99381e3b Agata Murawska
    except OSError, err:
1151 99381e3b Agata Murawska
      raise errors.OpPrereqError("Failed to create directory %s: %s" %
1152 99381e3b Agata Murawska
                                 (self.output_dir, err))
1153 99381e3b Agata Murawska
1154 99381e3b Agata Murawska
    self.results_template = self._GetInfo("disk template",
1155 99381e3b Agata Murawska
      self.options.disk_template, self._ParseTemplateOptions,
1156 99381e3b Agata Murawska
      self.ovf_reader.GetDiskTemplate)
1157 99381e3b Agata Murawska
    if not self.results_template:
1158 99381e3b Agata Murawska
      logging.info("Disk template not given")
1159 99381e3b Agata Murawska
1160 7bde29b5 Agata Murawska
    self.results_hypervisor = self._GetInfo("hypervisor",
1161 7bde29b5 Agata Murawska
      self.options.hypervisor, self._ParseHypervisorOptions,
1162 7bde29b5 Agata Murawska
      self.ovf_reader.GetHypervisorData)
1163 7bde29b5 Agata Murawska
    assert self.results_hypervisor["hypervisor_name"]
1164 7bde29b5 Agata Murawska
    if self.results_hypervisor["hypervisor_name"] == constants.VALUE_AUTO:
1165 7bde29b5 Agata Murawska
      logging.debug("Default hypervisor settings from the cluster will be used")
1166 7bde29b5 Agata Murawska
1167 7bde29b5 Agata Murawska
    self.results_os = self._GetInfo("OS", self.options.os,
1168 7bde29b5 Agata Murawska
      self._ParseOSOptions, self.ovf_reader.GetOSData)
1169 7bde29b5 Agata Murawska
    if not self.results_os.get("os_name"):
1170 7bde29b5 Agata Murawska
      raise errors.OpPrereqError("OS name must be provided")
1171 7bde29b5 Agata Murawska
1172 7bde29b5 Agata Murawska
    self.results_backend = self._GetInfo("backend", self.options.beparams,
1173 7bde29b5 Agata Murawska
      self._ParseBackendOptions, self.ovf_reader.GetBackendData)
1174 7bde29b5 Agata Murawska
    assert self.results_backend.get("vcpus")
1175 7bde29b5 Agata Murawska
    assert self.results_backend.get("memory")
1176 7bde29b5 Agata Murawska
    assert self.results_backend.get("auto_balance") is not None
1177 7bde29b5 Agata Murawska
1178 7bde29b5 Agata Murawska
    self.results_tags = self._GetInfo("tags", self.options.tags,
1179 7bde29b5 Agata Murawska
      self._ParseTags, self.ovf_reader.GetTagsData)
1180 7bde29b5 Agata Murawska
1181 7bde29b5 Agata Murawska
    ovf_version = self.ovf_reader.GetVersionData()
1182 7bde29b5 Agata Murawska
    if ovf_version:
1183 7bde29b5 Agata Murawska
      self.results_version = ovf_version
1184 7bde29b5 Agata Murawska
    else:
1185 7bde29b5 Agata Murawska
      self.results_version = constants.EXPORT_VERSION
1186 7bde29b5 Agata Murawska
1187 24b9469d Agata Murawska
    self.results_network = self._GetInfo("network", self.options.nics,
1188 24b9469d Agata Murawska
      self._ParseNicOptions, self.ovf_reader.GetNetworkData,
1189 24b9469d Agata Murawska
      ignore_test=self.options.no_nics)
1190 24b9469d Agata Murawska
1191 99381e3b Agata Murawska
    self.results_disk = self._GetInfo("disk", self.options.disks,
1192 99381e3b Agata Murawska
      self._ParseDiskOptions, self._GetDiskInfo,
1193 99381e3b Agata Murawska
      ignore_test=self.results_template == constants.DT_DISKLESS)
1194 99381e3b Agata Murawska
1195 24b9469d Agata Murawska
    if not self.results_disk and not self.results_network:
1196 24b9469d Agata Murawska
      raise errors.OpPrereqError("Either disk specification or network"
1197 24b9469d Agata Murawska
                                 " description must be present")
1198 24b9469d Agata Murawska
1199 99381e3b Agata Murawska
  @staticmethod
1200 99381e3b Agata Murawska
  def _GetInfo(name, cmd_arg, cmd_function, nocmd_function,
1201 99381e3b Agata Murawska
    ignore_test=False):
1202 99381e3b Agata Murawska
    """Get information about some section - e.g. disk, network, hypervisor.
1203 99381e3b Agata Murawska

1204 99381e3b Agata Murawska
    @type name: string
1205 99381e3b Agata Murawska
    @param name: name of the section
1206 99381e3b Agata Murawska
    @type cmd_arg: dict
1207 99381e3b Agata Murawska
    @param cmd_arg: command line argument specific for section 'name'
1208 99381e3b Agata Murawska
    @type cmd_function: callable
1209 99381e3b Agata Murawska
    @param cmd_function: function to call if 'cmd_args' exists
1210 99381e3b Agata Murawska
    @type nocmd_function: callable
1211 99381e3b Agata Murawska
    @param nocmd_function: function to call if 'cmd_args' is not there
1212 99381e3b Agata Murawska

1213 99381e3b Agata Murawska
    """
1214 99381e3b Agata Murawska
    if ignore_test:
1215 99381e3b Agata Murawska
      logging.info("Information for %s will be ignored", name)
1216 99381e3b Agata Murawska
      return {}
1217 99381e3b Agata Murawska
    if cmd_arg:
1218 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from command line", name)
1219 99381e3b Agata Murawska
      results = cmd_function()
1220 99381e3b Agata Murawska
    else:
1221 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from %s file",
1222 99381e3b Agata Murawska
        name, OVF_EXT)
1223 99381e3b Agata Murawska
      results = nocmd_function()
1224 99381e3b Agata Murawska
    logging.info("Options for %s were succesfully read", name)
1225 99381e3b Agata Murawska
    return results
1226 99381e3b Agata Murawska
1227 99381e3b Agata Murawska
  def _ParseNameOptions(self):
1228 99381e3b Agata Murawska
    """Returns name if one was given in command line.
1229 99381e3b Agata Murawska

1230 99381e3b Agata Murawska
    @rtype: string
1231 99381e3b Agata Murawska
    @return: name of an instance
1232 99381e3b Agata Murawska

1233 99381e3b Agata Murawska
    """
1234 99381e3b Agata Murawska
    return self.options.name
1235 99381e3b Agata Murawska
1236 99381e3b Agata Murawska
  def _ParseTemplateOptions(self):
1237 99381e3b Agata Murawska
    """Returns disk template if one was given in command line.
1238 99381e3b Agata Murawska

1239 99381e3b Agata Murawska
    @rtype: string
1240 99381e3b Agata Murawska
    @return: disk template name
1241 99381e3b Agata Murawska

1242 99381e3b Agata Murawska
    """
1243 99381e3b Agata Murawska
    return self.options.disk_template
1244 99381e3b Agata Murawska
1245 7bde29b5 Agata Murawska
  def _ParseHypervisorOptions(self):
1246 7bde29b5 Agata Murawska
    """Parses hypervisor options given in a command line.
1247 7bde29b5 Agata Murawska

1248 7bde29b5 Agata Murawska
    @rtype: dict
1249 7bde29b5 Agata Murawska
    @return: dictionary containing name of the chosen hypervisor and all the
1250 7bde29b5 Agata Murawska
      options
1251 7bde29b5 Agata Murawska

1252 7bde29b5 Agata Murawska
    """
1253 7bde29b5 Agata Murawska
    assert type(self.options.hypervisor) is tuple
1254 7bde29b5 Agata Murawska
    assert len(self.options.hypervisor) == 2
1255 7bde29b5 Agata Murawska
    results = {}
1256 7bde29b5 Agata Murawska
    if self.options.hypervisor[0]:
1257 7bde29b5 Agata Murawska
      results["hypervisor_name"] = self.options.hypervisor[0]
1258 7bde29b5 Agata Murawska
    else:
1259 7bde29b5 Agata Murawska
      results["hypervisor_name"] = constants.VALUE_AUTO
1260 7bde29b5 Agata Murawska
    results.update(self.options.hypervisor[1])
1261 7bde29b5 Agata Murawska
    return results
1262 7bde29b5 Agata Murawska
1263 7bde29b5 Agata Murawska
  def _ParseOSOptions(self):
1264 7bde29b5 Agata Murawska
    """Parses OS options given in command line.
1265 7bde29b5 Agata Murawska

1266 7bde29b5 Agata Murawska
    @rtype: dict
1267 7bde29b5 Agata Murawska
    @return: dictionary containing name of chosen OS and all its options
1268 7bde29b5 Agata Murawska

1269 7bde29b5 Agata Murawska
    """
1270 7bde29b5 Agata Murawska
    assert self.options.os
1271 7bde29b5 Agata Murawska
    results = {}
1272 7bde29b5 Agata Murawska
    results["os_name"] = self.options.os
1273 7bde29b5 Agata Murawska
    results.update(self.options.osparams)
1274 7bde29b5 Agata Murawska
    return results
1275 7bde29b5 Agata Murawska
1276 7bde29b5 Agata Murawska
  def _ParseBackendOptions(self):
1277 7bde29b5 Agata Murawska
    """Parses backend options given in command line.
1278 7bde29b5 Agata Murawska

1279 7bde29b5 Agata Murawska
    @rtype: dict
1280 7bde29b5 Agata Murawska
    @return: dictionary containing vcpus, memory and auto-balance options
1281 7bde29b5 Agata Murawska

1282 7bde29b5 Agata Murawska
    """
1283 7bde29b5 Agata Murawska
    assert self.options.beparams
1284 7bde29b5 Agata Murawska
    backend = {}
1285 7bde29b5 Agata Murawska
    backend.update(self.options.beparams)
1286 7bde29b5 Agata Murawska
    must_contain = ["vcpus", "memory", "auto_balance"]
1287 7bde29b5 Agata Murawska
    for element in must_contain:
1288 7bde29b5 Agata Murawska
      if backend.get(element) is None:
1289 7bde29b5 Agata Murawska
        backend[element] = constants.VALUE_AUTO
1290 7bde29b5 Agata Murawska
    return backend
1291 7bde29b5 Agata Murawska
1292 7bde29b5 Agata Murawska
  def _ParseTags(self):
1293 7bde29b5 Agata Murawska
    """Returns tags list given in command line.
1294 7bde29b5 Agata Murawska

1295 7bde29b5 Agata Murawska
    @rtype: string
1296 7bde29b5 Agata Murawska
    @return: string containing comma-separated tags
1297 7bde29b5 Agata Murawska

1298 7bde29b5 Agata Murawska
    """
1299 7bde29b5 Agata Murawska
    return self.options.tags
1300 7bde29b5 Agata Murawska
1301 24b9469d Agata Murawska
  def _ParseNicOptions(self):
1302 24b9469d Agata Murawska
    """Parses network options given in a command line or as a dictionary.
1303 24b9469d Agata Murawska

1304 24b9469d Agata Murawska
    @rtype: dict
1305 24b9469d Agata Murawska
    @return: dictionary of network-related options
1306 24b9469d Agata Murawska

1307 24b9469d Agata Murawska
    """
1308 24b9469d Agata Murawska
    assert self.options.nics
1309 24b9469d Agata Murawska
    results = {}
1310 24b9469d Agata Murawska
    for (nic_id, nic_desc) in self.options.nics:
1311 24b9469d Agata Murawska
      results["nic%s_mode" % nic_id] = \
1312 24b9469d Agata Murawska
        nic_desc.get("mode", constants.VALUE_AUTO)
1313 24b9469d Agata Murawska
      results["nic%s_mac" % nic_id] = nic_desc.get("mac", constants.VALUE_AUTO)
1314 24b9469d Agata Murawska
      results["nic%s_link" % nic_id] = \
1315 24b9469d Agata Murawska
        nic_desc.get("link", constants.VALUE_AUTO)
1316 24b9469d Agata Murawska
      if nic_desc.get("mode") == "bridged":
1317 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_NONE
1318 24b9469d Agata Murawska
      else:
1319 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_AUTO
1320 24b9469d Agata Murawska
    results["nic_count"] = str(len(self.options.nics))
1321 24b9469d Agata Murawska
    return results
1322 24b9469d Agata Murawska
1323 99381e3b Agata Murawska
  def _ParseDiskOptions(self):
1324 99381e3b Agata Murawska
    """Parses disk options given in a command line.
1325 99381e3b Agata Murawska

1326 99381e3b Agata Murawska
    @rtype: dict
1327 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1328 99381e3b Agata Murawska

1329 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk description does not contain size
1330 99381e3b Agata Murawska
      information or size information is invalid or creation failed
1331 99381e3b Agata Murawska

1332 99381e3b Agata Murawska
    """
1333 a002ed79 Agata Murawska
    CheckQemuImg()
1334 99381e3b Agata Murawska
    assert self.options.disks
1335 99381e3b Agata Murawska
    results = {}
1336 99381e3b Agata Murawska
    for (disk_id, disk_desc) in self.options.disks:
1337 99381e3b Agata Murawska
      results["disk%s_ivname" % disk_id] = "disk/%s" % disk_id
1338 99381e3b Agata Murawska
      if disk_desc.get("size"):
1339 99381e3b Agata Murawska
        try:
1340 99381e3b Agata Murawska
          disk_size = utils.ParseUnit(disk_desc["size"])
1341 99381e3b Agata Murawska
        except ValueError:
1342 99381e3b Agata Murawska
          raise errors.OpPrereqError("Invalid disk size for disk %s: %s" %
1343 99381e3b Agata Murawska
                                     (disk_id, disk_desc["size"]))
1344 99381e3b Agata Murawska
        new_path = utils.PathJoin(self.output_dir, str(disk_id))
1345 99381e3b Agata Murawska
        args = [
1346 e68949b0 Agata Murawska
          constants.QEMUIMG_PATH,
1347 99381e3b Agata Murawska
          "create",
1348 99381e3b Agata Murawska
          "-f",
1349 99381e3b Agata Murawska
          "raw",
1350 99381e3b Agata Murawska
          new_path,
1351 99381e3b Agata Murawska
          disk_size,
1352 99381e3b Agata Murawska
        ]
1353 99381e3b Agata Murawska
        run_result = utils.RunCmd(args)
1354 99381e3b Agata Murawska
        if run_result.failed:
1355 99381e3b Agata Murawska
          raise errors.OpPrereqError("Creation of disk %s failed, output was:"
1356 99381e3b Agata Murawska
                                     " %s" % (new_path, run_result.stderr))
1357 99381e3b Agata Murawska
        results["disk%s_size" % disk_id] = str(disk_size)
1358 99381e3b Agata Murawska
        results["disk%s_dump" % disk_id] = "disk%s.raw" % disk_id
1359 99381e3b Agata Murawska
      else:
1360 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks created for import must have their"
1361 99381e3b Agata Murawska
                                   " size specified")
1362 99381e3b Agata Murawska
    results["disk_count"] = str(len(self.options.disks))
1363 99381e3b Agata Murawska
    return results
1364 99381e3b Agata Murawska
1365 99381e3b Agata Murawska
  def _GetDiskInfo(self):
1366 99381e3b Agata Murawska
    """Gathers information about disks used by instance, perfomes conversion.
1367 99381e3b Agata Murawska

1368 99381e3b Agata Murawska
    @rtype: dict
1369 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1370 99381e3b Agata Murawska

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

1373 99381e3b Agata Murawska
    """
1374 99381e3b Agata Murawska
    results = {}
1375 99381e3b Agata Murawska
    disks_list = self.ovf_reader.GetDisksNames()
1376 99381e3b Agata Murawska
    for (counter, (disk_name, disk_compression)) in enumerate(disks_list):
1377 99381e3b Agata Murawska
      if os.path.dirname(disk_name):
1378 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks are not allowed to have absolute"
1379 99381e3b Agata Murawska
                                   " paths or paths outside main OVF directory")
1380 99381e3b Agata Murawska
      disk, _ = os.path.splitext(disk_name)
1381 99381e3b Agata Murawska
      disk_path = utils.PathJoin(self.input_dir, disk_name)
1382 fa337742 Agata Murawska
      if disk_compression not in NO_COMPRESSION:
1383 99381e3b Agata Murawska
        _, disk_path = self._CompressDisk(disk_path, disk_compression,
1384 99381e3b Agata Murawska
          DECOMPRESS)
1385 99381e3b Agata Murawska
        disk, _ = os.path.splitext(disk)
1386 99381e3b Agata Murawska
      if self._GetDiskQemuInfo(disk_path, "file format: (\S+)") != "raw":
1387 99381e3b Agata Murawska
        logging.info("Conversion to raw format is required")
1388 99381e3b Agata Murawska
      ext, new_disk_path = self._ConvertDisk("raw", disk_path)
1389 99381e3b Agata Murawska
1390 99381e3b Agata Murawska
      final_disk_path = LinkFile(new_disk_path, prefix=disk, suffix=ext,
1391 99381e3b Agata Murawska
        directory=self.output_dir)
1392 99381e3b Agata Murawska
      final_name = os.path.basename(final_disk_path)
1393 99381e3b Agata Murawska
      disk_size = os.path.getsize(final_disk_path) / (1024 * 1024)
1394 99381e3b Agata Murawska
      results["disk%s_dump" % counter] = final_name
1395 99381e3b Agata Murawska
      results["disk%s_size" % counter] = str(disk_size)
1396 99381e3b Agata Murawska
      results["disk%s_ivname" % counter] = "disk/%s" % str(counter)
1397 99381e3b Agata Murawska
    if disks_list:
1398 99381e3b Agata Murawska
      results["disk_count"] = str(len(disks_list))
1399 99381e3b Agata Murawska
    return results
1400 ced78a66 Agata Murawska
1401 ced78a66 Agata Murawska
  def Save(self):
1402 864cf6bf Agata Murawska
    """Saves all the gathered information in a constant.EXPORT_CONF_FILE file.
1403 864cf6bf Agata Murawska

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

1406 864cf6bf Agata Murawska
    """
1407 864cf6bf Agata Murawska
    logging.info("Conversion was succesfull, saving %s in %s directory",
1408 864cf6bf Agata Murawska
                 constants.EXPORT_CONF_FILE, self.output_dir)
1409 864cf6bf Agata Murawska
    results = {
1410 864cf6bf Agata Murawska
      constants.INISECT_INS: {},
1411 864cf6bf Agata Murawska
      constants.INISECT_BEP: {},
1412 864cf6bf Agata Murawska
      constants.INISECT_EXP: {},
1413 864cf6bf Agata Murawska
      constants.INISECT_OSP: {},
1414 864cf6bf Agata Murawska
      constants.INISECT_HYP: {},
1415 864cf6bf Agata Murawska
    }
1416 864cf6bf Agata Murawska
1417 864cf6bf Agata Murawska
    results[constants.INISECT_INS].update(self.results_disk)
1418 24b9469d Agata Murawska
    results[constants.INISECT_INS].update(self.results_network)
1419 7bde29b5 Agata Murawska
    results[constants.INISECT_INS]["hypervisor"] = \
1420 7bde29b5 Agata Murawska
      self.results_hypervisor["hypervisor_name"]
1421 864cf6bf Agata Murawska
    results[constants.INISECT_INS]["name"] = self.results_name
1422 864cf6bf Agata Murawska
    if self.results_template:
1423 864cf6bf Agata Murawska
      results[constants.INISECT_INS]["disk_template"] = self.results_template
1424 7bde29b5 Agata Murawska
    if self.results_tags:
1425 7bde29b5 Agata Murawska
      results[constants.INISECT_INS]["tags"] = self.results_tags
1426 7bde29b5 Agata Murawska
1427 7bde29b5 Agata Murawska
    results[constants.INISECT_BEP].update(self.results_backend)
1428 7bde29b5 Agata Murawska
1429 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["os"] = self.results_os["os_name"]
1430 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["version"] = self.results_version
1431 7bde29b5 Agata Murawska
1432 7bde29b5 Agata Murawska
    del self.results_os["os_name"]
1433 7bde29b5 Agata Murawska
    results[constants.INISECT_OSP].update(self.results_os)
1434 7bde29b5 Agata Murawska
1435 7bde29b5 Agata Murawska
    del self.results_hypervisor["hypervisor_name"]
1436 7bde29b5 Agata Murawska
    results[constants.INISECT_HYP].update(self.results_hypervisor)
1437 864cf6bf Agata Murawska
1438 864cf6bf Agata Murawska
    output_file_name = utils.PathJoin(self.output_dir,
1439 864cf6bf Agata Murawska
      constants.EXPORT_CONF_FILE)
1440 864cf6bf Agata Murawska
1441 864cf6bf Agata Murawska
    output = []
1442 864cf6bf Agata Murawska
    for section, options in results.iteritems():
1443 864cf6bf Agata Murawska
      output.append("[%s]" % section)
1444 864cf6bf Agata Murawska
      for name, value in options.iteritems():
1445 99381e3b Agata Murawska
        if value is None:
1446 99381e3b Agata Murawska
          value = ""
1447 864cf6bf Agata Murawska
        output.append("%s = %s" % (name, value))
1448 864cf6bf Agata Murawska
      output.append("")
1449 864cf6bf Agata Murawska
    output_contents = "\n".join(output)
1450 864cf6bf Agata Murawska
1451 864cf6bf Agata Murawska
    try:
1452 864cf6bf Agata Murawska
      utils.WriteFile(output_file_name, data=output_contents)
1453 864cf6bf Agata Murawska
    except errors.ProgrammerError, err:
1454 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Saving the config file failed: %s" % err)
1455 864cf6bf Agata Murawska
1456 864cf6bf Agata Murawska
    self.Cleanup()
1457 ced78a66 Agata Murawska
1458 ced78a66 Agata Murawska
1459 0963b26a Agata Murawska
class ConfigParserWithDefaults(ConfigParser.SafeConfigParser):
1460 0963b26a Agata Murawska
  """This is just a wrapper on SafeConfigParser, that uses default values
1461 0963b26a Agata Murawska

1462 0963b26a Agata Murawska
  """
1463 0963b26a Agata Murawska
  def get(self, section, options, raw=None, vars=None): # pylint: disable=W0622
1464 0963b26a Agata Murawska
    try:
1465 0963b26a Agata Murawska
      result = ConfigParser.SafeConfigParser.get(self, section, options, \
1466 0963b26a Agata Murawska
        raw=raw, vars=vars)
1467 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1468 0963b26a Agata Murawska
      result = None
1469 0963b26a Agata Murawska
    return result
1470 0963b26a Agata Murawska
1471 0963b26a Agata Murawska
  def getint(self, section, options):
1472 0963b26a Agata Murawska
    try:
1473 0963b26a Agata Murawska
      result = ConfigParser.SafeConfigParser.get(self, section, options)
1474 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1475 0963b26a Agata Murawska
      result = 0
1476 0963b26a Agata Murawska
    return int(result)
1477 0963b26a Agata Murawska
1478 0963b26a Agata Murawska
1479 ced78a66 Agata Murawska
class OVFExporter(Converter):
1480 0963b26a Agata Murawska
  """Converter from Ganeti config file to OVF
1481 0963b26a Agata Murawska

1482 0963b26a Agata Murawska
  @type input_dir: string
1483 0963b26a Agata Murawska
  @ivar input_dir: directory in which the config.ini file resides
1484 0963b26a Agata Murawska
  @type output_dir: string
1485 0963b26a Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
1486 0963b26a Agata Murawska
    written
1487 0963b26a Agata Murawska
  @type packed_dir: string
1488 0963b26a Agata Murawska
  @ivar packed_dir: if we want OVA package, this points to the real (i.e. not
1489 0963b26a Agata Murawska
    temp) output directory
1490 0963b26a Agata Murawska
  @type input_path: string
1491 0963b26a Agata Murawska
  @ivar input_path: complete path to the config.ini file
1492 0963b26a Agata Murawska
  @type output_path: string
1493 0963b26a Agata Murawska
  @ivar output_path: complete path to .ovf file
1494 0963b26a Agata Murawska
  @type config_parser: L{ConfigParserWithDefaults}
1495 0963b26a Agata Murawska
  @ivar config_parser: parser for the config.ini file
1496 7432d332 Agata Murawska
  @type reference_files: list
1497 7432d332 Agata Murawska
  @ivar reference_files: files referenced in the ovf file
1498 b179ce72 Agata Murawska
  @type results_disk: list
1499 b179ce72 Agata Murawska
  @ivar results_disk: list of dictionaries of disk options from config.ini
1500 b179ce72 Agata Murawska
  @type results_network: list
1501 b179ce72 Agata Murawska
  @ivar results_network: list of dictionaries of network options form config.ini
1502 0963b26a Agata Murawska
  @type results_name: string
1503 0963b26a Agata Murawska
  @ivar results_name: name of the instance
1504 b179ce72 Agata Murawska
  @type results_vcpus: string
1505 b179ce72 Agata Murawska
  @ivar results_vcpus: number of VCPUs
1506 b179ce72 Agata Murawska
  @type results_memory: string
1507 b179ce72 Agata Murawska
  @ivar results_memory: RAM memory in MB
1508 b179ce72 Agata Murawska
  @type results_ganeti: dict
1509 b179ce72 Agata Murawska
  @ivar results_ganeti: dictionary of Ganeti-specific options from config.ini
1510 0963b26a Agata Murawska

1511 0963b26a Agata Murawska
  """
1512 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
1513 0963b26a Agata Murawska
    """Reads the data on which the conversion will take place.
1514 0963b26a Agata Murawska

1515 0963b26a Agata Murawska
    @type input_path: string
1516 0963b26a Agata Murawska
    @param input_path: absolute path to the config.ini input file
1517 0963b26a Agata Murawska

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

1520 0963b26a Agata Murawska
    """
1521 0963b26a Agata Murawska
    input_dir = os.path.dirname(input_path)
1522 0963b26a Agata Murawska
    self.input_path = input_path
1523 0963b26a Agata Murawska
    self.input_dir = input_dir
1524 0963b26a Agata Murawska
    if self.options.output_dir:
1525 0963b26a Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1526 0963b26a Agata Murawska
    else:
1527 0963b26a Agata Murawska
      self.output_dir = input_dir
1528 0963b26a Agata Murawska
    self.config_parser = ConfigParserWithDefaults()
1529 0963b26a Agata Murawska
    logging.info("Reading configuration from %s file", input_path)
1530 0963b26a Agata Murawska
    try:
1531 0963b26a Agata Murawska
      self.config_parser.read(input_path)
1532 0963b26a Agata Murawska
    except ConfigParser.MissingSectionHeaderError, err:
1533 0963b26a Agata Murawska
      raise errors.OpPrereqError("Error when trying to read %s: %s" %
1534 0963b26a Agata Murawska
                                 (input_path, err))
1535 0963b26a Agata Murawska
    if self.options.ova_package:
1536 0963b26a Agata Murawska
      self.temp_dir = tempfile.mkdtemp()
1537 0963b26a Agata Murawska
      self.packed_dir = self.output_dir
1538 0963b26a Agata Murawska
      self.output_dir = self.temp_dir
1539 0963b26a Agata Murawska
1540 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1541 0963b26a Agata Murawska
1542 0963b26a Agata Murawska
  def _ParseName(self):
1543 0963b26a Agata Murawska
    """Parses name from command line options or config file.
1544 0963b26a Agata Murawska

1545 0963b26a Agata Murawska
    @rtype: string
1546 0963b26a Agata Murawska
    @return: name of Ganeti instance
1547 0963b26a Agata Murawska

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

1550 0963b26a Agata Murawska
    """
1551 0963b26a Agata Murawska
    if self.options.name:
1552 0963b26a Agata Murawska
      name = self.options.name
1553 0963b26a Agata Murawska
    else:
1554 0963b26a Agata Murawska
      name = self.config_parser.get(constants.INISECT_INS, NAME)
1555 0963b26a Agata Murawska
    if name is None:
1556 0963b26a Agata Murawska
      raise errors.OpPrereqError("No instance name found")
1557 0963b26a Agata Murawska
    return name
1558 ced78a66 Agata Murawska
1559 b179ce72 Agata Murawska
  def _ParseVCPUs(self):
1560 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1561 b179ce72 Agata Murawska

1562 b179ce72 Agata Murawska
    @rtype: int
1563 b179ce72 Agata Murawska
    @return: number of virtual CPUs
1564 b179ce72 Agata Murawska

1565 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if number of VCPUs equals 0
1566 b179ce72 Agata Murawska

1567 b179ce72 Agata Murawska
    """
1568 b179ce72 Agata Murawska
    vcpus = self.config_parser.getint(constants.INISECT_BEP, VCPUS)
1569 b179ce72 Agata Murawska
    if vcpus == 0:
1570 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No CPU information found")
1571 b179ce72 Agata Murawska
    return vcpus
1572 b179ce72 Agata Murawska
1573 b179ce72 Agata Murawska
  def _ParseMemory(self):
1574 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1575 b179ce72 Agata Murawska

1576 b179ce72 Agata Murawska
    @rtype: int
1577 b179ce72 Agata Murawska
    @return: amount of memory in MB
1578 b179ce72 Agata Murawska

1579 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if amount of memory equals 0
1580 b179ce72 Agata Murawska

1581 b179ce72 Agata Murawska
    """
1582 b179ce72 Agata Murawska
    memory = self.config_parser.getint(constants.INISECT_BEP, MEMORY)
1583 b179ce72 Agata Murawska
    if memory == 0:
1584 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No memory information found")
1585 b179ce72 Agata Murawska
    return memory
1586 b179ce72 Agata Murawska
1587 b179ce72 Agata Murawska
  def _ParseGaneti(self):
1588 b179ce72 Agata Murawska
    """Parses Ganeti data from config file.
1589 b179ce72 Agata Murawska

1590 b179ce72 Agata Murawska
    @rtype: dictionary
1591 b179ce72 Agata Murawska
    @return: dictionary of Ganeti-specific options
1592 b179ce72 Agata Murawska

1593 b179ce72 Agata Murawska
    """
1594 b179ce72 Agata Murawska
    results = {}
1595 b179ce72 Agata Murawska
    # hypervisor
1596 b179ce72 Agata Murawska
    results["hypervisor"] = {}
1597 b179ce72 Agata Murawska
    hyp_name = self.config_parser.get(constants.INISECT_INS, HYPERV)
1598 b179ce72 Agata Murawska
    if hyp_name is None:
1599 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No hypervisor information found")
1600 b179ce72 Agata Murawska
    results["hypervisor"]["name"] = hyp_name
1601 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_HYP)
1602 b179ce72 Agata Murawska
    for (name, value) in pairs:
1603 b179ce72 Agata Murawska
      results["hypervisor"][name] = value
1604 b179ce72 Agata Murawska
    # os
1605 b179ce72 Agata Murawska
    results["os"] = {}
1606 b179ce72 Agata Murawska
    os_name = self.config_parser.get(constants.INISECT_EXP, OS)
1607 b179ce72 Agata Murawska
    if os_name is None:
1608 b179ce72 Agata Murawska
      raise errors.OpPrereqError("No operating system information found")
1609 b179ce72 Agata Murawska
    results["os"]["name"] = os_name
1610 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_OSP)
1611 b179ce72 Agata Murawska
    for (name, value) in pairs:
1612 b179ce72 Agata Murawska
      results["os"][name] = value
1613 b179ce72 Agata Murawska
    # other
1614 b179ce72 Agata Murawska
    others = [
1615 b179ce72 Agata Murawska
      (constants.INISECT_INS, DISK_TEMPLATE, "disk_template"),
1616 b179ce72 Agata Murawska
      (constants.INISECT_BEP, AUTO_BALANCE, "auto_balance"),
1617 b179ce72 Agata Murawska
      (constants.INISECT_INS, TAGS, "tags"),
1618 b179ce72 Agata Murawska
      (constants.INISECT_EXP, VERSION, "version"),
1619 b179ce72 Agata Murawska
    ]
1620 b179ce72 Agata Murawska
    for (section, element, name) in others:
1621 b179ce72 Agata Murawska
      results[name] = self.config_parser.get(section, element)
1622 b179ce72 Agata Murawska
    return results
1623 b179ce72 Agata Murawska
1624 b179ce72 Agata Murawska
  def _ParseNetworks(self):
1625 b179ce72 Agata Murawska
    """Parses network data from config file.
1626 b179ce72 Agata Murawska

1627 b179ce72 Agata Murawska
    @rtype: list
1628 b179ce72 Agata Murawska
    @return: list of dictionaries of network options
1629 b179ce72 Agata Murawska

1630 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: then network mode is not recognized
1631 b179ce72 Agata Murawska

1632 b179ce72 Agata Murawska
    """
1633 b179ce72 Agata Murawska
    results = []
1634 b179ce72 Agata Murawska
    counter = 0
1635 b179ce72 Agata Murawska
    while True:
1636 b179ce72 Agata Murawska
      data_link = \
1637 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "nic%s_link" % counter)
1638 b179ce72 Agata Murawska
      if data_link is None:
1639 b179ce72 Agata Murawska
        break
1640 b179ce72 Agata Murawska
      results.append({
1641 b179ce72 Agata Murawska
        "mode": self.config_parser.get(constants.INISECT_INS,
1642 b179ce72 Agata Murawska
           "nic%s_mode" % counter),
1643 b179ce72 Agata Murawska
        "mac": self.config_parser.get(constants.INISECT_INS,
1644 b179ce72 Agata Murawska
           "nic%s_mac" % counter),
1645 b179ce72 Agata Murawska
        "ip": self.config_parser.get(constants.INISECT_INS,
1646 b179ce72 Agata Murawska
           "nic%s_ip" % counter),
1647 b179ce72 Agata Murawska
        "link": data_link,
1648 b179ce72 Agata Murawska
      })
1649 b179ce72 Agata Murawska
      if results[counter]["mode"] not in constants.NIC_VALID_MODES:
1650 b179ce72 Agata Murawska
        raise errors.OpPrereqError("Network mode %s not recognized"
1651 b179ce72 Agata Murawska
                                   % results[counter]["mode"])
1652 b179ce72 Agata Murawska
      counter += 1
1653 b179ce72 Agata Murawska
    return results
1654 b179ce72 Agata Murawska
1655 b179ce72 Agata Murawska
  def _GetDiskOptions(self, disk_file, compression):
1656 b179ce72 Agata Murawska
    """Convert the disk and gather disk info for .ovf file.
1657 b179ce72 Agata Murawska

1658 b179ce72 Agata Murawska
    @type disk_file: string
1659 b179ce72 Agata Murawska
    @param disk_file: name of the disk (without the full path)
1660 b179ce72 Agata Murawska
    @type compression: bool
1661 b179ce72 Agata Murawska
    @param compression: whether the disk should be compressed or not
1662 b179ce72 Agata Murawska

1663 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: when disk image does not exist
1664 b179ce72 Agata Murawska

1665 b179ce72 Agata Murawska
    """
1666 b179ce72 Agata Murawska
    disk_path = utils.PathJoin(self.input_dir, disk_file)
1667 b179ce72 Agata Murawska
    results = {}
1668 b179ce72 Agata Murawska
    if not os.path.isfile(disk_path):
1669 b179ce72 Agata Murawska
      raise errors.OpPrereqError("Disk image does not exist: %s" % disk_path)
1670 b179ce72 Agata Murawska
    if os.path.dirname(disk_file):
1671 b179ce72 Agata Murawska
      raise errors.OpPrereqError("Path for the disk: %s contains a directory"
1672 b179ce72 Agata Murawska
                                 " name" % disk_path)
1673 b179ce72 Agata Murawska
    disk_name, _ = os.path.splitext(disk_file)
1674 b179ce72 Agata Murawska
    ext, new_disk_path = self._ConvertDisk(self.options.disk_format, disk_path)
1675 b179ce72 Agata Murawska
    results["format"] = self.options.disk_format
1676 b179ce72 Agata Murawska
    results["virt-size"] = self._GetDiskQemuInfo(new_disk_path,
1677 b179ce72 Agata Murawska
      "virtual size: \S+ \((\d+) bytes\)")
1678 b179ce72 Agata Murawska
    if compression:
1679 b179ce72 Agata Murawska
      ext2, new_disk_path = self._CompressDisk(new_disk_path, "gzip",
1680 b179ce72 Agata Murawska
        COMPRESS)
1681 b179ce72 Agata Murawska
      disk_name, _ = os.path.splitext(disk_name)
1682 b179ce72 Agata Murawska
      results["compression"] = "gzip"
1683 b179ce72 Agata Murawska
      ext += ext2
1684 b179ce72 Agata Murawska
    final_disk_path = LinkFile(new_disk_path, prefix=disk_name, suffix=ext,
1685 b179ce72 Agata Murawska
      directory=self.output_dir)
1686 b179ce72 Agata Murawska
    final_disk_name = os.path.basename(final_disk_path)
1687 b179ce72 Agata Murawska
    results["real-size"] = os.path.getsize(final_disk_path)
1688 b179ce72 Agata Murawska
    results["path"] = final_disk_name
1689 b179ce72 Agata Murawska
    self.references_files.append(final_disk_path)
1690 b179ce72 Agata Murawska
    return results
1691 b179ce72 Agata Murawska
1692 b179ce72 Agata Murawska
  def _ParseDisks(self):
1693 b179ce72 Agata Murawska
    """Parses disk data from config file.
1694 b179ce72 Agata Murawska

1695 b179ce72 Agata Murawska
    @rtype: list
1696 b179ce72 Agata Murawska
    @return: list of dictionaries of disk options
1697 b179ce72 Agata Murawska

1698 b179ce72 Agata Murawska
    """
1699 b179ce72 Agata Murawska
    results = []
1700 b179ce72 Agata Murawska
    counter = 0
1701 b179ce72 Agata Murawska
    while True:
1702 b179ce72 Agata Murawska
      disk_file = \
1703 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "disk%s_dump" % counter)
1704 b179ce72 Agata Murawska
      if disk_file is None:
1705 b179ce72 Agata Murawska
        break
1706 b179ce72 Agata Murawska
      results.append(self._GetDiskOptions(disk_file, self.options.compression))
1707 b179ce72 Agata Murawska
      counter += 1
1708 b179ce72 Agata Murawska
    return results
1709 b179ce72 Agata Murawska
1710 ced78a66 Agata Murawska
  def Parse(self):
1711 0963b26a Agata Murawska
    """Parses the data and creates a structure containing all required info.
1712 0963b26a Agata Murawska

1713 0963b26a Agata Murawska
    """
1714 0963b26a Agata Murawska
    try:
1715 0963b26a Agata Murawska
      utils.Makedirs(self.output_dir)
1716 0963b26a Agata Murawska
    except OSError, err:
1717 0963b26a Agata Murawska
      raise errors.OpPrereqError("Failed to create directory %s: %s" %
1718 0963b26a Agata Murawska
                                 (self.output_dir, err))
1719 0963b26a Agata Murawska
1720 b179ce72 Agata Murawska
    self.references_files = []
1721 0963b26a Agata Murawska
    self.results_name = self._ParseName()
1722 b179ce72 Agata Murawska
    self.results_vcpus = self._ParseVCPUs()
1723 b179ce72 Agata Murawska
    self.results_memory = self._ParseMemory()
1724 b179ce72 Agata Murawska
    if not self.options.ext_usage:
1725 b179ce72 Agata Murawska
      self.results_ganeti = self._ParseGaneti()
1726 b179ce72 Agata Murawska
    self.results_network = self._ParseNetworks()
1727 b179ce72 Agata Murawska
    self.results_disk = self._ParseDisks()
1728 0963b26a Agata Murawska
1729 0963b26a Agata Murawska
  def _PrepareManifest(self, path):
1730 0963b26a Agata Murawska
    """Creates manifest for all the files in OVF package.
1731 0963b26a Agata Murawska

1732 0963b26a Agata Murawska
    @type path: string
1733 0963b26a Agata Murawska
    @param path: path to manifesto file
1734 0963b26a Agata Murawska

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

1737 0963b26a Agata Murawska
    """
1738 0963b26a Agata Murawska
    logging.info("Preparing manifest for the OVF package")
1739 0963b26a Agata Murawska
    lines = []
1740 0963b26a Agata Murawska
    files_list = [self.output_path]
1741 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1742 0963b26a Agata Murawska
    logging.warning("Calculating SHA1 checksums, this may take a while")
1743 0963b26a Agata Murawska
    sha1_sums = utils.FingerprintFiles(files_list)
1744 0963b26a Agata Murawska
    for file_path, value in sha1_sums.iteritems():
1745 0963b26a Agata Murawska
      file_name = os.path.basename(file_path)
1746 0963b26a Agata Murawska
      lines.append("SHA1(%s)= %s" % (file_name, value))
1747 0963b26a Agata Murawska
    lines.append("")
1748 0963b26a Agata Murawska
    data = "\n".join(lines)
1749 0963b26a Agata Murawska
    try:
1750 0963b26a Agata Murawska
      utils.WriteFile(path, data=data)
1751 0963b26a Agata Murawska
    except errors.ProgrammerError, err:
1752 0963b26a Agata Murawska
      raise errors.OpPrereqError("Saving the manifest file failed: %s" % err)
1753 0963b26a Agata Murawska
1754 0963b26a Agata Murawska
  @staticmethod
1755 0963b26a Agata Murawska
  def _PrepareTarFile(tar_path, files_list):
1756 0963b26a Agata Murawska
    """Creates tarfile from the files in OVF package.
1757 0963b26a Agata Murawska

1758 0963b26a Agata Murawska
    @type tar_path: string
1759 0963b26a Agata Murawska
    @param tar_path: path to the resulting file
1760 0963b26a Agata Murawska
    @type files_list: list
1761 0963b26a Agata Murawska
    @param files_list: list of files in the OVF package
1762 0963b26a Agata Murawska

1763 0963b26a Agata Murawska
    """
1764 0963b26a Agata Murawska
    logging.info("Preparing tarball for the OVF package")
1765 0963b26a Agata Murawska
    open(tar_path, mode="w").close()
1766 0963b26a Agata Murawska
    ova_package = tarfile.open(name=tar_path, mode="w")
1767 fa337742 Agata Murawska
    for file_path in files_list:
1768 fa337742 Agata Murawska
      file_name = os.path.basename(file_path)
1769 fa337742 Agata Murawska
      ova_package.add(file_path, arcname=file_name)
1770 0963b26a Agata Murawska
    ova_package.close()
1771 ced78a66 Agata Murawska
1772 ced78a66 Agata Murawska
  def Save(self):
1773 0963b26a Agata Murawska
    """Saves the gathered configuration in an apropriate format.
1774 0963b26a Agata Murawska

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

1777 0963b26a Agata Murawska
    """
1778 0963b26a Agata Murawska
    output_file = "%s%s" % (self.results_name, OVF_EXT)
1779 0963b26a Agata Murawska
    output_path = utils.PathJoin(self.output_dir, output_file)
1780 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1781 0963b26a Agata Murawska
    logging.info("Saving read data to %s", output_path)
1782 0963b26a Agata Murawska
1783 0963b26a Agata Murawska
    self.output_path = utils.PathJoin(self.output_dir, output_file)
1784 0963b26a Agata Murawska
    files_list = [self.output_path]
1785 0963b26a Agata Murawska
1786 7432d332 Agata Murawska
    self.ovf_writer.SaveDisksData(self.results_disk)
1787 7432d332 Agata Murawska
    self.ovf_writer.SaveNetworksData(self.results_network)
1788 7432d332 Agata Murawska
    if not self.options.ext_usage:
1789 7432d332 Agata Murawska
      self.ovf_writer.SaveGanetiData(self.results_ganeti, self.results_network)
1790 7432d332 Agata Murawska
1791 7432d332 Agata Murawska
    self.ovf_writer.SaveVirtualSystemData(self.results_name, self.results_vcpus,
1792 7432d332 Agata Murawska
      self.results_memory)
1793 7432d332 Agata Murawska
1794 0963b26a Agata Murawska
    data = self.ovf_writer.PrettyXmlDump()
1795 0963b26a Agata Murawska
    utils.WriteFile(self.output_path, data=data)
1796 0963b26a Agata Murawska
1797 0963b26a Agata Murawska
    manifest_file = "%s%s" % (self.results_name, MF_EXT)
1798 0963b26a Agata Murawska
    manifest_path = utils.PathJoin(self.output_dir, manifest_file)
1799 0963b26a Agata Murawska
    self._PrepareManifest(manifest_path)
1800 0963b26a Agata Murawska
    files_list.append(manifest_path)
1801 0963b26a Agata Murawska
1802 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1803 0963b26a Agata Murawska
1804 0963b26a Agata Murawska
    if self.options.ova_package:
1805 0963b26a Agata Murawska
      ova_file = "%s%s" % (self.results_name, OVA_EXT)
1806 0963b26a Agata Murawska
      packed_path = utils.PathJoin(self.packed_dir, ova_file)
1807 0963b26a Agata Murawska
      try:
1808 0963b26a Agata Murawska
        utils.Makedirs(self.packed_dir)
1809 0963b26a Agata Murawska
      except OSError, err:
1810 0963b26a Agata Murawska
        raise errors.OpPrereqError("Failed to create directory %s: %s" %
1811 0963b26a Agata Murawska
                                   (self.packed_dir, err))
1812 0963b26a Agata Murawska
      self._PrepareTarFile(packed_path, files_list)
1813 0963b26a Agata Murawska
    logging.info("Creation of the OVF package was successfull")
1814 0963b26a Agata Murawska
    self.Cleanup()