server_forge/
utils.rs

1//! # Utilities Module
2//!
3//! This module provides various utility functions used throughout the server setup
4//! and maintenance tool. It includes functions for logging, user input, configuration
5//! management, command execution, and report generation.
6
7use crate::config::Config;
8use chrono::Local;
9use log::{error, info};
10use std::error::Error;
11use std::fs;
12use std::io::{self, Write};
13use std::process::Command;
14
15/// Sets up logging for the application.
16///
17/// This function configures log4rs to write logs to a file in the /var/log directory.
18/// The log file name includes a timestamp to ensure uniqueness.
19///
20/// # Returns
21///
22/// Returns `Ok(())` if logging is set up successfully, or an error if setup fails.
23pub fn setup_logging() -> Result<(), Box<dyn Error>> {
24    let log_file = format!(
25        "/var/log/server_setup_{}.log",
26        Local::now().format("%Y%m%d_%H%M%S")
27    );
28    let file_appender = log4rs::append::file::FileAppender::builder()
29        .encoder(Box::new(log4rs::encode::pattern::PatternEncoder::new(
30            "{d} - {l} - {m}\n",
31        )))
32        .build(log_file)?;
33
34    let config = log4rs::config::Config::builder()
35        .appender(log4rs::config::Appender::builder().build("file", Box::new(file_appender)))
36        .build(
37            log4rs::config::Root::builder()
38                .appender("file")
39                .build(log::LevelFilter::Info),
40        )?;
41
42    log4rs::init_config(config)?;
43    Ok(())
44}
45
46/// Prompts the user for input to configure the server setup.
47///
48/// This function interactively asks the user for various configuration options
49/// and returns a `Config` struct with the user's choices.
50///
51/// # Returns
52///
53/// Returns a `Result` containing the `Config` struct if successful, or an error if input fails.
54pub fn get_user_input() -> Result<Config, Box<dyn Error>> {
55    let mut config = Config::default();
56
57    config.linux_distro = prompt("Enter Linux distribution (ubuntu/centos/fedora): ")?;
58    config.server_role = prompt("Enter server role (web/database/application): ")?;
59    config.security_level = prompt("Enter desired security level (basic/intermediate/advanced): ")?;
60    config.monitoring = prompt("Enable monitoring? (y/n): ")?.to_lowercase() == "y";
61    config.backup_frequency = prompt("Enter backup frequency (hourly/daily/weekly): ")?;
62    config.update_schedule = prompt("Enter update schedule (daily/weekly/monthly): ")?;
63    config.use_containers = prompt("Use containerization? (y/n): ")?.to_lowercase() == "y";
64
65    if config.use_containers {
66        config.use_kubernetes = prompt("Use Kubernetes? (y/n): ")?.to_lowercase() == "y";
67    }
68
69    let num_apps: usize = prompt("How many applications to deploy? ")?.parse()?;
70    for i in 0..num_apps {
71        let app = prompt(&format!("Enter application #{} to deploy: ", i + 1))?;
72        config.deployed_apps.push(app);
73    }
74
75    let num_rules: usize = prompt("How many custom firewall rules to add? ")?.parse()?;
76    for i in 0..num_rules {
77        let rule = prompt(&format!("Enter custom firewall rule #{}: ", i + 1))?;
78        config.custom_firewall_rules.push(rule);
79    }
80
81    Ok(config)
82}
83
84/// Prompts the user with a question and returns their response.
85///
86/// This function is a helper used by `get_user_input` to ask individual questions.
87///
88/// # Arguments
89///
90/// * `question` - A string slice containing the question to ask the user
91///
92/// # Returns
93///
94/// Returns a `Result` containing the user's response as a `String`, or an error if input fails.
95fn prompt(question: &str) -> Result<String, Box<dyn Error>> {
96    print!("{}", question);
97    io::stdout().flush()?;
98    let mut input = String::new();
99    io::stdin().read_line(&mut input)?;
100    Ok(input.trim().to_string())
101}
102
103/// Saves the configuration to a JSON file.
104///
105/// This function serializes the `Config` struct to JSON and saves it to /etc/server_setup_config.json.
106///
107/// # Arguments
108///
109/// * `config` - A reference to the `Config` struct to be saved
110///
111/// # Returns
112///
113/// Returns `Ok(())` if the config is saved successfully, or an error if saving fails.
114pub fn save_config(config: &Config) -> Result<(), Box<dyn Error>> {
115    let config_path = "/etc/server_setup_config.json";
116    let config_json = serde_json::to_string_pretty(config)?;
117    fs::write(config_path, config_json)?;
118    info!("Configuration saved to {}", config_path);
119    Ok(())
120}
121
122/// Executes a system command and logs the result.
123///
124/// This function runs a command with the given arguments, logs the execution,
125/// and returns an error if the command fails.
126///
127/// # Arguments
128///
129/// * `command` - A string slice containing the command to run
130/// * `args` - A slice of string slices containing the arguments for the command
131///
132/// # Returns
133///
134/// Returns `Ok(())` if the command executes successfully, or an error if execution fails.
135pub fn run_command(command: &str, args: &[&str]) -> Result<(), Box<dyn Error>> {
136    info!("Running command: {} {:?}", command, args);
137    let output = Command::new(command).args(args).output()?;
138    if !output.status.success() {
139        let error_message = format!(
140            "Command failed: {} {:?}\nError: {}",
141            command,
142            args,
143            String::from_utf8_lossy(&output.stderr)
144        );
145        error!("{}", error_message);
146        return Err(error_message.into());
147    }
148    Ok(())
149}
150
151/// Generates a report of the server setup.
152///
153/// This function creates a text file report containing details of the server configuration,
154/// deployed applications, firewall rules, and system information.
155///
156/// # Arguments
157///
158/// * `config` - A reference to the `Config` struct containing the server configuration
159///
160/// # Returns
161///
162/// Returns `Ok(())` if the report is generated successfully, or an error if generation fails.
163pub fn generate_report(config: &Config) -> Result<(), Box<dyn Error>> {
164    let report_path = "/root/server_setup_report.txt";
165    let mut report = String::new();
166
167    report.push_str("Server Setup Report\n");
168    report.push_str("===================\n\n");
169
170    report.push_str(&format!("Linux Distribution: {}\n", config.linux_distro));
171    report.push_str(&format!("Server Role: {}\n", config.server_role));
172    report.push_str(&format!("Security Level: {}\n", config.security_level));
173    report.push_str(&format!("Monitoring Enabled: {}\n", config.monitoring));
174    report.push_str(&format!("Backup Frequency: {}\n", config.backup_frequency));
175    report.push_str(&format!("Update Schedule: {}\n", config.update_schedule));
176    report.push_str(&format!("Containerization: {}\n", config.use_containers));
177    report.push_str(&format!("Kubernetes: {}\n", config.use_kubernetes));
178
179    report.push_str("\nDeployed Applications:\n");
180    for app in &config.deployed_apps {
181        report.push_str(&format!("- {}\n", app));
182    }
183
184    report.push_str("\nCustom Firewall Rules:\n");
185    for rule in &config.custom_firewall_rules {
186        report.push_str(&format!("- {}\n", rule));
187    }
188
189    // Add system information
190    report.push_str("\nSystem Information:\n");
191    if let Ok(output) = Command::new("uname").arg("-a").output() {
192        report.push_str(&format!(
193            "OS: {}\n",
194            String::from_utf8_lossy(&output.stdout).trim()
195        ));
196    }
197    if let Ok(output) = Command::new("lscpu").output() {
198        report.push_str(&format!(
199            "CPU: {}\n",
200            String::from_utf8_lossy(&output.stdout).trim()
201        ));
202    }
203    if let Ok(output) = Command::new("free").arg("-h").output() {
204        report.push_str(&format!(
205            "Memory: {}\n",
206            String::from_utf8_lossy(&output.stdout).trim()
207        ));
208    }
209
210    fs::write(report_path, report)?;
211    info!("Setup report generated at {}", report_path);
212    Ok(())
213}