Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 43fcf0d3

History | View | Annotate | Download (42.2 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_CDROM_IMAGE_PATH: hv_base.OPT_FILE_OR_URL_CHECK,
187
    constants.HV_BOOT_ORDER:
188
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
189
    constants.HV_NIC_TYPE:
190
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
191
    constants.HV_DISK_TYPE:
192
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
193
    constants.HV_USB_MOUSE:
194
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
195
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
196
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
197
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
198
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
199
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
200
    constants.HV_DISK_CACHE:
201
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
202
    constants.HV_SECURITY_MODEL:
203
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
204
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
205
    constants.HV_KVM_FLAG:
206
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
207
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
208
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
209
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
210
    }
211

    
212
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
213
                                    re.M | re.I)
214
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
215
  _MIGRATION_INFO_RETRY_DELAY = 1
216

    
217
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
218

    
219
  ANCILLARY_FILES = [
220
    _KVM_NETWORK_SCRIPT,
221
    ]
222

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

    
230
  @classmethod
231
  def _InstancePidFile(cls, instance_name):
232
    """Returns the instance pidfile.
233

234
    """
235
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
236

    
237
  @classmethod
238
  def _InstanceUidFile(cls, instance_name):
239
    """Returns the instance uidfile.
240

241
    """
242
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
243

    
244
  @classmethod
245
  def _InstancePidInfo(cls, pid):
246
    """Check pid file for instance information.
247

248
    Check that a pid file is associated with an instance, and retrieve
249
    information from its command line.
250

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

257
    """
258
    alive = utils.IsProcessAlive(pid)
259
    if not alive:
260
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
261

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

    
269
    instance = None
270
    memory = 0
271
    vcpus = 0
272

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

    
283
    if instance is None:
284
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
285
                                   " instance" % pid)
286

    
287
    return (instance, memory, vcpus)
288

    
289
  def _InstancePidAlive(self, instance_name):
290
    """Returns the instance pidfile, pid, and liveness.
291

292
    @type instance_name: string
293
    @param instance_name: instance name
294
    @rtype: tuple
295
    @return: (pid file name, pid, liveness)
296

297
    """
298
    pidfile = self._InstancePidFile(instance_name)
299
    pid = utils.ReadPidFile(pidfile)
300

    
301
    alive = False
302
    try:
303
      cmd_instance = self._InstancePidInfo(pid)[0]
304
      alive = (cmd_instance == instance_name)
305
    except errors.HypervisorError:
306
      pass
307

    
308
    return (pidfile, pid, alive)
309

    
310
  def _CheckDown(self, instance_name):
311
    """Raises an error unless the given instance is down.
312

313
    """
314
    alive = self._InstancePidAlive(instance_name)[2]
315
    if alive:
316
      raise errors.HypervisorError("Failed to start instance %s: %s" %
317
                                   (instance_name, "already running"))
318

    
319
  @classmethod
320
  def _InstanceMonitor(cls, instance_name):
321
    """Returns the instance monitor socket name
322

323
    """
324
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
325

    
326
  @classmethod
327
  def _InstanceSerial(cls, instance_name):
328
    """Returns the instance serial socket name
329

330
    """
331
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
332

    
333
  @staticmethod
334
  def _SocatUnixConsoleParams():
335
    """Returns the correct parameters for socat
336

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

339
    """
340
    if constants.SOCAT_USE_ESCAPE:
341
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
342
    else:
343
      return "echo=0,icanon=0"
344

    
345
  @classmethod
346
  def _InstanceKVMRuntime(cls, instance_name):
347
    """Returns the instance KVM runtime filename
348

349
    """
350
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
351

    
352
  @classmethod
353
  def _InstanceChrootDir(cls, instance_name):
354
    """Returns the name of the KVM chroot dir of the instance
355

356
    """
357
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
358

    
359
  @classmethod
360
  def _InstanceNICDir(cls, instance_name):
361
    """Returns the name of the directory holding the tap device files for a
362
    given instance.
363

364
    """
365
    return utils.PathJoin(cls._NICS_DIR, instance_name)
366

    
367
  @classmethod
368
  def _InstanceNICFile(cls, instance_name, seq):
369
    """Returns the name of the file containing the tap device for a given NIC
370

371
    """
372
    return utils.PathJoin(cls._InstanceNICDir(instance_name), str(seq))
373

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

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

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

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

    
427
  @staticmethod
428
  def _ConfigureNIC(instance, seq, nic, tap):
429
    """Run the network configuration script for a specified NIC
430

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

440
    """
441

    
442
    if instance.tags:
443
      tags = " ".join(instance.tags)
444
    else:
445
      tags = ""
446

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

    
457
    if nic.ip:
458
      env["IP"] = nic.ip
459

    
460
    if nic.nicparams[constants.NIC_LINK]:
461
      env["LINK"] = nic.nicparams[constants.NIC_LINK]
462

    
463
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
464
      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
465

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

    
472
  def ListInstances(self):
473
    """Get the list of running instances.
474

475
    We can do this by listing our live instances directory and
476
    checking whether the associated kvm process is still alive.
477

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

    
485
  def GetInstanceInfo(self, instance_name):
486
    """Get instance properties.
487

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

493
    """
494
    _, pid, alive = self._InstancePidAlive(instance_name)
495
    if not alive:
496
      return None
497

    
498
    _, memory, vcpus = self._InstancePidInfo(pid)
499
    stat = "---b-"
500
    times = "0"
501

    
502
    return (instance_name, pid, memory, vcpus, stat, times)
503

    
504
  def GetAllInstancesInfo(self):
505
    """Get properties of all instances.
506

507
    @return: list of tuples (name, id, memory, vcpus, stat, times)
508

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

    
520
  def _GenerateKVMRuntime(self, instance, block_devices):
521
    """Generate KVM information to start an instance.
522

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

    
536
    hvp = instance.hvparams
537
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
538
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
539
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
540

    
541
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
542
      kvm_cmd.extend(["-enable-kvm"])
543
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
544
      kvm_cmd.extend(["-disable-kvm"])
545

    
546
    if boot_network:
547
      kvm_cmd.extend(['-boot', 'n'])
548

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

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

    
587
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
588
    if iso_image:
589
      options = ',media=cdrom'
590
      if re.match(r'(https?|ftp)://', iso_image):
591
        # Check that the iso image is really there
592
        # See https://bugs.launchpad.net/qemu/+bug/597575
593
        if not _CheckUrl(iso_image):
594
          raise errors.HypervisorError("ISO image %s is not accessible" %
595
                                       iso_image)
596
      else:
597
        options = "%s,format=raw" % options
598
      if boot_cdrom:
599
        kvm_cmd.extend(['-boot', 'd'])
600
        if disk_type != constants.HT_DISK_IDE:
601
          options = '%s,boot=on' % options
602
      else:
603
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
604
          if_val = ',if=virtio'
605
        else:
606
          if_val = ',if=%s' % disk_type
607
        options = '%s%s' % (options, if_val)
608
      drive_val = 'file=%s%s' % (iso_image, options)
609
      kvm_cmd.extend(['-drive', drive_val])
610

    
611
    kernel_path = hvp[constants.HV_KERNEL_PATH]
612
    if kernel_path:
613
      kvm_cmd.extend(['-kernel', kernel_path])
614
      initrd_path = hvp[constants.HV_INITRD_PATH]
615
      if initrd_path:
616
        kvm_cmd.extend(['-initrd', initrd_path])
617
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
618
                     hvp[constants.HV_KERNEL_ARGS]]
619
      if hvp[constants.HV_SERIAL_CONSOLE]:
620
        root_append.append('console=ttyS0,38400')
621
      kvm_cmd.extend(['-append', ' '.join(root_append)])
622

    
623
    mem_path = hvp[constants.HV_MEM_PATH]
624
    if mem_path:
625
      kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
626

    
627
    mouse_type = hvp[constants.HV_USB_MOUSE]
628
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
629

    
630
    if mouse_type:
631
      kvm_cmd.extend(['-usb'])
632
      kvm_cmd.extend(['-usbdevice', mouse_type])
633
    elif vnc_bind_address:
634
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
635

    
636
    if vnc_bind_address:
637
      if netutils.IP4Address.IsValid(vnc_bind_address):
638
        if instance.network_port > constants.VNC_BASE_PORT:
639
          display = instance.network_port - constants.VNC_BASE_PORT
640
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
641
            vnc_arg = ':%d' % (display)
642
          else:
643
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
644
        else:
645
          logging.error("Network port is not a valid VNC display (%d < %d)."
646
                        " Not starting VNC", instance.network_port,
647
                        constants.VNC_BASE_PORT)
648
          vnc_arg = 'none'
649

    
650
        # Only allow tls and other option when not binding to a file, for now.
651
        # kvm/qemu gets confused otherwise about the filename to use.
652
        vnc_append = ''
653
        if hvp[constants.HV_VNC_TLS]:
654
          vnc_append = '%s,tls' % vnc_append
655
          if hvp[constants.HV_VNC_X509_VERIFY]:
656
            vnc_append = '%s,x509verify=%s' % (vnc_append,
657
                                               hvp[constants.HV_VNC_X509])
658
          elif hvp[constants.HV_VNC_X509]:
659
            vnc_append = '%s,x509=%s' % (vnc_append,
660
                                         hvp[constants.HV_VNC_X509])
661
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
662
          vnc_append = '%s,password' % vnc_append
663

    
664
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
665

    
666
      else:
667
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
668

    
669
      kvm_cmd.extend(['-vnc', vnc_arg])
670
    else:
671
      kvm_cmd.extend(['-nographic'])
672

    
673
    monitor_dev = ("unix:%s,server,nowait" %
674
                   self._InstanceMonitor(instance.name))
675
    kvm_cmd.extend(['-monitor', monitor_dev])
676
    if hvp[constants.HV_SERIAL_CONSOLE]:
677
      serial_dev = ('unix:%s,server,nowait' %
678
                    self._InstanceSerial(instance.name))
679
      kvm_cmd.extend(['-serial', serial_dev])
680
    else:
681
      kvm_cmd.extend(['-serial', 'none'])
682

    
683
    if hvp[constants.HV_USE_LOCALTIME]:
684
      kvm_cmd.extend(['-localtime'])
685

    
686
    if hvp[constants.HV_KVM_USE_CHROOT]:
687
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
688

    
689
    # Save the current instance nics, but defer their expansion as parameters,
690
    # as we'll need to generate executable temp files for them.
691
    kvm_nics = instance.nics
692
    hvparams = hvp
693

    
694
    return (kvm_cmd, kvm_nics, hvparams)
695

    
696
  def _WriteKVMRuntime(self, instance_name, data):
697
    """Write an instance's KVM runtime
698

699
    """
700
    try:
701
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
702
                      data=data)
703
    except EnvironmentError, err:
704
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
705

    
706
  def _ReadKVMRuntime(self, instance_name):
707
    """Read an instance's KVM runtime
708

709
    """
710
    try:
711
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
712
    except EnvironmentError, err:
713
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
714
    return file_content
715

    
716
  def _SaveKVMRuntime(self, instance, kvm_runtime):
717
    """Save an instance's KVM runtime
718

719
    """
720
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
721
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
722
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
723
    self._WriteKVMRuntime(instance.name, serialized_form)
724

    
725
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
726
    """Load an instance's KVM runtime
727

728
    """
729
    if not serialized_runtime:
730
      serialized_runtime = self._ReadKVMRuntime(instance.name)
731
    loaded_runtime = serializer.Load(serialized_runtime)
732
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
733
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
734
    return (kvm_cmd, kvm_nics, hvparams)
735

    
736
  def _RunKVMCmd(self, name, kvm_cmd, tap_fds=None):
737
    """Run the KVM cmd and check for errors
738

739
    @type name: string
740
    @param name: instance name
741
    @type kvm_cmd: list of strings
742
    @param kvm_cmd: runcmd input for kvm
743
    @type tap_fds: list of int
744
    @param tap_fds: fds of tap devices opened by Ganeti
745

746
    """
747
    try:
748
      result = utils.RunCmd(kvm_cmd, noclose_fds=tap_fds)
749
    finally:
750
      for fd in tap_fds:
751
        utils_wrapper.CloseFdNoError(fd)
752

    
753
    if result.failed:
754
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
755
                                   (name, result.fail_reason, result.output))
756
    if not self._InstancePidAlive(name)[2]:
757
      raise errors.HypervisorError("Failed to start instance %s" % name)
758

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

762
    @type incoming: tuple of strings
763
    @param incoming: (target_host_ip, port)
764

765
    """
766
    # Small _ExecuteKVMRuntime hv parameters programming howto:
767
    #  - conf_hvp contains the parameters as configured on ganeti. they might
768
    #    have changed since the instance started; only use them if the change
769
    #    won't affect the inside of the instance (which hasn't been rebooted).
770
    #  - up_hvp contains the parameters as they were when the instance was
771
    #    started, plus any new parameter which has been added between ganeti
772
    #    versions: it is paramount that those default to a value which won't
773
    #    affect the inside of the instance as well.
774
    conf_hvp = instance.hvparams
775
    name = instance.name
776
    self._CheckDown(name)
777

    
778
    temp_files = []
779

    
780
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
781
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
782

    
783
    kvm_version = self._GetKVMVersion()
784
    if kvm_version:
785
      _, v_major, v_min, _ = kvm_version
786
    else:
787
      raise errors.HypervisorError("Unable to get KVM version")
788

    
789
    # We know it's safe to run as a different user upon migration, so we'll use
790
    # the latest conf, from conf_hvp.
791
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
792
    if security_model == constants.HT_SM_USER:
793
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
794

    
795
    # We have reasons to believe changing something like the nic driver/type
796
    # upon migration won't exactly fly with the instance kernel, so for nic
797
    # related parameters we'll use up_hvp
798
    tapfds = []
799
    taps = []
800
    if not kvm_nics:
801
      kvm_cmd.extend(["-net", "none"])
802
    else:
803
      vnet_hdr = False
804
      tap_extra = ""
805
      nic_type = up_hvp[constants.HV_NIC_TYPE]
806
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
807
        # From version 0.12.0, kvm uses a new sintax for network configuration.
808
        if (v_major, v_min) >= (0, 12):
809
          nic_model = "virtio-net-pci"
810
          vnet_hdr = True
811
        else:
812
          nic_model = "virtio"
813

    
814
        if up_hvp[constants.HV_VHOST_NET]:
815
          # vhost_net is only available from version 0.13.0 or newer
816
          if (v_major, v_min) >= (0, 13):
817
            tap_extra = ",vhost=on"
818
          else:
819
            raise errors.HypervisorError("vhost_net is configured"
820
                                        " but it is not available")
821
      else:
822
        nic_model = nic_type
823

    
824
      for nic_seq, nic in enumerate(kvm_nics):
825
        tapname, tapfd = _OpenTap(vnet_hdr)
826
        tapfds.append(tapfd)
827
        taps.append(tapname)
828
        if (v_major, v_min) >= (0, 12):
829
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
830
          tap_val = "type=tap,id=netdev%s,fd=%d%s" % (nic_seq, tapfd, tap_extra)
831
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
832
        else:
833
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
834
                                                         nic.mac, nic_model)
835
          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
836
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
837

    
838
    if incoming:
839
      target, port = incoming
840
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
841

    
842
    # Changing the vnc password doesn't bother the guest that much. At most it
843
    # will surprise people who connect to it. Whether positively or negatively
844
    # it's debatable.
845
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
846
    vnc_pwd = None
847
    if vnc_pwd_file:
848
      try:
849
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
850
      except EnvironmentError, err:
851
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
852
                                     % (vnc_pwd_file, err))
853

    
854
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
855
      utils.EnsureDirs([(self._InstanceChrootDir(name),
856
                         constants.SECURE_DIR_MODE)])
857

    
858
    if not incoming:
859
      # Configure the network now for starting instances, during
860
      # FinalizeMigration for incoming instances
861
      for nic_seq, nic in enumerate(kvm_nics):
862
        self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
863

    
864
    if security_model == constants.HT_SM_POOL:
865
      ss = ssconf.SimpleStore()
866
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
867
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
868
      uid = uidpool.RequestUnusedUid(all_uids)
869
      try:
870
        username = pwd.getpwuid(uid.GetUid()).pw_name
871
        kvm_cmd.extend(["-runas", username])
872
        self._RunKVMCmd(name, kvm_cmd, tapfds)
873
      except:
874
        uidpool.ReleaseUid(uid)
875
        raise
876
      else:
877
        uid.Unlock()
878
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
879
    else:
880
      self._RunKVMCmd(name, kvm_cmd, tapfds)
881

    
882
    utils.EnsureDirs([(self._InstanceNICDir(instance.name),
883
                     constants.RUN_DIRS_MODE)])
884
    for nic_seq, tap in enumerate(taps):
885
      utils.WriteFile(self._InstanceNICFile(instance.name, nic_seq),
886
                      data=tap)
887

    
888
    if vnc_pwd:
889
      change_cmd = 'change vnc password %s' % vnc_pwd
890
      self._CallMonitorCommand(instance.name, change_cmd)
891

    
892
    for filename in temp_files:
893
      utils.RemoveFile(filename)
894

    
895
  def StartInstance(self, instance, block_devices):
896
    """Start an instance.
897

898
    """
899
    self._CheckDown(instance.name)
900
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
901
    self._SaveKVMRuntime(instance, kvm_runtime)
902
    self._ExecuteKVMRuntime(instance, kvm_runtime)
903

    
904
  def _CallMonitorCommand(self, instance_name, command):
905
    """Invoke a command on the instance monitor.
906

907
    """
908
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
909
             (utils.ShellQuote(command),
910
              constants.SOCAT_PATH,
911
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
912
    result = utils.RunCmd(socat)
913
    if result.failed:
914
      msg = ("Failed to send command '%s' to instance %s."
915
             " output: %s, error: %s, fail_reason: %s" %
916
             (command, instance_name,
917
              result.stdout, result.stderr, result.fail_reason))
918
      raise errors.HypervisorError(msg)
919

    
920
    return result
921

    
922
  @classmethod
923
  def _GetKVMVersion(cls):
924
    """Return the installed KVM version
925

926
    @return: (version, v_maj, v_min, v_rev), or None
927

928
    """
929
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
930
    if result.failed:
931
      return None
932
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
933
    if not match:
934
      return None
935

    
936
    return (match.group(0), int(match.group(1)), int(match.group(2)),
937
            int(match.group(3)))
938

    
939
  def StopInstance(self, instance, force=False, retry=False, name=None):
940
    """Stop an instance.
941

942
    """
943
    if name is not None and not force:
944
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
945
    if name is None:
946
      name = instance.name
947
      acpi = instance.hvparams[constants.HV_ACPI]
948
    else:
949
      acpi = False
950
    _, pid, alive = self._InstancePidAlive(name)
951
    if pid > 0 and alive:
952
      if force or not acpi:
953
        utils.KillProcess(pid)
954
      else:
955
        self._CallMonitorCommand(name, 'system_powerdown')
956

    
957
  def CleanupInstance(self, instance_name):
958
    """Cleanup after a stopped instance
959

960
    """
961
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
962
    if pid > 0 and alive:
963
      raise errors.HypervisorError("Cannot cleanup a live instance")
964
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
965

    
966
  def RebootInstance(self, instance):
967
    """Reboot an instance.
968

969
    """
970
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
971
    # socket the instance will stop, but now power up again. So we'll resort
972
    # to shutdown and restart.
973
    _, _, alive = self._InstancePidAlive(instance.name)
974
    if not alive:
975
      raise errors.HypervisorError("Failed to reboot instance %s:"
976
                                   " not running" % instance.name)
977
    # StopInstance will delete the saved KVM runtime so:
978
    # ...first load it...
979
    kvm_runtime = self._LoadKVMRuntime(instance)
980
    # ...now we can safely call StopInstance...
981
    if not self.StopInstance(instance):
982
      self.StopInstance(instance, force=True)
983
    # ...and finally we can save it again, and execute it...
984
    self._SaveKVMRuntime(instance, kvm_runtime)
985
    self._ExecuteKVMRuntime(instance, kvm_runtime)
986

    
987
  def MigrationInfo(self, instance):
988
    """Get instance information to perform a migration.
989

990
    @type instance: L{objects.Instance}
991
    @param instance: instance to be migrated
992
    @rtype: string
993
    @return: content of the KVM runtime file
994

995
    """
996
    return self._ReadKVMRuntime(instance.name)
997

    
998
  def AcceptInstance(self, instance, info, target):
999
    """Prepare to accept an instance.
1000

1001
    @type instance: L{objects.Instance}
1002
    @param instance: instance to be accepted
1003
    @type info: string
1004
    @param info: content of the KVM runtime file on the source node
1005
    @type target: string
1006
    @param target: target host (usually ip), on this node
1007

1008
    """
1009
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1010
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1011
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1012

    
1013
  def FinalizeMigration(self, instance, info, success):
1014
    """Finalize an instance migration.
1015

1016
    Stop the incoming mode KVM.
1017

1018
    @type instance: L{objects.Instance}
1019
    @param instance: instance whose migration is being finalized
1020

1021
    """
1022
    if success:
1023
      kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1024
      kvm_nics = kvm_runtime[1]
1025

    
1026
      for nic_seq, nic in enumerate(kvm_nics):
1027
        try:
1028
          tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
1029
        except EnvironmentError, err:
1030
          logging.warning("Failed to find host interface for %s NIC #%d: %s",
1031
                          instance.name, nic_seq, str(err))
1032
          continue
1033
        try:
1034
          self._ConfigureNIC(instance, nic_seq, nic, tap)
1035
        except errors.HypervisorError, err:
1036
          logging.warning(str(err))
1037

    
1038
      self._WriteKVMRuntime(instance.name, info)
1039
    else:
1040
      self.StopInstance(instance, force=True)
1041

    
1042
  def MigrateInstance(self, instance, target, live):
1043
    """Migrate an instance to a target node.
1044

1045
    The migration will not be attempted if the instance is not
1046
    currently running.
1047

1048
    @type instance: L{objects.Instance}
1049
    @param instance: the instance to be migrated
1050
    @type target: string
1051
    @param target: ip address of the target node
1052
    @type live: boolean
1053
    @param live: perform a live migration
1054

1055
    """
1056
    instance_name = instance.name
1057
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1058
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1059
    if not alive:
1060
      raise errors.HypervisorError("Instance not running, cannot migrate")
1061

    
1062
    if not live:
1063
      self._CallMonitorCommand(instance_name, 'stop')
1064

    
1065
    migrate_command = ('migrate_set_speed %dm' %
1066
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1067
    self._CallMonitorCommand(instance_name, migrate_command)
1068

    
1069
    migrate_command = ('migrate_set_downtime %dms' %
1070
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1071
    self._CallMonitorCommand(instance_name, migrate_command)
1072

    
1073
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1074
    self._CallMonitorCommand(instance_name, migrate_command)
1075

    
1076
    info_command = 'info migrate'
1077
    done = False
1078
    broken_answers = 0
1079
    while not done:
1080
      result = self._CallMonitorCommand(instance_name, info_command)
1081
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
1082
      if not match:
1083
        broken_answers += 1
1084
        if not result.stdout:
1085
          logging.info("KVM: empty 'info migrate' result")
1086
        else:
1087
          logging.warning("KVM: unknown 'info migrate' result: %s",
1088
                          result.stdout)
1089
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1090
      else:
1091
        status = match.group(1)
1092
        if status == 'completed':
1093
          done = True
1094
        elif status == 'active':
1095
          # reset the broken answers count
1096
          broken_answers = 0
1097
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1098
        elif status == 'failed' or status == 'cancelled':
1099
          if not live:
1100
            self._CallMonitorCommand(instance_name, 'cont')
1101
          raise errors.HypervisorError("Migration %s at the kvm level" %
1102
                                       status)
1103
        else:
1104
          logging.warning("KVM: unknown migration status '%s'", status)
1105
          broken_answers += 1
1106
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
1107
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
1108
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
1109

    
1110
    utils.KillProcess(pid)
1111
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1112

    
1113
  def GetNodeInfo(self):
1114
    """Return information about the node.
1115

1116
    This is just a wrapper over the base GetLinuxNodeInfo method.
1117

1118
    @return: a dict with the following keys (values in MiB):
1119
          - memory_total: the total memory size on the node
1120
          - memory_free: the available memory on the node for instances
1121
          - memory_dom0: the memory used by the node itself, if available
1122

1123
    """
1124
    return self.GetLinuxNodeInfo()
1125

    
1126
  @classmethod
1127
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1128
    """Return a command for connecting to the console of an instance.
1129

1130
    """
1131
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1132
      cmd = [constants.SOCAT_PATH,
1133
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1134
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1135
      return objects.InstanceConsole(instance=instance.name,
1136
                                     kind=constants.CONS_SSH,
1137
                                     host=instance.primary_node,
1138
                                     user=constants.GANETI_RUNAS,
1139
                                     command=cmd)
1140

    
1141
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1142
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1143
      display = instance.network_port - constants.VNC_BASE_PORT
1144
      return objects.InstanceConsole(instance=instance.name,
1145
                                     kind=constants.CONS_VNC,
1146
                                     host=vnc_bind_address,
1147
                                     port=instance.network_port,
1148
                                     display=display)
1149

    
1150
    return objects.InstanceConsole(instance=instance.name,
1151
                                   kind=constants.CONS_MESSAGE,
1152
                                   message=("No serial shell for instance %s" %
1153
                                            instance.name))
1154

    
1155
  def Verify(self):
1156
    """Verify the hypervisor.
1157

1158
    Check that the binary exists.
1159

1160
    """
1161
    if not os.path.exists(constants.KVM_PATH):
1162
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1163
    if not os.path.exists(constants.SOCAT_PATH):
1164
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1165

    
1166
  @classmethod
1167
  def CheckParameterSyntax(cls, hvparams):
1168
    """Check the given parameters for validity.
1169

1170
    @type hvparams:  dict
1171
    @param hvparams: dictionary with parameter names/value
1172
    @raise errors.HypervisorError: when a parameter is not valid
1173

1174
    """
1175
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1176

    
1177
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1178
    if kernel_path:
1179
      if not hvparams[constants.HV_ROOT_PATH]:
1180
        raise errors.HypervisorError("Need a root partition for the instance,"
1181
                                     " if a kernel is defined")
1182

    
1183
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1184
        not hvparams[constants.HV_VNC_X509]):
1185
      raise errors.HypervisorError("%s must be defined, if %s is" %
1186
                                   (constants.HV_VNC_X509,
1187
                                    constants.HV_VNC_X509_VERIFY))
1188

    
1189
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1190
    if (boot_order == constants.HT_BO_CDROM and
1191
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1192
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1193
                                   " ISO path")
1194

    
1195
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1196
    if security_model == constants.HT_SM_USER:
1197
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1198
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1199
                                     " must be specified")
1200
    elif (security_model == constants.HT_SM_NONE or
1201
          security_model == constants.HT_SM_POOL):
1202
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1203
        raise errors.HypervisorError("Cannot have a security domain when the"
1204
                                     " security model is 'none' or 'pool'")
1205

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

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

1214
    """
1215
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1216

    
1217
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1218
    if security_model == constants.HT_SM_USER:
1219
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1220
      try:
1221
        pwd.getpwnam(username)
1222
      except KeyError:
1223
        raise errors.HypervisorError("Unknown security domain user %s"
1224
                                     % username)
1225

    
1226
  @classmethod
1227
  def PowercycleNode(cls):
1228
    """KVM powercycle, just a wrapper over Linux powercycle.
1229

1230
    """
1231
    cls.LinuxPowercycle()