Support monitoring multiple validators
This commit is contained in:
parent
36fa3a1a0a
commit
ae8badb141
|
@ -6,12 +6,13 @@ use crate::notifier::Notifier;
|
||||||
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg};
|
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg};
|
||||||
use log::*;
|
use log::*;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_parsers::pubkey_of,
|
input_parsers::pubkeys_of,
|
||||||
input_validators::{is_pubkey_or_keypair, is_url},
|
input_validators::{is_pubkey_or_keypair, is_url},
|
||||||
};
|
};
|
||||||
use solana_cli_config::{Config, CONFIG_FILE};
|
use solana_cli_config::{Config, CONFIG_FILE};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_metrics::{datapoint_error, datapoint_info};
|
use solana_metrics::{datapoint_error, datapoint_info};
|
||||||
|
use solana_sdk::native_token::lamports_to_sol;
|
||||||
use std::{error, io, thread::sleep, time::Duration};
|
use std::{error, io, thread::sleep, time::Duration};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn error::Error>> {
|
fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
|
@ -49,11 +50,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
.help("Wait interval seconds between checking the cluster"),
|
.help("Wait interval seconds between checking the cluster"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("validator_identity")
|
Arg::with_name("validator_identitys")
|
||||||
.long("validator-identity")
|
.long("validator-identity")
|
||||||
.value_name("VALIDATOR IDENTITY PUBKEY")
|
.value_name("VALIDATOR IDENTITY PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.multiple(true)
|
||||||
.help("Monitor a specific validator only instead of the entire cluster"),
|
.help("Monitor a specific validator only instead of the entire cluster"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -73,13 +75,22 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
|
let interval = Duration::from_secs(value_t_or_exit!(matches, "interval", u64));
|
||||||
let json_rpc_url =
|
let json_rpc_url =
|
||||||
value_t!(matches, "json_rpc_url", String).unwrap_or_else(|_| config.json_rpc_url);
|
value_t!(matches, "json_rpc_url", String).unwrap_or_else(|_| config.json_rpc_url);
|
||||||
let validator_identity = pubkey_of(&matches, "validator_identity").map(|i| i.to_string());
|
let validator_identity_pubkeys: Vec<_> = pubkeys_of(&matches, "validator_identitys")
|
||||||
|
.unwrap_or_else(|| vec![])
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| i.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let no_duplicate_notifications = matches.is_present("no_duplicate_notifications");
|
let no_duplicate_notifications = matches.is_present("no_duplicate_notifications");
|
||||||
|
|
||||||
solana_logger::setup_with_default("solana=info");
|
solana_logger::setup_with_default("solana=info");
|
||||||
solana_metrics::set_panic_hook("watchtower");
|
solana_metrics::set_panic_hook("watchtower");
|
||||||
|
|
||||||
info!("RPC URL: {}", json_rpc_url);
|
info!("RPC URL: {}", json_rpc_url);
|
||||||
|
if !validator_identity_pubkeys.is_empty() {
|
||||||
|
info!("Monitored validators: {:?}", validator_identity_pubkeys);
|
||||||
|
}
|
||||||
|
|
||||||
let rpc_client = RpcClient::new(json_rpc_url);
|
let rpc_client = RpcClient::new(json_rpc_url);
|
||||||
|
|
||||||
let notifier = Notifier::new();
|
let notifier = Notifier::new();
|
||||||
|
@ -137,48 +148,63 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
&& rpc_client
|
&& rpc_client
|
||||||
.get_vote_accounts()
|
.get_vote_accounts()
|
||||||
.and_then(|vote_accounts| {
|
.and_then(|vote_accounts| {
|
||||||
|
|
||||||
|
let total_current_stake = vote_accounts
|
||||||
|
.current
|
||||||
|
.iter()
|
||||||
|
.fold(0, |acc, vote_account| acc + vote_account.activated_stake);
|
||||||
|
let total_delinquent_stake = vote_accounts
|
||||||
|
.delinquent
|
||||||
|
.iter()
|
||||||
|
.fold(0, |acc, vote_account| acc + vote_account.activated_stake);
|
||||||
|
|
||||||
|
let total_stake = total_current_stake + total_delinquent_stake;
|
||||||
|
let current_stake_percent = total_current_stake * 100 / total_stake;
|
||||||
|
info!(
|
||||||
|
"Current stake: {}% | Total stake: {} SOL, current stake: {} SOL, delinquent: {} SOL",
|
||||||
|
current_stake_percent,
|
||||||
|
lamports_to_sol(total_stake),
|
||||||
|
lamports_to_sol(total_current_stake),
|
||||||
|
lamports_to_sol(total_delinquent_stake)
|
||||||
|
);
|
||||||
|
|
||||||
info!("Current validator count: {}", vote_accounts.current.len());
|
info!("Current validator count: {}", vote_accounts.current.len());
|
||||||
info!(
|
info!(
|
||||||
"Delinquent validator count: {}",
|
"Delinquent validator count: {}",
|
||||||
vote_accounts.delinquent.len()
|
vote_accounts.delinquent.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
match validator_identity.as_ref() {
|
if validator_identity_pubkeys.is_empty() {
|
||||||
Some(validator_identity) => {
|
|
||||||
if vote_accounts
|
|
||||||
.current
|
|
||||||
.iter()
|
|
||||||
.any(|vai| vai.node_pubkey == *validator_identity)
|
|
||||||
{
|
|
||||||
Ok(true)
|
|
||||||
} else if vote_accounts
|
|
||||||
.delinquent
|
|
||||||
.iter()
|
|
||||||
.any(|vai| vai.node_pubkey == *validator_identity)
|
|
||||||
{
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("Validator {} is delinquent", validator_identity),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
format!("Validator {} is missing", validator_identity),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if vote_accounts.delinquent.is_empty() {
|
if vote_accounts.delinquent.is_empty() {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!("{} delinquent validators", vote_accounts.delinquent.len()),
|
||||||
"{} delinquent validators",
|
|
||||||
vote_accounts.delinquent.len()
|
|
||||||
),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let mut errors = vec![];
|
||||||
|
for validator_identity in validator_identity_pubkeys.iter() {
|
||||||
|
if vote_accounts
|
||||||
|
.delinquent
|
||||||
|
.iter()
|
||||||
|
.any(|vai| vai.node_pubkey == *validator_identity)
|
||||||
|
{
|
||||||
|
errors.push(format!("{} delinquent", validator_identity));
|
||||||
|
} else if !vote_accounts
|
||||||
|
.current
|
||||||
|
.iter()
|
||||||
|
.any(|vai| vai.node_pubkey == *validator_identity)
|
||||||
|
{
|
||||||
|
errors.push(format!("{} missing", validator_identity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.is_empty() {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, errors.join(",")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue