1use super::{
8 extra_vars::ExtraVarsDocBuilder,
9 inventory::{
10 generate_full_cone_private_node_static_environment_inventory,
11 generate_symmetric_private_node_static_environment_inventory,
12 },
13 AnsibleInventoryType, AnsiblePlaybook, AnsibleRunner,
14};
15use crate::{
16 ansible::inventory::{
17 generate_custom_environment_inventory,
18 generate_full_cone_nat_gateway_static_environment_inventory,
19 },
20 bootstrap::BootstrapOptions,
21 clients::ClientsDeployOptions,
22 deploy::DeployOptions,
23 error::{Error, Result},
24 funding::FundingOptions,
25 inventory::{DeploymentNodeRegistries, VirtualMachine},
26 print_duration, run_external_command, BinaryOption, CloudProvider, EvmNetwork, LogFormat,
27 NodeType, SshClient, UpgradeOptions,
28};
29use ant_service_management::NodeRegistry;
30use evmlib::common::U256;
31use log::{debug, error, trace};
32use semver::Version;
33use serde::{Deserialize, Serialize};
34use std::{
35 collections::HashMap,
36 net::IpAddr,
37 path::PathBuf,
38 time::{Duration, Instant},
39};
40use walkdir::WalkDir;
41
42use crate::ansible::extra_vars;
43
44pub const DEFAULT_BETA_ENCRYPTION_KEY: &str =
45 "49113d2083f57a976076adbe85decb75115820de1e6e74b47e0429338cef124a";
46
47#[derive(Clone, Serialize, Deserialize)]
48pub struct ProvisionOptions {
49 pub ant_version: Option<String>,
53 pub binary_option: BinaryOption,
54 pub chunk_size: Option<u64>,
55 pub client_env_variables: Option<Vec<(String, String)>>,
56 pub delayed_verifier_batch_size: Option<u16>,
57 pub delayed_verifier_quorum_value: Option<String>,
58 pub enable_delayed_verifier: bool,
59 pub enable_random_verifier: bool,
60 pub enable_performance_verifier: bool,
61 pub enable_telegraf: bool,
62 pub enable_uploaders: bool,
63 pub evm_data_payments_address: Option<String>,
64 pub evm_network: EvmNetwork,
65 pub evm_payment_token_address: Option<String>,
66 pub evm_rpc_url: Option<String>,
67 pub expected_hash: Option<String>,
68 pub expected_size: Option<u64>,
69 pub file_address: Option<String>,
70 pub full_cone_private_node_count: u16,
71 pub funding_wallet_secret_key: Option<String>,
72 pub gas_amount: Option<U256>,
73 pub interval: Option<Duration>,
74 pub log_format: Option<LogFormat>,
75 pub max_archived_log_files: u16,
76 pub max_log_files: u16,
77 pub max_uploads: Option<u32>,
78 pub name: String,
79 pub network_id: Option<u8>,
80 pub network_dashboard_branch: Option<String>,
81 pub node_count: u16,
82 pub node_env_variables: Option<Vec<(String, String)>>,
83 pub output_inventory_dir_path: PathBuf,
84 pub peer_cache_node_count: u16,
85 pub performance_verifier_batch_size: Option<u16>,
86 pub public_rpc: bool,
87 pub random_verifier_batch_size: Option<u16>,
88 pub rewards_address: Option<String>,
89 pub sleep_duration: Option<u16>,
90 pub symmetric_private_node_count: u16,
91 pub token_amount: Option<U256>,
92 pub upload_size: Option<u16>,
93 pub upload_interval: Option<u16>,
94 pub uploaders_count: Option<u16>,
95 pub wallet_secret_keys: Option<Vec<String>>,
96}
97
98#[derive(Clone, Debug)]
100pub struct PrivateNodeProvisionInventory {
101 pub full_cone_nat_gateway_vms: Vec<VirtualMachine>,
102 pub full_cone_private_node_vms: Vec<VirtualMachine>,
103 pub symmetric_nat_gateway_vms: Vec<VirtualMachine>,
104 pub symmetric_private_node_vms: Vec<VirtualMachine>,
105}
106
107impl PrivateNodeProvisionInventory {
108 pub fn new(
109 provisioner: &AnsibleProvisioner,
110 full_cone_private_node_vm_count: Option<u16>,
111 symmetric_private_node_vm_count: Option<u16>,
112 ) -> Result<Self> {
113 let should_provision_full_cone_private_nodes = full_cone_private_node_vm_count
115 .map(|count| count > 0)
116 .unwrap_or(true);
117 let should_provision_symmetric_private_nodes = symmetric_private_node_vm_count
118 .map(|count| count > 0)
119 .unwrap_or(true);
120
121 let mut inventory = Self {
122 full_cone_nat_gateway_vms: Default::default(),
123 full_cone_private_node_vms: Default::default(),
124 symmetric_nat_gateway_vms: Default::default(),
125 symmetric_private_node_vms: Default::default(),
126 };
127
128 if should_provision_full_cone_private_nodes {
129 let full_cone_private_node_vms = provisioner
130 .ansible_runner
131 .get_inventory(AnsibleInventoryType::FullConePrivateNodes, true)
132 .inspect_err(|err| {
133 println!("Failed to obtain the inventory of Full Cone private node: {err:?}");
134 })?;
135
136 let full_cone_nat_gateway_inventory = provisioner
137 .ansible_runner
138 .get_inventory(AnsibleInventoryType::FullConeNatGateway, true)
139 .inspect_err(|err| {
140 println!("Failed to get Full Cone NAT Gateway inventory {err:?}");
141 })?;
142
143 if full_cone_nat_gateway_inventory.len() != full_cone_private_node_vms.len() {
144 println!("The number of Full Cone private nodes does not match the number of Full Cone NAT Gateway VMs");
145 return Err(Error::VmCountMismatch(
146 Some(AnsibleInventoryType::FullConePrivateNodes),
147 Some(AnsibleInventoryType::FullConeNatGateway),
148 ));
149 }
150
151 inventory.full_cone_private_node_vms = full_cone_private_node_vms;
152 inventory.full_cone_nat_gateway_vms = full_cone_nat_gateway_inventory;
153 }
154
155 if should_provision_symmetric_private_nodes {
156 let symmetric_private_node_vms = provisioner
157 .ansible_runner
158 .get_inventory(AnsibleInventoryType::SymmetricPrivateNodes, true)
159 .inspect_err(|err| {
160 println!("Failed to obtain the inventory of Symmetric private node: {err:?}");
161 })?;
162
163 let symmetric_nat_gateway_inventory = provisioner
164 .ansible_runner
165 .get_inventory(AnsibleInventoryType::SymmetricNatGateway, true)
166 .inspect_err(|err| {
167 println!("Failed to get Symmetric NAT Gateway inventory {err:?}");
168 })?;
169
170 if symmetric_nat_gateway_inventory.len() != symmetric_private_node_vms.len() {
171 println!("The number of Symmetric private nodes does not match the number of Symmetric NAT Gateway VMs");
172 return Err(Error::VmCountMismatch(
173 Some(AnsibleInventoryType::SymmetricPrivateNodes),
174 Some(AnsibleInventoryType::SymmetricNatGateway),
175 ));
176 }
177
178 inventory.symmetric_private_node_vms = symmetric_private_node_vms;
179 inventory.symmetric_nat_gateway_vms = symmetric_nat_gateway_inventory;
180 }
181
182 Ok(inventory)
183 }
184
185 pub fn should_provision_full_cone_private_nodes(&self) -> bool {
186 !self.full_cone_private_node_vms.is_empty()
187 }
188
189 pub fn should_provision_symmetric_private_nodes(&self) -> bool {
190 !self.symmetric_private_node_vms.is_empty()
191 }
192
193 pub fn symmetric_private_node_and_gateway_map(
194 &self,
195 ) -> Result<HashMap<VirtualMachine, VirtualMachine>> {
196 Self::match_private_node_vm_and_gateway_vm(
197 &self.symmetric_private_node_vms,
198 &self.symmetric_nat_gateway_vms,
199 )
200 }
201
202 pub fn full_cone_private_node_and_gateway_map(
203 &self,
204 ) -> Result<HashMap<VirtualMachine, VirtualMachine>> {
205 Self::match_private_node_vm_and_gateway_vm(
206 &self.full_cone_private_node_vms,
207 &self.full_cone_nat_gateway_vms,
208 )
209 }
210
211 pub fn match_private_node_vm_and_gateway_vm(
212 private_node_vms: &[VirtualMachine],
213 nat_gateway_vms: &[VirtualMachine],
214 ) -> Result<HashMap<VirtualMachine, VirtualMachine>> {
215 if private_node_vms.len() != nat_gateway_vms.len() {
216 println!(
217 "The number of private node VMs ({}) does not match the number of NAT Gateway VMs ({})",
218 private_node_vms.len(),
219 nat_gateway_vms.len()
220 );
221 error!("The number of private node VMs does not match the number of NAT Gateway VMs: Private VMs: {private_node_vms:?} Nat gateway VMs: {nat_gateway_vms:?}");
222 return Err(Error::VmCountMismatch(None, None));
223 }
224
225 let mut map = HashMap::new();
226 for private_vm in private_node_vms {
227 let nat_gateway = nat_gateway_vms
228 .iter()
229 .find(|vm| {
230 let private_node_name = private_vm.name.split('-').next_back().unwrap();
231 let nat_gateway_name = vm.name.split('-').next_back().unwrap();
232 private_node_name == nat_gateway_name
233 })
234 .ok_or_else(|| {
235 println!(
236 "Failed to find a matching NAT Gateway for private node: {}",
237 private_vm.name
238 );
239 error!("Failed to find a matching NAT Gateway for private node: {}. Private VMs: {private_node_vms:?} Nat gateway VMs: {nat_gateway_vms:?}", private_vm.name);
240 Error::VmCountMismatch(None, None)
241 })?;
242
243 let _ = map.insert(private_vm.clone(), nat_gateway.clone());
244 }
245
246 Ok(map)
247 }
248}
249
250impl From<BootstrapOptions> for ProvisionOptions {
251 fn from(bootstrap_options: BootstrapOptions) -> Self {
252 ProvisionOptions {
253 ant_version: None,
254 binary_option: bootstrap_options.binary_option,
255 chunk_size: bootstrap_options.chunk_size,
256 client_env_variables: None,
257 delayed_verifier_batch_size: None,
258 delayed_verifier_quorum_value: None,
259 enable_delayed_verifier: false,
260 enable_random_verifier: false,
261 enable_performance_verifier: false,
262 enable_telegraf: true,
263 enable_uploaders: false,
264 evm_data_payments_address: bootstrap_options.evm_data_payments_address,
265 evm_network: bootstrap_options.evm_network,
266 evm_payment_token_address: bootstrap_options.evm_payment_token_address,
267 evm_rpc_url: bootstrap_options.evm_rpc_url,
268 expected_hash: None,
269 expected_size: None,
270 file_address: None,
271 full_cone_private_node_count: bootstrap_options.full_cone_private_node_count,
272 funding_wallet_secret_key: None,
273 gas_amount: None,
274 interval: Some(bootstrap_options.interval),
275 log_format: bootstrap_options.log_format,
276 max_archived_log_files: bootstrap_options.max_archived_log_files,
277 max_log_files: bootstrap_options.max_log_files,
278 max_uploads: None,
279 name: bootstrap_options.name,
280 network_id: Some(bootstrap_options.network_id),
281 network_dashboard_branch: None,
282 node_count: bootstrap_options.node_count,
283 node_env_variables: bootstrap_options.node_env_variables,
284 output_inventory_dir_path: bootstrap_options.output_inventory_dir_path,
285 peer_cache_node_count: 0,
286 performance_verifier_batch_size: None,
287 public_rpc: false,
288 random_verifier_batch_size: None,
289 rewards_address: Some(bootstrap_options.rewards_address),
290 sleep_duration: None,
291 symmetric_private_node_count: bootstrap_options.symmetric_private_node_count,
292 token_amount: None,
293 upload_size: None,
294 upload_interval: None,
295 uploaders_count: None,
296 wallet_secret_keys: None,
297 }
298 }
299}
300
301impl From<DeployOptions> for ProvisionOptions {
302 fn from(deploy_options: DeployOptions) -> Self {
303 ProvisionOptions {
304 ant_version: None,
305 binary_option: deploy_options.binary_option,
306 chunk_size: deploy_options.chunk_size,
307 client_env_variables: deploy_options.client_env_variables,
308 delayed_verifier_batch_size: None,
309 delayed_verifier_quorum_value: None,
310 enable_delayed_verifier: deploy_options.enable_delayed_verifier,
311 enable_performance_verifier: deploy_options.enable_performance_verifier,
312 enable_random_verifier: deploy_options.enable_random_verifier,
313 enable_telegraf: deploy_options.enable_telegraf,
314 enable_uploaders: true,
315 node_env_variables: deploy_options.node_env_variables,
316 evm_data_payments_address: deploy_options.evm_data_payments_address,
317 evm_network: deploy_options.evm_network,
318 evm_payment_token_address: deploy_options.evm_payment_token_address,
319 evm_rpc_url: deploy_options.evm_rpc_url,
320 expected_hash: None,
321 expected_size: None,
322 file_address: None,
323 full_cone_private_node_count: deploy_options.full_cone_private_node_count,
324 funding_wallet_secret_key: deploy_options.funding_wallet_secret_key,
325 gas_amount: deploy_options.initial_gas,
326 interval: Some(deploy_options.interval),
327 log_format: deploy_options.log_format,
328 max_archived_log_files: deploy_options.max_archived_log_files,
329 max_log_files: deploy_options.max_log_files,
330 max_uploads: None,
331 name: deploy_options.name,
332 network_id: Some(deploy_options.network_id),
333 network_dashboard_branch: deploy_options.network_dashboard_branch,
334 node_count: deploy_options.node_count,
335 output_inventory_dir_path: deploy_options.output_inventory_dir_path,
336 peer_cache_node_count: deploy_options.peer_cache_node_count,
337 performance_verifier_batch_size: None,
338 public_rpc: deploy_options.public_rpc,
339 random_verifier_batch_size: None,
340 rewards_address: Some(deploy_options.rewards_address),
341 sleep_duration: None,
342 symmetric_private_node_count: deploy_options.symmetric_private_node_count,
343 token_amount: deploy_options.initial_tokens,
344 upload_size: None,
345 upload_interval: Some(deploy_options.upload_interval),
346 uploaders_count: Some(deploy_options.uploaders_count),
347 wallet_secret_keys: None,
348 }
349 }
350}
351
352impl From<ClientsDeployOptions> for ProvisionOptions {
353 fn from(client_options: ClientsDeployOptions) -> Self {
354 Self {
355 ant_version: None,
356 binary_option: client_options.binary_option,
357 chunk_size: client_options.chunk_size,
358 client_env_variables: client_options.client_env_variables,
359 delayed_verifier_batch_size: client_options.delayed_verifier_batch_size,
360 delayed_verifier_quorum_value: client_options.delayed_verifier_quorum_value,
361 enable_delayed_verifier: client_options.enable_delayed_verifier,
362 enable_random_verifier: client_options.enable_random_verifier,
363 enable_performance_verifier: client_options.enable_performance_verifier,
364 enable_telegraf: client_options.enable_telegraf,
365 enable_uploaders: client_options.enable_uploaders,
366 evm_data_payments_address: client_options.evm_details.data_payments_address,
367 evm_network: client_options.evm_details.network,
368 evm_payment_token_address: client_options.evm_details.payment_token_address,
369 evm_rpc_url: client_options.evm_details.rpc_url,
370 expected_hash: client_options.expected_hash,
371 expected_size: client_options.expected_size,
372 file_address: client_options.file_address,
373 full_cone_private_node_count: 0,
374 funding_wallet_secret_key: client_options.funding_wallet_secret_key,
375 gas_amount: client_options.initial_gas,
376 interval: None,
377 log_format: None,
378 max_archived_log_files: client_options.max_archived_log_files,
379 max_log_files: client_options.max_log_files,
380 max_uploads: client_options.max_uploads,
381 name: client_options.name,
382 network_id: client_options.network_id,
383 network_dashboard_branch: None,
384 node_count: 0,
385 node_env_variables: None,
386 output_inventory_dir_path: client_options.output_inventory_dir_path,
387 peer_cache_node_count: 0,
388 performance_verifier_batch_size: client_options.performance_verifier_batch_size,
389 public_rpc: false,
390 random_verifier_batch_size: client_options.random_verifier_batch_size,
391 rewards_address: None,
392 sleep_duration: client_options.sleep_duration,
393 symmetric_private_node_count: 0,
394 token_amount: client_options.initial_tokens,
395 upload_size: client_options.upload_size,
396 upload_interval: None,
397 uploaders_count: Some(client_options.uploaders_count),
398 wallet_secret_keys: client_options.wallet_secret_keys,
399 }
400 }
401}
402
403#[derive(Clone)]
404pub struct AnsibleProvisioner {
405 pub ansible_runner: AnsibleRunner,
406 pub cloud_provider: CloudProvider,
407 pub ssh_client: SshClient,
408}
409
410impl AnsibleProvisioner {
411 pub fn new(
412 ansible_runner: AnsibleRunner,
413 cloud_provider: CloudProvider,
414 ssh_client: SshClient,
415 ) -> Self {
416 Self {
417 ansible_runner,
418 cloud_provider,
419 ssh_client,
420 }
421 }
422
423 pub fn build_autonomi_binaries(
424 &self,
425 options: &ProvisionOptions,
426 binaries_to_build: Option<Vec<String>>,
427 ) -> Result<()> {
428 let start = Instant::now();
429 println!("Obtaining IP address for build VM...");
430 let build_inventory = self
431 .ansible_runner
432 .get_inventory(AnsibleInventoryType::Build, true)?;
433 let build_ip = build_inventory[0].public_ip_addr;
434 self.ssh_client
435 .wait_for_ssh_availability(&build_ip, &self.cloud_provider.get_ssh_user())?;
436
437 println!("Running ansible against build VM...");
438 let base_extra_vars = extra_vars::build_binaries_extra_vars_doc(options)?;
439
440 let extra_vars = if let Some(binaries) = binaries_to_build {
441 let mut build_ant = false;
442 let mut build_antnode = false;
443 let mut build_antctl = false;
444 let mut build_antctld = false;
445
446 for binary in &binaries {
447 match binary.as_str() {
448 "ant" => build_ant = true,
449 "antnode" => build_antnode = true,
450 "antctl" => build_antctl = true,
451 "antctld" => build_antctld = true,
452 _ => return Err(Error::InvalidBinaryName(binary.clone())),
453 }
454 }
455
456 let mut json_value: serde_json::Value = serde_json::from_str(&base_extra_vars)?;
457 if let serde_json::Value::Object(ref mut map) = json_value {
458 map.insert("build_ant".to_string(), serde_json::Value::Bool(build_ant));
459 map.insert(
460 "build_antnode".to_string(),
461 serde_json::Value::Bool(build_antnode),
462 );
463 map.insert(
464 "build_antctl".to_string(),
465 serde_json::Value::Bool(build_antctl),
466 );
467 map.insert(
468 "build_antctld".to_string(),
469 serde_json::Value::Bool(build_antctld),
470 );
471 }
472 json_value.to_string()
473 } else {
474 base_extra_vars
475 };
476
477 self.ansible_runner.run_playbook(
478 AnsiblePlaybook::Build,
479 AnsibleInventoryType::Build,
480 Some(extra_vars),
481 )?;
482 print_duration(start.elapsed());
483 Ok(())
484 }
485
486 pub fn cleanup_node_logs(&self, setup_cron: bool) -> Result<()> {
487 for node_inv_type in AnsibleInventoryType::iter_node_type() {
488 self.ansible_runner.run_playbook(
489 AnsiblePlaybook::CleanupLogs,
490 node_inv_type,
491 Some(format!("{{ \"setup_cron\": \"{setup_cron}\" }}")),
492 )?;
493 }
494
495 Ok(())
496 }
497
498 pub fn copy_logs(&self, name: &str, resources_only: bool) -> Result<()> {
499 for node_inv_type in AnsibleInventoryType::iter_node_type() {
500 self.ansible_runner.run_playbook(
501 AnsiblePlaybook::CopyLogs,
502 node_inv_type,
503 Some(format!(
504 "{{ \"env_name\": \"{name}\", \"resources_only\" : \"{resources_only}\" }}"
505 )),
506 )?;
507 }
508 Ok(())
509 }
510
511 pub fn get_all_node_inventory(&self) -> Result<Vec<VirtualMachine>> {
512 let mut all_node_inventory = Vec::new();
513 for node_inv_type in AnsibleInventoryType::iter_node_type() {
514 all_node_inventory.extend(self.ansible_runner.get_inventory(node_inv_type, false)?);
515 }
516
517 Ok(all_node_inventory)
518 }
519
520 pub fn get_symmetric_nat_gateway_inventory(&self) -> Result<Vec<VirtualMachine>> {
521 self.ansible_runner
522 .get_inventory(AnsibleInventoryType::SymmetricNatGateway, false)
523 }
524
525 pub fn get_full_cone_nat_gateway_inventory(&self) -> Result<Vec<VirtualMachine>> {
526 self.ansible_runner
527 .get_inventory(AnsibleInventoryType::FullConeNatGateway, false)
528 }
529
530 pub fn get_client_inventory(&self) -> Result<Vec<VirtualMachine>> {
531 self.ansible_runner
532 .get_inventory(AnsibleInventoryType::Clients, false)
533 }
534
535 pub fn get_node_registries(
536 &self,
537 inventory_type: &AnsibleInventoryType,
538 ) -> Result<DeploymentNodeRegistries> {
539 debug!("Fetching node manager inventory for {inventory_type:?}");
540 let temp_dir_path = tempfile::tempdir()?.into_path();
541 let temp_dir_json = serde_json::to_string(&temp_dir_path)?;
542
543 self.ansible_runner.run_playbook(
544 AnsiblePlaybook::AntCtlInventory,
545 *inventory_type,
546 Some(format!("{{ \"dest\": {temp_dir_json} }}")),
547 )?;
548
549 let node_registry_paths = WalkDir::new(temp_dir_path)
550 .into_iter()
551 .flatten()
552 .filter_map(|entry| {
553 if entry.file_type().is_file()
554 && entry.path().extension().is_some_and(|ext| ext == "json")
555 {
556 let mut vm_name = entry.path().to_path_buf();
558 trace!("Found file with json extension: {vm_name:?}");
559 vm_name.pop();
560 vm_name.pop();
561 vm_name.pop();
562 trace!("Extracting the vm name from the path");
564 let vm_name = vm_name.file_name()?.to_str()?;
565 trace!("Extracted vm name from path: {vm_name}");
566 Some((vm_name.to_string(), entry.path().to_path_buf()))
567 } else {
568 None
569 }
570 })
571 .collect::<Vec<(String, PathBuf)>>();
572
573 let mut node_registries = Vec::new();
574 let mut failed_vms = Vec::new();
575 for (vm_name, file_path) in node_registry_paths {
576 match NodeRegistry::load(&file_path) {
577 Ok(node_registry) => node_registries.push((vm_name.clone(), node_registry)),
578 Err(_) => failed_vms.push(vm_name.clone()),
579 }
580 }
581
582 let deployment_registries = DeploymentNodeRegistries {
583 inventory_type: *inventory_type,
584 retrieved_registries: node_registries,
585 failed_vms,
586 };
587 Ok(deployment_registries)
588 }
589
590 pub fn provision_evm_nodes(&self, options: &ProvisionOptions) -> Result<()> {
591 let start = Instant::now();
592 println!("Obtaining IP address for EVM nodes...");
593 let evm_node_inventory = self
594 .ansible_runner
595 .get_inventory(AnsibleInventoryType::EvmNodes, true)?;
596 let evm_node_ip = evm_node_inventory[0].public_ip_addr;
597 self.ssh_client
598 .wait_for_ssh_availability(&evm_node_ip, &self.cloud_provider.get_ssh_user())?;
599
600 println!("Running ansible against EVM nodes...");
601 self.ansible_runner.run_playbook(
602 AnsiblePlaybook::EvmNodes,
603 AnsibleInventoryType::EvmNodes,
604 Some(extra_vars::build_evm_nodes_extra_vars_doc(
605 &options.name,
606 &self.cloud_provider,
607 &options.binary_option,
608 )),
609 )?;
610 print_duration(start.elapsed());
611 Ok(())
612 }
613
614 pub fn provision_genesis_node(&self, options: &ProvisionOptions) -> Result<()> {
615 let start = Instant::now();
616 let genesis_inventory = self
617 .ansible_runner
618 .get_inventory(AnsibleInventoryType::Genesis, true)?;
619 let genesis_ip = genesis_inventory[0].public_ip_addr;
620 self.ssh_client
621 .wait_for_ssh_availability(&genesis_ip, &self.cloud_provider.get_ssh_user())?;
622 self.ansible_runner.run_playbook(
623 AnsiblePlaybook::Genesis,
624 AnsibleInventoryType::Genesis,
625 Some(extra_vars::build_node_extra_vars_doc(
626 &self.cloud_provider.to_string(),
627 options,
628 NodeType::Genesis,
629 None,
630 None,
631 1,
632 options.evm_network.clone(),
633 false,
634 )?),
635 )?;
636
637 print_duration(start.elapsed());
638
639 Ok(())
640 }
641
642 pub fn provision_full_cone(
643 &self,
644 options: &ProvisionOptions,
645 initial_contact_peer: Option<String>,
646 initial_network_contacts_url: Option<String>,
647 private_node_inventory: PrivateNodeProvisionInventory,
648 new_full_cone_nat_gateway_new_vms_for_upscale: Option<Vec<VirtualMachine>>,
649 ) -> Result<()> {
650 let start = Instant::now();
652 self.print_ansible_run_banner("Provision Full Cone NAT Gateway - Step 1");
653
654 for vm in new_full_cone_nat_gateway_new_vms_for_upscale
655 .as_ref()
656 .unwrap_or(&private_node_inventory.full_cone_nat_gateway_vms)
657 .iter()
658 {
659 println!(
660 "Checking SSH availability for Full Cone NAT Gateway: {}",
661 vm.public_ip_addr
662 );
663 self.ssh_client
664 .wait_for_ssh_availability(&vm.public_ip_addr, &self.cloud_provider.get_ssh_user())
665 .map_err(|e| {
666 println!("Failed to establish SSH connection to Full Cone NAT Gateway: {e}");
667 e
668 })?;
669 }
670
671 let mut modified_private_node_inventory = private_node_inventory.clone();
672
673 if let Some(new_full_cone_nat_gateway_new_vms_for_upscale) =
675 &new_full_cone_nat_gateway_new_vms_for_upscale
676 {
677 debug!("Removing existing full cone NAT Gateway and private node VMs from the inventory. Old inventory: {modified_private_node_inventory:?}");
678 let mut names_to_keep = Vec::new();
679
680 for vm in new_full_cone_nat_gateway_new_vms_for_upscale.iter() {
681 let nat_gateway_name = vm.name.split('-').next_back().unwrap();
682 names_to_keep.push(nat_gateway_name);
683 }
684
685 modified_private_node_inventory
686 .full_cone_nat_gateway_vms
687 .retain(|vm| {
688 let nat_gateway_name = vm.name.split('-').next_back().unwrap();
689 names_to_keep.contains(&nat_gateway_name)
690 });
691 modified_private_node_inventory
692 .full_cone_private_node_vms
693 .retain(|vm| {
694 let nat_gateway_name = vm.name.split('-').next_back().unwrap();
695 names_to_keep.contains(&nat_gateway_name)
696 });
697 debug!("New inventory after removing existing full cone NAT Gateway and private node VMs: {modified_private_node_inventory:?}");
698 }
699
700 if modified_private_node_inventory
701 .full_cone_nat_gateway_vms
702 .is_empty()
703 {
704 error!("There are no full cone NAT Gateway VMs available to upscale");
705 return Ok(());
706 }
707
708 let private_node_ip_map = modified_private_node_inventory
709 .full_cone_private_node_and_gateway_map()?
710 .into_iter()
711 .map(|(k, v)| {
712 let gateway_name = if new_full_cone_nat_gateway_new_vms_for_upscale.is_some() {
713 debug!("Upscaling, using public IP address for gateway name");
714 v.public_ip_addr.to_string()
715 } else {
716 v.name.clone()
717 };
718 (gateway_name, k.private_ip_addr)
719 })
720 .collect::<HashMap<String, IpAddr>>();
721
722 if private_node_ip_map.is_empty() {
723 println!("There are no full cone private node VM available to be routed through the full cone NAT Gateway");
724 return Err(Error::EmptyInventory(
725 AnsibleInventoryType::FullConePrivateNodes,
726 ));
727 }
728
729 let vars = extra_vars::build_nat_gateway_extra_vars_doc(
730 &options.name,
731 private_node_ip_map.clone(),
732 "step1",
733 );
734 debug!("Provisioning Full Cone NAT Gateway - Step 1 with vars: {vars}");
735 let gateway_inventory = if new_full_cone_nat_gateway_new_vms_for_upscale.is_some() {
736 debug!("Upscaling, using static inventory for full cone nat gateway.");
737 generate_full_cone_nat_gateway_static_environment_inventory(
738 &modified_private_node_inventory.full_cone_nat_gateway_vms,
739 &options.name,
740 &options.output_inventory_dir_path,
741 )?;
742
743 AnsibleInventoryType::FullConeNatGatewayStatic
744 } else {
745 AnsibleInventoryType::FullConeNatGateway
746 };
747 self.ansible_runner.run_playbook(
748 AnsiblePlaybook::StaticFullConeNatGateway,
749 gateway_inventory,
750 Some(vars),
751 )?;
752
753 self.print_ansible_run_banner("Provisioning Full Cone Private Node Config");
755
756 generate_full_cone_private_node_static_environment_inventory(
757 &options.name,
758 &options.output_inventory_dir_path,
759 &private_node_inventory.full_cone_private_node_vms,
760 &private_node_inventory.full_cone_nat_gateway_vms,
761 &self.ssh_client.private_key_path,
762 )
763 .inspect_err(|err| {
764 error!("Failed to generate full cone private node static inv with err: {err:?}")
765 })?;
766
767 println!("Obtaining IP addresses for nodes...");
771 let inventory = self
772 .ansible_runner
773 .get_inventory(AnsibleInventoryType::FullConePrivateNodes, true)?;
774
775 println!("Waiting for SSH availability on Symmetric Private nodes...");
776 for vm in inventory.iter() {
777 println!(
778 "Checking SSH availability for {}: {}",
779 vm.name, vm.public_ip_addr
780 );
781 self.ssh_client
782 .wait_for_ssh_availability(&vm.public_ip_addr, &self.cloud_provider.get_ssh_user())
783 .map_err(|e| {
784 println!("Failed to establish SSH connection to {}: {}", vm.name, e);
785 e
786 })?;
787 }
788
789 println!("SSH is available on all nodes. Proceeding with provisioning...");
790
791 self.ansible_runner.run_playbook(
792 AnsiblePlaybook::PrivateNodeConfig,
793 AnsibleInventoryType::FullConePrivateNodes,
794 Some(
795 extra_vars::build_full_cone_private_node_config_extra_vars_docs(
796 &private_node_inventory,
797 )?,
798 ),
799 )?;
800
801 let vars = extra_vars::build_nat_gateway_extra_vars_doc(
804 &options.name,
805 private_node_ip_map,
806 "step2",
807 );
808
809 self.print_ansible_run_banner("Provisioning Full Cone NAT Gateway - Step 2");
810 debug!("Provisioning Full Cone NAT Gateway - Step 2 with vars: {vars}");
811 self.ansible_runner.run_playbook(
812 AnsiblePlaybook::StaticFullConeNatGateway,
813 gateway_inventory,
814 Some(vars),
815 )?;
816
817 let home_dir = std::env::var("HOME").inspect_err(|err| {
820 println!("Failed to get home directory with error: {err:?}",);
821 })?;
822 let known_hosts_path = format!("{home_dir}/.ssh/known_hosts");
823 debug!("Cleaning up known hosts file at {known_hosts_path} ");
824 run_external_command(
825 PathBuf::from("rm"),
826 std::env::current_dir()?,
827 vec![known_hosts_path],
828 false,
829 false,
830 )?;
831
832 self.print_ansible_run_banner("Provision Full Cone Private Nodes");
833
834 self.ssh_client.set_full_cone_nat_routed_vms(
835 &private_node_inventory.full_cone_private_node_vms,
836 &private_node_inventory.full_cone_nat_gateway_vms,
837 )?;
838
839 self.provision_nodes(
840 options,
841 initial_contact_peer,
842 initial_network_contacts_url,
843 NodeType::FullConePrivateNode,
844 )?;
845
846 print_duration(start.elapsed());
847 Ok(())
848 }
849 pub fn provision_symmetric_nat_gateway(
850 &self,
851 options: &ProvisionOptions,
852 private_node_inventory: &PrivateNodeProvisionInventory,
853 ) -> Result<()> {
854 let start = Instant::now();
855 for vm in &private_node_inventory.symmetric_nat_gateway_vms {
856 println!(
857 "Checking SSH availability for Symmetric NAT Gateway: {}",
858 vm.public_ip_addr
859 );
860 self.ssh_client
861 .wait_for_ssh_availability(&vm.public_ip_addr, &self.cloud_provider.get_ssh_user())
862 .map_err(|e| {
863 println!("Failed to establish SSH connection to Symmetric NAT Gateway: {e}");
864 e
865 })?;
866 }
867
868 let private_node_ip_map = private_node_inventory
869 .symmetric_private_node_and_gateway_map()?
870 .into_iter()
871 .map(|(k, v)| (v.name.clone(), k.private_ip_addr))
872 .collect::<HashMap<String, IpAddr>>();
873
874 if private_node_ip_map.is_empty() {
875 println!("There are no Symmetric private node VM available to be routed through the Symmetric NAT Gateway");
876 return Err(Error::EmptyInventory(
877 AnsibleInventoryType::SymmetricPrivateNodes,
878 ));
879 }
880
881 let vars = extra_vars::build_nat_gateway_extra_vars_doc(
882 &options.name,
883 private_node_ip_map,
884 "symmetric",
885 );
886 debug!("Provisioning Symmetric NAT Gateway with vars: {vars}");
887 self.ansible_runner.run_playbook(
888 AnsiblePlaybook::SymmetricNatGateway,
889 AnsibleInventoryType::SymmetricNatGateway,
890 Some(vars),
891 )?;
892
893 print_duration(start.elapsed());
894 Ok(())
895 }
896
897 pub fn provision_nodes(
898 &self,
899 options: &ProvisionOptions,
900 initial_contact_peer: Option<String>,
901 initial_network_contacts_url: Option<String>,
902 node_type: NodeType,
903 ) -> Result<()> {
904 let start = Instant::now();
905 let mut write_older_cache_files = false;
906 let (inventory_type, node_count) = match &node_type {
907 NodeType::FullConePrivateNode => (
908 node_type.to_ansible_inventory_type(),
909 options.full_cone_private_node_count,
910 ),
911 NodeType::Generic => (node_type.to_ansible_inventory_type(), options.node_count),
913 NodeType::Genesis => return Err(Error::InvalidNodeType(node_type)),
914 NodeType::PeerCache => {
915 write_older_cache_files = true;
916 (
917 node_type.to_ansible_inventory_type(),
918 options.peer_cache_node_count,
919 )
920 }
921 NodeType::SymmetricPrivateNode => (
922 node_type.to_ansible_inventory_type(),
923 options.symmetric_private_node_count,
924 ),
925 };
926
927 println!("Obtaining IP addresses for nodes...");
931 let inventory = self.ansible_runner.get_inventory(inventory_type, true)?;
932
933 println!("Waiting for SSH availability on {node_type:?} nodes...");
934 for vm in inventory.iter() {
935 println!(
936 "Checking SSH availability for {}: {}",
937 vm.name, vm.public_ip_addr
938 );
939 self.ssh_client
940 .wait_for_ssh_availability(&vm.public_ip_addr, &self.cloud_provider.get_ssh_user())
941 .map_err(|e| {
942 println!("Failed to establish SSH connection to {}: {}", vm.name, e);
943 e
944 })?;
945 }
946
947 println!("SSH is available on all nodes. Proceeding with provisioning...");
948
949 let playbook = match node_type {
950 NodeType::Generic => AnsiblePlaybook::Nodes,
951 NodeType::PeerCache => AnsiblePlaybook::PeerCacheNodes,
952 NodeType::FullConePrivateNode => AnsiblePlaybook::Nodes,
953 NodeType::SymmetricPrivateNode => AnsiblePlaybook::Nodes,
954 _ => return Err(Error::InvalidNodeType(node_type.clone())),
955 };
956 self.ansible_runner.run_playbook(
957 playbook,
958 inventory_type,
959 Some(extra_vars::build_node_extra_vars_doc(
960 &self.cloud_provider.to_string(),
961 options,
962 node_type.clone(),
963 initial_contact_peer,
964 initial_network_contacts_url,
965 node_count,
966 options.evm_network.clone(),
967 write_older_cache_files,
968 )?),
969 )?;
970
971 print_duration(start.elapsed());
972 Ok(())
973 }
974
975 pub fn provision_symmetric_private_nodes(
976 &self,
977 options: &mut ProvisionOptions,
978 initial_contact_peer: Option<String>,
979 initial_network_contacts_url: Option<String>,
980 private_node_inventory: &PrivateNodeProvisionInventory,
981 ) -> Result<()> {
982 let start = Instant::now();
983 self.print_ansible_run_banner("Provision Symmetric Private Node Config");
984
985 generate_symmetric_private_node_static_environment_inventory(
986 &options.name,
987 &options.output_inventory_dir_path,
988 &private_node_inventory.symmetric_private_node_vms,
989 &private_node_inventory.symmetric_nat_gateway_vms,
990 &self.ssh_client.private_key_path,
991 )
992 .inspect_err(|err| {
993 error!("Failed to generate symmetric private node static inv with err: {err:?}")
994 })?;
995
996 self.ssh_client.set_symmetric_nat_routed_vms(
997 &private_node_inventory.symmetric_private_node_vms,
998 &private_node_inventory.symmetric_nat_gateway_vms,
999 )?;
1000
1001 let inventory_type = AnsibleInventoryType::SymmetricPrivateNodes;
1002
1003 println!("Obtaining IP addresses for nodes...");
1007 let inventory = self.ansible_runner.get_inventory(inventory_type, true)?;
1008
1009 println!("Waiting for SSH availability on Symmetric Private nodes...");
1010 for vm in inventory.iter() {
1011 println!(
1012 "Checking SSH availability for {}: {}",
1013 vm.name, vm.public_ip_addr
1014 );
1015 self.ssh_client
1016 .wait_for_ssh_availability(&vm.public_ip_addr, &self.cloud_provider.get_ssh_user())
1017 .map_err(|e| {
1018 println!("Failed to establish SSH connection to {}: {}", vm.name, e);
1019 e
1020 })?;
1021 }
1022
1023 println!("SSH is available on all nodes. Proceeding with provisioning...");
1024
1025 self.ansible_runner.run_playbook(
1026 AnsiblePlaybook::PrivateNodeConfig,
1027 inventory_type,
1028 Some(
1029 extra_vars::build_symmetric_private_node_config_extra_vars_doc(
1030 private_node_inventory,
1031 )?,
1032 ),
1033 )?;
1034
1035 println!("Provisioned Symmetric Private Node Config");
1036 print_duration(start.elapsed());
1037
1038 self.provision_nodes(
1039 options,
1040 initial_contact_peer,
1041 initial_network_contacts_url,
1042 NodeType::SymmetricPrivateNode,
1043 )?;
1044
1045 Ok(())
1046 }
1047
1048 pub async fn provision_downloaders(
1049 &self,
1050 options: &ProvisionOptions,
1051 genesis_multiaddr: Option<String>,
1052 genesis_network_contacts_url: Option<String>,
1053 ) -> Result<()> {
1054 let start = Instant::now();
1055
1056 println!("Running ansible against Client machine to start the downloader script.");
1057 debug!("Running ansible against Client machine to start the downloader script.");
1058
1059 self.ansible_runner.run_playbook(
1060 AnsiblePlaybook::Downloaders,
1061 AnsibleInventoryType::Clients,
1062 Some(extra_vars::build_downloaders_extra_vars_doc(
1063 &self.cloud_provider.to_string(),
1064 options,
1065 genesis_multiaddr,
1066 genesis_network_contacts_url,
1067 )?),
1068 )?;
1069 print_duration(start.elapsed());
1070 Ok(())
1071 }
1072
1073 pub async fn provision_static_downloaders(
1074 &self,
1075 options: &ProvisionOptions,
1076 genesis_multiaddr: Option<String>,
1077 genesis_network_contacts_url: Option<String>,
1078 ) -> Result<()> {
1079 let start = Instant::now();
1080
1081 println!("Running ansible against client machine to start the static downloaders.");
1082 debug!("Running ansible against client machine to start the static downloaders.");
1083
1084 self.ansible_runner.run_playbook(
1085 AnsiblePlaybook::StaticDownloaders,
1086 AnsibleInventoryType::Clients,
1087 Some(extra_vars::build_downloaders_extra_vars_doc(
1088 &self.cloud_provider.to_string(),
1089 options,
1090 genesis_multiaddr,
1091 genesis_network_contacts_url,
1092 )?),
1093 )?;
1094 print_duration(start.elapsed());
1095 Ok(())
1096 }
1097
1098 pub async fn provision_clients(
1099 &self,
1100 options: &ProvisionOptions,
1101 genesis_multiaddr: Option<String>,
1102 genesis_network_contacts_url: Option<String>,
1103 ) -> Result<()> {
1104 let start = Instant::now();
1105
1106 let sk_map = if let Some(wallet_keys) = &options.wallet_secret_keys {
1107 self.prepare_pre_funded_wallets(wallet_keys).await?
1108 } else {
1109 self.deposit_funds_to_clients(&FundingOptions {
1110 evm_data_payments_address: options.evm_data_payments_address.clone(),
1111 evm_network: options.evm_network.clone(),
1112 evm_payment_token_address: options.evm_payment_token_address.clone(),
1113 evm_rpc_url: options.evm_rpc_url.clone(),
1114 funding_wallet_secret_key: options.funding_wallet_secret_key.clone(),
1115 gas_amount: options.gas_amount,
1116 token_amount: options.token_amount,
1117 uploaders_count: options.uploaders_count,
1118 })
1119 .await?
1120 };
1121
1122 self.ansible_runner.run_playbook(
1123 AnsiblePlaybook::Uploaders,
1124 AnsibleInventoryType::Clients,
1125 Some(extra_vars::build_clients_extra_vars_doc(
1126 &self.cloud_provider.to_string(),
1127 options,
1128 genesis_multiaddr,
1129 genesis_network_contacts_url,
1130 &sk_map,
1131 )?),
1132 )?;
1133 print_duration(start.elapsed());
1134 Ok(())
1135 }
1136
1137 pub fn start_nodes(
1138 &self,
1139 environment_name: &str,
1140 interval: Duration,
1141 node_type: Option<NodeType>,
1142 custom_inventory: Option<Vec<VirtualMachine>>,
1143 ) -> Result<()> {
1144 let mut extra_vars = ExtraVarsDocBuilder::default();
1145 extra_vars.add_variable("interval", &interval.as_millis().to_string());
1146
1147 if let Some(node_type) = node_type {
1148 println!("Running the start nodes playbook for {node_type:?} nodes");
1149 self.ansible_runner.run_playbook(
1150 AnsiblePlaybook::StartNodes,
1151 node_type.to_ansible_inventory_type(),
1152 Some(extra_vars.build()),
1153 )?;
1154 return Ok(());
1155 }
1156
1157 if let Some(custom_inventory) = custom_inventory {
1158 println!("Running the start nodes playbook with a custom inventory");
1159 generate_custom_environment_inventory(
1160 &custom_inventory,
1161 environment_name,
1162 &self.ansible_runner.working_directory_path.join("inventory"),
1163 )?;
1164 self.ansible_runner.run_playbook(
1165 AnsiblePlaybook::StartNodes,
1166 AnsibleInventoryType::Custom,
1167 Some(extra_vars.build()),
1168 )?;
1169 return Ok(());
1170 }
1171
1172 println!("Running the start nodes playbook for all node types");
1173 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1174 self.ansible_runner.run_playbook(
1175 AnsiblePlaybook::StartNodes,
1176 node_inv_type,
1177 Some(extra_vars.build()),
1178 )?;
1179 }
1180 Ok(())
1181 }
1182
1183 pub fn status(&self) -> Result<()> {
1184 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1185 self.ansible_runner
1186 .run_playbook(AnsiblePlaybook::Status, node_inv_type, None)?;
1187 }
1188 Ok(())
1189 }
1190
1191 pub fn start_telegraf(
1192 &self,
1193 environment_name: &str,
1194 node_type: Option<NodeType>,
1195 custom_inventory: Option<Vec<VirtualMachine>>,
1196 ) -> Result<()> {
1197 if let Some(node_type) = node_type {
1198 println!("Running the start telegraf playbook for {node_type:?} nodes");
1199 self.ansible_runner.run_playbook(
1200 AnsiblePlaybook::StartTelegraf,
1201 node_type.to_ansible_inventory_type(),
1202 None,
1203 )?;
1204 return Ok(());
1205 }
1206
1207 if let Some(custom_inventory) = custom_inventory {
1208 println!("Running the start telegraf playbook with a custom inventory");
1209 generate_custom_environment_inventory(
1210 &custom_inventory,
1211 environment_name,
1212 &self.ansible_runner.working_directory_path.join("inventory"),
1213 )?;
1214 self.ansible_runner.run_playbook(
1215 AnsiblePlaybook::StartTelegraf,
1216 AnsibleInventoryType::Custom,
1217 None,
1218 )?;
1219 return Ok(());
1220 }
1221
1222 println!("Running the start telegraf playbook for all node types");
1223 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1224 self.ansible_runner.run_playbook(
1225 AnsiblePlaybook::StartTelegraf,
1226 node_inv_type,
1227 None,
1228 )?;
1229 }
1230
1231 Ok(())
1232 }
1233
1234 pub fn stop_nodes(
1235 &self,
1236 environment_name: &str,
1237 interval: Duration,
1238 node_type: Option<NodeType>,
1239 custom_inventory: Option<Vec<VirtualMachine>>,
1240 delay: Option<u64>,
1241 service_names: Option<Vec<String>>,
1242 ) -> Result<()> {
1243 let mut extra_vars = ExtraVarsDocBuilder::default();
1244 extra_vars.add_variable("interval", &interval.as_millis().to_string());
1245 if let Some(delay) = delay {
1246 extra_vars.add_variable("delay", &delay.to_string());
1247 }
1248 if let Some(service_names) = service_names {
1249 extra_vars.add_list_variable("service_names", service_names);
1250 }
1251 let extra_vars = extra_vars.build();
1252
1253 if let Some(node_type) = node_type {
1254 println!("Running the stop nodes playbook for {node_type:?} nodes");
1255 self.ansible_runner.run_playbook(
1256 AnsiblePlaybook::StopNodes,
1257 node_type.to_ansible_inventory_type(),
1258 Some(extra_vars),
1259 )?;
1260 return Ok(());
1261 }
1262
1263 if let Some(custom_inventory) = custom_inventory {
1264 println!("Running the stop nodes playbook with a custom inventory");
1265 generate_custom_environment_inventory(
1266 &custom_inventory,
1267 environment_name,
1268 &self.ansible_runner.working_directory_path.join("inventory"),
1269 )?;
1270 self.ansible_runner.run_playbook(
1271 AnsiblePlaybook::StopNodes,
1272 AnsibleInventoryType::Custom,
1273 Some(extra_vars),
1274 )?;
1275 return Ok(());
1276 }
1277
1278 println!("Running the stop nodes playbook for all node types");
1279 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1280 self.ansible_runner.run_playbook(
1281 AnsiblePlaybook::StopNodes,
1282 node_inv_type,
1283 Some(extra_vars.clone()),
1284 )?;
1285 }
1286
1287 Ok(())
1288 }
1289
1290 pub fn stop_telegraf(
1291 &self,
1292 environment_name: &str,
1293 node_type: Option<NodeType>,
1294 custom_inventory: Option<Vec<VirtualMachine>>,
1295 ) -> Result<()> {
1296 if let Some(node_type) = node_type {
1297 println!("Running the stop telegraf playbook for {node_type:?} nodes");
1298 self.ansible_runner.run_playbook(
1299 AnsiblePlaybook::StopTelegraf,
1300 node_type.to_ansible_inventory_type(),
1301 None,
1302 )?;
1303 return Ok(());
1304 }
1305
1306 if let Some(custom_inventory) = custom_inventory {
1307 println!("Running the stop telegraf playbook with a custom inventory");
1308 generate_custom_environment_inventory(
1309 &custom_inventory,
1310 environment_name,
1311 &self.ansible_runner.working_directory_path.join("inventory"),
1312 )?;
1313 self.ansible_runner.run_playbook(
1314 AnsiblePlaybook::StopTelegraf,
1315 AnsibleInventoryType::Custom,
1316 None,
1317 )?;
1318 return Ok(());
1319 }
1320
1321 println!("Running the stop telegraf playbook for all node types");
1322 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1323 self.ansible_runner
1324 .run_playbook(AnsiblePlaybook::StopTelegraf, node_inv_type, None)?;
1325 }
1326
1327 Ok(())
1328 }
1329
1330 pub fn upgrade_node_telegraf(&self, name: &str) -> Result<()> {
1331 self.ansible_runner.run_playbook(
1332 AnsiblePlaybook::UpgradeNodeTelegrafConfig,
1333 AnsibleInventoryType::PeerCacheNodes,
1334 Some(extra_vars::build_node_telegraf_upgrade(
1335 name,
1336 &NodeType::PeerCache,
1337 )?),
1338 )?;
1339 self.ansible_runner.run_playbook(
1340 AnsiblePlaybook::UpgradeNodeTelegrafConfig,
1341 AnsibleInventoryType::Nodes,
1342 Some(extra_vars::build_node_telegraf_upgrade(
1343 name,
1344 &NodeType::Generic,
1345 )?),
1346 )?;
1347
1348 self.ansible_runner.run_playbook(
1349 AnsiblePlaybook::UpgradeNodeTelegrafConfig,
1350 AnsibleInventoryType::SymmetricPrivateNodes,
1351 Some(extra_vars::build_node_telegraf_upgrade(
1352 name,
1353 &NodeType::SymmetricPrivateNode,
1354 )?),
1355 )?;
1356
1357 self.ansible_runner.run_playbook(
1358 AnsiblePlaybook::UpgradeNodeTelegrafConfig,
1359 AnsibleInventoryType::FullConePrivateNodes,
1360 Some(extra_vars::build_node_telegraf_upgrade(
1361 name,
1362 &NodeType::FullConePrivateNode,
1363 )?),
1364 )?;
1365 Ok(())
1366 }
1367
1368 pub fn upgrade_client_telegraf(&self, name: &str) -> Result<()> {
1369 self.ansible_runner.run_playbook(
1370 AnsiblePlaybook::UpgradeClientTelegrafConfig,
1371 AnsibleInventoryType::Clients,
1372 Some(extra_vars::build_client_telegraf_upgrade(name)?),
1373 )?;
1374 Ok(())
1375 }
1376
1377 pub fn upgrade_nodes(&self, options: &UpgradeOptions) -> Result<()> {
1378 if let Some(custom_inventory) = &options.custom_inventory {
1379 println!("Running the UpgradeNodes with a custom inventory");
1380 generate_custom_environment_inventory(
1381 custom_inventory,
1382 &options.name,
1383 &self.ansible_runner.working_directory_path.join("inventory"),
1384 )?;
1385 match self.ansible_runner.run_playbook(
1386 AnsiblePlaybook::UpgradeNodes,
1387 AnsibleInventoryType::Custom,
1388 Some(options.get_ansible_vars()),
1389 ) {
1390 Ok(()) => println!("All nodes were successfully upgraded"),
1391 Err(_) => {
1392 println!("WARNING: some nodes may not have been upgraded or restarted");
1393 }
1394 }
1395 return Ok(());
1396 }
1397
1398 if let Some(node_type) = &options.node_type {
1399 println!("Running the UpgradeNodes playbook for {node_type:?} nodes");
1400 match self.ansible_runner.run_playbook(
1401 AnsiblePlaybook::UpgradeNodes,
1402 node_type.to_ansible_inventory_type(),
1403 Some(options.get_ansible_vars()),
1404 ) {
1405 Ok(()) => println!("All {node_type:?} nodes were successfully upgraded"),
1406 Err(_) => {
1407 println!(
1408 "WARNING: some {node_type:?} nodes may not have been upgraded or restarted"
1409 );
1410 }
1411 }
1412 return Ok(());
1413 }
1414
1415 println!("Running the UpgradeNodes playbook for all node types");
1416
1417 match self.ansible_runner.run_playbook(
1418 AnsiblePlaybook::UpgradeNodes,
1419 AnsibleInventoryType::PeerCacheNodes,
1420 Some(options.get_ansible_vars()),
1421 ) {
1422 Ok(()) => println!("All Peer Cache nodes were successfully upgraded"),
1423 Err(_) => {
1424 println!("WARNING: some Peer Cacche nodes may not have been upgraded or restarted");
1425 }
1426 }
1427 match self.ansible_runner.run_playbook(
1428 AnsiblePlaybook::UpgradeNodes,
1429 AnsibleInventoryType::Nodes,
1430 Some(options.get_ansible_vars()),
1431 ) {
1432 Ok(()) => println!("All generic nodes were successfully upgraded"),
1433 Err(_) => {
1434 println!("WARNING: some nodes may not have been upgraded or restarted");
1435 }
1436 }
1437 match self.ansible_runner.run_playbook(
1438 AnsiblePlaybook::UpgradeNodes,
1439 AnsibleInventoryType::SymmetricPrivateNodes,
1440 Some(options.get_ansible_vars()),
1441 ) {
1442 Ok(()) => println!("All private nodes were successfully upgraded"),
1443 Err(_) => {
1444 println!("WARNING: some nodes may not have been upgraded or restarted");
1445 }
1446 }
1447 match self.ansible_runner.run_playbook(
1449 AnsiblePlaybook::UpgradeNodes,
1450 AnsibleInventoryType::Genesis,
1451 Some(options.get_ansible_vars()),
1452 ) {
1453 Ok(()) => println!("The genesis nodes was successfully upgraded"),
1454 Err(_) => {
1455 println!("WARNING: the genesis node may not have been upgraded or restarted");
1456 }
1457 }
1458 Ok(())
1459 }
1460
1461 pub fn upgrade_antctl(
1462 &self,
1463 environment_name: &str,
1464 version: &Version,
1465 node_type: Option<NodeType>,
1466 custom_inventory: Option<Vec<VirtualMachine>>,
1467 ) -> Result<()> {
1468 let mut extra_vars = ExtraVarsDocBuilder::default();
1469 extra_vars.add_variable("version", &version.to_string());
1470
1471 if let Some(node_type) = node_type {
1472 println!("Running the upgrade safenode-manager playbook for {node_type:?} nodes");
1473 self.ansible_runner.run_playbook(
1474 AnsiblePlaybook::UpgradeAntctl,
1475 node_type.to_ansible_inventory_type(),
1476 Some(extra_vars.build()),
1477 )?;
1478 return Ok(());
1479 }
1480
1481 if let Some(custom_inventory) = custom_inventory {
1482 println!("Running the upgrade safenode-manager playbook with a custom inventory");
1483 generate_custom_environment_inventory(
1484 &custom_inventory,
1485 environment_name,
1486 &self.ansible_runner.working_directory_path.join("inventory"),
1487 )?;
1488 self.ansible_runner.run_playbook(
1489 AnsiblePlaybook::UpgradeAntctl,
1490 AnsibleInventoryType::Custom,
1491 Some(extra_vars.build()),
1492 )?;
1493 return Ok(());
1494 }
1495
1496 println!("Running the upgrade safenode-manager playbook for all node types");
1497 for node_inv_type in AnsibleInventoryType::iter_node_type() {
1498 self.ansible_runner.run_playbook(
1499 AnsiblePlaybook::UpgradeAntctl,
1500 node_inv_type,
1501 Some(extra_vars.build()),
1502 )?;
1503 }
1504
1505 Ok(())
1506 }
1507
1508 pub fn upgrade_nginx_config(
1509 &self,
1510 environment_name: &str,
1511 custom_inventory: Option<Vec<VirtualMachine>>,
1512 ) -> Result<()> {
1513 if let Some(custom_inventory) = custom_inventory {
1514 println!("Running the upgrade nginx config playbook with a custom inventory");
1515 generate_custom_environment_inventory(
1516 &custom_inventory,
1517 environment_name,
1518 &self.ansible_runner.working_directory_path.join("inventory"),
1519 )?;
1520 self.ansible_runner.run_playbook(
1521 AnsiblePlaybook::UpgradeNginx,
1522 AnsibleInventoryType::Custom,
1523 None,
1524 )?;
1525 return Ok(());
1526 }
1527
1528 println!("Running the upgrade nginx config playbook for peer cache nodes");
1529 self.ansible_runner.run_playbook(
1530 AnsiblePlaybook::UpgradeNginx,
1531 AnsibleInventoryType::PeerCacheNodes,
1532 None,
1533 )?;
1534 Ok(())
1535 }
1536
1537 pub fn upgrade_geoip_telegraf(&self, name: &str) -> Result<()> {
1538 self.ansible_runner.run_playbook(
1539 AnsiblePlaybook::UpgradeGeoIpTelegrafConfig,
1540 AnsibleInventoryType::PeerCacheNodes,
1541 Some(extra_vars::build_node_telegraf_upgrade(
1542 name,
1543 &NodeType::PeerCache,
1544 )?),
1545 )?;
1546 Ok(())
1547 }
1548
1549 pub fn print_ansible_run_banner(&self, s: &str) {
1550 let ansible_run_msg = "Ansible Run: ";
1551 let line = "=".repeat(s.len() + ansible_run_msg.len());
1552 println!("{line}\n{ansible_run_msg}{s}\n{line}");
1553 }
1554}