Skip to main content

Python bindings for the Genja runtime

Project description

genja-py

Python bindings for the Genja runtime.

This package exposes the genja module, which wraps the Rust runtime and lets Python code:

  • build a runtime from hosts, a full inventory, or a settings file
  • run Python-authored tasks
  • register Python plugins
  • inspect raw and transformed inventory data

Installation

For end users, install the package with pip:

pip install genja-py

The package currently exposes the genja Python module:

import genja

Quick Start

Create a runtime from a simple host mapping:

import genja
from genja.task import Host, TaskInfo, TaskRuntimeContext, TaskSuccessResult, task


@task(name="backup_config")
class BackupTask:
    def start(
        self,
        task: TaskInfo,
        host: Host,
        context: TaskRuntimeContext,
    ) -> TaskSuccessResult:
        connection = context.connection()
        command_output = None
        if connection is not None:
            command_output = connection.execute_command("show running-config")

        return TaskSuccessResult(
            summary=f"backed up {host.hostname}",
            metadata={"show_running_config": command_output},
        )


genja = genja.Genja.from_hosts(
    {
        "router1": {"hostname": "10.0.0.1", "platform": "ios"},
        "router2": {"hostname": "10.0.0.2", "platform": "nxos"},
    }
).with_runner("serial")

results = genja.run_task(BackupTask)
print(results.to_dict())

tasks = genja.Tasks()
tasks.add_task(BackupTask)

all_results = genja.run_tasks(tasks)
print([result.task_name for result in all_results])

results.to_dict() returns per-host task results with an outcome payload and separate execution_metadata for host timing and retry attempt information.

TaskRuntimeContext exposes the 1-based retry attempt through context.current_attempt and the resolved connection through context.connection() and context.has_connection(). Execution depth remains internal to the runtime.

For async Python applications, use the async entrypoints:

import asyncio
import genja
from genja.task import Host, TaskInfo, TaskRuntimeContext, TaskSuccessResult, task


@task(name="backup_config_async")
class BackupTaskAsync:
    async def start_async(
        self,
        task: TaskInfo,
        host: Host,
        context: TaskRuntimeContext,
    ) -> TaskSuccessResult:
        connection = context.connection()
        command_output = None
        if connection is not None:
            command_output = await connection.execute_command("show running-config")

        return TaskSuccessResult(
            summary=f"backed up {host.hostname}",
            metadata={"show_running_config": command_output},
        )


async def main() -> None:
    runtime = genja.Genja.from_hosts(
        {
            "router1": {"hostname": "10.0.0.1", "platform": "ios"},
        }
    ).with_runner("serial")

    results = await runtime.run_task_async(BackupTaskAsync)
    print(results.to_dict())


asyncio.run(main())

Use run_task_async(...) and run_tasks_async(...) when composing Genja with asyncio.gather(...) or other async application code. The synchronous run_task(...) and run_tasks(...) entrypoints remain available for scripts and non-async callers.

Python task authoring rules:

  • Define def start(...) for blocking tasks.
  • Define async def start_async(...) for async tasks.
  • Define exactly one of those methods on a @task(...) class.
  • Use sub_tasks=[ChildTask, ...] to declare child tasks.

Logging

Rust-side runtime logs emitted by Genja are forwarded into Python's standard logging system when the extension module is imported. Configure Python logging handlers and levels before running Genja tasks:

import logging

import genja

logging.basicConfig(level=logging.INFO)

runtime = genja.Genja.from_hosts({
    "router1": {"hostname": "10.0.0.1", "platform": "ios"},
}).with_runner("serial")

Tests can capture those records with pytest's caplog fixture.

Full Inventory

Use genja.inventory when you need groups and defaults:

import genja
from genja.inventory import Defaults, Group, Host, Inventory

inventory = Inventory(
    hosts={
        "router1": Host(hostname="10.0.0.1", groups=["core"]),
    },
    groups={
        "core": Group(platform="ios", data={"role": "core"}),
    },
    defaults=Defaults(username="admin", port=22),
)

genja = genja.Genja.from_inventory(inventory)

print(genja.inventory_full())
print(genja.inventory_raw())

Inventory Accessors

The runtime exposes three inventory views:

  • genja.inventory(): raw hosts only
  • genja.inventory_full(): transformed hosts, groups, and defaults
  • genja.inventory_raw(): raw hosts, groups, and defaults

Plugins

You can register Python plugins directly:

import genja


class MyProcessorPlugin:
    name = "audit"
    group = "ProcessorPlugin"

    def on_task_finish(self, context, results):
        return None


plugins = genja.PluginManager()
plugins.register_plugin(MyProcessorPlugin())

Rust plugins can be loaded from a directory:

plugins = genja.PluginManager()
plugins.load_rust_plugins_from_directory("./plugins")

Settings Files

From A Settings File

Build a runtime from a settings file:

import genja

genja = genja.Genja.from_settings_file("config.yaml")

Settings files are strict: unknown fields fail loading instead of being ignored. Use explicit option maps such as runner.options for plugin-specific values.

From Programmatic Settings

Settings can also be built directly in Python. Use from_settings(...) when inventory should be loaded from settings.inventory:

import genja

settings = genja.Settings(
    inventory=genja.InventoryConfig(
        options=genja.OptionsConfig(
            hosts_file="./inventory/hosts.yaml",
        ),
    ),
    runner=genja.RunnerConfig(
        plugin="serial",
        retry=genja.RunnerRetryConfig(
            allow=True,
            max_attempts=3,
            delay_ms=250,
        ),
    ),
)

genja = genja.Genja.from_settings(settings)

Programmatic construction itself does not read files, but runtime creation validates supplied settings before building the runtime. To validate explicitly, call settings.validate() or settings.ssh.validate().

With Explicit Inventory

When host data is supplied directly, from_hosts(...) and from_inventory(...) continue to use that explicit inventory instead of loading from settings.inventory.

With Python Plugins

If you need Python plugins during settings-file loading, provide a plugin manager:

plugins = genja.PluginManager()
genja = genja.Genja.from_settings_file("config.yaml", plugin_manager=plugins)

The same plugin_manager argument is available on Genja.from_settings(...) for Python-authored inventory plugins.

Development

The commands below assume a repository checkout and use PDM-managed tooling.

Clone the repository and move into the Python package directory:

git clone git@github.com:Smertan/genja.git
cd genja/genja-core-python

Install the development dependencies:

pdm install -d

Build and install the Rust extension into the project virtual environment:

pdm run maturin develop

Run the Rust-side binding tests:

pdm run test-rust

Use pdm run test-rust instead of plain cargo test. The Rust tests embed Python and need access to the PDM-managed virtualenv packages such as pydantic.

Run the Python test suite:

pdm run test

Run Ruff:

pdm run lint

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

genja_py-0.3.0.tar.gz (351.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

genja_py-0.3.0-cp314-cp314-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.14Windows x86-64

genja_py-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

genja_py-0.3.0-cp314-cp314-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

genja_py-0.3.0-cp313-cp313-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.13Windows x86-64

genja_py-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

genja_py-0.3.0-cp313-cp313-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

genja_py-0.3.0-cp312-cp312-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.12Windows x86-64

genja_py-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

genja_py-0.3.0-cp312-cp312-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

genja_py-0.3.0-cp311-cp311-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.11Windows x86-64

genja_py-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

genja_py-0.3.0-cp311-cp311-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

genja_py-0.3.0-cp310-cp310-win_amd64.whl (2.5 MB view details)

Uploaded CPython 3.10Windows x86-64

genja_py-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

genja_py-0.3.0-cp310-cp310-macosx_11_0_arm64.whl (2.6 MB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file genja_py-0.3.0.tar.gz.

File metadata

  • Download URL: genja_py-0.3.0.tar.gz
  • Upload date:
  • Size: 351.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0.tar.gz
Algorithm Hash digest
SHA256 80922060bd4688b38348c22273465fc7d238afa27835908058ab2ab6482daf52
MD5 645c346544407d56d53db9186a348b13
BLAKE2b-256 78cc199612c91ffb241bb8e008ac1b190f6f8759e3453fb471623ede8db8eb56

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0.tar.gz:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp314-cp314-win_amd64.whl.

File metadata

  • Download URL: genja_py-0.3.0-cp314-cp314-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: CPython 3.14, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 0ba481dd192abec187f3a3f8993523fa13e3a8973c56692d4b6ab75f4412cdfd
MD5 7e88a8d752de3b6a3145aa05f0a205a0
BLAKE2b-256 04a87075f6c550f28fc75d750b799dbc27fe6d0e43163220f1599cba85c9d5cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp314-cp314-win_amd64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 59547b7446207e78a5da79911ae95ca49e681b0a9ace8744966341c02fbdb2a6
MD5 2fc61eff04da87f7a1bc828d93e10cdb
BLAKE2b-256 c077c0189c11c0a5ef8b5573dc44003298f7bce5deda9a40e8b9420877b3d8fc

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 fd5b251192305c52925ccec5770e0b874aaa6bb32ffd1a60bf4de6eb4db61b94
MD5 f5d4547719d7fcf901599ab26be5bba0
BLAKE2b-256 6f97d81390acd7a2f44ed1f7ccf6f28314092d599facfb6e4df88436c24f49c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp314-cp314-macosx_11_0_arm64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: genja_py-0.3.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 712119ce917e91f569639bee168cd23f996844533e9c45bba82250324f2c73ac
MD5 2cf000cdc755d2be3a95081dd8f89461
BLAKE2b-256 f1253dcb3d1c8d3ec86b3818710be608f382f3ce802a245df07013939441c55f

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp313-cp313-win_amd64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cd0901a6b1d832b9c3bcdfcee57770781beaedfd6df927caa0a8df6cf4427d9e
MD5 056e39157478ec5e75fc175994ce5746
BLAKE2b-256 d87e3564b14a3b63eba83e4e0b28593475c6fcdfbc10755f77a4418c2e068655

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1631bcb9d569ae70f6c11fe1d5560c68ea5de3888c3c20c358fd922ec9188180
MD5 0296013ee1fdab77710ddb48482943bc
BLAKE2b-256 774e39a3a31f98c8647684ce038c9a38739ce19090679e7e61d09be8a46bc406

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: genja_py-0.3.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 4e8f65dff6d0c22f2988da10863b2d2ddd0f17f52691393246e2b92c0013f2ce
MD5 2beb081872832b201ae2180d79aa4741
BLAKE2b-256 b0f26c82e3742b6192edd58c0d4fac975bd002d7a57c67510e48ce6fe590bf0b

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp312-cp312-win_amd64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2b111a5e8a9e10cf2ec23e38bc249576456d8e46b8671906a00932c13088f969
MD5 b725c9bafdd2add4cd7b4214fcc64009
BLAKE2b-256 153afe14116217cc5d4bfbd6c3cc1bcd356c2856f0c96e44bf41c380260d8470

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a738206d533002cb86e2f9232039d8a7ef37929e2629e347f63091fad08ef411
MD5 35c7edaad66e6ddbd353ea535df1c33b
BLAKE2b-256 27dd4ae6e4143fd13a5a05c5793a04c07ec9676197c6b43778173127879ef324

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp311-cp311-win_amd64.whl.

File metadata

  • Download URL: genja_py-0.3.0-cp311-cp311-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: CPython 3.11, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 b9e546267bc493aa07ed9d701c35cfd7d8e2eb30768bfc4b6a4ed8c51539955f
MD5 353b43524fb24fad1d3398d7f5d5033b
BLAKE2b-256 b48ea8f4b2a4647a95ae5b7ff0591be12cd6fd3ea33257fac7b5a497439f61dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp311-cp311-win_amd64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 541e56e76212fd2b4b55c0d29755924e963b6c1122e22c07d3057ec1b1a48092
MD5 0eeee6ab2ae262e4682d3502ffddfc0a
BLAKE2b-256 1e8986dc9b4a7073ddd3bcdc211b1c147a2647beed6371979fce87193265ebc6

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 772db747f9c223d2c51f9e09f504f48cd66f2d898e7d4b8d7cdf107459658636
MD5 3261af21a1088309ad5bef3f2de620e5
BLAKE2b-256 55bb73ee3141d91a6bc9fb55585ea9af2754f596e69ff2bb8dc37deaefefe937

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: genja_py-0.3.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for genja_py-0.3.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 92e4b7682898f3c6263f99381e053e8cd96ce6b136beaf5c3deeb7d834ea5784
MD5 895c4ea021c211d7842644f02c82744d
BLAKE2b-256 002a12dae039e079b9761518266e6297920c2aba96e99439216f713482aaf0e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp310-cp310-win_amd64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 fed551ebaf0eff756a5dacd3a1de24b3d1971f15cd7314f99388f946da7d80f7
MD5 a09d42d94573da15b8a06dbb6753854f
BLAKE2b-256 5e9da827f4daec4a12fc766d5812365de0a8276ed90e9bc0a1679c03236fa270

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file genja_py-0.3.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for genja_py-0.3.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a36d951ee2c095bdf1bce9b0a040fd99523ba6ebf7c48f432ea55c276ad0464c
MD5 c2d4d9da68a64fabbd356875bba16415
BLAKE2b-256 15b61dbd1ff43a3273c679f67e78d846faff3156c032bd92213532b3e9379ea6

See more details on using hashes here.

Provenance

The following attestation bundles were made for genja_py-0.3.0-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: publish-python.yml on Smertan/genja

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page