Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor.py @ d6646186

History | View | Annotate | Download (20.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 a8083063 Iustin Pop
    # skip over the heading and the domain 0 line (optional)
174 a8083063 Iustin Pop
    if include_node:
175 a8083063 Iustin Pop
      to_skip = 1
176 a8083063 Iustin Pop
    else:
177 a8083063 Iustin Pop
      to_skip = 2
178 a8083063 Iustin Pop
    lines = result.stdout.splitlines()[to_skip:]
179 a8083063 Iustin Pop
    result = []
180 a8083063 Iustin Pop
    for line in lines:
181 a8083063 Iustin Pop
      # The format of lines is:
182 a8083063 Iustin Pop
      # Name      ID Mem(MiB) VCPUs State  Time(s)
183 a8083063 Iustin Pop
      # Domain-0   0  3418     4 r-----    266.2
184 a8083063 Iustin Pop
      data = line.split()
185 a8083063 Iustin Pop
      if len(data) != 6:
186 a8083063 Iustin Pop
        raise HypervisorError("Can't parse output of xm list, line: %s" % line)
187 a8083063 Iustin Pop
      try:
188 a8083063 Iustin Pop
        data[1] = int(data[1])
189 a8083063 Iustin Pop
        data[2] = int(data[2])
190 a8083063 Iustin Pop
        data[3] = int(data[3])
191 a8083063 Iustin Pop
        data[5] = float(data[5])
192 a8083063 Iustin Pop
      except ValueError, err:
193 a8083063 Iustin Pop
        raise HypervisorError("Can't parse output of xm list,"
194 a8083063 Iustin Pop
                              " line: %s, error: %s" % (line, err))
195 a8083063 Iustin Pop
      result.append(data)
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 a8083063 Iustin Pop
314 631eb662 Alexander Schreiber
class XenPvmHypervisor(XenHypervisor):
315 631eb662 Alexander Schreiber
  """Xen PVM hypervisor interface"""
316 631eb662 Alexander Schreiber
317 631eb662 Alexander Schreiber
  @staticmethod
318 631eb662 Alexander Schreiber
  def _WriteConfigFile(instance, block_devices, extra_args):
319 03abdbe8 Iustin Pop
    """Write the Xen config file for the instance.
320 631eb662 Alexander Schreiber

321 631eb662 Alexander Schreiber
    """
322 631eb662 Alexander Schreiber
    config = StringIO()
323 631eb662 Alexander Schreiber
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
324 03abdbe8 Iustin Pop
325 03abdbe8 Iustin Pop
    # kernel handling
326 03abdbe8 Iustin Pop
    if instance.kernel_path in (None, constants.VALUE_DEFAULT):
327 03abdbe8 Iustin Pop
      kpath = constants.XEN_KERNEL
328 03abdbe8 Iustin Pop
    else:
329 03abdbe8 Iustin Pop
      if not os.path.exists(instance.kernel_path):
330 03abdbe8 Iustin Pop
        raise errors.HypervisorError("The kernel %s for instance %s is"
331 03abdbe8 Iustin Pop
                                     " missing" % (instance.kernel_path,
332 03abdbe8 Iustin Pop
                                                   instance.name))
333 03abdbe8 Iustin Pop
      kpath = instance.kernel_path
334 03abdbe8 Iustin Pop
    config.write("kernel = '%s'\n" % kpath)
335 03abdbe8 Iustin Pop
336 03abdbe8 Iustin Pop
    # initrd handling
337 03abdbe8 Iustin Pop
    if instance.initrd_path in (None, constants.VALUE_DEFAULT):
338 03abdbe8 Iustin Pop
      if os.path.exists(constants.XEN_INITRD):
339 03abdbe8 Iustin Pop
        initrd_path = constants.XEN_INITRD
340 03abdbe8 Iustin Pop
      else:
341 03abdbe8 Iustin Pop
        initrd_path = None
342 03abdbe8 Iustin Pop
    elif instance.initrd_path == constants.VALUE_NONE:
343 03abdbe8 Iustin Pop
      initrd_path = None
344 03abdbe8 Iustin Pop
    else:
345 03abdbe8 Iustin Pop
      if not os.path.exists(instance.initrd_path):
346 03abdbe8 Iustin Pop
        raise errors.HypervisorError("The initrd %s for instance %s is"
347 03abdbe8 Iustin Pop
                                     " missing" % (instance.initrd_path,
348 03abdbe8 Iustin Pop
                                                   instance.name))
349 03abdbe8 Iustin Pop
      initrd_path = instance.initrd_path
350 03abdbe8 Iustin Pop
351 03abdbe8 Iustin Pop
    if initrd_path:
352 03abdbe8 Iustin Pop
      config.write("ramdisk = '%s'\n" % initrd_path)
353 03abdbe8 Iustin Pop
354 03abdbe8 Iustin Pop
    # rest of the settings
355 631eb662 Alexander Schreiber
    config.write("memory = %d\n" % instance.memory)
356 631eb662 Alexander Schreiber
    config.write("vcpus = %d\n" % instance.vcpus)
357 631eb662 Alexander Schreiber
    config.write("name = '%s'\n" % instance.name)
358 631eb662 Alexander Schreiber
359 631eb662 Alexander Schreiber
    vif_data = []
360 631eb662 Alexander Schreiber
    for nic in instance.nics:
361 631eb662 Alexander Schreiber
      nic_str = "mac=%s, bridge=%s" % (nic.mac, nic.bridge)
362 631eb662 Alexander Schreiber
      ip = getattr(nic, "ip", None)
363 631eb662 Alexander Schreiber
      if ip is not None:
364 631eb662 Alexander Schreiber
        nic_str += ", ip=%s" % ip
365 631eb662 Alexander Schreiber
      vif_data.append("'%s'" % nic_str)
366 631eb662 Alexander Schreiber
367 631eb662 Alexander Schreiber
    config.write("vif = [%s]\n" % ",".join(vif_data))
368 631eb662 Alexander Schreiber
369 631eb662 Alexander Schreiber
    disk_data = ["'phy:%s,%s,w'" % (rldev.dev_path, cfdev.iv_name)
370 631eb662 Alexander Schreiber
                 for cfdev, rldev in block_devices]
371 631eb662 Alexander Schreiber
    config.write("disk = [%s]\n" % ",".join(disk_data))
372 631eb662 Alexander Schreiber
373 631eb662 Alexander Schreiber
    config.write("root = '/dev/sda ro'\n")
374 631eb662 Alexander Schreiber
    config.write("on_poweroff = 'destroy'\n")
375 631eb662 Alexander Schreiber
    config.write("on_reboot = 'restart'\n")
376 631eb662 Alexander Schreiber
    config.write("on_crash = 'restart'\n")
377 631eb662 Alexander Schreiber
    if extra_args:
378 631eb662 Alexander Schreiber
      config.write("extra = '%s'\n" % extra_args)
379 631eb662 Alexander Schreiber
    # just in case it exists
380 631eb662 Alexander Schreiber
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
381 01121d61 Alexander Schreiber
    try:
382 01121d61 Alexander Schreiber
      f = open("/etc/xen/%s" % instance.name, "w")
383 01121d61 Alexander Schreiber
      try:
384 01121d61 Alexander Schreiber
        f.write(config.getvalue())
385 01121d61 Alexander Schreiber
      finally:
386 01121d61 Alexander Schreiber
        f.close()
387 01121d61 Alexander Schreiber
    except IOError, err:
388 01121d61 Alexander Schreiber
      raise errors.OpExecError("Cannot write Xen instance confile"
389 01121d61 Alexander Schreiber
                               " file /etc/xen/%s: %s" % (instance.name, err))
390 631eb662 Alexander Schreiber
    return True
391 631eb662 Alexander Schreiber
392 631eb662 Alexander Schreiber
  @staticmethod
393 631eb662 Alexander Schreiber
  def GetShellCommandForConsole(instance):
394 631eb662 Alexander Schreiber
    """Return a command for connecting to the console of an instance.
395 631eb662 Alexander Schreiber

396 631eb662 Alexander Schreiber
    """
397 631eb662 Alexander Schreiber
    return "xm console %s" % instance.name
398 631eb662 Alexander Schreiber
399 631eb662 Alexander Schreiber
400 a8083063 Iustin Pop
class FakeHypervisor(BaseHypervisor):
401 a8083063 Iustin Pop
  """Fake hypervisor interface.
402 a8083063 Iustin Pop

403 a8083063 Iustin Pop
  This can be used for testing the ganeti code without having to have
404 a8083063 Iustin Pop
  a real virtualisation software installed.
405 a8083063 Iustin Pop

406 a8083063 Iustin Pop
  """
407 1ed70996 Iustin Pop
  _ROOT_DIR = constants.RUN_DIR + "/ganeti-fake-hypervisor"
408 a8083063 Iustin Pop
409 a8083063 Iustin Pop
  def __init__(self):
410 a8083063 Iustin Pop
    BaseHypervisor.__init__(self)
411 a8083063 Iustin Pop
    if not os.path.exists(self._ROOT_DIR):
412 a8083063 Iustin Pop
      os.mkdir(self._ROOT_DIR)
413 a8083063 Iustin Pop
414 a8083063 Iustin Pop
  def ListInstances(self):
415 a8083063 Iustin Pop
    """Get the list of running instances.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
    """
418 a8083063 Iustin Pop
    return os.listdir(self._ROOT_DIR)
419 a8083063 Iustin Pop
420 a8083063 Iustin Pop
  def GetInstanceInfo(self, instance_name):
421 a8083063 Iustin Pop
    """Get instance properties.
422 a8083063 Iustin Pop

423 a8083063 Iustin Pop
    Args:
424 a8083063 Iustin Pop
      instance_name: the instance name
425 a8083063 Iustin Pop

426 a8083063 Iustin Pop
    Returns:
427 a8083063 Iustin Pop
      (name, id, memory, vcpus, stat, times)
428 a8083063 Iustin Pop
    """
429 a8083063 Iustin Pop
    file_name = "%s/%s" % (self._ROOT_DIR, instance_name)
430 a8083063 Iustin Pop
    if not os.path.exists(file_name):
431 a8083063 Iustin Pop
      return None
432 a8083063 Iustin Pop
    try:
433 a8083063 Iustin Pop
      fh = file(file_name, "r")
434 a8083063 Iustin Pop
      try:
435 a8083063 Iustin Pop
        inst_id = fh.readline().strip()
436 a8083063 Iustin Pop
        memory = fh.readline().strip()
437 a8083063 Iustin Pop
        vcpus = fh.readline().strip()
438 a8083063 Iustin Pop
        stat = "---b-"
439 a8083063 Iustin Pop
        times = "0"
440 a8083063 Iustin Pop
        return (instance_name, inst_id, memory, vcpus, stat, times)
441 a8083063 Iustin Pop
      finally:
442 a8083063 Iustin Pop
        fh.close()
443 a8083063 Iustin Pop
    except IOError, err:
444 a8083063 Iustin Pop
      raise HypervisorError("Failed to list instance %s: %s" %
445 a8083063 Iustin Pop
                            (instance_name, err))
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
  def GetAllInstancesInfo(self):
448 a8083063 Iustin Pop
    """Get properties of all instances.
449 a8083063 Iustin Pop

450 a8083063 Iustin Pop
    Returns:
451 a8083063 Iustin Pop
      [(name, id, memory, vcpus, stat, times),...]
452 a8083063 Iustin Pop
    """
453 a8083063 Iustin Pop
    data = []
454 a8083063 Iustin Pop
    for file_name in os.listdir(self._ROOT_DIR):
455 a8083063 Iustin Pop
      try:
456 a8083063 Iustin Pop
        fh = file(self._ROOT_DIR+"/"+file_name, "r")
457 a8083063 Iustin Pop
        inst_id = "-1"
458 a8083063 Iustin Pop
        memory = "0"
459 a8083063 Iustin Pop
        stat = "-----"
460 a8083063 Iustin Pop
        times = "-1"
461 a8083063 Iustin Pop
        try:
462 a8083063 Iustin Pop
          inst_id = fh.readline().strip()
463 a8083063 Iustin Pop
          memory = fh.readline().strip()
464 a8083063 Iustin Pop
          vcpus = fh.readline().strip()
465 a8083063 Iustin Pop
          stat = "---b-"
466 a8083063 Iustin Pop
          times = "0"
467 a8083063 Iustin Pop
        finally:
468 a8083063 Iustin Pop
          fh.close()
469 a8083063 Iustin Pop
        data.append((file_name, inst_id, memory, vcpus, stat, times))
470 a8083063 Iustin Pop
      except IOError, err:
471 a8083063 Iustin Pop
        raise HypervisorError("Failed to list instances: %s" % err)
472 a8083063 Iustin Pop
    return data
473 a8083063 Iustin Pop
474 a8083063 Iustin Pop
  def StartInstance(self, instance, force, extra_args):
475 a8083063 Iustin Pop
    """Start an instance.
476 a8083063 Iustin Pop

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

481 a8083063 Iustin Pop
    """
482 a8083063 Iustin Pop
    file_name = self._ROOT_DIR + "/%s" % instance.name
483 a8083063 Iustin Pop
    if os.path.exists(file_name):
484 a8083063 Iustin Pop
      raise HypervisorError("Failed to start instance %s: %s" %
485 a8083063 Iustin Pop
                            (instance.name, "already running"))
486 a8083063 Iustin Pop
    try:
487 a8083063 Iustin Pop
      fh = file(file_name, "w")
488 a8083063 Iustin Pop
      try:
489 a8083063 Iustin Pop
        fh.write("0\n%d\n%d\n" % (instance.memory, instance.vcpus))
490 a8083063 Iustin Pop
      finally:
491 a8083063 Iustin Pop
        fh.close()
492 a8083063 Iustin Pop
    except IOError, err:
493 a8083063 Iustin Pop
      raise HypervisorError("Failed to start instance %s: %s" %
494 a8083063 Iustin Pop
                            (instance.name, err))
495 a8083063 Iustin Pop
496 a8083063 Iustin Pop
  def StopInstance(self, instance, force=False):
497 a8083063 Iustin Pop
    """Stop an instance.
498 a8083063 Iustin Pop

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

502 a8083063 Iustin Pop
    """
503 a8083063 Iustin Pop
    file_name = self._ROOT_DIR + "/%s" % instance.name
504 a8083063 Iustin Pop
    if not os.path.exists(file_name):
505 a8083063 Iustin Pop
      raise HypervisorError("Failed to stop instance %s: %s" %
506 a8083063 Iustin Pop
                            (instance.name, "not running"))
507 a8083063 Iustin Pop
    utils.RemoveFile(file_name)
508 a8083063 Iustin Pop
509 7e1394dc Alexander Schreiber
  def RebootInstance(self, instance):
510 7e1394dc Alexander Schreiber
    """Reboot an instance.
511 7e1394dc Alexander Schreiber

512 7e1394dc Alexander Schreiber
    For the fake hypervisor, this does nothing.
513 7e1394dc Alexander Schreiber

514 7e1394dc Alexander Schreiber
    """
515 7e1394dc Alexander Schreiber
    return
516 7e1394dc Alexander Schreiber
517 a8083063 Iustin Pop
  def GetNodeInfo(self):
518 a8083063 Iustin Pop
    """Return information about the node.
519 a8083063 Iustin Pop

520 a8083063 Iustin Pop
    The return value is a dict, which has to have the following items:
521 a8083063 Iustin Pop
      (all values in MiB)
522 a8083063 Iustin Pop
      - memory_total: the total memory size on the node
523 a8083063 Iustin Pop
      - memory_free: the available memory on the node for instances
524 a8083063 Iustin Pop
      - memory_dom0: the memory used by the node itself, if available
525 a8083063 Iustin Pop

526 a8083063 Iustin Pop
    """
527 a8083063 Iustin Pop
    # global ram usage from the xm info command
528 a8083063 Iustin Pop
    # memory                 : 3583
529 a8083063 Iustin Pop
    # free_memory            : 747
530 a8083063 Iustin Pop
    # note: in xen 3, memory has changed to total_memory
531 a8083063 Iustin Pop
    try:
532 a8083063 Iustin Pop
      fh = file("/proc/meminfo")
533 a8083063 Iustin Pop
      try:
534 a8083063 Iustin Pop
        data = fh.readlines()
535 a8083063 Iustin Pop
      finally:
536 a8083063 Iustin Pop
        fh.close()
537 a8083063 Iustin Pop
    except IOError, err:
538 a8083063 Iustin Pop
      raise HypervisorError("Failed to list node info: %s" % err)
539 a8083063 Iustin Pop
540 a8083063 Iustin Pop
    result = {}
541 a8083063 Iustin Pop
    sum_free = 0
542 a8083063 Iustin Pop
    for line in data:
543 a8083063 Iustin Pop
      splitfields = line.split(":", 1)
544 a8083063 Iustin Pop
545 a8083063 Iustin Pop
      if len(splitfields) > 1:
546 a8083063 Iustin Pop
        key = splitfields[0].strip()
547 a8083063 Iustin Pop
        val = splitfields[1].strip()
548 a8083063 Iustin Pop
        if key == 'MemTotal':
549 a8083063 Iustin Pop
          result['memory_total'] = int(val.split()[0])/1024
550 a8083063 Iustin Pop
        elif key in ('MemFree', 'Buffers', 'Cached'):
551 a8083063 Iustin Pop
          sum_free += int(val.split()[0])/1024
552 a8083063 Iustin Pop
        elif key == 'Active':
553 a8083063 Iustin Pop
          result['memory_dom0'] = int(val.split()[0])/1024
554 a8083063 Iustin Pop
555 a8083063 Iustin Pop
    result['memory_free'] = sum_free
556 a8083063 Iustin Pop
    return result
557 a8083063 Iustin Pop
558 a8083063 Iustin Pop
  @staticmethod
559 30989e69 Alexander Schreiber
  def GetShellCommandForConsole(instance):
560 a8083063 Iustin Pop
    """Return a command for connecting to the console of an instance.
561 a8083063 Iustin Pop

562 a8083063 Iustin Pop
    """
563 a8083063 Iustin Pop
    return "echo Console not available for fake hypervisor"
564 a8083063 Iustin Pop
565 a8083063 Iustin Pop
  def Verify(self):
566 a8083063 Iustin Pop
    """Verify the hypervisor.
567 a8083063 Iustin Pop

568 a8083063 Iustin Pop
    For the fake hypervisor, it just checks the existence of the base
569 a8083063 Iustin Pop
    dir.
570 a8083063 Iustin Pop

571 a8083063 Iustin Pop
    """
572 a8083063 Iustin Pop
    if not os.path.exists(self._ROOT_DIR):
573 a8083063 Iustin Pop
      return "The required directory '%s' does not exist." % self._ROOT_DIR
574 2a6469d5 Alexander Schreiber
575 2a6469d5 Alexander Schreiber
576 2a6469d5 Alexander Schreiber
class XenHvmHypervisor(XenHypervisor):
577 2a6469d5 Alexander Schreiber
  """Xen HVM hypervisor interface"""
578 2a6469d5 Alexander Schreiber
579 2a6469d5 Alexander Schreiber
  @staticmethod
580 2a6469d5 Alexander Schreiber
  def _WriteConfigFile(instance, block_devices, extra_args):
581 2a6469d5 Alexander Schreiber
    """Create a Xen 3.1 HVM config file.
582 2a6469d5 Alexander Schreiber

583 2a6469d5 Alexander Schreiber
    """
584 2a6469d5 Alexander Schreiber
    config = StringIO()
585 2a6469d5 Alexander Schreiber
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
586 2a6469d5 Alexander Schreiber
    config.write("kernel = '/usr/lib/xen/boot/hvmloader'\n")
587 2a6469d5 Alexander Schreiber
    config.write("builder = 'hvm'\n")
588 2a6469d5 Alexander Schreiber
    config.write("memory = %d\n" % instance.memory)
589 2a6469d5 Alexander Schreiber
    config.write("vcpus = %d\n" % instance.vcpus)
590 2a6469d5 Alexander Schreiber
    config.write("name = '%s'\n" % instance.name)
591 2a6469d5 Alexander Schreiber
    config.write("pae = 1\n")
592 2a6469d5 Alexander Schreiber
    config.write("acpi = 1\n")
593 2a6469d5 Alexander Schreiber
    config.write("apic = 1\n")
594 2a6469d5 Alexander Schreiber
    arch = os.uname()[4]
595 2a6469d5 Alexander Schreiber
    if '64' in arch:
596 2a6469d5 Alexander Schreiber
      config.write("device_model = '/usr/lib64/xen/bin/qemu-dm'\n")
597 2a6469d5 Alexander Schreiber
    else:
598 2a6469d5 Alexander Schreiber
      config.write("device_model = '/usr/lib/xen/bin/qemu-dm'\n")
599 25c5878d Alexander Schreiber
    if instance.hvm_boot_order is None:
600 25c5878d Alexander Schreiber
      config.write("boot = '%s'\n" % constants.HT_HVM_DEFAULT_BOOT_ORDER)
601 25c5878d Alexander Schreiber
    else:
602 25c5878d Alexander Schreiber
      config.write("boot = '%s'\n" % instance.hvm_boot_order)
603 2a6469d5 Alexander Schreiber
    config.write("sdl = 0\n")
604 cdeef2ca Alexander Schreiber
    config.write("usb = 1\n");
605 cdeef2ca Alexander Schreiber
    config.write("usbdevice = 'tablet'\n");
606 2a6469d5 Alexander Schreiber
    config.write("vnc = 1\n")
607 2a6469d5 Alexander Schreiber
    config.write("vnclisten = '0.0.0.0'\n")
608 2a6469d5 Alexander Schreiber
609 2a6469d5 Alexander Schreiber
    if instance.network_port > constants.HT_HVM_VNC_BASE_PORT:
610 2a6469d5 Alexander Schreiber
      display = instance.network_port - constants.HT_HVM_VNC_BASE_PORT
611 2a6469d5 Alexander Schreiber
      config.write("vncdisplay = %s\n" % display)
612 2a6469d5 Alexander Schreiber
      config.write("vncunused = 0\n")
613 2a6469d5 Alexander Schreiber
    else:
614 2a6469d5 Alexander Schreiber
      config.write("# vncdisplay = 1\n")
615 2a6469d5 Alexander Schreiber
      config.write("vncunused = 1\n")
616 2a6469d5 Alexander Schreiber
617 2a6469d5 Alexander Schreiber
    try:
618 2a6469d5 Alexander Schreiber
      password_file = open(constants.VNC_PASSWORD_FILE, "r")
619 2a6469d5 Alexander Schreiber
      try:
620 2a6469d5 Alexander Schreiber
        password = password_file.readline()
621 2a6469d5 Alexander Schreiber
      finally:
622 2a6469d5 Alexander Schreiber
        password_file.close()
623 2a6469d5 Alexander Schreiber
    except IOError:
624 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("failed to open VNC password file %s " %
625 2a6469d5 Alexander Schreiber
                               constants.VNC_PASSWORD_FILE)
626 2a6469d5 Alexander Schreiber
627 2a6469d5 Alexander Schreiber
    config.write("vncpasswd = '%s'\n" % password.rstrip())
628 2a6469d5 Alexander Schreiber
629 2a6469d5 Alexander Schreiber
    config.write("serial = 'pty'\n")
630 2a6469d5 Alexander Schreiber
    config.write("localtime = 1\n")
631 2a6469d5 Alexander Schreiber
632 2a6469d5 Alexander Schreiber
    vif_data = []
633 2a6469d5 Alexander Schreiber
    for nic in instance.nics:
634 2a6469d5 Alexander Schreiber
      nic_str = "mac=%s, bridge=%s, type=ioemu" % (nic.mac, nic.bridge)
635 2a6469d5 Alexander Schreiber
      ip = getattr(nic, "ip", None)
636 2a6469d5 Alexander Schreiber
      if ip is not None:
637 2a6469d5 Alexander Schreiber
        nic_str += ", ip=%s" % ip
638 2a6469d5 Alexander Schreiber
      vif_data.append("'%s'" % nic_str)
639 2a6469d5 Alexander Schreiber
640 2a6469d5 Alexander Schreiber
    config.write("vif = [%s]\n" % ",".join(vif_data))
641 2a6469d5 Alexander Schreiber
642 2a6469d5 Alexander Schreiber
    disk_data = ["'phy:%s,%s,w'" %
643 2a6469d5 Alexander Schreiber
                 (rldev.dev_path, cfdev.iv_name.replace("sd", "ioemu:hd"))
644 2a6469d5 Alexander Schreiber
                 for cfdev, rldev in block_devices]
645 2a6469d5 Alexander Schreiber
    iso = "'file:/srv/ganeti/iso/hvm-install.iso,hdc:cdrom,r'"
646 2a6469d5 Alexander Schreiber
    config.write("disk = [%s, %s]\n" % (",".join(disk_data), iso) )
647 2a6469d5 Alexander Schreiber
648 2a6469d5 Alexander Schreiber
    config.write("on_poweroff = 'destroy'\n")
649 2a6469d5 Alexander Schreiber
    config.write("on_reboot = 'restart'\n")
650 2a6469d5 Alexander Schreiber
    config.write("on_crash = 'restart'\n")
651 2a6469d5 Alexander Schreiber
    if extra_args:
652 2a6469d5 Alexander Schreiber
      config.write("extra = '%s'\n" % extra_args)
653 2a6469d5 Alexander Schreiber
    # just in case it exists
654 2a6469d5 Alexander Schreiber
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
655 2a6469d5 Alexander Schreiber
    try:
656 2a6469d5 Alexander Schreiber
      f = open("/etc/xen/%s" % instance.name, "w")
657 2a6469d5 Alexander Schreiber
      try:
658 2a6469d5 Alexander Schreiber
        f.write(config.getvalue())
659 2a6469d5 Alexander Schreiber
      finally:
660 2a6469d5 Alexander Schreiber
        f.close()
661 2a6469d5 Alexander Schreiber
    except IOError, err:
662 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("Cannot write Xen instance confile"
663 2a6469d5 Alexander Schreiber
                               " file /etc/xen/%s: %s" % (instance.name, err))
664 2a6469d5 Alexander Schreiber
    return True
665 2a6469d5 Alexander Schreiber
666 2a6469d5 Alexander Schreiber
  @staticmethod
667 2a6469d5 Alexander Schreiber
  def GetShellCommandForConsole(instance):
668 2a6469d5 Alexander Schreiber
    """Return a command for connecting to the console of an instance.
669 2a6469d5 Alexander Schreiber

670 2a6469d5 Alexander Schreiber
    """
671 2a6469d5 Alexander Schreiber
    if instance.network_port is None:
672 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("no console port defined for %s"
673 2a6469d5 Alexander Schreiber
                               % instance.name)
674 2a6469d5 Alexander Schreiber
    else:
675 2a6469d5 Alexander Schreiber
      raise errors.OpExecError("no PTY console, connect to %s:%s via VNC"
676 2a6469d5 Alexander Schreiber
                               % (instance.primary_node,
677 2a6469d5 Alexander Schreiber
                                  instance.network_port))