src/api2/node/disks.rs: implement initgpt API
This commit is contained in:
parent
9069debcd8
commit
707974fdb3
|
@ -1,17 +1,19 @@
|
||||||
use anyhow::{Error};
|
use anyhow::{bail, Error};
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use proxmox::api::{api, Permission};
|
use proxmox::api::{api, Permission, RpcEnvironment, RpcEnvironmentType};
|
||||||
use proxmox::api::router::{Router, SubdirMap};
|
use proxmox::api::router::{Router, SubdirMap};
|
||||||
use proxmox::{sortable, identity};
|
use proxmox::{sortable, identity};
|
||||||
use proxmox::{list_subdirs_api_method};
|
use proxmox::{list_subdirs_api_method};
|
||||||
|
|
||||||
use crate::config::acl::{PRIV_SYS_AUDIT};
|
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
|
||||||
use crate::tools::disks::{
|
use crate::tools::disks::{
|
||||||
DiskUsageInfo, DiskUsageType, DiskManage, SmartData,
|
DiskUsageInfo, DiskUsageType, DiskManage, SmartData,
|
||||||
get_disks, get_smart_data,
|
get_disks, get_smart_data, get_disk_usage_info, inititialize_gpt_disk,
|
||||||
};
|
};
|
||||||
|
use crate::server::WorkerTask;
|
||||||
|
|
||||||
use crate::api2::types::{NODE_SCHEMA, BLOCKDEVICE_NAME_SCHEMA};
|
use crate::api2::types::{UPID_SCHEMA, NODE_SCHEMA, BLOCKDEVICE_NAME_SCHEMA};
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
protected: true,
|
protected: true,
|
||||||
|
@ -101,9 +103,71 @@ pub fn smart_status(
|
||||||
get_smart_data(&disk, healthonly)
|
get_smart_data(&disk, healthonly)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
protected: true,
|
||||||
|
input: {
|
||||||
|
properties: {
|
||||||
|
node: {
|
||||||
|
schema: NODE_SCHEMA,
|
||||||
|
},
|
||||||
|
disk: {
|
||||||
|
schema: BLOCKDEVICE_NAME_SCHEMA,
|
||||||
|
},
|
||||||
|
uuid: {
|
||||||
|
description: "UUID for the GPT table.",
|
||||||
|
type: String,
|
||||||
|
optional: true,
|
||||||
|
max_length: 36,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
returns: {
|
||||||
|
schema: UPID_SCHEMA,
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["system", "disks"], PRIV_SYS_MODIFY, false),
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
/// Initialize empty Disk with GPT
|
||||||
|
pub fn initialize_disk(
|
||||||
|
disk: String,
|
||||||
|
uuid: Option<String>,
|
||||||
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
|
let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false };
|
||||||
|
|
||||||
|
let username = rpcenv.get_user().unwrap();
|
||||||
|
|
||||||
|
let info = get_disk_usage_info(&disk, true)?;
|
||||||
|
|
||||||
|
if info.used != DiskUsageType::Unused {
|
||||||
|
bail!("disk '{}' is already in use.", disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
let upid_str = WorkerTask::new_thread(
|
||||||
|
"diskinit", Some(disk.clone()), &username.clone(), to_stdout, move |worker|
|
||||||
|
{
|
||||||
|
worker.log(format!("initialize disk {}", disk));
|
||||||
|
|
||||||
|
let disk_manager = DiskManage::new();
|
||||||
|
let disk_info = disk_manager.disk_by_node(&disk)?;
|
||||||
|
|
||||||
|
inititialize_gpt_disk(&disk_info, uuid.as_deref())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(json!(upid_str))
|
||||||
|
}
|
||||||
|
|
||||||
#[sortable]
|
#[sortable]
|
||||||
const SUBDIRS: SubdirMap = &sorted!([
|
const SUBDIRS: SubdirMap = &sorted!([
|
||||||
// ("lvm", &lvm::ROUTER),
|
// ("lvm", &lvm::ROUTER),
|
||||||
|
(
|
||||||
|
"initgpt", &Router::new()
|
||||||
|
.post(&API_METHOD_INITIALIZE_DISK)
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"list", &Router::new()
|
"list", &Router::new()
|
||||||
.get(&API_METHOD_LIST_DISKS)
|
.get(&API_METHOD_LIST_DISKS)
|
||||||
|
|
|
@ -32,6 +32,24 @@ async fn view_task_result(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: local worker already printf logs to stdout, so there is no need
|
||||||
|
// to fetch/display logs. We just wait for the worker to finish.
|
||||||
|
pub async fn wait_for_local_worker(upid_str: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
|
let upid: proxmox_backup::server::UPID = upid_str.parse()?;
|
||||||
|
|
||||||
|
let sleep_duration = core::time::Duration::new(0, 100_000_000);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if proxmox_backup::server::worker_is_active_local(&upid) {
|
||||||
|
tokio::time::delay_for(sleep_duration).await;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn connect() -> Result<HttpClient, Error> {
|
fn connect() -> Result<HttpClient, Error> {
|
||||||
|
|
||||||
let uid = nix::unistd::Uid::current();
|
let uid = nix::unistd::Uid::current();
|
||||||
|
|
|
@ -85,6 +85,40 @@ fn smart_attributes(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result
|
||||||
Ok(Value::Null)
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
input: {
|
||||||
|
properties: {
|
||||||
|
disk: {
|
||||||
|
schema: BLOCKDEVICE_NAME_SCHEMA,
|
||||||
|
},
|
||||||
|
uuid: {
|
||||||
|
description: "UUID for the GPT table.",
|
||||||
|
type: String,
|
||||||
|
optional: true,
|
||||||
|
max_length: 36,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
/// Initialize empty Disk with GPT
|
||||||
|
async fn initialize_disk(
|
||||||
|
mut param: Value,
|
||||||
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
|
param["node"] = "localhost".into();
|
||||||
|
|
||||||
|
let info = &api2::node::disks::API_METHOD_INITIALIZE_DISK;
|
||||||
|
let result = match info.handler {
|
||||||
|
ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
crate::wait_for_local_worker(result.as_str().unwrap()).await?;
|
||||||
|
|
||||||
|
Ok(Value::Null)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disk_commands() -> CommandLineInterface {
|
pub fn disk_commands() -> CommandLineInterface {
|
||||||
|
|
||||||
let cmd_def = CliCommandMap::new()
|
let cmd_def = CliCommandMap::new()
|
||||||
|
@ -93,6 +127,11 @@ pub fn disk_commands() -> CommandLineInterface {
|
||||||
CliCommand::new(&API_METHOD_SMART_ATTRIBUTES)
|
CliCommand::new(&API_METHOD_SMART_ATTRIBUTES)
|
||||||
.arg_param(&["disk"])
|
.arg_param(&["disk"])
|
||||||
.completion_cb("disk", complete_disk_name)
|
.completion_cb("disk", complete_disk_name)
|
||||||
|
)
|
||||||
|
.insert("initialize",
|
||||||
|
CliCommand::new(&API_METHOD_INITIALIZE_DISK)
|
||||||
|
.arg_param(&["disk"])
|
||||||
|
.completion_cb("disk", complete_disk_name)
|
||||||
);
|
);
|
||||||
|
|
||||||
cmd_def.into()
|
cmd_def.into()
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use anyhow::{format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use libc::dev_t;
|
use libc::dev_t;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
@ -708,6 +708,23 @@ fn scan_partitions(
|
||||||
Ok(used)
|
Ok(used)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get disk usage information for a single disk
|
||||||
|
pub fn get_disk_usage_info(
|
||||||
|
disk: &str,
|
||||||
|
no_smart: bool,
|
||||||
|
) -> Result<DiskUsageInfo, Error> {
|
||||||
|
let mut filter = Vec::new();
|
||||||
|
filter.push(disk.to_string());
|
||||||
|
let mut map = get_disks(Some(filter), no_smart)?;
|
||||||
|
if let Some(info) = map.remove(disk) {
|
||||||
|
return Ok(info);
|
||||||
|
} else {
|
||||||
|
bail!("failed to get disk usage info - internal error"); // should not happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get disk usage information for multiple disks
|
||||||
pub fn get_disks(
|
pub fn get_disks(
|
||||||
// filter - list of device names (without leading /dev)
|
// filter - list of device names (without leading /dev)
|
||||||
disks: Option<Vec<String>>,
|
disks: Option<Vec<String>>,
|
||||||
|
@ -820,6 +837,32 @@ pub fn get_disks(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize disk by writing a GPT partition table
|
||||||
|
pub fn inititialize_gpt_disk(disk: &Disk, uuid: Option<&str>) -> Result<(), Error> {
|
||||||
|
|
||||||
|
const SGDISK_BIN_PATH: &str = "/usr/sbin/sgdisk";
|
||||||
|
|
||||||
|
let disk_path = match disk.device_path() {
|
||||||
|
Some(path) => path,
|
||||||
|
None => bail!("disk {:?} has no node in /dev", disk.syspath()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uuid = uuid.unwrap_or("R"); // R .. random disk GUID
|
||||||
|
|
||||||
|
let mut command = std::process::Command::new(SGDISK_BIN_PATH);
|
||||||
|
command.arg(disk_path);
|
||||||
|
command.args(&["-U", uuid]);
|
||||||
|
|
||||||
|
let output = command.output()
|
||||||
|
.map_err(|err| format_err!("failed to execute '{}' - {}", SGDISK_BIN_PATH, err))?;
|
||||||
|
|
||||||
|
crate::tools::command_output(output, None)
|
||||||
|
.map_err(|err| format_err!("sgdisk command failed: {}", err))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block device name completion helper
|
||||||
pub fn complete_disk_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
|
pub fn complete_disk_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue