Skip to content

Commit

Permalink
BRAYNS-654 Add engine endpoints (#1276)
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrien4193 authored Aug 8, 2024
1 parent a010de5 commit b686c7e
Show file tree
Hide file tree
Showing 50 changed files with 1,632 additions and 760 deletions.
36 changes: 5 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,11 @@ Brayns comes with a main application:

## Building

Brayns is developed, maintained and run on Linux-based operating systems, being tested mainly on RHEL and Ubuntu. The following platforms and build environments have been tested:

* Linux: Ubuntu 16.04, Ubuntu 18.04, Ubuntu 20.04, Debian 9, RHEL 7 (Makefile, x64)
TODO

### System dependencies

The following components must be installed on the system where Brayns will be built:

* GCC 12.1 or higher (Requires C++ 20 support)
* CMake 3.15 or higher
* Make or Ninja build systems
* Git
* Package config
* SSL Development files
* Python 3.9 or higher
* Custom OSPRay 2.10.5 (https://github.com/BlueBrain/ospray/tree/v2.10.5)
* zlib

Optionally, to build the core plugins of Brayns, the following components are required.

* HDF5 development files
* Bzip2

Brayns uses further dependencies, but if they are not present on the system, it will download them by itself during build.

* Poco libraries 1.12.4 (https://github.com/pocoproject/poco/tree/poco-1.12.4-release)
* spdlog 1.9.2 (https://github.com/gabime/spdlog/tree/v1.9.2)
* stb (https://github.com/nothings/stb)
* tinyexr (https://github.com/syoyo/tinyexr/tree/v1.0.1)
* libsonata 0.1.22 (https://github.com/BlueBrain/libsonata/tree/v0.1.22)
* MorphIO 3.3.5 (https://github.com/BlueBrain/MorphIO/tree/v3.3.5)
TODO

### Build command

Expand Down Expand Up @@ -70,7 +44,7 @@ The following cmake options (shown with their default value) can be used during

To run the braynsService app, execute the following command (The command assumes braynsService executable is available on the system **PATH**):

$ braynsService --uri 0.0.0.0:5000
$ braynsService --host 0.0.0.0 --port 5000

The ***--uri*** parameter allows to specify an address and a port to bind to. In the example, the service is binding to all available addresses and the port 5000.

Expand All @@ -82,15 +56,15 @@ Use the following command to get more details about command line arguments.

Brayns is available as a docker image at https://hub.docker.com/r/bluebrain/brayns. The image allows to launch the braynsService application.

It is built with every commit merged into the main repository branch (develop), and deployed into docker hub as brayns:latest. Furthermore, when a new release is made, and a new tag created, an additional image is built and deployed with the same tag.
It is built with every commit merged into the main repository branch (master), and deployed into docker hub as brayns:latest. Furthermore, when a new release is made, and a new tag created, an additional image is built and deployed with the same tag.

To get Brayns docker image, you will need to have docker installed. Then execute the following command to download it:

$ docker pull bluebrain/brayns:latest

To run it, simply execute the following command:

$ docker run -ti --rm -p 5000:5000 bluebrain/brayns --uri 0.0.0.0:5000
$ docker run -ti --rm -p 5000:5000 bluebrain/brayns --host 0.0.0.0 --port 5000

Additional parameters, can be specified in a similar fashion as in the **braynsService** application.

Expand Down
9 changes: 4 additions & 5 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,17 @@ pip install -r requirements.txt
pip install -r requirements-dev.txt
```

3. For integration testing, create a `.env` file:
3. Optional (for integration testing), create a `.env` file:

```bash
BRAYNS_HOST=localhost
BRAYNS_PORT=5000
BRAYNS_EXECUTABLE=path/to/braynsService
LD_LIBRARY_PATH=path/to/additional/libs
BRAYNS_SSL=0
```

Note: integration testing can be disable using the pytest --without-integration flag.

4. Create a .vscode folder and create a `launch.json` inside to use to debug tests:
4. Create a .vscode folder and create a `launch.json` file inside to be able to debug tests:

```json
{
Expand All @@ -108,7 +107,7 @@ Note: integration testing can be disable using the pytest --without-integration
}
```

5. In the same folder, create a `settings.json` to configure pytest:
5. In the same folder, create a `settings.json` file to configure pytest:

```json
{
Expand Down
18 changes: 18 additions & 0 deletions python/brayns/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
This package provides an API to interact with Brayns service.
"""

from .api.core.objects import (
Object,
clear_objects,
create_empty_object,
get_all_objects,
get_object,
remove_objects,
update_object,
)
from .api.core.service import (
Endpoint,
Task,
Expand All @@ -49,19 +58,25 @@
JsonRpcSuccessResponse,
)
from .network.websocket import ServiceUnavailable, WebSocketError
from .utils.logger import create_logger
from .version import VERSION

__version__ = VERSION
"""Version tag of brayns Python package (major.minor.patch)."""

__all__ = [
"cancel_task",
"clear_objects",
"connect",
"Connection",
"create_empty_object",
"create_logger",
"Endpoint",
"FutureResponse",
"get_all_objects",
"get_endpoint",
"get_methods",
"get_object",
"get_task_result",
"get_task",
"get_tasks",
Expand All @@ -72,13 +87,16 @@
"JsonRpcRequest",
"JsonRpcResponse",
"JsonRpcSuccessResponse",
"Object",
"remove_objects",
"Request",
"Response",
"ServiceUnavailable",
"stop_service",
"Task",
"TaskInfo",
"TaskOperation",
"update_object",
"Version",
"WebSocketError",
]
72 changes: 72 additions & 0 deletions python/brayns/api/core/objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright (c) 2015-2024 EPFL/Blue Brain Project
# All rights reserved. Do not distribute without permission.
#
# Responsible Author: adrien.fleury@epfl.ch
#
# This file is part of Brayns <https://github.com/BlueBrain/Brayns>
#
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License version 3.0 as published
# by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from dataclasses import dataclass
from typing import Any

from brayns.network.connection import Connection
from brayns.utils.parsing import check_type, get, try_get


@dataclass
class Object:
id: int
type: str
user_data: Any


def parse_object(message: dict[str, Any]) -> Object:
return Object(
id=get(message, "id", int),
type=get(message, "type", str),
user_data=try_get(message, "user_data", Any, None),
)


async def get_all_objects(connection: Connection) -> list[Object]:
result = await connection.get_result("get-all-objects")
check_type(result, dict[str, Any])
objects = get(result, "objects", list[dict[str, Any]])
return [parse_object(item) for item in objects]


async def get_object(connection: Connection, id: int) -> Object:
result = await connection.get_result("get-object", {"id": id})
check_type(result, dict[str, Any])
return parse_object(result)


async def update_object(connection: Connection, id: int, user_data: Any) -> None:
properties = {"user_data": user_data}
await connection.get_result("update-object", {"id": id, "properties": properties})


async def remove_objects(connection: Connection, ids: list[int]) -> None:
await connection.get_result("remove-objects", {"ids": ids})


async def clear_objects(connection: Connection) -> None:
await connection.get_result("clear-objects")


async def create_empty_object(connection: Connection) -> int:
result = await connection.get_result("create-empty-object")
check_type(result, dict[str, Any])
return get(result, "id", int)
22 changes: 14 additions & 8 deletions python/brayns/network/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import asyncio
from logging import Logger
from logging import WARNING, Logger
from ssl import SSLContext
from typing import Any, NamedTuple, Self

from brayns.utils.logger import create_logger

from .json_rpc import (
JsonRpcError,
JsonRpcErrorResponse,
Expand Down Expand Up @@ -110,6 +112,9 @@ def done(self) -> bool:
return self._buffer.is_done(self._request_id)

async def poll(self) -> None:
if self._request_id is None:
raise ValueError("Cannot poll requests without ID")

if self.done:
return

Expand Down Expand Up @@ -203,7 +208,7 @@ async def connect(
logger: Logger | None = None,
) -> Connection:
if logger is None:
logger = Logger("Brayns")
logger = create_logger(WARNING)

protocol = "ws" if ssl is None else "wss"
url = f"{protocol}://{host}:{port}"
Expand All @@ -214,15 +219,16 @@ async def connect(
try:
logger.info("Connection attempt %d", attempt)
websocket = await connect_websocket(url, ssl, max_frame_size, logger)
break
logger.info("Connection suceeded")

return Connection(websocket)
except ServiceUnavailable as e:
logger.warning("Connection attempt failed: %s", e)
logger.warning("Connection attempt %d failed: %s", attempt, e)

attempt += 1

if max_attempts is not None and attempt >= max_attempts:
logger.warning("Max connection attempts reached, aborted")
raise

await asyncio.sleep(sleep_between_attempts)

logger.info("Connection suceeded")

return Connection(websocket)
2 changes: 1 addition & 1 deletion python/brayns/network/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ async def connect_websocket(url: str, ssl: SSLContext | None, max_frame_size: in
logger.warning("Connection failed: %s", e)
raise WebSocketError(str(e))
except OSError as e:
logger.warning("Service not found (probably not ready): %s", e)
logger.warning("Service not found (maybe not ready): %s", e)
raise ServiceUnavailable(str(e))

wrapper = _WebSocket(websocket, max_frame_size, logger)
Expand Down
36 changes: 36 additions & 0 deletions python/brayns/utils/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (c) 2015-2024 EPFL/Blue Brain Project
# All rights reserved. Do not distribute without permission.
#
# Responsible Author: adrien.fleury@epfl.ch
#
# This file is part of Brayns <https://github.com/BlueBrain/Brayns>
#
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License version 3.0 as published
# by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from logging import INFO, Formatter, Logger, StreamHandler
import sys


def create_logger(level: int | str = INFO) -> Logger:
logger = Logger("Brayns", level)

format = "[%(asctime)s][%(name)s][%(levelname)s] %(message)s"
formatter = Formatter(format)

handler = StreamHandler(sys.stdout)
handler.setFormatter(formatter)

logger.addHandler(handler)

return logger
33 changes: 33 additions & 0 deletions python/tests/data/certificate.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFoTCCA4mgAwIBAgIUEIpfZpNVx+nIciUxF3YET/9EzUAwDQYJKoZIhvcNAQEL
BQAwYDELMAkGA1UEBhMCU1cxDzANBgNVBAgMBkdlbmV2YTEPMA0GA1UEBwwGR2Vu
ZXZhMQ0wCwYDVQQKDARFUEZMMQwwCgYDVQQLDANCQlAxEjAQBgNVBAMMCWxvY2Fs
aG9zdDAeFw0yNDA4MDIwODI0MTJaFw0zNDA3MzEwODI0MTJaMGAxCzAJBgNVBAYT
AlNXMQ8wDQYDVQQIDAZHZW5ldmExDzANBgNVBAcMBkdlbmV2YTENMAsGA1UECgwE
RVBGTDEMMAoGA1UECwwDQkJQMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCOr1EI4RFp4Kmy32oKojKHnQ6uQCXA3kE+
3jMYlRSKtXfDSkmxZ7bct5w9L9LX2UrFStA7RpJ9tk9QIANdHMQ+ydA/Le3+nhxP
QblYmvZfHegYAFiWtxLasHjObNslAOYBhDmEGEgmXNrr3dGiNciUYdU9J1rU5Nqi
3MiwAl5wke9akUAwYA/AYb+044eNE8QU8lQJCBa7Y/oqhr4dE4vsxWbwrH76saIY
c2otztXPCdpLt8OZT6Wo7R4F0GfYOotirT9a3W1xwWSKQpOjxpWzljSjvEQnJJ/e
yaaPnM2ST3TxV7AGQdDAjJWPa66yxO5xYMZU6fdSzJmH2+kKBpxX1p9POfHRHhGZ
ua52gWuz7ylLtSOCGmyLvbmZugjn1DWsvciaDSczNbSeFGf7bUgBcjC4tl2M6BXq
lc9YzcNDn/yKLBzNF4BeMx1Z631nNhJiRu9dJTdMIV/gng+ZdVxLLz0hllCSNT6O
QSmQINFI/kGTOctDW62Obf/NeNMoat6kQG/x7VijVwPRiL5ryb2eiFF+jx7x7MtK
U3+tGYCtpwhHHR+QGgxkP8w6t0Wn70JGihClz7LUwcdLy6lrqUGUF4ZO9NtuBq6m
Zx4M56ijGp6Gnwh0EMaVLIpj37+3V+7CoiZ9wX7LjvbBODYM5I9c2gmA8/gImVxc
3m8UO24wXQIDAQABo1MwUTAdBgNVHQ4EFgQUMSLGq+5A3l4EHtyqpImM4LrYIpkw
HwYDVR0jBBgwFoAUMSLGq+5A3l4EHtyqpImM4LrYIpkwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAgEAcKm72f8HUmCN2cCsZnkbmP/qRWBsOW/P6x/P
udlwgcgmK5yg+GgkvBctTq221eiRQ4ASCYy6+2mbMDdBFQ8N4p7jF4yh682WNyTl
yayxfm4J9rphLlGwnHVvp8E+HuAfa876lYu7zrAeE6dYQoLInd1gBqY7683UBHqk
hJtGJymZYLolAbfgfIPTh8pZ97UXYSJv+K1R1AIItO1fEUl5WLCUx2GNdu/1eieq
9rDExehKhRtB8wCYKVyRkXJUshp3MCshBM1VRm0CGdD3ta49or6k7uJcHjo/K6yQ
nT5PfpbHNvF3A03IDLF1DQTTkuRMZIX+SZ4TJCoC5QH9bo4uyfKhEVmxmVYcaqMs
COvLo0xmoHw4fxJ4zXrJa65thl47ZXALVA7TKWeqsh73j7cwWgwtLNpZsfSEh4Z5
3qQAmvAzOH65pFNDtNS7DhJDh9c6pKk9ueZGwL4yVcAarUdEjG54gRQvxPy6YhWo
CkV2hWtkDPBnDxf2dziOa78NeB0oEPBw+tBt2P/jejdWB774wp7qgjgOIL1NKvGk
7/bN8TG8o/msicYenPCkBRv2K4/Klfh8wzCq2t8ailvTYM8k3R+/Nkz4LF/pesg/
QlkZHRkia7T4rJa8WPg+GzE87LsDhs8qbV8EK8X363TZyPwMfGAZWyDN+AiXud10
osxjP8g=
-----END CERTIFICATE-----
Loading

0 comments on commit b686c7e

Please sign in to comment.