[lxc-devel] [lxd/master] Issue #490 implement codegangsta/cli for LXC.
pcdummy on Github
lxc-bot at linuxcontainers.org
Sun Feb 28 01:12:54 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 568 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160228/94796174/attachment.bin>
-------------- next part --------------
From 1ce7684ec000cab10968073136f3aabb60013779 Mon Sep 17 00:00:00 2001
From: Rene Jochum <rene at jochums.at>
Date: Thu, 25 Feb 2016 00:32:01 +0100
Subject: [PATCH] Issue #490 implement codegangsta/cli for LXC.
Signed-off-by: Rene Jochum <rene at jochums.at>
---
lxc/action.go | 81 +++-
lxc/config.go | 762 +++++++++++++++++------------
lxc/copy.go | 49 +-
lxc/delete.go | 47 +-
lxc/exec.go | 57 ++-
lxc/exec_windows.go | 6 +-
lxc/file.go | 116 +++--
lxc/finger.go | 25 +-
lxc/help.go | 87 ----
lxc/image.go | 908 ++++++++++++++++++++---------------
lxc/info.go | 43 +-
lxc/init.go | 161 +++----
lxc/launch.go | 79 +--
lxc/list.go | 111 +++--
lxc/main.go | 326 +++++++------
lxc/monitor.go | 46 +-
lxc/move.go | 36 +-
lxc/profile.go | 570 +++++++++++++++-------
lxc/publish.go | 57 ++-
lxc/remote.go | 457 ++++++++++--------
lxc/restore.go | 45 +-
lxc/snapshot.go | 51 +-
lxc/version.go | 33 --
po/lxd.pot | 1276 +++++++++++++++++++++++++++++++------------------
test/suites/basic.sh | 6 +-
test/suites/remote.sh | 4 +-
26 files changed, 3196 insertions(+), 2243 deletions(-)
delete mode 100644 lxc/help.go
delete mode 100644 lxc/version.go
diff --git a/lxc/action.go b/lxc/action.go
index f358da4..b091bbb 100644
--- a/lxc/action.go
+++ b/lxc/action.go
@@ -3,40 +3,63 @@ package main
import (
"fmt"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type actionCmd struct {
- action shared.ContainerAction
- hasTimeout bool
- visible bool
- name string
- timeout int
- force bool
-}
+var commandStart = cli.Command{
+ Name: "start",
+ Usage: i18n.G("Changes one or more containers state to start."),
+ ArgsUsage: i18n.G("<name> [<name>...]"),
-func (c *actionCmd) showByDefault() bool {
- return c.visible
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionAction),
}
-func (c *actionCmd) usage() string {
- return fmt.Sprintf(i18n.G(
- `Changes state of one or more containers to %s.
+var commandStop = cli.Command{
+ Name: "stop",
+ Usage: i18n.G("Changes one or more containers state to stop."),
+ ArgsUsage: i18n.G("<name> [<name>...]"),
-lxc %s <name> [<name>...]`), c.name, c.name)
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "force",
+ Usage: i18n.G("Force the container to shutdown."),
+ },
+ cli.IntFlag{
+ Name: "timeout",
+ Usage: i18n.G("Time to wait for the container before killing it."),
+ },
+ ),
+ Action: commandWrapper(commandActionAction),
}
-func (c *actionCmd) flags() {
- if c.hasTimeout {
- gnuflag.IntVar(&c.timeout, "timeout", -1, i18n.G("Time to wait for the container before killing it."))
- gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the container to shutdown."))
- }
+var commandRestart = cli.Command{
+ Name: "restart",
+ Usage: i18n.G("Changes one or more containers state to restart."),
+ ArgsUsage: i18n.G("<name> [<name>...]"),
+
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "force",
+ Usage: i18n.G("Force the container to shutdown."),
+ },
+ cli.IntFlag{
+ Name: "timeout",
+ Usage: i18n.G("Time to wait for the container before killing it."),
+ },
+ ),
+ Action: commandWrapper(commandActionAction),
}
-func (c *actionCmd) run(config *lxd.Config, args []string) error {
+func commandActionAction(config *lxd.Config, c *cli.Context) error {
+ var actionName = c.Command.Name
+ var timeout = c.Int("timeout")
+ var force = c.Bool("force")
+ var args = c.Args()
+
if len(args) == 0 {
return errArgs
}
@@ -48,7 +71,21 @@ func (c *actionCmd) run(config *lxd.Config, args []string) error {
return err
}
- resp, err := d.Action(name, c.action, c.timeout, c.force)
+ var action shared.ContainerAction
+ switch actionName {
+ case "stop":
+ action = shared.Stop
+ case "start":
+ action = shared.Start
+ case "restart":
+ action = shared.Restart
+ case "freeze":
+ action = shared.Freeze
+ case "unfreeze":
+ action = shared.Unfreeze
+ }
+
+ resp, err := d.Action(name, action, timeout, force)
if err != nil {
return err
}
diff --git a/lxc/config.go b/lxc/config.go
index 5f02ee1..f9c3196 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -10,29 +10,215 @@ import (
"strings"
"syscall"
+ "github.com/codegangsta/cli"
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v2"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
+var commandConfigDevice = cli.Command{
+ Name: "device",
+ Usage: i18n.G("Device manipulation."),
+ Description: i18n.G(`Device manipulation
+
+ lxc config device add <[remote:]container> <name> <type> [key=value]...
+ Add a device to a container
+ lxc config device list [remote:]<container> List devices for container
+ lxc config device show [remote:]<container> Show full device details for container
+ lxc config device remove [remote:]<container> <name> Remove device from container
+`),
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "add",
+ ArgsUsage: i18n.G("<[remote:]container> <name> <type> [key=value]..."),
+ Usage: i18n.G("Add a device to a container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionDeviceAdd),
+ },
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[remote:]<container>"),
+ Usage: i18n.G("List devices for container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionDeviceList),
+ },
+
+ cli.Command{
+ Name: "show",
+ ArgsUsage: i18n.G("[remote:]<container>"),
+ Usage: i18n.G("Show full device details for container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionDeviceShow),
+ },
+
+ cli.Command{
+ Name: "remove",
+ ArgsUsage: i18n.G("[remote:]<container> <name>"),
+ Usage: i18n.G("Remove device from container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionDeviceRemove),
+ },
+ },
+}
+
+var commandConfigTrust = cli.Command{
+ Name: "trust",
+ Usage: i18n.G("Trust manipulation."),
+ Description: i18n.G(`Trust manipulation
+
+ lxc config trust list [remote] List all trusted certs.
+ lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.
+ lxc config trust remove [remote] [hostname|fingerprint]
+ Remove the cert from trusted hosts.
+`),
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[remote]"),
+ Usage: i18n.G("List all trusted certs."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionTrustList),
+ },
+
+ cli.Command{
+ Name: "add",
+ ArgsUsage: i18n.G("[remote] <certfile.crt>"),
+ Usage: i18n.G("Add certfile.crt to trusted hosts."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionTrustAdd),
+ },
+
+ cli.Command{
+ Name: "remove",
+ ArgsUsage: i18n.G("[remote] [hostname|fingerprint]"),
+ Usage: i18n.G("Remove the cert from trusted hosts."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionTrustRemove),
+ },
+ },
+}
+
+var commandConfig = cli.Command{
+ Name: "config",
+ Usage: i18n.G("Manage configuration."),
+ Description: i18n.G(`Manage configuration.
+
+ lxc config device add <[remote:]container> <name> <type> [key=value]... Add a device to a container.
+ lxc config device list [remote:]<container> List devices for container.
+ lxc config device show [remote:]<container> Show full device details for container.
+ lxc config device remove [remote:]<container> <name> Remove device from container.
+
+ lxc config get [remote:]<container> key Get configuration key.
+ lxc config set [remote:]<container> key value Set container configuration key.
+ lxc config unset [remote:]<container> key Unset container configuration key.
+ lxc config set key value Set server configuration key.
+ lxc config unset key Unset server configuration key.
+ lxc config show [--expanded] [remote:]<container> Show container configuration.
+ lxc config edit [remote:][container] Edit container configuration in external editor.
+ Edit configuration, either by launching external editor or reading STDIN.
+ Example: lxc config edit <container> # launch editor
+ cat config.yml | lxc config edit <config> # read from config.yml
+
+ lxc config trust list [remote] List all trusted certs.
+ lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.
+ lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts.
+
+ Examples:
+ To mount host's /share/c1 onto /opt in the container:
+ lxc config device add [remote:]container1 <device-name> disk source=/share/c1 path=opt
+
+ To set an lxc config value:
+ lxc config set [remote:]<container> raw.lxc 'lxc.aa_allow_incomplete = 1'
+
+ To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default):
+ lxc config set core.https_address [::]:8443
+
+ To set the server trust password:
+ lxc config set core.trust_password blah`),
+ Flags: commandGlobalFlags,
+ Subcommands: []cli.Command{
+
+ commandConfigDevice,
+
+ cli.Command{
+ Name: "edit",
+ ArgsUsage: i18n.G("[remote:]<container>"),
+ Usage: i18n.G("Edit container configuration in external editor."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionEdit),
+ },
+
+ cli.Command{
+ Name: "get",
+ ArgsUsage: i18n.G("[[remote:]<container>] key"),
+ Usage: i18n.G("Get configuration key."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionGet),
+ },
+
+ cli.Command{
+ Name: "unset",
+ ArgsUsage: i18n.G("key"),
+ Usage: i18n.G("Unset server configuration key."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionUnset),
+ },
+
+ cli.Command{
+ Name: "set",
+ ArgsUsage: i18n.G("[[remote:]<container>] key value"),
+ Usage: i18n.G("Set server/container configuration key."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(newConfigCmd().runActionSet),
+ },
+
+ cli.Command{
+ Name: "show",
+ ArgsUsage: i18n.G("[--expanded] [remote:]<container>"),
+ Usage: i18n.G("Show container configuration."),
+
+ Flags: append(commandGlobalFlags, cli.BoolFlag{
+ Name: "expanded",
+ Usage: i18n.G("Whether to show the expanded configuration."),
+ }),
+ Action: commandWrapper(newConfigCmd().runActionShow),
+ },
+
+ commandConfigTrust,
+ commandProfile,
+ },
+}
+
+func newConfigCmd() *configCmd {
+ return &configCmd{}
+}
+
type configCmd struct {
httpAddr string
- expanded bool
}
func (c *configCmd) showByDefault() bool {
return true
}
-func (c *configCmd) flags() {
- gnuflag.BoolVar(&c.expanded, "expanded", false, i18n.G("Whether to show the expanded configuration"))
-}
-
func (c *configCmd) configEditHelp() string {
return i18n.G(
`### This is a yaml representation of the configuration.
@@ -54,58 +240,20 @@ func (c *configCmd) configEditHelp() string {
### Note that the name is shown but cannot be changed`)
}
-func (c *configCmd) usage() string {
- return i18n.G(
- `Manage configuration.
-
-lxc config device add <[remote:]container> <name> <type> [key=value]... Add a device to a container.
-lxc config device list [remote:]<container> List devices for container.
-lxc config device show [remote:]<container> Show full device details for container.
-lxc config device remove [remote:]<container> <name> Remove device from container.
-
-lxc config get [remote:]<container> key Get configuration key.
-lxc config set [remote:]<container> key value Set container configuration key.
-lxc config unset [remote:]<container> key Unset container configuration key.
-lxc config set key value Set server configuration key.
-lxc config unset key Unset server configuration key.
-lxc config show [--expanded] [remote:]<container> Show container configuration.
-lxc config edit [remote:][container] Edit container configuration in external editor.
- Edit configuration, either by launching external editor or reading STDIN.
- Example: lxc config edit <container> # launch editor
- cat config.yml | lxc config edit <config> # read from config.yml
-
-lxc config trust list [remote] List all trusted certs.
-lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.
-lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts.
-
-Examples:
-To mount host's /share/c1 onto /opt in the container:
- lxc config device add [remote:]container1 <device-name> disk source=/share/c1 path=opt
-
-To set an lxc config value:
- lxc config set [remote:]<container> raw.lxc 'lxc.aa_allow_incomplete = 1'
-
-To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default):
- lxc config set core.https_address [::]:8443
-
-To set the server trust password:
- lxc config set core.trust_password blah`)
-}
-
func (c *configCmd) doSet(config *lxd.Config, args []string, unset bool) error {
- if len(args) != 4 {
+ if len(args) != 3 {
return errArgs
}
// [[lxc config]] set dakara:c1 limits.memory 200000
- remote, container := config.ParseRemoteAndContainer(args[1])
+ remote, container := config.ParseRemoteAndContainer(args[0])
d, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- key := args[2]
- value := args[3]
+ key := args[1]
+ value := args[2]
if !termios.IsTerminal(int(syscall.Stdin)) && value == "-" {
buf, err := ioutil.ReadAll(os.Stdin)
@@ -130,319 +278,317 @@ func (c *configCmd) doSet(config *lxd.Config, args []string, unset bool) error {
return d.SetContainerConfig(container, key, value)
}
-func (c *configCmd) run(config *lxd.Config, args []string) error {
+func (c *configCmd) runActionUnset(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
if len(args) < 1 {
return errArgs
}
- switch args[0] {
-
- case "unset":
- if len(args) < 2 {
- return errArgs
+ // Deal with local server
+ if len(args) == 1 {
+ c, err := lxd.NewClient(config, config.DefaultRemote)
+ if err != nil {
+ return err
}
- // Deal with local server
- if len(args) == 2 {
- c, err := lxd.NewClient(config, config.DefaultRemote)
- if err != nil {
- return err
- }
-
- ss, err := c.ServerStatus()
- if err != nil {
- return err
- }
-
- _, ok := ss.Config[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[1])
- }
-
- _, err = c.SetServerConfig(args[1], "")
+ ss, err := c.ServerStatus()
+ if err != nil {
return err
}
- // Deal with remote server
- remote, container := config.ParseRemoteAndContainer(args[1])
- if container == "" {
- c, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
-
- ss, err := c.ServerStatus()
- if err != nil {
- return err
- }
+ _, ok := ss.Config[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[0])
+ }
- _, ok := ss.Config[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[1])
- }
+ _, err = c.SetServerConfig(args[0], "")
+ return err
+ }
- _, err = c.SetServerConfig(args[2], "")
+ // Deal with remote server
+ remote, container := config.ParseRemoteAndContainer(args[0])
+ if container == "" {
+ c, err := lxd.NewClient(config, remote)
+ if err != nil {
return err
}
- // Deal with container
- args = append(args, "")
- return c.doSet(config, args, true)
+ ss, err := c.ServerStatus()
+ if err != nil {
+ return err
+ }
- case "set":
- if len(args) < 3 {
- return errArgs
+ _, ok := ss.Config[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[0])
}
- // Deal with local server
- if len(args) == 3 {
- c, err := lxd.NewClient(config, config.DefaultRemote)
- if err != nil {
- return err
- }
+ _, err = c.SetServerConfig(args[1], "")
+ return err
+ }
- _, err = c.SetServerConfig(args[1], args[2])
- return err
- }
+ // Deal with container
+ args = append(args, "")
+ return c.doSet(config, args, true)
+}
- // Deal with remote server
- remote, container := config.ParseRemoteAndContainer(args[1])
- if container == "" {
- c, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+func (c *configCmd) runActionSet(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- _, err = c.SetServerConfig(args[2], args[3])
+ if len(args) < 2 {
+ return errArgs
+ }
+
+ // Deal with local server
+ if len(args) == 2 {
+ c, err := lxd.NewClient(config, config.DefaultRemote)
+ if err != nil {
return err
}
- // Deal with container
- return c.doSet(config, args, false)
+ _, err = c.SetServerConfig(args[0], args[1])
+ return err
+ }
- case "trust":
- if len(args) < 2 {
- return errArgs
+ // Deal with remote server
+ remote, container := config.ParseRemoteAndContainer(args[0])
+ if container == "" {
+ c, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
}
- switch args[1] {
- case "list":
- var remote string
- if len(args) == 3 {
- remote = config.ParseRemote(args[2])
- } else {
- remote = config.DefaultRemote
- }
+ _, err = c.SetServerConfig(args[1], args[2])
+ return err
+ }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ // Deal with container
+ return c.doSet(config, args, false)
+}
- trust, err := d.CertificateList()
- if err != nil {
- return err
- }
+func (c *configCmd) runActionGet(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- data := [][]string{}
- for _, cert := range trust {
- fp := cert.Fingerprint[0:12]
+ if len(args) > 2 || len(args) < 1 {
+ return errArgs
+ }
- certBlock, _ := pem.Decode([]byte(cert.Certificate))
- cert, err := x509.ParseCertificate(certBlock.Bytes)
- if err != nil {
- return err
- }
+ remote := config.DefaultRemote
+ container := ""
+ key := args[0]
+ if len(args) > 1 {
+ remote, container = config.ParseRemoteAndContainer(args[0])
+ key = args[1]
+ }
- const layout = "Jan 2, 2006 at 3:04pm (MST)"
- issue := cert.NotBefore.Format(layout)
- expiry := cert.NotAfter.Format(layout)
- data = append(data, []string{fp, cert.Subject.CommonName, issue, expiry})
- }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
- table := tablewriter.NewWriter(os.Stdout)
- table.SetAutoWrapText(false)
- table.SetRowLine(true)
- table.SetHeader([]string{
- i18n.G("FINGERPRINT"),
- i18n.G("COMMON NAME"),
- i18n.G("ISSUE DATE"),
- i18n.G("EXPIRY DATE")})
- sort.Sort(SortImage(data))
- table.AppendBulk(data)
- table.Render()
-
- return nil
- case "add":
- var remote string
- if len(args) < 3 {
- return fmt.Errorf(i18n.G("No certificate provided to add"))
- } else if len(args) == 4 {
- remote = config.ParseRemote(args[2])
- } else {
- remote = config.DefaultRemote
- }
+ if container != "" {
+ resp, err := d.ContainerInfo(container)
+ if err != nil {
+ return err
+ }
+ fmt.Printf("%s: %s\n", key, resp.Config[key])
+ } else {
+ resp, err := d.ServerStatus()
+ if err != nil {
+ return err
+ }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ value := resp.Config[key]
+ if value == nil {
+ value = ""
+ } else if value == true {
+ value = "true"
+ } else if value == false {
+ value = "false"
+ }
- fname := args[len(args)-1]
- cert, err := shared.ReadCert(fname)
- if err != nil {
- return err
- }
+ fmt.Printf("%s: %s\n", key, value)
+ }
+ return nil
+}
- name, _ := shared.SplitExt(fname)
- return d.CertificateAdd(cert, name)
- case "remove":
- var remote string
- if len(args) < 3 {
- return fmt.Errorf(i18n.G("No fingerprint specified."))
- } else if len(args) == 4 {
- remote = config.ParseRemote(args[2])
- } else {
- remote = config.DefaultRemote
- }
+func (c *configCmd) runActionShow(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ remote := config.DefaultRemote
+ container := ""
+ if len(args) > 0 {
+ remote, container = config.ParseRemoteAndContainer(args[0])
+ }
- return d.CertificateRemove(args[len(args)-1])
- default:
- return errArgs
- }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ var data []byte
- case "show":
- remote := config.DefaultRemote
- container := ""
- if len(args) > 1 {
- remote, container = config.ParseRemoteAndContainer(args[1])
+ if len(args) == 0 || container == "" {
+ config, err := d.ServerStatus()
+ if err != nil {
+ return err
}
- d, err := lxd.NewClient(config, remote)
+ brief := config.Brief()
+ data, err = yaml.Marshal(&brief)
+ } else {
+ config, err := d.ContainerInfo(container)
if err != nil {
return err
}
- var data []byte
+ brief := config.Brief()
+ if context.Bool("expanded") {
+ brief = config.BriefExpanded()
+ }
+ data, err = yaml.Marshal(&brief)
+ }
- if len(args) == 1 || container == "" {
- config, err := d.ServerStatus()
- if err != nil {
- return err
- }
+ fmt.Printf("%s", data)
- brief := config.Brief()
- data, err = yaml.Marshal(&brief)
- } else {
- config, err := d.ContainerInfo(container)
- if err != nil {
- return err
- }
+ return nil
+}
- brief := config.Brief()
- if c.expanded {
- brief = config.BriefExpanded()
- }
- data, err = yaml.Marshal(&brief)
- }
+func (c *configCmd) runActionEdit(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- fmt.Printf("%s", data)
+ if len(args) == 0 {
+ return errArgs
+ }
- return nil
+ remote := config.DefaultRemote
+ container := ""
+ if len(args) > 1 {
+ remote, container = config.ParseRemoteAndContainer(args[0])
+ }
- case "get":
- if len(args) > 3 || len(args) < 2 {
- return errArgs
- }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
- remote := config.DefaultRemote
- container := ""
- key := args[1]
- if len(args) > 2 {
- remote, container = config.ParseRemoteAndContainer(args[1])
- key = args[2]
- }
+ if len(args) == 0 || container == "" {
+ return c.doDaemonConfigEdit(d)
+ }
+
+ return c.doContainerConfigEdit(d, container)
+}
- d, err := lxd.NewClient(config, remote)
+func (c *configCmd) runActionTrustList(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var remote string
+ if len(args) == 1 {
+ remote = config.ParseRemote(args[0])
+ } else {
+ remote = config.DefaultRemote
+ }
+
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ trust, err := d.CertificateList()
+ if err != nil {
+ return err
+ }
+
+ data := [][]string{}
+ for _, cert := range trust {
+ fp := cert.Fingerprint[0:12]
+
+ certBlock, _ := pem.Decode([]byte(cert.Certificate))
+ cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return err
}
- if container != "" {
- resp, err := d.ContainerInfo(container)
- if err != nil {
- return err
- }
- fmt.Printf("%s: %s\n", key, resp.Config[key])
- } else {
- resp, err := d.ServerStatus()
- if err != nil {
- return err
- }
-
- value := resp.Config[key]
- if value == nil {
- value = ""
- } else if value == true {
- value = "true"
- } else if value == false {
- value = "false"
- }
+ const layout = "Jan 2, 2006 at 3:04pm (MST)"
+ issue := cert.NotBefore.Format(layout)
+ expiry := cert.NotAfter.Format(layout)
+ data = append(data, []string{fp, cert.Subject.CommonName, issue, expiry})
+ }
- fmt.Printf("%s: %s\n", key, value)
- }
- return nil
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetAutoWrapText(false)
+ table.SetRowLine(true)
+ table.SetHeader([]string{
+ i18n.G("FINGERPRINT"),
+ i18n.G("COMMON NAME"),
+ i18n.G("ISSUE DATE"),
+ i18n.G("EXPIRY DATE")})
+ sort.Sort(sortImage(data))
+ table.AppendBulk(data)
+ table.Render()
- case "profile":
- case "device":
- if len(args) < 2 {
- return errArgs
- }
- switch args[1] {
- case "list":
- return c.deviceList(config, "container", args)
- case "add":
- return c.deviceAdd(config, "container", args)
- case "remove":
- return c.deviceRm(config, "container", args)
- case "show":
- return c.deviceShow(config, "container", args)
- default:
- return errArgs
- }
+ return nil
+}
- case "edit":
- if len(args) < 1 {
- return errArgs
- }
+func (c *configCmd) runActionTrustAdd(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var remote string
+ if len(args) < 1 {
+ return fmt.Errorf(i18n.G("No certificate provided to add"))
+ } else if len(args) == 2 {
+ remote = config.ParseRemote(args[0])
+ } else {
+ remote = config.DefaultRemote
+ }
- remote := config.DefaultRemote
- container := ""
- if len(args) > 1 {
- remote, container = config.ParseRemoteAndContainer(args[1])
- }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ fname := args[len(args)-1]
+ cert, err := shared.ReadCert(fname)
+ if err != nil {
+ return err
+ }
- if len(args) == 1 || container == "" {
- return c.doDaemonConfigEdit(d)
- }
+ name, _ := shared.SplitExt(fname)
+ return d.CertificateAdd(cert, name)
+}
- return c.doContainerConfigEdit(d, container)
+func (c *configCmd) runActionTrustRemove(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var remote string
+ if len(args) < 1 {
+ return fmt.Errorf(i18n.G("No fingerprint specified."))
+ } else if len(args) == 2 {
+ remote = config.ParseRemote(args[0])
+ } else {
+ remote = config.DefaultRemote
+ }
- default:
- return errArgs
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
}
- return errArgs
+ return d.CertificateRemove(args[len(args)-1])
+}
+
+func (c *configCmd) runActionDeviceAdd(config *lxd.Config, context *cli.Context) error {
+ return c.deviceAdd(config, "container", context.Args())
+}
+
+func (c *configCmd) runActionDeviceList(config *lxd.Config, context *cli.Context) error {
+ return c.deviceList(config, "container", context.Args())
+}
+
+func (c *configCmd) runActionDeviceShow(config *lxd.Config, context *cli.Context) error {
+ return c.deviceShow(config, "container", context.Args())
+}
+
+func (c *configCmd) runActionDeviceRemove(config *lxd.Config, context *cli.Context) error {
+ return c.deviceRm(config, "container", context.Args())
}
func (c *configCmd) doContainerConfigEdit(client *lxd.Client, cont string) error {
@@ -574,21 +720,21 @@ func (c *configCmd) doDaemonConfigEdit(client *lxd.Client) error {
}
func (c *configCmd) deviceAdd(config *lxd.Config, which string, args []string) error {
- if len(args) < 5 {
+ if len(args) < 3 {
return errArgs
}
- remote, name := config.ParseRemoteAndContainer(args[2])
+ remote, name := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- devname := args[3]
- devtype := args[4]
+ devname := args[1]
+ devtype := args[2]
var props []string
- if len(args) > 5 {
- props = args[5:]
+ if len(args) > 3 {
+ props = args[3:]
} else {
props = []string{}
}
@@ -610,17 +756,17 @@ func (c *configCmd) deviceAdd(config *lxd.Config, which string, args []string) e
}
func (c *configCmd) deviceRm(config *lxd.Config, which string, args []string) error {
- if len(args) < 4 {
+ if len(args) < 2 {
return errArgs
}
- remote, name := config.ParseRemoteAndContainer(args[2])
+ remote, name := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- devname := args[3]
+ devname := args[1]
var resp *lxd.Response
if which == "profile" {
resp, err = client.ProfileDeviceDelete(name, devname)
@@ -638,10 +784,10 @@ func (c *configCmd) deviceRm(config *lxd.Config, which string, args []string) er
}
func (c *configCmd) deviceList(config *lxd.Config, which string, args []string) error {
- if len(args) < 3 {
+ if len(args) < 1 {
return errArgs
}
- remote, name := config.ParseRemoteAndContainer(args[2])
+ remote, name := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
@@ -663,10 +809,10 @@ func (c *configCmd) deviceList(config *lxd.Config, which string, args []string)
}
func (c *configCmd) deviceShow(config *lxd.Config, which string, args []string) error {
- if len(args) < 3 {
+ if len(args) < 1 {
return errArgs
}
- remote, name := config.ParseRemoteAndContainer(args[2])
+ remote, name := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
diff --git a/lxc/copy.go b/lxc/copy.go
index a451919..ee73cd9 100644
--- a/lxc/copy.go
+++ b/lxc/copy.go
@@ -4,33 +4,39 @@ import (
"fmt"
"strings"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type copyCmd struct {
- ephem bool
-}
+var commandCopy = cli.Command{
+ Name: "copy",
+ Usage: i18n.G("Copy containers within or in between lxd instances."),
+ ArgsUsage: i18n.G("[remote:]<source container> [remote:]<destination container>"),
-func (c *copyCmd) showByDefault() bool {
- return true
+ Flags: append(commandGlobalFlags, cli.BoolFlag{
+ Name: "ephemeral, e",
+ Usage: i18n.G("Create a ephemeral copy."),
+ }),
+ Action: commandWrapper(commandActionCopy),
}
-func (c *copyCmd) usage() string {
- return i18n.G(
- `Copy containers within or in between lxd instances.
+func commandActionCopy(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 2 {
+ return errArgs
+ }
-lxc copy [remote:]<source container> [remote:]<destination container> [--ephemeral|e]`)
-}
+ ephemeral := 0
+ if context.Bool("ephemeral") {
+ ephemeral = 1
+ }
-func (c *copyCmd) flags() {
- gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
- gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
+ return copyContainer(config, args[0], args[1], false, ephemeral)
}
-func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
+func copyContainer(config *lxd.Config, sourceResource string, destResource string, keepVolatile bool, ephemeral int) error {
sourceRemote, sourceName := config.ParseRemoteAndContainer(sourceResource)
destRemote, destName := config.ParseRemoteAndContainer(destResource)
@@ -161,16 +167,3 @@ func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destR
return err
}
-
-func (c *copyCmd) run(config *lxd.Config, args []string) error {
- if len(args) != 2 {
- return errArgs
- }
-
- ephem := 0
- if c.ephem {
- ephem = 1
- }
-
- return c.copyContainer(config, args[0], args[1], false, ephem)
-}
diff --git a/lxc/delete.go b/lxc/delete.go
index 716832b..65e6d7d 100644
--- a/lxc/delete.go
+++ b/lxc/delete.go
@@ -6,35 +6,42 @@ import (
"os"
"strings"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type deleteCmd struct {
- force bool
- interactive bool
-}
-
-func (c *deleteCmd) showByDefault() bool {
- return true
+var commandDelete = cli.Command{
+ Name: "delete",
+ Usage: i18n.G("Delete containers or container snapshots."),
+ ArgsUsage: i18n.G("[remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...] [--force|-f] [--interactive|-i]"),
+ Description: i18n.G("Destroy containers or snapshots with any attached data (configuration, snapshots, ...)."),
+
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "force, f",
+ Usage: i18n.G("Force the removal of stopped containers."),
+ },
+
+ cli.BoolFlag{
+ Name: "interactive, i",
+ Usage: i18n.G("Require user confirmation."),
+ },
+ ),
+ Action: commandWrapper(commandActionDelete),
}
-func (c *deleteCmd) usage() string {
- return i18n.G(
- `Delete containers or container snapshots.
-
-lxc delete [remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...]
-
-Destroy containers or snapshots with any attached data (configuration, snapshots, ...).`)
+func commandActionDelete(config *lxd.Config, context *cli.Context) error {
+ var cmd = &deleteCmd{}
+ cmd.force = context.Bool("force")
+ cmd.interactive = context.Bool("interactive")
+ return cmd.run(config, context.Args())
}
-func (c *deleteCmd) flags() {
- gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the removal of stopped containers."))
- gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the removal of stopped containers."))
- gnuflag.BoolVar(&c.interactive, "i", false, i18n.G("Require user confirmation."))
- gnuflag.BoolVar(&c.interactive, "interactive", false, i18n.G("Require user confirmation."))
+type deleteCmd struct {
+ force bool
+ interactive bool
}
func (c *deleteCmd) promptDelete(name string) error {
diff --git a/lxc/exec.go b/lxc/exec.go
index d218fd5..4dd4df0 100644
--- a/lxc/exec.go
+++ b/lxc/exec.go
@@ -8,15 +8,45 @@ import (
"strings"
"syscall"
+ "github.com/codegangsta/cli"
"github.com/gorilla/websocket"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
+var commandExec = cli.Command{
+ Name: "exec",
+ Usage: i18n.G("Execute the specified command in a container."),
+ ArgsUsage: i18n.G("[remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>"),
+ Description: i18n.G(`Execute the specified command in a container.
+
+ Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`),
+
+ Flags: append(commandGlobalFlags,
+ cli.StringSliceFlag{
+ Name: "env",
+ Usage: i18n.G("An environment variable of the form HOME=/home/foo."),
+ },
+ cli.StringFlag{
+ Name: "mode",
+ Value: "auto",
+ Usage: i18n.G("Override the terminal mode (auto, interactive or non-interactive)"),
+ },
+ ),
+ Action: commandWrapper(commandActionExec),
+}
+
+func commandActionExec(config *lxd.Config, context *cli.Context) error {
+ var cmd = &execCmd{}
+ cmd.envArgs = context.StringSlice("env")
+ cmd.modeFlag = context.String("mode")
+
+ return cmd.run(config, context.Args())
+}
+
type envFlag []string
func (f *envFlag) String() string {
@@ -37,24 +67,6 @@ type execCmd struct {
envArgs envFlag
}
-func (c *execCmd) showByDefault() bool {
- return true
-}
-
-func (c *execCmd) usage() string {
- return i18n.G(
- `Execute the specified command in a container.
-
-lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>
-
-Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).`)
-}
-
-func (c *execCmd) flags() {
- gnuflag.Var(&c.envArgs, "env", i18n.G("An environment variable of the form HOME=/home/foo"))
- gnuflag.StringVar(&c.modeFlag, "mode", "auto", i18n.G("Override the terminal mode (auto, interactive or non-interactive)"))
-}
-
func (c *execCmd) sendTermSize(control *websocket.Conn) error {
width, height, err := termios.GetSize(int(syscall.Stdout))
if err != nil {
@@ -138,7 +150,12 @@ func (c *execCmd) run(config *lxd.Config, args []string) error {
}
stdout := c.getStdout()
- ret, err := d.Exec(name, args[1:], env, os.Stdin, stdout, os.Stderr, handler)
+ if args[1] == "--" {
+ args = args[2:]
+ } else {
+ args = args[1:]
+ }
+ ret, err := d.Exec(name, args, env, os.Stdin, stdout, os.Stderr, handler)
if err != nil {
return err
}
diff --git a/lxc/exec_windows.go b/lxc/exec_windows.go
index 5b51c78..dfc2d76 100644
--- a/lxc/exec_windows.go
+++ b/lxc/exec_windows.go
@@ -15,17 +15,17 @@ import (
// Windows doesn't process ANSI sequences natively, so we wrap
// os.Stdout for improved user experience for Windows client
-type WrappedWriteCloser struct {
+type wrappedWriteCloser struct {
io.Closer
wrapper io.Writer
}
-func (wwc *WrappedWriteCloser) Write(p []byte) (int, error) {
+func (wwc *wrappedWriteCloser) Write(p []byte) (int, error) {
return wwc.wrapper.Write(p)
}
func (c *execCmd) getStdout() io.WriteCloser {
- return &WrappedWriteCloser{os.Stdout, colorable.NewColorableStdout()}
+ return &wrappedWriteCloser{os.Stdout, colorable.NewColorableStdout()}
}
func (c *execCmd) controlSocketHandler(d *lxd.Client, control *websocket.Conn) {
diff --git a/lxc/file.go b/lxc/file.go
index f16844d..56b9a97 100644
--- a/lxc/file.go
+++ b/lxc/file.go
@@ -11,38 +11,93 @@ import (
"strings"
"syscall"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
-type fileCmd struct {
- uid int
- gid int
- mode string
+var commandFile = cli.Command{
+ Name: "file",
+ Usage: i18n.G("Manage files on a container."),
+ Description: i18n.G(`Manage files on a container.
+
+ lxc file pull <source> [<source>...] <target>
+ lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>
+ lxc file edit <file>
+
+ <source> in the case of pull and <target> in the case of push are <container name>/<path>`),
+ Flags: commandGlobalFlags,
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "pull",
+ ArgsUsage: i18n.G("<source> [<source>...] <target>"),
+ Usage: i18n.G("Get a file from a container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionFilePull),
+ },
+
+ cli.Command{
+ Name: "push",
+ ArgsUsage: i18n.G("[--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>"),
+ Usage: i18n.G("Push a file to a container."),
+
+ Flags: append(commandGlobalFlags,
+ cli.IntFlag{
+ Name: "gid",
+ Value: -1,
+ Usage: i18n.G("Set the file's gid on push."),
+ },
+ cli.IntFlag{
+ Name: "uid",
+ Value: -1,
+ Usage: i18n.G("Set the file's uid on push."),
+ },
+ cli.StringFlag{
+ Name: "mode",
+ Value: "",
+ Usage: i18n.G("Set the file's perms on push."),
+ },
+ ),
+ Action: commandWrapper(commandActionFilePush),
+ },
+
+ cli.Command{
+ Name: "edit",
+ ArgsUsage: i18n.G("<file>"),
+ Usage: i18n.G("Edit a file from a container."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionFileEdit),
+ },
+ },
}
-func (c *fileCmd) showByDefault() bool {
- return true
+func commandActionFilePull(config *lxd.Config, context *cli.Context) error {
+ var cmd = &fileCmd{}
+ return cmd.pull(config, context.Args())
}
-func (c *fileCmd) usage() string {
- return i18n.G(
- `Manage files on a container.
-
-lxc file pull <source> [<source>...] <target>
-lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>
-lxc file edit <file>
+func commandActionFilePush(config *lxd.Config, context *cli.Context) error {
+ var cmd = &fileCmd{}
+ cmd.uid = context.Int("uid")
+ cmd.gid = context.Int("gid")
+ cmd.mode = context.String("mode")
+ return cmd.push(config, context.Args())
+}
-<source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path>`)
+func commandActionFileEdit(config *lxd.Config, context *cli.Context) error {
+ var cmd = &fileCmd{}
+ return cmd.edit(config, context.Args())
}
-func (c *fileCmd) flags() {
- gnuflag.IntVar(&c.uid, "uid", -1, i18n.G("Set the file's uid on push"))
- gnuflag.IntVar(&c.gid, "gid", -1, i18n.G("Set the file's gid on push"))
- gnuflag.StringVar(&c.mode, "mode", "", i18n.G("Set the file's perms on push"))
+type fileCmd struct {
+ uid int
+ gid int
+ mode string
}
func (c *fileCmd) push(config *lxd.Config, args []string) error {
@@ -122,7 +177,7 @@ func (c *fileCmd) push(config *lxd.Config, args []string) error {
}
if c.mode == "" || c.uid == -1 || c.gid == -1 {
- fMode, fUid, fGid, err := c.getOwner(f)
+ fMode, fUID, fGID, err := c.getOwner(f)
if err != nil {
return err
}
@@ -132,11 +187,11 @@ func (c *fileCmd) push(config *lxd.Config, args []string) error {
}
if c.uid == -1 {
- uid = fUid
+ uid = fUID
}
if c.gid == -1 {
- gid = fGid
+ gid = fGID
}
}
@@ -259,20 +314,3 @@ func (c *fileCmd) edit(config *lxd.Config, args []string) error {
return nil
}
-
-func (c *fileCmd) run(config *lxd.Config, args []string) error {
- if len(args) < 1 {
- return errArgs
- }
-
- switch args[0] {
- case "push":
- return c.push(config, args[1:])
- case "pull":
- return c.pull(config, args[1:])
- case "edit":
- return c.edit(config, args[1:])
- default:
- return errArgs
- }
-}
diff --git a/lxc/finger.go b/lxc/finger.go
index 3fea385..73d4f27 100644
--- a/lxc/finger.go
+++ b/lxc/finger.go
@@ -1,28 +1,23 @@
package main
import (
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared/i18n"
)
-type fingerCmd struct {
- httpAddr string
-}
+// TODO: Make this a "hidden" command.
+var commandFinger = cli.Command{
+ Name: "finger",
+ Usage: i18n.G("Fingers the LXD instance to check if it is up and working."),
+ ArgsUsage: i18n.G("<remote>"),
-func (c *fingerCmd) showByDefault() bool {
- return false
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionFinger),
}
-func (c *fingerCmd) usage() string {
- return i18n.G(
- `Fingers the LXD instance to check if it is up and working.
-
-lxc finger <remote>`)
-}
-
-func (c *fingerCmd) flags() {}
-
-func (c *fingerCmd) run(config *lxd.Config, args []string) error {
+func commandActionFinger(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
if len(args) > 1 {
return errArgs
}
diff --git a/lxc/help.go b/lxc/help.go
deleted file mode 100644
index 15672a5..0000000
--- a/lxc/help.go
+++ /dev/null
@@ -1,87 +0,0 @@
-package main
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "os"
- "sort"
- "strings"
-
- "github.com/lxc/lxd"
- "github.com/lxc/lxd/shared/gnuflag"
- "github.com/lxc/lxd/shared/i18n"
-)
-
-type helpCmd struct {
- showAll bool
-}
-
-func (c *helpCmd) showByDefault() bool {
- return true
-}
-
-func (c *helpCmd) usage() string {
- return i18n.G(
- `Presents details on how to use LXD.
-
-lxd help [--all]`)
-}
-
-func (c *helpCmd) flags() {
- gnuflag.BoolVar(&c.showAll, "all", false, i18n.G("Show all commands (not just interesting ones)"))
-}
-
-func (c *helpCmd) run(_ *lxd.Config, args []string) error {
- if len(args) > 0 {
- for _, name := range args {
- cmd, ok := commands[name]
- if !ok {
- fmt.Fprintf(os.Stderr, i18n.G("error: unknown command: %s")+"\n", name)
- } else {
- fmt.Fprintf(os.Stderr, cmd.usage()+"\n")
- }
- }
- return nil
- }
-
- fmt.Println(i18n.G("Usage: lxc [subcommand] [options]"))
- fmt.Println(i18n.G("Available commands:"))
- var names []string
- for name := range commands {
- names = append(names, name)
- }
- sort.Strings(names)
- for _, name := range names {
- cmd := commands[name]
- if c.showAll || cmd.showByDefault() {
- fmt.Printf("\t%-10s - %s\n", name, c.summaryLine(cmd.usage()))
- }
- }
- if !c.showAll {
- fmt.Println()
- fmt.Println(i18n.G("Options:"))
- fmt.Println(" --all " + i18n.G("Print less common commands."))
- fmt.Println(" --debug " + i18n.G("Print debug information."))
- fmt.Println(" --verbose " + i18n.G("Print verbose information."))
- fmt.Println()
- fmt.Println(i18n.G("Environment:"))
- fmt.Println(" LXD_CONF " + i18n.G("Path to an alternate client configuration directory."))
- fmt.Println(" LXD_DIR " + i18n.G("Path to an alternate server directory."))
- }
- return nil
-}
-
-// summaryLine returns the first line of the help text. Conventionally, this
-// should be a one-line command summary, potentially followed by a longer
-// explanation.
-func (c *helpCmd) summaryLine(usage string) string {
- usage = strings.TrimSpace(usage)
- s := bufio.NewScanner(bytes.NewBufferString(usage))
- if s.Scan() {
- if len(s.Text()) > 1 {
- return s.Text()
- }
- }
- return i18n.G("Missing summary.")
-}
diff --git a/lxc/image.go b/lxc/image.go
index d875b46..cf76206 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -9,485 +9,629 @@ import (
"strings"
"syscall"
+ "github.com/codegangsta/cli"
"github.com/olekukonko/tablewriter"
"gopkg.in/yaml.v2"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
-type SortImage [][]string
-
-func (a SortImage) Len() int {
- return len(a)
+var commandImage = cli.Command{
+ Name: "image",
+ Usage: i18n.G("Manipulate container images."),
+ Description: i18n.G(`Manipulate container images.
+
+ In LXD containers are created from images. Those images were themselves
+ either generated from an existing container or downloaded from an image
+ server.
+
+ When using remote images, LXD will automatically cache images for you
+ and remove them upon expiration.
+
+ The image unique identifier is the hash (sha-256) of its representation
+ as a compressed tarball (or for split images, the concatenation of the
+ metadata and rootfs tarballs).
+
+ Images can be referenced by their full hash, shortest unique partial
+ hash or alias name (if one is set).
+
+
+ lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--alias=ALIAS]... [prop=value]...
+ Import an image tarball (or tarballs) into the LXD image store.
+
+ lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public]
+ Copy an image from one LXD daemon to another over the network.
+
+ lxc image delete [remote:]<image>
+ Delete an image from the LXD image store.
+
+ lxc image export [remote:]<image>
+ Export an image from the LXD image store into a distributable tarball.
+
+ lxc image info [remote:]<image>
+ Print everything LXD knows about a given image.
+
+ lxc image list [remote:] [filter]
+ List images in the LXD image store. Filters may be of the
+ <key>=<value> form for property based filtering, or part of the image
+ hash or part of the image alias name.
+
+ lxc image show [remote:]<image>
+ Yaml output of the user modifiable properties of an image.
+
+ lxc image edit [remote:]<image>
+ Edit image, either by launching external editor or reading STDIN.
+ Example: lxc image edit <image> # launch editor
+ cat image.yml | lxc image edit <image> # read from image.yml
+
+ lxc image alias create [remote:]<alias> <fingerprint>
+ Create a new alias for an existing image.
+
+ lxc image alias delete [remote:]<alias>
+ Delete an alias.
+
+ lxc image alias list [remote:]
+ List the aliases.`),
+
+ Flags: commandGlobalFlags,
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "import",
+ ArgsUsage: i18n.G("<tarball> [rootfs tarball|URL] [remote:] [--public] [--alias=ALIAS]... [prop=value]..."),
+ Usage: i18n.G("Import an image tarball (or tarballs) into the LXD image store."),
+
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "public",
+ Usage: i18n.G("Make image public."),
+ },
+ cli.StringSliceFlag{
+ Name: "alias",
+ Usage: i18n.G("An alias for this image."),
+ },
+ ),
+ Action: commandWrapper(commandActionImageImport),
+ },
+
+ cli.Command{
+ Name: "copy",
+ ArgsUsage: i18n.G("[remote:]<image> <remote>:"),
+ Usage: i18n.G("Copy an image to another destination."),
+
+ Flags: append(commandGlobalFlags,
+ cli.StringSliceFlag{
+ Name: "alias",
+ Usage: i18n.G("An alias for this image."),
+ },
+ cli.BoolFlag{
+ Name: "copy-aliases",
+ Usage: i18n.G("Also copy aliases."),
+ },
+ cli.BoolFlag{
+ Name: "public",
+ Usage: i18n.G("Make image public."),
+ },
+ ),
+ Action: commandWrapper(commandActionImageCopy),
+ },
+
+ cli.Command{
+ Name: "delete",
+ ArgsUsage: i18n.G("[remote:]<image>"),
+ Usage: i18n.G("Delete an image."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageDelete),
+ },
+
+ cli.Command{
+ Name: "export",
+ ArgsUsage: i18n.G("[remote:]<image>"),
+ Usage: i18n.G("Export an image."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageExport),
+ },
+
+ cli.Command{
+ Name: "info",
+ ArgsUsage: i18n.G("[remote:]<image>"),
+ Usage: i18n.G("Get informations form an image."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageInfo),
+ },
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[resource] [filters] [-c columns] [--fast]"),
+ Usage: i18n.G("List images."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageList),
+ },
+
+ cli.Command{
+ Name: "show",
+ ArgsUsage: i18n.G("[remote:]<image>"),
+ Usage: i18n.G("Show an image."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageShow),
+ },
+
+ cli.Command{
+ Name: "edit",
+ ArgsUsage: i18n.G("[remote:]<image>"),
+ Usage: i18n.G("Edit an image."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageEdit),
+ },
+
+ cli.Command{
+ Name: "alias",
+ Usage: i18n.G("Manipulate aliases."),
+
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "create",
+ ArgsUsage: i18n.G("<alias> <target>"),
+ Usage: i18n.G("Create an alias."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageAliasCreate),
+ },
+
+ cli.Command{
+ Name: "delete",
+ ArgsUsage: i18n.G("<alias>"),
+ Usage: i18n.G("Delete an alias."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageAliasDelete),
+ },
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[remote:]"),
+ Usage: i18n.G("List aliases."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionImageAliasList),
+ },
+ },
+ },
+ },
}
-func (a SortImage) Swap(i, j int) {
- a[i], a[j] = a[j], a[i]
-}
+func commandActionImageImport(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var public = context.Bool("public")
+ var alias = context.StringSlice("alias")
-func (a SortImage) Less(i, j int) bool {
- if a[i][0] == a[j][0] {
- if a[i][3] == "" {
- return false
- }
+ if len(args) < 1 {
+ return errArgs
+ }
- if a[j][3] == "" {
- return true
- }
+ var fingerprint string
+ var imageFile string
+ var rootfsFile string
+ var properties []string
+ var remote string
- return a[i][3] < a[j][3]
+ for _, arg := range args {
+ split := strings.Split(arg, "=")
+ if len(split) == 1 || shared.PathExists(arg) {
+ if strings.HasSuffix(arg, ":") {
+ remote = config.ParseRemote(arg)
+ } else {
+ if imageFile == "" {
+ imageFile = args[0]
+ } else {
+ rootfsFile = arg
+ }
+ }
+ } else {
+ properties = append(properties, arg)
+ }
}
- if a[i][0] == "" {
- return false
+ if remote == "" {
+ remote = config.DefaultRemote
}
- if a[j][0] == "" {
- return true
+ if imageFile == "" {
+ return errArgs
}
- return a[i][0] < a[j][0]
-}
-
-type aliasList []string
-
-func (f *aliasList) String() string {
- return fmt.Sprint(*f)
-}
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
-func (f *aliasList) Set(value string) error {
- if f == nil {
- *f = make(aliasList, 1)
+ if strings.HasPrefix(imageFile, "https://") {
+ fingerprint, err = d.PostImageURL(imageFile, public, alias)
+ } else if strings.HasPrefix(imageFile, "http://") {
+ return fmt.Errorf(i18n.G("Only https:// is supported for remote image import."))
} else {
- *f = append(*f, value)
+ fingerprint, err = d.PostImage(imageFile, rootfsFile, properties, public, alias)
}
- return nil
-}
-type imageCmd struct {
- addAliases aliasList
- publicImage bool
- copyAliases bool
-}
+ if err != nil {
+ return err
+ }
+ fmt.Printf(i18n.G("Image imported with fingerprint: %s")+"\n", fingerprint)
-func (c *imageCmd) showByDefault() bool {
- return true
+ return nil
}
-func (c *imageCmd) imageEditHelp() string {
- return i18n.G(
- `### This is a yaml representation of the image properties.
-### Any line starting with a '# will be ignored.
-###
-### Each property is represented by a single line:
-### An example would be:
-### description: My custom image`)
-}
+func commandActionImageCopy(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var public = context.Bool("public")
+ var alias = context.StringSlice("alias")
+ var copyAliases = context.Bool("copy-aliases")
-func (c *imageCmd) usage() string {
- return i18n.G(
- `Manipulate container images.
+ /* [<remote>:]<image> [<remote>:]<image> */
+ if len(args) != 2 {
+ return errArgs
+ }
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
+ destRemote, outName := config.ParseRemoteAndContainer(args[1])
+ if outName != "" {
+ return errArgs
+ }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+ dest, err := lxd.NewClient(config, destRemote)
+ if err != nil {
+ return err
+ }
-In LXD containers are created from images. Those images were themselves
-either generated from an existing container or downloaded from an image
-server.
+ progressHandler := func(progress string) {
+ fmt.Printf(i18n.G("Copying the image: %s")+"\r", progress)
+ }
-When using remote images, LXD will automatically cache images for you
-and remove them upon expiration.
+ err = d.CopyImage(inName, dest, copyAliases, alias, public, progressHandler)
+ if err == nil {
+ fmt.Println(i18n.G("Image copied successfully!"))
+ }
+ return err
+}
-The image unique identifier is the hash (sha-256) of its representation
-as a compressed tarball (or for split images, the concatenation of the
-metadata and rootfs tarballs).
+func commandActionImageDelete(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ /* [<remote>:]<image> */
+ if len(args) < 1 {
+ return errArgs
+ }
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+ var cmd = &imageCmd{}
+ image := cmd.dereferenceAlias(d, inName)
+ err = d.DeleteImage(image)
+ return err
+}
-Images can be referenced by their full hash, shortest unique partial
-hash or alias name (if one is set).
+func commandActionImageInfo(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 1 {
+ return errArgs
+ }
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
-lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [prop=value]
- Import an image tarball (or tarballs) into the LXD image store.
+ var cmd = &imageCmd{}
+ image := cmd.dereferenceAlias(d, inName)
-lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public]
- Copy an image from one LXD daemon to another over the network.
+ info, err := d.GetImageInfo(image)
+ if err != nil {
+ return err
+ }
+ fmt.Printf(i18n.G("Fingerprint: %s")+"\n", info.Fingerprint)
+ public := i18n.G("no")
-lxc image delete [remote:]<image>
- Delete an image from the LXD image store.
+ if info.Public {
+ public = i18n.G("yes")
+ }
-lxc image export [remote:]<image>
- Export an image from the LXD image store into a distributable tarball.
+ fmt.Printf(i18n.G("Size: %.2fMB")+"\n", float64(info.Size)/1024.0/1024.0)
+ fmt.Printf(i18n.G("Architecture: %s")+"\n", info.Architecture)
+ fmt.Printf(i18n.G("Public: %s")+"\n", public)
+ fmt.Printf(i18n.G("Timestamps:") + "\n")
+ const layout = "2006/01/02 15:04 UTC"
+ if info.CreationDate.UTC().Unix() != 0 {
+ fmt.Printf(" "+i18n.G("Created: %s")+"\n", info.CreationDate.UTC().Format(layout))
+ }
+ fmt.Printf(" "+i18n.G("Uploaded: %s")+"\n", info.UploadDate.UTC().Format(layout))
+ if info.ExpiryDate.UTC().Unix() != 0 {
+ fmt.Printf(" "+i18n.G("Expires: %s")+"\n", info.ExpiryDate.UTC().Format(layout))
+ } else {
+ fmt.Printf(" " + i18n.G("Expires: never") + "\n")
+ }
+ fmt.Println(i18n.G("Properties:"))
+ for key, value := range info.Properties {
+ fmt.Printf(" %s: %s\n", key, value)
+ }
+ fmt.Println(i18n.G("Aliases:"))
+ for _, alias := range info.Aliases {
+ fmt.Printf(" - %s\n", alias.Name)
+ }
+ return nil
+}
-lxc image info [remote:]<image>
- Print everything LXD knows about a given image.
+func commandActionImageExport(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
-lxc image list [remote:] [filter]
- List images in the LXD image store. Filters may be of the
- <key>=<value> form for property based filtering, or part of the image
- hash or part of the image alias name.
+ if len(args) < 1 {
+ return errArgs
+ }
-lxc image show [remote:]<image>
- Yaml output of the user modifiable properties of an image.
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
-lxc image edit [remote:]<image>
- Edit image, either by launching external editor or reading STDIN.
- Example: lxc image edit <image> # launch editor
- cat image.yml | lxc image edit <image> # read from image.yml
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
-lxc image alias create [remote:]<alias> <fingerprint>
- Create a new alias for an existing image.
+ var cmd = &imageCmd{}
+ image := cmd.dereferenceAlias(d, inName)
-lxc image alias delete [remote:]<alias>
- Delete an alias.
+ target := "."
+ if len(args) > 1 {
+ target = args[1]
+ }
+ outfile, err := d.ExportImage(image, target)
+ if err != nil {
+ return err
+ }
-lxc image alias list [remote:]
- List the aliases.
-`)
+ if target != "-" {
+ fmt.Printf(i18n.G("Output is in %s")+"\n", outfile)
+ }
+ return nil
}
-func (c *imageCmd) flags() {
- gnuflag.BoolVar(&c.publicImage, "public", false, i18n.G("Make image public"))
- gnuflag.BoolVar(&c.copyAliases, "copy-aliases", false, i18n.G("Copy aliases from source"))
- gnuflag.Var(&c.addAliases, "alias", i18n.G("New alias to define at target"))
-}
+func commandActionImageList(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ filters := []string{}
-func (c *imageCmd) doImageAlias(config *lxd.Config, args []string) error {
var remote string
- switch args[1] {
- case "list":
- /* alias list [<remote>:] */
- if len(args) > 2 {
- remote, _ = config.ParseRemoteAndContainer(args[2])
- } else {
+
+ if len(args) > 0 {
+ result := strings.SplitN(args[0], ":", 2)
+ if len(result) == 1 {
+ filters = append(filters, args[0])
remote, _ = config.ParseRemoteAndContainer("")
+ } else {
+ remote, _ = config.ParseRemoteAndContainer(args[0])
}
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ } else {
+ remote, _ = config.ParseRemoteAndContainer("")
+ }
- resp, err := d.ListAliases()
- if err != nil {
- return err
+ if len(args) > 1 {
+ for _, filter := range args[1:] {
+ filters = append(filters, filter)
}
+ }
- c.showAliases(resp)
-
- return nil
- case "create":
- /* alias create [<remote>:]<alias> <target> */
- if len(args) < 4 {
- return errArgs
- }
- remote, alias := config.ParseRemoteAndContainer(args[2])
- target := args[3]
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
- /* TODO - what about description? */
- err = d.PostAlias(alias, alias, target)
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
return err
- case "delete":
- /* alias delete [<remote>:]<alias> */
- if len(args) < 3 {
- return errArgs
- }
- remote, alias := config.ParseRemoteAndContainer(args[2])
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
- err = d.DeleteAlias(alias)
+ }
+
+ images, err := d.ListImages()
+ if err != nil {
return err
}
- return errArgs
+
+ var cmd = &imageCmd{}
+ return cmd.showImages(images, filters)
}
-func (c *imageCmd) run(config *lxd.Config, args []string) error {
- var remote string
+func commandActionImageShow(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
if len(args) < 1 {
return errArgs
}
- switch args[0] {
- case "alias":
- if len(args) < 2 {
- return errArgs
- }
- return c.doImageAlias(config, args)
-
- case "copy":
- /* copy [<remote>:]<image> [<rmeote>:]<image> */
- if len(args) != 3 {
- return errArgs
- }
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
- }
- destRemote, outName := config.ParseRemoteAndContainer(args[2])
- if outName != "" {
- return errArgs
- }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
- dest, err := lxd.NewClient(config, destRemote)
- if err != nil {
- return err
- }
-
- progressHandler := func(progress string) {
- fmt.Printf(i18n.G("Copying the image: %s")+"\r", progress)
- }
-
- err = d.CopyImage(inName, dest, c.copyAliases, c.addAliases, c.publicImage, progressHandler)
- if err == nil {
- fmt.Println(i18n.G("Image copied successfully!"))
- }
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
return err
+ }
- case "delete":
- /* delete [<remote>:]<image> */
- if len(args) < 2 {
- return errArgs
- }
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
- }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
- image := c.dereferenceAlias(d, inName)
- err = d.DeleteImage(image)
+ var cmd = &imageCmd{}
+ image := cmd.dereferenceAlias(d, inName)
+ info, err := d.GetImageInfo(image)
+ if err != nil {
return err
+ }
- case "info":
- if len(args) < 2 {
- return errArgs
- }
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
- }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
-
- image := c.dereferenceAlias(d, inName)
- info, err := d.GetImageInfo(image)
- if err != nil {
- return err
- }
- fmt.Printf(i18n.G("Fingerprint: %s")+"\n", info.Fingerprint)
- public := i18n.G("no")
-
- if info.Public {
- public = i18n.G("yes")
- }
-
- fmt.Printf(i18n.G("Size: %.2fMB")+"\n", float64(info.Size)/1024.0/1024.0)
- fmt.Printf(i18n.G("Architecture: %s")+"\n", info.Architecture)
- fmt.Printf(i18n.G("Public: %s")+"\n", public)
- fmt.Printf(i18n.G("Timestamps:") + "\n")
- const layout = "2006/01/02 15:04 UTC"
- if info.CreationDate.UTC().Unix() != 0 {
- fmt.Printf(" "+i18n.G("Created: %s")+"\n", info.CreationDate.UTC().Format(layout))
- }
- fmt.Printf(" "+i18n.G("Uploaded: %s")+"\n", info.UploadDate.UTC().Format(layout))
- if info.ExpiryDate.UTC().Unix() != 0 {
- fmt.Printf(" "+i18n.G("Expires: %s")+"\n", info.ExpiryDate.UTC().Format(layout))
- } else {
- fmt.Printf(" " + i18n.G("Expires: never") + "\n")
- }
- fmt.Println(i18n.G("Properties:"))
- for key, value := range info.Properties {
- fmt.Printf(" %s: %s\n", key, value)
- }
- fmt.Println(i18n.G("Aliases:"))
- for _, alias := range info.Aliases {
- fmt.Printf(" - %s\n", alias.Name)
- }
- return nil
-
- case "import":
- if len(args) < 2 {
- return errArgs
- }
+ properties := info.Brief()
- var fingerprint string
- var imageFile string
- var rootfsFile string
- var properties []string
- var remote string
-
- for _, arg := range args[1:] {
- split := strings.Split(arg, "=")
- if len(split) == 1 || shared.PathExists(arg) {
- if strings.HasSuffix(arg, ":") {
- remote = config.ParseRemote(arg)
- } else {
- if imageFile == "" {
- imageFile = args[1]
- } else {
- rootfsFile = arg
- }
- }
- } else {
- properties = append(properties, arg)
- }
- }
+ data, err := yaml.Marshal(&properties)
+ fmt.Printf("%s", data)
+ return err
+}
- if remote == "" {
- remote = config.DefaultRemote
- }
+func commandActionImageEdit(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- if imageFile == "" {
- return errArgs
- }
+ if len(args) < 1 {
+ return errArgs
+ }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ remote, inName := config.ParseRemoteAndContainer(args[0])
+ if inName == "" {
+ return errArgs
+ }
- if strings.HasPrefix(imageFile, "https://") {
- fingerprint, err = d.PostImageURL(imageFile, c.publicImage, c.addAliases)
- } else if strings.HasPrefix(imageFile, "http://") {
- return fmt.Errorf(i18n.G("Only https:// is supported for remote image import."))
- } else {
- fingerprint, err = d.PostImage(imageFile, rootfsFile, properties, c.publicImage, c.addAliases)
- }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
- if err != nil {
- return err
- }
- fmt.Printf(i18n.G("Image imported with fingerprint: %s")+"\n", fingerprint)
+ var cmd = &imageCmd{}
+ image := cmd.dereferenceAlias(d, inName)
+ if image == "" {
+ image = inName
+ }
- return nil
+ return cmd.doImageEdit(d, image)
+}
- case "list":
- filters := []string{}
+func commandActionImageAliasCreate(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- if len(args) > 1 {
- result := strings.SplitN(args[1], ":", 2)
- if len(result) == 1 {
- filters = append(filters, args[1])
- remote, _ = config.ParseRemoteAndContainer("")
- } else {
- remote, _ = config.ParseRemoteAndContainer(args[1])
- }
- } else {
- remote, _ = config.ParseRemoteAndContainer("")
- }
+ /* [<remote>:]<alias> <target> */
+ if len(args) < 2 {
+ return errArgs
+ }
+ remote, alias := config.ParseRemoteAndContainer(args[0])
+ target := args[1]
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+ /* TODO - what about description? */
+ err = d.PostAlias(alias, alias, target)
+ return err
+}
- if len(args) > 2 {
- for _, filter := range args[2:] {
- filters = append(filters, filter)
- }
- }
+func commandActionImageAliasDelete(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ /* [<remote>:]<alias> */
+ if len(args) < 1 {
+ return errArgs
+ }
+ remote, alias := config.ParseRemoteAndContainer(args[0])
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+ err = d.DeleteAlias(alias)
+ return err
+}
- images, err := d.ListImages()
- if err != nil {
- return err
- }
+func commandActionImageAliasList(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var remote string
- return c.showImages(images, filters)
+ /* alias list [<remote>:] */
+ if len(args) > 1 {
+ remote, _ = config.ParseRemoteAndContainer(args[0])
+ } else {
+ remote, _ = config.ParseRemoteAndContainer("")
+ }
+ d, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
- case "edit":
- if len(args) < 2 {
- return errArgs
- }
+ resp, err := d.ListAliases()
+ if err != nil {
+ return err
+ }
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
- }
+ var cmd = &imageCmd{}
+ cmd.showAliases(resp)
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+ return nil
+}
- image := c.dereferenceAlias(d, inName)
- if image == "" {
- image = inName
- }
+type sortImage [][]string
- return c.doImageEdit(d, image)
+func (a sortImage) Len() int {
+ return len(a)
+}
- case "export":
- if len(args) < 2 {
- return errArgs
- }
+func (a sortImage) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
+func (a sortImage) Less(i, j int) bool {
+ if a[i][0] == a[j][0] {
+ if a[i][3] == "" {
+ return false
}
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
+ if a[j][3] == "" {
+ return true
}
- image := c.dereferenceAlias(d, inName)
+ return a[i][3] < a[j][3]
+ }
- target := "."
- if len(args) > 2 {
- target = args[2]
- }
+ if a[i][0] == "" {
+ return false
+ }
- outfile, err := d.ExportImage(image, target)
- if err != nil {
- return err
- }
+ if a[j][0] == "" {
+ return true
+ }
- if target != "-" {
- fmt.Printf(i18n.G("Output is in %s")+"\n", outfile)
- }
- return nil
+ return a[i][0] < a[j][0]
+}
- case "show":
- if len(args) < 2 {
- return errArgs
- }
- remote, inName := config.ParseRemoteAndContainer(args[1])
- if inName == "" {
- return errArgs
- }
- d, err := lxd.NewClient(config, remote)
- if err != nil {
- return err
- }
+type aliasList []string
- image := c.dereferenceAlias(d, inName)
- info, err := d.GetImageInfo(image)
- if err != nil {
- return err
- }
+func (f *aliasList) String() string {
+ return fmt.Sprint(*f)
+}
- properties := info.Brief()
+func (f *aliasList) Set(value string) error {
+ if f == nil {
+ *f = make(aliasList, 1)
+ } else {
+ *f = append(*f, value)
+ }
+ return nil
+}
- data, err := yaml.Marshal(&properties)
- fmt.Printf("%s", data)
- return err
+type imageCmd struct {
+ addAliases aliasList
+ publicImage bool
+ copyAliases bool
+}
- default:
- return errArgs
- }
+func (c *imageCmd) imageEditHelp() string {
+ return i18n.G(
+ `### This is a yaml representation of the image properties.
+### Any line starting with a '# will be ignored.
+###
+### Each property is represented by a single line:
+### An example would be:
+### description: My custom image`)
}
func (c *imageCmd) dereferenceAlias(d *lxd.Client, inName string) string {
@@ -558,7 +702,7 @@ func (c *imageCmd) showImages(images []shared.ImageInfo, filters []string) error
i18n.G("ARCH"),
i18n.G("SIZE"),
i18n.G("UPLOAD DATE")})
- sort.Sort(SortImage(data))
+ sort.Sort(sortImage(data))
table.AppendBulk(data)
table.Render()
@@ -578,7 +722,7 @@ func (c *imageCmd) showAliases(aliases shared.ImageAliases) error {
i18n.G("ALIAS"),
i18n.G("FINGERPRINT"),
i18n.G("DESCRIPTION")})
- sort.Sort(SortImage(data))
+ sort.Sort(sortImage(data))
table.AppendBulk(data)
table.Render()
diff --git a/lxc/info.go b/lxc/info.go
index 9f76181..32008dc 100644
--- a/lxc/info.go
+++ b/lxc/info.go
@@ -7,30 +7,35 @@ import (
"gopkg.in/yaml.v2"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type infoCmd struct {
- showLog bool
-}
+var commandInfo = cli.Command{
+ Name: "info",
+ Usage: i18n.G("List information on containers."),
+ ArgsUsage: i18n.G("[<remote>:]container [--show-log]"),
+ Description: i18n.G("This will support remotes and images as well, but only containers for now."),
-func (c *infoCmd) showByDefault() bool {
- return true
-}
+ Flags: append(commandGlobalFlags, cli.BoolFlag{
+ Name: "show-log",
+ Usage: i18n.G("Also show the log."),
+ }),
-func (c *infoCmd) usage() string {
- return i18n.G(
- `List information on containers.
+ Action: commandWrapper(commandActionInfo),
+}
-This will support remotes and images as well, but only containers for now.
+func commandActionInfo(config *lxd.Config, context *cli.Context) error {
+ var cmd = &infoCmd{
+ showLog: context.Bool("show-log"),
+ }
-lxc info [<remote>:]container [--show-log]`)
+ return cmd.run(config, context.Args())
}
-func (c *infoCmd) flags() {
- gnuflag.BoolVar(&c.showLog, "show-log", false, i18n.G("Show the container's last 100 log lines?"))
+type infoCmd struct {
+ showLog bool
}
func (c *infoCmd) run(config *lxd.Config, args []string) error {
@@ -49,9 +54,9 @@ func (c *infoCmd) run(config *lxd.Config, args []string) error {
if cName == "" {
return c.remoteInfo(d)
- } else {
- return c.containerInfo(d, cName, c.showLog)
}
+
+ return c.containerInfo(d, cName, c.showLog)
}
func (c *infoCmd) remoteInfo(d *lxd.Client) error {
@@ -119,14 +124,14 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error
}
// List snapshots
- first_snapshot := true
+ firstSnapshot := true
snaps, err := d.ListSnapshots(name)
if err != nil {
return nil
}
for _, snap := range snaps {
- if first_snapshot {
+ if firstSnapshot {
fmt.Println(i18n.G("Snapshots:"))
}
fmt.Printf(" %s", snap.Name)
@@ -142,7 +147,7 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error
}
fmt.Printf("\n")
- first_snapshot = false
+ firstSnapshot = false
}
if showLog {
diff --git a/lxc/init.go b/lxc/init.go
index 96830cd..2670692 100644
--- a/lxc/init.go
+++ b/lxc/init.go
@@ -2,15 +2,72 @@ package main
import (
"fmt"
- "os"
"strings"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
+// TODO: Make this a "hidden" command.
+var commandInit = cli.Command{
+ Name: "init",
+ Usage: i18n.G("Initializes a container using the specified image and name."),
+ ArgsUsage: i18n.G("[remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...]"),
+
+ Description: `Initialize a container from a particular image.
+
+ lxc init [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]
+
+ Initializes a container using the specified image and name.
+
+ Not specifying -p will result in the default profile.
+ Specifying "-p" with an empty argument will result in no profile.
+
+ Example:
+ lxc init ubuntu u1`,
+
+ Flags: []cli.Flag{
+ cli.BoolFlag{
+ Name: "debug",
+ Usage: i18n.G("Print debug information."),
+ },
+
+ cli.BoolFlag{
+ Name: "verbose",
+ Usage: i18n.G("Print verbose information."),
+ },
+
+ cli.BoolFlag{
+ Name: "ephemeral, e",
+ Usage: i18n.G("Ephemeral container."),
+ },
+
+ cli.StringSliceFlag{
+ Name: "config, c",
+ Value: nil,
+ Usage: i18n.G("Config key/value to apply to the new container."),
+ },
+
+ cli.StringSliceFlag{
+ Name: "profile, p",
+ Value: nil,
+ Usage: i18n.G("Profile to apply to the new container."),
+ },
+ },
+ Action: commandWrapper(commandActionInit),
+}
+
+func commandActionInit(config *lxd.Config, context *cli.Context) error {
+ var cmd = &initCmd{}
+ cmd.confArgs = context.StringSlice("config")
+ cmd.profArgs = context.StringSlice("profile")
+ cmd.ephem = context.Bool("ephemeral")
+
+ return cmd.run(config, context.Args())
+}
+
type profileList []string
var configMap map[string]string
@@ -65,80 +122,6 @@ type initCmd struct {
ephem bool
}
-func (c *initCmd) showByDefault() bool {
- return false
-}
-
-func (c *initCmd) usage() string {
- return i18n.G(
- `Initialize a container from a particular image.
-
-lxc init [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]
-
-Initializes a container using the specified image and name.
-
-Not specifying -p will result in the default profile.
-Specifying "-p" with no argument will result in no profile.
-
-Example:
-lxc init ubuntu u1`)
-}
-
-func (c *initCmd) is_ephem(s string) bool {
- switch s {
- case "-e":
- return true
- case "--ephemeral":
- return true
- }
- return false
-}
-
-func (c *initCmd) is_profile(s string) bool {
- switch s {
- case "-p":
- return true
- case "--profile":
- return true
- }
- return false
-}
-
-func (c *initCmd) massage_args() {
- l := len(os.Args)
- if l < 2 {
- return
- }
-
- if c.is_profile(os.Args[l-1]) {
- initRequestedEmptyProfiles = true
- os.Args = os.Args[0 : l-1]
- return
- }
-
- if l < 3 {
- return
- }
-
- /* catch "lxc init ubuntu -p -e */
- if c.is_ephem(os.Args[l-1]) && c.is_profile(os.Args[l-2]) {
- initRequestedEmptyProfiles = true
- newargs := os.Args[0 : l-2]
- newargs = append(newargs, os.Args[l-1])
- return
- }
-}
-
-func (c *initCmd) flags() {
- c.massage_args()
- gnuflag.Var(&c.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
- gnuflag.Var(&c.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
- gnuflag.Var(&c.profArgs, "profile", i18n.G("Profile to apply to the new container"))
- gnuflag.Var(&c.profArgs, "p", i18n.G("Profile to apply to the new container"))
- gnuflag.BoolVar(&c.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
- gnuflag.BoolVar(&c.ephem, "e", false, i18n.G("Ephemeral container"))
-}
-
func (c *initCmd) run(config *lxd.Config, args []string) error {
if len(args) > 2 || len(args) < 1 {
return errArgs
@@ -193,22 +176,22 @@ func (c *initCmd) run(config *lxd.Config, args []string) error {
if err != nil {
return err
- } else {
- op, err := resp.MetadataAsOperation()
- if err != nil {
- return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
- }
+ }
- containers, ok := op.Resources["containers"]
- if !ok || len(containers) == 0 {
- return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
- }
+ op, err := resp.MetadataAsOperation()
+ if err != nil {
+ return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
+ }
- if len(containers) == 1 && name == "" {
- fields := strings.Split(containers[0], "/")
- fmt.Printf(i18n.G("Container name is: %s")+"\n", fields[len(fields)-1])
- }
+ containers, ok := op.Resources["containers"]
+ if !ok || len(containers) == 0 {
+ return fmt.Errorf(i18n.G("didn't get any affected image, container or snapshot from server"))
}
+
+ if len(containers) == 1 && name == "" {
+ fmt.Printf(i18n.G("Container name is: %s"), name)
+ }
+
return nil
}
diff --git a/lxc/launch.go b/lxc/launch.go
index e2c9bd6..291edf6 100644
--- a/lxc/launch.go
+++ b/lxc/launch.go
@@ -4,45 +4,68 @@ import (
"fmt"
"strings"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type launchCmd struct {
- init initCmd
+var commandLaunch = cli.Command{
+ Name: "launch",
+ Usage: i18n.G("Launch a container from a particular image."),
+ ArgsUsage: i18n.G("[remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...]"),
+ Description: i18n.G(`Launches a container using the specified image and name.
+
+ Not specifying -p will result in the default profile.
+ Specifying "-p" with no argument will result in no profile.`),
+
+ Flags: []cli.Flag{
+ cli.BoolFlag{
+ Name: "debug",
+ Usage: i18n.G("Print debug information."),
+ },
+
+ cli.BoolFlag{
+ Name: "verbose",
+ Usage: i18n.G("Print verbose information."),
+ },
+
+ cli.BoolFlag{
+ Name: "ephemeral, e",
+ Usage: i18n.G("Ephemeral."),
+ },
+
+ cli.StringSliceFlag{
+ Name: "config, c",
+ Usage: i18n.G("Config key/value to apply to the new container."),
+ },
+
+ cli.StringSliceFlag{
+ Name: "profile, p",
+ Usage: i18n.G("Profile to apply to the new container."),
+ },
+ },
+
+ Action: commandWrapper(commandActionLaunch),
}
-func (c *launchCmd) showByDefault() bool {
- return true
-}
-
-func (c *launchCmd) usage() string {
- return i18n.G(
- `Launch a container from a particular image.
-
-lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]
-
-Launches a container using the specified image and name.
+func commandActionLaunch(config *lxd.Config, c *cli.Context) error {
+ var cmd = &launchCmd{}
+ cmd.init.confArgs = c.StringSlice("config")
+ var profiles = c.StringSlice("profile")
+ if len(profiles) == 1 && profiles[0] == "" {
+ initRequestedEmptyProfiles = true
+ profiles = []string{}
+ }
+ cmd.init.profArgs = profiles
-Not specifying -p will result in the default profile.
-Specifying "-p" with no argument will result in no profile.
+ cmd.init.ephem = c.Bool("ephemeral")
-Example:
-lxc launch ubuntu u1`)
+ return cmd.run(config, c.Args())
}
-func (c *launchCmd) flags() {
- c.init = initCmd{}
-
- c.init.massage_args()
- gnuflag.Var(&c.init.confArgs, "config", i18n.G("Config key/value to apply to the new container"))
- gnuflag.Var(&c.init.confArgs, "c", i18n.G("Config key/value to apply to the new container"))
- gnuflag.Var(&c.init.profArgs, "profile", i18n.G("Profile to apply to the new container"))
- gnuflag.Var(&c.init.profArgs, "p", i18n.G("Profile to apply to the new container"))
- gnuflag.BoolVar(&c.init.ephem, "ephemeral", false, i18n.G("Ephemeral container"))
- gnuflag.BoolVar(&c.init.ephem, "e", false, i18n.G("Ephemeral container"))
+type launchCmd struct {
+ init initCmd
}
func (c *launchCmd) run(config *lxd.Config, args []string) error {
diff --git a/lxc/list.go b/lxc/list.go
index dfa85df..89c4b85 100644
--- a/lxc/list.go
+++ b/lxc/list.go
@@ -8,14 +8,64 @@ import (
"strings"
"sync"
+ "github.com/codegangsta/cli"
"github.com/olekukonko/tablewriter"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
+var commandList = cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[resource] [filters] [-c columns] [--fast]"),
+ Usage: i18n.G("Lists the available resources."),
+ Description: i18n.G(`Lists the available resources.
+
+ The filters are:
+ * A single keyword like "web" which will list any container with "web" in its name.
+ * A key/value pair referring to a configuration item. For those, the namespace can be abreviated to the smallest unambiguous identifier:
+ * "user.blah=abc" will list all containers with the "blah" user property set to "abc"
+ * "u.blah=abc" will do the same
+ * "security.privileged=1" will list all privileged containers
+ * "s.privileged=1" will do the same
+
+ The columns are:
+ * 4 - IPv4 address
+ * 6 - IPv6 address
+ * a - architecture
+ * c - creation date
+ * n - name
+ * p - pid of container init process
+ * P - profiles
+ * s - state
+ * t - type (persistent or ephemeral)
+
+ Default column layout: ns46tS
+ Fast column layout: nsacPt`),
+
+ Flags: append(commandGlobalFlags,
+ cli.StringFlag{
+ Name: "columns, c",
+ Value: "ns46tS",
+ Usage: i18n.G("Columns."),
+ },
+ cli.BoolFlag{
+ Name: "fast",
+ Usage: i18n.G("Fast mode (same as --columns=nsacPt."),
+ },
+ ),
+ Action: commandWrapper(commandActionList),
+}
+
+func commandActionList(config *lxd.Config, c *cli.Context) error {
+ var cmd = &listCmd{}
+ cmd.chosenColumnRunes = c.String("columns")
+ cmd.fast = c.Bool("fast")
+
+ return cmd.run(config, c.Args())
+}
+
type column struct {
Name string
Data columnData
@@ -52,45 +102,6 @@ type listCmd struct {
fast bool
}
-func (c *listCmd) showByDefault() bool {
- return true
-}
-
-func (c *listCmd) usage() string {
- return i18n.G(
- `Lists the available resources.
-
-lxc list [resource] [filters] [-c columns] [--fast]
-
-The filters are:
-* A single keyword like "web" which will list any container with "web" in its name.
-* A key/value pair referring to a configuration item. For those, the namespace can be abreviated to the smallest unambiguous identifier:
-* "user.blah=abc" will list all containers with the "blah" user property set to "abc"
-* "u.blah=abc" will do the same
-* "security.privileged=1" will list all privileged containers
-* "s.privileged=1" will do the same
-
-The columns are:
-* 4 - IPv4 address
-* 6 - IPv6 address
-* a - architecture
-* c - creation date
-* n - name
-* p - pid of container init process
-* P - profiles
-* s - state
-* t - type (persistent or ephemeral)
-
-Default column layout: ns46tS
-Fast column layout: nsacPt`)
-}
-
-func (c *listCmd) flags() {
- gnuflag.StringVar(&c.chosenColumnRunes, "c", "ns46tS", i18n.G("Columns"))
- gnuflag.StringVar(&c.chosenColumnRunes, "columns", "ns46tS", i18n.G("Columns"))
- gnuflag.BoolVar(&c.fast, "fast", false, i18n.G("Fast mode (same as --columns=nsacPt"))
-}
-
// This seems a little excessive.
func (c *listCmd) dotPrefixMatch(short string, full string) bool {
fullMembs := strings.Split(full, ".")
@@ -100,7 +111,7 @@ func (c *listCmd) dotPrefixMatch(short string, full string) bool {
return false
}
- for i, _ := range fullMembs {
+ for i := range fullMembs {
if !strings.HasPrefix(fullMembs[i], shortMembs[i]) {
return false
}
@@ -292,7 +303,7 @@ func (c *listCmd) run(config *lxd.Config, args []string) error {
filters := []string{}
- if len(args) != 0 {
+ if len(args) > 0 {
filters = args
if strings.Contains(args[0], ":") {
remote, name = config.ParseRemoteAndContainer(args[0])
@@ -328,7 +339,7 @@ func (c *listCmd) run(config *lxd.Config, args []string) error {
}
}
- columns_map := map[rune]column{
+ columnsMap := map[rune]column{
'4': column{i18n.G("IPV4"), c.IP4ColumnData, true, false},
'6': column{i18n.G("IPV6"), c.IP6ColumnData, true, false},
'a': column{i18n.G("ARCHITECTURE"), c.ArchitectureColumnData, false, false},
@@ -347,7 +358,7 @@ func (c *listCmd) run(config *lxd.Config, args []string) error {
columns := []column{}
for _, columnRune := range c.chosenColumnRunes {
- if column, ok := columns_map[columnRune]; ok {
+ if column, ok := columnsMap[columnRune]; ok {
columns = append(columns, column)
} else {
return fmt.Errorf("%s does contain invalid column characters\n", c.chosenColumnRunes)
@@ -384,9 +395,9 @@ func (c *listCmd) IP4ColumnData(cInfo shared.ContainerInfo, cState *shared.Conta
}
}
return strings.Join(ipv4s, "\n")
- } else {
- return ""
}
+
+ return ""
}
func (c *listCmd) IP6ColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
@@ -408,17 +419,17 @@ func (c *listCmd) IP6ColumnData(cInfo shared.ContainerInfo, cState *shared.Conta
}
}
return strings.Join(ipv6s, "\n")
- } else {
- return ""
}
+
+ return ""
}
func (c *listCmd) typeColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
if cInfo.Ephemeral {
return i18n.G("EPHEMERAL")
- } else {
- return i18n.G("PERSISTENT")
}
+
+ return i18n.G("PERSISTENT")
}
func (c *listCmd) numberSnapshotsColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
diff --git a/lxc/main.go b/lxc/main.go
index 88845f5..16b7452 100644
--- a/lxc/main.go
+++ b/lxc/main.go
@@ -10,17 +10,185 @@ import (
"strings"
"syscall"
+ "github.com/codegangsta/cli"
+
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/logging"
)
-var configPath string
+var errArgs = fmt.Errorf(i18n.G("wrong number of subcommand arguments"))
+
+// Gets set in commandGetConfig
+var commandConfigPath string
+
+var commandGlobalFlags = []cli.Flag{
+ cli.StringFlag{
+ Name: "directory",
+ Usage: i18n.G("Path to an alternate server directory."),
+ EnvVar: "LXD_DIR",
+ },
+
+ // BoolFlag is a switch that defaults to false
+ cli.BoolFlag{
+ Name: "debug",
+ Usage: i18n.G("Print debug information."),
+ },
+
+ cli.BoolFlag{
+ Name: "verbose",
+ Usage: i18n.G("Print verbose information."),
+ },
+
+ cli.BoolFlag{
+ Name: "no-alias",
+ Usage: i18n.G("Magic flag to run lxc without alias support."),
+ },
+
+ cli.BoolFlag{
+ Name: "force-local",
+ Usage: i18n.G("Force using the local unix socket."),
+ },
+}
func main() {
- if err := run(); err != nil {
+ app := cli.NewApp()
+ app.Name = "lxc"
+ app.Version = shared.Version
+ app.Usage = "LXD is pronounced lex-dee."
+ app.Flags = append(commandGlobalFlags, cli.StringFlag{
+ Name: "config",
+ Usage: i18n.G("Alternate config directory."),
+ EnvVar: "LXD_CONF",
+ })
+ app.Commands = []cli.Command{
+ commandConfig,
+ commandCopy,
+ commandDelete,
+ commandExec,
+ commandFile,
+ commandFinger,
+ commandImage,
+ commandInfo,
+ commandInit,
+ commandLaunch,
+ commandList,
+ commandMonitor,
+ commandMove,
+ commandProfile,
+ commandPublish,
+ commandRemote,
+ commandRestart,
+ commandRestore,
+ commandSnapshot,
+ commandStart,
+ commandStop,
+
+ cli.Command{
+ Name: "version",
+ Usage: i18n.G("Prints the version number of LXD."),
+ ArgsUsage: i18n.G(""),
+
+ Action: commandActionVersion,
+ },
+ }
+
+ app.Run(os.Args)
+}
+
+func commandWrapper(callable func(*lxd.Config, *cli.Context) error) func(*cli.Context) {
+ return func(c *cli.Context) {
+ var err error
+
+ // LXD_DIR
+ var lxddir = c.String("directory")
+ if lxddir == "" {
+ lxddir = c.GlobalString("directory")
+ }
+ if lxddir != "" {
+ os.Setenv("LXD_DIR", lxddir)
+ }
+
+ // Config
+ var configDir = "$HOME/.config/lxc"
+ if c.GlobalString("config") != "" {
+ configDir = c.GlobalString("config")
+ }
+ commandConfigPath = os.ExpandEnv(path.Join(configDir, "config.yml"))
+
+ var config *lxd.Config
+ var forceLocal = false
+ if c.GlobalBool("force-local") || c.Bool("force-local") {
+ forceLocal = true
+ }
+ if forceLocal {
+ config = &lxd.DefaultConfig
+ } else {
+ config, err = lxd.LoadConfig(commandConfigPath)
+ if err != nil {
+ commandExitError(err)
+ }
+
+ // One time migration from old config
+ if config.DefaultRemote == "" {
+ _, ok := config.Remotes["local"]
+ if !ok {
+ config.Remotes["local"] = lxd.LocalRemote
+ }
+ config.DefaultRemote = "local"
+ lxd.SaveConfig(config, commandConfigPath)
+ }
+ }
+
+ // Handle command aliases
+ var noAlias = false
+ if c.GlobalBool("no-alias") || c.Bool("no-alias") {
+ noAlias = true
+ }
+ if !noAlias {
+ // syscall.Exec replaces that process.
+ commandExecIfAliases(config, os.Args)
+ }
+
+ // Configure logging
+ var verbose = false
+ if c.GlobalBool("verbose") || c.Bool("verbose") {
+ verbose = true
+ }
+
+ var debug = false
+ if c.GlobalBool("debug") || c.Bool("debug") {
+ debug = true
+ }
+
+ shared.Log, err = logging.GetLogger("", "", verbose, debug, nil)
+ if err != nil {
+ commandExitError(err)
+ }
+
+ // Create a client cert on first start.
+ certf := config.ConfigPath("client.crt")
+ keyf := config.ConfigPath("client.key")
+
+ if !forceLocal && (!shared.PathExists(certf) || !shared.PathExists(keyf)) {
+ fmt.Fprintf(os.Stderr, i18n.G("Generating a client certificate. This may take a minute...")+"\n")
+
+ err = shared.FindOrGenCert(certf, keyf)
+ if err != nil {
+ commandExitError(err)
+ }
+
+ fmt.Fprintf(os.Stderr, i18n.G("If this is your first run, you will need to import images using the 'lxd-images' script.")+"\n")
+ fmt.Fprintf(os.Stderr, i18n.G("For example: 'lxd-images import ubuntu --alias ubuntu'.")+"\n")
+ }
+
+ commandExitError(callable(config, c))
+ }
+}
+
+func commandExitError(err error) {
+ if err != nil {
// The action we take depends on the error we get.
msg := fmt.Sprintf(i18n.G("error: %v"), err)
switch t := err.(type) {
@@ -50,153 +218,11 @@ func main() {
}
}
-func run() error {
- verbose := gnuflag.Bool("verbose", false, i18n.G("Enables verbose mode."))
- debug := gnuflag.Bool("debug", false, i18n.G("Enables debug mode."))
- forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket."))
- noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run."))
-
- configDir := "$HOME/.config/lxc"
- if os.Getenv("LXD_CONF") != "" {
- configDir = os.Getenv("LXD_CONF")
- }
- configPath = os.ExpandEnv(path.Join(configDir, "config.yml"))
-
- if len(os.Args) >= 3 && os.Args[1] == "config" && os.Args[2] == "profile" {
- fmt.Fprintf(os.Stderr, i18n.G("`lxc config profile` is deprecated, please use `lxc profile`")+"\n")
- os.Args = append(os.Args[:1], os.Args[2:]...)
- }
-
- if len(os.Args) >= 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
- os.Args[1] = "help"
- }
-
- if len(os.Args) >= 2 && (os.Args[1] == "--all") {
- os.Args[1] = "help"
- os.Args = append(os.Args, "--all")
- }
-
- if len(os.Args) == 2 && os.Args[1] == "--version" {
- os.Args[1] = "version"
- }
-
- if len(os.Args) < 2 {
- commands["help"].run(nil, nil)
- os.Exit(1)
- }
-
- var config *lxd.Config
- var err error
-
- if *forceLocal {
- config = &lxd.DefaultConfig
- } else {
- config, err = lxd.LoadConfig(configPath)
- if err != nil {
- return err
- }
- }
-
- // This is quite impolite, but it seems gnuflag needs us to shift our
- // own exename out of the arguments before parsing them. However, this
- // is useful for execIfAlias, which wants to know exactly the command
- // line we received, and in some cases is called before this shift, and
- // in others after. So, let's save the original args.
- origArgs := os.Args
- name := os.Args[1]
-
- /* at this point we haven't parsed the args, so we have to look for
- * --no-alias by hand.
- */
- if !shared.StringInSlice("--no-alias", origArgs) {
- execIfAliases(config, origArgs)
- }
- cmd, ok := commands[name]
- if !ok {
- commands["help"].run(nil, nil)
- fmt.Fprintf(os.Stderr, "\n"+i18n.G("error: unknown command: %s")+"\n", name)
- os.Exit(1)
- }
- cmd.flags()
- gnuflag.Usage = func() {
- fmt.Fprintf(os.Stderr, i18n.G("Usage: %s")+"\n\n"+i18n.G("Options:")+"\n\n", strings.TrimSpace(cmd.usage()))
- gnuflag.PrintDefaults()
- }
-
- os.Args = os.Args[1:]
- gnuflag.Parse(true)
-
- shared.Log, err = logging.GetLogger("", "", *verbose, *debug, nil)
- if err != nil {
- return err
- }
-
- certf := config.ConfigPath("client.crt")
- keyf := config.ConfigPath("client.key")
-
- if !*forceLocal && os.Args[0] != "help" && os.Args[0] != "version" && (!shared.PathExists(certf) || !shared.PathExists(keyf)) {
- fmt.Fprintf(os.Stderr, i18n.G("Generating a client certificate. This may take a minute...")+"\n")
-
- err = shared.FindOrGenCert(certf, keyf)
- if err != nil {
- return err
- }
-
- fmt.Fprintf(os.Stderr, i18n.G("If this is your first run, you will need to import images using the 'lxd-images' script.")+"\n")
- fmt.Fprintf(os.Stderr, i18n.G("For example: 'lxd-images import ubuntu --alias ubuntu'.")+"\n")
- }
-
- err = cmd.run(config, gnuflag.Args())
- if err == errArgs {
- /* If we got an error about invalid arguments, let's try to
- * expand this as an alias
- */
- if !*noAlias {
- execIfAliases(config, origArgs)
- }
- fmt.Fprintf(os.Stderr, "%s\n\n"+i18n.G("error: %v")+"\n", cmd.usage(), err)
- os.Exit(1)
- }
- return err
-}
-
-type command interface {
- usage() string
- flags()
- showByDefault() bool
- run(config *lxd.Config, args []string) error
-}
-
-var commands = map[string]command{
- "config": &configCmd{},
- "copy": ©Cmd{},
- "delete": &deleteCmd{},
- "exec": &execCmd{},
- "file": &fileCmd{},
- "finger": &fingerCmd{},
- "help": &helpCmd{},
- "image": &imageCmd{},
- "info": &infoCmd{},
- "init": &initCmd{},
- "launch": &launchCmd{},
- "list": &listCmd{},
- "monitor": &monitorCmd{},
- "move": &moveCmd{},
- "pause": &actionCmd{shared.Freeze, false, false, "pause", -1, false},
- "profile": &profileCmd{},
- "publish": &publishCmd{},
- "remote": &remoteCmd{},
- "restart": &actionCmd{shared.Restart, true, true, "restart", -1, false},
- "restore": &restoreCmd{},
- "snapshot": &snapshotCmd{},
- "start": &actionCmd{shared.Start, false, true, "start", -1, false},
- "stop": &actionCmd{shared.Stop, true, true, "stop", -1, false},
- "version": &versionCmd{},
+func commandActionVersion(c *cli.Context) {
+ println(shared.Version)
}
-var errArgs = fmt.Errorf(i18n.G("wrong number of subcommand arguments"))
-
-func expandAlias(config *lxd.Config, origArgs []string) ([]string, bool) {
+func commandExpandAlias(config *lxd.Config, origArgs []string) ([]string, bool) {
foundAlias := false
aliasKey := []string{}
aliasValue := []string{}
@@ -253,8 +279,8 @@ func expandAlias(config *lxd.Config, origArgs []string) ([]string, bool) {
return newArgs, true
}
-func execIfAliases(config *lxd.Config, origArgs []string) {
- newArgs, expanded := expandAlias(config, origArgs)
+func commandExecIfAliases(config *lxd.Config, origArgs []string) {
+ newArgs, expanded := commandExpandAlias(config, origArgs)
if !expanded {
return
}
diff --git a/lxc/monitor.go b/lxc/monitor.go
index dedb7de..a793f3d 100644
--- a/lxc/monitor.go
+++ b/lxc/monitor.go
@@ -5,11 +5,32 @@ import (
"gopkg.in/yaml.v2"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
+var commandMonitor = cli.Command{
+ Name: "monitor",
+ Usage: i18n.G("Monitor activity on the LXD server."),
+ ArgsUsage: i18n.G("[remote:] [--type=TYPE...]"),
+
+ Flags: append(commandGlobalFlags,
+ cli.StringSliceFlag{
+ Name: "type",
+ Usage: i18n.G("Event type to listen for"),
+ },
+ ),
+ Action: commandWrapper(commmandActionMonitor),
+}
+
+func commmandActionMonitor(config *lxd.Config, context *cli.Context) error {
+ var cmd = &monitorCmd{
+ typeArgs: context.StringSlice("type"),
+ }
+ return cmd.run(config, context.Args())
+}
+
type typeList []string
func (f *typeList) String() string {
@@ -33,29 +54,6 @@ type monitorCmd struct {
typeArgs typeList
}
-func (c *monitorCmd) showByDefault() bool {
- return false
-}
-
-func (c *monitorCmd) usage() string {
- return i18n.G(
- `Monitor activity on the LXD server.
-
-lxc monitor [remote:] [--type=TYPE...]
-
-Connects to the monitoring interface of the specified LXD server.
-
-By default will listen to all message types.
-Specific types to listen to can be specified with --type.
-
-Example:
-lxc monitor --type=logging`)
-}
-
-func (c *monitorCmd) flags() {
- gnuflag.Var(&c.typeArgs, "type", i18n.G("Event type to listen for"))
-}
-
func (c *monitorCmd) run(config *lxd.Config, args []string) error {
var remote string
diff --git a/lxc/move.go b/lxc/move.go
index 42f85f1..f670888 100644
--- a/lxc/move.go
+++ b/lxc/move.go
@@ -1,33 +1,22 @@
package main
import (
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared/i18n"
)
-type moveCmd struct {
- httpAddr string
-}
+var commandMove = cli.Command{
+ Name: "move",
+ Usage: i18n.G("Move containers within or in between lxd instances."),
+ ArgsUsage: i18n.G("[remote:]<source container> [remote:]<destination container>"),
-func (c *moveCmd) showByDefault() bool {
- return true
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commmandActionMove),
}
-func (c *moveCmd) usage() string {
- return i18n.G(
- `Move containers within or in between lxd instances.
-
-lxc move [remote:]<source container> [remote:]<destination container>
- Move a container between two hosts, renaming it if destination name differs.
-
-lxc move <old name> <new name>
- Rename a local container.
-`)
-}
-
-func (c *moveCmd) flags() {}
-
-func (c *moveCmd) run(config *lxd.Config, args []string) error {
+func commmandActionMove(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
if len(args) != 2 {
return errArgs
}
@@ -54,13 +43,12 @@ func (c *moveCmd) run(config *lxd.Config, args []string) error {
return source.WaitForSuccess(rename.Operation)
}
- cpy := copyCmd{}
-
// A move is just a copy followed by a delete; however, we want to
// keep the volatile entries around since we are moving the container.
- if err := cpy.copyContainer(config, args[0], args[1], true, -1); err != nil {
+ if err := copyContainer(config, args[0], args[1], true, -1); err != nil {
return err
}
- return commands["delete"].run(config, args[:1])
+ var cmd = &deleteCmd{}
+ return cmd.run(config, args[:1])
}
diff --git a/lxc/profile.go b/lxc/profile.go
index ebcf1aa..0cf0b4c 100644
--- a/lxc/profile.go
+++ b/lxc/profile.go
@@ -9,21 +9,279 @@ import (
"gopkg.in/yaml.v2"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
-type profileCmd struct {
- httpAddr string
+var commandProfile = cli.Command{
+ Name: "profile",
+ Usage: i18n.G("Manage configuration profiles."),
+ Description: i18n.G(`Manage configuration profiles.
+
+ lxc profile list [filters] List available profiles
+ lxc profile show <profile> Show details of a profile
+ lxc profile create <profile> Create a profile
+ lxc profile edit <profile> Edit profile in external editor
+ lxc profile copy <profile> <remote> Copy the profile to the specified remote
+ lxc profile get <profile> <key> Get profile configuration
+ lxc profile set <profile> <key> <value> Set profile configuration
+ lxc profile unset <profile> <key> Unset profile configuration
+ lxc profile delete <profile> Delete a profile
+ lxc profile apply <container> <profiles>
+ Apply a comma-separated list of profiles to a container, in order.
+ All profiles passed in this call (and only those) will be applied
+ to the specified container.
+ Example: lxc profile apply foo default,bar # Apply default and bar
+ lxc profile apply foo default # Only default is active
+ lxc profile apply '' # no profiles are applied anymore
+ lxc profile apply bar,default # Apply default second now
+
+ Devices:
+ lxc profile device list <profile> List devices in the given profile.
+ lxc profile device show <profile> Show full device details in the given profile.
+ lxc profile device remove <profile> <name> Remove a device from a profile.
+ lxc profile device add <profile name> <device name> <device type> [key=value]...
+ Add a profile device, such as a disk or a nic, to the containers
+ using the specified profile.`),
+
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("[filters]"),
+ Usage: i18n.G("List available profiles."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileList),
+ },
+
+ cli.Command{
+ Name: "show",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("Show details of a profile."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileShow),
+ },
+
+ cli.Command{
+ Name: "create",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("Create a profile."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileCreate),
+ },
+
+ cli.Command{
+ Name: "edit",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("Edit a profile."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileEdit),
+ },
+
+ cli.Command{
+ Name: "copy",
+ ArgsUsage: i18n.G("<profile> <remote>"),
+ Usage: i18n.G("Copy the profile to the specified remote."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileCopy),
+ },
+
+ cli.Command{
+ Name: "get",
+ ArgsUsage: i18n.G("<profile> <key>"),
+ Usage: i18n.G("Get profile configuration."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileGet),
+ },
+
+ cli.Command{
+ Name: "set",
+ ArgsUsage: i18n.G("<profile> <key> <value>"),
+ Usage: i18n.G("Set profile configuration."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileSet),
+ },
+
+ cli.Command{
+ Name: "unset",
+ ArgsUsage: i18n.G("<profile> <key>"),
+ Usage: i18n.G("Unset profile configuration."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileUnset),
+ },
+
+ cli.Command{
+ Name: "delete",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("Delete a profile."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileDelete),
+ },
+
+ cli.Command{
+ Name: "apply",
+ ArgsUsage: i18n.G("<container> <profiles>"),
+ Usage: i18n.G("Apply a comma-separated list of profiles to a container."),
+ Description: i18n.G(`Apply a comma-separated list of profiles to a container.
+
+ Apply a comma-separated list of profiles to a container, in order.
+ All profiles passed in this call (and only those) will be applied
+ to the specified container.
+ Example: lxc profile apply foo default,bar # Apply default and bar
+ lxc profile apply foo default # Only default is active
+ lxc profile apply '' # no profiles are applied anymore
+ lxc profile apply bar,default # Apply default second now
+`),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileApply),
+ },
+
+ cli.Command{
+ Name: "device",
+ Usage: i18n.G("Profile device manipulation."),
+
+ Subcommands: []cli.Command{
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("List devices in the given profile."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileDeviceList),
+ },
+
+ cli.Command{
+ Name: "show",
+ ArgsUsage: i18n.G("<profile>"),
+ Usage: i18n.G("Show full device details in the given profile."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileDeviceShow),
+ },
+
+ cli.Command{
+ Name: "remove",
+ ArgsUsage: i18n.G("<profile> <name>"),
+ Usage: i18n.G("Remove a device from a profile."),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileDeviceRemove),
+ },
+
+ cli.Command{
+ Name: "add",
+ ArgsUsage: i18n.G("<profile name> <device name> <device type> [key=value]..."),
+ Usage: i18n.G(`Add a profile device, such as a disk or a nic, to
+ the containers using the specified profile.`),
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionProfileDeviceAdd),
+ },
+ },
+ },
+ },
}
-func (c *profileCmd) showByDefault() bool {
- return true
+func commandActionProfileList(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var remote string
+ if len(args) > 0 {
+ var name string
+ remote, name = config.ParseRemoteAndContainer(args[0])
+ if name != "" {
+ return fmt.Errorf(i18n.G("Cannot provide container name to list"))
+ }
+ } else {
+ remote = config.DefaultRemote
+ }
+
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ profiles, err := client.ListProfiles()
+ if err != nil {
+ return err
+ }
+ fmt.Printf("%s\n", strings.Join(profiles, "\n"))
+ return nil
+}
+
+func commandActionProfileCreate(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ err = client.ProfileCreate(profile)
+ if err == nil {
+ fmt.Printf(i18n.G("Profile %s created")+"\n", profile)
+ }
+ return err
+}
+
+func commandActionProfileShow(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ profileData, err := client.ProfileConfig(profile)
+ if err != nil {
+ return err
+ }
+
+ data, err := yaml.Marshal(&profileData)
+ fmt.Printf("%s", data)
+
+ return nil
}
-func (c *profileCmd) profileEditHelp() string {
+func commandActionProfileDelete(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ err = client.ProfileDelete(profile)
+ if err == nil {
+ fmt.Printf(i18n.G("Profile %s deleted")+"\n", profile)
+ }
+ return err
+}
+
+func profileEditHelp() string {
return i18n.G(
`### This is a yaml representation of the profile.
### Any line starting with a '# will be ignored.
@@ -44,104 +302,19 @@ func (c *profileCmd) profileEditHelp() string {
### Note that the name is shown but cannot be changed`)
}
-func (c *profileCmd) usage() string {
- return i18n.G(
- `Manage configuration profiles.
-
-lxc profile list [filters] List available profiles.
-lxc profile show <profile> Show details of a profile.
-lxc profile create <profile> Create a profile.
-lxc profile copy <profile> <remote> Copy the profile to the specified remote.
-lxc profile get <profile> <key> Get profile configuration.
-lxc profile set <profile> <key> <value> Set profile configuration.
-lxc profile delete <profile> Delete a profile.
-lxc profile edit <profile>
- Edit profile, either by launching external editor or reading STDIN.
- Example: lxc profile edit <profile> # launch editor
- cat profile.yml | lxc profile edit <profile> # read from profile.yml
-lxc profile apply <container> <profiles>
- Apply a comma-separated list of profiles to a container, in order.
- All profiles passed in this call (and only those) will be applied
- to the specified container.
- Example: lxc profile apply foo default,bar # Apply default and bar
- lxc profile apply foo default # Only default is active
- lxc profile apply '' # no profiles are applied anymore
- lxc profile apply bar,default # Apply default second now
-
-Devices:
-lxc profile device list <profile> List devices in the given profile.
-lxc profile device show <profile> Show full device details in the given profile.
-lxc profile device remove <profile> <name> Remove a device from a profile.
-lxc profile device add <profile name> <device name> <device type> [key=value]...
- Add a profile device, such as a disk or a nic, to the containers
- using the specified profile.`)
-}
-
-func (c *profileCmd) flags() {}
+func commandActionProfileEdit(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
-func (c *profileCmd) run(config *lxd.Config, args []string) error {
if len(args) < 1 {
return errArgs
}
- if args[0] == "list" {
- return c.doProfileList(config, args)
- }
-
- if len(args) < 2 {
- return errArgs
- }
-
- remote, profile := config.ParseRemoteAndContainer(args[1])
+ remote, profile := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- switch args[0] {
- case "create":
- return c.doProfileCreate(client, profile)
- case "delete":
- return c.doProfileDelete(client, profile)
- case "device":
- return c.doProfileDevice(config, args)
- case "edit":
- return c.doProfileEdit(client, profile)
- case "apply":
- container := profile
- switch len(args) {
- case 2:
- profile = ""
- case 3:
- profile = args[2]
- default:
- return errArgs
- }
- return c.doProfileApply(client, container, profile)
- case "get":
- return c.doProfileGet(client, profile, args[2:])
- case "set":
- return c.doProfileSet(client, profile, args[2:])
- case "unset":
- return c.doProfileSet(client, profile, args[2:])
- case "copy":
- return c.doProfileCopy(config, client, profile, args[2:])
- case "show":
- return c.doProfileShow(client, profile)
- default:
- return errArgs
- }
-}
-
-func (c *profileCmd) doProfileCreate(client *lxd.Client, p string) error {
- err := client.ProfileCreate(p)
- if err == nil {
- fmt.Printf(i18n.G("Profile %s created")+"\n", p)
- }
- return err
-}
-
-func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
// If stdin isn't a terminal, read text from it
if !termios.IsTerminal(int(syscall.Stdin)) {
contents, err := ioutil.ReadAll(os.Stdin)
@@ -154,22 +327,22 @@ func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
if err != nil {
return err
}
- return client.PutProfile(p, newdata)
+ return client.PutProfile(profile, newdata)
}
// Extract the current value
- profile, err := client.ProfileConfig(p)
+ profileData, err := client.ProfileConfig(profile)
if err != nil {
return err
}
- data, err := yaml.Marshal(&profile)
+ data, err := yaml.Marshal(&profileData)
if err != nil {
return err
}
// Spawn the editor
- content, err := shared.TextEditor("", []byte(c.profileEditHelp()+"\n\n"+string(data)))
+ content, err := shared.TextEditor("", []byte(profileEditHelp()+"\n\n"+string(data)))
if err != nil {
return err
}
@@ -179,7 +352,7 @@ func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
newdata := shared.ProfileConfig{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
- err = client.PutProfile(p, newdata)
+ err = client.PutProfile(profile, newdata)
}
// Respawn the editor
@@ -203,50 +376,22 @@ func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
return nil
}
-func (c *profileCmd) doProfileDelete(client *lxd.Client, p string) error {
- err := client.ProfileDelete(p)
- if err == nil {
- fmt.Printf(i18n.G("Profile %s deleted")+"\n", p)
- }
- return err
-}
-
-func (c *profileCmd) doProfileApply(client *lxd.Client, d string, p string) error {
- resp, err := client.ApplyProfile(d, p)
- if err != nil {
- return err
- }
+func commandActionProfileCopy(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- err = client.WaitForSuccess(resp.Operation)
- if err == nil {
- if p == "" {
- p = i18n.G("(none)")
- }
- fmt.Printf(i18n.G("Profile %s applied to %s")+"\n", p, d)
+ if len(args) != 2 {
+ return errArgs
}
- return err
-}
-
-func (c *profileCmd) doProfileShow(client *lxd.Client, p string) error {
- profile, err := client.ProfileConfig(p)
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- data, err := yaml.Marshal(&profile)
- fmt.Printf("%s", data)
-
- return nil
-}
-
-func (c *profileCmd) doProfileCopy(config *lxd.Config, client *lxd.Client, p string, args []string) error {
- if len(args) != 1 {
- return errArgs
- }
- remote, newname := config.ParseRemoteAndContainer(args[0])
+ remote, newname := config.ParseRemoteAndContainer(args[1])
if newname == "" {
- newname = p
+ newname = profile
}
dest, err := lxd.NewClient(config, remote)
@@ -254,63 +399,54 @@ func (c *profileCmd) doProfileCopy(config *lxd.Config, client *lxd.Client, p str
return err
}
- return client.ProfileCopy(p, newname, dest)
+ return client.ProfileCopy(profile, newname, dest)
}
-func (c *profileCmd) doProfileDevice(config *lxd.Config, args []string) error {
- // device add b1 eth0 nic type=bridged
- // device list b1
- // device remove b1 eth0
- if len(args) < 3 {
- return errArgs
- }
-
- cfg := configCmd{}
+func commandActionProfileGet(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
- switch args[1] {
- case "add":
- return cfg.deviceAdd(config, "profile", args)
- case "remove":
- return cfg.deviceRm(config, "profile", args)
- case "list":
- return cfg.deviceList(config, "profile", args)
- case "show":
- return cfg.deviceShow(config, "profile", args)
- default:
+ if len(args) < 2 {
return errArgs
}
-}
-func (c *profileCmd) doProfileGet(client *lxd.Client, p string, args []string) error {
- // we shifted @args so so it should read "<key>"
- if len(args) != 1 {
- return errArgs
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
}
- resp, err := client.GetProfileConfig(p)
+ resp, err := client.GetProfileConfig(profile)
if err != nil {
return err
}
for k, v := range resp {
- if k == args[0] {
+ if k == args[1] {
fmt.Printf("%s\n", v)
}
}
+
return nil
}
-func (c *profileCmd) doProfileSet(client *lxd.Client, p string, args []string) error {
- // we shifted @args so so it should read "<key> [<value>]"
- if len(args) < 1 {
+func commandActionProfileSet(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 3 {
return errArgs
}
- key := args[0]
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
+ if err != nil {
+ return err
+ }
+
+ key := args[1]
var value string
- if len(args) < 2 {
+ if len(args) < 3 {
value = ""
} else {
- value = args[1]
+ value = args[2]
}
if !termios.IsTerminal(int(syscall.Stdin)) && value == "-" {
@@ -321,31 +457,103 @@ func (c *profileCmd) doProfileSet(client *lxd.Client, p string, args []string) e
value = string(buf[:])
}
- err := client.SetProfileConfigItem(p, key, value)
+ err = client.SetProfileConfigItem(profile, key, value)
return err
}
-func (c *profileCmd) doProfileList(config *lxd.Config, args []string) error {
- var remote string
- if len(args) > 1 {
- var name string
- remote, name = config.ParseRemoteAndContainer(args[1])
- if name != "" {
- return fmt.Errorf(i18n.G("Cannot provide container name to list"))
- }
- } else {
- remote = config.DefaultRemote
+func commandActionProfileUnset(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 2 {
+ return errArgs
}
+ remote, profile := config.ParseRemoteAndContainer(args[0])
client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- profiles, err := client.ListProfiles()
+ key := args[1]
+ err = client.SetProfileConfigItem(profile, key, "")
+ return err
+}
+
+func commandActionProfileApply(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ remote, profile := config.ParseRemoteAndContainer(args[0])
+ client, err := lxd.NewClient(config, remote)
if err != nil {
return err
}
- fmt.Printf("%s\n", strings.Join(profiles, "\n"))
- return nil
+
+ container := profile
+ switch len(args) {
+ case 1:
+ profile = ""
+ case 2:
+ profile = args[1]
+ default:
+ return errArgs
+ }
+
+ resp, err := client.ApplyProfile(container, profile)
+ if err != nil {
+ return err
+ }
+
+ err = client.WaitForSuccess(resp.Operation)
+ if err == nil {
+ if profile == "" {
+ profile = i18n.G("(none)")
+ }
+ fmt.Printf(i18n.G("Profile %s applied to %s")+"\n", profile, container)
+ }
+
+ return err
+}
+
+func commandActionProfileDeviceAdd(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ cfg := configCmd{}
+ return cfg.deviceAdd(config, "profile", args)
+}
+
+func commandActionProfileDeviceRemove(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ cfg := configCmd{}
+ return cfg.deviceRm(config, "profile", args)
+}
+
+func commandActionProfileDeviceList(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ cfg := configCmd{}
+ return cfg.deviceList(config, "profile", args)
+}
+
+func commandActionProfileDeviceShow(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 1 {
+ return errArgs
+ }
+
+ cfg := configCmd{}
+ return cfg.deviceShow(config, "profile", args)
}
diff --git a/lxc/publish.go b/lxc/publish.go
index 8ccd663..4c8a642 100644
--- a/lxc/publish.go
+++ b/lxc/publish.go
@@ -4,38 +4,43 @@ import (
"fmt"
"strings"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared"
)
-type publishCmd struct {
- pAliases aliasList // aliasList defined in lxc/image.go
- makePublic bool
- Force bool
+var commandPublish = cli.Command{
+ Name: "publish",
+ Usage: i18n.G("Publish containers as images."),
+ ArgsUsage: i18n.G("[remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]..."),
+
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "force",
+ Usage: i18n.G("Stop the container if currently running."),
+ },
+
+ cli.StringSliceFlag{
+ Name: "alias",
+ Usage: i18n.G("New alias to define at target."),
+ },
+
+ cli.BoolFlag{
+ Name: "public",
+ Usage: i18n.G("Make the image public."),
+ },
+ ),
+ Action: commandWrapper(commmandActionPublish),
}
-func (c *publishCmd) showByDefault() bool {
- return true
-}
-
-func (c *publishCmd) usage() string {
- return i18n.G(
- `Publish containers as images.
-
-lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]...`)
-}
-
-func (c *publishCmd) flags() {
- gnuflag.BoolVar(&c.makePublic, "public", false, i18n.G("Make the image public"))
- gnuflag.Var(&c.pAliases, "alias", i18n.G("New alias to define at target"))
- gnuflag.BoolVar(&c.Force, "force", false, i18n.G("Stop the container if currently running"))
- gnuflag.BoolVar(&c.Force, "f", false, i18n.G("Stop the container if currently running"))
-}
+func commmandActionPublish(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var pAliases = context.StringSlice("alias")
+ var makePublic = context.Bool("public")
+ var force = context.Bool("force")
-func (c *publishCmd) run(config *lxd.Config, args []string) error {
var cRemote string
var cName string
iName := ""
@@ -85,7 +90,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
wasEphemeral := ct.Ephemeral
if wasRunning {
- if !c.Force {
+ if !force {
return fmt.Errorf(i18n.G("The container is currently running. Use --force to have it stopped and restarted."))
}
@@ -134,7 +139,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
// Optimized local publish
if cRemote == iRemote {
- fp, err = d.ImageFromContainer(cName, c.makePublic, c.pAliases, properties)
+ fp, err = d.ImageFromContainer(cName, makePublic, pAliases, properties)
if err != nil {
return err
}
@@ -148,7 +153,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
}
defer s.DeleteImage(fp)
- err = s.CopyImage(fp, d, false, c.pAliases, c.makePublic, nil)
+ err = s.CopyImage(fp, d, false, pAliases, makePublic, nil)
if err != nil {
return err
}
diff --git a/lxc/remote.go b/lxc/remote.go
index 0e1cde7..9ecebfe 100644
--- a/lxc/remote.go
+++ b/lxc/remote.go
@@ -12,47 +12,291 @@ import (
"sort"
"strings"
+ "github.com/codegangsta/cli"
"github.com/olekukonko/tablewriter"
"golang.org/x/crypto/ssh/terminal"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type remoteCmd struct {
- httpAddr string
- acceptCert bool
- password string
- public bool
+var commandRemote = cli.Command{
+ Name: "remote",
+ Usage: i18n.G("Manage remote LXD servers."),
+ Description: i18n.G(`Manage remote LXD servers.
+
+ lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD] [--public] Add the remote <name> at <url>.
+ lxc remote remove <name> Remove the remote <name>.
+ lxc remote list List all remotes.
+ lxc remote rename <old> <new> Rename remote <old> to <new>.
+ lxc remote set-url <name> <url> Update <name>'s url to <url>.
+ lxc remote set-default <name> Set the default remote.
+ lxc remote get-default Print the default remote.`),
+
+ Subcommands: []cli.Command{
+
+ cli.Command{
+ Name: "add",
+ ArgsUsage: i18n.G("<name> <url> [--accept-certificate] [--password=PASSWORD] [--public]"),
+ Usage: i18n.G("Add the remote <name> at <url>."),
+
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "accept-certificate",
+ Usage: i18n.G("Accept certificate."),
+ },
+ cli.StringFlag{
+ Name: "password",
+ Usage: i18n.G("Remote admin password."),
+ },
+ cli.BoolFlag{
+ Name: "public",
+ Usage: i18n.G("Public image server."),
+ },
+ ),
+ Action: commandWrapper(commandActionRemoteAdd),
+ },
+
+ cli.Command{
+ Name: "remove",
+ ArgsUsage: i18n.G("<name>"),
+ Usage: i18n.G("Remove the remote <name>."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteRemove),
+ },
+
+ cli.Command{
+ Name: "list",
+ ArgsUsage: i18n.G(""),
+ Usage: i18n.G("List all remotes."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteList),
+ },
+
+ cli.Command{
+ Name: "rename",
+ ArgsUsage: i18n.G("<old> <new>"),
+ Usage: i18n.G("Rename remote <old> to <new>."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteRename),
+ },
+
+ cli.Command{
+ Name: "set-url",
+ ArgsUsage: i18n.G("<name> <url>"),
+ Usage: i18n.G("Update <name>'s url to <url>."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteSetURL),
+ },
+
+ cli.Command{
+ Name: "set-default",
+ ArgsUsage: i18n.G("<name>"),
+ Usage: i18n.G("Set the default remote."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteSetDefault),
+ },
+
+ cli.Command{
+ Name: "get-default",
+ ArgsUsage: i18n.G(""),
+ Usage: i18n.G("Print the default remote."),
+
+ Flags: commandGlobalFlags,
+ Action: commandWrapper(commandActionRemoteGetDefault),
+ },
+ },
}
-func (c *remoteCmd) showByDefault() bool {
- return true
+func remoteRemoveCertificate(config *lxd.Config, remote string) error {
+ delete(config.Remotes, remote)
+
+ certf := config.ServerCertPath(remote)
+ shared.Debugf("Trying to remove %s", certf)
+
+ return os.Remove(certf)
+}
+
+func commandActionRemoteAdd(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) < 2 {
+ return errArgs
+ }
+
+ if rc, ok := config.Remotes[args[0]]; ok {
+ return fmt.Errorf(i18n.G("remote %s exists as <%s>"), args[0], rc.Addr)
+ }
+
+ err := remoteAddServer(
+ config, args[0], args[1],
+ context.Bool("accept-certificate"),
+ context.String("password"),
+ context.Bool("public"),
+ )
+ if err != nil {
+ delete(config.Remotes, args[0])
+ remoteRemoveCertificate(config, args[0])
+ return err
+ }
+
+ return lxd.SaveConfig(config, commandConfigPath)
+}
+
+func commandActionRemoteRemove(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) != 1 {
+ return errArgs
+ }
+
+ rc, ok := config.Remotes[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[0])
+ }
+
+ if rc.Static {
+ return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[0])
+ }
+
+ if config.DefaultRemote == args[0] {
+ return fmt.Errorf(i18n.G("can't remove the default remote"))
+ }
+
+ delete(config.Remotes, args[0])
+
+ remoteRemoveCertificate(config, args[0])
+
+ return lxd.SaveConfig(config, commandConfigPath)
+}
+
+func commandActionRemoteList(config *lxd.Config, context *cli.Context) error {
+ data := [][]string{}
+ for name, rc := range config.Remotes {
+ strPublic := i18n.G("NO")
+ if rc.Public {
+ strPublic = i18n.G("YES")
+ }
+
+ strStatic := i18n.G("NO")
+ if rc.Static {
+ strStatic = i18n.G("YES")
+ }
+
+ if rc.Protocol == "" {
+ rc.Protocol = "lxd"
+ }
+
+ strName := name
+ if name == config.DefaultRemote {
+ strName = fmt.Sprintf("%s (%s)", name, i18n.G("default"))
+ }
+ data = append(data, []string{strName, rc.Addr, rc.Protocol, strPublic, strStatic})
+ }
+
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetAutoWrapText(false)
+ table.SetRowLine(true)
+ table.SetHeader([]string{
+ i18n.G("NAME"),
+ i18n.G("URL"),
+ i18n.G("PROTOCOL"),
+ i18n.G("PUBLIC"),
+ i18n.G("STATIC")})
+ sort.Sort(byName(data))
+ table.AppendBulk(data)
+ table.Render()
+
+ return nil
}
-func (c *remoteCmd) usage() string {
- return i18n.G(
- `Manage remote LXD servers.
-
-lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD] [--public] Add the remote <name> at <url>.
-lxc remote remove <name> Remove the remote <name>.
-lxc remote list List all remotes.
-lxc remote rename <old> <new> Rename remote <old> to <new>.
-lxc remote set-url <name> <url> Update <name>'s url to <url>.
-lxc remote set-default <name> Set the default remote.
-lxc remote get-default Print the default remote.`)
+func commandActionRemoteRename(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) != 2 {
+ return errArgs
+ }
+
+ rc, ok := config.Remotes[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[0])
+ }
+
+ if rc.Static {
+ return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[0])
+ }
+
+ if _, ok := config.Remotes[args[1]]; ok {
+ return fmt.Errorf(i18n.G("remote %s already exists"), args[1])
+ }
+
+ // Rename the certificate file
+ oldPath := filepath.Join(config.ConfigPath("servercerts"), fmt.Sprintf("%s.crt", args[0]))
+ newPath := filepath.Join(config.ConfigPath("servercerts"), fmt.Sprintf("%s.crt", args[1]))
+ if shared.PathExists(oldPath) {
+ err := os.Rename(oldPath, newPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ config.Remotes[args[1]] = rc
+ delete(config.Remotes, args[0])
+
+ if config.DefaultRemote == args[0] {
+ config.DefaultRemote = args[1]
+ }
+
+ return lxd.SaveConfig(config, commandConfigPath)
}
-func (c *remoteCmd) flags() {
- gnuflag.BoolVar(&c.acceptCert, "accept-certificate", false, i18n.G("Accept certificate"))
- gnuflag.StringVar(&c.password, "password", "", i18n.G("Remote admin password"))
- gnuflag.BoolVar(&c.public, "public", false, i18n.G("Public image server"))
+func commandActionRemoteSetURL(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) != 2 {
+ return errArgs
+ }
+ rc, ok := config.Remotes[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[0])
+ }
+
+ if rc.Static {
+ return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[0])
+ }
+
+ config.Remotes[args[0]] = lxd.RemoteConfig{Addr: args[1]}
+
+ return lxd.SaveConfig(config, commandConfigPath)
}
-func (c *remoteCmd) addServer(config *lxd.Config, server string, addr string, acceptCert bool, password string, public bool) error {
+func commandActionRemoteSetDefault(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ if len(args) != 1 {
+ return errArgs
+ }
+
+ _, ok := config.Remotes[args[0]]
+ if !ok {
+ return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[0])
+ }
+ config.DefaultRemote = args[0]
+
+ return lxd.SaveConfig(config, commandConfigPath)
+}
+
+func commandActionRemoteGetDefault(config *lxd.Config, context *cli.Context) error {
+ if len(context.Args()) != 0 {
+ return errArgs
+ }
+ fmt.Println(config.DefaultRemote)
+ return nil
+}
+
+func remoteAddServer(config *lxd.Config, server string, addr string, acceptCert bool, password string, public bool) error {
var rScheme string
var rHost string
var rPort string
@@ -229,168 +473,3 @@ func (c *remoteCmd) addServer(config *lxd.Config, server string, addr string, ac
fmt.Println(i18n.G("Client certificate stored at server: "), server)
return nil
}
-
-func (c *remoteCmd) removeCertificate(config *lxd.Config, remote string) {
- certf := config.ServerCertPath(remote)
- shared.Debugf("Trying to remove %s", certf)
-
- os.Remove(certf)
-}
-
-func (c *remoteCmd) run(config *lxd.Config, args []string) error {
- if len(args) < 1 {
- return errArgs
- }
-
- switch args[0] {
- case "add":
- if len(args) < 3 {
- return errArgs
- }
-
- if rc, ok := config.Remotes[args[1]]; ok {
- return fmt.Errorf(i18n.G("remote %s exists as <%s>"), args[1], rc.Addr)
- }
-
- err := c.addServer(config, args[1], args[2], c.acceptCert, c.password, c.public)
- if err != nil {
- delete(config.Remotes, args[1])
- c.removeCertificate(config, args[1])
- return err
- }
-
- case "remove":
- if len(args) != 2 {
- return errArgs
- }
-
- rc, ok := config.Remotes[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[1])
- }
-
- if rc.Static {
- return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[1])
- }
-
- if config.DefaultRemote == args[1] {
- return fmt.Errorf(i18n.G("can't remove the default remote"))
- }
-
- delete(config.Remotes, args[1])
-
- c.removeCertificate(config, args[1])
-
- case "list":
- data := [][]string{}
- for name, rc := range config.Remotes {
- strPublic := i18n.G("NO")
- if rc.Public {
- strPublic = i18n.G("YES")
- }
-
- strStatic := i18n.G("NO")
- if rc.Static {
- strStatic = i18n.G("YES")
- }
-
- if rc.Protocol == "" {
- rc.Protocol = "lxd"
- }
-
- strName := name
- if name == config.DefaultRemote {
- strName = fmt.Sprintf("%s (%s)", name, i18n.G("default"))
- }
- data = append(data, []string{strName, rc.Addr, rc.Protocol, strPublic, strStatic})
- }
-
- table := tablewriter.NewWriter(os.Stdout)
- table.SetAutoWrapText(false)
- table.SetRowLine(true)
- table.SetHeader([]string{
- i18n.G("NAME"),
- i18n.G("URL"),
- i18n.G("PROTOCOL"),
- i18n.G("PUBLIC"),
- i18n.G("STATIC")})
- sort.Sort(byName(data))
- table.AppendBulk(data)
- table.Render()
-
- return nil
-
- case "rename":
- if len(args) != 3 {
- return errArgs
- }
-
- rc, ok := config.Remotes[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[1])
- }
-
- if rc.Static {
- return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[1])
- }
-
- if _, ok := config.Remotes[args[2]]; ok {
- return fmt.Errorf(i18n.G("remote %s already exists"), args[2])
- }
-
- // Rename the certificate file
- oldPath := filepath.Join(config.ConfigPath("servercerts"), fmt.Sprintf("%s.crt", args[1]))
- newPath := filepath.Join(config.ConfigPath("servercerts"), fmt.Sprintf("%s.crt", args[2]))
- if shared.PathExists(oldPath) {
- err := os.Rename(oldPath, newPath)
- if err != nil {
- return err
- }
- }
-
- config.Remotes[args[2]] = rc
- delete(config.Remotes, args[1])
-
- if config.DefaultRemote == args[1] {
- config.DefaultRemote = args[2]
- }
-
- case "set-url":
- if len(args) != 3 {
- return errArgs
- }
-
- rc, ok := config.Remotes[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[1])
- }
-
- if rc.Static {
- return fmt.Errorf(i18n.G("remote %s is static and cannot be modified"), args[1])
- }
-
- config.Remotes[args[1]] = lxd.RemoteConfig{Addr: args[2]}
-
- case "set-default":
- if len(args) != 2 {
- return errArgs
- }
-
- _, ok := config.Remotes[args[1]]
- if !ok {
- return fmt.Errorf(i18n.G("remote %s doesn't exist"), args[1])
- }
- config.DefaultRemote = args[1]
-
- case "get-default":
- if len(args) != 1 {
- return errArgs
- }
- fmt.Println(config.DefaultRemote)
- return nil
- default:
- return errArgs
- }
-
- return lxd.SaveConfig(config, configPath)
-}
diff --git a/lxc/restore.go b/lxc/restore.go
index 440babf..9f053f0 100644
--- a/lxc/restore.go
+++ b/lxc/restore.go
@@ -3,39 +3,40 @@ package main
import (
"fmt"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type restoreCmd struct {
- stateful bool
-}
-
-func (c *restoreCmd) showByDefault() bool {
- return true
-}
+var commandRestore = cli.Command{
+ Name: "restore",
+ Usage: i18n.G("Set the current state of a resource back to what it was when it was snapshotted."),
+ ArgsUsage: i18n.G("[remote:]<resource> <snapshot name> [--stateful]"),
+ Description: i18n.G(`Set the current state of a resource back to a snapshot.
-func (c *restoreCmd) usage() string {
- return i18n.G(
- `Set the current state of a resource back to a snapshot.
+ lxc restore [remote:]<container> <snapshot name> [--stateful]
-lxc restore [remote:]<container> <snapshot name> [--stateful]
+ Restores a container from a snapshot (optionally with running state, see
+ snapshot help for details).
-Restores a container from a snapshot (optionally with running state, see
-snapshot help for details).
+ For example:
+ lxc snapshot u1 snap0 # create the snapshot
+ lxc restore u1 snap0 # restore the snapshot`),
-For example:
-lxc snapshot u1 snap0 # create the snapshot
-lxc restore u1 snap0 # restore the snapshot`)
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "stateful",
+ Usage: i18n.G("Whether or not to restore the container's running state from snapshot (if available)."),
+ },
+ ),
+ Action: commandWrapper(commmandActionRestore),
}
-func (c *restoreCmd) flags() {
- gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Whether or not to restore the container's running state from snapshot (if available)"))
-}
+func commmandActionRestore(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var stateful = context.Bool("stateful")
-func (c *restoreCmd) run(config *lxd.Config, args []string) error {
if len(args) < 2 {
return errArgs
}
@@ -52,7 +53,7 @@ func (c *restoreCmd) run(config *lxd.Config, args []string) error {
snapname = fmt.Sprintf("%s/%s", name, snapname)
}
- resp, err := d.RestoreSnapshot(name, snapname, c.stateful)
+ resp, err := d.RestoreSnapshot(name, snapname, stateful)
if err != nil {
return err
}
diff --git a/lxc/snapshot.go b/lxc/snapshot.go
index 7dad294..fd43a9a 100644
--- a/lxc/snapshot.go
+++ b/lxc/snapshot.go
@@ -3,42 +3,43 @@ package main
import (
"fmt"
+ "github.com/codegangsta/cli"
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
-type snapshotCmd struct {
- stateful bool
-}
-
-func (c *snapshotCmd) showByDefault() bool {
- return true
-}
+var commandSnapshot = cli.Command{
+ Name: "snapshot",
+ Usage: i18n.G("Create a read-only snapshot of a container."),
+ ArgsUsage: i18n.G("[remote:]<source> <snapshot name> [--stateful]"),
+ Description: i18n.G(`Create a read-only snapshot of a container.
-func (c *snapshotCmd) usage() string {
- return i18n.G(
- `Create a read-only snapshot of a container.
+ lxc snapshot [remote:]<source> <snapshot name> [--stateful]
-lxc snapshot [remote:]<source> <snapshot name> [--stateful]
+ Creates a snapshot of the container (optionally with the container's memory
+ state). When --stateful is used, LXD attempts to checkpoint the container's
+ running state, including process memory state, TCP connections, etc. so that it
+ can be restored (via lxc restore) at a later time (although some things, e.g.
+ TCP connections after the TCP timeout window has expired, may not be restored
+ successfully).
-Creates a snapshot of the container (optionally with the container's memory
-state). When --stateful is used, LXD attempts to checkpoint the container's
-running state, including process memory state, TCP connections, etc. so that it
-can be restored (via lxc restore) at a later time (although some things, e.g.
-TCP connections after the TCP timeout window has expired, may not be restored
-successfully).
+ Example:
+ lxc snapshot u1 snap0`),
-Example:
-lxc snapshot u1 snap0`)
+ Flags: append(commandGlobalFlags,
+ cli.BoolFlag{
+ Name: "stateful",
+ Usage: i18n.G("Whether or not to snapshot the container's running state."),
+ },
+ ),
+ Action: commandWrapper(commandActionSnapshot),
}
-func (c *snapshotCmd) flags() {
- gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Whether or not to snapshot the container's running state"))
-}
+func commandActionSnapshot(config *lxd.Config, context *cli.Context) error {
+ var args = context.Args()
+ var stateful = context.Bool("stateful")
-func (c *snapshotCmd) run(config *lxd.Config, args []string) error {
if len(args) < 1 {
return errArgs
}
@@ -61,7 +62,7 @@ func (c *snapshotCmd) run(config *lxd.Config, args []string) error {
return fmt.Errorf(i18n.G("'/' not allowed in snapshot name"))
}
- resp, err := d.Snapshot(name, snapname, c.stateful)
+ resp, err := d.Snapshot(name, snapname, stateful)
if err != nil {
return err
}
diff --git a/lxc/version.go b/lxc/version.go
deleted file mode 100644
index 8e5680c..0000000
--- a/lxc/version.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/lxc/lxd"
- "github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/i18n"
-)
-
-type versionCmd struct{}
-
-func (c *versionCmd) showByDefault() bool {
- return true
-}
-
-func (c *versionCmd) usage() string {
- return i18n.G(
- `Prints the version number of LXD.
-
-lxc version`)
-}
-
-func (c *versionCmd) flags() {
-}
-
-func (c *versionCmd) run(_ *lxd.Config, args []string) error {
- if len(args) > 0 {
- return errArgs
- }
- fmt.Println(shared.Version)
- return nil
-}
diff --git a/po/lxd.pot b/po/lxd.pot
index 50f539a..d0cb525 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: lxd\n"
"Report-Msgid-Bugs-To: lxc-devel at lists.linuxcontainers.org\n"
- "POT-Creation-Date: 2016-02-25 23:25-0500\n"
+ "POT-Creation-Date: 2016-02-28 01:59+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at li.org>\n"
@@ -16,7 +16,11 @@ msgstr "Project-Id-Version: lxd\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: lxc/config.go:37
+#: lxc/main.go:91 lxc/remote.go:73 lxc/remote.go:109
+msgid ""
+msgstr ""
+
+#: lxc/config.go:223
msgid "### This is a yaml representation of the configuration.\n"
"### Any line starting with a '# will be ignored.\n"
"###\n"
@@ -36,7 +40,7 @@ msgid "### This is a yaml representation of the configuration.\n"
"### Note that the name is shown but cannot be changed"
msgstr ""
-#: lxc/image.go:82
+#: lxc/image.go:628
msgid "### This is a yaml representation of the image properties.\n"
"### Any line starting with a '# will be ignored.\n"
"###\n"
@@ -45,7 +49,7 @@ msgid "### This is a yaml representation of the image properties.\n"
"### description: My custom image"
msgstr ""
-#: lxc/profile.go:27
+#: lxc/profile.go:285
msgid "### This is a yaml representation of the profile.\n"
"### Any line starting with a '# will be ignored.\n"
"###\n"
@@ -65,1032 +69,1394 @@ msgid "### This is a yaml representation of the profile.\n"
"### Note that the name is shown but cannot be changed"
msgstr ""
-#: lxc/image.go:535
+#: lxc/image.go:678
#, c-format
msgid "%s (%d more)"
msgstr ""
-#: lxc/snapshot.go:61
+#: lxc/snapshot.go:62
msgid "'/' not allowed in snapshot name"
msgstr ""
-#: lxc/profile.go:223
+#: lxc/profile.go:513
msgid "(none)"
msgstr ""
-#: lxc/image.go:555 lxc/image.go:579
+#: lxc/config.go:38
+msgid "<[remote:]container> <name> <type> [key=value]..."
+msgstr ""
+
+#: lxc/image.go:192
+msgid "<alias>"
+msgstr ""
+
+#: lxc/image.go:184
+msgid "<alias> <target>"
+msgstr ""
+
+#: lxc/profile.go:135
+msgid "<container> <profiles>"
+msgstr ""
+
+#: lxc/file.go:70
+msgid "<file>"
+msgstr ""
+
+#: lxc/remote.go:64 lxc/remote.go:100
+msgid "<name>"
+msgstr ""
+
+#: lxc/remote.go:91
+msgid "<name> <url>"
+msgstr ""
+
+#: lxc/remote.go:42
+msgid "<name> <url> [--accept-certificate] [--password=PASSWORD] [--public]"
+msgstr ""
+
+#: lxc/action.go:15 lxc/action.go:24 lxc/action.go:42
+msgid "<name> [<name>...]"
+msgstr ""
+
+#: lxc/remote.go:82
+msgid "<old> <new>"
+msgstr ""
+
+#: lxc/profile.go:183
+msgid "<profile name> <device name> <device type> [key=value]..."
+msgstr ""
+
+#: lxc/profile.go:63 lxc/profile.go:72 lxc/profile.go:81 lxc/profile.go:126 lxc/profile.go:159 lxc/profile.go:167
+msgid "<profile>"
+msgstr ""
+
+#: lxc/profile.go:99 lxc/profile.go:117
+msgid "<profile> <key>"
+msgstr ""
+
+#: lxc/profile.go:108
+msgid "<profile> <key> <value>"
+msgstr ""
+
+#: lxc/profile.go:175
+msgid "<profile> <name>"
+msgstr ""
+
+#: lxc/profile.go:90
+msgid "<profile> <remote>"
+msgstr ""
+
+#: lxc/finger.go:13
+msgid "<remote>"
+msgstr ""
+
+#: lxc/file.go:36
+msgid "<source> [<source>...] <target>"
+msgstr ""
+
+#: lxc/image.go:84
+msgid "<tarball> [rootfs tarball|URL] [remote:] [--public] [--alias=ALIAS]... [prop=value]..."
+msgstr ""
+
+#: lxc/image.go:698 lxc/image.go:722
msgid "ALIAS"
msgstr ""
-#: lxc/image.go:559
+#: lxc/image.go:702
msgid "ARCH"
msgstr ""
-#: lxc/list.go:334
+#: lxc/list.go:345
msgid "ARCHITECTURE"
msgstr ""
-#: lxc/remote.go:50
-msgid "Accept certificate"
+#: lxc/remote.go:48
+msgid "Accept certificate."
+msgstr ""
+
+#: lxc/config.go:39
+msgid "Add a device to a container."
+msgstr ""
+
+#: lxc/profile.go:184
+msgid "Add a profile device, such as a disk or a nic, to\n"
+ " the containers using the specified profile."
msgstr ""
-#: lxc/remote.go:206
+#: lxc/config.go:98
+msgid "Add certfile.crt to trusted hosts."
+msgstr ""
+
+#: lxc/remote.go:43
+msgid "Add the remote <name> at <url>."
+msgstr ""
+
+#: lxc/remote.go:450
#, c-format
msgid "Admin password for %s: "
msgstr ""
-#: lxc/image.go:315
+#: lxc/image.go:376
msgid "Aliases:"
msgstr ""
-#: lxc/exec.go:54
-msgid "An environment variable of the form HOME=/home/foo"
+#: lxc/image.go:112
+msgid "Also copy aliases."
msgstr ""
-#: lxc/image.go:298 lxc/info.go:87
-#, c-format
-msgid "Architecture: %s"
+#: lxc/info.go:23
+msgid "Also show the log."
+msgstr ""
+
+#: lxc/main.go:62
+msgid "Alternate config directory."
msgstr ""
-#: lxc/help.go:49
-msgid "Available commands:"
+#: lxc/image.go:94 lxc/image.go:108
+msgid "An alias for this image."
msgstr ""
-#: lxc/config.go:268
+#: lxc/exec.go:31
+msgid "An environment variable of the form HOME=/home/foo."
+msgstr ""
+
+#: lxc/profile.go:136
+msgid "Apply a comma-separated list of profiles to a container."
+msgstr ""
+
+#: lxc/profile.go:137
+msgid "Apply a comma-separated list of profiles to a container.\n"
+ "\n"
+ " Apply a comma-separated list of profiles to a container, in order.\n"
+ " All profiles passed in this call (and only those) will be applied\n"
+ " to the specified container.\n"
+ " Example: lxc profile apply foo default,bar # Apply default and bar\n"
+ " lxc profile apply foo default # Only default is active\n"
+ " lxc profile apply '' # no profiles are applied anymore\n"
+ " lxc profile apply bar,default # Apply default second now\n"
+msgstr ""
+
+#: lxc/image.go:359 lxc/info.go:92
+#, c-format
+msgid "Architecture: %s"
+msgstr ""
+
+#: lxc/config.go:523
msgid "COMMON NAME"
msgstr ""
-#: lxc/list.go:335
+#: lxc/list.go:346
msgid "CREATED AT"
msgstr ""
-#: lxc/config.go:113
+#: lxc/config.go:261
#, c-format
msgid "Can't read from stdin: %s"
msgstr ""
-#: lxc/config.go:126 lxc/config.go:159 lxc/config.go:181
+#: lxc/config.go:274 lxc/config.go:302 lxc/config.go:324
#, c-format
msgid "Can't unset key '%s', it's not currently set."
msgstr ""
-#: lxc/profile.go:334
+#: lxc/profile.go:201
msgid "Cannot provide container name to list"
msgstr ""
-#: lxc/remote.go:156
+#: lxc/remote.go:400
#, c-format
msgid "Certificate fingerprint: %x"
msgstr ""
-#: lxc/action.go:26
-#, c-format
-msgid "Changes state of one or more containers to %s.\n"
- "\n"
- "lxc %s <name> [<name>...]"
+#: lxc/action.go:41
+msgid "Changes one or more containers state to restart."
+msgstr ""
+
+#: lxc/action.go:14
+msgid "Changes one or more containers state to start."
+msgstr ""
+
+#: lxc/action.go:23
+msgid "Changes one or more containers state to stop."
msgstr ""
-#: lxc/remote.go:229
+#: lxc/remote.go:473
msgid "Client certificate stored at server: "
msgstr ""
-#: lxc/list.go:89 lxc/list.go:90
-msgid "Columns"
+#: lxc/list.go:51
+msgid "Columns."
msgstr ""
-#: lxc/init.go:134 lxc/init.go:135 lxc/launch.go:40 lxc/launch.go:41
-msgid "Config key/value to apply to the new container"
+#: lxc/init.go:50 lxc/launch.go:40
+msgid "Config key/value to apply to the new container."
msgstr ""
-#: lxc/config.go:492 lxc/config.go:557 lxc/image.go:633 lxc/profile.go:187
+#: lxc/config.go:638 lxc/config.go:703 lxc/image.go:776 lxc/profile.go:360
#, c-format
msgid "Config parsing error: %s"
msgstr ""
-#: lxc/main.go:37
+#: lxc/main.go:205
msgid "Connection refused; is LXD running?"
msgstr ""
-#: lxc/publish.go:59
+#: lxc/publish.go:64
msgid "Container name is mandatory"
msgstr ""
-#: lxc/init.go:208
+#: lxc/init.go:192
#, c-format
msgid "Container name is: %s"
msgstr ""
-#: lxc/publish.go:141 lxc/publish.go:156
+#: lxc/publish.go:146 lxc/publish.go:161
#, c-format
msgid "Container published with fingerprint: %s"
msgstr ""
-#: lxc/image.go:151
-msgid "Copy aliases from source"
+#: lxc/image.go:103
+msgid "Copy an image to another destination."
msgstr ""
-#: lxc/copy.go:22
-msgid "Copy containers within or in between lxd instances.\n"
- "\n"
- "lxc copy [remote:]<source container> [remote:]<destination container> [--ephemeral|e]"
+#: lxc/copy.go:15
+msgid "Copy containers within or in between lxd instances."
msgstr ""
-#: lxc/image.go:246
+#: lxc/profile.go:91
+msgid "Copy the profile to the specified remote."
+msgstr ""
+
+#: lxc/image.go:299
#, c-format
msgid "Copying the image: %s"
msgstr ""
-#: lxc/remote.go:171
+#: lxc/remote.go:415
msgid "Could not create server cert dir"
msgstr ""
-#: lxc/snapshot.go:21
+#: lxc/copy.go:20
+msgid "Create a ephemeral copy."
+msgstr ""
+
+#: lxc/profile.go:73
+msgid "Create a profile."
+msgstr ""
+
+#: lxc/snapshot.go:14
+msgid "Create a read-only snapshot of a container."
+msgstr ""
+
+#: lxc/snapshot.go:16
msgid "Create a read-only snapshot of a container.\n"
"\n"
- "lxc snapshot [remote:]<source> <snapshot name> [--stateful]\n"
+ " lxc snapshot [remote:]<source> <snapshot name> [--stateful]\n"
"\n"
- "Creates a snapshot of the container (optionally with the container's memory\n"
- "state). When --stateful is used, LXD attempts to checkpoint the container's\n"
- "running state, including process memory state, TCP connections, etc. so that it\n"
- "can be restored (via lxc restore) at a later time (although some things, e.g.\n"
- "TCP connections after the TCP timeout window has expired, may not be restored\n"
- "successfully).\n"
+ " Creates a snapshot of the container (optionally with the container's memory\n"
+ " state). When --stateful is used, LXD attempts to checkpoint the container's\n"
+ " running state, including process memory state, TCP connections, etc. so that it\n"
+ " can be restored (via lxc restore) at a later time (although some things, e.g.\n"
+ " TCP connections after the TCP timeout window has expired, may not be restored\n"
+ " successfully).\n"
"\n"
- "Example:\n"
- "lxc snapshot u1 snap0"
+ " Example:\n"
+ " lxc snapshot u1 snap0"
msgstr ""
-#: lxc/image.go:303 lxc/info.go:89
+#: lxc/image.go:185
+msgid "Create an alias."
+msgstr ""
+
+#: lxc/image.go:364 lxc/info.go:94
#, c-format
msgid "Created: %s"
msgstr ""
-#: lxc/init.go:177 lxc/launch.go:116
+#: lxc/init.go:160 lxc/launch.go:139
#, c-format
msgid "Creating %s"
msgstr ""
-#: lxc/init.go:175
+#: lxc/init.go:158
msgid "Creating the container"
msgstr ""
-#: lxc/image.go:558 lxc/image.go:581
+#: lxc/image.go:701 lxc/image.go:724
msgid "DESCRIPTION"
msgstr ""
-#: lxc/delete.go:25
-msgid "Delete containers or container snapshots.\n"
- "\n"
- "lxc delete [remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...]\n"
- "\n"
- "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)."
+#: lxc/profile.go:127
+msgid "Delete a profile."
msgstr ""
-#: lxc/config.go:605
+#: lxc/image.go:193
+msgid "Delete an alias."
+msgstr ""
+
+#: lxc/image.go:125
+msgid "Delete an image."
+msgstr ""
+
+#: lxc/delete.go:17
+msgid "Delete containers or container snapshots."
+msgstr ""
+
+#: lxc/delete.go:19
+msgid "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)."
+msgstr ""
+
+#: lxc/config.go:751
#, c-format
msgid "Device %s added to %s"
msgstr ""
-#: lxc/config.go:633
+#: lxc/config.go:779
#, c-format
msgid "Device %s removed from %s"
msgstr ""
-#: lxc/list.go:418
+#: lxc/config.go:25
+msgid "Device manipulation."
+msgstr ""
+
+#: lxc/config.go:26
+msgid "Device manipulation\n"
+ "\n"
+ " lxc config device add <[remote:]container> <name> <type> [key=value]...\n"
+ " Add a device to a container\n"
+ " lxc config device list [remote:]<container> List devices for container\n"
+ " lxc config device show [remote:]<container> Show full device details for container\n"
+ " lxc config device remove [remote:]<container> <name> Remove device from container\n"
+msgstr ""
+
+#: lxc/list.go:429
msgid "EPHEMERAL"
msgstr ""
-#: lxc/config.go:270
+#: lxc/config.go:525
msgid "EXPIRY DATE"
msgstr ""
-#: lxc/main.go:55
-msgid "Enables debug mode."
+#: lxc/file.go:71
+msgid "Edit a file from a container."
msgstr ""
-#: lxc/main.go:54
-msgid "Enables verbose mode."
+#: lxc/profile.go:82
+msgid "Edit a profile."
msgstr ""
-#: lxc/help.go:68
-msgid "Environment:"
+#: lxc/image.go:170
+msgid "Edit an image."
msgstr ""
-#: lxc/copy.go:29 lxc/copy.go:30 lxc/init.go:138 lxc/init.go:139 lxc/launch.go:44 lxc/launch.go:45
-msgid "Ephemeral container"
+#: lxc/config.go:160
+msgid "Edit container configuration in external editor."
msgstr ""
-#: lxc/monitor.go:56
+#: lxc/init.go:44
+msgid "Ephemeral container."
+msgstr ""
+
+#: lxc/launch.go:35
+msgid "Ephemeral."
+msgstr ""
+
+#: lxc/monitor.go:21
msgid "Event type to listen for"
msgstr ""
-#: lxc/exec.go:45
+#: lxc/exec.go:22
+msgid "Execute the specified command in a container."
+msgstr ""
+
+#: lxc/exec.go:24
msgid "Execute the specified command in a container.\n"
"\n"
- "lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>\n"
- "\n"
- "Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored)."
+ " Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored)."
msgstr ""
-#: lxc/image.go:307
+#: lxc/image.go:368
#, c-format
msgid "Expires: %s"
msgstr ""
-#: lxc/image.go:309
+#: lxc/image.go:370
msgid "Expires: never"
msgstr ""
-#: lxc/config.go:267 lxc/image.go:556 lxc/image.go:580
+#: lxc/image.go:134
+msgid "Export an image."
+msgstr ""
+
+#: lxc/config.go:522 lxc/image.go:699 lxc/image.go:723
msgid "FINGERPRINT"
msgstr ""
-#: lxc/list.go:91
-msgid "Fast mode (same as --columns=nsacPt"
+#: lxc/list.go:55
+msgid "Fast mode (same as --columns=nsacPt."
msgstr ""
-#: lxc/image.go:290
+#: lxc/image.go:351
#, c-format
msgid "Fingerprint: %s"
msgstr ""
-#: lxc/finger.go:17
-msgid "Fingers the LXD instance to check if it is up and working.\n"
- "\n"
- "lxc finger <remote>"
+#: lxc/finger.go:12
+msgid "Fingers the LXD instance to check if it is up and working."
msgstr ""
-#: lxc/main.go:146
+#: lxc/main.go:183
msgid "For example: 'lxd-images import ubuntu --alias ubuntu'."
msgstr ""
-#: lxc/action.go:35
+#: lxc/action.go:29 lxc/action.go:47
msgid "Force the container to shutdown."
msgstr ""
-#: lxc/delete.go:34 lxc/delete.go:35
+#: lxc/delete.go:24
msgid "Force the removal of stopped containers."
msgstr ""
-#: lxc/main.go:56
+#: lxc/main.go:51
msgid "Force using the local unix socket."
msgstr ""
-#: lxc/main.go:138
+#: lxc/main.go:175
msgid "Generating a client certificate. This may take a minute..."
msgstr ""
-#: lxc/list.go:332
+#: lxc/file.go:37
+msgid "Get a file from a container."
+msgstr ""
+
+#: lxc/config.go:169
+msgid "Get configuration key."
+msgstr ""
+
+#: lxc/image.go:143
+msgid "Get informations form an image."
+msgstr ""
+
+#: lxc/profile.go:100
+msgid "Get profile configuration."
+msgstr ""
+
+#: lxc/list.go:343
msgid "IPV4"
msgstr ""
-#: lxc/list.go:333
+#: lxc/list.go:344
msgid "IPV6"
msgstr ""
-#: lxc/config.go:269
+#: lxc/config.go:524
msgid "ISSUE DATE"
msgstr ""
-#: lxc/main.go:145
+#: lxc/main.go:182
msgid "If this is your first run, you will need to import images using the 'lxd-images' script."
msgstr ""
-#: lxc/main.go:57
-msgid "Ignore aliases when determining what command to run."
-msgstr ""
-
-#: lxc/image.go:251
+#: lxc/image.go:304
msgid "Image copied successfully!"
msgstr ""
-#: lxc/image.go:373
+#: lxc/image.go:266
#, c-format
msgid "Image imported with fingerprint: %s"
msgstr ""
-#: lxc/init.go:73
-msgid "Initialize a container from a particular image.\n"
- "\n"
- "lxc init [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]\n"
- "\n"
- "Initializes a container using the specified image and name.\n"
- "\n"
- "Not specifying -p will result in the default profile.\n"
- "Specifying \"-p\" with no argument will result in no profile.\n"
- "\n"
- "Example:\n"
- "lxc init ubuntu u1"
+#: lxc/image.go:85
+msgid "Import an image tarball (or tarballs) into the LXD image store."
+msgstr ""
+
+#: lxc/init.go:16
+msgid "Initializes a container using the specified image and name."
msgstr ""
-#: lxc/init.go:30 lxc/init.go:35
+#: lxc/init.go:87 lxc/init.go:92
msgid "Invalid configuration key"
msgstr ""
-#: lxc/file.go:186
+#: lxc/file.go:241
#, c-format
msgid "Invalid source %s"
msgstr ""
-#: lxc/file.go:57
+#: lxc/file.go:112
#, c-format
msgid "Invalid target %s"
msgstr ""
-#: lxc/info.go:116
+#: lxc/info.go:121
msgid "Ips:"
msgstr ""
-#: lxc/main.go:35
+#: lxc/main.go:203
msgid "LXD socket not found; is LXD running?"
msgstr ""
-#: lxc/launch.go:22
-msgid "Launch a container from a particular image.\n"
- "\n"
- "lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]\n"
- "\n"
- "Launches a container using the specified image and name.\n"
- "\n"
- "Not specifying -p will result in the default profile.\n"
- "Specifying \"-p\" with no argument will result in no profile.\n"
- "\n"
- "Example:\n"
- "lxc launch ubuntu u1"
+#: lxc/launch.go:15
+msgid "Launch a container from a particular image."
msgstr ""
-#: lxc/info.go:24
-msgid "List information on containers.\n"
- "\n"
- "This will support remotes and images as well, but only containers for now.\n"
+#: lxc/launch.go:17
+msgid "Launches a container using the specified image and name.\n"
"\n"
- "lxc info [<remote>:]container [--show-log]"
+ " Not specifying -p will result in the default profile.\n"
+ " Specifying \"-p\" with no argument will result in no profile."
+msgstr ""
+
+#: lxc/image.go:201
+msgid "List aliases."
+msgstr ""
+
+#: lxc/remote.go:74
+msgid "List all remotes."
+msgstr ""
+
+#: lxc/config.go:89
+msgid "List all trusted certs."
+msgstr ""
+
+#: lxc/profile.go:55
+msgid "List available profiles."
+msgstr ""
+
+#: lxc/config.go:48
+msgid "List devices for container."
+msgstr ""
+
+#: lxc/profile.go:160
+msgid "List devices in the given profile."
msgstr ""
-#: lxc/list.go:60
+#: lxc/image.go:152
+msgid "List images."
+msgstr ""
+
+#: lxc/info.go:17
+msgid "List information on containers."
+msgstr ""
+
+#: lxc/list.go:22
+msgid "Lists the available resources."
+msgstr ""
+
+#: lxc/list.go:23
msgid "Lists the available resources.\n"
"\n"
- "lxc list [resource] [filters] [-c columns] [--fast]\n"
- "\n"
- "The filters are:\n"
- "* A single keyword like \"web\" which will list any container with \"web\" in its name.\n"
- "* A key/value pair referring to a configuration item. For those, the namespace can be abreviated to the smallest unambiguous identifier:\n"
- "* \"user.blah=abc\" will list all containers with the \"blah\" user property set to \"abc\"\n"
- "* \"u.blah=abc\" will do the same\n"
- "* \"security.privileged=1\" will list all privileged containers\n"
- "* \"s.privileged=1\" will do the same\n"
- "\n"
- "The columns are:\n"
- "* 4 - IPv4 address\n"
- "* 6 - IPv6 address\n"
- "* a - architecture\n"
- "* c - creation date\n"
- "* n - name\n"
- "* p - pid of container init process\n"
- "* P - profiles\n"
- "* s - state\n"
- "* t - type (persistent or ephemeral)\n"
- "\n"
- "Default column layout: ns46tS\n"
- "Fast column layout: nsacPt"
+ " The filters are:\n"
+ " * A single keyword like \"web\" which will list any container with \"web\" in its name.\n"
+ " * A key/value pair referring to a configuration item. For those, the namespace can be abreviated to the smallest unambiguous identifier:\n"
+ " * \"user.blah=abc\" will list all containers with the \"blah\" user property set to \"abc\"\n"
+ " * \"u.blah=abc\" will do the same\n"
+ " * \"security.privileged=1\" will list all privileged containers\n"
+ " * \"s.privileged=1\" will do the same\n"
+ "\n"
+ " The columns are:\n"
+ " * 4 - IPv4 address\n"
+ " * 6 - IPv6 address\n"
+ " * a - architecture\n"
+ " * c - creation date\n"
+ " * n - name\n"
+ " * p - pid of container init process\n"
+ " * P - profiles\n"
+ " * s - state\n"
+ " * t - type (persistent or ephemeral)\n"
+ "\n"
+ " Default column layout: ns46tS\n"
+ " Fast column layout: nsacPt"
+msgstr ""
+
+#: lxc/info.go:164
+msgid "Log:"
msgstr ""
-#: lxc/info.go:159
-msgid "Log:"
+#: lxc/main.go:46
+msgid "Magic flag to run lxc without alias support."
msgstr ""
-#: lxc/image.go:150
-msgid "Make image public"
+#: lxc/image.go:90 lxc/image.go:116
+msgid "Make image public."
msgstr ""
#: lxc/publish.go:32
-msgid "Make the image public"
+msgid "Make the image public."
msgstr ""
-#: lxc/profile.go:48
-msgid "Manage configuration profiles.\n"
- "\n"
- "lxc profile list [filters] List available profiles.\n"
- "lxc profile show <profile> Show details of a profile.\n"
- "lxc profile create <profile> Create a profile.\n"
- "lxc profile copy <profile> <remote> Copy the profile to the specified remote.\n"
- "lxc profile get <profile> <key> Get profile configuration.\n"
- "lxc profile set <profile> <key> <value> Set profile configuration.\n"
- "lxc profile delete <profile> Delete a profile.\n"
- "lxc profile edit <profile>\n"
- " Edit profile, either by launching external editor or reading STDIN.\n"
- " Example: lxc profile edit <profile> # launch editor\n"
- " cat profile.yml | lxc profile edit <profile> # read from profile.yml\n"
- "lxc profile apply <container> <profiles>\n"
- " Apply a comma-separated list of profiles to a container, in order.\n"
- " All profiles passed in this call (and only those) will be applied\n"
- " to the specified container.\n"
- " Example: lxc profile apply foo default,bar # Apply default and bar\n"
- " lxc profile apply foo default # Only default is active\n"
- " lxc profile apply '' # no profiles are applied anymore\n"
- " lxc profile apply bar,default # Apply default second now\n"
- "\n"
- "Devices:\n"
- "lxc profile device list <profile> List devices in the given profile.\n"
- "lxc profile device show <profile> Show full device details in the given profile.\n"
- "lxc profile device remove <profile> <name> Remove a device from a profile.\n"
- "lxc profile device add <profile name> <device name> <device type> [key=value]...\n"
- " Add a profile device, such as a disk or a nic, to the containers\n"
- " using the specified profile."
+#: lxc/profile.go:21
+msgid "Manage configuration profiles."
msgstr ""
-#: lxc/config.go:58
+#: lxc/profile.go:22
+msgid "Manage configuration profiles.\n"
+ "\n"
+ " lxc profile list [filters] List available profiles\n"
+ " lxc profile show <profile> Show details of a profile\n"
+ " lxc profile create <profile> Create a profile\n"
+ " lxc profile edit <profile> Edit profile in external editor\n"
+ " lxc profile copy <profile> <remote> Copy the profile to the specified remote\n"
+ " lxc profile get <profile> <key> Get profile configuration\n"
+ " lxc profile set <profile> <key> <value> Set profile configuration\n"
+ " lxc profile unset <profile> <key> Unset profile configuration\n"
+ " lxc profile delete <profile> Delete a profile\n"
+ " lxc profile apply <container> <profiles>\n"
+ " Apply a comma-separated list of profiles to a container, in order.\n"
+ " All profiles passed in this call (and only those) will be applied\n"
+ " to the specified container.\n"
+ " Example: lxc profile apply foo default,bar # Apply default and bar\n"
+ " lxc profile apply foo default # Only default is active\n"
+ " lxc profile apply '' # no profiles are applied anymore\n"
+ " lxc profile apply bar,default # Apply default second now\n"
+ "\n"
+ " Devices:\n"
+ " lxc profile device list <profile> List devices in the given profile.\n"
+ " lxc profile device show <profile> Show full device details in the given profile.\n"
+ " lxc profile device remove <profile> <name> Remove a device from a profile.\n"
+ " lxc profile device add <profile name> <device name> <device type> [key=value]...\n"
+ " Add a profile device, such as a disk or a nic, to the containers\n"
+ " using the specified profile."
+msgstr ""
+
+#: lxc/config.go:117
+msgid "Manage configuration."
+msgstr ""
+
+#: lxc/config.go:118
msgid "Manage configuration.\n"
"\n"
- "lxc config device add <[remote:]container> <name> <type> [key=value]... Add a device to a container.\n"
- "lxc config device list [remote:]<container> List devices for container.\n"
- "lxc config device show [remote:]<container> Show full device details for container.\n"
- "lxc config device remove [remote:]<container> <name> Remove device from container.\n"
+ " lxc config device add <[remote:]container> <name> <type> [key=value]... Add a device to a container.\n"
+ " lxc config device list [remote:]<container> List devices for container.\n"
+ " lxc config device show [remote:]<container> Show full device details for container.\n"
+ " lxc config device remove [remote:]<container> <name> Remove device from container.\n"
"\n"
- "lxc config get [remote:]<container> key Get configuration key.\n"
- "lxc config set [remote:]<container> key value Set container configuration key.\n"
- "lxc config unset [remote:]<container> key Unset container configuration key.\n"
- "lxc config set key value Set server configuration key.\n"
- "lxc config unset key Unset server configuration key.\n"
- "lxc config show [--expanded] [remote:]<container> Show container configuration.\n"
- "lxc config edit [remote:][container] Edit container configuration in external editor.\n"
- " Edit configuration, either by launching external editor or reading STDIN.\n"
- " Example: lxc config edit <container> # launch editor\n"
- " cat config.yml | lxc config edit <config> # read from config.yml\n"
+ " lxc config get [remote:]<container> key Get configuration key.\n"
+ " lxc config set [remote:]<container> key value Set container configuration key.\n"
+ " lxc config unset [remote:]<container> key Unset container configuration key.\n"
+ " lxc config set key value Set server configuration key.\n"
+ " lxc config unset key Unset server configuration key.\n"
+ " lxc config show [--expanded] [remote:]<container> Show container configuration.\n"
+ " lxc config edit [remote:][container] Edit container configuration in external editor.\n"
+ " Edit configuration, either by launching external editor or reading STDIN.\n"
+ " Example: lxc config edit <container> # launch editor\n"
+ " cat config.yml | lxc config edit <config> # read from config.yml\n"
"\n"
- "lxc config trust list [remote] List all trusted certs.\n"
- "lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.\n"
- "lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts.\n"
+ " lxc config trust list [remote] List all trusted certs.\n"
+ " lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.\n"
+ " lxc config trust remove [remote] [hostname|fingerprint] Remove the cert from trusted hosts.\n"
"\n"
- "Examples:\n"
- "To mount host's /share/c1 onto /opt in the container:\n"
- " lxc config device add [remote:]container1 <device-name> disk source=/share/c1 path=opt\n"
+ " Examples:\n"
+ " To mount host's /share/c1 onto /opt in the container:\n"
+ " lxc config device add [remote:]container1 <device-name> disk source=/share/c1 path=opt\n"
"\n"
- "To set an lxc config value:\n"
- " lxc config set [remote:]<container> raw.lxc 'lxc.aa_allow_incomplete = 1'\n"
+ " To set an lxc config value:\n"
+ " lxc config set [remote:]<container> raw.lxc 'lxc.aa_allow_incomplete = 1'\n"
"\n"
- "To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default):\n"
- " lxc config set core.https_address [::]:8443\n"
+ " To listen on IPv4 and IPv6 port 8443 (you can omit the 8443 its the default):\n"
+ " lxc config set core.https_address [::]:8443\n"
"\n"
- "To set the server trust password:\n"
- " lxc config set core.trust_password blah"
+ " To set the server trust password:\n"
+ " lxc config set core.trust_password blah"
+msgstr ""
+
+#: lxc/file.go:23
+msgid "Manage files on a container."
msgstr ""
-#: lxc/file.go:32
+#: lxc/file.go:24
msgid "Manage files on a container.\n"
"\n"
- "lxc file pull <source> [<source>...] <target>\n"
- "lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>\n"
- "lxc file edit <file>\n"
+ " lxc file pull <source> [<source>...] <target>\n"
+ " lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>\n"
+ " lxc file edit <file>\n"
"\n"
- "<source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path>"
+ " <source> in the case of pull and <target> in the case of push are <container name>/<path>"
msgstr ""
-#: lxc/remote.go:37
+#: lxc/remote.go:27
+msgid "Manage remote LXD servers."
+msgstr ""
+
+#: lxc/remote.go:28
msgid "Manage remote LXD servers.\n"
"\n"
- "lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD] [--public] Add the remote <name> at <url>.\n"
- "lxc remote remove <name> Remove the remote <name>.\n"
- "lxc remote list List all remotes.\n"
- "lxc remote rename <old> <new> Rename remote <old> to <new>.\n"
- "lxc remote set-url <name> <url> Update <name>'s url to <url>.\n"
- "lxc remote set-default <name> Set the default remote.\n"
- "lxc remote get-default Print the default remote."
+ " lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD] [--public] Add the remote <name> at <url>.\n"
+ " lxc remote remove <name> Remove the remote <name>.\n"
+ " lxc remote list List all remotes.\n"
+ " lxc remote rename <old> <new> Rename remote <old> to <new>.\n"
+ " lxc remote set-url <name> <url> Update <name>'s url to <url>.\n"
+ " lxc remote set-default <name> Set the default remote.\n"
+ " lxc remote get-default Print the default remote."
msgstr ""
-#: lxc/image.go:92
+#: lxc/image.go:178
+msgid "Manipulate aliases."
+msgstr ""
+
+#: lxc/image.go:24
+msgid "Manipulate container images."
+msgstr ""
+
+#: lxc/image.go:25
msgid "Manipulate container images.\n"
"\n"
- "In LXD containers are created from images. Those images were themselves\n"
- "either generated from an existing container or downloaded from an image\n"
- "server.\n"
+ " In LXD containers are created from images. Those images were themselves\n"
+ " either generated from an existing container or downloaded from an image\n"
+ " server.\n"
"\n"
- "When using remote images, LXD will automatically cache images for you\n"
- "and remove them upon expiration.\n"
+ " When using remote images, LXD will automatically cache images for you\n"
+ " and remove them upon expiration.\n"
"\n"
- "The image unique identifier is the hash (sha-256) of its representation\n"
- "as a compressed tarball (or for split images, the concatenation of the\n"
- "metadata and rootfs tarballs).\n"
+ " The image unique identifier is the hash (sha-256) of its representation\n"
+ " as a compressed tarball (or for split images, the concatenation of the\n"
+ " metadata and rootfs tarballs).\n"
"\n"
- "Images can be referenced by their full hash, shortest unique partial\n"
- "hash or alias name (if one is set).\n"
+ " Images can be referenced by their full hash, shortest unique partial\n"
+ " hash or alias name (if one is set).\n"
"\n"
"\n"
- "lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--created-at=ISO-8601] [--expires-at=ISO-8601] [--fingerprint=FINGERPRINT] [prop=value]\n"
- " Import an image tarball (or tarballs) into the LXD image store.\n"
+ " lxc image import <tarball> [rootfs tarball|URL] [remote:] [--public] [--alias=ALIAS]... [prop=value]...\n"
+ " Import an image tarball (or tarballs) into the LXD image store.\n"
"\n"
- "lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public]\n"
- " Copy an image from one LXD daemon to another over the network.\n"
+ " lxc image copy [remote:]<image> <remote>: [--alias=ALIAS].. [--copy-aliases] [--public]\n"
+ " Copy an image from one LXD daemon to another over the network.\n"
"\n"
- "lxc image delete [remote:]<image>\n"
- " Delete an image from the LXD image store.\n"
+ " lxc image delete [remote:]<image>\n"
+ " Delete an image from the LXD image store.\n"
"\n"
- "lxc image export [remote:]<image>\n"
- " Export an image from the LXD image store into a distributable tarball.\n"
+ " lxc image export [remote:]<image>\n"
+ " Export an image from the LXD image store into a distributable tarball.\n"
"\n"
- "lxc image info [remote:]<image>\n"
- " Print everything LXD knows about a given image.\n"
+ " lxc image info [remote:]<image>\n"
+ " Print everything LXD knows about a given image.\n"
"\n"
- "lxc image list [remote:] [filter]\n"
- " List images in the LXD image store. Filters may be of the\n"
- " <key>=<value> form for property based filtering, or part of the image\n"
- " hash or part of the image alias name.\n"
+ " lxc image list [remote:] [filter]\n"
+ " List images in the LXD image store. Filters may be of the\n"
+ " <key>=<value> form for property based filtering, or part of the image\n"
+ " hash or part of the image alias name.\n"
"\n"
- "lxc image show [remote:]<image>\n"
- " Yaml output of the user modifiable properties of an image.\n"
+ " lxc image show [remote:]<image>\n"
+ " Yaml output of the user modifiable properties of an image.\n"
"\n"
- "lxc image edit [remote:]<image>\n"
- " Edit image, either by launching external editor or reading STDIN.\n"
- " Example: lxc image edit <image> # launch editor\n"
- " cat image.yml | lxc image edit <image> # read from image.yml\n"
+ " lxc image edit [remote:]<image>\n"
+ " Edit image, either by launching external editor or reading STDIN.\n"
+ " Example: lxc image edit <image> # launch editor\n"
+ " cat image.yml | lxc image edit <image> # read from image.yml\n"
"\n"
- "lxc image alias create [remote:]<alias> <fingerprint>\n"
- " Create a new alias for an existing image.\n"
+ " lxc image alias create [remote:]<alias> <fingerprint>\n"
+ " Create a new alias for an existing image.\n"
"\n"
- "lxc image alias delete [remote:]<alias>\n"
- " Delete an alias.\n"
+ " lxc image alias delete [remote:]<alias>\n"
+ " Delete an alias.\n"
"\n"
- "lxc image alias list [remote:]\n"
- " List the aliases.\n"
+ " lxc image alias list [remote:]\n"
+ " List the aliases."
msgstr ""
-#: lxc/help.go:86
-msgid "Missing summary."
+#: lxc/monitor.go:15
+msgid "Monitor activity on the LXD server."
msgstr ""
-#: lxc/monitor.go:41
-msgid "Monitor activity on the LXD server.\n"
- "\n"
- "lxc monitor [remote:] [--type=TYPE...]\n"
- "\n"
- "Connects to the monitoring interface of the specified LXD server.\n"
- "\n"
- "By default will listen to all message types.\n"
- "Specific types to listen to can be specified with --type.\n"
- "\n"
- "Example:\n"
- "lxc monitor --type=logging"
-msgstr ""
-
-#: lxc/file.go:174
+#: lxc/file.go:229
msgid "More than one file to download, but target is not a directory"
msgstr ""
-#: lxc/move.go:17
-msgid "Move containers within or in between lxd instances.\n"
- "\n"
- "lxc move [remote:]<source container> [remote:]<destination container>\n"
- " Move a container between two hosts, renaming it if destination name differs.\n"
- "\n"
- "lxc move <old name> <new name>\n"
- " Rename a local container.\n"
+#: lxc/move.go:11
+msgid "Move containers within or in between lxd instances."
msgstr ""
-#: lxc/list.go:336 lxc/remote.go:312
+#: lxc/list.go:347 lxc/remote.go:206
msgid "NAME"
msgstr ""
-#: lxc/remote.go:287 lxc/remote.go:292
+#: lxc/remote.go:181 lxc/remote.go:186
msgid "NO"
msgstr ""
-#: lxc/info.go:86
+#: lxc/info.go:91
#, c-format
msgid "Name: %s"
msgstr ""
-#: lxc/image.go:152 lxc/publish.go:33
-msgid "New alias to define at target"
+#: lxc/publish.go:27
+msgid "New alias to define at target."
msgstr ""
-#: lxc/config.go:279
+#: lxc/config.go:537
msgid "No certificate provided to add"
msgstr ""
-#: lxc/config.go:302
+#: lxc/config.go:563
msgid "No fingerprint specified."
msgstr ""
-#: lxc/image.go:365
+#: lxc/image.go:258
msgid "Only https:// is supported for remote image import."
msgstr ""
-#: lxc/help.go:63 lxc/main.go:122
-msgid "Options:"
-msgstr ""
-
-#: lxc/image.go:460
+#: lxc/image.go:413
#, c-format
msgid "Output is in %s"
msgstr ""
-#: lxc/exec.go:55
+#: lxc/exec.go:36
msgid "Override the terminal mode (auto, interactive or non-interactive)"
msgstr ""
-#: lxc/list.go:420
+#: lxc/list.go:432
msgid "PERSISTENT"
msgstr ""
-#: lxc/list.go:337
+#: lxc/list.go:348
msgid "PID"
msgstr ""
-#: lxc/list.go:338
+#: lxc/list.go:349
msgid "PROFILES"
msgstr ""
-#: lxc/remote.go:314
+#: lxc/remote.go:208
msgid "PROTOCOL"
msgstr ""
-#: lxc/image.go:557 lxc/remote.go:315
+#: lxc/image.go:700 lxc/remote.go:209
msgid "PUBLIC"
msgstr ""
-#: lxc/help.go:69
-msgid "Path to an alternate client configuration directory."
-msgstr ""
-
-#: lxc/help.go:70
+#: lxc/main.go:29
msgid "Path to an alternate server directory."
msgstr ""
-#: lxc/main.go:39
+#: lxc/main.go:207
msgid "Permisson denied, are you in the lxd group?"
msgstr ""
-#: lxc/info.go:100
+#: lxc/info.go:105
#, c-format
msgid "Pid: %d"
msgstr ""
-#: lxc/help.go:25
-msgid "Presents details on how to use LXD.\n"
- "\n"
- "lxd help [--all]"
-msgstr ""
-
-#: lxc/profile.go:188
+#: lxc/profile.go:361
msgid "Press enter to open the editor again"
msgstr ""
-#: lxc/config.go:493 lxc/config.go:558 lxc/image.go:634
+#: lxc/config.go:639 lxc/config.go:704 lxc/image.go:777
msgid "Press enter to start the editor again"
msgstr ""
-#: lxc/help.go:65
+#: lxc/init.go:34 lxc/launch.go:25 lxc/main.go:36
msgid "Print debug information."
msgstr ""
-#: lxc/help.go:64
-msgid "Print less common commands."
+#: lxc/remote.go:110
+msgid "Print the default remote."
msgstr ""
-#: lxc/help.go:66
+#: lxc/init.go:39 lxc/launch.go:30 lxc/main.go:41
msgid "Print verbose information."
msgstr ""
-#: lxc/version.go:18
-msgid "Prints the version number of LXD.\n"
- "\n"
- "lxc version"
+#: lxc/main.go:90
+msgid "Prints the version number of LXD."
msgstr ""
-#: lxc/info.go:101
+#: lxc/info.go:106
#, c-format
msgid "Processes: %d"
msgstr ""
-#: lxc/profile.go:225
+#: lxc/profile.go:515
#, c-format
msgid "Profile %s applied to %s"
msgstr ""
-#: lxc/profile.go:139
+#: lxc/profile.go:235
#, c-format
msgid "Profile %s created"
msgstr ""
-#: lxc/profile.go:209
+#: lxc/profile.go:279
#, c-format
msgid "Profile %s deleted"
msgstr ""
-#: lxc/init.go:136 lxc/init.go:137 lxc/launch.go:42 lxc/launch.go:43
-msgid "Profile to apply to the new container"
+#: lxc/profile.go:154
+msgid "Profile device manipulation."
+msgstr ""
+
+#: lxc/init.go:56 lxc/launch.go:45
+msgid "Profile to apply to the new container."
msgstr ""
-#: lxc/info.go:98
+#: lxc/info.go:103
#, c-format
msgid "Profiles: %s"
msgstr ""
-#: lxc/image.go:311
+#: lxc/image.go:372
msgid "Properties:"
msgstr ""
-#: lxc/remote.go:52
-msgid "Public image server"
+#: lxc/remote.go:56
+msgid "Public image server."
msgstr ""
-#: lxc/image.go:299
+#: lxc/image.go:360
#, c-format
msgid "Public: %s"
msgstr ""
-#: lxc/publish.go:25
-msgid "Publish containers as images.\n"
- "\n"
- "lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]..."
+#: lxc/publish.go:16
+msgid "Publish containers as images."
msgstr ""
-#: lxc/remote.go:51
-msgid "Remote admin password"
+#: lxc/file.go:46
+msgid "Push a file to a container."
+msgstr ""
+
+#: lxc/remote.go:52
+msgid "Remote admin password."
msgstr ""
-#: lxc/delete.go:42
+#: lxc/delete.go:49
#, c-format
msgid "Remove %s (yes/no): "
msgstr ""
-#: lxc/delete.go:36 lxc/delete.go:37
+#: lxc/profile.go:176
+msgid "Remove a device from a profile."
+msgstr ""
+
+#: lxc/config.go:66
+msgid "Remove device from container."
+msgstr ""
+
+#: lxc/config.go:107
+msgid "Remove the cert from trusted hosts."
+msgstr ""
+
+#: lxc/remote.go:65
+msgid "Remove the remote <name>."
+msgstr ""
+
+#: lxc/remote.go:83
+msgid "Rename remote <old> to <new>."
+msgstr ""
+
+#: lxc/delete.go:29
msgid "Require user confirmation."
msgstr ""
-#: lxc/init.go:246
+#: lxc/init.go:230
#, c-format
msgid "Retrieving image: %s"
msgstr ""
-#: lxc/image.go:560
+#: lxc/image.go:703
msgid "SIZE"
msgstr ""
-#: lxc/list.go:339
+#: lxc/list.go:350
msgid "SNAPSHOTS"
msgstr ""
-#: lxc/list.go:340
+#: lxc/list.go:351
msgid "STATE"
msgstr ""
-#: lxc/remote.go:316
+#: lxc/remote.go:210
msgid "STATIC"
msgstr ""
-#: lxc/remote.go:164
+#: lxc/remote.go:408
msgid "Server certificate NACKed by user"
msgstr ""
-#: lxc/remote.go:226
+#: lxc/remote.go:470
msgid "Server doesn't trust us after adding our cert"
msgstr ""
-#: lxc/restore.go:21
+#: lxc/profile.go:109
+msgid "Set profile configuration."
+msgstr ""
+
+#: lxc/config.go:187
+msgid "Set server/container configuration key."
+msgstr ""
+
+#: lxc/restore.go:16
msgid "Set the current state of a resource back to a snapshot.\n"
"\n"
- "lxc restore [remote:]<container> <snapshot name> [--stateful]\n"
+ " lxc restore [remote:]<container> <snapshot name> [--stateful]\n"
"\n"
- "Restores a container from a snapshot (optionally with running state, see\n"
- "snapshot help for details).\n"
+ " Restores a container from a snapshot (optionally with running state, see\n"
+ " snapshot help for details).\n"
"\n"
- "For example:\n"
- "lxc snapshot u1 snap0 # create the snapshot\n"
- "lxc restore u1 snap0 # restore the snapshot"
+ " For example:\n"
+ " lxc snapshot u1 snap0 # create the snapshot\n"
+ " lxc restore u1 snap0 # restore the snapshot"
msgstr ""
-#: lxc/file.go:44
-msgid "Set the file's gid on push"
+#: lxc/restore.go:14
+msgid "Set the current state of a resource back to what it was when it was snapshotted."
msgstr ""
-#: lxc/file.go:45
-msgid "Set the file's perms on push"
+#: lxc/remote.go:101
+msgid "Set the default remote."
+msgstr ""
+
+#: lxc/file.go:52
+msgid "Set the file's gid on push."
+msgstr ""
+
+#: lxc/file.go:62
+msgid "Set the file's perms on push."
+msgstr ""
+
+#: lxc/file.go:57
+msgid "Set the file's uid on push."
+msgstr ""
+
+#: lxc/image.go:161
+msgid "Show an image."
+msgstr ""
+
+#: lxc/config.go:196
+msgid "Show container configuration."
msgstr ""
-#: lxc/file.go:43
-msgid "Set the file's uid on push"
+#: lxc/profile.go:64
+msgid "Show details of a profile."
msgstr ""
-#: lxc/help.go:32
-msgid "Show all commands (not just interesting ones)"
+#: lxc/config.go:57
+msgid "Show full device details for container."
msgstr ""
-#: lxc/info.go:33
-msgid "Show the container's last 100 log lines?"
+#: lxc/profile.go:168
+msgid "Show full device details in the given profile."
msgstr ""
-#: lxc/image.go:297
+#: lxc/image.go:358
#, c-format
msgid "Size: %.2fMB"
msgstr ""
-#: lxc/info.go:130
+#: lxc/info.go:135
msgid "Snapshots:"
msgstr ""
-#: lxc/launch.go:122
+#: lxc/launch.go:145
#, c-format
msgid "Starting %s"
msgstr ""
-#: lxc/info.go:92
+#: lxc/info.go:97
#, c-format
msgid "Status: %s"
msgstr ""
-#: lxc/publish.go:34 lxc/publish.go:35
-msgid "Stop the container if currently running"
+#: lxc/publish.go:22
+msgid "Stop the container if currently running."
msgstr ""
-#: lxc/delete.go:106 lxc/publish.go:111
+#: lxc/delete.go:113 lxc/publish.go:116
msgid "Stopping container failed!"
msgstr ""
-#: lxc/list.go:341
+#: lxc/list.go:352
msgid "TYPE"
msgstr ""
-#: lxc/delete.go:92
+#: lxc/delete.go:99
msgid "The container is currently running, stop it first or pass --force."
msgstr ""
-#: lxc/publish.go:89
+#: lxc/publish.go:94
msgid "The container is currently running. Use --force to have it stopped and restarted."
msgstr ""
-#: lxc/publish.go:62
+#: lxc/publish.go:67
msgid "There is no \"image name\". Did you want an alias?"
msgstr ""
-#: lxc/action.go:34
+#: lxc/info.go:19
+msgid "This will support remotes and images as well, but only containers for now."
+msgstr ""
+
+#: lxc/action.go:33 lxc/action.go:51
msgid "Time to wait for the container before killing it."
msgstr ""
-#: lxc/image.go:300
+#: lxc/image.go:361
msgid "Timestamps:"
msgstr ""
-#: lxc/action.go:61 lxc/launch.go:130
+#: lxc/config.go:76
+msgid "Trust manipulation."
+msgstr ""
+
+#: lxc/config.go:77
+msgid "Trust manipulation\n"
+ "\n"
+ " lxc config trust list [remote] List all trusted certs.\n"
+ " lxc config trust add [remote] <certfile.crt> Add certfile.crt to trusted hosts.\n"
+ " lxc config trust remove [remote] [hostname|fingerprint]\n"
+ " Remove the cert from trusted hosts.\n"
+msgstr ""
+
+#: lxc/action.go:98 lxc/launch.go:153
#, c-format
msgid "Try `lxc info --show-log %s` for more info"
msgstr ""
-#: lxc/info.go:94
+#: lxc/info.go:99
msgid "Type: ephemeral"
msgstr ""
-#: lxc/info.go:96
+#: lxc/info.go:101
msgid "Type: persistent"
msgstr ""
-#: lxc/image.go:561
+#: lxc/image.go:704
msgid "UPLOAD DATE"
msgstr ""
-#: lxc/remote.go:313
+#: lxc/remote.go:207
msgid "URL"
msgstr ""
-#: lxc/image.go:305
-#, c-format
-msgid "Uploaded: %s"
+#: lxc/profile.go:118
+msgid "Unset profile configuration."
msgstr ""
-#: lxc/main.go:122
-#, c-format
-msgid "Usage: %s"
+#: lxc/config.go:178
+msgid "Unset server configuration key."
msgstr ""
-#: lxc/help.go:48
-msgid "Usage: lxc [subcommand] [options]"
+#: lxc/remote.go:92
+msgid "Update <name>'s url to <url>."
msgstr ""
-#: lxc/delete.go:46
+#: lxc/image.go:366
+#, c-format
+msgid "Uploaded: %s"
+msgstr ""
+
+#: lxc/delete.go:53
msgid "User aborted delete operation."
msgstr ""
-#: lxc/restore.go:35
-msgid "Whether or not to restore the container's running state from snapshot (if available)"
+#: lxc/restore.go:30
+msgid "Whether or not to restore the container's running state from snapshot (if available)."
msgstr ""
-#: lxc/snapshot.go:38
-msgid "Whether or not to snapshot the container's running state"
+#: lxc/snapshot.go:33
+msgid "Whether or not to snapshot the container's running state."
msgstr ""
-#: lxc/config.go:33
-msgid "Whether to show the expanded configuration"
+#: lxc/config.go:200
+msgid "Whether to show the expanded configuration."
msgstr ""
-#: lxc/remote.go:289 lxc/remote.go:294
+#: lxc/remote.go:183 lxc/remote.go:188
msgid "YES"
msgstr ""
-#: lxc/main.go:66
-msgid "`lxc config profile` is deprecated, please use `lxc profile`"
+#: lxc/config.go:195
+msgid "[--expanded] [remote:]<container>"
+msgstr ""
+
+#: lxc/file.go:45
+msgid "[--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>"
+msgstr ""
+
+#: lxc/info.go:18
+msgid "[<remote>:]container [--show-log]"
+msgstr ""
+
+#: lxc/config.go:168
+msgid "[[remote:]<container>] key"
+msgstr ""
+
+#: lxc/config.go:186
+msgid "[[remote:]<container>] key value"
+msgstr ""
+
+#: lxc/profile.go:54
+msgid "[filters]"
+msgstr ""
+
+#: lxc/image.go:200
+msgid "[remote:]"
+msgstr ""
+
+#: lxc/monitor.go:16
+msgid "[remote:] [--type=TYPE...]"
+msgstr ""
+
+#: lxc/config.go:47 lxc/config.go:56 lxc/config.go:159
+msgid "[remote:]<container>"
+msgstr ""
+
+#: lxc/config.go:65
+msgid "[remote:]<container> <name>"
+msgstr ""
+
+#: lxc/delete.go:18
+msgid "[remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...] [--force|-f] [--interactive|-i]"
+msgstr ""
+
+#: lxc/image.go:124 lxc/image.go:133 lxc/image.go:142 lxc/image.go:160 lxc/image.go:169
+msgid "[remote:]<image>"
msgstr ""
-#: lxc/launch.go:109
+#: lxc/image.go:102
+msgid "[remote:]<image> <remote>:"
+msgstr ""
+
+#: lxc/init.go:17 lxc/launch.go:16
+msgid "[remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...]"
+msgstr ""
+
+#: lxc/restore.go:15
+msgid "[remote:]<resource> <snapshot name> [--stateful]"
+msgstr ""
+
+#: lxc/copy.go:16 lxc/move.go:12
+msgid "[remote:]<source container> [remote:]<destination container>"
+msgstr ""
+
+#: lxc/snapshot.go:15
+msgid "[remote:]<source> <snapshot name> [--stateful]"
+msgstr ""
+
+#: lxc/exec.go:23
+msgid "[remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>"
+msgstr ""
+
+#: lxc/publish.go:17
+msgid "[remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]..."
+msgstr ""
+
+#: lxc/config.go:88
+msgid "[remote]"
+msgstr ""
+
+#: lxc/config.go:97
+msgid "[remote] <certfile.crt>"
+msgstr ""
+
+#: lxc/config.go:106
+msgid "[remote] [hostname|fingerprint]"
+msgstr ""
+
+#: lxc/image.go:151 lxc/list.go:21
+msgid "[resource] [filters] [-c columns] [--fast]"
+msgstr ""
+
+#: lxc/launch.go:132
msgid "bad number of things scanned from image, container or snapshot"
msgstr ""
-#: lxc/action.go:57
+#: lxc/action.go:94
msgid "bad result type from action"
msgstr ""
-#: lxc/copy.go:78
+#: lxc/copy.go:84
msgid "can't copy to the same container name"
msgstr ""
-#: lxc/remote.go:277
+#: lxc/remote.go:168
msgid "can't remove the default remote"
msgstr ""
-#: lxc/remote.go:303
+#: lxc/remote.go:197
msgid "default"
msgstr ""
-#: lxc/init.go:199 lxc/init.go:204 lxc/launch.go:93 lxc/launch.go:98
+#: lxc/init.go:183 lxc/init.go:188 lxc/launch.go:116 lxc/launch.go:121
msgid "didn't get any affected image, container or snapshot from server"
msgstr ""
-#: lxc/main.go:25 lxc/main.go:157
+#: lxc/main.go:193
#, c-format
msgid "error: %v"
msgstr ""
-#: lxc/help.go:40 lxc/main.go:117
-#, c-format
-msgid "error: unknown command: %s"
+#: lxc/launch.go:136
+msgid "got bad version"
msgstr ""
-#: lxc/launch.go:113
-msgid "got bad version"
+#: lxc/config.go:177
+msgid "key"
msgstr ""
-#: lxc/image.go:291 lxc/image.go:538
+#: lxc/image.go:352 lxc/image.go:681
msgid "no"
msgstr ""
-#: lxc/copy.go:101
+#: lxc/copy.go:107
msgid "not all the profiles from the source exist on the target"
msgstr ""
-#: lxc/remote.go:157
+#: lxc/remote.go:401
msgid "ok (y/n)?"
msgstr ""
-#: lxc/main.go:264 lxc/main.go:268
+#: lxc/main.go:290 lxc/main.go:294
#, c-format
msgid "processing aliases failed %s\n"
msgstr ""
-#: lxc/remote.go:338
+#: lxc/remote.go:234
#, c-format
msgid "remote %s already exists"
msgstr ""
-#: lxc/remote.go:269 lxc/remote.go:330 lxc/remote.go:365 lxc/remote.go:381
+#: lxc/remote.go:160 lxc/remote.go:226 lxc/remote.go:264 lxc/remote.go:284
#, c-format
msgid "remote %s doesn't exist"
msgstr ""
-#: lxc/remote.go:252
+#: lxc/remote.go:134
#, c-format
msgid "remote %s exists as <%s>"
msgstr ""
-#: lxc/remote.go:273 lxc/remote.go:334 lxc/remote.go:369
+#: lxc/remote.go:164 lxc/remote.go:230 lxc/remote.go:268
#, c-format
msgid "remote %s is static and cannot be modified"
msgstr ""
-#: lxc/info.go:139
+#: lxc/info.go:144
msgid "stateful"
msgstr ""
-#: lxc/info.go:141
+#: lxc/info.go:146
msgid "stateless"
msgstr ""
-#: lxc/info.go:135
+#: lxc/info.go:140
#, c-format
msgid "taken at %s"
msgstr ""
-#: lxc/exec.go:159
+#: lxc/exec.go:176
msgid "unreachable return reached"
msgstr ""
-#: lxc/main.go:197
+#: lxc/main.go:21
msgid "wrong number of subcommand arguments"
msgstr ""
-#: lxc/delete.go:45 lxc/image.go:294 lxc/image.go:542
+#: lxc/delete.go:52 lxc/image.go:355 lxc/image.go:685
msgid "yes"
msgstr ""
-#: lxc/copy.go:38
+#: lxc/copy.go:44
msgid "you must specify a source container name"
msgstr ""
diff --git a/test/suites/basic.sh b/test/suites/basic.sh
index 29d4720..d838854 100644
--- a/test/suites/basic.sh
+++ b/test/suites/basic.sh
@@ -68,7 +68,7 @@ test_basic_usage() {
curl -k -s -X GET "https://${LXD_ADDR}/1.0/containers/foo" | grep 403
# Test unprivileged container publish
- lxc publish bar --alias=foo-image prop1=val1
+ lxc publish --alias=foo-image bar prop1=val1
lxc image show foo-image | grep val1
curl -k -s --cert "${LXD_CONF}/client3.crt" --key "${LXD_CONF}/client3.key" -X GET "https://${LXD_ADDR}/1.0/images" | grep "/1.0/images/" && false
lxc image delete foo-image
@@ -77,7 +77,7 @@ test_basic_usage() {
lxc profile create priv
lxc profile set priv security.privileged true
lxc init testimage barpriv -p default -p priv
- lxc publish barpriv --alias=foo-image prop1=val1
+ lxc publish --alias=foo-image barpriv prop1=val1
lxc image show foo-image | grep val1
curl -k -s --cert "${LXD_CONF}/client3.crt" --key "${LXD_CONF}/client3.key" -X GET "https://${LXD_ADDR}/1.0/images" | grep "/1.0/images/" && false
lxc image delete foo-image
@@ -98,7 +98,7 @@ test_basic_usage() {
fi
# Test public images
- lxc publish --public bar --alias=foo-image2
+ lxc publish bar --alias=foo-image2 --public
curl -k -s --cert "${LXD_CONF}/client3.crt" --key "${LXD_CONF}/client3.key" -X GET "https://${LXD_ADDR}/1.0/images" | grep "/1.0/images/"
lxc image delete foo-image2
diff --git a/test/suites/remote.sh b/test/suites/remote.sh
index e5f2107..c3735a7 100644
--- a/test/suites/remote.sh
+++ b/test/suites/remote.sh
@@ -34,6 +34,8 @@ test_remote_url() {
}
test_remote_admin() {
+ lxc_remote remote list
+ lxc_remote config trust list
lxc_remote remote add badpass "${LXD_ADDR}" --accept-certificate --password bad || true
! lxc_remote list badpass:
@@ -112,7 +114,7 @@ test_remote_usage() {
# test remote publish
lxc_remote init testimage pub
- lxc_remote publish pub lxd2: --alias bar --public a=b
+ lxc_remote publish --alias bar --public pub lxd2: a=b
lxc_remote image show lxd2:bar | grep -q "a: b"
lxc_remote image show lxd2:bar | grep -q "public: true"
! lxc_remote image show bar
More information about the lxc-devel
mailing list