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
{
266 config
.set_data(&id
, "mirror", &data
)?
;
267 proxmox_offline_mirror
::config
::save_config(&config_file
, &config
)?
;
278 description
: "Path to mirroring config file.",
281 schema
: OUTPUT_FORMAT
,
287 /// List configured media.
288 async
fn list_media(config
: Option
<String
>, param
: Value
) -> Result
<Value
, Error
> {
289 let config
= config
.unwrap_or_else(get_config_path
);
291 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
292 let config
: Vec
<MediaConfig
> = config
.convert_to_typed_array("medium")?
;
294 let output_format
= get_output_format(¶m
);
295 let options
= default_table_format_options()
296 .column(ColumnConfig
::new("id").header("ID"))
297 .column(ColumnConfig
::new("mountpoint"))
298 .column(ColumnConfig
::new("mirrors"))
299 .column(ColumnConfig
::new("verify"))
300 .column(ColumnConfig
::new("sync"));
302 format_and_print_result_full(
303 &mut serde_json
::json
!(config
),
304 &LIST_MEDIA_RETURN_TYPE
,
318 description
: "Path to mirroring config file.",
321 schema
: MEDIA_ID_SCHEMA
,
324 schema
: OUTPUT_FORMAT
,
330 /// Show full medium config entry.
331 async
fn show_medium(config
: Option
<String
>, id
: String
, param
: Value
) -> Result
<Value
, Error
> {
332 let config
= config
.unwrap_or_else(get_config_path
);
334 let (config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
335 let mut config
= config
.lookup_json("medium", &id
)?
;
337 let output_format
= get_output_format(¶m
);
338 format_and_print_result_full(
340 &SHOW_MEDIUM_RETURN_TYPE
,
342 &default_table_format_options(),
354 description
: "Path to mirroring config file.",
361 schema
: OUTPUT_FORMAT
,
367 /// Create new medium config entry.
369 config
: Option
<String
>,
372 ) -> Result
<Value
, Error
> {
373 let config
= config
.unwrap_or_else(get_config_path
);
375 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config
)?
;
377 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config
)?
;
379 if section_config
.sections
.get(&data
.id
).is_some() {
380 param_bail
!("name", "config section '{}' already exists.", data
.id
);
383 // TODO check mountpoint and mirrors exist?
385 section_config
.set_data(&data
.id
, "medium", &data
)?
;
386 proxmox_offline_mirror
::config
::save_config(&config
, §ion_config
)?
;
397 description
: "Path to mirroring config file.",
400 schema
: MEDIA_ID_SCHEMA
,
404 description
: "Remove ALL DATA on medium as well.",
407 schema
: OUTPUT_FORMAT
,
413 /// Remove medium config entry.
414 async
fn remove_medium(
415 config
: Option
<String
>,
419 ) -> Result
<Value
, Error
> {
420 let config_file
= config
.unwrap_or_else(get_config_path
);
422 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
424 let (mut section_config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
425 match section_config
.lookup
::<MediaConfig
>("medium", &id
) {
428 let medium_base
= Path
::new(&medium
.mountpoint
);
429 if !medium_base
.exists() {
430 bail
!("Medium mountpoint doesn't exist.");
432 remove_dir_all(medium_base
)?
;
435 section_config
.sections
.remove(&id
);
438 param_bail
!("id", "config section '{}' does not exist!", id
);
442 proxmox_offline_mirror
::config
::save_config(&config_file
, §ion_config
)?
;
453 description
: "Path to mirroring config file.",
456 schema
: MEDIA_ID_SCHEMA
,
459 type: MediaConfigUpdater
,
465 /// Update medium config entry.
466 pub fn update_medium(
467 update
: MediaConfigUpdater
,
468 config
: Option
<String
>,
470 ) -> Result
<(), Error
> {
471 let config_file
= config
.unwrap_or_else(get_config_path
);
473 let _lock
= proxmox_offline_mirror
::config
::lock_config(&config_file
)?
;
475 let (mut config
, _digest
) = proxmox_offline_mirror
::config
::config(&config_file
)?
;
477 let mut data
: MediaConfig
= config
.lookup("medium", &id
)?
;
479 if let Some(mountpoint
) = update
.mountpoint
{
480 data
.mountpoint
= mountpoint
482 if let Some(mirrors
) = update
.mirrors
{
483 data
.mirrors
= mirrors
485 if let Some(sync
) = update
.sync
{
488 if let Some(verify
) = update
.verify
{
492 config
.set_data(&id
, "medium", &data
)?
;
493 proxmox_offline_mirror
::config
::save_config(&config_file
, &config
)?
;
498 pub fn config_commands() -> CommandLineInterface
{
499 let mirror_cmd_def
= CliCommandMap
::new()
500 .insert("list", CliCommand
::new(&API_METHOD_LIST_MIRROR
))
501 .insert("add", CliCommand
::new(&API_METHOD_ADD_MIRROR
))
502 .insert("show", CliCommand
::new(&API_METHOD_SHOW_MIRROR
))
503 .insert("remove", CliCommand
::new(&API_METHOD_REMOVE_MIRROR
))
504 .insert("update", CliCommand
::new(&API_METHOD_UPDATE_MIRROR
));
506 let media_cmd_def
= CliCommandMap
::new()
507 .insert("list", CliCommand
::new(&API_METHOD_LIST_MEDIA
))
508 .insert("add", CliCommand
::new(&API_METHOD_ADD_MEDIUM
))
509 .insert("show", CliCommand
::new(&API_METHOD_SHOW_MEDIUM
))
510 .insert("remove", CliCommand
::new(&API_METHOD_REMOVE_MEDIUM
))
511 .insert("update", CliCommand
::new(&API_METHOD_UPDATE_MEDIUM
));
513 let cmd_def
= CliCommandMap
::new()
514 .insert("media", media_cmd_def
)
515 .insert("mirror", mirror_cmd_def
);