Skip to content

Commit

Permalink
Podman machine info
Browse files Browse the repository at this point in the history
Add podman machine info command, which displays infor about the machine
host as well as version info.

Signed-off-by: Ashley Cui <[email protected]>
  • Loading branch information
ashley-cui committed Jul 5, 2022
1 parent b00e65a commit 9d6efb3
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 9 deletions.
182 changes: 182 additions & 0 deletions cmd/podman/machine/info.go
Original file line number Diff line number Diff line change
@@ -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
}
20 changes: 13 additions & 7 deletions cmd/podman/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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)

Expand Down
36 changes: 36 additions & 0 deletions docs/source/markdown/podman-machine-info.1.md
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
3 changes: 2 additions & 1 deletion docs/source/markdown/podman-machine.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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 <[email protected]>
1 change: 1 addition & 0 deletions pkg/machine/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Provider interface {
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error)
RemoveAndCleanMachines() error
VMType() string
}

type RemoteConnectionType string
Expand Down
20 changes: 20 additions & 0 deletions pkg/machine/e2e/config_info.go
Original file line number Diff line number Diff line change
@@ -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
}
58 changes: 58 additions & 0 deletions pkg/machine/e2e/info_test.go
Original file line number Diff line number Diff line change
@@ -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())
})
})
5 changes: 4 additions & 1 deletion pkg/machine/qemu/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions pkg/machine/wsl/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -1553,3 +1553,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
}
return prevErr
}

func (p *Provider) VMType() string {
return vmtype
}

0 comments on commit 9d6efb3

Please sign in to comment.