11 use serde
::{de, Deserialize, Deserializer, Serialize, Serializer}
;
15 AdvancedBootdiskOptions
, BtrfsRaidLevel
, Disk
, FsType
, InstallerOptions
,
16 ZfsBootdiskOptions
, ZfsChecksumOption
, ZfsCompressOption
, ZfsRaidLevel
,
18 utils
::{CidrAddress, Fqdn}
,
21 #[allow(clippy::upper_case_acronyms)]
22 #[derive(Clone, Copy, Deserialize, PartialEq)]
23 #[serde(rename_all = "lowercase")]
24 pub enum ProxmoxProduct
{
31 pub fn default_hostname(self) -> &'
static str {
40 #[derive(Clone, Deserialize)]
41 pub struct ProductConfig
{
43 pub product
: ProxmoxProduct
,
44 #[serde(deserialize_with = "deserialize_bool_from_int")]
45 pub enable_btrfs
: bool
,
48 #[derive(Clone, Deserialize)]
51 pub isorelease
: String
,
54 /// Paths in the ISO environment containing installer data.
55 #[derive(Clone, Deserialize)]
56 pub struct IsoLocations
{
60 #[derive(Clone, Deserialize)]
61 pub struct SetupInfo
{
62 #[serde(rename = "product-cfg")]
63 pub config
: ProductConfig
,
64 #[serde(rename = "iso-info")]
65 pub iso_info
: IsoInfo
,
66 pub locations
: IsoLocations
,
69 #[derive(Clone, Deserialize)]
70 pub struct CountryInfo
{
77 #[derive(Clone, Deserialize, Eq, PartialEq)]
78 pub struct KeyboardMapping
{
80 #[serde(rename = "kvm")]
82 #[serde(rename = "x11")]
83 pub xkb_layout
: String
,
84 #[serde(rename = "x11var")]
85 pub xkb_variant
: String
,
88 impl cmp
::PartialOrd
for KeyboardMapping
{
89 fn partial_cmp(&self, other
: &Self) -> Option
<cmp
::Ordering
> {
90 self.name
.partial_cmp(&other
.name
)
94 impl cmp
::Ord
for KeyboardMapping
{
95 fn cmp(&self, other
: &Self) -> cmp
::Ordering
{
96 self.name
.cmp(&other
.name
)
100 #[derive(Clone, Deserialize)]
101 pub struct LocaleInfo
{
102 #[serde(deserialize_with = "deserialize_cczones_map")]
103 pub cczones
: HashMap
<String
, Vec
<String
>>,
104 #[serde(rename = "country")]
105 pub countries
: HashMap
<String
, CountryInfo
>,
106 pub kmap
: HashMap
<String
, KeyboardMapping
>,
110 struct InstallZfsOption
{
112 #[serde(serialize_with = "serialize_as_display")]
113 compress
: ZfsCompressOption
,
114 #[serde(serialize_with = "serialize_as_display")]
115 checksum
: ZfsChecksumOption
,
119 impl From
<ZfsBootdiskOptions
> for InstallZfsOption
{
120 fn from(opts
: ZfsBootdiskOptions
) -> Self {
123 compress
: opts
.compress
,
124 checksum
: opts
.checksum
,
130 /// See Proxmox::Install::Config
132 pub struct InstallConfig
{
135 #[serde(serialize_with = "serialize_fstype")]
138 #[serde(skip_serializing_if = "Option::is_none")]
139 swapsize
: Option
<f64>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 maxroot
: Option
<f64>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 minfree
: Option
<f64>,
144 #[serde(skip_serializing_if = "Option::is_none")]
147 #[serde(skip_serializing_if = "Option::is_none")]
148 zfs_opts
: Option
<InstallZfsOption
>,
151 serialize_with
= "serialize_disk_opt",
152 skip_serializing_if
= "Option::is_none"
154 target_hd
: Option
<Disk
>,
155 #[serde(skip_serializing_if = "HashMap::is_empty")]
156 disk_selection
: HashMap
<String
, String
>,
169 #[serde(serialize_with = "serialize_as_display")]
175 impl From
<InstallerOptions
> for InstallConfig
{
176 fn from(options
: InstallerOptions
) -> Self {
177 let mut config
= Self {
178 autoreboot
: options
.autoreboot
as usize,
180 filesys
: options
.bootdisk
.fstype
,
188 disk_selection
: HashMap
::new(),
190 country
: options
.timezone
.country
,
191 timezone
: options
.timezone
.timezone
,
192 keymap
: options
.timezone
.kb_layout
,
194 password
: options
.password
.root_password
,
195 mailto
: options
.password
.email
,
197 mngmt_nic
: options
.network
.ifname
,
203 .unwrap_or_else(|| crate::current_product().default_hostname())
205 domain
: options
.network
.fqdn
.domain(),
206 cidr
: options
.network
.address
,
207 gateway
: options
.network
.gateway
,
208 dns
: options
.network
.dns_server
,
211 match &options
.bootdisk
.advanced
{
212 AdvancedBootdiskOptions
::Lvm(lvm
) => {
213 config
.hdsize
= lvm
.total_size
;
214 config
.target_hd
= Some(options
.bootdisk
.disks
[0].clone());
215 config
.swapsize
= lvm
.swap_size
;
216 config
.maxroot
= lvm
.max_root_size
;
217 config
.minfree
= lvm
.min_lvm_free
;
218 config
.maxvz
= lvm
.max_data_size
;
220 AdvancedBootdiskOptions
::Zfs(zfs
) => {
221 config
.hdsize
= zfs
.disk_size
;
222 config
.zfs_opts
= Some(zfs
.clone().into());
224 for (i
, disk
) in options
.bootdisk
.disks
.iter().enumerate() {
227 .insert(i
.to_string(), disk
.index
.clone());
230 AdvancedBootdiskOptions
::Btrfs(btrfs
) => {
231 config
.hdsize
= btrfs
.disk_size
;
233 for (i
, disk
) in options
.bootdisk
.disks
.iter().enumerate() {
236 .insert(i
.to_string(), disk
.index
.clone());
245 pub fn read_json
<T
: for<'de
> Deserialize
<'de
>, P
: AsRef
<Path
>>(path
: P
) -> Result
<T
, String
> {
246 let file
= File
::open(path
).map_err(|err
| err
.to_string())?
;
247 let reader
= BufReader
::new(file
);
249 serde_json
::from_reader(reader
).map_err(|err
| format
!("failed to parse JSON: {err}"))
252 fn deserialize_bool_from_int
<'de
, D
>(deserializer
: D
) -> Result
<bool
, D
::Error
>
254 D
: Deserializer
<'de
>,
256 let val
: u32 = Deserialize
::deserialize(deserializer
)?
;
260 fn deserialize_cczones_map
<'de
, D
>(
262 ) -> Result
<HashMap
<String
, Vec
<String
>>, D
::Error
>
264 D
: Deserializer
<'de
>,
266 let map
: HashMap
<String
, HashMap
<String
, u32>> = Deserialize
::deserialize(deserializer
)?
;
268 let mut result
= HashMap
::new();
269 for (cc
, list
) in map
.into_iter() {
270 result
.insert(cc
, list
.into_keys().collect());
276 fn deserialize_disks_map
<'de
, D
>(deserializer
: D
) -> Result
<Vec
<Disk
>, D
::Error
>
278 D
: Deserializer
<'de
>,
280 let disks
= <Vec
<(usize, String
, f64, String
, f64, String
)>>::deserialize(deserializer
)?
;
284 |(index
, device
, size_mb
, model
, logical_bsize
, _syspath
)| Disk
{
285 index
: index
.to_string(),
286 size
: (size_mb
* logical_bsize
) / 1024. / 1024. / 1024.,
288 model
: (!model
.is_empty()).then_some(model
),
294 fn deserialize_cidr_list
<'de
, D
>(deserializer
: D
) -> Result
<Option
<Vec
<CidrAddress
>>, D
::Error
>
296 D
: Deserializer
<'de
>,
298 #[derive(Deserialize)]
299 struct CidrDescriptor
{
302 // family is implied anyway by parsing the address
305 let list
: Vec
<CidrDescriptor
> = Deserialize
::deserialize(deserializer
)?
;
307 let mut result
= Vec
::with_capacity(list
.len());
312 .map_err(|err
| de
::Error
::custom(format
!("{:?}", err
)))?
;
315 CidrAddress
::new(ip_addr
, desc
.prefix
)
316 .map_err(|err
| de
::Error
::custom(format
!("{:?}", err
)))?
,
323 fn serialize_disk_opt
<S
>(value
: &Option
<Disk
>, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
327 if let Some(disk
) = value
{
328 serializer
.serialize_str(&disk
.path
)
330 serializer
.serialize_none()
334 fn serialize_fstype
<S
>(value
: &FsType
, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
339 let value
= match value
{
340 // proxinstall::$fssetup
343 // proxinstall::get_zfs_raid_setup()
344 Zfs(ZfsRaidLevel
::Raid0
) => "zfs (RAID0)",
345 Zfs(ZfsRaidLevel
::Raid1
) => "zfs (RAID1)",
346 Zfs(ZfsRaidLevel
::Raid10
) => "zfs (RAID10)",
347 Zfs(ZfsRaidLevel
::RaidZ
) => "zfs (RAIDZ-1)",
348 Zfs(ZfsRaidLevel
::RaidZ2
) => "zfs (RAIDZ-2)",
349 Zfs(ZfsRaidLevel
::RaidZ3
) => "zfs (RAIDZ-3)",
350 // proxinstall::get_btrfs_raid_setup()
351 Btrfs(BtrfsRaidLevel
::Raid0
) => "btrfs (RAID0)",
352 Btrfs(BtrfsRaidLevel
::Raid1
) => "btrfs (RAID1)",
353 Btrfs(BtrfsRaidLevel
::Raid10
) => "btrfs (RAID10)",
356 serializer
.collect_str(value
)
359 fn serialize_as_display
<S
, T
>(value
: &T
, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
364 serializer
.collect_str(value
)
367 #[derive(Clone, Deserialize)]
368 pub struct RuntimeInfo
{
369 /// Detected country if available.
370 pub country
: Option
<String
>,
372 /// Maps devices to their information.
373 #[serde(deserialize_with = "deserialize_disks_map")]
374 pub disks
: Vec
<Disk
>,
376 /// Network addresses, gateways and DNS info.
377 pub network
: NetworkInfo
,
379 /// Total memory of the system in MiB.
380 pub total_memory
: usize,
382 /// Whether the CPU supports hardware-accelerated virtualization
383 #[serde(deserialize_with = "deserialize_bool_from_int")]
384 pub hvm_supported
: bool
,
387 #[derive(Clone, Deserialize)]
388 pub struct NetworkInfo
{
390 pub routes
: Option
<Routes
>,
392 /// Maps devices to their configuration, if it has a usable configuration.
393 /// (Contains no entries for devices with only link-local addresses.)
395 pub interfaces
: HashMap
<String
, Interface
>,
398 #[derive(Clone, Deserialize)]
400 #[serde(deserialize_with = "deserialize_invalid_value_as_none")]
401 pub domain
: Option
<Fqdn
>,
403 /// List of stringified IP addresses.
405 pub dns
: Vec
<IpAddr
>,
408 #[derive(Clone, Deserialize)]
411 pub gateway4
: Option
<Gateway
>,
414 pub gateway6
: Option
<Gateway
>,
417 #[derive(Clone, Deserialize)]
419 /// Outgoing network device.
422 /// Stringified gateway IP address.
426 #[derive(Clone, Deserialize)]
427 pub struct Interface
{
435 #[serde(deserialize_with = "deserialize_cidr_list")]
436 pub addresses
: Option
<Vec
<CidrAddress
>>,
439 fn deserialize_invalid_value_as_none
<'de
, D
, T
>(deserializer
: D
) -> Result
<Option
<T
>, D
::Error
>
441 D
: Deserializer
<'de
>,
444 Ok(Deserialize
::deserialize(deserializer
).ok())