def post(self, uri, body=None, logon_required=True,
             wait_for_completion=False, operation_timeout=None):
        """
        Perform the HTTP POST method against the resource identified by a URI,
        using a provided request body.

        A set of standard HTTP headers is automatically part of the request.

        HMC operations using HTTP POST are either synchronous or asynchronous.
        Asynchronous operations return the URI of an asynchronously executing
        job that can be queried for status and result.

        Examples for synchronous operations:

        * With no result: "Logon", "Update CPC Properties"
        * With a result: "Create Partition"

        Examples for asynchronous operations:

        * With no result: "Start Partition"

        The `wait_for_completion` parameter of this method can be used to deal
        with asynchronous HMC operations in a synchronous way.

        If executing the operation reveals that the HMC session token is
        expired, this method re-logs on and retries the operation.

        The timeout and retry

        Parameters:

          uri (:term:`string`):
            Relative URI path of the resource, e.g. "/api/session".
            This URI is relative to the base URL of the session (see the
            :attr:`~zhmcclient.Session.base_url` property).
            Must not be `None`.

          body (:term:`json object`):
            JSON object to be used as the HTTP request body (payload).
            `None` means the same as an empty dictionary, namely that no HTTP
            body is included in the request.

          logon_required (bool):
            Boolean indicating whether the operation requires that the session
            is logged on to the HMC. For example, the "Logon" operation does
            not require that.

          wait_for_completion (bool):
            Boolean controlling whether this method should wait for completion
            of the requested asynchronous HMC operation.

            A value of `True` will cause an additional entry in the time
            statistics to be created that represents the entire asynchronous
            operation including the waiting for its completion.
            That time statistics entry will have a URI that is the targeted
            URI, appended with "+completion".

            For synchronous HMC operations, this parameter has no effect on
            the operation execution or on the return value of this method, but
            it should still be set (or defaulted) to `False` in order to avoid
            the additional entry in the time statistics.

          operation_timeout (:term:`number`):
            Timeout in seconds, when waiting for completion of an asynchronous
            operation. The special value 0 means that no timeout is set. `None`
            means that the default async operation timeout of the session is
            used.

            For `wait_for_completion=True`, a
            :exc:`~zhmcclient.OperationTimeout` is raised when the timeout
            expires.

            For `wait_for_completion=False`, this parameter has no effect.

        Returns:

          : A :term:`json object` or `None` or a :class:`~zhmcclient.Job`
          object, as follows:

          * For synchronous HMC operations, and for asynchronous HMC
            operations with `wait_for_completion=True`:

            If this method returns, the HMC operation has completed
            successfully (otherwise, an exception is raised).
            For asynchronous HMC operations, the associated job has been
            deleted.

            The return value is the result of the HMC operation as a
            :term:`json object`, or `None` if the operation has no result.
            See the section in the :term:`HMC API` book about the specific
            HMC operation for a description of the members of the returned
            JSON object.

          * For asynchronous HMC operations with `wait_for_completion=False`:

            If this method returns, the asynchronous execution of the HMC
            operation has been started successfully as a job on the HMC (if
            the operation could not be started, an exception is raised).

            The return value is a :class:`~zhmcclient.Job` object
            representing the job on the HMC.

        Raises:

          :exc:`~zhmcclient.HTTPError`
          :exc:`~zhmcclient.ParseError`
          :exc:`~zhmcclient.ClientAuthError`
          :exc:`~zhmcclient.ServerAuthError`
          :exc:`~zhmcclient.ConnectionError`
          :exc:`~zhmcclient.OperationTimeout`: The timeout expired while
            waiting for completion of the asynchronous operation.
          :exc:`TypeError`: Body has invalid type.
        """
        if logon_required:
            self.logon()
        url = self.base_url + uri
        headers = self.headers.copy()  # Standard headers

        if body is None:
            data = None
        elif isinstance(body, dict):
            data = json.dumps(body)
            # Content-type is already set in standard headers.
        elif isinstance(body, six.text_type):
            data = body.encode('utf-8')
            headers['Content-type'] = 'application/octet-stream'
        elif isinstance(body, six.binary_type):
            data = body
            headers['Content-type'] = 'application/octet-stream'
        elif isinstance(body, collections.Iterable):
            # For example, open files: open(), io.open()
            data = body
            headers['Content-type'] = 'application/octet-stream'
        else:
            raise TypeError("Body has invalid type: {}".format(type(body)))

        self._log_http_request('POST', url, headers=headers, content=data)
        req = self._session or requests
        req_timeout = (self.retry_timeout_config.connect_timeout,
                       self.retry_timeout_config.read_timeout)
        if wait_for_completion:
            stats_total = self.time_stats_keeper.get_stats(
                'post ' + uri + '+completion')
            stats_total.begin()
        try:
            stats = self.time_stats_keeper.get_stats('post ' + uri)
            stats.begin()
            try:
                if data is None:
                    result = req.post(url, headers=headers,
                                      verify=False, timeout=req_timeout)
                else:
                    result = req.post(url, data=data, headers=headers,
                                      verify=False, timeout=req_timeout)
            except requests.exceptions.RequestException as exc:
                _handle_request_exc(exc, self.retry_timeout_config)
            finally:
                stats.end()
            self._log_http_response('POST', url,
                                    status=result.status_code,
                                    headers=result.headers,
                                    content=result.content)

            if result.status_code in (200, 201):
                return _result_object(result)
            elif result.status_code == 204:
                # No content
                return None
            elif result.status_code == 202:
                if result.content == '':
                    # Some operations (e.g. "Restart Console",
                    # "Shutdown Console" or "Cancel Job") return 202
                    # with no response content.
                    return None
                else:
                    # This is the most common case to return 202: An
                    # asynchronous job has been started.
                    result_object = _result_object(result)
                    job_uri = result_object['job-uri']
                    job = Job(self, job_uri, 'POST', uri)
                    if wait_for_completion:
                        return job.wait_for_completion(operation_timeout)
                    else:
                        return job
            elif result.status_code == 403:
                result_object = _result_object(result)
                reason = result_object.get('reason', None)
                if reason == 5:
                    # API session token expired: re-logon and retry
                    self._do_logon()
                    return self.post(uri, body, logon_required)
                else:
                    msg = result_object.get('message', None)
                    raise ServerAuthError("HTTP authentication failed: {}".
                                          format(msg),
                                          HTTPError(result_object))
            else:
                result_object = _result_object(result)
                raise HTTPError(result_object)
        finally:
            if wait_for_completion:
                stats_total.end()