summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/machine/info.go182
-rw-r--r--cmd/podman/machine/machine.go20
-rw-r--r--docs/source/markdown/podman-machine-info.1.md36
-rw-r--r--docs/source/markdown/podman-machine.1.md3
-rw-r--r--pkg/machine/config.go1
-rw-r--r--pkg/machine/e2e/config_info.go20
-rw-r--r--pkg/machine/e2e/info_test.go58
-rw-r--r--pkg/machine/qemu/machine.go5
-rw-r--r--pkg/machine/wsl/machine.go4
9 files changed, 320 insertions, 9 deletions
diff --git a/cmd/podman/machine/info.go b/cmd/podman/machine/info.go
new file mode 100644
index 000000000..9932027d8
--- /dev/null
+++ b/cmd/podman/machine/info.go
@@ -0,0 +1,182 @@
+//go:build amd64 || arm64
+// +build amd64 arm64
+
+package machine
+
+import (
+ "fmt"
+ "html/template"
+ "os"
+ "runtime"
+
+ "github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/common/pkg/report"
+ "github.com/containers/podman/v4/cmd/podman/common"
+ "github.com/containers/podman/v4/cmd/podman/registry"
+ "github.com/containers/podman/v4/cmd/podman/validate"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/machine"
+ "github.com/ghodss/yaml"
+ "github.com/spf13/cobra"
+)
+
+var infoDescription = `Display information pertaining to the machine host.`
+
+var (
+ infoCmd = &cobra.Command{
+ Use: "info [options]",
+ Short: "Display machine host info",
+ Long: infoDescription,
+ PersistentPreRunE: rootlessOnly,
+ RunE: info,
+ Args: validate.NoArgs,
+ ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman machine info`,
+ }
+)
+
+var (
+ inFormat string
+)
+
+// Info contains info on the machine host and version info
+type Info struct {
+ Host *HostInfo `json:"Host"`
+ Version define.Version `json:"Version"`
+}
+
+// HostInfo contains info on the machine host
+type HostInfo struct {
+ Arch string `json:"Arch"`
+ CurrentMachine string `json:"CurrentMachine"`
+ DefaultMachine string `json:"DefaultMachine"`
+ EventsDir string `json:"EventsDir"`
+ MachineConfigDir string `json:"MachineConfigDir"`
+ MachineImageDir string `json:"MachineImageDir"`
+ MachineState string `json:"MachineState"`
+ NumberOfMachines int `json:"NumberOfMachines"`
+ OS string `json:"OS"`
+ VMType string `json:"VMType"`
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Command: infoCmd,
+ Parent: machineCmd,
+ })
+
+ flags := infoCmd.Flags()
+ formatFlagName := "format"
+ flags.StringVarP(&inFormat, formatFlagName, "f", "", "Change the output format to JSON or a Go template")
+ _ = infoCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&define.Info{}))
+}
+
+func info(cmd *cobra.Command, args []string) error {
+ info := Info{}
+ version, err := define.GetVersion()
+ if err != nil {
+ return fmt.Errorf("error getting version info %w", err)
+ }
+ info.Version = version
+
+ host, err := hostInfo()
+ if err != nil {
+ return err
+ }
+ info.Host = host
+
+ switch {
+ case report.IsJSON(inFormat):
+ b, err := json.MarshalIndent(info, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ case cmd.Flags().Changed("format"):
+ tmpl := template.New(cmd.Name()).Funcs(template.FuncMap(report.DefaultFuncs))
+ inFormat = report.NormalizeFormat(inFormat)
+ tmpl, err := tmpl.Parse(inFormat)
+ if err != nil {
+ return err
+ }
+ return tmpl.Execute(os.Stdout, info)
+ default:
+ b, err := yaml.Marshal(info)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ }
+
+ return nil
+}
+
+func hostInfo() (*HostInfo, error) {
+ host := HostInfo{}
+
+ host.Arch = runtime.GOARCH
+ host.OS = runtime.GOOS
+
+ provider := GetSystemDefaultProvider()
+ var listOpts machine.ListOptions
+ listResponse, err := provider.List(listOpts)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machines %w", err)
+ }
+
+ host.NumberOfMachines = len(listResponse)
+
+ cfg, err := config.ReadCustomConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ // Default state of machine is stopped
+ host.MachineState = "Stopped"
+ for _, vm := range listResponse {
+ // Set default machine if found
+ if vm.Name == cfg.Engine.ActiveService {
+ host.DefaultMachine = vm.Name
+ }
+ // If machine is running or starting, it is automatically the current machine
+ if vm.Running {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Running"
+ } else if vm.Starting {
+ host.CurrentMachine = vm.Name
+ host.MachineState = "Starting"
+ }
+ }
+ // If no machines are starting or running, set current machine to default machine
+ // If no default machines are found, do not report a default machine or a state
+ if host.CurrentMachine == "" {
+ if host.DefaultMachine == "" {
+ host.MachineState = ""
+ } else {
+ host.CurrentMachine = host.DefaultMachine
+ }
+ }
+
+ host.VMType = provider.VMType()
+
+ dataDir, err := machine.GetDataDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine image dir")
+ }
+ host.MachineImageDir = dataDir
+
+ confDir, err := machine.GetConfDir(host.VMType)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get machine config dir %w", err)
+ }
+ host.MachineConfigDir = confDir
+
+ eventsDir, err := eventSockDir()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get events dir: %w", err)
+ }
+ host.EventsDir = eventsDir
+
+ return &host, nil
+}
diff --git a/cmd/podman/machine/machine.go b/cmd/podman/machine/machine.go
index 5a8a06b9d..0618337cc 100644
--- a/cmd/podman/machine/machine.go
+++ b/cmd/podman/machine/machine.go
@@ -101,12 +101,6 @@ func resolveEventSock() ([]string, error) {
return []string{sock}, nil
}
- xdg, err := util.GetRuntimeDir()
- if err != nil {
- logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
- return nil, nil
- }
-
re := regexp.MustCompile(`machine_events.*\.sock`)
sockPaths := make([]string, 0)
fn := func(path string, info os.DirEntry, err error) error {
@@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) {
sockPaths = append(sockPaths, path)
return nil
}
+ sockDir, err := eventSockDir()
+ if err != nil {
+ logrus.Warnf("Failed to get runtime dir, machine events will not be published: %s", err)
+ }
- if err := filepath.WalkDir(filepath.Join(xdg, "podman"), fn); err != nil {
+ if err := filepath.WalkDir(sockDir, fn); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
@@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) {
return sockPaths, nil
}
+func eventSockDir() (string, error) {
+ xdg, err := util.GetRuntimeDir()
+ if err != nil {
+ return "", err
+ }
+ return filepath.Join(xdg, "podman"), nil
+}
+
func newMachineEvent(status events.Status, event events.Event) {
openEventSock.Do(initMachineEvents)
diff --git a/docs/source/markdown/podman-machine-info.1.md b/docs/source/markdown/podman-machine-info.1.md
new file mode 100644
index 000000000..33c207d32
--- /dev/null
+++ b/docs/source/markdown/podman-machine-info.1.md
@@ -0,0 +1,36 @@
+% podman-machine-info(1)
+
+## NAME
+podman\-machine\-info - Display machine host info
+
+## SYNOPSIS
+**podman machine info**
+
+## DESCRIPTION
+
+Display information pertaining to the machine host.
+Rootless only, as all `podman machine` commands can be only be used with rootless Podman.
+
+## OPTIONS
+
+#### **--format**=*format*, **-f**
+
+Change output format to "json" or a Go template.
+
+#### **--help**
+
+Print usage statement.
+
+## EXAMPLES
+
+```
+$ podman machine info
+$ podman machine info --format json
+$ podman machine info --format {{.Host.Arch}}
+```
+
+## SEE ALSO
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
+
+## HISTORY
+June 2022, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md
index c55226e02..6197b8d4e 100644
--- a/docs/source/markdown/podman-machine.1.md
+++ b/docs/source/markdown/podman-machine.1.md
@@ -20,6 +20,7 @@ All `podman machine` commands are rootless only.
| Command | Man Page | Description |
|---------|------------------------------------------------------|-----------------------------------|
+| info | [podman-machine-info(1)](podman-machine-info.1.md) | Display machine host info |
| 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 |
@@ -30,7 +31,7 @@ All `podman machine` commands are rootless only.
| 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-machine-inspect(1)](podman-machine-inspect.1.md)**
+**[podman(1)](podman.1.md)**, **[podman-machine-info(1)](podman-machine-info.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 fcc129338..66fa6ab91 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -55,6 +55,7 @@ type Provider interface {
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error)
RemoveAndCleanMachines() error
+ VMType() string
}
type RemoteConnectionType string
diff --git a/pkg/machine/e2e/config_info.go b/pkg/machine/e2e/config_info.go
new file mode 100644
index 000000000..410c7e518
--- /dev/null
+++ b/pkg/machine/e2e/config_info.go
@@ -0,0 +1,20 @@
+package e2e
+
+type infoMachine struct {
+ format string
+ cmd []string
+}
+
+func (i *infoMachine) buildCmd(m *machineTestBuilder) []string {
+ cmd := []string{"machine", "info"}
+ if len(i.format) > 0 {
+ cmd = append(cmd, "--format", i.format)
+ }
+ i.cmd = cmd
+ return cmd
+}
+
+func (i *infoMachine) withFormat(format string) *infoMachine {
+ i.format = format
+ return i
+}
diff --git a/pkg/machine/e2e/info_test.go b/pkg/machine/e2e/info_test.go
new file mode 100644
index 000000000..eeabb78af
--- /dev/null
+++ b/pkg/machine/e2e/info_test.go
@@ -0,0 +1,58 @@
+package e2e
+
+import (
+ "github.com/containers/podman/v4/cmd/podman/machine"
+ jsoniter "github.com/json-iterator/go"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("podman machine info", func() {
+ var (
+ mb *machineTestBuilder
+ testDir string
+ )
+
+ BeforeEach(func() {
+ testDir, mb = setup()
+ })
+ AfterEach(func() {
+ teardown(originalHomeDir, testDir, mb)
+ })
+
+ It("machine info", func() {
+ info := new(infoMachine)
+ infoSession, err := mb.setCmd(info).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+
+ // Verify go template works and check for no running machines
+ info = new(infoMachine)
+ infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+ Expect(infoSession.outputToString()).To(Equal("0"))
+
+ // Create a machine and check if info has been updated
+ i := new(initMachine)
+ initSession, err := mb.setCmd(i.withImagePath(mb.imagePath)).run()
+ Expect(err).To(BeNil())
+ Expect(initSession).To(Exit(0))
+
+ info = new(infoMachine)
+ infoSession, err = mb.setCmd(info.withFormat("{{.Host.NumberOfMachines}}")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+ Expect(infoSession.outputToString()).To(Equal("1"))
+
+ // Check if json is in correct format
+ infoSession, err = mb.setCmd(info.withFormat("json")).run()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(infoSession).Should(Exit(0))
+
+ infoReport := &machine.Info{}
+ err = jsoniter.Unmarshal(infoSession.Bytes(), infoReport)
+ Expect(err).To(BeNil())
+ })
+})
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 2fe0230cf..616d573eb 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -1698,6 +1698,9 @@ func isProcessAlive(pid int) bool {
if err == nil || err == unix.EPERM {
return true
}
-
return false
}
+
+func (p *Provider) VMType() string {
+ return vmtype
+}
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 075f42cb2..6553fb6a5 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -1553,3 +1553,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
}
return prevErr
}
+
+func (p *Provider) VMType() string {
+ return vmtype
+}