Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 4f958b0b

History | View | Annotate | Download (38.1 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.IsValidIP4(x) or utils.IsNormAbsPath(x)),
179
       "the VNC bind address must be either a valid IP address or an absolute"
180
       " pathname", None, None),
181
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
182
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
183
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
184
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
185
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
186
    constants.HV_BOOT_ORDER:
187
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
188
    constants.HV_NIC_TYPE:
189
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
190
    constants.HV_DISK_TYPE:
191
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
192
    constants.HV_USB_MOUSE:
193
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
194
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
195
    constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
196
    constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
197
    constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
198
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
199
    constants.HV_DISK_CACHE:
200
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
201
    constants.HV_SECURITY_MODEL:
202
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
203
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
204
    constants.HV_KVM_FLAG:
205
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
206
    constants.HV_VHOST_NET: hv_base.NO_CHECK,
207
    constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
208
    constants.HV_MEM_PATH: hv_base.OPT_DIR_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
  _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
217

    
218
  ANCILLARY_FILES = [
219
    _KVM_NETWORK_SCRIPT,
220
    ]
221

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

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

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

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

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

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

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

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

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

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

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

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

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

    
286
    return (instance, memory, vcpus)
287

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

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

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

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

    
307
    return (pidfile, pid, alive)
308

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

410
    This can be used by any qemu-type hypervisor.
411

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

421
    """
422
    script = _WriteNetScript(instance, nic, seq)
423

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

    
435
  def ListInstances(self):
436
    """Get the list of running instances.
437

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

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

    
448
  def GetInstanceInfo(self, instance_name):
449
    """Get instance properties.
450

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

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

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

    
465
    return (instance_name, pid, memory, vcpus, stat, times)
466

    
467
  def GetAllInstancesInfo(self):
468
    """Get properties of all instances.
469

470
    @return: list of tuples (name, id, memory, vcpus, stat, times)
471

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

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

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

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

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

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

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

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

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

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

    
571
    mem_path = hvp[constants.HV_MEM_PATH]
572
    if mem_path:
573
      kvm_cmd.extend(["-mem-path", mem_path])
574
      kvm_cmd.extend(["-mem-prealloc"])
575

    
576
    mouse_type = hvp[constants.HV_USB_MOUSE]
577
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
578

    
579
    if mouse_type:
580
      kvm_cmd.extend(['-usb'])
581
      kvm_cmd.extend(['-usbdevice', mouse_type])
582
    elif vnc_bind_address:
583
      kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
584

    
585
    if vnc_bind_address:
586
      if netutils.IsValidIP4(vnc_bind_address):
587
        if instance.network_port > constants.VNC_BASE_PORT:
588
          display = instance.network_port - constants.VNC_BASE_PORT
589
          if vnc_bind_address == constants.IP4_ADDRESS_ANY:
590
            vnc_arg = ':%d' % (display)
591
          else:
592
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
593
        else:
594
          logging.error("Network port is not a valid VNC display (%d < %d)."
595
                        " Not starting VNC", instance.network_port,
596
                        constants.VNC_BASE_PORT)
597
          vnc_arg = 'none'
598

    
599
        # Only allow tls and other option when not binding to a file, for now.
600
        # kvm/qemu gets confused otherwise about the filename to use.
601
        vnc_append = ''
602
        if hvp[constants.HV_VNC_TLS]:
603
          vnc_append = '%s,tls' % vnc_append
604
          if hvp[constants.HV_VNC_X509_VERIFY]:
605
            vnc_append = '%s,x509verify=%s' % (vnc_append,
606
                                               hvp[constants.HV_VNC_X509])
607
          elif hvp[constants.HV_VNC_X509]:
608
            vnc_append = '%s,x509=%s' % (vnc_append,
609
                                         hvp[constants.HV_VNC_X509])
610
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
611
          vnc_append = '%s,password' % vnc_append
612

    
613
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
614

    
615
      else:
616
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
617

    
618
      kvm_cmd.extend(['-vnc', vnc_arg])
619
    else:
620
      kvm_cmd.extend(['-nographic'])
621

    
622
    monitor_dev = ("unix:%s,server,nowait" %
623
                   self._InstanceMonitor(instance.name))
624
    kvm_cmd.extend(['-monitor', monitor_dev])
625
    if hvp[constants.HV_SERIAL_CONSOLE]:
626
      serial_dev = ('unix:%s,server,nowait' %
627
                    self._InstanceSerial(instance.name))
628
      kvm_cmd.extend(['-serial', serial_dev])
629
    else:
630
      kvm_cmd.extend(['-serial', 'none'])
631

    
632
    if hvp[constants.HV_USE_LOCALTIME]:
633
      kvm_cmd.extend(['-localtime'])
634

    
635
    if hvp[constants.HV_KVM_USE_CHROOT]:
636
      kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
637

    
638
    # Save the current instance nics, but defer their expansion as parameters,
639
    # as we'll need to generate executable temp files for them.
640
    kvm_nics = instance.nics
641
    hvparams = hvp
642

    
643
    return (kvm_cmd, kvm_nics, hvparams)
644

    
645
  def _WriteKVMRuntime(self, instance_name, data):
646
    """Write an instance's KVM runtime
647

648
    """
649
    try:
650
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
651
                      data=data)
652
    except EnvironmentError, err:
653
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
654

    
655
  def _ReadKVMRuntime(self, instance_name):
656
    """Read an instance's KVM runtime
657

658
    """
659
    try:
660
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
661
    except EnvironmentError, err:
662
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
663
    return file_content
664

    
665
  def _SaveKVMRuntime(self, instance, kvm_runtime):
666
    """Save an instance's KVM runtime
667

668
    """
669
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
670
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
671
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
672
    self._WriteKVMRuntime(instance.name, serialized_form)
673

    
674
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
675
    """Load an instance's KVM runtime
676

677
    """
678
    if not serialized_runtime:
679
      serialized_runtime = self._ReadKVMRuntime(instance.name)
680
    loaded_runtime = serializer.Load(serialized_runtime)
681
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
682
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
683
    return (kvm_cmd, kvm_nics, hvparams)
684

    
685
  def _RunKVMCmd(self, name, kvm_cmd):
686
    """Run the KVM cmd and check for errors
687

688
    @type name: string
689
    @param name: instance name
690
    @type kvm_cmd: list of strings
691
    @param kvm_cmd: runcmd input for kvm
692

693
    """
694
    result = utils.RunCmd(kvm_cmd)
695
    if result.failed:
696
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
697
                                   (name, result.fail_reason, result.output))
698
    if not self._InstancePidAlive(name)[2]:
699
      raise errors.HypervisorError("Failed to start instance %s" % name)
700

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

704
    @type incoming: tuple of strings
705
    @param incoming: (target_host_ip, port)
706

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

    
720
    temp_files = []
721

    
722
    kvm_cmd, kvm_nics, up_hvp = kvm_runtime
723
    up_hvp = objects.FillDict(conf_hvp, up_hvp)
724

    
725
    # We know it's safe to run as a different user upon migration, so we'll use
726
    # the latest conf, from conf_hvp.
727
    security_model = conf_hvp[constants.HV_SECURITY_MODEL]
728
    if security_model == constants.HT_SM_USER:
729
      kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
730

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

    
746
      for nic_seq, nic in enumerate(kvm_nics):
747
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
748
        script = self._WriteNetScriptFile(instance, nic_seq, nic)
749
        tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
750
        kvm_cmd.extend(["-net", nic_val])
751
        kvm_cmd.extend(["-net", tap_val])
752
        temp_files.append(script)
753

    
754
    if incoming:
755
      target, port = incoming
756
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
757

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

    
770
    if conf_hvp[constants.HV_KVM_USE_CHROOT]:
771
      utils.EnsureDirs([(self._InstanceChrootDir(name),
772
                         constants.SECURE_DIR_MODE)])
773

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

    
792
    if vnc_pwd:
793
      change_cmd = 'change vnc password %s' % vnc_pwd
794
      self._CallMonitorCommand(instance.name, change_cmd)
795

    
796
    for filename in temp_files:
797
      utils.RemoveFile(filename)
798

    
799
  def StartInstance(self, instance, block_devices):
800
    """Start an instance.
801

802
    """
803
    self._CheckDown(instance.name)
804
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
805
    self._SaveKVMRuntime(instance, kvm_runtime)
806
    self._ExecuteKVMRuntime(instance, kvm_runtime)
807

    
808
  def _CallMonitorCommand(self, instance_name, command):
809
    """Invoke a command on the instance monitor.
810

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

    
824
    return result
825

    
826
  @classmethod
827
  def _GetKVMVersion(cls):
828
    """Return the installed KVM version
829

830
    @return: (version, v_maj, v_min, v_rev), or None
831

832
    """
833
    result = utils.RunCmd([constants.KVM_PATH, "--help"])
834
    if result.failed:
835
      return None
836
    match = cls._VERSION_RE.search(result.output.splitlines()[0])
837
    if not match:
838
      return None
839
    return (match.group(0), match.group(1), match.group(2), match.group(3))
840

    
841
  def StopInstance(self, instance, force=False, retry=False, name=None):
842
    """Stop an instance.
843

844
    """
845
    if name is not None and not force:
846
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
847
    if name is None:
848
      name = instance.name
849
      acpi = instance.hvparams[constants.HV_ACPI]
850
    else:
851
      acpi = False
852
    _, pid, alive = self._InstancePidAlive(name)
853
    if pid > 0 and alive:
854
      if force or not acpi:
855
        utils.KillProcess(pid)
856
      else:
857
        self._CallMonitorCommand(name, 'system_powerdown')
858

    
859
  def CleanupInstance(self, instance_name):
860
    """Cleanup after a stopped instance
861

862
    """
863
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
864
    if pid > 0 and alive:
865
      raise errors.HypervisorError("Cannot cleanup a live instance")
866
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
867

    
868
  def RebootInstance(self, instance):
869
    """Reboot an instance.
870

871
    """
872
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
873
    # socket the instance will stop, but now power up again. So we'll resort
874
    # to shutdown and restart.
875
    _, _, alive = self._InstancePidAlive(instance.name)
876
    if not alive:
877
      raise errors.HypervisorError("Failed to reboot instance %s:"
878
                                   " not running" % instance.name)
879
    # StopInstance will delete the saved KVM runtime so:
880
    # ...first load it...
881
    kvm_runtime = self._LoadKVMRuntime(instance)
882
    # ...now we can safely call StopInstance...
883
    if not self.StopInstance(instance):
884
      self.StopInstance(instance, force=True)
885
    # ...and finally we can save it again, and execute it...
886
    self._SaveKVMRuntime(instance, kvm_runtime)
887
    self._ExecuteKVMRuntime(instance, kvm_runtime)
888

    
889
  def MigrationInfo(self, instance):
890
    """Get instance information to perform a migration.
891

892
    @type instance: L{objects.Instance}
893
    @param instance: instance to be migrated
894
    @rtype: string
895
    @return: content of the KVM runtime file
896

897
    """
898
    return self._ReadKVMRuntime(instance.name)
899

    
900
  def AcceptInstance(self, instance, info, target):
901
    """Prepare to accept an instance.
902

903
    @type instance: L{objects.Instance}
904
    @param instance: instance to be accepted
905
    @type info: string
906
    @param info: content of the KVM runtime file on the source node
907
    @type target: string
908
    @param target: target host (usually ip), on this node
909

910
    """
911
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
912
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
913
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
914

    
915
  def FinalizeMigration(self, instance, info, success):
916
    """Finalize an instance migration.
917

918
    Stop the incoming mode KVM.
919

920
    @type instance: L{objects.Instance}
921
    @param instance: instance whose migration is being finalized
922

923
    """
924
    if success:
925
      self._WriteKVMRuntime(instance.name, info)
926
    else:
927
      self.StopInstance(instance, force=True)
928

    
929
  def MigrateInstance(self, instance, target, live):
930
    """Migrate an instance to a target node.
931

932
    The migration will not be attempted if the instance is not
933
    currently running.
934

935
    @type instance: L{objects.Instance}
936
    @param instance: the instance to be migrated
937
    @type target: string
938
    @param target: ip address of the target node
939
    @type live: boolean
940
    @param live: perform a live migration
941

942
    """
943
    instance_name = instance.name
944
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
945
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
946
    if not alive:
947
      raise errors.HypervisorError("Instance not running, cannot migrate")
948

    
949
    if not live:
950
      self._CallMonitorCommand(instance_name, 'stop')
951

    
952
    migrate_command = ('migrate_set_speed %dm' %
953
        instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
954
    self._CallMonitorCommand(instance_name, migrate_command)
955

    
956
    migrate_command = ('migrate_set_downtime %dms' %
957
        instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
958
    self._CallMonitorCommand(instance_name, migrate_command)
959

    
960
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
961
    self._CallMonitorCommand(instance_name, migrate_command)
962

    
963
    info_command = 'info migrate'
964
    done = False
965
    broken_answers = 0
966
    while not done:
967
      result = self._CallMonitorCommand(instance_name, info_command)
968
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
969
      if not match:
970
        broken_answers += 1
971
        if not result.stdout:
972
          logging.info("KVM: empty 'info migrate' result")
973
        else:
974
          logging.warning("KVM: unknown 'info migrate' result: %s",
975
                          result.stdout)
976
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
977
      else:
978
        status = match.group(1)
979
        if status == 'completed':
980
          done = True
981
        elif status == 'active':
982
          # reset the broken answers count
983
          broken_answers = 0
984
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
985
        elif status == 'failed' or status == 'cancelled':
986
          if not live:
987
            self._CallMonitorCommand(instance_name, 'cont')
988
          raise errors.HypervisorError("Migration %s at the kvm level" %
989
                                       status)
990
        else:
991
          logging.warning("KVM: unknown migration status '%s'", status)
992
          broken_answers += 1
993
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
994
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
995
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
996

    
997
    utils.KillProcess(pid)
998
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
999

    
1000
  def GetNodeInfo(self):
1001
    """Return information about the node.
1002

1003
    This is just a wrapper over the base GetLinuxNodeInfo method.
1004

1005
    @return: a dict with the following keys (values in MiB):
1006
          - memory_total: the total memory size on the node
1007
          - memory_free: the available memory on the node for instances
1008
          - memory_dom0: the memory used by the node itself, if available
1009

1010
    """
1011
    return self.GetLinuxNodeInfo()
1012

    
1013
  @classmethod
1014
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
1015
    """Return a command for connecting to the console of an instance.
1016

1017
    """
1018
    if hvparams[constants.HV_SERIAL_CONSOLE]:
1019
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
1020
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
1021
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
1022
    else:
1023
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
1024

    
1025
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
1026
    if vnc_bind_address:
1027
      if instance.network_port > constants.VNC_BASE_PORT:
1028
        display = instance.network_port - constants.VNC_BASE_PORT
1029
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
1030
                       " (display: %d)'" % (vnc_bind_address,
1031
                                            instance.network_port,
1032
                                            display))
1033
        shell_command = "%s; %s" % (vnc_command, shell_command)
1034

    
1035
    return shell_command
1036

    
1037
  def Verify(self):
1038
    """Verify the hypervisor.
1039

1040
    Check that the binary exists.
1041

1042
    """
1043
    if not os.path.exists(constants.KVM_PATH):
1044
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
1045
    if not os.path.exists(constants.SOCAT_PATH):
1046
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
1047

    
1048

    
1049
  @classmethod
1050
  def CheckParameterSyntax(cls, hvparams):
1051
    """Check the given parameters for validity.
1052

1053
    @type hvparams:  dict
1054
    @param hvparams: dictionary with parameter names/value
1055
    @raise errors.HypervisorError: when a parameter is not valid
1056

1057
    """
1058
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
1059

    
1060
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
1061
    if kernel_path:
1062
      if not hvparams[constants.HV_ROOT_PATH]:
1063
        raise errors.HypervisorError("Need a root partition for the instance,"
1064
                                     " if a kernel is defined")
1065

    
1066
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
1067
        not hvparams[constants.HV_VNC_X509]):
1068
      raise errors.HypervisorError("%s must be defined, if %s is" %
1069
                                   (constants.HV_VNC_X509,
1070
                                    constants.HV_VNC_X509_VERIFY))
1071

    
1072
    boot_order = hvparams[constants.HV_BOOT_ORDER]
1073
    if (boot_order == constants.HT_BO_CDROM and
1074
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
1075
      raise errors.HypervisorError("Cannot boot from cdrom without an"
1076
                                   " ISO path")
1077

    
1078
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1079
    if security_model == constants.HT_SM_USER:
1080
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
1081
        raise errors.HypervisorError("A security domain (user to run kvm as)"
1082
                                     " must be specified")
1083
    elif (security_model == constants.HT_SM_NONE or
1084
          security_model == constants.HT_SM_POOL):
1085
      if hvparams[constants.HV_SECURITY_DOMAIN]:
1086
        raise errors.HypervisorError("Cannot have a security domain when the"
1087
                                     " security model is 'none' or 'pool'")
1088

    
1089
  @classmethod
1090
  def ValidateParameters(cls, hvparams):
1091
    """Check the given parameters for validity.
1092

1093
    @type hvparams:  dict
1094
    @param hvparams: dictionary with parameter names/value
1095
    @raise errors.HypervisorError: when a parameter is not valid
1096

1097
    """
1098
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
1099

    
1100
    security_model = hvparams[constants.HV_SECURITY_MODEL]
1101
    if security_model == constants.HT_SM_USER:
1102
      username = hvparams[constants.HV_SECURITY_DOMAIN]
1103
      try:
1104
        pwd.getpwnam(username)
1105
      except KeyError:
1106
        raise errors.HypervisorError("Unknown security domain user %s"
1107
                                     % username)
1108

    
1109
  @classmethod
1110
  def PowercycleNode(cls):
1111
    """KVM powercycle, just a wrapper over Linux powercycle.
1112

1113
    """
1114
    cls.LinuxPowercycle()