Add new spindle_count node parameter
[ganeti-local] / lib / rapi / rlib2.py
index 73b9398..46e0029 100644 (file)
@@ -51,7 +51,7 @@ PUT should be prefered over POST.
 
 """
 
 
 """
 
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
 
 # C0103: Invalid name, since the R_* names are not conforming
 
 
 # C0103: Invalid name, since the R_* names are not conforming
 
@@ -94,6 +94,7 @@ G_FIELDS = [
   "name",
   "node_cnt",
   "node_list",
   "name",
   "node_cnt",
   "node_list",
+  "ipolicy",
   ] + _COMMON_FIELDS
 
 J_FIELDS_BULK = [
   ] + _COMMON_FIELDS
 
 J_FIELDS_BULK = [
@@ -161,6 +162,12 @@ class R_root(baserlib.ResourceBase):
     return None
 
 
     return None
 
 
+class R_2(R_root):
+  """/2 resource.
+
+  """
+
+
 class R_version(baserlib.ResourceBase):
   """/version resource.
 
 class R_version(baserlib.ResourceBase):
   """/version resource.
 
@@ -176,10 +183,12 @@ class R_version(baserlib.ResourceBase):
     return constants.RAPI_VERSION
 
 
     return constants.RAPI_VERSION
 
 
-class R_2_info(baserlib.ResourceBase):
+class R_2_info(baserlib.OpcodeResource):
   """/2/info resource.
 
   """
   """/2/info resource.
 
   """
+  GET_OPCODE = opcodes.OpClusterQuery
+
   def GET(self):
     """Returns cluster information.
 
   def GET(self):
     """Returns cluster information.
 
@@ -200,10 +209,12 @@ class R_2_features(baserlib.ResourceBase):
     return list(ALL_FEATURES)
 
 
     return list(ALL_FEATURES)
 
 
-class R_2_os(baserlib.ResourceBase):
+class R_2_os(baserlib.OpcodeResource):
   """/2/os resource.
 
   """
   """/2/os resource.
 
   """
+  GET_OPCODE = opcodes.OpOsDiagnose
+
   def GET(self):
     """Return a list of all OSes.
 
   def GET(self):
     """Return a list of all OSes.
 
@@ -345,10 +356,12 @@ class R_2_jobs_id_wait(baserlib.ResourceBase):
       }
 
 
       }
 
 
-class R_2_nodes(baserlib.ResourceBase):
+class R_2_nodes(baserlib.OpcodeResource):
   """/2/nodes resource.
 
   """
   """/2/nodes resource.
 
   """
+  GET_OPCODE = opcodes.OpNodeQuery
+
   def GET(self):
     """Returns a list of all nodes.
 
   def GET(self):
     """Returns a list of all nodes.
 
@@ -365,10 +378,12 @@ class R_2_nodes(baserlib.ResourceBase):
                                    uri_fields=("id", "uri"))
 
 
                                    uri_fields=("id", "uri"))
 
 
-class R_2_nodes_name(baserlib.ResourceBase):
+class R_2_nodes_name(baserlib.OpcodeResource):
   """/2/nodes/[node_name] resource.
 
   """
   """/2/nodes/[node_name] resource.
 
   """
+  GET_OPCODE = opcodes.OpNodeQuery
+
   def GET(self):
     """Send information about a node.
 
   def GET(self):
     """Send information about a node.
 
@@ -383,6 +398,22 @@ class R_2_nodes_name(baserlib.ResourceBase):
     return baserlib.MapFields(N_FIELDS, result[0])
 
 
     return baserlib.MapFields(N_FIELDS, result[0])
 
 
+class R_2_nodes_name_powercycle(baserlib.OpcodeResource):
+  """/2/nodes/[node_name]/powercycle resource.
+
+  """
+  POST_OPCODE = opcodes.OpNodePowercycle
+
+  def GetPostOpInput(self):
+    """Tries to powercycle a node.
+
+    """
+    return (self.request_body, {
+      "node_name": self.items[0],
+      "force": self.useForce(),
+      })
+
+
 class R_2_nodes_name_role(baserlib.OpcodeResource):
   """/2/nodes/[node_name]/role resource.
 
 class R_2_nodes_name_role(baserlib.OpcodeResource):
   """/2/nodes/[node_name]/role resource.
 
@@ -438,6 +469,7 @@ class R_2_nodes_name_role(baserlib.OpcodeResource):
       "offline": offline,
       "drained": drained,
       "force": self.useForce(),
       "offline": offline,
       "drained": drained,
       "force": self.useForce(),
+      "auto_promote": bool(self._checkIntVariable("auto-promote", default=0)),
       })
 
 
       })
 
 
@@ -492,6 +524,23 @@ class R_2_nodes_name_migrate(baserlib.OpcodeResource):
       })
 
 
       })
 
 
+class R_2_nodes_name_modify(baserlib.OpcodeResource):
+  """/2/nodes/[node_name]/modify resource.
+
+  """
+  POST_OPCODE = opcodes.OpNodeSetParams
+
+  def GetPostOpInput(self):
+    """Changes parameters of a node.
+
+    """
+    assert len(self.items) == 1
+
+    return (self.request_body, {
+      "node_name": self.items[0],
+      })
+
+
 class R_2_nodes_name_storage(baserlib.OpcodeResource):
   """/2/nodes/[node_name]/storage resource.
 
 class R_2_nodes_name_storage(baserlib.OpcodeResource):
   """/2/nodes/[node_name]/storage resource.
 
@@ -576,6 +625,7 @@ class R_2_groups(baserlib.OpcodeResource):
   """/2/groups resource.
 
   """
   """/2/groups resource.
 
   """
+  GET_OPCODE = opcodes.OpGroupQuery
   POST_OPCODE = opcodes.OpGroupAdd
   POST_RENAME = {
     "name": "group_name",
   POST_OPCODE = opcodes.OpGroupAdd
   POST_RENAME = {
     "name": "group_name",
@@ -687,30 +737,17 @@ class R_2_groups_name_assign_nodes(baserlib.OpcodeResource):
       })
 
 
       })
 
 
-def _ParseInstanceCreateRequestVersion1(data, dry_run):
-  """Parses an instance creation request version 1.
-
-  @rtype: L{opcodes.OpInstanceCreate}
-  @return: Instance creation opcode
+class R_2_instances(baserlib.OpcodeResource):
+  """/2/instances resource.
 
   """
 
   """
-  override = {
-    "dry_run": dry_run,
-    }
-
-  rename = {
+  GET_OPCODE = opcodes.OpInstanceQuery
+  POST_OPCODE = opcodes.OpInstanceCreate
+  POST_RENAME = {
     "os": "os_type",
     "name": "instance_name",
     }
 
     "os": "os_type",
     "name": "instance_name",
     }
 
-  return baserlib.FillOpcode(opcodes.OpInstanceCreate, data, override,
-                             rename=rename)
-
-
-class R_2_instances(baserlib.ResourceBase):
-  """/2/instances resource.
-
-  """
   def GET(self):
     """Returns a list of all available instances.
 
   def GET(self):
     """Returns a list of all available instances.
 
@@ -727,14 +764,13 @@ class R_2_instances(baserlib.ResourceBase):
       return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
                                    uri_fields=("id", "uri"))
 
       return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
                                    uri_fields=("id", "uri"))
 
-  def POST(self):
+  def GetPostOpInput(self):
     """Create an instance.
 
     @return: a job id
 
     """
     """Create an instance.
 
     @return: a job id
 
     """
-    if not isinstance(self.request_body, dict):
-      raise http.HttpBadRequest("Invalid body contents, not a dictionary")
+    baserlib.CheckType(self.request_body, dict, "Body contents")
 
     # Default to request data version 0
     data_version = self.getBodyParameter(_REQ_DATA_VERSION, 0)
 
     # Default to request data version 0
     data_version = self.getBodyParameter(_REQ_DATA_VERSION, 0)
@@ -742,22 +778,24 @@ class R_2_instances(baserlib.ResourceBase):
     if data_version == 0:
       raise http.HttpBadRequest("Instance creation request version 0 is no"
                                 " longer supported")
     if data_version == 0:
       raise http.HttpBadRequest("Instance creation request version 0 is no"
                                 " longer supported")
-    elif data_version == 1:
-      data = self.request_body.copy()
-      # Remove "__version__"
-      data.pop(_REQ_DATA_VERSION, None)
-      op = _ParseInstanceCreateRequestVersion1(data, self.dryRun())
-    else:
+    elif data_version != 1:
       raise http.HttpBadRequest("Unsupported request data version %s" %
                                 data_version)
 
       raise http.HttpBadRequest("Unsupported request data version %s" %
                                 data_version)
 
-    return self.SubmitJob([op])
+    data = self.request_body.copy()
+    # Remove "__version__"
+    data.pop(_REQ_DATA_VERSION, None)
+
+    return (data, {
+      "dry_run": self.dryRun(),
+      })
 
 
 class R_2_instances_name(baserlib.OpcodeResource):
   """/2/instances/[instance_name] resource.
 
   """
 
 
 class R_2_instances_name(baserlib.OpcodeResource):
   """/2/instances/[instance_name] resource.
 
   """
+  GET_OPCODE = opcodes.OpInstanceQuery
   DELETE_OPCODE = opcodes.OpInstanceRemove
 
   def GET(self):
   DELETE_OPCODE = opcodes.OpInstanceRemove
 
   def GET(self):
@@ -893,12 +931,14 @@ def _ParseInstanceReinstallRequest(name, data):
   return ops
 
 
   return ops
 
 
-class R_2_instances_name_reinstall(baserlib.ResourceBase):
+class R_2_instances_name_reinstall(baserlib.OpcodeResource):
   """/2/instances/[instance_name]/reinstall resource.
 
   Implements an instance reinstall.
 
   """
   """/2/instances/[instance_name]/reinstall resource.
 
   Implements an instance reinstall.
 
   """
+  POST_OPCODE = opcodes.OpInstanceReinstall
+
   def POST(self):
     """Reinstall an instance.
 
   def POST(self):
     """Reinstall an instance.
 
@@ -936,23 +976,38 @@ class R_2_instances_name_replace_disks(baserlib.OpcodeResource):
     """Replaces disks on an instance.
 
     """
     """Replaces disks on an instance.
 
     """
-    data = self.request_body.copy()
     static = {
       "instance_name": self.items[0],
       }
 
     static = {
       "instance_name": self.items[0],
       }
 
+    if self.request_body:
+      data = self.request_body
+    elif self.queryargs:
+      # Legacy interface, do not modify/extend
+      data = {
+        "remote_node": self._checkStringVariable("remote_node", default=None),
+        "mode": self._checkStringVariable("mode", default=None),
+        "disks": self._checkStringVariable("disks", default=None),
+        "iallocator": self._checkStringVariable("iallocator", default=None),
+        }
+    else:
+      data = {}
+
     # Parse disks
     try:
     # Parse disks
     try:
-      raw_disks = data["disks"]
+      raw_disks = data.pop("disks")
     except KeyError:
       pass
     else:
     except KeyError:
       pass
     else:
-      if not ht.TListOf(ht.TInt)(raw_disks): # pylint: disable-msg=E1102
-        # Backwards compatibility for strings of the format "1, 2, 3"
-        try:
-          data["disks"] = [int(part) for part in raw_disks.split(",")]
-        except (TypeError, ValueError), err:
-          raise http.HttpBadRequest("Invalid disk index passed: %s" % err)
+      if raw_disks:
+        if ht.TListOf(ht.TInt)(raw_disks): # pylint: disable=E1102
+          data["disks"] = raw_disks
+        else:
+          # Backwards compatibility for strings of the format "1, 2, 3"
+          try:
+            data["disks"] = [int(part) for part in raw_disks.split(",")]
+          except (TypeError, ValueError), err:
+            raise http.HttpBadRequest("Invalid disk index passed: %s" % err)
 
     return (data, static)
 
 
     return (data, static)
 
@@ -990,6 +1045,21 @@ class R_2_instances_name_deactivate_disks(baserlib.OpcodeResource):
       })
 
 
       })
 
 
+class R_2_instances_name_recreate_disks(baserlib.OpcodeResource):
+  """/2/instances/[instance_name]/recreate-disks resource.
+
+  """
+  POST_OPCODE = opcodes.OpInstanceRecreateDisks
+
+  def GetPostOpInput(self):
+    """Recreate disks for an instance.
+
+    """
+    return ({}, {
+      "instance_name": self.items[0],
+      })
+
+
 class R_2_instances_name_prepare_export(baserlib.OpcodeResource):
   """/2/instances/[instance_name]/prepare-export resource.
 
 class R_2_instances_name_prepare_export(baserlib.OpcodeResource):
   """/2/instances/[instance_name]/prepare-export resource.
 
@@ -1105,6 +1175,7 @@ class R_2_instances_name_console(baserlib.ResourceBase):
 
   """
   GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
 
   """
   GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+  GET_OPCODE = opcodes.OpInstanceConsole
 
   def GET(self):
     """Request information for connecting to instance's console.
 
   def GET(self):
     """Request information for connecting to instance's console.
@@ -1149,9 +1220,11 @@ class R_2_query(baserlib.ResourceBase):
   """
   # Results might contain sensitive information
   GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
   """
   # Results might contain sensitive information
   GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+  GET_OPCODE = opcodes.OpQuery
+  PUT_OPCODE = opcodes.OpQuery
 
 
-  def _Query(self, fields, filter_):
-    return self.GetClient().Query(self.items[0], fields, filter_).ToDict()
+  def _Query(self, fields, qfilter):
+    return self.GetClient().Query(self.items[0], fields, qfilter).ToDict()
 
   def GET(self):
     """Returns resource information.
 
   def GET(self):
     """Returns resource information.
@@ -1176,13 +1249,20 @@ class R_2_query(baserlib.ResourceBase):
     except KeyError:
       fields = _GetQueryFields(self.queryargs)
 
     except KeyError:
       fields = _GetQueryFields(self.queryargs)
 
-    return self._Query(fields, self.request_body.get("filter", None))
+    qfilter = body.get("qfilter", None)
+    # TODO: remove this after 2.7
+    if qfilter is None:
+      qfilter = body.get("filter", None)
+
+    return self._Query(fields, qfilter)
 
 
 class R_2_query_fields(baserlib.ResourceBase):
   """/2/query/[resource]/fields resource.
 
   """
 
 
 class R_2_query_fields(baserlib.ResourceBase):
   """/2/query/[resource]/fields resource.
 
   """
+  GET_OPCODE = opcodes.OpQueryFields
+
   def GET(self):
     """Retrieves list of available fields for a resource.
 
   def GET(self):
     """Retrieves list of available fields for a resource.
 
@@ -1199,7 +1279,7 @@ class R_2_query_fields(baserlib.ResourceBase):
     return self.GetClient().QueryFields(self.items[0], fields).ToDict()
 
 
     return self.GetClient().QueryFields(self.items[0], fields).ToDict()
 
 
-class _R_Tags(baserlib.ResourceBase):
+class _R_Tags(baserlib.OpcodeResource):
   """ Quasiclass for tagging resources
 
   Manages tags. When inheriting this class you must define the
   """ Quasiclass for tagging resources
 
   Manages tags. When inheriting this class you must define the
@@ -1207,14 +1287,17 @@ class _R_Tags(baserlib.ResourceBase):
 
   """
   TAG_LEVEL = None
 
   """
   TAG_LEVEL = None
+  GET_OPCODE = opcodes.OpTagsGet
+  PUT_OPCODE = opcodes.OpTagsSet
+  DELETE_OPCODE = opcodes.OpTagsDel
 
 
-  def __init__(self, items, queryargs, req):
+  def __init__(self, items, queryargs, req, **kwargs):
     """A tag resource constructor.
 
     We have to override the default to sort out cluster naming case.
 
     """
     """A tag resource constructor.
 
     We have to override the default to sort out cluster naming case.
 
     """
-    baserlib.ResourceBase.__init__(self, items, queryargs, req)
+    baserlib.OpcodeResource.__init__(self, items, queryargs, req, **kwargs)
 
     if self.TAG_LEVEL == constants.TAG_CLUSTER:
       self.name = None
 
     if self.TAG_LEVEL == constants.TAG_CLUSTER:
       self.name = None
@@ -1255,22 +1338,21 @@ class _R_Tags(baserlib.ResourceBase):
 
     return list(tags)
 
 
     return list(tags)
 
-  def PUT(self):
+  def GetPutOpInput(self):
     """Add a set of tags.
 
     The request as a list of strings should be PUT to this URI. And
     you'll have back a job id.
 
     """
     """Add a set of tags.
 
     The request as a list of strings should be PUT to this URI. And
     you'll have back a job id.
 
     """
-    # pylint: disable-msg=W0212
-    if "tag" not in self.queryargs:
-      raise http.HttpBadRequest("Please specify tag(s) to add using the"
-                                " the 'tag' parameter")
-    op = opcodes.OpTagsSet(kind=self.TAG_LEVEL, name=self.name,
-                           tags=self.queryargs["tag"], dry_run=self.dryRun())
-    return self.SubmitJob([op])
+    return ({}, {
+      "kind": self.TAG_LEVEL,
+      "name": self.name,
+      "tags": self.queryargs.get("tag", []),
+      "dry_run": self.dryRun(),
+      })
 
 
-  def DELETE(self):
+  def GetDeleteOpInput(self):
     """Delete a tag.
 
     In order to delete a set of tags, the DELETE
     """Delete a tag.
 
     In order to delete a set of tags, the DELETE
@@ -1278,14 +1360,8 @@ class _R_Tags(baserlib.ResourceBase):
     /tags?tag=[tag]&tag=[tag]
 
     """
     /tags?tag=[tag]&tag=[tag]
 
     """
-    # pylint: disable-msg=W0212
-    if "tag" not in self.queryargs:
-      # no we not gonna delete all tags
-      raise http.HttpBadRequest("Cannot delete all tags - please specify"
-                                " tag(s) using the 'tag' parameter")
-    op = opcodes.OpTagsDel(kind=self.TAG_LEVEL, name=self.name,
-                           tags=self.queryargs["tag"], dry_run=self.dryRun())
-    return self.SubmitJob([op])
+    # Re-use code
+    return self.GetPutOpInput()
 
 
 class R_2_instances_name_tags(_R_Tags):
 
 
 class R_2_instances_name_tags(_R_Tags):