Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ e4dd2299

History | View | Annotate | Download (27.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
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
77
    constants.HV_DISK_CACHE:
78
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
79
    }
80

    
81
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
82
                                    re.M | re.I)
83
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
84
  _MIGRATION_INFO_RETRY_DELAY = 2
85

    
86
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
87

    
88
  ANCILLARY_FILES = [
89
    _KVM_NETWORK_SCRIPT,
90
    ]
91

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

    
99
  def _InstancePidFile(self, instance_name):
100
    """Returns the instance pidfile.
101

102
    """
103
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
104
    return pidfile
105

    
106
  def _InstancePidAlive(self, instance_name):
107
    """Returns the instance pid and pidfile
108

109
    """
110
    pidfile = self._InstancePidFile(instance_name)
111
    pid = utils.ReadPidFile(pidfile)
112
    alive = utils.IsProcessAlive(pid)
113

    
114
    return (pidfile, pid, alive)
115

    
116
  def _CheckDown(self, instance_name):
117
    """Raises an error unless the given instance is down.
118

119
    """
120
    alive = self._InstancePidAlive(instance_name)[2]
121
    if alive:
122
      raise errors.HypervisorError("Failed to start instance %s: %s" %
123
                                   (instance_name, "already running"))
124

    
125
  @classmethod
126
  def _InstanceMonitor(cls, instance_name):
127
    """Returns the instance monitor socket name
128

129
    """
130
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
131

    
132
  @classmethod
133
  def _InstanceSerial(cls, instance_name):
134
    """Returns the instance serial socket name
135

136
    """
137
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
138

    
139
  @staticmethod
140
  def _SocatUnixConsoleParams():
141
    """Returns the correct parameters for socat
142

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

145
    """
146
    if constants.SOCAT_USE_ESCAPE:
147
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
148
    else:
149
      return "echo=0,icanon=0"
150

    
151
  @classmethod
152
  def _InstanceKVMRuntime(cls, instance_name):
153
    """Returns the instance KVM runtime filename
154

155
    """
156
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
157

    
158
  @classmethod
159
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
160
    """Removes an instance's rutime sockets/files.
161

162
    """
163
    utils.RemoveFile(pidfile)
164
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
165
    utils.RemoveFile(cls._InstanceSerial(instance_name))
166
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
167

    
168
  def _WriteNetScript(self, instance, seq, nic):
169
    """Write a script to connect a net interface to the proper bridge.
170

171
    This can be used by any qemu-type hypervisor.
172

173
    @param instance: instance we're acting on
174
    @type instance: instance object
175
    @param seq: nic sequence number
176
    @type seq: int
177
    @param nic: nic we're acting on
178
    @type nic: nic object
179
    @return: netscript file name
180
    @rtype: string
181

182
    """
183
    script = StringIO()
184
    script.write("#!/bin/sh\n")
185
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
186
    script.write("export INSTANCE=%s\n" % instance.name)
187
    script.write("export MAC=%s\n" % nic.mac)
188
    if nic.ip:
189
      script.write("export IP=%s\n" % nic.ip)
190
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
191
    if nic.nicparams[constants.NIC_LINK]:
192
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
193
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
194
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
195
    script.write("export INTERFACE=$1\n")
196
    # TODO: make this configurable at ./configure time
197
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
198
    script.write("  # Execute the user-specific vif file\n")
199
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
200
    script.write("else\n")
201
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
202
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
203
      script.write("  # Connect the interface to the bridge\n")
204
      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
205
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
206
      if not nic.ip:
207
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
208
      script.write("  # Route traffic targeted at the IP to the interface\n")
209
      if nic.nicparams[constants.NIC_LINK]:
210
        script.write("  while /sbin/ip rule del dev $INTERFACE; do :; done\n")
211
        script.write("  /sbin/ip rule add dev $INTERFACE table $LINK\n")
212
        script.write("  /sbin/ip route replace $IP table $LINK proto static"
213
                     " dev $INTERFACE\n")
214
      else:
215
        script.write("  /sbin/ip route replace $IP proto static"
216
                     " dev $INTERFACE\n")
217
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
218
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
219
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
220
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
221
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
222
      script.write("  fi\n")
223
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
224
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
225
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
226
      script.write("  fi\n")
227
    script.write("fi\n\n")
228
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
229
    # mounted noexec sometimes, so we'll have to find another place.
230
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
231
    tmpfile = os.fdopen(tmpfd, 'w')
232
    try:
233
      tmpfile.write(script.getvalue())
234
    finally:
235
      tmpfile.close()
236
    os.chmod(tmpfile_name, 0755)
237
    return tmpfile_name
238

    
239
  def ListInstances(self):
240
    """Get the list of running instances.
241

242
    We can do this by listing our live instances directory and
243
    checking whether the associated kvm process is still alive.
244

245
    """
246
    result = []
247
    for name in os.listdir(self._PIDS_DIR):
248
      filename = "%s/%s" % (self._PIDS_DIR, name)
249
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
250
        result.append(name)
251
    return result
252

    
253
  def GetInstanceInfo(self, instance_name):
254
    """Get instance properties.
255

256
    @param instance_name: the instance name
257

258
    @return: tuple (name, id, memory, vcpus, stat, times)
259

260
    """
261
    _, pid, alive = self._InstancePidAlive(instance_name)
262
    if not alive:
263
      return None
264

    
265
    cmdline_file = "/proc/%s/cmdline" % pid
266
    try:
267
      cmdline = utils.ReadFile(cmdline_file)
268
    except EnvironmentError, err:
269
      raise errors.HypervisorError("Failed to list instance %s: %s" %
270
                                   (instance_name, err))
271

    
272
    memory = 0
273
    vcpus = 0
274
    stat = "---b-"
275
    times = "0"
276

    
277
    arg_list = cmdline.split('\x00')
278
    while arg_list:
279
      arg =  arg_list.pop(0)
280
      if arg == '-m':
281
        memory = int(arg_list.pop(0))
282
      elif arg == '-smp':
283
        vcpus = int(arg_list.pop(0))
284

    
285
    return (instance_name, pid, memory, vcpus, stat, times)
286

    
287
  def GetAllInstancesInfo(self):
288
    """Get properties of all instances.
289

290
    @return: list of tuples (name, id, memory, vcpus, stat, times)
291

292
    """
293
    data = []
294
    for name in os.listdir(self._PIDS_DIR):
295
      filename = "%s/%s" % (self._PIDS_DIR, name)
296
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
297
        try:
298
          info = self.GetInstanceInfo(name)
299
        except errors.HypervisorError:
300
          continue
301
        if info:
302
          data.append(info)
303

    
304
    return data
305

    
306
  def _GenerateKVMRuntime(self, instance, block_devices):
307
    """Generate KVM information to start an instance.
308

309
    """
310
    pidfile  = self._InstancePidFile(instance.name)
311
    kvm = constants.KVM_PATH
312
    kvm_cmd = [kvm]
313
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
314
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
315
    kvm_cmd.extend(['-pidfile', pidfile])
316
    # used just by the vnc server, if enabled
317
    kvm_cmd.extend(['-name', instance.name])
318
    kvm_cmd.extend(['-daemonize'])
319
    if not instance.hvparams[constants.HV_ACPI]:
320
      kvm_cmd.extend(['-no-acpi'])
321

    
322
    hvp = instance.hvparams
323
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
324
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
325
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
326

    
327
    if boot_network:
328
      kvm_cmd.extend(['-boot', 'n'])
329

    
330
    disk_type = hvp[constants.HV_DISK_TYPE]
331
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
332
      if_val = ',if=virtio'
333
    else:
334
      if_val = ',if=%s' % disk_type
335
    # Cache mode
336
    disk_cache = hvp[constants.HV_DISK_CACHE]
337
    if disk_cache != constants.HT_CACHE_DEFAULT:
338
      cache_val = ",cache=%s" % disk_cache
339
    else:
340
      cache_val = ""
341
    for cfdev, dev_path in block_devices:
342
      if cfdev.mode != constants.DISK_RDWR:
343
        raise errors.HypervisorError("Instance has read-only disks which"
344
                                     " are not supported by KVM")
345
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
346
      if boot_disk:
347
        kvm_cmd.extend(['-boot', 'c'])
348
        boot_val = ',boot=on'
349
        # We only boot from the first disk
350
        boot_disk = False
351
      else:
352
        boot_val = ''
353

    
354
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
355
                                                cache_val)
356
      kvm_cmd.extend(['-drive', drive_val])
357

    
358
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
359
    if iso_image:
360
      options = ',format=raw,media=cdrom'
361
      if boot_cdrom:
362
        kvm_cmd.extend(['-boot', 'd'])
363
        options = '%s,boot=on' % options
364
      else:
365
        options = '%s,if=virtio' % options
366
      drive_val = 'file=%s%s' % (iso_image, options)
367
      kvm_cmd.extend(['-drive', drive_val])
368

    
369
    kernel_path = hvp[constants.HV_KERNEL_PATH]
370
    if kernel_path:
371
      kvm_cmd.extend(['-kernel', kernel_path])
372
      initrd_path = hvp[constants.HV_INITRD_PATH]
373
      if initrd_path:
374
        kvm_cmd.extend(['-initrd', initrd_path])
375
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
376
                     hvp[constants.HV_KERNEL_ARGS]]
377
      if hvp[constants.HV_SERIAL_CONSOLE]:
378
        root_append.append('console=ttyS0,38400')
379
      kvm_cmd.extend(['-append', ' '.join(root_append)])
380

    
381
    mouse_type = hvp[constants.HV_USB_MOUSE]
382
    if mouse_type:
383
      kvm_cmd.extend(['-usb'])
384
      kvm_cmd.extend(['-usbdevice', mouse_type])
385

    
386
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
387
    if vnc_bind_address:
388
      if utils.IsValidIP(vnc_bind_address):
389
        if instance.network_port > constants.VNC_BASE_PORT:
390
          display = instance.network_port - constants.VNC_BASE_PORT
391
          if vnc_bind_address == '0.0.0.0':
392
            vnc_arg = ':%d' % (display)
393
          else:
394
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
395
        else:
396
          logging.error("Network port is not a valid VNC display (%d < %d)."
397
                        " Not starting VNC", instance.network_port,
398
                        constants.VNC_BASE_PORT)
399
          vnc_arg = 'none'
400

    
401
        # Only allow tls and other option when not binding to a file, for now.
402
        # kvm/qemu gets confused otherwise about the filename to use.
403
        vnc_append = ''
404
        if hvp[constants.HV_VNC_TLS]:
405
          vnc_append = '%s,tls' % vnc_append
406
          if hvp[constants.HV_VNC_X509_VERIFY]:
407
            vnc_append = '%s,x509verify=%s' % (vnc_append,
408
                                               hvp[constants.HV_VNC_X509])
409
          elif hvp[constants.HV_VNC_X509]:
410
            vnc_append = '%s,x509=%s' % (vnc_append,
411
                                         hvp[constants.HV_VNC_X509])
412
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
413
          vnc_append = '%s,password' % vnc_append
414

    
415
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
416

    
417
      else:
418
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
419

    
420
      kvm_cmd.extend(['-vnc', vnc_arg])
421
    else:
422
      kvm_cmd.extend(['-nographic'])
423

    
424
    monitor_dev = ("unix:%s,server,nowait" %
425
                   self._InstanceMonitor(instance.name))
426
    kvm_cmd.extend(['-monitor', monitor_dev])
427
    if hvp[constants.HV_SERIAL_CONSOLE]:
428
      serial_dev = ('unix:%s,server,nowait' %
429
                    self._InstanceSerial(instance.name))
430
      kvm_cmd.extend(['-serial', serial_dev])
431
    else:
432
      kvm_cmd.extend(['-serial', 'none'])
433

    
434
    if hvp[constants.HV_USE_LOCALTIME]:
435
      kvm_cmd.extend(['-localtime'])
436

    
437
    # Save the current instance nics, but defer their expansion as parameters,
438
    # as we'll need to generate executable temp files for them.
439
    kvm_nics = instance.nics
440
    hvparams = hvp
441

    
442
    return (kvm_cmd, kvm_nics, hvparams)
443

    
444
  def _WriteKVMRuntime(self, instance_name, data):
445
    """Write an instance's KVM runtime
446

447
    """
448
    try:
449
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
450
                      data=data)
451
    except EnvironmentError, err:
452
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
453

    
454
  def _ReadKVMRuntime(self, instance_name):
455
    """Read an instance's KVM runtime
456

457
    """
458
    try:
459
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
460
    except EnvironmentError, err:
461
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
462
    return file_content
463

    
464
  def _SaveKVMRuntime(self, instance, kvm_runtime):
465
    """Save an instance's KVM runtime
466

467
    """
468
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
469
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
470
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
471
    self._WriteKVMRuntime(instance.name, serialized_form)
472

    
473
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
474
    """Load an instance's KVM runtime
475

476
    """
477
    if not serialized_runtime:
478
      serialized_runtime = self._ReadKVMRuntime(instance.name)
479
    loaded_runtime = serializer.Load(serialized_runtime)
480
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
481
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
482
    return (kvm_cmd, kvm_nics, hvparams)
483

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

487
    @type incoming: tuple of strings
488
    @param incoming: (target_host_ip, port)
489

490
    """
491
    hvp = instance.hvparams
492
    name = instance.name
493
    self._CheckDown(name)
494

    
495
    temp_files = []
496

    
497
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
498

    
499
    if not kvm_nics:
500
      kvm_cmd.extend(['-net', 'none'])
501
    else:
502
      nic_type = hvparams[constants.HV_NIC_TYPE]
503
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
504
        nic_model = "model=virtio"
505
      else:
506
        nic_model = "model=%s" % nic_type
507

    
508
      for nic_seq, nic in enumerate(kvm_nics):
509
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
510
        script = self._WriteNetScript(instance, nic_seq, nic)
511
        kvm_cmd.extend(['-net', nic_val])
512
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
513
        temp_files.append(script)
514

    
515
    if incoming:
516
      target, port = incoming
517
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
518

    
519
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
520
    vnc_pwd = None
521
    if vnc_pwd_file:
522
      try:
523
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
524
      except EnvironmentError, err:
525
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
526
                                     % (vnc_pwd_file, err))
527

    
528
    result = utils.RunCmd(kvm_cmd)
529
    if result.failed:
530
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
531
                                   (name, result.fail_reason, result.output))
532

    
533
    if not self._InstancePidAlive(name)[2]:
534
      raise errors.HypervisorError("Failed to start instance %s" % name)
535

    
536
    if vnc_pwd:
537
      change_cmd = 'change vnc password %s' % vnc_pwd
538
      self._CallMonitorCommand(instance.name, change_cmd)
539

    
540
    for filename in temp_files:
541
      utils.RemoveFile(filename)
542

    
543
  def StartInstance(self, instance, block_devices):
544
    """Start an instance.
545

546
    """
547
    self._CheckDown(instance.name)
548
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
549
    self._SaveKVMRuntime(instance, kvm_runtime)
550
    self._ExecuteKVMRuntime(instance, kvm_runtime)
551

    
552
  def _CallMonitorCommand(self, instance_name, command):
553
    """Invoke a command on the instance monitor.
554

555
    """
556
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
557
             (utils.ShellQuote(command),
558
              constants.SOCAT_PATH,
559
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
560
    result = utils.RunCmd(socat)
561
    if result.failed:
562
      msg = ("Failed to send command '%s' to instance %s."
563
             " output: %s, error: %s, fail_reason: %s" %
564
             (command, instance_name,
565
              result.stdout, result.stderr, result.fail_reason))
566
      raise errors.HypervisorError(msg)
567

    
568
    return result
569

    
570
  def StopInstance(self, instance, force=False, retry=False):
571
    """Stop an instance.
572

573
    """
574
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
575
    if pid > 0 and alive:
576
      if force or not instance.hvparams[constants.HV_ACPI]:
577
        utils.KillProcess(pid)
578
      else:
579
        self._CallMonitorCommand(instance.name, 'system_powerdown')
580

    
581
    if not utils.IsProcessAlive(pid):
582
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
583
      return True
584
    else:
585
      return False
586

    
587
  def RebootInstance(self, instance):
588
    """Reboot an instance.
589

590
    """
591
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
592
    # socket the instance will stop, but now power up again. So we'll resort
593
    # to shutdown and restart.
594
    _, _, alive = self._InstancePidAlive(instance.name)
595
    if not alive:
596
      raise errors.HypervisorError("Failed to reboot instance %s:"
597
                                   " not running" % instance.name)
598
    # StopInstance will delete the saved KVM runtime so:
599
    # ...first load it...
600
    kvm_runtime = self._LoadKVMRuntime(instance)
601
    # ...now we can safely call StopInstance...
602
    if not self.StopInstance(instance):
603
      self.StopInstance(instance, force=True)
604
    # ...and finally we can save it again, and execute it...
605
    self._SaveKVMRuntime(instance, kvm_runtime)
606
    self._ExecuteKVMRuntime(instance, kvm_runtime)
607

    
608
  def MigrationInfo(self, instance):
609
    """Get instance information to perform a migration.
610

611
    @type instance: L{objects.Instance}
612
    @param instance: instance to be migrated
613
    @rtype: string
614
    @return: content of the KVM runtime file
615

616
    """
617
    return self._ReadKVMRuntime(instance.name)
618

    
619
  def AcceptInstance(self, instance, info, target):
620
    """Prepare to accept an instance.
621

622
    @type instance: L{objects.Instance}
623
    @param instance: instance to be accepted
624
    @type info: string
625
    @param info: content of the KVM runtime file on the source node
626
    @type target: string
627
    @param target: target host (usually ip), on this node
628

629
    """
630
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
631
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
632
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
633

    
634
  def FinalizeMigration(self, instance, info, success):
635
    """Finalize an instance migration.
636

637
    Stop the incoming mode KVM.
638

639
    @type instance: L{objects.Instance}
640
    @param instance: instance whose migration is being aborted
641

642
    """
643
    if success:
644
      self._WriteKVMRuntime(instance.name, info)
645
    else:
646
      self.StopInstance(instance, force=True)
647

    
648
  def MigrateInstance(self, instance, target, live):
649
    """Migrate an instance to a target node.
650

651
    The migration will not be attempted if the instance is not
652
    currently running.
653

654
    @type instance: L{objects.Instance}
655
    @param instance: the instance to be migrated
656
    @type target: string
657
    @param target: ip address of the target node
658
    @type live: boolean
659
    @param live: perform a live migration
660

661
    """
662
    instance_name = instance.name
663
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
664
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
665
    if not alive:
666
      raise errors.HypervisorError("Instance not running, cannot migrate")
667

    
668
    if not utils.TcpPing(target, port, live_port_needed=True):
669
      raise errors.HypervisorError("Remote host %s not listening on port"
670
                                   " %s, cannot migrate" % (target, port))
671

    
672
    if not live:
673
      self._CallMonitorCommand(instance_name, 'stop')
674

    
675
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
676
    self._CallMonitorCommand(instance_name, migrate_command)
677

    
678
    info_command = 'info migrate'
679
    done = False
680
    broken_answers = 0
681
    while not done:
682
      result = self._CallMonitorCommand(instance_name, info_command)
683
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
684
      if not match:
685
        broken_answers += 1
686
        if not result.stdout:
687
          logging.info("KVM: empty 'info migrate' result")
688
        else:
689
          logging.warning("KVM: unknown 'info migrate' result: %s",
690
                          result.stdout)
691
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
692
      else:
693
        status = match.group(1)
694
        if status == 'completed':
695
          done = True
696
        elif status == 'active':
697
          # reset the broken answers count
698
          broken_answers = 0
699
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
700
        elif status == 'failed' or status == 'cancelled':
701
          if not live:
702
            self._CallMonitorCommand(instance_name, 'cont')
703
          raise errors.HypervisorError("Migration %s at the kvm level" %
704
                                       status)
705
        else:
706
          logging.warning("KVM: unknown migration status '%s'", status)
707
          broken_answers += 1
708
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
709
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
710
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
711

    
712
    utils.KillProcess(pid)
713
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
714

    
715
  def GetNodeInfo(self):
716
    """Return information about the node.
717

718
    This is just a wrapper over the base GetLinuxNodeInfo method.
719

720
    @return: a dict with the following keys (values in MiB):
721
          - memory_total: the total memory size on the node
722
          - memory_free: the available memory on the node for instances
723
          - memory_dom0: the memory used by the node itself, if available
724

725
    """
726
    return self.GetLinuxNodeInfo()
727

    
728
  @classmethod
729
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
730
    """Return a command for connecting to the console of an instance.
731

732
    """
733
    if hvparams[constants.HV_SERIAL_CONSOLE]:
734
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
735
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
736
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
737
    else:
738
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
739

    
740
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
741
    if vnc_bind_address:
742
      if instance.network_port > constants.VNC_BASE_PORT:
743
        display = instance.network_port - constants.VNC_BASE_PORT
744
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
745
                       " (display: %d)'" % (vnc_bind_address,
746
                                            instance.network_port,
747
                                            display))
748
        shell_command = "%s; %s" % (vnc_command, shell_command)
749

    
750
    return shell_command
751

    
752
  def Verify(self):
753
    """Verify the hypervisor.
754

755
    Check that the binary exists.
756

757
    """
758
    if not os.path.exists(constants.KVM_PATH):
759
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
760
    if not os.path.exists(constants.SOCAT_PATH):
761
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
762

    
763

    
764
  @classmethod
765
  def CheckParameterSyntax(cls, hvparams):
766
    """Check the given parameters for validity.
767

768
    @type hvparams:  dict
769
    @param hvparams: dictionary with parameter names/value
770
    @raise errors.HypervisorError: when a parameter is not valid
771

772
    """
773
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
774

    
775
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
776
    if kernel_path:
777
      if not hvparams[constants.HV_ROOT_PATH]:
778
        raise errors.HypervisorError("Need a root partition for the instance,"
779
                                     " if a kernel is defined")
780

    
781
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
782
        not hvparams[constants.HV_VNC_X509]):
783
      raise errors.HypervisorError("%s must be defined, if %s is" %
784
                                   (constants.HV_VNC_X509,
785
                                    constants.HV_VNC_X509_VERIFY))
786

    
787
    boot_order = hvparams[constants.HV_BOOT_ORDER]
788

    
789
    if (boot_order == constants.HT_BO_CDROM and
790
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
791
      raise errors.HypervisorError("Cannot boot from cdrom without an"
792
                                   " ISO path")
793
    if (boot_order == constants.HT_BO_NETWORK and
794
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
795
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
796
                                   " change the NIC type.")
797

    
798
  @classmethod
799
  def PowercycleNode(cls):
800
    """KVM powercycle, just a wrapper over Linux powercycle.
801

802
    """
803
    cls.LinuxPowercycle()