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::{sortable, identity};
|
||||
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::{
|
||||
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(
|
||||
protected: true,
|
||||
@ -101,9 +103,71 @@ pub fn smart_status(
|
||||
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]
|
||||
const SUBDIRS: SubdirMap = &sorted!([
|
||||
// ("lvm", &lvm::ROUTER),
|
||||
// ("lvm", &lvm::ROUTER),
|
||||
(
|
||||
"initgpt", &Router::new()
|
||||
.post(&API_METHOD_INITIALIZE_DISK)
|
||||
),
|
||||
(
|
||||
"list", &Router::new()
|
||||
.get(&API_METHOD_LIST_DISKS)
|
||||
|
@ -32,6 +32,24 @@ async fn view_task_result(
|
||||
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> {
|
||||
|
||||
let uid = nix::unistd::Uid::current();
|
||||
|
@ -85,6 +85,40 @@ fn smart_attributes(mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result
|
||||
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 {
|
||||
|
||||
let cmd_def = CliCommandMap::new()
|
||||
@ -93,6 +127,11 @@ pub fn disk_commands() -> CommandLineInterface {
|
||||
CliCommand::new(&API_METHOD_SMART_ATTRIBUTES)
|
||||
.arg_param(&["disk"])
|
||||
.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()
|
||||
|
@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use anyhow::{format_err, Error};
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use libc::dev_t;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
@ -708,6 +708,23 @@ fn scan_partitions(
|
||||
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(
|
||||
// filter - list of device names (without leading /dev)
|
||||
disks: Option<Vec<String>>,
|
||||
@ -820,6 +837,32 @@ pub fn get_disks(
|
||||
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> {
|
||||
let mut list = Vec::new();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user