Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 990ade2d

History | View | Annotate | Download (44.7 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
    constants.HV_REBOOT_BEHAVIOR:
198
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS)
199
    }
200

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

    
206
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
207

    
208
  ANCILLARY_FILES = [
209
    _KVM_NETWORK_SCRIPT,
210
    ]
211

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

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

223
    """
224
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
225

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

230
    """
231
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
232

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

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

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

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

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

    
258
    instance = None
259
    memory = 0
260
    vcpus = 0
261

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

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

    
276
    return (instance, memory, vcpus)
277

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

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

286
    """
287
    pidfile = self._InstancePidFile(instance_name)
288
    pid = utils.ReadPidFile(pidfile)
289

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

    
297
    return (pidfile, pid, alive)
298

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

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

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

312
    """
313
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
314

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

319
    """
320
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
321

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

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

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

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

338
    """
339
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
340

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

345
    """
346
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
347

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

353
    """
354
    return utils.PathJoin(cls._NICS_DIR, instance_name)
355

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

360
    """
361
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
362

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

367
    """
368
    return utils.PathJoin(cls._KEYMAP_DIR, instance_name)
369

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

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

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

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

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

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

437
    """
438

    
439
    if instance.tags:
440
      tags = " ".join(instance.tags)
441
    else:
442
      tags = ""
443

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

    
454
    if nic.ip:
455
      env["IP"] = nic.ip
456

    
457
    if nic.nicparams[constants.NIC_LINK]:
458
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
459

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

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

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

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

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

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

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

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

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

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

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

504
    @return: list of tuples (name, id, memory, vcpus, stat, times)
505

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

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

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

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

    
544
    hvp = instance.hvparams
545
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
546
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
547
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
548
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
549

    
550
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
551
      kvm_cmd.extend(["-enable-kvm"])
552
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
553
      kvm_cmd.extend(["-disable-kvm"])
554

    
555
    if boot_network:
556
      kvm_cmd.extend(["-boot", "n"])
557

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

    
588
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
589
                                                cache_val)
590
      kvm_cmd.extend(["-drive", drive_val])
591

    
592
    #Now we can specify a different device type for CDROM devices.
593
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
594
    if not cdrom_disk_type:
595
      cdrom_disk_type = disk_type
596

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

    
615
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
616
    if iso_image2:
617
      options = ",format=raw,media=cdrom"
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_image2, options)
624
      kvm_cmd.extend(["-drive", drive_val])
625

    
626
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
627
    if floppy_image:
628
      options = ",format=raw,media=disk"
629
      if boot_floppy:
630
        kvm_cmd.extend(["-boot", "a"])
631
        options = "%s,boot=on" % options
632
      if_val = ",if=floppy"
633
      options = "%s%s" % (options, if_val)
634
      drive_val = "file=%s%s" % (floppy_image, options)
635
      kvm_cmd.extend(["-drive", drive_val])
636

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

    
649
    mem_path = hvp[constants.HV_MEM_PATH]
650
    if mem_path:
651
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
652

    
653
    mouse_type = hvp[constants.HV_USB_MOUSE]
654
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
655

    
656
    if mouse_type:
657
      kvm_cmd.extend(["-usb"])
658
      kvm_cmd.extend(["-usbdevice", mouse_type])
659
    elif vnc_bind_address:
660
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
661

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

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

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

    
700
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
701

    
702
      else:
703
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
704

    
705
      kvm_cmd.extend(["-vnc", vnc_arg])
706
    else:
707
      kvm_cmd.extend(["-nographic"])
708

    
709
    monitor_dev = ("unix:%s,server,nowait" %
710
                   self._InstanceMonitor(instance.name))
711
    kvm_cmd.extend(["-monitor", monitor_dev])
712
    if hvp[constants.HV_SERIAL_CONSOLE]:
713
      serial_dev = ("unix:%s,server,nowait" %
714
                    self._InstanceSerial(instance.name))
715
      kvm_cmd.extend(["-serial", serial_dev])
716
    else:
717
      kvm_cmd.extend(["-serial", "none"])
718

    
719
    if hvp[constants.HV_USE_LOCALTIME]:
720
      kvm_cmd.extend(["-localtime"])
721

    
722
    if hvp[constants.HV_KVM_USE_CHROOT]:
723
      kvm_cmd.extend(["-chroot", self._InstanceChrootDir(instance.name)])
724

    
725
    # Save the current instance nics, but defer their expansion as parameters,
726
    # as we'll need to generate executable temp files for them.
727
    kvm_nics = instance.nics
728
    hvparams = hvp
729

    
730
    return (kvm_cmd, kvm_nics, hvparams)
731

    
732
  def _WriteKVMRuntime(self, instance_name, data):
733
    """Write an instance's KVM runtime
734

735
    """
736
    try:
737
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
738
                      data=data)
739
    except EnvironmentError, err:
740
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
741

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

745
    """
746
    try:
747
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
748
    except EnvironmentError, err:
749
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
750
    return file_content
751

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

755
    """
756
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
757
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
758
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
759
    self._WriteKVMRuntime(instance.name, serialized_form)
760

    
761
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
762
    """Load an instance's KVM runtime
763

764
    """
765
    if not serialized_runtime:
766
      serialized_runtime = self._ReadKVMRuntime(instance.name)
767
    loaded_runtime = serializer.Load(serialized_runtime)
768
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
769
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
770
    return (kvm_cmd, kvm_nics, hvparams)
771

    
772
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
773
    """Run the KVM cmd and check for errors
774

775
    @type name: string
776
    @param name: instance name
777
    @type kvm_cmd: list of strings
778
    @param kvm_cmd: runcmd input for kvm
779
    @type tap_fds: list of int
780
    @param tap_fds: fds of tap devices opened by Ganeti
781

782
    """
783
    try:
784
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
785
    finally:
786
      for fd in tap_fds:
787
        utils_wrapper.CloseFdNoError(fd)
788

    
789
    if result.failed:
790
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
791
                                   (name, result.fail_reason, result.output))
792
    if not self._InstancePidAlive(name)[2]:
793
      raise errors.HypervisorError("Failed to start instance %s" % name)
794

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

798
    @type incoming: tuple of strings
799
    @param incoming: (target_host_ip, port)
800

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

    
814
    temp_files = []
815

    
816
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
817
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
818

    
819
    kvm_version = self._GetKVMVersion()
820
    if kvm_version:
821
      _, v_major, v_min, _ = kvm_version
822
    else:
823
      raise errors.HypervisorError("Unable to get KVM version")
824

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

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

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

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

    
874
    if incoming:
875
      target, port = incoming
876
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
877

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

    
890
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
891
      utils.EnsureDirs([(self._InstanceChrootDir(name),
892
                         constants.SECURE_DIR_MODE)])
893

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

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

    
920
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
921
                     constants.RUN_DIRS_MODE)])
922
    for nic_seq, tap in enumerate(taps):
923
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
924
                      data=tap)
925

    
926
    if vnc_pwd:
927
      change_cmd = "change vnc password %s" % vnc_pwd
928
      self._CallMonitorCommand(instance.name, change_cmd)
929

    
930
    for filename in temp_files:
931
      utils.RemoveFile(filename)
932

    
933
  def StartInstance(self, instance, block_devices, startup_paused):
934
    """Start an instance.
935

936
    """
937
    self._CheckDown(instance.name)
938
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
939
                                           startup_paused)
940
    self._SaveKVMRuntime(instance, kvm_runtime)
941
    self._ExecuteKVMRuntime(instance, kvm_runtime)
942

    
943
  def _CallMonitorCommand(self, instance_name, command):
944
    """Invoke a command on the instance monitor.
945

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

    
959
    return result
960

    
961
  @classmethod
962
  def _GetKVMVersion(cls):
963
    """Return the installed KVM version
964

965
    @return: (version, v_maj, v_min, v_rev), or None
966

967
    """
968
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
969
    if result.failed:
970
      return None
971
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
972
    if not match:
973
      return None
974

    
975
    return (match.group(0), int(match.group(1)), int(match.group(2)),
976
            int(match.group(3)))
977

    
978
  def StopInstance(self, instance, force=False, retry=False, name=None):
979
    """Stop an instance.
980

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

    
996
  def CleanupInstance(self, instance_name):
997
    """Cleanup after a stopped instance
998

999
    """
1000
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1001
    if pid > 0 and alive:
1002
      raise errors.HypervisorError("Cannot cleanup a live instance")
1003
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1004

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

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

    
1026
  def MigrationInfo(self, instance):
1027
    """Get instance information to perform a migration.
1028

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

1034
    """
1035
    return self._ReadKVMRuntime(instance.name)
1036

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

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

1047
    """
1048
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1049
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1050
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1051

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

1055
    Stop the incoming mode KVM.
1056

1057
    @type instance: L{objects.Instance}
1058
    @param instance: instance whose migration is being finalized
1059

1060
    """
1061
    if success:
1062
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1063
      kvm_nics = kvm_runtime[1]
1064

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

    
1080
      self._WriteKVMRuntime(instance.name, info)
1081
    else:
1082
      self.StopInstance(instance, force=True)
1083

    
1084
  def MigrateInstance(self, instance, target, live):
1085
    """Migrate an instance to a target node.
1086

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

1090
    @type instance: L{objects.Instance}
1091
    @param instance: the instance to be migrated
1092
    @type target: string
1093
    @param target: ip address of the target node
1094
    @type live: boolean
1095
    @param live: perform a live migration
1096

1097
    """
1098
    instance_name = instance.name
1099
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1100
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1101
    if not alive:
1102
      raise errors.HypervisorError("Instance not running, cannot migrate")
1103

    
1104
    if not live:
1105
      self._CallMonitorCommand(instance_name, "stop")
1106

    
1107
    migrate_command = ("migrate_set_speed %dm" %
1108
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1109
    self._CallMonitorCommand(instance_name, migrate_command)
1110

    
1111
    migrate_command = ("migrate_set_downtime %dms" %
1112
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1113
    self._CallMonitorCommand(instance_name, migrate_command)
1114

    
1115
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1116
    self._CallMonitorCommand(instance_name, migrate_command)
1117

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

    
1152
    utils.KillProcess(pid)
1153
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1154

    
1155
  def GetNodeInfo(self):
1156
    """Return information about the node.
1157

1158
    This is just a wrapper over the base GetLinuxNodeInfo method.
1159

1160
    @return: a dict with the following keys (values in MiB):
1161
          - memory_total: the total memory size on the node
1162
          - memory_free: the available memory on the node for instances
1163
          - memory_dom0: the memory used by the node itself, if available
1164

1165
    """
1166
    return self.GetLinuxNodeInfo()
1167

    
1168
  @classmethod
1169
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1170
    """Return a command for connecting to the console of an instance.
1171

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

    
1185
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1186
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1187
      display = instance.network_port - constants.VNC_BASE_PORT
1188
      return objects.InstanceConsole(instance=instance.name,
1189
                                     kind=constants.CONS_VNC,
1190
                                     host=vnc_bind_address,
1191
                                     port=instance.network_port,
1192
                                     display=display)
1193

    
1194
    return objects.InstanceConsole(instance=instance.name,
1195
                                   kind=constants.CONS_MESSAGE,
1196
                                   message=("No serial shell for instance %s" %
1197
                                            instance.name))
1198

    
1199
  def Verify(self):
1200
    """Verify the hypervisor.
1201

1202
    Check that the binary exists.
1203

1204
    """
1205
    if not os.path.exists(constants.KVM_PATH):
1206
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1207
    if not os.path.exists(constants.SOCAT_PATH):
1208
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1209

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

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

1218
    """
1219
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1220

    
1221
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1222
    if kernel_path:
1223
      if not hvparams[constants.HV_ROOT_PATH]:
1224
        raise errors.HypervisorError("Need a root partition for the instance,"
1225
                                     " if a kernel is defined")
1226

    
1227
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1228
        not hvparams[constants.HV_VNC_X509]):
1229
      raise errors.HypervisorError("%s must be defined, if %s is" %
1230
                                   (constants.HV_VNC_X509,
1231
                                    constants.HV_VNC_X509_VERIFY))
1232

    
1233
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1234
    if (boot_order == constants.HT_BO_CDROM and
1235
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1236
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1237
                                   " ISO path")
1238

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

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

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

1258
    """
1259
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1260

    
1261
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1262
    if security_model == constants.HT_SM_USER:
1263
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1264
      try:
1265
        pwd.getpwnam(username)
1266
      except KeyError:
1267
        raise errors.HypervisorError("Unknown security domain user %s"
1268
                                     % username)
1269

    
1270
  @classmethod
1271
  def PowercycleNode(cls):
1272
    """KVM powercycle, just a wrapper over Linux powercycle.
1273

1274
    """
1275
    cls.LinuxPowercycle()