Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ a2faf9ee

History | View | Annotate | Download (22.1 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,
53
    constants.HV_INITRD_PATH,
54
    constants.HV_ROOT_PATH,
55
    constants.HV_ACPI,
56
    constants.HV_SERIAL_CONSOLE,
57
    ]
58

    
59
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
60
                                    re.M | re.I)
61

    
62
  def __init__(self):
63
    hv_base.BaseHypervisor.__init__(self)
64
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
65
    # in a tmpfs filesystem or has been otherwise wiped out.
66
    for mydir in self._DIRS:
67
      if not os.path.exists(mydir):
68
        os.mkdir(mydir)
69

    
70
  def _InstancePidAlive(self, instance_name):
71
    """Returns the instance pid and pidfile
72

73
    """
74
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
75
    pid = utils.ReadPidFile(pidfile)
76
    alive = utils.IsProcessAlive(pid)
77

    
78
    return (pidfile, pid, alive)
79

    
80
  @classmethod
81
  def _InstanceMonitor(cls, instance_name):
82
    """Returns the instance monitor socket name
83

84
    """
85
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
86

    
87
  @classmethod
88
  def _InstanceSerial(cls, instance_name):
89
    """Returns the instance serial socket name
90

91
    """
92
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
93

    
94
  @classmethod
95
  def _InstanceKVMRuntime(cls, instance_name):
96
    """Returns the instance KVM runtime filename
97

98
    """
99
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
100

    
101
  def _WriteNetScript(self, instance, seq, nic):
102
    """Write a script to connect a net interface to the proper bridge.
103

104
    This can be used by any qemu-type hypervisor.
105

106
    @param instance: instance we're acting on
107
    @type instance: instance object
108
    @param seq: nic sequence number
109
    @type seq: int
110
    @param nic: nic we're acting on
111
    @type nic: nic object
112
    @return: netscript file name
113
    @rtype: string
114

115
    """
116
    script = StringIO()
117
    script.write("#!/bin/sh\n")
118
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
119
    script.write("export INSTANCE=%s\n" % instance.name)
120
    script.write("export MAC=%s\n" % nic.mac)
121
    script.write("export IP=%s\n" % nic.ip)
122
    script.write("export BRIDGE=%s\n" % nic.bridge)
123
    script.write("export INTERFACE=$1\n")
124
    # TODO: make this configurable at ./configure time
125
    script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
126
    script.write("  # Execute the user-specific vif file\n")
127
    script.write("  /etc/ganeti/kvm-vif-bridge\n")
128
    script.write("else\n")
129
    script.write("  # Connect the interface to the bridge\n")
130
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
131
    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
132
    script.write("fi\n\n")
133
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
134
    # mounted noexec sometimes, so we'll have to find another place.
135
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
136
    tmpfile = os.fdopen(tmpfd, 'w')
137
    tmpfile.write(script.getvalue())
138
    tmpfile.close()
139
    os.chmod(tmpfile_name, 0755)
140
    return tmpfile_name
141

    
142
  def ListInstances(self):
143
    """Get the list of running instances.
144

145
    We can do this by listing our live instances directory and
146
    checking whether the associated kvm process is still alive.
147

148
    """
149
    result = []
150
    for name in os.listdir(self._PIDS_DIR):
151
      filename = "%s/%s" % (self._PIDS_DIR, name)
152
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
153
        result.append(name)
154
    return result
155

    
156
  def GetInstanceInfo(self, instance_name):
157
    """Get instance properties.
158

159
    @param instance_name: the instance name
160

161
    @return: tuple (name, id, memory, vcpus, stat, times)
162

163
    """
164
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
165
    if not alive:
166
      return None
167

    
168
    cmdline_file = "/proc/%s/cmdline" % pid
169
    try:
170
      fh = open(cmdline_file, 'r')
171
      try:
172
        cmdline = fh.read()
173
      finally:
174
        fh.close()
175
    except EnvironmentError, err:
176
      raise errors.HypervisorError("Failed to list instance %s: %s" %
177
                                   (instance_name, err))
178

    
179
    memory = 0
180
    vcpus = 0
181
    stat = "---b-"
182
    times = "0"
183

    
184
    arg_list = cmdline.split('\x00')
185
    while arg_list:
186
      arg =  arg_list.pop(0)
187
      if arg == '-m':
188
        memory = arg_list.pop(0)
189
      elif arg == '-smp':
190
        vcpus = arg_list.pop(0)
191

    
192
    return (instance_name, pid, memory, vcpus, stat, times)
193

    
194
  def GetAllInstancesInfo(self):
195
    """Get properties of all instances.
196

197
    @return: list of tuples (name, id, memory, vcpus, stat, times)
198

199
    """
200
    data = []
201
    for name in os.listdir(self._PIDS_DIR):
202
      filename = "%s/%s" % (self._PIDS_DIR, name)
203
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
204
        data.append(self.GetInstanceInfo(name))
205

    
206
    return data
207

    
208
  def _GenerateKVMRuntime(self, instance, block_devices, extra_args):
209
    """Generate KVM information to start an instance.
210

211
    """
212
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
213
    kvm = constants.KVM_PATH
214
    kvm_cmd = [kvm]
215
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
216
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
217
    kvm_cmd.extend(['-pidfile', pidfile])
218
    # used just by the vnc server, if enabled
219
    kvm_cmd.extend(['-name', instance.name])
220
    kvm_cmd.extend(['-daemonize'])
221
    if not instance.hvparams[constants.HV_ACPI]:
222
      kvm_cmd.extend(['-no-acpi'])
223

    
224
    boot_drive = True
225
    for cfdev, dev_path in block_devices:
226
      if cfdev.mode != constants.DISK_RDWR:
227
        raise errors.HypervisorError("Instance has read-only disks which"
228
                                     " are not supported by KVM")
229
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
230
      if boot_drive:
231
        boot_val = ',boot=on'
232
        boot_drive = False
233
      else:
234
        boot_val = ''
235

    
236
      # TODO: handle different if= types
237
      if_val = ',if=virtio'
238

    
239
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
240
      kvm_cmd.extend(['-drive', drive_val])
241

    
242
    kvm_cmd.extend(['-kernel', instance.hvparams[constants.HV_KERNEL_PATH]])
243

    
244
    initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
245
    if initrd_path:
246
      kvm_cmd.extend(['-initrd', initrd_path])
247

    
248
    root_append = 'root=%s ro' % instance.hvparams[constants.HV_ROOT_PATH]
249
    if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
250
      kvm_cmd.extend(['-append', 'console=ttyS0,38400 %s' % root_append])
251
    else:
252
      kvm_cmd.extend(['-append', root_append])
253

    
254
    #"hvm_boot_order",
255
    #"hvm_cdrom_image_path",
256

    
257
    kvm_cmd.extend(['-nographic'])
258
    # FIXME: handle vnc, if needed
259
    # How do we decide whether to have it or not?? :(
260
    #"vnc_bind_address",
261
    #"network_port"
262
    monitor_dev = 'unix:%s,server,nowait' % \
263
      self._InstanceMonitor(instance.name)
264
    kvm_cmd.extend(['-monitor', monitor_dev])
265
    if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
266
      serial_dev = 'unix:%s,server,nowait' % self._InstanceSerial(instance.name)
267
      kvm_cmd.extend(['-serial', serial_dev])
268
    else:
269
      kvm_cmd.extend(['-serial', 'none'])
270

    
271
    # Save the current instance nics, but defer their expansion as parameters,
272
    # as we'll need to generate executable temp files for them.
273
    kvm_nics = instance.nics
274

    
275
    return (kvm_cmd, kvm_nics)
276

    
277
  def _WriteKVMRuntime(self, instance_name, data):
278
    """Write an instance's KVM runtime
279

280
    """
281
    try:
282
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
283
                      data=data)
284
    except EnvironmentError, err:
285
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
286

    
287
  def _ReadKVMRuntime(self, instance_name):
288
    """Read an instance's KVM runtime
289

290
    """
291
    try:
292
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
293
    except EnvironmentError, err:
294
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
295
    return file_content
296

    
297
  def _SaveKVMRuntime(self, instance, kvm_runtime):
298
    """Save an instance's KVM runtime
299

300
    """
301
    kvm_cmd, kvm_nics = kvm_runtime
302
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
303
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics))
304
    self._WriteKVMRuntime(instance.name, serialized_form)
305

    
306
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
307
    """Load an instance's KVM runtime
308

309
    """
310
    if not serialized_runtime:
311
      serialized_runtime = self._ReadKVMRuntime(instance.name)
312
    loaded_runtime = serializer.Load(serialized_runtime)
313
    kvm_cmd, serialized_nics = loaded_runtime
314
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
315
    return (kvm_cmd, kvm_nics)
316

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

320
    @type incoming: tuple of strings
321
    @param incoming: (target_host_ip, port)
322

323
    """
324
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
325
    if alive:
326
      raise errors.HypervisorError("Failed to start instance %s: %s" %
327
                                   (instance.name, "already running"))
328

    
329
    temp_files = []
330

    
331
    kvm_cmd, kvm_nics = kvm_runtime
332

    
333
    if not kvm_nics:
334
      kvm_cmd.extend(['-net', 'none'])
335
    else:
336
      for nic_seq, nic in enumerate(kvm_nics):
337
        nic_val = "nic,macaddr=%s,model=virtio" % nic.mac
338
        script = self._WriteNetScript(instance, nic_seq, nic)
339
        kvm_cmd.extend(['-net', nic_val])
340
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
341
        temp_files.append(script)
342

    
343
    if incoming:
344
      target, port = incoming
345
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
346

    
347
    result = utils.RunCmd(kvm_cmd)
348
    if result.failed:
349
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
350
                                   (instance.name, result.fail_reason,
351
                                    result.output))
352

    
353
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
354
      raise errors.HypervisorError("Failed to start instance %s: %s" %
355
                                   (instance.name))
356

    
357
    for filename in temp_files:
358
      utils.RemoveFile(filename)
359

    
360
  def StartInstance(self, instance, block_devices, extra_args):
361
    """Start an instance.
362

363
    """
364
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
365
    if alive:
366
      raise errors.HypervisorError("Failed to start instance %s: %s" %
367
                                   (instance.name, "already running"))
368

    
369
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, extra_args)
370
    self._SaveKVMRuntime(instance, kvm_runtime)
371
    self._ExecuteKVMRuntime(instance, kvm_runtime)
372

    
373
  def _CallMonitorCommand(self, instance_name, command):
374
    """Invoke a command on the instance monitor.
375

376
    """
377
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
378
             (utils.ShellQuote(command),
379
              constants.SOCAT_PATH,
380
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
381
    result = utils.RunCmd(socat)
382
    if result.failed:
383
      msg = ("Failed to send command '%s' to instance %s."
384
             " output: %s, error: %s, fail_reason: %s" %
385
             (instance.name, result.stdout, result.stderr, result.fail_reason))
386
      raise errors.HypervisorError(msg)
387

    
388
    return result
389

    
390
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
391
    """Wait for an instance  to power down.
392

393
    """
394
    # Wait up to $timeout seconds
395
    end = time.time() + timeout
396
    wait = 1
397
    while time.time() < end and utils.IsProcessAlive(pid):
398
      self._CallMonitorCommand(instance.name, 'system_powerdown')
399
      time.sleep(wait)
400
      # Make wait time longer for next try
401
      if wait < 5:
402
        wait *= 1.3
403

    
404
  def StopInstance(self, instance, force=False):
405
    """Stop an instance.
406

407
    """
408
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
409
    if pid > 0 and alive:
410
      if force or not instance.hvparams[constants.HV_ACPI]:
411
        utils.KillProcess(pid)
412
      else:
413
        self._RetryInstancePowerdown(instance, pid)
414

    
415
    if not utils.IsProcessAlive(pid):
416
      utils.RemoveFile(pidfile)
417
      utils.RemoveFile(self._InstanceMonitor(instance.name))
418
      utils.RemoveFile(self._InstanceSerial(instance.name))
419
      utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
420
      return True
421
    else:
422
      return False
423

    
424
  def RebootInstance(self, instance):
425
    """Reboot an instance.
426

427
    """
428
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
429
    # socket the instance will stop, but now power up again. So we'll resort
430
    # to shutdown and restart.
431
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
432
    if not alive:
433
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
434
                                             (instance.name))
435
    # StopInstance will delete the saved KVM runtime so:
436
    # ...first load it...
437
    kvm_runtime = self._LoadKVMRuntime(instance)
438
    # ...now we can safely call StopInstance...
439
    if not self.StopInstance(instance):
440
      self.StopInstance(instance, force=True)
441
    # ...and finally we can save it again, and execute it...
442
    self._SaveKVMRuntime(instance, kvm_runtime)
443
    self._ExecuteKVMRuntime(instance, kvm_runtime)
444

    
445
  def MigrationInfo(self, instance):
446
    """Get instance information to perform a migration.
447

448
    @type instance: L{objects.Instance}
449
    @param instance: instance to be migrated
450
    @rtype: string
451
    @return: content of the KVM runtime file
452

453
    """
454
    return self._ReadKVMRuntime(instance.name)
455

    
456
  def AcceptInstance(self, instance, info, target):
457
    """Prepare to accept an instance.
458

459
    @type instance: L{objects.Instance}
460
    @param instance: instance to be accepted
461
    @type info: string
462
    @param info: content of the KVM runtime file on the source node
463
    @type target: string
464
    @param target: target host (usually ip), on this node
465

466
    """
467
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
468
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
469
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
470

    
471
  def FinalizeMigration(self, instance, info, success):
472
    """Finalize an instance migration.
473

474
    Stop the incoming mode KVM.
475

476
    @type instance: L{objects.Instance}
477
    @param instance: instance whose migration is being aborted
478

479
    """
480
    if success:
481
      self._WriteKVMRuntime(instance.name, info)
482
    else:
483
      self.StopInstance(instance, force=True)
484

    
485
  def MigrateInstance(self, instance_name, target, live):
486
    """Migrate an instance to a target node.
487

488
    The migration will not be attempted if the instance is not
489
    currently running.
490

491
    @type instance_name: string
492
    @param instance_name: name of the instance to be migrated
493
    @type target: string
494
    @param target: ip address of the target node
495
    @type live: boolean
496
    @param live: perform a live migration
497

498
    """
499
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
500
    if not alive:
501
      raise errors.HypervisorError("Instance not running, cannot migrate")
502

    
503
    if not live:
504
      self._CallMonitorCommand(instance_name, 'stop')
505

    
506
    migrate_command = ('migrate -d tcp:%s:%s' %
507
                       (target, constants.KVM_MIGRATION_PORT))
508
    self._CallMonitorCommand(instance_name, migrate_command)
509

    
510
    info_command = 'info migrate'
511
    done = False
512
    while not done:
513
      result = self._CallMonitorCommand(instance_name, info_command)
514
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
515
      if not match:
516
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
517
                                     result.stdout)
518
      else:
519
        status = match.group(1)
520
        if status == 'completed':
521
          done = True
522
        elif status == 'active':
523
          time.sleep(2)
524
        elif status == 'failed' or status == 'cancelled':
525
          if not live:
526
            self._CallMonitorCommand(instance_name, 'cont')
527
          raise errors.HypervisorError("Migration %s at the kvm level" %
528
                                       status)
529
        else:
530
          logging.info("KVM: unknown migration status '%s'" % status)
531
          time.sleep(2)
532

    
533
    utils.KillProcess(pid)
534
    utils.RemoveFile(pidfile)
535
    utils.RemoveFile(self._InstanceMonitor(instance_name))
536
    utils.RemoveFile(self._InstanceSerial(instance_name))
537
    utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
538

    
539
  def GetNodeInfo(self):
540
    """Return information about the node.
541

542
    @return: a dict with the following keys (values in MiB):
543
          - memory_total: the total memory size on the node
544
          - memory_free: the available memory on the node for instances
545
          - memory_dom0: the memory used by the node itself, if available
546

547
    """
548
    # global ram usage from the xm info command
549
    # memory                 : 3583
550
    # free_memory            : 747
551
    # note: in xen 3, memory has changed to total_memory
552
    try:
553
      fh = file("/proc/meminfo")
554
      try:
555
        data = fh.readlines()
556
      finally:
557
        fh.close()
558
    except EnvironmentError, err:
559
      raise errors.HypervisorError("Failed to list node info: %s" % err)
560

    
561
    result = {}
562
    sum_free = 0
563
    for line in data:
564
      splitfields = line.split(":", 1)
565

    
566
      if len(splitfields) > 1:
567
        key = splitfields[0].strip()
568
        val = splitfields[1].strip()
569
        if key == 'MemTotal':
570
          result['memory_total'] = int(val.split()[0])/1024
571
        elif key in ('MemFree', 'Buffers', 'Cached'):
572
          sum_free += int(val.split()[0])/1024
573
        elif key == 'Active':
574
          result['memory_dom0'] = int(val.split()[0])/1024
575
    result['memory_free'] = sum_free
576

    
577
    cpu_total = 0
578
    try:
579
      fh = open("/proc/cpuinfo")
580
      try:
581
        cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
582
                                   fh.read()))
583
      finally:
584
        fh.close()
585
    except EnvironmentError, err:
586
      raise errors.HypervisorError("Failed to list node info: %s" % err)
587
    result['cpu_total'] = cpu_total
588

    
589
    return result
590

    
591
  @classmethod
592
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
593
    """Return a command for connecting to the console of an instance.
594

595
    """
596
    if hvparams[constants.HV_SERIAL_CONSOLE]:
597
      # FIXME: The socat shell is not perfect. In particular the way we start
598
      # it ctrl+c will close it, rather than being passed to the other end.
599
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
600
      # will be no way of exiting socat (except killing it from another shell)
601
      # and ctrl+c doesn't work anyway, printing ^C rather than being
602
      # interpreted by kvm. For now we'll leave it this way, which at least
603
      # allows a minimal interaction and changes on the machine.
604
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
605
                       (constants.SOCAT_PATH,
606
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
607
    else:
608
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
609
    return shell_command
610

    
611
  def Verify(self):
612
    """Verify the hypervisor.
613

614
    Check that the binary exists.
615

616
    """
617
    if not os.path.exists(constants.KVM_PATH):
618
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
619
    if not os.path.exists(constants.SOCAT_PATH):
620
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
621

    
622

    
623
  @classmethod
624
  def CheckParameterSyntax(cls, hvparams):
625
    """Check the given parameters for validity.
626

627
    For the KVM hypervisor, this only check the existence of the
628
    kernel.
629

630
    @type hvparams:  dict
631
    @param hvparams: dictionary with parameter names/value
632
    @raise errors.HypervisorError: when a parameter is not valid
633

634
    """
635
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
636

    
637
    if not hvparams[constants.HV_KERNEL_PATH]:
638
      raise errors.HypervisorError("Need a kernel for the instance")
639

    
640
    if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
641
      raise errors.HypervisorError("The kernel path must be an absolute path")
642

    
643
    if not hvparams[constants.HV_ROOT_PATH]:
644
      raise errors.HypervisorError("Need a root partition for the instance")
645

    
646
    if hvparams[constants.HV_INITRD_PATH]:
647
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
648
        raise errors.HypervisorError("The initrd path must be an absolute path"
649
                                     ", if defined")
650

    
651
  def ValidateParameters(self, hvparams):
652
    """Check the given parameters for validity.
653

654
    For the KVM hypervisor, this checks the existence of the
655
    kernel.
656

657
    """
658
    super(KVMHypervisor, self).ValidateParameters(hvparams)
659

    
660
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
661
    if not os.path.isfile(kernel_path):
662
      raise errors.HypervisorError("Instance kernel '%s' not found or"
663
                                   " not a file" % kernel_path)
664
    initrd_path = hvparams[constants.HV_INITRD_PATH]
665
    if initrd_path and not os.path.isfile(initrd_path):
666
      raise errors.HypervisorError("Instance initrd '%s' not found or"
667
                                   " not a file" % initrd_path)