def GetPutOpInput(self): """Sets the node role. """ baserlib.CheckType(self.request_body, basestring, "Body contents") role = self.request_body if role == _NR_REGULAR: candidate = False offline = False drained = False elif role == _NR_MASTER_CANDIDATE: candidate = True offline = drained = None elif role == _NR_DRAINED: drained = True candidate = offline = None elif role == _NR_OFFLINE: offline = True candidate = drained = None else: raise http.HttpBadRequest("Can't set '%s' role" % role) assert len(self.items) == 1 return ({}, { "node_name": self.items[0], "master_candidate": candidate, "offline": offline, "drained": drained, "force": self.useForce(), "auto_promote": bool(self._checkIntVariable("auto-promote", default=0)), })
def GetPostOpInput(self): """Replaces disks on an instance. """ 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: raw_disks = data.pop("disks") except KeyError: pass else: 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)
def __call__(self, fn): """Handles a request. @type fn: callable @param fn: Callback for retrieving HTTP request, must return a tuple containing request message (L{http.HttpMessage}) and C{None} or the message reader (L{_HttpClientToServerMessageReader}) """ response_msg = http.HttpMessage() response_msg.start_line = \ http.HttpServerToClientStartLine(version=self.default_request_version, code=None, reason=None) force_close = True try: (request_msg, req_msg_reader) = fn() response_msg.start_line.version = request_msg.start_line.version # RFC2616, 14.23: All Internet-based HTTP/1.1 servers MUST respond # with a 400 (Bad Request) status code to any HTTP/1.1 request # message which lacks a Host header field. if (request_msg.start_line.version == http.HTTP_1_1 and not (request_msg.headers and http.HTTP_HOST in request_msg.headers)): raise http.HttpBadRequest(message="Missing Host header") (response_msg.start_line.code, response_msg.headers, response_msg.body) = \ _HandleServerRequestInner(self._handler, request_msg, req_msg_reader) except http.HttpException as err: self._SetError(self.responses, self._handler, response_msg, err) request_msg = http.HttpMessage() req_msg_reader = None else: # Only wait for client to close if we didn't have any exception. force_close = False return (request_msg, req_msg_reader, force_close, self._Finalize(self.responses, response_msg))
def ExtractUserPassword(req): """Extracts a user and a password from the http authorization header. @type req: L{http.server._HttpServerRequest} @param req: HTTP request @rtype: (str, str) @return: A tuple containing a user and a password. One or both values might be None if they are not presented """ credentials = req.request_headers.get(http.HTTP_AUTHORIZATION, None) if not credentials: return None, None # Extract scheme parts = credentials.strip().split(None, 2) if len(parts) < 1: # Missing scheme return None, None # RFC2617, section 1.2: "[...] It uses an extensible, case-insensitive # token to identify the authentication scheme [...]" scheme = parts[0].lower() if scheme == HTTP_BASIC_AUTH.lower(): # Do basic authentication if len(parts) < 2: raise http.HttpBadRequest( message=("Basic authentication requires" " credentials")) return HttpServerRequestAuthentication._ExtractBasicUserPassword( parts[1]) elif scheme == HTTP_DIGEST_AUTH.lower(): # TODO: Implement digest authentication # RFC2617, section 3.3: "Note that the HTTP server does not actually need # to know the user's cleartext password. As long as H(A1) is available to # the server, the validity of an Authorization header may be verified." pass # Unsupported authentication scheme return None, None
def _CheckAuthorization(self, req): """Checks 'Authorization' header sent by client. @type req: L{http.server._HttpServerRequest} @param req: HTTP request context @rtype: bool @return: Whether user is allowed to execute request """ credentials = req.request_headers.get(http.HTTP_AUTHORIZATION, None) if not credentials: return False # Extract scheme parts = credentials.strip().split(None, 2) if len(parts) < 1: # Missing scheme return False # RFC2617, section 1.2: "[...] It uses an extensible, case-insensitive # token to identify the authentication scheme [...]" scheme = parts[0].lower() if scheme == HTTP_BASIC_AUTH.lower(): # Do basic authentication if len(parts) < 2: raise http.HttpBadRequest( message=("Basic authentication requires" " credentials")) return self._CheckBasicAuthorization(req, parts[1]) elif scheme == HTTP_DIGEST_AUTH.lower(): # TODO: Implement digest authentication # RFC2617, section 3.3: "Note that the HTTP server does not actually need # to know the user's cleartext password. As long as H(A1) is available to # the server, the validity of an Authorization header may be verified." pass # Unsupported authentication scheme return False
def _ParseInstanceReinstallRequest(name, data): """Parses a request for reinstalling an instance. """ if not isinstance(data, dict): raise http.HttpBadRequest("Invalid body contents, not a dictionary") ostype = baserlib.CheckParameter(data, "os", default=None) start = baserlib.CheckParameter(data, "start", exptype=bool, default=True) osparams = baserlib.CheckParameter(data, "osparams", default=None) ops = [ opcodes.OpInstanceShutdown(instance_name=name), opcodes.OpInstanceReinstall(instance_name=name, os_type=ostype, osparams=osparams), ] if start: ops.append(opcodes.OpInstanceStartup(instance_name=name, force=False)) return ops
def HandleRequest(self, req): """Handle a request. """ if req.request_method.upper() != http.HTTP_POST: raise http.HttpBadRequest("Only the POST method is supported") path = req.request_path if path.startswith("/"): path = path[1:] method = getattr(self, "perspective_%s" % path, None) if method is None: raise http.HttpNotFound() try: result = (True, method(serializer.LoadJson(req.request_body))) except backend.RPCFail as err: # our custom failure exception; str(err) works fine if the # exception was constructed with a single argument, and in # this case, err.message == err.args[0] == str(err) result = (False, str(err)) except errors.QuitGanetiException as err: # Tell parent to quit logging.info("Shutting down the node daemon, arguments: %s", str(err.args)) os.kill(self.noded_pid, signal.SIGTERM) # And return the error's arguments, which must be already in # correct tuple format result = err.args except Exception as err: # pylint: disable=W0703 logging.exception("Error in RPC call") result = (False, "Error while executing backend function: %s" % str(err)) return serializer.DumpJson(result)
def GetPutOpInput(self): """Modifies a storage volume on a node. """ storage_type = self._checkStringVariable("storage_type", None) name = self._checkStringVariable("name", None) if not name: raise http.HttpBadRequest("Missing the required 'name'" " parameter") changes = {} if "allocatable" in self.queryargs: changes[constants.SF_ALLOCATABLE] = \ bool(self._checkIntVariable("allocatable", default=1)) return ({}, { "node_name": self.items[0], "storage_type": storage_type, "name": name, "changes": changes, })
def HandleRequest(self, req): """Handle a request. """ if req.request_method.upper() != http.HTTP_POST: raise http.HttpBadRequest("Only the POST method is supported") path = req.request_path if path.startswith("/"): path = path[1:] method = getattr(self, "perspective_%s" % path, None) if method is None: raise http.HttpNotFound() try: result = (True, method(serializer.LoadJson(req.request_body))) except backend.RPCFail, err: # our custom failure exception; str(err) works fine if the # exception was constructed with a single argument, and in # this case, err.message == err.args[0] == str(err) result = (False, str(err))
def _ExtractBasicUserPassword(in_data): """Extracts user and password from the contents of an authorization header. @type in_data: str @param in_data: Username and password encoded as Base64 @rtype: (str, str) @return: A tuple containing user and password. One or both values might be None if they are not presented """ try: creds = base64.b64decode(in_data.encode("ascii")).decode("ascii") except (TypeError, binascii.Error, UnicodeError): logging.exception( "Error when decoding Basic authentication credentials") raise http.HttpBadRequest( message=("Invalid basic authorization header")) if ":" not in creds: # We have just a username without password return creds, None # return (user, password) tuple return creds.split(":", 1)