add systemd configuration file parser/writer, start job configuration

This commit is contained in:
Dietmar Maurer
2020-05-12 13:07:49 +02:00
parent 7f5a27d302
commit f486e9e50e
7 changed files with 399 additions and 0 deletions

2
src/tools/systemd.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod types;
pub mod parser;

View File

@ -0,0 +1,81 @@
use anyhow::Error;
use lazy_static::lazy_static;
use super::types::*;
use proxmox::api::{
schema::*,
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
}
};
use proxmox::tools::{fs::replace_file, fs::CreateOptions};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
fn init() -> SectionConfig {
let mut config = SectionConfig::with_systemd_syntax(&SYSTEMD_SECTION_NAME_SCHEMA);
match SystemdUnitSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Unit".to_string(), obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdInstallSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Install".to_string(), obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdServiceSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Service".to_string(), obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
match SystemdTimerSection::API_SCHEMA {
Schema::Object(ref obj_schema) => {
let plugin = SectionConfigPlugin::new("Timer".to_string(), obj_schema);
config.register_plugin(plugin);
}
_ => unreachable!(),
};
config
}
pub fn parse_systemd_config(filename: &str) -> Result<SectionConfigData, Error> {
let raw = proxmox::tools::fs::file_get_contents(filename)?;
let input = String::from_utf8(raw)?;
let data = CONFIG.parse(filename, &input)?;
Ok(data)
}
pub fn save_systemd_config(filename: &str, config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(filename, &config)?;
let mode = nix::sys::stat::Mode::from_bits_truncate(0o0644);
// set the correct owner/group/permissions while saving file, owner(rw) = root
let options = CreateOptions::new()
.perm(mode)
.owner(nix::unistd::ROOT);
replace_file(filename, raw.as_bytes(), options)?;
Ok(())
}

140
src/tools/systemd/types.rs Normal file
View File

@ -0,0 +1,140 @@
use serde::{Serialize, Deserialize};
use proxmox::api::{ api, schema::* };
use crate::api2::types::SINGLE_LINE_COMMENT_FORMAT;
pub const SYSTEMD_SECTION_NAME_SCHEMA: Schema = StringSchema::new(
"Section name")
.format(&ApiStringFormat::Enum(&[
EnumEntry::new("Unit", "Unit"),
EnumEntry::new("Timer", "Timer"),
EnumEntry::new("Install", "Install"),
EnumEntry::new("Service", "Service")]))
.schema();
pub const SYSTEMD_STRING_SCHEMA: Schema =
StringSchema::new("Systemd configuration value.")
.format(&SINGLE_LINE_COMMENT_FORMAT)
.schema();
pub const SYSTEMD_STRING_ARRAY_SCHEMA: Schema = ArraySchema::new(
"Array of Strings", &SYSTEMD_STRING_SCHEMA)
.schema();
#[api(
properties: {
"OnCalendar": {
type: Array,
optional: true,
items: {
description: "Calendar event expression.",
type: String,
},
},
},
)]
#[derive(Serialize, Deserialize, Default)]
#[allow(non_snake_case)]
/// Systemd Timer Section
pub struct SystemdTimerSection {
/// Calender event list.
#[serde(skip_serializing_if="Option::is_none")]
pub OnCalendar: Option<Vec<String>>,
/// If true, the time when the service unit was last triggered is stored on disk.
#[serde(skip_serializing_if="Option::is_none")]
pub Persistent: Option<bool>,
}
#[api(
properties: {
"Type": {
type: ServiceStartup,
optional: true,
},
"ExecStart": {
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
optional: true,
},
}
)]
#[derive(Serialize, Deserialize, Default)]
#[allow(non_snake_case)]
/// Systemd Service Section
pub struct SystemdServiceSection {
/// The process start-up type for this service unit.
#[serde(skip_serializing_if="Option::is_none")]
pub Type: Option<ServiceStartup>,
#[serde(skip_serializing_if="Option::is_none")]
pub ExecStart: Option<Vec<String>>,
}
#[api()]
#[derive(Serialize, Deserialize, Default)]
#[allow(non_snake_case)]
/// Systemd Unit Section
pub struct SystemdUnitSection {
/// A human readable name for the unit.
pub Description: String,
/// Check whether the system has AC power.
#[serde(skip_serializing_if="Option::is_none")]
pub ConditionACPower: Option<bool>,
}
#[api(
properties: {
"Alias": {
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
optional: true,
},
"Also": {
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
optional: true,
},
"DefaultInstance": {
optional: true,
},
"WantedBy": {
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
optional: true,
},
"RequiredBy": {
schema: SYSTEMD_STRING_ARRAY_SCHEMA,
optional: true,
},
},
)]
#[derive(Serialize, Deserialize, Default)]
#[allow(non_snake_case)]
/// Systemd Install Section
pub struct SystemdInstallSection {
#[serde(skip_serializing_if="Option::is_none")]
pub Alias: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
pub Also: Option<Vec<String>>,
/// DefaultInstance for template unit.
#[serde(skip_serializing_if="Option::is_none")]
pub DefaultInstance: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub WantedBy: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")]
pub RequiredBy: Option<Vec<String>>,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Node Power command type.
pub enum ServiceStartup {
/// Simple fork
Simple,
/// Like fork, but wait until exec succeeds
Exec,
/// Fork daemon
Forking,
/// Like 'simple', but consider the unit up after the process exits.
Oneshot,
/// Like 'simple', but use DBUS to synchronize startup.
Dbus,
/// Like 'simple', but use sd_notify to synchronize startup.
Notify,
}