Fix iallocator instance info
[ganeti-local] / lib / hypervisor.py
index 4ba7019..5c60c6e 100644 (file)
@@ -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()
@@ -133,7 +136,7 @@ class XenHypervisor(BaseHypervisor):
 
   @staticmethod
   def _WriteConfigFile(instance, block_devices, extra_args):
-    """A Xen instance config file.
+    """Write the Xen config file for the instance.
 
     """
     raise NotImplementedError
@@ -167,12 +170,8 @@ class XenHypervisor(BaseHypervisor):
       raise HypervisorError("xm list failed, retries exceeded (%s): %s" %
                             (result.fail_reason, result.stderr))
 
-    # skip over the heading and the domain 0 line (optional)
-    if include_node:
-      to_skip = 1
-    else:
-      to_skip = 2
-    lines = result.stdout.splitlines()[to_skip:]
+    # skip over the heading
+    lines = result.stdout.splitlines()[1:]
     result = []
     for line in lines:
       # The format of lines is:
@@ -189,7 +188,11 @@ class XenHypervisor(BaseHypervisor):
       except ValueError, err:
         raise HypervisorError("Can't parse output of xm list,"
                               " line: %s, error: %s" % (line, err))
-      result.append(data)
+
+      # skip the Domain-0 (optional)
+      if include_node or data[0] != 'Domain-0':
+        result.append(data)
+
     return result
 
   def ListInstances(self):
@@ -307,20 +310,81 @@ class XenHypervisor(BaseHypervisor):
     if not utils.CheckDaemonAlive('/var/run/xend.pid', 'xend'):
       return "xend daemon is not running"
 
+  @staticmethod
+  def _GetConfigFileDiskData(disk_template, block_devices):
+    """Get disk directive for xen config file.
+
+    This method builds the xen config disk directive according to the
+    given disk_template and block_devices.
+
+    Args:
+      disk_template: String containing instance disk template
+      block_devices: List[tuple1,tuple2,...]
+        tuple: (cfdev, rldev)
+          cfdev: dict containing ganeti config disk part
+          rldev: ganeti.bdev.BlockDev object
+
+    Returns:
+      String containing disk directive for xen instance config file
+
+    """
+    FILE_DRIVER_MAP = {
+      constants.FD_LOOP: "file",
+      constants.FD_BLKTAP: "tap:aio",
+      }
+    disk_data = []
+    for cfdev, rldev in block_devices:
+      if cfdev.dev_type == constants.LD_FILE:
+        line = "'%s:%s,%s,w'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
+                                 rldev.dev_path, cfdev.iv_name)
+      else:
+        line = "'phy:%s,%s,w'" % (rldev.dev_path, cfdev.iv_name)
+      disk_data.append(line)
+
+    return disk_data
+
 
 class XenPvmHypervisor(XenHypervisor):
   """Xen PVM hypervisor interface"""
 
-  @staticmethod
-  def _WriteConfigFile(instance, block_devices, extra_args):
-    """Create a Xen instance config file.
+  @classmethod
+  def _WriteConfigFile(cls, instance, block_devices, extra_args):
+    """Write the Xen config file for the instance.
 
     """
     config = StringIO()
     config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
-    config.write("kernel = '%s'\n" % constants.XEN_KERNEL)
-    if os.path.exists(constants.XEN_INITRD):
-      config.write("ramdisk = '%s'\n" % constants.XEN_INITRD)
+
+    # kernel handling
+    if instance.kernel_path in (None, constants.VALUE_DEFAULT):
+      kpath = constants.XEN_KERNEL
+    else:
+      if not os.path.exists(instance.kernel_path):
+        raise errors.HypervisorError("The kernel %s for instance %s is"
+                                     " missing" % (instance.kernel_path,
+                                                   instance.name))
+      kpath = instance.kernel_path
+    config.write("kernel = '%s'\n" % kpath)
+
+    # initrd handling
+    if instance.initrd_path in (None, constants.VALUE_DEFAULT):
+      if os.path.exists(constants.XEN_INITRD):
+        initrd_path = constants.XEN_INITRD
+      else:
+        initrd_path = None
+    elif instance.initrd_path == constants.VALUE_NONE:
+      initrd_path = None
+    else:
+      if not os.path.exists(instance.initrd_path):
+        raise errors.HypervisorError("The initrd %s for instance %s is"
+                                     " missing" % (instance.initrd_path,
+                                                   instance.name))
+      initrd_path = instance.initrd_path
+
+    if initrd_path:
+      config.write("ramdisk = '%s'\n" % initrd_path)
+
+    # rest of the settings
     config.write("memory = %d\n" % instance.memory)
     config.write("vcpus = %d\n" % instance.vcpus)
     config.write("name = '%s'\n" % instance.name)
@@ -334,11 +398,9 @@ class XenPvmHypervisor(XenHypervisor):
       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)
-                 for cfdev, rldev in block_devices]
-    config.write("disk = [%s]\n" % ",".join(disk_data))
-
+    config.write("disk = [%s]\n" % ",".join(
+                 cls._GetConfigFileDiskData(instance.disk_template,
+                                            block_devices)))
     config.write("root = '/dev/sda ro'\n")
     config.write("on_poweroff = 'destroy'\n")
     config.write("on_reboot = 'restart'\n")
@@ -347,9 +409,15 @@ class XenPvmHypervisor(XenHypervisor):
       config.write("extra = '%s'\n" % extra_args)
     # just in case it exists
     utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
-    f = open("/etc/xen/%s" % instance.name, "w")
-    f.write(config.getvalue())
-    f.close()
+    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
@@ -367,7 +435,7 @@ class FakeHypervisor(BaseHypervisor):
   a real virtualisation software installed.
 
   """
-  _ROOT_DIR = "/var/run/ganeti-fake-hypervisor"
+  _ROOT_DIR = constants.RUN_DIR + "/ganeti-fake-hypervisor"
 
   def __init__(self):
     BaseHypervisor.__init__(self)
@@ -534,3 +602,104 @@ 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"""
+
+  @classmethod
+  def _WriteConfigFile(cls, 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")
+    if instance.hvm_boot_order is None:
+      config.write("boot = '%s'\n" % constants.HT_HVM_DEFAULT_BOOT_ORDER)
+    else:
+      config.write("boot = '%s'\n" % instance.hvm_boot_order)
+    config.write("sdl = 0\n")
+    config.write("usb = 1\n");
+    config.write("usbdevice = 'tablet'\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))
+    iso = "'file:/srv/ganeti/iso/hvm-install.iso,hdc:cdrom,r'"
+    config.write("disk = [%s, %s]\n" % (",".join(
+                 cls._GetConfigFileDiskData(instance.disk_template,
+                                            block_devices)), 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))