mirror of
https://github.com/containers/podman.git
synced 2025-10-17 19:24:04 +08:00
Merge pull request #13829 from baude/machineinspect
Introduce machine inspect
This commit is contained in:
90
cmd/podman/machine/inspect.go
Normal file
90
cmd/podman/machine/inspect.go
Normal file
@ -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)
|
||||
}
|
35
docs/source/markdown/podman-machine-inspect.1.md
Normal file
35
docs/source/markdown/podman-machine-inspect.1.md
Normal file
@ -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>
|
@ -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>
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user