Skip to content

Commit da31ba9

Browse files
committed
libvirt: Support --connect globally
We really want this for all options consistently, so move it to be part of the libvirt verb and passed down to each sub-option. Signed-off-by: Colin Walters <[email protected]>
1 parent 1384826 commit da31ba9

19 files changed

+284
-193
lines changed

crates/kit/src/libvirt/create.rs

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ pub struct LibvirtCreateOpts {
5757
#[clap(long)]
5858
pub vnc: bool,
5959

60-
/// Hypervisor connection URI (e.g., qemu:///system, qemu+ssh://host/system)
61-
#[clap(short = 'c', long = "connect")]
62-
pub connect: Option<String>,
63-
6460
/// VNC port (default: auto-assign)
6561
#[clap(long)]
6662
pub vnc_port: Option<u16>,
@@ -123,12 +119,8 @@ pub struct SshConfig {
123119

124120
impl LibvirtCreateOpts {
125121
/// Build a virsh command with optional connection URI
126-
fn virsh_command(&self) -> Command {
127-
let mut cmd = Command::new("virsh");
128-
if let Some(ref connect) = self.connect {
129-
cmd.arg("-c").arg(connect);
130-
}
131-
cmd
122+
fn virsh_command(&self, global_opts: &crate::libvirt::LibvirtOptions) -> Command {
123+
global_opts.virsh_command()
132124
}
133125

134126
/// Check if the input appears to be a container image (vs volume name)
@@ -161,7 +153,11 @@ impl LibvirtCreateOpts {
161153
}
162154

163155
/// Find existing volume by container image digest using name-based lookup
164-
fn find_cached_volume(&self, image_digest: &str) -> Result<Option<String>> {
156+
fn find_cached_volume(
157+
&self,
158+
global_opts: &crate::libvirt::LibvirtOptions,
159+
image_digest: &str,
160+
) -> Result<Option<String>> {
165161
debug!("Looking for cached volume with digest: {}", image_digest);
166162

167163
// Create a temporary upload opts to get the expected cached volume name
@@ -176,14 +172,13 @@ impl LibvirtCreateOpts {
176172
},
177173
vcpus: Some(default_vcpus()),
178174
karg: vec![],
179-
connect: self.connect.clone(),
180175
};
181176

182177
let expected_volume_name = temp_upload_opts.get_cached_volume_name(image_digest);
183178
let expected_volume_path = format!("{}.raw", expected_volume_name);
184179

185180
// Check if this specific volume exists
186-
let output = self
181+
let output = global_opts
187182
.virsh_command()
188183
.args(&["vol-info", &expected_volume_path, "--pool", &self.pool])
189184
.output()?;
@@ -198,7 +193,7 @@ impl LibvirtCreateOpts {
198193
}
199194

200195
/// Automatically upload container image if no cached volume exists
201-
fn ensure_volume_exists(&self) -> Result<String> {
196+
fn ensure_volume_exists(&self, global_opts: &crate::libvirt::LibvirtOptions) -> Result<String> {
202197
if !self.is_container_image() {
203198
// If it's already a volume name, just return it
204199
return Ok(self.volume_name_or_image.clone());
@@ -207,7 +202,7 @@ impl LibvirtCreateOpts {
207202
// It's a container image, check for cached volume
208203
let image_digest = images::get_image_digest(&self.volume_name_or_image)?;
209204

210-
if let Some(cached_volume) = self.find_cached_volume(&image_digest)? {
205+
if let Some(cached_volume) = self.find_cached_volume(global_opts, &image_digest)? {
211206
debug!("Using cached volume: {}", cached_volume);
212207
return Ok(cached_volume);
213208
}
@@ -227,11 +222,10 @@ impl LibvirtCreateOpts {
227222
memory: self.install_memory.clone(),
228223
vcpus: self.install_vcpus,
229224
karg: self.karg.clone(),
230-
connect: self.connect.clone(),
231225
};
232226

233227
// Run the upload
234-
crate::libvirt::upload::run(upload_opts.clone())?;
228+
crate::libvirt::upload::run(global_opts, upload_opts.clone())?;
235229

236230
// Return the generated volume name (with digest)
237231
Ok(upload_opts.get_cached_volume_name(&image_digest))
@@ -243,15 +237,19 @@ impl LibvirtCreateOpts {
243237
}
244238

245239
/// Check if volume exists in the specified pool
246-
fn check_volume_exists(&self, volume_name: &str) -> Result<String> {
240+
fn check_volume_exists(
241+
&self,
242+
global_opts: &crate::libvirt::LibvirtOptions,
243+
volume_name: &str,
244+
) -> Result<String> {
247245
let volume_path = if volume_name.ends_with(".raw") {
248246
volume_name.to_string()
249247
} else {
250248
format!("{}.raw", volume_name)
251249
};
252250

253251
let output = self
254-
.virsh_command()
252+
.virsh_command(global_opts)
255253
.args(&["vol-info", &volume_path, "--pool", &self.pool])
256254
.output()?;
257255

@@ -267,7 +265,7 @@ impl LibvirtCreateOpts {
267265

268266
// Get the full volume path
269267
let vol_path_output = self
270-
.virsh_command()
268+
.virsh_command(global_opts)
271269
.args(&["vol-path", &volume_path, "--pool", &self.pool])
272270
.output()?;
273271

@@ -280,15 +278,19 @@ impl LibvirtCreateOpts {
280278
}
281279

282280
/// Extract metadata from bootc volume
283-
fn get_volume_metadata(&self, volume_name: &str) -> Result<BootcVolumeMetadata> {
281+
fn get_volume_metadata(
282+
&self,
283+
global_opts: &crate::libvirt::LibvirtOptions,
284+
volume_name: &str,
285+
) -> Result<BootcVolumeMetadata> {
284286
let volume_path = if volume_name.ends_with(".raw") {
285287
volume_name.to_string()
286288
} else {
287289
format!("{}.raw", volume_name)
288290
};
289291

290292
let output = self
291-
.virsh_command()
293+
.virsh_command(global_opts)
292294
.args(&["vol-dumpxml", &volume_path, "--pool", &self.pool])
293295
.output()?;
294296
if !output.status.success() {
@@ -311,9 +313,13 @@ impl LibvirtCreateOpts {
311313
}
312314

313315
/// Check if domain already exists
314-
fn check_domain_exists(&self, domain_name: &str) -> bool {
316+
fn check_domain_exists(
317+
&self,
318+
global_opts: &crate::libvirt::LibvirtOptions,
319+
domain_name: &str,
320+
) -> bool {
315321
let output = self
316-
.virsh_command()
322+
.virsh_command(global_opts)
317323
.args(&["dominfo", domain_name])
318324
.output();
319325

@@ -324,22 +330,27 @@ impl LibvirtCreateOpts {
324330
}
325331

326332
/// Create a domain-specific copy of the volume
327-
fn create_domain_volume(&self, source_volume_name: &str, domain_name: &str) -> Result<String> {
333+
fn create_domain_volume(
334+
&self,
335+
global_opts: &crate::libvirt::LibvirtOptions,
336+
source_volume_name: &str,
337+
domain_name: &str,
338+
) -> Result<String> {
328339
let domain_volume_name = format!("{}-{}", source_volume_name, domain_name);
329340
let domain_volume_path = format!("{}.raw", domain_volume_name);
330341
let source_volume_path = format!("{}.raw", source_volume_name);
331342

332343
// Check if domain volume already exists
333344
let check_output = self
334-
.virsh_command()
345+
.virsh_command(global_opts)
335346
.args(&["vol-info", &domain_volume_path, "--pool", &self.pool])
336347
.output()?;
337348

338349
if check_output.status.success() {
339350
if self.force {
340351
debug!("Removing existing domain volume: {}", domain_volume_name);
341352
let _ = self
342-
.virsh_command()
353+
.virsh_command(global_opts)
343354
.args(&["vol-delete", &domain_volume_path, "--pool", &self.pool])
344355
.output();
345356
} else {
@@ -357,7 +368,7 @@ impl LibvirtCreateOpts {
357368

358369
// Clone the source volume to create a domain-specific copy
359370
let output = self
360-
.virsh_command()
371+
.virsh_command(global_opts)
361372
.args(&[
362373
"vol-clone",
363374
&source_volume_path,
@@ -374,7 +385,7 @@ impl LibvirtCreateOpts {
374385

375386
// Get the path to the new domain volume
376387
let vol_path_output = self
377-
.virsh_command()
388+
.virsh_command(global_opts)
378389
.args(&["vol-path", &domain_volume_path, "--pool", &self.pool])
379390
.output()?;
380391

@@ -389,6 +400,7 @@ impl LibvirtCreateOpts {
389400
/// Create the libvirt domain
390401
fn create_domain(
391402
&self,
403+
global_opts: &crate::libvirt::LibvirtOptions,
392404
_volume_path: &str,
393405
metadata: &BootcVolumeMetadata,
394406
volume_name: &str,
@@ -397,7 +409,8 @@ impl LibvirtCreateOpts {
397409
let memory_mb = self.parse_memory()?;
398410

399411
// Create a domain-specific volume copy to avoid file locking issues
400-
let domain_volume_path = self.create_domain_volume(volume_name, &domain_name)?;
412+
let domain_volume_path =
413+
self.create_domain_volume(global_opts, volume_name, &domain_name)?;
401414
debug!("Using domain-specific volume: {}", domain_volume_path);
402415

403416
// Setup SSH configuration
@@ -408,22 +421,22 @@ impl LibvirtCreateOpts {
408421
domain_name, volume_name, self.pool
409422
);
410423

411-
if self.check_domain_exists(&domain_name) && !self.force {
424+
if self.check_domain_exists(global_opts, &domain_name) && !self.force {
412425
return Err(eyre!(
413426
"Domain '{}' already exists. Use --force to recreate.",
414427
domain_name
415428
));
416429
}
417430

418431
// If domain exists and force is specified, undefine it first
419-
if self.check_domain_exists(&domain_name) && self.force {
432+
if self.check_domain_exists(global_opts, &domain_name) && self.force {
420433
debug!("Domain exists, removing it first (--force specified)");
421434
let _ = self
422-
.virsh_command()
435+
.virsh_command(global_opts)
423436
.args(&["destroy", &domain_name])
424437
.output();
425438
let _ = self
426-
.virsh_command()
439+
.virsh_command(global_opts)
427440
.args(&["undefine", &domain_name])
428441
.output();
429442
}
@@ -514,7 +527,7 @@ impl LibvirtCreateOpts {
514527

515528
// Define the domain
516529
let output = self
517-
.virsh_command()
530+
.virsh_command(global_opts)
518531
.args(&["define", "/dev/stdin"])
519532
.stdin(std::process::Stdio::piped())
520533
.stdout(std::process::Stdio::piped())
@@ -539,7 +552,7 @@ impl LibvirtCreateOpts {
539552
if self.start {
540553
debug!("Starting domain '{}'", domain_name);
541554
let output = self
542-
.virsh_command()
555+
.virsh_command(global_opts)
543556
.args(&["start", &domain_name])
544557
.output()?;
545558

@@ -676,27 +689,27 @@ impl LibvirtCreateOpts {
676689
}
677690

678691
/// Execute the libvirt domain creation process
679-
pub fn run(opts: LibvirtCreateOpts) -> Result<()> {
692+
pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtCreateOpts) -> Result<()> {
680693
debug!(
681694
"Creating libvirt domain from: {}",
682695
opts.volume_name_or_image
683696
);
684697

685698
// Phase 1: Ensure volume exists (auto-upload if needed)
686-
let volume_name = opts.ensure_volume_exists()?;
699+
let volume_name = opts.ensure_volume_exists(global_opts)?;
687700

688701
// Phase 2: Validate volume exists and get path
689-
let volume_path = opts.check_volume_exists(&volume_name)?;
702+
let volume_path = opts.check_volume_exists(global_opts, &volume_name)?;
690703
debug!("Found volume at: {}", volume_path);
691704

692705
// Phase 3: Extract volume metadata
693-
let metadata = opts.get_volume_metadata(&volume_name)?;
706+
let metadata = opts.get_volume_metadata(global_opts, &volume_name)?;
694707
if let Some(ref source_image) = metadata.source_image {
695708
debug!("Volume contains bootc image: {}", source_image);
696709
}
697710

698711
// Phase 4: Create and optionally start domain
699-
opts.create_domain(&volume_path, &metadata, &volume_name)?;
712+
opts.create_domain(global_opts, &volume_path, &metadata, &volume_name)?;
700713

701714
Ok(())
702715
}

crates/kit/src/libvirt/inspect.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,15 @@ pub struct LibvirtInspectOpts {
1818
}
1919

2020
/// Execute the libvirt inspect command
21-
pub fn run(opts: LibvirtInspectOpts) -> Result<()> {
22-
inspect_vm_impl(opts)
23-
}
24-
25-
/// Show detailed information about a VM (implementation)
26-
pub fn inspect_vm_impl(opts: LibvirtInspectOpts) -> Result<()> {
21+
pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtInspectOpts) -> Result<()> {
2722
use crate::domain_list::DomainLister;
2823
use color_eyre::eyre::Context;
2924

30-
let lister = DomainLister::new();
25+
let connect_uri = global_opts.connect.as_ref();
26+
let lister = match connect_uri {
27+
Some(uri) => DomainLister::with_connection(uri.clone()),
28+
None => DomainLister::new(),
29+
};
3130

3231
// Get domain info
3332
let vm = lister

crates/kit/src/libvirt/list.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,16 @@ pub struct LibvirtListOpts {
1919
}
2020

2121
/// Execute the libvirt list command
22-
pub fn run(opts: LibvirtListOpts) -> Result<()> {
23-
list_vms_impl(opts)
24-
}
25-
26-
/// List all VMs (implementation)
27-
pub fn list_vms_impl(opts: LibvirtListOpts) -> Result<()> {
22+
pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtListOpts) -> Result<()> {
2823
use crate::domain_list::DomainLister;
2924
use color_eyre::eyre::Context;
3025

3126
// Use libvirt as the source of truth for domain listing
32-
let lister = DomainLister::new();
27+
let connect_uri = global_opts.connect.as_ref();
28+
let lister = match connect_uri {
29+
Some(uri) => DomainLister::with_connection(uri.clone()),
30+
None => DomainLister::new(),
31+
};
3332

3433
let domains = if opts.all {
3534
lister

0 commit comments

Comments
 (0)