hv_kvm.py 32.8 KB
Newer Older
Guido Trotter's avatar
Guido Trotter committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#
#

# Copyright (C) 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


"""KVM hypervisor

"""

import os
import os.path
import re
import tempfile
30
import time
Guido Trotter's avatar
Guido Trotter committed
31
import logging
32
import pwd
Guido Trotter's avatar
Guido Trotter committed
33 34 35 36 37
from cStringIO import StringIO

from ganeti import utils
from ganeti import constants
from ganeti import errors
38 39
from ganeti import serializer
from ganeti import objects
40 41
from ganeti import uidpool
from ganeti import ssconf
Guido Trotter's avatar
Guido Trotter committed
42 43 44 45
from ganeti.hypervisor import hv_base


class KVMHypervisor(hv_base.BaseHypervisor):
Guido Trotter's avatar
Guido Trotter committed
46
  """KVM hypervisor interface"""
Guido Trotter's avatar
Guido Trotter committed
47 48

  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
Guido Trotter's avatar
Guido Trotter committed
49
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
50
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
Guido Trotter's avatar
Guido Trotter committed
51 52
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
53
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR]
Guido Trotter's avatar
Guido Trotter committed
54

55 56 57 58 59 60 61
  PARAMETERS = {
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
    constants.HV_ACPI: hv_base.NO_CHECK,
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
Michael Hanselmann's avatar
Michael Hanselmann committed
62 63 64 65
    constants.HV_VNC_BIND_ADDRESS:
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
       "the VNC bind address must be either a valid IP address or an absolute"
       " pathname", None, None),
66 67 68
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
69
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
70
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
Michael Hanselmann's avatar
Michael Hanselmann committed
71 72 73 74 75 76 77 78
    constants.HV_BOOT_ORDER:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
    constants.HV_NIC_TYPE:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
    constants.HV_DISK_TYPE:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
    constants.HV_USB_MOUSE:
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
79
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
80
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
81 82
    constants.HV_DISK_CACHE:
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
83 84 85
    constants.HV_SECURITY_MODEL:
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
Guido Trotter's avatar
Guido Trotter committed
86 87
    constants.HV_KVM_FLAG:
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
88
    }
89

Guido Trotter's avatar
Guido Trotter committed
90 91
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
                                    re.M | re.I)
92 93
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
  _MIGRATION_INFO_RETRY_DELAY = 2
Guido Trotter's avatar
Guido Trotter committed
94

95 96
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"

97 98 99 100
  ANCILLARY_FILES = [
    _KVM_NETWORK_SCRIPT,
    ]

Guido Trotter's avatar
Guido Trotter committed
101 102 103 104
  def __init__(self):
    hv_base.BaseHypervisor.__init__(self)
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
    # in a tmpfs filesystem or has been otherwise wiped out.
Iustin Pop's avatar
Iustin Pop committed
105
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
Guido Trotter's avatar
Guido Trotter committed
106
    utils.EnsureDirs(dirs)
Guido Trotter's avatar
Guido Trotter committed
107

108 109
  @classmethod
  def _InstancePidFile(cls, instance_name):
110 111 112
    """Returns the instance pidfile.

    """
113
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
114

115 116 117 118 119 120 121
  @classmethod
  def _InstanceUidFile(cls, instance_name):
    """Returns the instance uidfile.

    """
    return utils.PathJoin(cls._UIDS_DIR, instance_name)

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  @classmethod
  def _InstancePidInfo(cls, pid):
    """Check pid file for instance information.

    Check that a pid file is associated with an instance, and retrieve
    information from its command line.

    @type pid: string or int
    @param pid: process id of the instance to check
    @rtype: tuple
    @return: (instance_name, memory, vcpus)
    @raise errors.HypervisorError: when an instance cannot be found

    """
    alive = utils.IsProcessAlive(pid)
    if not alive:
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)

    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
    try:
      cmdline = utils.ReadFile(cmdline_file)
    except EnvironmentError, err:
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
                                   (pid, err))

    instance = None
    memory = 0
    vcpus = 0

    arg_list = cmdline.split('\x00')
    while arg_list:
      arg =  arg_list.pop(0)
      if arg == "-name":
        instance = arg_list.pop(0)
      elif arg == "-m":
        memory = int(arg_list.pop(0))
      elif arg == "-smp":
        vcpus = int(arg_list.pop(0))

    if instance is None:
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
                                   " instance" % pid)

    return (instance, memory, vcpus)

167
  def _InstancePidAlive(self, instance_name):
168 169 170 171 172 173
    """Returns the instance pidfile, pid, and liveness.

    @type instance_name: string
    @param instance_name: instance name
    @rtype: tuple
    @return: (pid file name, pid, liveness)
174 175

    """
176
    pidfile = self._InstancePidFile(instance_name)
177
    pid = utils.ReadPidFile(pidfile)
178 179 180 181 182 183 184

    alive = False
    try:
      cmd_instance = self._InstancePidInfo(pid)[0]
      alive = (cmd_instance == instance_name)
    except errors.HypervisorError:
      pass
185 186 187

    return (pidfile, pid, alive)

188 189 190 191 192 193 194 195 196
  def _CheckDown(self, instance_name):
    """Raises an error unless the given instance is down.

    """
    alive = self._InstancePidAlive(instance_name)[2]
    if alive:
      raise errors.HypervisorError("Failed to start instance %s: %s" %
                                   (instance_name, "already running"))

197 198
  @classmethod
  def _InstanceMonitor(cls, instance_name):
199 200 201
    """Returns the instance monitor socket name

    """
202
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
203

204 205
  @classmethod
  def _InstanceSerial(cls, instance_name):
206 207 208
    """Returns the instance serial socket name

    """
209
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
210

211 212 213 214 215 216 217
  @staticmethod
  def _SocatUnixConsoleParams():
    """Returns the correct parameters for socat

    If we have a new-enough socat we can use raw mode with an escape character.

    """
218
    if constants.SOCAT_USE_ESCAPE:
219 220 221 222
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
    else:
      return "echo=0,icanon=0"

223 224
  @classmethod
  def _InstanceKVMRuntime(cls, instance_name):
225 226 227
    """Returns the instance KVM runtime filename

    """
228
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
229

230 231 232 233 234 235 236 237
  @classmethod
  def _TryReadUidFile(cls, uid_file):
    """Try to read a uid file

    """
    if os.path.exists(uid_file):
      try:
        uid = int(utils.ReadFile(uid_file))
238
        return uid
239 240 241 242
      except EnvironmentError:
        logging.warning("Can't read uid file", exc_info=True)
      except (TypeError, ValueError):
        logging.warning("Can't parse uid file contents", exc_info=True)
243
    return None
244

245 246 247 248 249 250 251 252 253
  @classmethod
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
    """Removes an instance's rutime sockets/files.

    """
    utils.RemoveFile(pidfile)
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
    utils.RemoveFile(cls._InstanceSerial(instance_name))
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
254 255 256 257 258
    uid_file = cls._InstanceUidFile(instance_name)
    uid = cls._TryReadUidFile(uid_file)
    utils.RemoveFile(uid_file)
    if uid is not None:
      uidpool.ReleaseUid(uid)
259

Guido Trotter's avatar
Guido Trotter committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
  def _WriteNetScript(self, instance, seq, nic):
    """Write a script to connect a net interface to the proper bridge.

    This can be used by any qemu-type hypervisor.

    @param instance: instance we're acting on
    @type instance: instance object
    @param seq: nic sequence number
    @type seq: int
    @param nic: nic we're acting on
    @type nic: nic object
    @return: netscript file name
    @rtype: string

    """
    script = StringIO()
    script.write("#!/bin/sh\n")
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
278
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
Guido Trotter's avatar
Guido Trotter committed
279 280
    script.write("export INSTANCE=%s\n" % instance.name)
    script.write("export MAC=%s\n" % nic.mac)
Guido Trotter's avatar
Guido Trotter committed
281 282 283 284 285 286 287
    if nic.ip:
      script.write("export IP=%s\n" % nic.ip)
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
    if nic.nicparams[constants.NIC_LINK]:
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
Guido Trotter's avatar
Guido Trotter committed
288 289
    script.write("export INTERFACE=$1\n")
    # TODO: make this configurable at ./configure time
290
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
Guido Trotter's avatar
Guido Trotter committed
291
    script.write("  # Execute the user-specific vif file\n")
292
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
Guido Trotter's avatar
Guido Trotter committed
293
    script.write("else\n")
294
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
Guido Trotter's avatar
Guido Trotter committed
295 296
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
      script.write("  # Connect the interface to the bridge\n")
297
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
Guido Trotter's avatar
Guido Trotter committed
298
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
299 300
      if not nic.ip:
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
Guido Trotter's avatar
Guido Trotter committed
301
      script.write("  # Route traffic targeted at the IP to the interface\n")
302
      if nic.nicparams[constants.NIC_LINK]:
303 304 305
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
        script.write("  ip route replace $IP table $LINK proto static"
Guido Trotter's avatar
Guido Trotter committed
306
                     " dev $INTERFACE\n")
307
      else:
308
        script.write("  ip route replace $IP proto static"
309
                     " dev $INTERFACE\n")
310 311 312 313 314 315 316 317 318 319
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
      script.write("  fi\n")
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
      script.write("  fi\n")
Guido Trotter's avatar
Guido Trotter committed
320 321 322 323 324
    script.write("fi\n\n")
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
    # mounted noexec sometimes, so we'll have to find another place.
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
    tmpfile = os.fdopen(tmpfd, 'w')
Michael Hanselmann's avatar
Michael Hanselmann committed
325 326 327 328
    try:
      tmpfile.write(script.getvalue())
    finally:
      tmpfile.close()
Guido Trotter's avatar
Guido Trotter committed
329 330 331 332 333 334
    os.chmod(tmpfile_name, 0755)
    return tmpfile_name

  def ListInstances(self):
    """Get the list of running instances.

Iustin Pop's avatar
Iustin Pop committed
335 336
    We can do this by listing our live instances directory and
    checking whether the associated kvm process is still alive.
Guido Trotter's avatar
Guido Trotter committed
337 338 339 340

    """
    result = []
    for name in os.listdir(self._PIDS_DIR):
341
      if self._InstancePidAlive(name)[2]:
Guido Trotter's avatar
Guido Trotter committed
342 343 344 345 346 347
        result.append(name)
    return result

  def GetInstanceInfo(self, instance_name):
    """Get instance properties.

348
    @type instance_name: string
Iustin Pop's avatar
Iustin Pop committed
349
    @param instance_name: the instance name
350 351
    @rtype: tuple of strings
    @return: (name, id, memory, vcpus, stat, times)
Guido Trotter's avatar
Guido Trotter committed
352 353

    """
354
    _, pid, alive = self._InstancePidAlive(instance_name)
355
    if not alive:
Guido Trotter's avatar
Guido Trotter committed
356 357
      return None

358
    _, memory, vcpus = self._InstancePidInfo(pid)
Guido Trotter's avatar
Guido Trotter committed
359 360 361 362 363 364 365 366
    stat = "---b-"
    times = "0"

    return (instance_name, pid, memory, vcpus, stat, times)

  def GetAllInstancesInfo(self):
    """Get properties of all instances.

Iustin Pop's avatar
Iustin Pop committed
367 368
    @return: list of tuples (name, id, memory, vcpus, stat, times)

Guido Trotter's avatar
Guido Trotter committed
369 370 371
    """
    data = []
    for name in os.listdir(self._PIDS_DIR):
372 373 374 375 376 377
      try:
        info = self.GetInstanceInfo(name)
      except errors.HypervisorError:
        continue
      if info:
        data.append(info)
Guido Trotter's avatar
Guido Trotter committed
378 379
    return data

380
  def _GenerateKVMRuntime(self, instance, block_devices):
381
    """Generate KVM information to start an instance.
Guido Trotter's avatar
Guido Trotter committed
382 383

    """
384
    pidfile  = self._InstancePidFile(instance.name)
Guido Trotter's avatar
Guido Trotter committed
385 386
    kvm = constants.KVM_PATH
    kvm_cmd = [kvm]
387 388
    # used just by the vnc server, if enabled
    kvm_cmd.extend(['-name', instance.name])
389 390
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
Guido Trotter's avatar
Guido Trotter committed
391 392
    kvm_cmd.extend(['-pidfile', pidfile])
    kvm_cmd.extend(['-daemonize'])
393
    if not instance.hvparams[constants.HV_ACPI]:
Guido Trotter's avatar
Guido Trotter committed
394 395
      kvm_cmd.extend(['-no-acpi'])

396
    hvp = instance.hvparams
397 398 399
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
Guido Trotter's avatar
Guido Trotter committed
400

Guido Trotter's avatar
Guido Trotter committed
401 402 403 404 405
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
      kvm_cmd.extend(["-enable-kvm"])
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
      kvm_cmd.extend(["-disable-kvm"])

Guido Trotter's avatar
Guido Trotter committed
406 407
    if boot_network:
      kvm_cmd.extend(['-boot', 'n'])
408

409
    disk_type = hvp[constants.HV_DISK_TYPE]
410 411 412 413
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
      if_val = ',if=virtio'
    else:
      if_val = ',if=%s' % disk_type
414 415 416 417 418 419
    # Cache mode
    disk_cache = hvp[constants.HV_DISK_CACHE]
    if disk_cache != constants.HT_CACHE_DEFAULT:
      cache_val = ",cache=%s" % disk_cache
    else:
      cache_val = ""
420
    for cfdev, dev_path in block_devices:
421 422 423
      if cfdev.mode != constants.DISK_RDWR:
        raise errors.HypervisorError("Instance has read-only disks which"
                                     " are not supported by KVM")
Guido Trotter's avatar
Guido Trotter committed
424
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
425
      if boot_disk:
426
        kvm_cmd.extend(['-boot', 'c'])
Guido Trotter's avatar
Guido Trotter committed
427
        boot_val = ',boot=on'
Guido Trotter's avatar
Guido Trotter committed
428
        # We only boot from the first disk
429
        boot_disk = False
Guido Trotter's avatar
Guido Trotter committed
430 431 432
      else:
        boot_val = ''

433 434
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
                                                cache_val)
Guido Trotter's avatar
Guido Trotter committed
435 436
      kvm_cmd.extend(['-drive', drive_val])

437
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
438
    if iso_image:
439
      options = ',format=raw,media=cdrom'
440
      if boot_cdrom:
441
        kvm_cmd.extend(['-boot', 'd'])
442
        options = '%s,boot=on' % options
443 444
      else:
        options = '%s,if=virtio' % options
445 446 447
      drive_val = 'file=%s%s' % (iso_image, options)
      kvm_cmd.extend(['-drive', drive_val])

448
    kernel_path = hvp[constants.HV_KERNEL_PATH]
449 450
    if kernel_path:
      kvm_cmd.extend(['-kernel', kernel_path])
451
      initrd_path = hvp[constants.HV_INITRD_PATH]
452 453
      if initrd_path:
        kvm_cmd.extend(['-initrd', initrd_path])
454 455 456 457 458
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
                     hvp[constants.HV_KERNEL_ARGS]]
      if hvp[constants.HV_SERIAL_CONSOLE]:
        root_append.append('console=ttyS0,38400')
      kvm_cmd.extend(['-append', ' '.join(root_append)])
Guido Trotter's avatar
Guido Trotter committed
459

460
    mouse_type = hvp[constants.HV_USB_MOUSE]
461 462 463 464
    if mouse_type:
      kvm_cmd.extend(['-usb'])
      kvm_cmd.extend(['-usbdevice', mouse_type])

465
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
466
    if vnc_bind_address:
467
      if utils.IsValidIP(vnc_bind_address):
468 469
        if instance.network_port > constants.VNC_BASE_PORT:
          display = instance.network_port - constants.VNC_BASE_PORT
470 471 472
          if vnc_bind_address == '0.0.0.0':
            vnc_arg = ':%d' % (display)
          else:
473
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
474
        else:
475
          logging.error("Network port is not a valid VNC display (%d < %d)."
Iustin Pop's avatar
Iustin Pop committed
476 477
                        " Not starting VNC", instance.network_port,
                        constants.VNC_BASE_PORT)
478
          vnc_arg = 'none'
479 480 481 482

        # Only allow tls and other option when not binding to a file, for now.
        # kvm/qemu gets confused otherwise about the filename to use.
        vnc_append = ''
483
        if hvp[constants.HV_VNC_TLS]:
484
          vnc_append = '%s,tls' % vnc_append
485
          if hvp[constants.HV_VNC_X509_VERIFY]:
486
            vnc_append = '%s,x509verify=%s' % (vnc_append,
487 488
                                               hvp[constants.HV_VNC_X509])
          elif hvp[constants.HV_VNC_X509]:
489
            vnc_append = '%s,x509=%s' % (vnc_append,
490
                                         hvp[constants.HV_VNC_X509])
491 492 493
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
          vnc_append = '%s,password' % vnc_append

494 495
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)

496
      else:
497 498
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)

499
      kvm_cmd.extend(['-vnc', vnc_arg])
500 501 502 503

      # Also add a tablet USB device to act as a mouse
      # This solves various mouse alignment issues
      kvm_cmd.extend(['-usbdevice', 'tablet'])
504 505 506
    else:
      kvm_cmd.extend(['-nographic'])

507 508
    monitor_dev = ("unix:%s,server,nowait" %
                   self._InstanceMonitor(instance.name))
Guido Trotter's avatar
Guido Trotter committed
509
    kvm_cmd.extend(['-monitor', monitor_dev])
510 511 512
    if hvp[constants.HV_SERIAL_CONSOLE]:
      serial_dev = ('unix:%s,server,nowait' %
                    self._InstanceSerial(instance.name))
513 514 515
      kvm_cmd.extend(['-serial', serial_dev])
    else:
      kvm_cmd.extend(['-serial', 'none'])
Guido Trotter's avatar
Guido Trotter committed
516

517 518 519
    if hvp[constants.HV_USE_LOCALTIME]:
      kvm_cmd.extend(['-localtime'])

520 521 522
    # Save the current instance nics, but defer their expansion as parameters,
    # as we'll need to generate executable temp files for them.
    kvm_nics = instance.nics
523
    hvparams = hvp
524

525
    return (kvm_cmd, kvm_nics, hvparams)
526

527 528 529 530 531 532 533
  def _WriteKVMRuntime(self, instance_name, data):
    """Write an instance's KVM runtime

    """
    try:
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
                      data=data)
534
    except EnvironmentError, err:
535 536 537 538 539 540 541 542
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)

  def _ReadKVMRuntime(self, instance_name):
    """Read an instance's KVM runtime

    """
    try:
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
543
    except EnvironmentError, err:
544 545 546 547 548 549 550
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
    return file_content

  def _SaveKVMRuntime(self, instance, kvm_runtime):
    """Save an instance's KVM runtime

    """
551
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
552
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
553
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
554 555
    self._WriteKVMRuntime(instance.name, serialized_form)

Guido Trotter's avatar
Guido Trotter committed
556
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
557 558 559
    """Load an instance's KVM runtime

    """
Guido Trotter's avatar
Guido Trotter committed
560 561 562
    if not serialized_runtime:
      serialized_runtime = self._ReadKVMRuntime(instance.name)
    loaded_runtime = serializer.Load(serialized_runtime)
563
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
564
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
565
    return (kvm_cmd, kvm_nics, hvparams)
566

567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
  def _RunKVMCmd(self, name, kvm_cmd):
    """Run the KVM cmd and check for errors

    @type name: string
    @param name: instance name
    @type kvm_cmd: list of strings
    @param kvm_cmd: runcmd input for kvm

    """
    result = utils.RunCmd(kvm_cmd)
    if result.failed:
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
                                   (name, result.fail_reason, result.output))
    if not self._InstancePidAlive(name)[2]:
      raise errors.HypervisorError("Failed to start instance %s" % name)

Guido Trotter's avatar
Guido Trotter committed
583
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
584 585
    """Execute a KVM cmd, after completing it with some last minute data

Guido Trotter's avatar
Guido Trotter committed
586 587 588
    @type incoming: tuple of strings
    @param incoming: (target_host_ip, port)

589
    """
590
    hvp = instance.hvparams
591 592
    name = instance.name
    self._CheckDown(name)
593 594 595

    temp_files = []

596
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
597

598 599 600 601
    security_model = hvp[constants.HV_SECURITY_MODEL]
    if security_model == constants.HT_SM_USER:
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])

602 603 604
    if not kvm_nics:
      kvm_cmd.extend(['-net', 'none'])
    else:
605 606 607 608 609 610
      nic_type = hvparams[constants.HV_NIC_TYPE]
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
        nic_model = "model=virtio"
      else:
        nic_model = "model=%s" % nic_type

611
      for nic_seq, nic in enumerate(kvm_nics):
612
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
613 614
        script = self._WriteNetScript(instance, nic_seq, nic)
        kvm_cmd.extend(['-net', nic_val])
615
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
616 617
        temp_files.append(script)

Guido Trotter's avatar
Guido Trotter committed
618 619 620 621
    if incoming:
      target, port = incoming
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])

622 623 624 625 626 627 628 629 630
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
    vnc_pwd = None
    if vnc_pwd_file:
      try:
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
      except EnvironmentError, err:
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
                                     % (vnc_pwd_file, err))

631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
    if security_model == constants.HT_SM_POOL:
      ss = ssconf.SimpleStore()
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
      uid = uidpool.RequestUnusedUid(all_uids)
      try:
        username = pwd.getpwuid(uid.GetUid()).pw_name
        kvm_cmd.extend(["-runas", username])
        self._RunKVMCmd(name, kvm_cmd)
      except:
        uidpool.ReleaseUid(uid)
        raise
      else:
        uid.Unlock()
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
    else:
      self._RunKVMCmd(name, kvm_cmd)
Guido Trotter's avatar
Guido Trotter committed
648

649 650 651 652
    if vnc_pwd:
      change_cmd = 'change vnc password %s' % vnc_pwd
      self._CallMonitorCommand(instance.name, change_cmd)

653 654
    for filename in temp_files:
      utils.RemoveFile(filename)
Guido Trotter's avatar
Guido Trotter committed
655

656
  def StartInstance(self, instance, block_devices):
657 658 659
    """Start an instance.

    """
660
    self._CheckDown(instance.name)
661
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
662
    self._SaveKVMRuntime(instance, kvm_runtime)
663 664
    self._ExecuteKVMRuntime(instance, kvm_runtime)

665 666 667 668 669 670 671 672 673 674 675 676
  def _CallMonitorCommand(self, instance_name, command):
    """Invoke a command on the instance monitor.

    """
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
             (utils.ShellQuote(command),
              constants.SOCAT_PATH,
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
    result = utils.RunCmd(socat)
    if result.failed:
      msg = ("Failed to send command '%s' to instance %s."
             " output: %s, error: %s, fail_reason: %s" %
677 678
             (command, instance_name,
              result.stdout, result.stderr, result.fail_reason))
679 680 681 682
      raise errors.HypervisorError(msg)

    return result

683
  def StopInstance(self, instance, force=False, retry=False, name=None):
Guido Trotter's avatar
Guido Trotter committed
684 685 686
    """Stop an instance.

    """
687 688 689 690 691 692 693
    if name is not None and not force:
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
    if name is None:
      name = instance.name
      acpi = instance.hvparams[constants.HV_ACPI]
    else:
      acpi = False
694
    _, pid, alive = self._InstancePidAlive(name)
695
    if pid > 0 and alive:
696
      if force or not acpi:
Guido Trotter's avatar
Guido Trotter committed
697 698
        utils.KillProcess(pid)
      else:
699
        self._CallMonitorCommand(name, 'system_powerdown')
Guido Trotter's avatar
Guido Trotter committed
700

701 702 703 704 705 706 707 708
  def CleanupInstance(self, instance_name):
    """Cleanup after a stopped instance

    """
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
    if pid > 0 and alive:
      raise errors.HypervisorError("Cannot cleanup a live instance")
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
Guido Trotter's avatar
Guido Trotter committed
709 710 711 712 713 714 715 716

  def RebootInstance(self, instance):
    """Reboot an instance.

    """
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
    # socket the instance will stop, but now power up again. So we'll resort
    # to shutdown and restart.
717
    _, _, alive = self._InstancePidAlive(instance.name)
718
    if not alive:
719 720
      raise errors.HypervisorError("Failed to reboot instance %s:"
                                   " not running" % instance.name)
Guido Trotter's avatar
Guido Trotter committed
721 722 723 724 725 726 727 728 729
    # StopInstance will delete the saved KVM runtime so:
    # ...first load it...
    kvm_runtime = self._LoadKVMRuntime(instance)
    # ...now we can safely call StopInstance...
    if not self.StopInstance(instance):
      self.StopInstance(instance, force=True)
    # ...and finally we can save it again, and execute it...
    self._SaveKVMRuntime(instance, kvm_runtime)
    self._ExecuteKVMRuntime(instance, kvm_runtime)
Guido Trotter's avatar
Guido Trotter committed
730

Guido Trotter's avatar
Guido Trotter committed
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
  def MigrationInfo(self, instance):
    """Get instance information to perform a migration.

    @type instance: L{objects.Instance}
    @param instance: instance to be migrated
    @rtype: string
    @return: content of the KVM runtime file

    """
    return self._ReadKVMRuntime(instance.name)

  def AcceptInstance(self, instance, info, target):
    """Prepare to accept an instance.

    @type instance: L{objects.Instance}
    @param instance: instance to be accepted
    @type info: string
    @param info: content of the KVM runtime file on the source node
    @type target: string
    @param target: target host (usually ip), on this node

    """
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
754
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
Guido Trotter's avatar
Guido Trotter committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)

  def FinalizeMigration(self, instance, info, success):
    """Finalize an instance migration.

    Stop the incoming mode KVM.

    @type instance: L{objects.Instance}
    @param instance: instance whose migration is being aborted

    """
    if success:
      self._WriteKVMRuntime(instance.name, info)
    else:
      self.StopInstance(instance, force=True)

771
  def MigrateInstance(self, instance, target, live):
Guido Trotter's avatar
Guido Trotter committed
772 773 774 775 776
    """Migrate an instance to a target node.

    The migration will not be attempted if the instance is not
    currently running.

777 778
    @type instance: L{objects.Instance}
    @param instance: the instance to be migrated
Guido Trotter's avatar
Guido Trotter committed
779 780 781 782 783 784
    @type target: string
    @param target: ip address of the target node
    @type live: boolean
    @param live: perform a live migration

    """
785
    instance_name = instance.name
786
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
Guido Trotter's avatar
Guido Trotter committed
787 788 789 790
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
    if not alive:
      raise errors.HypervisorError("Instance not running, cannot migrate")

791 792 793 794
    if not utils.TcpPing(target, port, live_port_needed=True):
      raise errors.HypervisorError("Remote host %s not listening on port"
                                   " %s, cannot migrate" % (target, port))

Guido Trotter's avatar
Guido Trotter committed
795 796 797
    if not live:
      self._CallMonitorCommand(instance_name, 'stop')

798
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
Guido Trotter's avatar
Guido Trotter committed
799 800 801 802
    self._CallMonitorCommand(instance_name, migrate_command)

    info_command = 'info migrate'
    done = False
803
    broken_answers = 0
Guido Trotter's avatar
Guido Trotter committed
804 805 806 807
    while not done:
      result = self._CallMonitorCommand(instance_name, info_command)
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
      if not match:
808 809 810 811
        broken_answers += 1
        if not result.stdout:
          logging.info("KVM: empty 'info migrate' result")
        else:
Guido Trotter's avatar
Guido Trotter committed
812
          logging.warning("KVM: unknown 'info migrate' result: %s",
813 814
                          result.stdout)
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
Guido Trotter's avatar
Guido Trotter committed
815 816 817 818 819
      else:
        status = match.group(1)
        if status == 'completed':
          done = True
        elif status == 'active':
820 821 822
          # reset the broken answers count
          broken_answers = 0
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
823 824 825 826 827
        elif status == 'failed' or status == 'cancelled':
          if not live:
            self._CallMonitorCommand(instance_name, 'cont')
          raise errors.HypervisorError("Migration %s at the kvm level" %
                                       status)
Guido Trotter's avatar
Guido Trotter committed
828
        else:
829 830 831 832 833
          logging.warning("KVM: unknown migration status '%s'", status)
          broken_answers += 1
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
Guido Trotter's avatar
Guido Trotter committed
834 835

    utils.KillProcess(pid)
836
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
Guido Trotter's avatar
Guido Trotter committed
837

Guido Trotter's avatar
Guido Trotter committed
838 839 840
  def GetNodeInfo(self):
    """Return information about the node.

841 842
    This is just a wrapper over the base GetLinuxNodeInfo method.

Iustin Pop's avatar
Iustin Pop committed
843 844 845 846
    @return: a dict with the following keys (values in MiB):
          - memory_total: the total memory size on the node
          - memory_free: the available memory on the node for instances
          - memory_dom0: the memory used by the node itself, if available
Guido Trotter's avatar
Guido Trotter committed
847 848

    """
849
    return self.GetLinuxNodeInfo()
Guido Trotter's avatar
Guido Trotter committed
850

851
  @classmethod
852
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
Guido Trotter's avatar
Guido Trotter committed
853 854 855
    """Return a command for connecting to the console of an instance.

    """
856
    if hvparams[constants.HV_SERIAL_CONSOLE]:
857 858
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
859 860 861
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
    else:
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
862 863 864

    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
    if vnc_bind_address:
865 866
      if instance.network_port > constants.VNC_BASE_PORT:
        display = instance.network_port - constants.VNC_BASE_PORT
867 868 869 870 871 872
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
                       " (display: %d)'" % (vnc_bind_address,
                                            instance.network_port,
                                            display))
        shell_command = "%s; %s" % (vnc_command, shell_command)

873
    return shell_command
Guido Trotter's avatar
Guido Trotter committed
874 875 876 877 878 879 880 881 882

  def Verify(self):
    """Verify the hypervisor.

    Check that the binary exists.

    """
    if not os.path.exists(constants.KVM_PATH):
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
Guido Trotter's avatar
Guido Trotter committed
883 884 885
    if not os.path.exists(constants.SOCAT_PATH):
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH

886 887 888 889 890 891 892 893 894 895

  @classmethod
  def CheckParameterSyntax(cls, hvparams):
    """Check the given parameters for validity.

    @type hvparams:  dict
    @param hvparams: dictionary with parameter names/value
    @raise errors.HypervisorError: when a parameter is not valid

    """
896
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
897

898 899 900
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
    if kernel_path:
      if not hvparams[constants.HV_ROOT_PATH]:
901 902
        raise errors.HypervisorError("Need a root partition for the instance,"
                                     " if a kernel is defined")
903

904 905 906 907 908
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
        not hvparams[constants.HV_VNC_X509]):
      raise errors.HypervisorError("%s must be defined, if %s is" %
                                   (constants.HV_VNC_X509,
                                    constants.HV_VNC_X509_VERIFY))
909 910

    boot_order = hvparams[constants.HV_BOOT_ORDER]
911 912
    if (boot_order == constants.HT_BO_CDROM and
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
913 914
      raise errors.HypervisorError("Cannot boot from cdrom without an"
                                   " ISO path")
Iustin Pop's avatar
Iustin Pop committed
915

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
    security_model = hvparams[constants.HV_SECURITY_MODEL]
    if security_model == constants.HT_SM_USER:
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
        raise errors.HypervisorError("A security domain (user to run kvm as)"
                                     " must be specified")
    elif (security_model == constants.HT_SM_NONE or
          security_model == constants.HT_SM_POOL):
      if hvparams[constants.HV_SECURITY_DOMAIN]:
        raise errors.HypervisorError("Cannot have a security domain when the"
                                     " security model is 'none' or 'pool'")

  @classmethod
  def ValidateParameters(cls, hvparams):
    """Check the given parameters for validity.

    @type hvparams:  dict
    @param hvparams: dictionary with parameter names/value
    @raise errors.HypervisorError: when a parameter is not valid

    """
    super(KVMHypervisor, cls).ValidateParameters(hvparams)

    security_model = hvparams[constants.HV_SECURITY_MODEL]
    if security_model == constants.HT_SM_USER:
      username = hvparams[constants.HV_SECURITY_DOMAIN]
      try:
Guido Trotter's avatar
Guido Trotter committed
942
        pwd.getpwnam(username)
943 944 945 946
      except KeyError:
        raise errors.HypervisorError("Unknown security domain user %s"
                                     % username)

Iustin Pop's avatar
Iustin Pop committed
947 948 949 950 951 952
  @classmethod
  def PowercycleNode(cls):
    """KVM powercycle, just a wrapper over Linux powercycle.

    """
    cls.LinuxPowercycle()