diff --git a/Cargo.lock b/Cargo.lock index 09e9fa766..8f06075a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,6 @@ version = "35.0.0" dependencies = [ "anyhow", "api_client", - "argh", "clap", "dhat", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 535fffd0a..1bc56e660 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ debug = true [dependencies] anyhow = "1.0.75" api_client = { path = "api_client" } -argh = "0.1.9" clap = { version = "4.3.11", features = ["string"] } dhat = { version = "0.3.2", optional = true } epoll = "4.3.3" diff --git a/src/bin/ch-remote.rs b/src/bin/ch-remote.rs index 4d726a688..1d5d5d0ba 100644 --- a/src/bin/ch-remote.rs +++ b/src/bin/ch-remote.rs @@ -7,7 +7,7 @@ use api_client::simple_api_command; use api_client::simple_api_command_with_fds; use api_client::simple_api_full_command; use api_client::Error as ApiClientError; -use argh::FromArgs; +use clap::{Arg, ArgAction, ArgMatches, Command}; use option_parser::{ByteSized, ByteSizedParseError}; use std::fmt; use std::io::Read; @@ -24,6 +24,7 @@ enum Error { HttpApiClient(ApiClientError), #[cfg(feature = "dbus_api")] DBusApiClient(zbus::Error), + InvalidCpuCount(std::num::ParseIntError), InvalidMemorySize(ByteSizedParseError), InvalidBalloonSize(ByteSizedParseError), AddDeviceConfig(vmm::config::Error), @@ -46,6 +47,7 @@ impl fmt::Display for Error { HttpApiClient(e) => e.fmt(f), #[cfg(feature = "dbus_api")] DBusApiClient(e) => write!(f, "Error D-Bus proxy: {e}"), + InvalidCpuCount(e) => write!(f, "Error parsing CPU count: {e}"), InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"), InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"), AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"), @@ -252,128 +254,240 @@ impl<'a> DBusApi1ProxyBlocking<'a> { } impl<'a> TargetApi<'a> { - fn do_command(&mut self, toplevel: &TopLevel) -> ApiResult { + fn do_command(&mut self, matches: &ArgMatches) -> ApiResult { match self { - Self::HttpApi(api_socket, _) => rest_api_do_command(toplevel, api_socket), + Self::HttpApi(api_socket, _) => rest_api_do_command(matches, api_socket), #[cfg(feature = "dbus_api")] - Self::DBusApi(proxy) => dbus_api_do_command(toplevel, proxy), + Self::DBusApi(proxy) => dbus_api_do_command(matches, proxy), } } } -fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResult { - match toplevel.command { - SubCommandEnum::Boot(_) => { +fn rest_api_do_command(matches: &ArgMatches, socket: &mut UnixStream) -> ApiResult { + match matches.subcommand_name() { + Some("boot") => { simple_api_command(socket, "PUT", "boot", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Delete(_) => { + Some("delete") => { simple_api_command(socket, "PUT", "delete", None).map_err(Error::HttpApiClient) } - SubCommandEnum::ShutdownVmm(_) => { - simple_api_full_command(socket, "PUT", "vmm.shutdown", None) - .map_err(Error::HttpApiClient) - } - SubCommandEnum::Resume(_) => { + Some("shutdown-vmm") => simple_api_full_command(socket, "PUT", "vmm.shutdown", None) + .map_err(Error::HttpApiClient), + Some("resume") => { simple_api_command(socket, "PUT", "resume", None).map_err(Error::HttpApiClient) } - SubCommandEnum::PowerButton(_) => { + Some("power-button") => { simple_api_command(socket, "PUT", "power-button", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Reboot(_) => { + Some("reboot") => { simple_api_command(socket, "PUT", "reboot", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Pause(_) => { + Some("pause") => { simple_api_command(socket, "PUT", "pause", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Info(_) => { + Some("info") => { simple_api_command(socket, "GET", "info", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Counters(_) => { + Some("counters") => { simple_api_command(socket, "GET", "counters", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Ping(_) => { + Some("ping") => { simple_api_full_command(socket, "GET", "vmm.ping", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Shutdown(_) => { + Some("shutdown") => { simple_api_command(socket, "PUT", "shutdown", None).map_err(Error::HttpApiClient) } - SubCommandEnum::Resize(ref config) => { - let resize = resize_config(config.cpus, &config.memory, &config.balloon)?; + Some("resize") => { + let resize = resize_config( + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("cpus") + .map(|x| x as &str), + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("memory") + .map(|x| x as &str), + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("balloon") + .map(|x| x as &str), + )?; simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient) } - SubCommandEnum::ResizeZone(ref config) => { - let resize_zone = resize_zone_config(&config.id, &config.size)?; + Some("resize-zone") => { + let resize_zone = resize_zone_config( + matches + .subcommand_matches("resize-zone") + .unwrap() + .get_one::("id") + .unwrap(), + matches + .subcommand_matches("resize-zone") + .unwrap() + .get_one::("size") + .unwrap(), + )?; simple_api_command(socket, "PUT", "resize-zone", Some(&resize_zone)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddDevice(ref config) => { - let device_config = add_device_config(&config.device_config)?; + Some("add-device") => { + let device_config = add_device_config( + matches + .subcommand_matches("add-device") + .unwrap() + .get_one::("device_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-device", Some(&device_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::RemoveDevice(ref config) => { - let remove_device_data = remove_device_config(&config.device_config); + Some("remove-device") => { + let remove_device_data = remove_device_config( + matches + .subcommand_matches("remove-device") + .unwrap() + .get_one::("id") + .unwrap(), + ); simple_api_command(socket, "PUT", "remove-device", Some(&remove_device_data)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddDisk(ref config) => { - let disk_config = add_disk_config(&config.disk_config)?; + Some("add-disk") => { + let disk_config = add_disk_config( + matches + .subcommand_matches("add-disk") + .unwrap() + .get_one::("disk_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-disk", Some(&disk_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddFs(ref config) => { - let fs_config = add_fs_config(&config.fs_config)?; + Some("add-fs") => { + let fs_config = add_fs_config( + matches + .subcommand_matches("add-fs") + .unwrap() + .get_one::("fs_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-fs", Some(&fs_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddPmem(ref config) => { - let pmem_config = add_pmem_config(&config.pmem_config)?; + Some("add-pmem") => { + let pmem_config = add_pmem_config( + matches + .subcommand_matches("add-pmem") + .unwrap() + .get_one::("pmem_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-pmem", Some(&pmem_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddNet(ref config) => { - let (net_config, fds) = add_net_config(&config.net_config)?; + Some("add-net") => { + let (net_config, fds) = add_net_config( + matches + .subcommand_matches("add-net") + .unwrap() + .get_one::("net_config") + .unwrap(), + )?; simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddUserDevice(ref config) => { - let device_config = add_user_device_config(&config.device_config)?; + Some("add-user-device") => { + let device_config = add_user_device_config( + matches + .subcommand_matches("add-user-device") + .unwrap() + .get_one::("device_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-user-device", Some(&device_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddVdpa(ref config) => { - let vdpa_config = add_vdpa_config(&config.vdpa_config)?; + Some("add-vdpa") => { + let vdpa_config = add_vdpa_config( + matches + .subcommand_matches("add-vdpa") + .unwrap() + .get_one::("vdpa_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-vdpa", Some(&vdpa_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::AddVsock(ref config) => { - let vsock_config = add_vsock_config(&config.vsock_config)?; + Some("add-vsock") => { + let vsock_config = add_vsock_config( + matches + .subcommand_matches("add-vsock") + .unwrap() + .get_one::("vsock_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "add-vsock", Some(&vsock_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::Snapshot(ref config) => { - let snapshot_config = snapshot_api_config(&config.snapshot_config); + Some("snapshot") => { + let snapshot_config = snapshot_config( + matches + .subcommand_matches("snapshot") + .unwrap() + .get_one::("snapshot_config") + .unwrap(), + ); simple_api_command(socket, "PUT", "snapshot", Some(&snapshot_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::Restore(ref config) => { - let restore_config = restore_config(&config.restore_config)?; + Some("restore") => { + let restore_config = restore_config( + matches + .subcommand_matches("restore") + .unwrap() + .get_one::("restore_config") + .unwrap(), + )?; simple_api_command(socket, "PUT", "restore", Some(&restore_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::Coredump(ref config) => { - let coredump_config = coredump_config(&config.coredump_config); + Some("coredump") => { + let coredump_config = coredump_config( + matches + .subcommand_matches("coredump") + .unwrap() + .get_one::("coredump_config") + .unwrap(), + ); simple_api_command(socket, "PUT", "coredump", Some(&coredump_config)) .map_err(Error::HttpApiClient) } - SubCommandEnum::SendMigration(ref config) => { - let send_migration_data = - send_migration_data(&config.send_migration_config, config.send_migration_local); + Some("send-migration") => { + let send_migration_data = send_migration_data( + matches + .subcommand_matches("send-migration") + .unwrap() + .get_one::("send_migration_config") + .unwrap(), + matches + .subcommand_matches("send-migration") + .unwrap() + .get_flag("send_migration_local"), + ); simple_api_command(socket, "PUT", "send-migration", Some(&send_migration_data)) .map_err(Error::HttpApiClient) } - SubCommandEnum::ReceiveMigration(ref config) => { - let receive_migration_data = receive_migration_data(&config.receive_migration_config); + Some("receive-migration") => { + let receive_migration_data = receive_migration_data( + matches + .subcommand_matches("receive-migration") + .unwrap() + .get_one::("receive_migration_config") + .unwrap(), + ); simple_api_command( socket, "PUT", @@ -382,111 +496,238 @@ fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResul ) .map_err(Error::HttpApiClient) } - SubCommandEnum::Create(ref config) => { - let data = create_data(&config.vm_config)?; + Some("create") => { + let data = create_data( + matches + .subcommand_matches("create") + .unwrap() + .get_one::("path") + .unwrap(), + )?; simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::HttpApiClient) } - SubCommandEnum::Version(_) => { - // Already handled outside of this function - panic!() - } + _ => unreachable!(), } } #[cfg(feature = "dbus_api")] -fn dbus_api_do_command(toplevel: &TopLevel, proxy: &DBusApi1ProxyBlocking<'_>) -> ApiResult { - match toplevel.command { - SubCommandEnum::Boot(_) => proxy.api_vm_boot(), - SubCommandEnum::Delete(_) => proxy.api_vm_delete(), - SubCommandEnum::ShutdownVmm(_) => proxy.api_vmm_shutdown(), - SubCommandEnum::Resume(_) => proxy.api_vm_resume(), - SubCommandEnum::PowerButton(_) => proxy.api_vm_power_button(), - SubCommandEnum::Reboot(_) => proxy.api_vm_reboot(), - SubCommandEnum::Pause(_) => proxy.api_vm_pause(), - SubCommandEnum::Info(_) => proxy.api_vm_info(), - SubCommandEnum::Counters(_) => proxy.api_vm_counters(), - SubCommandEnum::Ping(_) => proxy.api_vmm_ping(), - SubCommandEnum::Shutdown(_) => proxy.api_vm_shutdown(), - SubCommandEnum::Resize(ref config) => { - let resize = resize_config(config.cpus, &config.memory, &config.balloon)?; +fn dbus_api_do_command(matches: &ArgMatches, proxy: &DBusApi1ProxyBlocking<'_>) -> ApiResult { + match matches.subcommand_name() { + Some("boot") => proxy.api_vm_boot(), + Some("delete") => proxy.api_vm_delete(), + Some("shutdown-vmm") => proxy.api_vmm_shutdown(), + Some("resume") => proxy.api_vm_resume(), + Some("power-button") => proxy.api_vm_power_button(), + Some("reboot") => proxy.api_vm_reboot(), + Some("pause") => proxy.api_vm_pause(), + Some("info") => proxy.api_vm_info(), + Some("counters") => proxy.api_vm_counters(), + Some("ping") => proxy.api_vmm_ping(), + Some("shutdown") => proxy.api_vm_shutdown(), + Some("resize") => { + let resize = resize_config( + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("cpus") + .map(|x| x as &str), + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("memory") + .map(|x| x as &str), + matches + .subcommand_matches("resize") + .unwrap() + .get_one::("balloon") + .map(|x| x as &str), + )?; proxy.api_vm_resize(&resize) } - SubCommandEnum::ResizeZone(ref config) => { - let resize_zone = resize_zone_config(&config.id, &config.size)?; + Some("resize-zone") => { + let resize_zone = resize_zone_config( + matches + .subcommand_matches("resize-zone") + .unwrap() + .get_one::("id") + .unwrap(), + matches + .subcommand_matches("resize-zone") + .unwrap() + .get_one::("size") + .unwrap(), + )?; proxy.api_vm_resize_zone(&resize_zone) } - SubCommandEnum::AddDevice(ref config) => { - let device_config = add_device_config(&config.device_config)?; + Some("add-device") => { + let device_config = add_device_config( + matches + .subcommand_matches("add-device") + .unwrap() + .get_one::("device_config") + .unwrap(), + )?; proxy.api_vm_add_device(&device_config) } - SubCommandEnum::RemoveDevice(ref config) => { - let remove_device_data = remove_device_config(&config.device_config); + Some("remove-device") => { + let remove_device_data = remove_device_config( + matches + .subcommand_matches("remove-device") + .unwrap() + .get_one::("id") + .unwrap(), + ); proxy.api_vm_remove_device(&remove_device_data) } - SubCommandEnum::AddDisk(ref config) => { - let disk_config = add_disk_config(&config.disk_config)?; + Some("add-disk") => { + let disk_config = add_disk_config( + matches + .subcommand_matches("add-disk") + .unwrap() + .get_one::("disk_config") + .unwrap(), + )?; proxy.api_vm_add_disk(&disk_config) } - SubCommandEnum::AddFs(ref config) => { - let fs_config = add_fs_config(&config.fs_config)?; + Some("add-fs") => { + let fs_config = add_fs_config( + matches + .subcommand_matches("add-fs") + .unwrap() + .get_one::("fs_config") + .unwrap(), + )?; proxy.api_vm_add_fs(&fs_config) } - SubCommandEnum::AddPmem(ref config) => { - let pmem_config = add_pmem_config(&config.pmem_config)?; + Some("add-pmem") => { + let pmem_config = add_pmem_config( + matches + .subcommand_matches("add-pmem") + .unwrap() + .get_one::("pmem_config") + .unwrap(), + )?; proxy.api_vm_add_pmem(&pmem_config) } - SubCommandEnum::AddNet(ref config) => { - let (net_config, _fds) = add_net_config(&config.net_config)?; + Some("add-net") => { + let (net_config, _fds) = add_net_config( + matches + .subcommand_matches("add-net") + .unwrap() + .get_one::("net_config") + .unwrap(), + )?; proxy.api_vm_add_net(&net_config) } - SubCommandEnum::AddUserDevice(ref config) => { - let device_config = add_user_device_config(&config.device_config)?; + Some("add-user-device") => { + let device_config = add_user_device_config( + matches + .subcommand_matches("add-user-device") + .unwrap() + .get_one::("device_config") + .unwrap(), + )?; proxy.api_vm_add_user_device(&device_config) } - SubCommandEnum::AddVdpa(ref config) => { - let vdpa_config = add_vdpa_config(&config.vdpa_config)?; + Some("add-vdpa") => { + let vdpa_config = add_vdpa_config( + matches + .subcommand_matches("add-vdpa") + .unwrap() + .get_one::("vdpa_config") + .unwrap(), + )?; proxy.api_vm_add_vdpa(&vdpa_config) } - SubCommandEnum::AddVsock(ref config) => { - let vsock_config = add_vsock_config(&config.vsock_config)?; + Some("add-vsock") => { + let vsock_config = add_vsock_config( + matches + .subcommand_matches("add-vsock") + .unwrap() + .get_one::("vsock_config") + .unwrap(), + )?; proxy.api_vm_add_vsock(&vsock_config) } - SubCommandEnum::Snapshot(ref config) => { - let snapshot_config = snapshot_api_config(&config.snapshot_config); + Some("snapshot") => { + let snapshot_config = snapshot_config( + matches + .subcommand_matches("snapshot") + .unwrap() + .get_one::("snapshot_config") + .unwrap(), + ); proxy.api_vm_snapshot(&snapshot_config) } - SubCommandEnum::Restore(ref config) => { - let restore_config = restore_config(&config.restore_config)?; + Some("restore") => { + let restore_config = restore_config( + matches + .subcommand_matches("restore") + .unwrap() + .get_one::("restore_config") + .unwrap(), + )?; proxy.api_vm_restore(&restore_config) } - SubCommandEnum::Coredump(ref config) => { - let coredump_config = coredump_config(&config.coredump_config); + Some("coredump") => { + let coredump_config = coredump_config( + matches + .subcommand_matches("coredump") + .unwrap() + .get_one::("coredump_config") + .unwrap(), + ); proxy.api_vm_coredump(&coredump_config) } - SubCommandEnum::SendMigration(ref config) => { - let send_migration_data = - send_migration_data(&config.send_migration_config, config.send_migration_local); + Some("send-migration") => { + let send_migration_data = send_migration_data( + matches + .subcommand_matches("send-migration") + .unwrap() + .get_one::("send_migration_config") + .unwrap(), + matches + .subcommand_matches("send-migration") + .unwrap() + .get_flag("send_migration_local"), + ); proxy.api_vm_send_migration(&send_migration_data) } - SubCommandEnum::ReceiveMigration(ref config) => { - let receive_migration_data = receive_migration_data(&config.receive_migration_config); + Some("receive-migration") => { + let receive_migration_data = receive_migration_data( + matches + .subcommand_matches("receive-migration") + .unwrap() + .get_one::("receive_migration_config") + .unwrap(), + ); proxy.api_vm_receive_migration(&receive_migration_data) } - SubCommandEnum::Create(ref config) => { - let data = create_data(&config.vm_config)?; + Some("create") => { + let data = create_data( + matches + .subcommand_matches("create") + .unwrap() + .get_one::("path") + .unwrap(), + )?; proxy.api_vm_create(&data) } - SubCommandEnum::Version(_) => { - // Already handled outside of this function - panic!() - } + _ => unreachable!(), } } + fn resize_config( - desired_vcpus: Option, - memory: &Option, - balloon: &Option, + cpus: Option<&str>, + memory: Option<&str>, + balloon: Option<&str>, ) -> Result { + let desired_vcpus: Option = if let Some(cpus) = cpus { + Some(cpus.parse().map_err(Error::InvalidCpuCount)?) + } else { + None + }; + let desired_ram: Option = if let Some(memory) = memory { Some( memory @@ -599,7 +840,7 @@ fn add_vsock_config(config: &str) -> Result { Ok(vsock_config) } -fn snapshot_api_config(url: &str) -> String { +fn snapshot_config(url: &str) -> String { let snapshot_config = vmm::api::VmSnapshotConfig { destination_url: String::from(url), }; @@ -652,334 +893,239 @@ fn create_data(path: &str) -> Result { Ok(data) } -#[derive(FromArgs, PartialEq, Debug)] -#[doc = "Remotely control a cloud-hypervisor VMM.\n\nPlease refer to cloud-hypervisor for configuration syntaxes."] -struct TopLevel { - #[argh(subcommand)] - command: SubCommandEnum, - - #[argh(option, long = "api-socket")] - /// HTTP API socket path (UNIX domain socket) - api_socket: Option, - - #[cfg(feature = "dbus_api")] - #[argh(option, long = "dbus-service-name")] - /// well known name of the dbus service - dbus_name: Option, - - #[cfg(feature = "dbus_api")] - #[argh(option, long = "dbus-object-path")] - /// object path which the interface is being served at - dbus_path: Option, - - #[cfg(feature = "dbus_api")] - #[argh(switch, long = "dbus-system-bus")] - /// use the system bus instead of a session bus - dbus_system_bus: bool, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand)] -enum SubCommandEnum { - AddDevice(AddDeviceSubcommand), - AddDisk(AddDiskSubcommand), - AddFs(AddFsSubcommand), - AddPmem(AddPmemSubcommand), - AddNet(AddNetSubcommand), - AddUserDevice(AddUserDeviceSubcommand), - AddVdpa(AddVdpaSubcommand), - AddVsock(AddVsockSubcommand), - RemoveDevice(RemoveDeviceSubcommand), - Info(InfoSubcommand), - Counters(CountersSubcommand), - Pause(PauseSubcommand), - Reboot(RebootSubcommand), - PowerButton(PowerButtonSubcommand), - Resume(ResumeSubcommand), - Boot(BootSubcommand), - Delete(DeleteSubcommand), - Shutdown(ShutdownSubcommand), - Ping(PingSubcommand), - ShutdownVmm(ShutdownVmmSubcommand), - Resize(ResizeSubcommand), - ResizeZone(ResizeZoneSubcommand), - Snapshot(SnapshotSubcommand), - Restore(RestoreSubcommand), - Coredump(CoredumpSubcommand), - SendMigration(SendMigrationSubcommand), - ReceiveMigration(ReceiveMigrationSubcommand), - Create(CreateSubcommand), - Version(VersionSubcommand), -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-device")] -/// Add VFIO device -struct AddDeviceSubcommand { - #[argh(positional)] - /// device config - device_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-disk")] -/// Add block device -struct AddDiskSubcommand { - #[argh(positional)] - /// disk config - disk_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-fs")] -/// Add virtio-fs backed fs device -struct AddFsSubcommand { - #[argh(positional)] - /// virtio-fs config - fs_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-pmem")] -/// Add virtio-fs backed fs device -struct AddPmemSubcommand { - #[argh(positional)] - /// pmem config - pmem_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-net")] -/// Add virtio-fs backed fs device -struct AddNetSubcommand { - #[argh(positional)] - /// net config - net_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-user-device")] -/// Add userspace device -struct AddUserDeviceSubcommand { - #[argh(positional)] - /// device config - device_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-vdpa")] -/// Add vdpa device -struct AddVdpaSubcommand { - #[argh(positional)] - /// vdpa config - vdpa_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "add-vsock")] -/// Add vsock device -struct AddVsockSubcommand { - #[argh(positional)] - /// vsock config - vsock_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "remove-device")] -/// Remove VFIO device -struct RemoveDeviceSubcommand { - #[argh(positional)] - /// device config - device_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "info")] -/// Information on the VM -struct InfoSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "counters")] -/// Counters from the VM -struct CountersSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "pause")] -/// Pause the VM -struct PauseSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "reboot")] -/// Reboot the VM -struct RebootSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "power-button")] -/// Trigger a power button in the VM -struct PowerButtonSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "resume")] -/// Resume the VM -struct ResumeSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "boot")] -/// Boot a created VM -struct BootSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "delete")] -/// Delete a VM -struct DeleteSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "shutdown")] -/// Shutdown a VM -struct ShutdownSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "ping")] -/// Ping the VMM to check for API server availability -struct PingSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "shutdown-vmm")] -/// Shutdown the VMM -struct ShutdownVmmSubcommand {} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "resize")] -/// Resize the VM -struct ResizeSubcommand { - #[argh(option, long = "cpus")] - /// new VCPUs count - cpus: Option, - - #[argh(option, long = "memory")] - /// new memory size in bytes (supports K/M/G suffix)" - memory: Option, - - #[argh(option, long = "balloon")] - /// new balloon size in bytes (supports K/M/G suffix)" - balloon: Option, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "resize-zone")] -/// Resize a memory zone -struct ResizeZoneSubcommand { - #[argh(option, long = "id")] - /// memory zone identifier - id: String, - - #[argh(option, long = "size")] - /// new memory size in bytes (supports K/M/G suffix)" - size: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "snapshot")] -/// Create a snapshot from VM -struct SnapshotSubcommand { - #[argh(positional)] - /// destination_url - snapshot_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "restore")] -/// Restore VM from a snapshot -struct RestoreSubcommand { - #[argh(positional)] - /// restore config - restore_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "coredump")] -/// Create a coredump from VM -struct CoredumpSubcommand { - #[argh(positional)] - /// coredump config - coredump_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "send-migration")] -/// Initiate a VM migration -struct SendMigrationSubcommand { - #[argh(switch, long = "local")] - /// local migration - send_migration_local: bool, - - #[argh(positional)] - /// destination_url - send_migration_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "receive-migration")] -/// Receive a VM migration -struct ReceiveMigrationSubcommand { - #[argh(positional)] - /// receiver url - receive_migration_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "create")] -/// Create a VM from a JSON configuration -struct CreateSubcommand { - #[argh(positional, default = "String::from(\"-\")")] - /// vm config - vm_config: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "version")] -/// Print version information -struct VersionSubcommand {} - fn main() { - let toplevel: TopLevel = argh::from_env(); + let app = Command::new("ch-remote") + .author(env!("CARGO_PKG_AUTHORS")) + .version(env!("BUILD_VERSION")) + .about("Remotely control a cloud-hypervisor VMM.") + .args([ + Arg::new("api-socket") + .long("api-socket") + .help("HTTP API socket path (UNIX domain socket).") + .num_args(1), + #[cfg(feature = "dbus_api")] + Arg::new("dbus-service-name") + .long("dbus-service-name") + .help("Well known name of the dbus service") + .num_args(1), + #[cfg(feature = "dbus_api")] + Arg::new("dbus-object-path") + .long("dbus-object-path") + .help("Object path which the interface is being served at") + .num_args(1), + #[cfg(feature = "dbus_api")] + Arg::new("dbus-system-bus") + .long("dbus-system-bus") + .action(ArgAction::SetTrue) + .num_args(0) + .help("Use the system bus instead of a session bus"), + ]) + .subcommand( + Command::new("add-device").about("Add VFIO device").arg( + Arg::new("device_config") + .index(1) + .help(vmm::config::DeviceConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-disk").about("Add block device").arg( + Arg::new("disk_config") + .index(1) + .help(vmm::config::DiskConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-fs") + .about("Add virtio-fs backed fs device") + .arg( + Arg::new("fs_config") + .index(1) + .help(vmm::config::FsConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-pmem") + .about("Add persistent memory device") + .arg( + Arg::new("pmem_config") + .index(1) + .help(vmm::config::PmemConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-net").about("Add network device").arg( + Arg::new("net_config") + .index(1) + .help(vmm::config::NetConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-user-device") + .about("Add userspace device") + .arg( + Arg::new("device_config") + .index(1) + .help(vmm::config::UserDeviceConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-vdpa").about("Add vDPA device").arg( + Arg::new("vdpa_config") + .index(1) + .help(vmm::config::VdpaConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("add-vsock").about("Add vsock device").arg( + Arg::new("vsock_config") + .index(1) + .help(vmm::config::VsockConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("remove-device") + .about("Remove VFIO device") + .arg(Arg::new("id").index(1).help("")), + ) + .subcommand(Command::new("info").about("Info on the VM")) + .subcommand(Command::new("counters").about("Counters from the VM")) + .subcommand(Command::new("pause").about("Pause the VM")) + .subcommand(Command::new("reboot").about("Reboot the VM")) + .subcommand(Command::new("power-button").about("Trigger a power button in the VM")) + .subcommand( + Command::new("resize") + .about("Resize the VM") + .arg( + Arg::new("cpus") + .long("cpus") + .help("New vCPUs count") + .num_args(1), + ) + .arg( + Arg::new("memory") + .long("memory") + .help("New memory size in bytes (supports K/M/G suffix)") + .num_args(1), + ) + .arg( + Arg::new("balloon") + .long("balloon") + .help("New balloon size in bytes (supports K/M/G suffix)") + .num_args(1), + ), + ) + .subcommand( + Command::new("resize-zone") + .about("Resize a memory zone") + .arg( + Arg::new("id") + .long("id") + .help("Memory zone identifier") + .num_args(1), + ) + .arg( + Arg::new("size") + .long("size") + .help("New memory zone size in bytes (supports K/M/G suffix)") + .num_args(1), + ), + ) + .subcommand(Command::new("resume").about("Resume the VM")) + .subcommand(Command::new("boot").about("Boot a created VM")) + .subcommand(Command::new("delete").about("Delete a VM")) + .subcommand(Command::new("shutdown").about("Shutdown the VM")) + .subcommand( + Command::new("snapshot") + .about("Create a snapshot from VM") + .arg( + Arg::new("snapshot_config") + .index(1) + .help(""), + ), + ) + .subcommand( + Command::new("restore") + .about("Restore VM from a snapshot") + .arg( + Arg::new("restore_config") + .index(1) + .help(vmm::config::RestoreConfig::SYNTAX), + ), + ) + .subcommand( + Command::new("coredump") + .about("Create a coredump from VM") + .arg(Arg::new("coredump_config").index(1).help("")), + ) + .subcommand( + Command::new("send-migration") + .about("Initiate a VM migration") + .arg( + Arg::new("send_migration_config") + .index(1) + .help(""), + ) + .arg( + Arg::new("send_migration_local") + .long("local") + .num_args(0) + .action(ArgAction::SetTrue), + ), + ) + .subcommand( + Command::new("receive-migration") + .about("Receive a VM migration") + .arg( + Arg::new("receive_migration_config") + .index(1) + .help(""), + ), + ) + .subcommand( + Command::new("create") + .about("Create VM from a JSON configuration") + .arg(Arg::new("path").index(1).default_value("-")), + ) + .subcommand(Command::new("ping").about("Ping the VMM to check for API server availability")) + .subcommand(Command::new("shutdown-vmm").about("Shutdown the VMM")); - if matches!(toplevel.command, SubCommandEnum::Version(_)) { - println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION")); - return; - } + let matches = app.get_matches(); let mut target_api = match ( - &toplevel.api_socket, + matches.get_one::("api-socket"), #[cfg(feature = "dbus_api")] - &toplevel.dbus_name, + matches.get_one::("dbus-service-name"), #[cfg(feature = "dbus_api")] - &toplevel.dbus_path, + matches.get_one::("dbus-object-path"), ) { #[cfg(not(feature = "dbus_api"))] - (Some(ref api_socket),) => TargetApi::HttpApi( - UnixStream::connect(api_socket).unwrap_or_else(|e| { + (Some(api_sock),) => TargetApi::HttpApi( + UnixStream::connect(api_sock).unwrap_or_else(|e| { eprintln!("Error opening HTTP socket: {e}"); process::exit(1) }), PhantomData, ), #[cfg(feature = "dbus_api")] - (Some(ref api_socket), None, None) => TargetApi::HttpApi( - UnixStream::connect(api_socket).unwrap_or_else(|e| { + (Some(api_sock), None, None) => TargetApi::HttpApi( + UnixStream::connect(api_sock).unwrap_or_else(|e| { eprintln!("Error opening HTTP socket: {e}"); process::exit(1) }), PhantomData, ), #[cfg(feature = "dbus_api")] - (None, Some(ref dbus_name), Some(ref dbus_path)) => TargetApi::DBusApi( - DBusApi1ProxyBlocking::new_connection(dbus_name, dbus_path, toplevel.dbus_system_bus) - .map_err(Error::DBusApiClient) - .unwrap_or_else(|e| { - eprintln!("Error creating D-Bus proxy: {e}"); - process::exit(1) - }), + (None, Some(dbus_name), Some(dbus_path)) => TargetApi::DBusApi( + DBusApi1ProxyBlocking::new_connection( + dbus_name, + dbus_path, + matches.get_flag("dbus-system-bus"), + ) + .map_err(Error::DBusApiClient) + .unwrap_or_else(|e| { + eprintln!("Error creating D-Bus proxy: {e}"); + process::exit(1) + }), ), #[cfg(feature = "dbus_api")] (Some(_), Some(_) | None, Some(_) | None) => { @@ -994,7 +1140,7 @@ fn main() { } }; - if let Err(e) = target_api.do_command(&toplevel) { + if let Err(e) = target_api.do_command(&matches) { eprintln!("Error running command: {e}"); process::exit(1) };