Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 8b312c1d

History | View | Annotate | Download (37.8 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
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
from ganeti import netutils
45

    
46

    
47
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
48

    
49

    
50
def _WriteNetScript(instance, nic, index):
51
  """Write a script to connect a net interface to the proper bridge.
52

53
  This can be used by any qemu-type hypervisor.
54

55
  @type instance: L{objects.Instance}
56
  @param instance: Instance object
57
  @type nic: L{objects.NIC}
58
  @param nic: NIC object
59
  @type index: int
60
  @param index: NIC index
61
  @return: Script
62
  @rtype: string
63

64
  """
65
  if instance.tags:
66
    tags = " ".join(instance.tags)
67
  else:
68
    tags = ""
69

    
70
  buf = StringIO()
71
  sw = utils.ShellWriter(buf)
72
  sw.Write("#!/bin/sh")
73
  sw.Write("# this is autogenerated by Ganeti, please do not edit")
74
  sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
75
  sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
76
  sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
77
  sw.Write("export MODE=%s",
78
           utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
79
  sw.Write("export INTERFACE=\"$1\"")
80
  sw.Write("export TAGS=%s", utils.ShellQuote(tags))
81

    
82
  if nic.ip:
83
    sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
84

    
85
  if nic.nicparams[constants.NIC_LINK]:
86
    sw.Write("export LINK=%s",
87
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
88

    
89
  if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
90
    sw.Write("export BRIDGE=%s",
91
             utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
92

    
93
  # TODO: make this configurable at ./configure time
94
  sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
95
  sw.IncIndent()
96
  try:
97
    sw.Write("# Execute the user-specific vif file")
98
    sw.Write(_KVM_NETWORK_SCRIPT)
99
  finally:
100
    sw.DecIndent()
101
  sw.Write("else")
102
  sw.IncIndent()
103
  try:
104
    sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
105

    
106
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
107
      sw.Write("# Connect the interface to the bridge")
108
      sw.Write("brctl addif $BRIDGE $INTERFACE")
109

    
110
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
111
      if not nic.ip:
112
        raise errors.HypervisorError("nic/%d is routed, but has no IP"
113
                                     " address" % index)
114

    
115
      sw.Write("# Route traffic targeted at the IP to the interface")
116
      if nic.nicparams[constants.NIC_LINK]:
117
        sw.Write("while ip rule del dev $INTERFACE; do :; done")
118
        sw.Write("ip rule add dev $INTERFACE table $LINK")
119
        sw.Write("ip route replace $IP table $LINK proto static"
120
                 " dev $INTERFACE")
121
      else:
122
        sw.Write("ip route replace $IP proto static dev $INTERFACE")
123

    
124
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
125
      sw.Write(" if [ -d %s ]; then", interface_v4_conf)
126
      sw.IncIndent()
127
      try:
128
        sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
129
        sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
130
      finally:
131
        sw.DecIndent()
132
      sw.Write("fi")
133

    
134
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
135
      sw.Write("if [ -d %s ]; then", interface_v6_conf)
136
      sw.IncIndent()
137
      try:
138
        sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
139
        sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
140
      finally:
141
        sw.DecIndent()
142
      sw.Write("fi")
143
  finally:
144
    sw.DecIndent()
145
  sw.Write("fi")
146

    
147
  return buf.getvalue()
148

    
149

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

    
154
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
155
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
156
  _UIDS_DIR = _ROOT_DIR + "/uid" # contains instances reserved uids
157
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
158
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
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,
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_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
    }
210

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

    
216
  ANCILLARY_FILES = [
217
    _KVM_NETWORK_SCRIPT,
218
    ]
219

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

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

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

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

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

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

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

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

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

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

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

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

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

    
284
    return (instance, memory, vcpus)
285

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

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

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

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

    
305
    return (pidfile, pid, alive)
306

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

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

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

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

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

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

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

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

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

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

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

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

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

    
356
  @classmethod
357
  def _TryReadUidFile(cls, uid_file):
358
    """Try to read a uid file
359

360
    """
361
    if os.path.exists(uid_file):
362
      try:
363
        uid = int(utils.ReadOneLineFile(uid_file))
364
        return uid
365
      except EnvironmentError:
366
        logging.warning("Can't read uid file", exc_info=True)
367
      except (TypeError, ValueError):
368
        logging.warning("Can't parse uid file contents", exc_info=True)
369
    return None
370

    
371
  @classmethod
372
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
373
    """Removes an instance's rutime sockets/files/dirs.
374

375
    """
376
    utils.RemoveFile(pidfile)
377
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
378
    utils.RemoveFile(cls._InstanceSerial(instance_name))
379
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
380
    uid_file = cls._InstanceUidFile(instance_name)
381
    uid = cls._TryReadUidFile(uid_file)
382
    utils.RemoveFile(uid_file)
383
    if uid is not None:
384
      uidpool.ReleaseUid(uid)
385
    try:
386
      chroot_dir = cls._InstanceChrootDir(instance_name)
387
      utils.RemoveDir(chroot_dir)
388
    except OSError, err:
389
      if err.errno == errno.ENOTEMPTY:
390
        # The chroot directory is expected to be empty, but it isn't.
391
        new_chroot_dir = tempfile.mkdtemp(dir=cls._CHROOT_QUARANTINE_DIR,
392
                                          prefix="%s-%s-" %
393
                                          (instance_name,
394
                                           utils.TimestampForFilename()))
395
        logging.warning("The chroot directory of instance %s can not be"
396
                        " removed as it is not empty. Moving it to the"
397
                        " quarantine instead. Please investigate the"
398
                        " contents (%s) and clean up manually",
399
                        instance_name, new_chroot_dir)
400
        utils.RenameFile(chroot_dir, new_chroot_dir)
401
      else:
402
        raise
403

    
404
  @staticmethod
405
  def _WriteNetScriptFile(instance, seq, nic):
406
    """Write a script to connect a net interface to the proper bridge.
407

408
    This can be used by any qemu-type hypervisor.
409

410
    @param instance: instance we're acting on
411
    @type instance: instance object
412
    @param seq: nic sequence number
413
    @type seq: int
414
    @param nic: nic we're acting on
415
    @type nic: nic object
416
    @return: netscript file name
417
    @rtype: string
418

419
    """
420
    script = _WriteNetScript(instance, nic, seq)
421

    
422
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
423
    # mounted noexec sometimes, so we'll have to find another place.
424
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
425
    tmpfile = os.fdopen(tmpfd, 'w')
426
    try:
427
      tmpfile.write(script)
428
    finally:
429
      tmpfile.close()
430
    os.chmod(tmpfile_name, 0755)
431
    return tmpfile_name
432

    
433
  def ListInstances(self):
434
    """Get the list of running instances.
435

436
    We can do this by listing our live instances directory and
437
    checking whether the associated kvm process is still alive.
438

439
    """
440
    result = []
441
    for name in os.listdir(self._PIDS_DIR):
442
      if self._InstancePidAlive(name)[2]:
443
        result.append(name)
444
    return result
445

    
446
  def GetInstanceInfo(self, instance_name):
447
    """Get instance properties.
448

449
    @type instance_name: string
450
    @param instance_name: the instance name
451
    @rtype: tuple of strings
452
    @return: (name, id, memory, vcpus, stat, times)
453

454
    """
455
    _, pid, alive = self._InstancePidAlive(instance_name)
456
    if not alive:
457
      return None
458

    
459
    _, memory, vcpus = self._InstancePidInfo(pid)
460
    stat = "---b-"
461
    times = "0"
462

    
463
    return (instance_name, pid, memory, vcpus, stat, times)
464

    
465
  def GetAllInstancesInfo(self):
466
    """Get properties of all instances.
467

468
    @return: list of tuples (name, id, memory, vcpus, stat, times)
469

470
    """
471
    data = []
472
    for name in os.listdir(self._PIDS_DIR):
473
      try:
474
        info = self.GetInstanceInfo(name)
475
      except errors.HypervisorError:
476
        continue
477
      if info:
478
        data.append(info)
479
    return data
480

    
481
  def _GenerateKVMRuntime(self, instance, block_devices):
482
    """Generate KVM information to start an instance.
483

484
    """
485
    pidfile  = self._InstancePidFile(instance.name)
486
    kvm = constants.KVM_PATH
487
    kvm_cmd = [kvm]
488
    # used just by the vnc server, if enabled
489
    kvm_cmd.extend(['-name', instance.name])
490
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
491
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
492
    kvm_cmd.extend(['-pidfile', pidfile])
493
    kvm_cmd.extend(['-daemonize'])
494
    if not instance.hvparams[constants.HV_ACPI]:
495
      kvm_cmd.extend(['-no-acpi'])
496

    
497
    hvp = instance.hvparams
498
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
499
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
500
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
501

    
502
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
503
      kvm_cmd.extend(["-enable-kvm"])
504
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
505
      kvm_cmd.extend(["-disable-kvm"])
506

    
507
    if boot_network:
508
      kvm_cmd.extend(['-boot', 'n'])
509

    
510
    disk_type = hvp[constants.HV_DISK_TYPE]
511
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
512
      if_val = ',if=virtio'
513
    else:
514
      if_val = ',if=%s' % disk_type
515
    # Cache mode
516
    disk_cache = hvp[constants.HV_DISK_CACHE]
517
    if disk_cache != constants.HT_CACHE_DEFAULT:
518
      cache_val = ",cache=%s" % disk_cache
519
    else:
520
      cache_val = ""
521
    for cfdev, dev_path in block_devices:
522
      if cfdev.mode != constants.DISK_RDWR:
523
        raise errors.HypervisorError("Instance has read-only disks which"
524
                                     " are not supported by KVM")
525
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
526
      if boot_disk:
527
        kvm_cmd.extend(['-boot', 'c'])
528
        if disk_type != constants.HT_DISK_IDE:
529
          boot_val = ',boot=on'
530
        else:
531
          boot_val = ''
532
        # We only boot from the first disk
533
        boot_disk = False
534
      else:
535
        boot_val = ''
536

    
537
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
538
                                                cache_val)
539
      kvm_cmd.extend(['-drive', drive_val])
540

    
541
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
542
    if iso_image:
543
      options = ',format=raw,media=cdrom'
544
      if boot_cdrom:
545
        kvm_cmd.extend(['-boot', 'd'])
546
        if disk_type != constants.HT_DISK_IDE:
547
          options = '%s,boot=on' % options
548
      else:
549
        if disk_type == constants.HT_DISK_PARAVIRTUAL:
550
          if_val = ',if=virtio'
551
        else:
552
          if_val = ',if=%s' % disk_type
553
        options = '%s%s' % (options, if_val)
554
      drive_val = 'file=%s%s' % (iso_image, options)
555
      kvm_cmd.extend(['-drive', drive_val])
556

    
557
    kernel_path = hvp[constants.HV_KERNEL_PATH]
558
    if kernel_path:
559
      kvm_cmd.extend(['-kernel', kernel_path])
560
      initrd_path = hvp[constants.HV_INITRD_PATH]
561
      if initrd_path:
562
        kvm_cmd.extend(['-initrd', initrd_path])
563
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
564
                     hvp[constants.HV_KERNEL_ARGS]]
565
      if hvp[constants.HV_SERIAL_CONSOLE]:
566
        root_append.append('console=ttyS0,38400')
567
      kvm_cmd.extend(['-append', ' '.join(root_append)])
568

    
569
    mouse_type = hvp[constants.HV_USB_MOUSE]
570
    if mouse_type:
571
      kvm_cmd.extend(['-usb'])
572
      kvm_cmd.extend(['-usbdevice', mouse_type])
573

    
574
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
575
    if vnc_bind_address:
576
      if netutils.IP4Address.IsValid(vnc_bind_address):
577
        if instance.network_port > constants.VNC_BASE_PORT:
578
          display = instance.network_port - constants.VNC_BASE_PORT
579
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
580
            vnc_arg = ':%d' % (display)
581
          else:
582
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
583
        else:
584
          logging.error("Network port is not a valid VNC display (%d < %d)."
585
                        " Not starting VNC", instance.network_port,
586
                        constants.VNC_BASE_PORT)
587
          vnc_arg = 'none'
588

    
589
        # Only allow tls and other option when not binding to a file, for now.
590
        # kvm/qemu gets confused otherwise about the filename to use.
591
        vnc_append = ''
592
        if hvp[constants.HV_VNC_TLS]:
593
          vnc_append = '%s,tls' % vnc_append
594
          if hvp[constants.HV_VNC_X509_VERIFY]:
595
            vnc_append = '%s,x509verify=%s' % (vnc_append,
596
                                               hvp[constants.HV_VNC_X509])
597
          elif hvp[constants.HV_VNC_X509]:
598
            vnc_append = '%s,x509=%s' % (vnc_append,
599
                                         hvp[constants.HV_VNC_X509])
600
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
601
          vnc_append = '%s,password' % vnc_append
602

    
603
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
604

    
605
      else:
606
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
607

    
608
      kvm_cmd.extend(['-vnc', vnc_arg])
609

    
610
      # Also add a tablet USB device to act as a mouse
611
      # This solves various mouse alignment issues
612
      kvm_cmd.extend(['-usbdevice', 'tablet'])
613
    else:
614
      kvm_cmd.extend(['-nographic'])
615

    
616
    monitor_dev = ("unix:%s,server,nowait" %
617
                   self._InstanceMonitor(instance.name))
618
    kvm_cmd.extend(['-monitor', monitor_dev])
619
    if hvp[constants.HV_SERIAL_CONSOLE]:
620
      serial_dev = ('unix:%s,server,nowait' %
621
                    self._InstanceSerial(instance.name))
622
      kvm_cmd.extend(['-serial', serial_dev])
623
    else:
624
      kvm_cmd.extend(['-serial', 'none'])
625

    
626
    if hvp[constants.HV_USE_LOCALTIME]:
627
      kvm_cmd.extend(['-localtime'])
628

    
629
    if hvp[constants.HV_KVM_USE_CHROOT]:
630
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
631

    
632
    # Save the current instance nics, but defer their expansion as parameters,
633
    # as we'll need to generate executable temp files for them.
634
    kvm_nics = instance.nics
635
    hvparams = hvp
636

    
637
    return (kvm_cmd, kvm_nics, hvparams)
638

    
639
  def _WriteKVMRuntime(self, instance_name, data):
640
    """Write an instance's KVM runtime
641

642
    """
643
    try:
644
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
645
                      data=data)
646
    except EnvironmentError, err:
647
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
648

    
649
  def _ReadKVMRuntime(self, instance_name):
650
    """Read an instance's KVM runtime
651

652
    """
653
    try:
654
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
655
    except EnvironmentError, err:
656
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
657
    return file_content
658

    
659
  def _SaveKVMRuntime(self, instance, kvm_runtime):
660
    """Save an instance's KVM runtime
661

662
    """
663
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
664
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
665
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
666
    self._WriteKVMRuntime(instance.name, serialized_form)
667

    
668
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
669
    """Load an instance's KVM runtime
670

671
    """
672
    if not serialized_runtime:
673
      serialized_runtime = self._ReadKVMRuntime(instance.name)
674
    loaded_runtime = serializer.Load(serialized_runtime)
675
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
676
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
677
    return (kvm_cmd, kvm_nics, hvparams)
678

    
679
  def _RunKVMCmd(self, name, kvm_cmd):
680
    """Run the KVM cmd and check for errors
681

682
    @type name: string
683
    @param name: instance name
684
    @type kvm_cmd: list of strings
685
    @param kvm_cmd: runcmd input for kvm
686

687
    """
688
    result = utils.RunCmd(kvm_cmd)
689
    if result.failed:
690
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
691
                                   (name, result.fail_reason, result.output))
692
    if not self._InstancePidAlive(name)[2]:
693
      raise errors.HypervisorError("Failed to start instance %s" % name)
694

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

698
    @type incoming: tuple of strings
699
    @param incoming: (target_host_ip, port)
700

701
    """
702
    # Small _ExecuteKVMRuntime hv parameters programming howto:
703
    #  - conf_hvp contains the parameters as configured on ganeti. they might
704
    #    have changed since the instance started; only use them if the change
705
    #    won't affect the inside of the instance (which hasn't been rebooted).
706
    #  - up_hvp contains the parameters as they were when the instance was
707
    #    started, plus any new parameter which has been added between ganeti
708
    #    versions: it is paramount that those default to a value which won't
709
    #    affect the inside of the instance as well.
710
    conf_hvp = instance.hvparams
711
    name = instance.name
712
    self._CheckDown(name)
713

    
714
    temp_files = []
715

    
716
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
717
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
718

    
719
    # We know it's safe to run as a different user upon migration, so we'll use
720
    # the latest conf, from conf_hvp.
721
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
722
    if security_model == constants.HT_SM_USER:
723
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
724

    
725
    # We have reasons to believe changing something like the nic driver/type
726
    # upon migration won't exactly fly with the instance kernel, so for nic
727
    # related parameters we'll use up_hvp
728
    if not kvm_nics:
729
      kvm_cmd.extend(["-net", "none"])
730
    else:
731
      tap_extra = ""
732
      nic_type = up_hvp[constants.HV_NIC_TYPE]
733
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
734
        nic_model = "model=virtio"
735
        if up_hvp[constants.HV_VHOST_NET]:
736
          tap_extra = ",vhost=on"
737
      else:
738
        nic_model = "model=%s" % nic_type
739

    
740
      for nic_seq, nic in enumerate(kvm_nics):
741
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
742
        script = self._WriteNetScriptFile(instance, nic_seq, nic)
743
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
744
        kvm_cmd.extend(["-net", nic_val])
745
        kvm_cmd.extend(["-net", tap_val])
746
        temp_files.append(script)
747

    
748
    if incoming:
749
      target, port = incoming
750
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
751

    
752
    # Changing the vnc password doesn't bother the guest that much. At most it
753
    # will surprise people who connect to it. Whether positively or negatively
754
    # it's debatable.
755
    vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
756
    vnc_pwd = None
757
    if vnc_pwd_file:
758
      try:
759
        vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
760
      except EnvironmentError, err:
761
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
762
                                     % (vnc_pwd_file, err))
763

    
764
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
765
      utils.EnsureDirs([(self._InstanceChrootDir(name),
766
                         constants.SECURE_DIR_MODE)])
767

    
768
    if security_model == constants.HT_SM_POOL:
769
      ss = ssconf.SimpleStore()
770
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
771
      all_uids = set(uidpool.ExpandUidPool(uid_pool))
772
      uid = uidpool.RequestUnusedUid(all_uids)
773
      try:
774
        username = pwd.getpwuid(uid.GetUid()).pw_name
775
        kvm_cmd.extend(["-runas", username])
776
        self._RunKVMCmd(name, kvm_cmd)
777
      except:
778
        uidpool.ReleaseUid(uid)
779
        raise
780
      else:
781
        uid.Unlock()
782
        utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
783
    else:
784
      self._RunKVMCmd(name, kvm_cmd)
785

    
786
    if vnc_pwd:
787
      change_cmd = 'change vnc password %s' % vnc_pwd
788
      self._CallMonitorCommand(instance.name, change_cmd)
789

    
790
    for filename in temp_files:
791
      utils.RemoveFile(filename)
792

    
793
  def StartInstance(self, instance, block_devices):
794
    """Start an instance.
795

796
    """
797
    self._CheckDown(instance.name)
798
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
799
    self._SaveKVMRuntime(instance, kvm_runtime)
800
    self._ExecuteKVMRuntime(instance, kvm_runtime)
801

    
802
  def _CallMonitorCommand(self, instance_name, command):
803
    """Invoke a command on the instance monitor.
804

805
    """
806
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
807
             (utils.ShellQuote(command),
808
              constants.SOCAT_PATH,
809
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
810
    result = utils.RunCmd(socat)
811
    if result.failed:
812
      msg = ("Failed to send command '%s' to instance %s."
813
             " output: %s, error: %s, fail_reason: %s" %
814
             (command, instance_name,
815
              result.stdout, result.stderr, result.fail_reason))
816
      raise errors.HypervisorError(msg)
817

    
818
    return result
819

    
820
  def StopInstance(self, instance, force=False, retry=False, name=None):
821
    """Stop an instance.
822

823
    """
824
    if name is not None and not force:
825
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
826
    if name is None:
827
      name = instance.name
828
      acpi = instance.hvparams[constants.HV_ACPI]
829
    else:
830
      acpi = False
831
    _, pid, alive = self._InstancePidAlive(name)
832
    if pid > 0 and alive:
833
      if force or not acpi:
834
        utils.KillProcess(pid)
835
      else:
836
        self._CallMonitorCommand(name, 'system_powerdown')
837

    
838
  def CleanupInstance(self, instance_name):
839
    """Cleanup after a stopped instance
840

841
    """
842
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
843
    if pid > 0 and alive:
844
      raise errors.HypervisorError("Cannot cleanup a live instance")
845
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
846

    
847
  def RebootInstance(self, instance):
848
    """Reboot an instance.
849

850
    """
851
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
852
    # socket the instance will stop, but now power up again. So we'll resort
853
    # to shutdown and restart.
854
    _, _, alive = self._InstancePidAlive(instance.name)
855
    if not alive:
856
      raise errors.HypervisorError("Failed to reboot instance %s:"
857
                                   " not running" % instance.name)
858
    # StopInstance will delete the saved KVM runtime so:
859
    # ...first load it...
860
    kvm_runtime = self._LoadKVMRuntime(instance)
861
    # ...now we can safely call StopInstance...
862
    if not self.StopInstance(instance):
863
      self.StopInstance(instance, force=True)
864
    # ...and finally we can save it again, and execute it...
865
    self._SaveKVMRuntime(instance, kvm_runtime)
866
    self._ExecuteKVMRuntime(instance, kvm_runtime)
867

    
868
  def MigrationInfo(self, instance):
869
    """Get instance information to perform a migration.
870

871
    @type instance: L{objects.Instance}
872
    @param instance: instance to be migrated
873
    @rtype: string
874
    @return: content of the KVM runtime file
875

876
    """
877
    return self._ReadKVMRuntime(instance.name)
878

    
879
  def AcceptInstance(self, instance, info, target):
880
    """Prepare to accept an instance.
881

882
    @type instance: L{objects.Instance}
883
    @param instance: instance to be accepted
884
    @type info: string
885
    @param info: content of the KVM runtime file on the source node
886
    @type target: string
887
    @param target: target host (usually ip), on this node
888

889
    """
890
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
891
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
892
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
893

    
894
  def FinalizeMigration(self, instance, info, success):
895
    """Finalize an instance migration.
896

897
    Stop the incoming mode KVM.
898

899
    @type instance: L{objects.Instance}
900
    @param instance: instance whose migration is being finalized
901

902
    """
903
    if success:
904
      self._WriteKVMRuntime(instance.name, info)
905
    else:
906
      self.StopInstance(instance, force=True)
907

    
908
  def MigrateInstance(self, instance, target, live):
909
    """Migrate an instance to a target node.
910

911
    The migration will not be attempted if the instance is not
912
    currently running.
913

914
    @type instance: L{objects.Instance}
915
    @param instance: the instance to be migrated
916
    @type target: string
917
    @param target: ip address of the target node
918
    @type live: boolean
919
    @param live: perform a live migration
920

921
    """
922
    instance_name = instance.name
923
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
924
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
925
    if not alive:
926
      raise errors.HypervisorError("Instance not running, cannot migrate")
927

    
928
    if not netutils.TcpPing(target, port, live_port_needed=True):
929
      raise errors.HypervisorError("Remote host %s not listening on port"
930
                                   " %s, cannot migrate" % (target, port))
931

    
932
    if not live:
933
      self._CallMonitorCommand(instance_name, 'stop')
934

    
935
    migrate_command = ('migrate_set_speed %dm' %
936
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
937
    self._CallMonitorCommand(instance_name, migrate_command)
938

    
939
    migrate_command = ('migrate_set_downtime %dms' %
940
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
941
    self._CallMonitorCommand(instance_name, migrate_command)
942

    
943
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
944
    self._CallMonitorCommand(instance_name, migrate_command)
945

    
946
    info_command = 'info migrate'
947
    done = False
948
    broken_answers = 0
949
    while not done:
950
      result = self._CallMonitorCommand(instance_name, info_command)
951
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
952
      if not match:
953
        broken_answers += 1
954
        if not result.stdout:
955
          logging.info("KVM: empty 'info migrate' result")
956
        else:
957
          logging.warning("KVM: unknown 'info migrate' result: %s",
958
                          result.stdout)
959
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
960
      else:
961
        status = match.group(1)
962
        if status == 'completed':
963
          done = True
964
        elif status == 'active':
965
          # reset the broken answers count
966
          broken_answers = 0
967
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
968
        elif status == 'failed' or status == 'cancelled':
969
          if not live:
970
            self._CallMonitorCommand(instance_name, 'cont')
971
          raise errors.HypervisorError("Migration %s at the kvm level" %
972
                                       status)
973
        else:
974
          logging.warning("KVM: unknown migration status '%s'", status)
975
          broken_answers += 1
976
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
977
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
978
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
979

    
980
    utils.KillProcess(pid)
981
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
982

    
983
  def GetNodeInfo(self):
984
    """Return information about the node.
985

986
    This is just a wrapper over the base GetLinuxNodeInfo method.
987

988
    @return: a dict with the following keys (values in MiB):
989
          - memory_total: the total memory size on the node
990
          - memory_free: the available memory on the node for instances
991
          - memory_dom0: the memory used by the node itself, if available
992

993
    """
994
    return self.GetLinuxNodeInfo()
995

    
996
  @classmethod
997
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
998
    """Return a command for connecting to the console of an instance.
999

1000
    """
1001
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1002
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
1003
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
1004
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
1005
    else:
1006
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
1007

    
1008
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1009
    if vnc_bind_address:
1010
      if instance.network_port > constants.VNC_BASE_PORT:
1011
        display = instance.network_port - constants.VNC_BASE_PORT
1012
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
1013
                       " (display: %d)'" % (vnc_bind_address,
1014
                                            instance.network_port,
1015
                                            display))
1016
        shell_command = "%s; %s" % (vnc_command, shell_command)
1017

    
1018
    return shell_command
1019

    
1020
  def Verify(self):
1021
    """Verify the hypervisor.
1022

1023
    Check that the binary exists.
1024

1025
    """
1026
    if not os.path.exists(constants.KVM_PATH):
1027
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1028
    if not os.path.exists(constants.SOCAT_PATH):
1029
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1030

    
1031

    
1032
  @classmethod
1033
  def CheckParameterSyntax(cls, hvparams):
1034
    """Check the given parameters for validity.
1035

1036
    @type hvparams:  dict
1037
    @param hvparams: dictionary with parameter names/value
1038
    @raise errors.HypervisorError: when a parameter is not valid
1039

1040
    """
1041
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1042

    
1043
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1044
    if kernel_path:
1045
      if not hvparams[constants.HV_ROOT_PATH]:
1046
        raise errors.HypervisorError("Need a root partition for the instance,"
1047
                                     " if a kernel is defined")
1048

    
1049
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1050
        not hvparams[constants.HV_VNC_X509]):
1051
      raise errors.HypervisorError("%s must be defined, if %s is" %
1052
                                   (constants.HV_VNC_X509,
1053
                                    constants.HV_VNC_X509_VERIFY))
1054

    
1055
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1056
    if (boot_order == constants.HT_BO_CDROM and
1057
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1058
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1059
                                   " ISO path")
1060

    
1061
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1062
    if security_model == constants.HT_SM_USER:
1063
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1064
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1065
                                     " must be specified")
1066
    elif (security_model == constants.HT_SM_NONE or
1067
          security_model == constants.HT_SM_POOL):
1068
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1069
        raise errors.HypervisorError("Cannot have a security domain when the"
1070
                                     " security model is 'none' or 'pool'")
1071

    
1072
  @classmethod
1073
  def ValidateParameters(cls, hvparams):
1074
    """Check the given parameters for validity.
1075

1076
    @type hvparams:  dict
1077
    @param hvparams: dictionary with parameter names/value
1078
    @raise errors.HypervisorError: when a parameter is not valid
1079

1080
    """
1081
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1082

    
1083
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1084
    if security_model == constants.HT_SM_USER:
1085
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1086
      try:
1087
        pwd.getpwnam(username)
1088
      except KeyError:
1089
        raise errors.HypervisorError("Unknown security domain user %s"
1090
                                     % username)
1091

    
1092
  @classmethod
1093
  def PowercycleNode(cls):
1094
    """KVM powercycle, just a wrapper over Linux powercycle.
1095

1096
    """
1097
    cls.LinuxPowercycle()