Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 839642c2

History | View | Annotate | Download (50.2 kB)

1
#
2
#
3

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

    
21

    
22
"""KVM hypervisor
23

24
"""
25

    
26
import errno
27
import os
28
import os.path
29
import re
30
import tempfile
31
import time
32
import logging
33
import pwd
34
import struct
35
import fcntl
36
import shutil
37

    
38
from ganeti import utils
39
from ganeti import constants
40
from ganeti import errors
41
from ganeti import serializer
42
from ganeti import objects
43
from ganeti import uidpool
44
from ganeti import ssconf
45
from ganeti.hypervisor import hv_base
46
from ganeti import netutils
47
from ganeti.utils import wrapper as utils_wrapper
48

    
49

    
50
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
51

    
52
# TUN/TAP driver constants, taken from <linux/if_tun.h>
53
# They are architecture-independent and already hardcoded in qemu-kvm source,
54
# so we can safely include them here.
55
TUNSETIFF = 0x400454ca
56
TUNGETIFF = 0x800454d2
57
TUNGETFEATURES = 0x800454cf
58
IFF_TAP = 0x0002
59
IFF_NO_PI = 0x1000
60
IFF_VNET_HDR = 0x4000
61

    
62

    
63
def _ProbeTapVnetHdr(fd):
64
  """Check whether to enable the IFF_VNET_HDR flag.
65

66
  To do this, _all_ of the following conditions must be met:
67
   1. TUNGETFEATURES ioctl() *must* be implemented
68
   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
69
   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
70
      drivers/net/tun.c there is no way to test this until after the tap device
71
      has been created using TUNSETIFF, and there is no way to change the
72
      IFF_VNET_HDR flag after creating the interface, catch-22! However both
73
      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
74
      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
75

76
   @type fd: int
77
   @param fd: the file descriptor of /dev/net/tun
78

79
  """
80
  req = struct.pack("I", 0)
81
  try:
82
    res = fcntl.ioctl(fd, TUNGETFEATURES, req)
83
  except EnvironmentError:
84
    logging.warning("TUNGETFEATURES ioctl() not implemented")
85
    return False
86

    
87
  tunflags = struct.unpack("I", res)[0]
88
  if tunflags & IFF_VNET_HDR:
89
    return True
90
  else:
91
    logging.warning("Host does not support IFF_VNET_HDR, not enabling")
92
    return False
93

    
94

    
95
def _OpenTap(vnet_hdr=True):
96
  """Open a new tap device and return its file descriptor.
97

98
  This is intended to be used by a qemu-type hypervisor together with the -net
99
  tap,fd=<fd> command line parameter.
100

101
  @type vnet_hdr: boolean
102
  @param vnet_hdr: Enable the VNET Header
103
  @return: (ifname, tapfd)
104
  @rtype: tuple
105

106
  """
107
  try:
108
    tapfd = os.open("/dev/net/tun", os.O_RDWR)
109
  except EnvironmentError:
110
    raise errors.HypervisorError("Failed to open /dev/net/tun")
111

    
112
  flags = IFF_TAP | IFF_NO_PI
113

    
114
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
115
    flags |= IFF_VNET_HDR
116

    
117
  # The struct ifreq ioctl request (see netdevice(7))
118
  ifr = struct.pack("16sh", "", flags)
119

    
120
  try:
121
    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
122
  except EnvironmentError:
123
    raise errors.HypervisorError("Failed to allocate a new TAP device")
124

    
125
  # Get the interface name from the ioctl
126
  ifname = struct.unpack("16sh", res)[0].strip("\x00")
127
  return (ifname, tapfd)
128

    
129

    
130
class KVMHypervisor(hv_base.BaseHypervisor):
131
  """KVM hypervisor interface"""
132
  CAN_MIGRATE = True
133

    
134
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
135
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
136
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
137
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
138
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
139
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
140
  _KEYMAP_DIR = _ROOT_DIR + "/keymap" # contains instances keymaps
141
  # KVM instances with chroot enabled are started in empty chroot directories.
142
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
143
  # After an instance is stopped, its chroot directory is removed.
144
  # If the chroot directory is not empty, it can't be removed.
145
  # A non-empty chroot directory indicates a possible security incident.
146
  # To support forensics, the non-empty chroot directory is quarantined in
147
  # a separate directory, called 'chroot-quarantine'.
148
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
149
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
150
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR, _KEYMAP_DIR]
151

    
152
  PARAMETERS = {
153
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
154
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
155
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
156
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
157
    constants.HV_ACPI: hv_base.NO_CHECK,
158
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
159
    constants.HV_VNC_BIND_ADDRESS:
160
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
161
                         utils.IsNormAbsPath(x)),
162
       "the VNC bind address must be either a valid IP address or an absolute"
163
       " pathname", None, None),
164
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
165
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
166
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
167
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
168
    constants.HV_KVM_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
169
    constants.HV_KVM_SPICE_IP_VERSION:
170
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
171
                         x in constants.VALID_IP_VERSIONS),
172
       "the SPICE IP version should be 4 or 6",
173
       None, None),
174
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
175
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
176
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
177
    constants.HV_BOOT_ORDER:
178
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
179
    constants.HV_NIC_TYPE:
180
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
181
    constants.HV_DISK_TYPE:
182
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
183
    constants.HV_KVM_CDROM_DISK_TYPE:
184
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
185
    constants.HV_USB_MOUSE:
186
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
187
    constants.HV_KEYMAP: hv_base.NO_CHECK,
188
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
189
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
190
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
191
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
192
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
193
    constants.HV_DISK_CACHE:
194
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
195
    constants.HV_SECURITY_MODEL:
196
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
197
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
198
    constants.HV_KVM_FLAG:
199
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
200
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
201
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
202
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
203
    constants.HV_REBOOT_BEHAVIOR:
204
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
205
    }
206

    
207
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
208
                                    re.M | re.I)
209
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
210
  _MIGRATION_INFO_RETRY_DELAY = 2
211

    
212
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)(\.(\d+))?\b")
213

    
214
  ANCILLARY_FILES = [
215
    _KVM_NETWORK_SCRIPT,
216
    ]
217

    
218
  def __init__(self):
219
    hv_base.BaseHypervisor.__init__(self)
220
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
221
    # in a tmpfs filesystem or has been otherwise wiped out.
222
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
223
    utils.EnsureDirs(dirs)
224

    
225
  @classmethod
226
  def _InstancePidFile(cls, instance_name):
227
    """Returns the instance pidfile.
228

229
    """
230
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
231

    
232
  @classmethod
233
  def _InstanceUidFile(cls, instance_name):
234
    """Returns the instance uidfile.
235

236
    """
237
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
238

    
239
  @classmethod
240
  def _InstancePidInfo(cls, pid):
241
    """Check pid file for instance information.
242

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

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

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

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

    
264
    instance = None
265
    memory = 0
266
    vcpus = 0
267

    
268
    arg_list = cmdline.split("\x00")
269
    while arg_list:
270
      arg = arg_list.pop(0)
271
      if arg == "-name":
272
        instance = arg_list.pop(0)
273
      elif arg == "-m":
274
        memory = int(arg_list.pop(0))
275
      elif arg == "-smp":
276
        vcpus = int(arg_list.pop(0))
277

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

    
282
    return (instance, memory, vcpus)
283

    
284
  def _InstancePidAlive(self, instance_name):
285
    """Returns the instance pidfile, pid, and liveness.
286

287
    @type instance_name: string
288
    @param instance_name: instance name
289
    @rtype: tuple
290
    @return: (pid file name, pid, liveness)
291

292
    """
293
    pidfile = self._InstancePidFile(instance_name)
294
    pid = utils.ReadPidFile(pidfile)
295

    
296
    alive = False
297
    try:
298
      cmd_instance = self._InstancePidInfo(pid)[0]
299
      alive = (cmd_instance == instance_name)
300
    except errors.HypervisorError:
301
      pass
302

    
303
    return (pidfile, pid, alive)
304

    
305
  def _CheckDown(self, instance_name):
306
    """Raises an error unless the given instance is down.
307

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

    
314
  @classmethod
315
  def _InstanceMonitor(cls, instance_name):
316
    """Returns the instance monitor socket name
317

318
    """
319
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
320

    
321
  @classmethod
322
  def _InstanceSerial(cls, instance_name):
323
    """Returns the instance serial socket name
324

325
    """
326
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
327

    
328
  @staticmethod
329
  def _SocatUnixConsoleParams():
330
    """Returns the correct parameters for socat
331

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

334
    """
335
    if constants.SOCAT_USE_ESCAPE:
336
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
337
    else:
338
      return "echo=0,icanon=0"
339

    
340
  @classmethod
341
  def _InstanceKVMRuntime(cls, instance_name):
342
    """Returns the instance KVM runtime filename
343

344
    """
345
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
346

    
347
  @classmethod
348
  def _InstanceChrootDir(cls, instance_name):
349
    """Returns the name of the KVM chroot dir of the instance
350

351
    """
352
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
353

    
354
  @classmethod
355
  def _InstanceNICDir(cls, instance_name):
356
    """Returns the name of the directory holding the tap device files for a
357
    given instance.
358

359
    """
360
    return utils.PathJoin(cls._NICS_DIR, instance_name)
361

    
362
  @classmethod
363
  def _InstanceNICFile(cls, instance_name, seq):
364
    """Returns the name of the file containing the tap device for a given NIC
365

366
    """
367
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
368

    
369
  @classmethod
370
  def _InstanceKeymapFile(cls, instance_name):
371
    """Returns the name of the file containing the keymap for a given instance
372

373
    """
374
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
375

    
376
  @classmethod
377
  def _TryReadUidFile(cls, uid_file):
378
    """Try to read a uid file
379

380
    """
381
    if os.path.exists(uid_file):
382
      try:
383
        uid = int(utils.ReadOneLineFile(uid_file))
384
        return uid
385
      except EnvironmentError:
386
        logging.warning("Can't read uid file", exc_info=True)
387
      except (TypeError, ValueError):
388
        logging.warning("Can't parse uid file contents", exc_info=True)
389
    return None
390

    
391
  @classmethod
392
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
393
    """Removes an instance's rutime sockets/files/dirs.
394

395
    """
396
    utils.RemoveFile(pidfile)
397
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
398
    utils.RemoveFile(cls._InstanceSerial(instance_name))
399
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
400
    utils.RemoveFile(cls._InstanceKeymapFile(instance_name))
401
    uid_file = cls._InstanceUidFile(instance_name)
402
    uid = cls._TryReadUidFile(uid_file)
403
    utils.RemoveFile(uid_file)
404
    if uid is not None:
405
      uidpool.ReleaseUid(uid)
406
    try:
407
      shutil.rmtree(cls._InstanceNICDir(instance_name))
408
    except OSError, err:
409
      if err.errno != errno.ENOENT:
410
        raise
411
    try:
412
      chroot_dir = cls._InstanceChrootDir(instance_name)
413
      utils.RemoveDir(chroot_dir)
414
    except OSError, err:
415
      if err.errno == errno.ENOTEMPTY:
416
        # The chroot directory is expected to be empty, but it isn't.
417
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
418
                                          prefix="%s-%s-" %
419
                                          (instance_name,
420
                                           utils.TimestampForFilename()))
421
        logging.warning("The chroot directory of instance %s can not be"
422
                        " removed as it is not empty. Moving it to the"
423
                        " quarantine instead. Please investigate the"
424
                        " contents (%s) and clean up manually",
425
                        instance_name, new_chroot_dir)
426
        utils.RenameFile(chroot_dir, new_chroot_dir)
427
      else:
428
        raise
429

    
430
  @staticmethod
431
  def _ConfigureNIC(instance, seq, nic, tap):
432
    """Run the network configuration script for a specified NIC
433

434
    @param instance: instance we're acting on
435
    @type instance: instance object
436
    @param seq: nic sequence number
437
    @type seq: int
438
    @param nic: nic we're acting on
439
    @type nic: nic object
440
    @param tap: the host's tap interface this NIC corresponds to
441
    @type tap: str
442

443
    """
444

    
445
    if instance.tags:
446
      tags = " ".join(instance.tags)
447
    else:
448
      tags = ""
449

    
450
    env = {
451
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
452
      "INSTANCE": instance.name,
453
      "MAC": nic.mac,
454
      "MODE": nic.nicparams[constants.NIC_MODE],
455
      "INTERFACE": tap,
456
      "INTERFACE_INDEX": str(seq),
457
      "TAGS": tags,
458
    }
459

    
460
    if nic.ip:
461
      env["IP"] = nic.ip
462

    
463
    if nic.nicparams[constants.NIC_LINK]:
464
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
465

    
466
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
467
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
468

    
469
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
470
    if result.failed:
471
      raise errors.HypervisorError("Failed to configure interface %s: %s."
472
                                   " Network configuration script output: %s" %
473
                                   (tap, result.fail_reason, result.output))
474

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

478
    We can do this by listing our live instances directory and
479
    checking whether the associated kvm process is still alive.
480

481
    """
482
    result = []
483
    for name in os.listdir(self._PIDS_DIR):
484
      if self._InstancePidAlive(name)[2]:
485
        result.append(name)
486
    return result
487

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

491
    @type instance_name: string
492
    @param instance_name: the instance name
493
    @rtype: tuple of strings
494
    @return: (name, id, memory, vcpus, stat, times)
495

496
    """
497
    _, pid, alive = self._InstancePidAlive(instance_name)
498
    if not alive:
499
      return None
500

    
501
    _, memory, vcpus = self._InstancePidInfo(pid)
502
    stat = "---b-"
503
    times = "0"
504

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

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

510
    @return: list of tuples (name, id, memory, vcpus, stat, times)
511

512
    """
513
    data = []
514
    for name in os.listdir(self._PIDS_DIR):
515
      try:
516
        info = self.GetInstanceInfo(name)
517
      except errors.HypervisorError:
518
        continue
519
      if info:
520
        data.append(info)
521
    return data
522

    
523
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
524
    """Generate KVM information to start an instance.
525

526
    @attention: this function must not have any side-effects; for
527
        example, it must not write to the filesystem, or read values
528
        from the current system the are expected to differ between
529
        nodes, since it is only run once at instance startup;
530
        actions/kvm arguments that can vary between systems should be
531
        done in L{_ExecuteKVMRuntime}
532

533
    """
534
    _, v_major, v_min, _ = self._GetKVMVersion()
535

    
536
    pidfile = self._InstancePidFile(instance.name)
537
    kvm = constants.KVM_PATH
538
    kvm_cmd = [kvm]
539
    # used just by the vnc server, if enabled
540
    kvm_cmd.extend(["-name", instance.name])
541
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
542
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
543
    kvm_cmd.extend(["-pidfile", pidfile])
544
    kvm_cmd.extend(["-daemonize"])
545
    if not instance.hvparams[constants.HV_ACPI]:
546
      kvm_cmd.extend(["-no-acpi"])
547
    if startup_paused:
548
      kvm_cmd.extend(["-S"])
549
    if instance.hvparams[constants.HV_REBOOT_BEHAVIOR] == \
550
        constants.INSTANCE_REBOOT_EXIT:
551
      kvm_cmd.extend(["-no-reboot"])
552

    
553
    hvp = instance.hvparams
554
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
555
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
556
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
557
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
558

    
559
    self.ValidateParameters(hvp)
560

    
561
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
562
      kvm_cmd.extend(["-enable-kvm"])
563
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
564
      kvm_cmd.extend(["-disable-kvm"])
565

    
566
    if boot_network:
567
      kvm_cmd.extend(["-boot", "n"])
568

    
569
    disk_type = hvp[constants.HV_DISK_TYPE]
570
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
571
      if_val = ",if=virtio"
572
    else:
573
      if_val = ",if=%s" % disk_type
574
    # Cache mode
575
    disk_cache = hvp[constants.HV_DISK_CACHE]
576
    if instance.disk_template in constants.DTS_EXT_MIRROR:
577
      if disk_cache != "none":
578
        # TODO: make this a hard error, instead of a silent overwrite
579
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
580
                        " to prevent shared storage corruption on migration",
581
                        disk_cache)
582
      cache_val = ",cache=none"
583
    elif disk_cache != constants.HT_CACHE_DEFAULT:
584
      cache_val = ",cache=%s" % disk_cache
585
    else:
586
      cache_val = ""
587
    for cfdev, dev_path in block_devices:
588
      if cfdev.mode != constants.DISK_RDWR:
589
        raise errors.HypervisorError("Instance has read-only disks which"
590
                                     " are not supported by KVM")
591
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
592
      boot_val = ""
593
      if boot_disk:
594
        kvm_cmd.extend(["-boot", "c"])
595
        boot_disk = False
596
        if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
597
          boot_val = ",boot=on"
598

    
599
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
600
                                                cache_val)
601
      kvm_cmd.extend(["-drive", drive_val])
602

    
603
    #Now we can specify a different device type for CDROM devices.
604
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
605
    if not cdrom_disk_type:
606
      cdrom_disk_type = disk_type
607

    
608
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
609
    if iso_image:
610
      options = ",format=raw,media=cdrom"
611
      if boot_cdrom:
612
        kvm_cmd.extend(["-boot", "d"])
613
        if cdrom_disk_type != constants.HT_DISK_IDE:
614
          options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
615
        else:
616
          options = "%s,boot=on" % options
617
      else:
618
        if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
619
          if_val = ",if=virtio"
620
        else:
621
          if_val = ",if=%s" % cdrom_disk_type
622
        options = "%s%s" % (options, if_val)
623
      drive_val = "file=%s%s" % (iso_image, options)
624
      kvm_cmd.extend(["-drive", drive_val])
625

    
626
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
627
    if iso_image2:
628
      options = ",format=raw,media=cdrom"
629
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
630
        if_val = ",if=virtio"
631
      else:
632
        if_val = ",if=%s" % cdrom_disk_type
633
      options = "%s%s" % (options, if_val)
634
      drive_val = "file=%s%s" % (iso_image2, options)
635
      kvm_cmd.extend(["-drive", drive_val])
636

    
637
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
638
    if floppy_image:
639
      options = ",format=raw,media=disk"
640
      if boot_floppy:
641
        kvm_cmd.extend(["-boot", "a"])
642
        options = "%s,boot=on" % options
643
      if_val = ",if=floppy"
644
      options = "%s%s" % (options, if_val)
645
      drive_val = "file=%s%s" % (floppy_image, options)
646
      kvm_cmd.extend(["-drive", drive_val])
647

    
648
    kernel_path = hvp[constants.HV_KERNEL_PATH]
649
    if kernel_path:
650
      kvm_cmd.extend(["-kernel", kernel_path])
651
      initrd_path = hvp[constants.HV_INITRD_PATH]
652
      if initrd_path:
653
        kvm_cmd.extend(["-initrd", initrd_path])
654
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
655
                     hvp[constants.HV_KERNEL_ARGS]]
656
      if hvp[constants.HV_SERIAL_CONSOLE]:
657
        root_append.append("console=ttyS0,38400")
658
      kvm_cmd.extend(["-append", " ".join(root_append)])
659

    
660
    mem_path = hvp[constants.HV_MEM_PATH]
661
    if mem_path:
662
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
663

    
664
    monitor_dev = ("unix:%s,server,nowait" %
665
                   self._InstanceMonitor(instance.name))
666
    kvm_cmd.extend(["-monitor", monitor_dev])
667
    if hvp[constants.HV_SERIAL_CONSOLE]:
668
      serial_dev = ("unix:%s,server,nowait" %
669
                    self._InstanceSerial(instance.name))
670
      kvm_cmd.extend(["-serial", serial_dev])
671
    else:
672
      kvm_cmd.extend(["-serial", "none"])
673

    
674
    mouse_type = hvp[constants.HV_USB_MOUSE]
675
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
676
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
677
    spice_ip_version = None
678

    
679
    if mouse_type:
680
      kvm_cmd.extend(["-usb"])
681
      kvm_cmd.extend(["-usbdevice", mouse_type])
682
    elif vnc_bind_address:
683
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
684

    
685
    if vnc_bind_address:
686
      if netutils.IP4Address.IsValid(vnc_bind_address):
687
        if instance.network_port > constants.VNC_BASE_PORT:
688
          display = instance.network_port - constants.VNC_BASE_PORT
689
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
690
            vnc_arg = ":%d" % (display)
691
          else:
692
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
693
        else:
694
          logging.error("Network port is not a valid VNC display (%d < %d)."
695
                        " Not starting VNC", instance.network_port,
696
                        constants.VNC_BASE_PORT)
697
          vnc_arg = "none"
698

    
699
        # Only allow tls and other option when not binding to a file, for now.
700
        # kvm/qemu gets confused otherwise about the filename to use.
701
        vnc_append = ""
702
        if hvp[constants.HV_VNC_TLS]:
703
          vnc_append = "%s,tls" % vnc_append
704
          if hvp[constants.HV_VNC_X509_VERIFY]:
705
            vnc_append = "%s,x509verify=%s" % (vnc_append,
706
                                               hvp[constants.HV_VNC_X509])
707
          elif hvp[constants.HV_VNC_X509]:
708
            vnc_append = "%s,x509=%s" % (vnc_append,
709
                                         hvp[constants.HV_VNC_X509])
710
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
711
          vnc_append = "%s,password" % vnc_append
712

    
713
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
714

    
715
      else:
716
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
717

    
718
      kvm_cmd.extend(["-vnc", vnc_arg])
719
    elif spice_bind:
720
      # FIXME: this is wrong here; the iface ip address differs
721
      # between systems, so it should be done in _ExecuteKVMRuntime
722
      if netutils.IsValidInterface(spice_bind):
723
        # The user specified a network interface, we have to figure out the IP
724
        # address.
725
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
726
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
727

    
728
        # if the user specified an IP version and the interface does not
729
        # have that kind of IP addresses, throw an exception
730
        if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
731
          if not addresses[spice_ip_version]:
732
            raise errors.HypervisorError("spice: unable to get an IPv%s address"
733
                                         " for %s" % (spice_ip_version,
734
                                                      spice_bind))
735

    
736
        # the user did not specify an IP version, we have to figure it out
737
        elif (addresses[constants.IP4_VERSION] and
738
              addresses[constants.IP6_VERSION]):
739
          # we have both ipv4 and ipv6, let's use the cluster default IP
740
          # version
741
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
742
          spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
743
              cluster_family)
744
        elif addresses[constants.IP4_VERSION]:
745
          spice_ip_version = constants.IP4_VERSION
746
        elif addresses[constants.IP6_VERSION]:
747
          spice_ip_version = constants.IP6_VERSION
748
        else:
749
          raise errors.HypervisorError("spice: unable to get an IP address"
750
                                       " for %s" % (spice_bind))
751

    
752
        spice_address = addresses[spice_ip_version][0]
753

    
754
      else:
755
        # spice_bind is known to be a valid IP address, because
756
        # ValidateParameters checked it.
757
        spice_address = spice_bind
758

    
759
      spice_arg = "addr=%s,port=%s,disable-ticketing" % (spice_address,
760
                                                         instance.network_port)
761
      if spice_ip_version:
762
        spice_arg = "%s,ipv%s" % (spice_arg, spice_ip_version)
763

    
764
      logging.info("KVM: SPICE will listen on port %s", instance.network_port)
765
      kvm_cmd.extend(["-spice", spice_arg])
766

    
767
    else:
768
      kvm_cmd.extend(["-nographic"])
769

    
770
    if hvp[constants.HV_USE_LOCALTIME]:
771
      kvm_cmd.extend(["-localtime"])
772

    
773
    if hvp[constants.HV_KVM_USE_CHROOT]:
774
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
775

    
776
    # Save the current instance nics, but defer their expansion as parameters,
777
    # as we'll need to generate executable temp files for them.
778
    kvm_nics = instance.nics
779
    hvparams = hvp
780

    
781
    return (kvm_cmd, kvm_nics, hvparams)
782

    
783
  def _WriteKVMRuntime(self, instance_name, data):
784
    """Write an instance's KVM runtime
785

786
    """
787
    try:
788
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
789
                      data=data)
790
    except EnvironmentError, err:
791
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
792

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

796
    """
797
    try:
798
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
799
    except EnvironmentError, err:
800
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
801
    return file_content
802

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

806
    """
807
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
808
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
809
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
810
    self._WriteKVMRuntime(instance.name, serialized_form)
811

    
812
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
813
    """Load an instance's KVM runtime
814

815
    """
816
    if not serialized_runtime:
817
      serialized_runtime = self._ReadKVMRuntime(instance.name)
818
    loaded_runtime = serializer.Load(serialized_runtime)
819
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
820
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
821
    return (kvm_cmd, kvm_nics, hvparams)
822

    
823
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
824
    """Run the KVM cmd and check for errors
825

826
    @type name: string
827
    @param name: instance name
828
    @type kvm_cmd: list of strings
829
    @param kvm_cmd: runcmd input for kvm
830
    @type tap_fds: list of int
831
    @param tap_fds: fds of tap devices opened by Ganeti
832

833
    """
834
    try:
835
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
836
    finally:
837
      for fd in tap_fds:
838
        utils_wrapper.CloseFdNoError(fd)
839

    
840
    if result.failed:
841
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
842
                                   (name, result.fail_reason, result.output))
843
    if not self._InstancePidAlive(name)[2]:
844
      raise errors.HypervisorError("Failed to start instance %s" % name)
845

    
846
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
847
    """Execute a KVM cmd, after completing it with some last minute data.
848

849
    @type incoming: tuple of strings
850
    @param incoming: (target_host_ip, port)
851

852
    """
853
    # Small _ExecuteKVMRuntime hv parameters programming howto:
854
    #  - conf_hvp contains the parameters as configured on ganeti. they might
855
    #    have changed since the instance started; only use them if the change
856
    #    won't affect the inside of the instance (which hasn't been rebooted).
857
    #  - up_hvp contains the parameters as they were when the instance was
858
    #    started, plus any new parameter which has been added between ganeti
859
    #    versions: it is paramount that those default to a value which won't
860
    #    affect the inside of the instance as well.
861
    conf_hvp = instance.hvparams
862
    name = instance.name
863
    self._CheckDown(name)
864

    
865
    temp_files = []
866

    
867
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
868
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
869

    
870
    _, v_major, v_min, _ = self._GetKVMVersion()
871

    
872
    # We know it's safe to run as a different user upon migration, so we'll use
873
    # the latest conf, from conf_hvp.
874
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
875
    if security_model == constants.HT_SM_USER:
876
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
877

    
878
    keymap = conf_hvp[constants.HV_KEYMAP]
879
    if keymap:
880
      keymap_path = self._InstanceKeymapFile(name)
881
      # If a keymap file is specified, KVM won't use its internal defaults. By
882
      # first including the "en-us" layout, an error on loading the actual
883
      # layout (e.g. because it can't be found) won't lead to a non-functional
884
      # keyboard. A keyboard with incorrect keys is still better than none.
885
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
886
      kvm_cmd.extend(["-k", keymap_path])
887

    
888
    # We have reasons to believe changing something like the nic driver/type
889
    # upon migration won't exactly fly with the instance kernel, so for nic
890
    # related parameters we'll use up_hvp
891
    tapfds = []
892
    taps = []
893
    if not kvm_nics:
894
      kvm_cmd.extend(["-net", "none"])
895
    else:
896
      vnet_hdr = False
897
      tap_extra = ""
898
      nic_type = up_hvp[constants.HV_NIC_TYPE]
899
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
900
        # From version 0.12.0, kvm uses a new sintax for network configuration.
901
        if (v_major, v_min) >= (0, 12):
902
          nic_model = "virtio-net-pci"
903
          vnet_hdr = True
904
        else:
905
          nic_model = "virtio"
906

    
907
        if up_hvp[constants.HV_VHOST_NET]:
908
          # vhost_net is only available from version 0.13.0 or newer
909
          if (v_major, v_min) >= (0, 13):
910
            tap_extra = ",vhost=on"
911
          else:
912
            raise errors.HypervisorError("vhost_net is configured"
913
                                        " but it is not available")
914
      else:
915
        nic_model = nic_type
916

    
917
      for nic_seq, nic in enumerate(kvm_nics):
918
        tapname, tapfd = _OpenTap(vnet_hdr)
919
        tapfds.append(tapfd)
920
        taps.append(tapname)
921
        if (v_major, v_min) >= (0, 12):
922
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
923
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
924
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
925
        else:
926
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
927
                                                         nic.mac, nic_model)
928
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
929
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
930

    
931
    if incoming:
932
      target, port = incoming
933
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
934

    
935
    # Changing the vnc password doesn't bother the guest that much. At most it
936
    # will surprise people who connect to it. Whether positively or negatively
937
    # it's debatable.
938
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
939
    vnc_pwd = None
940
    if vnc_pwd_file:
941
      try:
942
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
943
      except EnvironmentError, err:
944
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
945
                                     % (vnc_pwd_file, err))
946

    
947
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
948
      utils.EnsureDirs([(self._InstanceChrootDir(name),
949
                         constants.SECURE_DIR_MODE)])
950

    
951
    # Configure the network now for starting instances and bridged interfaces,
952
    # during FinalizeMigration for incoming instances' routed interfaces
953
    for nic_seq, nic in enumerate(kvm_nics):
954
      if (incoming and
955
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
956
        continue
957
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
958

    
959
    if security_model == constants.HT_SM_POOL:
960
      ss = ssconf.SimpleStore()
961
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
962
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
963
      uid = uidpool.RequestUnusedUid(all_uids)
964
      try:
965
        username = pwd.getpwuid(uid.GetUid()).pw_name
966
        kvm_cmd.extend(["-runas", username])
967
        self._RunKVMCmd(name, kvm_cmd, tapfds)
968
      except:
969
        uidpool.ReleaseUid(uid)
970
        raise
971
      else:
972
        uid.Unlock()
973
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
974
    else:
975
      self._RunKVMCmd(name, kvm_cmd, tapfds)
976

    
977
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
978
                     constants.RUN_DIRS_MODE)])
979
    for nic_seq, tap in enumerate(taps):
980
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
981
                      data=tap)
982

    
983
    if vnc_pwd:
984
      change_cmd = "change vnc password %s" % vnc_pwd
985
      self._CallMonitorCommand(instance.name, change_cmd)
986

    
987
    for filename in temp_files:
988
      utils.RemoveFile(filename)
989

    
990
  def StartInstance(self, instance, block_devices, startup_paused):
991
    """Start an instance.
992

993
    """
994
    self._CheckDown(instance.name)
995
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
996
                                           startup_paused)
997
    self._SaveKVMRuntime(instance, kvm_runtime)
998
    self._ExecuteKVMRuntime(instance, kvm_runtime)
999

    
1000
  def _CallMonitorCommand(self, instance_name, command):
1001
    """Invoke a command on the instance monitor.
1002

1003
    """
1004
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
1005
             (utils.ShellQuote(command),
1006
              constants.SOCAT_PATH,
1007
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
1008
    result = utils.RunCmd(socat)
1009
    if result.failed:
1010
      msg = ("Failed to send command '%s' to instance %s."
1011
             " output: %s, error: %s, fail_reason: %s" %
1012
             (command, instance_name,
1013
              result.stdout, result.stderr, result.fail_reason))
1014
      raise errors.HypervisorError(msg)
1015

    
1016
    return result
1017

    
1018
  @classmethod
1019
  def _ParseKVMVersion(cls, text):
1020
    """Parse the KVM version from the --help output.
1021

1022
    @type text: string
1023
    @param text: output of kvm --help
1024
    @return: (version, v_maj, v_min, v_rev)
1025
    @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1026

1027
    """
1028
    match = cls._VERSION_RE.search(text.splitlines()[0])
1029
    if not match:
1030
      raise errors.HypervisorError("Unable to get KVM version")
1031

    
1032
    v_all = match.group(0)
1033
    v_maj = int(match.group(1))
1034
    v_min = int(match.group(2))
1035
    if match.group(4):
1036
      v_rev = int(match.group(4))
1037
    else:
1038
      v_rev = 0
1039
    return (v_all, v_maj, v_min, v_rev)
1040

    
1041
  @classmethod
1042
  def _GetKVMVersion(cls):
1043
    """Return the installed KVM version.
1044

1045
    @return: (version, v_maj, v_min, v_rev)
1046
    @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1047

1048
    """
1049
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1050
    if result.failed:
1051
      raise errors.HypervisorError("Unable to get KVM version")
1052
    return cls._ParseKVMVersion(result.output)
1053

    
1054
  def StopInstance(self, instance, force=False, retry=False, name=None):
1055
    """Stop an instance.
1056

1057
    """
1058
    if name is not None and not force:
1059
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1060
    if name is None:
1061
      name = instance.name
1062
      acpi = instance.hvparams[constants.HV_ACPI]
1063
    else:
1064
      acpi = False
1065
    _, pid, alive = self._InstancePidAlive(name)
1066
    if pid > 0 and alive:
1067
      if force or not acpi:
1068
        utils.KillProcess(pid)
1069
      else:
1070
        self._CallMonitorCommand(name, "system_powerdown")
1071

    
1072
  def CleanupInstance(self, instance_name):
1073
    """Cleanup after a stopped instance
1074

1075
    """
1076
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1077
    if pid > 0 and alive:
1078
      raise errors.HypervisorError("Cannot cleanup a live instance")
1079
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1080

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

1084
    """
1085
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1086
    # socket the instance will stop, but now power up again. So we'll resort
1087
    # to shutdown and restart.
1088
    _, _, alive = self._InstancePidAlive(instance.name)
1089
    if not alive:
1090
      raise errors.HypervisorError("Failed to reboot instance %s:"
1091
                                   " not running" % instance.name)
1092
    # StopInstance will delete the saved KVM runtime so:
1093
    # ...first load it...
1094
    kvm_runtime = self._LoadKVMRuntime(instance)
1095
    # ...now we can safely call StopInstance...
1096
    if not self.StopInstance(instance):
1097
      self.StopInstance(instance, force=True)
1098
    # ...and finally we can save it again, and execute it...
1099
    self._SaveKVMRuntime(instance, kvm_runtime)
1100
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1101

    
1102
  def MigrationInfo(self, instance):
1103
    """Get instance information to perform a migration.
1104

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

1110
    """
1111
    return self._ReadKVMRuntime(instance.name)
1112

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

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

1123
    """
1124
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1125
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1126
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1127

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

1131
    Stop the incoming mode KVM.
1132

1133
    @type instance: L{objects.Instance}
1134
    @param instance: instance whose migration is being finalized
1135

1136
    """
1137
    if success:
1138
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1139
      kvm_nics = kvm_runtime[1]
1140

    
1141
      for nic_seq, nic in enumerate(kvm_nics):
1142
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1143
          # Bridged interfaces have already been configured
1144
          continue
1145
        try:
1146
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1147
        except EnvironmentError, err:
1148
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1149
                          instance.name, nic_seq, str(err))
1150
          continue
1151
        try:
1152
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1153
        except errors.HypervisorError, err:
1154
          logging.warning(str(err))
1155

    
1156
      self._WriteKVMRuntime(instance.name, info)
1157
    else:
1158
      self.StopInstance(instance, force=True)
1159

    
1160
  def MigrateInstance(self, instance, target, live):
1161
    """Migrate an instance to a target node.
1162

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

1166
    @type instance: L{objects.Instance}
1167
    @param instance: the instance to be migrated
1168
    @type target: string
1169
    @param target: ip address of the target node
1170
    @type live: boolean
1171
    @param live: perform a live migration
1172

1173
    """
1174
    instance_name = instance.name
1175
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1176
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1177
    if not alive:
1178
      raise errors.HypervisorError("Instance not running, cannot migrate")
1179

    
1180
    if not live:
1181
      self._CallMonitorCommand(instance_name, "stop")
1182

    
1183
    migrate_command = ("migrate_set_speed %dm" %
1184
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1185
    self._CallMonitorCommand(instance_name, migrate_command)
1186

    
1187
    migrate_command = ("migrate_set_downtime %dms" %
1188
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1189
    self._CallMonitorCommand(instance_name, migrate_command)
1190

    
1191
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1192
    self._CallMonitorCommand(instance_name, migrate_command)
1193

    
1194
    info_command = "info migrate"
1195
    done = False
1196
    broken_answers = 0
1197
    while not done:
1198
      result = self._CallMonitorCommand(instance_name, info_command)
1199
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1200
      if not match:
1201
        broken_answers += 1
1202
        if not result.stdout:
1203
          logging.info("KVM: empty 'info migrate' result")
1204
        else:
1205
          logging.warning("KVM: unknown 'info migrate' result: %s",
1206
                          result.stdout)
1207
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1208
      else:
1209
        status = match.group(1)
1210
        if status == "completed":
1211
          done = True
1212
        elif status == "active":
1213
          # reset the broken answers count
1214
          broken_answers = 0
1215
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1216
        elif status == "failed" or status == "cancelled":
1217
          if not live:
1218
            self._CallMonitorCommand(instance_name, 'cont')
1219
          raise errors.HypervisorError("Migration %s at the kvm level" %
1220
                                       status)
1221
        else:
1222
          logging.warning("KVM: unknown migration status '%s'", status)
1223
          broken_answers += 1
1224
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1225
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1226
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1227

    
1228
    utils.KillProcess(pid)
1229
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1230

    
1231
  def GetNodeInfo(self):
1232
    """Return information about the node.
1233

1234
    This is just a wrapper over the base GetLinuxNodeInfo method.
1235

1236
    @return: a dict with the following keys (values in MiB):
1237
          - memory_total: the total memory size on the node
1238
          - memory_free: the available memory on the node for instances
1239
          - memory_dom0: the memory used by the node itself, if available
1240

1241
    """
1242
    return self.GetLinuxNodeInfo()
1243

    
1244
  @classmethod
1245
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1246
    """Return a command for connecting to the console of an instance.
1247

1248
    """
1249
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1250
      cmd = [constants.KVM_CONSOLE_WRAPPER,
1251
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1252
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1253
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1254
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1255
      return objects.InstanceConsole(instance=instance.name,
1256
                                     kind=constants.CONS_SSH,
1257
                                     host=instance.primary_node,
1258
                                     user=constants.GANETI_RUNAS,
1259
                                     command=cmd)
1260

    
1261
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1262
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1263
      display = instance.network_port - constants.VNC_BASE_PORT
1264
      return objects.InstanceConsole(instance=instance.name,
1265
                                     kind=constants.CONS_VNC,
1266
                                     host=vnc_bind_address,
1267
                                     port=instance.network_port,
1268
                                     display=display)
1269

    
1270
    return objects.InstanceConsole(instance=instance.name,
1271
                                   kind=constants.CONS_MESSAGE,
1272
                                   message=("No serial shell for instance %s" %
1273
                                            instance.name))
1274

    
1275
  def Verify(self):
1276
    """Verify the hypervisor.
1277

1278
    Check that the binary exists.
1279

1280
    """
1281
    if not os.path.exists(constants.KVM_PATH):
1282
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1283
    if not os.path.exists(constants.SOCAT_PATH):
1284
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1285

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

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

1294
    """
1295
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1296

    
1297
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1298
    if kernel_path:
1299
      if not hvparams[constants.HV_ROOT_PATH]:
1300
        raise errors.HypervisorError("Need a root partition for the instance,"
1301
                                     " if a kernel is defined")
1302

    
1303
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1304
        not hvparams[constants.HV_VNC_X509]):
1305
      raise errors.HypervisorError("%s must be defined, if %s is" %
1306
                                   (constants.HV_VNC_X509,
1307
                                    constants.HV_VNC_X509_VERIFY))
1308

    
1309
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1310
    if (boot_order == constants.HT_BO_CDROM and
1311
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1312
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1313
                                   " ISO path")
1314

    
1315
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1316
    if security_model == constants.HT_SM_USER:
1317
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1318
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1319
                                     " must be specified")
1320
    elif (security_model == constants.HT_SM_NONE or
1321
          security_model == constants.HT_SM_POOL):
1322
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1323
        raise errors.HypervisorError("Cannot have a security domain when the"
1324
                                     " security model is 'none' or 'pool'")
1325

    
1326
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1327
    if spice_bind:
1328
      spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1329
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1330
        # if an IP version is specified, the spice_bind parameter must be an
1331
        # IP of that family
1332
        if (netutils.IP4Address.IsValid(spice_bind) and
1333
            spice_ip_version != constants.IP4_VERSION):
1334
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1335
                                       " the specified IP version is %s" %
1336
                                       (spice_bind, spice_ip_version))
1337

    
1338
        if (netutils.IP6Address.IsValid(spice_bind) and
1339
            spice_ip_version != constants.IP6_VERSION):
1340
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1341
                                       " the specified IP version is %s" %
1342
                                       (spice_bind, spice_ip_version))
1343

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

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

1352
    """
1353
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1354

    
1355
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1356
    if security_model == constants.HT_SM_USER:
1357
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1358
      try:
1359
        pwd.getpwnam(username)
1360
      except KeyError:
1361
        raise errors.HypervisorError("Unknown security domain user %s"
1362
                                     % username)
1363

    
1364
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1365
    if spice_bind:
1366
      # only one of VNC and SPICE can be used currently.
1367
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1368
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
1369
                                     " only one of them can be used at a"
1370
                                     " given time.")
1371

    
1372
      # KVM version should be >= 0.14.0
1373
      _, v_major, v_min, _ = cls._GetKVMVersion()
1374
      if (v_major, v_min) < (0, 14):
1375
        raise errors.HypervisorError("spice is configured, but it is not"
1376
                                     " available in versions of KVM < 0.14")
1377

    
1378
      # if spice_bind is not an IP address, it must be a valid interface
1379
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1380
                       or netutils.IP6Address.IsValid(spice_bind))
1381
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1382
        raise errors.HypervisorError("spice: the %s parameter must be either"
1383
                                     " a valid IP address or interface name" %
1384
                                     constants.HV_KVM_SPICE_BIND)
1385

    
1386
  @classmethod
1387
  def PowercycleNode(cls):
1388
    """KVM powercycle, just a wrapper over Linux powercycle.
1389

1390
    """
1391
    cls.LinuxPowercycle()