Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 86d6bc2a

History | View | Annotate | Download (26.9 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 os
27
import os.path
28
import re
29
import tempfile
30
import time
31
import logging
32
from cStringIO import StringIO
33

    
34
from ganeti import utils
35
from ganeti import constants
36
from ganeti import errors
37
from ganeti import serializer
38
from ganeti import objects
39
from ganeti.hypervisor import hv_base
40

    
41

    
42
class KVMHypervisor(hv_base.BaseHypervisor):
43
  """KVM hypervisor interface"""
44

    
45
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
46
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
47
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
48
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
49
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
50

    
51
  PARAMETERS = {
52
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
53
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
54
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
55
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
56
    constants.HV_ACPI: hv_base.NO_CHECK,
57
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
58
    constants.HV_VNC_BIND_ADDRESS:
59
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
60
       "the VNC bind address must be either a valid IP address or an absolute"
61
       " pathname", None, None),
62
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
63
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
64
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
65
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
66
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
67
    constants.HV_BOOT_ORDER:
68
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
69
    constants.HV_NIC_TYPE:
70
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
71
    constants.HV_DISK_TYPE:
72
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
73
    constants.HV_USB_MOUSE:
74
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
75
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
76
    }
77

    
78
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
79
                                    re.M | re.I)
80

    
81
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
82

    
83
  ANCILLARY_FILES = [
84
    _KVM_NETWORK_SCRIPT,
85
    ]
86

    
87
  def __init__(self):
88
    hv_base.BaseHypervisor.__init__(self)
89
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
90
    # in a tmpfs filesystem or has been otherwise wiped out.
91
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
92
    utils.EnsureDirs(dirs)
93

    
94
  def _InstancePidAlive(self, instance_name):
95
    """Returns the instance pid and pidfile
96

97
    """
98
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
99
    pid = utils.ReadPidFile(pidfile)
100
    alive = utils.IsProcessAlive(pid)
101

    
102
    return (pidfile, pid, alive)
103

    
104
  @classmethod
105
  def _InstanceMonitor(cls, instance_name):
106
    """Returns the instance monitor socket name
107

108
    """
109
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
110

    
111
  @classmethod
112
  def _InstanceSerial(cls, instance_name):
113
    """Returns the instance serial socket name
114

115
    """
116
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
117

    
118
  @staticmethod
119
  def _SocatUnixConsoleParams():
120
    """Returns the correct parameters for socat
121

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

124
    """
125
    if constants.SOCAT_ESCAPE:
126
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
127
    else:
128
      return "echo=0,icanon=0"
129

    
130
  @classmethod
131
  def _InstanceKVMRuntime(cls, instance_name):
132
    """Returns the instance KVM runtime filename
133

134
    """
135
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
136

    
137
  @classmethod
138
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
139
    """Removes an instance's rutime sockets/files.
140

141
    """
142
    utils.RemoveFile(pidfile)
143
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
144
    utils.RemoveFile(cls._InstanceSerial(instance_name))
145
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
146

    
147
  def _WriteNetScript(self, instance, seq, nic):
148
    """Write a script to connect a net interface to the proper bridge.
149

150
    This can be used by any qemu-type hypervisor.
151

152
    @param instance: instance we're acting on
153
    @type instance: instance object
154
    @param seq: nic sequence number
155
    @type seq: int
156
    @param nic: nic we're acting on
157
    @type nic: nic object
158
    @return: netscript file name
159
    @rtype: string
160

161
    """
162
    script = StringIO()
163
    script.write("#!/bin/sh\n")
164
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
165
    script.write("export INSTANCE=%s\n" % instance.name)
166
    script.write("export MAC=%s\n" % nic.mac)
167
    if nic.ip:
168
      script.write("export IP=%s\n" % nic.ip)
169
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
170
    if nic.nicparams[constants.NIC_LINK]:
171
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
172
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
173
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
174
    script.write("export INTERFACE=$1\n")
175
    # TODO: make this configurable at ./configure time
176
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
177
    script.write("  # Execute the user-specific vif file\n")
178
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
179
    script.write("else\n")
180
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
181
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
182
      script.write("  # Connect the interface to the bridge\n")
183
      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
184
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
185
      script.write("  # Route traffic targeted at the IP to the interface\n")
186
      if nic.nicparams[constants.NIC_LINK]:
187
        script.write("  while /sbin/ip rule del dev $INTERFACE; do :; done\n")
188
        script.write("  /sbin/ip rule add dev $INTERFACE table $LINK\n")
189
        script.write("  /sbin/ip route replace $IP table $LINK proto static"
190
                     " dev $INTERFACE\n")
191
      else:
192
        script.write("  /sbin/ip route replace $IP proto static"
193
                     " dev $INTERFACE\n")
194
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
195
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
196
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
197
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
198
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
199
      script.write("  fi\n")
200
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
201
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
202
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
203
      script.write("  fi\n")
204
    script.write("fi\n\n")
205
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
206
    # mounted noexec sometimes, so we'll have to find another place.
207
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
208
    tmpfile = os.fdopen(tmpfd, 'w')
209
    try:
210
      tmpfile.write(script.getvalue())
211
    finally:
212
      tmpfile.close()
213
    os.chmod(tmpfile_name, 0755)
214
    return tmpfile_name
215

    
216
  def ListInstances(self):
217
    """Get the list of running instances.
218

219
    We can do this by listing our live instances directory and
220
    checking whether the associated kvm process is still alive.
221

222
    """
223
    result = []
224
    for name in os.listdir(self._PIDS_DIR):
225
      filename = "%s/%s" % (self._PIDS_DIR, name)
226
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
227
        result.append(name)
228
    return result
229

    
230
  def GetInstanceInfo(self, instance_name):
231
    """Get instance properties.
232

233
    @param instance_name: the instance name
234

235
    @return: tuple (name, id, memory, vcpus, stat, times)
236

237
    """
238
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
239
    if not alive:
240
      return None
241

    
242
    cmdline_file = "/proc/%s/cmdline" % pid
243
    try:
244
      cmdline = utils.ReadFile(cmdline_file)
245
    except EnvironmentError, err:
246
      raise errors.HypervisorError("Failed to list instance %s: %s" %
247
                                   (instance_name, err))
248

    
249
    memory = 0
250
    vcpus = 0
251
    stat = "---b-"
252
    times = "0"
253

    
254
    arg_list = cmdline.split('\x00')
255
    while arg_list:
256
      arg =  arg_list.pop(0)
257
      if arg == '-m':
258
        memory = int(arg_list.pop(0))
259
      elif arg == '-smp':
260
        vcpus = int(arg_list.pop(0))
261

    
262
    return (instance_name, pid, memory, vcpus, stat, times)
263

    
264
  def GetAllInstancesInfo(self):
265
    """Get properties of all instances.
266

267
    @return: list of tuples (name, id, memory, vcpus, stat, times)
268

269
    """
270
    data = []
271
    for name in os.listdir(self._PIDS_DIR):
272
      filename = "%s/%s" % (self._PIDS_DIR, name)
273
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
274
        try:
275
          info = self.GetInstanceInfo(name)
276
        except errors.HypervisorError, err:
277
          continue
278
        if info:
279
          data.append(info)
280

    
281
    return data
282

    
283
  def _GenerateKVMRuntime(self, instance, block_devices):
284
    """Generate KVM information to start an instance.
285

286
    """
287
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
288
    kvm = constants.KVM_PATH
289
    kvm_cmd = [kvm]
290
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
291
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
292
    kvm_cmd.extend(['-pidfile', pidfile])
293
    # used just by the vnc server, if enabled
294
    kvm_cmd.extend(['-name', instance.name])
295
    kvm_cmd.extend(['-daemonize'])
296
    if not instance.hvparams[constants.HV_ACPI]:
297
      kvm_cmd.extend(['-no-acpi'])
298

    
299
    hvp = instance.hvparams
300
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
301
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
302
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
303

    
304
    if boot_network:
305
      kvm_cmd.extend(['-boot', 'n'])
306

    
307
    disk_type = hvp[constants.HV_DISK_TYPE]
308
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
309
      if_val = ',if=virtio'
310
    else:
311
      if_val = ',if=%s' % disk_type
312
    for cfdev, dev_path in block_devices:
313
      if cfdev.mode != constants.DISK_RDWR:
314
        raise errors.HypervisorError("Instance has read-only disks which"
315
                                     " are not supported by KVM")
316
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
317
      if boot_disk:
318
        kvm_cmd.extend(['-boot', 'c'])
319
        boot_val = ',boot=on'
320
        # We only boot from the first disk
321
        boot_disk = False
322
      else:
323
        boot_val = ''
324

    
325
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
326
      kvm_cmd.extend(['-drive', drive_val])
327

    
328
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
329
    if iso_image:
330
      options = ',format=raw,media=cdrom'
331
      if boot_cdrom:
332
        kvm_cmd.extend(['-boot', 'd'])
333
        options = '%s,boot=on' % options
334
      else:
335
        options = '%s,if=virtio' % options
336
      drive_val = 'file=%s%s' % (iso_image, options)
337
      kvm_cmd.extend(['-drive', drive_val])
338

    
339
    kernel_path = hvp[constants.HV_KERNEL_PATH]
340
    if kernel_path:
341
      kvm_cmd.extend(['-kernel', kernel_path])
342
      initrd_path = hvp[constants.HV_INITRD_PATH]
343
      if initrd_path:
344
        kvm_cmd.extend(['-initrd', initrd_path])
345
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
346
                     hvp[constants.HV_KERNEL_ARGS]]
347
      if hvp[constants.HV_SERIAL_CONSOLE]:
348
        root_append.append('console=ttyS0,38400')
349
      kvm_cmd.extend(['-append', ' '.join(root_append)])
350

    
351
    mouse_type = hvp[constants.HV_USB_MOUSE]
352
    if mouse_type:
353
      kvm_cmd.extend(['-usb'])
354
      kvm_cmd.extend(['-usbdevice', mouse_type])
355

    
356
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
357
    if vnc_bind_address:
358
      if utils.IsValidIP(vnc_bind_address):
359
        if instance.network_port > constants.VNC_BASE_PORT:
360
          display = instance.network_port - constants.VNC_BASE_PORT
361
          if vnc_bind_address == '0.0.0.0':
362
            vnc_arg = ':%d' % (display)
363
          else:
364
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
365
        else:
366
          logging.error("Network port is not a valid VNC display (%d < %d)."
367
                        " Not starting VNC" %
368
                        (instance.network_port,
369
                         constants.VNC_BASE_PORT))
370
          vnc_arg = 'none'
371

    
372
        # Only allow tls and other option when not binding to a file, for now.
373
        # kvm/qemu gets confused otherwise about the filename to use.
374
        vnc_append = ''
375
        if hvp[constants.HV_VNC_TLS]:
376
          vnc_append = '%s,tls' % vnc_append
377
          if hvp[constants.HV_VNC_X509_VERIFY]:
378
            vnc_append = '%s,x509verify=%s' % (vnc_append,
379
                                               hvp[constants.HV_VNC_X509])
380
          elif hvp[constants.HV_VNC_X509]:
381
            vnc_append = '%s,x509=%s' % (vnc_append,
382
                                         hvp[constants.HV_VNC_X509])
383
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
384
          vnc_append = '%s,password' % vnc_append
385

    
386
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
387

    
388
      else:
389
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
390

    
391
      kvm_cmd.extend(['-vnc', vnc_arg])
392
    else:
393
      kvm_cmd.extend(['-nographic'])
394

    
395
    monitor_dev = ("unix:%s,server,nowait" %
396
                   self._InstanceMonitor(instance.name))
397
    kvm_cmd.extend(['-monitor', monitor_dev])
398
    if hvp[constants.HV_SERIAL_CONSOLE]:
399
      serial_dev = ('unix:%s,server,nowait' %
400
                    self._InstanceSerial(instance.name))
401
      kvm_cmd.extend(['-serial', serial_dev])
402
    else:
403
      kvm_cmd.extend(['-serial', 'none'])
404

    
405
    # Save the current instance nics, but defer their expansion as parameters,
406
    # as we'll need to generate executable temp files for them.
407
    kvm_nics = instance.nics
408
    hvparams = hvp
409

    
410
    return (kvm_cmd, kvm_nics, hvparams)
411

    
412
  def _WriteKVMRuntime(self, instance_name, data):
413
    """Write an instance's KVM runtime
414

415
    """
416
    try:
417
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
418
                      data=data)
419
    except EnvironmentError, err:
420
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
421

    
422
  def _ReadKVMRuntime(self, instance_name):
423
    """Read an instance's KVM runtime
424

425
    """
426
    try:
427
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
428
    except EnvironmentError, err:
429
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
430
    return file_content
431

    
432
  def _SaveKVMRuntime(self, instance, kvm_runtime):
433
    """Save an instance's KVM runtime
434

435
    """
436
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
437
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
438
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
439
    self._WriteKVMRuntime(instance.name, serialized_form)
440

    
441
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
442
    """Load an instance's KVM runtime
443

444
    """
445
    if not serialized_runtime:
446
      serialized_runtime = self._ReadKVMRuntime(instance.name)
447
    loaded_runtime = serializer.Load(serialized_runtime)
448
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
449
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
450
    return (kvm_cmd, kvm_nics, hvparams)
451

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

455
    @type incoming: tuple of strings
456
    @param incoming: (target_host_ip, port)
457

458
    """
459
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
460
    hvp = instance.hvparams
461
    if alive:
462
      raise errors.HypervisorError("Failed to start instance %s: %s" %
463
                                   (instance.name, "already running"))
464

    
465
    temp_files = []
466

    
467
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
468

    
469
    if not kvm_nics:
470
      kvm_cmd.extend(['-net', 'none'])
471
    else:
472
      nic_type = hvparams[constants.HV_NIC_TYPE]
473
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
474
        nic_model = "model=virtio"
475
      else:
476
        nic_model = "model=%s" % nic_type
477

    
478
      for nic_seq, nic in enumerate(kvm_nics):
479
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
480
        script = self._WriteNetScript(instance, nic_seq, nic)
481
        kvm_cmd.extend(['-net', nic_val])
482
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
483
        temp_files.append(script)
484

    
485
    if incoming:
486
      target, port = incoming
487
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
488

    
489
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
490
    vnc_pwd = None
491
    if vnc_pwd_file:
492
      try:
493
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
494
      except EnvironmentError, err:
495
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
496
                                     % (vnc_pwd_file, err))
497

    
498
    result = utils.RunCmd(kvm_cmd)
499
    if result.failed:
500
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
501
                                   (instance.name, result.fail_reason,
502
                                    result.output))
503

    
504
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
505
      raise errors.HypervisorError("Failed to start instance %s: %s" %
506
                                   (instance.name))
507

    
508
    if vnc_pwd:
509
      change_cmd = 'change vnc password %s' % vnc_pwd
510
      self._CallMonitorCommand(instance.name, change_cmd)
511

    
512
    for filename in temp_files:
513
      utils.RemoveFile(filename)
514

    
515
  def StartInstance(self, instance, block_devices):
516
    """Start an instance.
517

518
    """
519
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
520
    if alive:
521
      raise errors.HypervisorError("Failed to start instance %s: %s" %
522
                                   (instance.name, "already running"))
523

    
524
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
525
    self._SaveKVMRuntime(instance, kvm_runtime)
526
    self._ExecuteKVMRuntime(instance, kvm_runtime)
527

    
528
  def _CallMonitorCommand(self, instance_name, command):
529
    """Invoke a command on the instance monitor.
530

531
    """
532
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
533
             (utils.ShellQuote(command),
534
              constants.SOCAT_PATH,
535
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
536
    result = utils.RunCmd(socat)
537
    if result.failed:
538
      msg = ("Failed to send command '%s' to instance %s."
539
             " output: %s, error: %s, fail_reason: %s" %
540
             (command, instance_name,
541
              result.stdout, result.stderr, result.fail_reason))
542
      raise errors.HypervisorError(msg)
543

    
544
    return result
545

    
546
  def StopInstance(self, instance, force=False, retry=False):
547
    """Stop an instance.
548

549
    """
550
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
551
    if pid > 0 and alive:
552
      if force or not instance.hvparams[constants.HV_ACPI]:
553
        utils.KillProcess(pid)
554
      else:
555
        self._CallMonitorCommand(instance.name, 'system_powerdown')
556

    
557
    if not utils.IsProcessAlive(pid):
558
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
559
      return True
560
    else:
561
      return False
562

    
563
  def RebootInstance(self, instance):
564
    """Reboot an instance.
565

566
    """
567
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
568
    # socket the instance will stop, but now power up again. So we'll resort
569
    # to shutdown and restart.
570
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
571
    if not alive:
572
      raise errors.HypervisorError("Failed to reboot instance %s:"
573
                                   " not running" % instance.name)
574
    # StopInstance will delete the saved KVM runtime so:
575
    # ...first load it...
576
    kvm_runtime = self._LoadKVMRuntime(instance)
577
    # ...now we can safely call StopInstance...
578
    if not self.StopInstance(instance):
579
      self.StopInstance(instance, force=True)
580
    # ...and finally we can save it again, and execute it...
581
    self._SaveKVMRuntime(instance, kvm_runtime)
582
    self._ExecuteKVMRuntime(instance, kvm_runtime)
583

    
584
  def MigrationInfo(self, instance):
585
    """Get instance information to perform a migration.
586

587
    @type instance: L{objects.Instance}
588
    @param instance: instance to be migrated
589
    @rtype: string
590
    @return: content of the KVM runtime file
591

592
    """
593
    return self._ReadKVMRuntime(instance.name)
594

    
595
  def AcceptInstance(self, instance, info, target):
596
    """Prepare to accept an instance.
597

598
    @type instance: L{objects.Instance}
599
    @param instance: instance to be accepted
600
    @type info: string
601
    @param info: content of the KVM runtime file on the source node
602
    @type target: string
603
    @param target: target host (usually ip), on this node
604

605
    """
606
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
607
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
608
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
609

    
610
  def FinalizeMigration(self, instance, info, success):
611
    """Finalize an instance migration.
612

613
    Stop the incoming mode KVM.
614

615
    @type instance: L{objects.Instance}
616
    @param instance: instance whose migration is being aborted
617

618
    """
619
    if success:
620
      self._WriteKVMRuntime(instance.name, info)
621
    else:
622
      self.StopInstance(instance, force=True)
623

    
624
  def MigrateInstance(self, instance, target, live):
625
    """Migrate an instance to a target node.
626

627
    The migration will not be attempted if the instance is not
628
    currently running.
629

630
    @type instance: L{objects.Instance}
631
    @param instance: the instance to be migrated
632
    @type target: string
633
    @param target: ip address of the target node
634
    @type live: boolean
635
    @param live: perform a live migration
636

637
    """
638
    instance_name = instance.name
639
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
640
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
641
    if not alive:
642
      raise errors.HypervisorError("Instance not running, cannot migrate")
643

    
644
    if not utils.TcpPing(target, port, live_port_needed=True):
645
      raise errors.HypervisorError("Remote host %s not listening on port"
646
                                   " %s, cannot migrate" % (target, port))
647

    
648
    if not live:
649
      self._CallMonitorCommand(instance_name, 'stop')
650

    
651
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
652
    self._CallMonitorCommand(instance_name, migrate_command)
653

    
654
    info_command = 'info migrate'
655
    done = False
656
    while not done:
657
      result = self._CallMonitorCommand(instance_name, info_command)
658
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
659
      if not match:
660
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
661
                                     result.stdout)
662
      else:
663
        status = match.group(1)
664
        if status == 'completed':
665
          done = True
666
        elif status == 'active':
667
          time.sleep(2)
668
        elif status == 'failed' or status == 'cancelled':
669
          if not live:
670
            self._CallMonitorCommand(instance_name, 'cont')
671
          raise errors.HypervisorError("Migration %s at the kvm level" %
672
                                       status)
673
        else:
674
          logging.info("KVM: unknown migration status '%s'" % status)
675
          time.sleep(2)
676

    
677
    utils.KillProcess(pid)
678
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
679

    
680
  def GetNodeInfo(self):
681
    """Return information about the node.
682

683
    This is just a wrapper over the base GetLinuxNodeInfo method.
684

685
    @return: a dict with the following keys (values in MiB):
686
          - memory_total: the total memory size on the node
687
          - memory_free: the available memory on the node for instances
688
          - memory_dom0: the memory used by the node itself, if available
689

690
    """
691
    return self.GetLinuxNodeInfo()
692

    
693
  @classmethod
694
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
695
    """Return a command for connecting to the console of an instance.
696

697
    """
698
    if hvparams[constants.HV_SERIAL_CONSOLE]:
699
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
700
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
701
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
702
    else:
703
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
704

    
705
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
706
    if vnc_bind_address:
707
      if instance.network_port > constants.VNC_BASE_PORT:
708
        display = instance.network_port - constants.VNC_BASE_PORT
709
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
710
                       " (display: %d)'" % (vnc_bind_address,
711
                                            instance.network_port,
712
                                            display))
713
        shell_command = "%s; %s" % (vnc_command, shell_command)
714

    
715
    return shell_command
716

    
717
  def Verify(self):
718
    """Verify the hypervisor.
719

720
    Check that the binary exists.
721

722
    """
723
    if not os.path.exists(constants.KVM_PATH):
724
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
725
    if not os.path.exists(constants.SOCAT_PATH):
726
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
727

    
728

    
729
  @classmethod
730
  def CheckParameterSyntax(cls, hvparams):
731
    """Check the given parameters for validity.
732

733
    @type hvparams:  dict
734
    @param hvparams: dictionary with parameter names/value
735
    @raise errors.HypervisorError: when a parameter is not valid
736

737
    """
738
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
739

    
740
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
741
    if kernel_path:
742
      if not hvparams[constants.HV_ROOT_PATH]:
743
        raise errors.HypervisorError("Need a root partition for the instance,"
744
                                     " if a kernel is defined")
745

    
746
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
747
        not hvparams[constants.HV_VNC_X509]):
748
      raise errors.HypervisorError("%s must be defined, if %s is" %
749
                                   (constants.HV_VNC_X509,
750
                                    constants.HV_VNC_X509_VERIFY))
751

    
752
    boot_order = hvparams[constants.HV_BOOT_ORDER]
753

    
754
    if (boot_order == constants.HT_BO_CDROM and
755
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
756
      raise errors.HypervisorError("Cannot boot from cdrom without an"
757
                                   " ISO path")
758
    if (boot_order == constants.HT_BO_NETWORK and
759
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
760
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
761
                                   " change the NIC type.")
762

    
763
  @classmethod
764
  def PowercycleNode(cls):
765
    """KVM powercycle, just a wrapper over Linux powercycle.
766

767
    """
768
    cls.LinuxPowercycle()