2019-04-15 13:36:14 -07:00
//! A command-line executable for monitoring a cluster's gossip plane.
2019-04-01 16:12:30 -07:00
2019-04-30 16:42:56 -07:00
#[ macro_use ]
extern crate solana ;
2019-04-22 14:51:20 -07:00
use clap ::{ crate_description , crate_name , crate_version , App , AppSettings , Arg , SubCommand } ;
use solana ::contact_info ::ContactInfo ;
2019-04-01 16:12:30 -07:00
use solana ::gossip_service ::discover ;
2019-04-22 14:51:20 -07:00
use solana_client ::rpc_client ::RpcClient ;
2019-04-01 16:12:30 -07:00
use solana_sdk ::pubkey ::Pubkey ;
use std ::error ;
use std ::net ::SocketAddr ;
use std ::process ::exit ;
fn pubkey_validator ( pubkey : String ) -> Result < ( ) , String > {
match pubkey . parse ::< Pubkey > ( ) {
Ok ( _ ) = > Ok ( ( ) ) ,
Err ( err ) = > Err ( format! ( " {:?} " , err ) ) ,
}
}
fn main ( ) -> Result < ( ) , Box < dyn error ::Error > > {
2019-04-13 18:43:59 -07:00
env_logger ::Builder ::from_env ( env_logger ::Env ::new ( ) . default_filter_or ( " solana=info " ) ) . init ( ) ;
2019-05-03 15:00:19 -07:00
let mut entrypoint_addr = SocketAddr ::from ( ( [ 127 , 0 , 0 , 1 ] , 8001 ) ) ;
let entrypoint_string = entrypoint_addr . to_string ( ) ;
2019-04-01 16:12:30 -07:00
let matches = App ::new ( crate_name! ( ) )
. about ( crate_description! ( ) )
. version ( crate_version! ( ) )
2019-04-22 14:51:20 -07:00
. setting ( AppSettings ::SubcommandRequiredElseHelp )
2019-04-01 16:12:30 -07:00
. arg (
2019-05-03 15:00:19 -07:00
Arg ::with_name ( " entrypoint " )
2019-04-01 16:12:30 -07:00
. short ( " n " )
2019-05-03 15:00:19 -07:00
. long ( " entrypoint " )
2019-04-01 16:12:30 -07:00
. value_name ( " HOST:PORT " )
. takes_value ( true )
2019-05-03 15:00:19 -07:00
. default_value ( & entrypoint_string )
. help ( " Rendezvous with the cluster at this entry point " ) ,
2019-04-01 16:12:30 -07:00
)
2019-04-22 14:51:20 -07:00
. subcommand (
SubCommand ::with_name ( " spy " )
2019-05-03 15:00:19 -07:00
. about ( " Monitor the gossip entrypoint " )
2019-04-22 14:51:20 -07:00
. setting ( AppSettings ::DisableVersion )
2019-04-30 16:42:56 -07:00
. arg (
clap ::Arg ::with_name ( " pull_only " )
. long ( " pull-only " )
. takes_value ( false )
2019-05-03 15:00:19 -07:00
. help ( " Use a partial gossip node (Pulls only) to spy on the cluster. By default it will use a full fledged gossip node (Pushes and Pulls). Useful when behind a NAT " ) ,
2019-04-30 16:42:56 -07:00
)
2019-04-22 14:51:20 -07:00
. arg (
Arg ::with_name ( " num_nodes " )
. short ( " N " )
. long ( " num-nodes " )
. value_name ( " NUM " )
. takes_value ( true )
. conflicts_with ( " num_nodes_exactly " )
. help ( " Wait for at least NUM nodes to converge " ) ,
)
. arg (
Arg ::with_name ( " num_nodes_exactly " )
. short ( " E " )
. long ( " num-nodes-exactly " )
. value_name ( " NUM " )
. takes_value ( true )
. help ( " Wait for exactly NUM nodes to converge " ) ,
)
. arg (
Arg ::with_name ( " node_pubkey " )
. short ( " p " )
. long ( " pubkey " )
. value_name ( " PUBKEY " )
. takes_value ( true )
. validator ( pubkey_validator )
. help ( " Public key of a specific node to wait for " ) ,
)
. arg (
Arg ::with_name ( " timeout " )
. long ( " timeout " )
. value_name ( " SECS " )
. takes_value ( true )
. help (
" Maximum time to wait for cluster to converge [default: wait forever] " ,
) ,
) ,
2019-04-01 16:12:30 -07:00
)
2019-04-22 14:51:20 -07:00
. subcommand (
SubCommand ::with_name ( " stop " )
. about ( " Send stop request to a node " )
. setting ( AppSettings ::DisableVersion )
. arg (
Arg ::with_name ( " node_pubkey " )
. index ( 1 )
. required ( true )
. value_name ( " PUBKEY " )
. validator ( pubkey_validator )
. help ( " Public key of a specific node to stop " ) ,
) ,
2019-04-01 16:12:30 -07:00
)
. get_matches ( ) ;
2019-05-03 15:00:19 -07:00
if let Some ( addr ) = matches . value_of ( " entrypoint " ) {
entrypoint_addr = solana_netutil ::parse_host_port ( addr ) . unwrap_or_else ( | e | {
eprintln! ( " failed to parse entrypoint address: {} " , e ) ;
2019-04-01 16:12:30 -07:00
exit ( 1 )
} ) ;
}
2019-04-22 14:51:20 -07:00
match matches . subcommand ( ) {
( " spy " , Some ( matches ) ) = > {
let num_nodes_exactly = matches
. value_of ( " num_nodes_exactly " )
. map ( | num | num . to_string ( ) . parse ( ) . unwrap ( ) ) ;
let num_nodes = matches
. value_of ( " num_nodes " )
. map ( | num | num . to_string ( ) . parse ( ) . unwrap ( ) )
. or ( num_nodes_exactly ) ;
let timeout = matches
. value_of ( " timeout " )
. map ( | secs | secs . to_string ( ) . parse ( ) . unwrap ( ) ) ;
let pubkey = matches
. value_of ( " node_pubkey " )
. map ( | pubkey_str | pubkey_str . parse ::< Pubkey > ( ) . unwrap ( ) ) ;
2019-04-01 16:12:30 -07:00
2019-04-30 16:42:56 -07:00
let gossip_addr = if matches . is_present ( " pull_only " ) {
None
} else {
let mut addr = socketaddr_any! ( ) ;
2019-05-03 11:01:35 -07:00
addr . set_ip (
2019-05-03 15:00:19 -07:00
solana_netutil ::get_public_ip_addr ( & entrypoint_addr ) . unwrap_or_else ( | err | {
eprintln! ( " failed to contact {} : {} " , entrypoint_addr , err ) ;
2019-05-03 11:01:35 -07:00
exit ( 1 )
} ) ,
) ;
2019-04-30 16:42:56 -07:00
Some ( addr )
} ;
2019-05-16 07:14:58 -07:00
let ( nodes , _replicators ) = discover (
2019-05-03 15:00:19 -07:00
& entrypoint_addr ,
2019-04-30 16:42:56 -07:00
num_nodes ,
timeout ,
pubkey ,
gossip_addr . as_ref ( ) ,
) ? ;
2019-04-01 16:12:30 -07:00
2019-04-22 14:51:20 -07:00
if timeout . is_some ( ) {
if let Some ( num ) = num_nodes {
if nodes . len ( ) < num {
let add = if num_nodes_exactly . is_some ( ) {
" "
} else {
" or more "
} ;
eprintln! (
" Error: Insufficient nodes discovered. Expecting {}{} " ,
num , add ,
) ;
}
}
if let Some ( node ) = pubkey {
if nodes . iter ( ) . find ( | x | x . id = = node ) . is_none ( ) {
eprintln! ( " Error: Could not find node {:?} " , node ) ;
}
}
}
if num_nodes_exactly . is_some ( ) & & nodes . len ( ) > num_nodes_exactly . unwrap ( ) {
2019-04-01 16:12:30 -07:00
eprintln! (
2019-04-22 14:51:20 -07:00
" Error: Extra nodes discovered. Expecting exactly {} " ,
num_nodes_exactly . unwrap ( )
2019-04-01 16:12:30 -07:00
) ;
}
}
2019-04-22 14:51:20 -07:00
( " stop " , Some ( matches ) ) = > {
let pubkey = matches
. value_of ( " node_pubkey " )
. unwrap ( )
. parse ::< Pubkey > ( )
. unwrap ( ) ;
2019-05-16 07:14:58 -07:00
let ( nodes , _replicators ) = discover ( & entrypoint_addr , None , None , Some ( pubkey ) , None ) ? ;
2019-04-22 14:51:20 -07:00
let node = nodes . iter ( ) . find ( | x | x . id = = pubkey ) . unwrap ( ) ;
if ! ContactInfo ::is_valid_address ( & node . rpc ) {
eprintln! ( " Error: RPC service is not enabled on node {:?} " , pubkey ) ;
}
println! ( " \n Sending stop request to node {:?} " , pubkey ) ;
let result = RpcClient ::new_socket ( node . rpc ) . fullnode_exit ( ) ? ;
if result {
println! ( " Stop signal accepted " ) ;
} else {
eprintln! ( " Error: Stop signal ignored " ) ;
2019-04-01 16:12:30 -07:00
}
}
2019-04-22 14:51:20 -07:00
_ = > unreachable! ( ) ,
2019-04-01 16:12:30 -07:00
}
2019-04-22 14:51:20 -07:00
2019-04-01 16:12:30 -07:00
Ok ( ( ) )
}