Add cloud-localds as a required program
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@ -122,19 +122,22 @@ pub const API_TOKEN_DESCRIPTION_MAX_LENGTH: usize = 30;
|
|||||||
pub const API_TOKEN_RIGHT_PATH_MAX_LENGTH: usize = 255;
|
pub const API_TOKEN_RIGHT_PATH_MAX_LENGTH: usize = 255;
|
||||||
|
|
||||||
/// Qemu image program path
|
/// Qemu image program path
|
||||||
pub const QEMU_IMAGE_PROGRAM: &str = "/usr/bin/qemu-img";
|
pub const PROGRAM_QEMU_IMAGE: &str = "/usr/bin/qemu-img";
|
||||||
|
|
||||||
/// IP program path
|
/// IP program path
|
||||||
pub const IP_PROGRAM: &str = "/usr/sbin/ip";
|
pub const PROGRAM_IP: &str = "/usr/sbin/ip";
|
||||||
|
|
||||||
/// Copy program path
|
/// Copy program path
|
||||||
pub const COPY_PROGRAM: &str = "/bin/cp";
|
pub const PROGRAM_COPY: &str = "/bin/cp";
|
||||||
|
|
||||||
/// Gzip program path
|
/// Gzip program path
|
||||||
pub const GZIP_PROGRAM: &str = "/usr/bin/gzip";
|
pub const PROGRAM_GZIP: &str = "/usr/bin/gzip";
|
||||||
|
|
||||||
/// Bash program
|
/// Bash program
|
||||||
pub const BASH_PROGRAM: &str = "/usr/bin/bash";
|
pub const PROGRAM_BASH: &str = "/usr/bin/bash";
|
||||||
|
|
||||||
/// DD program
|
/// DD program
|
||||||
pub const DD_PROGRAM: &str = "/usr/bin/dd";
|
pub const PROGRAM_DD: &str = "/usr/bin/dd";
|
||||||
|
|
||||||
|
/// cloud-localds program
|
||||||
|
pub const PROGRAM_CLOUD_LOCALDS: &str = "/usr/bin/cloud-localds";
|
||||||
|
@ -47,13 +47,17 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
log::debug!("Checking for required programs");
|
log::debug!("Checking for required programs");
|
||||||
exec_utils::check_program(
|
exec_utils::check_program(
|
||||||
constants::QEMU_IMAGE_PROGRAM,
|
constants::PROGRAM_QEMU_IMAGE,
|
||||||
"QEMU disk image utility is required to manipulate QCow2 files!",
|
"QEMU disk image utility is required to manipulate QCow2 files!",
|
||||||
);
|
);
|
||||||
exec_utils::check_program(
|
exec_utils::check_program(
|
||||||
constants::IP_PROGRAM,
|
constants::PROGRAM_IP,
|
||||||
"ip is required to access bridges information!",
|
"ip is required to access bridges information!",
|
||||||
);
|
);
|
||||||
|
exec_utils::check_program(
|
||||||
|
constants::PROGRAM_CLOUD_LOCALDS,
|
||||||
|
"cloud-localds from package cloud-image-utils is required to build cloud-init images!",
|
||||||
|
);
|
||||||
|
|
||||||
log::debug!("Create required directory, if missing");
|
log::debug!("Create required directory, if missing");
|
||||||
files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap();
|
files_utils::create_directory_if_missing(AppConfig::get().iso_storage_path()).unwrap();
|
||||||
|
@ -124,7 +124,7 @@ impl DiskFileInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DiskFileFormat::QCow2 { virtual_size } => {
|
DiskFileFormat::QCow2 { virtual_size } => {
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
|
||||||
cmd.arg("create")
|
cmd.arg("create")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("qcow2")
|
.arg("qcow2")
|
||||||
@ -161,7 +161,7 @@ impl DiskFileInfo {
|
|||||||
let mut cmd = match (self.format, dest_format) {
|
let mut cmd = match (self.format, dest_format) {
|
||||||
// Decompress QCow2
|
// Decompress QCow2
|
||||||
(DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
|
(DiskFileFormat::CompressedQCow2, DiskFileFormat::QCow2 { .. }) => {
|
||||||
let mut cmd = Command::new(constants::GZIP_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_GZIP);
|
||||||
cmd.arg("--keep")
|
cmd.arg("--keep")
|
||||||
.arg("--decompress")
|
.arg("--decompress")
|
||||||
.arg("--to-stdout")
|
.arg("--to-stdout")
|
||||||
@ -172,7 +172,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Compress QCow2
|
// Compress QCow2
|
||||||
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => {
|
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::CompressedQCow2) => {
|
||||||
let mut cmd = Command::new(constants::GZIP_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_GZIP);
|
||||||
cmd.arg("--keep")
|
cmd.arg("--keep")
|
||||||
.arg("--to-stdout")
|
.arg("--to-stdout")
|
||||||
.arg(&self.file_path)
|
.arg(&self.file_path)
|
||||||
@ -182,7 +182,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Convert QCow2 to Raw file
|
// Convert QCow2 to Raw file
|
||||||
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::Raw { is_sparse }) => {
|
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::Raw { is_sparse }) => {
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
|
||||||
cmd.arg("convert")
|
cmd.arg("convert")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("qcow2")
|
.arg("qcow2")
|
||||||
@ -201,7 +201,7 @@ impl DiskFileInfo {
|
|||||||
// Clone a QCow file, using qemu-image instead of cp might improve "sparsification" of
|
// Clone a QCow file, using qemu-image instead of cp might improve "sparsification" of
|
||||||
// file
|
// file
|
||||||
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::QCow2 { .. }) => {
|
(DiskFileFormat::QCow2 { .. }, DiskFileFormat::QCow2 { .. }) => {
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
|
||||||
cmd.arg("convert")
|
cmd.arg("convert")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("qcow2")
|
.arg("qcow2")
|
||||||
@ -214,7 +214,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Convert Raw to QCow2 file
|
// Convert Raw to QCow2 file
|
||||||
(DiskFileFormat::Raw { .. }, DiskFileFormat::QCow2 { .. }) => {
|
(DiskFileFormat::Raw { .. }, DiskFileFormat::QCow2 { .. }) => {
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
|
||||||
cmd.arg("convert")
|
cmd.arg("convert")
|
||||||
.arg("-f")
|
.arg("-f")
|
||||||
.arg("raw")
|
.arg("raw")
|
||||||
@ -228,7 +228,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Render raw file non sparse
|
// Render raw file non sparse
|
||||||
(DiskFileFormat::Raw { is_sparse: true }, DiskFileFormat::Raw { is_sparse: false }) => {
|
(DiskFileFormat::Raw { is_sparse: true }, DiskFileFormat::Raw { is_sparse: false }) => {
|
||||||
let mut cmd = Command::new(constants::COPY_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_COPY);
|
||||||
cmd.arg("--sparse=never")
|
cmd.arg("--sparse=never")
|
||||||
.arg(&self.file_path)
|
.arg(&self.file_path)
|
||||||
.arg(&temp_file);
|
.arg(&temp_file);
|
||||||
@ -237,7 +237,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Render raw file sparse
|
// Render raw file sparse
|
||||||
(DiskFileFormat::Raw { is_sparse: false }, DiskFileFormat::Raw { is_sparse: true }) => {
|
(DiskFileFormat::Raw { is_sparse: false }, DiskFileFormat::Raw { is_sparse: true }) => {
|
||||||
let mut cmd = Command::new(constants::DD_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_DD);
|
||||||
cmd.arg("conv=sparse")
|
cmd.arg("conv=sparse")
|
||||||
.arg(format!("if={}", self.file_path.display()))
|
.arg(format!("if={}", self.file_path.display()))
|
||||||
.arg(format!("of={}", temp_file.display()));
|
.arg(format!("of={}", temp_file.display()));
|
||||||
@ -246,7 +246,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Compress Raw
|
// Compress Raw
|
||||||
(DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => {
|
(DiskFileFormat::Raw { .. }, DiskFileFormat::CompressedRaw) => {
|
||||||
let mut cmd = Command::new(constants::GZIP_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_GZIP);
|
||||||
cmd.arg("--keep")
|
cmd.arg("--keep")
|
||||||
.arg("--to-stdout")
|
.arg("--to-stdout")
|
||||||
.arg(&self.file_path)
|
.arg(&self.file_path)
|
||||||
@ -256,7 +256,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Decompress Raw to not sparse file
|
// Decompress Raw to not sparse file
|
||||||
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
|
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: false }) => {
|
||||||
let mut cmd = Command::new(constants::GZIP_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_GZIP);
|
||||||
cmd.arg("--keep")
|
cmd.arg("--keep")
|
||||||
.arg("--decompress")
|
.arg("--decompress")
|
||||||
.arg("--to-stdout")
|
.arg("--to-stdout")
|
||||||
@ -268,12 +268,12 @@ impl DiskFileInfo {
|
|||||||
// Decompress Raw to sparse file
|
// Decompress Raw to sparse file
|
||||||
// https://benou.fr/www/ben/decompressing-sparse-files.html
|
// https://benou.fr/www/ben/decompressing-sparse-files.html
|
||||||
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
|
(DiskFileFormat::CompressedRaw, DiskFileFormat::Raw { is_sparse: true }) => {
|
||||||
let mut cmd = Command::new(constants::BASH_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_BASH);
|
||||||
cmd.arg("-c").arg(format!(
|
cmd.arg("-c").arg(format!(
|
||||||
"{} -d -c {} | {} conv=sparse of={}",
|
"{} -d -c {} | {} conv=sparse of={}",
|
||||||
constants::GZIP_PROGRAM,
|
constants::PROGRAM_GZIP,
|
||||||
self.file_path.display(),
|
self.file_path.display(),
|
||||||
constants::DD_PROGRAM,
|
constants::PROGRAM_DD,
|
||||||
temp_file.display()
|
temp_file.display()
|
||||||
));
|
));
|
||||||
cmd
|
cmd
|
||||||
@ -281,7 +281,7 @@ impl DiskFileInfo {
|
|||||||
|
|
||||||
// Dumb copy of file
|
// Dumb copy of file
|
||||||
(a, b) if a == b => {
|
(a, b) if a == b => {
|
||||||
let mut cmd = Command::new(constants::COPY_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_COPY);
|
||||||
cmd.arg("--sparse=auto")
|
cmd.arg("--sparse=auto")
|
||||||
.arg(&self.file_path)
|
.arg(&self.file_path)
|
||||||
.arg(&temp_file);
|
.arg(&temp_file);
|
||||||
@ -341,7 +341,7 @@ struct QCowInfoOutput {
|
|||||||
/// Get QCow2 virtual size
|
/// Get QCow2 virtual size
|
||||||
fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
|
fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
|
||||||
// Run qemu-img
|
// Run qemu-img
|
||||||
let mut cmd = Command::new(constants::QEMU_IMAGE_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_QEMU_IMAGE);
|
||||||
cmd.args([
|
cmd.args([
|
||||||
"info",
|
"info",
|
||||||
path.to_str().unwrap_or(""),
|
path.to_str().unwrap_or(""),
|
||||||
@ -353,7 +353,7 @@ fn qcow_virt_size(path: &Path) -> anyhow::Result<FileSize> {
|
|||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"{} info failed, status: {}, stderr: {}",
|
"{} info failed, status: {}, stderr: {}",
|
||||||
constants::QEMU_IMAGE_PROGRAM,
|
constants::PROGRAM_QEMU_IMAGE,
|
||||||
output.status,
|
output.status,
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
|
@ -145,13 +145,13 @@ struct IPBridgeInfo {
|
|||||||
|
|
||||||
/// Get the list of bridge interfaces
|
/// Get the list of bridge interfaces
|
||||||
pub fn bridges_list() -> anyhow::Result<Vec<String>> {
|
pub fn bridges_list() -> anyhow::Result<Vec<String>> {
|
||||||
let mut cmd = Command::new(constants::IP_PROGRAM);
|
let mut cmd = Command::new(constants::PROGRAM_IP);
|
||||||
cmd.args(["-json", "link", "show", "type", "bridge"]);
|
cmd.args(["-json", "link", "show", "type", "bridge"]);
|
||||||
let output = cmd.output()?;
|
let output = cmd.output()?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"{} failed, status: {}, stderr: {}",
|
"{} failed, status: {}, stderr: {}",
|
||||||
constants::IP_PROGRAM,
|
constants::PROGRAM_IP,
|
||||||
output.status,
|
output.status,
|
||||||
String::from_utf8_lossy(&output.stderr)
|
String::from_utf8_lossy(&output.stderr)
|
||||||
);
|
);
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
sudo apt install libvirt-dev
|
sudo apt install libvirt-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Libvirt must also be installed:
|
2. Libvirt and cloud image utilities must also be installed:
|
||||||
```bash
|
```bash
|
||||||
sudo apt install qemu-kvm libvirt-daemon-system
|
sudo apt install qemu-kvm libvirt-daemon-system cloud-image-utils
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Allow the current user to manage VMs:
|
3. Allow the current user to manage VMs:
|
||||||
|
@ -12,10 +12,10 @@ The release file will be available in `virtweb_backend/target/release/virtweb_ba
|
|||||||
This is the only artifact that must be copied to the server. It is recommended to copy it to the `/usr/local/bin` directory.
|
This is the only artifact that must be copied to the server. It is recommended to copy it to the `/usr/local/bin` directory.
|
||||||
|
|
||||||
## Install requirements
|
## Install requirements
|
||||||
In order to work properly, VirtWeb relies on `libvirt`, `qemu` and `kvm`:
|
In order to work properly, VirtWeb relies on `libvirt`, `qemu`, `kvm` and `cloud-localds`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install qemu-kvm libvirt-daemon-system libvirt0 libvirt-clients libvirt-daemon bridge-utils
|
sudo apt install qemu-kvm libvirt-daemon-system libvirt0 libvirt-clients libvirt-daemon bridge-utils cloud-image-utils
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dedicated user
|
## Dedicated user
|
||||||
|
Reference in New Issue
Block a user