aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/machine/list.go143
-rw-r--r--docs/source/machine.rst1
-rw-r--r--docs/source/markdown/links/podman-machine-ls.11
-rw-r--r--docs/source/markdown/podman-machine-list.1.md50
-rw-r--r--docs/source/markdown/podman-machine.1.md11
-rw-r--r--pkg/machine/config.go11
-rw-r--r--pkg/machine/qemu/machine.go50
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 652a077cc..58486fc73 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
+}