Ejemplo n.º 1
0
    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)),
        })
Ejemplo n.º 2
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)
Ejemplo n.º 3
0
    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))
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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,
        })
Ejemplo n.º 9
0
    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))
Ejemplo n.º 10
0
    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)