genact/modules/
ansible.rs

1//! Pretend to run Ansible to set up some systems
2use async_trait::async_trait;
3use fake::faker::internet::en::*;
4use fake::Fake;
5use rand::prelude::*;
6use rand_distr::{Distribution, Normal};
7use yansi::Paint;
8
9use crate::args::AppConfig;
10use crate::data::ANSIBLE_ROLES_LIST;
11use crate::data::ANSIBLE_TASKS_LIST;
12use crate::io::{csleep, get_terminal_width, newline, print};
13use crate::modules::Module;
14
15pub struct Ansible;
16
17async fn do_for_all_hosts(hosts: &[String], is_gather: bool) {
18    let mut rng = thread_rng();
19
20    let latency_distr = Normal::new(500.0, 100.0).unwrap();
21
22    // To spice things up, add a mode where everything shows up as either failed, changed, or
23    // skipped.
24    let global_outcome = rng.gen_range(1..20);
25    for host in hosts {
26        let host_outcome = rng.gen_range(1..50);
27
28        // If this is the gather task, we always want to return all ok.
29        let text = if is_gather {
30            Paint::green(format!("ok: [{host}]")).to_string()
31        } else {
32            match global_outcome {
33                1 => Paint::cyan(format!("skipping: [{host}]")).to_string(),
34                2 => Paint::red(format!("failed: [{host}]")).to_string(),
35                3 => Paint::yellow(format!("changed: [{host}]")).to_string(),
36                _ => match host_outcome {
37                    1 => Paint::cyan(format!("skipping: [{host}]")).to_string(),
38                    2 => Paint::red(format!("failed: [{host}]")).to_string(),
39                    3..=5 => Paint::yellow(format!("changed: [{host}]")).to_string(),
40                    _ => Paint::green(format!("ok: [{host}]")).to_string(),
41                },
42            }
43        };
44        print(text).await;
45        newline().await;
46        let sleep: f64 = latency_distr.sample(&mut rng);
47        csleep(sleep.round() as u64).await;
48    }
49}
50
51#[async_trait(?Send)]
52impl Module for Ansible {
53    fn name(&self) -> &'static str {
54        "ansible"
55    }
56
57    fn signature(&self) -> String {
58        "ansible-playbook".to_string()
59    }
60
61    async fn run(&self, appconfig: &AppConfig) {
62        let mut rng = thread_rng();
63
64        let term_width = get_terminal_width();
65        let play_text = format!(
66            "PLAY [setup {server}] ",
67            server = Username().fake::<String>()
68        );
69
70        print(format!("{play_text:*<term_width$}",)).await;
71        newline().await;
72        csleep(rng.gen_range(1000..3000)).await;
73        newline().await;
74
75        let num_ipv4_hosts = 1..rng.gen_range(1..20);
76        let num_ipv6_hosts = 1..rng.gen_range(1..20);
77        let ipv4_hosts = num_ipv4_hosts
78            .map(|_| IPv4().fake())
79            .collect::<Vec<String>>();
80        let ipv6_hosts = num_ipv6_hosts
81            .map(|_| IPv6().fake::<String>().to_lowercase())
82            .collect::<Vec<String>>();
83        let mut hosts = [ipv4_hosts, ipv6_hosts].concat();
84        hosts.shuffle(&mut rng);
85
86        let gathering_text = "TASK [Gathering Facts] ";
87        csleep(rng.gen_range(1000..3000)).await;
88        print(format!("{gathering_text:*<term_width$}",)).await;
89        do_for_all_hosts(&hosts, true).await;
90        csleep(rng.gen_range(1000..3000)).await;
91
92        let num_roles = rng.gen_range(3..10);
93        for _ in 1..num_roles {
94            let role = ANSIBLE_ROLES_LIST.choose(&mut rng).unwrap_or(&"unknown");
95            let num_tasks = rng.gen_range(3..10);
96            for _ in 1..num_tasks {
97                newline().await;
98                let task = ANSIBLE_TASKS_LIST.choose(&mut rng).unwrap_or(&"unknown");
99                let task_text = format!("TASK [{role} : {task}] ");
100                print(format!("{task_text:*<term_width$}")).await;
101                csleep(rng.gen_range(1000..3000)).await;
102                do_for_all_hosts(&hosts, false).await;
103
104                if appconfig.should_exit() {
105                    return;
106                }
107            }
108        }
109    }
110}