]>
Commit | Line | Data |
---|---|---|
9143507d | 1 | use clap::ValueEnum; |
c7edc2e1 AL |
2 | use proxmox_installer_common::{ |
3 | options::{BtrfsRaidLevel, FsType, ZfsChecksumOption, ZfsCompressOption, ZfsRaidLevel}, | |
4 | utils::{CidrAddress, Fqdn}, | |
5 | }; | |
6 | use serde::{Deserialize, Serialize}; | |
7 | use std::{collections::BTreeMap, net::IpAddr}; | |
8 | ||
0d555de1 WB |
9 | // BTreeMap is used to store filters as the order of the filters will be stable, compared to |
10 | // storing them in a HashMap | |
c7edc2e1 AL |
11 | |
12 | #[derive(Clone, Deserialize, Debug)] | |
20c927b7 | 13 | #[serde(rename_all = "kebab-case", deny_unknown_fields)] |
c7edc2e1 AL |
14 | pub struct Answer { |
15 | pub global: Global, | |
16 | pub network: Network, | |
17 | #[serde(rename = "disk-setup")] | |
18 | pub disks: Disks, | |
19 | } | |
20 | ||
21 | #[derive(Clone, Deserialize, Debug)] | |
20c927b7 | 22 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
23 | pub struct Global { |
24 | pub country: String, | |
25 | pub fqdn: Fqdn, | |
26 | pub keyboard: String, | |
27 | pub mailto: String, | |
28 | pub timezone: String, | |
77ca432f | 29 | pub root_password: String, |
c7edc2e1 AL |
30 | #[serde(default)] |
31 | pub reboot_on_error: bool, | |
79bea2a7 CH |
32 | #[serde(default)] |
33 | pub root_ssh_keys: Vec<String>, | |
c7edc2e1 AL |
34 | } |
35 | ||
98036cb1 TL |
36 | #[derive(Clone, Deserialize, Debug, Default, PartialEq)] |
37 | #[serde(deny_unknown_fields)] | |
38 | enum NetworkConfigMode { | |
39 | #[default] | |
40 | #[serde(rename = "from-dhcp")] | |
41 | FromDhcp, | |
42 | #[serde(rename = "from-answer")] | |
43 | FromAnswer, | |
44 | } | |
45 | ||
c7edc2e1 | 46 | #[derive(Clone, Deserialize, Debug)] |
20c927b7 | 47 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
48 | struct NetworkInAnswer { |
49 | #[serde(default)] | |
98036cb1 | 50 | pub source: NetworkConfigMode, |
c7edc2e1 AL |
51 | pub cidr: Option<CidrAddress>, |
52 | pub dns: Option<IpAddr>, | |
53 | pub gateway: Option<IpAddr>, | |
54 | pub filter: Option<BTreeMap<String, String>>, | |
55 | } | |
56 | ||
57 | #[derive(Clone, Deserialize, Debug)] | |
20c927b7 | 58 | #[serde(try_from = "NetworkInAnswer", deny_unknown_fields)] |
c7edc2e1 AL |
59 | pub struct Network { |
60 | pub network_settings: NetworkSettings, | |
61 | } | |
62 | ||
63 | impl TryFrom<NetworkInAnswer> for Network { | |
64 | type Error = &'static str; | |
65 | ||
98036cb1 TL |
66 | fn try_from(network: NetworkInAnswer) -> Result<Self, Self::Error> { |
67 | if network.source == NetworkConfigMode::FromAnswer { | |
68 | if network.cidr.is_none() { | |
c7edc2e1 AL |
69 | return Err("Field 'cidr' must be set."); |
70 | } | |
98036cb1 | 71 | if network.dns.is_none() { |
c7edc2e1 AL |
72 | return Err("Field 'dns' must be set."); |
73 | } | |
98036cb1 | 74 | if network.gateway.is_none() { |
c7edc2e1 AL |
75 | return Err("Field 'gateway' must be set."); |
76 | } | |
98036cb1 | 77 | if network.filter.is_none() { |
c7edc2e1 AL |
78 | return Err("Field 'filter' must be set."); |
79 | } | |
80 | ||
81 | Ok(Network { | |
82 | network_settings: NetworkSettings::Manual(NetworkManual { | |
98036cb1 TL |
83 | cidr: network.cidr.unwrap(), |
84 | dns: network.dns.unwrap(), | |
85 | gateway: network.gateway.unwrap(), | |
86 | filter: network.filter.unwrap(), | |
c7edc2e1 AL |
87 | }), |
88 | }) | |
89 | } else { | |
90 | Ok(Network { | |
98036cb1 | 91 | network_settings: NetworkSettings::FromDhcp, |
c7edc2e1 AL |
92 | }) |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | #[derive(Clone, Debug)] | |
98 | pub enum NetworkSettings { | |
98036cb1 | 99 | FromDhcp, |
c7edc2e1 AL |
100 | Manual(NetworkManual), |
101 | } | |
102 | ||
103 | #[derive(Clone, Debug)] | |
104 | pub struct NetworkManual { | |
105 | pub cidr: CidrAddress, | |
106 | pub dns: IpAddr, | |
107 | pub gateway: IpAddr, | |
108 | pub filter: BTreeMap<String, String>, | |
109 | } | |
110 | ||
111 | #[derive(Clone, Debug, Deserialize)] | |
20c927b7 | 112 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
113 | pub struct DiskSetup { |
114 | pub filesystem: Filesystem, | |
115 | #[serde(default)] | |
116 | pub disk_list: Vec<String>, | |
117 | pub filter: Option<BTreeMap<String, String>>, | |
118 | pub filter_match: Option<FilterMatch>, | |
119 | pub zfs: Option<ZfsOptions>, | |
120 | pub lvm: Option<LvmOptions>, | |
121 | pub btrfs: Option<BtrfsOptions>, | |
122 | } | |
123 | ||
124 | #[derive(Clone, Debug, Deserialize)] | |
20c927b7 | 125 | #[serde(try_from = "DiskSetup", deny_unknown_fields)] |
c7edc2e1 AL |
126 | pub struct Disks { |
127 | pub fs_type: FsType, | |
128 | pub disk_selection: DiskSelection, | |
129 | pub filter_match: Option<FilterMatch>, | |
130 | pub fs_options: FsOptions, | |
131 | } | |
132 | ||
133 | impl TryFrom<DiskSetup> for Disks { | |
134 | type Error = &'static str; | |
135 | ||
136 | fn try_from(source: DiskSetup) -> Result<Self, Self::Error> { | |
137 | if source.disk_list.is_empty() && source.filter.is_none() { | |
138 | return Err("Need either 'disk_list' or 'filter' set"); | |
139 | } | |
140 | if !source.disk_list.is_empty() && source.filter.is_some() { | |
141 | return Err("Cannot use both, 'disk_list' and 'filter'"); | |
142 | } | |
143 | ||
144 | let disk_selection = if !source.disk_list.is_empty() { | |
145 | DiskSelection::Selection(source.disk_list.clone()) | |
146 | } else { | |
147 | DiskSelection::Filter(source.filter.clone().unwrap()) | |
148 | }; | |
149 | ||
150 | let lvm_checks = |source: &DiskSetup| -> Result<(), Self::Error> { | |
151 | if source.zfs.is_some() || source.btrfs.is_some() { | |
152 | return Err("make sure only 'lvm' options are set"); | |
153 | } | |
154 | if source.disk_list.len() > 1 { | |
155 | return Err("make sure to define only one disk for ext4 and xfs"); | |
156 | } | |
157 | Ok(()) | |
158 | }; | |
159 | // TODO: improve checks for foreign FS options. E.g. less verbose and handling new FS types | |
160 | // automatically | |
161 | let (fs, fs_options) = match source.filesystem { | |
162 | Filesystem::Xfs => { | |
163 | lvm_checks(&source)?; | |
164 | ( | |
165 | FsType::Xfs, | |
810c860d | 166 | FsOptions::LVM(source.lvm.unwrap_or_default()), |
c7edc2e1 AL |
167 | ) |
168 | } | |
169 | Filesystem::Ext4 => { | |
170 | lvm_checks(&source)?; | |
171 | ( | |
172 | FsType::Ext4, | |
810c860d | 173 | FsOptions::LVM(source.lvm.unwrap_or_default()), |
c7edc2e1 AL |
174 | ) |
175 | } | |
176 | Filesystem::Zfs => { | |
177 | if source.lvm.is_some() || source.btrfs.is_some() { | |
178 | return Err("make sure only 'zfs' options are set"); | |
179 | } | |
180 | match source.zfs { | |
181 | None | Some(ZfsOptions { raid: None, .. }) => { | |
182 | return Err("ZFS raid level 'zfs.raid' must be set") | |
183 | } | |
184 | Some(opts) => (FsType::Zfs(opts.raid.unwrap()), FsOptions::ZFS(opts)), | |
185 | } | |
186 | } | |
187 | Filesystem::Btrfs => { | |
188 | if source.zfs.is_some() || source.lvm.is_some() { | |
189 | return Err("make sure only 'btrfs' options are set"); | |
190 | } | |
191 | match source.btrfs { | |
192 | None | Some(BtrfsOptions { raid: None, .. }) => { | |
193 | return Err("BTRFS raid level 'btrfs.raid' must be set") | |
194 | } | |
195 | Some(opts) => (FsType::Btrfs(opts.raid.unwrap()), FsOptions::BTRFS(opts)), | |
196 | } | |
197 | } | |
198 | }; | |
199 | ||
200 | let res = Disks { | |
201 | fs_type: fs, | |
202 | disk_selection, | |
203 | filter_match: source.filter_match, | |
204 | fs_options, | |
205 | }; | |
206 | Ok(res) | |
207 | } | |
208 | } | |
209 | ||
210 | #[derive(Clone, Debug)] | |
211 | pub enum FsOptions { | |
212 | LVM(LvmOptions), | |
213 | ZFS(ZfsOptions), | |
214 | BTRFS(BtrfsOptions), | |
215 | } | |
216 | ||
217 | #[derive(Clone, Debug)] | |
218 | pub enum DiskSelection { | |
219 | Selection(Vec<String>), | |
220 | Filter(BTreeMap<String, String>), | |
221 | } | |
9143507d | 222 | #[derive(Clone, Deserialize, Debug, PartialEq, ValueEnum)] |
20c927b7 | 223 | #[serde(rename_all = "lowercase", deny_unknown_fields)] |
c7edc2e1 AL |
224 | pub enum FilterMatch { |
225 | Any, | |
226 | All, | |
227 | } | |
228 | ||
229 | #[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] | |
20c927b7 | 230 | #[serde(rename_all = "lowercase", deny_unknown_fields)] |
c7edc2e1 AL |
231 | pub enum Filesystem { |
232 | Ext4, | |
233 | Xfs, | |
234 | Zfs, | |
235 | Btrfs, | |
236 | } | |
237 | ||
238 | #[derive(Clone, Copy, Default, Deserialize, Debug)] | |
20c927b7 | 239 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
240 | pub struct ZfsOptions { |
241 | pub raid: Option<ZfsRaidLevel>, | |
242 | pub ashift: Option<usize>, | |
243 | pub arc_max: Option<usize>, | |
244 | pub checksum: Option<ZfsChecksumOption>, | |
245 | pub compress: Option<ZfsCompressOption>, | |
246 | pub copies: Option<usize>, | |
247 | pub hdsize: Option<f64>, | |
248 | } | |
249 | ||
250 | #[derive(Clone, Copy, Default, Deserialize, Serialize, Debug)] | |
20c927b7 | 251 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
252 | pub struct LvmOptions { |
253 | pub hdsize: Option<f64>, | |
254 | pub swapsize: Option<f64>, | |
255 | pub maxroot: Option<f64>, | |
256 | pub maxvz: Option<f64>, | |
257 | pub minfree: Option<f64>, | |
258 | } | |
259 | ||
260 | #[derive(Clone, Copy, Default, Deserialize, Debug)] | |
20c927b7 | 261 | #[serde(deny_unknown_fields)] |
c7edc2e1 AL |
262 | pub struct BtrfsOptions { |
263 | pub hdsize: Option<f64>, | |
264 | pub raid: Option<BtrfsRaidLevel>, | |
265 | } |