1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """KVM hypervisor
23
24 """
25
26 import errno
27 import os
28 import os.path
29 import re
30 import tempfile
31 import time
32 import logging
33 import pwd
34 from cStringIO import StringIO
35
36 from ganeti import utils
37 from ganeti import constants
38 from ganeti import errors
39 from ganeti import serializer
40 from ganeti import objects
41 from ganeti import uidpool
42 from ganeti import ssconf
43 from ganeti.hypervisor import hv_base
47 """KVM hypervisor interface"""
48 CAN_MIGRATE = True
49
50 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
51 _PIDS_DIR = _ROOT_DIR + "/pid"
52 _UIDS_DIR = _ROOT_DIR + "/uid"
53 _CTRL_DIR = _ROOT_DIR + "/ctrl"
54 _CONF_DIR = _ROOT_DIR + "/conf"
55
56 _CHROOT_DIR = _ROOT_DIR + "/chroot"
57
58
59
60
61
62 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
63 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
64 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
65
66 PARAMETERS = {
67 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
68 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
69 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
70 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
71 constants.HV_ACPI: hv_base.NO_CHECK,
72 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
73 constants.HV_VNC_BIND_ADDRESS:
74 (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
75 "the VNC bind address must be either a valid IP address or an absolute"
76 " pathname", None, None),
77 constants.HV_VNC_TLS: hv_base.NO_CHECK,
78 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
79 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
80 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
81 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
82 constants.HV_BOOT_ORDER:
83 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
84 constants.HV_NIC_TYPE:
85 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
86 constants.HV_DISK_TYPE:
87 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
88 constants.HV_USB_MOUSE:
89 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
90 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
91 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
92 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
93 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
94 constants.HV_DISK_CACHE:
95 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
96 constants.HV_SECURITY_MODEL:
97 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
98 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
99 constants.HV_KVM_FLAG:
100 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
101 constants.HV_VHOST_NET: hv_base.NO_CHECK,
102 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
103 }
104
105 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
106 re.M | re.I)
107 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
108 _MIGRATION_INFO_RETRY_DELAY = 2
109
110 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
111
112 ANCILLARY_FILES = [
113 _KVM_NETWORK_SCRIPT,
114 ]
115
122
123 @classmethod
129
130 @classmethod
136
137 @classmethod
139 """Check pid file for instance information.
140
141 Check that a pid file is associated with an instance, and retrieve
142 information from its command line.
143
144 @type pid: string or int
145 @param pid: process id of the instance to check
146 @rtype: tuple
147 @return: (instance_name, memory, vcpus)
148 @raise errors.HypervisorError: when an instance cannot be found
149
150 """
151 alive = utils.IsProcessAlive(pid)
152 if not alive:
153 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
154
155 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
156 try:
157 cmdline = utils.ReadFile(cmdline_file)
158 except EnvironmentError, err:
159 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
160 (pid, err))
161
162 instance = None
163 memory = 0
164 vcpus = 0
165
166 arg_list = cmdline.split('\x00')
167 while arg_list:
168 arg = arg_list.pop(0)
169 if arg == "-name":
170 instance = arg_list.pop(0)
171 elif arg == "-m":
172 memory = int(arg_list.pop(0))
173 elif arg == "-smp":
174 vcpus = int(arg_list.pop(0))
175
176 if instance is None:
177 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
178 " instance" % pid)
179
180 return (instance, memory, vcpus)
181
183 """Returns the instance pidfile, pid, and liveness.
184
185 @type instance_name: string
186 @param instance_name: instance name
187 @rtype: tuple
188 @return: (pid file name, pid, liveness)
189
190 """
191 pidfile = self._InstancePidFile(instance_name)
192 pid = utils.ReadPidFile(pidfile)
193
194 alive = False
195 try:
196 cmd_instance = self._InstancePidInfo(pid)[0]
197 alive = (cmd_instance == instance_name)
198 except errors.HypervisorError:
199 pass
200
201 return (pidfile, pid, alive)
202
204 """Raises an error unless the given instance is down.
205
206 """
207 alive = self._InstancePidAlive(instance_name)[2]
208 if alive:
209 raise errors.HypervisorError("Failed to start instance %s: %s" %
210 (instance_name, "already running"))
211
212 @classmethod
214 """Returns the instance monitor socket name
215
216 """
217 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
218
219 @classmethod
221 """Returns the instance serial socket name
222
223 """
224 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
225
226 @staticmethod
228 """Returns the correct parameters for socat
229
230 If we have a new-enough socat we can use raw mode with an escape character.
231
232 """
233 if constants.SOCAT_USE_ESCAPE:
234 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
235 else:
236 return "echo=0,icanon=0"
237
238 @classmethod
240 """Returns the instance KVM runtime filename
241
242 """
243 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
244
245 @classmethod
247 """Returns the name of the KVM chroot dir of the instance
248
249 """
250 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
251
252 @classmethod
254 """Try to read a uid file
255
256 """
257 if os.path.exists(uid_file):
258 try:
259 uid = int(utils.ReadOneLineFile(uid_file))
260 return uid
261 except EnvironmentError:
262 logging.warning("Can't read uid file", exc_info=True)
263 except (TypeError, ValueError):
264 logging.warning("Can't parse uid file contents", exc_info=True)
265 return None
266
267 @classmethod
299
301 """Write a script to connect a net interface to the proper bridge.
302
303 This can be used by any qemu-type hypervisor.
304
305 @param instance: instance we're acting on
306 @type instance: instance object
307 @param seq: nic sequence number
308 @type seq: int
309 @param nic: nic we're acting on
310 @type nic: nic object
311 @return: netscript file name
312 @rtype: string
313
314 """
315 script = StringIO()
316 script.write("#!/bin/sh\n")
317 script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
318 script.write("PATH=$PATH:/sbin:/usr/sbin\n")
319 script.write("export INSTANCE=%s\n" % instance.name)
320 script.write("export MAC=%s\n" % nic.mac)
321 if nic.ip:
322 script.write("export IP=%s\n" % nic.ip)
323 script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
324 if nic.nicparams[constants.NIC_LINK]:
325 script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
326 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
327 script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
328 script.write("export INTERFACE=$1\n")
329 if instance.tags:
330 script.write("export TAGS=\"%s\"\n" % " ".join(instance.tags))
331
332 script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
333 script.write(" # Execute the user-specific vif file\n")
334 script.write(" %s\n" % self._KVM_NETWORK_SCRIPT)
335 script.write("else\n")
336 script.write(" ifconfig $INTERFACE 0.0.0.0 up\n")
337 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
338 script.write(" # Connect the interface to the bridge\n")
339 script.write(" brctl addif $BRIDGE $INTERFACE\n")
340 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
341 if not nic.ip:
342 raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
343 script.write(" # Route traffic targeted at the IP to the interface\n")
344 if nic.nicparams[constants.NIC_LINK]:
345 script.write(" while ip rule del dev $INTERFACE; do :; done\n")
346 script.write(" ip rule add dev $INTERFACE table $LINK\n")
347 script.write(" ip route replace $IP table $LINK proto static"
348 " dev $INTERFACE\n")
349 else:
350 script.write(" ip route replace $IP proto static"
351 " dev $INTERFACE\n")
352 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
353 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
354 script.write(" if [ -d %s ]; then\n" % interface_v4_conf)
355 script.write(" echo 1 > %s/proxy_arp\n" % interface_v4_conf)
356 script.write(" echo 1 > %s/forwarding\n" % interface_v4_conf)
357 script.write(" fi\n")
358 script.write(" if [ -d %s ]; then\n" % interface_v6_conf)
359 script.write(" echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
360 script.write(" echo 1 > %s/forwarding\n" % interface_v6_conf)
361 script.write(" fi\n")
362 script.write("fi\n\n")
363
364
365 (tmpfd, tmpfile_name) = tempfile.mkstemp()
366 tmpfile = os.fdopen(tmpfd, 'w')
367 try:
368 tmpfile.write(script.getvalue())
369 finally:
370 tmpfile.close()
371 os.chmod(tmpfile_name, 0755)
372 return tmpfile_name
373
375 """Get the list of running instances.
376
377 We can do this by listing our live instances directory and
378 checking whether the associated kvm process is still alive.
379
380 """
381 result = []
382 for name in os.listdir(self._PIDS_DIR):
383 if self._InstancePidAlive(name)[2]:
384 result.append(name)
385 return result
386
388 """Get instance properties.
389
390 @type instance_name: string
391 @param instance_name: the instance name
392 @rtype: tuple of strings
393 @return: (name, id, memory, vcpus, stat, times)
394
395 """
396 _, pid, alive = self._InstancePidAlive(instance_name)
397 if not alive:
398 return None
399
400 _, memory, vcpus = self._InstancePidInfo(pid)
401 stat = "---b-"
402 times = "0"
403
404 return (instance_name, pid, memory, vcpus, stat, times)
405
407 """Get properties of all instances.
408
409 @return: list of tuples (name, id, memory, vcpus, stat, times)
410
411 """
412 data = []
413 for name in os.listdir(self._PIDS_DIR):
414 try:
415 info = self.GetInstanceInfo(name)
416 except errors.HypervisorError:
417 continue
418 if info:
419 data.append(info)
420 return data
421
423 """Generate KVM information to start an instance.
424
425 """
426 pidfile = self._InstancePidFile(instance.name)
427 kvm = constants.KVM_PATH
428 kvm_cmd = [kvm]
429
430 kvm_cmd.extend(['-name', instance.name])
431 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
432 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
433 kvm_cmd.extend(['-pidfile', pidfile])
434 kvm_cmd.extend(['-daemonize'])
435 if not instance.hvparams[constants.HV_ACPI]:
436 kvm_cmd.extend(['-no-acpi'])
437
438 hvp = instance.hvparams
439 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
440 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
441 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
442
443 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
444 kvm_cmd.extend(["-enable-kvm"])
445 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
446 kvm_cmd.extend(["-disable-kvm"])
447
448 if boot_network:
449 kvm_cmd.extend(['-boot', 'n'])
450
451 disk_type = hvp[constants.HV_DISK_TYPE]
452 if disk_type == constants.HT_DISK_PARAVIRTUAL:
453 if_val = ',if=virtio'
454 else:
455 if_val = ',if=%s' % disk_type
456
457 disk_cache = hvp[constants.HV_DISK_CACHE]
458 if disk_cache != constants.HT_CACHE_DEFAULT:
459 cache_val = ",cache=%s" % disk_cache
460 else:
461 cache_val = ""
462 for cfdev, dev_path in block_devices:
463 if cfdev.mode != constants.DISK_RDWR:
464 raise errors.HypervisorError("Instance has read-only disks which"
465 " are not supported by KVM")
466
467 if boot_disk:
468 kvm_cmd.extend(['-boot', 'c'])
469 if disk_type != constants.HT_DISK_IDE:
470 boot_val = ',boot=on'
471 else:
472 boot_val = ''
473
474 boot_disk = False
475 else:
476 boot_val = ''
477
478 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
479 cache_val)
480 kvm_cmd.extend(['-drive', drive_val])
481
482 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
483 if iso_image:
484 options = ',format=raw,media=cdrom'
485 if boot_cdrom:
486 kvm_cmd.extend(['-boot', 'd'])
487 if disk_type != constants.HT_DISK_IDE:
488 options = '%s,boot=on' % options
489 else:
490 if disk_type == constants.HT_DISK_PARAVIRTUAL:
491 if_val = ',if=virtio'
492 else:
493 if_val = ',if=%s' % disk_type
494 options = '%s%s' % (options, if_val)
495 drive_val = 'file=%s%s' % (iso_image, options)
496 kvm_cmd.extend(['-drive', drive_val])
497
498 kernel_path = hvp[constants.HV_KERNEL_PATH]
499 if kernel_path:
500 kvm_cmd.extend(['-kernel', kernel_path])
501 initrd_path = hvp[constants.HV_INITRD_PATH]
502 if initrd_path:
503 kvm_cmd.extend(['-initrd', initrd_path])
504 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
505 hvp[constants.HV_KERNEL_ARGS]]
506 if hvp[constants.HV_SERIAL_CONSOLE]:
507 root_append.append('console=ttyS0,38400')
508 kvm_cmd.extend(['-append', ' '.join(root_append)])
509
510 mouse_type = hvp[constants.HV_USB_MOUSE]
511 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
512
513 if mouse_type:
514 kvm_cmd.extend(['-usb'])
515 kvm_cmd.extend(['-usbdevice', mouse_type])
516 elif vnc_bind_address:
517 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
518
519 if vnc_bind_address:
520 if utils.IsValidIP(vnc_bind_address):
521 if instance.network_port > constants.VNC_BASE_PORT:
522 display = instance.network_port - constants.VNC_BASE_PORT
523 if vnc_bind_address == '0.0.0.0':
524 vnc_arg = ':%d' % (display)
525 else:
526 vnc_arg = '%s:%d' % (vnc_bind_address, display)
527 else:
528 logging.error("Network port is not a valid VNC display (%d < %d)."
529 " Not starting VNC", instance.network_port,
530 constants.VNC_BASE_PORT)
531 vnc_arg = 'none'
532
533
534
535 vnc_append = ''
536 if hvp[constants.HV_VNC_TLS]:
537 vnc_append = '%s,tls' % vnc_append
538 if hvp[constants.HV_VNC_X509_VERIFY]:
539 vnc_append = '%s,x509verify=%s' % (vnc_append,
540 hvp[constants.HV_VNC_X509])
541 elif hvp[constants.HV_VNC_X509]:
542 vnc_append = '%s,x509=%s' % (vnc_append,
543 hvp[constants.HV_VNC_X509])
544 if hvp[constants.HV_VNC_PASSWORD_FILE]:
545 vnc_append = '%s,password' % vnc_append
546
547 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
548
549 else:
550 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
551
552 kvm_cmd.extend(['-vnc', vnc_arg])
553 else:
554 kvm_cmd.extend(['-nographic'])
555
556 monitor_dev = ("unix:%s,server,nowait" %
557 self._InstanceMonitor(instance.name))
558 kvm_cmd.extend(['-monitor', monitor_dev])
559 if hvp[constants.HV_SERIAL_CONSOLE]:
560 serial_dev = ('unix:%s,server,nowait' %
561 self._InstanceSerial(instance.name))
562 kvm_cmd.extend(['-serial', serial_dev])
563 else:
564 kvm_cmd.extend(['-serial', 'none'])
565
566 if hvp[constants.HV_USE_LOCALTIME]:
567 kvm_cmd.extend(['-localtime'])
568
569 if hvp[constants.HV_KVM_USE_CHROOT]:
570 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
571
572
573
574 kvm_nics = instance.nics
575 hvparams = hvp
576
577 return (kvm_cmd, kvm_nics, hvparams)
578
588
590 """Read an instance's KVM runtime
591
592 """
593 try:
594 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
595 except EnvironmentError, err:
596 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
597 return file_content
598
600 """Save an instance's KVM runtime
601
602 """
603 kvm_cmd, kvm_nics, hvparams = kvm_runtime
604 serialized_nics = [nic.ToDict() for nic in kvm_nics]
605 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
606 self._WriteKVMRuntime(instance.name, serialized_form)
607
609 """Load an instance's KVM runtime
610
611 """
612 if not serialized_runtime:
613 serialized_runtime = self._ReadKVMRuntime(instance.name)
614 loaded_runtime = serializer.Load(serialized_runtime)
615 kvm_cmd, serialized_nics, hvparams = loaded_runtime
616 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
617 return (kvm_cmd, kvm_nics, hvparams)
618
620 """Run the KVM cmd and check for errors
621
622 @type name: string
623 @param name: instance name
624 @type kvm_cmd: list of strings
625 @param kvm_cmd: runcmd input for kvm
626
627 """
628 result = utils.RunCmd(kvm_cmd)
629 if result.failed:
630 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
631 (name, result.fail_reason, result.output))
632 if not self._InstancePidAlive(name)[2]:
633 raise errors.HypervisorError("Failed to start instance %s" % name)
634
636 """Execute a KVM cmd, after completing it with some last minute data
637
638 @type incoming: tuple of strings
639 @param incoming: (target_host_ip, port)
640
641 """
642
643
644
645
646
647
648
649
650 conf_hvp = instance.hvparams
651 name = instance.name
652 self._CheckDown(name)
653
654 temp_files = []
655
656 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
657 up_hvp = objects.FillDict(conf_hvp, up_hvp)
658
659
660
661 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
662 if security_model == constants.HT_SM_USER:
663 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
664
665
666
667
668 if not kvm_nics:
669 kvm_cmd.extend(["-net", "none"])
670 else:
671 tap_extra = ""
672 nic_type = up_hvp[constants.HV_NIC_TYPE]
673 if nic_type == constants.HT_NIC_PARAVIRTUAL:
674 nic_model = "model=virtio"
675 if up_hvp[constants.HV_VHOST_NET]:
676 tap_extra = ",vhost=on"
677 else:
678 nic_model = "model=%s" % nic_type
679
680 for nic_seq, nic in enumerate(kvm_nics):
681 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
682 script = self._WriteNetScript(instance, nic_seq, nic)
683 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
684 kvm_cmd.extend(["-net", nic_val])
685 kvm_cmd.extend(["-net", tap_val])
686 temp_files.append(script)
687
688 if incoming:
689 target, port = incoming
690 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
691
692
693
694
695 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
696 vnc_pwd = None
697 if vnc_pwd_file:
698 try:
699 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
700 except EnvironmentError, err:
701 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
702 % (vnc_pwd_file, err))
703
704 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
705 utils.EnsureDirs([(self._InstanceChrootDir(name),
706 constants.SECURE_DIR_MODE)])
707
708 if security_model == constants.HT_SM_POOL:
709 ss = ssconf.SimpleStore()
710 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
711 all_uids = set(uidpool.ExpandUidPool(uid_pool))
712 uid = uidpool.RequestUnusedUid(all_uids)
713 try:
714 username = pwd.getpwuid(uid.GetUid()).pw_name
715 kvm_cmd.extend(["-runas", username])
716 self._RunKVMCmd(name, kvm_cmd)
717 except:
718 uidpool.ReleaseUid(uid)
719 raise
720 else:
721 uid.Unlock()
722 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
723 else:
724 self._RunKVMCmd(name, kvm_cmd)
725
726 if vnc_pwd:
727 change_cmd = 'change vnc password %s' % vnc_pwd
728 self._CallMonitorCommand(instance.name, change_cmd)
729
730 for filename in temp_files:
731 utils.RemoveFile(filename)
732
741
743 """Invoke a command on the instance monitor.
744
745 """
746 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
747 (utils.ShellQuote(command),
748 constants.SOCAT_PATH,
749 utils.ShellQuote(self._InstanceMonitor(instance_name))))
750 result = utils.RunCmd(socat)
751 if result.failed:
752 msg = ("Failed to send command '%s' to instance %s."
753 " output: %s, error: %s, fail_reason: %s" %
754 (command, instance_name,
755 result.stdout, result.stderr, result.fail_reason))
756 raise errors.HypervisorError(msg)
757
758 return result
759
760 - def StopInstance(self, instance, force=False, retry=False, name=None):
761 """Stop an instance.
762
763 """
764 if name is not None and not force:
765 raise errors.HypervisorError("Cannot shutdown cleanly by name only")
766 if name is None:
767 name = instance.name
768 acpi = instance.hvparams[constants.HV_ACPI]
769 else:
770 acpi = False
771 _, pid, alive = self._InstancePidAlive(name)
772 if pid > 0 and alive:
773 if force or not acpi:
774 utils.KillProcess(pid)
775 else:
776 self._CallMonitorCommand(name, 'system_powerdown')
777
786
807
809 """Get instance information to perform a migration.
810
811 @type instance: L{objects.Instance}
812 @param instance: instance to be migrated
813 @rtype: string
814 @return: content of the KVM runtime file
815
816 """
817 return self._ReadKVMRuntime(instance.name)
818
820 """Prepare to accept an instance.
821
822 @type instance: L{objects.Instance}
823 @param instance: instance to be accepted
824 @type info: string
825 @param info: content of the KVM runtime file on the source node
826 @type target: string
827 @param target: target host (usually ip), on this node
828
829 """
830 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
831 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
832 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
833
835 """Finalize an instance migration.
836
837 Stop the incoming mode KVM.
838
839 @type instance: L{objects.Instance}
840 @param instance: instance whose migration is being finalized
841
842 """
843 if success:
844 self._WriteKVMRuntime(instance.name, info)
845 else:
846 self.StopInstance(instance, force=True)
847
849 """Migrate an instance to a target node.
850
851 The migration will not be attempted if the instance is not
852 currently running.
853
854 @type instance: L{objects.Instance}
855 @param instance: the instance to be migrated
856 @type target: string
857 @param target: ip address of the target node
858 @type live: boolean
859 @param live: perform a live migration
860
861 """
862 instance_name = instance.name
863 port = instance.hvparams[constants.HV_MIGRATION_PORT]
864 pidfile, pid, alive = self._InstancePidAlive(instance_name)
865 if not alive:
866 raise errors.HypervisorError("Instance not running, cannot migrate")
867
868 if not utils.TcpPing(target, port, live_port_needed=True):
869 raise errors.HypervisorError("Remote host %s not listening on port"
870 " %s, cannot migrate" % (target, port))
871
872 if not live:
873 self._CallMonitorCommand(instance_name, 'stop')
874
875 migrate_command = ('migrate_set_speed %dm' %
876 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
877 self._CallMonitorCommand(instance_name, migrate_command)
878
879 migrate_command = ('migrate_set_downtime %dms' %
880 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
881 self._CallMonitorCommand(instance_name, migrate_command)
882
883 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
884 self._CallMonitorCommand(instance_name, migrate_command)
885
886 info_command = 'info migrate'
887 done = False
888 broken_answers = 0
889 while not done:
890 result = self._CallMonitorCommand(instance_name, info_command)
891 match = self._MIGRATION_STATUS_RE.search(result.stdout)
892 if not match:
893 broken_answers += 1
894 if not result.stdout:
895 logging.info("KVM: empty 'info migrate' result")
896 else:
897 logging.warning("KVM: unknown 'info migrate' result: %s",
898 result.stdout)
899 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
900 else:
901 status = match.group(1)
902 if status == 'completed':
903 done = True
904 elif status == 'active':
905
906 broken_answers = 0
907 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
908 elif status == 'failed' or status == 'cancelled':
909 if not live:
910 self._CallMonitorCommand(instance_name, 'cont')
911 raise errors.HypervisorError("Migration %s at the kvm level" %
912 status)
913 else:
914 logging.warning("KVM: unknown migration status '%s'", status)
915 broken_answers += 1
916 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
917 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
918 raise errors.HypervisorError("Too many 'info migrate' broken answers")
919
920 utils.KillProcess(pid)
921 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
922
924 """Return information about the node.
925
926 This is just a wrapper over the base GetLinuxNodeInfo method.
927
928 @return: a dict with the following keys (values in MiB):
929 - memory_total: the total memory size on the node
930 - memory_free: the available memory on the node for instances
931 - memory_dom0: the memory used by the node itself, if available
932
933 """
934 return self.GetLinuxNodeInfo()
935
936 @classmethod
959
970
971
972 @classmethod
1011
1012 @classmethod
1031
1032 @classmethod
1034 """KVM powercycle, just a wrapper over Linux powercycle.
1035
1036 """
1037 cls.LinuxPowercycle()
1038