RAPI: Convert instance shutdown to the new FillOpCode
[ganeti-local] / lib / rapi / rlib2.py
index 94511a2..66b0ece 100644 (file)
@@ -86,13 +86,15 @@ _NR_OFFLINE = "offline"
 _NR_REGULAR = "regular"
 
 _NR_MAP = {
-  "M": _NR_MASTER,
-  "C": _NR_MASTER_CANDIATE,
-  "D": _NR_DRAINED,
-  "O": _NR_OFFLINE,
-  "R": _NR_REGULAR,
+  constants.NR_MASTER: _NR_MASTER,
+  constants.NR_MCANDIDATE: _NR_MASTER_CANDIATE,
+  constants.NR_DRAINED: _NR_DRAINED,
+  constants.NR_OFFLINE: _NR_OFFLINE,
+  constants.NR_REGULAR: _NR_REGULAR,
   }
 
+assert frozenset(_NR_MAP.keys()) == constants.NR_ALL
+
 # Request data version field
 _REQ_DATA_VERSION = "__version__"
 
@@ -429,7 +431,7 @@ class R_2_nodes_name_evacuate(baserlib.R_Generic):
     result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
 
     jobs = []
-    for iname, node in result:
+    for iname, node in result[0]:
       if dry_run:
         jid = None
       else:
@@ -749,67 +751,6 @@ class R_2_instances(baserlib.R_Generic):
       return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
                                    uri_fields=("id", "uri"))
 
-  def _ParseVersion0CreateRequest(self):
-    """Parses an instance creation request version 0.
-
-    Request data version 0 is deprecated and should not be used anymore.
-
-    @rtype: L{opcodes.OpInstanceCreate}
-    @return: Instance creation opcode
-
-    """
-    # Do not modify anymore, request data version 0 is deprecated
-    beparams = baserlib.MakeParamsDict(self.request_body,
-                                       constants.BES_PARAMETERS)
-    hvparams = baserlib.MakeParamsDict(self.request_body,
-                                       constants.HVS_PARAMETERS)
-    fn = self.getBodyParameter
-
-    # disk processing
-    disk_data = fn('disks')
-    if not isinstance(disk_data, list):
-      raise http.HttpBadRequest("The 'disks' parameter should be a list")
-    disks = []
-    for idx, d in enumerate(disk_data):
-      if not isinstance(d, int):
-        raise http.HttpBadRequest("Disk %d specification wrong: should"
-                                  " be an integer" % idx)
-      disks.append({"size": d})
-
-    # nic processing (one nic only)
-    nics = [{"mac": fn("mac", constants.VALUE_AUTO)}]
-    if fn("ip", None) is not None:
-      nics[0]["ip"] = fn("ip")
-    if fn("mode", None) is not None:
-      nics[0]["mode"] = fn("mode")
-    if fn("link", None) is not None:
-      nics[0]["link"] = fn("link")
-    if fn("bridge", None) is not None:
-      nics[0]["bridge"] = fn("bridge")
-
-    # Do not modify anymore, request data version 0 is deprecated
-    return opcodes.OpInstanceCreate(
-      mode=constants.INSTANCE_CREATE,
-      instance_name=fn('name'),
-      disks=disks,
-      disk_template=fn('disk_template'),
-      os_type=fn('os'),
-      pnode=fn('pnode', None),
-      snode=fn('snode', None),
-      iallocator=fn('iallocator', None),
-      nics=nics,
-      start=fn('start', True),
-      ip_check=fn('ip_check', True),
-      name_check=fn('name_check', True),
-      wait_for_sync=True,
-      hypervisor=fn('hypervisor', None),
-      hvparams=hvparams,
-      beparams=beparams,
-      file_storage_dir=fn('file_storage_dir', None),
-      file_driver=fn('file_driver', constants.FD_LOOP),
-      dry_run=bool(self.dryRun()),
-      )
-
   def POST(self):
     """Create an instance.
 
@@ -823,10 +764,13 @@ class R_2_instances(baserlib.R_Generic):
     data_version = self.getBodyParameter(_REQ_DATA_VERSION, 0)
 
     if data_version == 0:
-      op = self._ParseVersion0CreateRequest()
+      raise http.HttpBadRequest("Instance creation request version 0 is no"
+                                " longer supported")
     elif data_version == 1:
-      op = _ParseInstanceCreateRequestVersion1(self.request_body,
-                                               self.dryRun())
+      data = self.request_body.copy()
+      # Remove "__version__"
+      data.pop(_REQ_DATA_VERSION, None)
+      op = _ParseInstanceCreateRequestVersion1(data, self.dryRun())
     else:
       raise http.HttpBadRequest("Unsupported request data version %s" %
                                 data_version)
@@ -925,6 +869,19 @@ class R_2_instances_name_startup(baserlib.R_Generic):
     return baserlib.SubmitJob([op])
 
 
+def _ParseShutdownInstanceRequest(name, data, dry_run):
+  """Parses a request for an instance shutdown.
+
+  @rtype: L{opcodes.OpInstanceShutdown}
+  @return: Instance shutdown opcode
+
+  """
+  return baserlib.FillOpcode(opcodes.OpInstanceShutdown, data, {
+    "instance_name": name,
+    "dry_run": dry_run,
+    })
+
+
 class R_2_instances_name_shutdown(baserlib.R_Generic):
   """/2/instances/[instance_name]/shutdown resource.
 
@@ -934,10 +891,13 @@ class R_2_instances_name_shutdown(baserlib.R_Generic):
   def PUT(self):
     """Shutdown an instance.
 
+    @return: a job id
+
     """
-    instance_name = self.items[0]
-    op = opcodes.OpInstanceShutdown(instance_name=instance_name,
-                                    dry_run=bool(self.dryRun()))
+    baserlib.CheckType(self.request_body, dict, "Body contents")
+
+    op = _ParseShutdownInstanceRequest(self.items[0], self.request_body,
+                                       bool(self.dryRun()))
 
     return baserlib.SubmitJob([op])
 
@@ -949,7 +909,7 @@ def _ParseInstanceReinstallRequest(name, data):
   if not isinstance(data, dict):
     raise http.HttpBadRequest("Invalid body contents, not a dictionary")
 
-  ostype = baserlib.CheckParameter(data, "os")
+  ostype = baserlib.CheckParameter(data, "os", default=None)
   start = baserlib.CheckParameter(data, "start", exptype=bool,
                                   default=True)
   osparams = baserlib.CheckParameter(data, "osparams", default=None)
@@ -985,14 +945,14 @@ class R_2_instances_name_reinstall(baserlib.R_Generic):
         raise http.HttpBadRequest("Can't combine query and body parameters")
 
       body = self.request_body
-    else:
-      if not self.queryargs:
-        raise http.HttpBadRequest("Missing query parameters")
+    elif self.queryargs:
       # Legacy interface, do not modify/extend
       body = {
         "os": self._checkStringVariable("os"),
         "start": not self._checkIntVariable("nostartup"),
         }
+    else:
+      body = {}
 
     ops = _ParseInstanceReinstallRequest(self.items[0], body)
 
@@ -1233,6 +1193,105 @@ class R_2_instances_name_disk_grow(baserlib.R_Generic):
     return baserlib.SubmitJob([op])
 
 
+class R_2_instances_name_console(baserlib.R_Generic):
+  """/2/instances/[instance_name]/console resource.
+
+  """
+  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+
+  def GET(self):
+    """Request information for connecting to instance's console.
+
+    @return: Serialized instance console description, see
+             L{objects.InstanceConsole}
+
+    """
+    client = baserlib.GetClient()
+
+    ((console, ), ) = client.QueryInstances([self.items[0]], ["console"], False)
+
+    if console is None:
+      raise http.HttpServiceUnavailable("Instance console unavailable")
+
+    assert isinstance(console, dict)
+    return console
+
+
+def _GetQueryFields(args):
+  """
+
+  """
+  try:
+    fields = args["fields"]
+  except KeyError:
+    raise http.HttpBadRequest("Missing 'fields' query argument")
+
+  return _SplitQueryFields(fields[0])
+
+
+def _SplitQueryFields(fields):
+  """
+
+  """
+  return [i.strip() for i in fields.split(",")]
+
+
+class R_2_query(baserlib.R_Generic):
+  """/2/query/[resource] resource.
+
+  """
+  # Results might contain sensitive information
+  GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+
+  def _Query(self, fields, filter_):
+    return baserlib.GetClient().Query(self.items[0], fields, filter_).ToDict()
+
+  def GET(self):
+    """Returns resource information.
+
+    @return: Query result, see L{objects.QueryResponse}
+
+    """
+    return self._Query(_GetQueryFields(self.queryargs), None)
+
+  def PUT(self):
+    """Submits job querying for resources.
+
+    @return: Query result, see L{objects.QueryResponse}
+
+    """
+    body = self.request_body
+
+    baserlib.CheckType(body, dict, "Body contents")
+
+    try:
+      fields = body["fields"]
+    except KeyError:
+      fields = _GetQueryFields(self.queryargs)
+
+    return self._Query(fields, self.request_body.get("filter", None))
+
+
+class R_2_query_fields(baserlib.R_Generic):
+  """/2/query/[resource]/fields resource.
+
+  """
+  def GET(self):
+    """Retrieves list of available fields for a resource.
+
+    @return: List of serialized L{objects.QueryFieldDefinition}
+
+    """
+    try:
+      raw_fields = self.queryargs["fields"]
+    except KeyError:
+      fields = None
+    else:
+      fields = _SplitQueryFields(raw_fields[0])
+
+    return baserlib.GetClient().QueryFields(self.items[0], fields).ToDict()
+
+
 class _R_Tags(baserlib.R_Generic):
   """ Quasiclass for tagging resources