1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Xen hypervisors
23
24 """
25
26 import logging
27 import string
28 from cStringIO import StringIO
29
30 from ganeti import constants
31 from ganeti import errors
32 from ganeti import utils
33 from ganeti.hypervisor import hv_base
34 from ganeti import netutils
35 from ganeti import objects
36 from ganeti import pathutils
37 from ganeti import ssconf
38
39
40 XEND_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xend-config.sxp")
41 XL_CONFIG_FILE = utils.PathJoin(pathutils.XEN_CONFIG_DIR, "xen/xl.conf")
42 VIF_BRIDGE_SCRIPT = utils.PathJoin(pathutils.XEN_CONFIG_DIR,
43 "scripts/vif-bridge")
44 _DOM0_NAME = "Domain-0"
45 _DISK_LETTERS = string.ascii_lowercase
46
47 _FILE_DRIVER_MAP = {
48 constants.FD_LOOP: "file",
49 constants.FD_BLKTAP: "tap:aio",
50 constants.FD_BLKTAP2: "tap2:tapdisk:aio",
51 }
55 """Create a CPU config string for Xen's config file.
56
57 """
58
59 cpu_list = utils.ParseMultiCpuMask(cpu_mask)
60
61 if len(cpu_list) == 1:
62 all_cpu_mapping = cpu_list[0]
63 if all_cpu_mapping == constants.CPU_PINNING_OFF:
64
65
66 return None
67 else:
68
69
70 return "cpu = \"%s\"" % ",".join(map(str, all_cpu_mapping))
71 else:
72
73 def _GetCPUMap(vcpu):
74 if vcpu[0] == constants.CPU_PINNING_ALL_VAL:
75 cpu_map = constants.CPU_PINNING_ALL_XEN
76 else:
77 cpu_map = ",".join(map(str, vcpu))
78 return "\"%s\"" % cpu_map
79
80
81
82
83 return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
84
87 """Helper function for L{_GetXmList} to run "xm list".
88
89 @type fn: callable
90 @param fn: Function returning result of running C{xm list}
91 @type xmllist_errors: list
92 @param xmllist_errors: Error list
93 @rtype: list
94
95 """
96 result = fn()
97 if result.failed:
98 logging.error("xm list failed (%s): %s", result.fail_reason,
99 result.output)
100 xmllist_errors.append(result)
101 raise utils.RetryAgain()
102
103
104 return result.stdout.splitlines()
105
108 """Parses the output of C{xm list}.
109
110 @type lines: list
111 @param lines: Output lines of C{xm list}
112 @type include_node: boolean
113 @param include_node: If True, return information for Dom0
114 @return: list of tuple containing (name, id, memory, vcpus, state, time
115 spent)
116
117 """
118 result = []
119
120
121 for line in lines[1:]:
122
123
124
125 data = line.split()
126 if len(data) != 6:
127 raise errors.HypervisorError("Can't parse output of xm list,"
128 " line: %s" % line)
129 try:
130 data[1] = int(data[1])
131 data[2] = int(data[2])
132 data[3] = int(data[3])
133 data[5] = float(data[5])
134 except (TypeError, ValueError), err:
135 raise errors.HypervisorError("Can't parse output of xm list,"
136 " line: %s, error: %s" % (line, err))
137
138
139 if include_node or data[0] != _DOM0_NAME:
140 result.append(data)
141
142 return result
143
146 """Return the list of running instances.
147
148 See L{_RunXmList} and L{_ParseXmList} for parameter details.
149
150 """
151 xmllist_errors = []
152 try:
153 lines = utils.Retry(_RunXmList, (0.3, 1.5, 1.0), _timeout,
154 args=(fn, xmllist_errors))
155 except utils.RetryTimeout:
156 if xmllist_errors:
157 xmlist_result = xmllist_errors.pop()
158
159 errmsg = ("xm list failed, timeout exceeded (%s): %s" %
160 (xmlist_result.fail_reason, xmlist_result.output))
161 else:
162 errmsg = "xm list failed"
163
164 raise errors.HypervisorError(errmsg)
165
166 return _ParseXmList(lines, include_node)
167
170 return instance_info == "r-----" \
171 or instance_info == "-b----"
172
175 return instance_info == "---s--"
176
179 """Return information about the node.
180
181 @return: a dict with the following keys (memory values in MiB):
182 - memory_total: the total memory size on the node
183 - memory_free: the available memory on the node for instances
184 - nr_cpus: total number of CPUs
185 - nr_nodes: in a NUMA system, the number of domains
186 - nr_sockets: the number of physical CPU sockets in the node
187 - hv_version: the hypervisor version in the form (major, minor)
188
189 """
190 result = {}
191 cores_per_socket = threads_per_core = nr_cpus = None
192 xen_major, xen_minor = None, None
193 memory_total = None
194 memory_free = None
195
196 for line in info.splitlines():
197 fields = line.split(":", 1)
198
199 if len(fields) < 2:
200 continue
201
202 (key, val) = map(lambda s: s.strip(), fields)
203
204
205 if key in ("memory", "total_memory"):
206 memory_total = int(val)
207 elif key == "free_memory":
208 memory_free = int(val)
209 elif key == "nr_cpus":
210 nr_cpus = result["cpu_total"] = int(val)
211 elif key == "nr_nodes":
212 result["cpu_nodes"] = int(val)
213 elif key == "cores_per_socket":
214 cores_per_socket = int(val)
215 elif key == "threads_per_core":
216 threads_per_core = int(val)
217 elif key == "xen_major":
218 xen_major = int(val)
219 elif key == "xen_minor":
220 xen_minor = int(val)
221
222 if None not in [cores_per_socket, threads_per_core, nr_cpus]:
223 result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
224
225 if memory_free is not None:
226 result["memory_free"] = memory_free
227
228 if memory_total is not None:
229 result["memory_total"] = memory_total
230
231 if not (xen_major is None or xen_minor is None):
232 result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
233
234 return result
235
238 """Updates node information from L{_ParseNodeInfo} with instance info.
239
240 @type info: dict
241 @param info: Result from L{_ParseNodeInfo}
242 @type fn: callable
243 @param fn: Function returning result of running C{xm list}
244 @rtype: dict
245
246 """
247 total_instmem = 0
248
249 for (name, _, mem, vcpus, _, _) in fn(True):
250 if name == _DOM0_NAME:
251 info["memory_dom0"] = mem
252 info["dom0_cpus"] = vcpus
253
254
255 total_instmem += mem
256
257 memory_free = info.get("memory_free")
258 memory_total = info.get("memory_total")
259
260
261 if None not in [memory_total, memory_free, total_instmem]:
262 info["memory_hv"] = memory_total - memory_free - total_instmem
263
264 return info
265
268 """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
269
270 """
271 return _MergeInstanceInfo(_ParseNodeInfo(info), fn)
272
276 """Get disk directives for Xen config file.
277
278 This method builds the xen config disk directive according to the
279 given disk_template and block_devices.
280
281 @param block_devices: list of tuples (cfdev, rldev):
282 - cfdev: dict containing ganeti config disk part
283 - rldev: ganeti.bdev.BlockDev object
284 @param blockdev_prefix: a string containing blockdevice prefix,
285 e.g. "sd" for /dev/sda
286
287 @return: string containing disk directive for xen instance config file
288
289 """
290 if len(block_devices) > len(_letters):
291 raise errors.HypervisorError("Too many disks")
292
293 disk_data = []
294
295 for sd_suffix, (cfdev, dev_path) in zip(_letters, block_devices):
296 sd_name = blockdev_prefix + sd_suffix
297
298 if cfdev.mode == constants.DISK_RDWR:
299 mode = "w"
300 else:
301 mode = "r"
302
303 if cfdev.dev_type == constants.LD_FILE:
304 driver = _FILE_DRIVER_MAP[cfdev.physical_id[0]]
305 else:
306 driver = "phy"
307
308 disk_data.append("'%s:%s,%s,%s'" % (driver, dev_path, sd_name, mode))
309
310 return disk_data
311
314 """Xen generic hypervisor interface
315
316 This is the Xen base class used for both Xen PVM and HVM. It contains
317 all the functionality that is identical for both.
318
319 """
320 CAN_MIGRATE = True
321 REBOOT_RETRY_COUNT = 60
322 REBOOT_RETRY_INTERVAL = 10
323
324 ANCILLARY_FILES = [
325 XEND_CONFIG_FILE,
326 XL_CONFIG_FILE,
327 VIF_BRIDGE_SCRIPT,
328 ]
329 ANCILLARY_FILES_OPT = [
330 XL_CONFIG_FILE,
331 ]
332
333 - def __init__(self, _cfgdir=None, _run_cmd_fn=None, _cmd=None):
347
362
363 - def _RunXen(self, args, timeout=None):
364 """Wrapper around L{utils.process.RunCmd} to run Xen command.
365
366 If a timeout (in seconds) is specified, the command will be terminated after
367 that number of seconds.
368
369 @see: L{utils.process.RunCmd}
370
371 """
372 cmd = []
373
374 if timeout is not None:
375 cmd.extend(["timeout", str(timeout)])
376
377 cmd.extend([self._GetCommand()])
378 cmd.extend(args)
379
380 return self._run_cmd_fn(cmd)
381
383 """Get the config file name for an instance.
384
385 @param instance_name: instance name
386 @type instance_name: str
387 @return: fully qualified path to instance config file
388 @rtype: str
389
390 """
391 return utils.PathJoin(self._cfgdir, instance_name)
392
393 @classmethod
394 - def _GetConfig(cls, instance, startup_memory, block_devices):
395 """Build Xen configuration for an instance.
396
397 """
398 raise NotImplementedError
399
401 """Write the Xen config file for the instance.
402
403 This version of the function just writes the config file from static data.
404
405 """
406
407 utils.RemoveFile(utils.PathJoin(self._cfgdir, "auto", instance_name))
408
409 cfg_file = self._ConfigFileName(instance_name)
410 try:
411 utils.WriteFile(cfg_file, data=data)
412 except EnvironmentError, err:
413 raise errors.HypervisorError("Cannot write Xen instance configuration"
414 " file %s: %s" % (cfg_file, err))
415
417 """Returns the contents of the instance config file.
418
419 """
420 filename = self._ConfigFileName(instance_name)
421
422 try:
423 file_content = utils.ReadFile(filename)
424 except EnvironmentError, err:
425 raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
426
427 return file_content
428
430 """Remove the xen configuration file.
431
432 """
433 utils.RemoveFile(self._ConfigFileName(instance_name))
434
436 """Move the Xen config file to the log directory and return its new path.
437
438 """
439 old_filename = self._ConfigFileName(instance_name)
440 base = ("%s-%s" %
441 (instance_name, utils.TimestampForFilename()))
442 new_filename = utils.PathJoin(pathutils.LOG_XEN_DIR, base)
443 utils.RenameFile(old_filename, new_filename)
444 return new_filename
445
447 """Wrapper around module level L{_GetXmList}.
448
449 """
450 return _GetXmList(lambda: self._RunXen(["list"]), include_node)
451
453 """Get the list of running instances.
454
455 """
456 xm_list = self._GetXmList(False)
457 names = [info[0] for info in xm_list]
458 return names
459
461 """Get instance properties.
462
463 @param instance_name: the instance name
464
465 @return: tuple (name, id, memory, vcpus, stat, times)
466
467 """
468 xm_list = self._GetXmList(instance_name == _DOM0_NAME)
469 result = None
470 for data in xm_list:
471 if data[0] == instance_name:
472 result = data
473 break
474 return result
475
477 """Get properties of all instances.
478
479 @return: list of tuples (name, id, memory, vcpus, stat, times)
480
481 """
482 xm_list = self._GetXmList(False)
483 return xm_list
484
486 """Gather configuration details and write to disk.
487
488 See L{_GetConfig} for arguments.
489
490 """
491 buf = StringIO()
492 buf.write("# Automatically generated by Ganeti. Do not edit!\n")
493 buf.write("\n")
494 buf.write(self._GetConfig(instance, startup_memory, block_devices))
495 buf.write("\n")
496
497 self._WriteConfigFile(instance.name, buf.getvalue())
498
499 - def StartInstance(self, instance, block_devices, startup_paused):
521
522 - def StopInstance(self, instance, force=False, retry=False, name=None,
523 timeout=None):
524 """Stop an instance.
525
526 A soft shutdown can be interrupted. A hard shutdown tries forever.
527
528 """
529 assert(timeout is None or force is not None)
530
531 if name is None:
532 name = instance.name
533
534 return self._StopInstance(name, force, timeout)
535
537 """Shutdown an instance if the instance is running.
538
539 The '-w' flag waits for shutdown to complete which avoids the need
540 to poll in the case where we want to destroy the domain
541 immediately after shutdown.
542
543 @type name: string
544 @param name: name of the instance to stop
545 @type timeout: int or None
546 @param timeout: a timeout after which the shutdown command should be killed,
547 or None for no timeout
548
549 """
550 instance_info = self.GetInstanceInfo(name)
551
552 if instance_info is None or _IsInstanceShutdown(instance_info[4]):
553 logging.info("Failed to shutdown instance %s, not running", name)
554 return None
555
556 return self._RunXen(["shutdown", "-w", name], timeout)
557
559 """Destroy an instance if the instance if the instance exists.
560
561 @type name: string
562 @param name: name of the instance to destroy
563
564 """
565 instance_info = self.GetInstanceInfo(name)
566
567 if instance_info is None:
568 logging.info("Failed to destroy instance %s, does not exist", name)
569 return None
570
571 return self._RunXen(["destroy", name])
572
574 """Stop an instance.
575
576 @type name: string
577 @param name: name of the instance to destroy
578
579 @type force: boolean
580 @param force: whether to do a "hard" stop (destroy)
581
582 @type timeout: int or None
583 @param timeout: a timeout after which the shutdown command should be killed,
584 or None for no timeout
585
586 """
587 if force:
588 result = self._DestroyInstance(name)
589 else:
590 self._ShutdownInstance(name, timeout)
591 result = self._DestroyInstance(name)
592
593 if result is not None and result.failed and \
594 self.GetInstanceInfo(name) is not None:
595 raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
596 (name, result.fail_reason, result.output))
597
598
599 self._RemoveConfigFile(name)
600
626
627 try:
628 utils.Retry(_CheckInstance, self.REBOOT_RETRY_INTERVAL,
629 self.REBOOT_RETRY_INTERVAL * self.REBOOT_RETRY_COUNT)
630 except utils.RetryTimeout:
631 raise errors.HypervisorError("Failed to reboot instance %s: instance"
632 " did not reboot in the expected interval" %
633 (instance.name, ))
634
636 """Balloon an instance memory to a certain value.
637
638 @type instance: L{objects.Instance}
639 @param instance: instance to be accepted
640 @type mem: int
641 @param mem: actual memory size to use for instance runtime
642
643 """
644 result = self._RunXen(["mem-set", instance.name, mem])
645 if result.failed:
646 raise errors.HypervisorError("Failed to balloon instance %s: %s (%s)" %
647 (instance.name, result.fail_reason,
648 result.output))
649
650
651 cmd = ["sed", "-ie", "s/^memory.*$/memory = %s/" % mem]
652 cmd.append(self._ConfigFileName(instance.name))
653
654 result = utils.RunCmd(cmd)
655 if result.failed:
656 raise errors.HypervisorError("Failed to update memory for %s: %s (%s)" %
657 (instance.name, result.fail_reason,
658 result.output))
659
661 """Return information about the node.
662
663 @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
664
665 """
666 result = self._RunXen(["info"])
667 if result.failed:
668 logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
669 result.output)
670 return None
671
672 return _GetNodeInfo(result.stdout, self._GetXmList)
673
674 @classmethod
685
687 """Verify the hypervisor.
688
689 For Xen, this verifies that the xend process is running.
690
691 @return: Problem description if something is wrong, C{None} otherwise
692
693 """
694 result = self._RunXen(["info"])
695 if result.failed:
696 return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
697
698 return None
699
701 """Get instance information to perform a migration.
702
703 @type instance: L{objects.Instance}
704 @param instance: instance to be migrated
705 @rtype: string
706 @return: content of the xen config file
707
708 """
709 return self._ReadConfigFile(instance.name)
710
712 """Prepare to accept an instance.
713
714 @type instance: L{objects.Instance}
715 @param instance: instance to be accepted
716 @type info: string
717 @param info: content of the xen config file on the source node
718 @type target: string
719 @param target: target host (usually ip), on this node
720
721 """
722 pass
723
725 """Finalize an instance migration.
726
727 After a successful migration we write the xen config file.
728 We do nothing on a failure, as we did not change anything at accept time.
729
730 @type instance: L{objects.Instance}
731 @param instance: instance whose migration is being finalized
732 @type info: string
733 @param info: content of the xen config file on the source node
734 @type success: boolean
735 @param success: whether the migration was a success or a failure
736
737 """
738 if success:
739 self._WriteConfigFile(instance.name, info)
740
742 """Migrate an instance to a target node.
743
744 The migration will not be attempted if the instance is not
745 currently running.
746
747 @type instance: L{objects.Instance}
748 @param instance: the instance to be migrated
749 @type target: string
750 @param target: ip address of the target node
751 @type live: boolean
752 @param live: perform a live migration
753
754 """
755 port = instance.hvparams[constants.HV_MIGRATION_PORT]
756
757
758 cluster_name = ssconf.SimpleStore().GetClusterName()
759
760 return self._MigrateInstance(cluster_name, instance.name, target, port,
761 live)
762
765 """Migrate an instance to a target node.
766
767 @see: L{MigrateInstance} for details
768
769 """
770 if self.GetInstanceInfo(instance_name) is None:
771 raise errors.HypervisorError("Instance not running, cannot migrate")
772
773 cmd = self._GetCommand()
774
775 if (cmd == constants.XEN_CMD_XM and
776 not _ping_fn(target, port, live_port_needed=True)):
777 raise errors.HypervisorError("Remote host %s not listening on port"
778 " %s, cannot migrate" % (target, port))
779
780 args = ["migrate"]
781
782 if cmd == constants.XEN_CMD_XM:
783 args.extend(["-p", "%d" % port])
784 if live:
785 args.append("-l")
786
787 elif cmd == constants.XEN_CMD_XL:
788 args.extend([
789 "-s", constants.XL_SSH_CMD % cluster_name,
790 "-C", self._ConfigFileName(instance_name),
791 ])
792
793 else:
794 raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
795
796 args.extend([instance_name, target])
797
798 result = self._RunXen(args)
799 if result.failed:
800 raise errors.HypervisorError("Failed to migrate instance %s: %s" %
801 (instance_name, result.output))
802
804 """Finalize the instance migration on the source node.
805
806 @type instance: L{objects.Instance}
807 @param instance: the instance that was migrated
808 @type success: bool
809 @param success: whether the migration succeeded or not
810 @type live: bool
811 @param live: whether the user requested a live migration or not
812
813 """
814
815 if success:
816
817 try:
818 self._RemoveConfigFile(instance.name)
819 except EnvironmentError:
820 logging.exception("Failure while removing instance config file")
821
823 """Get the migration status
824
825 As MigrateInstance for Xen is still blocking, if this method is called it
826 means that MigrateInstance has completed successfully. So we can safely
827 assume that the migration was successful and notify this fact to the client.
828
829 @type instance: L{objects.Instance}
830 @param instance: the instance that is being migrated
831 @rtype: L{objects.MigrationStatus}
832 @return: the status of the current migration (one of
833 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
834 progress info that can be retrieved from the hypervisor
835
836 """
837 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)
838
839 @classmethod
841 """Xen-specific powercycle.
842
843 This first does a Linux reboot (which triggers automatically a Xen
844 reboot), and if that fails it tries to do a Xen reboot. The reason
845 we don't try a Xen reboot first is that the xen reboot launches an
846 external command which connects to the Xen hypervisor, and that
847 won't work in case the root filesystem is broken and/or the xend
848 daemon is not working.
849
850 """
851 try:
852 cls.LinuxPowercycle()
853 finally:
854 utils.RunCmd([constants.XEN_CMD, "debug", "R"])
855
858 """Xen PVM hypervisor interface"""
859
860 PARAMETERS = {
861 constants.HV_USE_BOOTLOADER: hv_base.NO_CHECK,
862 constants.HV_BOOTLOADER_PATH: hv_base.OPT_FILE_CHECK,
863 constants.HV_BOOTLOADER_ARGS: hv_base.NO_CHECK,
864 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
865 constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
866 constants.HV_ROOT_PATH: hv_base.NO_CHECK,
867 constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
868 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
869 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
870
871 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
872 constants.HV_REBOOT_BEHAVIOR:
873 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
874 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
875 constants.HV_CPU_CAP: hv_base.OPT_NONNEGATIVE_INT_CHECK,
876 constants.HV_CPU_WEIGHT:
877 (False, lambda x: 0 < x < 65536, "invalid weight", None, None),
878 }
879
880 - def _GetConfig(self, instance, startup_memory, block_devices):
955
958 """Xen HVM hypervisor interface"""
959
960 ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + [
961 pathutils.VNC_PASSWORD_FILE,
962 ]
963 ANCILLARY_FILES_OPT = XenHypervisor.ANCILLARY_FILES_OPT + [
964 pathutils.VNC_PASSWORD_FILE,
965 ]
966
967 PARAMETERS = {
968 constants.HV_ACPI: hv_base.NO_CHECK,
969 constants.HV_BOOT_ORDER: (True, ) +
970 (lambda x: x and len(x.strip("acdn")) == 0,
971 "Invalid boot order specified, must be one or more of [acdn]",
972 None, None),
973 constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
974 constants.HV_DISK_TYPE:
975 hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
976 constants.HV_NIC_TYPE:
977 hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
978 constants.HV_PAE: hv_base.NO_CHECK,
979 constants.HV_VNC_BIND_ADDRESS:
980 (False, netutils.IP4Address.IsValid,
981 "VNC bind address is not a valid IP address", None, None),
982 constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
983 constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
984 constants.HV_VNC_PASSWORD_FILE: hv_base.REQ_FILE_CHECK,
985 constants.HV_MIGRATION_PORT: hv_base.REQ_NET_PORT_CHECK,
986 constants.HV_MIGRATION_MODE: hv_base.MIGRATION_MODE_CHECK,
987 constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
988
989 constants.HV_BLOCKDEV_PREFIX: hv_base.NO_CHECK,
990
991 constants.HV_PASSTHROUGH: hv_base.NO_CHECK,
992 constants.HV_REBOOT_BEHAVIOR:
993 hv_base.ParamInSet(True, constants.REBOOT_BEHAVIORS),
994 constants.HV_CPU_MASK: hv_base.OPT_MULTI_CPU_MASK_CHECK,
995 constants.HV_CPU_CAP: hv_base.NO_CHECK,
996 constants.HV_CPU_WEIGHT:
997 (False, lambda x: 0 < x < 65535, "invalid weight", None, None),
998 constants.HV_VIF_TYPE:
999 hv_base.ParamInSet(False, constants.HT_HVM_VALID_VIF_TYPES),
1000 constants.HV_VIRIDIAN: hv_base.NO_CHECK,
1001 }
1002
1003 - def _GetConfig(self, instance, startup_memory, block_devices):
1004 """Create a Xen 3.1 HVM config file.
1005
1006 """
1007 hvp = instance.hvparams
1008
1009 config = StringIO()
1010
1011
1012 kpath = hvp[constants.HV_KERNEL_PATH]
1013 config.write("kernel = '%s'\n" % kpath)
1014
1015 config.write("builder = 'hvm'\n")
1016 config.write("memory = %d\n" % startup_memory)
1017 config.write("maxmem = %d\n" % instance.beparams[constants.BE_MAXMEM])
1018 config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
1019 cpu_pinning = _CreateConfigCpus(hvp[constants.HV_CPU_MASK])
1020 if cpu_pinning:
1021 config.write("%s\n" % cpu_pinning)
1022 cpu_cap = hvp[constants.HV_CPU_CAP]
1023 if cpu_cap:
1024 config.write("cpu_cap=%d\n" % cpu_cap)
1025 cpu_weight = hvp[constants.HV_CPU_WEIGHT]
1026 if cpu_weight:
1027 config.write("cpu_weight=%d\n" % cpu_weight)
1028
1029 config.write("name = '%s'\n" % instance.name)
1030 if hvp[constants.HV_PAE]:
1031 config.write("pae = 1\n")
1032 else:
1033 config.write("pae = 0\n")
1034 if hvp[constants.HV_ACPI]:
1035 config.write("acpi = 1\n")
1036 else:
1037 config.write("acpi = 0\n")
1038 if hvp[constants.HV_VIRIDIAN]:
1039 config.write("viridian = 1\n")
1040 else:
1041 config.write("viridian = 0\n")
1042
1043 config.write("apic = 1\n")
1044 config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
1045 config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
1046 config.write("sdl = 0\n")
1047 config.write("usb = 1\n")
1048 config.write("usbdevice = 'tablet'\n")
1049 config.write("vnc = 1\n")
1050 if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
1051 config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
1052 else:
1053 config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
1054
1055 if instance.network_port > constants.VNC_BASE_PORT:
1056 display = instance.network_port - constants.VNC_BASE_PORT
1057 config.write("vncdisplay = %s\n" % display)
1058 config.write("vncunused = 0\n")
1059 else:
1060 config.write("# vncdisplay = 1\n")
1061 config.write("vncunused = 1\n")
1062
1063 vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
1064 try:
1065 password = utils.ReadFile(vnc_pwd_file)
1066 except EnvironmentError, err:
1067 raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
1068 (vnc_pwd_file, err))
1069
1070 config.write("vncpasswd = '%s'\n" % password.rstrip())
1071
1072 config.write("serial = 'pty'\n")
1073 if hvp[constants.HV_USE_LOCALTIME]:
1074 config.write("localtime = 1\n")
1075
1076 vif_data = []
1077
1078
1079
1080 nic_type = hvp[constants.HV_NIC_TYPE]
1081
1082 if nic_type is None:
1083 vif_type_str = ""
1084 if hvp[constants.HV_VIF_TYPE]:
1085 vif_type_str = ", type=%s" % hvp[constants.HV_VIF_TYPE]
1086
1087 nic_type_str = vif_type_str
1088 elif nic_type == constants.HT_NIC_PARAVIRTUAL:
1089 nic_type_str = ", type=paravirtualized"
1090 else:
1091
1092 nic_type_str = ", model=%s, type=%s" % \
1093 (nic_type, constants.HT_HVM_VIF_IOEMU)
1094 for nic in instance.nics:
1095 nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
1096 ip = getattr(nic, "ip", None)
1097 if ip is not None:
1098 nic_str += ", ip=%s" % ip
1099 if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
1100 nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
1101 vif_data.append("'%s'" % nic_str)
1102
1103 config.write("vif = [%s]\n" % ",".join(vif_data))
1104
1105 disk_data = \
1106 _GetConfigFileDiskData(block_devices, hvp[constants.HV_BLOCKDEV_PREFIX])
1107
1108 iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
1109 if iso_path:
1110 iso = "'file:%s,hdc:cdrom,r'" % iso_path
1111 disk_data.append(iso)
1112
1113 config.write("disk = [%s]\n" % (",".join(disk_data)))
1114
1115 pci_pass_arr = []
1116 pci_pass = hvp[constants.HV_PASSTHROUGH]
1117 if pci_pass:
1118 pci_pass_arr = pci_pass.split(";")
1119 config.write("pci = %s\n" % pci_pass_arr)
1120 config.write("on_poweroff = 'destroy'\n")
1121 if hvp[constants.HV_REBOOT_BEHAVIOR] == constants.INSTANCE_REBOOT_ALLOWED:
1122 config.write("on_reboot = 'restart'\n")
1123 else:
1124 config.write("on_reboot = 'destroy'\n")
1125 config.write("on_crash = 'restart'\n")
1126
1127 return config.getvalue()
1128