mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
182
cmd/podman/machine/info.go
Normal file
182
cmd/podman/machine/info.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
|
||||
|
36
docs/source/markdown/podman-machine-info.1.md
Normal file
36
docs/source/markdown/podman-machine-info.1.md
Normal file
@ -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>
|
@ -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>
|
||||
|
@ -55,6 +55,7 @@ type Provider interface {
|
||||
IsValidVMName(name string) (bool, error)
|
||||
CheckExclusiveActiveVM() (bool, string, error)
|
||||
RemoveAndCleanMachines() error
|
||||
VMType() string
|
||||
}
|
||||
|
||||
type RemoteConnectionType string
|
||||
|
20
pkg/machine/e2e/config_info.go
Normal file
20
pkg/machine/e2e/config_info.go
Normal file
@ -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
pkg/machine/e2e/info_test.go
Normal file
58
pkg/machine/e2e/info_test.go
Normal file
@ -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())
|
||||
})
|
||||
})
|
@ -1701,6 +1701,9 @@ func isProcessAlive(pid int) bool {
|
||||
if err == nil || err == unix.EPERM {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Provider) VMType() string {
|
||||
return vmtype
|
||||
}
|
||||
|
@ -1655,3 +1655,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
|
||||
}
|
||||
return prevErr
|
||||
}
|
||||
|
||||
func (p *Provider) VMType() string {
|
||||
return vmtype
|
||||
}
|
||||
|
Reference in New Issue
Block a user