Statistics
| Branch: | Tag: | Revision:

root / lib / ovf.py @ 5e12acfe

History | View | Annotate | Download (65.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

260 864cf6bf Agata Murawska
    Function follows the path from root node to the desired tags using path,
261 864cf6bf Agata Murawska
    then reads the apropriate attribute values.
262 864cf6bf Agata Murawska

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

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

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

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

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

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

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

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

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

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

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

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

373 99381e3b Agata Murawska
    @rtype: string
374 99381e3b Agata Murawska
    @return: instance name string
375 99381e3b Agata Murawska

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

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

393 7bde29b5 Agata Murawska
    @rtype: dict
394 7bde29b5 Agata Murawska
    @return: dictionary containing name of the used hypervisor and all the
395 7bde29b5 Agata Murawska
      specified options
396 7bde29b5 Agata Murawska

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

414 7bde29b5 Agata Murawska
    @rtype: dict
415 7bde29b5 Agata Murawska
    @return: dictionary containing name and options for the chosen OS
416 7bde29b5 Agata Murawska

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

431 7bde29b5 Agata Murawska
    @rtype: dict
432 7bde29b5 Agata Murawska
    @return: dictionary containing options for vcpus, memory and auto balance
433 7bde29b5 Agata Murawska
      settings
434 7bde29b5 Agata Murawska

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

478 7bde29b5 Agata Murawska
    @rtype: string or None
479 7bde29b5 Agata Murawska
    @return: string of comma-separated tags for the instance
480 7bde29b5 Agata Murawska

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

492 7bde29b5 Agata Murawska
    @rtype: string
493 7bde29b5 Agata Murawska
    @return: string containing the version number
494 7bde29b5 Agata Murawska

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

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

509 24b9469d Agata Murawska
    @rtype: dict
510 24b9469d Agata Murawska
    @return: dictionary containing all the network information
511 24b9469d Agata Murawska

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

573 99381e3b Agata Murawska
    @rtype: list
574 99381e3b Agata Murawska
    @return: list of file names, as referenced in .ovf file
575 99381e3b Agata Murawska

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

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

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

620 0963b26a Agata Murawska
  """
621 0963b26a Agata Murawska
  def __init__(self, has_gnt_section):
622 0963b26a Agata Murawska
    """Initialize the writer - set the top element.
623 0963b26a Agata Murawska

624 0963b26a Agata Murawska
    @type has_gnt_section: bool
625 0963b26a Agata Murawska
    @param has_gnt_section: if the Ganeti schema should be added - i.e. this
626 0963b26a Agata Murawska
      means that Ganeti section will be present
627 0963b26a Agata Murawska

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

650 7432d332 Agata Murawska
    @type disks: list
651 7432d332 Agata Murawska
    @param disks: list of dictionaries of disk options from config.ini
652 7432d332 Agata Murawska

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

689 7432d332 Agata Murawska
    @type networks: list
690 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
691 7432d332 Agata Murawska

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

715 7432d332 Agata Murawska
    @type root: ET.Element
716 7432d332 Agata Murawska
    @param root: root element for the Name and Parameters
717 7432d332 Agata Murawska
    @type data: dict
718 7432d332 Agata Murawska
    @param data: data from which we gather the values
719 7432d332 Agata Murawska

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

731 7432d332 Agata Murawska
    @type ganeti: dict
732 7432d332 Agata Murawska
    @param ganeti: dictionary of Ganeti-specific options from config.ini
733 7432d332 Agata Murawska
    @type networks: list
734 7432d332 Agata Murawska
    @param networks: list of dictionaries of network options form config.ini
735 7432d332 Agata Murawska

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

765 7432d332 Agata Murawska
    @type name: string
766 7432d332 Agata Murawska
    @param name: name of the instance
767 7432d332 Agata Murawska
    @type vcpus: int
768 7432d332 Agata Murawska
    @param vcpus: number of VCPUs
769 7432d332 Agata Murawska
    @type memory: int
770 7432d332 Agata Murawska
    @param memory: RAM memory in MB
771 7432d332 Agata Murawska

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

826 0963b26a Agata Murawska
    @rtype: string
827 0963b26a Agata Murawska
    @return: XML tree in the form of nicely-formatted string
828 0963b26a Agata Murawska

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

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

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

854 ced78a66 Agata Murawska
  """
855 ced78a66 Agata Murawska
  def __init__(self, input_path, options):
856 ced78a66 Agata Murawska
    """Initialize the converter.
857 ced78a66 Agata Murawska

858 ced78a66 Agata Murawska
    @type input_path: string
859 ced78a66 Agata Murawska
    @param input_path: path to the Converter input file
860 ced78a66 Agata Murawska
    @type options: optparse.Values
861 ced78a66 Agata Murawska
    @param options: command line options
862 ced78a66 Agata Murawska

863 ced78a66 Agata Murawska
    @raise errors.OpPrereqError: if file does not exist
864 ced78a66 Agata Murawska

865 ced78a66 Agata Murawska
    """
866 ced78a66 Agata Murawska
    input_path = os.path.abspath(input_path)
867 ced78a66 Agata Murawska
    if not os.path.isfile(input_path):
868 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("File does not exist: %s" % input_path,
869 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
870 ced78a66 Agata Murawska
    self.options = options
871 ced78a66 Agata Murawska
    self.temp_file_manager = utils.TemporaryFileManager()
872 ced78a66 Agata Murawska
    self.temp_dir = None
873 ced78a66 Agata Murawska
    self.output_dir = None
874 ced78a66 Agata Murawska
    self._ReadInputData(input_path)
875 ced78a66 Agata Murawska
876 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
877 ced78a66 Agata Murawska
    """Reads the data on which the conversion will take place.
878 ced78a66 Agata Murawska

879 ced78a66 Agata Murawska
    @type input_path: string
880 ced78a66 Agata Murawska
    @param input_path: absolute path to the Converter input file
881 ced78a66 Agata Murawska

882 ced78a66 Agata Murawska
    """
883 ced78a66 Agata Murawska
    raise NotImplementedError()
884 ced78a66 Agata Murawska
885 99381e3b Agata Murawska
  def _CompressDisk(self, disk_path, compression, action):
886 99381e3b Agata Murawska
    """Performs (de)compression on the disk and returns the new path
887 99381e3b Agata Murawska

888 99381e3b Agata Murawska
    @type disk_path: string
889 99381e3b Agata Murawska
    @param disk_path: path to the disk
890 99381e3b Agata Murawska
    @type compression: string
891 99381e3b Agata Murawska
    @param compression: compression type
892 99381e3b Agata Murawska
    @type action: string
893 99381e3b Agata Murawska
    @param action: whether the action is compression or decompression
894 99381e3b Agata Murawska
    @rtype: string
895 99381e3b Agata Murawska
    @return: new disk path after (de)compression
896 99381e3b Agata Murawska

897 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk (de)compression failed or "compression"
898 99381e3b Agata Murawska
      is not supported
899 99381e3b Agata Murawska

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

927 99381e3b Agata Murawska
    @type disk_format: string
928 99381e3b Agata Murawska
    @param disk_format: format to which the disk should be converted
929 99381e3b Agata Murawska
    @type disk_path: string
930 99381e3b Agata Murawska
    @param disk_path: path to the disk that should be converted
931 99381e3b Agata Murawska
    @rtype: string
932 99381e3b Agata Murawska
    @return path to the output disk
933 99381e3b Agata Murawska

934 99381e3b Agata Murawska
    @raise errors.OpPrereqError: convertion of the disk failed
935 99381e3b Agata Murawska

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

966 99381e3b Agata Murawska
    @type disk_path: string
967 99381e3b Agata Murawska
    @param disk_path: path to the disk we want to know the format of
968 99381e3b Agata Murawska
    @type regexp: string
969 99381e3b Agata Murawska
    @param regexp: string that has to be matched, it has to contain one group
970 99381e3b Agata Murawska
    @rtype: string
971 99381e3b Agata Murawska
    @return: disk format
972 99381e3b Agata Murawska

973 99381e3b Agata Murawska
    @raise errors.OpPrereqError: format information cannot be retrieved
974 99381e3b Agata Murawska

975 99381e3b Agata Murawska
    """
976 a002ed79 Agata Murawska
    CheckQemuImg()
977 e68949b0 Agata Murawska
    args = [constants.QEMUIMG_PATH, "info", disk_path]
978 99381e3b Agata Murawska
    run_result = utils.RunCmd(args, cwd=os.getcwd())
979 99381e3b Agata Murawska
    if run_result.failed:
980 99381e3b Agata Murawska
      raise errors.OpPrereqError("Gathering info about the disk using qemu-img"
981 2cfbc784 Iustin Pop
                                 " failed, output was: %s" % run_result.stderr,
982 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
983 99381e3b Agata Murawska
    result = run_result.output
984 99381e3b Agata Murawska
    regexp = r"%s" % regexp
985 99381e3b Agata Murawska
    match = re.search(regexp, result)
986 99381e3b Agata Murawska
    if match:
987 99381e3b Agata Murawska
      disk_format = match.group(1)
988 99381e3b Agata Murawska
    else:
989 99381e3b Agata Murawska
      raise errors.OpPrereqError("No file information matching %s found in:"
990 2cfbc784 Iustin Pop
                                 " %s" % (regexp, result),
991 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
992 99381e3b Agata Murawska
    return disk_format
993 99381e3b Agata Murawska
994 ced78a66 Agata Murawska
  def Parse(self):
995 ced78a66 Agata Murawska
    """Parses the data and creates a structure containing all required info.
996 ced78a66 Agata Murawska

997 ced78a66 Agata Murawska
    """
998 ced78a66 Agata Murawska
    raise NotImplementedError()
999 ced78a66 Agata Murawska
1000 ced78a66 Agata Murawska
  def Save(self):
1001 ced78a66 Agata Murawska
    """Saves the gathered configuration in an apropriate format.
1002 ced78a66 Agata Murawska

1003 ced78a66 Agata Murawska
    """
1004 ced78a66 Agata Murawska
    raise NotImplementedError()
1005 ced78a66 Agata Murawska
1006 ced78a66 Agata Murawska
  def Cleanup(self):
1007 ced78a66 Agata Murawska
    """Cleans the temporary directory, if one was created.
1008 ced78a66 Agata Murawska

1009 ced78a66 Agata Murawska
    """
1010 ced78a66 Agata Murawska
    self.temp_file_manager.Cleanup()
1011 ced78a66 Agata Murawska
    if self.temp_dir:
1012 ced78a66 Agata Murawska
      shutil.rmtree(self.temp_dir)
1013 ced78a66 Agata Murawska
      self.temp_dir = None
1014 ced78a66 Agata Murawska
1015 ced78a66 Agata Murawska
1016 ced78a66 Agata Murawska
class OVFImporter(Converter):
1017 864cf6bf Agata Murawska
  """Converter from OVF to Ganeti config file.
1018 864cf6bf Agata Murawska

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

1053 864cf6bf Agata Murawska
  """
1054 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
1055 864cf6bf Agata Murawska
    """Reads the data on which the conversion will take place.
1056 864cf6bf Agata Murawska

1057 864cf6bf Agata Murawska
    @type input_path: string
1058 864cf6bf Agata Murawska
    @param input_path: absolute path to the .ovf or .ova input file
1059 864cf6bf Agata Murawska

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

1062 864cf6bf Agata Murawska
    """
1063 864cf6bf Agata Murawska
    (input_dir, input_file) = os.path.split(input_path)
1064 864cf6bf Agata Murawska
    (_, input_extension) = os.path.splitext(input_file)
1065 864cf6bf Agata Murawska
1066 864cf6bf Agata Murawska
    if input_extension == OVF_EXT:
1067 864cf6bf Agata Murawska
      logging.info("%s file extension found, no unpacking necessary", OVF_EXT)
1068 864cf6bf Agata Murawska
      self.input_dir = input_dir
1069 864cf6bf Agata Murawska
      self.input_path = input_path
1070 864cf6bf Agata Murawska
      self.temp_dir = None
1071 864cf6bf Agata Murawska
    elif input_extension == OVA_EXT:
1072 864cf6bf Agata Murawska
      logging.info("%s file extension found, proceeding to unpacking", OVA_EXT)
1073 864cf6bf Agata Murawska
      self._UnpackOVA(input_path)
1074 864cf6bf Agata Murawska
    else:
1075 864cf6bf Agata Murawska
      raise errors.OpPrereqError("Unknown file extension; expected %s or %s"
1076 2cfbc784 Iustin Pop
                                 " file" % (OVA_EXT, OVF_EXT),
1077 2cfbc784 Iustin Pop
                                 errors.ECODE_INVAL)
1078 864cf6bf Agata Murawska
    assert ((input_extension == OVA_EXT and self.temp_dir) or
1079 864cf6bf Agata Murawska
            (input_extension == OVF_EXT and not self.temp_dir))
1080 864cf6bf Agata Murawska
    assert self.input_dir in self.input_path
1081 864cf6bf Agata Murawska
1082 864cf6bf Agata Murawska
    if self.options.output_dir:
1083 864cf6bf Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1084 fe496b25 Michael Hanselmann
      if (os.path.commonprefix([pathutils.EXPORT_DIR, self.output_dir]) !=
1085 fe496b25 Michael Hanselmann
          pathutils.EXPORT_DIR):
1086 864cf6bf Agata Murawska
        logging.warning("Export path is not under %s directory, import to"
1087 864cf6bf Agata Murawska
                        " Ganeti using gnt-backup may fail",
1088 fe496b25 Michael Hanselmann
                        pathutils.EXPORT_DIR)
1089 864cf6bf Agata Murawska
    else:
1090 fe496b25 Michael Hanselmann
      self.output_dir = pathutils.EXPORT_DIR
1091 864cf6bf Agata Murawska
1092 864cf6bf Agata Murawska
    self.ovf_reader = OVFReader(self.input_path)
1093 864cf6bf Agata Murawska
    self.ovf_reader.VerifyManifest()
1094 864cf6bf Agata Murawska
1095 864cf6bf Agata Murawska
  def _UnpackOVA(self, input_path):
1096 864cf6bf Agata Murawska
    """Unpacks the .ova package into temporary directory.
1097 864cf6bf Agata Murawska

1098 864cf6bf Agata Murawska
    @type input_path: string
1099 864cf6bf Agata Murawska
    @param input_path: path to the .ova package file
1100 864cf6bf Agata Murawska

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

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

1146 99381e3b Agata Murawska
    The method reads the information given either as a command line option or as
1147 99381e3b Agata Murawska
    a part of the OVF description.
1148 99381e3b Agata Murawska

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

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

1220 99381e3b Agata Murawska
    @type name: string
1221 99381e3b Agata Murawska
    @param name: name of the section
1222 99381e3b Agata Murawska
    @type cmd_arg: dict
1223 99381e3b Agata Murawska
    @param cmd_arg: command line argument specific for section 'name'
1224 99381e3b Agata Murawska
    @type cmd_function: callable
1225 99381e3b Agata Murawska
    @param cmd_function: function to call if 'cmd_args' exists
1226 99381e3b Agata Murawska
    @type nocmd_function: callable
1227 99381e3b Agata Murawska
    @param nocmd_function: function to call if 'cmd_args' is not there
1228 99381e3b Agata Murawska

1229 99381e3b Agata Murawska
    """
1230 99381e3b Agata Murawska
    if ignore_test:
1231 99381e3b Agata Murawska
      logging.info("Information for %s will be ignored", name)
1232 99381e3b Agata Murawska
      return {}
1233 99381e3b Agata Murawska
    if cmd_arg:
1234 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from command line", name)
1235 99381e3b Agata Murawska
      results = cmd_function()
1236 99381e3b Agata Murawska
    else:
1237 99381e3b Agata Murawska
      logging.info("Information for %s will be parsed from %s file",
1238 5ae4945a Iustin Pop
                   name, OVF_EXT)
1239 99381e3b Agata Murawska
      results = nocmd_function()
1240 99381e3b Agata Murawska
    logging.info("Options for %s were succesfully read", name)
1241 99381e3b Agata Murawska
    return results
1242 99381e3b Agata Murawska
1243 99381e3b Agata Murawska
  def _ParseNameOptions(self):
1244 99381e3b Agata Murawska
    """Returns name if one was given in command line.
1245 99381e3b Agata Murawska

1246 99381e3b Agata Murawska
    @rtype: string
1247 99381e3b Agata Murawska
    @return: name of an instance
1248 99381e3b Agata Murawska

1249 99381e3b Agata Murawska
    """
1250 99381e3b Agata Murawska
    return self.options.name
1251 99381e3b Agata Murawska
1252 99381e3b Agata Murawska
  def _ParseTemplateOptions(self):
1253 99381e3b Agata Murawska
    """Returns disk template if one was given in command line.
1254 99381e3b Agata Murawska

1255 99381e3b Agata Murawska
    @rtype: string
1256 99381e3b Agata Murawska
    @return: disk template name
1257 99381e3b Agata Murawska

1258 99381e3b Agata Murawska
    """
1259 99381e3b Agata Murawska
    return self.options.disk_template
1260 99381e3b Agata Murawska
1261 7bde29b5 Agata Murawska
  def _ParseHypervisorOptions(self):
1262 7bde29b5 Agata Murawska
    """Parses hypervisor options given in a command line.
1263 7bde29b5 Agata Murawska

1264 7bde29b5 Agata Murawska
    @rtype: dict
1265 7bde29b5 Agata Murawska
    @return: dictionary containing name of the chosen hypervisor and all the
1266 7bde29b5 Agata Murawska
      options
1267 7bde29b5 Agata Murawska

1268 7bde29b5 Agata Murawska
    """
1269 7bde29b5 Agata Murawska
    assert type(self.options.hypervisor) is tuple
1270 7bde29b5 Agata Murawska
    assert len(self.options.hypervisor) == 2
1271 7bde29b5 Agata Murawska
    results = {}
1272 7bde29b5 Agata Murawska
    if self.options.hypervisor[0]:
1273 7bde29b5 Agata Murawska
      results["hypervisor_name"] = self.options.hypervisor[0]
1274 7bde29b5 Agata Murawska
    else:
1275 7bde29b5 Agata Murawska
      results["hypervisor_name"] = constants.VALUE_AUTO
1276 7bde29b5 Agata Murawska
    results.update(self.options.hypervisor[1])
1277 7bde29b5 Agata Murawska
    return results
1278 7bde29b5 Agata Murawska
1279 7bde29b5 Agata Murawska
  def _ParseOSOptions(self):
1280 7bde29b5 Agata Murawska
    """Parses OS options given in command line.
1281 7bde29b5 Agata Murawska

1282 7bde29b5 Agata Murawska
    @rtype: dict
1283 7bde29b5 Agata Murawska
    @return: dictionary containing name of chosen OS and all its options
1284 7bde29b5 Agata Murawska

1285 7bde29b5 Agata Murawska
    """
1286 7bde29b5 Agata Murawska
    assert self.options.os
1287 7bde29b5 Agata Murawska
    results = {}
1288 7bde29b5 Agata Murawska
    results["os_name"] = self.options.os
1289 7bde29b5 Agata Murawska
    results.update(self.options.osparams)
1290 7bde29b5 Agata Murawska
    return results
1291 7bde29b5 Agata Murawska
1292 7bde29b5 Agata Murawska
  def _ParseBackendOptions(self):
1293 7bde29b5 Agata Murawska
    """Parses backend options given in command line.
1294 7bde29b5 Agata Murawska

1295 7bde29b5 Agata Murawska
    @rtype: dict
1296 7bde29b5 Agata Murawska
    @return: dictionary containing vcpus, memory and auto-balance options
1297 7bde29b5 Agata Murawska

1298 7bde29b5 Agata Murawska
    """
1299 7bde29b5 Agata Murawska
    assert self.options.beparams
1300 7bde29b5 Agata Murawska
    backend = {}
1301 7bde29b5 Agata Murawska
    backend.update(self.options.beparams)
1302 7bde29b5 Agata Murawska
    must_contain = ["vcpus", "memory", "auto_balance"]
1303 7bde29b5 Agata Murawska
    for element in must_contain:
1304 7bde29b5 Agata Murawska
      if backend.get(element) is None:
1305 7bde29b5 Agata Murawska
        backend[element] = constants.VALUE_AUTO
1306 7bde29b5 Agata Murawska
    return backend
1307 7bde29b5 Agata Murawska
1308 7bde29b5 Agata Murawska
  def _ParseTags(self):
1309 7bde29b5 Agata Murawska
    """Returns tags list given in command line.
1310 7bde29b5 Agata Murawska

1311 7bde29b5 Agata Murawska
    @rtype: string
1312 7bde29b5 Agata Murawska
    @return: string containing comma-separated tags
1313 7bde29b5 Agata Murawska

1314 7bde29b5 Agata Murawska
    """
1315 7bde29b5 Agata Murawska
    return self.options.tags
1316 7bde29b5 Agata Murawska
1317 24b9469d Agata Murawska
  def _ParseNicOptions(self):
1318 24b9469d Agata Murawska
    """Parses network options given in a command line or as a dictionary.
1319 24b9469d Agata Murawska

1320 24b9469d Agata Murawska
    @rtype: dict
1321 24b9469d Agata Murawska
    @return: dictionary of network-related options
1322 24b9469d Agata Murawska

1323 24b9469d Agata Murawska
    """
1324 24b9469d Agata Murawska
    assert self.options.nics
1325 24b9469d Agata Murawska
    results = {}
1326 24b9469d Agata Murawska
    for (nic_id, nic_desc) in self.options.nics:
1327 24b9469d Agata Murawska
      results["nic%s_mode" % nic_id] = \
1328 24b9469d Agata Murawska
        nic_desc.get("mode", constants.VALUE_AUTO)
1329 24b9469d Agata Murawska
      results["nic%s_mac" % nic_id] = nic_desc.get("mac", constants.VALUE_AUTO)
1330 24b9469d Agata Murawska
      results["nic%s_link" % nic_id] = \
1331 24b9469d Agata Murawska
        nic_desc.get("link", constants.VALUE_AUTO)
1332 24b9469d Agata Murawska
      if nic_desc.get("mode") == "bridged":
1333 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_NONE
1334 24b9469d Agata Murawska
      else:
1335 24b9469d Agata Murawska
        results["nic%s_ip" % nic_id] = constants.VALUE_AUTO
1336 24b9469d Agata Murawska
    results["nic_count"] = str(len(self.options.nics))
1337 24b9469d Agata Murawska
    return results
1338 24b9469d Agata Murawska
1339 99381e3b Agata Murawska
  def _ParseDiskOptions(self):
1340 99381e3b Agata Murawska
    """Parses disk options given in a command line.
1341 99381e3b Agata Murawska

1342 99381e3b Agata Murawska
    @rtype: dict
1343 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1344 99381e3b Agata Murawska

1345 99381e3b Agata Murawska
    @raise errors.OpPrereqError: disk description does not contain size
1346 99381e3b Agata Murawska
      information or size information is invalid or creation failed
1347 99381e3b Agata Murawska

1348 99381e3b Agata Murawska
    """
1349 a002ed79 Agata Murawska
    CheckQemuImg()
1350 99381e3b Agata Murawska
    assert self.options.disks
1351 99381e3b Agata Murawska
    results = {}
1352 99381e3b Agata Murawska
    for (disk_id, disk_desc) in self.options.disks:
1353 99381e3b Agata Murawska
      results["disk%s_ivname" % disk_id] = "disk/%s" % disk_id
1354 99381e3b Agata Murawska
      if disk_desc.get("size"):
1355 99381e3b Agata Murawska
        try:
1356 99381e3b Agata Murawska
          disk_size = utils.ParseUnit(disk_desc["size"])
1357 99381e3b Agata Murawska
        except ValueError:
1358 99381e3b Agata Murawska
          raise errors.OpPrereqError("Invalid disk size for disk %s: %s" %
1359 2cfbc784 Iustin Pop
                                     (disk_id, disk_desc["size"]),
1360 2cfbc784 Iustin Pop
                                     errors.ECODE_INVAL)
1361 99381e3b Agata Murawska
        new_path = utils.PathJoin(self.output_dir, str(disk_id))
1362 99381e3b Agata Murawska
        args = [
1363 e68949b0 Agata Murawska
          constants.QEMUIMG_PATH,
1364 99381e3b Agata Murawska
          "create",
1365 99381e3b Agata Murawska
          "-f",
1366 99381e3b Agata Murawska
          "raw",
1367 99381e3b Agata Murawska
          new_path,
1368 99381e3b Agata Murawska
          disk_size,
1369 99381e3b Agata Murawska
        ]
1370 99381e3b Agata Murawska
        run_result = utils.RunCmd(args)
1371 99381e3b Agata Murawska
        if run_result.failed:
1372 99381e3b Agata Murawska
          raise errors.OpPrereqError("Creation of disk %s failed, output was:"
1373 2cfbc784 Iustin Pop
                                     " %s" % (new_path, run_result.stderr),
1374 2cfbc784 Iustin Pop
                                     errors.ECODE_ENVIRON)
1375 99381e3b Agata Murawska
        results["disk%s_size" % disk_id] = str(disk_size)
1376 99381e3b Agata Murawska
        results["disk%s_dump" % disk_id] = "disk%s.raw" % disk_id
1377 99381e3b Agata Murawska
      else:
1378 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks created for import must have their"
1379 2cfbc784 Iustin Pop
                                   " size specified",
1380 2cfbc784 Iustin Pop
                                   errors.ECODE_INVAL)
1381 99381e3b Agata Murawska
    results["disk_count"] = str(len(self.options.disks))
1382 99381e3b Agata Murawska
    return results
1383 99381e3b Agata Murawska
1384 99381e3b Agata Murawska
  def _GetDiskInfo(self):
1385 99381e3b Agata Murawska
    """Gathers information about disks used by instance, perfomes conversion.
1386 99381e3b Agata Murawska

1387 99381e3b Agata Murawska
    @rtype: dict
1388 99381e3b Agata Murawska
    @return: dictionary of disk-related options
1389 99381e3b Agata Murawska

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

1392 99381e3b Agata Murawska
    """
1393 99381e3b Agata Murawska
    results = {}
1394 99381e3b Agata Murawska
    disks_list = self.ovf_reader.GetDisksNames()
1395 99381e3b Agata Murawska
    for (counter, (disk_name, disk_compression)) in enumerate(disks_list):
1396 99381e3b Agata Murawska
      if os.path.dirname(disk_name):
1397 99381e3b Agata Murawska
        raise errors.OpPrereqError("Disks are not allowed to have absolute"
1398 2cfbc784 Iustin Pop
                                   " paths or paths outside main OVF"
1399 2cfbc784 Iustin Pop
                                   " directory", errors.ECODE_ENVIRON)
1400 99381e3b Agata Murawska
      disk, _ = os.path.splitext(disk_name)
1401 99381e3b Agata Murawska
      disk_path = utils.PathJoin(self.input_dir, disk_name)
1402 fa337742 Agata Murawska
      if disk_compression not in NO_COMPRESSION:
1403 99381e3b Agata Murawska
        _, disk_path = self._CompressDisk(disk_path, disk_compression,
1404 5ae4945a Iustin Pop
                                          DECOMPRESS)
1405 99381e3b Agata Murawska
        disk, _ = os.path.splitext(disk)
1406 99381e3b Agata Murawska
      if self._GetDiskQemuInfo(disk_path, "file format: (\S+)") != "raw":
1407 99381e3b Agata Murawska
        logging.info("Conversion to raw format is required")
1408 99381e3b Agata Murawska
      ext, new_disk_path = self._ConvertDisk("raw", disk_path)
1409 99381e3b Agata Murawska
1410 99381e3b Agata Murawska
      final_disk_path = LinkFile(new_disk_path, prefix=disk, suffix=ext,
1411 5ae4945a Iustin Pop
                                 directory=self.output_dir)
1412 99381e3b Agata Murawska
      final_name = os.path.basename(final_disk_path)
1413 99381e3b Agata Murawska
      disk_size = os.path.getsize(final_disk_path) / (1024 * 1024)
1414 99381e3b Agata Murawska
      results["disk%s_dump" % counter] = final_name
1415 99381e3b Agata Murawska
      results["disk%s_size" % counter] = str(disk_size)
1416 99381e3b Agata Murawska
      results["disk%s_ivname" % counter] = "disk/%s" % str(counter)
1417 99381e3b Agata Murawska
    if disks_list:
1418 99381e3b Agata Murawska
      results["disk_count"] = str(len(disks_list))
1419 99381e3b Agata Murawska
    return results
1420 ced78a66 Agata Murawska
1421 ced78a66 Agata Murawska
  def Save(self):
1422 864cf6bf Agata Murawska
    """Saves all the gathered information in a constant.EXPORT_CONF_FILE file.
1423 864cf6bf Agata Murawska

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

1426 864cf6bf Agata Murawska
    """
1427 864cf6bf Agata Murawska
    logging.info("Conversion was succesfull, saving %s in %s directory",
1428 864cf6bf Agata Murawska
                 constants.EXPORT_CONF_FILE, self.output_dir)
1429 864cf6bf Agata Murawska
    results = {
1430 864cf6bf Agata Murawska
      constants.INISECT_INS: {},
1431 864cf6bf Agata Murawska
      constants.INISECT_BEP: {},
1432 864cf6bf Agata Murawska
      constants.INISECT_EXP: {},
1433 864cf6bf Agata Murawska
      constants.INISECT_OSP: {},
1434 864cf6bf Agata Murawska
      constants.INISECT_HYP: {},
1435 864cf6bf Agata Murawska
    }
1436 864cf6bf Agata Murawska
1437 864cf6bf Agata Murawska
    results[constants.INISECT_INS].update(self.results_disk)
1438 24b9469d Agata Murawska
    results[constants.INISECT_INS].update(self.results_network)
1439 7bde29b5 Agata Murawska
    results[constants.INISECT_INS]["hypervisor"] = \
1440 7bde29b5 Agata Murawska
      self.results_hypervisor["hypervisor_name"]
1441 864cf6bf Agata Murawska
    results[constants.INISECT_INS]["name"] = self.results_name
1442 864cf6bf Agata Murawska
    if self.results_template:
1443 864cf6bf Agata Murawska
      results[constants.INISECT_INS]["disk_template"] = self.results_template
1444 7bde29b5 Agata Murawska
    if self.results_tags:
1445 7bde29b5 Agata Murawska
      results[constants.INISECT_INS]["tags"] = self.results_tags
1446 7bde29b5 Agata Murawska
1447 7bde29b5 Agata Murawska
    results[constants.INISECT_BEP].update(self.results_backend)
1448 7bde29b5 Agata Murawska
1449 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["os"] = self.results_os["os_name"]
1450 7bde29b5 Agata Murawska
    results[constants.INISECT_EXP]["version"] = self.results_version
1451 7bde29b5 Agata Murawska
1452 7bde29b5 Agata Murawska
    del self.results_os["os_name"]
1453 7bde29b5 Agata Murawska
    results[constants.INISECT_OSP].update(self.results_os)
1454 7bde29b5 Agata Murawska
1455 7bde29b5 Agata Murawska
    del self.results_hypervisor["hypervisor_name"]
1456 7bde29b5 Agata Murawska
    results[constants.INISECT_HYP].update(self.results_hypervisor)
1457 864cf6bf Agata Murawska
1458 864cf6bf Agata Murawska
    output_file_name = utils.PathJoin(self.output_dir,
1459 5ae4945a Iustin Pop
                                      constants.EXPORT_CONF_FILE)
1460 864cf6bf Agata Murawska
1461 864cf6bf Agata Murawska
    output = []
1462 864cf6bf Agata Murawska
    for section, options in results.iteritems():
1463 864cf6bf Agata Murawska
      output.append("[%s]" % section)
1464 864cf6bf Agata Murawska
      for name, value in options.iteritems():
1465 99381e3b Agata Murawska
        if value is None:
1466 99381e3b Agata Murawska
          value = ""
1467 864cf6bf Agata Murawska
        output.append("%s = %s" % (name, value))
1468 864cf6bf Agata Murawska
      output.append("")
1469 864cf6bf Agata Murawska
    output_contents = "\n".join(output)
1470 864cf6bf Agata Murawska
1471 864cf6bf Agata Murawska
    try:
1472 864cf6bf Agata Murawska
      utils.WriteFile(output_file_name, data=output_contents)
1473 864cf6bf Agata Murawska
    except errors.ProgrammerError, err:
1474 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("Saving the config file failed: %s" % err,
1475 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1476 864cf6bf Agata Murawska
1477 864cf6bf Agata Murawska
    self.Cleanup()
1478 ced78a66 Agata Murawska
1479 ced78a66 Agata Murawska
1480 0963b26a Agata Murawska
class ConfigParserWithDefaults(ConfigParser.SafeConfigParser):
1481 0963b26a Agata Murawska
  """This is just a wrapper on SafeConfigParser, that uses default values
1482 0963b26a Agata Murawska

1483 0963b26a Agata Murawska
  """
1484 0963b26a Agata Murawska
  def get(self, section, options, raw=None, vars=None): # pylint: disable=W0622
1485 0963b26a Agata Murawska
    try:
1486 5ae4945a Iustin Pop
      result = ConfigParser.SafeConfigParser.get(self, section, options,
1487 5ae4945a Iustin Pop
                                                 raw=raw, vars=vars)
1488 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1489 0963b26a Agata Murawska
      result = None
1490 0963b26a Agata Murawska
    return result
1491 0963b26a Agata Murawska
1492 0963b26a Agata Murawska
  def getint(self, section, options):
1493 0963b26a Agata Murawska
    try:
1494 0963b26a Agata Murawska
      result = ConfigParser.SafeConfigParser.get(self, section, options)
1495 0963b26a Agata Murawska
    except ConfigParser.NoOptionError:
1496 0963b26a Agata Murawska
      result = 0
1497 0963b26a Agata Murawska
    return int(result)
1498 0963b26a Agata Murawska
1499 0963b26a Agata Murawska
1500 ced78a66 Agata Murawska
class OVFExporter(Converter):
1501 0963b26a Agata Murawska
  """Converter from Ganeti config file to OVF
1502 0963b26a Agata Murawska

1503 0963b26a Agata Murawska
  @type input_dir: string
1504 0963b26a Agata Murawska
  @ivar input_dir: directory in which the config.ini file resides
1505 0963b26a Agata Murawska
  @type output_dir: string
1506 0963b26a Agata Murawska
  @ivar output_dir: directory to which the results of conversion shall be
1507 0963b26a Agata Murawska
    written
1508 0963b26a Agata Murawska
  @type packed_dir: string
1509 0963b26a Agata Murawska
  @ivar packed_dir: if we want OVA package, this points to the real (i.e. not
1510 0963b26a Agata Murawska
    temp) output directory
1511 0963b26a Agata Murawska
  @type input_path: string
1512 0963b26a Agata Murawska
  @ivar input_path: complete path to the config.ini file
1513 0963b26a Agata Murawska
  @type output_path: string
1514 0963b26a Agata Murawska
  @ivar output_path: complete path to .ovf file
1515 0963b26a Agata Murawska
  @type config_parser: L{ConfigParserWithDefaults}
1516 0963b26a Agata Murawska
  @ivar config_parser: parser for the config.ini file
1517 7432d332 Agata Murawska
  @type reference_files: list
1518 7432d332 Agata Murawska
  @ivar reference_files: files referenced in the ovf file
1519 b179ce72 Agata Murawska
  @type results_disk: list
1520 b179ce72 Agata Murawska
  @ivar results_disk: list of dictionaries of disk options from config.ini
1521 b179ce72 Agata Murawska
  @type results_network: list
1522 b179ce72 Agata Murawska
  @ivar results_network: list of dictionaries of network options form config.ini
1523 0963b26a Agata Murawska
  @type results_name: string
1524 0963b26a Agata Murawska
  @ivar results_name: name of the instance
1525 b179ce72 Agata Murawska
  @type results_vcpus: string
1526 b179ce72 Agata Murawska
  @ivar results_vcpus: number of VCPUs
1527 b179ce72 Agata Murawska
  @type results_memory: string
1528 b179ce72 Agata Murawska
  @ivar results_memory: RAM memory in MB
1529 b179ce72 Agata Murawska
  @type results_ganeti: dict
1530 b179ce72 Agata Murawska
  @ivar results_ganeti: dictionary of Ganeti-specific options from config.ini
1531 0963b26a Agata Murawska

1532 0963b26a Agata Murawska
  """
1533 ced78a66 Agata Murawska
  def _ReadInputData(self, input_path):
1534 0963b26a Agata Murawska
    """Reads the data on which the conversion will take place.
1535 0963b26a Agata Murawska

1536 0963b26a Agata Murawska
    @type input_path: string
1537 0963b26a Agata Murawska
    @param input_path: absolute path to the config.ini input file
1538 0963b26a Agata Murawska

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

1541 0963b26a Agata Murawska
    """
1542 0963b26a Agata Murawska
    input_dir = os.path.dirname(input_path)
1543 0963b26a Agata Murawska
    self.input_path = input_path
1544 0963b26a Agata Murawska
    self.input_dir = input_dir
1545 0963b26a Agata Murawska
    if self.options.output_dir:
1546 0963b26a Agata Murawska
      self.output_dir = os.path.abspath(self.options.output_dir)
1547 0963b26a Agata Murawska
    else:
1548 0963b26a Agata Murawska
      self.output_dir = input_dir
1549 0963b26a Agata Murawska
    self.config_parser = ConfigParserWithDefaults()
1550 0963b26a Agata Murawska
    logging.info("Reading configuration from %s file", input_path)
1551 0963b26a Agata Murawska
    try:
1552 0963b26a Agata Murawska
      self.config_parser.read(input_path)
1553 0963b26a Agata Murawska
    except ConfigParser.MissingSectionHeaderError, err:
1554 0963b26a Agata Murawska
      raise errors.OpPrereqError("Error when trying to read %s: %s" %
1555 2cfbc784 Iustin Pop
                                 (input_path, err), errors.ECODE_ENVIRON)
1556 0963b26a Agata Murawska
    if self.options.ova_package:
1557 0963b26a Agata Murawska
      self.temp_dir = tempfile.mkdtemp()
1558 0963b26a Agata Murawska
      self.packed_dir = self.output_dir
1559 0963b26a Agata Murawska
      self.output_dir = self.temp_dir
1560 0963b26a Agata Murawska
1561 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1562 0963b26a Agata Murawska
1563 0963b26a Agata Murawska
  def _ParseName(self):
1564 0963b26a Agata Murawska
    """Parses name from command line options or config file.
1565 0963b26a Agata Murawska

1566 0963b26a Agata Murawska
    @rtype: string
1567 0963b26a Agata Murawska
    @return: name of Ganeti instance
1568 0963b26a Agata Murawska

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

1571 0963b26a Agata Murawska
    """
1572 0963b26a Agata Murawska
    if self.options.name:
1573 0963b26a Agata Murawska
      name = self.options.name
1574 0963b26a Agata Murawska
    else:
1575 0963b26a Agata Murawska
      name = self.config_parser.get(constants.INISECT_INS, NAME)
1576 0963b26a Agata Murawska
    if name is None:
1577 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("No instance name found",
1578 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1579 0963b26a Agata Murawska
    return name
1580 ced78a66 Agata Murawska
1581 b179ce72 Agata Murawska
  def _ParseVCPUs(self):
1582 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1583 b179ce72 Agata Murawska

1584 b179ce72 Agata Murawska
    @rtype: int
1585 b179ce72 Agata Murawska
    @return: number of virtual CPUs
1586 b179ce72 Agata Murawska

1587 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if number of VCPUs equals 0
1588 b179ce72 Agata Murawska

1589 b179ce72 Agata Murawska
    """
1590 b179ce72 Agata Murawska
    vcpus = self.config_parser.getint(constants.INISECT_BEP, VCPUS)
1591 b179ce72 Agata Murawska
    if vcpus == 0:
1592 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("No CPU information found",
1593 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1594 b179ce72 Agata Murawska
    return vcpus
1595 b179ce72 Agata Murawska
1596 b179ce72 Agata Murawska
  def _ParseMemory(self):
1597 b179ce72 Agata Murawska
    """Parses vcpus number from config file.
1598 b179ce72 Agata Murawska

1599 b179ce72 Agata Murawska
    @rtype: int
1600 b179ce72 Agata Murawska
    @return: amount of memory in MB
1601 b179ce72 Agata Murawska

1602 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: if amount of memory equals 0
1603 b179ce72 Agata Murawska

1604 b179ce72 Agata Murawska
    """
1605 b179ce72 Agata Murawska
    memory = self.config_parser.getint(constants.INISECT_BEP, MEMORY)
1606 b179ce72 Agata Murawska
    if memory == 0:
1607 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("No memory information found",
1608 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1609 b179ce72 Agata Murawska
    return memory
1610 b179ce72 Agata Murawska
1611 b179ce72 Agata Murawska
  def _ParseGaneti(self):
1612 b179ce72 Agata Murawska
    """Parses Ganeti data from config file.
1613 b179ce72 Agata Murawska

1614 b179ce72 Agata Murawska
    @rtype: dictionary
1615 b179ce72 Agata Murawska
    @return: dictionary of Ganeti-specific options
1616 b179ce72 Agata Murawska

1617 b179ce72 Agata Murawska
    """
1618 b179ce72 Agata Murawska
    results = {}
1619 b179ce72 Agata Murawska
    # hypervisor
1620 b179ce72 Agata Murawska
    results["hypervisor"] = {}
1621 b179ce72 Agata Murawska
    hyp_name = self.config_parser.get(constants.INISECT_INS, HYPERV)
1622 b179ce72 Agata Murawska
    if hyp_name is None:
1623 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("No hypervisor information found",
1624 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1625 b179ce72 Agata Murawska
    results["hypervisor"]["name"] = hyp_name
1626 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_HYP)
1627 b179ce72 Agata Murawska
    for (name, value) in pairs:
1628 b179ce72 Agata Murawska
      results["hypervisor"][name] = value
1629 b179ce72 Agata Murawska
    # os
1630 b179ce72 Agata Murawska
    results["os"] = {}
1631 b179ce72 Agata Murawska
    os_name = self.config_parser.get(constants.INISECT_EXP, OS)
1632 b179ce72 Agata Murawska
    if os_name is None:
1633 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("No operating system information found",
1634 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1635 b179ce72 Agata Murawska
    results["os"]["name"] = os_name
1636 b179ce72 Agata Murawska
    pairs = self.config_parser.items(constants.INISECT_OSP)
1637 b179ce72 Agata Murawska
    for (name, value) in pairs:
1638 b179ce72 Agata Murawska
      results["os"][name] = value
1639 b179ce72 Agata Murawska
    # other
1640 b179ce72 Agata Murawska
    others = [
1641 b179ce72 Agata Murawska
      (constants.INISECT_INS, DISK_TEMPLATE, "disk_template"),
1642 b179ce72 Agata Murawska
      (constants.INISECT_BEP, AUTO_BALANCE, "auto_balance"),
1643 b179ce72 Agata Murawska
      (constants.INISECT_INS, TAGS, "tags"),
1644 b179ce72 Agata Murawska
      (constants.INISECT_EXP, VERSION, "version"),
1645 b179ce72 Agata Murawska
    ]
1646 b179ce72 Agata Murawska
    for (section, element, name) in others:
1647 b179ce72 Agata Murawska
      results[name] = self.config_parser.get(section, element)
1648 b179ce72 Agata Murawska
    return results
1649 b179ce72 Agata Murawska
1650 b179ce72 Agata Murawska
  def _ParseNetworks(self):
1651 b179ce72 Agata Murawska
    """Parses network data from config file.
1652 b179ce72 Agata Murawska

1653 b179ce72 Agata Murawska
    @rtype: list
1654 b179ce72 Agata Murawska
    @return: list of dictionaries of network options
1655 b179ce72 Agata Murawska

1656 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: then network mode is not recognized
1657 b179ce72 Agata Murawska

1658 b179ce72 Agata Murawska
    """
1659 b179ce72 Agata Murawska
    results = []
1660 b179ce72 Agata Murawska
    counter = 0
1661 b179ce72 Agata Murawska
    while True:
1662 b179ce72 Agata Murawska
      data_link = \
1663 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "nic%s_link" % counter)
1664 b179ce72 Agata Murawska
      if data_link is None:
1665 b179ce72 Agata Murawska
        break
1666 b179ce72 Agata Murawska
      results.append({
1667 b179ce72 Agata Murawska
        "mode": self.config_parser.get(constants.INISECT_INS,
1668 5ae4945a Iustin Pop
                                       "nic%s_mode" % counter),
1669 b179ce72 Agata Murawska
        "mac": self.config_parser.get(constants.INISECT_INS,
1670 5ae4945a Iustin Pop
                                      "nic%s_mac" % counter),
1671 b179ce72 Agata Murawska
        "ip": self.config_parser.get(constants.INISECT_INS,
1672 5ae4945a Iustin Pop
                                     "nic%s_ip" % counter),
1673 b179ce72 Agata Murawska
        "link": data_link,
1674 b179ce72 Agata Murawska
      })
1675 b179ce72 Agata Murawska
      if results[counter]["mode"] not in constants.NIC_VALID_MODES:
1676 b179ce72 Agata Murawska
        raise errors.OpPrereqError("Network mode %s not recognized"
1677 2cfbc784 Iustin Pop
                                   % results[counter]["mode"],
1678 2cfbc784 Iustin Pop
                                   errors.ECODE_INVAL)
1679 b179ce72 Agata Murawska
      counter += 1
1680 b179ce72 Agata Murawska
    return results
1681 b179ce72 Agata Murawska
1682 b179ce72 Agata Murawska
  def _GetDiskOptions(self, disk_file, compression):
1683 b179ce72 Agata Murawska
    """Convert the disk and gather disk info for .ovf file.
1684 b179ce72 Agata Murawska

1685 b179ce72 Agata Murawska
    @type disk_file: string
1686 b179ce72 Agata Murawska
    @param disk_file: name of the disk (without the full path)
1687 b179ce72 Agata Murawska
    @type compression: bool
1688 b179ce72 Agata Murawska
    @param compression: whether the disk should be compressed or not
1689 b179ce72 Agata Murawska

1690 b179ce72 Agata Murawska
    @raise errors.OpPrereqError: when disk image does not exist
1691 b179ce72 Agata Murawska

1692 b179ce72 Agata Murawska
    """
1693 b179ce72 Agata Murawska
    disk_path = utils.PathJoin(self.input_dir, disk_file)
1694 b179ce72 Agata Murawska
    results = {}
1695 b179ce72 Agata Murawska
    if not os.path.isfile(disk_path):
1696 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("Disk image does not exist: %s" % disk_path,
1697 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1698 b179ce72 Agata Murawska
    if os.path.dirname(disk_file):
1699 b179ce72 Agata Murawska
      raise errors.OpPrereqError("Path for the disk: %s contains a directory"
1700 2cfbc784 Iustin Pop
                                 " name" % disk_path, errors.ECODE_ENVIRON)
1701 b179ce72 Agata Murawska
    disk_name, _ = os.path.splitext(disk_file)
1702 b179ce72 Agata Murawska
    ext, new_disk_path = self._ConvertDisk(self.options.disk_format, disk_path)
1703 b179ce72 Agata Murawska
    results["format"] = self.options.disk_format
1704 5ae4945a Iustin Pop
    results["virt-size"] = self._GetDiskQemuInfo(
1705 5ae4945a Iustin Pop
      new_disk_path, "virtual size: \S+ \((\d+) bytes\)")
1706 b179ce72 Agata Murawska
    if compression:
1707 b179ce72 Agata Murawska
      ext2, new_disk_path = self._CompressDisk(new_disk_path, "gzip",
1708 5ae4945a Iustin Pop
                                               COMPRESS)
1709 b179ce72 Agata Murawska
      disk_name, _ = os.path.splitext(disk_name)
1710 b179ce72 Agata Murawska
      results["compression"] = "gzip"
1711 b179ce72 Agata Murawska
      ext += ext2
1712 b179ce72 Agata Murawska
    final_disk_path = LinkFile(new_disk_path, prefix=disk_name, suffix=ext,
1713 5ae4945a Iustin Pop
                               directory=self.output_dir)
1714 b179ce72 Agata Murawska
    final_disk_name = os.path.basename(final_disk_path)
1715 b179ce72 Agata Murawska
    results["real-size"] = os.path.getsize(final_disk_path)
1716 b179ce72 Agata Murawska
    results["path"] = final_disk_name
1717 b179ce72 Agata Murawska
    self.references_files.append(final_disk_path)
1718 b179ce72 Agata Murawska
    return results
1719 b179ce72 Agata Murawska
1720 b179ce72 Agata Murawska
  def _ParseDisks(self):
1721 b179ce72 Agata Murawska
    """Parses disk data from config file.
1722 b179ce72 Agata Murawska

1723 b179ce72 Agata Murawska
    @rtype: list
1724 b179ce72 Agata Murawska
    @return: list of dictionaries of disk options
1725 b179ce72 Agata Murawska

1726 b179ce72 Agata Murawska
    """
1727 b179ce72 Agata Murawska
    results = []
1728 b179ce72 Agata Murawska
    counter = 0
1729 b179ce72 Agata Murawska
    while True:
1730 b179ce72 Agata Murawska
      disk_file = \
1731 b179ce72 Agata Murawska
        self.config_parser.get(constants.INISECT_INS, "disk%s_dump" % counter)
1732 b179ce72 Agata Murawska
      if disk_file is None:
1733 b179ce72 Agata Murawska
        break
1734 b179ce72 Agata Murawska
      results.append(self._GetDiskOptions(disk_file, self.options.compression))
1735 b179ce72 Agata Murawska
      counter += 1
1736 b179ce72 Agata Murawska
    return results
1737 b179ce72 Agata Murawska
1738 ced78a66 Agata Murawska
  def Parse(self):
1739 0963b26a Agata Murawska
    """Parses the data and creates a structure containing all required info.
1740 0963b26a Agata Murawska

1741 0963b26a Agata Murawska
    """
1742 0963b26a Agata Murawska
    try:
1743 0963b26a Agata Murawska
      utils.Makedirs(self.output_dir)
1744 0963b26a Agata Murawska
    except OSError, err:
1745 0963b26a Agata Murawska
      raise errors.OpPrereqError("Failed to create directory %s: %s" %
1746 2cfbc784 Iustin Pop
                                 (self.output_dir, err), errors.ECODE_ENVIRON)
1747 0963b26a Agata Murawska
1748 b179ce72 Agata Murawska
    self.references_files = []
1749 0963b26a Agata Murawska
    self.results_name = self._ParseName()
1750 b179ce72 Agata Murawska
    self.results_vcpus = self._ParseVCPUs()
1751 b179ce72 Agata Murawska
    self.results_memory = self._ParseMemory()
1752 b179ce72 Agata Murawska
    if not self.options.ext_usage:
1753 b179ce72 Agata Murawska
      self.results_ganeti = self._ParseGaneti()
1754 b179ce72 Agata Murawska
    self.results_network = self._ParseNetworks()
1755 b179ce72 Agata Murawska
    self.results_disk = self._ParseDisks()
1756 0963b26a Agata Murawska
1757 0963b26a Agata Murawska
  def _PrepareManifest(self, path):
1758 0963b26a Agata Murawska
    """Creates manifest for all the files in OVF package.
1759 0963b26a Agata Murawska

1760 0963b26a Agata Murawska
    @type path: string
1761 0963b26a Agata Murawska
    @param path: path to manifesto file
1762 0963b26a Agata Murawska

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

1765 0963b26a Agata Murawska
    """
1766 0963b26a Agata Murawska
    logging.info("Preparing manifest for the OVF package")
1767 0963b26a Agata Murawska
    lines = []
1768 0963b26a Agata Murawska
    files_list = [self.output_path]
1769 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1770 0963b26a Agata Murawska
    logging.warning("Calculating SHA1 checksums, this may take a while")
1771 0963b26a Agata Murawska
    sha1_sums = utils.FingerprintFiles(files_list)
1772 0963b26a Agata Murawska
    for file_path, value in sha1_sums.iteritems():
1773 0963b26a Agata Murawska
      file_name = os.path.basename(file_path)
1774 0963b26a Agata Murawska
      lines.append("SHA1(%s)= %s" % (file_name, value))
1775 0963b26a Agata Murawska
    lines.append("")
1776 0963b26a Agata Murawska
    data = "\n".join(lines)
1777 0963b26a Agata Murawska
    try:
1778 0963b26a Agata Murawska
      utils.WriteFile(path, data=data)
1779 0963b26a Agata Murawska
    except errors.ProgrammerError, err:
1780 2cfbc784 Iustin Pop
      raise errors.OpPrereqError("Saving the manifest file failed: %s" % err,
1781 2cfbc784 Iustin Pop
                                 errors.ECODE_ENVIRON)
1782 0963b26a Agata Murawska
1783 0963b26a Agata Murawska
  @staticmethod
1784 0963b26a Agata Murawska
  def _PrepareTarFile(tar_path, files_list):
1785 0963b26a Agata Murawska
    """Creates tarfile from the files in OVF package.
1786 0963b26a Agata Murawska

1787 0963b26a Agata Murawska
    @type tar_path: string
1788 0963b26a Agata Murawska
    @param tar_path: path to the resulting file
1789 0963b26a Agata Murawska
    @type files_list: list
1790 0963b26a Agata Murawska
    @param files_list: list of files in the OVF package
1791 0963b26a Agata Murawska

1792 0963b26a Agata Murawska
    """
1793 0963b26a Agata Murawska
    logging.info("Preparing tarball for the OVF package")
1794 0963b26a Agata Murawska
    open(tar_path, mode="w").close()
1795 0963b26a Agata Murawska
    ova_package = tarfile.open(name=tar_path, mode="w")
1796 fa337742 Agata Murawska
    for file_path in files_list:
1797 fa337742 Agata Murawska
      file_name = os.path.basename(file_path)
1798 fa337742 Agata Murawska
      ova_package.add(file_path, arcname=file_name)
1799 0963b26a Agata Murawska
    ova_package.close()
1800 ced78a66 Agata Murawska
1801 ced78a66 Agata Murawska
  def Save(self):
1802 0963b26a Agata Murawska
    """Saves the gathered configuration in an apropriate format.
1803 0963b26a Agata Murawska

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

1806 0963b26a Agata Murawska
    """
1807 0963b26a Agata Murawska
    output_file = "%s%s" % (self.results_name, OVF_EXT)
1808 0963b26a Agata Murawska
    output_path = utils.PathJoin(self.output_dir, output_file)
1809 0963b26a Agata Murawska
    self.ovf_writer = OVFWriter(not self.options.ext_usage)
1810 0963b26a Agata Murawska
    logging.info("Saving read data to %s", output_path)
1811 0963b26a Agata Murawska
1812 0963b26a Agata Murawska
    self.output_path = utils.PathJoin(self.output_dir, output_file)
1813 0963b26a Agata Murawska
    files_list = [self.output_path]
1814 0963b26a Agata Murawska
1815 7432d332 Agata Murawska
    self.ovf_writer.SaveDisksData(self.results_disk)
1816 7432d332 Agata Murawska
    self.ovf_writer.SaveNetworksData(self.results_network)
1817 7432d332 Agata Murawska
    if not self.options.ext_usage:
1818 7432d332 Agata Murawska
      self.ovf_writer.SaveGanetiData(self.results_ganeti, self.results_network)
1819 7432d332 Agata Murawska
1820 7432d332 Agata Murawska
    self.ovf_writer.SaveVirtualSystemData(self.results_name, self.results_vcpus,
1821 5ae4945a Iustin Pop
                                          self.results_memory)
1822 7432d332 Agata Murawska
1823 0963b26a Agata Murawska
    data = self.ovf_writer.PrettyXmlDump()
1824 0963b26a Agata Murawska
    utils.WriteFile(self.output_path, data=data)
1825 0963b26a Agata Murawska
1826 0963b26a Agata Murawska
    manifest_file = "%s%s" % (self.results_name, MF_EXT)
1827 0963b26a Agata Murawska
    manifest_path = utils.PathJoin(self.output_dir, manifest_file)
1828 0963b26a Agata Murawska
    self._PrepareManifest(manifest_path)
1829 0963b26a Agata Murawska
    files_list.append(manifest_path)
1830 0963b26a Agata Murawska
1831 0963b26a Agata Murawska
    files_list.extend(self.references_files)
1832 0963b26a Agata Murawska
1833 0963b26a Agata Murawska
    if self.options.ova_package:
1834 0963b26a Agata Murawska
      ova_file = "%s%s" % (self.results_name, OVA_EXT)
1835 0963b26a Agata Murawska
      packed_path = utils.PathJoin(self.packed_dir, ova_file)
1836 0963b26a Agata Murawska
      try:
1837 0963b26a Agata Murawska
        utils.Makedirs(self.packed_dir)
1838 0963b26a Agata Murawska
      except OSError, err:
1839 0963b26a Agata Murawska
        raise errors.OpPrereqError("Failed to create directory %s: %s" %
1840 2cfbc784 Iustin Pop
                                   (self.packed_dir, err),
1841 2cfbc784 Iustin Pop
                                   errors.ECODE_ENVIRON)
1842 0963b26a Agata Murawska
      self._PrepareTarFile(packed_path, files_list)
1843 0963b26a Agata Murawska
    logging.info("Creation of the OVF package was successfull")
1844 0963b26a Agata Murawska
    self.Cleanup()