RAPI: Allow waiting for job changes
[ganeti-local] / lib / rapi / rlib2.py
index 8b97bd9..2dffc52 100644 (file)
 
 """Remote API version 2 baserlib.library.
 
+  PUT or POST?
+  ============
+
+  According to RFC2616 the main difference between PUT and POST is that
+  POST can create new resources but PUT can only create the resource the
+  URI was pointing to on the PUT request.
+
+  To be in context of this module for instance creation POST on
+  /2/instances is legitim while PUT would be not, due to it does create a
+  new entity and not just replace /2/instances with it.
+
+  So when adding new methods, if they are operating on the URI entity itself,
+  PUT should be prefered over POST.
+
 """
 
 # pylint: disable-msg=C0103
@@ -69,6 +83,9 @@ _NR_MAP = {
   "R": _NR_REGULAR,
   }
 
+# Timeout for /2/jobs/[job_id]/wait. Gives job up to 10 seconds to change.
+_WFJC_TIMEOUT = 10
+
 
 class R_version(baserlib.R_Generic):
   """/version resource.
@@ -197,6 +214,55 @@ class R_2_jobs_id(baserlib.R_Generic):
     return result
 
 
+class R_2_jobs_id_wait(baserlib.R_Generic):
+  """/2/jobs/[job_id]/wait resource.
+
+  """
+  # WaitForJobChange provides access to sensitive information and blocks
+  # machine resources (it's a blocking RAPI call), hence restricting access.
+  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+
+  def GET(self):
+    """Waits for job changes.
+
+    """
+    job_id = self.items[0]
+
+    fields = self.getBodyParameter("fields")
+    prev_job_info = self.getBodyParameter("previous_job_info", None)
+    prev_log_serial = self.getBodyParameter("previous_log_serial", None)
+
+    if not isinstance(fields, list):
+      raise http.HttpBadRequest("The 'fields' parameter should be a list")
+
+    if not (prev_job_info is None or isinstance(prev_job_info, list)):
+      raise http.HttpBadRequest("The 'previous_job_info' parameter should"
+                                " be a list")
+
+    if not (prev_log_serial is None or
+            isinstance(prev_log_serial, (int, long))):
+      raise http.HttpBadRequest("The 'previous_log_serial' parameter should"
+                                " be a number")
+
+    client = baserlib.GetClient()
+    result = client.WaitForJobChangeOnce(job_id, fields,
+                                         prev_job_info, prev_log_serial,
+                                         timeout=_WFJC_TIMEOUT)
+    if not result:
+      raise http.HttpNotFound()
+
+    if result == constants.JOB_NOTCHANGED:
+      # No changes
+      return None
+
+    (job_info, log_entries) = result
+
+    return {
+      "job_info": job_info,
+      "log_entries": log_entries,
+      }
+
+
 class R_2_nodes(baserlib.R_Generic):
   """/2/nodes resource.
 
@@ -668,6 +734,21 @@ class R_2_instances_name_activate_disks(baserlib.R_Generic):
     return baserlib.SubmitJob([op])
 
 
+class R_2_instances_name_deactivate_disks(baserlib.R_Generic):
+  """/2/instances/[instance_name]/deactivate-disks resource.
+
+  """
+  def PUT(self):
+    """Deactivate disks for an instance.
+
+    """
+    instance_name = self.items[0]
+
+    op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
+
+    return baserlib.SubmitJob([op])
+
+
 class _R_Tags(baserlib.R_Generic):
   """ Quasiclass for tagging resources