diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 5cdedf6311c12f6206ef3545ac1d3ffc28e3dae9..b9c8014fb933eb86ca229545984dcd086e45af19 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -499,6 +499,12 @@ class LUInitCluster(LogicalUnit): if config.ConfigWriter.IsCluster(): raise errors.OpPrereqError("Cluster is already initialised") + if self.op.hypervisor_type == constants.HT_XEN_HVM31: + if not os.path.exists(constants.VNC_PASSWORD_FILE): + raise errors.OpPrereqError("Please prepare the cluster VNC" + "password file %s" % + constants.VNC_PASSWORD_FILE) + self.hostname = hostname = utils.HostInfo() if hostname.ip.startswith("127."): @@ -1470,6 +1476,11 @@ class LUAddNode(LogicalUnit): primary_ip=primary_ip, secondary_ip=secondary_ip) + if self.sstore.GetHypervisorType() == constants.HT_XEN_HVM31: + if not os.path.exists(constants.VNC_PASSWORD_FILE): + raise errors.OpPrereqError("Cluster VNC password file %s missing" % + constants.VNC_PASSWORD_FILE) + def Exec(self, feedback_fn): """Adds the new node to the cluster. @@ -1589,6 +1600,8 @@ class LUAddNode(LogicalUnit): (fname, to_node)) to_copy = ss.GetFileList() + if self.sstore.GetHypervisorType() == constants.HT_XEN_HVM31: + to_copy.append(constants.VNC_PASSWORD_FILE) for fname in to_copy: if not ssh.CopyFileToNode(node, fname): logger.Error("could not copy file %s to node %s" % (fname, node)) @@ -3028,7 +3041,11 @@ class LUCreateInstance(LogicalUnit): if self.inst_ip is not None: nic.ip = self.inst_ip - network_port = None # placeholder assignment for later + ht_kind = self.sstore.GetHypervisorType() + if ht_kind in constants.HTS_REQ_PORT: + network_port = self.cfg.AllocatePort() + else: + network_port = None disks = _GenerateDiskTemplate(self.cfg, self.op.disk_template, diff --git a/lib/constants.py b/lib/constants.py index 365472f83e5ed029d0f77c32d6dc07d024cf5fbc..10c2644960ee7d066c891bfa7d3b9d5bb621ca3b 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -151,5 +151,9 @@ INSTANCE_REBOOT_FULL = "full" # Hypervisor constants HT_XEN_PVM30 = "xen-3.0" HT_FAKE = "fake" +HT_XEN_HVM31 = "xen-hvm-3.1" +HYPER_TYPES = frozenset([HT_XEN_PVM30, HT_FAKE, HT_XEN_HVM31]) +HTS_REQ_PORT = frozenset([HT_XEN_HVM31]) -HYPER_TYPES = frozenset([HT_XEN_PVM30, HT_FAKE]) +HT_HVM_VNC_BASE_PORT = 5900 +VNC_PASSWORD_FILE = _autoconf.SYSCONFDIR + "/ganeti/vnc-cluster-password" diff --git a/lib/hypervisor.py b/lib/hypervisor.py index 130c1e6e3737787842e6cb05bc28fbf765520d67..534114c7178cff96ebbbe37a8f2c4eed82782925 100644 --- a/lib/hypervisor.py +++ b/lib/hypervisor.py @@ -31,6 +31,7 @@ from ganeti import utils from ganeti import logger from ganeti import ssconf from ganeti import constants +from ganeti import errors from ganeti.errors import HypervisorError @@ -46,6 +47,8 @@ def GetHypervisor(): cls = XenPvmHypervisor elif ht_kind == constants.HT_FAKE: cls = FakeHypervisor + elif ht_kind == constants.HT_XEN_HVM31: + cls = XenHvmHypervisor else: raise HypervisorError("Unknown hypervisor type '%s'" % ht_kind) return cls() @@ -540,3 +543,102 @@ class FakeHypervisor(BaseHypervisor): """ if not os.path.exists(self._ROOT_DIR): return "The required directory '%s' does not exist." % self._ROOT_DIR + + +class XenHvmHypervisor(XenHypervisor): + """Xen HVM hypervisor interface""" + + @staticmethod + def _WriteConfigFile(instance, block_devices, extra_args): + """Create a Xen 3.1 HVM config file. + + """ + config = StringIO() + config.write("# this is autogenerated by Ganeti, please do not edit\n#\n") + config.write("kernel = '/usr/lib/xen/boot/hvmloader'\n") + config.write("builder = 'hvm'\n") + config.write("memory = %d\n" % instance.memory) + config.write("vcpus = %d\n" % instance.vcpus) + config.write("name = '%s'\n" % instance.name) + config.write("pae = 1\n") + config.write("acpi = 1\n") + config.write("apic = 1\n") + arch = os.uname()[4] + if '64' in arch: + config.write("device_model = '/usr/lib64/xen/bin/qemu-dm'\n") + else: + config.write("device_model = '/usr/lib/xen/bin/qemu-dm'\n") + config.write("boot = 'dc'\n") + config.write("sdl = 0\n") + config.write("vnc = 1\n") + config.write("vnclisten = '0.0.0.0'\n") + + if instance.network_port > constants.HT_HVM_VNC_BASE_PORT: + display = instance.network_port - constants.HT_HVM_VNC_BASE_PORT + config.write("vncdisplay = %s\n" % display) + config.write("vncunused = 0\n") + else: + config.write("# vncdisplay = 1\n") + config.write("vncunused = 1\n") + + try: + password_file = open(constants.VNC_PASSWORD_FILE, "r") + try: + password = password_file.readline() + finally: + password_file.close() + except IOError: + raise errors.OpExecError("failed to open VNC password file %s " % + constants.VNC_PASSWORD_FILE) + + config.write("vncpasswd = '%s'\n" % password.rstrip()) + + config.write("serial = 'pty'\n") + config.write("localtime = 1\n") + + vif_data = [] + for nic in instance.nics: + nic_str = "mac=%s, bridge=%s, type=ioemu" % (nic.mac, nic.bridge) + ip = getattr(nic, "ip", None) + if ip is not None: + nic_str += ", ip=%s" % ip + vif_data.append("'%s'" % nic_str) + + config.write("vif = [%s]\n" % ",".join(vif_data)) + + disk_data = ["'phy:%s,%s,w'" % + (rldev.dev_path, cfdev.iv_name.replace("sd", "ioemu:hd")) + for cfdev, rldev in block_devices] + iso = "'file:/srv/ganeti/iso/hvm-install.iso,hdc:cdrom,r'" + config.write("disk = [%s, %s]\n" % (",".join(disk_data), iso) ) + + config.write("on_poweroff = 'destroy'\n") + config.write("on_reboot = 'restart'\n") + config.write("on_crash = 'restart'\n") + if extra_args: + config.write("extra = '%s'\n" % extra_args) + # just in case it exists + utils.RemoveFile("/etc/xen/auto/%s" % instance.name) + try: + f = open("/etc/xen/%s" % instance.name, "w") + try: + f.write(config.getvalue()) + finally: + f.close() + except IOError, err: + raise errors.OpExecError("Cannot write Xen instance confile" + " file /etc/xen/%s: %s" % (instance.name, err)) + return True + + @staticmethod + def GetShellCommandForConsole(instance): + """Return a command for connecting to the console of an instance. + + """ + if instance.network_port is None: + raise errors.OpExecError("no console port defined for %s" + % instance.name) + else: + raise errors.OpExecError("no PTY console, connect to %s:%s via VNC" + % (instance.primary_node, + instance.network_port)) diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster index 424b239d77fc64920db8c994999c4318daf49b48..d8570865bfe1f7e63129ee76edd3f2a205829a46 100755 --- a/scripts/gnt-cluster +++ b/scripts/gnt-cluster @@ -257,8 +257,11 @@ commands = { " addresses", metavar="ADDRESS", default=None), make_option("-t", "--hypervisor-type", dest="hypervisor_type", - help="Specify the hypervisor type (xen-3.0, fake)", - metavar="TYPE", choices=["xen-3.0", "fake"], + help="Specify the hypervisor type " + "(xen-3.0, fake, xen-hvm-3.1)", + metavar="TYPE", choices=["xen-3.0", + "fake", + "xen-hvm-3.1"], default="xen-3.0",), make_option("-m", "--mac-prefix", dest="mac_prefix", help="Specify the mac prefix for the instance IP"