diff options
-rw-r--r-- | cmd/podman/machine/list.go | 143 | ||||
-rw-r--r-- | docs/source/machine.rst | 1 | ||||
-rw-r--r-- | docs/source/markdown/links/podman-machine-ls.1 | 1 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine-list.1.md | 50 | ||||
-rw-r--r-- | docs/source/markdown/podman-machine.1.md | 11 | ||||
-rw-r--r-- | pkg/machine/config.go | 11 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 50 |
7 files changed, 262 insertions, 5 deletions
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go new file mode 100644 index 000000000..3c8368c6b --- /dev/null +++ b/cmd/podman/machine/list.go @@ -0,0 +1,143 @@ +// +build amd64,linux arm64,linux amd64,darwin arm64,darwin + +package machine + +import ( + "os" + "sort" + "text/tabwriter" + "text/template" + "time" + + "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/machine" + "github.com/containers/podman/v3/pkg/machine/qemu" + "github.com/docker/go-units" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + lsCmd = &cobra.Command{ + Use: "list [options]", + Aliases: []string{"ls"}, + Short: "List machines", + Long: "List Podman managed virtual machines.", + RunE: list, + Args: validate.NoArgs, + Example: `podman machine list, + podman machine ls`, + ValidArgsFunction: completion.AutocompleteNone, + } + listFlag = listFlagType{} +) + +type listFlagType struct { + format string + noHeading bool +} + +type machineReporter struct { + Name string + Created string + LastUp string + VMType string +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: lsCmd, + Parent: machineCmd, + }) + + flags := lsCmd.Flags() + formatFlagName := "format" + flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n", "Format volume output using Go template") + _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) +} + +func list(cmd *cobra.Command, args []string) error { + var opts machine.ListOptions + // We only have qemu VM's for now + listResponse, err := qemu.List(opts) + if err != nil { + return errors.Wrap(err, "error listing vms") + } + + // 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 + }) + machineReporter, err := toHumanFormat(listResponse) + if err != nil { + return err + } + + return outputTemplate(cmd, machineReporter) +} + +func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { + headers := report.Headers(machineReporter{}, map[string]string{ + "LastUp": "LAST UP", + "VmType": "VM TYPE", + }) + + row := report.NormalizeFormat(listFlag.format) + format := parse.EnforceRange(row) + + tmpl, err := template.New("list machines").Parse(format) + if err != nil { + return err + } + w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) + defer w.Flush() + + if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + listFlag.noHeading = true + } + + if !listFlag.noHeading { + if err := tmpl.Execute(w, headers); err != nil { + return errors.Wrapf(err, "failed to write report column headers") + } + } + return tmpl.Execute(w, responses) +} + +func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) { + cfg, err := config.ReadCustomConfig() + if err != nil { + return nil, err + } + + humanResponses := make([]*machineReporter, 0, len(vms)) + for _, vm := range vms { + response := new(machineReporter) + if vm.Name == cfg.Engine.ActiveService { + response.Name = vm.Name + "*" + } else { + response.Name = vm.Name + } + if vm.Running { + response.LastUp = "Currently running" + } else { + response.LastUp = units.HumanDuration(time.Since(vm.LastUp)) + " ago" + } + response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago" + response.VMType = vm.VMType + + humanResponses = append(humanResponses, response) + } + return humanResponses, nil +} diff --git a/docs/source/machine.rst b/docs/source/machine.rst index be9ef1e95..16c042173 100644 --- a/docs/source/machine.rst +++ b/docs/source/machine.rst @@ -3,6 +3,7 @@ Machine :doc:`init <markdown/podman-machine-init.1>` Initialize a new virtual machine +:doc:`list <markdown/podman-machine-list.1>` List virtual machines :doc:`rm <markdown/podman-machine-rm.1>` Remove a virtual machine :doc:`ssh <markdown/podman-machine-ssh.1>` SSH into a virtual machine :doc:`start <markdown/podman-machine-start.1>` Start a virtual machine diff --git a/docs/source/markdown/links/podman-machine-ls.1 b/docs/source/markdown/links/podman-machine-ls.1 new file mode 100644 index 000000000..f6acb7f74 --- /dev/null +++ b/docs/source/markdown/links/podman-machine-ls.1 @@ -0,0 +1 @@ +.so man1/podman-machine-list.1 diff --git a/docs/source/markdown/podman-machine-list.1.md b/docs/source/markdown/podman-machine-list.1.md new file mode 100644 index 000000000..bd5608258 --- /dev/null +++ b/docs/source/markdown/podman-machine-list.1.md @@ -0,0 +1,50 @@ +% podman-machine-ls(1) + +## NAME +podman\-machine\-list - List virtual machines + +## SYNOPSIS +**podman machine list** [*options*] + +**podman machine ls** [*options*] + +## DESCRIPTION + +List Podman managed virtual machines. + +Podman on macOS requires a virtual machine. This is because containers are Linux - +containers do not run on any other OS because containers' core functionality is +tied to the Linux kernel. + +## OPTIONS + +#### **\-\-format**=*format* + +Format list output using a Go template. + +Valid placeholders for the Go template are listed below: + +| **Placeholder** | **Description** | +| --------------- | ------------------------------- | +| .Name | VM name | +| .Created | Time since VM creation | +| .LastUp | Time since the VM was last run | +| .VMType | VM type | + +#### **\-\-help** + +Print usage statement. + +## EXAMPLES + +``` +$ podman machine list + +$ podman machine ls --format {{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n +``` + +## SEE ALSO +podman-machine(1) + +## HISTORY +March 2021, 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 a5d3b78df..693f42fe3 100644 --- a/docs/source/markdown/podman-machine.1.md +++ b/docs/source/markdown/podman-machine.1.md @@ -14,13 +14,14 @@ podman\-machine - Manage Podman's virtual machine | Command | Man Page | Description | | ------- | ------------------------------------------------------- | --------------------------------- | | init | [podman-machine-init(1)](podman-machine-init.1.md) | Initialize a new virtual machine | -| rm | [podman-machine-rm(1)](podman-machine-rm.1.md)| Remove a virtual machine | -| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | -| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | -| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | +| list | [podman-machine-list(1)](podman-machine-list.1.md) | List virtual machines | +| rm | [podman-machine-rm(1)](podman-machine-rm.1.md) | Remove a virtual machine | +| ssh | [podman-machine-ssh(1)](podman-machine-ssh.1.md) | SSH into a virtual machine | +| start | [podman-machine-start(1)](podman-machine-start.1.md) | Start a virtual machine | +| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine | ## SEE ALSO -podman(1) +podman(1), podman-machine-init(1), podman-machine-list(1), podman-machine-rm(1), podman-machine-ssh(1), podman-machine-start(1), podman-machine-stop(1) ## HISTORY March 2021, Originally compiled by Ashley Cui <acui@redhat.com> diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 273deca00..554ea7c97 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -5,6 +5,7 @@ import ( "net/url" "os" "path/filepath" + "time" "github.com/containers/storage/pkg/homedir" "github.com/pkg/errors" @@ -44,6 +45,16 @@ type Download struct { VMName string } +type ListOptions struct{} + +type ListResponse struct { + Name string + CreatedAt time.Time + LastUp time.Time + Running bool + VMType string +} + type SSHOptions struct { Execute bool Args []string diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index fdb528a86..b4427f160 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "strconv" + "strings" "time" "github.com/containers/podman/v3/pkg/machine" @@ -426,3 +427,52 @@ func getDiskSize(path string) (uint64, error) { } return tmpInfo.VirtualSize, nil } + +// List lists all vm's that use qemu virtualization +func List(opts machine.ListOptions) ([]*machine.ListResponse, error) { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return nil, err + } + + var listed []*machine.ListResponse + + if err = filepath.Walk(vmConfigDir, func(path string, info os.FileInfo, err error) error { + vm := new(MachineVM) + if strings.HasSuffix(info.Name(), ".json") { + fullPath := filepath.Join(vmConfigDir, info.Name()) + b, err := ioutil.ReadFile(fullPath) + if err != nil { + return err + } + err = json.Unmarshal(b, vm) + if err != nil { + return err + } + listEntry := new(machine.ListResponse) + + listEntry.Name = vm.Name + listEntry.VMType = "qemu" + fi, err := os.Stat(fullPath) + if err != nil { + return err + } + listEntry.CreatedAt = fi.ModTime() + + fi, err = os.Stat(vm.ImagePath) + if err != nil { + return err + } + listEntry.LastUp = fi.ModTime() + if vm.isRunning() { + listEntry.Running = true + } + + listed = append(listed, listEntry) + } + return nil + }); err != nil { + return nil, err + } + return listed, err +} |