CLI: Print gossip nodes with `cli-output` crate
This commit is contained in:
parent
d4aa7d512c
commit
cb5e000615
|
@ -15,8 +15,8 @@ use {
|
|||
solana_account_decoder::parse_token::UiTokenAccount,
|
||||
solana_clap_utils::keypair::SignOnly,
|
||||
solana_client::rpc_response::{
|
||||
RpcAccountBalance, RpcInflationGovernor, RpcInflationRate, RpcKeyedAccount, RpcSupply,
|
||||
RpcVoteAccountInfo,
|
||||
RpcAccountBalance, RpcContactInfo, RpcInflationGovernor, RpcInflationRate, RpcKeyedAccount,
|
||||
RpcSupply, RpcVoteAccountInfo,
|
||||
},
|
||||
solana_sdk::{
|
||||
clock::{Epoch, Slot, UnixTimestamp},
|
||||
|
@ -2283,6 +2283,97 @@ impl fmt::Display for CliTransactionConfirmation {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CliGossipNode {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ip_address: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub identity_label: Option<String>,
|
||||
pub identity_pubkey: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub gossip_port: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tpu_port: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rpc_host: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
impl CliGossipNode {
|
||||
pub fn new(info: RpcContactInfo, labels: &HashMap<String, String>) -> Self {
|
||||
Self {
|
||||
ip_address: info.gossip.map(|addr| addr.ip().to_string()),
|
||||
identity_label: labels.get(&info.pubkey).cloned(),
|
||||
identity_pubkey: info.pubkey,
|
||||
gossip_port: info.gossip.map(|addr| addr.port()),
|
||||
tpu_port: info.tpu.map(|addr| addr.port()),
|
||||
rpc_host: info.rpc.map(|addr| addr.to_string()),
|
||||
version: info.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_to_string_or_none<T>(option: Option<T>) -> String
|
||||
where
|
||||
T: std::string::ToString,
|
||||
{
|
||||
unwrap_to_string_or_default(option, "none")
|
||||
}
|
||||
|
||||
fn unwrap_to_string_or_default<T>(option: Option<T>, default: &str) -> String
|
||||
where
|
||||
T: std::string::ToString,
|
||||
{
|
||||
option
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| default.to_string())
|
||||
}
|
||||
|
||||
impl fmt::Display for CliGossipNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
unwrap_to_string_or_none(self.ip_address.as_ref()),
|
||||
self.identity_label
|
||||
.as_ref()
|
||||
.unwrap_or(&self.identity_pubkey),
|
||||
unwrap_to_string_or_none(self.gossip_port.as_ref()),
|
||||
unwrap_to_string_or_none(self.tpu_port.as_ref()),
|
||||
unwrap_to_string_or_none(self.rpc_host.as_ref()),
|
||||
unwrap_to_string_or_default(self.version.as_ref(), "unknown"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliGossipNode {}
|
||||
impl VerboseDisplay for CliGossipNode {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CliGossipNodes(pub Vec<CliGossipNode>);
|
||||
|
||||
impl fmt::Display for CliGossipNodes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------",
|
||||
)?;
|
||||
for node in self.0.iter() {
|
||||
writeln!(f, "{}", node)?;
|
||||
}
|
||||
writeln!(f, "Nodes: {}", self.0.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl QuietDisplay for CliGossipNodes {}
|
||||
impl VerboseDisplay for CliGossipNodes {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -61,7 +61,6 @@ use solana_vote_program::vote_state::VoteState;
|
|||
use std::{
|
||||
collections::{BTreeMap, HashMap, VecDeque},
|
||||
fmt,
|
||||
net::SocketAddr,
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
|
@ -1655,40 +1654,14 @@ pub fn process_live_slots(config: &CliConfig) -> ProcessResult {
|
|||
pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
|
||||
let cluster_nodes = rpc_client.get_cluster_nodes()?;
|
||||
|
||||
fn format_port(addr: Option<SocketAddr>) -> String {
|
||||
addr.map(|addr| addr.port().to_string())
|
||||
.unwrap_or_else(|| "none".to_string())
|
||||
}
|
||||
|
||||
let s: Vec<_> = cluster_nodes
|
||||
let nodes: Vec<_> = cluster_nodes
|
||||
.into_iter()
|
||||
.map(|node| {
|
||||
format!(
|
||||
"{:15} | {:44} | {:6} | {:5} | {:21} | {}",
|
||||
node.gossip
|
||||
.map(|addr| addr.ip().to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
format_labeled_address(&node.pubkey, &config.address_labels),
|
||||
format_port(node.gossip),
|
||||
format_port(node.tpu),
|
||||
node.rpc
|
||||
.map(|addr| addr.to_string())
|
||||
.unwrap_or_else(|| "none".to_string()),
|
||||
node.version.unwrap_or_else(|| "unknown".to_string()),
|
||||
)
|
||||
})
|
||||
.map(|node| CliGossipNode::new(node, &config.address_labels))
|
||||
.collect();
|
||||
|
||||
Ok(format!(
|
||||
"IP Address | Node identifier \
|
||||
| Gossip | TPU | RPC Address | Version\n\
|
||||
----------------+----------------------------------------------+\
|
||||
--------+-------+-----------------------+----------------\n\
|
||||
{}\n\
|
||||
Nodes: {}",
|
||||
s.join("\n"),
|
||||
s.len(),
|
||||
))
|
||||
Ok(config
|
||||
.output_format
|
||||
.formatted_string(&CliGossipNodes(nodes)))
|
||||
}
|
||||
|
||||
pub fn process_show_stakes(
|
||||
|
|
Loading…
Reference in New Issue