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
|
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`)
|
re := regexp.MustCompile(`machine_events.*\.sock`)
|
||||||
sockPaths := make([]string, 0)
|
sockPaths := make([]string, 0)
|
||||||
fn := func(path string, info os.DirEntry, err error) error {
|
fn := func(path string, info os.DirEntry, err error) error {
|
||||||
@ -125,8 +119,12 @@ func resolveEventSock() ([]string, error) {
|
|||||||
sockPaths = append(sockPaths, path)
|
sockPaths = append(sockPaths, path)
|
||||||
return nil
|
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) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -135,6 +133,14 @@ func resolveEventSock() ([]string, error) {
|
|||||||
return sockPaths, nil
|
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) {
|
func newMachineEvent(status events.Status, event events.Event) {
|
||||||
openEventSock.Do(initMachineEvents)
|
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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
|
||||||
|
|
||||||
## SEE ALSO
|
## 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
|
## HISTORY
|
||||||
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
|
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
|
||||||
|
@ -55,6 +55,7 @@ type Provider interface {
|
|||||||
IsValidVMName(name string) (bool, error)
|
IsValidVMName(name string) (bool, error)
|
||||||
CheckExclusiveActiveVM() (bool, string, error)
|
CheckExclusiveActiveVM() (bool, string, error)
|
||||||
RemoveAndCleanMachines() error
|
RemoveAndCleanMachines() error
|
||||||
|
VMType() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RemoteConnectionType 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 {
|
if err == nil || err == unix.EPERM {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) VMType() string {
|
||||||
|
return vmtype
|
||||||
|
}
|
||||||
|
@ -1655,3 +1655,7 @@ func (p *Provider) RemoveAndCleanMachines() error {
|
|||||||
}
|
}
|
||||||
return prevErr
|
return prevErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) VMType() string {
|
||||||
|
return vmtype
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user