Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 5420ffc9

History | View | Annotate | Download (24.8 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""KVM hypervisor
23

24
"""
25

    
26
import 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 os.path.isabs(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_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
66
    constants.HV_BOOT_ORDER: \
67
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
68
    constants.HV_NIC_TYPE: \
69
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
70
    constants.HV_DISK_TYPE: \
71
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
72
    constants.HV_USB_MOUSE: \
73
    hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
74
    }
75

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

    
79
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
80

    
81
  def __init__(self):
82
    hv_base.BaseHypervisor.__init__(self)
83
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
84
    # in a tmpfs filesystem or has been otherwise wiped out.
85
    dirs = [(dir, constants.RUN_DIRS_MODE) for dir in self._DIRS]
86
    utils.EnsureDirs(dirs)
87

    
88
  def _InstancePidAlive(self, instance_name):
89
    """Returns the instance pid and pidfile
90

91
    """
92
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
93
    pid = utils.ReadPidFile(pidfile)
94
    alive = utils.IsProcessAlive(pid)
95

    
96
    return (pidfile, pid, alive)
97

    
98
  @classmethod
99
  def _InstanceMonitor(cls, instance_name):
100
    """Returns the instance monitor socket name
101

102
    """
103
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
104

    
105
  @classmethod
106
  def _InstanceSerial(cls, instance_name):
107
    """Returns the instance serial socket name
108

109
    """
110
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
111

    
112
  @classmethod
113
  def _InstanceKVMRuntime(cls, instance_name):
114
    """Returns the instance KVM runtime filename
115

116
    """
117
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
118

    
119
  @classmethod
120
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
121
    """Removes an instance's rutime sockets/files.
122

123
    """
124
    utils.RemoveFile(pidfile)
125
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
126
    utils.RemoveFile(cls._InstanceSerial(instance_name))
127
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
128

    
129
  def _WriteNetScript(self, instance, seq, nic):
130
    """Write a script to connect a net interface to the proper bridge.
131

132
    This can be used by any qemu-type hypervisor.
133

134
    @param instance: instance we're acting on
135
    @type instance: instance object
136
    @param seq: nic sequence number
137
    @type seq: int
138
    @param nic: nic we're acting on
139
    @type nic: nic object
140
    @return: netscript file name
141
    @rtype: string
142

143
    """
144
    script = StringIO()
145
    script.write("#!/bin/sh\n")
146
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
147
    script.write("export INSTANCE=%s\n" % instance.name)
148
    script.write("export MAC=%s\n" % nic.mac)
149
    script.write("export IP=%s\n" % nic.ip)
150
    script.write("export BRIDGE=%s\n" % nic.bridge)
151
    script.write("export INTERFACE=$1\n")
152
    # TODO: make this configurable at ./configure time
153
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
154
    script.write("  # Execute the user-specific vif file\n")
155
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
156
    script.write("else\n")
157
    script.write("  # Connect the interface to the bridge\n")
158
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
159
    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
160
    script.write("fi\n\n")
161
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
162
    # mounted noexec sometimes, so we'll have to find another place.
163
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
164
    tmpfile = os.fdopen(tmpfd, 'w')
165
    tmpfile.write(script.getvalue())
166
    tmpfile.close()
167
    os.chmod(tmpfile_name, 0755)
168
    return tmpfile_name
169

    
170
  def ListInstances(self):
171
    """Get the list of running instances.
172

173
    We can do this by listing our live instances directory and
174
    checking whether the associated kvm process is still alive.
175

176
    """
177
    result = []
178
    for name in os.listdir(self._PIDS_DIR):
179
      filename = "%s/%s" % (self._PIDS_DIR, name)
180
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
181
        result.append(name)
182
    return result
183

    
184
  def GetInstanceInfo(self, instance_name):
185
    """Get instance properties.
186

187
    @param instance_name: the instance name
188

189
    @return: tuple (name, id, memory, vcpus, stat, times)
190

191
    """
192
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
193
    if not alive:
194
      return None
195

    
196
    cmdline_file = "/proc/%s/cmdline" % pid
197
    try:
198
      fh = open(cmdline_file, 'r')
199
      try:
200
        cmdline = fh.read()
201
      finally:
202
        fh.close()
203
    except EnvironmentError, err:
204
      raise errors.HypervisorError("Failed to list instance %s: %s" %
205
                                   (instance_name, err))
206

    
207
    memory = 0
208
    vcpus = 0
209
    stat = "---b-"
210
    times = "0"
211

    
212
    arg_list = cmdline.split('\x00')
213
    while arg_list:
214
      arg =  arg_list.pop(0)
215
      if arg == '-m':
216
        memory = int(arg_list.pop(0))
217
      elif arg == '-smp':
218
        vcpus = int(arg_list.pop(0))
219

    
220
    return (instance_name, pid, memory, vcpus, stat, times)
221

    
222
  def GetAllInstancesInfo(self):
223
    """Get properties of all instances.
224

225
    @return: list of tuples (name, id, memory, vcpus, stat, times)
226

227
    """
228
    data = []
229
    for name in os.listdir(self._PIDS_DIR):
230
      filename = "%s/%s" % (self._PIDS_DIR, name)
231
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
232
        try:
233
          info = self.GetInstanceInfo(name)
234
        except errors.HypervisorError, err:
235
          continue
236
        if info:
237
          data.append(info)
238

    
239
    return data
240

    
241
  def _GenerateKVMRuntime(self, instance, block_devices):
242
    """Generate KVM information to start an instance.
243

244
    """
245
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
246
    kvm = constants.KVM_PATH
247
    kvm_cmd = [kvm]
248
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
249
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
250
    kvm_cmd.extend(['-pidfile', pidfile])
251
    # used just by the vnc server, if enabled
252
    kvm_cmd.extend(['-name', instance.name])
253
    kvm_cmd.extend(['-daemonize'])
254
    if not instance.hvparams[constants.HV_ACPI]:
255
      kvm_cmd.extend(['-no-acpi'])
256

    
257
    hvp = instance.hvparams
258
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
259
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
260
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
261

    
262
    if boot_network:
263
      kvm_cmd.extend(['-boot', 'n'])
264

    
265
    disk_type = hvp[constants.HV_DISK_TYPE]
266
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
267
      if_val = ',if=virtio'
268
    else:
269
      if_val = ',if=%s' % disk_type
270
    for cfdev, dev_path in block_devices:
271
      if cfdev.mode != constants.DISK_RDWR:
272
        raise errors.HypervisorError("Instance has read-only disks which"
273
                                     " are not supported by KVM")
274
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
275
      if boot_disk:
276
        kvm_cmd.extend(['-boot', 'c'])
277
        boot_val = ',boot=on'
278
        # We only boot from the first disk
279
        boot_disk = False
280
      else:
281
        boot_val = ''
282

    
283
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
284
      kvm_cmd.extend(['-drive', drive_val])
285

    
286
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
287
    if iso_image:
288
      options = ',format=raw,media=cdrom'
289
      if boot_cdrom:
290
        kvm_cmd.extend(['-boot', 'd'])
291
        options = '%s,boot=on' % options
292
      else:
293
        options = '%s,if=virtio' % options
294
      drive_val = 'file=%s%s' % (iso_image, options)
295
      kvm_cmd.extend(['-drive', drive_val])
296

    
297
    kernel_path = hvp[constants.HV_KERNEL_PATH]
298
    if kernel_path:
299
      kvm_cmd.extend(['-kernel', kernel_path])
300
      initrd_path = hvp[constants.HV_INITRD_PATH]
301
      if initrd_path:
302
        kvm_cmd.extend(['-initrd', initrd_path])
303
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
304
                     hvp[constants.HV_KERNEL_ARGS]]
305
      if hvp[constants.HV_SERIAL_CONSOLE]:
306
        root_append.append('console=ttyS0,38400')
307
      kvm_cmd.extend(['-append', ' '.join(root_append)])
308

    
309
    mouse_type = hvp[constants.HV_USB_MOUSE]
310
    if mouse_type:
311
      kvm_cmd.extend(['-usb'])
312
      kvm_cmd.extend(['-usbdevice', mouse_type])
313

    
314
    # FIXME: handle vnc password
315
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
316
    if vnc_bind_address:
317
      if utils.IsValidIP(vnc_bind_address):
318
        if instance.network_port > constants.VNC_BASE_PORT:
319
          display = instance.network_port - constants.VNC_BASE_PORT
320
          if vnc_bind_address == '0.0.0.0':
321
            vnc_arg = ':%d' % (display)
322
          else:
323
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
324
        else:
325
          logging.error("Network port is not a valid VNC display (%d < %d)."
326
                        " Not starting VNC" %
327
                        (instance.network_port,
328
                         constants.VNC_BASE_PORT))
329
          vnc_arg = 'none'
330

    
331
        # Only allow tls and other option when not binding to a file, for now.
332
        # kvm/qemu gets confused otherwise about the filename to use.
333
        vnc_append = ''
334
        if hvp[constants.HV_VNC_TLS]:
335
          vnc_append = '%s,tls' % vnc_append
336
          if hvp[constants.HV_VNC_X509_VERIFY]:
337
            vnc_append = '%s,x509verify=%s' % (vnc_append,
338
                                               hvp[constants.HV_VNC_X509])
339
          elif hvp[constants.HV_VNC_X509]:
340
            vnc_append = '%s,x509=%s' % (vnc_append,
341
                                         hvp[constants.HV_VNC_X509])
342
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
343

    
344
      else:
345
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
346

    
347
      kvm_cmd.extend(['-vnc', vnc_arg])
348
    else:
349
      kvm_cmd.extend(['-nographic'])
350

    
351
    monitor_dev = 'unix:%s,server,nowait' % \
352
      self._InstanceMonitor(instance.name)
353
    kvm_cmd.extend(['-monitor', monitor_dev])
354
    if hvp[constants.HV_SERIAL_CONSOLE]:
355
      serial_dev = ('unix:%s,server,nowait' %
356
                    self._InstanceSerial(instance.name))
357
      kvm_cmd.extend(['-serial', serial_dev])
358
    else:
359
      kvm_cmd.extend(['-serial', 'none'])
360

    
361
    # Save the current instance nics, but defer their expansion as parameters,
362
    # as we'll need to generate executable temp files for them.
363
    kvm_nics = instance.nics
364
    hvparams = hvp
365

    
366
    return (kvm_cmd, kvm_nics, hvparams)
367

    
368
  def _WriteKVMRuntime(self, instance_name, data):
369
    """Write an instance's KVM runtime
370

371
    """
372
    try:
373
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
374
                      data=data)
375
    except EnvironmentError, err:
376
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
377

    
378
  def _ReadKVMRuntime(self, instance_name):
379
    """Read an instance's KVM runtime
380

381
    """
382
    try:
383
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
384
    except EnvironmentError, err:
385
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
386
    return file_content
387

    
388
  def _SaveKVMRuntime(self, instance, kvm_runtime):
389
    """Save an instance's KVM runtime
390

391
    """
392
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
393
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
394
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
395
    self._WriteKVMRuntime(instance.name, serialized_form)
396

    
397
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
398
    """Load an instance's KVM runtime
399

400
    """
401
    if not serialized_runtime:
402
      serialized_runtime = self._ReadKVMRuntime(instance.name)
403
    loaded_runtime = serializer.Load(serialized_runtime)
404
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
405
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
406
    return (kvm_cmd, kvm_nics, hvparams)
407

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

411
    @type incoming: tuple of strings
412
    @param incoming: (target_host_ip, port)
413

414
    """
415
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
416
    if alive:
417
      raise errors.HypervisorError("Failed to start instance %s: %s" %
418
                                   (instance.name, "already running"))
419

    
420
    temp_files = []
421

    
422
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
423

    
424
    if not kvm_nics:
425
      kvm_cmd.extend(['-net', 'none'])
426
    else:
427
      nic_type = hvparams[constants.HV_NIC_TYPE]
428
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
429
        nic_model = "model=virtio"
430
      else:
431
        nic_model = "model=%s" % nic_type
432

    
433
      for nic_seq, nic in enumerate(kvm_nics):
434
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
435
        script = self._WriteNetScript(instance, nic_seq, nic)
436
        kvm_cmd.extend(['-net', nic_val])
437
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
438
        temp_files.append(script)
439

    
440
    if incoming:
441
      target, port = incoming
442
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
443

    
444
    result = utils.RunCmd(kvm_cmd)
445
    if result.failed:
446
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
447
                                   (instance.name, result.fail_reason,
448
                                    result.output))
449

    
450
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
451
      raise errors.HypervisorError("Failed to start instance %s: %s" %
452
                                   (instance.name))
453

    
454
    for filename in temp_files:
455
      utils.RemoveFile(filename)
456

    
457
  def StartInstance(self, instance, block_devices):
458
    """Start an instance.
459

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

    
466
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
467
    self._SaveKVMRuntime(instance, kvm_runtime)
468
    self._ExecuteKVMRuntime(instance, kvm_runtime)
469

    
470
  def _CallMonitorCommand(self, instance_name, command):
471
    """Invoke a command on the instance monitor.
472

473
    """
474
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
475
             (utils.ShellQuote(command),
476
              constants.SOCAT_PATH,
477
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
478
    result = utils.RunCmd(socat)
479
    if result.failed:
480
      msg = ("Failed to send command '%s' to instance %s."
481
             " output: %s, error: %s, fail_reason: %s" %
482
             (command, instance_name,
483
              result.stdout, result.stderr, result.fail_reason))
484
      raise errors.HypervisorError(msg)
485

    
486
    return result
487

    
488
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
489
    """Wait for an instance  to power down.
490

491
    """
492
    # Wait up to $timeout seconds
493
    end = time.time() + timeout
494
    wait = 1
495
    while time.time() < end and utils.IsProcessAlive(pid):
496
      self._CallMonitorCommand(instance.name, 'system_powerdown')
497
      time.sleep(wait)
498
      # Make wait time longer for next try
499
      if wait < 5:
500
        wait *= 1.3
501

    
502
  def StopInstance(self, instance, force=False):
503
    """Stop an instance.
504

505
    """
506
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
507
    if pid > 0 and alive:
508
      if force or not instance.hvparams[constants.HV_ACPI]:
509
        utils.KillProcess(pid)
510
      else:
511
        self._RetryInstancePowerdown(instance, pid)
512

    
513
    if not utils.IsProcessAlive(pid):
514
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
515
      return True
516
    else:
517
      return False
518

    
519
  def RebootInstance(self, instance):
520
    """Reboot an instance.
521

522
    """
523
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
524
    # socket the instance will stop, but now power up again. So we'll resort
525
    # to shutdown and restart.
526
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
527
    if not alive:
528
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
529
                                             (instance.name))
530
    # StopInstance will delete the saved KVM runtime so:
531
    # ...first load it...
532
    kvm_runtime = self._LoadKVMRuntime(instance)
533
    # ...now we can safely call StopInstance...
534
    if not self.StopInstance(instance):
535
      self.StopInstance(instance, force=True)
536
    # ...and finally we can save it again, and execute it...
537
    self._SaveKVMRuntime(instance, kvm_runtime)
538
    self._ExecuteKVMRuntime(instance, kvm_runtime)
539

    
540
  def MigrationInfo(self, instance):
541
    """Get instance information to perform a migration.
542

543
    @type instance: L{objects.Instance}
544
    @param instance: instance to be migrated
545
    @rtype: string
546
    @return: content of the KVM runtime file
547

548
    """
549
    return self._ReadKVMRuntime(instance.name)
550

    
551
  def AcceptInstance(self, instance, info, target):
552
    """Prepare to accept an instance.
553

554
    @type instance: L{objects.Instance}
555
    @param instance: instance to be accepted
556
    @type info: string
557
    @param info: content of the KVM runtime file on the source node
558
    @type target: string
559
    @param target: target host (usually ip), on this node
560

561
    """
562
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
563
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
564
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
565

    
566
  def FinalizeMigration(self, instance, info, success):
567
    """Finalize an instance migration.
568

569
    Stop the incoming mode KVM.
570

571
    @type instance: L{objects.Instance}
572
    @param instance: instance whose migration is being aborted
573

574
    """
575
    if success:
576
      self._WriteKVMRuntime(instance.name, info)
577
    else:
578
      self.StopInstance(instance, force=True)
579

    
580
  def MigrateInstance(self, instance_name, target, live):
581
    """Migrate an instance to a target node.
582

583
    The migration will not be attempted if the instance is not
584
    currently running.
585

586
    @type instance_name: string
587
    @param instance_name: name of the instance to be migrated
588
    @type target: string
589
    @param target: ip address of the target node
590
    @type live: boolean
591
    @param live: perform a live migration
592

593
    """
594
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
595
    if not alive:
596
      raise errors.HypervisorError("Instance not running, cannot migrate")
597

    
598
    if not live:
599
      self._CallMonitorCommand(instance_name, 'stop')
600

    
601
    migrate_command = ('migrate -d tcp:%s:%s' %
602
                       (target, constants.KVM_MIGRATION_PORT))
603
    self._CallMonitorCommand(instance_name, migrate_command)
604

    
605
    info_command = 'info migrate'
606
    done = False
607
    while not done:
608
      result = self._CallMonitorCommand(instance_name, info_command)
609
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
610
      if not match:
611
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
612
                                     result.stdout)
613
      else:
614
        status = match.group(1)
615
        if status == 'completed':
616
          done = True
617
        elif status == 'active':
618
          time.sleep(2)
619
        elif status == 'failed' or status == 'cancelled':
620
          if not live:
621
            self._CallMonitorCommand(instance_name, 'cont')
622
          raise errors.HypervisorError("Migration %s at the kvm level" %
623
                                       status)
624
        else:
625
          logging.info("KVM: unknown migration status '%s'" % status)
626
          time.sleep(2)
627

    
628
    utils.KillProcess(pid)
629
    self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
630

    
631
  def GetNodeInfo(self):
632
    """Return information about the node.
633

634
    This is just a wrapper over the base GetLinuxNodeInfo method.
635

636
    @return: a dict with the following keys (values in MiB):
637
          - memory_total: the total memory size on the node
638
          - memory_free: the available memory on the node for instances
639
          - memory_dom0: the memory used by the node itself, if available
640

641
    """
642
    return self.GetLinuxNodeInfo()
643

    
644
  @classmethod
645
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
646
    """Return a command for connecting to the console of an instance.
647

648
    """
649
    if hvparams[constants.HV_SERIAL_CONSOLE]:
650
      # FIXME: The socat shell is not perfect. In particular the way we start
651
      # it ctrl+c will close it, rather than being passed to the other end.
652
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
653
      # will be no way of exiting socat (except killing it from another shell)
654
      # and ctrl+c doesn't work anyway, printing ^C rather than being
655
      # interpreted by kvm. For now we'll leave it this way, which at least
656
      # allows a minimal interaction and changes on the machine.
657
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
658
                       (constants.SOCAT_PATH,
659
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
660
    else:
661
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
662

    
663
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
664
    if vnc_bind_address:
665
      if instance.network_port > constants.VNC_BASE_PORT:
666
        display = instance.network_port - constants.VNC_BASE_PORT
667
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
668
                       " (display: %d)'" % (vnc_bind_address,
669
                                            instance.network_port,
670
                                            display))
671
        shell_command = "%s; %s" % (vnc_command, shell_command)
672

    
673
    return shell_command
674

    
675
  def Verify(self):
676
    """Verify the hypervisor.
677

678
    Check that the binary exists.
679

680
    """
681
    if not os.path.exists(constants.KVM_PATH):
682
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
683
    if not os.path.exists(constants.SOCAT_PATH):
684
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
685

    
686

    
687
  @classmethod
688
  def CheckParameterSyntax(cls, hvparams):
689
    """Check the given parameters for validity.
690

691
    @type hvparams:  dict
692
    @param hvparams: dictionary with parameter names/value
693
    @raise errors.HypervisorError: when a parameter is not valid
694

695
    """
696
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
697

    
698
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
699
    if kernel_path:
700
      if not hvparams[constants.HV_ROOT_PATH]:
701
        raise errors.HypervisorError("Need a root partition for the instance,"
702
                                     " if a kernel is defined")
703

    
704
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
705
        not hvparams[constants.HV_VNC_X509]):
706
      raise errors.HypervisorError("%s must be defined, if %s is" %
707
                                   (constants.HV_VNC_X509,
708
                                    constants.HV_VNC_X509_VERIFY))
709

    
710
    boot_order = hvparams[constants.HV_BOOT_ORDER]
711

    
712
    if (boot_order == constants.HT_BO_CDROM and
713
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
714
      raise errors.HypervisorError("Cannot boot from cdrom without an"
715
                                   " ISO path")
716
    if (boot_order == constants.HT_BO_NETWORK and
717
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
718
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
719
                                   " change the NIC type.")