Actually mark drives as read-only if so configured
[ganeti-local] / lib / hypervisor / hv_xen.py
index 0aa9560..c8dd581 100644 (file)
@@ -51,6 +51,26 @@ class XenHypervisor(hv_base.BaseHypervisor):
     raise NotImplementedError
 
   @staticmethod
+  def _WriteConfigFileStatic(instance_name, data):
+    """Write the Xen config file for the instance.
+
+    This version of the function just writes the config file from static data.
+
+    """
+    utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
+
+  @staticmethod
+  def _ReadConfigFile(instance_name):
+    """Returns the contents of the instance config file.
+
+    """
+    try:
+      file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
+    return file_content
+
+  @staticmethod
   def _RemoveConfigFile(instance_name):
     """Remove the xen configuration file.
 
@@ -212,8 +232,8 @@ class XenHypervisor(hv_base.BaseHypervisor):
 
     return result
 
-  @staticmethod
-  def GetShellCommandForConsole(instance):
+  @classmethod
+  def GetShellCommandForConsole(cls, instance):
     """Return a command for connecting to the console of an instance.
 
     """
@@ -257,28 +277,73 @@ class XenHypervisor(hv_base.BaseHypervisor):
     # directly export their info (currently HVM will just sed this info)
     namespace = ["sd" + chr(i + ord('a')) for i in range(24)]
     for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
+      if cfdev.mode == constants.DISK_RDWR:
+        mode = "w"
+      else:
+        mode = "r"
       if cfdev.dev_type == constants.LD_FILE:
-        line = "'%s:%s,%s,w'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
-                                 dev_path, sd_name)
+        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
+                                  dev_path, sd_name, mode)
       else:
-        line = "'phy:%s,%s,w'" % (dev_path, sd_name)
+        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
       disk_data.append(line)
 
     return disk_data
 
-  def MigrateInstance(self, instance, target, live):
-    """Migrate an instance to a target node.
+  def MigrationInfo(self, instance):
+    """Get instance information to perform a migration.
+
+    @type instance: L{objects.Instance}
+    @param instance: instance to be migrated
+    @rtype: string
+    @return: content of the xen config file
+
+    """
+    return self._ReadConfigFile(instance.name)
+
+  def AcceptInstance(self, instance, info, target):
+    """Prepare to accept an instance.
 
-    Arguments:
-      - instance: the name of the instance
-      - target: the ip of the target node
-      - live: whether to do live migration or not
+    @type instance: L{objects.Instance}
+    @param instance: instance to be accepted
+    @type info: string
+    @param info: content of the xen config file on the source node
+    @type target: string
+    @param target: target host (usually ip), on this node
 
-    Returns: none, errors will be signaled by exception.
+    """
+    pass
+
+  def FinalizeMigration(self, instance, info, success):
+    """Finalize an instance migration.
+
+    After a successful migration we write the xen config file.
+    We do nothing on a failure, as we did not change anything at accept time.
+
+    @type instance: L{objects.Instance}
+    @param instance: instance whose migration is being aborted
+    @type info: string
+    @param info: content of the xen config file on the source node
+    @type success: boolean
+    @param success: whether the migration was a success or a failure
+
+    """
+    if success:
+      self._WriteConfigFileStatic(instance.name, info)
+
+  def MigrateInstance(self, instance, target, live):
+    """Migrate an instance to a target node.
 
     The migration will not be attempted if the instance is not
     currently running.
 
+    @type instance: string
+    @param instance: instance name
+    @type target: string
+    @param target: ip address of the target node
+    @type live: boolean
+    @param live: perform a live migration
+
     """
     if self.GetInstanceInfo(instance) is None:
       raise errors.HypervisorError("Instance not running, cannot migrate")
@@ -293,9 +358,8 @@ class XenHypervisor(hv_base.BaseHypervisor):
     # remove old xen file after migration succeeded
     try:
       self._RemoveConfigFile(instance)
-    except EnvironmentError, err:
-      logger.Error("Failure while removing instance config file: %s" %
-                   str(err))
+    except EnvironmentError:
+      logging.exception("Failure while removing instance config file")
 
 
 class XenPvmHypervisor(XenHypervisor):
@@ -304,6 +368,7 @@ class XenPvmHypervisor(XenHypervisor):
   PARAMETERS = [
     constants.HV_KERNEL_PATH,
     constants.HV_INITRD_PATH,
+    constants.HV_ROOT_PATH,
     ]
 
   @classmethod
@@ -324,11 +389,14 @@ class XenPvmHypervisor(XenHypervisor):
       raise errors.HypervisorError("Need a kernel for the instance")
 
     if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
-      raise errors.HypervisorError("The kernel path must an absolute path")
+      raise errors.HypervisorError("The kernel path must be an absolute path")
+
+    if not hvparams[constants.HV_ROOT_PATH]:
+      raise errors.HypervisorError("Need a root partition for the instance")
 
     if hvparams[constants.HV_INITRD_PATH]:
       if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
-        raise errors.HypervisorError("The initrd path must an absolute path"
+        raise errors.HypervisorError("The initrd path must be an absolute path"
                                      ", if defined")
 
   def ValidateParameters(self, hvparams):
@@ -383,7 +451,9 @@ class XenPvmHypervisor(XenHypervisor):
     config.write("disk = [%s]\n" % ",".join(
                  cls._GetConfigFileDiskData(instance.disk_template,
                                             block_devices)))
-    config.write("root = '/dev/sda ro'\n")
+
+    rpath = instance.hvparams[constants.HV_ROOT_PATH]
+    config.write("root = '%s ro'\n" % rpath)
     config.write("on_poweroff = 'destroy'\n")
     config.write("on_reboot = 'restart'\n")
     config.write("on_crash = 'restart'\n")
@@ -392,14 +462,13 @@ class XenPvmHypervisor(XenHypervisor):
     # 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))
+      utils.WriteFile("/etc/xen/%s" % instance.name,
+                      data=config.getvalue())
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Cannot write Xen instance confile"
+                                   " file /etc/xen/%s: %s" %
+                                   (instance.name, err))
+
     return True
 
 
@@ -521,14 +590,10 @@ class XenHvmHypervisor(XenHypervisor):
       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)
+      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
+                                   (constants.VNC_PASSWORD_FILE, err))
 
     config.write("vncpasswd = '%s'\n" % password.rstrip())
 
@@ -575,12 +640,11 @@ class XenHvmHypervisor(XenHypervisor):
     # 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))
+      utils.WriteFile("/etc/xen/%s" % instance.name,
+                      data=config.getvalue())
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Cannot write Xen instance confile"
+                                   " file /etc/xen/%s: %s" %
+                                   (instance.name, err))
+
     return True