[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":     &copyCmd{},
-	"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