Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ cef34868

History | View | Annotate | Download (30.7 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
import pwd
33
from cStringIO import StringIO
34

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

    
42

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

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

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

    
85
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
86
                                    re.M | re.I)
87
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
88
  _MIGRATION_INFO_RETRY_DELAY = 2
89

    
90
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
91

    
92
  ANCILLARY_FILES = [
93
    _KVM_NETWORK_SCRIPT,
94
    ]
95

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

    
103
  @classmethod
104
  def _InstancePidFile(cls, instance_name):
105
    """Returns the instance pidfile.
106

107
    """
108
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
109

    
110
  @classmethod
111
  def _InstancePidInfo(cls, pid):
112
    """Check pid file for instance information.
113

114
    Check that a pid file is associated with an instance, and retrieve
115
    information from its command line.
116

117
    @type pid: string or int
118
    @param pid: process id of the instance to check
119
    @rtype: tuple
120
    @return: (instance_name, memory, vcpus)
121
    @raise errors.HypervisorError: when an instance cannot be found
122

123
    """
124
    alive = utils.IsProcessAlive(pid)
125
    if not alive:
126
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
127

    
128
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
129
    try:
130
      cmdline = utils.ReadFile(cmdline_file)
131
    except EnvironmentError, err:
132
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
133
                                   (pid, err))
134

    
135
    instance = None
136
    memory = 0
137
    vcpus = 0
138

    
139
    arg_list = cmdline.split('\x00')
140
    while arg_list:
141
      arg =  arg_list.pop(0)
142
      if arg == "-name":
143
        instance = arg_list.pop(0)
144
      elif arg == "-m":
145
        memory = int(arg_list.pop(0))
146
      elif arg == "-smp":
147
        vcpus = int(arg_list.pop(0))
148

    
149
    if instance is None:
150
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
151
                                   " instance" % pid)
152

    
153
    return (instance, memory, vcpus)
154

    
155
  def _InstancePidAlive(self, instance_name):
156
    """Returns the instance pidfile, pid, and liveness.
157

158
    @type instance_name: string
159
    @param instance_name: instance name
160
    @rtype: tuple
161
    @return: (pid file name, pid, liveness)
162

163
    """
164
    pidfile = self._InstancePidFile(instance_name)
165
    pid = utils.ReadPidFile(pidfile)
166

    
167
    alive = False
168
    try:
169
      cmd_instance = self._InstancePidInfo(pid)[0]
170
      alive = (cmd_instance == instance_name)
171
    except errors.HypervisorError:
172
      pass
173

    
174
    return (pidfile, pid, alive)
175

    
176
  def _CheckDown(self, instance_name):
177
    """Raises an error unless the given instance is down.
178

179
    """
180
    alive = self._InstancePidAlive(instance_name)[2]
181
    if alive:
182
      raise errors.HypervisorError("Failed to start instance %s: %s" %
183
                                   (instance_name, "already running"))
184

    
185
  @classmethod
186
  def _InstanceMonitor(cls, instance_name):
187
    """Returns the instance monitor socket name
188

189
    """
190
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
191

    
192
  @classmethod
193
  def _InstanceSerial(cls, instance_name):
194
    """Returns the instance serial socket name
195

196
    """
197
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
198

    
199
  @staticmethod
200
  def _SocatUnixConsoleParams():
201
    """Returns the correct parameters for socat
202

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

205
    """
206
    if constants.SOCAT_USE_ESCAPE:
207
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
208
    else:
209
      return "echo=0,icanon=0"
210

    
211
  @classmethod
212
  def _InstanceKVMRuntime(cls, instance_name):
213
    """Returns the instance KVM runtime filename
214

215
    """
216
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
217

    
218
  @classmethod
219
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
220
    """Removes an instance's rutime sockets/files.
221

222
    """
223
    utils.RemoveFile(pidfile)
224
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
225
    utils.RemoveFile(cls._InstanceSerial(instance_name))
226
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
227

    
228
  def _WriteNetScript(self, instance, seq, nic):
229
    """Write a script to connect a net interface to the proper bridge.
230

231
    This can be used by any qemu-type hypervisor.
232

233
    @param instance: instance we're acting on
234
    @type instance: instance object
235
    @param seq: nic sequence number
236
    @type seq: int
237
    @param nic: nic we're acting on
238
    @type nic: nic object
239
    @return: netscript file name
240
    @rtype: string
241

242
    """
243
    script = StringIO()
244
    script.write("#!/bin/sh\n")
245
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
246
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
247
    script.write("export INSTANCE=%s\n" % instance.name)
248
    script.write("export MAC=%s\n" % nic.mac)
249
    if nic.ip:
250
      script.write("export IP=%s\n" % nic.ip)
251
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
252
    if nic.nicparams[constants.NIC_LINK]:
253
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
254
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
255
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
256
    script.write("export INTERFACE=$1\n")
257
    # TODO: make this configurable at ./configure time
258
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
259
    script.write("  # Execute the user-specific vif file\n")
260
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
261
    script.write("else\n")
262
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
263
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
264
      script.write("  # Connect the interface to the bridge\n")
265
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
266
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
267
      if not nic.ip:
268
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
269
      script.write("  # Route traffic targeted at the IP to the interface\n")
270
      if nic.nicparams[constants.NIC_LINK]:
271
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
272
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
273
        script.write("  ip route replace $IP table $LINK proto static"
274
                     " dev $INTERFACE\n")
275
      else:
276
        script.write("  ip route replace $IP proto static"
277
                     " dev $INTERFACE\n")
278
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
279
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
280
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
281
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
282
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
283
      script.write("  fi\n")
284
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
285
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
286
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
287
      script.write("  fi\n")
288
    script.write("fi\n\n")
289
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
290
    # mounted noexec sometimes, so we'll have to find another place.
291
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
292
    tmpfile = os.fdopen(tmpfd, 'w')
293
    try:
294
      tmpfile.write(script.getvalue())
295
    finally:
296
      tmpfile.close()
297
    os.chmod(tmpfile_name, 0755)
298
    return tmpfile_name
299

    
300
  def ListInstances(self):
301
    """Get the list of running instances.
302

303
    We can do this by listing our live instances directory and
304
    checking whether the associated kvm process is still alive.
305

306
    """
307
    result = []
308
    for name in os.listdir(self._PIDS_DIR):
309
      if self._InstancePidAlive(name)[2]:
310
        result.append(name)
311
    return result
312

    
313
  def GetInstanceInfo(self, instance_name):
314
    """Get instance properties.
315

316
    @type instance_name: string
317
    @param instance_name: the instance name
318
    @rtype: tuple of strings
319
    @return: (name, id, memory, vcpus, stat, times)
320

321
    """
322
    _, pid, alive = self._InstancePidAlive(instance_name)
323
    if not alive:
324
      return None
325

    
326
    _, memory, vcpus = self._InstancePidInfo(pid)
327
    stat = "---b-"
328
    times = "0"
329

    
330
    return (instance_name, pid, memory, vcpus, stat, times)
331

    
332
  def GetAllInstancesInfo(self):
333
    """Get properties of all instances.
334

335
    @return: list of tuples (name, id, memory, vcpus, stat, times)
336

337
    """
338
    data = []
339
    for name in os.listdir(self._PIDS_DIR):
340
      try:
341
        info = self.GetInstanceInfo(name)
342
      except errors.HypervisorError:
343
        continue
344
      if info:
345
        data.append(info)
346
    return data
347

    
348
  def _GenerateKVMRuntime(self, instance, block_devices):
349
    """Generate KVM information to start an instance.
350

351
    """
352
    pidfile  = self._InstancePidFile(instance.name)
353
    kvm = constants.KVM_PATH
354
    kvm_cmd = [kvm]
355
    # used just by the vnc server, if enabled
356
    kvm_cmd.extend(['-name', instance.name])
357
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
358
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
359
    kvm_cmd.extend(['-pidfile', pidfile])
360
    kvm_cmd.extend(['-daemonize'])
361
    if not instance.hvparams[constants.HV_ACPI]:
362
      kvm_cmd.extend(['-no-acpi'])
363

    
364
    hvp = instance.hvparams
365
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
366
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
367
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
368

    
369
    if boot_network:
370
      kvm_cmd.extend(['-boot', 'n'])
371

    
372
    disk_type = hvp[constants.HV_DISK_TYPE]
373
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
374
      if_val = ',if=virtio'
375
    else:
376
      if_val = ',if=%s' % disk_type
377
    # Cache mode
378
    disk_cache = hvp[constants.HV_DISK_CACHE]
379
    if disk_cache != constants.HT_CACHE_DEFAULT:
380
      cache_val = ",cache=%s" % disk_cache
381
    else:
382
      cache_val = ""
383
    for cfdev, dev_path in block_devices:
384
      if cfdev.mode != constants.DISK_RDWR:
385
        raise errors.HypervisorError("Instance has read-only disks which"
386
                                     " are not supported by KVM")
387
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
388
      if boot_disk:
389
        kvm_cmd.extend(['-boot', 'c'])
390
        boot_val = ',boot=on'
391
        # We only boot from the first disk
392
        boot_disk = False
393
      else:
394
        boot_val = ''
395

    
396
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
397
                                                cache_val)
398
      kvm_cmd.extend(['-drive', drive_val])
399

    
400
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
401
    if iso_image:
402
      options = ',format=raw,media=cdrom'
403
      if boot_cdrom:
404
        kvm_cmd.extend(['-boot', 'd'])
405
        options = '%s,boot=on' % options
406
      else:
407
        options = '%s,if=virtio' % options
408
      drive_val = 'file=%s%s' % (iso_image, options)
409
      kvm_cmd.extend(['-drive', drive_val])
410

    
411
    kernel_path = hvp[constants.HV_KERNEL_PATH]
412
    if kernel_path:
413
      kvm_cmd.extend(['-kernel', kernel_path])
414
      initrd_path = hvp[constants.HV_INITRD_PATH]
415
      if initrd_path:
416
        kvm_cmd.extend(['-initrd', initrd_path])
417
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
418
                     hvp[constants.HV_KERNEL_ARGS]]
419
      if hvp[constants.HV_SERIAL_CONSOLE]:
420
        root_append.append('console=ttyS0,38400')
421
      kvm_cmd.extend(['-append', ' '.join(root_append)])
422

    
423
    mouse_type = hvp[constants.HV_USB_MOUSE]
424
    if mouse_type:
425
      kvm_cmd.extend(['-usb'])
426
      kvm_cmd.extend(['-usbdevice', mouse_type])
427

    
428
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
429
    if vnc_bind_address:
430
      if utils.IsValidIP(vnc_bind_address):
431
        if instance.network_port > constants.VNC_BASE_PORT:
432
          display = instance.network_port - constants.VNC_BASE_PORT
433
          if vnc_bind_address == '0.0.0.0':
434
            vnc_arg = ':%d' % (display)
435
          else:
436
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
437
        else:
438
          logging.error("Network port is not a valid VNC display (%d < %d)."
439
                        " Not starting VNC", instance.network_port,
440
                        constants.VNC_BASE_PORT)
441
          vnc_arg = 'none'
442

    
443
        # Only allow tls and other option when not binding to a file, for now.
444
        # kvm/qemu gets confused otherwise about the filename to use.
445
        vnc_append = ''
446
        if hvp[constants.HV_VNC_TLS]:
447
          vnc_append = '%s,tls' % vnc_append
448
          if hvp[constants.HV_VNC_X509_VERIFY]:
449
            vnc_append = '%s,x509verify=%s' % (vnc_append,
450
                                               hvp[constants.HV_VNC_X509])
451
          elif hvp[constants.HV_VNC_X509]:
452
            vnc_append = '%s,x509=%s' % (vnc_append,
453
                                         hvp[constants.HV_VNC_X509])
454
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
455
          vnc_append = '%s,password' % vnc_append
456

    
457
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
458

    
459
      else:
460
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
461

    
462
      kvm_cmd.extend(['-vnc', vnc_arg])
463
    else:
464
      kvm_cmd.extend(['-nographic'])
465

    
466
    monitor_dev = ("unix:%s,server,nowait" %
467
                   self._InstanceMonitor(instance.name))
468
    kvm_cmd.extend(['-monitor', monitor_dev])
469
    if hvp[constants.HV_SERIAL_CONSOLE]:
470
      serial_dev = ('unix:%s,server,nowait' %
471
                    self._InstanceSerial(instance.name))
472
      kvm_cmd.extend(['-serial', serial_dev])
473
    else:
474
      kvm_cmd.extend(['-serial', 'none'])
475

    
476
    if hvp[constants.HV_USE_LOCALTIME]:
477
      kvm_cmd.extend(['-localtime'])
478

    
479
    # Save the current instance nics, but defer their expansion as parameters,
480
    # as we'll need to generate executable temp files for them.
481
    kvm_nics = instance.nics
482
    hvparams = hvp
483

    
484
    return (kvm_cmd, kvm_nics, hvparams)
485

    
486
  def _WriteKVMRuntime(self, instance_name, data):
487
    """Write an instance's KVM runtime
488

489
    """
490
    try:
491
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
492
                      data=data)
493
    except EnvironmentError, err:
494
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
495

    
496
  def _ReadKVMRuntime(self, instance_name):
497
    """Read an instance's KVM runtime
498

499
    """
500
    try:
501
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
502
    except EnvironmentError, err:
503
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
504
    return file_content
505

    
506
  def _SaveKVMRuntime(self, instance, kvm_runtime):
507
    """Save an instance's KVM runtime
508

509
    """
510
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
511
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
512
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
513
    self._WriteKVMRuntime(instance.name, serialized_form)
514

    
515
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
516
    """Load an instance's KVM runtime
517

518
    """
519
    if not serialized_runtime:
520
      serialized_runtime = self._ReadKVMRuntime(instance.name)
521
    loaded_runtime = serializer.Load(serialized_runtime)
522
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
523
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
524
    return (kvm_cmd, kvm_nics, hvparams)
525

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

529
    @type incoming: tuple of strings
530
    @param incoming: (target_host_ip, port)
531

532
    """
533
    hvp = instance.hvparams
534
    name = instance.name
535
    self._CheckDown(name)
536

    
537
    temp_files = []
538

    
539
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
540

    
541
    security_model = hvp[constants.HV_SECURITY_MODEL]
542
    if security_model == constants.HT_SM_USER:
543
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
544

    
545
    if not kvm_nics:
546
      kvm_cmd.extend(['-net', 'none'])
547
    else:
548
      nic_type = hvparams[constants.HV_NIC_TYPE]
549
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
550
        nic_model = "model=virtio"
551
      else:
552
        nic_model = "model=%s" % nic_type
553

    
554
      for nic_seq, nic in enumerate(kvm_nics):
555
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
556
        script = self._WriteNetScript(instance, nic_seq, nic)
557
        kvm_cmd.extend(['-net', nic_val])
558
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
559
        temp_files.append(script)
560

    
561
    if incoming:
562
      target, port = incoming
563
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
564

    
565
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
566
    vnc_pwd = None
567
    if vnc_pwd_file:
568
      try:
569
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
570
      except EnvironmentError, err:
571
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
572
                                     % (vnc_pwd_file, err))
573

    
574
    result = utils.RunCmd(kvm_cmd)
575
    if result.failed:
576
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
577
                                   (name, result.fail_reason, result.output))
578

    
579
    if not self._InstancePidAlive(name)[2]:
580
      raise errors.HypervisorError("Failed to start instance %s" % name)
581

    
582
    if vnc_pwd:
583
      change_cmd = 'change vnc password %s' % vnc_pwd
584
      self._CallMonitorCommand(instance.name, change_cmd)
585

    
586
    for filename in temp_files:
587
      utils.RemoveFile(filename)
588

    
589
  def StartInstance(self, instance, block_devices):
590
    """Start an instance.
591

592
    """
593
    self._CheckDown(instance.name)
594
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
595
    self._SaveKVMRuntime(instance, kvm_runtime)
596
    self._ExecuteKVMRuntime(instance, kvm_runtime)
597

    
598
  def _CallMonitorCommand(self, instance_name, command):
599
    """Invoke a command on the instance monitor.
600

601
    """
602
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
603
             (utils.ShellQuote(command),
604
              constants.SOCAT_PATH,
605
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
606
    result = utils.RunCmd(socat)
607
    if result.failed:
608
      msg = ("Failed to send command '%s' to instance %s."
609
             " output: %s, error: %s, fail_reason: %s" %
610
             (command, instance_name,
611
              result.stdout, result.stderr, result.fail_reason))
612
      raise errors.HypervisorError(msg)
613

    
614
    return result
615

    
616
  def StopInstance(self, instance, force=False, retry=False, name=None):
617
    """Stop an instance.
618

619
    """
620
    if name is not None and not force:
621
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
622
    if name is None:
623
      name = instance.name
624
      acpi = instance.hvparams[constants.HV_ACPI]
625
    else:
626
      acpi = False
627
    pidfile, pid, alive = self._InstancePidAlive(name)
628
    if pid > 0 and alive:
629
      if force or not acpi:
630
        utils.KillProcess(pid)
631
      else:
632
        self._CallMonitorCommand(name, 'system_powerdown')
633

    
634
    if not self._InstancePidAlive(name)[2]:
635
      self._RemoveInstanceRuntimeFiles(pidfile, name)
636
      return True
637
    else:
638
      return False
639

    
640
  def RebootInstance(self, instance):
641
    """Reboot an instance.
642

643
    """
644
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
645
    # socket the instance will stop, but now power up again. So we'll resort
646
    # to shutdown and restart.
647
    _, _, alive = self._InstancePidAlive(instance.name)
648
    if not alive:
649
      raise errors.HypervisorError("Failed to reboot instance %s:"
650
                                   " not running" % instance.name)
651
    # StopInstance will delete the saved KVM runtime so:
652
    # ...first load it...
653
    kvm_runtime = self._LoadKVMRuntime(instance)
654
    # ...now we can safely call StopInstance...
655
    if not self.StopInstance(instance):
656
      self.StopInstance(instance, force=True)
657
    # ...and finally we can save it again, and execute it...
658
    self._SaveKVMRuntime(instance, kvm_runtime)
659
    self._ExecuteKVMRuntime(instance, kvm_runtime)
660

    
661
  def MigrationInfo(self, instance):
662
    """Get instance information to perform a migration.
663

664
    @type instance: L{objects.Instance}
665
    @param instance: instance to be migrated
666
    @rtype: string
667
    @return: content of the KVM runtime file
668

669
    """
670
    return self._ReadKVMRuntime(instance.name)
671

    
672
  def AcceptInstance(self, instance, info, target):
673
    """Prepare to accept an instance.
674

675
    @type instance: L{objects.Instance}
676
    @param instance: instance to be accepted
677
    @type info: string
678
    @param info: content of the KVM runtime file on the source node
679
    @type target: string
680
    @param target: target host (usually ip), on this node
681

682
    """
683
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
684
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
685
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
686

    
687
  def FinalizeMigration(self, instance, info, success):
688
    """Finalize an instance migration.
689

690
    Stop the incoming mode KVM.
691

692
    @type instance: L{objects.Instance}
693
    @param instance: instance whose migration is being aborted
694

695
    """
696
    if success:
697
      self._WriteKVMRuntime(instance.name, info)
698
    else:
699
      self.StopInstance(instance, force=True)
700

    
701
  def MigrateInstance(self, instance, target, live):
702
    """Migrate an instance to a target node.
703

704
    The migration will not be attempted if the instance is not
705
    currently running.
706

707
    @type instance: L{objects.Instance}
708
    @param instance: the instance to be migrated
709
    @type target: string
710
    @param target: ip address of the target node
711
    @type live: boolean
712
    @param live: perform a live migration
713

714
    """
715
    instance_name = instance.name
716
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
717
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
718
    if not alive:
719
      raise errors.HypervisorError("Instance not running, cannot migrate")
720

    
721
    if not utils.TcpPing(target, port, live_port_needed=True):
722
      raise errors.HypervisorError("Remote host %s not listening on port"
723
                                   " %s, cannot migrate" % (target, port))
724

    
725
    if not live:
726
      self._CallMonitorCommand(instance_name, 'stop')
727

    
728
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
729
    self._CallMonitorCommand(instance_name, migrate_command)
730

    
731
    info_command = 'info migrate'
732
    done = False
733
    broken_answers = 0
734
    while not done:
735
      result = self._CallMonitorCommand(instance_name, info_command)
736
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
737
      if not match:
738
        broken_answers += 1
739
        if not result.stdout:
740
          logging.info("KVM: empty 'info migrate' result")
741
        else:
742
          logging.warning("KVM: unknown 'info migrate' result: %s",
743
                          result.stdout)
744
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
745
      else:
746
        status = match.group(1)
747
        if status == 'completed':
748
          done = True
749
        elif status == 'active':
750
          # reset the broken answers count
751
          broken_answers = 0
752
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
753
        elif status == 'failed' or status == 'cancelled':
754
          if not live:
755
            self._CallMonitorCommand(instance_name, 'cont')
756
          raise errors.HypervisorError("Migration %s at the kvm level" %
757
                                       status)
758
        else:
759
          logging.warning("KVM: unknown migration status '%s'", status)
760
          broken_answers += 1
761
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
762
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
763
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
764

    
765
    utils.KillProcess(pid)
766
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
767

    
768
  def GetNodeInfo(self):
769
    """Return information about the node.
770

771
    This is just a wrapper over the base GetLinuxNodeInfo method.
772

773
    @return: a dict with the following keys (values in MiB):
774
          - memory_total: the total memory size on the node
775
          - memory_free: the available memory on the node for instances
776
          - memory_dom0: the memory used by the node itself, if available
777

778
    """
779
    return self.GetLinuxNodeInfo()
780

    
781
  @classmethod
782
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
783
    """Return a command for connecting to the console of an instance.
784

785
    """
786
    if hvparams[constants.HV_SERIAL_CONSOLE]:
787
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
788
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
789
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
790
    else:
791
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
792

    
793
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
794
    if vnc_bind_address:
795
      if instance.network_port > constants.VNC_BASE_PORT:
796
        display = instance.network_port - constants.VNC_BASE_PORT
797
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
798
                       " (display: %d)'" % (vnc_bind_address,
799
                                            instance.network_port,
800
                                            display))
801
        shell_command = "%s; %s" % (vnc_command, shell_command)
802

    
803
    return shell_command
804

    
805
  def Verify(self):
806
    """Verify the hypervisor.
807

808
    Check that the binary exists.
809

810
    """
811
    if not os.path.exists(constants.KVM_PATH):
812
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
813
    if not os.path.exists(constants.SOCAT_PATH):
814
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
815

    
816

    
817
  @classmethod
818
  def CheckParameterSyntax(cls, hvparams):
819
    """Check the given parameters for validity.
820

821
    @type hvparams:  dict
822
    @param hvparams: dictionary with parameter names/value
823
    @raise errors.HypervisorError: when a parameter is not valid
824

825
    """
826
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
827

    
828
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
829
    if kernel_path:
830
      if not hvparams[constants.HV_ROOT_PATH]:
831
        raise errors.HypervisorError("Need a root partition for the instance,"
832
                                     " if a kernel is defined")
833

    
834
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
835
        not hvparams[constants.HV_VNC_X509]):
836
      raise errors.HypervisorError("%s must be defined, if %s is" %
837
                                   (constants.HV_VNC_X509,
838
                                    constants.HV_VNC_X509_VERIFY))
839

    
840
    boot_order = hvparams[constants.HV_BOOT_ORDER]
841
    if (boot_order == constants.HT_BO_CDROM and
842
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
843
      raise errors.HypervisorError("Cannot boot from cdrom without an"
844
                                   " ISO path")
845

    
846
    security_model = hvparams[constants.HV_SECURITY_MODEL]
847
    if security_model == constants.HT_SM_USER:
848
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
849
        raise errors.HypervisorError("A security domain (user to run kvm as)"
850
                                     " must be specified")
851
    elif (security_model == constants.HT_SM_NONE or
852
          security_model == constants.HT_SM_POOL):
853
      if hvparams[constants.HV_SECURITY_DOMAIN]:
854
        raise errors.HypervisorError("Cannot have a security domain when the"
855
                                     " security model is 'none' or 'pool'")
856
    if security_model == constants.HT_SM_POOL:
857
      raise errors.HypervisorError("Security model pool is not supported yet")
858

    
859
  @classmethod
860
  def ValidateParameters(cls, hvparams):
861
    """Check the given parameters for validity.
862

863
    @type hvparams:  dict
864
    @param hvparams: dictionary with parameter names/value
865
    @raise errors.HypervisorError: when a parameter is not valid
866

867
    """
868
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
869

    
870
    security_model = hvparams[constants.HV_SECURITY_MODEL]
871
    if security_model == constants.HT_SM_USER:
872
      username = hvparams[constants.HV_SECURITY_DOMAIN]
873
      try:
874
        pwd.getpwnam(username)
875
      except KeyError:
876
        raise errors.HypervisorError("Unknown security domain user %s"
877
                                     % username)
878

    
879
  @classmethod
880
  def PowercycleNode(cls):
881
    """KVM powercycle, just a wrapper over Linux powercycle.
882

883
    """
884
    cls.LinuxPowercycle()