Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 199b2053

History | View | Annotate | Download (41.9 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008, 2009, 2010 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
from cStringIO import StringIO
37

    
38
from ganeti import utils
39
from ganeti import constants
40
from ganeti import errors
41
from ganeti import serializer
42
from ganeti import objects
43
from ganeti import uidpool
44
from ganeti import ssconf
45
from ganeti.hypervisor import hv_base
46
from ganeti import netutils
47

    
48

    
49
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
50

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

    
61

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

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

75
   @type fd: int
76
   @param fd: the file descriptor of /dev/net/tun
77

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

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

    
93

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

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

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

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

    
111
  flags = IFF_TAP | IFF_NO_PI
112

    
113
  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
114
    flags |= IFF_VNET_HDR
115

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

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

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

    
128

    
129
def _WriteNetScript(instance, nic, index):
130
  """Write a script to connect a net interface to the proper bridge.
131

132
  This can be used by any qemu-type hypervisor.
133

134
  @type instance: L{objects.Instance}
135
  @param instance: Instance object
136
  @type nic: L{objects.NIC}
137
  @param nic: NIC object
138
  @type index: int
139
  @param index: NIC index
140
  @return: Script
141
  @rtype: string
142

143
  """
144
  if instance.tags:
145
    tags = " ".join(instance.tags)
146
  else:
147
    tags = ""
148

    
149
  buf = StringIO()
150
  sw = utils.ShellWriter(buf)
151
  sw.Write("#!/bin/sh")
152
  sw.Write("# this is autogenerated by Ganeti, please do not edit")
153
  sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
154
  sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
155
  sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
156
  sw.Write("export MODE=%s",
157
           utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
158
  sw.Write("export INTERFACE=\"$1\"")
159
  sw.Write("export TAGS=%s", utils.ShellQuote(tags))
160

    
161
  if nic.ip:
162
    sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
163

    
164
  if nic.nicparams[constants.NIC_LINK]:
165
    sw.Write("export LINK=%s",
166
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
167

    
168
  if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
169
    sw.Write("export BRIDGE=%s",
170
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
171

    
172
  # TODO: make this configurable at ./configure time
173
  sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
174
  sw.IncIndent()
175
  try:
176
    sw.Write("# Execute the user-specific vif file")
177
    sw.Write(_KVM_NETWORK_SCRIPT)
178
  finally:
179
    sw.DecIndent()
180
  sw.Write("else")
181
  sw.IncIndent()
182
  try:
183
    sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
184

    
185
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
186
      sw.Write("# Connect the interface to the bridge")
187
      sw.Write("brctl addif $BRIDGE $INTERFACE")
188

    
189
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
190
      if not nic.ip:
191
        raise errors.HypervisorError("nic/%d is routed, but has no IP"
192
                                     " address" % index)
193

    
194
      sw.Write("# Route traffic targeted at the IP to the interface")
195
      if nic.nicparams[constants.NIC_LINK]:
196
        sw.Write("while ip rule del dev $INTERFACE; do :; done")
197
        sw.Write("ip rule add dev $INTERFACE table $LINK")
198
        sw.Write("ip route replace $IP table $LINK proto static"
199
                 " dev $INTERFACE")
200
      else:
201
        sw.Write("ip route replace $IP proto static dev $INTERFACE")
202

    
203
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
204
      sw.Write(" if [ -d %s ]; then", interface_v4_conf)
205
      sw.IncIndent()
206
      try:
207
        sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
208
        sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
209
      finally:
210
        sw.DecIndent()
211
      sw.Write("fi")
212

    
213
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
214
      sw.Write("if [ -d %s ]; then", interface_v6_conf)
215
      sw.IncIndent()
216
      try:
217
        sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
218
        sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
219
      finally:
220
        sw.DecIndent()
221
      sw.Write("fi")
222
  finally:
223
    sw.DecIndent()
224
  sw.Write("fi")
225

    
226
  return buf.getvalue()
227

    
228

    
229
class KVMHypervisor(hv_base.BaseHypervisor):
230
  """KVM hypervisor interface"""
231
  CAN_MIGRATE = True
232

    
233
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
234
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
235
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
236
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
237
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
238
  # KVM instances with chroot enabled are started in empty chroot directories.
239
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
240
  # After an instance is stopped, its chroot directory is removed.
241
  # If the chroot directory is not empty, it can't be removed.
242
  # A non-empty chroot directory indicates a possible security incident.
243
  # To support forensics, the non-empty chroot directory is quarantined in
244
  # a separate directory, called 'chroot-quarantine'.
245
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
246
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
247
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
248

    
249
  PARAMETERS = {
250
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
251
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
252
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
253
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
254
    constants.HV_ACPI: hv_base.NO_CHECK,
255
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
256
    constants.HV_VNC_BIND_ADDRESS:
257
      (False, lambda x: (netutils.IP4Address.IsValid(x) or
258
                         utils.IsNormAbsPath(x)),
259
       "the VNC bind address must be either a valid IP address or an absolute"
260
       " pathname", None, None),
261
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
262
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
263
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
264
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
265
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
266
    constants.HV_BOOT_ORDER:
267
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
268
    constants.HV_NIC_TYPE:
269
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
270
    constants.HV_DISK_TYPE:
271
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
272
    constants.HV_USB_MOUSE:
273
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
274
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
275
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
276
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
277
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
278
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
279
    constants.HV_DISK_CACHE:
280
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
281
    constants.HV_SECURITY_MODEL:
282
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
283
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
284
    constants.HV_KVM_FLAG:
285
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
286
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
287
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
288
    constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
289
    }
290

    
291
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
292
                                    re.M | re.I)
293
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
294
  _MIGRATION_INFO_RETRY_DELAY = 2
295

    
296
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
297

    
298
  ANCILLARY_FILES = [
299
    _KVM_NETWORK_SCRIPT,
300
    ]
301

    
302
  def __init__(self):
303
    hv_base.BaseHypervisor.__init__(self)
304
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
305
    # in a tmpfs filesystem or has been otherwise wiped out.
306
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
307
    utils.EnsureDirs(dirs)
308

    
309
  @classmethod
310
  def _InstancePidFile(cls, instance_name):
311
    """Returns the instance pidfile.
312

313
    """
314
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
315

    
316
  @classmethod
317
  def _InstanceUidFile(cls, instance_name):
318
    """Returns the instance uidfile.
319

320
    """
321
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
322

    
323
  @classmethod
324
  def _InstancePidInfo(cls, pid):
325
    """Check pid file for instance information.
326

327
    Check that a pid file is associated with an instance, and retrieve
328
    information from its command line.
329

330
    @type pid: string or int
331
    @param pid: process id of the instance to check
332
    @rtype: tuple
333
    @return: (instance_name, memory, vcpus)
334
    @raise errors.HypervisorError: when an instance cannot be found
335

336
    """
337
    alive = utils.IsProcessAlive(pid)
338
    if not alive:
339
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
340

    
341
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
342
    try:
343
      cmdline = utils.ReadFile(cmdline_file)
344
    except EnvironmentError, err:
345
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
346
                                   (pid, err))
347

    
348
    instance = None
349
    memory = 0
350
    vcpus = 0
351

    
352
    arg_list = cmdline.split('\x00')
353
    while arg_list:
354
      arg =  arg_list.pop(0)
355
      if arg == "-name":
356
        instance = arg_list.pop(0)
357
      elif arg == "-m":
358
        memory = int(arg_list.pop(0))
359
      elif arg == "-smp":
360
        vcpus = int(arg_list.pop(0))
361

    
362
    if instance is None:
363
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
364
                                   " instance" % pid)
365

    
366
    return (instance, memory, vcpus)
367

    
368
  def _InstancePidAlive(self, instance_name):
369
    """Returns the instance pidfile, pid, and liveness.
370

371
    @type instance_name: string
372
    @param instance_name: instance name
373
    @rtype: tuple
374
    @return: (pid file name, pid, liveness)
375

376
    """
377
    pidfile = self._InstancePidFile(instance_name)
378
    pid = utils.ReadPidFile(pidfile)
379

    
380
    alive = False
381
    try:
382
      cmd_instance = self._InstancePidInfo(pid)[0]
383
      alive = (cmd_instance == instance_name)
384
    except errors.HypervisorError:
385
      pass
386

    
387
    return (pidfile, pid, alive)
388

    
389
  def _CheckDown(self, instance_name):
390
    """Raises an error unless the given instance is down.
391

392
    """
393
    alive = self._InstancePidAlive(instance_name)[2]
394
    if alive:
395
      raise errors.HypervisorError("Failed to start instance %s: %s" %
396
                                   (instance_name, "already running"))
397

    
398
  @classmethod
399
  def _InstanceMonitor(cls, instance_name):
400
    """Returns the instance monitor socket name
401

402
    """
403
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
404

    
405
  @classmethod
406
  def _InstanceSerial(cls, instance_name):
407
    """Returns the instance serial socket name
408

409
    """
410
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
411

    
412
  @staticmethod
413
  def _SocatUnixConsoleParams():
414
    """Returns the correct parameters for socat
415

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

418
    """
419
    if constants.SOCAT_USE_ESCAPE:
420
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
421
    else:
422
      return "echo=0,icanon=0"
423

    
424
  @classmethod
425
  def _InstanceKVMRuntime(cls, instance_name):
426
    """Returns the instance KVM runtime filename
427

428
    """
429
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
430

    
431
  @classmethod
432
  def _InstanceChrootDir(cls, instance_name):
433
    """Returns the name of the KVM chroot dir of the instance
434

435
    """
436
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
437

    
438
  @classmethod
439
  def _TryReadUidFile(cls, uid_file):
440
    """Try to read a uid file
441

442
    """
443
    if os.path.exists(uid_file):
444
      try:
445
        uid = int(utils.ReadOneLineFile(uid_file))
446
        return uid
447
      except EnvironmentError:
448
        logging.warning("Can't read uid file", exc_info=True)
449
      except (TypeError, ValueError):
450
        logging.warning("Can't parse uid file contents", exc_info=True)
451
    return None
452

    
453
  @classmethod
454
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
455
    """Removes an instance's rutime sockets/files/dirs.
456

457
    """
458
    utils.RemoveFile(pidfile)
459
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
460
    utils.RemoveFile(cls._InstanceSerial(instance_name))
461
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
462
    uid_file = cls._InstanceUidFile(instance_name)
463
    uid = cls._TryReadUidFile(uid_file)
464
    utils.RemoveFile(uid_file)
465
    if uid is not None:
466
      uidpool.ReleaseUid(uid)
467
    try:
468
      chroot_dir = cls._InstanceChrootDir(instance_name)
469
      utils.RemoveDir(chroot_dir)
470
    except OSError, err:
471
      if err.errno == errno.ENOTEMPTY:
472
        # The chroot directory is expected to be empty, but it isn't.
473
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
474
                                          prefix="%s-%s-" %
475
                                          (instance_name,
476
                                           utils.TimestampForFilename()))
477
        logging.warning("The chroot directory of instance %s can not be"
478
                        " removed as it is not empty. Moving it to the"
479
                        " quarantine instead. Please investigate the"
480
                        " contents (%s) and clean up manually",
481
                        instance_name, new_chroot_dir)
482
        utils.RenameFile(chroot_dir, new_chroot_dir)
483
      else:
484
        raise
485

    
486
  @staticmethod
487
  def _WriteNetScriptFile(instance, seq, nic):
488
    """Write a script to connect a net interface to the proper bridge.
489

490
    This can be used by any qemu-type hypervisor.
491

492
    @param instance: instance we're acting on
493
    @type instance: instance object
494
    @param seq: nic sequence number
495
    @type seq: int
496
    @param nic: nic we're acting on
497
    @type nic: nic object
498
    @return: netscript file name
499
    @rtype: string
500

501
    """
502
    script = _WriteNetScript(instance, nic, seq)
503

    
504
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
505
    # mounted noexec sometimes, so we'll have to find another place.
506
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
507
    tmpfile = os.fdopen(tmpfd, 'w')
508
    try:
509
      tmpfile.write(script)
510
    finally:
511
      tmpfile.close()
512
    os.chmod(tmpfile_name, 0755)
513
    return tmpfile_name
514

    
515
  def ListInstances(self):
516
    """Get the list of running instances.
517

518
    We can do this by listing our live instances directory and
519
    checking whether the associated kvm process is still alive.
520

521
    """
522
    result = []
523
    for name in os.listdir(self._PIDS_DIR):
524
      if self._InstancePidAlive(name)[2]:
525
        result.append(name)
526
    return result
527

    
528
  def GetInstanceInfo(self, instance_name):
529
    """Get instance properties.
530

531
    @type instance_name: string
532
    @param instance_name: the instance name
533
    @rtype: tuple of strings
534
    @return: (name, id, memory, vcpus, stat, times)
535

536
    """
537
    _, pid, alive = self._InstancePidAlive(instance_name)
538
    if not alive:
539
      return None
540

    
541
    _, memory, vcpus = self._InstancePidInfo(pid)
542
    stat = "---b-"
543
    times = "0"
544

    
545
    return (instance_name, pid, memory, vcpus, stat, times)
546

    
547
  def GetAllInstancesInfo(self):
548
    """Get properties of all instances.
549

550
    @return: list of tuples (name, id, memory, vcpus, stat, times)
551

552
    """
553
    data = []
554
    for name in os.listdir(self._PIDS_DIR):
555
      try:
556
        info = self.GetInstanceInfo(name)
557
      except errors.HypervisorError:
558
        continue
559
      if info:
560
        data.append(info)
561
    return data
562

    
563
  def _GenerateKVMRuntime(self, instance, block_devices):
564
    """Generate KVM information to start an instance.
565

566
    """
567
    pidfile  = self._InstancePidFile(instance.name)
568
    kvm = constants.KVM_PATH
569
    kvm_cmd = [kvm]
570
    # used just by the vnc server, if enabled
571
    kvm_cmd.extend(['-name', instance.name])
572
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
573
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
574
    kvm_cmd.extend(['-pidfile', pidfile])
575
    kvm_cmd.extend(['-daemonize'])
576
    if not instance.hvparams[constants.HV_ACPI]:
577
      kvm_cmd.extend(['-no-acpi'])
578

    
579
    hvp = instance.hvparams
580
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
581
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
582
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
583

    
584
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
585
      kvm_cmd.extend(["-enable-kvm"])
586
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
587
      kvm_cmd.extend(["-disable-kvm"])
588

    
589
    if boot_network:
590
      kvm_cmd.extend(['-boot', 'n'])
591

    
592
    disk_type = hvp[constants.HV_DISK_TYPE]
593
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
594
      if_val = ',if=virtio'
595
    else:
596
      if_val = ',if=%s' % disk_type
597
    # Cache mode
598
    disk_cache = hvp[constants.HV_DISK_CACHE]
599
    if disk_cache != constants.HT_CACHE_DEFAULT:
600
      cache_val = ",cache=%s" % disk_cache
601
    else:
602
      cache_val = ""
603
    for cfdev, dev_path in block_devices:
604
      if cfdev.mode != constants.DISK_RDWR:
605
        raise errors.HypervisorError("Instance has read-only disks which"
606
                                     " are not supported by KVM")
607
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
608
      if boot_disk:
609
        kvm_cmd.extend(['-boot', 'c'])
610
        if disk_type != constants.HT_DISK_IDE:
611
          boot_val = ',boot=on'
612
        else:
613
          boot_val = ''
614
        # We only boot from the first disk
615
        boot_disk = False
616
      else:
617
        boot_val = ''
618

    
619
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
620
                                                cache_val)
621
      kvm_cmd.extend(['-drive', drive_val])
622

    
623
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
624
    if iso_image:
625
      options = ',format=raw,media=cdrom'
626
      if boot_cdrom:
627
        kvm_cmd.extend(['-boot', 'd'])
628
        if disk_type != constants.HT_DISK_IDE:
629
          options = '%s,boot=on' % options
630
      else:
631
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
632
          if_val = ',if=virtio'
633
        else:
634
          if_val = ',if=%s' % disk_type
635
        options = '%s%s' % (options, if_val)
636
      drive_val = 'file=%s%s' % (iso_image, options)
637
      kvm_cmd.extend(['-drive', drive_val])
638

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

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

    
655
    mouse_type = hvp[constants.HV_USB_MOUSE]
656
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
657

    
658
    if mouse_type:
659
      kvm_cmd.extend(['-usb'])
660
      kvm_cmd.extend(['-usbdevice', mouse_type])
661
    elif vnc_bind_address:
662
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
663

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

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

    
692
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
693

    
694
      else:
695
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
696

    
697
      kvm_cmd.extend(['-vnc', vnc_arg])
698
    else:
699
      kvm_cmd.extend(['-nographic'])
700

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

    
711
    if hvp[constants.HV_USE_LOCALTIME]:
712
      kvm_cmd.extend(['-localtime'])
713

    
714
    if hvp[constants.HV_KVM_USE_CHROOT]:
715
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
716

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

    
722
    return (kvm_cmd, kvm_nics, hvparams)
723

    
724
  def _WriteKVMRuntime(self, instance_name, data):
725
    """Write an instance's KVM runtime
726

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

    
734
  def _ReadKVMRuntime(self, instance_name):
735
    """Read an instance's KVM runtime
736

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

    
744
  def _SaveKVMRuntime(self, instance, kvm_runtime):
745
    """Save an instance's KVM runtime
746

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

    
753
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
754
    """Load an instance's KVM runtime
755

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

    
764
  def _RunKVMCmd(self, name, kvm_cmd):
765
    """Run the KVM cmd and check for errors
766

767
    @type name: string
768
    @param name: instance name
769
    @type kvm_cmd: list of strings
770
    @param kvm_cmd: runcmd input for kvm
771

772
    """
773
    result = utils.RunCmd(kvm_cmd)
774
    if result.failed:
775
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
776
                                   (name, result.fail_reason, result.output))
777
    if not self._InstancePidAlive(name)[2]:
778
      raise errors.HypervisorError("Failed to start instance %s" % name)
779

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

783
    @type incoming: tuple of strings
784
    @param incoming: (target_host_ip, port)
785

786
    """
787
    # Small _ExecuteKVMRuntime hv parameters programming howto:
788
    #  - conf_hvp contains the parameters as configured on ganeti. they might
789
    #    have changed since the instance started; only use them if the change
790
    #    won't affect the inside of the instance (which hasn't been rebooted).
791
    #  - up_hvp contains the parameters as they were when the instance was
792
    #    started, plus any new parameter which has been added between ganeti
793
    #    versions: it is paramount that those default to a value which won't
794
    #    affect the inside of the instance as well.
795
    conf_hvp = instance.hvparams
796
    name = instance.name
797
    self._CheckDown(name)
798

    
799
    temp_files = []
800

    
801
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
802
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
803

    
804
    kvm_version = self._GetKVMVersion()
805
    if kvm_version:
806
      _, v_major, v_min, _ = kvm_version
807
    else:
808
      raise errors.HypervisorError("Unable to get KVM version")
809

    
810
    # We know it's safe to run as a different user upon migration, so we'll use
811
    # the latest conf, from conf_hvp.
812
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
813
    if security_model == constants.HT_SM_USER:
814
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
815

    
816
    # We have reasons to believe changing something like the nic driver/type
817
    # upon migration won't exactly fly with the instance kernel, so for nic
818
    # related parameters we'll use up_hvp
819
    if not kvm_nics:
820
      kvm_cmd.extend(["-net", "none"])
821
    else:
822
      tap_extra = ""
823
      nic_type = up_hvp[constants.HV_NIC_TYPE]
824
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
825
        # From version 0.12.0, kvm uses a new sintax for network configuration.
826
        if (v_major, v_min) >= (0, 12):
827
          nic_model = "virtio-net-pci"
828
        else:
829
          nic_model = "virtio"
830

    
831
        if up_hvp[constants.HV_VHOST_NET]:
832
          # vhost_net is only available from version 0.13.0 or newer
833
          if (v_major, v_min) >= (0, 13):
834
            tap_extra = ",vhost=on"
835
          else:
836
            raise errors.HypervisorError("vhost_net is configured"
837
                                        " but it is not available")
838
      else:
839
        nic_model = nic_type
840

    
841
      for nic_seq, nic in enumerate(kvm_nics):
842
        script = self._WriteNetScriptFile(instance, nic_seq, nic)
843
        temp_files.append(script)
844
        if (v_major, v_min) >= (0, 12):
845
          nic_val = "%s,mac=%s,netdev=netdev%s" % (nic_model, nic.mac, nic_seq)
846
          tap_val = "type=tap,id=netdev%s,script=%s%s" % (nic_seq,
847
                                                          script, tap_extra)
848
          kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
849
        else:
850
          nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
851
                                                         nic.mac, nic_model)
852
          tap_val = "tap,vlan=%s,script=%s" % (nic_seq, script)
853
          kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
854

    
855
    if incoming:
856
      target, port = incoming
857
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
858

    
859
    # Changing the vnc password doesn't bother the guest that much. At most it
860
    # will surprise people who connect to it. Whether positively or negatively
861
    # it's debatable.
862
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
863
    vnc_pwd = None
864
    if vnc_pwd_file:
865
      try:
866
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
867
      except EnvironmentError, err:
868
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
869
                                     % (vnc_pwd_file, err))
870

    
871
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
872
      utils.EnsureDirs([(self._InstanceChrootDir(name),
873
                         constants.SECURE_DIR_MODE)])
874

    
875
    if security_model == constants.HT_SM_POOL:
876
      ss = ssconf.SimpleStore()
877
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
878
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
879
      uid = uidpool.RequestUnusedUid(all_uids)
880
      try:
881
        username = pwd.getpwuid(uid.GetUid()).pw_name
882
        kvm_cmd.extend(["-runas", username])
883
        self._RunKVMCmd(name, kvm_cmd)
884
      except:
885
        uidpool.ReleaseUid(uid)
886
        raise
887
      else:
888
        uid.Unlock()
889
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
890
    else:
891
      self._RunKVMCmd(name, kvm_cmd)
892

    
893
    if vnc_pwd:
894
      change_cmd = 'change vnc password %s' % vnc_pwd
895
      self._CallMonitorCommand(instance.name, change_cmd)
896

    
897
    for filename in temp_files:
898
      utils.RemoveFile(filename)
899

    
900
  def StartInstance(self, instance, block_devices):
901
    """Start an instance.
902

903
    """
904
    self._CheckDown(instance.name)
905
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
906
    self._SaveKVMRuntime(instance, kvm_runtime)
907
    self._ExecuteKVMRuntime(instance, kvm_runtime)
908

    
909
  def _CallMonitorCommand(self, instance_name, command):
910
    """Invoke a command on the instance monitor.
911

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

    
925
    return result
926

    
927
  @classmethod
928
  def _GetKVMVersion(cls):
929
    """Return the installed KVM version
930

931
    @return: (version, v_maj, v_min, v_rev), or None
932

933
    """
934
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
935
    if result.failed:
936
      return None
937
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
938
    if not match:
939
      return None
940

    
941
    return (match.group(0), int(match.group(1)), int(match.group(2)),
942
            int(match.group(3)))
943

    
944
  def StopInstance(self, instance, force=False, retry=False, name=None):
945
    """Stop an instance.
946

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

    
962
  def CleanupInstance(self, instance_name):
963
    """Cleanup after a stopped instance
964

965
    """
966
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
967
    if pid > 0 and alive:
968
      raise errors.HypervisorError("Cannot cleanup a live instance")
969
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
970

    
971
  def RebootInstance(self, instance):
972
    """Reboot an instance.
973

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

    
992
  def MigrationInfo(self, instance):
993
    """Get instance information to perform a migration.
994

995
    @type instance: L{objects.Instance}
996
    @param instance: instance to be migrated
997
    @rtype: string
998
    @return: content of the KVM runtime file
999

1000
    """
1001
    return self._ReadKVMRuntime(instance.name)
1002

    
1003
  def AcceptInstance(self, instance, info, target):
1004
    """Prepare to accept an instance.
1005

1006
    @type instance: L{objects.Instance}
1007
    @param instance: instance to be accepted
1008
    @type info: string
1009
    @param info: content of the KVM runtime file on the source node
1010
    @type target: string
1011
    @param target: target host (usually ip), on this node
1012

1013
    """
1014
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
1015
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
1016
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
1017

    
1018
  def FinalizeMigration(self, instance, info, success):
1019
    """Finalize an instance migration.
1020

1021
    Stop the incoming mode KVM.
1022

1023
    @type instance: L{objects.Instance}
1024
    @param instance: instance whose migration is being finalized
1025

1026
    """
1027
    if success:
1028
      self._WriteKVMRuntime(instance.name, info)
1029
    else:
1030
      self.StopInstance(instance, force=True)
1031

    
1032
  def MigrateInstance(self, instance, target, live):
1033
    """Migrate an instance to a target node.
1034

1035
    The migration will not be attempted if the instance is not
1036
    currently running.
1037

1038
    @type instance: L{objects.Instance}
1039
    @param instance: the instance to be migrated
1040
    @type target: string
1041
    @param target: ip address of the target node
1042
    @type live: boolean
1043
    @param live: perform a live migration
1044

1045
    """
1046
    instance_name = instance.name
1047
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
1048
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
1049
    if not alive:
1050
      raise errors.HypervisorError("Instance not running, cannot migrate")
1051

    
1052
    if not live:
1053
      self._CallMonitorCommand(instance_name, 'stop')
1054

    
1055
    migrate_command = ('migrate_set_speed %dm' %
1056
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
1057
    self._CallMonitorCommand(instance_name, migrate_command)
1058

    
1059
    migrate_command = ('migrate_set_downtime %dms' %
1060
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
1061
    self._CallMonitorCommand(instance_name, migrate_command)
1062

    
1063
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
1064
    self._CallMonitorCommand(instance_name, migrate_command)
1065

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

    
1100
    utils.KillProcess(pid)
1101
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
1102

    
1103
  def GetNodeInfo(self):
1104
    """Return information about the node.
1105

1106
    This is just a wrapper over the base GetLinuxNodeInfo method.
1107

1108
    @return: a dict with the following keys (values in MiB):
1109
          - memory_total: the total memory size on the node
1110
          - memory_free: the available memory on the node for instances
1111
          - memory_dom0: the memory used by the node itself, if available
1112

1113
    """
1114
    return self.GetLinuxNodeInfo()
1115

    
1116
  @classmethod
1117
  def GetInstanceConsole(cls, instance, hvparams, beparams):
1118
    """Return a command for connecting to the console of an instance.
1119

1120
    """
1121
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1122
      cmd = [constants.SOCAT_PATH,
1123
             "STDIO,%s" % cls._SocatUnixConsoleParams(),
1124
             "UNIX-CONNECT:%s" % cls._InstanceSerial(instance.name)]
1125
      return objects.InstanceConsole(instance=instance.name,
1126
                                     kind=constants.CONS_SSH,
1127
                                     host=instance.primary_node,
1128
                                     user=constants.GANETI_RUNAS,
1129
                                     command=cmd)
1130

    
1131
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1132
    if vnc_bind_address and instance.network_port > constants.VNC_BASE_PORT:
1133
      display = instance.network_port - constants.VNC_BASE_PORT
1134
      return objects.InstanceConsole(instance=instance.name,
1135
                                     kind=constants.CONS_VNC,
1136
                                     host=vnc_bind_address,
1137
                                     port=instance.network_port,
1138
                                     display=display)
1139

    
1140
    return objects.InstanceConsole(instance=instance.name,
1141
                                   kind=constants.CONS_MESSAGE,
1142
                                   message=("No serial shell for instance %s" %
1143
                                            instance.name))
1144

    
1145
  def Verify(self):
1146
    """Verify the hypervisor.
1147

1148
    Check that the binary exists.
1149

1150
    """
1151
    if not os.path.exists(constants.KVM_PATH):
1152
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1153
    if not os.path.exists(constants.SOCAT_PATH):
1154
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1155

    
1156
  @classmethod
1157
  def CheckParameterSyntax(cls, hvparams):
1158
    """Check the given parameters for validity.
1159

1160
    @type hvparams:  dict
1161
    @param hvparams: dictionary with parameter names/value
1162
    @raise errors.HypervisorError: when a parameter is not valid
1163

1164
    """
1165
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1166

    
1167
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1168
    if kernel_path:
1169
      if not hvparams[constants.HV_ROOT_PATH]:
1170
        raise errors.HypervisorError("Need a root partition for the instance,"
1171
                                     " if a kernel is defined")
1172

    
1173
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1174
        not hvparams[constants.HV_VNC_X509]):
1175
      raise errors.HypervisorError("%s must be defined, if %s is" %
1176
                                   (constants.HV_VNC_X509,
1177
                                    constants.HV_VNC_X509_VERIFY))
1178

    
1179
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1180
    if (boot_order == constants.HT_BO_CDROM and
1181
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1182
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1183
                                   " ISO path")
1184

    
1185
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1186
    if security_model == constants.HT_SM_USER:
1187
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1188
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1189
                                     " must be specified")
1190
    elif (security_model == constants.HT_SM_NONE or
1191
          security_model == constants.HT_SM_POOL):
1192
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1193
        raise errors.HypervisorError("Cannot have a security domain when the"
1194
                                     " security model is 'none' or 'pool'")
1195

    
1196
  @classmethod
1197
  def ValidateParameters(cls, hvparams):
1198
    """Check the given parameters for validity.
1199

1200
    @type hvparams:  dict
1201
    @param hvparams: dictionary with parameter names/value
1202
    @raise errors.HypervisorError: when a parameter is not valid
1203

1204
    """
1205
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1206

    
1207
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1208
    if security_model == constants.HT_SM_USER:
1209
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1210
      try:
1211
        pwd.getpwnam(username)
1212
      except KeyError:
1213
        raise errors.HypervisorError("Unknown security domain user %s"
1214
                                     % username)
1215

    
1216
  @classmethod
1217
  def PowercycleNode(cls):
1218
    """KVM powercycle, just a wrapper over Linux powercycle.
1219

1220
    """
1221
    cls.LinuxPowercycle()