Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ a7f884d3

History | View | Annotate | Download (49.1 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_SPICE_BIND: hv_base.NO_CHECK, # will be checked later
169
    constants.HV_KVM_SPICE_IP_VERSION:
170
      (False, lambda x: (x == constants.IFACE_NO_IP_VERSION_SPECIFIED or
171
                         x in constants.VALID_IP_VERSIONS),
172
       "the SPICE IP version should be 4 or 6",
173
       None, None),
174
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
175
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
176
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
177
    constants.HV_BOOT_ORDER:
178
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
179
    constants.HV_NIC_TYPE:
180
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
181
    constants.HV_DISK_TYPE:
182
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
183
    constants.HV_KVM_CDROM_DISK_TYPE:
184
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
185
    constants.HV_USB_MOUSE:
186
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
187
    constants.HV_KEYMAP: hv_base.NO_CHECK,
188
    constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
189
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
190
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
191
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
192
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
193
    constants.HV_DISK_CACHE:
194
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
195
    constants.HV_SECURITY_MODEL:
196
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
197
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
198
    constants.HV_KVM_FLAG:
199
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
200
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
201
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
202
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
203
    constants.HV_REBOOT_BEHAVIOR:
204
      hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
205
    constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
206
    }
207

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

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

    
215
  ANCILLARY_FILES = [
216
    _KVM_NETWORK_SCRIPT,
217
    ]
218

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

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

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

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

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

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

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

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

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

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

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

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

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

    
283
    return (instance, memory, vcpus)
284

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

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

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

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

    
304
    return (pidfile, pid, alive)
305

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

444
    """
445

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

527
    """
528
    _, v_major, v_min, _ = self._GetKVMVersion()
529

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

    
547
    hvp = instance.hvparams
548
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
549
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
550
    boot_floppy = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_FLOPPY
551
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
552

    
553
    self.ValidateParameters(hvp)
554

    
555
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
556
      kvm_cmd.extend(["-enable-kvm"])
557
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
558
      kvm_cmd.extend(["-disable-kvm"])
559

    
560
    if boot_network:
561
      kvm_cmd.extend(["-boot", "n"])
562

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

    
593
      drive_val = "file=%s,format=raw%s%s%s" % (dev_path, if_val, boot_val,
594
                                                cache_val)
595
      kvm_cmd.extend(["-drive", drive_val])
596

    
597
    #Now we can specify a different device type for CDROM devices.
598
    cdrom_disk_type = hvp[constants.HV_KVM_CDROM_DISK_TYPE]
599
    if not cdrom_disk_type:
600
      cdrom_disk_type = disk_type
601

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

    
620
    iso_image2 = hvp[constants.HV_KVM_CDROM2_IMAGE_PATH]
621
    if iso_image2:
622
      options = ",format=raw,media=cdrom"
623
      if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
624
        if_val = ",if=virtio"
625
      else:
626
        if_val = ",if=%s" % cdrom_disk_type
627
      options = "%s%s" % (options, if_val)
628
      drive_val = "file=%s%s" % (iso_image2, options)
629
      kvm_cmd.extend(["-drive", drive_val])
630

    
631
    floppy_image = hvp[constants.HV_KVM_FLOPPY_IMAGE_PATH]
632
    if floppy_image:
633
      options = ",format=raw,media=disk"
634
      if boot_floppy:
635
        kvm_cmd.extend(["-boot", "a"])
636
        options = "%s,boot=on" % options
637
      if_val = ",if=floppy"
638
      options = "%s%s" % (options, if_val)
639
      drive_val = "file=%s%s" % (floppy_image, options)
640
      kvm_cmd.extend(["-drive", drive_val])
641

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

    
654
    mem_path = hvp[constants.HV_MEM_PATH]
655
    if mem_path:
656
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
657

    
658
    mouse_type = hvp[constants.HV_USB_MOUSE]
659
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
660

    
661
    if mouse_type:
662
      kvm_cmd.extend(["-usb"])
663
      kvm_cmd.extend(["-usbdevice", mouse_type])
664
    elif vnc_bind_address:
665
      kvm_cmd.extend(["-usbdevice", constants.HT_MOUSE_TABLET])
666

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

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

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

    
705
        vnc_arg = "%s%s" % (vnc_arg, vnc_append)
706

    
707
      else:
708
        vnc_arg = "unix:%s/%s.vnc" % (vnc_bind_address, instance.name)
709

    
710
      kvm_cmd.extend(["-vnc", vnc_arg])
711
    else:
712
      kvm_cmd.extend(["-nographic"])
713

    
714
    monitor_dev = ("unix:%s,server,nowait" %
715
                   self._InstanceMonitor(instance.name))
716
    kvm_cmd.extend(["-monitor", monitor_dev])
717
    if hvp[constants.HV_SERIAL_CONSOLE]:
718
      serial_dev = ("unix:%s,server,nowait" %
719
                    self._InstanceSerial(instance.name))
720
      kvm_cmd.extend(["-serial", serial_dev])
721
    else:
722
      kvm_cmd.extend(["-serial", "none"])
723

    
724
    spice_bind = hvp[constants.HV_KVM_SPICE_BIND]
725
    if spice_bind:
726
      if netutils.IsValidInterface(spice_bind):
727
        # The user specified a network interface, we have to figure out the IP
728
        # address.
729
        addresses = netutils.GetInterfaceIpAddresses(spice_bind)
730
        spice_ip_version = hvp[constants.HV_KVM_SPICE_IP_VERSION]
731

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

    
740
        # the user did not specify an IP version, we have to figure it out
741
        elif (addresses[constants.IP4_VERSION] and
742
              addresses[constants.IP6_VERSION]):
743
          # we have both ipv4 and ipv6, let's use the cluster default IP
744
          # version
745
          cluster_family = ssconf.SimpleStore().GetPrimaryIPFamily()
746
          spice_ip_version = netutils.IPAddress.GetVersionFromAddressFamily(
747
              cluster_family)
748
        elif addresses[constants.IP4_VERSION]:
749
          spice_ip_version = constants.IP4_VERSION
750
        else:
751
          spice_ip_version = constants.IP6_VERSION
752

    
753
        spice_address = addresses[spice_ip_version][0]
754

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

    
760
      spice_arg = "addr=%s,ipv%s,port=%s" % (spice_address,
761
                                             spice_ip_version,
762
                                             instance.network_port)
763

    
764
      spice_arg = "%s,disable-ticketing" % spice_arg
765

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

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

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

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

    
780
    return (kvm_cmd, kvm_nics, hvparams)
781

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
864
    temp_files = []
865

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

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

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

    
877
    # We have reasons to believe changing something like the nic driver/type
878
    # upon migration won't exactly fly with the instance kernel, so for nic
879
    # related parameters we'll use up_hvp
880
    tapfds = []
881
    taps = []
882
    if not kvm_nics:
883
      kvm_cmd.extend(["-net", "none"])
884
    else:
885
      vnet_hdr = False
886
      tap_extra = ""
887
      nic_type = up_hvp[constants.HV_NIC_TYPE]
888
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
889
        # From version 0.12.0, kvm uses a new sintax for network configuration.
890
        if (v_major, v_min) >= (0, 12):
891
          nic_model = "virtio-net-pci"
892
          vnet_hdr = True
893
        else:
894
          nic_model = "virtio"
895

    
896
        if up_hvp[constants.HV_VHOST_NET]:
897
          # vhost_net is only available from version 0.13.0 or newer
898
          if (v_major, v_min) >= (0, 13):
899
            tap_extra = ",vhost=on"
900
          else:
901
            raise errors.HypervisorError("vhost_net is configured"
902
                                        " but it is not available")
903
      else:
904
        nic_model = nic_type
905

    
906
      for nic_seq, nic in enumerate(kvm_nics):
907
        tapname, tapfd = _OpenTap(vnet_hdr)
908
        tapfds.append(tapfd)
909
        taps.append(tapname)
910
        if (v_major, v_min) >= (0, 12):
911
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
912
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
913
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
914
        else:
915
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
916
                                                         nic.mac, nic_model)
917
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
918
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
919

    
920
    if incoming:
921
      target, port = incoming
922
      kvm_cmd.extend(["-incoming", "tcp:%s:%s" % (target, port)])
923

    
924
    # Changing the vnc password doesn't bother the guest that much. At most it
925
    # will surprise people who connect to it. Whether positively or negatively
926
    # it's debatable.
927
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
928
    vnc_pwd = None
929
    if vnc_pwd_file:
930
      try:
931
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
932
      except EnvironmentError, err:
933
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
934
                                     % (vnc_pwd_file, err))
935

    
936
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
937
      utils.EnsureDirs([(self._InstanceChrootDir(name),
938
                         constants.SECURE_DIR_MODE)])
939

    
940
    # Configure the network now for starting instances and bridged interfaces,
941
    # during FinalizeMigration for incoming instances' routed interfaces
942
    for nic_seq, nic in enumerate(kvm_nics):
943
      if (incoming and
944
          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
945
        continue
946
      self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
947

    
948
    if security_model == constants.HT_SM_POOL:
949
      ss = ssconf.SimpleStore()
950
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
951
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
952
      uid = uidpool.RequestUnusedUid(all_uids)
953
      try:
954
        username = pwd.getpwuid(uid.GetUid()).pw_name
955
        kvm_cmd.extend(["-runas", username])
956
        self._RunKVMCmd(name, kvm_cmd, tapfds)
957
      except:
958
        uidpool.ReleaseUid(uid)
959
        raise
960
      else:
961
        uid.Unlock()
962
        utils.WriteFile(self._InstanceUidFile(name), data=uid.AsStr())
963
    else:
964
      self._RunKVMCmd(name, kvm_cmd, tapfds)
965

    
966
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
967
                     constants.RUN_DIRS_MODE)])
968
    for nic_seq, tap in enumerate(taps):
969
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
970
                      data=tap)
971

    
972
    if vnc_pwd:
973
      change_cmd = "change vnc password %s" % vnc_pwd
974
      self._CallMonitorCommand(instance.name, change_cmd)
975

    
976
    for filename in temp_files:
977
      utils.RemoveFile(filename)
978

    
979
  def StartInstance(self, instance, block_devices, startup_paused):
980
    """Start an instance.
981

982
    """
983
    self._CheckDown(instance.name)
984
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices,
985
                                           startup_paused)
986
    self._SaveKVMRuntime(instance, kvm_runtime)
987
    self._ExecuteKVMRuntime(instance, kvm_runtime)
988

    
989
  def _CallMonitorCommand(self, instance_name, command):
990
    """Invoke a command on the instance monitor.
991

992
    """
993
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
994
             (utils.ShellQuote(command),
995
              constants.SOCAT_PATH,
996
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
997
    result = utils.RunCmd(socat)
998
    if result.failed:
999
      msg = ("Failed to send command '%s' to instance %s."
1000
             " output: %s, error: %s, fail_reason: %s" %
1001
             (command, instance_name,
1002
              result.stdout, result.stderr, result.fail_reason))
1003
      raise errors.HypervisorError(msg)
1004

    
1005
    return result
1006

    
1007
  @classmethod
1008
  def _GetKVMVersion(cls):
1009
    """Return the installed KVM version.
1010

1011
    @return: (version, v_maj, v_min, v_rev)
1012
    @raise L{errors.HypervisorError}: when the KVM version cannot be retrieved
1013

1014
    """
1015
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
1016
    if result.failed:
1017
      raise errors.HypervisorError("Unable to get KVM version")
1018
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
1019
    if not match:
1020
      raise errors.HypervisorError("Unable to get KVM version")
1021

    
1022
    return (match.group(0), int(match.group(1)), int(match.group(2)),
1023
            int(match.group(3)))
1024

    
1025
  def StopInstance(self, instance, force=False, retry=False, name=None):
1026
    """Stop an instance.
1027

1028
    """
1029
    if name is not None and not force:
1030
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
1031
    if name is None:
1032
      name = instance.name
1033
      acpi = instance.hvparams[constants.HV_ACPI]
1034
    else:
1035
      acpi = False
1036
    _, pid, alive = self._InstancePidAlive(name)
1037
    if pid > 0 and alive:
1038
      if force or not acpi:
1039
        utils.KillProcess(pid)
1040
      else:
1041
        self._CallMonitorCommand(name, "system_powerdown")
1042

    
1043
  def CleanupInstance(self, instance_name):
1044
    """Cleanup after a stopped instance
1045

1046
    """
1047
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1048
    if pid > 0 and alive:
1049
      raise errors.HypervisorError("Cannot cleanup a live instance")
1050
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1051

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

1055
    """
1056
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
1057
    # socket the instance will stop, but now power up again. So we'll resort
1058
    # to shutdown and restart.
1059
    _, _, alive = self._InstancePidAlive(instance.name)
1060
    if not alive:
1061
      raise errors.HypervisorError("Failed to reboot instance %s:"
1062
                                   " not running" % instance.name)
1063
    # StopInstance will delete the saved KVM runtime so:
1064
    # ...first load it...
1065
    kvm_runtime = self._LoadKVMRuntime(instance)
1066
    # ...now we can safely call StopInstance...
1067
    if not self.StopInstance(instance):
1068
      self.StopInstance(instance, force=True)
1069
    # ...and finally we can save it again, and execute it...
1070
    self._SaveKVMRuntime(instance, kvm_runtime)
1071
    self._ExecuteKVMRuntime(instance, kvm_runtime)
1072

    
1073
  def MigrationInfo(self, instance):
1074
    """Get instance information to perform a migration.
1075

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

1081
    """
1082
    return self._ReadKVMRuntime(instance.name)
1083

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

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

1094
    """
1095
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1096
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1097
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1098

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

1102
    Stop the incoming mode KVM.
1103

1104
    @type instance: L{objects.Instance}
1105
    @param instance: instance whose migration is being finalized
1106

1107
    """
1108
    if success:
1109
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1110
      kvm_nics = kvm_runtime[1]
1111

    
1112
      for nic_seq, nic in enumerate(kvm_nics):
1113
        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1114
          # Bridged interfaces have already been configured
1115
          continue
1116
        try:
1117
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1118
        except EnvironmentError, err:
1119
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1120
                          instance.name, nic_seq, str(err))
1121
          continue
1122
        try:
1123
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1124
        except errors.HypervisorError, err:
1125
          logging.warning(str(err))
1126

    
1127
      self._WriteKVMRuntime(instance.name, info)
1128
    else:
1129
      self.StopInstance(instance, force=True)
1130

    
1131
  def MigrateInstance(self, instance, target, live):
1132
    """Migrate an instance to a target node.
1133

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

1137
    @type instance: L{objects.Instance}
1138
    @param instance: the instance to be migrated
1139
    @type target: string
1140
    @param target: ip address of the target node
1141
    @type live: boolean
1142
    @param live: perform a live migration
1143

1144
    """
1145
    instance_name = instance.name
1146
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1147
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1148
    if not alive:
1149
      raise errors.HypervisorError("Instance not running, cannot migrate")
1150

    
1151
    if not live:
1152
      self._CallMonitorCommand(instance_name, "stop")
1153

    
1154
    migrate_command = ("migrate_set_speed %dm" %
1155
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1156
    self._CallMonitorCommand(instance_name, migrate_command)
1157

    
1158
    migrate_command = ("migrate_set_downtime %dms" %
1159
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1160
    self._CallMonitorCommand(instance_name, migrate_command)
1161

    
1162
    migrate_command = "migrate -d tcp:%s:%s" % (target, port)
1163
    self._CallMonitorCommand(instance_name, migrate_command)
1164

    
1165
    info_command = "info migrate"
1166
    done = False
1167
    broken_answers = 0
1168
    while not done:
1169
      result = self._CallMonitorCommand(instance_name, info_command)
1170
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1171
      if not match:
1172
        broken_answers += 1
1173
        if not result.stdout:
1174
          logging.info("KVM: empty 'info migrate' result")
1175
        else:
1176
          logging.warning("KVM: unknown 'info migrate' result: %s",
1177
                          result.stdout)
1178
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1179
      else:
1180
        status = match.group(1)
1181
        if status == "completed":
1182
          done = True
1183
        elif status == "active":
1184
          # reset the broken answers count
1185
          broken_answers = 0
1186
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1187
        elif status == "failed" or status == "cancelled":
1188
          if not live:
1189
            self._CallMonitorCommand(instance_name, 'cont')
1190
          raise errors.HypervisorError("Migration %s at the kvm level" %
1191
                                       status)
1192
        else:
1193
          logging.warning("KVM: unknown migration status '%s'", status)
1194
          broken_answers += 1
1195
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1196
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1197
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1198

    
1199
    utils.KillProcess(pid)
1200
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1201

    
1202
  def GetNodeInfo(self):
1203
    """Return information about the node.
1204

1205
    This is just a wrapper over the base GetLinuxNodeInfo method.
1206

1207
    @return: a dict with the following keys (values in MiB):
1208
          - memory_total: the total memory size on the node
1209
          - memory_free: the available memory on the node for instances
1210
          - memory_dom0: the memory used by the node itself, if available
1211

1212
    """
1213
    return self.GetLinuxNodeInfo()
1214

    
1215
  @classmethod
1216
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1217
    """Return a command for connecting to the console of an instance.
1218

1219
    """
1220
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1221
      cmd = [constants.KVM_CONSOLE_WRAPPER,
1222
             constants.SOCAT_PATH, utils.ShellQuote(instance.name),
1223
             utils.ShellQuote(cls._InstanceMonitor(instance.name)),
1224
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1225
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1226
      return objects.InstanceConsole(instance=instance.name,
1227
                                     kind=constants.CONS_SSH,
1228
                                     host=instance.primary_node,
1229
                                     user=constants.GANETI_RUNAS,
1230
                                     command=cmd)
1231

    
1232
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1233
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1234
      display = instance.network_port - constants.VNC_BASE_PORT
1235
      return objects.InstanceConsole(instance=instance.name,
1236
                                     kind=constants.CONS_VNC,
1237
                                     host=vnc_bind_address,
1238
                                     port=instance.network_port,
1239
                                     display=display)
1240

    
1241
    return objects.InstanceConsole(instance=instance.name,
1242
                                   kind=constants.CONS_MESSAGE,
1243
                                   message=("No serial shell for instance %s" %
1244
                                            instance.name))
1245

    
1246
  def Verify(self):
1247
    """Verify the hypervisor.
1248

1249
    Check that the binary exists.
1250

1251
    """
1252
    if not os.path.exists(constants.KVM_PATH):
1253
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1254
    if not os.path.exists(constants.SOCAT_PATH):
1255
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1256

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

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

1265
    """
1266
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1267

    
1268
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1269
    if kernel_path:
1270
      if not hvparams[constants.HV_ROOT_PATH]:
1271
        raise errors.HypervisorError("Need a root partition for the instance,"
1272
                                     " if a kernel is defined")
1273

    
1274
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1275
        not hvparams[constants.HV_VNC_X509]):
1276
      raise errors.HypervisorError("%s must be defined, if %s is" %
1277
                                   (constants.HV_VNC_X509,
1278
                                    constants.HV_VNC_X509_VERIFY))
1279

    
1280
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1281
    if (boot_order == constants.HT_BO_CDROM and
1282
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1283
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1284
                                   " ISO path")
1285

    
1286
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1287
    if security_model == constants.HT_SM_USER:
1288
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1289
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1290
                                     " must be specified")
1291
    elif (security_model == constants.HT_SM_NONE or
1292
          security_model == constants.HT_SM_POOL):
1293
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1294
        raise errors.HypervisorError("Cannot have a security domain when the"
1295
                                     " security model is 'none' or 'pool'")
1296

    
1297
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1298
    if spice_bind:
1299
      spice_ip_version = hvparams[constants.HV_KVM_SPICE_IP_VERSION]
1300
      if spice_ip_version != constants.IFACE_NO_IP_VERSION_SPECIFIED:
1301
        # if an IP version is specified, the spice_bind parameter must be an
1302
        # IP of that family
1303
        if (netutils.IP4Address.IsValid(spice_bind) and
1304
            spice_ip_version != constants.IP4_VERSION):
1305
          raise errors.HypervisorError("spice: got an IPv4 address (%s), but"
1306
                                       " the specified IP version is %s" %
1307
                                       (spice_bind, spice_ip_version))
1308

    
1309
        if (netutils.IP6Address.IsValid(spice_bind) and
1310
            spice_ip_version != constants.IP6_VERSION):
1311
          raise errors.HypervisorError("spice: got an IPv6 address (%s), but"
1312
                                       " the specified IP version is %s" %
1313
                                       (spice_bind, spice_ip_version))
1314

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

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

1323
    """
1324
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1325

    
1326
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1327
    if security_model == constants.HT_SM_USER:
1328
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1329
      try:
1330
        pwd.getpwnam(username)
1331
      except KeyError:
1332
        raise errors.HypervisorError("Unknown security domain user %s"
1333
                                     % username)
1334

    
1335
    spice_bind = hvparams[constants.HV_KVM_SPICE_BIND]
1336
    if spice_bind:
1337
      # only one of VNC and SPICE can be used currently.
1338
      if hvparams[constants.HV_VNC_BIND_ADDRESS]:
1339
        raise errors.HypervisorError("both SPICE and VNC are configured, but"
1340
                                     " only one of them can be used at a"
1341
                                     " given time.")
1342

    
1343
      # KVM version should be >= 0.14.0
1344
      _, v_major, v_min, _ = cls._GetKVMVersion()
1345
      if (v_major, v_min) < (0, 14):
1346
        raise errors.HypervisorError("spice is configured, but it is not"
1347
                                     " available in versions of KVM < 0.14")
1348

    
1349
      # if spice_bind is not an IP address, it must be a valid interface
1350
      bound_to_addr = (netutils.IP4Address.IsValid(spice_bind)
1351
                       or netutils.IP6Address.IsValid(spice_bind))
1352
      if not bound_to_addr and not netutils.IsValidInterface(spice_bind):
1353
        raise errors.HypervisorError("spice: the %s parameter must be either"
1354
                                     " a valid IP address or interface name" %
1355
                                     constants.HV_KVM_SPICE_BIND)
1356

    
1357
  @classmethod
1358
  def PowercycleNode(cls):
1359
    """KVM powercycle, just a wrapper over Linux powercycle.
1360

1361
    """
1362
    cls.LinuxPowercycle()