Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor.py @ b74159ee

History | View | Annotate | Download (21.1 kB)

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

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
import time
27 a8083063 Iustin Pop
import os
28 a8083063 Iustin Pop
from cStringIO import StringIO
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from ganeti import utils
31 a8083063 Iustin Pop
from ganeti import logger
32 a8083063 Iustin Pop
from ganeti import ssconf
33 f00b46bc Michael Hanselmann
from ganeti import constants
34 2a6469d5 Alexander Schreiber
from ganeti import errors
35 a8083063 Iustin Pop
from ganeti.errors import HypervisorError
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
def GetHypervisor():
39 a8083063 Iustin Pop
  """Return a Hypervisor instance.
40 a8083063 Iustin Pop

41 a8083063 Iustin Pop
  This function parses the cluster hypervisor configuration file and
42 a8083063 Iustin Pop
  instantiates a class based on the value of this file.
43 a8083063 Iustin Pop

44 a8083063 Iustin Pop
  """
45 a8083063 Iustin Pop
  ht_kind = ssconf.SimpleStore().GetHypervisorType()
46 631eb662 Alexander Schreiber
  if ht_kind == constants.HT_XEN_PVM30:
47 631eb662 Alexander Schreiber
    cls = XenPvmHypervisor
48 2584d4a4 Alexander Schreiber
  elif ht_kind == constants.HT_FAKE:
49 a8083063 Iustin Pop
    cls = FakeHypervisor
50 2a6469d5 Alexander Schreiber
  elif ht_kind == constants.HT_XEN_HVM31:
51 2a6469d5 Alexander Schreiber
    cls = XenHvmHypervisor
52 a8083063 Iustin Pop
  else:
53 3ecf6786 Iustin Pop
    raise HypervisorError("Unknown hypervisor type '%s'" % ht_kind)
54 a8083063 Iustin Pop
  return cls()
55 a8083063 Iustin Pop
56 a8083063 Iustin Pop
57 a8083063 Iustin Pop
class BaseHypervisor(object):
58 a8083063 Iustin Pop
  """Abstract virtualisation technology interface
59 a8083063 Iustin Pop

60 a8083063 Iustin Pop
  The goal is that all aspects of the virtualisation technology must
61 a8083063 Iustin Pop
  be abstracted away from the rest of code.
62 a8083063 Iustin Pop

63 a8083063 Iustin Pop
  """
64 a8083063 Iustin Pop
  def __init__(self):
65 a8083063 Iustin Pop
    pass
66 a8083063 Iustin Pop
67 a8083063 Iustin Pop
  def StartInstance(self, instance, block_devices, extra_args):
68 a8083063 Iustin Pop
    """Start an instance."""
69 a8083063 Iustin Pop
    raise NotImplementedError
70 a8083063 Iustin Pop
71 a8083063 Iustin Pop
  def StopInstance(self, instance, force=False):
72 a8083063 Iustin Pop
    """Stop an instance."""
73 a8083063 Iustin Pop
    raise NotImplementedError
74 a8083063 Iustin Pop
75 7e1394dc Alexander Schreiber
  def RebootInstance(self, instance):
76 7e1394dc Alexander Schreiber
    """Reboot an instance."""
77 7e1394dc Alexander Schreiber
    raise NotImplementedError
78 7e1394dc Alexander Schreiber
79 a8083063 Iustin Pop
  def ListInstances(self):
80 a8083063 Iustin Pop
    """Get the list of running instances."""
81 a8083063 Iustin Pop
    raise NotImplementedError
82 a8083063 Iustin Pop
83 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
84 a8083063 Iustin Pop
    """Get instance properties.
85 a8083063 Iustin Pop

86 a8083063 Iustin Pop
    Args:
87 a8083063 Iustin Pop
      instance_name: the instance name
88 a8083063 Iustin Pop

89 a8083063 Iustin Pop
    Returns:
90 a8083063 Iustin Pop
      (name, id, memory, vcpus, state, times)
91 a8083063 Iustin Pop

92 a8083063 Iustin Pop
    """
93 a8083063 Iustin Pop
    raise NotImplementedError
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
  def GetAllInstancesInfo(self):
96 a8083063 Iustin Pop
    """Get properties of all instances.
97 a8083063 Iustin Pop

98 a8083063 Iustin Pop
    Returns:
99 a8083063 Iustin Pop
      [(name, id, memory, vcpus, stat, times),...]
100 a8083063 Iustin Pop
    """
101 a8083063 Iustin Pop
    raise NotImplementedError
102 a8083063 Iustin Pop
103 a8083063 Iustin Pop
  def GetNodeInfo(self):
104 a8083063 Iustin Pop
    """Return information about the node.
105 a8083063 Iustin Pop

106 a8083063 Iustin Pop
    The return value is a dict, which has to have the following items:
107 a8083063 Iustin Pop
      (all values in MiB)
108 a8083063 Iustin Pop
      - memory_total: the total memory size on the node
109 a8083063 Iustin Pop
      - memory_free: the available memory on the node for instances
110 a8083063 Iustin Pop
      - memory_dom0: the memory used by the node itself, if available
111 a8083063 Iustin Pop

112 a8083063 Iustin Pop
    """
113 a8083063 Iustin Pop
    raise NotImplementedError
114 a8083063 Iustin Pop
115 a8083063 Iustin Pop
  @staticmethod
116 30989e69 Alexander Schreiber
  def GetShellCommandForConsole(instance):
117 a8083063 Iustin Pop
    """Return a command for connecting to the console of an instance.
118 a8083063 Iustin Pop

119 a8083063 Iustin Pop
    """
120 a8083063 Iustin Pop
    raise NotImplementedError
121 a8083063 Iustin Pop
122 a8083063 Iustin Pop
  def Verify(self):
123 a8083063 Iustin Pop
    """Verify the hypervisor.
124 a8083063 Iustin Pop

125 a8083063 Iustin Pop
    """
126 a8083063 Iustin Pop
    raise NotImplementedError
127 a8083063 Iustin Pop
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
class XenHypervisor(BaseHypervisor):
130 631eb662 Alexander Schreiber
  """Xen generic hypervisor interface
131 631eb662 Alexander Schreiber

132 631eb662 Alexander Schreiber
  This is the Xen base class used for both Xen PVM and HVM. It contains
133 631eb662 Alexander Schreiber
  all the functionality that is identical for both.
134 631eb662 Alexander Schreiber

135 631eb662 Alexander Schreiber
  """
136 a8083063 Iustin Pop
137 a8083063 Iustin Pop
  @staticmethod
138 a8083063 Iustin Pop
  def _WriteConfigFile(instance, block_devices, extra_args):
139 03abdbe8 Iustin Pop
    """Write the Xen config file for the instance.
140 a8083063 Iustin Pop

141 a8083063 Iustin Pop
    """
142 631eb662 Alexander Schreiber
    raise NotImplementedError
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  @staticmethod
145 a8083063 Iustin Pop
  def _RemoveConfigFile(instance):
146 a8083063 Iustin Pop
    """Remove the xen configuration file.
147 a8083063 Iustin Pop

148 a8083063 Iustin Pop
    """
149 a8083063 Iustin Pop
    utils.RemoveFile("/etc/xen/%s" % instance.name)
150 a8083063 Iustin Pop
151 a8083063 Iustin Pop
  @staticmethod
152 a8083063 Iustin Pop
  def _GetXMList(include_node):
153 a8083063 Iustin Pop
    """Return the list of running instances.
154 a8083063 Iustin Pop

155 a8083063 Iustin Pop
    If the `include_node` argument is True, then we return information
156 a8083063 Iustin Pop
    for dom0 also, otherwise we filter that from the return value.
157 a8083063 Iustin Pop

158 a8083063 Iustin Pop
    The return value is a list of (name, id, memory, vcpus, state, time spent)
159 a8083063 Iustin Pop

160 a8083063 Iustin Pop
    """
161 a8083063 Iustin Pop
    for dummy in range(5):
162 a8083063 Iustin Pop
      result = utils.RunCmd(["xm", "list"])
163 a8083063 Iustin Pop
      if not result.failed:
164 a8083063 Iustin Pop
        break
165 a8083063 Iustin Pop
      logger.Error("xm list failed (%s): %s" % (result.fail_reason,
166 a8083063 Iustin Pop
                                                result.output))
167 a8083063 Iustin Pop
      time.sleep(1)
168 a8083063 Iustin Pop
169 a8083063 Iustin Pop
    if result.failed:
170 a8083063 Iustin Pop
      raise HypervisorError("xm list failed, retries exceeded (%s): %s" %
171 a8083063 Iustin Pop
                            (result.fail_reason, result.stderr))
172 a8083063 Iustin Pop
173 a424ce50 Alexander Schreiber
    # skip over the heading
174 a424ce50 Alexander Schreiber
    lines = result.stdout.splitlines()[1:]
175 a8083063 Iustin Pop
    result = []
176 a8083063 Iustin Pop
    for line in lines:
177 a8083063 Iustin Pop
      # The format of lines is:
178 a8083063 Iustin Pop
      # Name      ID Mem(MiB) VCPUs State  Time(s)
179 a8083063 Iustin Pop
      # Domain-0   0  3418     4 r-----    266.2
180 a8083063 Iustin Pop
      data = line.split()
181 a8083063 Iustin Pop
      if len(data) != 6:
182 a8083063 Iustin Pop
        raise HypervisorError("Can't parse output of xm list, line: %s" % line)
183 a8083063 Iustin Pop
      try:
184 a8083063 Iustin Pop
        data[1] = int(data[1])
185 a8083063 Iustin Pop
        data[2] = int(data[2])
186 a8083063 Iustin Pop
        data[3] = int(data[3])
187 a8083063 Iustin Pop
        data[5] = float(data[5])
188 a8083063 Iustin Pop
      except ValueError, err:
189 a8083063 Iustin Pop
        raise HypervisorError("Can't parse output of xm list,"
190 a8083063 Iustin Pop
                              " line: %s, error: %s" % (line, err))
191 a424ce50 Alexander Schreiber
192 a424ce50 Alexander Schreiber
      # skip the Domain-0 (optional)
193 a424ce50 Alexander Schreiber
      if include_node or data[0] != 'Domain-0':
194 a424ce50 Alexander Schreiber
        result.append(data)
195 a424ce50 Alexander Schreiber
196 a8083063 Iustin Pop
    return result
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
  def ListInstances(self):
199 a8083063 Iustin Pop
    """Get the list of running instances.
200 a8083063 Iustin Pop

201 a8083063 Iustin Pop
    """
202 a8083063 Iustin Pop
    xm_list = self._GetXMList(False)
203 a8083063 Iustin Pop
    names = [info[0] for info in xm_list]
204 a8083063 Iustin Pop
    return names
205 a8083063 Iustin Pop
206 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
207 a8083063 Iustin Pop
    """Get instance properties.
208 a8083063 Iustin Pop

209 a8083063 Iustin Pop
    Args:
210 a8083063 Iustin Pop
      instance_name: the instance name
211 a8083063 Iustin Pop

212 a8083063 Iustin Pop
    Returns:
213 a8083063 Iustin Pop
      (name, id, memory, vcpus, stat, times)
214 a8083063 Iustin Pop
    """
215 a8083063 Iustin Pop
    xm_list = self._GetXMList(instance_name=="Domain-0")
216 a8083063 Iustin Pop
    result = None
217 a8083063 Iustin Pop
    for data in xm_list:
218 a8083063 Iustin Pop
      if data[0] == instance_name:
219 a8083063 Iustin Pop
        result = data
220 a8083063 Iustin Pop
        break
221 a8083063 Iustin Pop
    return result
222 a8083063 Iustin Pop
223 a8083063 Iustin Pop
  def GetAllInstancesInfo(self):
224 a8083063 Iustin Pop
    """Get properties of all instances.
225 a8083063 Iustin Pop

226 a8083063 Iustin Pop
    Returns:
227 a8083063 Iustin Pop
      [(name, id, memory, vcpus, stat, times),...]
228 a8083063 Iustin Pop
    """
229 a8083063 Iustin Pop
    xm_list = self._GetXMList(False)
230 a8083063 Iustin Pop
    return xm_list
231 a8083063 Iustin Pop
232 a8083063 Iustin Pop
  def StartInstance(self, instance, block_devices, extra_args):
233 a8083063 Iustin Pop
    """Start an instance."""
234 a8083063 Iustin Pop
    self._WriteConfigFile(instance, block_devices, extra_args)
235 a8083063 Iustin Pop
    result = utils.RunCmd(["xm", "create", instance.name])
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
    if result.failed:
238 523687d7 Iustin Pop
      raise HypervisorError("Failed to start instance %s: %s (%s)" %
239 523687d7 Iustin Pop
                            (instance.name, result.fail_reason, result.output))
240 a8083063 Iustin Pop
241 a8083063 Iustin Pop
  def StopInstance(self, instance, force=False):
242 a8083063 Iustin Pop
    """Stop an instance."""
243 a8083063 Iustin Pop
    self._RemoveConfigFile(instance)
244 a8083063 Iustin Pop
    if force:
245 a8083063 Iustin Pop
      command = ["xm", "destroy", instance.name]
246 a8083063 Iustin Pop
    else:
247 a8083063 Iustin Pop
      command = ["xm", "shutdown", instance.name]
248 a8083063 Iustin Pop
    result = utils.RunCmd(command)
249 a8083063 Iustin Pop
250 a8083063 Iustin Pop
    if result.failed:
251 a8083063 Iustin Pop
      raise HypervisorError("Failed to stop instance %s: %s" %
252 a8083063 Iustin Pop
                            (instance.name, result.fail_reason))
253 a8083063 Iustin Pop
254 7e1394dc Alexander Schreiber
  def RebootInstance(self, instance):
255 7e1394dc Alexander Schreiber
    """Reboot an instance."""
256 7e1394dc Alexander Schreiber
    result = utils.RunCmd(["xm", "reboot", instance.name])
257 7e1394dc Alexander Schreiber
258 7e1394dc Alexander Schreiber
    if result.failed:
259 7e1394dc Alexander Schreiber
      raise HypervisorError("Failed to reboot instance %s: %s" %
260 7e1394dc Alexander Schreiber
                            (instance.name, result.fail_reason))
261 7e1394dc Alexander Schreiber
262 a8083063 Iustin Pop
  def GetNodeInfo(self):
263 a8083063 Iustin Pop
    """Return information about the node.
264 a8083063 Iustin Pop

265 a8083063 Iustin Pop
    The return value is a dict, which has to have the following items:
266 a8083063 Iustin Pop
      (all values in MiB)
267 a8083063 Iustin Pop
      - memory_total: the total memory size on the node
268 a8083063 Iustin Pop
      - memory_free: the available memory on the node for instances
269 a8083063 Iustin Pop
      - memory_dom0: the memory used by the node itself, if available
270 a8083063 Iustin Pop

271 a8083063 Iustin Pop
    """
272 a8083063 Iustin Pop
    # note: in xen 3, memory has changed to total_memory
273 a8083063 Iustin Pop
    result = utils.RunCmd(["xm", "info"])
274 a8083063 Iustin Pop
    if result.failed:
275 a8083063 Iustin Pop
      logger.Error("Can't run 'xm info': %s" % result.fail_reason)
276 a8083063 Iustin Pop
      return None
277 a8083063 Iustin Pop
278 a8083063 Iustin Pop
    xmoutput = result.stdout.splitlines()
279 a8083063 Iustin Pop
    result = {}
280 a8083063 Iustin Pop
    for line in xmoutput:
281 a8083063 Iustin Pop
      splitfields = line.split(":", 1)
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
      if len(splitfields) > 1:
284 a8083063 Iustin Pop
        key = splitfields[0].strip()
285 a8083063 Iustin Pop
        val = splitfields[1].strip()
286 a8083063 Iustin Pop
        if key == 'memory' or key == 'total_memory':
287 a8083063 Iustin Pop
          result['memory_total'] = int(val)
288 a8083063 Iustin Pop
        elif key == 'free_memory':
289 a8083063 Iustin Pop
          result['memory_free'] = int(val)
290 a8083063 Iustin Pop
    dom0_info = self.GetInstanceInfo("Domain-0")
291 a8083063 Iustin Pop
    if dom0_info is not None:
292 a8083063 Iustin Pop
      result['memory_dom0'] = dom0_info[2]
293 a8083063 Iustin Pop
294 a8083063 Iustin Pop
    return result
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
  @staticmethod
297 30989e69 Alexander Schreiber
  def GetShellCommandForConsole(instance):
298 a8083063 Iustin Pop
    """Return a command for connecting to the console of an instance.
299 a8083063 Iustin Pop

300 a8083063 Iustin Pop
    """
301 631eb662 Alexander Schreiber
    raise NotImplementedError
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
  def Verify(self):
305 a8083063 Iustin Pop
    """Verify the hypervisor.
306 a8083063 Iustin Pop

307 a8083063 Iustin Pop
    For Xen, this verifies that the xend process is running.
308 a8083063 Iustin Pop

309 a8083063 Iustin Pop
    """
310 a8083063 Iustin Pop
    if not utils.CheckDaemonAlive('/var/run/xend.pid', 'xend'):
311 a8083063 Iustin Pop
      return "xend daemon is not running"
312 a8083063 Iustin Pop
313 e994fcba Manuel Franceschini
  @staticmethod
314 e994fcba Manuel Franceschini
  def _GetConfigFileDiskData(disk_template, block_devices):
315 e994fcba Manuel Franceschini
    """Get disk directive for xen config file.
316 e994fcba Manuel Franceschini

317 e994fcba Manuel Franceschini
    This method builds the xen config disk directive according to the
318 e994fcba Manuel Franceschini
    given disk_template and block_devices.
319 e994fcba Manuel Franceschini

320 e994fcba Manuel Franceschini
    Args:
321 e994fcba Manuel Franceschini
      disk_template: String containing instance disk template
322 e994fcba Manuel Franceschini
      block_devices: List[tuple1,tuple2,...]
323 e994fcba Manuel Franceschini
        tuple: (cfdev, rldev)
324 e994fcba Manuel Franceschini
          cfdev: dict containing ganeti config disk part
325 e994fcba Manuel Franceschini
          rldev: ganeti.bdev.BlockDev object
326 e994fcba Manuel Franceschini

327 e994fcba Manuel Franceschini
    Returns:
328 e994fcba Manuel Franceschini
      String containing disk directive for xen instance config file
329 e994fcba Manuel Franceschini

330 e994fcba Manuel Franceschini
    """
331 e994fcba Manuel Franceschini
    FILE_DRIVER_MAP = {
332 e994fcba Manuel Franceschini
      constants.FD_LOOP: "file",
333 e994fcba Manuel Franceschini
      constants.FD_BLKTAP: "tap:aio",
334 e994fcba Manuel Franceschini
      }
335 e994fcba Manuel Franceschini
    disk_data = []
336 e994fcba Manuel Franceschini
    for cfdev, rldev in block_devices:
337 e994fcba Manuel Franceschini
      if cfdev.dev_type == constants.LD_FILE:
338 e994fcba Manuel Franceschini
        line = "'%s:%s,%s,w'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
339 e994fcba Manuel Franceschini
                                 rldev.dev_path, cfdev.iv_name)
340 e994fcba Manuel Franceschini
      else:
341 e994fcba Manuel Franceschini
        line = "'phy:%s,%s,w'" % (rldev.dev_path, cfdev.iv_name)
342 e994fcba Manuel Franceschini
      disk_data.append(line)
343 e994fcba Manuel Franceschini
344 e994fcba Manuel Franceschini
    return disk_data
345 e994fcba Manuel Franceschini
346 a8083063 Iustin Pop
347 631eb662 Alexander Schreiber
class XenPvmHypervisor(XenHypervisor):
348 631eb662 Alexander Schreiber
  """Xen PVM hypervisor interface"""
349 631eb662 Alexander Schreiber
350 e994fcba Manuel Franceschini
  @classmethod
351 e994fcba Manuel Franceschini
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
352 03abdbe8 Iustin Pop
    """Write the Xen config file for the instance.
353 631eb662 Alexander Schreiber

354 631eb662 Alexander Schreiber
    """
355 631eb662 Alexander Schreiber
    config = StringIO()
356 631eb662 Alexander Schreiber
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
357 03abdbe8 Iustin Pop
358 03abdbe8 Iustin Pop
    # kernel handling
359 03abdbe8 Iustin Pop
    if instance.kernel_path in (None, constants.VALUE_DEFAULT):
360 03abdbe8 Iustin Pop
      kpath = constants.XEN_KERNEL
361 03abdbe8 Iustin Pop
    else:
362 03abdbe8 Iustin Pop
      if not os.path.exists(instance.kernel_path):
363 03abdbe8 Iustin Pop
        raise errors.HypervisorError("The kernel %s for instance %s is"
364 03abdbe8 Iustin Pop
                                     " missing" % (instance.kernel_path,
365 03abdbe8 Iustin Pop
                                                   instance.name))
366 03abdbe8 Iustin Pop
      kpath = instance.kernel_path
367 03abdbe8 Iustin Pop
    config.write("kernel = '%s'\n" % kpath)
368 03abdbe8 Iustin Pop
369 03abdbe8 Iustin Pop
    # initrd handling
370 03abdbe8 Iustin Pop
    if instance.initrd_path in (None, constants.VALUE_DEFAULT):
371 03abdbe8 Iustin Pop
      if os.path.exists(constants.XEN_INITRD):
372 03abdbe8 Iustin Pop
        initrd_path = constants.XEN_INITRD
373 03abdbe8 Iustin Pop
      else:
374 03abdbe8 Iustin Pop
        initrd_path = None
375 03abdbe8 Iustin Pop
    elif instance.initrd_path == constants.VALUE_NONE:
376 03abdbe8 Iustin Pop
      initrd_path = None
377 03abdbe8 Iustin Pop
    else:
378 03abdbe8 Iustin Pop
      if not os.path.exists(instance.initrd_path):
379 03abdbe8 Iustin Pop
        raise errors.HypervisorError("The initrd %s for instance %s is"
380 03abdbe8 Iustin Pop
                                     " missing" % (instance.initrd_path,
381 03abdbe8 Iustin Pop
                                                   instance.name))
382 03abdbe8 Iustin Pop
      initrd_path = instance.initrd_path
383 03abdbe8 Iustin Pop
384 03abdbe8 Iustin Pop
    if initrd_path:
385 03abdbe8 Iustin Pop
      config.write("ramdisk = '%s'\n" % initrd_path)
386 03abdbe8 Iustin Pop
387 03abdbe8 Iustin Pop
    # rest of the settings
388 631eb662 Alexander Schreiber
    config.write("memory = %d\n" % instance.memory)
389 631eb662 Alexander Schreiber
    config.write("vcpus = %d\n" % instance.vcpus)
390 631eb662 Alexander Schreiber
    config.write("name = '%s'\n" % instance.name)
391 631eb662 Alexander Schreiber
392 631eb662 Alexander Schreiber
    vif_data = []
393 631eb662 Alexander Schreiber
    for nic in instance.nics:
394 631eb662 Alexander Schreiber
      nic_str = "mac=%s, bridge=%s" % (nic.mac, nic.bridge)
395 631eb662 Alexander Schreiber
      ip = getattr(nic, "ip", None)
396 631eb662 Alexander Schreiber
      if ip is not None:
397 631eb662 Alexander Schreiber
        nic_str += ", ip=%s" % ip
398 631eb662 Alexander Schreiber
      vif_data.append("'%s'" % nic_str)
399 631eb662 Alexander Schreiber
400 631eb662 Alexander Schreiber
    config.write("vif = [%s]\n" % ",".join(vif_data))
401 e994fcba Manuel Franceschini
    config.write("disk = [%s]\n" % ",".join(
402 e994fcba Manuel Franceschini
                 cls._GetConfigFileDiskData(instance.disk_template,
403 e994fcba Manuel Franceschini
                                            block_devices)))
404 631eb662 Alexander Schreiber
    config.write("root = '/dev/sda ro'\n")
405 631eb662 Alexander Schreiber
    config.write("on_poweroff = 'destroy'\n")
406 631eb662 Alexander Schreiber
    config.write("on_reboot = 'restart'\n")
407 631eb662 Alexander Schreiber
    config.write("on_crash = 'restart'\n")
408 631eb662 Alexander Schreiber
    if extra_args:
409 631eb662 Alexander Schreiber
      config.write("extra = '%s'\n" % extra_args)
410 631eb662 Alexander Schreiber
    # just in case it exists
411 631eb662 Alexander Schreiber
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
412 01121d61 Alexander Schreiber
    try:
413 01121d61 Alexander Schreiber
      f = open("/etc/xen/%s" % instance.name, "w")
414 01121d61 Alexander Schreiber
      try:
415 01121d61 Alexander Schreiber
        f.write(config.getvalue())
416 01121d61 Alexander Schreiber
      finally:
417 01121d61 Alexander Schreiber
        f.close()
418 01121d61 Alexander Schreiber
    except IOError, err:
419 01121d61 Alexander Schreiber
      raise errors.OpExecError("Cannot write Xen instance confile"
420 01121d61 Alexander Schreiber
                               " file /etc/xen/%s: %s" % (instance.name, err))
421 631eb662 Alexander Schreiber
    return True
422 631eb662 Alexander Schreiber
423 631eb662 Alexander Schreiber
  @staticmethod
424 631eb662 Alexander Schreiber
  def GetShellCommandForConsole(instance):
425 631eb662 Alexander Schreiber
    """Return a command for connecting to the console of an instance.
426 631eb662 Alexander Schreiber

427 631eb662 Alexander Schreiber
    """
428 631eb662 Alexander Schreiber
    return "xm console %s" % instance.name
429 631eb662 Alexander Schreiber
430 631eb662 Alexander Schreiber
431 a8083063 Iustin Pop
class FakeHypervisor(BaseHypervisor):
432 a8083063 Iustin Pop
  """Fake hypervisor interface.
433 a8083063 Iustin Pop

434 a8083063 Iustin Pop
  This can be used for testing the ganeti code without having to have
435 a8083063 Iustin Pop
  a real virtualisation software installed.
436 a8083063 Iustin Pop

437 a8083063 Iustin Pop
  """
438 1ed70996 Iustin Pop
  _ROOT_DIR = constants.RUN_DIR + "/ganeti-fake-hypervisor"
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
  def __init__(self):
441 a8083063 Iustin Pop
    BaseHypervisor.__init__(self)
442 a8083063 Iustin Pop
    if not os.path.exists(self._ROOT_DIR):
443 a8083063 Iustin Pop
      os.mkdir(self._ROOT_DIR)
444 a8083063 Iustin Pop
445 a8083063 Iustin Pop
  def ListInstances(self):
446 a8083063 Iustin Pop
    """Get the list of running instances.
447 a8083063 Iustin Pop

448 a8083063 Iustin Pop
    """
449 a8083063 Iustin Pop
    return os.listdir(self._ROOT_DIR)
450 a8083063 Iustin Pop
451 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
452 a8083063 Iustin Pop
    """Get instance properties.
453 a8083063 Iustin Pop

454 a8083063 Iustin Pop
    Args:
455 a8083063 Iustin Pop
      instance_name: the instance name
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
    Returns:
458 a8083063 Iustin Pop
      (name, id, memory, vcpus, stat, times)
459 a8083063 Iustin Pop
    """
460 a8083063 Iustin Pop
    file_name = "%s/%s" % (self._ROOT_DIR, instance_name)
461 a8083063 Iustin Pop
    if not os.path.exists(file_name):
462 a8083063 Iustin Pop
      return None
463 a8083063 Iustin Pop
    try:
464 a8083063 Iustin Pop
      fh = file(file_name, "r")
465 a8083063 Iustin Pop
      try:
466 a8083063 Iustin Pop
        inst_id = fh.readline().strip()
467 a8083063 Iustin Pop
        memory = fh.readline().strip()
468 a8083063 Iustin Pop
        vcpus = fh.readline().strip()
469 a8083063 Iustin Pop
        stat = "---b-"
470 a8083063 Iustin Pop
        times = "0"
471 a8083063 Iustin Pop
        return (instance_name, inst_id, memory, vcpus, stat, times)
472 a8083063 Iustin Pop
      finally:
473 a8083063 Iustin Pop
        fh.close()
474 a8083063 Iustin Pop
    except IOError, err:
475 a8083063 Iustin Pop
      raise HypervisorError("Failed to list instance %s: %s" %
476 a8083063 Iustin Pop
                            (instance_name, err))
477 a8083063 Iustin Pop
478 a8083063 Iustin Pop
  def GetAllInstancesInfo(self):
479 a8083063 Iustin Pop
    """Get properties of all instances.
480 a8083063 Iustin Pop

481 a8083063 Iustin Pop
    Returns:
482 a8083063 Iustin Pop
      [(name, id, memory, vcpus, stat, times),...]
483 a8083063 Iustin Pop
    """
484 a8083063 Iustin Pop
    data = []
485 a8083063 Iustin Pop
    for file_name in os.listdir(self._ROOT_DIR):
486 a8083063 Iustin Pop
      try:
487 a8083063 Iustin Pop
        fh = file(self._ROOT_DIR+"/"+file_name, "r")
488 a8083063 Iustin Pop
        inst_id = "-1"
489 a8083063 Iustin Pop
        memory = "0"
490 a8083063 Iustin Pop
        stat = "-----"
491 a8083063 Iustin Pop
        times = "-1"
492 a8083063 Iustin Pop
        try:
493 a8083063 Iustin Pop
          inst_id = fh.readline().strip()
494 a8083063 Iustin Pop
          memory = fh.readline().strip()
495 a8083063 Iustin Pop
          vcpus = fh.readline().strip()
496 a8083063 Iustin Pop
          stat = "---b-"
497 a8083063 Iustin Pop
          times = "0"
498 a8083063 Iustin Pop
        finally:
499 a8083063 Iustin Pop
          fh.close()
500 a8083063 Iustin Pop
        data.append((file_name, inst_id, memory, vcpus, stat, times))
501 a8083063 Iustin Pop
      except IOError, err:
502 a8083063 Iustin Pop
        raise HypervisorError("Failed to list instances: %s" % err)
503 a8083063 Iustin Pop
    return data
504 a8083063 Iustin Pop
505 a8083063 Iustin Pop
  def StartInstance(self, instance, force, extra_args):
506 a8083063 Iustin Pop
    """Start an instance.
507 a8083063 Iustin Pop

508 a8083063 Iustin Pop
    For the fake hypervisor, it just creates a file in the base dir,
509 a8083063 Iustin Pop
    creating an exception if it already exists. We don't actually
510 a8083063 Iustin Pop
    handle race conditions properly, since these are *FAKE* instances.
511 a8083063 Iustin Pop

512 a8083063 Iustin Pop
    """
513 a8083063 Iustin Pop
    file_name = self._ROOT_DIR + "/%s" % instance.name
514 a8083063 Iustin Pop
    if os.path.exists(file_name):
515 a8083063 Iustin Pop
      raise HypervisorError("Failed to start instance %s: %s" %
516 a8083063 Iustin Pop
                            (instance.name, "already running"))
517 a8083063 Iustin Pop
    try:
518 a8083063 Iustin Pop
      fh = file(file_name, "w")
519 a8083063 Iustin Pop
      try:
520 a8083063 Iustin Pop
        fh.write("0\n%d\n%d\n" % (instance.memory, instance.vcpus))
521 a8083063 Iustin Pop
      finally:
522 a8083063 Iustin Pop
        fh.close()
523 a8083063 Iustin Pop
    except IOError, err:
524 a8083063 Iustin Pop
      raise HypervisorError("Failed to start instance %s: %s" %
525 a8083063 Iustin Pop
                            (instance.name, err))
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
  def StopInstance(self, instance, force=False):
528 a8083063 Iustin Pop
    """Stop an instance.
529 a8083063 Iustin Pop

530 a8083063 Iustin Pop
    For the fake hypervisor, this just removes the file in the base
531 a8083063 Iustin Pop
    dir, if it exist, otherwise we raise an exception.
532 a8083063 Iustin Pop

533 a8083063 Iustin Pop
    """
534 a8083063 Iustin Pop
    file_name = self._ROOT_DIR + "/%s" % instance.name
535 a8083063 Iustin Pop
    if not os.path.exists(file_name):
536 a8083063 Iustin Pop
      raise HypervisorError("Failed to stop instance %s: %s" %
537 a8083063 Iustin Pop
                            (instance.name, "not running"))
538 a8083063 Iustin Pop
    utils.RemoveFile(file_name)
539 a8083063 Iustin Pop
540 7e1394dc Alexander Schreiber
  def RebootInstance(self, instance):
541 7e1394dc Alexander Schreiber
    """Reboot an instance.
542 7e1394dc Alexander Schreiber

543 7e1394dc Alexander Schreiber
    For the fake hypervisor, this does nothing.
544 7e1394dc Alexander Schreiber

545 7e1394dc Alexander Schreiber
    """
546 7e1394dc Alexander Schreiber
    return
547 7e1394dc Alexander Schreiber
548 a8083063 Iustin Pop
  def GetNodeInfo(self):
549 a8083063 Iustin Pop
    """Return information about the node.
550 a8083063 Iustin Pop

551 a8083063 Iustin Pop
    The return value is a dict, which has to have the following items:
552 a8083063 Iustin Pop
      (all values in MiB)
553 a8083063 Iustin Pop
      - memory_total: the total memory size on the node
554 a8083063 Iustin Pop
      - memory_free: the available memory on the node for instances
555 a8083063 Iustin Pop
      - memory_dom0: the memory used by the node itself, if available
556 a8083063 Iustin Pop

557 a8083063 Iustin Pop
    """
558 a8083063 Iustin Pop
    # global ram usage from the xm info command
559 a8083063 Iustin Pop
    # memory                 : 3583
560 a8083063 Iustin Pop
    # free_memory            : 747
561 a8083063 Iustin Pop
    # note: in xen 3, memory has changed to total_memory
562 a8083063 Iustin Pop
    try:
563 a8083063 Iustin Pop
      fh = file("/proc/meminfo")
564 a8083063 Iustin Pop
      try:
565 a8083063 Iustin Pop
        data = fh.readlines()
566 a8083063 Iustin Pop
      finally:
567 a8083063 Iustin Pop
        fh.close()
568 a8083063 Iustin Pop
    except IOError, err:
569 a8083063 Iustin Pop
      raise HypervisorError("Failed to list node info: %s" % err)
570 a8083063 Iustin Pop
571 a8083063 Iustin Pop
    result = {}
572 a8083063 Iustin Pop
    sum_free = 0
573 a8083063 Iustin Pop
    for line in data:
574 a8083063 Iustin Pop
      splitfields = line.split(":", 1)
575 a8083063 Iustin Pop
576 a8083063 Iustin Pop
      if len(splitfields) > 1:
577 a8083063 Iustin Pop
        key = splitfields[0].strip()
578 a8083063 Iustin Pop
        val = splitfields[1].strip()
579 a8083063 Iustin Pop
        if key == 'MemTotal':
580 a8083063 Iustin Pop
          result['memory_total'] = int(val.split()[0])/1024
581 a8083063 Iustin Pop
        elif key in ('MemFree', 'Buffers', 'Cached'):
582 a8083063 Iustin Pop
          sum_free += int(val.split()[0])/1024
583 a8083063 Iustin Pop
        elif key == 'Active':
584 a8083063 Iustin Pop
          result['memory_dom0'] = int(val.split()[0])/1024
585 a8083063 Iustin Pop
586 a8083063 Iustin Pop
    result['memory_free'] = sum_free
587 a8083063 Iustin Pop
    return result
588 a8083063 Iustin Pop
589 a8083063 Iustin Pop
  @staticmethod
590 30989e69 Alexander Schreiber
  def GetShellCommandForConsole(instance):
591 a8083063 Iustin Pop
    """Return a command for connecting to the console of an instance.
592 a8083063 Iustin Pop

593 a8083063 Iustin Pop
    """
594 a8083063 Iustin Pop
    return "echo Console not available for fake hypervisor"
595 a8083063 Iustin Pop
596 a8083063 Iustin Pop
  def Verify(self):
597 a8083063 Iustin Pop
    """Verify the hypervisor.
598 a8083063 Iustin Pop

599 a8083063 Iustin Pop
    For the fake hypervisor, it just checks the existence of the base
600 a8083063 Iustin Pop
    dir.
601 a8083063 Iustin Pop

602 a8083063 Iustin Pop
    """
603 a8083063 Iustin Pop
    if not os.path.exists(self._ROOT_DIR):
604 a8083063 Iustin Pop
      return "The required directory '%s' does not exist." % self._ROOT_DIR
605 2a6469d5 Alexander Schreiber
606 2a6469d5 Alexander Schreiber
607 2a6469d5 Alexander Schreiber
class XenHvmHypervisor(XenHypervisor):
608 2a6469d5 Alexander Schreiber
  """Xen HVM hypervisor interface"""
609 2a6469d5 Alexander Schreiber
610 e994fcba Manuel Franceschini
  @classmethod
611 e994fcba Manuel Franceschini
  def _WriteConfigFile(cls, instance, block_devices, extra_args):
612 2a6469d5 Alexander Schreiber
    """Create a Xen 3.1 HVM config file.
613 2a6469d5 Alexander Schreiber

614 2a6469d5 Alexander Schreiber
    """
615 2a6469d5 Alexander Schreiber
    config = StringIO()
616 2a6469d5 Alexander Schreiber
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
617 2a6469d5 Alexander Schreiber
    config.write("kernel = '/usr/lib/xen/boot/hvmloader'\n")
618 2a6469d5 Alexander Schreiber
    config.write("builder = 'hvm'\n")
619 2a6469d5 Alexander Schreiber
    config.write("memory = %d\n" % instance.memory)
620 2a6469d5 Alexander Schreiber
    config.write("vcpus = %d\n" % instance.vcpus)
621 2a6469d5 Alexander Schreiber
    config.write("name = '%s'\n" % instance.name)
622 2a6469d5 Alexander Schreiber
    config.write("pae = 1\n")
623 2a6469d5 Alexander Schreiber
    config.write("acpi = 1\n")
624 2a6469d5 Alexander Schreiber
    config.write("apic = 1\n")
625 2a6469d5 Alexander Schreiber
    arch = os.uname()[4]
626 2a6469d5 Alexander Schreiber
    if '64' in arch:
627 2a6469d5 Alexander Schreiber
      config.write("device_model = '/usr/lib64/xen/bin/qemu-dm'\n")
628 2a6469d5 Alexander Schreiber
    else:
629 2a6469d5 Alexander Schreiber
      config.write("device_model = '/usr/lib/xen/bin/qemu-dm'\n")
630 25c5878d Alexander Schreiber
    if instance.hvm_boot_order is None:
631 25c5878d Alexander Schreiber
      config.write("boot = '%s'\n" % constants.HT_HVM_DEFAULT_BOOT_ORDER)
632 25c5878d Alexander Schreiber
    else:
633 25c5878d Alexander Schreiber
      config.write("boot = '%s'\n" % instance.hvm_boot_order)
634 2a6469d5 Alexander Schreiber
    config.write("sdl = 0\n")
635 cdeef2ca Alexander Schreiber
    config.write("usb = 1\n");
636 cdeef2ca Alexander Schreiber
    config.write("usbdevice = 'tablet'\n");
637 2a6469d5 Alexander Schreiber
    config.write("vnc = 1\n")
638 2a6469d5 Alexander Schreiber
    config.write("vnclisten = '0.0.0.0'\n")
639 2a6469d5 Alexander Schreiber
640 2a6469d5 Alexander Schreiber
    if instance.network_port > constants.HT_HVM_VNC_BASE_PORT:
641 2a6469d5 Alexander Schreiber
      display = instance.network_port - constants.HT_HVM_VNC_BASE_PORT
642 2a6469d5 Alexander Schreiber
      config.write("vncdisplay = %s\n" % display)
643 2a6469d5 Alexander Schreiber
      config.write("vncunused = 0\n")
644 2a6469d5 Alexander Schreiber
    else:
645 2a6469d5 Alexander Schreiber
      config.write("# vncdisplay = 1\n")
646 2a6469d5 Alexander Schreiber
      config.write("vncunused = 1\n")
647 2a6469d5 Alexander Schreiber
648 2a6469d5 Alexander Schreiber
    try:
649 2a6469d5 Alexander Schreiber
      password_file = open(constants.VNC_PASSWORD_FILE, "r")
650 2a6469d5 Alexander Schreiber
      try:
651 2a6469d5 Alexander Schreiber
        password = password_file.readline()
652 2a6469d5 Alexander Schreiber
      finally:
653 2a6469d5 Alexander Schreiber
        password_file.close()
654 2a6469d5 Alexander Schreiber
    except IOError:
655 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("failed to open VNC password file %s " %
656 2a6469d5 Alexander Schreiber
                               constants.VNC_PASSWORD_FILE)
657 2a6469d5 Alexander Schreiber
658 2a6469d5 Alexander Schreiber
    config.write("vncpasswd = '%s'\n" % password.rstrip())
659 2a6469d5 Alexander Schreiber
660 2a6469d5 Alexander Schreiber
    config.write("serial = 'pty'\n")
661 2a6469d5 Alexander Schreiber
    config.write("localtime = 1\n")
662 2a6469d5 Alexander Schreiber
663 2a6469d5 Alexander Schreiber
    vif_data = []
664 2a6469d5 Alexander Schreiber
    for nic in instance.nics:
665 2a6469d5 Alexander Schreiber
      nic_str = "mac=%s, bridge=%s, type=ioemu" % (nic.mac, nic.bridge)
666 2a6469d5 Alexander Schreiber
      ip = getattr(nic, "ip", None)
667 2a6469d5 Alexander Schreiber
      if ip is not None:
668 2a6469d5 Alexander Schreiber
        nic_str += ", ip=%s" % ip
669 2a6469d5 Alexander Schreiber
      vif_data.append("'%s'" % nic_str)
670 2a6469d5 Alexander Schreiber
671 2a6469d5 Alexander Schreiber
    config.write("vif = [%s]\n" % ",".join(vif_data))
672 2a6469d5 Alexander Schreiber
    iso = "'file:/srv/ganeti/iso/hvm-install.iso,hdc:cdrom,r'"
673 e994fcba Manuel Franceschini
    config.write("disk = [%s, %s]\n" % (",".join(
674 e994fcba Manuel Franceschini
                 cls._GetConfigFileDiskData(instance.disk_template,
675 e994fcba Manuel Franceschini
                                            block_devices)), iso))
676 2a6469d5 Alexander Schreiber
    config.write("on_poweroff = 'destroy'\n")
677 2a6469d5 Alexander Schreiber
    config.write("on_reboot = 'restart'\n")
678 2a6469d5 Alexander Schreiber
    config.write("on_crash = 'restart'\n")
679 2a6469d5 Alexander Schreiber
    if extra_args:
680 2a6469d5 Alexander Schreiber
      config.write("extra = '%s'\n" % extra_args)
681 2a6469d5 Alexander Schreiber
    # just in case it exists
682 2a6469d5 Alexander Schreiber
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
683 2a6469d5 Alexander Schreiber
    try:
684 2a6469d5 Alexander Schreiber
      f = open("/etc/xen/%s" % instance.name, "w")
685 2a6469d5 Alexander Schreiber
      try:
686 2a6469d5 Alexander Schreiber
        f.write(config.getvalue())
687 2a6469d5 Alexander Schreiber
      finally:
688 2a6469d5 Alexander Schreiber
        f.close()
689 2a6469d5 Alexander Schreiber
    except IOError, err:
690 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("Cannot write Xen instance confile"
691 2a6469d5 Alexander Schreiber
                               " file /etc/xen/%s: %s" % (instance.name, err))
692 2a6469d5 Alexander Schreiber
    return True
693 2a6469d5 Alexander Schreiber
694 2a6469d5 Alexander Schreiber
  @staticmethod
695 2a6469d5 Alexander Schreiber
  def GetShellCommandForConsole(instance):
696 2a6469d5 Alexander Schreiber
    """Return a command for connecting to the console of an instance.
697 2a6469d5 Alexander Schreiber

698 2a6469d5 Alexander Schreiber
    """
699 2a6469d5 Alexander Schreiber
    if instance.network_port is None:
700 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("no console port defined for %s"
701 2a6469d5 Alexander Schreiber
                               % instance.name)
702 2a6469d5 Alexander Schreiber
    else:
703 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("no PTY console, connect to %s:%s via VNC"
704 2a6469d5 Alexander Schreiber
                               % (instance.primary_node,
705 2a6469d5 Alexander Schreiber
                                  instance.network_port))