patroni.api module

Implement Patroni’s REST API.

Exposes a REST API of patroni operations functions, such as status, performance and management to web clients.

Much of what can be achieved with the command line tool patronictl can be done via the API. Patroni CLI and daemon utilises the API to perform these functions.

class _patroni.api.RestApiHandler(_request: Any, client_address: Any, server: RestApiServer | https://docs.python.org/3/library/ http.server.html# http.server.HTTPServer[HTTPServer]) View on GitHub

Bases: https://docs.python.org/3/library/ http.server.html# http.server.BaseHTTPRequestHandler[BaseHTTPRequestHandler] + Define how to handle each of the requests that are made against the REST API server. +

__init\\__(request: Any, client_address: Any, server: RestApiServer | https://docs.python.org/3/library/ http.server.html# http.server.HTTPServer[HTTPServer]) → None View on GitHub

Create a RestApiHandler instance. + Note

  Currently not different from its superclass link:#patroni.api.RestApiHandler.\\__init\\__[`+\\__init\\__()+`], and only used so `+pyright+` can understand the type of `+server+` attribute.
  +
  Parameters:::
+
read_json_content(_body_is_optional: bool = False) → Dict[ Any, Any] | None View on GitHub

Read JSON from HTTP request body. + Note

Retrieves the request body based on content-length HTTP header. The body is expected to be a JSON string with that length.
If request body is expected but content-length HTTP header is absent, then write an HTTP response with HTTP status `+411+`.
  If request body is expected but contains nothing, or if an exception is faced, then write an HTTP response with HTTP status `+400+`.
  +
  Parameters:::
    *body_is_optional* – if `+False+` then the request must contain a body. If `+True+`, then the request may or may not contain a body.
  Returns:::
    deserialized JSON string from request body, if present. If body is absent, but _body_is_optional_ is `+True+`, then return an empty dictionary. Returns `+None+` otherwise.
+
write_json_response(_status_code: int, response: Any) → None View on GitHub

Write an HTTP response with a JSON content type. + Call write_response() with content_type as application/json. +

Parameters

+

write_status_code_only(_status_code: int) → None View on GitHub

Write a response that is composed only of the HTTP status. + The response is written with these values separated by space: + __\\__ __\\__ + Note

  This is usually useful for replying to requests from software like HAProxy.
  +
  Parameters:::
    *status_code* – HTTP status code.
  Example:::
+
write_status_response(_status_code: int, response: Dict[ str, Any]) → None View on GitHub

Write an HTTP response with Patroni/Postgres status in JSON format. + Modifies response before sending it to the client. Defines the patroni key, which is a dictionary that contains the mandatory keys: + __\\__ __\\__ + May also add the following optional keys, depending on the status of this Patroni/PostgreSQL node: + __\\__ __\\__ +

Parameters

+

do_DELETE_restart(*args: Any, **kwargs: Any) → None View on GitHub

+

do_DELETE_switchover(*args: Any, **kwargs: Any) → None View on GitHub

+

do_GET(write_status_code_only: bool = False) → None View on GitHub

Process all GET requests which can not be routed to other methods. + Is used for handling all health-checks requests. E.g. “GET /(primary|replica|sync|async|etc…)”. + The (optional) query parameters and the HTTP response status depend on the requested path: + __\\__

  • /, primary, or read-write: + __\\__ __\\__

  • /standby-leader: + __\\__ __\\__

  • /leader: + __\\__ __\\__

  • /replica: + __\\__

    • Query parameters: + __\\__ __\\__

    • HTTP status 200: if up and running as a standby and without noloadbalance tag. __\\__

  • /read-only: + __\\__ __\\__

  • /synchronous or /sync: + __\\__ __\\__

  • /read-only-sync: + __\\__ __\\__

  • /asynchronous: + __\\__

    • Query parameters: + __\\__ __\\__

    • HTTP status 200: if up and running as an asynchronous standby. __\\__

  • /health: + __\\__ __\\__ __\\__ + Note

    If not able to honor the query parameter, or not able to match the condition described for HTTP status `+200+` in each path above, then HTTP status will be `+503+`.
    +
    Note
      Independently of the requested path, if _write_status_code_only_ is `+False+`, then it always write an HTTP response through link:#patroni.api.RestApiHandler._write_status_response[`+_write_status_response()+`], with the node status.
      +
      Parameters:::
        *write_status_code_only* – indicates that instead of a normal HTTP response we should send only the HTTP Status Code and close the connection. Useful when health-checks are executed by HAProxy.
    +
do_GET_cluster() → None View on GitHub

Handle a GET request to /cluster path.
Write an HTTP response with JSON content based on the output of cluster_as_json(), with HTTP status 200 and the JSON representation of the cluster topology. +

do_GET_config() → None View on GitHub

Handle a GET request to /config path.
Write an HTTP response with a JSON content representing the Patroni configuration that is stored in the DCS, with HTTP status 200.
If the cluster information is not available in the DCS, then it will respond with no body and HTTP status 502 instead. +

do_GET_failsafe() → None View on GitHub

Handle a GET request to /failsafe path. + Writes a response with a JSON string body containing all nodes that are known to Patroni at a given point in time, with HTTP status 200. The JSON contains a dictionary, each key is the name of the Patroni node, and the corresponding value is the URI to access /patroni path of its REST API. + Note

  If `+failsafe_mode+` is not enabled, then write a response with HTTP status `+502+`.
+
do_GET_history() → None View on GitHub

Handle a GET request to /history path.
Write an HTTP response with a JSON content representing the history of events in the cluster, with HTTP status 200.
The response contains a list of failover/switchover events. Each item is a list with the following items:
__\\__ __\\__ +

do_GET_liveness() → None View on GitHub

Handle a GET request to /liveness path. + Write a simple HTTP response with HTTP status: + __\\__

  • 200: + __\\__ __\\__

  • 503:
    __\\__ __\\__ __\\__ +

do_GET_metrics() → None View on GitHub

Handle a GET request to /metrics path.
Write an HTTP response with plain text content in the format used by Prometheus, with HTTP status 200.
The response contains the following items:
__\\__ __\\__
For PostgreSQL v9.6+ the response will also have the following:
__\\__ __\\__ +

do_GET_patroni() → None View on GitHub

Handle a GET request to /patroni path.
Write an HTTP response through _write_status_response(), with HTTP status 200 and the status of Postgres. +

do_GET_readiness() → None View on GitHub

Handle a GET request to /readiness path. + Write a simple HTTP response which HTTP status can be: + __\\__

  • 200: + __\\__ __\\__

  • 503: if none of the previous conditions apply. __\\__ +

do_HEAD() → None View on GitHub

Handle a HEAD request.
Write a simple HTTP response that represents the current PostgreSQL status. Send only 200+ ``+OK or 503+ ``+Service`+ ``+Unavailable` as a response and nothing more, particularly no headers. +

do_OPTIONS() → None View on GitHub

Handle an OPTIONS request.
Write a simple HTTP response that represents the current PostgreSQL status. Send only 200+ ``+OK or 503+ ``+Service`+ ``+Unavailable` as a response and nothing more, particularly no headers. +

do_PATCH_config(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_citus(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_failover(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_failsafe(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_mpp() → None View on GitHub

Handle a POST request to /mpp path. + Call handle_event() to handle the request, then write a response with HTTP status code 200. + Note

  If unable to parse the request body, then the request is silently discarded.
+
do_POST_reinitialize(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_reload(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_restart(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_sigterm(*args: Any, **kwargs: Any) → None View on GitHub

+

do_POST_switchover() → None View on GitHub

Handle a POST request to /switchover path.
Calls do_POST_failover() with switchover option. +

do_PUT_config(*args: Any, **kwargs: Any) → None View on GitHub

+

get_postgresql_status(retry: bool = False) → Dict[ str, Any] View on GitHub

Builds an object representing a status of “postgres”. + Some of the values are collected by executing a query and other are taken from the state stored in memory. +

Parameters

retry – whether the query should be retried if failed or give up immediately

Returns

a dict with the status of Postgres/Patroni. The keys are: +

  • state: Postgres state among stopping, stopped, stop+ ``+failed, crashed, running, starting, start+ ``+failed, restarting, restart+ ``+failed, initializing+ ``+new+ ``+cluster, initdb+ ``+failed, running+ ``+custom+ ``+bootstrap+ ``+script, custom+ ``+bootstrap+ ``+failed, creating+ ``+replica, or unknown;

  • postmaster_start_time: pg_postmaster_start_time();

  • role: replica or master based on pg_is_in_recovery() output;

  • server_version: Postgres version without periods, e.g. 150002 for Postgres 15.2;

  • xlog: dictionary. Its structure depends on role: + __\\__

    • If master: + __\\__ __\\__

    • If replica: + __\\__ __\\__ __\\__

  • sync_standby: True if replication mode is synchronous and this is a sync standby;

  • timeline: PostgreSQL primary node timeline;

  • +

replication: list of dict entries, one for each replication connection. Each entry

contains the following keys:

  • pause: True if cluster is in maintenance mode;

  • cluster_unlocked: True if cluster has no node holding the leader lock;

  • failsafe_mode_is_active: True if DCS failsafe mode is currently active;

  • dcs_last_seen: epoch timestamp DCS was last reached by Patroni. +

handle_one_request() → None View on GitHub

Parse and dispatch a request to the appropriate do_* method. + Note

  This is only used to keep track of latency when logging messages through link:#patroni.api.RestApiHandler.log_message[`+log_message()+`].
+
is_failover_possible(cluster: Cluster, leader: str | None, candidate: str | None, action: str) → str | None View on GitHub

Checks whether there are nodes that could take over after demoting the primary. +

Parameters
Returns

a string with the error message or None if good nodes are found. +

log_message(format: str, *args: Any) → None View on GitHub

Log a custom debug message. + Additionally, to format, the log entry contains the client IP address and the current latency of the request. +

Parameters

+

parse_request() → bool View on GitHub

Override parse_request() to enrich basic functionality of https://docs.python.org/3/library/ http.server.html# http.server.BaseHTTPRequestHandler[BaseHTTPRequestHandler]. + Original class can only invoke do_GET(), do_POST(), do_PUT(), etc method implementations if they are defined. + But we would like to have at least some simple routing mechanism, i.e.: + __\\__ __\\__ + If the do_ method does not exist we’ll fall back to original behavior. +

Returns

True for success, False for failure; on failure, any relevant error response has already been sent back. +

static _parse_schedule(_schedule: str, action: str) → Tuple[ int | None, str | None, datetime | None] View on GitHub

Parse the given schedule and validate it. +

Parameters
Returns

a tuple composed of 3 items: +

  • Suggested HTTP status code for a response: + __\\__ __\\__

  • An error message, if any error is faced, otherwise None;

  • Parsed schedule, if able to parse, otherwise None. +

poll_failover_result(leader: str | None, candidate: str | None, action: str) → Tuple[ int, str] View on GitHub

Poll failover/switchover operation until it finishes or times out. +

Parameters
Returns

a tuple composed of 2 items: +

  • Response HTTP status codes: + __\\__ __\\__

  • A status message about the operation. +

query(sql: str, *params: Any, retry: bool = False) → List[ Tuple[ Any, …​]] View on GitHub

Execute sql query with params and optionally return results. +

Parameters
Returns

a list of rows that were fetched from the database. +

write_response(status_code: int, body: str, content_type: str = 'text/html', headers: Dict[ str, str] | None = None) → None View on GitHub

Write an HTTP response. + Note

Besides `+Content-Type+` header, and the HTTP headers passed through _headers_, this function will also write the HTTP headers defined through `+restapi. http_extra_headers+` and `+restapi. https_extra_headers+` from Patroni configuration.
+
Parameters:::
class _patroni.api.RestApiServer(_patroni: Patroni, config: Dict[ str, Any]) View on GitHub

Bases: ThreadingMixIn, https://docs.python.org/3/library/ http.server.html# http.server.HTTPServer[HTTPServer], Thread + Patroni REST API server. + An asynchronous thread-based HTTP server. +

\__has_dual_stack() → bool

Check if the system has support for dual stack sockets. +

Returns

True if it has support for dual stack sockets. +

__ httpserver_init(_host: str_, port: int) → Nonelink:#patroni.api.RestApiServer.\\__ httpserver_init[]

Start REST API HTTP server. + Note

  If system has no support for dual stack sockets, then IPv4 is preferred over IPv6.
  +
  Parameters:::
+
__init\\__(patroni: Patroni, config: Dict[ str, Any]) → None View on GitHub

Establish patroni configuration for the REST API daemon. + Create a RestApiServer instance. +

Parameters

+

__initialize(_listen: str_, ssl_options: Dict[ str, Any]) → None

Configure and start REST API HTTP server. + Note

  This method can be called upon first initialization, and also when reloading Patroni. When reloading Patroni, it restarts the HTTP server thread.
  +
  Parameters:::
  Raises:::
     https://docs.python.org/3/library/exceptions.html#ValueError[`+ValueError+`]: if any issue is faced while parsing _listen_.
+
\__members_ips() → Iterator[ IPv4Network | IPv6Network]

Resolve each Patroni node restapi.connect_address to IP networks. + Note

  Only yields object if `+restapi.allowlist_include_members+` setting is enabled.
  +
  Yields:::
    each node `+restapi.connect_address+` resolved to an IP network.
+
__resolve_ips(_port: int_) → Iterator[ IPv4Network | IPv6Network]

Resolve host + port to one or more IP networks. +

Parameters
Yields

host + port resolved to IP networks. +

build_allowlist(_value: List[ str] | None) → Iterator[ IPv4Network | IPv6Network] View on GitHub

Resolve each entry in value to an IP network object. +

Parameters

value – list of IPs and/or networks contained in restapi.allowlist setting. Each item can be a host, an IP, or a network in CIDR format.

Yields

host + port resolved to IP networks. +

static \\__set_fd_cloexec(_fd: socket) → None View on GitHub

Set FD_CLOEXEC for fd. + It is used to avoid inheriting the REST API port when forking its process. + Note

  Only takes effect on non-Windows environments.
  +
  Parameters:::
    *fd* – socket file descriptor.
+
check_access(rh: RestApiHandler) → bool | None View on GitHub

Ensure client has enough privileges to perform a given request. + Write a response back to the client if any issue is observed, and the HTTP status may be: + __\\__

  • 401: if Authorization header is missing or contain an invalid password;

  • 403: if: + __\\__ __\\__ __\\__ +

    Parameters

    rh – the request which access should be checked.

    Returns

    True if client access verification succeeded, otherwise None. +

check_auth_header(auth_header: str | None) → str | None View on GitHub

Validate HTTP Basic authorization header, if present. +

Parameters

auth_header – value of Authorization HTTP header, if present, else None.

Returns

an error message if any issue is found, None otherwise. +

check_basic_auth_key(key: str) → bool View on GitHub

Check if key matches the password configured for the REST API. +

Parameters

key – the password received through the Basic authorization header of an HTTP request.

Returns

True if key matches the password configured for the REST API. +

daemon_threads_ = True_

+

get_certificate_serial_number() → str | None View on GitHub

Get serial number of the certificate used by the REST API. +

Returns

serial number of the certificate configured through restapi.certfile setting. +

handle_error(request: socket | Tuple[ bytes, socket], client_address: Tuple[ str, int]) → None View on GitHub

Handle any exception that is thrown while handling a request to the REST API. + Logs WARNING messages with the client information, and the stack trace of the faced exception. +

Parameters

+

process_request_thread(request: socket | Tuple[ bytes, socket], client_address: Tuple[ str, int]) → None View on GitHub

Process a request to the REST API. + Wrapper for process_request_thread() that additionally: + __\\__ __\\__ +

Parameters

+

query(sql: str, *params: Any) → List[ Tuple[ Any, …​]] View on GitHub

Execute sql query with params and optionally return results. + Note

  Prefer to use own connection to postgres and fallback to `+heartbeat+` when own isn’t available.
  +
  Parameters:::
  Returns:::
    a list of rows that were fetched from the database.
  Raises:::
    `+psycopg.Error+`: if had issues while executing _sql_. link:patroni.exceptions.html#patroni.exceptions.PostgresConnectionException[`+PostgresConnectionException+`]: if had issues while connecting to the database.
+
reload_config(config: Dict[ str, Any]) → None View on GitHub

Reload REST API configuration. +

Parameters

config – dictionary representing values under the restapi configuration section.

Raises

ValueError: if listen key is not present in config. +

reload_local_certificate() → bool | None View on GitHub

Reload the SSL certificate used by the REST API. +

Returns

True if a different certificate has been configured through restapi.certfile\'+ ``+setting,+ ``\'\'None+ otherwise. +

shutdown_request(request: socket | Tuple[ bytes, socket]) → None View on GitHub

Shut down a request to the REST API. + Wrapper for + http.server.HTTPServer.shutdown_request()+ that additionally: + __\\__ __\\__ +

Parameters

request – socket to handle the client request.

patroni.api.check_access(func: Callable[[…​], None]) → Callable[[…​], None] View on GitHub

Check the source ip, authorization header, or client certificates. + Note

The actual logic to check access is implemented through link:#patroni.api.RestApiServer.check_access[`+RestApiServer.check_access()+`].
+
Parameters:;;
  *func* – function to be decorated.
Returns:;;
  a decorator that executes _func_ only if link:#patroni.api.RestApiServer.check_access[`+RestApiServer.check_access()+`] returns `+True+`.
Example:;;
>>> class FooServer:
...   def check_access(self, *args, **kwargs):
...     print(f'In FooServer: {args[0].\\__class\\__.\\__name\\__}')
...     return True
...

+

>>> class Foo:
...   server = FooServer()
...   @check_access
...   def do_PUT_foo(self):
...      print('In do_PUT_foo')

+

>>> f = Foo()
>>> f.do_PUT_foo()
In FooServer: Foo
In do_PUT_foo

© Copyright 2015 Compose, Zalando SE. Revision 9d231aee.

Built with Sphinx using a theme provided by Read the Docs.

Read the Docs v: latest

+ Builds