Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LLT-5831] QNAP: move cgi logic to teliod #1012

Merged
merged 6 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .unreleased/LLT-5831
Empty file.
52 changes: 45 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ci/build_libtelio.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ def post_qnap_build_wrap_binary_on_qpkg(config, args):
"packages": {
"teliod": {"teliod": "teliod"},
},
"build_args": ("--features", "qnap"),
},
"macos": {
"packages": {
Expand Down
6 changes: 6 additions & 0 deletions clis/teliod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ anyhow.workspace = true
smart-default = "0.7.1"
base64 = "0.22.1"
dirs = "4.0.0"
const_format = { version = "0.2.33", optional = true }
rust-cgi = { version = "0.7.1", optional = true }

[dev-dependencies]
rand = "0.8.5"
serial_test = "3.2.0"

[features]
qnap = ["const_format", "rust-cgi"]
128 changes: 128 additions & 0 deletions clis/teliod/QNAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# QNAP Build

To build Teliod package for QNAP devices there are two alternatives:

- Run cargo build directly:
```cargo build --verbose --target x86_64-unknown-linux-musl --package teliod --features qnap```

- Run build script [Recommended]:
```../../ci/build_libtelio.py build qnap x86_64 [--debug]```

The build script has an additional stage at the end of the build where it creates the QNAP package.

## REST API

This REST API allows interaction with the Teliod daemon. It provides endpoints for managing the daemon, updating its configuration, and retrieving logs and status information.

### Endpoints

#### 1. **Start the Daemon**
- **Endpoint**: `/`
- **Method**: `POST`
- **Description**: Starts the Teliod Daemon in the background.
- **Request Body**: None
- **Responses**:
- **201 OK**: Daemon started successfully.
- **400 Bad Request**: Daemon is already running.
- **500 Internal Server Error**: Failed to start the daemon.

#### 2. **Stop the Daemon**
- **Endpoint**: `/`
- **Method**: `DELETE`
- **Description**: Stops the running Teliod Daemon.
- **Request Body**: None
- **Responses**:
- **200 OK**: Daemon stopped successfully.
- **410 Bad Request**: Daemon is not running.

#### 3. **Update Configuration**
- **Endpoint**: `/`
- **Method**: `PATCH`
- **Description**: Updates the daemon configuration with provided settings.
- **Request Body**: JSON object containing the configuration updates. Only specified fields will be updated; others remain unchanged.
- **Example Request Body**:
```json
{
"log_level": "info",
"log_file_path": "/new/path/to/log.log"
}
```
- **Responses**:
- **200 OK**: Configuration updated successfully
- **400 Bad Request**: Invalid JSON payload or configuration fields.
- **500 Internal Server Error**: Failed to update configuration.

#### 4. **Get Meshnet Status**
- **Endpoint**: `/?info=get-status`
- **Method**: `GET`
- **Description**: Retrieves the current status of the Meshnet from Teliod daemon.
- **Request Body**: None
- **Responses**:
- **200 OK**: Status information in JSON format.
```json
{
"telio_is_running": true,
"meshnet_ip": null,
"external_nodes": []
...
}
```
- **502 Internal Server Error**: Failed to retrieve status (Bad daemon response).
- **410 Gone**: Failed to communicate with the daemon (Couldn't send command/Daemon not accessible).
- **502 Gateway Timeout**: Failed to communicate with the daemon (Timeout while waiting daemon).

#### 5. **Get Meshnet Logs**
- **Endpoint**: `/?info=get-meshnet-logs`
- **Method**: `GET`
- **Description**: Retrieves the latest logs of the Meshnet.
- **Request Body**: None
- **Responses**:
- **200 OK**: Log content in text format.
```
{
"Log line 1\nLog line 2\nLog line 3\n..."
}
```
- **502 Bad Gateway**: Error reading log file.

#### 6. **Get Teliod Logs**
- **Endpoint**: `/?info=get-teliod-logs`
- **Method**: `GET`
- **Description**: Retrieves the latest logs of the Teliod Daemon.
- **Request Body**: None
- **Responses**:
- **200 OK**: Log content in text format.
```
{
"Log line 1\nLog line 2\nLog line 3\n..."
}
```
- **502 Bad Gateway**: Error reading log file.

### Error Handling

For all endpoints, the following error codes may be returned:
- **400 Bad Request**: The request was malformed or invalid.
- **404 Not Found**: Uri path is invalid.

### Example usage with curl

#### Start Teliod daemon:
```bash
curl -X POST http://<NAS-IP>:8080/
```

#### Stop Teliod daemon:
```bash
curl -X DELETE http://<NAS-IP>:8080/
```

#### Get Meshnet logs:
```bash
curl -X GET "http://<NAS-IP>:8080/?info=get-meshnet-logs"
```

#### Update Config:
```bash
curl -X PATCH -H "Content-Type: application/json" -d '{"log_level":"info", authentication_token": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}' http://<NAS-IP>:8080/cgi-bin/qpkg/teliod.cgi
```
14 changes: 13 additions & 1 deletion clis/teliod/src/command_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ impl CommandListener {
})
.await
}
ClientCmd::QuitDaemon =>
{
#[allow(mpsc_blocking_send)]
self.telio_task_tx
.send(TelioTaskCmd::Quit)
.await
.map(|_| CommandResponse::Ok)
.map_err(|e| {
error!("Error sending command: {}", e);
TeliodError::CommandFailed(ClientCmd::QuitDaemon)
})
}
ClientCmd::IsAlive => Ok(CommandResponse::Ok),
}
}

Expand All @@ -88,7 +101,6 @@ impl CommandListener {
connection.respond(response.serialize()).await?;
Ok(command)
} else {
error!("Received invalid command from client: {}", command_str);
connection
.respond(
CommandResponse::Err(format!("Invalid command: {}", command_str)).serialize(),
Expand Down
Loading
Loading