Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ d0c8c01d

History | View | Annotate | Download (44.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010, 2011 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]
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_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
169
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
170
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
171
    constants.HV_BOOT_ORDER:
172
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
173
    constants.HV_NIC_TYPE:
174
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
175
    constants.HV_DISK_TYPE:
176
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
177
    constants.HV_KVM_CDROM_DISK_TYPE:
178
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
179
    constants.HV_USB_MOUSE:
180
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
181
    constants.HV_KEYMAP: hv_base.NO_CHECK,
182
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
183
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
184
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
185
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
186
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
187
    constants.HV_DISK_CACHE:
188
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
189
    constants.HV_SECURITY_MODEL:
190
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
191
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
192
    constants.HV_KVM_FLAG:
193
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
194
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
195
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
196
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
197
    }
198

    
199
  _MIGRATION_STATUS_RE = re.compile("Migration\s+status:\s+(\w+)",
200
                                    re.M | re.I)
201
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
202
  _MIGRATION_INFO_RETRY_DELAY = 2
203

    
204
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
205

    
206
  ANCILLARY_FILES = [
207
    _KVM_NETWORK_SCRIPT,
208
    ]
209

    
210
  def __init__(self):
211
    hv_base.BaseHypervisor.__init__(self)
212
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
213
    # in a tmpfs filesystem or has been otherwise wiped out.
214
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
215
    utils.EnsureDirs(dirs)
216

    
217
  @classmethod
218
  def _InstancePidFile(cls, instance_name):
219
    """Returns the instance pidfile.
220

221
    """
222
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
223

    
224
  @classmethod
225
  def _InstanceUidFile(cls, instance_name):
226
    """Returns the instance uidfile.
227

228
    """
229
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
230

    
231
  @classmethod
232
  def _InstancePidInfo(cls, pid):
233
    """Check pid file for instance information.
234

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

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

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

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

    
256
    instance = None
257
    memory = 0
258
    vcpus = 0
259

    
260
    arg_list = cmdline.split("\x00")
261
    while arg_list:
262
      arg =  arg_list.pop(0)
263
      if arg == "-name":
264
        instance = arg_list.pop(0)
265
      elif arg == "-m":
266
        memory = int(arg_list.pop(0))
267
      elif arg == "-smp":
268
        vcpus = int(arg_list.pop(0))
269

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

    
274
    return (instance, memory, vcpus)
275

    
276
  def _InstancePidAlive(self, instance_name):
277
    """Returns the instance pidfile, pid, and liveness.
278

279
    @type instance_name: string
280
    @param instance_name: instance name
281
    @rtype: tuple
282
    @return: (pid file name, pid, liveness)
283

284
    """
285
    pidfile = self._InstancePidFile(instance_name)
286
    pid = utils.ReadPidFile(pidfile)
287

    
288
    alive = False
289
    try:
290
      cmd_instance = self._InstancePidInfo(pid)[0]
291
      alive = (cmd_instance == instance_name)
292
    except errors.HypervisorError:
293
      pass
294

    
295
    return (pidfile, pid, alive)
296

    
297
  def _CheckDown(self, instance_name):
298
    """Raises an error unless the given instance is down.
299

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

    
306
  @classmethod
307
  def _InstanceMonitor(cls, instance_name):
308
    """Returns the instance monitor socket name
309

310
    """
311
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
312

    
313
  @classmethod
314
  def _InstanceSerial(cls, instance_name):
315
    """Returns the instance serial socket name
316

317
    """
318
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
319

    
320
  @staticmethod
321
  def _SocatUnixConsoleParams():
322
    """Returns the correct parameters for socat
323

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

326
    """
327
    if constants.SOCAT_USE_ESCAPE:
328
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
329
    else:
330
      return "echo=0,icanon=0"
331

    
332
  @classmethod
333
  def _InstanceKVMRuntime(cls, instance_name):
334
    """Returns the instance KVM runtime filename
335

336
    """
337
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
338

    
339
  @classmethod
340
  def _InstanceChrootDir(cls, instance_name):
341
    """Returns the name of the KVM chroot dir of the instance
342

343
    """
344
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
345

    
346
  @classmethod
347
  def _InstanceNICDir(cls, instance_name):
348
    """Returns the name of the directory holding the tap device files for a
349
    given instance.
350

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

    
354
  @classmethod
355
  def _InstanceNICFile(cls, instance_name, seq):
356
    """Returns the name of the file containing the tap device for a given NIC
357

358
    """
359
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
360

    
361
  @classmethod
362
  def _InstanceKeymapFile(cls, instance_name):
363
    """Returns the name of the file containing the keymap for a given instance
364

365
    """
366
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
367

    
368
  @classmethod
369
  def _TryReadUidFile(cls, uid_file):
370
    """Try to read a uid file
371

372
    """
373
    if os.path.exists(uid_file):
374
      try:
375
        uid = int(utils.ReadOneLineFile(uid_file))
376
        return uid
377
      except EnvironmentError:
378
        logging.warning("Can't read uid file", exc_info=True)
379
      except (TypeError, ValueError):
380
        logging.warning("Can't parse uid file contents", exc_info=True)
381
    return None
382

    
383
  @classmethod
384
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
385
    """Removes an instance's rutime sockets/files/dirs.
386

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

    
422
  @staticmethod
423
  def _ConfigureNIC(instance, seq, nic, tap):
424
    """Run the network configuration script for a specified NIC
425

426
    @param instance: instance we're acting on
427
    @type instance: instance object
428
    @param seq: nic sequence number
429
    @type seq: int
430
    @param nic: nic we're acting on
431
    @type nic: nic object
432
    @param tap: the host's tap interface this NIC corresponds to
433
    @type tap: str
434

435
    """
436

    
437
    if instance.tags:
438
      tags = " ".join(instance.tags)
439
    else:
440
      tags = ""
441

    
442
    env = {
443
      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
444
      "INSTANCE": instance.name,
445
      "MAC": nic.mac,
446
      "MODE": nic.nicparams[constants.NIC_MODE],
447
      "INTERFACE": tap,
448
      "INTERFACE_INDEX": str(seq),
449
      "TAGS": tags,
450
    }
451

    
452
    if nic.ip:
453
      env["IP"] = nic.ip
454

    
455
    if nic.nicparams[constants.NIC_LINK]:
456
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
457

    
458
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
459
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
460

    
461
    result = utils.RunCmd([constants.KVM_IFUP, tap], env=env)
462
    if result.failed:
463
      raise errors.HypervisorError("Failed to configure interface %s: %s."
464
                                   " Network configuration script output: %s" %
465
                                   (tap, result.fail_reason, result.output))
466

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

470
    We can do this by listing our live instances directory and
471
    checking whether the associated kvm process is still alive.
472

473
    """
474
    result = []
475
    for name in os.listdir(self._PIDS_DIR):
476
      if self._InstancePidAlive(name)[2]:
477
        result.append(name)
478
    return result
479

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

483
    @type instance_name: string
484
    @param instance_name: the instance name
485
    @rtype: tuple of strings
486
    @return: (name, id, memory, vcpus, stat, times)
487

488
    """
489
    _, pid, alive = self._InstancePidAlive(instance_name)
490
    if not alive:
491
      return None
492

    
493
    _, memory, vcpus = self._InstancePidInfo(pid)
494
    stat = "---b-"
495
    times = "0"
496

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

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

502
    @return: list of tuples (name, id, memory, vcpus, stat, times)
503

504
    """
505
    data = []
506
    for name in os.listdir(self._PIDS_DIR):
507
      try:
508
        info = self.GetInstanceInfo(name)
509
      except errors.HypervisorError:
510
        continue
511
      if info:
512
        data.append(info)
513
    return data
514

    
515
  def _GenerateKVMRuntime(self, instance, block_devices, startup_paused):
516
    """Generate KVM information to start an instance.
517

518
    """
519
    kvm_version = self._GetKVMVersion()
520
    if kvm_version:
521
      _, v_major, v_min, _ = kvm_version
522
    else:
523
      raise errors.HypervisorError("Unable to get KVM version")
524

    
525
    pidfile  = self._InstancePidFile(instance.name)
526
    kvm = constants.KVM_PATH
527
    kvm_cmd = [kvm]
528
    # used just by the vnc server, if enabled
529
    kvm_cmd.extend(["-name", instance.name])
530
    kvm_cmd.extend(["-m", instance.beparams[constants.BE_MEMORY]])
531
    kvm_cmd.extend(["-smp", instance.beparams[constants.BE_VCPUS]])
532
    kvm_cmd.extend(["-pidfile", pidfile])
533
    kvm_cmd.extend(["-daemonize"])
534
    if not instance.hvparams[constants.HV_ACPI]:
535
      kvm_cmd.extend(["-no-acpi"])
536
    if startup_paused:
537
      kvm_cmd.extend(["-S"])
538

    
539
    hvp = instance.hvparams
540
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
541
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
542
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
543
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
544

    
545
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
546
      kvm_cmd.extend(["-enable-kvm"])
547
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
548
      kvm_cmd.extend(["-disable-kvm"])
549

    
550
    if boot_network:
551
      kvm_cmd.extend(["-boot", "n"])
552

    
553
    disk_type = hvp[constants.HV_DISK_TYPE]
554
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
555
      if_val = ",if=virtio"
556
    else:
557
      if_val = ",if=%s" % disk_type
558
    # Cache mode
559
    disk_cache = hvp[constants.HV_DISK_CACHE]
560
    if instance.disk_template in constants.DTS_EXT_MIRROR:
561
      if disk_cache != "none":
562
        # TODO: make this a hard error, instead of a silent overwrite
563
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
564
                        " to prevent shared storage corruption on migration",
565
                        disk_cache)
566
      cache_val = ",cache=none"
567
    elif disk_cache != constants.HT_CACHE_DEFAULT:
568
      cache_val = ",cache=%s" % disk_cache
569
    else:
570
      cache_val = ""
571
    for cfdev, dev_path in block_devices:
572
      if cfdev.mode != constants.DISK_RDWR:
573
        raise errors.HypervisorError("Instance has read-only disks which"
574
                                     " are not supported by KVM")
575
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
576
      boot_val = ""
577
      if boot_disk:
578
        kvm_cmd.extend(["-boot", "c"])
579
        boot_disk = False
580
        if (v_major, v_min) < (0, 14) and disk_type != constants.HT_DISK_IDE:
581
          boot_val = ",boot=on"
582

    
583
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
584
                                                cache_val)
585
      kvm_cmd.extend(["-drive", drive_val])
586

    
587
    #Now we can specify a different device type for CDROM devices.
588
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
589
    if not cdrom_disk_type:
590
      cdrom_disk_type = disk_type
591

    
592
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
593
    if iso_image:
594
      options = ",format=raw,media=cdrom"
595
      if boot_cdrom:
596
        kvm_cmd.extend(["-boot", "d"])
597
        if cdrom_disk_type != constants.HT_DISK_IDE:
598
          options = "%s,boot=on,if=%s" % (options, constants.HT_DISK_IDE)
599
        else:
600
          options = "%s,boot=on" % options
601
      else:
602
        if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
603
          if_val = ",if=virtio"
604
        else:
605
          if_val = ",if=%s" % cdrom_disk_type
606
        options = "%s%s" % (options, if_val)
607
      drive_val = "file=%s%s" % (iso_image, options)
608
      kvm_cmd.extend(["-drive", drive_val])
609

    
610
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
611
    if iso_image2:
612
      options = ",format=raw,media=cdrom"
613
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
614
        if_val = ",if=virtio"
615
      else:
616
        if_val = ",if=%s" % cdrom_disk_type
617
      options = "%s%s" % (options, if_val)
618
      drive_val = "file=%s%s" % (iso_image2, options)
619
      kvm_cmd.extend(["-drive", drive_val])
620

    
621
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
622
    if floppy_image:
623
      options = ",format=raw,media=disk"
624
      if boot_floppy:
625
        kvm_cmd.extend(["-boot", "a"])
626
        options = "%s,boot=on" % options
627
      if_val = ",if=floppy"
628
      options = "%s%s" % (options, if_val)
629
      drive_val = "file=%s%s" % (floppy_image, options)
630
      kvm_cmd.extend(["-drive", drive_val])
631

    
632
    kernel_path = hvp[constants.HV_KERNEL_PATH]
633
    if kernel_path:
634
      kvm_cmd.extend(["-kernel", kernel_path])
635
      initrd_path = hvp[constants.HV_INITRD_PATH]
636
      if initrd_path:
637
        kvm_cmd.extend(["-initrd", initrd_path])
638
      root_append = ["root=%s" % hvp[constants.HV_ROOT_PATH],
639
                     hvp[constants.HV_KERNEL_ARGS]]
640
      if hvp[constants.HV_SERIAL_CONSOLE]:
641
        root_append.append("console=ttyS0,38400")
642
      kvm_cmd.extend(["-append", " ".join(root_append)])
643

    
644
    mem_path = hvp[constants.HV_MEM_PATH]
645
    if mem_path:
646
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
647

    
648
    mouse_type = hvp[constants.HV_USB_MOUSE]
649
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
650

    
651
    if mouse_type:
652
      kvm_cmd.extend(["-usb"])
653
      kvm_cmd.extend(["-usbdevice", mouse_type])
654
    elif vnc_bind_address:
655
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
656

    
657
    keymap = hvp[constants.HV_KEYMAP]
658
    if keymap:
659
      keymap_path = self._InstanceKeymapFile(instance.name)
660
      # If a keymap file is specified, KVM won't use its internal defaults. By
661
      # first including the "en-us" layout, an error on loading the actual
662
      # layout (e.g. because it can't be found) won't lead to a non-functional
663
      # keyboard. A keyboard with incorrect keys is still better than none.
664
      utils.WriteFile(keymap_path, data="include en-us\ninclude %s\n" % keymap)
665
      kvm_cmd.extend(["-k", keymap_path])
666

    
667
    if vnc_bind_address:
668
      if netutils.IP4Address.IsValid(vnc_bind_address):
669
        if instance.network_port > constants.VNC_BASE_PORT:
670
          display = instance.network_port - constants.VNC_BASE_PORT
671
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
672
            vnc_arg = ":%d" % (display)
673
          else:
674
            vnc_arg = "%s:%d" % (vnc_bind_address, display)
675
        else:
676
          logging.error("Network port is not a valid VNC display (%d < %d)."
677
                        " Not starting VNC", instance.network_port,
678
                        constants.VNC_BASE_PORT)
679
          vnc_arg = "none"
680

    
681
        # Only allow tls and other option when not binding to a file, for now.
682
        # kvm/qemu gets confused otherwise about the filename to use.
683
        vnc_append = ""
684
        if hvp[constants.HV_VNC_TLS]:
685
          vnc_append = "%s,tls" % vnc_append
686
          if hvp[constants.HV_VNC_X509_VERIFY]:
687
            vnc_append = "%s,x509verify=%s" % (vnc_append,
688
                                               hvp[constants.HV_VNC_X509])
689
          elif hvp[constants.HV_VNC_X509]:
690
            vnc_append = "%s,x509=%s" % (vnc_append,
691
                                         hvp[constants.HV_VNC_X509])
692
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
693
          vnc_append = "%s,password" % vnc_append
694

    
695
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
696

    
697
      else:
698
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
699

    
700
      kvm_cmd.extend(["-vnc", vnc_arg])
701
    else:
702
      kvm_cmd.extend(["-nographic"])
703

    
704
    monitor_dev = ("unix:%s,server,nowait" %
705
                   self._InstanceMonitor(instance.name))
706
    kvm_cmd.extend(["-monitor", monitor_dev])
707
    if hvp[constants.HV_SERIAL_CONSOLE]:
708
      serial_dev = ("unix:%s,server,nowait" %
709
                    self._InstanceSerial(instance.name))
710
      kvm_cmd.extend(["-serial", serial_dev])
711
    else:
712
      kvm_cmd.extend(["-serial", "none"])
713

    
714
    if hvp[constants.HV_USE_LOCALTIME]:
715
      kvm_cmd.extend(["-localtime"])
716

    
717
    if hvp[constants.HV_KVM_USE_CHROOT]:
718
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
719

    
720
    # Save the current instance nics, but defer their expansion as parameters,
721
    # as we'll need to generate executable temp files for them.
722
    kvm_nics = instance.nics
723
    hvparams = hvp
724

    
725
    return (kvm_cmd, kvm_nics, hvparams)
726

    
727
  def _WriteKVMRuntime(self, instance_name, data):
728
    """Write an instance's KVM runtime
729

730
    """
731
    try:
732
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
733
                      data=data)
734
    except EnvironmentError, err:
735
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
736

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

740
    """
741
    try:
742
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
743
    except EnvironmentError, err:
744
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
745
    return file_content
746

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

750
    """
751
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
752
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
753
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
754
    self._WriteKVMRuntime(instance.name, serialized_form)
755

    
756
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
757
    """Load an instance's KVM runtime
758

759
    """
760
    if not serialized_runtime:
761
      serialized_runtime = self._ReadKVMRuntime(instance.name)
762
    loaded_runtime = serializer.Load(serialized_runtime)
763
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
764
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
765
    return (kvm_cmd, kvm_nics, hvparams)
766

    
767
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
768
    """Run the KVM cmd and check for errors
769

770
    @type name: string
771
    @param name: instance name
772
    @type kvm_cmd: list of strings
773
    @param kvm_cmd: runcmd input for kvm
774
    @type tap_fds: list of int
775
    @param tap_fds: fds of tap devices opened by Ganeti
776

777
    """
778
    try:
779
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
780
    finally:
781
      for fd in tap_fds:
782
        utils_wrapper.CloseFdNoError(fd)
783

    
784
    if result.failed:
785
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
786
                                   (name, result.fail_reason, result.output))
787
    if not self._InstancePidAlive(name)[2]:
788
      raise errors.HypervisorError("Failed to start instance %s" % name)
789

    
790
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
791
    """Execute a KVM cmd, after completing it with some last minute data
792

793
    @type incoming: tuple of strings
794
    @param incoming: (target_host_ip, port)
795

796
    """
797
    # Small _ExecuteKVMRuntime hv parameters programming howto:
798
    #  - conf_hvp contains the parameters as configured on ganeti. they might
799
    #    have changed since the instance started; only use them if the change
800
    #    won't affect the inside of the instance (which hasn't been rebooted).
801
    #  - up_hvp contains the parameters as they were when the instance was
802
    #    started, plus any new parameter which has been added between ganeti
803
    #    versions: it is paramount that those default to a value which won't
804
    #    affect the inside of the instance as well.
805
    conf_hvp = instance.hvparams
806
    name = instance.name
807
    self._CheckDown(name)
808

    
809
    temp_files = []
810

    
811
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
812
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
813

    
814
    kvm_version = self._GetKVMVersion()
815
    if kvm_version:
816
      _, v_major, v_min, _ = kvm_version
817
    else:
818
      raise errors.HypervisorError("Unable to get KVM version")
819

    
820
    # We know it's safe to run as a different user upon migration, so we'll use
821
    # the latest conf, from conf_hvp.
822
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
823
    if security_model == constants.HT_SM_USER:
824
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
825

    
826
    # We have reasons to believe changing something like the nic driver/type
827
    # upon migration won't exactly fly with the instance kernel, so for nic
828
    # related parameters we'll use up_hvp
829
    tapfds = []
830
    taps = []
831
    if not kvm_nics:
832
      kvm_cmd.extend(["-net", "none"])
833
    else:
834
      vnet_hdr = False
835
      tap_extra = ""
836
      nic_type = up_hvp[constants.HV_NIC_TYPE]
837
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
838
        # From version 0.12.0, kvm uses a new sintax for network configuration.
839
        if (v_major, v_min) >= (0, 12):
840
          nic_model = "virtio-net-pci"
841
          vnet_hdr = True
842
        else:
843
          nic_model = "virtio"
844

    
845
        if up_hvp[constants.HV_VHOST_NET]:
846
          # vhost_net is only available from version 0.13.0 or newer
847
          if (v_major, v_min) >= (0, 13):
848
            tap_extra = ",vhost=on"
849
          else:
850
            raise errors.HypervisorError("vhost_net is configured"
851
                                        " but it is not available")
852
      else:
853
        nic_model = nic_type
854

    
855
      for nic_seq, nic in enumerate(kvm_nics):
856
        tapname, tapfd = _OpenTap(vnet_hdr)
857
        tapfds.append(tapfd)
858
        taps.append(tapname)
859
        if (v_major, v_min) >= (0, 12):
860
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
861
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
862
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
863
        else:
864
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
865
                                                         nic.mac, nic_model)
866
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
867
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
868

    
869
    if incoming:
870
      target, port = incoming
871
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
872

    
873
    # Changing the vnc password doesn't bother the guest that much. At most it
874
    # will surprise people who connect to it. Whether positively or negatively
875
    # it's debatable.
876
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
877
    vnc_pwd = None
878
    if vnc_pwd_file:
879
      try:
880
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
881
      except EnvironmentError, err:
882
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
883
                                     % (vnc_pwd_file, err))
884

    
885
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
886
      utils.EnsureDirs([(self._InstanceChrootDir(name),
887
                         constants.SECURE_DIR_MODE)])
888

    
889
    # Configure the network now for starting instances and bridged interfaces,
890
    # during FinalizeMigration for incoming instances' routed interfaces
891
    for nic_seq, nic in enumerate(kvm_nics):
892
      if (incoming and
893
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
894
        continue
895
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
896

    
897
    if security_model == constants.HT_SM_POOL:
898
      ss = ssconf.SimpleStore()
899
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
900
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
901
      uid = uidpool.RequestUnusedUid(all_uids)
902
      try:
903
        username = pwd.getpwuid(uid.GetUid()).pw_name
904
        kvm_cmd.extend(["-runas", username])
905
        self._RunKVMCmd(name, kvm_cmd, tapfds)
906
      except:
907
        uidpool.ReleaseUid(uid)
908
        raise
909
      else:
910
        uid.Unlock()
911
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
912
    else:
913
      self._RunKVMCmd(name, kvm_cmd, tapfds)
914

    
915
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
916
                     constants.RUN_DIRS_MODE)])
917
    for nic_seq, tap in enumerate(taps):
918
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
919
                      data=tap)
920

    
921
    if vnc_pwd:
922
      change_cmd = "change vnc password %s" % vnc_pwd
923
      self._CallMonitorCommand(instance.name, change_cmd)
924

    
925
    for filename in temp_files:
926
      utils.RemoveFile(filename)
927

    
928
  def StartInstance(self, instance, block_devices, startup_paused):
929
    """Start an instance.
930

931
    """
932
    self._CheckDown(instance.name)
933
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
934
                                           startup_paused)
935
    self._SaveKVMRuntime(instance, kvm_runtime)
936
    self._ExecuteKVMRuntime(instance, kvm_runtime)
937

    
938
  def _CallMonitorCommand(self, instance_name, command):
939
    """Invoke a command on the instance monitor.
940

941
    """
942
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
943
             (utils.ShellQuote(command),
944
              constants.SOCAT_PATH,
945
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
946
    result = utils.RunCmd(socat)
947
    if result.failed:
948
      msg = ("Failed to send command '%s' to instance %s."
949
             " output: %s, error: %s, fail_reason: %s" %
950
             (command, instance_name,
951
              result.stdout, result.stderr, result.fail_reason))
952
      raise errors.HypervisorError(msg)
953

    
954
    return result
955

    
956
  @classmethod
957
  def _GetKVMVersion(cls):
958
    """Return the installed KVM version
959

960
    @return: (version, v_maj, v_min, v_rev), or None
961

962
    """
963
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
964
    if result.failed:
965
      return None
966
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
967
    if not match:
968
      return None
969

    
970
    return (match.group(0), int(match.group(1)), int(match.group(2)),
971
            int(match.group(3)))
972

    
973
  def StopInstance(self, instance, force=False, retry=False, name=None):
974
    """Stop an instance.
975

976
    """
977
    if name is not None and not force:
978
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
979
    if name is None:
980
      name = instance.name
981
      acpi = instance.hvparams[constants.HV_ACPI]
982
    else:
983
      acpi = False
984
    _, pid, alive = self._InstancePidAlive(name)
985
    if pid > 0 and alive:
986
      if force or not acpi:
987
        utils.KillProcess(pid)
988
      else:
989
        self._CallMonitorCommand(name, "system_powerdown")
990

    
991
  def CleanupInstance(self, instance_name):
992
    """Cleanup after a stopped instance
993

994
    """
995
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
996
    if pid > 0 and alive:
997
      raise errors.HypervisorError("Cannot cleanup a live instance")
998
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
999

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

1003
    """
1004
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1005
    # socket the instance will stop, but now power up again. So we'll resort
1006
    # to shutdown and restart.
1007
    _, _, alive = self._InstancePidAlive(instance.name)
1008
    if not alive:
1009
      raise errors.HypervisorError("Failed to reboot instance %s:"
1010
                                   " not running" % instance.name)
1011
    # StopInstance will delete the saved KVM runtime so:
1012
    # ...first load it...
1013
    kvm_runtime = self._LoadKVMRuntime(instance)
1014
    # ...now we can safely call StopInstance...
1015
    if not self.StopInstance(instance):
1016
      self.StopInstance(instance, force=True)
1017
    # ...and finally we can save it again, and execute it...
1018
    self._SaveKVMRuntime(instance, kvm_runtime)
1019
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1020

    
1021
  def MigrationInfo(self, instance):
1022
    """Get instance information to perform a migration.
1023

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

1029
    """
1030
    return self._ReadKVMRuntime(instance.name)
1031

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

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

1042
    """
1043
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1044
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1045
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1046

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

1050
    Stop the incoming mode KVM.
1051

1052
    @type instance: L{objects.Instance}
1053
    @param instance: instance whose migration is being finalized
1054

1055
    """
1056
    if success:
1057
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1058
      kvm_nics = kvm_runtime[1]
1059

    
1060
      for nic_seq, nic in enumerate(kvm_nics):
1061
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1062
          # Bridged interfaces have already been configured
1063
          continue
1064
        try:
1065
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1066
        except EnvironmentError, err:
1067
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1068
                          instance.name, nic_seq, str(err))
1069
          continue
1070
        try:
1071
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1072
        except errors.HypervisorError, err:
1073
          logging.warning(str(err))
1074

    
1075
      self._WriteKVMRuntime(instance.name, info)
1076
    else:
1077
      self.StopInstance(instance, force=True)
1078

    
1079
  def MigrateInstance(self, instance, target, live):
1080
    """Migrate an instance to a target node.
1081

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

1085
    @type instance: L{objects.Instance}
1086
    @param instance: the instance to be migrated
1087
    @type target: string
1088
    @param target: ip address of the target node
1089
    @type live: boolean
1090
    @param live: perform a live migration
1091

1092
    """
1093
    instance_name = instance.name
1094
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1095
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1096
    if not alive:
1097
      raise errors.HypervisorError("Instance not running, cannot migrate")
1098

    
1099
    if not live:
1100
      self._CallMonitorCommand(instance_name, "stop")
1101

    
1102
    migrate_command = ("migrate_set_speed %dm" %
1103
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1104
    self._CallMonitorCommand(instance_name, migrate_command)
1105

    
1106
    migrate_command = ("migrate_set_downtime %dms" %
1107
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1108
    self._CallMonitorCommand(instance_name, migrate_command)
1109

    
1110
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1111
    self._CallMonitorCommand(instance_name, migrate_command)
1112

    
1113
    info_command = "info migrate"
1114
    done = False
1115
    broken_answers = 0
1116
    while not done:
1117
      result = self._CallMonitorCommand(instance_name, info_command)
1118
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1119
      if not match:
1120
        broken_answers += 1
1121
        if not result.stdout:
1122
          logging.info("KVM: empty 'info migrate' result")
1123
        else:
1124
          logging.warning("KVM: unknown 'info migrate' result: %s",
1125
                          result.stdout)
1126
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1127
      else:
1128
        status = match.group(1)
1129
        if status == "completed":
1130
          done = True
1131
        elif status == "active":
1132
          # reset the broken answers count
1133
          broken_answers = 0
1134
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1135
        elif status == "failed" or status == "cancelled":
1136
          if not live:
1137
            self._CallMonitorCommand(instance_name, 'cont')
1138
          raise errors.HypervisorError("Migration %s at the kvm level" %
1139
                                       status)
1140
        else:
1141
          logging.warning("KVM: unknown migration status '%s'", status)
1142
          broken_answers += 1
1143
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1144
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1145
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1146

    
1147
    utils.KillProcess(pid)
1148
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1149

    
1150
  def GetNodeInfo(self):
1151
    """Return information about the node.
1152

1153
    This is just a wrapper over the base GetLinuxNodeInfo method.
1154

1155
    @return: a dict with the following keys (values in MiB):
1156
          - memory_total: the total memory size on the node
1157
          - memory_free: the available memory on the node for instances
1158
          - memory_dom0: the memory used by the node itself, if available
1159

1160
    """
1161
    return self.GetLinuxNodeInfo()
1162

    
1163
  @classmethod
1164
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1165
    """Return a command for connecting to the console of an instance.
1166

1167
    """
1168
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1169
      cmd = [constants.KVM_CONSOLE_WRAPPER,
1170
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1171
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1172
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1173
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1174
      return objects.InstanceConsole(instance=instance.name,
1175
                                     kind=constants.CONS_SSH,
1176
                                     host=instance.primary_node,
1177
                                     user=constants.GANETI_RUNAS,
1178
                                     command=cmd)
1179

    
1180
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1181
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1182
      display = instance.network_port - constants.VNC_BASE_PORT
1183
      return objects.InstanceConsole(instance=instance.name,
1184
                                     kind=constants.CONS_VNC,
1185
                                     host=vnc_bind_address,
1186
                                     port=instance.network_port,
1187
                                     display=display)
1188

    
1189
    return objects.InstanceConsole(instance=instance.name,
1190
                                   kind=constants.CONS_MESSAGE,
1191
                                   message=("No serial shell for instance %s" %
1192
                                            instance.name))
1193

    
1194
  def Verify(self):
1195
    """Verify the hypervisor.
1196

1197
    Check that the binary exists.
1198

1199
    """
1200
    if not os.path.exists(constants.KVM_PATH):
1201
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1202
    if not os.path.exists(constants.SOCAT_PATH):
1203
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1204

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

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

1213
    """
1214
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1215

    
1216
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1217
    if kernel_path:
1218
      if not hvparams[constants.HV_ROOT_PATH]:
1219
        raise errors.HypervisorError("Need a root partition for the instance,"
1220
                                     " if a kernel is defined")
1221

    
1222
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1223
        not hvparams[constants.HV_VNC_X509]):
1224
      raise errors.HypervisorError("%s must be defined, if %s is" %
1225
                                   (constants.HV_VNC_X509,
1226
                                    constants.HV_VNC_X509_VERIFY))
1227

    
1228
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1229
    if (boot_order == constants.HT_BO_CDROM and
1230
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1231
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1232
                                   " ISO path")
1233

    
1234
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1235
    if security_model == constants.HT_SM_USER:
1236
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1237
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1238
                                     " must be specified")
1239
    elif (security_model == constants.HT_SM_NONE or
1240
          security_model == constants.HT_SM_POOL):
1241
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1242
        raise errors.HypervisorError("Cannot have a security domain when the"
1243
                                     " security model is 'none' or 'pool'")
1244

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

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

1253
    """
1254
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1255

    
1256
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1257
    if security_model == constants.HT_SM_USER:
1258
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1259
      try:
1260
        pwd.getpwnam(username)
1261
      except KeyError:
1262
        raise errors.HypervisorError("Unknown security domain user %s"
1263
                                     % username)
1264

    
1265
  @classmethod
1266
  def PowercycleNode(cls):
1267
    """KVM powercycle, just a wrapper over Linux powercycle.
1268

1269
    """
1270
    cls.LinuxPowercycle()