1 use std
::{env, fs::remove_dir_all, path::Path}
;
3 use anyhow
::{bail, Error}
;
6 use proxmox_router
::cli
::{
7 default_table_format_options
, format_and_print_result_full
, get_output_format
, CliCommand
,
8 CliCommandMap
, ColumnConfig
, CommandLineInterface
, OUTPUT_FORMAT
,
10 use proxmox_schema
::{api, param_bail, ApiType, ArraySchema, ReturnType}
;
12 use proxmox_offline_mirror
::{
13 config
::{MediaConfig, MediaConfigUpdater, MirrorConfig, MirrorConfigUpdater}
,
15 types
::{MEDIA_ID_SCHEMA, MIRROR_ID_SCHEMA}
,
18 pub fn get_config_path() -> String
{
19 env
::var("PROXMOX_OFFLINE_MIRROR_CONFIG")
20 .unwrap_or_else(|_
| "/etc/proxmox-offline-mirror.cfg".to_string())
23 pub const LIST_MIRRORS_RETURN_TYPE
: ReturnType
= ReturnType
{
25 schema
: &ArraySchema
::new("Returns the list of mirrors.", &MirrorConfig
::API_SCHEMA
).schema(),
28 pub const SHOW_MIRROR_RETURN_TYPE
: ReturnType
= ReturnType
{
29 schema
: &MirrorConfig
::API_SCHEMA
,
33 pub const LIST_MEDIA_RETURN_TYPE
: ReturnType
= ReturnType
{
35 schema
: &ArraySchema
::new("Returns the list of mirrors.", &MediaConfig
::API_SCHEMA
).schema(),
38 pub const SHOW_MEDIUM_RETURN_TYPE
: ReturnType
= ReturnType
{
39 schema
: &MediaConfig
::API_SCHEMA
,
49 description
: "Path to mirroring config file.",
52 schema
: OUTPUT_FORMAT
,
58 /// List configured mirrors
59 async
fn list_mirror(config
: Option
<String
>, param
: Value
) -> Result
<Value
, Error
> {
60 let config
= config
.unwrap_or_else(get_config_path
);
62 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
63 let config
: Vec
<MirrorConfig
> = config
.convert_to_typed_array("mirror")?
;
65 let output_format
= get_output_format(¶m
);
66 let options
= default_table_format_options()
67 .column(ColumnConfig
::new("id").header("ID"))
68 .column(ColumnConfig
::new("repository"))
69 .column(ColumnConfig
::new("base-dir"))
70 .column(ColumnConfig
::new("verify"))
71 .column(ColumnConfig
::new("sync"));
73 format_and_print_result_full(
74 &mut serde_json
::json
!(config
),
75 &LIST_MIRRORS_RETURN_TYPE
,
89 description
: "Path to mirroring config file.",
92 schema
: MIRROR_ID_SCHEMA
,
95 schema
: OUTPUT_FORMAT
,
101 /// Show full mirror config
102 async
fn show_mirror(config
: Option
<String
>, id
: String
, param
: Value
) -> Result
<Value
, Error
> {
103 let config
= config
.unwrap_or_else(get_config_path
);
105 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
106 let mut config
= config
.lookup_json("mirror", &id
)?
;
108 let output_format
= get_output_format(¶m
);
109 format_and_print_result_full(
111 &SHOW_MIRROR_RETURN_TYPE
,
113 &default_table_format_options(),
125 description
: "Path to mirroring config file.",
132 schema
: OUTPUT_FORMAT
,
138 /// Create new mirror config entry.
140 config
: Option
<String
>,
143 ) -> Result
<Value
, Error
> {
144 let config
= config
.unwrap_or_else(get_config_path
);
146 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config
)?
;
148 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
150 if section_config
.sections
.get(&data
.id
).is_some() {
151 param_bail
!("name", "mirror config entry '{}' already exists.", data
.id
);
154 mirror
::init(&data
)?
;
156 section_config
.set_data(&data
.id
, "mirror", &data
)?
;
157 proxmox_offline_mirror
::config
::save_config(&config
, §ion_config
)?
;
168 description
: "Path to mirroring config file.",
171 schema
: MIRROR_ID_SCHEMA
,
175 description
: "Remove mirror data as well.",
178 schema
: OUTPUT_FORMAT
,
184 /// Remove mirror config entry.
185 async
fn remove_mirror(
186 config
: Option
<String
>,
190 ) -> Result
<Value
, Error
> {
191 let config_file
= config
.unwrap_or_else(get_config_path
);
193 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
195 // TODO (optionally?) remove media entries?
196 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
197 match section_config
.lookup
::<MirrorConfig
>("mirror", &id
) {
200 mirror
::destroy(&config
)?
;
203 section_config
.sections
.remove(&id
);
206 param_bail
!("id", "mirror config entry '{}' does not exist!", id
);
210 proxmox_offline_mirror
::config
::save_config(&config_file
, §ion_config
)?
;
221 description
: "Path to mirroring config file.",
224 schema
: MIRROR_ID_SCHEMA
,
227 type: MirrorConfigUpdater
,
233 /// Update mirror config entry.
234 pub fn update_mirror(
235 update
: MirrorConfigUpdater
,
236 config
: Option
<String
>,
238 ) -> Result
<(), Error
> {
239 let config_file
= config
.unwrap_or_else(get_config_path
);
241 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
243 let (mut config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
245 let mut data
: MirrorConfig
= config
.lookup("mirror", &id
)?
;
247 if let Some(key_path
) = update
.key_path
{
248 data
.key_path
= key_path
250 if let Some(repository
) = update
.repository
{
251 data
.repository
= repository
253 if let Some(base_dir
) = update
.base_dir
{
254 data
.base_dir
= base_dir
256 if let Some(architectures
) = update
.architectures
{
257 data
.architectures
= architectures
259 if let Some(sync
) = update
.sync
{
262 if let Some(verify
) = update
.verify
{
265 if let Some(ignore_errors
) = update
.ignore_errors
{
266 data
.ignore_errors
= ignore_errors
269 config
.set_data(&id
, "mirror", &data
)?
;
270 proxmox_offline_mirror
::config
::save_config(&config_file
, &config
)?
;
281 description
: "Path to mirroring config file.",
284 schema
: OUTPUT_FORMAT
,
290 /// List configured media.
291 async
fn list_media(config
: Option
<String
>, param
: Value
) -> Result
<Value
, Error
> {
292 let config
= config
.unwrap_or_else(get_config_path
);
294 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
295 let config
: Vec
<MediaConfig
> = config
.convert_to_typed_array("medium")?
;
297 let output_format
= get_output_format(¶m
);
298 let options
= default_table_format_options()
299 .column(ColumnConfig
::new("id").header("ID"))
300 .column(ColumnConfig
::new("mountpoint"))
301 .column(ColumnConfig
::new("mirrors"))
302 .column(ColumnConfig
::new("verify"))
303 .column(ColumnConfig
::new("sync"));
305 format_and_print_result_full(
306 &mut serde_json
::json
!(config
),
307 &LIST_MEDIA_RETURN_TYPE
,
321 description
: "Path to mirroring config file.",
324 schema
: MEDIA_ID_SCHEMA
,
327 schema
: OUTPUT_FORMAT
,
333 /// Show full medium config entry.
334 async
fn show_medium(config
: Option
<String
>, id
: String
, param
: Value
) -> Result
<Value
, Error
> {
335 let config
= config
.unwrap_or_else(get_config_path
);
337 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
338 let mut config
= config
.lookup_json("medium", &id
)?
;
340 let output_format
= get_output_format(¶m
);
341 format_and_print_result_full(
343 &SHOW_MEDIUM_RETURN_TYPE
,
345 &default_table_format_options(),
357 description
: "Path to mirroring config file.",
364 schema
: OUTPUT_FORMAT
,
370 /// Create new medium config entry.
372 config
: Option
<String
>,
375 ) -> Result
<Value
, Error
> {
376 let config
= config
.unwrap_or_else(get_config_path
);
378 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config
)?
;
380 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
382 if section_config
.sections
.get(&data
.id
).is_some() {
383 param_bail
!("name", "config section '{}' already exists.", data
.id
);
386 // TODO check mountpoint and mirrors exist?
388 section_config
.set_data(&data
.id
, "medium", &data
)?
;
389 proxmox_offline_mirror
::config
::save_config(&config
, §ion_config
)?
;
400 description
: "Path to mirroring config file.",
403 schema
: MEDIA_ID_SCHEMA
,
407 description
: "Remove ALL DATA on medium as well.",
410 schema
: OUTPUT_FORMAT
,
416 /// Remove medium config entry.
417 async
fn remove_medium(
418 config
: Option
<String
>,
422 ) -> Result
<Value
, Error
> {
423 let config_file
= config
.unwrap_or_else(get_config_path
);
425 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
427 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
428 match section_config
.lookup
::<MediaConfig
>("medium", &id
) {
431 let medium_base
= Path
::new(&medium
.mountpoint
);
432 if !medium_base
.exists() {
433 bail
!("Medium mountpoint doesn't exist.");
435 remove_dir_all(medium_base
)?
;
438 section_config
.sections
.remove(&id
);
441 param_bail
!("id", "config section '{}' does not exist!", id
);
445 proxmox_offline_mirror
::config
::save_config(&config_file
, §ion_config
)?
;
456 description
: "Path to mirroring config file.",
459 schema
: MEDIA_ID_SCHEMA
,
462 type: MediaConfigUpdater
,
468 /// Update medium config entry.
469 pub fn update_medium(
470 update
: MediaConfigUpdater
,
471 config
: Option
<String
>,
473 ) -> Result
<(), Error
> {
474 let config_file
= config
.unwrap_or_else(get_config_path
);
476 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
478 let (mut config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
480 let mut data
: MediaConfig
= config
.lookup("medium", &id
)?
;
482 if let Some(mountpoint
) = update
.mountpoint
{
483 data
.mountpoint
= mountpoint
485 if let Some(mirrors
) = update
.mirrors
{
486 data
.mirrors
= mirrors
488 if let Some(sync
) = update
.sync
{
491 if let Some(verify
) = update
.verify
{
495 config
.set_data(&id
, "medium", &data
)?
;
496 proxmox_offline_mirror
::config
::save_config(&config_file
, &config
)?
;
501 pub fn config_commands() -> CommandLineInterface
{
502 let mirror_cmd_def
= CliCommandMap
::new()
503 .insert("list", CliCommand
::new(&API_METHOD_LIST_MIRROR
))
504 .insert("add", CliCommand
::new(&API_METHOD_ADD_MIRROR
))
505 .insert("show", CliCommand
::new(&API_METHOD_SHOW_MIRROR
))
506 .insert("remove", CliCommand
::new(&API_METHOD_REMOVE_MIRROR
))
507 .insert("update", CliCommand
::new(&API_METHOD_UPDATE_MIRROR
));
509 let media_cmd_def
= CliCommandMap
::new()
510 .insert("list", CliCommand
::new(&API_METHOD_LIST_MEDIA
))
511 .insert("add", CliCommand
::new(&API_METHOD_ADD_MEDIUM
))
512 .insert("show", CliCommand
::new(&API_METHOD_SHOW_MEDIUM
))
513 .insert("remove", CliCommand
::new(&API_METHOD_REMOVE_MEDIUM
))
514 .insert("update", CliCommand
::new(&API_METHOD_UPDATE_MEDIUM
));
516 let cmd_def
= CliCommandMap
::new()
517 .insert("media", media_cmd_def
)
518 .insert("mirror", mirror_cmd_def
);