Add |solana-validator monitor| subcommand (#15118)

This commit is contained in:
Michael Vines
2021-02-05 22:39:23 -08:00
committed by GitHub
parent 819d829c41
commit f34b8643c7
6 changed files with 375 additions and 175 deletions

View File

@@ -1,8 +1,6 @@
use {
clap::{value_t, value_t_or_exit, App, Arg},
console::style,
fd_lock::FdLock,
indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle},
solana_clap_utils::{
input_parsers::{pubkey_of, pubkeys_of},
input_validators::{
@@ -10,20 +8,21 @@ use {
normalize_to_url_if_moniker,
},
},
solana_client::{client_error, rpc_client::RpcClient, rpc_request},
solana_client::rpc_client::RpcClient,
solana_core::rpc::JsonRpcConfig,
solana_faucet::faucet::{run_local_faucet_with_port, FAUCET_PORT},
solana_sdk::{
account::Account,
clock::{Slot, DEFAULT_TICKS_PER_SLOT, MS_PER_TICK},
commitment_config::CommitmentConfig,
native_token::{sol_to_lamports, Sol},
clock::Slot,
native_token::sol_to_lamports,
pubkey::Pubkey,
rpc_port,
signature::{read_keypair_file, write_keypair_file, Keypair, Signer},
system_program,
},
solana_validator::{redirect_stderr_to_file, test_validator::*},
solana_validator::{
dashboard::Dashboard, record_start, redirect_stderr_to_file, test_validator::*,
},
std::{
collections::HashSet,
fs, io,
@@ -31,8 +30,7 @@ use {
path::{Path, PathBuf},
process::exit,
sync::mpsc::channel,
thread,
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
time::{SystemTime, UNIX_EPOCH},
},
};
@@ -43,21 +41,6 @@ enum Output {
Dashboard,
}
/// Creates a new process bar for processing that will take an unknown amount of time
fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);
progress_bar.set_draw_target(ProgressDrawTarget::stdout());
progress_bar
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
progress_bar.enable_steady_tick(100);
progress_bar
}
/// Pretty print a "name value"
fn println_name_value(name: &str, value: &str) {
println!("{} {}", style(name).bold(), value);
}
fn main() {
let default_rpc_port = rpc_port::DEFAULT_RPC_PORT.to_string();
@@ -289,7 +272,7 @@ fn main() {
let mut ledger_fd_lock = FdLock::new(fs::File::open(&ledger_path).unwrap());
let _ledger_lock = ledger_fd_lock.try_lock().unwrap_or_else(|_| {
println!(
"Error: Unable to lock {} directory. Check if another solana-test-validator is running",
"Error: Unable to lock {} directory. Check if another validator is running",
ledger_path.display()
);
exit(1);
@@ -342,6 +325,7 @@ fn main() {
},
);
}
let faucet_keypair =
read_keypair_file(faucet_keypair_file.to_str().unwrap()).unwrap_or_else(|err| {
println!(
@@ -351,26 +335,38 @@ fn main() {
);
exit(1);
});
let faucet_pubkey = faucet_keypair.pubkey();
let validator_start = Instant::now();
if let Some(faucet_addr) = &faucet_addr {
let (sender, receiver) = channel();
run_local_faucet_with_port(faucet_keypair, sender, None, faucet_addr.port());
let _ = receiver.recv().expect("run faucet").unwrap_or_else(|err| {
println!("Error: failed to start faucet: {}", err);
exit(1);
});
}
record_start(
&ledger_path,
Some(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
rpc_port,
)),
)
.unwrap_or_else(|err| println!("Error: failed to record validator start: {}", err));
let dashboard = if output == Output::Dashboard {
Some(Dashboard::new(&ledger_path, Some(&validator_log_symlink)).unwrap())
} else {
None
};
let test_validator = {
let _progress_bar = if output == Output::Dashboard {
println_name_value("Mint address:", &mint_address.to_string());
println_name_value("Ledger location:", &format!("{}", ledger_path.display()));
println_name_value("Log:", &format!("{}", validator_log_symlink.display()));
let progress_bar = new_spinner_progress_bar();
progress_bar.set_message("Initializing...");
Some(progress_bar)
} else {
None
};
let mut genesis = TestValidatorGenesis::default();
genesis
.ledger_path(&ledger_path)
.add_account(
faucet_keypair.pubkey(),
faucet_pubkey,
Account::new(faucet_lamports, 0, &system_program::id()),
)
.rpc_config(JsonRpcConfig {
@@ -396,143 +392,19 @@ fn main() {
genesis.warp_slot(warp_slot);
}
genesis.start_with_mint_address(mint_address)
}
.unwrap_or_else(|err| {
println!("Error: failed to start validator: {}", err);
exit(1);
});
};
if let Some(faucet_addr) = &faucet_addr {
let (sender, receiver) = channel();
run_local_faucet_with_port(faucet_keypair, sender, None, faucet_addr.port());
let _ = receiver.recv().expect("run faucet").unwrap_or_else(|err| {
println!("Error: failed to start faucet: {}", err);
match test_validator {
Ok(_test_validator) => match dashboard {
Some(dashboard) => dashboard.run(),
None => std::thread::park(),
},
Err(err) => {
drop(dashboard);
println!("Error: failed to start validator: {}", err);
exit(1);
});
}
if output == Output::Dashboard {
let rpc_client = test_validator.rpc_client().0;
let identity = &rpc_client.get_identity().expect("get_identity");
println_name_value("Identity:", &identity.to_string());
println_name_value(
"Version:",
&rpc_client.get_version().expect("get_version").solana_core,
);
println_name_value("JSON RPC URL:", &test_validator.rpc_url());
println_name_value(
"JSON RPC PubSub Websocket:",
&test_validator.rpc_pubsub_url(),
);
println_name_value("Gossip Address:", &test_validator.gossip().to_string());
println_name_value("TPU Address:", &test_validator.tpu().to_string());
if let Some(faucet_addr) = &faucet_addr {
println_name_value(
"Faucet Address:",
&format!("{}:{}", &test_validator.gossip().ip(), faucet_addr.port()),
);
}
let progress_bar = new_spinner_progress_bar();
fn get_validator_stats(
rpc_client: &RpcClient,
identity: &Pubkey,
) -> client_error::Result<(Slot, Slot, Slot, u64, Sol, String)> {
let processed_slot =
rpc_client.get_slot_with_commitment(CommitmentConfig::processed())?;
let confirmed_slot =
rpc_client.get_slot_with_commitment(CommitmentConfig::confirmed())?;
let finalized_slot =
rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?;
let transaction_count =
rpc_client.get_transaction_count_with_commitment(CommitmentConfig::processed())?;
let identity_balance = rpc_client
.get_balance_with_commitment(identity, CommitmentConfig::confirmed())?
.value;
let health = match rpc_client.get_health() {
Ok(()) => "ok".to_string(),
Err(err) => {
if let client_error::ClientErrorKind::RpcError(
rpc_request::RpcError::RpcResponseError {
code: _,
message: _,
data:
rpc_request::RpcResponseErrorData::NodeUnhealthy {
num_slots_behind: Some(num_slots_behind),
},
},
) = &err.kind
{
format!("{} slots behind", num_slots_behind)
} else {
"unhealthy".to_string()
}
}
};
Ok((
processed_slot,
confirmed_slot,
finalized_slot,
transaction_count,
Sol(identity_balance),
health,
))
}
loop {
let snapshot_slot = rpc_client.get_snapshot_slot().ok();
for _i in 0..10 {
match get_validator_stats(&rpc_client, &identity) {
Ok((
processed_slot,
confirmed_slot,
finalized_slot,
transaction_count,
identity_balance,
health,
)) => {
let uptime = chrono::Duration::from_std(validator_start.elapsed()).unwrap();
progress_bar.set_message(&format!(
"{:02}:{:02}:{:02} \
{}| \
Processed Slot: {} | Confirmed Slot: {} | Finalized Slot: {} | \
Snapshot Slot: {} | \
Transactions: {} | {}",
uptime.num_hours(),
uptime.num_minutes() % 60,
uptime.num_seconds() % 60,
if health == "ok" {
"".to_string()
} else {
format!("| {} ", style(health).bold().red())
},
processed_slot,
confirmed_slot,
finalized_slot,
snapshot_slot
.map(|s| s.to_string())
.unwrap_or_else(|| "-".to_string()),
transaction_count,
identity_balance
));
}
Err(err) => {
progress_bar.set_message(&format!("{}", err));
}
}
thread::sleep(Duration::from_millis(
MS_PER_TICK * DEFAULT_TICKS_PER_SLOT / 2,
));
}
}
}
std::thread::park();
}
fn remove_directory_contents(ledger_path: &Path) -> Result<(), io::Error> {