diff options
-rw-r--r-- | cmd/podman/machine/inspect.go | 90 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-inspect.1.md | 35 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine.1.md | 21 | ||||
-rw-r--r-- | pkg/machine/config.go | 15 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 50 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 7 |
6 files changed, 174 insertions, 44 deletions
diff --git a/cmd/podman/machine/inspect.go b/cmd/podman/machine/inspect.go new file mode 100644 index 000000000..d43cabf6b --- /dev/null +++ b/cmd/podman/machine/inspect.go @@ -0,0 +1,90 @@ +//go:build amd64 || arm64 +// +build amd64 arm64 + +package machine + +import ( + "encoding/json" + "os" + + "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/cmd/podman/utils" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/machine" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + inspectCmd = &cobra.Command{ + Use: "inspect [options] [MACHINE...]", + Short: "Inspect an existing machine", + Long: "Provide details on a managed virtual machine", + RunE: inspect, + Example: `podman machine inspect myvm`, + ValidArgsFunction: autocompleteMachine, + } + inspectFlag = inspectFlagType{} +) + +type inspectFlagType struct { + format string +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: inspectCmd, + Parent: machineCmd, + }) + + flags := inspectCmd.Flags() + formatFlagName := "format" + flags.StringVar(&inspectFlag.format, formatFlagName, "", "Format volume output using JSON or a Go template") + _ = inspectCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(machine.InspectInfo{})) +} + +func inspect(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + if len(args) < 1 { + args = append(args, defaultMachineName) + } + vms := make([]machine.InspectInfo, 0, len(args)) + provider := getSystemDefaultProvider() + for _, vmName := range args { + vm, err := provider.LoadVMByName(vmName) + if err != nil { + errs = append(errs, err) + continue + } + state, err := vm.State() + if err != nil { + errs = append(errs, err) + continue + } + ii := machine.InspectInfo{ + State: state, + VM: vm, + } + vms = append(vms, ii) + } + if len(inspectFlag.format) > 0 { + // need jhonce to work his template magic + return define.ErrNotImplemented + } + if err := printJSON(vms); err != nil { + logrus.Error(err) + } + return errs.PrintErrors() +} + +func printJSON(data []machine.InspectInfo) error { + enc := json.NewEncoder(os.Stdout) + // by default, json marshallers will force utf=8 from + // a string. this breaks healthchecks that use <,>, &&. + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(data) +} diff --git a/docs/source/markdown/podman-machine-inspect.1.md b/docs/source/markdown/podman-machine-inspect.1.md new file mode 100644 index 000000000..38eb66b0d --- /dev/null +++ b/docs/source/markdown/podman-machine-inspect.1.md @@ -0,0 +1,35 @@ +% podman-machine-inspect(1) + +## NAME +podman\-machine\-inspect - Inspect one or more virtual machines + +## SYNOPSIS +**podman machine inspect** [*options] *name* ... + +## DESCRIPTION + +Inspect one or more virtual machines + +Obtain greater detail about Podman virtual machines. More than one virtual machine can be +inspected at once. + +## OPTIONS +#### **--format** + +Print results with a Go template. + +#### **--help** + +Print usage statement. + +## EXAMPLES + +``` +$ podman machine inspect podman-machine-default +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)** + +## HISTORY +April 2022, Originally compiled by Brent Baude <bbaude@redhat.com> diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md index 3bdfd0be9..e9f6c7d20 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -11,18 +11,19 @@ podman\-machine - Manage Podman's virtual machine ## SUBCOMMANDS -| Command | Man Page | Description | -| ------- | ------------------------------------------------------- | --------------------------------- | -| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | -| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | -| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine | -| set | [podman-machine-set(1)](podman-machine-set.1.md) | Sets a virtual machine setting | -| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | -| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | -| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | +| Command | Man Page | Description | +|---------|------------------------------------------------------|-----------------------------------| +| init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | +| inspect | [podman-machine-inspect(1)](podman-machine-inspect.1.md) | Inspect one or more virtual machines | +| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | +| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine | +| set | [podman-machine-set(1)](podman-machine-set.1.md) | Sets a virtual machine setting | +| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | +| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | +| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)** +**[podman(1)](podman.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**, **[podman-machine-inspect(1)](podman-machine-inspect.1.md)** ## HISTORY March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 7e1561506..6c2fab0e5 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -33,14 +33,14 @@ type InitOptions struct { UID string } -type QemuMachineStatus = string +type Status = string const ( // Running indicates the qemu vm is running. - Running QemuMachineStatus = "running" + Running Status = "running" // Stopped indicates the vm has stopped. - Stopped QemuMachineStatus = "stopped" - DefaultMachineName string = "podman-machine-default" + Stopped Status = "stopped" + DefaultMachineName string = "podman-machine-default" ) type Provider interface { @@ -113,12 +113,15 @@ type RemoveOptions struct { SaveIgnition bool } +type InspectOptions struct{} + type VM interface { Init(opts InitOptions) (bool, error) Remove(name string, opts RemoveOptions) (string, func() error, error) Set(name string, opts SetOptions) error SSH(name string, opts SSHOptions) error Start(name string, opts StartOptions) error + State() (Status, error) Stop(name string, opts StopOptions) error } @@ -126,6 +129,10 @@ type DistributionDownload interface { HasUsableCache() (bool, error) Get() *Download } +type InspectInfo struct { + State Status + VM +} func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL { //TODO Should this function have input verification? diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 321c1b99c..a3dedeedb 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -439,12 +439,12 @@ func (v *MachineVM) Set(_ string, opts machine.SetOptions) error { return nil } - running, err := v.isRunning() + state, err := v.State() if err != nil { return err } - if running { + if state == machine.Running { suffix := "" if v.Name != machine.DefaultMachineName { suffix = " " + v.Name @@ -581,14 +581,14 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { } if len(v.Mounts) > 0 { - running, err := v.isRunning() + state, err := v.State() if err != nil { return err } listening := v.isListening() - for !running || !listening { + for state != machine.Running || !listening { time.Sleep(100 * time.Millisecond) - running, err = v.isRunning() + state, err = v.State() if err != nil { return err } @@ -634,7 +634,7 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return nil } -func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachineStatus, error) { +func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.Status, error) { // this is the format returned from the monitor // {"return": {"status": "running", "singlestep": false, "running": true}} @@ -748,11 +748,11 @@ func (v *MachineVM) Stop(_ string, _ machine.StopOptions) error { disconnected = true waitInternal := 250 * time.Millisecond for i := 0; i < 5; i++ { - running, err := v.isRunning() + state, err := v.State() if err != nil { return err } - if !running { + if state != machine.Running { break } time.Sleep(waitInternal) @@ -800,11 +800,11 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func() ) // cannot remove a running vm unless --force is used - running, err := v.isRunning() + state, err := v.State() if err != nil { return "", nil, err } - if running && !opts.Force { + if state == machine.Running && !opts.Force { return "", nil, errors.Errorf("running vm %q cannot be destroyed", v.Name) } @@ -858,10 +858,7 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func() confirmationMessage += "\n" return confirmationMessage, func() error { for _, f := range files { - if err := os.Remove(f); err != nil { - if errors.Is(err, os.ErrNotExist) { - continue - } + if err := os.Remove(f); err != nil && !errors.Is(err, os.ErrNotExist) { logrus.Error(err) } } @@ -869,19 +866,19 @@ func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func() }, nil } -func (v *MachineVM) isRunning() (bool, error) { +func (v *MachineVM) State() (machine.Status, error) { // Check if qmp socket path exists if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) { - return false, nil + return "", nil } // Check if we can dial it monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout) if err != nil { // FIXME: this error should probably be returned - return false, nil // nolint: nilerr + return "", err } if err := monitor.Connect(); err != nil { - return false, err + return "", err } defer func() { if err := monitor.Disconnect(); err != nil { @@ -889,14 +886,7 @@ func (v *MachineVM) isRunning() (bool, error) { } }() // If there is a monitor, lets see if we can query state - state, err := v.checkStatus(monitor) - if err != nil { - return false, err - } - if state == machine.Running { - return true, nil - } - return false, nil + return v.checkStatus(monitor) } func (v *MachineVM) isListening() bool { @@ -912,11 +902,11 @@ func (v *MachineVM) isListening() bool { // SSH opens an interactive SSH session to the vm specified. // Added ssh function to VM interface: pkg/machine/config/go : line 58 func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error { - running, err := v.isRunning() + state, err := v.State() if err != nil { return err } - if !running { + if state != machine.Running { return errors.Errorf("vm %q is not running.", v.Name) } @@ -1037,11 +1027,11 @@ func getVMInfos() ([]*machine.ListResponse, error) { return err } listEntry.LastUp = fi.ModTime() - running, err := vm.isRunning() + state, err := vm.State() if err != nil { return err } - if running { + if state == machine.Running { listEntry.Running = true } diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index fdda45ca6..1da042f6a 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -18,6 +18,7 @@ import ( "strings" "time" + "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/homedir" @@ -1013,6 +1014,12 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return nil } +// TODO: We need to rename isRunning to State(); I do not have a +// windows system to test this on. +func (v *MachineVM) State() (machine.Status, error) { + return "", define.ErrNotImplemented +} + func stopWinProxy(v *MachineVM) error { pid, tid, tidFile, err := readWinProxyTid(v) if err != nil { |