Skip to main content

genact/modules/
ansible.rs

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