X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/e987f166f35084ff9eaa4cb9ee791ed0c9cea101..ecabe27efd4413ef4c011529e19505133d2d2ffa:/lib/rapi/rlib2.py diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py index 3278875..ab89270 100644 --- a/lib/rapi/rlib2.py +++ b/lib/rapi/rlib2.py @@ -19,25 +19,39 @@ # 02110-1301, USA. -"""Remote API version 2 baserlib.library. +"""Remote API resource implementations. - PUT or POST? - ============ +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. +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. +In the context of this module POST on ``/2/instances`` to change an existing +entity is legitimate, while PUT would not be. PUT creates a new entity (e.g. a +new instance) with a name specified in the request. - So when adding new methods, if they are operating on the URI entity itself, - PUT should be prefered over POST. +Quoting from RFC2616, section 9.6:: + + The fundamental difference between the POST and PUT requests is reflected in + the different meaning of the Request-URI. The URI in a POST request + identifies the resource that will handle the enclosed entity. That resource + might be a data-accepting process, a gateway to some other protocol, or a + separate entity that accepts annotations. In contrast, the URI in a PUT + request identifies the entity enclosed with the request -- the user agent + knows what URI is intended and the server MUST NOT attempt to apply the + request to some other resource. If the server desires that the request be + applied to a different URI, it MUST send a 301 (Moved Permanently) response; + the user agent MAY then make its own decision regarding whether or not to + redirect the request. + +So when adding new methods, if they are operating on the URI entity itself, +PUT should be prefered over POST. """ -# pylint: disable-msg=C0103 +# pylint: disable=C0103 # C0103: Invalid name, since the R_* names are not conforming @@ -74,18 +88,24 @@ N_FIELDS = ["name", "offline", "master_candidate", "drained", "group.uuid", ] + _COMMON_FIELDS -G_FIELDS = ["name", "uuid", - "alloc_policy", - "node_cnt", "node_list", - "ctime", "mtime", "serial_no", - ] # "tags" is missing to be able to use _COMMON_FIELDS here. +G_FIELDS = [ + "alloc_policy", + "name", + "node_cnt", + "node_list", + ] + _COMMON_FIELDS -J_FIELDS = [ +J_FIELDS_BULK = [ "id", "ops", "status", "summary", - "opstatus", "opresult", "oplog", + "opstatus", "received_ts", "start_ts", "end_ts", ] +J_FIELDS = J_FIELDS_BULK + [ + "oplog", + "opresult", + ] + _NR_DRAINED = "drained" _NR_MASTER_CANDIATE = "master-candidate" _NR_MASTER = "master" @@ -239,8 +259,8 @@ class R_2_jobs(baserlib.R_Generic): client = baserlib.GetClient() if self.useBulk(): - bulkdata = client.QueryJobs(None, J_FIELDS) - return baserlib.MapBulkFields(bulkdata, J_FIELDS) + bulkdata = client.QueryJobs(None, J_FIELDS_BULK) + return baserlib.MapBulkFields(bulkdata, J_FIELDS_BULK) else: jobdata = map(compat.fst, client.QueryJobs(None, ["id"])) return baserlib.BuildUriList(jobdata, "/2/jobs/%s", @@ -395,6 +415,8 @@ class R_2_nodes_name_role(baserlib.R_Generic): node_name = self.items[0] role = self.request_body + auto_promote = bool(self._checkIntVariable("auto-promote")) + if role == _NR_REGULAR: candidate = False offline = False @@ -419,6 +441,7 @@ class R_2_nodes_name_role(baserlib.R_Generic): master_candidate=candidate, offline=offline, drained=drained, + auto_promote=auto_promote, force=bool(self.useForce())) return baserlib.SubmitJob([op]) @@ -477,6 +500,25 @@ class R_2_nodes_name_migrate(baserlib.R_Generic): return baserlib.SubmitJob([op]) +class R_2_nodes_name_modify(baserlib.R_Generic): + """/2/nodes/[node_name]/modify resource. + + """ + def POST(self): + """Changes parameters of a node. + + @return: a job id + + """ + baserlib.CheckType(self.request_body, dict, "Body contents") + + op = baserlib.FillOpcode(opcodes.OpNodeSetParams, self.request_body, { + "node_name": self.items[0], + }) + + return baserlib.SubmitJob([op]) + + class R_2_nodes_name_storage(baserlib.R_Generic): """/2/nodes/[node_name]/storage resource. @@ -644,7 +686,6 @@ def _ParseModifyGroupRequest(name, data): }) - class R_2_groups_name_modify(baserlib.R_Generic): """/2/groups/[group_name]/modify resource. @@ -903,8 +944,6 @@ class R_2_instances_name_shutdown(baserlib.R_Generic): @return: a job id """ - baserlib.CheckType(self.request_body, dict, "Body contents") - no_remember = bool(self._checkIntVariable("no_remember")) op = _ParseShutdownInstanceRequest(self.items[0], self.request_body, bool(self.dryRun()), no_remember) @@ -982,16 +1021,19 @@ def _ParseInstanceReplaceDisksRequest(name, data): # Parse disks try: - raw_disks = data["disks"] + raw_disks = data.pop("disks") 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" % str(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" % str(err)) return baserlib.FillOpcode(opcodes.OpInstanceReplaceDisks, data, override) @@ -1004,7 +1046,20 @@ class R_2_instances_name_replace_disks(baserlib.R_Generic): """Replaces disks on an instance. """ - op = _ParseInstanceReplaceDisksRequest(self.items[0], self.request_body) + if self.request_body: + body = self.request_body + elif self.queryargs: + # Legacy interface, do not modify/extend + body = { + "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: + body = {} + + op = _ParseInstanceReplaceDisksRequest(self.items[0], body) return baserlib.SubmitJob([op]) @@ -1349,7 +1404,7 @@ class _R_Tags(baserlib.R_Generic): Example: ["tag1", "tag2", "tag3"] """ - # pylint: disable-msg=W0212 + # pylint: disable=W0212 return baserlib._Tags_GET(self.TAG_LEVEL, name=self.name) def PUT(self): @@ -1359,7 +1414,7 @@ class _R_Tags(baserlib.R_Generic): you'll have back a job id. """ - # pylint: disable-msg=W0212 + # pylint: disable=W0212 if "tag" not in self.queryargs: raise http.HttpBadRequest("Please specify tag(s) to add using the" " the 'tag' parameter") @@ -1375,7 +1430,7 @@ class _R_Tags(baserlib.R_Generic): /tags?tag=[tag]&tag=[tag] """ - # pylint: disable-msg=W0212 + # pylint: disable=W0212 if "tag" not in self.queryargs: # no we not gonna delete all tags raise http.HttpBadRequest("Cannot delete all tags - please specify"