automation-tests/cmd/podman/machine/list.go

234 lines
6.8 KiB
Go

//go:build amd64 || arm64
package machine
import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"time"
"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/report"
"github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/validate"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/machine"
provider2 "github.com/containers/podman/v5/pkg/machine/provider"
"github.com/containers/podman/v5/pkg/machine/shim"
"github.com/containers/podman/v5/pkg/machine/vmconfigs"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)
var (
lsCmd = &cobra.Command{
Use: "list [options]",
Aliases: []string{"ls"},
Short: "List machines",
Long: "List managed virtual machines.",
// do not use machinePreRunE, as that pre-sets the provider
PersistentPreRunE: rootlessOnly,
RunE: list,
Args: validate.NoArgs,
ValidArgsFunction: completion.AutocompleteNone,
Example: `podman machine list,
podman machine list --format json
podman machine ls`,
}
listFlag = listFlagType{}
)
type listFlagType struct {
format string
noHeading bool
quiet bool
allProviders bool
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: lsCmd,
Parent: machineCmd,
})
flags := lsCmd.Flags()
formatFlagName := "format"
flags.StringVar(&listFlag.format, formatFlagName, "{{range .}}{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n{{end -}}", "Format volume output using JSON or a Go template")
_ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.ListReporter{}))
flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print headers")
flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Show only machine names")
flags.BoolVar(&listFlag.allProviders, "all-providers", false, "Show machines from all providers")
}
func list(cmd *cobra.Command, args []string) error {
var (
opts machine.ListOptions
err error
)
var providers []vmconfigs.VMProvider
if listFlag.allProviders {
providers = provider2.GetAll()
} else {
provider, err = provider2.Get()
if err != nil {
return err
}
providers = []vmconfigs.VMProvider{provider}
}
listResponse, err := shim.List(providers, opts)
if err != nil {
return err
}
// Sort by last run
sort.Slice(listResponse, func(i, j int) bool {
return listResponse[i].LastUp.After(listResponse[j].LastUp)
})
// Bring currently running machines to top
sort.Slice(listResponse, func(i, j int) bool {
return listResponse[i].Running
})
// ignore the error here we only want to know if we have a default connection to show it in list
defaultCon, _ := registry.PodmanConfig().ContainersConfDefaultsRO.GetConnection("", true)
if report.IsJSON(listFlag.format) {
machineReporter := toMachineFormat(listResponse, defaultCon)
b, err := json.MarshalIndent(machineReporter, "", " ")
if err != nil {
return err
}
os.Stdout.Write(b)
return nil
}
machineReporter := toHumanFormat(listResponse, defaultCon)
return outputTemplate(cmd, machineReporter)
}
func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) error {
headers := report.Headers(entities.ListReporter{}, map[string]string{
"LastUp": "LAST UP",
"VmType": "VM TYPE",
"CPUs": "CPUS",
"Memory": "MEMORY",
"DiskSize": "DISK SIZE",
})
rpt := report.New(os.Stdout, cmd.Name())
defer rpt.Flush()
var err error
switch {
case cmd.Flag("format").Changed:
rpt, err = rpt.Parse(report.OriginUser, listFlag.format)
case listFlag.quiet:
rpt, err = rpt.Parse(report.OriginUser, "{{.Name}}\n")
default:
rpt, err = rpt.Parse(report.OriginPodman, listFlag.format)
}
if err != nil {
return err
}
if rpt.RenderHeaders && !listFlag.noHeading {
if err := rpt.Execute(headers); err != nil {
return fmt.Errorf("failed to write report column headers: %w", err)
}
}
return rpt.Execute(responses)
}
func strTime(t time.Time) string {
iso, err := t.MarshalText()
if err != nil {
return ""
}
return string(iso)
}
func strUint(u uint64) string {
return strconv.FormatUint(u, 10)
}
func streamName(imageStream string) string {
if imageStream == "" {
return "default"
}
return imageStream
}
func toMachineFormat(vms []*machine.ListResponse, defaultCon *config.Connection) []*entities.ListReporter {
machineResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
isDefault := false
// check port, in case we somehow have machines with the same name in different providers
if defaultCon != nil {
isDefault = vm.Name == defaultCon.Name && strings.Contains(defaultCon.URI, strconv.Itoa((vm.Port)))
}
response := new(entities.ListReporter)
response.Default = isDefault
response.Name = vm.Name
response.Running = vm.Running
response.LastUp = strTime(vm.LastUp)
response.Created = strTime(vm.CreatedAt)
response.Stream = streamName(vm.Stream)
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = strUint(uint64(vm.Memory.ToBytes()))
response.DiskSize = strUint(uint64(vm.DiskSize.ToBytes()))
response.Port = vm.Port
response.RemoteUsername = vm.RemoteUsername
response.IdentityPath = vm.IdentityPath
response.Starting = vm.Starting
response.UserModeNetworking = vm.UserModeNetworking
machineResponses = append(machineResponses, response)
}
return machineResponses
}
func toHumanFormat(vms []*machine.ListResponse, defaultCon *config.Connection) []*entities.ListReporter {
humanResponses := make([]*entities.ListReporter, 0, len(vms))
for _, vm := range vms {
response := new(entities.ListReporter)
isDefault := false
// check port, in case we somehow have machines with the same name in different providers
if defaultCon != nil {
isDefault = vm.Name == defaultCon.Name && strings.Contains(defaultCon.URI, strconv.Itoa((vm.Port)))
}
if isDefault {
response.Name = vm.Name + "*"
response.Default = true
} else {
response.Name = vm.Name
}
switch {
case vm.Starting:
response.LastUp = "Currently starting"
response.Starting = true
case vm.Running:
response.LastUp = "Currently running"
response.Running = true
case vm.LastUp.IsZero():
response.LastUp = "Never"
default:
response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago"
}
response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago"
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = units.BytesSize(float64(vm.Memory.ToBytes()))
response.DiskSize = units.BytesSize(float64(vm.DiskSize.ToBytes()))
humanResponses = append(humanResponses, response)
}
return humanResponses
}