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
44 from ganeti import netutils
45
46
47 _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
51 """Write a script to connect a net interface to the proper bridge.
52
53 This can be used by any qemu-type hypervisor.
54
55 @type instance: L{objects.Instance}
56 @param instance: Instance object
57 @type nic: L{objects.NIC}
58 @param nic: NIC object
59 @type index: int
60 @param index: NIC index
61 @return: Script
62 @rtype: string
63
64 """
65 if instance.tags:
66 tags = " ".join(instance.tags)
67 else:
68 tags = ""
69
70 buf = StringIO()
71 sw = utils.ShellWriter(buf)
72 sw.Write("#!/bin/sh")
73 sw.Write("# this is autogenerated by Ganeti, please do not edit")
74 sw.Write("export PATH=$PATH:/sbin:/usr/sbin")
75 sw.Write("export INSTANCE=%s", utils.ShellQuote(instance.name))
76 sw.Write("export MAC=%s", utils.ShellQuote(nic.mac))
77 sw.Write("export MODE=%s",
78 utils.ShellQuote(nic.nicparams[constants.NIC_MODE]))
79 sw.Write("export INTERFACE=\"$1\"")
80 sw.Write("export TAGS=%s", utils.ShellQuote(tags))
81
82 if nic.ip:
83 sw.Write("export IP=%s", utils.ShellQuote(nic.ip))
84
85 if nic.nicparams[constants.NIC_LINK]:
86 sw.Write("export LINK=%s",
87 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
88
89 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
90 sw.Write("export BRIDGE=%s",
91 utils.ShellQuote(nic.nicparams[constants.NIC_LINK]))
92
93
94 sw.Write("if [ -x %s ]; then", utils.ShellQuote(_KVM_NETWORK_SCRIPT))
95 sw.IncIndent()
96 try:
97 sw.Write("# Execute the user-specific vif file")
98 sw.Write(_KVM_NETWORK_SCRIPT)
99 finally:
100 sw.DecIndent()
101 sw.Write("else")
102 sw.IncIndent()
103 try:
104 sw.Write("ifconfig $INTERFACE 0.0.0.0 up")
105
106 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
107 sw.Write("# Connect the interface to the bridge")
108 sw.Write("brctl addif $BRIDGE $INTERFACE")
109
110 elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
111 if not nic.ip:
112 raise errors.HypervisorError("nic/%d is routed, but has no IP"
113 " address" % index)
114
115 sw.Write("# Route traffic targeted at the IP to the interface")
116 if nic.nicparams[constants.NIC_LINK]:
117 sw.Write("while ip rule del dev $INTERFACE; do :; done")
118 sw.Write("ip rule add dev $INTERFACE table $LINK")
119 sw.Write("ip route replace $IP table $LINK proto static"
120 " dev $INTERFACE")
121 else:
122 sw.Write("ip route replace $IP proto static dev $INTERFACE")
123
124 interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
125 sw.Write(" if [ -d %s ]; then", interface_v4_conf)
126 sw.IncIndent()
127 try:
128 sw.Write("echo 1 > %s/proxy_arp", interface_v4_conf)
129 sw.Write("echo 1 > %s/forwarding", interface_v4_conf)
130 finally:
131 sw.DecIndent()
132 sw.Write("fi")
133
134 interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
135 sw.Write("if [ -d %s ]; then", interface_v6_conf)
136 sw.IncIndent()
137 try:
138 sw.Write("echo 1 > %s/proxy_ndp", interface_v6_conf)
139 sw.Write("echo 1 > %s/forwarding", interface_v6_conf)
140 finally:
141 sw.DecIndent()
142 sw.Write("fi")
143 finally:
144 sw.DecIndent()
145 sw.Write("fi")
146
147 return buf.getvalue()
148
151 """KVM hypervisor interface"""
152 CAN_MIGRATE = True
153
154 _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
155 _PIDS_DIR = _ROOT_DIR + "/pid"
156 _UIDS_DIR = _ROOT_DIR + "/uid"
157 _CTRL_DIR = _ROOT_DIR + "/ctrl"
158 _CONF_DIR = _ROOT_DIR + "/conf"
159
160 _CHROOT_DIR = _ROOT_DIR + "/chroot"
161
162
163
164
165
166 _CHROOT_QUARANTINE_DIR = _ROOT_DIR + "/chroot-quarantine"
167 _DIRS = [_ROOT_DIR, _PIDS_DIR, _UIDS_DIR, _CTRL_DIR, _CONF_DIR,
168 _CHROOT_DIR, _CHROOT_QUARANTINE_DIR]
169
170 PARAMETERS = {
171 constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
172 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
173 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
174 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
175 constants.HV_ACPI: hv_base.NO_CHECK,
176 constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
177 constants.HV_VNC_BIND_ADDRESS:
178 (False, lambda x: (netutils.IP4Address.IsValid(x) or
179 utils.IsNormAbsPath(x)),
180 "the VNC bind address must be either a valid IP address or an absolute"
181 " pathname", None, None),
182 constants.HV_VNC_TLS: hv_base.NO_CHECK,
183 constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
184 constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
185 constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
186 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
187 constants.HV_BOOT_ORDER:
188 hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
189 constants.HV_NIC_TYPE:
190 hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
191 constants.HV_DISK_TYPE:
192 hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
193 constants.HV_USB_MOUSE:
194 hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
195 constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
196 constants.HV_MIGRATION_BANDWIDTH: hv_base.NO_CHECK,
197 constants.HV_MIGRATION_DOWNTIME: hv_base.NO_CHECK,
198 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
199 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
200 constants.HV_DISK_CACHE:
201 hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
202 constants.HV_SECURITY_MODEL:
203 hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
204 constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
205 constants.HV_KVM_FLAG:
206 hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
207 constants.HV_VHOST_NET: hv_base.NO_CHECK,
208 constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
209 constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
210 }
211
212 _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
213 re.M | re.I)
214 _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
215 _MIGRATION_INFO_RETRY_DELAY = 2
216
217 _VERSION_RE = re.compile(r"\b(\d+)\.(\d+)\.(\d+)\b")
218
219 ANCILLARY_FILES = [
220 _KVM_NETWORK_SCRIPT,
221 ]
222
229
230 @classmethod
236
237 @classmethod
243
244 @classmethod
246 """Check pid file for instance information.
247
248 Check that a pid file is associated with an instance, and retrieve
249 information from its command line.
250
251 @type pid: string or int
252 @param pid: process id of the instance to check
253 @rtype: tuple
254 @return: (instance_name, memory, vcpus)
255 @raise errors.HypervisorError: when an instance cannot be found
256
257 """
258 alive = utils.IsProcessAlive(pid)
259 if not alive:
260 raise errors.HypervisorError("Cannot get info for pid %s" % pid)
261
262 cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
263 try:
264 cmdline = utils.ReadFile(cmdline_file)
265 except EnvironmentError, err:
266 raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
267 (pid, err))
268
269 instance = None
270 memory = 0
271 vcpus = 0
272
273 arg_list = cmdline.split('\x00')
274 while arg_list:
275 arg = arg_list.pop(0)
276 if arg == "-name":
277 instance = arg_list.pop(0)
278 elif arg == "-m":
279 memory = int(arg_list.pop(0))
280 elif arg == "-smp":
281 vcpus = int(arg_list.pop(0))
282
283 if instance is None:
284 raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
285 " instance" % pid)
286
287 return (instance, memory, vcpus)
288
290 """Returns the instance pidfile, pid, and liveness.
291
292 @type instance_name: string
293 @param instance_name: instance name
294 @rtype: tuple
295 @return: (pid file name, pid, liveness)
296
297 """
298 pidfile = self._InstancePidFile(instance_name)
299 pid = utils.ReadPidFile(pidfile)
300
301 alive = False
302 try:
303 cmd_instance = self._InstancePidInfo(pid)[0]
304 alive = (cmd_instance == instance_name)
305 except errors.HypervisorError:
306 pass
307
308 return (pidfile, pid, alive)
309
311 """Raises an error unless the given instance is down.
312
313 """
314 alive = self._InstancePidAlive(instance_name)[2]
315 if alive:
316 raise errors.HypervisorError("Failed to start instance %s: %s" %
317 (instance_name, "already running"))
318
319 @classmethod
321 """Returns the instance monitor socket name
322
323 """
324 return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
325
326 @classmethod
328 """Returns the instance serial socket name
329
330 """
331 return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
332
333 @staticmethod
335 """Returns the correct parameters for socat
336
337 If we have a new-enough socat we can use raw mode with an escape character.
338
339 """
340 if constants.SOCAT_USE_ESCAPE:
341 return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
342 else:
343 return "echo=0,icanon=0"
344
345 @classmethod
347 """Returns the instance KVM runtime filename
348
349 """
350 return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
351
352 @classmethod
354 """Returns the name of the KVM chroot dir of the instance
355
356 """
357 return utils.PathJoin(cls._CHROOT_DIR, instance_name)
358
359 @classmethod
361 """Try to read a uid file
362
363 """
364 if os.path.exists(uid_file):
365 try:
366 uid = int(utils.ReadOneLineFile(uid_file))
367 return uid
368 except EnvironmentError:
369 logging.warning("Can't read uid file", exc_info=True)
370 except (TypeError, ValueError):
371 logging.warning("Can't parse uid file contents", exc_info=True)
372 return None
373
374 @classmethod
406
407 @staticmethod
409 """Write a script to connect a net interface to the proper bridge.
410
411 This can be used by any qemu-type hypervisor.
412
413 @param instance: instance we're acting on
414 @type instance: instance object
415 @param seq: nic sequence number
416 @type seq: int
417 @param nic: nic we're acting on
418 @type nic: nic object
419 @return: netscript file name
420 @rtype: string
421
422 """
423 script = _WriteNetScript(instance, nic, seq)
424
425
426
427 (tmpfd, tmpfile_name) = tempfile.mkstemp()
428 tmpfile = os.fdopen(tmpfd, 'w')
429 try:
430 tmpfile.write(script)
431 finally:
432 tmpfile.close()
433 os.chmod(tmpfile_name, 0755)
434 return tmpfile_name
435
437 """Get the list of running instances.
438
439 We can do this by listing our live instances directory and
440 checking whether the associated kvm process is still alive.
441
442 """
443 result = []
444 for name in os.listdir(self._PIDS_DIR):
445 if self._InstancePidAlive(name)[2]:
446 result.append(name)
447 return result
448
450 """Get instance properties.
451
452 @type instance_name: string
453 @param instance_name: the instance name
454 @rtype: tuple of strings
455 @return: (name, id, memory, vcpus, stat, times)
456
457 """
458 _, pid, alive = self._InstancePidAlive(instance_name)
459 if not alive:
460 return None
461
462 _, memory, vcpus = self._InstancePidInfo(pid)
463 stat = "---b-"
464 times = "0"
465
466 return (instance_name, pid, memory, vcpus, stat, times)
467
469 """Get properties of all instances.
470
471 @return: list of tuples (name, id, memory, vcpus, stat, times)
472
473 """
474 data = []
475 for name in os.listdir(self._PIDS_DIR):
476 try:
477 info = self.GetInstanceInfo(name)
478 except errors.HypervisorError:
479 continue
480 if info:
481 data.append(info)
482 return data
483
485 """Generate KVM information to start an instance.
486
487 """
488 pidfile = self._InstancePidFile(instance.name)
489 kvm = constants.KVM_PATH
490 kvm_cmd = [kvm]
491
492 kvm_cmd.extend(['-name', instance.name])
493 kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
494 kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
495 kvm_cmd.extend(['-pidfile', pidfile])
496 kvm_cmd.extend(['-daemonize'])
497 if not instance.hvparams[constants.HV_ACPI]:
498 kvm_cmd.extend(['-no-acpi'])
499
500 hvp = instance.hvparams
501 boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
502 boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
503 boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
504
505 if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
506 kvm_cmd.extend(["-enable-kvm"])
507 elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
508 kvm_cmd.extend(["-disable-kvm"])
509
510 if boot_network:
511 kvm_cmd.extend(['-boot', 'n'])
512
513 disk_type = hvp[constants.HV_DISK_TYPE]
514 if disk_type == constants.HT_DISK_PARAVIRTUAL:
515 if_val = ',if=virtio'
516 else:
517 if_val = ',if=%s' % disk_type
518
519 disk_cache = hvp[constants.HV_DISK_CACHE]
520 if disk_cache != constants.HT_CACHE_DEFAULT:
521 cache_val = ",cache=%s" % disk_cache
522 else:
523 cache_val = ""
524 for cfdev, dev_path in block_devices:
525 if cfdev.mode != constants.DISK_RDWR:
526 raise errors.HypervisorError("Instance has read-only disks which"
527 " are not supported by KVM")
528
529 if boot_disk:
530 kvm_cmd.extend(['-boot', 'c'])
531 if disk_type != constants.HT_DISK_IDE:
532 boot_val = ',boot=on'
533 else:
534 boot_val = ''
535
536 boot_disk = False
537 else:
538 boot_val = ''
539
540 drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
541 cache_val)
542 kvm_cmd.extend(['-drive', drive_val])
543
544 iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
545 if iso_image:
546 options = ',format=raw,media=cdrom'
547 if boot_cdrom:
548 kvm_cmd.extend(['-boot', 'd'])
549 if disk_type != constants.HT_DISK_IDE:
550 options = '%s,boot=on' % options
551 else:
552 if disk_type == constants.HT_DISK_PARAVIRTUAL:
553 if_val = ',if=virtio'
554 else:
555 if_val = ',if=%s' % disk_type
556 options = '%s%s' % (options, if_val)
557 drive_val = 'file=%s%s' % (iso_image, options)
558 kvm_cmd.extend(['-drive', drive_val])
559
560 kernel_path = hvp[constants.HV_KERNEL_PATH]
561 if kernel_path:
562 kvm_cmd.extend(['-kernel', kernel_path])
563 initrd_path = hvp[constants.HV_INITRD_PATH]
564 if initrd_path:
565 kvm_cmd.extend(['-initrd', initrd_path])
566 root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
567 hvp[constants.HV_KERNEL_ARGS]]
568 if hvp[constants.HV_SERIAL_CONSOLE]:
569 root_append.append('console=ttyS0,38400')
570 kvm_cmd.extend(['-append', ' '.join(root_append)])
571
572 mem_path = hvp[constants.HV_MEM_PATH]
573 if mem_path:
574 kvm_cmd.extend(["-mem-path", mem_path, "-mem-prealloc"])
575
576 mouse_type = hvp[constants.HV_USB_MOUSE]
577 vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
578
579 if mouse_type:
580 kvm_cmd.extend(['-usb'])
581 kvm_cmd.extend(['-usbdevice', mouse_type])
582 elif vnc_bind_address:
583 kvm_cmd.extend(['-usbdevice', constants.HT_MOUSE_TABLET])
584
585 if vnc_bind_address:
586 if netutils.IP4Address.IsValid(vnc_bind_address):
587 if instance.network_port > constants.VNC_BASE_PORT:
588 display = instance.network_port - constants.VNC_BASE_PORT
589 if vnc_bind_address == constants.IP4_ADDRESS_ANY:
590 vnc_arg = ':%d' % (display)
591 else:
592 vnc_arg = '%s:%d' % (vnc_bind_address, display)
593 else:
594 logging.error("Network port is not a valid VNC display (%d < %d)."
595 " Not starting VNC", instance.network_port,
596 constants.VNC_BASE_PORT)
597 vnc_arg = 'none'
598
599
600
601 vnc_append = ''
602 if hvp[constants.HV_VNC_TLS]:
603 vnc_append = '%s,tls' % vnc_append
604 if hvp[constants.HV_VNC_X509_VERIFY]:
605 vnc_append = '%s,x509verify=%s' % (vnc_append,
606 hvp[constants.HV_VNC_X509])
607 elif hvp[constants.HV_VNC_X509]:
608 vnc_append = '%s,x509=%s' % (vnc_append,
609 hvp[constants.HV_VNC_X509])
610 if hvp[constants.HV_VNC_PASSWORD_FILE]:
611 vnc_append = '%s,password' % vnc_append
612
613 vnc_arg = '%s%s' % (vnc_arg, vnc_append)
614
615 else:
616 vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
617
618 kvm_cmd.extend(['-vnc', vnc_arg])
619 else:
620 kvm_cmd.extend(['-nographic'])
621
622 monitor_dev = ("unix:%s,server,nowait" %
623 self._InstanceMonitor(instance.name))
624 kvm_cmd.extend(['-monitor', monitor_dev])
625 if hvp[constants.HV_SERIAL_CONSOLE]:
626 serial_dev = ('unix:%s,server,nowait' %
627 self._InstanceSerial(instance.name))
628 kvm_cmd.extend(['-serial', serial_dev])
629 else:
630 kvm_cmd.extend(['-serial', 'none'])
631
632 if hvp[constants.HV_USE_LOCALTIME]:
633 kvm_cmd.extend(['-localtime'])
634
635 if hvp[constants.HV_KVM_USE_CHROOT]:
636 kvm_cmd.extend(['-chroot', self._InstanceChrootDir(instance.name)])
637
638
639
640 kvm_nics = instance.nics
641 hvparams = hvp
642
643 return (kvm_cmd, kvm_nics, hvparams)
644
654
656 """Read an instance's KVM runtime
657
658 """
659 try:
660 file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
661 except EnvironmentError, err:
662 raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
663 return file_content
664
666 """Save an instance's KVM runtime
667
668 """
669 kvm_cmd, kvm_nics, hvparams = kvm_runtime
670 serialized_nics = [nic.ToDict() for nic in kvm_nics]
671 serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
672 self._WriteKVMRuntime(instance.name, serialized_form)
673
675 """Load an instance's KVM runtime
676
677 """
678 if not serialized_runtime:
679 serialized_runtime = self._ReadKVMRuntime(instance.name)
680 loaded_runtime = serializer.Load(serialized_runtime)
681 kvm_cmd, serialized_nics, hvparams = loaded_runtime
682 kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
683 return (kvm_cmd, kvm_nics, hvparams)
684
686 """Run the KVM cmd and check for errors
687
688 @type name: string
689 @param name: instance name
690 @type kvm_cmd: list of strings
691 @param kvm_cmd: runcmd input for kvm
692
693 """
694 result = utils.RunCmd(kvm_cmd)
695 if result.failed:
696 raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
697 (name, result.fail_reason, result.output))
698 if not self._InstancePidAlive(name)[2]:
699 raise errors.HypervisorError("Failed to start instance %s" % name)
700
702 """Execute a KVM cmd, after completing it with some last minute data
703
704 @type incoming: tuple of strings
705 @param incoming: (target_host_ip, port)
706
707 """
708
709
710
711
712
713
714
715
716 conf_hvp = instance.hvparams
717 name = instance.name
718 self._CheckDown(name)
719
720 temp_files = []
721
722 kvm_cmd, kvm_nics, up_hvp = kvm_runtime
723 up_hvp = objects.FillDict(conf_hvp, up_hvp)
724
725
726
727 security_model = conf_hvp[constants.HV_SECURITY_MODEL]
728 if security_model == constants.HT_SM_USER:
729 kvm_cmd.extend(["-runas", conf_hvp[constants.HV_SECURITY_DOMAIN]])
730
731
732
733
734 if not kvm_nics:
735 kvm_cmd.extend(["-net", "none"])
736 else:
737 tap_extra = ""
738 nic_type = up_hvp[constants.HV_NIC_TYPE]
739 if nic_type == constants.HT_NIC_PARAVIRTUAL:
740 nic_model = "model=virtio"
741 if up_hvp[constants.HV_VHOST_NET]:
742 tap_extra = ",vhost=on"
743 else:
744 nic_model = "model=%s" % nic_type
745
746 for nic_seq, nic in enumerate(kvm_nics):
747 nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
748 script = self._WriteNetScriptFile(instance, nic_seq, nic)
749 tap_val = "tap,vlan=%s,script=%s%s" % (nic_seq, script, tap_extra)
750 kvm_cmd.extend(["-net", nic_val])
751 kvm_cmd.extend(["-net", tap_val])
752 temp_files.append(script)
753
754 if incoming:
755 target, port = incoming
756 kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
757
758
759
760
761 vnc_pwd_file = conf_hvp[constants.HV_VNC_PASSWORD_FILE]
762 vnc_pwd = None
763 if vnc_pwd_file:
764 try:
765 vnc_pwd = utils.ReadOneLineFile(vnc_pwd_file, strict=True)
766 except EnvironmentError, err:
767 raise errors.HypervisorError("Failed to open VNC password file %s: %s"
768 % (vnc_pwd_file, err))
769
770 if conf_hvp[constants.HV_KVM_USE_CHROOT]:
771 utils.EnsureDirs([(self._InstanceChrootDir(name),
772 constants.SECURE_DIR_MODE)])
773
774 if security_model == constants.HT_SM_POOL:
775 ss = ssconf.SimpleStore()
776 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\n")
777 all_uids = set(uidpool.ExpandUidPool(uid_pool))
778 uid = uidpool.RequestUnusedUid(all_uids)
779 try:
780 username = pwd.getpwuid(uid.GetUid()).pw_name
781 kvm_cmd.extend(["-runas", username])
782 self._RunKVMCmd(name, kvm_cmd)
783 except:
784 uidpool.ReleaseUid(uid)
785 raise
786 else:
787 uid.Unlock()
788 utils.WriteFile(self._InstanceUidFile(name), data=str(uid))
789 else:
790 self._RunKVMCmd(name, kvm_cmd)
791
792 if vnc_pwd:
793 change_cmd = 'change vnc password %s' % vnc_pwd
794 self._CallMonitorCommand(instance.name, change_cmd)
795
796 for filename in temp_files:
797 utils.RemoveFile(filename)
798
807
809 """Invoke a command on the instance monitor.
810
811 """
812 socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
813 (utils.ShellQuote(command),
814 constants.SOCAT_PATH,
815 utils.ShellQuote(self._InstanceMonitor(instance_name))))
816 result = utils.RunCmd(socat)
817 if result.failed:
818 msg = ("Failed to send command '%s' to instance %s."
819 " output: %s, error: %s, fail_reason: %s" %
820 (command, instance_name,
821 result.stdout, result.stderr, result.fail_reason))
822 raise errors.HypervisorError(msg)
823
824 return result
825
826 @classmethod
828 """Return the installed KVM version
829
830 @return: (version, v_maj, v_min, v_rev), or None
831
832 """
833 result = utils.RunCmd([constants.KVM_PATH, "--help"])
834 if result.failed:
835 return None
836 match = cls._VERSION_RE.search(result.output.splitlines()[0])
837 if not match:
838 return None
839 return (match.group(0), match.group(1), match.group(2), match.group(3))
840
841 - def StopInstance(self, instance, force=False, retry=False, name=None):
858
867
888
890 """Get instance information to perform a migration.
891
892 @type instance: L{objects.Instance}
893 @param instance: instance to be migrated
894 @rtype: string
895 @return: content of the KVM runtime file
896
897 """
898 return self._ReadKVMRuntime(instance.name)
899
901 """Prepare to accept an instance.
902
903 @type instance: L{objects.Instance}
904 @param instance: instance to be accepted
905 @type info: string
906 @param info: content of the KVM runtime file on the source node
907 @type target: string
908 @param target: target host (usually ip), on this node
909
910 """
911 kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
912 incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
913 self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
914
916 """Finalize an instance migration.
917
918 Stop the incoming mode KVM.
919
920 @type instance: L{objects.Instance}
921 @param instance: instance whose migration is being finalized
922
923 """
924 if success:
925 self._WriteKVMRuntime(instance.name, info)
926 else:
927 self.StopInstance(instance, force=True)
928
930 """Migrate an instance to a target node.
931
932 The migration will not be attempted if the instance is not
933 currently running.
934
935 @type instance: L{objects.Instance}
936 @param instance: the instance to be migrated
937 @type target: string
938 @param target: ip address of the target node
939 @type live: boolean
940 @param live: perform a live migration
941
942 """
943 instance_name = instance.name
944 port = instance.hvparams[constants.HV_MIGRATION_PORT]
945 pidfile, pid, alive = self._InstancePidAlive(instance_name)
946 if not alive:
947 raise errors.HypervisorError("Instance not running, cannot migrate")
948
949 if not live:
950 self._CallMonitorCommand(instance_name, 'stop')
951
952 migrate_command = ('migrate_set_speed %dm' %
953 instance.hvparams[constants.HV_MIGRATION_BANDWIDTH])
954 self._CallMonitorCommand(instance_name, migrate_command)
955
956 migrate_command = ('migrate_set_downtime %dms' %
957 instance.hvparams[constants.HV_MIGRATION_DOWNTIME])
958 self._CallMonitorCommand(instance_name, migrate_command)
959
960 migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
961 self._CallMonitorCommand(instance_name, migrate_command)
962
963 info_command = 'info migrate'
964 done = False
965 broken_answers = 0
966 while not done:
967 result = self._CallMonitorCommand(instance_name, info_command)
968 match = self._MIGRATION_STATUS_RE.search(result.stdout)
969 if not match:
970 broken_answers += 1
971 if not result.stdout:
972 logging.info("KVM: empty 'info migrate' result")
973 else:
974 logging.warning("KVM: unknown 'info migrate' result: %s",
975 result.stdout)
976 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
977 else:
978 status = match.group(1)
979 if status == 'completed':
980 done = True
981 elif status == 'active':
982
983 broken_answers = 0
984 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
985 elif status == 'failed' or status == 'cancelled':
986 if not live:
987 self._CallMonitorCommand(instance_name, 'cont')
988 raise errors.HypervisorError("Migration %s at the kvm level" %
989 status)
990 else:
991 logging.warning("KVM: unknown migration status '%s'", status)
992 broken_answers += 1
993 time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
994 if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
995 raise errors.HypervisorError("Too many 'info migrate' broken answers")
996
997 utils.KillProcess(pid)
998 self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
999
1001 """Return information about the node.
1002
1003 This is just a wrapper over the base GetLinuxNodeInfo method.
1004
1005 @return: a dict with the following keys (values in MiB):
1006 - memory_total: the total memory size on the node
1007 - memory_free: the available memory on the node for instances
1008 - memory_dom0: the memory used by the node itself, if available
1009
1010 """
1011 return self.GetLinuxNodeInfo()
1012
1013 @classmethod
1036
1047
1048
1049 @classmethod
1088
1089 @classmethod
1108
1109 @classmethod
1111 """KVM powercycle, just a wrapper over Linux powercycle.
1112
1113 """
1114 cls.LinuxPowercycle()
1115