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

Add client/ site for client statistics #19

Merged
merged 1 commit into from
Sep 24, 2023
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
87 changes: 83 additions & 4 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ env_logger = "0.10"
gethostname = "0.4.3"
log = "0.4.20"
rustic_core = "0.1"
sailfish = "0.8"
serde = "1"
serde_derive = "1.0.188"
serde_json = "1"
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ It allows to define client groups which are all backed up the same way.
the config.
- On each client, run `rustic-scheduler-client <ADDR>`, where `<ADDR>` is the
websocket address to connect, e.g.
`rustic-scheduler-client http://server.localdomain:3012/ws`.
`rustic-scheduler-client ws://server.localdomain:3012/ws`.
- Backups on your clients are automatically started based on the configured
schedule(s).
- Statistics for a specific clients are available under `/client/%client`, e.g.
`http://server.localdomain:3012/client/my_server1`

## Are binaries available?

Expand Down
53 changes: 41 additions & 12 deletions src/bin/rustic-scheduler-server.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
use std::{collections::HashMap, fs::read_to_string, time::Duration};

use anyhow::Result;
use axum::{
extract::{
ws::{WebSocket, WebSocketUpgrade},
State,
Path, State,
},
response::Response,
response::{Html, Response},
routing::get,
Router,
};
use chrono::Local;
use log::warn;
use sailfish::TemplateOnce;
use tokio::{
spawn,
sync::mpsc::{self, Sender},
sync::{mpsc, oneshot},
time::sleep,
};

use rustic_scheduler::config::{AllBackupOptions, ConfigFile};
use rustic_scheduler::message::{BackupMessage, BackupResultMessage, HandshakeMessage};
use rustic_scheduler::scheduler::{Client, Clients, Source, SourceBackupStatus};
use rustic_scheduler::scheduler::{Client, ClientStats, Clients, Source, SourceBackupStatus};

enum ClientMessage {
Backup { client: String, msg: BackupMessage },
Expand All @@ -28,7 +30,7 @@ enum ClientMessage {
enum NotifyMessage {
Connect {
client: String,
channel: Sender<ClientMessage>,
channel: mpsc::Sender<ClientMessage>,
},
Disconnect {
client: String,
Expand All @@ -37,6 +39,10 @@ enum NotifyMessage {
client: String,
msg: BackupResultMessage,
},
StatsRequest {
client: String,
channel: oneshot::Sender<Result<ClientStats>>,
},
}

#[tokio::main]
Expand Down Expand Up @@ -69,7 +75,7 @@ async fn main() {

// The backup loop handling the schedules
spawn(async move {
let mut client_channels: HashMap<String, Sender<ClientMessage>> = HashMap::new();
let mut client_channels: HashMap<String, mpsc::Sender<ClientMessage>> = HashMap::new();
let sleep_timer = sleep(Duration::ZERO);
tokio::pin!(sleep_timer);

Expand Down Expand Up @@ -116,6 +122,9 @@ async fn main() {
client_channels.remove(&client);
clients.disconnect_client(client);
}
NotifyMessage::StatsRequest{client, channel} => {
channel.send(clients.client_stats(client)).unwrap();
}
}
}
}
Expand All @@ -131,9 +140,9 @@ async fn main() {

// build our application with a single route
let app = Router::new()
.route("/ws", get(handler))
.with_state(wtx)
.route("/", get(|| async { "Hello, World!" }));
.route("/ws", get(ws_handler))
.route("/client/:client", get(client_handler))
.with_state(wtx);

// run it with hyper on localhost:3012
axum::Server::bind(&config.global.address.parse().unwrap())
Expand All @@ -142,11 +151,31 @@ async fn main() {
.unwrap();
}

async fn handler(ws: WebSocketUpgrade, State(state): State<Sender<NotifyMessage>>) -> Response {
ws.on_upgrade(|socket| handle_socket(socket, state))
async fn client_handler(
Path(client): Path<String>,
State(wtx): State<mpsc::Sender<NotifyMessage>>,
) -> Html<String> {
let (tx, wrx) = oneshot::channel();

wtx.send(NotifyMessage::StatsRequest {
client,
channel: tx,
})
.await
.unwrap();

let stats = wrx.await.unwrap().unwrap();
Html(stats.render_once().unwrap())
}

async fn ws_handler(
ws: WebSocketUpgrade,
State(wtx): State<mpsc::Sender<NotifyMessage>>,
) -> Response {
ws.on_upgrade(|socket| handle_socket(socket, wtx))
}

async fn handle_socket(mut socket: WebSocket, wtx: Sender<NotifyMessage>) {
async fn handle_socket(mut socket: WebSocket, wtx: mpsc::Sender<NotifyMessage>) {
let (tx, mut wrx) = mpsc::channel(1);

// handshake
Expand Down
Loading
Loading