Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ fea922fa

History | View | Annotate | Download (35.8 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008 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
from cStringIO import StringIO
35

    
36
from ganeti import utils
37
from ganeti import constants
38
from ganeti import errors
39
from ganeti import serializer
40
from ganeti import objects
41
from ganeti import uidpool
42
from ganeti import ssconf
43
from ganeti.hypervisor import hv_base
44

    
45

    
46
class KVMHypervisor(hv_base.BaseHypervisor):
47
  """KVM hypervisor interface"""
48
  CAN_MIGRATE = True
49

    
50
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
51
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
52
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
53
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
54
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
55
  # KVM instances with chroot enabled are started in empty chroot directories.
56
  _CHROOT_DIR = _ROOT_DIR + "/chroot" # for empty chroot directories
57
  # After an instance is stopped, its chroot directory is removed.
58
  # If the chroot directory is not empty, it can't be removed.
59
  # A non-empty chroot directory indicates a possible security incident.
60
  # To support forensics, the non-empty chroot directory is quarantined in
61
  # a separate directory, called 'chroot-quarantine'.
62
  _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
63
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
64
           _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
65

    
66
  PARAMETERS = {
67
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
68
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
69
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
70
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
71
    constants.HV_ACPI: hv_base.NO_CHECK,
72
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
73
    constants.HV_VNC_BIND_ADDRESS:
74
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
75
       "the VNC bind address must be either a valid IP address or an absolute"
76
       " pathname", None, None),
77
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
78
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
79
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
80
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
81
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
82
    constants.HV_BOOT_ORDER:
83
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
84
    constants.HV_NIC_TYPE:
85
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
86
    constants.HV_DISK_TYPE:
87
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
88
    constants.HV_USB_MOUSE:
89
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
90
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
91
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
92
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
93
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
94
    constants.HV_DISK_CACHE:
95
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
96
    constants.HV_SECURITY_MODEL:
97
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
98
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
99
    constants.HV_KVM_FLAG:
100
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
101
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
102
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
103
    }
104

    
105
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
106
                                    re.M | re.I)
107
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
108
  _MIGRATION_INFO_RETRY_DELAY = 2
109

    
110
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
111

    
112
  ANCILLARY_FILES = [
113
    _KVM_NETWORK_SCRIPT,
114
    ]
115

    
116
  def __init__(self):
117
    hv_base.BaseHypervisor.__init__(self)
118
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
119
    # in a tmpfs filesystem or has been otherwise wiped out.
120
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
121
    utils.EnsureDirs(dirs)
122

    
123
  @classmethod
124
  def _InstancePidFile(cls, instance_name):
125
    """Returns the instance pidfile.
126

127
    """
128
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
129

    
130
  @classmethod
131
  def _InstanceUidFile(cls, instance_name):
132
    """Returns the instance uidfile.
133

134
    """
135
    return utils.PathJoin(cls._UIDS_DIR, instance_name)
136

    
137
  @classmethod
138
  def _InstancePidInfo(cls, pid):
139
    """Check pid file for instance information.
140

141
    Check that a pid file is associated with an instance, and retrieve
142
    information from its command line.
143

144
    @type pid: string or int
145
    @param pid: process id of the instance to check
146
    @rtype: tuple
147
    @return: (instance_name, memory, vcpus)
148
    @raise errors.HypervisorError: when an instance cannot be found
149

150
    """
151
    alive = utils.IsProcessAlive(pid)
152
    if not alive:
153
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
154

    
155
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
156
    try:
157
      cmdline = utils.ReadFile(cmdline_file)
158
    except EnvironmentError, err:
159
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
160
                                   (pid, err))
161

    
162
    instance = None
163
    memory = 0
164
    vcpus = 0
165

    
166
    arg_list = cmdline.split('\x00')
167
    while arg_list:
168
      arg =  arg_list.pop(0)
169
      if arg == "-name":
170
        instance = arg_list.pop(0)
171
      elif arg == "-m":
172
        memory = int(arg_list.pop(0))
173
      elif arg == "-smp":
174
        vcpus = int(arg_list.pop(0))
175

    
176
    if instance is None:
177
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
178
                                   " instance" % pid)
179

    
180
    return (instance, memory, vcpus)
181

    
182
  def _InstancePidAlive(self, instance_name):
183
    """Returns the instance pidfile, pid, and liveness.
184

185
    @type instance_name: string
186
    @param instance_name: instance name
187
    @rtype: tuple
188
    @return: (pid file name, pid, liveness)
189

190
    """
191
    pidfile = self._InstancePidFile(instance_name)
192
    pid = utils.ReadPidFile(pidfile)
193

    
194
    alive = False
195
    try:
196
      cmd_instance = self._InstancePidInfo(pid)[0]
197
      alive = (cmd_instance == instance_name)
198
    except errors.HypervisorError:
199
      pass
200

    
201
    return (pidfile, pid, alive)
202

    
203
  def _CheckDown(self, instance_name):
204
    """Raises an error unless the given instance is down.
205

206
    """
207
    alive = self._InstancePidAlive(instance_name)[2]
208
    if alive:
209
      raise errors.HypervisorError("Failed to start instance %s: %s" %
210
                                   (instance_name, "already running"))
211

    
212
  @classmethod
213
  def _InstanceMonitor(cls, instance_name):
214
    """Returns the instance monitor socket name
215

216
    """
217
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
218

    
219
  @classmethod
220
  def _InstanceSerial(cls, instance_name):
221
    """Returns the instance serial socket name
222

223
    """
224
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
225

    
226
  @staticmethod
227
  def _SocatUnixConsoleParams():
228
    """Returns the correct parameters for socat
229

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

232
    """
233
    if constants.SOCAT_USE_ESCAPE:
234
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
235
    else:
236
      return "echo=0,icanon=0"
237

    
238
  @classmethod
239
  def _InstanceKVMRuntime(cls, instance_name):
240
    """Returns the instance KVM runtime filename
241

242
    """
243
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
244

    
245
  @classmethod
246
  def _InstanceChrootDir(cls, instance_name):
247
    """Returns the name of the KVM chroot dir of the instance
248

249
    """
250
    return utils.PathJoin(cls._CHROOT_DIR, instance_name)
251

    
252
  @classmethod
253
  def _TryReadUidFile(cls, uid_file):
254
    """Try to read a uid file
255

256
    """
257
    if os.path.exists(uid_file):
258
      try:
259
        uid = int(utils.ReadOneLineFile(uid_file))
260
        return uid
261
      except EnvironmentError:
262
        logging.warning("Can't read uid file", exc_info=True)
263
      except (TypeError, ValueError):
264
        logging.warning("Can't parse uid file contents", exc_info=True)
265
    return None
266

    
267
  @classmethod
268
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
269
    """Removes an instance's rutime sockets/files/dirs.
270

271
    """
272
    utils.RemoveFile(pidfile)
273
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
274
    utils.RemoveFile(cls._InstanceSerial(instance_name))
275
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
276
    uid_file = cls._InstanceUidFile(instance_name)
277
    uid = cls._TryReadUidFile(uid_file)
278
    utils.RemoveFile(uid_file)
279
    if uid is not None:
280
      uidpool.ReleaseUid(uid)
281
    try:
282
      chroot_dir = cls._InstanceChrootDir(instance_name)
283
      utils.RemoveDir(chroot_dir)
284
    except OSError, err:
285
      if err.errno == errno.ENOTEMPTY:
286
        # The chroot directory is expected to be empty, but it isn't.
287
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
288
                                          prefix="%s-%s-" %
289
                                          (instance_name,
290
                                           utils.TimestampForFilename()))
291
        logging.warning("The chroot directory of instance %s can not be"
292
                        " removed as it is not empty. Moving it to the"
293
                        " quarantine instead. Please investigate the"
294
                        " contents (%s) and clean up manually",
295
                        instance_name, new_chroot_dir)
296
        utils.RenameFile(chroot_dir, new_chroot_dir)
297
      else:
298
        raise
299

    
300
  def _WriteNetScript(self, instance, seq, nic):
301
    """Write a script to connect a net interface to the proper bridge.
302

303
    This can be used by any qemu-type hypervisor.
304

305
    @param instance: instance we're acting on
306
    @type instance: instance object
307
    @param seq: nic sequence number
308
    @type seq: int
309
    @param nic: nic we're acting on
310
    @type nic: nic object
311
    @return: netscript file name
312
    @rtype: string
313

314
    """
315
    script = StringIO()
316
    script.write("#!/bin/sh\n")
317
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
318
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
319
    script.write("export INSTANCE=%s\n" % instance.name)
320
    script.write("export MAC=%s\n" % nic.mac)
321
    if nic.ip:
322
      script.write("export IP=%s\n" % nic.ip)
323
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
324
    if nic.nicparams[constants.NIC_LINK]:
325
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
326
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
327
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
328
    script.write("export INTERFACE=$1\n")
329
    if instance.tags:
330
      script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
331
    # TODO: make this configurable at ./configure time
332
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
333
    script.write("  # Execute the user-specific vif file\n")
334
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
335
    script.write("else\n")
336
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
337
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
338
      script.write("  # Connect the interface to the bridge\n")
339
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
340
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
341
      if not nic.ip:
342
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
343
      script.write("  # Route traffic targeted at the IP to the interface\n")
344
      if nic.nicparams[constants.NIC_LINK]:
345
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
346
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
347
        script.write("  ip route replace $IP table $LINK proto static"
348
                     " dev $INTERFACE\n")
349
      else:
350
        script.write("  ip route replace $IP proto static"
351
                     " dev $INTERFACE\n")
352
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
353
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
354
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
355
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
356
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
357
      script.write("  fi\n")
358
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
359
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
360
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
361
      script.write("  fi\n")
362
    script.write("fi\n\n")
363
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
364
    # mounted noexec sometimes, so we'll have to find another place.
365
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
366
    tmpfile = os.fdopen(tmpfd, 'w')
367
    try:
368
      tmpfile.write(script.getvalue())
369
    finally:
370
      tmpfile.close()
371
    os.chmod(tmpfile_name, 0755)
372
    return tmpfile_name
373

    
374
  def ListInstances(self):
375
    """Get the list of running instances.
376

377
    We can do this by listing our live instances directory and
378
    checking whether the associated kvm process is still alive.
379

380
    """
381
    result = []
382
    for name in os.listdir(self._PIDS_DIR):
383
      if self._InstancePidAlive(name)[2]:
384
        result.append(name)
385
    return result
386

    
387
  def GetInstanceInfo(self, instance_name):
388
    """Get instance properties.
389

390
    @type instance_name: string
391
    @param instance_name: the instance name
392
    @rtype: tuple of strings
393
    @return: (name, id, memory, vcpus, stat, times)
394

395
    """
396
    _, pid, alive = self._InstancePidAlive(instance_name)
397
    if not alive:
398
      return None
399

    
400
    _, memory, vcpus = self._InstancePidInfo(pid)
401
    stat = "---b-"
402
    times = "0"
403

    
404
    return (instance_name, pid, memory, vcpus, stat, times)
405

    
406
  def GetAllInstancesInfo(self):
407
    """Get properties of all instances.
408

409
    @return: list of tuples (name, id, memory, vcpus, stat, times)
410

411
    """
412
    data = []
413
    for name in os.listdir(self._PIDS_DIR):
414
      try:
415
        info = self.GetInstanceInfo(name)
416
      except errors.HypervisorError:
417
        continue
418
      if info:
419
        data.append(info)
420
    return data
421

    
422
  def _GenerateKVMRuntime(self, instance, block_devices):
423
    """Generate KVM information to start an instance.
424

425
    """
426
    pidfile  = self._InstancePidFile(instance.name)
427
    kvm = constants.KVM_PATH
428
    kvm_cmd = [kvm]
429
    # used just by the vnc server, if enabled
430
    kvm_cmd.extend(['-name', instance.name])
431
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
432
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
433
    kvm_cmd.extend(['-pidfile', pidfile])
434
    kvm_cmd.extend(['-daemonize'])
435
    if not instance.hvparams[constants.HV_ACPI]:
436
      kvm_cmd.extend(['-no-acpi'])
437

    
438
    hvp = instance.hvparams
439
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
440
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
441
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
442

    
443
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
444
      kvm_cmd.extend(["-enable-kvm"])
445
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
446
      kvm_cmd.extend(["-disable-kvm"])
447

    
448
    if boot_network:
449
      kvm_cmd.extend(['-boot', 'n'])
450

    
451
    disk_type = hvp[constants.HV_DISK_TYPE]
452
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
453
      if_val = ',if=virtio'
454
    else:
455
      if_val = ',if=%s' % disk_type
456
    # Cache mode
457
    disk_cache = hvp[constants.HV_DISK_CACHE]
458
    if disk_cache != constants.HT_CACHE_DEFAULT:
459
      cache_val = ",cache=%s" % disk_cache
460
    else:
461
      cache_val = ""
462
    for cfdev, dev_path in block_devices:
463
      if cfdev.mode != constants.DISK_RDWR:
464
        raise errors.HypervisorError("Instance has read-only disks which"
465
                                     " are not supported by KVM")
466
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
467
      if boot_disk:
468
        kvm_cmd.extend(['-boot', 'c'])
469
        if disk_type != constants.HT_DISK_IDE:
470
          boot_val = ',boot=on'
471
        else:
472
          boot_val = ''
473
        # We only boot from the first disk
474
        boot_disk = False
475
      else:
476
        boot_val = ''
477

    
478
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
479
                                                cache_val)
480
      kvm_cmd.extend(['-drive', drive_val])
481

    
482
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
483
    if iso_image:
484
      options = ',format=raw,media=cdrom'
485
      if boot_cdrom:
486
        kvm_cmd.extend(['-boot', 'd'])
487
        if disk_type != constants.HT_DISK_IDE:
488
          options = '%s,boot=on' % options
489
      else:
490
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
491
          if_val = ',if=virtio'
492
        else:
493
          if_val = ',if=%s' % disk_type
494
        options = '%s%s' % (options, if_val)
495
      drive_val = 'file=%s%s' % (iso_image, options)
496
      kvm_cmd.extend(['-drive', drive_val])
497

    
498
    kernel_path = hvp[constants.HV_KERNEL_PATH]
499
    if kernel_path:
500
      kvm_cmd.extend(['-kernel', kernel_path])
501
      initrd_path = hvp[constants.HV_INITRD_PATH]
502
      if initrd_path:
503
        kvm_cmd.extend(['-initrd', initrd_path])
504
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
505
                     hvp[constants.HV_KERNEL_ARGS]]
506
      if hvp[constants.HV_SERIAL_CONSOLE]:
507
        root_append.append('console=ttyS0,38400')
508
      kvm_cmd.extend(['-append', ' '.join(root_append)])
509

    
510
    mouse_type = hvp[constants.HV_USB_MOUSE]
511
    if mouse_type:
512
      kvm_cmd.extend(['-usb'])
513
      kvm_cmd.extend(['-usbdevice', mouse_type])
514

    
515
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
516
    if vnc_bind_address:
517
      if utils.IsValidIP(vnc_bind_address):
518
        if instance.network_port > constants.VNC_BASE_PORT:
519
          display = instance.network_port - constants.VNC_BASE_PORT
520
          if vnc_bind_address == '0.0.0.0':
521
            vnc_arg = ':%d' % (display)
522
          else:
523
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
524
        else:
525
          logging.error("Network port is not a valid VNC display (%d < %d)."
526
                        " Not starting VNC", instance.network_port,
527
                        constants.VNC_BASE_PORT)
528
          vnc_arg = 'none'
529

    
530
        # Only allow tls and other option when not binding to a file, for now.
531
        # kvm/qemu gets confused otherwise about the filename to use.
532
        vnc_append = ''
533
        if hvp[constants.HV_VNC_TLS]:
534
          vnc_append = '%s,tls' % vnc_append
535
          if hvp[constants.HV_VNC_X509_VERIFY]:
536
            vnc_append = '%s,x509verify=%s' % (vnc_append,
537
                                               hvp[constants.HV_VNC_X509])
538
          elif hvp[constants.HV_VNC_X509]:
539
            vnc_append = '%s,x509=%s' % (vnc_append,
540
                                         hvp[constants.HV_VNC_X509])
541
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
542
          vnc_append = '%s,password' % vnc_append
543

    
544
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
545

    
546
      else:
547
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
548

    
549
      kvm_cmd.extend(['-vnc', vnc_arg])
550

    
551
      # Also add a tablet USB device to act as a mouse
552
      # This solves various mouse alignment issues
553
      kvm_cmd.extend(['-usbdevice', 'tablet'])
554
    else:
555
      kvm_cmd.extend(['-nographic'])
556

    
557
    monitor_dev = ("unix:%s,server,nowait" %
558
                   self._InstanceMonitor(instance.name))
559
    kvm_cmd.extend(['-monitor', monitor_dev])
560
    if hvp[constants.HV_SERIAL_CONSOLE]:
561
      serial_dev = ('unix:%s,server,nowait' %
562
                    self._InstanceSerial(instance.name))
563
      kvm_cmd.extend(['-serial', serial_dev])
564
    else:
565
      kvm_cmd.extend(['-serial', 'none'])
566

    
567
    if hvp[constants.HV_USE_LOCALTIME]:
568
      kvm_cmd.extend(['-localtime'])
569

    
570
    if hvp[constants.HV_KVM_USE_CHROOT]:
571
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
572

    
573
    # Save the current instance nics, but defer their expansion as parameters,
574
    # as we'll need to generate executable temp files for them.
575
    kvm_nics = instance.nics
576
    hvparams = hvp
577

    
578
    return (kvm_cmd, kvm_nics, hvparams)
579

    
580
  def _WriteKVMRuntime(self, instance_name, data):
581
    """Write an instance's KVM runtime
582

583
    """
584
    try:
585
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
586
                      data=data)
587
    except EnvironmentError, err:
588
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
589

    
590
  def _ReadKVMRuntime(self, instance_name):
591
    """Read an instance's KVM runtime
592

593
    """
594
    try:
595
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
596
    except EnvironmentError, err:
597
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
598
    return file_content
599

    
600
  def _SaveKVMRuntime(self, instance, kvm_runtime):
601
    """Save an instance's KVM runtime
602

603
    """
604
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
605
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
606
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
607
    self._WriteKVMRuntime(instance.name, serialized_form)
608

    
609
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
610
    """Load an instance's KVM runtime
611

612
    """
613
    if not serialized_runtime:
614
      serialized_runtime = self._ReadKVMRuntime(instance.name)
615
    loaded_runtime = serializer.Load(serialized_runtime)
616
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
617
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
618
    return (kvm_cmd, kvm_nics, hvparams)
619

    
620
  def _RunKVMCmd(self, name, kvm_cmd):
621
    """Run the KVM cmd and check for errors
622

623
    @type name: string
624
    @param name: instance name
625
    @type kvm_cmd: list of strings
626
    @param kvm_cmd: runcmd input for kvm
627

628
    """
629
    result = utils.RunCmd(kvm_cmd)
630
    if result.failed:
631
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
632
                                   (name, result.fail_reason, result.output))
633
    if not self._InstancePidAlive(name)[2]:
634
      raise errors.HypervisorError("Failed to start instance %s" % name)
635

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

639
    @type incoming: tuple of strings
640
    @param incoming: (target_host_ip, port)
641

642
    """
643
    hvp = instance.hvparams
644
    name = instance.name
645
    self._CheckDown(name)
646

    
647
    temp_files = []
648

    
649
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
650

    
651
    security_model = hvp[constants.HV_SECURITY_MODEL]
652
    if security_model == constants.HT_SM_USER:
653
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
654

    
655
    if not kvm_nics:
656
      kvm_cmd.extend(["-net", "none"])
657
    else:
658
      tap_extra = ""
659
      nic_type = hvparams[constants.HV_NIC_TYPE]
660
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
661
        nic_model = "model=virtio"
662
        if hvparams[constants.HV_VHOST_NET]:
663
          tap_extra = ",vhost=on"
664
      else:
665
        nic_model = "model=%s" % nic_type
666

    
667
      for nic_seq, nic in enumerate(kvm_nics):
668
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
669
        script = self._WriteNetScript(instance, nic_seq, nic)
670
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
671
        kvm_cmd.extend(["-net", nic_val])
672
        kvm_cmd.extend(["-net", tap_val])
673
        temp_files.append(script)
674

    
675
    if incoming:
676
      target, port = incoming
677
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
678

    
679
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
680
    vnc_pwd = None
681
    if vnc_pwd_file:
682
      try:
683
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
684
      except EnvironmentError, err:
685
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
686
                                     % (vnc_pwd_file, err))
687

    
688
    if hvp[constants.HV_KVM_USE_CHROOT]:
689
      utils.EnsureDirs([(self._InstanceChrootDir(name),
690
                         constants.SECURE_DIR_MODE)])
691

    
692
    if security_model == constants.HT_SM_POOL:
693
      ss = ssconf.SimpleStore()
694
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
695
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
696
      uid = uidpool.RequestUnusedUid(all_uids)
697
      try:
698
        username = pwd.getpwuid(uid.GetUid()).pw_name
699
        kvm_cmd.extend(["-runas", username])
700
        self._RunKVMCmd(name, kvm_cmd)
701
      except:
702
        uidpool.ReleaseUid(uid)
703
        raise
704
      else:
705
        uid.Unlock()
706
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
707
    else:
708
      self._RunKVMCmd(name, kvm_cmd)
709

    
710
    if vnc_pwd:
711
      change_cmd = 'change vnc password %s' % vnc_pwd
712
      self._CallMonitorCommand(instance.name, change_cmd)
713

    
714
    for filename in temp_files:
715
      utils.RemoveFile(filename)
716

    
717
  def StartInstance(self, instance, block_devices):
718
    """Start an instance.
719

720
    """
721
    self._CheckDown(instance.name)
722
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
723
    self._SaveKVMRuntime(instance, kvm_runtime)
724
    self._ExecuteKVMRuntime(instance, kvm_runtime)
725

    
726
  def _CallMonitorCommand(self, instance_name, command):
727
    """Invoke a command on the instance monitor.
728

729
    """
730
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
731
             (utils.ShellQuote(command),
732
              constants.SOCAT_PATH,
733
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
734
    result = utils.RunCmd(socat)
735
    if result.failed:
736
      msg = ("Failed to send command '%s' to instance %s."
737
             " output: %s, error: %s, fail_reason: %s" %
738
             (command, instance_name,
739
              result.stdout, result.stderr, result.fail_reason))
740
      raise errors.HypervisorError(msg)
741

    
742
    return result
743

    
744
  def StopInstance(self, instance, force=False, retry=False, name=None):
745
    """Stop an instance.
746

747
    """
748
    if name is not None and not force:
749
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
750
    if name is None:
751
      name = instance.name
752
      acpi = instance.hvparams[constants.HV_ACPI]
753
    else:
754
      acpi = False
755
    _, pid, alive = self._InstancePidAlive(name)
756
    if pid > 0 and alive:
757
      if force or not acpi:
758
        utils.KillProcess(pid)
759
      else:
760
        self._CallMonitorCommand(name, 'system_powerdown')
761

    
762
  def CleanupInstance(self, instance_name):
763
    """Cleanup after a stopped instance
764

765
    """
766
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
767
    if pid > 0 and alive:
768
      raise errors.HypervisorError("Cannot cleanup a live instance")
769
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
770

    
771
  def RebootInstance(self, instance):
772
    """Reboot an instance.
773

774
    """
775
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
776
    # socket the instance will stop, but now power up again. So we'll resort
777
    # to shutdown and restart.
778
    _, _, alive = self._InstancePidAlive(instance.name)
779
    if not alive:
780
      raise errors.HypervisorError("Failed to reboot instance %s:"
781
                                   " not running" % instance.name)
782
    # StopInstance will delete the saved KVM runtime so:
783
    # ...first load it...
784
    kvm_runtime = self._LoadKVMRuntime(instance)
785
    # ...now we can safely call StopInstance...
786
    if not self.StopInstance(instance):
787
      self.StopInstance(instance, force=True)
788
    # ...and finally we can save it again, and execute it...
789
    self._SaveKVMRuntime(instance, kvm_runtime)
790
    self._ExecuteKVMRuntime(instance, kvm_runtime)
791

    
792
  def MigrationInfo(self, instance):
793
    """Get instance information to perform a migration.
794

795
    @type instance: L{objects.Instance}
796
    @param instance: instance to be migrated
797
    @rtype: string
798
    @return: content of the KVM runtime file
799

800
    """
801
    return self._ReadKVMRuntime(instance.name)
802

    
803
  def AcceptInstance(self, instance, info, target):
804
    """Prepare to accept an instance.
805

806
    @type instance: L{objects.Instance}
807
    @param instance: instance to be accepted
808
    @type info: string
809
    @param info: content of the KVM runtime file on the source node
810
    @type target: string
811
    @param target: target host (usually ip), on this node
812

813
    """
814
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
815
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
816
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
817

    
818
  def FinalizeMigration(self, instance, info, success):
819
    """Finalize an instance migration.
820

821
    Stop the incoming mode KVM.
822

823
    @type instance: L{objects.Instance}
824
    @param instance: instance whose migration is being finalized
825

826
    """
827
    if success:
828
      self._WriteKVMRuntime(instance.name, info)
829
    else:
830
      self.StopInstance(instance, force=True)
831

    
832
  def MigrateInstance(self, instance, target, live):
833
    """Migrate an instance to a target node.
834

835
    The migration will not be attempted if the instance is not
836
    currently running.
837

838
    @type instance: L{objects.Instance}
839
    @param instance: the instance to be migrated
840
    @type target: string
841
    @param target: ip address of the target node
842
    @type live: boolean
843
    @param live: perform a live migration
844

845
    """
846
    instance_name = instance.name
847
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
848
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
849
    if not alive:
850
      raise errors.HypervisorError("Instance not running, cannot migrate")
851

    
852
    if not utils.TcpPing(target, port, live_port_needed=True):
853
      raise errors.HypervisorError("Remote host %s not listening on port"
854
                                   " %s, cannot migrate" % (target, port))
855

    
856
    if not live:
857
      self._CallMonitorCommand(instance_name, 'stop')
858

    
859
    migrate_command = ('migrate_set_speed %dm' %
860
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
861
    self._CallMonitorCommand(instance_name, migrate_command)
862

    
863
    migrate_command = ('migrate_set_downtime %dms' %
864
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
865
    self._CallMonitorCommand(instance_name, migrate_command)
866

    
867
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
868
    self._CallMonitorCommand(instance_name, migrate_command)
869

    
870
    info_command = 'info migrate'
871
    done = False
872
    broken_answers = 0
873
    while not done:
874
      result = self._CallMonitorCommand(instance_name, info_command)
875
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
876
      if not match:
877
        broken_answers += 1
878
        if not result.stdout:
879
          logging.info("KVM: empty 'info migrate' result")
880
        else:
881
          logging.warning("KVM: unknown 'info migrate' result: %s",
882
                          result.stdout)
883
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
884
      else:
885
        status = match.group(1)
886
        if status == 'completed':
887
          done = True
888
        elif status == 'active':
889
          # reset the broken answers count
890
          broken_answers = 0
891
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
892
        elif status == 'failed' or status == 'cancelled':
893
          if not live:
894
            self._CallMonitorCommand(instance_name, 'cont')
895
          raise errors.HypervisorError("Migration %s at the kvm level" %
896
                                       status)
897
        else:
898
          logging.warning("KVM: unknown migration status '%s'", status)
899
          broken_answers += 1
900
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
901
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
902
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
903

    
904
    utils.KillProcess(pid)
905
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
906

    
907
  def GetNodeInfo(self):
908
    """Return information about the node.
909

910
    This is just a wrapper over the base GetLinuxNodeInfo method.
911

912
    @return: a dict with the following keys (values in MiB):
913
          - memory_total: the total memory size on the node
914
          - memory_free: the available memory on the node for instances
915
          - memory_dom0: the memory used by the node itself, if available
916

917
    """
918
    return self.GetLinuxNodeInfo()
919

    
920
  @classmethod
921
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
922
    """Return a command for connecting to the console of an instance.
923

924
    """
925
    if hvparams[constants.HV_SERIAL_CONSOLE]:
926
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
927
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
928
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
929
    else:
930
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
931

    
932
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
933
    if vnc_bind_address:
934
      if instance.network_port > constants.VNC_BASE_PORT:
935
        display = instance.network_port - constants.VNC_BASE_PORT
936
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
937
                       " (display: %d)'" % (vnc_bind_address,
938
                                            instance.network_port,
939
                                            display))
940
        shell_command = "%s; %s" % (vnc_command, shell_command)
941

    
942
    return shell_command
943

    
944
  def Verify(self):
945
    """Verify the hypervisor.
946

947
    Check that the binary exists.
948

949
    """
950
    if not os.path.exists(constants.KVM_PATH):
951
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
952
    if not os.path.exists(constants.SOCAT_PATH):
953
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
954

    
955

    
956
  @classmethod
957
  def CheckParameterSyntax(cls, hvparams):
958
    """Check the given parameters for validity.
959

960
    @type hvparams:  dict
961
    @param hvparams: dictionary with parameter names/value
962
    @raise errors.HypervisorError: when a parameter is not valid
963

964
    """
965
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
966

    
967
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
968
    if kernel_path:
969
      if not hvparams[constants.HV_ROOT_PATH]:
970
        raise errors.HypervisorError("Need a root partition for the instance,"
971
                                     " if a kernel is defined")
972

    
973
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
974
        not hvparams[constants.HV_VNC_X509]):
975
      raise errors.HypervisorError("%s must be defined, if %s is" %
976
                                   (constants.HV_VNC_X509,
977
                                    constants.HV_VNC_X509_VERIFY))
978

    
979
    boot_order = hvparams[constants.HV_BOOT_ORDER]
980
    if (boot_order == constants.HT_BO_CDROM and
981
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
982
      raise errors.HypervisorError("Cannot boot from cdrom without an"
983
                                   " ISO path")
984

    
985
    security_model = hvparams[constants.HV_SECURITY_MODEL]
986
    if security_model == constants.HT_SM_USER:
987
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
988
        raise errors.HypervisorError("A security domain (user to run kvm as)"
989
                                     " must be specified")
990
    elif (security_model == constants.HT_SM_NONE or
991
          security_model == constants.HT_SM_POOL):
992
      if hvparams[constants.HV_SECURITY_DOMAIN]:
993
        raise errors.HypervisorError("Cannot have a security domain when the"
994
                                     " security model is 'none' or 'pool'")
995

    
996
  @classmethod
997
  def ValidateParameters(cls, hvparams):
998
    """Check the given parameters for validity.
999

1000
    @type hvparams:  dict
1001
    @param hvparams: dictionary with parameter names/value
1002
    @raise errors.HypervisorError: when a parameter is not valid
1003

1004
    """
1005
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1006

    
1007
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1008
    if security_model == constants.HT_SM_USER:
1009
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1010
      try:
1011
        pwd.getpwnam(username)
1012
      except KeyError:
1013
        raise errors.HypervisorError("Unknown security domain user %s"
1014
                                     % username)
1015

    
1016
  @classmethod
1017
  def PowercycleNode(cls):
1018
    """KVM powercycle, just a wrapper over Linux powercycle.
1019

1020
    """
1021
    cls.LinuxPowercycle()