231 lines
5.6 KiB
Rust
231 lines
5.6 KiB
Rust
use std::collections::BTreeMap as Map;
|
|
|
|
pub const TAKE_ALL: i16 = -1;
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct Config {
|
|
pub anti_phishing_code: String,
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub keymap: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub modules: Option<String>,
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub resolv_conf: Option<String>,
|
|
|
|
#[serde(default)]
|
|
pub vpns: Map<String, String>,
|
|
|
|
pub networks: Vec<Network>,
|
|
|
|
pub auths: Vec<Auth>,
|
|
#[serde(default)]
|
|
pub ssh: SSHServer,
|
|
|
|
#[serde(default)]
|
|
pub pre_lvm_crypt: Vec<CryptDev>,
|
|
#[serde(default)]
|
|
pub lvm: Vec<LvmVG>,
|
|
#[serde(default)]
|
|
pub crypt: Vec<CryptDev>,
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub signer_public_key: Option<String>,
|
|
|
|
pub bootstrap: Bootstrap,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct Auth {
|
|
pub name: String,
|
|
#[serde(alias = "sshKey")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub ssh_key: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub password: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct Network {
|
|
pub name: String,
|
|
pub interfaces: Vec<NetworkInterface>,
|
|
pub script: String,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct NetworkInterface {
|
|
pub var: String,
|
|
pub n: i16,
|
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
pub regexps: Vec<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub filter: Option<UdevFilter>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct SSHServer {
|
|
pub listen: String,
|
|
pub user_ca: Option<String>,
|
|
}
|
|
impl Default for SSHServer {
|
|
fn default() -> Self {
|
|
Self {
|
|
listen: "[::]:22".to_string(),
|
|
user_ca: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct LvmVG {
|
|
#[serde(alias = "vg")]
|
|
pub name: String,
|
|
pub pvs: LvmPV,
|
|
|
|
#[serde(default)]
|
|
pub defaults: LvmLVDefaults,
|
|
|
|
pub lvs: Vec<LvmLV>,
|
|
}
|
|
|
|
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
|
pub struct LvmLVDefaults {
|
|
#[serde(default)]
|
|
pub fs: Filesystem,
|
|
#[serde(default)]
|
|
pub raid: Raid,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum Filesystem {
|
|
Ext4,
|
|
Xfs,
|
|
Btrfs,
|
|
Other(String),
|
|
}
|
|
|
|
impl Filesystem {
|
|
pub fn fstype(&self) -> &str {
|
|
use Filesystem as F;
|
|
match self {
|
|
F::Ext4 => "ext4",
|
|
F::Xfs => "xfs",
|
|
F::Btrfs => "btrfs",
|
|
F::Other(t) => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for Filesystem {
|
|
fn default() -> Self {
|
|
Filesystem::Ext4
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct LvmLV {
|
|
pub name: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub fs: Option<Filesystem>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub raid: Option<Raid>,
|
|
#[serde(flatten)]
|
|
pub size: LvSize,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum LvSize {
|
|
Size(String),
|
|
Extents(String),
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct LvmPV {
|
|
pub n: i16,
|
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
pub regexps: Vec<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub udev: Option<UdevFilter>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct CryptDev {
|
|
pub name: String,
|
|
// hit the limit of enum representation here (flatten + enum variant case)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub dev: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub prefix: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub udev: Option<UdevFilter>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub optional: Option<bool>,
|
|
}
|
|
impl CryptDev {
|
|
pub fn filter(&self) -> DevFilter<'_> {
|
|
if let Some(dev) = self.dev.as_deref() {
|
|
DevFilter::Dev(dev)
|
|
} else if let Some(prefix) = self.prefix.as_deref() {
|
|
DevFilter::Prefix(prefix)
|
|
} else if let Some(udev) = self.udev.as_ref() {
|
|
DevFilter::Udev(udev)
|
|
} else {
|
|
DevFilter::None
|
|
}
|
|
}
|
|
|
|
pub fn optional(&self) -> bool {
|
|
self.optional.unwrap_or_else(|| match self.filter() {
|
|
DevFilter::None => true,
|
|
DevFilter::Dev(_) => false,
|
|
DevFilter::Prefix(_) => true,
|
|
DevFilter::Udev(_) => true,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_crypt_dev() {
|
|
for s in [
|
|
"name: sys0\ndev: /dev/sda\n",
|
|
"name: crypt-\nprefix: /dev/sd\n",
|
|
"name: crypt-${name}\nudev: !glob [ DEVNAME, /dev/sd* ]\n",
|
|
] {
|
|
let dev: CryptDev = serde_yaml::from_str(s).unwrap();
|
|
dev.filter();
|
|
dev.optional();
|
|
}
|
|
}
|
|
|
|
pub enum DevFilter<'t> {
|
|
None,
|
|
Dev(&'t str),
|
|
Prefix(&'t str),
|
|
Udev(&'t UdevFilter),
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum UdevFilter {
|
|
Eq(String, String),
|
|
Glob(String, String),
|
|
And(Vec<UdevFilter>),
|
|
Or(Vec<UdevFilter>),
|
|
Not(Box<UdevFilter>),
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
pub struct Raid {
|
|
pub mirrors: Option<u8>,
|
|
pub stripes: Option<u8>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
|
pub struct Bootstrap {
|
|
pub dev: String,
|
|
pub seed: Option<String>,
|
|
}
|