Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ c1dc897b

History | View | Annotate | Download (43.6 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
import urllib2
38

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

    
50

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

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

    
63

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

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

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

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

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

    
95

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

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

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

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

    
113
  flags = IFF_TAP | IFF_NO_PI
114

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

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

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

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

    
130

    
131
def _CheckUrl(url):
132
  """Check if a given URL exists on the server
133

134
  """
135
  req = urllib2.Request(url)
136

    
137
  # XXX: ugly but true
138
  req.get_method = lambda: "HEAD"
139

    
140
  try:
141
    resp = urllib2.urlopen(req)
142
  except urllib2.URLError:
143
    return False
144

    
145
  del resp
146
  return True
147

    
148

    
149
class KVMHypervisor(hv_base.BaseHypervisor):
150
  """KVM hypervisor interface"""
151
  CAN_MIGRATE = True
152

    
153
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
154
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
155
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
156
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
157
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
158
  _NICS_DIR = _ROOT_DIR + "/nic" # contains instances nic <-> tap associations
159
  # KVM instances with chroot enabled are started in empty chroot directories.
160
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
161
  # After an instance is stopped, its chroot directory is removed.
162
  # If the chroot directory is not empty, it can't be removed.
163
  # A non-empty chroot directory indicates a possible security incident.
164
  # To support forensics, the non-empty chroot directory is quarantined in
165
  # a separate directory, called 'chroot-quarantine'.
166
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
167
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR, _NICS_DIR,
168
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
169

    
170
  PARAMETERS = {
171
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
172
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
173
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
174
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
175
    constants.HV_ACPI: hv_base.NO_CHECK,
176
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
177
    constants.HV_VNC_BIND_ADDRESS:
178
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
179
                         utils.IsNormAbsPath(x)),
180
       "the VNC bind address must be either a valid IP address or an absolute"
181
       " pathname", None, None),
182
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
183
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
184
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
185
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
186
    constants.HV_KVM_FLOPPY_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
187
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
188
    constants.HV_KVM_CDROM2_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
189
    constants.HV_BOOT_ORDER:
190
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
191
    constants.HV_NIC_TYPE:
192
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
193
    constants.HV_DISK_TYPE:
194
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
195
    constants.HV_KVM_CDROM_DISK_TYPE:
196
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_DISK_TYPES),
197
    constants.HV_USB_MOUSE:
198
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
199
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
200
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
201
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
202
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
203
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
204
    constants.HV_DISK_CACHE:
205
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
206
    constants.HV_SECURITY_MODEL:
207
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
208
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
209
    constants.HV_KVM_FLAG:
210
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
211
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
212
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
213
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
214
    }
215

    
216
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
217
                                    re.M | re.I)
218
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
219
  _MIGRATION_INFO_RETRY_DELAY = 1
220

    
221
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
222

    
223
  ANCILLARY_FILES = [
224
    _KVM_NETWORK_SCRIPT,
225
    ]
226

    
227
  def __init__(self):
228
    hv_base.BaseHypervisor.__init__(self)
229
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
230
    # in a tmpfs filesystem or has been otherwise wiped out.
231
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
232
    utils.EnsureDirs(dirs)
233

    
234
  @classmethod
235
  def _InstancePidFile(cls, instance_name):
236
    """Returns the instance pidfile.
237

238
    """
239
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
240

    
241
  @classmethod
242
  def _InstanceUidFile(cls, instance_name):
243
    """Returns the instance uidfile.
244

245
    """
246
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
247

    
248
  @classmethod
249
  def _InstancePidInfo(cls, pid):
250
    """Check pid file for instance information.
251

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

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

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

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

    
273
    instance = None
274
    memory = 0
275
    vcpus = 0
276

    
277
    arg_list = cmdline.split('\x00')
278
    while arg_list:
279
      arg =  arg_list.pop(0)
280
      if arg == "-name":
281
        instance = arg_list.pop(0)
282
      elif arg == "-m":
283
        memory = int(arg_list.pop(0))
284
      elif arg == "-smp":
285
        vcpus = int(arg_list.pop(0))
286

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

    
291
    return (instance, memory, vcpus)
292

    
293
  def _InstancePidAlive(self, instance_name):
294
    """Returns the instance pidfile, pid, and liveness.
295

296
    @type instance_name: string
297
    @param instance_name: instance name
298
    @rtype: tuple
299
    @return: (pid file name, pid, liveness)
300

301
    """
302
    pidfile = self._InstancePidFile(instance_name)
303
    pid = utils.ReadPidFile(pidfile)
304

    
305
    alive = False
306
    try:
307
      cmd_instance = self._InstancePidInfo(pid)[0]
308
      alive = (cmd_instance == instance_name)
309
    except errors.HypervisorError:
310
      pass
311

    
312
    return (pidfile, pid, alive)
313

    
314
  def _CheckDown(self, instance_name):
315
    """Raises an error unless the given instance is down.
316

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

    
323
  @classmethod
324
  def _InstanceMonitor(cls, instance_name):
325
    """Returns the instance monitor socket name
326

327
    """
328
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
329

    
330
  @classmethod
331
  def _InstanceSerial(cls, instance_name):
332
    """Returns the instance serial socket name
333

334
    """
335
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
336

    
337
  @staticmethod
338
  def _SocatUnixConsoleParams():
339
    """Returns the correct parameters for socat
340

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

343
    """
344
    if constants.SOCAT_USE_ESCAPE:
345
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
346
    else:
347
      return "echo=0,icanon=0"
348

    
349
  @classmethod
350
  def _InstanceKVMRuntime(cls, instance_name):
351
    """Returns the instance KVM runtime filename
352

353
    """
354
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
355

    
356
  @classmethod
357
  def _InstanceChrootDir(cls, instance_name):
358
    """Returns the name of the KVM chroot dir of the instance
359

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

    
363
  @classmethod
364
  def _InstanceNICDir(cls, instance_name):
365
    """Returns the name of the directory holding the tap device files for a
366
    given instance.
367

368
    """
369
    return utils.PathJoin(cls._NICS_DIR, instance_name)
370

    
371
  @classmethod
372
  def _InstanceNICFile(cls, instance_name, seq):
373
    """Returns the name of the file containing the tap device for a given NIC
374

375
    """
376
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
377

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

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

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

397
    """
398
    utils.RemoveFile(pidfile)
399
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
400
    utils.RemoveFile(cls._InstanceSerial(instance_name))
401
    utils.RemoveFile(cls._InstanceKVMRuntime(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):
525
    """Generate KVM information to start an instance.
526

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

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

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

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

    
554
    disk_type = hvp[constants.HV_DISK_TYPE]
555
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
556
      if_val = ',if=virtio'
557
    else:
558
      if_val = ',if=%s' % disk_type
559
    # Cache mode
560
    disk_cache = hvp[constants.HV_DISK_CACHE]
561
    if instance.disk_template in constants.DTS_EXT_MIRROR:
562
      if disk_cache != "none":
563
        # TODO: make this a hard error, instead of a silent overwrite
564
        logging.warning("KVM: overriding disk_cache setting '%s' with 'none'"
565
                        " to prevent shared storage corruption on migration",
566
                        disk_cache)
567
      cache_val = ",cache=none"
568
    elif disk_cache != constants.HT_CACHE_DEFAULT:
569
      cache_val = ",cache=%s" % disk_cache
570
    else:
571
      cache_val = ""
572
    for cfdev, dev_path in block_devices:
573
      if cfdev.mode != constants.DISK_RDWR:
574
        raise errors.HypervisorError("Instance has read-only disks which"
575
                                     " are not supported by KVM")
576
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
577
      if boot_disk:
578
        kvm_cmd.extend(['-boot', 'c'])
579
        if disk_type != constants.HT_DISK_IDE:
580
          boot_val = ',boot=on'
581
        else:
582
          boot_val = ''
583
        # We only boot from the first disk
584
        boot_disk = False
585
      else:
586
        boot_val = ''
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 = ',media=cdrom'
600
      if re.match(r'(https?|ftp)://', iso_image):
601
        # Check that the iso image is really there
602
        # See https://bugs.launchpad.net/qemu/+bug/597575
603
        if not _CheckUrl(iso_image):
604
          raise errors.HypervisorError("ISO image %s is not accessible" %
605
                                       iso_image)
606
      else:
607
        options = "%s,format=raw" % options
608
      if boot_cdrom:
609
        kvm_cmd.extend(['-boot', 'd'])
610
        if cdrom_disk_type != constants.HT_DISK_IDE:
611
          options = '%s,boot=on,if=%s' % (options, constants.HT_DISK_IDE)
612
        else:
613
          options = '%s,boot=on' % options
614
      else:
615
        if cdrom_disk_type == constants.HT_DISK_PARAVIRTUAL:
616
          if_val = ',if=virtio'
617
        else:
618
          if_val = ',if=%s' % cdrom_disk_type
619
        options = '%s%s' % (options, if_val)
620
      drive_val = 'file=%s%s' % (iso_image, options)
621
      kvm_cmd.extend(['-drive', drive_val])
622

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

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

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

    
657
    mem_path = hvp[constants.HV_MEM_PATH]
658
    if mem_path:
659
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
660

    
661
    mouse_type = hvp[constants.HV_USB_MOUSE]
662
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
663

    
664
    if mouse_type:
665
      kvm_cmd.extend(['-usb'])
666
      kvm_cmd.extend(['-usbdevice', mouse_type])
667
    elif vnc_bind_address:
668
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
669

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

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

    
698
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
699

    
700
      else:
701
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
702

    
703
      kvm_cmd.extend(['-vnc', vnc_arg])
704
    else:
705
      kvm_cmd.extend(['-nographic'])
706

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

    
717
    if hvp[constants.HV_USE_LOCALTIME]:
718
      kvm_cmd.extend(['-localtime'])
719

    
720
    if hvp[constants.HV_KVM_USE_CHROOT]:
721
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
722

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

    
728
    return (kvm_cmd, kvm_nics, hvparams)
729

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

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

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

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

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

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

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

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

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

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

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

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

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

796
    @type incoming: tuple of strings
797
    @param incoming: (target_host_ip, port)
798

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

    
812
    temp_files = []
813

    
814
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
815
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
816

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

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

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

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

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

    
872
    if incoming:
873
      target, port = incoming
874
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
875

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

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

    
892
    if not incoming:
893
      # Configure the network now for starting instances, during
894
      # FinalizeMigration for incoming instances
895
      for nic_seq, nic in enumerate(kvm_nics):
896
        self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
897

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

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

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

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

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

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

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

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

    
954
    return result
955

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1050
    Stop the incoming mode KVM.
1051

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

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

    
1060
      for nic_seq, nic in enumerate(kvm_nics):
1061
        try:
1062
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1063
        except EnvironmentError, err:
1064
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1065
                          instance.name, nic_seq, str(err))
1066
          continue
1067
        try:
1068
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1069
        except errors.HypervisorError, err:
1070
          logging.warning(str(err))
1071

    
1072
      self._WriteKVMRuntime(instance.name, info)
1073
    else:
1074
      self.StopInstance(instance, force=True)
1075

    
1076
  def MigrateInstance(self, instance, target, live):
1077
    """Migrate an instance to a target node.
1078

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

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

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

    
1096
    if not live:
1097
      self._CallMonitorCommand(instance_name, 'stop')
1098

    
1099
    migrate_command = ('migrate_set_speed %dm' %
1100
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1101
    self._CallMonitorCommand(instance_name, migrate_command)
1102

    
1103
    migrate_command = ('migrate_set_downtime %dms' %
1104
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1105
    self._CallMonitorCommand(instance_name, migrate_command)
1106

    
1107
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1108
    self._CallMonitorCommand(instance_name, migrate_command)
1109

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

    
1144
    utils.KillProcess(pid)
1145
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1146

    
1147
  def GetNodeInfo(self):
1148
    """Return information about the node.
1149

1150
    This is just a wrapper over the base GetLinuxNodeInfo method.
1151

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

1157
    """
1158
    return self.GetLinuxNodeInfo()
1159

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

1164
    """
1165
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1166
      cmd = [constants.SOCAT_PATH,
1167
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1168
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1169
      return objects.InstanceConsole(instance=instance.name,
1170
                                     kind=constants.CONS_SSH,
1171
                                     host=instance.primary_node,
1172
                                     user=constants.GANETI_RUNAS,
1173
                                     command=cmd)
1174

    
1175
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1176
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1177
      display = instance.network_port - constants.VNC_BASE_PORT
1178
      return objects.InstanceConsole(instance=instance.name,
1179
                                     kind=constants.CONS_VNC,
1180
                                     host=vnc_bind_address,
1181
                                     port=instance.network_port,
1182
                                     display=display)
1183

    
1184
    return objects.InstanceConsole(instance=instance.name,
1185
                                   kind=constants.CONS_MESSAGE,
1186
                                   message=("No serial shell for instance %s" %
1187
                                            instance.name))
1188

    
1189
  def Verify(self):
1190
    """Verify the hypervisor.
1191

1192
    Check that the binary exists.
1193

1194
    """
1195
    if not os.path.exists(constants.KVM_PATH):
1196
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1197
    if not os.path.exists(constants.SOCAT_PATH):
1198
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1199

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

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

1208
    """
1209
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1210

    
1211
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1212
    if kernel_path:
1213
      if not hvparams[constants.HV_ROOT_PATH]:
1214
        raise errors.HypervisorError("Need a root partition for the instance,"
1215
                                     " if a kernel is defined")
1216

    
1217
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1218
        not hvparams[constants.HV_VNC_X509]):
1219
      raise errors.HypervisorError("%s must be defined, if %s is" %
1220
                                   (constants.HV_VNC_X509,
1221
                                    constants.HV_VNC_X509_VERIFY))
1222

    
1223
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1224
    if (boot_order == constants.HT_BO_CDROM and
1225
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1226
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1227
                                   " ISO path")
1228

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

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

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

1248
    """
1249
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1250

    
1251
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1252
    if security_model == constants.HT_SM_USER:
1253
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1254
      try:
1255
        pwd.getpwnam(username)
1256
      except KeyError:
1257
        raise errors.HypervisorError("Unknown security domain user %s"
1258
                                     % username)
1259

    
1260
  @classmethod
1261
  def PowercycleNode(cls):
1262
    """KVM powercycle, just a wrapper over Linux powercycle.
1263

1264
    """
1265
    cls.LinuxPowercycle()