Allow logged-in users to update ispConfig.py via web UI

1) lqos_config understands writing to a new ispConfig.py file, copying the
   previous one and amending to avoid changing any not-covered data.
2) The web UI can adjust the config, upload a new one and it is saved.

Signed-off-by: Herbert Wolverson <herberticus@gmail.com>
This commit is contained in:
Herbert Wolverson 2023-01-10 16:12:09 +00:00
parent bebcbaf7c6
commit 393a489bca
5 changed files with 89 additions and 2 deletions

2
.gitignore vendored
View File

@ -37,6 +37,8 @@ src/ShapedDevices.csv
src/ShapedDevices.lastLoaded.csv
src/network.json
src/ispConfig.py
src/ispConfig.py.backup
src/ispConfig.py.test
src/statsByCircuit.json
src/statsByParentNode.json
src/lastGoodConfig.json

View File

@ -1,6 +1,6 @@
use anyhow::{Error, Result};
use serde::{Serialize, Deserialize};
use std::{fs, path::{Path, PathBuf}};
use std::{fs::{self, remove_file, OpenOptions, read_to_string}, path::{Path, PathBuf}, io::Write};
use crate::etc;
#[derive(Serialize, Deserialize, Debug)]
@ -126,6 +126,51 @@ impl LibreQoSConfig {
}
Ok(())
}
pub fn save(&self) -> Result<()> {
// Find the config
let cfg = etc::EtcLqos::load()?;
let base_path = Path::new(&cfg.lqos_directory);
let final_path = base_path.join("ispConfig.py");
let backup_path = base_path.join("ispConfig.py.backup");
std::fs::copy(&final_path, &backup_path)?;
// Load existing file
let original = read_to_string(final_path)?;
// Temporary
let final_path = base_path.join("ispConfig.py.test");
// Update config entries line by line
let mut config = String::new();
for line in original.split('\n') {
let mut line = line.to_string();
if line.starts_with("interfaceA") { line = format!("interfaceA = '{}'", self.isp_interface); }
if line.starts_with("interfaceB") { line = format!("interfaceB = '{}'", self.internet_interface); }
if line.starts_with("OnAStick") { line = format!("OnAStick = {}", if self.on_a_stick_mode { "True" } else { "False" } ); }
if line.starts_with("StickVlanA") { line = format!("StickVlanA = {}", self.stick_vlans.0); }
if line.starts_with("StickVlanB") { line = format!("StickVlanB = {}", self.stick_vlans.1); }
if line.starts_with("sqm") { line = format!("sqm = '{}'", self.sqm); }
if line.starts_with("upstreamBandwidthCapacityDownloadMbps") { line = format!("upstreamBandwidthCapacityDownloadMbps = {}", self.total_download_mbps); }
if line.starts_with("upstreamBandwidthCapacityUploadMbps") { line = format!("upstreamBandwidthCapacityUploadMbps = {}", self.total_upload_mbps); }
if line.starts_with("monitorOnlyMode") { line = format!("monitorOnlyMode = {}", if self.monitor_mode { "True" } else { "False" } ); }
if line.starts_with("generatedPNDownloadMbps") { line = format!("generatedPNDownloadMbps = {}", self.generated_download_mbps); }
if line.starts_with("generatedPNUploadMbps") { line = format!("generatedPNUploadMbps = {}", self.generated_upload_mbps); }
if line.starts_with("useBinPackingToBalanceCPU") { line = format!("useBinPackingToBalanceCPU = {}", if self.use_binpacking { "True" } else { "False" } ); }
if line.starts_with("enableActualShellCommands") { line = format!("enableActualShellCommands = {}", if self.enable_shell_commands { "True" } else { "False" } ); }
if line.starts_with("runShellCommandsAsSudo") { line = format!("runShellCommandsAsSudo = {}", if self.run_as_sudo { "True" } else { "False" } ); }
if line.starts_with("queuesAvailableOverride") { line = format!("queuesAvailableOverride = {}", self.override_queue_count); }
config += &format!("{line}\n");
}
// Actually save to disk
if final_path.exists() {
remove_file(&final_path)?;
}
let mut file = OpenOptions::new().write(true).create_new(true).open(&final_path)?;
file.write_all(&config.as_bytes())?;
Ok(())
}
}
fn split_at_equals(line: &str) -> String {

View File

@ -40,4 +40,10 @@ pub async fn get_current_lqosd_config(_auth: AuthGuard) -> NoCache<Json<EtcLqos>
let config = lqos_config::EtcLqos::load().unwrap();
println!("{:#?}", config);
NoCache::new(Json(config))
}
#[post("/api/python_config", data="<config>")]
pub async fn update_python_config(_auth: AuthGuard, config: Json<LibreQoSConfig>) -> Json<String> {
config.save().unwrap();
Json("OK".to_string())
}

View File

@ -56,6 +56,7 @@ fn rocket() -> _ {
config_control::get_nic_list,
config_control::get_current_python_config,
config_control::get_current_lqosd_config,
config_control::update_python_config,
auth_guard::create_first_user,
auth_guard::login,
auth_guard::admin_check,

View File

@ -57,7 +57,7 @@
<h5 class="card-title"><i class="fa fa-users"></i> Configuration</h5>
<div class="col-sm-8 mx-auto" style="padding: 4px; margin-bottom: 4px;" id="controls">
<a href="#" class="btn btn-primary"><i class="fa fa-save"></i> Save ispConfig.py</a>&nbsp;
<a href="#" class="btn btn-primary" id="btnSaveIspConfig"><i class="fa fa-save"></i> Save ispConfig.py</a>&nbsp;
<a href="#" class="btn btn-danger"><i class="fa fa-save"></i> Save /etc/lqos</a>&nbsp;
<a href="#" class="btn btn-primary"><i class="fa fa-refresh"></i> Reload LibreQoS</a>&nbsp;
<a href="#" class="btn btn-danger"><i class="fa fa-refresh"></i> (Re)Start lqosd</a>
@ -383,6 +383,39 @@
if (!is_admin) {
$("#controls").html("<p class='alert alert-danger' role='alert'>You have to be an administrative user to change configuration.");
$("#userManager").html("<p class='alert alert-danger' role='alert'>Only administrators can see/change user information.");
} else {
// Handle Saving ispConfig.py
$("#btnSaveIspConfig").on('click', (data) => {
let new_config = python_config;
new_config.isp_interface = $("#nicCore").val();
new_config.internet_interface = $("#nicInternet").val();
new_config.on_a_stick_mode = $("#onAStick").prop('checked');
new_config.stick_vlans[0] = Number($("#StickVLANCore").val());
new_config.stick_vlans[1] = Number($("#StickVLANInternet").val());
new_config.sqm = $("#sqmMode").val();
new_config.total_download_mbps = Number($("#maxDownload").val());
new_config.total_upload_mbps = Number($("#maxUpload").val());
new_config.monitor_mode = $("#monitorMode").prop('checked');
new_config.generated_download_mbps = Number($("#generatedDownload").val());
new_config.generated_upload_mbps = Number($("#generatedUpload").val());
new_config.use_binpacking = $("#binpacking").prop('checked');
new_config.enable_shell_commands = $("#actualShellCommands").prop('checked');
new_config.run_as_sudo = $("#useSudo").prop('checked');
new_config.override_queue_count = Number($("#overrideQueues").val());
$.ajax({
type: "POST",
url: "/api/python_config",
data: JSON.stringify(new_config),
success: (data) => {
if (data == "ERROR") {
alert("Unable to create a first user.")
} else {
alert("Save Successful. Original backed up in ispConfig.py.backup. The window will now reload with the new configuration.");
window.location.reload()
}
}
})
});
}
$.get("/api/python_config", (data) => {
python_config = data;