diff --git a/src/ansible/provisioning.rs b/src/ansible/provisioning.rs index 1ee5094a..51c728c4 100644 --- a/src/ansible/provisioning.rs +++ b/src/ansible/provisioning.rs @@ -293,7 +293,7 @@ impl AnsibleProvisioner { Some(extra_vars::build_node_extra_vars_doc( &self.cloud_provider.to_string(), options, - NodeType::Bootstrap, + NodeType::Genesis, None, 1, options.evm_network.clone(), @@ -344,16 +344,18 @@ impl AnsibleProvisioner { evm_testnet_data: Option, ) -> Result<()> { let start = Instant::now(); - let (inventory_type, node_count) = match node_type { + let (inventory_type, node_count) = match &node_type { NodeType::Bootstrap => ( - AnsibleInventoryType::BootstrapNodes, + node_type.to_ansible_inventory_type(), options.bootstrap_node_count, ), - NodeType::Normal => (AnsibleInventoryType::Nodes, options.node_count), + NodeType::Normal => (node_type.to_ansible_inventory_type(), options.node_count), NodeType::Private => ( - AnsibleInventoryType::PrivateNodes, + node_type.to_ansible_inventory_type(), options.private_node_count, ), + // use provision_genesis_node fn + NodeType::Genesis => return Err(Error::InvalidNodeType(node_type)), }; // For a new deployment, it's quite probable that SSH is available, because this part occurs @@ -498,11 +500,22 @@ impl AnsibleProvisioner { &self, environment_name: &str, interval: Duration, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { let mut extra_vars = ExtraVarsDocBuilder::default(); extra_vars.add_variable("interval", &interval.as_millis().to_string()); + if let Some(node_type) = node_type { + println!("Running the start nodes playbook for {node_type:?} nodes"); + self.ansible_runner.run_playbook( + AnsiblePlaybook::StartNodes, + node_type.to_ansible_inventory_type(), + Some(extra_vars.build()), + )?; + return Ok(()); + } + if let Some(custom_inventory) = custom_inventory { println!("Running the start nodes playbook with a custom inventory"); generate_custom_environment_inventory( @@ -518,6 +531,7 @@ impl AnsibleProvisioner { return Ok(()); } + println!("Running the start nodes playbook for all node types"); for node_inv_type in AnsibleInventoryType::iter_node_type() { self.ansible_runner.run_playbook( AnsiblePlaybook::StartNodes, @@ -539,8 +553,19 @@ impl AnsibleProvisioner { pub fn start_telegraf( &self, environment_name: &str, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { + if let Some(node_type) = node_type { + println!("Running the start telegraf playbook for {node_type:?} nodes"); + self.ansible_runner.run_playbook( + AnsiblePlaybook::StartTelegraf, + node_type.to_ansible_inventory_type(), + None, + )?; + return Ok(()); + } + if let Some(custom_inventory) = custom_inventory { println!("Running the start telegraf playbook with a custom inventory"); generate_custom_environment_inventory( @@ -556,6 +581,7 @@ impl AnsibleProvisioner { return Ok(()); } + println!("Running the start telegraf playbook for all node types"); for node_inv_type in AnsibleInventoryType::iter_node_type() { self.ansible_runner.run_playbook( AnsiblePlaybook::StartTelegraf, @@ -571,11 +597,22 @@ impl AnsibleProvisioner { &self, environment_name: &str, interval: Duration, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { let mut extra_vars = ExtraVarsDocBuilder::default(); extra_vars.add_variable("interval", &interval.as_millis().to_string()); + if let Some(node_type) = node_type { + println!("Running the stop nodes playbook for {node_type:?} nodes"); + self.ansible_runner.run_playbook( + AnsiblePlaybook::StopNodes, + node_type.to_ansible_inventory_type(), + Some(extra_vars.build()), + )?; + return Ok(()); + } + if let Some(custom_inventory) = custom_inventory { println!("Running the stop nodes playbook with a custom inventory"); generate_custom_environment_inventory( @@ -591,6 +628,7 @@ impl AnsibleProvisioner { return Ok(()); } + println!("Running the stop nodes playbook for all node types"); for node_inv_type in AnsibleInventoryType::iter_node_type() { self.ansible_runner.run_playbook( AnsiblePlaybook::StopNodes, @@ -605,8 +643,19 @@ impl AnsibleProvisioner { pub fn stop_telegraf( &self, environment_name: &str, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { + if let Some(node_type) = node_type { + println!("Running the stop telegraf playbook for {node_type:?} nodes"); + self.ansible_runner.run_playbook( + AnsiblePlaybook::StopTelegraf, + node_type.to_ansible_inventory_type(), + None, + )?; + return Ok(()); + } + if let Some(custom_inventory) = custom_inventory { println!("Running the stop telegraf playbook with a custom inventory"); generate_custom_environment_inventory( @@ -622,6 +671,7 @@ impl AnsibleProvisioner { return Ok(()); } + println!("Running the stop telegraf playbook for all node types"); for node_inv_type in AnsibleInventoryType::iter_node_type() { self.ansible_runner .run_playbook(AnsiblePlaybook::StopTelegraf, node_inv_type, None)?; diff --git a/src/error.rs b/src/error.rs index b7bf2ae6..591e9ada 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,7 @@ // This SAFE Network Software is licensed under the BSD-3-Clause license. // Please see the LICENSE file for more details. -use crate::ansible::inventory::AnsibleInventoryType; +use crate::{ansible::inventory::AnsibleInventoryType, NodeType}; use evmlib::contract::network_token; use thiserror::Error; use tokio::task::JoinError; @@ -69,6 +69,8 @@ pub enum Error { GetS3ObjectError(String, String), #[error(transparent)] InquireError(#[from] inquire::InquireError), + #[error("The node type '{0:?}' is not supported")] + InvalidNodeType(NodeType), #[error( "The '{0}' deployment type for the environment is not supported for upscaling uploaders" )] diff --git a/src/lib.rs b/src/lib.rs index e2c423e9..276a9fdf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,21 +112,47 @@ impl std::str::FromStr for DeploymentType { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NodeType { Bootstrap, + Genesis, Normal, Private, } +impl std::str::FromStr for NodeType { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "bootstrap" => Ok(NodeType::Bootstrap), + "genesis" => Ok(NodeType::Genesis), + "normal" => Ok(NodeType::Normal), + "private" => Ok(NodeType::Private), + _ => Err(format!("Invalid node type: {}", s)), + } + } +} + impl NodeType { pub fn telegraph_role(&self) -> &'static str { match self { NodeType::Bootstrap => "BOOTSTRAP_NODE", + // Genesis node should be marked as a bootstrap node for telegraf + NodeType::Genesis => "BOOTSTRAP_NODE", NodeType::Normal => "GENERIC_NODE", NodeType::Private => "NAT_RANDOMIZED_NODE", } } + + pub fn to_ansible_inventory_type(&self) -> AnsibleInventoryType { + match self { + NodeType::Bootstrap => AnsibleInventoryType::BootstrapNodes, + NodeType::Genesis => AnsibleInventoryType::Genesis, + NodeType::Normal => AnsibleInventoryType::Nodes, + NodeType::Private => AnsibleInventoryType::PrivateNodes, + } + } } #[derive(Clone, Debug, Default, Eq, Serialize, Deserialize, PartialEq)] @@ -635,10 +661,15 @@ impl TestnetDeployer { pub fn start( &self, interval: Duration, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { - self.ansible_provisioner - .start_nodes(&self.environment_name, interval, custom_inventory)?; + self.ansible_provisioner.start_nodes( + &self.environment_name, + interval, + node_type, + custom_inventory, + )?; Ok(()) } @@ -677,25 +708,44 @@ impl TestnetDeployer { Ok(()) } - pub fn start_telegraf(&self, custom_inventory: Option>) -> Result<()> { - self.ansible_provisioner - .start_telegraf(&self.environment_name, custom_inventory)?; + pub fn start_telegraf( + &self, + node_type: Option, + custom_inventory: Option>, + ) -> Result<()> { + self.ansible_provisioner.start_telegraf( + &self.environment_name, + node_type, + custom_inventory, + )?; Ok(()) } pub fn stop( &self, interval: Duration, + node_type: Option, custom_inventory: Option>, ) -> Result<()> { - self.ansible_provisioner - .stop_nodes(&self.environment_name, interval, custom_inventory)?; + self.ansible_provisioner.stop_nodes( + &self.environment_name, + interval, + node_type, + custom_inventory, + )?; Ok(()) } - pub fn stop_telegraf(&self, custom_inventory: Option>) -> Result<()> { - self.ansible_provisioner - .stop_telegraf(&self.environment_name, custom_inventory)?; + pub fn stop_telegraf( + &self, + node_type: Option, + custom_inventory: Option>, + ) -> Result<()> { + self.ansible_provisioner.stop_telegraf( + &self.environment_name, + node_type, + custom_inventory, + )?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 207d35b7..40724c93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ use sn_testnet_deploy::{ setup::setup_dotenv_file, upscale::UpscaleOptions, BinaryOption, CloudProvider, EnvironmentType, EvmCustomTestnetData, EvmNetwork, LogFormat, - TestnetDeployBuilder, UpgradeOptions, + NodeType, TestnetDeployBuilder, UpgradeOptions, }; use std::{env, net::IpAddr}; use std::{str::FromStr, time::Duration}; @@ -574,6 +574,11 @@ enum Commands { /// The name of the environment. #[arg(short = 'n', long)] name: String, + /// The type of node to start. If not provided, all nodes will be started. + /// + /// Valid values are "bootstrap", "genesis", "generic" and "private". + #[arg(long)] + node_type: Option, /// The cloud provider for the environment. #[clap(long, value_parser = parse_provider, verbatim_doc_comment, default_value_t = CloudProvider::DigitalOcean)] provider: CloudProvider, @@ -607,6 +612,11 @@ enum Commands { /// The name of the environment. #[arg(short = 'n', long)] name: String, + /// The type of node on which to start Telegraf. If not provided, Telegraf will be started on all nodes. + /// + /// Valid values are "bootstrap", "genesis", "generic" and "private". + #[arg(long)] + node_type: Option, /// The cloud provider for the environment. #[clap(long, value_parser = parse_provider, verbatim_doc_comment, default_value_t = CloudProvider::DigitalOcean)] provider: CloudProvider, @@ -628,6 +638,11 @@ enum Commands { /// The name of the environment. #[arg(short = 'n', long)] name: String, + /// The type of node to stop. If not provided, all nodes will be stopped. + /// + /// Valid values are "bootstrap", "genesis", "generic" and "private". + #[arg(long)] + node_type: Option, /// The cloud provider for the environment. #[clap(long, value_parser = parse_provider, verbatim_doc_comment, default_value_t = CloudProvider::DigitalOcean)] provider: CloudProvider, @@ -648,6 +663,11 @@ enum Commands { /// The name of the environment. #[arg(short = 'n', long)] name: String, + /// The type of node on which to stop Telegraf. If not provided, Telegraf will be stopped on all nodes. + /// + /// Valid values are "bootstrap", "genesis", "generic" and "private". + #[arg(long)] + node_type: Option, /// The cloud provider for the environment. #[clap(long, value_parser = parse_provider, verbatim_doc_comment, default_value_t = CloudProvider::DigitalOcean)] provider: CloudProvider, @@ -2008,6 +2028,7 @@ async fn main() -> Result<()> { forks, interval, name, + node_type, provider, } => { let testnet_deployer = TestnetDeployBuilder::default() @@ -2034,7 +2055,7 @@ async fn main() -> Result<()> { None }; - testnet_deployer.start(interval, custom_inventory)?; + testnet_deployer.start(interval, node_type, custom_inventory)?; Ok(()) } @@ -2042,6 +2063,7 @@ async fn main() -> Result<()> { custom_inventory, forks, name, + node_type, provider, } => { let testnet_deployer = TestnetDeployBuilder::default() @@ -2068,7 +2090,7 @@ async fn main() -> Result<()> { None }; - testnet_deployer.start_telegraf(custom_inventory)?; + testnet_deployer.start_telegraf(node_type, custom_inventory)?; Ok(()) } @@ -2102,6 +2124,7 @@ async fn main() -> Result<()> { forks, interval, name, + node_type, provider, } => { let testnet_deployer = TestnetDeployBuilder::default() @@ -2125,7 +2148,7 @@ async fn main() -> Result<()> { None }; - testnet_deployer.stop(interval, custom_inventory)?; + testnet_deployer.stop(interval, node_type, custom_inventory)?; Ok(()) } @@ -2133,6 +2156,7 @@ async fn main() -> Result<()> { custom_inventory, forks, name, + node_type, provider, } => { let testnet_deployer = TestnetDeployBuilder::default() @@ -2159,7 +2183,7 @@ async fn main() -> Result<()> { None }; - testnet_deployer.stop_telegraf(custom_inventory)?; + testnet_deployer.stop_telegraf(node_type, custom_inventory)?; Ok(()) }